From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CBF683CA4A3 for ; Thu, 15 Oct 2026 15:24:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792077876; cv=none; b=qrRty48po9Gu5gj1U3hMg/Rc0VDXc1RSgRgBHiMk4rPFziIX7URGgFhDq90gM/r9vG6ewTHfeqx+s9oEvD3X725Ggrk4eQZOzthPEdwvMKUJvCny1UeXTwx3j9+vn8C0V+2/LdL5h8uAmJ947h17JWN7FE/h0A5DWGzsQBbrbiQ= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792077876; c=relaxed/simple; bh=ujQjBCGzl2ehFcQ7gtL6uRbc79RfIshC09NSjgYgCdA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=IiErPzBN+hnIQDhWTh8UN/S5Bzkhmpg5eXej5jl6vTLvzKZZdkMs+JTwLfUoXavErZTz52VNmYOD/haUCamva50Z026s9fyPpmWeE+97vIiRSvTQKtfEvol530yXghxaByX6u3AacAj85FAkpqUyvpaH+XtmZvb2lGAxdnyo3rQ= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=FMwh7djK; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="FMwh7djK" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 172BC1F000C4; Thu, 15 Oct 2026 15:24:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1792077864; bh=8RRvROUjkff+kig96BCOU4Dpb+zqVWljvGwTGJ6lxR8=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=FMwh7djKYw6npK8a74YCBMRFeGnZoYIl47Jj0kDKB6BwtOAuQ4sSe10fyIOfjDH8E d4SkaZNjYL4CoUQuUALpTVfq2AzwB01/mYTdqIgYjDQiiXDHZ7FKYgIxXdyQdrbWPq vvuMt2/v6LePOXgKr9GGSCGgbIcIDqLpKQaXjuIiH2PH5yMs1HXkLMVj0r5MrivqcE ZUNxcvgis+rgSuCuPlxpNyW8Py7Wj8KF3qwt7RrYeLbCQRMVLXiroT8aw3USqlCcWO Eq/R3vhDMx+TMSmV7BzXBuRZcMb8OpAivQLVMOjsF0JTmjpdVtxunZ837TTIPjaFKo FFOkerkWn94Yg== From: Linus Walleij Date: Thu, 15 Oct 2026 17:24:06 +0200 Subject: [PATCH net-next v5 06/14] net: ethernet: cortina: Index free queue fragments with XArray Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20261015-gemini-ethernet-fixes-3-v5-6-b5c9e2f7b8a3@kernel.org> References: <20261015-gemini-ethernet-fixes-3-v5-0-b5c9e2f7b8a3@kernel.org> In-Reply-To: <20261015-gemini-ethernet-fixes-3-v5-0-b5c9e2f7b8a3@kernel.org> To: Hans Ulli Kroll , Andrew Lunn , "David S. Miller" , Jakub Kicinski , Paolo Abeni , =?utf-8?q?Micha=C5=82_Miros=C5=82aw?= , Myeonghun Pak , Eric Dumazet Cc: netdev@vger.kernel.org, Linus Walleij X-Mailer: b4 0.16.0 RX descriptors return the DMA address of the free queue fragment used by the hardware. The driver currently recovers its page by scanning every free queue page and comparing address ranges. This isn't very efficient... Instead index each fragment in an XArray using its DMA address in fragment units. Populate and erase the fragment index under the free queue lock. The lookup remains serialized with refill and validates the exact fragment address before returning the page and offset snapshot. Assisted-by: LLM Signed-off-by: Linus Walleij --- drivers/net/ethernet/cortina/gemini.c | 105 ++++++++++++++++++++++++---------- 1 file changed, 76 insertions(+), 29 deletions(-) diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c index 006362a03395..c1c42e1f93f4 100644 --- a/drivers/net/ethernet/cortina/gemini.c +++ b/drivers/net/ethernet/cortina/gemini.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -167,6 +168,7 @@ struct gemini_ethernet { struct gmac_rxdesc *freeq_ring; dma_addr_t freeq_dma_base; struct gmac_queue_page *freeq_pages; + struct xarray freeq_mappings; unsigned int num_freeq_pages; spinlock_t freeq_lock; /* Locks queue from reentrance */ }; @@ -726,43 +728,47 @@ static int gmac_setup_rxq(struct net_device *netdev) return 0; } +static unsigned long +geth_freeq_mapping_index(const struct gemini_ethernet *geth, + dma_addr_t mapping) +{ + return (unsigned long)(mapping >> geth->freeq_frag_order); +} + static struct page *geth_freeq_lookup(struct gemini_ethernet *geth, dma_addr_t mapping, unsigned int *page_offs) { unsigned int frag_len = 1 << geth->freeq_frag_order; struct gmac_queue_page *gpage; + unsigned long index; unsigned long flags; dma_addr_t page_mapping; - struct page *page = NULL; - int i; - - spin_lock_irqsave(&geth->freeq_lock, flags); - if (!geth->freeq_pages) { - dev_err_ratelimited(geth->dev, - "try to get page with no page list\n"); - goto unlock; - } - - /* Look up a ring buffer page from virtual mapping */ - for (i = 0; i < geth->num_freeq_pages; i++) { - gpage = &geth->freeq_pages[i]; - if (!gpage->page || mapping < gpage->mapping) - continue; - - page_mapping = gpage->mapping; - if (mapping - page_mapping > PAGE_SIZE - frag_len || - ((mapping - page_mapping) & (frag_len - 1))) - continue; + struct page *page; + bool valid; - page = gpage->page; - *page_offs = mapping - page_mapping; - break; - } + index = geth_freeq_mapping_index(geth, mapping); -unlock: + spin_lock_irqsave(&geth->freeq_lock, flags); + gpage = xa_load(&geth->freeq_mappings, index); + if (!gpage || !gpage->page) + goto err_unlock; + + page = gpage->page; + page_mapping = gpage->mapping; + valid = mapping >= page_mapping && + mapping - page_mapping <= PAGE_SIZE - frag_len && + !((mapping - page_mapping) & (frag_len - 1)); + if (!valid) + goto err_unlock; + + *page_offs = mapping - page_mapping; spin_unlock_irqrestore(&geth->freeq_lock, flags); return page; + +err_unlock: + spin_unlock_irqrestore(&geth->freeq_lock, flags); + return NULL; } static void gmac_cleanup_rxq(struct net_device *netdev) @@ -821,12 +827,16 @@ static struct page *geth_freeq_alloc_map_page(struct gemini_ethernet *geth, struct gmac_rxdesc *freeq_entry; struct gmac_queue_page *gpage; unsigned int fpp_order; + unsigned int fragments; unsigned int frag_len; dma_addr_t page_mapping; dma_addr_t mapping; struct page *page; + int ret; int i; + lockdep_assert_held(&geth->freeq_lock); + /* First allocate and DMA map a single page */ page = alloc_page(GFP_ATOMIC); if (!page) @@ -855,9 +865,26 @@ static struct page *geth_freeq_alloc_map_page(struct gemini_ethernet *geth, */ frag_len = 1 << geth->freeq_frag_order; /* Usually 2048 */ fpp_order = PAGE_SHIFT - geth->freeq_frag_order; + fragments = 1 << fpp_order; + gpage = &geth->freeq_pages[pn]; - if (gpage->page) + for (i = 0; i < fragments; i++) { + mapping = page_mapping + i * frag_len; + ret = xa_insert(&geth->freeq_mappings, + geth_freeq_mapping_index(geth, mapping), + gpage, GFP_ATOMIC); + if (ret) + goto err_mappings; + } + + if (gpage->page) { + for (i = 0; i < fragments; i++) { + mapping = gpage->mapping + i * frag_len; + xa_erase(&geth->freeq_mappings, + geth_freeq_mapping_index(geth, mapping)); + } put_page(gpage->page); + } /* Then put our new mapping into the page table */ gpage->mapping = page_mapping; @@ -876,6 +903,17 @@ static struct page *geth_freeq_alloc_map_page(struct gemini_ethernet *geth, pn, &page_mapping, page); return page; + +err_mappings: + while (i--) { + mapping = page_mapping + i * frag_len; + xa_erase(&geth->freeq_mappings, + geth_freeq_mapping_index(geth, mapping)); + } + dma_unmap_single(geth->dev, page_mapping, PAGE_SIZE, + DMA_FROM_DEVICE); + put_page(page); + return NULL; } /** @@ -948,6 +986,8 @@ static int geth_setup_freeq(struct gemini_ethernet *geth) unsigned int pages = len >> fpp_order; union queue_threshold qt; union dma_skb_size skbsz; + unsigned long flags; + struct page *page; unsigned int filled; unsigned int pn; u16 rptr; @@ -962,16 +1002,20 @@ static int geth_setup_freeq(struct gemini_ethernet *geth) goto err_freeq; } - /* Allocate a mapping to page look-up index */ + /* Allocate page metadata referenced by the mapping index */ geth->freeq_pages = kzalloc_objs(*geth->freeq_pages, pages); if (!geth->freeq_pages) goto err_freeq; geth->num_freeq_pages = pages; dev_info(geth->dev, "allocate %d pages for queue\n", pages); - for (pn = 0; pn < pages; pn++) - if (!geth_freeq_alloc_map_page(geth, pn)) + for (pn = 0; pn < pages; pn++) { + spin_lock_irqsave(&geth->freeq_lock, flags); + page = geth_freeq_alloc_map_page(geth, pn); + spin_unlock_irqrestore(&geth->freeq_lock, flags); + if (!page) goto err_freeq_alloc; + } /* Match the software pointer to the retained hardware pointer. */ rptr = GET_RPTR(geth->base + GLOBAL_SWFQ_RWPTR_REG); @@ -1002,6 +1046,7 @@ static int geth_setup_freeq(struct gemini_ethernet *geth) DMA_FROM_DEVICE); put_page(gpage->page); } + xa_destroy(&geth->freeq_mappings); kfree(geth->freeq_pages); geth->freeq_pages = NULL; @@ -1041,6 +1086,7 @@ static void geth_cleanup_freeq(struct gemini_ethernet *geth) while (page_ref_count(gpage->page) > 0) put_page(gpage->page); } + xa_destroy(&geth->freeq_mappings); kfree(geth->freeq_pages); geth->freeq_pages = NULL; @@ -2748,6 +2794,7 @@ static int gemini_ethernet_probe(struct platform_device *pdev) spin_lock_init(&geth->irq_lock); spin_lock_init(&geth->freeq_lock); + xa_init(&geth->freeq_mappings); /* Quiesce firmware state before child IRQ handlers are registered. */ gemini_ethernet_disable_irqs(geth); -- 2.56.0