summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_common.c323
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_prueth.c60
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_prueth.h2
3 files changed, 313 insertions, 72 deletions
diff --git a/drivers/net/ethernet/ti/icssg/icssg_common.c b/drivers/net/ethernet/ti/icssg/icssg_common.c
index b88cfe99e8b7..090aa74d3ce7 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_common.c
+++ b/drivers/net/ethernet/ti/icssg/icssg_common.c
@@ -465,6 +465,29 @@ fail:
}
EXPORT_SYMBOL_GPL(prueth_init_tx_chns);
+static struct page_pool *prueth_create_page_pool(struct prueth_emac *emac,
+ struct device *dma_dev,
+ int size)
+{
+ struct page_pool_params pp_params = { 0 };
+ struct page_pool *pool;
+
+ pp_params.order = 0;
+ pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV;
+ pp_params.pool_size = size;
+ pp_params.nid = dev_to_node(emac->prueth->dev);
+ pp_params.dma_dir = DMA_BIDIRECTIONAL;
+ pp_params.dev = dma_dev;
+ pp_params.napi = &emac->napi_rx;
+ pp_params.max_len = PAGE_SIZE;
+
+ pool = page_pool_create(&pp_params);
+ if (IS_ERR(pool))
+ netdev_err(emac->ndev, "cannot create rx page pool\n");
+
+ return pool;
+}
+
int prueth_init_rx_chns(struct prueth_emac *emac,
struct prueth_rx_chn *rx_chn,
char *name, u32 max_rflows,
@@ -474,6 +497,7 @@ int prueth_init_rx_chns(struct prueth_emac *emac,
struct device *dev = emac->prueth->dev;
struct net_device *ndev = emac->ndev;
u32 fdqring_id, hdesc_size;
+ struct page_pool *pool;
int i, ret = 0, slice;
int flow_id_base;
@@ -516,6 +540,14 @@ int prueth_init_rx_chns(struct prueth_emac *emac,
goto fail;
}
+ pool = prueth_create_page_pool(emac, rx_chn->dma_dev, rx_chn->descs_num);
+ if (IS_ERR(pool)) {
+ ret = PTR_ERR(pool);
+ goto fail;
+ }
+
+ rx_chn->pg_pool = pool;
+
flow_id_base = k3_udma_glue_rx_get_flow_id_base(rx_chn->rx_chn);
if (emac->is_sr1 && !strcmp(name, "rxmgm")) {
emac->rx_mgm_flow_id_base = flow_id_base;
@@ -797,12 +829,188 @@ drop:
fallthrough; /* handle aborts by dropping packet */
case XDP_DROP:
ndev->stats.rx_dropped++;
- page_pool_recycle_direct(emac->rx_chns.pg_pool,
- virt_to_head_page(xdp->data));
return ICSSG_XDP_CONSUMED;
}
}
+static int prueth_dma_rx_push_mapped_zc(struct prueth_emac *emac,
+ struct prueth_rx_chn *rx_chn,
+ struct xdp_buff *xdp)
+{
+ struct net_device *ndev = emac->ndev;
+ struct cppi5_host_desc_t *desc_rx;
+ struct prueth_swdata *swdata;
+ dma_addr_t desc_dma;
+ dma_addr_t buf_dma;
+ int buf_len;
+
+ buf_dma = xsk_buff_xdp_get_dma(xdp);
+ desc_rx = k3_cppi_desc_pool_alloc(rx_chn->desc_pool);
+ if (!desc_rx) {
+ netdev_err(ndev, "rx push: failed to allocate descriptor\n");
+ return -ENOMEM;
+ }
+ desc_dma = k3_cppi_desc_pool_virt2dma(rx_chn->desc_pool, desc_rx);
+
+ cppi5_hdesc_init(desc_rx, CPPI5_INFO0_HDESC_EPIB_PRESENT,
+ PRUETH_NAV_PS_DATA_SIZE);
+ k3_udma_glue_rx_dma_to_cppi5_addr(rx_chn->rx_chn, &buf_dma);
+ buf_len = xsk_pool_get_rx_frame_size(rx_chn->xsk_pool);
+ cppi5_hdesc_attach_buf(desc_rx, buf_dma, buf_len, buf_dma, buf_len);
+ swdata = cppi5_hdesc_get_swdata(desc_rx);
+ swdata->type = PRUETH_SWDATA_XSK;
+ swdata->data.xdp = xdp;
+
+ return k3_udma_glue_push_rx_chn(rx_chn->rx_chn, PRUETH_RX_FLOW_DATA,
+ desc_rx, desc_dma);
+}
+
+static int prueth_rx_alloc_zc(struct prueth_emac *emac, int budget)
+{
+ struct prueth_rx_chn *rx_chn = &emac->rx_chns;
+ struct xdp_buff *xdp;
+ int i, ret;
+
+ for (i = 0; i < budget; i++) {
+ xdp = xsk_buff_alloc(rx_chn->xsk_pool);
+ if (!xdp)
+ break;
+
+ ret = prueth_dma_rx_push_mapped_zc(emac, rx_chn, xdp);
+ if (ret) {
+ netdev_err(emac->ndev, "rx alloc: failed to map descriptors to xdp buff\n");
+ xsk_buff_free(xdp);
+ break;
+ }
+ }
+
+ return i;
+}
+
+static void emac_dispatch_skb_zc(struct prueth_emac *emac, struct xdp_buff *xdp, u32 *psdata)
+{
+ unsigned int headroom = xdp->data - xdp->data_hard_start;
+ unsigned int pkt_len = xdp->data_end - xdp->data;
+ struct net_device *ndev = emac->ndev;
+ struct sk_buff *skb;
+
+ skb = napi_alloc_skb(&emac->napi_rx, xdp->data_end - xdp->data_hard_start);
+ if (unlikely(!skb)) {
+ ndev->stats.rx_dropped++;
+ return;
+ }
+
+ skb_reserve(skb, headroom);
+ skb_put(skb, pkt_len);
+ skb->dev = ndev;
+
+ /* RX HW timestamp */
+ if (emac->rx_ts_enabled)
+ emac_rx_timestamp(emac, skb, psdata);
+
+ if (emac->prueth->is_switch_mode)
+ skb->offload_fwd_mark = emac->offload_fwd_mark;
+ skb->protocol = eth_type_trans(skb, ndev);
+
+ skb_mark_for_recycle(skb);
+ napi_gro_receive(&emac->napi_rx, skb);
+ ndev->stats.rx_bytes += pkt_len;
+ ndev->stats.rx_packets++;
+}
+
+static int emac_rx_packet_zc(struct prueth_emac *emac, u32 flow_id,
+ int budget)
+{
+ struct prueth_rx_chn *rx_chn = &emac->rx_chns;
+ u32 buf_dma_len, pkt_len, port_id = 0;
+ struct net_device *ndev = emac->ndev;
+ struct cppi5_host_desc_t *desc_rx;
+ struct prueth_swdata *swdata;
+ dma_addr_t desc_dma, buf_dma;
+ struct xdp_buff *xdp;
+ int xdp_status = 0;
+ int count = 0;
+ u32 *psdata;
+ int ret;
+
+ while (count < budget) {
+ ret = k3_udma_glue_pop_rx_chn(rx_chn->rx_chn, flow_id, &desc_dma);
+ if (ret) {
+ if (ret != -ENODATA)
+ netdev_err(ndev, "rx pop: failed: %d\n", ret);
+ break;
+ }
+
+ if (cppi5_desc_is_tdcm(desc_dma)) {
+ complete(&emac->tdown_complete);
+ break;
+ }
+
+ desc_rx = k3_cppi_desc_pool_dma2virt(rx_chn->desc_pool, desc_dma);
+ swdata = cppi5_hdesc_get_swdata(desc_rx);
+ if (swdata->type != PRUETH_SWDATA_XSK) {
+ netdev_err(ndev, "rx_pkt: invalid swdata->type %d\n", swdata->type);
+ k3_cppi_desc_pool_free(rx_chn->desc_pool, desc_rx);
+ break;
+ }
+
+ xdp = swdata->data.xdp;
+ cppi5_hdesc_get_obuf(desc_rx, &buf_dma, &buf_dma_len);
+ k3_udma_glue_rx_cppi5_to_dma_addr(rx_chn->rx_chn, &buf_dma);
+ pkt_len = cppi5_hdesc_get_pktlen(desc_rx);
+ /* firmware adds 4 CRC bytes, strip them */
+ pkt_len -= 4;
+ cppi5_desc_get_tags_ids(&desc_rx->hdr, &port_id, NULL);
+ psdata = cppi5_hdesc_get_psdata(desc_rx);
+ k3_cppi_desc_pool_free(rx_chn->desc_pool, desc_rx);
+ count++;
+ xsk_buff_set_size(xdp, pkt_len);
+ xsk_buff_dma_sync_for_cpu(xdp);
+
+ if (prueth_xdp_is_enabled(emac)) {
+ ret = emac_run_xdp(emac, xdp, &pkt_len);
+ switch (ret) {
+ case ICSSG_XDP_PASS:
+ /* prepare skb and send to n/w stack */
+ emac_dispatch_skb_zc(emac, xdp, psdata);
+ xsk_buff_free(xdp);
+ break;
+ case ICSSG_XDP_CONSUMED:
+ xsk_buff_free(xdp);
+ break;
+ case ICSSG_XDP_TX:
+ case ICSSG_XDP_REDIR:
+ xdp_status |= ret;
+ break;
+ }
+ } else {
+ /* prepare skb and send to n/w stack */
+ emac_dispatch_skb_zc(emac, xdp, psdata);
+ xsk_buff_free(xdp);
+ }
+ }
+
+ if (xdp_status & ICSSG_XDP_REDIR)
+ xdp_do_flush();
+
+ /* Allocate xsk buffers from the pool for the "count" number of
+ * packets processed in order to be able to receive more packets.
+ */
+ ret = prueth_rx_alloc_zc(emac, count);
+
+ if (xsk_uses_need_wakeup(rx_chn->xsk_pool)) {
+ /* If the user space doesn't provide enough buffers then it must
+ * explicitly wake up the kernel when new buffers are available
+ */
+ if (ret < count)
+ xsk_set_rx_need_wakeup(rx_chn->xsk_pool);
+ else
+ xsk_clear_rx_need_wakeup(rx_chn->xsk_pool);
+ }
+
+ return count;
+}
+
static int emac_rx_packet(struct prueth_emac *emac, u32 flow_id, u32 *xdp_state)
{
struct prueth_rx_chn *rx_chn = &emac->rx_chns;
@@ -849,7 +1057,6 @@ static int emac_rx_packet(struct prueth_emac *emac, u32 flow_id, u32 *xdp_state)
/* firmware adds 4 CRC bytes, strip them */
pkt_len -= 4;
cppi5_desc_get_tags_ids(&desc_rx->hdr, &port_id, NULL);
-
k3_cppi_desc_pool_free(rx_chn->desc_pool, desc_rx);
/* if allocation fails we drop the packet but push the
@@ -921,12 +1128,16 @@ void prueth_rx_cleanup(void *data, dma_addr_t desc_dma)
struct cppi5_host_desc_t *desc_rx;
struct prueth_swdata *swdata;
struct page_pool *pool;
+ struct xdp_buff *xdp;
struct page *page;
pool = rx_chn->pg_pool;
desc_rx = k3_cppi_desc_pool_dma2virt(rx_chn->desc_pool, desc_dma);
swdata = cppi5_hdesc_get_swdata(desc_rx);
- if (swdata->type == PRUETH_SWDATA_PAGE) {
+ if (rx_chn->xsk_pool) {
+ xdp = swdata->data.xdp;
+ xsk_buff_free(xdp);
+ } else {
page = swdata->data.page;
page_pool_recycle_direct(pool, page);
}
@@ -1174,6 +1385,7 @@ irqreturn_t prueth_rx_irq(int irq, void *dev_id)
{
struct prueth_emac *emac = dev_id;
+ emac->rx_chns.irq_disabled = true;
disable_irq_nosync(irq);
napi_schedule(&emac->napi_rx);
@@ -1201,6 +1413,7 @@ int icssg_napi_rx_poll(struct napi_struct *napi_rx, int budget)
PRUETH_RX_FLOW_DATA_SR1 : PRUETH_RX_FLOW_DATA;
int flow = emac->is_sr1 ?
PRUETH_MAX_RX_FLOWS_SR1 : PRUETH_MAX_RX_FLOWS;
+ struct prueth_rx_chn *rx_chn = &emac->rx_chns;
int xdp_state_or = 0;
int num_rx = 0;
int cur_budget;
@@ -1208,14 +1421,18 @@ int icssg_napi_rx_poll(struct napi_struct *napi_rx, int budget)
int ret;
while (flow--) {
- cur_budget = budget - num_rx;
-
- while (cur_budget--) {
- ret = emac_rx_packet(emac, flow, &xdp_state);
- xdp_state_or |= xdp_state;
- if (ret)
- break;
- num_rx++;
+ if (rx_chn->xsk_pool) {
+ num_rx = emac_rx_packet_zc(emac, flow, budget);
+ } else {
+ cur_budget = budget - num_rx;
+
+ while (cur_budget--) {
+ ret = emac_rx_packet(emac, flow, &xdp_state);
+ xdp_state_or |= xdp_state;
+ if (ret)
+ break;
+ num_rx++;
+ }
}
if (num_rx >= budget)
@@ -1231,7 +1448,11 @@ int icssg_napi_rx_poll(struct napi_struct *napi_rx, int budget)
ns_to_ktime(emac->rx_pace_timeout_ns),
HRTIMER_MODE_REL_PINNED);
} else {
- enable_irq(emac->rx_chns.irq[rx_flow]);
+ if (emac->rx_chns.irq_disabled) {
+ /* re-enable the RX IRQ */
+ emac->rx_chns.irq_disabled = false;
+ enable_irq(emac->rx_chns.irq[rx_flow]);
+ }
}
}
@@ -1239,62 +1460,48 @@ int icssg_napi_rx_poll(struct napi_struct *napi_rx, int budget)
}
EXPORT_SYMBOL_GPL(icssg_napi_rx_poll);
-static struct page_pool *prueth_create_page_pool(struct prueth_emac *emac,
- struct device *dma_dev,
- int size)
-{
- struct page_pool_params pp_params = { 0 };
- struct page_pool *pool;
-
- pp_params.order = 0;
- pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV;
- pp_params.pool_size = size;
- pp_params.nid = dev_to_node(emac->prueth->dev);
- pp_params.dma_dir = DMA_BIDIRECTIONAL;
- pp_params.dev = dma_dev;
- pp_params.napi = &emac->napi_rx;
- pp_params.max_len = PAGE_SIZE;
-
- pool = page_pool_create(&pp_params);
- if (IS_ERR(pool))
- netdev_err(emac->ndev, "cannot create rx page pool\n");
-
- return pool;
-}
-
int prueth_prepare_rx_chan(struct prueth_emac *emac,
struct prueth_rx_chn *chn,
int buf_size)
{
- struct page_pool *pool;
struct page *page;
+ int desc_avail;
int i, ret;
- pool = prueth_create_page_pool(emac, chn->dma_dev, chn->descs_num);
- if (IS_ERR(pool))
- return PTR_ERR(pool);
-
- chn->pg_pool = pool;
+ desc_avail = k3_cppi_desc_pool_avail(chn->desc_pool);
+ if (desc_avail < chn->descs_num)
+ netdev_warn(emac->ndev,
+ "not enough RX descriptors available %d < %d\n",
+ desc_avail, chn->descs_num);
- for (i = 0; i < chn->descs_num; i++) {
- /* NOTE: we're not using memory efficiently here.
- * 1 full page (4KB?) used here instead of
- * PRUETH_MAX_PKT_SIZE (~1.5KB?)
+ if (chn->xsk_pool) {
+ /* get pages from xsk_pool and push to RX ring
+ * queue as much as possible
*/
- page = page_pool_dev_alloc_pages(pool);
- if (!page) {
- netdev_err(emac->ndev, "couldn't allocate rx page\n");
- ret = -ENOMEM;
+ ret = prueth_rx_alloc_zc(emac, desc_avail);
+ if (!ret)
goto recycle_alloc_pg;
- }
+ } else {
+ for (i = 0; i < desc_avail; i++) {
+ /* NOTE: we're not using memory efficiently here.
+ * 1 full page (4KB?) used here instead of
+ * PRUETH_MAX_PKT_SIZE (~1.5KB?)
+ */
+ page = page_pool_dev_alloc_pages(chn->pg_pool);
+ if (!page) {
+ netdev_err(emac->ndev, "couldn't allocate rx page\n");
+ ret = -ENOMEM;
+ goto recycle_alloc_pg;
+ }
- ret = prueth_dma_rx_push_mapped(emac, chn, page, buf_size);
- if (ret < 0) {
- netdev_err(emac->ndev,
- "cannot submit page for rx chan %s ret %d\n",
- chn->name, ret);
- page_pool_recycle_direct(pool, page);
- goto recycle_alloc_pg;
+ ret = prueth_dma_rx_push_mapped(emac, chn, page, buf_size);
+ if (ret < 0) {
+ netdev_err(emac->ndev,
+ "cannot submit page for rx chan %s ret %d\n",
+ chn->name, ret);
+ page_pool_recycle_direct(chn->pg_pool, page);
+ goto recycle_alloc_pg;
+ }
}
}
diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.c b/drivers/net/ethernet/ti/icssg/icssg_prueth.c
index bb8d42ba0102..22de04ac18cb 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_prueth.c
+++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.c
@@ -395,7 +395,11 @@ static enum hrtimer_restart emac_rx_timer_callback(struct hrtimer *timer)
container_of(timer, struct prueth_emac, rx_hrtimer);
int rx_flow = PRUETH_RX_FLOW_DATA;
- enable_irq(emac->rx_chns.irq[rx_flow]);
+ if (emac->rx_chns.irq_disabled) {
+ /* re-enable the RX IRQ */
+ emac->rx_chns.irq_disabled = false;
+ enable_irq(emac->rx_chns.irq[rx_flow]);
+ }
return HRTIMER_NORESTART;
}
@@ -569,31 +573,41 @@ const struct icss_iep_clockops prueth_iep_clockops = {
.perout_enable = prueth_perout_enable,
};
+static void prueth_destroy_xdp_rxqs(struct prueth_emac *emac)
+{
+ struct xdp_rxq_info *rxq = &emac->rx_chns.xdp_rxq;
+
+ if (xdp_rxq_info_is_reg(rxq))
+ xdp_rxq_info_unreg(rxq);
+}
+
static int prueth_create_xdp_rxqs(struct prueth_emac *emac)
{
struct xdp_rxq_info *rxq = &emac->rx_chns.xdp_rxq;
struct page_pool *pool = emac->rx_chns.pg_pool;
+ struct prueth_rx_chn *rx_chn = &emac->rx_chns;
int ret;
ret = xdp_rxq_info_reg(rxq, emac->ndev, 0, emac->napi_rx.napi_id);
if (ret)
return ret;
- ret = xdp_rxq_info_reg_mem_model(rxq, MEM_TYPE_PAGE_POOL, pool);
- if (ret)
- xdp_rxq_info_unreg(rxq);
-
- return ret;
-}
-
-static void prueth_destroy_xdp_rxqs(struct prueth_emac *emac)
-{
- struct xdp_rxq_info *rxq = &emac->rx_chns.xdp_rxq;
+ if (rx_chn->xsk_pool) {
+ ret = xdp_rxq_info_reg_mem_model(rxq, MEM_TYPE_XSK_BUFF_POOL, NULL);
+ if (ret)
+ goto xdp_unreg;
+ xsk_pool_set_rxq_info(rx_chn->xsk_pool, rxq);
+ } else {
+ ret = xdp_rxq_info_reg_mem_model(rxq, MEM_TYPE_PAGE_POOL, pool);
+ if (ret)
+ goto xdp_unreg;
+ }
- if (!xdp_rxq_info_is_reg(rxq))
- return;
+ return 0;
- xdp_rxq_info_unreg(rxq);
+xdp_unreg:
+ prueth_destroy_xdp_rxqs(emac);
+ return ret;
}
static int icssg_prueth_add_mcast(struct net_device *ndev, const u8 *addr)
@@ -1349,6 +1363,12 @@ int prueth_xsk_wakeup(struct net_device *ndev, u32 qid, u32 flags)
{
struct prueth_emac *emac = netdev_priv(ndev);
struct prueth_tx_chn *tx_chn = &emac->tx_chns[qid];
+ struct prueth_rx_chn *rx_chn = &emac->rx_chns;
+
+ if (emac->xsk_qid != qid) {
+ netdev_err(ndev, "XSK queue %d not registered\n", qid);
+ return -EINVAL;
+ }
if (qid >= PRUETH_MAX_RX_FLOWS || qid >= emac->tx_ch_num) {
netdev_err(ndev, "Invalid XSK queue ID %d\n", qid);
@@ -1360,6 +1380,11 @@ int prueth_xsk_wakeup(struct net_device *ndev, u32 qid, u32 flags)
return -EINVAL;
}
+ if (!rx_chn->xsk_pool) {
+ netdev_err(ndev, "XSK pool not registered for RX queue %d\n", qid);
+ return -EINVAL;
+ }
+
if (flags & XDP_WAKEUP_TX) {
if (!napi_if_scheduled_mark_missed(&tx_chn->napi_tx)) {
if (likely(napi_schedule_prep(&tx_chn->napi_tx)))
@@ -1367,6 +1392,13 @@ int prueth_xsk_wakeup(struct net_device *ndev, u32 qid, u32 flags)
}
}
+ if (flags & XDP_WAKEUP_RX) {
+ if (!napi_if_scheduled_mark_missed(&emac->napi_rx)) {
+ if (likely(napi_schedule_prep(&emac->napi_rx)))
+ __napi_schedule(&emac->napi_rx);
+ }
+ }
+
return 0;
}
diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.h b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
index 3147a1d8f59a..10eadd356650 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_prueth.h
+++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
@@ -143,6 +143,7 @@ struct prueth_rx_chn {
struct page_pool *pg_pool;
struct xdp_rxq_info xdp_rxq;
struct xsk_buff_pool *xsk_pool;
+ bool irq_disabled;
};
enum prueth_swdata_type {
@@ -166,6 +167,7 @@ struct prueth_swdata {
struct page *page;
u32 cmd;
struct xdp_frame *xdpf;
+ struct xdp_buff *xdp;
} data;
};