From d69baff8cc981d98ca209d34636963d73c823e02 Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Sat, 17 Nov 2018 00:34:39 +0100 Subject: [PATCH] virtio_net: Align transfer buffers, check get_memory_map result. The buffers were allocated with B_ANY_KERNEL_ADDRESS and the buffer plus header block would produce uneven sizes. This lead to some buffers crossing page boundaries. The call to get_memory_map() is only supplied with a single entry, which in such a situation may not be enough if the physical pages are not contiguous. Due to missing result checks this was not noticed. Use B_ANY_KERNEL_BLOCK_ADDRESS and align the size of the total buffer to avoid such page boundary crossing. Fixes randomly truncated frames in the receive path. --- .../drivers/network/virtio/virtio_net.cpp | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/add-ons/kernel/drivers/network/virtio/virtio_net.cpp b/src/add-ons/kernel/drivers/network/virtio/virtio_net.cpp index c1d19a9115..87b1d4f63f 100644 --- a/src/add-ons/kernel/drivers/network/virtio/virtio_net.cpp +++ b/src/add-ons/kernel/drivers/network/virtio/virtio_net.cpp @@ -23,7 +23,6 @@ #define VIRTIO_NET_DEVICE_ID_GENERATOR "virtio_net/device_id" #define BUFFER_SIZE 2048 -// #define MAX_FRAME_SIZE (BUFFER_SIZE - sizeof(virtio_net_hdr)) #define MAX_FRAME_SIZE 1536 @@ -269,8 +268,6 @@ virtio_net_init_device(void* _info, void** _cookie) char* rxBuffer; char* txBuffer; - const size_t rxBufferSize = BUFFER_SIZE + sizeof(virtio_net_rx_hdr); - const size_t txBufferSize = BUFFER_SIZE + sizeof(virtio_net_tx_hdr); info->rxQueues = new(std::nothrow) virtio_queue[info->pairsCount]; info->txQueues = new(std::nothrow) virtio_queue[info->pairsCount]; @@ -301,8 +298,8 @@ virtio_net_init_device(void* _info, void** _cookie) // create receive buffer area info->rxArea = create_area("virtionet rx buffer", (void**)&rxBuffer, - B_ANY_KERNEL_ADDRESS, ROUND_TO_PAGE_SIZE( - rxBufferSize * info->rxSizes[0]), + B_ANY_KERNEL_BLOCK_ADDRESS, ROUND_TO_PAGE_SIZE( + BUFFER_SIZE * info->rxSizes[0]), B_FULL_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); if (info->rxArea < B_OK) { status = info->rxArea; @@ -316,19 +313,27 @@ virtio_net_init_device(void* _info, void** _cookie) status = B_NO_MEMORY; goto err4; } + info->rxBufInfos[i] = buf; buf->hdr = (struct virtio_net_hdr*)((addr_t)rxBuffer - + i * rxBufferSize); + + i * BUFFER_SIZE); buf->buffer = (char*)((addr_t)buf->hdr + sizeof(virtio_net_rx_hdr)); - get_memory_map(buf->buffer, BUFFER_SIZE, &buf->entry, 1); - get_memory_map(buf->hdr, sizeof(struct virtio_net_hdr), + + status = get_memory_map(buf->buffer, + BUFFER_SIZE - sizeof(virtio_net_rx_hdr), &buf->entry, 1); + if (status != B_OK) + goto err4; + + status = get_memory_map(buf->hdr, sizeof(struct virtio_net_hdr), &buf->hdrEntry, 1); + if (status != B_OK) + goto err4; } // create transmit buffer area info->txArea = create_area("virtionet tx buffer", (void**)&txBuffer, - B_ANY_KERNEL_ADDRESS, ROUND_TO_PAGE_SIZE( - txBufferSize * info->txSizes[0]), + B_ANY_KERNEL_BLOCK_ADDRESS, ROUND_TO_PAGE_SIZE( + BUFFER_SIZE * info->txSizes[0]), B_FULL_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); if (info->txArea < B_OK) { status = info->txArea; @@ -342,13 +347,22 @@ virtio_net_init_device(void* _info, void** _cookie) status = B_NO_MEMORY; goto err6; } + info->txBufInfos[i] = buf; buf->hdr = (struct virtio_net_hdr*)((addr_t)txBuffer - + i * txBufferSize); + + i * BUFFER_SIZE); buf->buffer = (char*)((addr_t)buf->hdr + sizeof(virtio_net_tx_hdr)); - get_memory_map(buf->buffer, BUFFER_SIZE, &buf->entry, 1); - get_memory_map(buf->hdr, sizeof(struct virtio_net_hdr), + + status = get_memory_map(buf->buffer, + BUFFER_SIZE - sizeof(virtio_net_tx_hdr), &buf->entry, 1); + if (status != B_OK) + goto err6; + + status = get_memory_map(buf->hdr, sizeof(struct virtio_net_hdr), &buf->hdrEntry, 1); + if (status != B_OK) + goto err6; + info->txFreeList.Add(buf); }