sched: replace __QDISC_STATE_RUNNING bit with a spin lock
authorPaolo Abeni <pabeni@redhat.com>
Tue, 15 May 2018 14:24:36 +0000 (16:24 +0200)
committerDavid S. Miller <davem@davemloft.net>
Thu, 17 May 2018 16:46:54 +0000 (12:46 -0400)
So that we can use lockdep on it.
The newly introduced sequence lock has the same scope of busylock,
so it shares the same lockdep annotation, but it's only used for
NOLOCK qdiscs.

With this changeset we acquire such lock in the control path around
flushing operation (qdisc reset), to allow more NOLOCK qdisc perf
improvement in the next patch.

Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/sch_generic.h
net/sched/sch_generic.c

index 4d2b37226e75c495b58cdf5b6dceb88336989321..98c10a28cd01c39ebeeb3d0090f15537f2aec429 100644 (file)
@@ -30,7 +30,6 @@ struct qdisc_rate_table {
 enum qdisc_state_t {
        __QDISC_STATE_SCHED,
        __QDISC_STATE_DEACTIVATED,
-       __QDISC_STATE_RUNNING,
 };
 
 struct qdisc_size_table {
@@ -102,6 +101,7 @@ struct Qdisc {
        refcount_t              refcnt;
 
        spinlock_t              busylock ____cacheline_aligned_in_smp;
+       spinlock_t              seqlock;
 };
 
 static inline void qdisc_refcount_inc(struct Qdisc *qdisc)
@@ -111,17 +111,17 @@ static inline void qdisc_refcount_inc(struct Qdisc *qdisc)
        refcount_inc(&qdisc->refcnt);
 }
 
-static inline bool qdisc_is_running(const struct Qdisc *qdisc)
+static inline bool qdisc_is_running(struct Qdisc *qdisc)
 {
        if (qdisc->flags & TCQ_F_NOLOCK)
-               return test_bit(__QDISC_STATE_RUNNING, &qdisc->state);
+               return spin_is_locked(&qdisc->seqlock);
        return (raw_read_seqcount(&qdisc->running) & 1) ? true : false;
 }
 
 static inline bool qdisc_run_begin(struct Qdisc *qdisc)
 {
        if (qdisc->flags & TCQ_F_NOLOCK) {
-               if (test_and_set_bit(__QDISC_STATE_RUNNING, &qdisc->state))
+               if (!spin_trylock(&qdisc->seqlock))
                        return false;
        } else if (qdisc_is_running(qdisc)) {
                return false;
@@ -138,7 +138,7 @@ static inline void qdisc_run_end(struct Qdisc *qdisc)
 {
        write_seqcount_end(&qdisc->running);
        if (qdisc->flags & TCQ_F_NOLOCK)
-               clear_bit(__QDISC_STATE_RUNNING, &qdisc->state);
+               spin_unlock(&qdisc->seqlock);
 }
 
 static inline bool qdisc_may_bulk(const struct Qdisc *qdisc)
index ff3ce71aec9375a5b33b88e124f7a2741b764fe5..a126f16bc30b9c753800fc4e186d012b4f5c9ae5 100644 (file)
@@ -858,6 +858,11 @@ struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue,
        lockdep_set_class(&sch->busylock,
                          dev->qdisc_tx_busylock ?: &qdisc_tx_busylock);
 
+       /* seqlock has the same scope of busylock, for NOLOCK qdisc */
+       spin_lock_init(&sch->seqlock);
+       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);
@@ -1097,6 +1102,10 @@ static void dev_deactivate_queue(struct net_device *dev,
 
        qdisc = rtnl_dereference(dev_queue->qdisc);
        if (qdisc) {
+               bool nolock = qdisc->flags & TCQ_F_NOLOCK;
+
+               if (nolock)
+                       spin_lock_bh(&qdisc->seqlock);
                spin_lock_bh(qdisc_lock(qdisc));
 
                if (!(qdisc->flags & TCQ_F_BUILTIN))
@@ -1106,6 +1115,8 @@ static void dev_deactivate_queue(struct net_device *dev,
                qdisc_reset(qdisc);
 
                spin_unlock_bh(qdisc_lock(qdisc));
+               if (nolock)
+                       spin_unlock_bh(&qdisc->seqlock);
        }
 }