net_sched: add the ability to defer skb freeing
[cascardo/linux.git] / net / sched / sch_generic.c
index 80742ed..773b632 100644 (file)
@@ -49,6 +49,7 @@ static inline int dev_requeue_skb(struct sk_buff *skb, struct Qdisc *q)
 {
        q->gso_skb = skb;
        q->qstats.requeues++;
+       qdisc_qstats_backlog_inc(q, skb);
        q->q.qlen++;    /* it's still part of the queue */
        __netif_schedule(q);
 
@@ -92,6 +93,7 @@ static struct sk_buff *dequeue_skb(struct Qdisc *q, bool *validate,
                txq = skb_get_tx_queue(txq->dev, skb);
                if (!netif_xmit_frozen_or_stopped(txq)) {
                        q->gso_skb = NULL;
+                       qdisc_qstats_backlog_dec(q, skb);
                        q->q.qlen--;
                } else
                        skb = NULL;
@@ -108,38 +110,9 @@ static struct sk_buff *dequeue_skb(struct Qdisc *q, bool *validate,
        return skb;
 }
 
-static inline int handle_dev_cpu_collision(struct sk_buff *skb,
-                                          struct netdev_queue *dev_queue,
-                                          struct Qdisc *q)
-{
-       int ret;
-
-       if (unlikely(dev_queue->xmit_lock_owner == smp_processor_id())) {
-               /*
-                * Same CPU holding the lock. It may be a transient
-                * configuration error, when hard_start_xmit() recurses. We
-                * detect it by checking xmit owner and drop the packet when
-                * deadloop is detected. Return OK to try the next skb.
-                */
-               kfree_skb_list(skb);
-               net_warn_ratelimited("Dead loop on netdevice %s, fix it urgently!\n",
-                                    dev_queue->dev->name);
-               ret = qdisc_qlen(q);
-       } else {
-               /*
-                * Another cpu is holding lock, requeue & delay xmits for
-                * some time.
-                */
-               __this_cpu_inc(softnet_data.cpu_collision);
-               ret = dev_requeue_skb(skb, q);
-       }
-
-       return ret;
-}
-
 /*
  * Transmit possibly several skbs, and handle the return status as
- * required. Holding the __QDISC___STATE_RUNNING bit guarantees that
+ * required. Owning running seqcount bit guarantees that
  * only one CPU can execute this function.
  *
  * Returns to the caller:
@@ -174,9 +147,6 @@ int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q,
        if (dev_xmit_complete(ret)) {
                /* Driver sent out skb successfully or skb was consumed */
                ret = qdisc_qlen(q);
-       } else if (ret == NETDEV_TX_LOCKED) {
-               /* Driver try lock failed */
-               ret = handle_dev_cpu_collision(skb, txq, q);
        } else {
                /* Driver returned NETDEV_TX_BUSY - requeue skb */
                if (unlikely(ret != NETDEV_TX_BUSY))
@@ -195,7 +165,7 @@ int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q,
 /*
  * NOTE: Called under qdisc_lock(q) with locally disabled BH.
  *
- * __QDISC___STATE_RUNNING guarantees only one CPU can process
+ * running seqcount guarantees only one CPU can process
  * this qdisc at a time. qdisc_lock(q) serializes queue accesses for
  * this queue.
  *
@@ -259,13 +229,12 @@ unsigned long dev_trans_start(struct net_device *dev)
 
        if (is_vlan_dev(dev))
                dev = vlan_dev_real_dev(dev);
-       res = dev->trans_start;
-       for (i = 0; i < dev->num_tx_queues; i++) {
+       res = netdev_get_tx_queue(dev, 0)->trans_start;
+       for (i = 1; i < dev->num_tx_queues; i++) {
                val = netdev_get_tx_queue(dev, i)->trans_start;
                if (val && time_after(val, res))
                        res = val;
        }
-       dev->trans_start = res;
 
        return res;
 }
@@ -288,10 +257,7 @@ static void dev_watchdog(unsigned long arg)
                                struct netdev_queue *txq;
 
                                txq = netdev_get_tx_queue(dev, i);
-                               /*
-                                * old device drivers set dev->trans_start
-                                */
-                               trans_start = txq->trans_start ? : dev->trans_start;
+                               trans_start = txq->trans_start;
                                if (netif_xmit_stopped(txq) &&
                                    time_after(jiffies, (trans_start +
                                                         dev->watchdog_timeo))) {
@@ -415,6 +381,7 @@ struct Qdisc noop_qdisc = {
        .list           =       LIST_HEAD_INIT(noop_qdisc.list),
        .q.lock         =       __SPIN_LOCK_UNLOCKED(noop_qdisc.q.lock),
        .dev_queue      =       &noop_netdev_queue,
+       .running        =       SEQCNT_ZERO(noop_qdisc.running),
        .busylock       =       __SPIN_LOCK_UNLOCKED(noop_qdisc.busylock),
 };
 EXPORT_SYMBOL(noop_qdisc);
@@ -526,7 +493,7 @@ static void pfifo_fast_reset(struct Qdisc *qdisc)
        struct pfifo_fast_priv *priv = qdisc_priv(qdisc);
 
        for (prio = 0; prio < PFIFO_FAST_BANDS; prio++)
-               __qdisc_reset_queue(qdisc, band2list(priv, prio));
+               __qdisc_reset_queue(band2list(priv, prio));
 
        priv->bitmap = 0;
        qdisc->qstats.backlog = 0;
@@ -573,6 +540,7 @@ struct Qdisc_ops pfifo_fast_ops __read_mostly = {
 EXPORT_SYMBOL(pfifo_fast_ops);
 
 static struct lock_class_key qdisc_tx_busylock;
+static struct lock_class_key qdisc_running_key;
 
 struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue,
                          const struct Qdisc_ops *ops)
@@ -606,6 +574,10 @@ struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue,
        lockdep_set_class(&sch->busylock,
                          dev->qdisc_tx_busylock ?: &qdisc_tx_busylock);
 
+       seqcount_init(&sch->running);
+       lockdep_set_class(&sch->running,
+                         dev->qdisc_running_key ?: &qdisc_running_key);
+
        sch->ops = ops;
        sch->enqueue = ops->enqueue;
        sch->dequeue = ops->dequeue;
@@ -807,7 +779,7 @@ void dev_activate(struct net_device *dev)
                transition_one_qdisc(dev, dev_ingress_queue(dev), NULL);
 
        if (need_watchdog) {
-               dev->trans_start = jiffies;
+               netif_trans_update(dev);
                dev_watchdog_up(dev);
        }
 }