The free queue metadata bitmap currently searches for a free slot from zero for every page allocation. Under sustained traffic this repeatedly scans occupied low-numbered slots. Remember the position after the most recently allocated slot and wrap the bitmap search at its end. Reset the cursor when the bitmap is released so the two remain consistent across failed setup and teardown. This keeps allocation cost distributed across the metadata table. Signed-off-by: Linus Walleij --- drivers/net/ethernet/cortina/gemini.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c index 9602429388b1..1befa8dd13a6 100644 --- a/drivers/net/ethernet/cortina/gemini.c +++ b/drivers/net/ethernet/cortina/gemini.c @@ -175,6 +175,7 @@ struct gemini_ethernet { struct xarray freeq_mappings; unsigned int num_freeq_pages; unsigned long *freeq_page_bitmap; + unsigned int freeq_page_cursor; spinlock_t freeq_lock; /* Locks queue from reentrance */ }; @@ -746,12 +747,20 @@ static int geth_freeq_alloc_slot(struct gemini_ethernet *geth) lockdep_assert_held(&geth->freeq_lock); - slot = find_first_zero_bit(geth->freeq_page_bitmap, - geth->num_freeq_pages); - if (slot == geth->num_freeq_pages) - return -ENOSPC; + slot = find_next_zero_bit(geth->freeq_page_bitmap, + geth->num_freeq_pages, + geth->freeq_page_cursor); + if (slot == geth->num_freeq_pages) { + slot = find_first_zero_bit(geth->freeq_page_bitmap, + geth->freeq_page_cursor); + if (slot == geth->freeq_page_cursor) + return -ENOSPC; + } __set_bit(slot, geth->freeq_page_bitmap); + geth->freeq_page_cursor = slot + 1; + if (geth->freeq_page_cursor == geth->num_freeq_pages) + geth->freeq_page_cursor = 0; return slot; } @@ -1113,6 +1122,7 @@ static int geth_setup_freeq(struct gemini_ethernet *geth) if (!geth->freeq_page_bitmap) goto err_freeq_pages; geth->num_freeq_pages = page_slots; + geth->freeq_page_cursor = 0; /* Match the software pointer to the retained hardware pointer. */ rptr = GET_RPTR(geth->base + GLOBAL_SWFQ_RWPTR_REG); @@ -1144,6 +1154,7 @@ static int geth_setup_freeq(struct gemini_ethernet *geth) kfree(geth->freeq_pages); geth->freeq_pages = NULL; geth->num_freeq_pages = 0; + geth->freeq_page_cursor = 0; err_freeq: dma_free_coherent(geth->dev, sizeof(*geth->freeq_ring) << geth->freeq_order, @@ -1172,6 +1183,7 @@ static void geth_cleanup_freeq(struct gemini_ethernet *geth) kfree(geth->freeq_pages); geth->freeq_pages = NULL; geth->num_freeq_pages = 0; + geth->freeq_page_cursor = 0; dma_free_coherent(geth->dev, sizeof(*geth->freeq_ring) << geth->freeq_order, -- 2.56.0