ethernet: Make it possible for ethernet drivers to send/receive net_buffers.
This paves the way for a variety of more interesting interactions between drivers and the stack which are currently not possible (e.g. checksum offload, #18744). The main advantage for the moment is that we will save a memcpy of the buffer on each send/receive. Adapt the virtio_net driver so that at least one driver is using the new interface. Network still seems to work OK with it. Change-Id: Ic5832e4865e3e1bed7462583ca1ffd16418d7cab Reviewed-on: https://review.haiku-os.org/c/haiku/+/7912 Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
committed by
waddlesplash
parent
60e949faf8
commit
1485e71d8c
@@ -23,8 +23,11 @@ enum {
|
||||
ETHER_GETFRAMESIZE, /* get frame size (required) (int *) */
|
||||
ETHER_SET_LINK_STATE_SEM,
|
||||
/* pass over a semaphore to release on link state changes (sem_id *) */
|
||||
ETHER_GET_LINK_STATE
|
||||
ETHER_GET_LINK_STATE,
|
||||
/* get line speed, quality, duplex mode, etc. (ether_link_state_t *) */
|
||||
|
||||
ETHER_SEND_NET_BUFFER, /* send a net_buffer */
|
||||
ETHER_RECEIVE_NET_BUFFER, /* receive a net_buffer */
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,10 @@
|
||||
#include <new>
|
||||
|
||||
#include <ethernet.h>
|
||||
#include <kernel.h>
|
||||
#include <lock.h>
|
||||
#include <net_buffer.h>
|
||||
#include <util/AutoLock.h>
|
||||
#include <util/DoublyLinkedList.h>
|
||||
#include <virtio.h>
|
||||
|
||||
@@ -116,6 +119,7 @@ typedef struct {
|
||||
|
||||
|
||||
static device_manager_info* sDeviceManager;
|
||||
static net_buffer_module_info* sBufferModule;
|
||||
|
||||
|
||||
static void virtio_net_rxDone(void* driverCookie, void* cookie);
|
||||
@@ -608,15 +612,15 @@ virtio_net_rxDone(void* driverCookie, void* cookie)
|
||||
|
||||
|
||||
static status_t
|
||||
virtio_net_read(void* cookie, off_t pos, void* buffer, size_t* _length)
|
||||
virtio_net_receive(void* cookie, net_buffer** _buffer)
|
||||
{
|
||||
CALLED();
|
||||
virtio_net_handle* handle = (virtio_net_handle*)cookie;
|
||||
virtio_net_driver_info* info = handle->info;
|
||||
|
||||
mutex_lock(&info->rxLock);
|
||||
MutexLocker rxLocker(info->rxLock);
|
||||
while (info->rxFullList.Head() == NULL) {
|
||||
mutex_unlock(&info->rxLock);
|
||||
rxLocker.Unlock();
|
||||
|
||||
if (info->nonblocking)
|
||||
return B_WOULD_BLOCK;
|
||||
@@ -631,7 +635,7 @@ virtio_net_read(void* cookie, off_t pos, void* buffer, size_t* _length)
|
||||
if (semCount > 0)
|
||||
acquire_sem_etc(info->rxDone, semCount, B_RELATIVE_TIMEOUT, 0);
|
||||
|
||||
mutex_lock(&info->rxLock);
|
||||
rxLocker.Lock();
|
||||
while (info->rxDone != -1) {
|
||||
uint32 usedLength = 0;
|
||||
BufInfo* buf = NULL;
|
||||
@@ -646,12 +650,22 @@ virtio_net_read(void* cookie, off_t pos, void* buffer, size_t* _length)
|
||||
TRACE("virtio_net_read: finished waiting\n");
|
||||
}
|
||||
|
||||
net_buffer* buffer = sBufferModule->create(0);
|
||||
if (buffer == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
BufInfo* buf = info->rxFullList.RemoveHead();
|
||||
*_length = MIN(buf->rxUsedLength, *_length);
|
||||
memcpy(buffer, buf->buffer, *_length);
|
||||
rxLocker.Unlock();
|
||||
|
||||
if (sBufferModule->append(buffer, buf->buffer, buf->rxUsedLength) != B_OK) {
|
||||
sBufferModule->free(buffer);
|
||||
buffer = NULL;
|
||||
}
|
||||
*_buffer = buffer;
|
||||
|
||||
rxLocker.Lock();
|
||||
virtio_net_rx_enqueue_buf(info, buf);
|
||||
mutex_unlock(&info->rxLock);
|
||||
return B_OK;
|
||||
return (buffer != NULL) ? B_OK : B_NO_MEMORY;
|
||||
}
|
||||
|
||||
|
||||
@@ -666,8 +680,7 @@ virtio_net_txDone(void* driverCookie, void* cookie)
|
||||
|
||||
|
||||
static status_t
|
||||
virtio_net_write(void* cookie, off_t pos, const void* buffer,
|
||||
size_t* _length)
|
||||
virtio_net_send(void* cookie, net_buffer* buffer)
|
||||
{
|
||||
CALLED();
|
||||
virtio_net_handle* handle = (virtio_net_handle*)cookie;
|
||||
@@ -703,25 +716,32 @@ virtio_net_write(void* cookie, off_t pos, const void* buffer,
|
||||
}
|
||||
BufInfo* buf = info->txFreeList.RemoveHead();
|
||||
|
||||
TRACE("virtio_net_write: copying %lu\n", MIN(MAX_FRAME_SIZE, *_length));
|
||||
memcpy(buf->buffer, buffer, MIN(MAX_FRAME_SIZE, *_length));
|
||||
const size_t size = MIN(MAX_FRAME_SIZE, buffer->size);
|
||||
TRACE("virtio_net_write: copying %lu\n", size);
|
||||
if (sBufferModule->read(buffer, 0, buf->buffer, size) != B_OK) {
|
||||
info->txFreeList.Add(buf);
|
||||
mutex_unlock(&info->txLock);
|
||||
return B_BAD_DATA;
|
||||
}
|
||||
memset(buf->hdr, 0, sizeof(virtio_net_hdr));
|
||||
|
||||
physical_entry entries[2];
|
||||
entries[0] = buf->hdrEntry;
|
||||
entries[0].size = sizeof(virtio_net_hdr);
|
||||
entries[1] = buf->entry;
|
||||
entries[1].size = MIN(MAX_FRAME_SIZE, *_length);
|
||||
entries[1].size = size;
|
||||
|
||||
// queue the virtio_net_hdr + buffer data
|
||||
status_t status = info->virtio->queue_request_v(info->txQueues[0],
|
||||
entries, 2, 0, buf);
|
||||
mutex_unlock(&info->txLock);
|
||||
|
||||
if (status != B_OK) {
|
||||
ERROR("tx queueing on queue %d failed (%s)\n", 0, strerror(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
sBufferModule->free(buffer);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@@ -853,6 +873,20 @@ virtio_net_ioctl(void* cookie, uint32 op, void* buffer, size_t length)
|
||||
return user_memcpy(buffer, &state, sizeof(ether_link_state_t));
|
||||
}
|
||||
|
||||
case ETHER_SEND_NET_BUFFER:
|
||||
if (buffer == NULL || length == 0)
|
||||
return B_BAD_DATA;
|
||||
if (!IS_KERNEL_ADDRESS(buffer))
|
||||
return B_BAD_ADDRESS;
|
||||
return virtio_net_send(cookie, (net_buffer*)buffer);
|
||||
|
||||
case ETHER_RECEIVE_NET_BUFFER:
|
||||
if (buffer == NULL || length == 0)
|
||||
return B_BAD_DATA;
|
||||
if (!IS_KERNEL_ADDRESS(buffer))
|
||||
return B_BAD_ADDRESS;
|
||||
return virtio_net_receive(cookie, (net_buffer**)buffer);
|
||||
|
||||
default:
|
||||
ERROR("ioctl: unknown message %" B_PRIx32 "\n", op);
|
||||
break;
|
||||
@@ -961,6 +995,7 @@ virtio_net_register_child_devices(void* _cookie)
|
||||
|
||||
module_dependency module_dependencies[] = {
|
||||
{B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager},
|
||||
{NET_BUFFER_MODULE_NAME, (module_info**)&sBufferModule},
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -978,8 +1013,8 @@ struct device_module_info sVirtioNetDevice = {
|
||||
virtio_net_open,
|
||||
virtio_net_close,
|
||||
virtio_net_free,
|
||||
virtio_net_read,
|
||||
virtio_net_write,
|
||||
NULL, // read
|
||||
NULL, // write
|
||||
NULL, // io
|
||||
virtio_net_ioctl,
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ struct ethernet_device : net_device, DoublyLinkedListLinkImpl<ethernet_device> {
|
||||
|
||||
int fd;
|
||||
uint32 frame_size;
|
||||
bool supports_net_buffer;
|
||||
|
||||
void* read_buffer, *write_buffer;
|
||||
mutex read_buffer_lock, write_buffer_lock;
|
||||
@@ -199,6 +200,12 @@ ethernet_up(net_device *_device)
|
||||
if (ioctl(device->fd, ETHER_GETADDR, device->address.data, ETHER_ADDRESS_LENGTH) < 0)
|
||||
goto err;
|
||||
|
||||
if (ioctl(device->fd, ETHER_SEND_NET_BUFFER, NULL, 0) != 0) {
|
||||
// Check if the returned error code is B_BAD_DATA (not EINVAL or EOPNOTSUPP).
|
||||
if (errno == B_BAD_DATA)
|
||||
device->supports_net_buffer = true;
|
||||
}
|
||||
|
||||
if (ioctl(device->fd, ETHER_GETFRAMESIZE, &device->frame_size, sizeof(uint32)) < 0) {
|
||||
// this call is obviously optional
|
||||
device->frame_size = ETHER_MAX_FRAME_SIZE;
|
||||
@@ -287,6 +294,12 @@ ethernet_send_data(net_device *_device, net_buffer *buffer)
|
||||
if (buffer->size > device->frame_size || buffer->size < ETHER_HEADER_LENGTH)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
if (device->supports_net_buffer) {
|
||||
if (ioctl(device->fd, ETHER_SEND_NET_BUFFER, buffer, sizeof(net_buffer)) != 0)
|
||||
return errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
net_buffer *allocated = NULL;
|
||||
net_buffer *original = buffer;
|
||||
|
||||
@@ -347,6 +360,12 @@ ethernet_receive_data(net_device *_device, net_buffer **_buffer)
|
||||
if (device->fd == -1)
|
||||
return B_FILE_ERROR;
|
||||
|
||||
if (device->supports_net_buffer) {
|
||||
if (ioctl(device->fd, ETHER_RECEIVE_NET_BUFFER, _buffer, sizeof(net_buffer*)) != 0)
|
||||
return errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: better header space
|
||||
net_buffer *buffer = gBufferModule->create(256);
|
||||
if (buffer == NULL)
|
||||
|
||||
Reference in New Issue
Block a user