net: ethernet: ti: davinci_cpdma: split descs num between all channels
authorIvan Khoronzhuk <ivan.khoronzhuk@linaro.org>
Mon, 22 Aug 2016 18:18:24 +0000 (21:18 +0300)
committerDavid S. Miller <davem@davemloft.net>
Tue, 23 Aug 2016 07:13:10 +0000 (00:13 -0700)
Tx channels share same pool of descriptors. Thus one channel can
block another if pool is emptied by one. But, the shaper should
decide which channel is allowed to send packets. To avoid such
impact of one channel on another, let every channel to have its
own piece of pool.

Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
Reviewed-by: Mugunthan V N <mugunthanvnm@ti.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/ti/cpsw.c
drivers/net/ethernet/ti/davinci_cpdma.c
drivers/net/ethernet/ti/davinci_cpdma.h

index 421ebdaf12089da578a2806a186bb28981cd69e8..4065f9662bad380110ab01ee89b72a12e6b8adaa 100644 (file)
@@ -1213,6 +1213,40 @@ static void cpsw_init_host_port(struct cpsw_priv *priv)
        }
 }
 
+static int cpsw_fill_rx_channels(struct cpsw_priv *priv)
+{
+       struct cpsw_common *cpsw = priv->cpsw;
+       struct sk_buff *skb;
+       int ch_buf_num;
+       int i, ret;
+
+       ch_buf_num = cpdma_chan_get_rx_buf_num(cpsw->rxch);
+       for (i = 0; i < ch_buf_num; i++) {
+               skb = __netdev_alloc_skb_ip_align(priv->ndev,
+                                                 cpsw->rx_packet_max,
+                                                 GFP_KERNEL);
+               if (!skb) {
+                       cpsw_err(priv, ifup, "cannot allocate skb\n");
+                       return -ENOMEM;
+               }
+
+               ret = cpdma_chan_submit(cpsw->rxch, skb, skb->data,
+                                       skb_tailroom(skb), 0);
+               if (ret < 0) {
+                       cpsw_err(priv, ifup,
+                                "cannot submit skb to rx channel, error %d\n",
+                                ret);
+                       kfree_skb(skb);
+                       return ret;
+               }
+               kmemleak_not_leak(skb);
+       }
+
+       cpsw_info(priv, ifup, "submitted %d rx descriptors\n", ch_buf_num);
+
+       return ch_buf_num;
+}
+
 static void cpsw_slave_stop(struct cpsw_slave *slave, struct cpsw_common *cpsw)
 {
        u32 slave_port;
@@ -1233,7 +1267,7 @@ static int cpsw_ndo_open(struct net_device *ndev)
 {
        struct cpsw_priv *priv = netdev_priv(ndev);
        struct cpsw_common *cpsw = priv->cpsw;
-       int i, ret;
+       int ret;
        u32 reg;
 
        ret = pm_runtime_get_sync(cpsw->dev);
@@ -1265,8 +1299,6 @@ static int cpsw_ndo_open(struct net_device *ndev)
                                  ALE_ALL_PORTS, ALE_ALL_PORTS, 0, 0);
 
        if (!cpsw_common_res_usage_state(cpsw)) {
-               int buf_num;
-
                /* setup tx dma to fixed prio and zero offset */
                cpdma_control_set(cpsw->dma, CPDMA_TX_PRIO_FIXED, 1);
                cpdma_control_set(cpsw->dma, CPDMA_RX_BUFFER_OFFSET, 0);
@@ -1293,27 +1325,9 @@ static int cpsw_ndo_open(struct net_device *ndev)
                        enable_irq(cpsw->irqs_table[0]);
                }
 
-               buf_num = cpdma_chan_get_rx_buf_num(cpsw->dma);
-               for (i = 0; i < buf_num; i++) {
-                       struct sk_buff *skb;
-
-                       ret = -ENOMEM;
-                       skb = __netdev_alloc_skb_ip_align(priv->ndev,
-                                       cpsw->rx_packet_max, GFP_KERNEL);
-                       if (!skb)
-                               goto err_cleanup;
-                       ret = cpdma_chan_submit(cpsw->rxch, skb, skb->data,
-                                               skb_tailroom(skb), 0);
-                       if (ret < 0) {
-                               kfree_skb(skb);
-                               goto err_cleanup;
-                       }
-                       kmemleak_not_leak(skb);
-               }
-               /* continue even if we didn't manage to submit all
-                * receive descs
-                */
-               cpsw_info(priv, ifup, "submitted %d rx descriptors\n", i);
+               ret = cpsw_fill_rx_channels(priv);
+               if (ret < 0)
+                       goto err_cleanup;
 
                if (cpts_register(cpsw->dev, cpsw->cpts,
                                  cpsw->data.cpts_clock_mult,
index cf72b3390d855d0a33196c50299be35b87af4635..167fd659319dafb83a964a8e88a1ce952cf9a468 100644 (file)
@@ -104,6 +104,7 @@ struct cpdma_ctlr {
        struct cpdma_desc_pool  *pool;
        spinlock_t              lock;
        struct cpdma_chan       *channels[2 * CPDMA_MAX_CHANNELS];
+       int chan_num;
 };
 
 struct cpdma_chan {
@@ -256,6 +257,7 @@ struct cpdma_ctlr *cpdma_ctlr_create(struct cpdma_params *params)
        ctlr->state = CPDMA_STATE_IDLE;
        ctlr->params = *params;
        ctlr->dev = params->dev;
+       ctlr->chan_num = 0;
        spin_lock_init(&ctlr->lock);
 
        ctlr->pool = cpdma_desc_pool_create(ctlr->dev,
@@ -399,6 +401,31 @@ void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr, u32 value)
 }
 EXPORT_SYMBOL_GPL(cpdma_ctlr_eoi);
 
+/**
+ * cpdma_chan_split_pool - Splits ctrl pool between all channels.
+ * Has to be called under ctlr lock
+ */
+static void cpdma_chan_split_pool(struct cpdma_ctlr *ctlr)
+{
+       struct cpdma_desc_pool *pool = ctlr->pool;
+       struct cpdma_chan *chan;
+       int ch_desc_num;
+       int i;
+
+       if (!ctlr->chan_num)
+               return;
+
+       /* calculate average size of pool slice */
+       ch_desc_num = pool->num_desc / ctlr->chan_num;
+
+       /* split ctlr pool */
+       for (i = 0; i < ARRAY_SIZE(ctlr->channels); i++) {
+               chan = ctlr->channels[i];
+               if (chan)
+                       chan->desc_num = ch_desc_num;
+       }
+}
+
 struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num,
                                     cpdma_handler_fn handler)
 {
@@ -447,14 +474,25 @@ struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num,
        spin_lock_init(&chan->lock);
 
        ctlr->channels[chan_num] = chan;
+       ctlr->chan_num++;
+
+       cpdma_chan_split_pool(ctlr);
+
        spin_unlock_irqrestore(&ctlr->lock, flags);
        return chan;
 }
 EXPORT_SYMBOL_GPL(cpdma_chan_create);
 
-int cpdma_chan_get_rx_buf_num(struct cpdma_ctlr *ctlr)
+int cpdma_chan_get_rx_buf_num(struct cpdma_chan *chan)
 {
-       return ctlr->pool->num_desc / 2;
+       unsigned long flags;
+       int desc_num;
+
+       spin_lock_irqsave(&chan->lock, flags);
+       desc_num = chan->desc_num;
+       spin_unlock_irqrestore(&chan->lock, flags);
+
+       return desc_num;
 }
 EXPORT_SYMBOL_GPL(cpdma_chan_get_rx_buf_num);
 
@@ -471,6 +509,10 @@ int cpdma_chan_destroy(struct cpdma_chan *chan)
        if (chan->state != CPDMA_STATE_IDLE)
                cpdma_chan_stop(chan);
        ctlr->channels[chan->chan_num] = NULL;
+       ctlr->chan_num--;
+
+       cpdma_chan_split_pool(ctlr);
+
        spin_unlock_irqrestore(&ctlr->lock, flags);
        return 0;
 }
index 4b46cd6e9a3f245ce8d5eba1ea86ea17d55bf16a..9119b43f4f7d54b74cb84fc52babc6ddd9d3ecb5 100644 (file)
@@ -80,7 +80,7 @@ int cpdma_ctlr_stop(struct cpdma_ctlr *ctlr);
 
 struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num,
                                     cpdma_handler_fn handler);
-int cpdma_chan_get_rx_buf_num(struct cpdma_ctlr *ctlr);
+int cpdma_chan_get_rx_buf_num(struct cpdma_chan *chan);
 int cpdma_chan_destroy(struct cpdma_chan *chan);
 int cpdma_chan_start(struct cpdma_chan *chan);
 int cpdma_chan_stop(struct cpdma_chan *chan);