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:
Augustin Cavalier
2024-07-30 17:03:36 +00:00
committed by waddlesplash
parent 60e949faf8
commit 1485e71d8c
3 changed files with 73 additions and 16 deletions
+4 -1
View File
@@ -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)