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_GETFRAMESIZE, /* get frame size (required) (int *) */
|
||||||
ETHER_SET_LINK_STATE_SEM,
|
ETHER_SET_LINK_STATE_SEM,
|
||||||
/* pass over a semaphore to release on link state changes (sem_id *) */
|
/* 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 *) */
|
/* 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 <new>
|
||||||
|
|
||||||
#include <ethernet.h>
|
#include <ethernet.h>
|
||||||
|
#include <kernel.h>
|
||||||
#include <lock.h>
|
#include <lock.h>
|
||||||
|
#include <net_buffer.h>
|
||||||
|
#include <util/AutoLock.h>
|
||||||
#include <util/DoublyLinkedList.h>
|
#include <util/DoublyLinkedList.h>
|
||||||
#include <virtio.h>
|
#include <virtio.h>
|
||||||
|
|
||||||
@@ -116,6 +119,7 @@ typedef struct {
|
|||||||
|
|
||||||
|
|
||||||
static device_manager_info* sDeviceManager;
|
static device_manager_info* sDeviceManager;
|
||||||
|
static net_buffer_module_info* sBufferModule;
|
||||||
|
|
||||||
|
|
||||||
static void virtio_net_rxDone(void* driverCookie, void* cookie);
|
static void virtio_net_rxDone(void* driverCookie, void* cookie);
|
||||||
@@ -608,15 +612,15 @@ virtio_net_rxDone(void* driverCookie, void* cookie)
|
|||||||
|
|
||||||
|
|
||||||
static status_t
|
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();
|
CALLED();
|
||||||
virtio_net_handle* handle = (virtio_net_handle*)cookie;
|
virtio_net_handle* handle = (virtio_net_handle*)cookie;
|
||||||
virtio_net_driver_info* info = handle->info;
|
virtio_net_driver_info* info = handle->info;
|
||||||
|
|
||||||
mutex_lock(&info->rxLock);
|
MutexLocker rxLocker(info->rxLock);
|
||||||
while (info->rxFullList.Head() == NULL) {
|
while (info->rxFullList.Head() == NULL) {
|
||||||
mutex_unlock(&info->rxLock);
|
rxLocker.Unlock();
|
||||||
|
|
||||||
if (info->nonblocking)
|
if (info->nonblocking)
|
||||||
return B_WOULD_BLOCK;
|
return B_WOULD_BLOCK;
|
||||||
@@ -631,7 +635,7 @@ virtio_net_read(void* cookie, off_t pos, void* buffer, size_t* _length)
|
|||||||
if (semCount > 0)
|
if (semCount > 0)
|
||||||
acquire_sem_etc(info->rxDone, semCount, B_RELATIVE_TIMEOUT, 0);
|
acquire_sem_etc(info->rxDone, semCount, B_RELATIVE_TIMEOUT, 0);
|
||||||
|
|
||||||
mutex_lock(&info->rxLock);
|
rxLocker.Lock();
|
||||||
while (info->rxDone != -1) {
|
while (info->rxDone != -1) {
|
||||||
uint32 usedLength = 0;
|
uint32 usedLength = 0;
|
||||||
BufInfo* buf = NULL;
|
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");
|
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();
|
BufInfo* buf = info->rxFullList.RemoveHead();
|
||||||
*_length = MIN(buf->rxUsedLength, *_length);
|
rxLocker.Unlock();
|
||||||
memcpy(buffer, buf->buffer, *_length);
|
|
||||||
|
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);
|
virtio_net_rx_enqueue_buf(info, buf);
|
||||||
mutex_unlock(&info->rxLock);
|
return (buffer != NULL) ? B_OK : B_NO_MEMORY;
|
||||||
return B_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -666,8 +680,7 @@ virtio_net_txDone(void* driverCookie, void* cookie)
|
|||||||
|
|
||||||
|
|
||||||
static status_t
|
static status_t
|
||||||
virtio_net_write(void* cookie, off_t pos, const void* buffer,
|
virtio_net_send(void* cookie, net_buffer* buffer)
|
||||||
size_t* _length)
|
|
||||||
{
|
{
|
||||||
CALLED();
|
CALLED();
|
||||||
virtio_net_handle* handle = (virtio_net_handle*)cookie;
|
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();
|
BufInfo* buf = info->txFreeList.RemoveHead();
|
||||||
|
|
||||||
TRACE("virtio_net_write: copying %lu\n", MIN(MAX_FRAME_SIZE, *_length));
|
const size_t size = MIN(MAX_FRAME_SIZE, buffer->size);
|
||||||
memcpy(buf->buffer, buffer, MIN(MAX_FRAME_SIZE, *_length));
|
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));
|
memset(buf->hdr, 0, sizeof(virtio_net_hdr));
|
||||||
|
|
||||||
physical_entry entries[2];
|
physical_entry entries[2];
|
||||||
entries[0] = buf->hdrEntry;
|
entries[0] = buf->hdrEntry;
|
||||||
entries[0].size = sizeof(virtio_net_hdr);
|
entries[0].size = sizeof(virtio_net_hdr);
|
||||||
entries[1] = buf->entry;
|
entries[1] = buf->entry;
|
||||||
entries[1].size = MIN(MAX_FRAME_SIZE, *_length);
|
entries[1].size = size;
|
||||||
|
|
||||||
// queue the virtio_net_hdr + buffer data
|
// queue the virtio_net_hdr + buffer data
|
||||||
status_t status = info->virtio->queue_request_v(info->txQueues[0],
|
status_t status = info->virtio->queue_request_v(info->txQueues[0],
|
||||||
entries, 2, 0, buf);
|
entries, 2, 0, buf);
|
||||||
mutex_unlock(&info->txLock);
|
mutex_unlock(&info->txLock);
|
||||||
|
|
||||||
if (status != B_OK) {
|
if (status != B_OK) {
|
||||||
ERROR("tx queueing on queue %d failed (%s)\n", 0, strerror(status));
|
ERROR("tx queueing on queue %d failed (%s)\n", 0, strerror(status));
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sBufferModule->free(buffer);
|
||||||
return B_OK;
|
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));
|
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:
|
default:
|
||||||
ERROR("ioctl: unknown message %" B_PRIx32 "\n", op);
|
ERROR("ioctl: unknown message %" B_PRIx32 "\n", op);
|
||||||
break;
|
break;
|
||||||
@@ -961,6 +995,7 @@ virtio_net_register_child_devices(void* _cookie)
|
|||||||
|
|
||||||
module_dependency module_dependencies[] = {
|
module_dependency module_dependencies[] = {
|
||||||
{B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager},
|
{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_open,
|
||||||
virtio_net_close,
|
virtio_net_close,
|
||||||
virtio_net_free,
|
virtio_net_free,
|
||||||
virtio_net_read,
|
NULL, // read
|
||||||
virtio_net_write,
|
NULL, // write
|
||||||
NULL, // io
|
NULL, // io
|
||||||
virtio_net_ioctl,
|
virtio_net_ioctl,
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ struct ethernet_device : net_device, DoublyLinkedListLinkImpl<ethernet_device> {
|
|||||||
|
|
||||||
int fd;
|
int fd;
|
||||||
uint32 frame_size;
|
uint32 frame_size;
|
||||||
|
bool supports_net_buffer;
|
||||||
|
|
||||||
void* read_buffer, *write_buffer;
|
void* read_buffer, *write_buffer;
|
||||||
mutex read_buffer_lock, write_buffer_lock;
|
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)
|
if (ioctl(device->fd, ETHER_GETADDR, device->address.data, ETHER_ADDRESS_LENGTH) < 0)
|
||||||
goto err;
|
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) {
|
if (ioctl(device->fd, ETHER_GETFRAMESIZE, &device->frame_size, sizeof(uint32)) < 0) {
|
||||||
// this call is obviously optional
|
// this call is obviously optional
|
||||||
device->frame_size = ETHER_MAX_FRAME_SIZE;
|
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)
|
if (buffer->size > device->frame_size || buffer->size < ETHER_HEADER_LENGTH)
|
||||||
return B_BAD_VALUE;
|
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 *allocated = NULL;
|
||||||
net_buffer *original = buffer;
|
net_buffer *original = buffer;
|
||||||
|
|
||||||
@@ -347,6 +360,12 @@ ethernet_receive_data(net_device *_device, net_buffer **_buffer)
|
|||||||
if (device->fd == -1)
|
if (device->fd == -1)
|
||||||
return B_FILE_ERROR;
|
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
|
// TODO: better header space
|
||||||
net_buffer *buffer = gBufferModule->create(256);
|
net_buffer *buffer = gBufferModule->create(256);
|
||||||
if (buffer == NULL)
|
if (buffer == NULL)
|
||||||
|
|||||||
Reference in New Issue
Block a user