sfc: Support only two rx buffers per page

- Pull the loop handling into efx_init_rx_buffers_(skb|page)
- Remove rx_queue->buf_page, and associated clean up code
- Remove unmap_addr, since unmap_addr is trivially calculable

This will allow us to recycle discarded buffers directly
from efx_rx_packet(), since will never be in the middle of
splitting a page.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Steve Hodgson 2010-06-01 11:33:17 +00:00 committed by David S. Miller
parent 90d683afd1
commit f7d6f379db
2 changed files with 92 additions and 138 deletions

View file

@ -222,7 +222,6 @@ struct efx_tx_queue {
* If both this and skb are %NULL, the buffer slot is currently free. * If both this and skb are %NULL, the buffer slot is currently free.
* @data: Pointer to ethernet header * @data: Pointer to ethernet header
* @len: Buffer length, in bytes. * @len: Buffer length, in bytes.
* @unmap_addr: DMA address to unmap
*/ */
struct efx_rx_buffer { struct efx_rx_buffer {
dma_addr_t dma_addr; dma_addr_t dma_addr;
@ -230,7 +229,6 @@ struct efx_rx_buffer {
struct page *page; struct page *page;
char *data; char *data;
unsigned int len; unsigned int len;
dma_addr_t unmap_addr;
}; };
/** /**
@ -257,11 +255,6 @@ struct efx_rx_buffer {
* @alloc_page_count: RX allocation strategy counter. * @alloc_page_count: RX allocation strategy counter.
* @alloc_skb_count: RX allocation strategy counter. * @alloc_skb_count: RX allocation strategy counter.
* @slow_fill: Timer used to defer efx_nic_generate_fill_event(). * @slow_fill: Timer used to defer efx_nic_generate_fill_event().
* @buf_page: Page for next RX buffer.
* We can use a single page for multiple RX buffers. This tracks
* the remaining space in the allocation.
* @buf_dma_addr: Page's DMA address.
* @buf_data: Page's host address.
* @flushed: Use when handling queue flushing * @flushed: Use when handling queue flushing
*/ */
struct efx_rx_queue { struct efx_rx_queue {
@ -284,9 +277,6 @@ struct efx_rx_queue {
struct timer_list slow_fill; struct timer_list slow_fill;
unsigned int slow_fill_count; unsigned int slow_fill_count;
struct page *buf_page;
dma_addr_t buf_dma_addr;
char *buf_data;
enum efx_flush_state flushed; enum efx_flush_state flushed;
}; };

View file

@ -98,155 +98,132 @@ static inline unsigned int efx_rx_buf_size(struct efx_nic *efx)
return PAGE_SIZE << efx->rx_buffer_order; return PAGE_SIZE << efx->rx_buffer_order;
} }
/** /**
* efx_init_rx_buffer_skb - create new RX buffer using skb-based allocation * efx_init_rx_buffers_skb - create EFX_RX_BATCH skb-based RX buffers
* *
* @rx_queue: Efx RX queue * @rx_queue: Efx RX queue
* @rx_buf: RX buffer structure to populate
* *
* This allocates memory for a new receive buffer, maps it for DMA, * This allocates EFX_RX_BATCH skbs, maps them for DMA, and populates a
* and populates a struct efx_rx_buffer with the relevant * struct efx_rx_buffer for each one. Return a negative error code or 0
* information. Return a negative error code or 0 on success. * on success. May fail having only inserted fewer than EFX_RX_BATCH
* buffers.
*/ */
static int efx_init_rx_buffer_skb(struct efx_rx_queue *rx_queue, static int efx_init_rx_buffers_skb(struct efx_rx_queue *rx_queue)
struct efx_rx_buffer *rx_buf)
{ {
struct efx_nic *efx = rx_queue->efx; struct efx_nic *efx = rx_queue->efx;
struct net_device *net_dev = efx->net_dev; struct net_device *net_dev = efx->net_dev;
struct efx_rx_buffer *rx_buf;
int skb_len = efx->rx_buffer_len; int skb_len = efx->rx_buffer_len;
unsigned index, count;
rx_buf->skb = netdev_alloc_skb(net_dev, skb_len); for (count = 0; count < EFX_RX_BATCH; ++count) {
if (unlikely(!rx_buf->skb)) index = rx_queue->added_count & EFX_RXQ_MASK;
return -ENOMEM; rx_buf = efx_rx_buffer(rx_queue, index);
/* Adjust the SKB for padding and checksum */ rx_buf->skb = netdev_alloc_skb(net_dev, skb_len);
skb_reserve(rx_buf->skb, NET_IP_ALIGN); if (unlikely(!rx_buf->skb))
rx_buf->len = skb_len - NET_IP_ALIGN; return -ENOMEM;
rx_buf->data = (char *)rx_buf->skb->data; rx_buf->page = NULL;
rx_buf->skb->ip_summed = CHECKSUM_UNNECESSARY;
rx_buf->dma_addr = pci_map_single(efx->pci_dev, /* Adjust the SKB for padding and checksum */
rx_buf->data, rx_buf->len, skb_reserve(rx_buf->skb, NET_IP_ALIGN);
PCI_DMA_FROMDEVICE); rx_buf->len = skb_len - NET_IP_ALIGN;
rx_buf->data = (char *)rx_buf->skb->data;
rx_buf->skb->ip_summed = CHECKSUM_UNNECESSARY;
if (unlikely(pci_dma_mapping_error(efx->pci_dev, rx_buf->dma_addr))) { rx_buf->dma_addr = pci_map_single(efx->pci_dev,
dev_kfree_skb_any(rx_buf->skb); rx_buf->data, rx_buf->len,
rx_buf->skb = NULL; PCI_DMA_FROMDEVICE);
return -EIO; if (unlikely(pci_dma_mapping_error(efx->pci_dev,
rx_buf->dma_addr))) {
dev_kfree_skb_any(rx_buf->skb);
rx_buf->skb = NULL;
return -EIO;
}
++rx_queue->added_count;
++rx_queue->alloc_skb_count;
} }
return 0; return 0;
} }
/** /**
* efx_init_rx_buffer_page - create new RX buffer using page-based allocation * efx_init_rx_buffers_page - create EFX_RX_BATCH page-based RX buffers
* *
* @rx_queue: Efx RX queue * @rx_queue: Efx RX queue
* @rx_buf: RX buffer structure to populate
* *
* This allocates memory for a new receive buffer, maps it for DMA, * This allocates memory for EFX_RX_BATCH receive buffers, maps them for DMA,
* and populates a struct efx_rx_buffer with the relevant * and populates struct efx_rx_buffers for each one. Return a negative error
* information. Return a negative error code or 0 on success. * code or 0 on success. If a single page can be split between two buffers,
* then the page will either be inserted fully, or not at at all.
*/ */
static int efx_init_rx_buffer_page(struct efx_rx_queue *rx_queue, static int efx_init_rx_buffers_page(struct efx_rx_queue *rx_queue)
struct efx_rx_buffer *rx_buf)
{ {
struct efx_nic *efx = rx_queue->efx; struct efx_nic *efx = rx_queue->efx;
int bytes, space, offset; struct efx_rx_buffer *rx_buf;
struct page *page;
char *page_addr;
dma_addr_t dma_addr;
unsigned index, count;
bytes = efx->rx_buffer_len - EFX_PAGE_IP_ALIGN; /* We can split a page between two buffers */
BUILD_BUG_ON(EFX_RX_BATCH & 1);
/* If there is space left in the previously allocated page, for (count = 0; count < EFX_RX_BATCH; ++count) {
* then use it. Otherwise allocate a new one */ page = alloc_pages(__GFP_COLD | __GFP_COMP | GFP_ATOMIC,
rx_buf->page = rx_queue->buf_page; efx->rx_buffer_order);
if (rx_buf->page == NULL) { if (unlikely(page == NULL))
dma_addr_t dma_addr;
rx_buf->page = alloc_pages(__GFP_COLD | __GFP_COMP | GFP_ATOMIC,
efx->rx_buffer_order);
if (unlikely(rx_buf->page == NULL))
return -ENOMEM; return -ENOMEM;
dma_addr = pci_map_page(efx->pci_dev, page, 0,
dma_addr = pci_map_page(efx->pci_dev, rx_buf->page, efx_rx_buf_size(efx),
0, efx_rx_buf_size(efx),
PCI_DMA_FROMDEVICE); PCI_DMA_FROMDEVICE);
if (unlikely(pci_dma_mapping_error(efx->pci_dev, dma_addr))) { if (unlikely(pci_dma_mapping_error(efx->pci_dev, dma_addr))) {
__free_pages(rx_buf->page, efx->rx_buffer_order); __free_pages(page, efx->rx_buffer_order);
rx_buf->page = NULL;
return -EIO; return -EIO;
} }
EFX_BUG_ON_PARANOID(dma_addr & (PAGE_SIZE - 1));
page_addr = page_address(page) + EFX_PAGE_IP_ALIGN;
dma_addr += EFX_PAGE_IP_ALIGN;
rx_queue->buf_page = rx_buf->page; split:
rx_queue->buf_dma_addr = dma_addr; index = rx_queue->added_count & EFX_RXQ_MASK;
rx_queue->buf_data = (page_address(rx_buf->page) + rx_buf = efx_rx_buffer(rx_queue, index);
EFX_PAGE_IP_ALIGN); rx_buf->dma_addr = dma_addr;
} rx_buf->skb = NULL;
rx_buf->page = page;
rx_buf->data = page_addr;
rx_buf->len = efx->rx_buffer_len - EFX_PAGE_IP_ALIGN;
++rx_queue->added_count;
++rx_queue->alloc_page_count;
rx_buf->len = bytes; if ((~count & 1) && (efx->rx_buffer_len < (PAGE_SIZE >> 1))) {
rx_buf->data = rx_queue->buf_data; /* Use the second half of the page */
offset = efx_rx_buf_offset(rx_buf); get_page(page);
rx_buf->dma_addr = rx_queue->buf_dma_addr + offset; dma_addr += (PAGE_SIZE >> 1);
page_addr += (PAGE_SIZE >> 1);
/* Try to pack multiple buffers per page */ ++count;
if (efx->rx_buffer_order == 0) { goto split;
/* The next buffer starts on the next 512 byte boundary */
rx_queue->buf_data += ((bytes + 0x1ff) & ~0x1ff);
offset += ((bytes + 0x1ff) & ~0x1ff);
space = efx_rx_buf_size(efx) - offset;
if (space >= bytes) {
/* Refs dropped on kernel releasing each skb */
get_page(rx_queue->buf_page);
goto out;
} }
} }
/* This is the final RX buffer for this page, so mark it for
* unmapping */
rx_queue->buf_page = NULL;
rx_buf->unmap_addr = rx_queue->buf_dma_addr;
out:
return 0; return 0;
} }
/* This allocates memory for a new receive buffer, maps it for DMA,
* and populates a struct efx_rx_buffer with the relevant
* information.
*/
static int efx_init_rx_buffer(struct efx_rx_queue *rx_queue,
struct efx_rx_buffer *new_rx_buf)
{
int rc = 0;
if (rx_queue->channel->rx_alloc_push_pages) {
new_rx_buf->skb = NULL;
rc = efx_init_rx_buffer_page(rx_queue, new_rx_buf);
rx_queue->alloc_page_count++;
} else {
new_rx_buf->page = NULL;
rc = efx_init_rx_buffer_skb(rx_queue, new_rx_buf);
rx_queue->alloc_skb_count++;
}
if (unlikely(rc < 0))
EFX_LOG_RL(rx_queue->efx, "%s RXQ[%d] =%d\n", __func__,
rx_queue->queue, rc);
return rc;
}
static void efx_unmap_rx_buffer(struct efx_nic *efx, static void efx_unmap_rx_buffer(struct efx_nic *efx,
struct efx_rx_buffer *rx_buf) struct efx_rx_buffer *rx_buf)
{ {
if (rx_buf->page) { if (rx_buf->page) {
EFX_BUG_ON_PARANOID(rx_buf->skb); EFX_BUG_ON_PARANOID(rx_buf->skb);
if (rx_buf->unmap_addr) {
pci_unmap_page(efx->pci_dev, rx_buf->unmap_addr, /* Unmap the buffer if there's only one buffer per page(s),
* or this is the second half of a two buffer page. */
if (efx->rx_buffer_order != 0 ||
(efx_rx_buf_offset(rx_buf) & (PAGE_SIZE >> 1)) != 0) {
pci_unmap_page(efx->pci_dev,
rx_buf->dma_addr & ~(PAGE_SIZE - 1),
efx_rx_buf_size(efx), efx_rx_buf_size(efx),
PCI_DMA_FROMDEVICE); PCI_DMA_FROMDEVICE);
rx_buf->unmap_addr = 0;
} }
} else if (likely(rx_buf->skb)) { } else if (likely(rx_buf->skb)) {
pci_unmap_single(efx->pci_dev, rx_buf->dma_addr, pci_unmap_single(efx->pci_dev, rx_buf->dma_addr,
@ -286,9 +263,9 @@ static void efx_fini_rx_buffer(struct efx_rx_queue *rx_queue,
*/ */
void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue) void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue)
{ {
struct efx_rx_buffer *rx_buf; struct efx_channel *channel = rx_queue->channel;
unsigned fill_level, index; unsigned fill_level;
int i, space, rc = 0; int space, rc = 0;
/* Calculate current fill level, and exit if we don't need to fill */ /* Calculate current fill level, and exit if we don't need to fill */
fill_level = (rx_queue->added_count - rx_queue->removed_count); fill_level = (rx_queue->added_count - rx_queue->removed_count);
@ -309,21 +286,18 @@ void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue)
EFX_TRACE(rx_queue->efx, "RX queue %d fast-filling descriptor ring from" EFX_TRACE(rx_queue->efx, "RX queue %d fast-filling descriptor ring from"
" level %d to level %d using %s allocation\n", " level %d to level %d using %s allocation\n",
rx_queue->queue, fill_level, rx_queue->fast_fill_limit, rx_queue->queue, fill_level, rx_queue->fast_fill_limit,
rx_queue->channel->rx_alloc_push_pages ? "page" : "skb"); channel->rx_alloc_push_pages ? "page" : "skb");
do { do {
for (i = 0; i < EFX_RX_BATCH; ++i) { if (channel->rx_alloc_push_pages)
index = rx_queue->added_count & EFX_RXQ_MASK; rc = efx_init_rx_buffers_page(rx_queue);
rx_buf = efx_rx_buffer(rx_queue, index); else
rc = efx_init_rx_buffer(rx_queue, rx_buf); rc = efx_init_rx_buffers_skb(rx_queue);
if (unlikely(rc)) { if (unlikely(rc)) {
/* Ensure that we don't leave the rx queue /* Ensure that we don't leave the rx queue empty */
* empty */ if (rx_queue->added_count == rx_queue->removed_count)
if (rx_queue->added_count == rx_queue->removed_count) efx_schedule_slow_fill(rx_queue);
efx_schedule_slow_fill(rx_queue); goto out;
goto out;
}
++rx_queue->added_count;
} }
} while ((space -= EFX_RX_BATCH) >= EFX_RX_BATCH); } while ((space -= EFX_RX_BATCH) >= EFX_RX_BATCH);
@ -638,16 +612,6 @@ void efx_fini_rx_queue(struct efx_rx_queue *rx_queue)
efx_fini_rx_buffer(rx_queue, rx_buf); efx_fini_rx_buffer(rx_queue, rx_buf);
} }
} }
/* For a page that is part-way through splitting into RX buffers */
if (rx_queue->buf_page != NULL) {
pci_unmap_page(rx_queue->efx->pci_dev, rx_queue->buf_dma_addr,
efx_rx_buf_size(rx_queue->efx),
PCI_DMA_FROMDEVICE);
__free_pages(rx_queue->buf_page,
rx_queue->efx->rx_buffer_order);
rx_queue->buf_page = NULL;
}
} }
void efx_remove_rx_queue(struct efx_rx_queue *rx_queue) void efx_remove_rx_queue(struct efx_rx_queue *rx_queue)