[RFC PATCH net-next 11/28] net: ethernet: ti: cpsw-proxy-client: add NAPI TX polling function

From: Siddharth Vadapalli
Date: Sat May 18 2024 - 08:46:26 EST


Add the "vport_tx_poll()" function to be registered as the NAPI TX
polling function via "netif_napi_add_tx()".

Signed-off-by: Siddharth Vadapalli <s-vadapalli@xxxxxx>
---
drivers/net/ethernet/ti/cpsw-proxy-client.c | 140 ++++++++++++++++++++
1 file changed, 140 insertions(+)

diff --git a/drivers/net/ethernet/ti/cpsw-proxy-client.c b/drivers/net/ethernet/ti/cpsw-proxy-client.c
index 16e8e585adce..cf99d8b6c1ec 100644
--- a/drivers/net/ethernet/ti/cpsw-proxy-client.c
+++ b/drivers/net/ethernet/ti/cpsw-proxy-client.c
@@ -66,6 +66,7 @@ struct tx_dma_chan {
struct device *dev;
struct k3_cppi_desc_pool *desc_pool;
struct k3_udma_glue_tx_channel *tx_chan;
+ struct napi_struct napi_tx;
u32 rel_chan_idx;
u32 thread_id;
u32 num_descs;
@@ -74,11 +75,26 @@ struct tx_dma_chan {
bool in_use;
};

+struct vport_netdev_stats {
+ u64 tx_packets;
+ u64 tx_bytes;
+ u64 rx_packets;
+ u64 rx_bytes;
+ struct u64_stats_sync syncp;
+};
+
+struct vport_netdev_priv {
+ struct vport_netdev_stats __percpu *stats;
+ struct virtual_port *vport;
+};
+
struct virtual_port {
struct cpsw_proxy_priv *proxy_priv;
struct rx_dma_chan *rx_chans;
struct tx_dma_chan *tx_chans;
+ struct completion tdown_complete;
enum virtual_port_type port_type;
+ atomic_t tdown_cnt;
u32 port_id;
u32 port_token;
u32 port_features;
@@ -672,6 +688,7 @@ static int init_tx_chans(struct cpsw_proxy_priv *proxy_priv)

for (i = 0; i < proxy_priv->num_virt_ports; i++) {
vport = &proxy_priv->virt_ports[i];
+ init_completion(&vport->tdown_complete);

for (j = 0; j < vport->num_tx_chan; j++) {
tx_chn = &vport->tx_chans[j];
@@ -848,6 +865,129 @@ static int init_rx_chans(struct cpsw_proxy_priv *proxy_priv)
return ret;
}

+static void vport_xmit_free(struct tx_dma_chan *tx_chn, struct device *dev,
+ struct cppi5_host_desc_t *desc)
+{
+ struct cppi5_host_desc_t *first_desc, *next_desc;
+ dma_addr_t buf_dma, next_desc_dma;
+ u32 buf_dma_len;
+
+ first_desc = desc;
+ next_desc = first_desc;
+
+ cppi5_hdesc_get_obuf(first_desc, &buf_dma, &buf_dma_len);
+
+ dma_unmap_single(dev, buf_dma, buf_dma_len,
+ DMA_TO_DEVICE);
+
+ next_desc_dma = cppi5_hdesc_get_next_hbdesc(first_desc);
+ while (next_desc_dma) {
+ next_desc = k3_cppi_desc_pool_dma2virt(tx_chn->desc_pool,
+ next_desc_dma);
+ cppi5_hdesc_get_obuf(next_desc, &buf_dma, &buf_dma_len);
+
+ dma_unmap_page(dev, buf_dma, buf_dma_len,
+ DMA_TO_DEVICE);
+
+ next_desc_dma = cppi5_hdesc_get_next_hbdesc(next_desc);
+
+ k3_cppi_desc_pool_free(tx_chn->desc_pool, next_desc);
+ }
+
+ k3_cppi_desc_pool_free(tx_chn->desc_pool, first_desc);
+}
+
+static int tx_compl_packets(struct virtual_port *vport, unsigned int tx_chan_idx,
+ unsigned int budget, bool *tdown)
+{
+ struct cpsw_proxy_priv *proxy_priv = vport->proxy_priv;
+ struct device *dev = proxy_priv->dev;
+ struct cppi5_host_desc_t *desc_tx;
+ struct netdev_queue *netif_txq;
+ unsigned int total_bytes = 0;
+ struct tx_dma_chan *tx_chn;
+ struct net_device *ndev;
+ struct sk_buff *skb;
+ dma_addr_t desc_dma;
+ int res, num_tx = 0;
+ void **swdata;
+
+ tx_chn = &vport->tx_chans[tx_chan_idx];
+
+ while (budget--) {
+ struct vport_netdev_priv *ndev_priv;
+ struct vport_netdev_stats *stats;
+
+ res = k3_udma_glue_pop_tx_chn(tx_chn->tx_chan, &desc_dma);
+ if (res == -ENODATA)
+ break;
+
+ if (desc_dma & 0x1) {
+ if (atomic_dec_and_test(&vport->tdown_cnt))
+ complete(&vport->tdown_complete);
+ *tdown = true;
+ break;
+ }
+
+ desc_tx = k3_cppi_desc_pool_dma2virt(tx_chn->desc_pool,
+ desc_dma);
+ swdata = cppi5_hdesc_get_swdata(desc_tx);
+ skb = *(swdata);
+ vport_xmit_free(tx_chn, dev, desc_tx);
+
+ ndev = skb->dev;
+
+ ndev_priv = netdev_priv(ndev);
+ stats = this_cpu_ptr(ndev_priv->stats);
+ u64_stats_update_begin(&stats->syncp);
+ stats->tx_packets++;
+ stats->tx_bytes += skb->len;
+ u64_stats_update_end(&stats->syncp);
+
+ total_bytes += skb->len;
+ napi_consume_skb(skb, budget);
+ num_tx++;
+ }
+
+ if (!num_tx)
+ return 0;
+
+ netif_txq = netdev_get_tx_queue(ndev, tx_chan_idx);
+ netdev_tx_completed_queue(netif_txq, num_tx, total_bytes);
+
+ if (netif_tx_queue_stopped(netif_txq)) {
+ __netif_tx_lock(netif_txq, smp_processor_id());
+ if (netif_running(ndev) &&
+ (k3_cppi_desc_pool_avail(tx_chn->desc_pool) >=
+ MAX_SKB_FRAGS))
+ netif_tx_wake_queue(netif_txq);
+
+ __netif_tx_unlock(netif_txq);
+ }
+
+ return num_tx;
+}
+
+static int vport_tx_poll(struct napi_struct *napi_tx, int budget)
+{
+ struct tx_dma_chan *tx_chn = container_of(napi_tx, struct tx_dma_chan,
+ napi_tx);
+ struct virtual_port *vport = tx_chn->vport;
+ bool tdown = false;
+ int num_tx;
+
+ /* process every unprocessed channel */
+ num_tx = tx_compl_packets(vport, tx_chn->rel_chan_idx, budget, &tdown);
+
+ if (num_tx >= budget)
+ return budget;
+
+ if (napi_complete_done(napi_tx, num_tx))
+ enable_irq(tx_chn->irq);
+
+ return 0;
+}
+
static int cpsw_proxy_client_probe(struct rpmsg_device *rpdev)
{
struct cpsw_proxy_priv *proxy_priv;
--
2.40.1