diff --git a/headers/private/net/ether_driver.h b/headers/private/net/ether_driver.h index 0ce22aa079..9d8788f5ea 100644 --- a/headers/private/net/ether_driver.h +++ b/headers/private/net/ether_driver.h @@ -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 */ }; diff --git a/src/add-ons/kernel/drivers/network/ether/virtio/virtio_net.cpp b/src/add-ons/kernel/drivers/network/ether/virtio/virtio_net.cpp index 6a64e010d1..8cae494a1e 100644 --- a/src/add-ons/kernel/drivers/network/ether/virtio/virtio_net.cpp +++ b/src/add-ons/kernel/drivers/network/ether/virtio/virtio_net.cpp @@ -9,7 +9,10 @@ #include #include +#include #include +#include +#include #include #include @@ -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, diff --git a/src/add-ons/kernel/network/devices/ethernet/ethernet.cpp b/src/add-ons/kernel/network/devices/ethernet/ethernet.cpp index e277e0a4e5..7ab40f2bfe 100644 --- a/src/add-ons/kernel/network/devices/ethernet/ethernet.cpp +++ b/src/add-ons/kernel/network/devices/ethernet/ethernet.cpp @@ -39,6 +39,7 @@ struct ethernet_device : net_device, DoublyLinkedListLinkImpl { 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)