[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <bf90de49-c0aa-4326-8f27-c9614f384049@linux.dev>
Date: Tue, 8 Jul 2025 14:27:09 +0100
From: Vadim Fedorenko <vadim.fedorenko@...ux.dev>
To: Xin Tian <tianx@...silicon.com>, netdev@...r.kernel.org
Cc: leon@...nel.org, andrew+netdev@...n.ch, kuba@...nel.org,
pabeni@...hat.com, edumazet@...gle.com, davem@...emloft.net,
jeff.johnson@....qualcomm.com, przemyslaw.kitszel@...el.com,
weihg@...silicon.com, wanry@...silicon.com, jacky@...silicon.com,
horms@...nel.org, parthiban.veerasooran@...rochip.com, masahiroy@...nel.org,
kalesh-anakkur.purayil@...adcom.com, geert+renesas@...der.be,
geert@...ux-m68k.org
Subject: Re: [PATCH net-next v12 13/14] xsc: Add eth reception data path
On 03/07/2025 08:54, Xin Tian wrote:
> rx data path:
> 1. The hardware writes incoming packets into the RQ ring buffer and
> generates a event queue entry
> 2. The event handler function(xsc_eth_completion_event in
> xsc_eth_events.c) is triggered, invokes napi_schedule() to schedule
> a softirq.
> 3. The kernel triggers the softirq handler net_rx_action, which calls
> the driver's NAPI poll function (xsc_eth_napi_poll in xsc_eth_txrx.c).
> The driver retrieves CQEs from the Completion Queue (CQ) via
> xsc_poll_rx_cq.
> 4. xsc_eth_build_rx_skb constructs an sk_buff structure, and submits the
> SKB to the kernel network stack via napi_gro_receive
> 5. The driver recycles the RX buffer and notifies the NIC via
> xsc_eth_post_rx_wqes to prepare for new packets.
>
> Co-developed-by: Honggang Wei <weihg@...silicon.com>
> Signed-off-by: Honggang Wei <weihg@...silicon.com>
> Co-developed-by: Lei Yan <jacky@...silicon.com>
> Signed-off-by: Lei Yan <jacky@...silicon.com>
> Signed-off-by: Xin Tian <tianx@...silicon.com>
> ---
> .../yunsilicon/xsc/net/xsc_eth_common.h | 10 +
> .../ethernet/yunsilicon/xsc/net/xsc_eth_rx.c | 527 +++++++++++++++++-
> .../yunsilicon/xsc/net/xsc_eth_txrx.c | 13 +
> .../yunsilicon/xsc/net/xsc_eth_txrx.h | 1 +
> .../ethernet/yunsilicon/xsc/net/xsc_queue.h | 19 +-
> 5 files changed, 547 insertions(+), 23 deletions(-)
>
> diff --git a/drivers/net/ethernet/yunsilicon/xsc/net/xsc_eth_common.h b/drivers/net/ethernet/yunsilicon/xsc/net/xsc_eth_common.h
> index 4f47dac5c..41abe3d3c 100644
> --- a/drivers/net/ethernet/yunsilicon/xsc/net/xsc_eth_common.h
> +++ b/drivers/net/ethernet/yunsilicon/xsc/net/xsc_eth_common.h
> @@ -22,6 +22,8 @@
> #define XSC_SW2HW_RX_PKT_LEN(mtu) \
> ((mtu) + ETH_HLEN + XSC_ETH_RX_MAX_HEAD_ROOM)
>
> +#define XSC_RX_MAX_HEAD (256)
> +
> #define XSC_QPN_SQN_STUB 1025
> #define XSC_QPN_RQN_STUB 1024
>
> @@ -186,4 +188,12 @@ union xsc_send_doorbell {
> u32 send_data;
> };
>
> +union xsc_recv_doorbell {
> + struct{
> + s32 next_pid : 13;
> + u32 qp_num : 15;
> + };
> + u32 recv_data;
> +};
> +
> #endif
> diff --git a/drivers/net/ethernet/yunsilicon/xsc/net/xsc_eth_rx.c b/drivers/net/ethernet/yunsilicon/xsc/net/xsc_eth_rx.c
> index 13145345e..212e55d78 100644
> --- a/drivers/net/ethernet/yunsilicon/xsc/net/xsc_eth_rx.c
> +++ b/drivers/net/ethernet/yunsilicon/xsc/net/xsc_eth_rx.c
> @@ -5,38 +5,547 @@
> * Copyright (c) 2015-2016, Mellanox Technologies. All rights reserved.
> */
>
> +#include <linux/net_tstamp.h>
> +#include <linux/device.h>
> +
> +#include "xsc_pp.h"
> +#include "xsc_eth.h"
> #include "xsc_eth_txrx.h"
> +#include "xsc_eth_common.h"
> +#include "xsc_pph.h"
> +
> +static void xsc_rq_notify_hw(struct xsc_rq *rq)
> +{
> + struct xsc_core_device *xdev = rq->cq.xdev;
> + union xsc_recv_doorbell doorbell_value;
> + struct xsc_wq_cyc *wq = &rq->wqe.wq;
> + u64 rqwqe_id;
> +
> + rqwqe_id = wq->wqe_ctr << (ilog2(xdev->caps.recv_ds_num));
> + /*reverse wqe index to ds index*/
> + doorbell_value.next_pid = rqwqe_id;
> + doorbell_value.qp_num = rq->rqn;
> +
> + /* Make sure that descriptors are written before
> + * updating doorbell record and ringing the doorbell
> + */
> + wmb();
> + writel(doorbell_value.recv_data, XSC_REG_ADDR(xdev, xdev->regs.rx_db));
> +}
> +
> +static void xsc_skb_set_hash(struct xsc_adapter *adapter,
> + struct xsc_cqe *cqe,
> + struct sk_buff *skb)
> +{
> + struct xsc_rss_params *rss = &adapter->rss_param;
> + bool l3_hash = false;
> + bool l4_hash = false;
> + u32 hash_field;
> + int ht = 0;
> +
> + if (adapter->netdev->features & NETIF_F_RXHASH) {
> + if (skb->protocol == htons(ETH_P_IP)) {
> + hash_field = rss->rx_hash_fields[XSC_TT_IPV4_TCP];
> + if (hash_field & XSC_HASH_FIELD_SEL_SRC_IP ||
> + hash_field & XSC_HASH_FIELD_SEL_DST_IP)
> + l3_hash = true;
> +
> + if (hash_field & XSC_HASH_FIELD_SEL_SPORT ||
> + hash_field & XSC_HASH_FIELD_SEL_DPORT)
> + l4_hash = true;
> + } else if (skb->protocol == htons(ETH_P_IPV6)) {
> + hash_field = rss->rx_hash_fields[XSC_TT_IPV6_TCP];
> + if (hash_field & XSC_HASH_FIELD_SEL_SRC_IPV6 ||
> + hash_field & XSC_HASH_FIELD_SEL_DST_IPV6)
> + l3_hash = true;
> +
> + if (hash_field & XSC_HASH_FIELD_SEL_SPORT_V6 ||
> + hash_field & XSC_HASH_FIELD_SEL_DPORT_V6)
> + l4_hash = true;
> + }
> +
> + if (l3_hash && l4_hash)
> + ht = PKT_HASH_TYPE_L4;
> + else if (l3_hash)
> + ht = PKT_HASH_TYPE_L3;
> + if (ht)
> + skb_set_hash(skb, le32_to_cpu(cqe->vni), ht);
> + }
> +}
> +
> +static void xsc_handle_csum(struct xsc_cqe *cqe, struct xsc_rq *rq,
> + struct sk_buff *skb, struct xsc_wqe_frag_info *wi)
> +{
> + struct xsc_dma_info *dma_info;
> + struct net_device *netdev;
> + struct epp_pph *hw_pph;
> + struct xsc_channel *c;
> + int offset_from;
> +
> + c = rq->cq.channel;
> + netdev = c->adapter->netdev;
> + dma_info = wi->di;
> + offset_from = wi->offset;
> + hw_pph = page_address(dma_info->page) + offset_from;
> +
> + if (unlikely((netdev->features & NETIF_F_RXCSUM) == 0))
> + goto csum_none;
> +
> + if (unlikely(XSC_GET_EPP2SOC_PPH_ERROR_BITMAP(hw_pph) & PACKET_UNKNOWN))
> + goto csum_none;
> +
> + if (XSC_GET_EPP2SOC_PPH_EXT_TUNNEL_TYPE(hw_pph) &&
> + (!(FIELD_GET(XSC_CQE_CSUM_ERR_MASK, le32_to_cpu(cqe->data0)) &
> + OUTER_AND_INNER))) {
> + skb->ip_summed = CHECKSUM_UNNECESSARY;
> + skb->csum_level = 1;
> + skb->encapsulation = 1;
> + } else if (XSC_GET_EPP2SOC_PPH_EXT_TUNNEL_TYPE(hw_pph) &&
> + (!(FIELD_GET(XSC_CQE_CSUM_ERR_MASK,
> + le32_to_cpu(cqe->data0)) &
> + OUTER_BIT) &&
> + (FIELD_GET(XSC_CQE_CSUM_ERR_MASK,
> + le32_to_cpu(cqe->data0)) &
> + INNER_BIT))) {
> + skb->ip_summed = CHECKSUM_UNNECESSARY;
> + skb->csum_level = 0;
> + skb->encapsulation = 1;
> + } else if (!XSC_GET_EPP2SOC_PPH_EXT_TUNNEL_TYPE(hw_pph) &&
> + (!(FIELD_GET(XSC_CQE_CSUM_ERR_MASK,
> + le32_to_cpu(cqe->data0)) &
> + OUTER_BIT))) {
> + skb->ip_summed = CHECKSUM_UNNECESSARY;
> + }
> +
> + goto out;
> +
> +csum_none:
> + skb->csum = 0;
> + skb->ip_summed = CHECKSUM_NONE;
> +out:
> + return;
> +}
> +
> +static void xsc_build_rx_skb(struct xsc_cqe *cqe,
> + u32 cqe_bcnt,
> + struct xsc_rq *rq,
> + struct sk_buff *skb,
> + struct xsc_wqe_frag_info *wi)
> +{
> + struct xsc_adapter *adapter;
> + struct net_device *netdev;
> + struct xsc_channel *c;
> +
> + c = rq->cq.channel;
> + adapter = c->adapter;
> + netdev = c->netdev;
> +
> + skb->mac_len = ETH_HLEN;
> +
> + skb_record_rx_queue(skb, rq->ix);
> + xsc_handle_csum(cqe, rq, skb, wi);
> +
> + skb->protocol = eth_type_trans(skb, netdev);
> + xsc_skb_set_hash(adapter, cqe, skb);
> +}
> +
> +static void xsc_complete_rx_cqe(struct xsc_rq *rq,
> + struct xsc_cqe *cqe,
> + u32 cqe_bcnt,
> + struct sk_buff *skb,
> + struct xsc_wqe_frag_info *wi)
> +{
> + xsc_build_rx_skb(cqe, cqe_bcnt, rq, skb, wi);
> +}
> +
> +static void xsc_add_skb_frag(struct xsc_rq *rq,
> + struct sk_buff *skb,
> + struct xsc_dma_info *di,
> + u32 frag_offset, u32 len,
> + unsigned int truesize)
> +{
> + struct xsc_channel *c = rq->cq.channel;
> + struct device *dev = c->adapter->dev;
> +
> + dma_sync_single_for_cpu(dev, di->addr + frag_offset,
> + len, DMA_FROM_DEVICE);
> + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
> + di->page, frag_offset, len, truesize);
> +}
> +
> +static void xsc_copy_skb_header(struct device *dev,
> + struct sk_buff *skb,
> + struct xsc_dma_info *dma_info,
> + int offset_from, u32 headlen)
> +{
> + void *from = page_address(dma_info->page) + offset_from;
> + /* Aligning len to sizeof(long) optimizes memcpy performance */
> + unsigned int len = ALIGN(headlen, sizeof(long));
> +
> + dma_sync_single_for_cpu(dev, dma_info->addr + offset_from, len,
> + DMA_FROM_DEVICE);
> + skb_copy_to_linear_data(skb, from, len);
> +}
> +
> +static struct sk_buff *xsc_build_linear_skb(struct xsc_rq *rq, void *va,
> + u32 frag_size, u16 headroom,
> + u32 cqe_bcnt)
> +{
> + struct sk_buff *skb = build_skb(va, frag_size);
> +
> + if (unlikely(!skb))
> + return NULL;
> +
> + skb_reserve(skb, headroom);
> + skb_put(skb, cqe_bcnt);
> +
> + return skb;
> +}
>
> struct sk_buff *xsc_skb_from_cqe_linear(struct xsc_rq *rq,
> struct xsc_wqe_frag_info *wi,
> u32 cqe_bcnt, u8 has_pph)
> {
> - /* TBD */
> - return NULL;
> + int pph_len = has_pph ? XSC_PPH_HEAD_LEN : 0;
> + u16 rx_headroom = rq->buff.headroom;
> + struct xsc_dma_info *di = wi->di;
> + struct sk_buff *skb;
> + void *va, *data;
> + u32 frag_size;
> +
> + va = page_address(di->page) + wi->offset;
> + data = va + rx_headroom + pph_len;
> + frag_size = XSC_SKB_FRAG_SZ(rx_headroom + cqe_bcnt);
> +
> + dma_sync_single_range_for_cpu(rq->cq.xdev->device, di->addr, wi->offset,
> + frag_size, DMA_FROM_DEVICE);
> + net_prefetchw(va); /* xdp_frame data area */
> + net_prefetch(data);
> +
> + skb = xsc_build_linear_skb(rq, va, frag_size, (rx_headroom + pph_len),
> + (cqe_bcnt - pph_len));
> + if (unlikely(!skb))
> + return NULL;
> +
> + return skb;
> }
>
> struct sk_buff *xsc_skb_from_cqe_nonlinear(struct xsc_rq *rq,
> struct xsc_wqe_frag_info *wi,
> u32 cqe_bcnt, u8 has_pph)
> {
> - /* TBD */
> - return NULL;
> + struct xsc_rq_frag_info *frag_info = &rq->wqe.info.arr[0];
> + u16 headlen = min_t(u32, XSC_RX_MAX_HEAD, cqe_bcnt);
> + struct xsc_wqe_frag_info *head_wi = wi;
> + struct xsc_wqe_frag_info *rx_wi = wi;
> + u16 head_offset = head_wi->offset;
> + u16 byte_cnt = cqe_bcnt - headlen;
> + u16 frag_consumed_bytes = 0;
> + u16 frag_headlen = headlen;
> + struct net_device *netdev;
> + struct xsc_channel *c;
> + struct sk_buff *skb;
> + struct device *dev;
> + u8 fragcnt = 0;
> + int i = 0;
> +
> + c = rq->cq.channel;
> + dev = c->adapter->dev;
> + netdev = c->adapter->netdev;
> +
> + skb = napi_alloc_skb(rq->cq.napi, ALIGN(XSC_RX_MAX_HEAD, sizeof(long)));
> + if (unlikely(!skb))
> + return NULL;
> +
> + net_prefetchw(skb->data);
> +
> + if (likely(has_pph)) {
> + headlen = min_t(u32, XSC_RX_MAX_HEAD,
> + (cqe_bcnt - XSC_PPH_HEAD_LEN));
> + frag_headlen = headlen + XSC_PPH_HEAD_LEN;
> + byte_cnt = cqe_bcnt - headlen - XSC_PPH_HEAD_LEN;
> + head_offset += XSC_PPH_HEAD_LEN;
> + }
> +
> + for (i = 0; i < rq->wqe.info.num_frags; i++, rx_wi++)
> + rx_wi->is_available = 0;
> +
> + while (byte_cnt) {
> + /*figure out whether the first fragment can be a page ?*/
> + frag_consumed_bytes =
> + min_t(u16, frag_info->frag_size - frag_headlen,
> + byte_cnt);
> +
> + xsc_add_skb_frag(rq, skb, wi->di, wi->offset + frag_headlen,
> + frag_consumed_bytes, frag_info->frag_stride);
> + byte_cnt -= frag_consumed_bytes;
> +
> + /*to protect extend wqe read, drop exceed bytes*/
> + frag_headlen = 0;
> + fragcnt++;
> + if (fragcnt == rq->wqe.info.num_frags) {
> + if (byte_cnt) {
> + netdev_warn(netdev,
> + "large packet reach the maximum rev-wqe num.\n");
> + netdev_warn(netdev,
> + "%u bytes dropped: frag_num=%d, headlen=%d, cqe_cnt=%d, frag0_bytes=%d, frag_size=%d\n",
> + byte_cnt, fragcnt,
> + headlen, cqe_bcnt,
> + frag_consumed_bytes,
> + frag_info->frag_size);
> + }
> + break;
> + }
> +
> + frag_info++;
> + wi++;
> + }
> +
> + /* copy header */
> + xsc_copy_skb_header(dev, skb, head_wi->di, head_offset, headlen);
> +
> + /* skb linear part was allocated with headlen and aligned to long */
> + skb->tail += headlen;
> + skb->len += headlen;
> +
> + return skb;
> +}
> +
> +static void xsc_put_rx_frag(struct xsc_rq *rq,
> + struct xsc_wqe_frag_info *frag, bool recycle)
> +{
> + if (frag->last_in_page)
> + page_pool_recycle_direct(rq->page_pool, frag->di->page);
> +}
> +
> +static struct xsc_wqe_frag_info *get_frag(struct xsc_rq *rq, u16 ix)
> +{
> + return &rq->wqe.frags[ix << rq->wqe.info.log_num_frags];
> +}
> +
> +static void xsc_free_rx_wqe(struct xsc_rq *rq,
> + struct xsc_wqe_frag_info *wi, bool recycle)
> +{
> + int i;
> +
> + for (i = 0; i < rq->wqe.info.num_frags; i++, wi++) {
> + if (wi->is_available && recycle)
> + continue;
> + xsc_put_rx_frag(rq, wi, recycle);
> + }
> +}
> +
> +static void xsc_dump_error_rqcqe(struct xsc_rq *rq,
> + struct xsc_cqe *cqe)
> +{
> + struct net_device *netdev;
> + struct xsc_channel *c;
> + u32 ci;
> +
> + c = rq->cq.channel;
> + netdev = c->adapter->netdev;
> + ci = xsc_cqwq_get_ci(&rq->cq.wq);
> +
> + net_err_ratelimited("Error cqe on dev=%s, cqn=%d, ci=%d, rqn=%d, qpn=%ld, error_code=0x%x\n",
> + netdev->name, rq->cq.xcq.cqn, ci,
> + rq->rqn,
> + FIELD_GET(XSC_CQE_QP_ID_MASK,
> + le32_to_cpu(cqe->data0)),
> + get_cqe_opcode(cqe));
> }
>
> void xsc_eth_handle_rx_cqe(struct xsc_cqwq *cqwq,
> struct xsc_rq *rq, struct xsc_cqe *cqe)
> {
> - /* TBD */
> + struct xsc_wq_cyc *wq = &rq->wqe.wq;
> + u8 cqe_opcode = get_cqe_opcode(cqe);
> + struct xsc_wqe_frag_info *wi;
> + struct sk_buff *skb;
> + u32 cqe_bcnt;
> + u16 ci;
> +
> + ci = xsc_wq_cyc_ctr2ix(wq, cqwq->cc);
> + wi = get_frag(rq, ci);
> + if (unlikely(cqe_opcode & BIT(7))) {
> + xsc_dump_error_rqcqe(rq, cqe);
> + goto free_wqe;
> + }
> +
> + cqe_bcnt = le32_to_cpu(cqe->msg_len);
> + if ((le32_to_cpu(cqe->data0) & XSC_CQE_HAS_PPH) &&
> + cqe_bcnt <= XSC_PPH_HEAD_LEN)
> + goto free_wqe;
> +
> + if (unlikely(cqe_bcnt > rq->frags_sz))
> + goto free_wqe;
> +
> + cqe_bcnt = min_t(u32, cqe_bcnt, rq->frags_sz);
I didn't get this code. You checked that cqe_bcnt is smaller or
equal to rq->frags_sz. And after that you try to find a minimum
between the same 2 values. It will always be cqe_bcnt, so this
line is meaningless..
> + skb = rq->wqe.skb_from_cqe(rq, wi, cqe_bcnt,
> + !!(le32_to_cpu(cqe->data0) &
> + XSC_CQE_HAS_PPH));
> + if (!skb)
> + goto free_wqe;
> +
> + xsc_complete_rx_cqe(rq, cqe,
> + (le32_to_cpu(cqe->data0) & XSC_CQE_HAS_PPH) ?
> + cqe_bcnt - XSC_PPH_HEAD_LEN : cqe_bcnt,
> + skb, wi);
> +
> + napi_gro_receive(rq->cq.napi, skb);
> +
> +free_wqe:
> + xsc_free_rx_wqe(rq, wi, true);
> + xsc_wq_cyc_pop(wq);
> +}
> +
> +int xsc_poll_rx_cq(struct xsc_cq *cq, int budget)
> +{
> + struct xsc_rq *rq = container_of(cq, struct xsc_rq, cq);
> + struct xsc_cqwq *cqwq = &cq->wq;
> + struct xsc_cqe *cqe;
> + int work_done = 0;
> +
> + if (!test_bit(XSC_ETH_RQ_STATE_ENABLED, &rq->state))
> + return 0;
> +
> + while ((work_done < budget) && (cqe = xsc_cqwq_get_cqe(cqwq))) {
> + rq->handle_rx_cqe(cqwq, rq, cqe);
> + ++work_done;
> +
> + xsc_cqwq_pop(cqwq);
> + }
> +
> + if (!work_done)
> + goto out;
> +
> + xsc_cq_notify_hw(cq);
> + /* ensure cq space is freed before enabling more cqes */
> + wmb();
> +
> +out:
> + return work_done;
> +}
> +
> +static int xsc_page_alloc_pool(struct xsc_rq *rq,
> + struct xsc_dma_info *dma_info)
> +{
> + dma_info->page = page_pool_dev_alloc_pages(rq->page_pool);
> + if (unlikely(!dma_info->page))
> + return -ENOMEM;
> + dma_info->addr = page_pool_get_dma_addr(dma_info->page);
> +
> + return 0;
> +}
> +
> +static int xsc_get_rx_frag(struct xsc_rq *rq,
> + struct xsc_wqe_frag_info *frag)
> +{
> + int err = 0;
> +
> + if (!frag->offset && !frag->is_available)
> + /* On first frag (offset == 0), replenish page (dma_info
> + * actually). Other frags that point to the same dma_info
> + * (with a different offset) should just use the new one
> + * without replenishing again by themselves.
> + */
> + err = xsc_page_alloc_pool(rq, frag->di);
> +
> + return err;
> +}
> +
> +static int xsc_alloc_rx_wqe(struct xsc_rq *rq,
> + struct xsc_eth_rx_wqe_cyc *wqe,
> + u16 ix)
> +{
> + struct xsc_wqe_frag_info *frag = get_frag(rq, ix);
> + u64 addr;
> + int err;
> + int i;
> +
> + for (i = 0; i < rq->wqe.info.num_frags; i++, frag++) {
> + err = xsc_get_rx_frag(rq, frag);
> + if (unlikely(err))
> + goto err_free_frags;
> +
> + addr = frag->di->addr + frag->offset + rq->buff.headroom;
> + wqe->data[i].va = cpu_to_le64(addr);
> + }
> +
> + return 0;
> +
> +err_free_frags:
> + while (--i >= 0)
> + xsc_put_rx_frag(rq, --frag, true);
> +
> + return err;
> }
>
> void xsc_eth_dealloc_rx_wqe(struct xsc_rq *rq, u16 ix)
> {
> - /* TBD */
> + struct xsc_wqe_frag_info *wi = get_frag(rq, ix);
> +
> + xsc_free_rx_wqe(rq, wi, false);
> }
>
> -bool xsc_eth_post_rx_wqes(struct xsc_rq *rq)
> +static int xsc_alloc_rx_wqes(struct xsc_rq *rq, u16 ix, u8 wqe_bulk)
> {
> - /* TBD */
> - return true;
> + struct xsc_wq_cyc *wq = &rq->wqe.wq;
> + struct xsc_eth_rx_wqe_cyc *wqe;
> + int err;
> + int idx;
> + int i;
> +
> + for (i = 0; i < wqe_bulk; i++) {
> + idx = xsc_wq_cyc_ctr2ix(wq, (ix + i));
> + wqe = xsc_wq_cyc_get_wqe(wq, idx);
> +
> + err = xsc_alloc_rx_wqe(rq, wqe, idx);
> + if (unlikely(err))
> + goto err_free_wqes;
> + }
> +
> + return 0;
> +
> +err_free_wqes:
> + while (--i >= 0)
> + xsc_eth_dealloc_rx_wqe(rq, ix + i);
> +
> + return err;
> }
>
> +bool xsc_eth_post_rx_wqes(struct xsc_rq *rq)
> +{
> + struct xsc_wq_cyc *wq = &rq->wqe.wq;
> + u8 wqe_bulk, wqe_bulk_min;
> + int err = 0;
> + int alloc;
> + u16 head;
> +
> + wqe_bulk = rq->wqe.info.wqe_bulk;
> + wqe_bulk_min = rq->wqe.info.wqe_bulk_min;
> + if (xsc_wq_cyc_missing(wq) < wqe_bulk)
> + return false;
> +
> + do {
> + head = xsc_wq_cyc_get_head(wq);
> +
> + alloc = min_t(int, wqe_bulk, xsc_wq_cyc_missing(wq));
> + if (alloc < wqe_bulk && alloc >= wqe_bulk_min)
> + alloc = alloc & 0xfffffffe;
> +
> + if (alloc > 0) {
> + err = xsc_alloc_rx_wqes(rq, head, alloc);
> + if (unlikely(err))
> + break;
> +
> + xsc_wq_cyc_push_n(wq, alloc);
> + }
> + } while (xsc_wq_cyc_missing(wq) >= wqe_bulk_min);
> +
> + dma_wmb();
> +
> + /* ensure wqes are visible to device before updating doorbell record */
> + xsc_rq_notify_hw(rq);
> +
> + return !!err;
> +}
> diff --git a/drivers/net/ethernet/yunsilicon/xsc/net/xsc_eth_txrx.c b/drivers/net/ethernet/yunsilicon/xsc/net/xsc_eth_txrx.c
> index 9784816c3..3a843b152 100644
> --- a/drivers/net/ethernet/yunsilicon/xsc/net/xsc_eth_txrx.c
> +++ b/drivers/net/ethernet/yunsilicon/xsc/net/xsc_eth_txrx.c
> @@ -140,6 +140,7 @@ int xsc_eth_napi_poll(struct napi_struct *napi, int budget)
> {
> struct xsc_channel *c = container_of(napi, struct xsc_channel, napi);
> struct xsc_eth_params *params = &c->adapter->nic_param;
> + struct xsc_rq *rq = &c->qp.rq[0];
> struct xsc_sq *sq = NULL;
> bool busy = false;
> int work_done = 0;
> @@ -152,11 +153,21 @@ int xsc_eth_napi_poll(struct napi_struct *napi, int budget)
> for (i = 0; i < c->num_tc; i++)
> busy |= xsc_poll_tx_cq(&c->qp.sq[i].cq, tx_budget);
>
> + /* budget=0 means: don't poll rx rings */
> + if (likely(budget)) {
> + work_done = xsc_poll_rx_cq(&rq->cq, budget);
> + busy |= work_done == budget;
> + }
> +
> + busy |= rq->post_wqes(rq);
> +
> if (busy) {
> if (likely(xsc_channel_no_affinity_change(c))) {
> rcu_read_unlock();
> return budget;
> }
> + if (budget && work_done == budget)
> + work_done--;
> }
>
> if (unlikely(!napi_complete_done(napi, work_done)))
> @@ -166,6 +177,8 @@ int xsc_eth_napi_poll(struct napi_struct *napi, int budget)
> sq = &c->qp.sq[i];
> xsc_cq_notify_hw_rearm(&sq->cq);
> }
> +
> + xsc_cq_notify_hw_rearm(&rq->cq);
> err_out:
> rcu_read_unlock();
> return work_done;
> diff --git a/drivers/net/ethernet/yunsilicon/xsc/net/xsc_eth_txrx.h b/drivers/net/ethernet/yunsilicon/xsc/net/xsc_eth_txrx.h
> index f8acc6bbb..d0d303efa 100644
> --- a/drivers/net/ethernet/yunsilicon/xsc/net/xsc_eth_txrx.h
> +++ b/drivers/net/ethernet/yunsilicon/xsc/net/xsc_eth_txrx.h
> @@ -31,6 +31,7 @@ struct sk_buff *xsc_skb_from_cqe_linear(struct xsc_rq *rq,
> struct sk_buff *xsc_skb_from_cqe_nonlinear(struct xsc_rq *rq,
> struct xsc_wqe_frag_info *wi,
> u32 cqe_bcnt, u8 has_pph);
> +int xsc_poll_rx_cq(struct xsc_cq *cq, int budget);
>
> netdev_tx_t xsc_eth_xmit_start(struct sk_buff *skb, struct net_device *netdev);
>
> diff --git a/drivers/net/ethernet/yunsilicon/xsc/net/xsc_queue.h b/drivers/net/ethernet/yunsilicon/xsc/net/xsc_queue.h
> index 4c9f183fc..4601eec3b 100644
> --- a/drivers/net/ethernet/yunsilicon/xsc/net/xsc_queue.h
> +++ b/drivers/net/ethernet/yunsilicon/xsc/net/xsc_queue.h
> @@ -49,19 +49,6 @@ enum {
> XSC_ETH_SQ_STATE_AM,
> };
>
> -struct xsc_dma_info {
> - struct page *page;
> - dma_addr_t addr;
> -};
> -
> -struct xsc_page_cache {
> - struct xsc_dma_info *page_cache;
> - u32 head;
> - u32 tail;
> - u32 sz;
> - u32 resv;
> -};
> -
> struct xsc_cq {
> /* data path - accessed per cqe */
> struct xsc_cqwq wq;
> @@ -85,6 +72,11 @@ struct xsc_wqe_frag_info {
> u8 is_available;
> };
>
> +struct xsc_dma_info {
> + struct page *page;
> + dma_addr_t addr;
> +};
> +
> struct xsc_rq_frag_info {
> int frag_size;
> int frag_stride;
> @@ -139,7 +131,6 @@ struct xsc_rq {
> xsc_fp_handle_rx_cqe handle_rx_cqe;
> xsc_fp_post_rx_wqes post_wqes;
> xsc_fp_dealloc_wqe dealloc_wqe;
> - struct xsc_page_cache page_cache;
> } ____cacheline_aligned_in_smp;
>
> enum xsc_dma_map_type {
Powered by blists - more mailing lists