Skip to content

Commit 4cace67

Browse files
Gabriel Krisman Bertazidavem330
authored andcommitted
bnx2x: Alloc 4k fragment for each rx ring buffer element
The driver allocates one page for each buffer on the rx ring, which is too much on architectures like ppc64 and can cause unexpected allocation failures when the system is under stress. Now, we keep a memory pool per queue, and if the architecture's PAGE_SIZE is greater than 4k, we fragment pages and assign each 4k segment to a ring element, which reduces the overall memory consumption on such architectures. This helps avoiding errors like the example below: [bnx2x_alloc_rx_sge:435(eth1)]Can't alloc sge [c00000037ffeb900] [d000000075eddeb4] .bnx2x_alloc_rx_sge+0x44/0x200 [bnx2x] [c00000037ffeb9b0] [d000000075ee0b34] .bnx2x_fill_frag_skb+0x1ac/0x460 [bnx2x] [c00000037ffebac0] [d000000075ee11f0] .bnx2x_tpa_stop+0x160/0x2e8 [bnx2x] [c00000037ffebb90] [d000000075ee1560] .bnx2x_rx_int+0x1e8/0xc30 [bnx2x] [c00000037ffebcd0] [d000000075ee2084] .bnx2x_poll+0xdc/0x3d8 [bnx2x] (unreliable) Signed-off-by: Gabriel Krisman Bertazi <[email protected]> Acked-by: Yuval Mintz <[email protected]> Reviewed-by: Lino Sanfilippo <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent ccea744 commit 4cace67

File tree

3 files changed

+80
-24
lines changed

3 files changed

+80
-24
lines changed

drivers/net/ethernet/broadcom/bnx2x/bnx2x.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ struct sw_tx_bd {
357357
struct sw_rx_page {
358358
struct page *page;
359359
DEFINE_DMA_UNMAP_ADDR(mapping);
360+
unsigned int offset;
360361
};
361362

362363
union db_prod {
@@ -381,9 +382,10 @@ union db_prod {
381382

382383
#define PAGES_PER_SGE_SHIFT 0
383384
#define PAGES_PER_SGE (1 << PAGES_PER_SGE_SHIFT)
384-
#define SGE_PAGE_SIZE PAGE_SIZE
385-
#define SGE_PAGE_SHIFT PAGE_SHIFT
386-
#define SGE_PAGE_ALIGN(addr) PAGE_ALIGN((typeof(PAGE_SIZE))(addr))
385+
#define SGE_PAGE_SHIFT 12
386+
#define SGE_PAGE_SIZE (1 << SGE_PAGE_SHIFT)
387+
#define SGE_PAGE_MASK (~(SGE_PAGE_SIZE - 1))
388+
#define SGE_PAGE_ALIGN(addr) (((addr) + SGE_PAGE_SIZE - 1) & SGE_PAGE_MASK)
387389
#define SGE_PAGES (SGE_PAGE_SIZE * PAGES_PER_SGE)
388390
#define TPA_AGG_SIZE min_t(u32, (min_t(u32, 8, MAX_SKB_FRAGS) * \
389391
SGE_PAGES), 0xffff)
@@ -526,6 +528,12 @@ enum bnx2x_tpa_mode_t {
526528
TPA_MODE_GRO
527529
};
528530

531+
struct bnx2x_alloc_pool {
532+
struct page *page;
533+
dma_addr_t dma;
534+
unsigned int offset;
535+
};
536+
529537
struct bnx2x_fastpath {
530538
struct bnx2x *bp; /* parent */
531539

@@ -599,6 +607,8 @@ struct bnx2x_fastpath {
599607
4 (for the digits and to make it DWORD aligned) */
600608
#define FP_NAME_SIZE (sizeof(((struct net_device *)0)->name) + 8)
601609
char name[FP_NAME_SIZE];
610+
611+
struct bnx2x_alloc_pool page_pool;
602612
};
603613

604614
#define bnx2x_fp(bp, nr, var) ((bp)->fp[(nr)].var)

drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -544,30 +544,49 @@ static void bnx2x_set_gro_params(struct sk_buff *skb, u16 parsing_flags,
544544
static int bnx2x_alloc_rx_sge(struct bnx2x *bp, struct bnx2x_fastpath *fp,
545545
u16 index, gfp_t gfp_mask)
546546
{
547-
struct page *page = alloc_pages(gfp_mask, PAGES_PER_SGE_SHIFT);
548547
struct sw_rx_page *sw_buf = &fp->rx_page_ring[index];
549548
struct eth_rx_sge *sge = &fp->rx_sge_ring[index];
549+
struct bnx2x_alloc_pool *pool = &fp->page_pool;
550550
dma_addr_t mapping;
551551

552-
if (unlikely(page == NULL)) {
553-
BNX2X_ERR("Can't alloc sge\n");
554-
return -ENOMEM;
555-
}
552+
if (!pool->page || (PAGE_SIZE - pool->offset) < SGE_PAGE_SIZE) {
556553

557-
mapping = dma_map_page(&bp->pdev->dev, page, 0,
558-
SGE_PAGES, DMA_FROM_DEVICE);
559-
if (unlikely(dma_mapping_error(&bp->pdev->dev, mapping))) {
560-
__free_pages(page, PAGES_PER_SGE_SHIFT);
561-
BNX2X_ERR("Can't map sge\n");
562-
return -ENOMEM;
554+
/* put page reference used by the memory pool, since we
555+
* won't be using this page as the mempool anymore.
556+
*/
557+
if (pool->page)
558+
put_page(pool->page);
559+
560+
pool->page = alloc_pages(gfp_mask, PAGES_PER_SGE_SHIFT);
561+
if (unlikely(!pool->page)) {
562+
BNX2X_ERR("Can't alloc sge\n");
563+
return -ENOMEM;
564+
}
565+
566+
pool->dma = dma_map_page(&bp->pdev->dev, pool->page, 0,
567+
PAGE_SIZE, DMA_FROM_DEVICE);
568+
if (unlikely(dma_mapping_error(&bp->pdev->dev,
569+
pool->dma))) {
570+
__free_pages(pool->page, PAGES_PER_SGE_SHIFT);
571+
pool->page = NULL;
572+
BNX2X_ERR("Can't map sge\n");
573+
return -ENOMEM;
574+
}
575+
pool->offset = 0;
563576
}
564577

565-
sw_buf->page = page;
578+
get_page(pool->page);
579+
sw_buf->page = pool->page;
580+
sw_buf->offset = pool->offset;
581+
582+
mapping = pool->dma + sw_buf->offset;
566583
dma_unmap_addr_set(sw_buf, mapping, mapping);
567584

568585
sge->addr_hi = cpu_to_le32(U64_HI(mapping));
569586
sge->addr_lo = cpu_to_le32(U64_LO(mapping));
570587

588+
pool->offset += SGE_PAGE_SIZE;
589+
571590
return 0;
572591
}
573592

@@ -629,20 +648,22 @@ static int bnx2x_fill_frag_skb(struct bnx2x *bp, struct bnx2x_fastpath *fp,
629648
return err;
630649
}
631650

632-
/* Unmap the page as we're going to pass it to the stack */
633-
dma_unmap_page(&bp->pdev->dev,
634-
dma_unmap_addr(&old_rx_pg, mapping),
635-
SGE_PAGES, DMA_FROM_DEVICE);
651+
dma_unmap_single(&bp->pdev->dev,
652+
dma_unmap_addr(&old_rx_pg, mapping),
653+
SGE_PAGE_SIZE, DMA_FROM_DEVICE);
636654
/* Add one frag and update the appropriate fields in the skb */
637655
if (fp->mode == TPA_MODE_LRO)
638-
skb_fill_page_desc(skb, j, old_rx_pg.page, 0, frag_len);
656+
skb_fill_page_desc(skb, j, old_rx_pg.page,
657+
old_rx_pg.offset, frag_len);
639658
else { /* GRO */
640659
int rem;
641660
int offset = 0;
642661
for (rem = frag_len; rem > 0; rem -= gro_size) {
643662
int len = rem > gro_size ? gro_size : rem;
644663
skb_fill_page_desc(skb, frag_id++,
645-
old_rx_pg.page, offset, len);
664+
old_rx_pg.page,
665+
old_rx_pg.offset + offset,
666+
len);
646667
if (offset)
647668
get_page(old_rx_pg.page);
648669
offset += len;

drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -804,9 +804,13 @@ static inline void bnx2x_free_rx_sge(struct bnx2x *bp,
804804
if (!page)
805805
return;
806806

807-
dma_unmap_page(&bp->pdev->dev, dma_unmap_addr(sw_buf, mapping),
808-
SGE_PAGES, DMA_FROM_DEVICE);
809-
__free_pages(page, PAGES_PER_SGE_SHIFT);
807+
/* Since many fragments can share the same page, make sure to
808+
* only unmap and free the page once.
809+
*/
810+
dma_unmap_single(&bp->pdev->dev, dma_unmap_addr(sw_buf, mapping),
811+
SGE_PAGE_SIZE, DMA_FROM_DEVICE);
812+
813+
put_page(page);
810814

811815
sw_buf->page = NULL;
812816
sge->addr_hi = 0;
@@ -964,6 +968,25 @@ static inline void bnx2x_set_fw_mac_addr(__le16 *fw_hi, __le16 *fw_mid,
964968
((u8 *)fw_lo)[1] = mac[4];
965969
}
966970

971+
static inline void bnx2x_free_rx_mem_pool(struct bnx2x *bp,
972+
struct bnx2x_alloc_pool *pool)
973+
{
974+
if (!pool->page)
975+
return;
976+
977+
/* Page was not fully fragmented. Unmap unused space */
978+
if (pool->offset < PAGE_SIZE) {
979+
dma_addr_t dma = pool->dma + pool->offset;
980+
int size = PAGE_SIZE - pool->offset;
981+
982+
dma_unmap_single(&bp->pdev->dev, dma, size, DMA_FROM_DEVICE);
983+
}
984+
985+
put_page(pool->page);
986+
987+
pool->page = NULL;
988+
}
989+
967990
static inline void bnx2x_free_rx_sge_range(struct bnx2x *bp,
968991
struct bnx2x_fastpath *fp, int last)
969992
{
@@ -974,6 +997,8 @@ static inline void bnx2x_free_rx_sge_range(struct bnx2x *bp,
974997

975998
for (i = 0; i < last; i++)
976999
bnx2x_free_rx_sge(bp, fp, i);
1000+
1001+
bnx2x_free_rx_mem_pool(bp, &fp->page_pool);
9771002
}
9781003

9791004
static inline void bnx2x_set_next_page_rx_bd(struct bnx2x_fastpath *fp)

0 commit comments

Comments
 (0)