sched: refactor NOLOCK qdiscs
Subsystems (3)
Paolo Abeni
maintainercover sched: refactor NOLOCK qdiscs
With the introduction of NOLOCK qdiscs, pfifo_fast performances in the uncontended scenario degraded measurably, especially after the commit eb82a9944792 ("net: sched, fix OOO packets with pfifo_fast").
This series restore the pfifo_fast performances in such scenario back the previous level, mainly reducing the number of atomic operations required to perform the qdisc_run() call. Even performances in the contended scenario increase measurably.
Note: This series is on top of:
sched: manipulate __QDISC_STATE_RUNNING in qdisc_run_* helpers
Paolo Abeni (2):
sched: replace __QDISC_STATE_RUNNING bit with a spin lock
pfifo_fast: drop unneeded additional lock on dequeue
include/linux/skb_array.h | 5 +++++ include/net/sch_generic.h | 10 +++++----- net/sched/sch_generic.c | 15 +++++++++++++-- 3 files changed, 23 insertions(+), 7 deletions(-)
Signature · 1 line
2.14.3
Paolo Abeni
maintainerPATCH 1/2 sched: replace __QDISC_STATE_RUNNING bit with a spin lock
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.
--- include/net/sch_generic.h | 10 +++++----- net/sched/sch_generic.c | 11 +++++++++++ 2 files changed, 16 insertions(+), 5 deletions(-)
include/net/sch_generic.h +5 -5
@@ -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)
net/sched/sch_generic.c +11 -0
@@ -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); } }
Signature · 1 line
2.14.3
Paolo Abeni
maintainerPATCH 2/2 pfifo_fast: drop unneeded additional lock on dequeue
In reply to Paolo AbeniAfter the previous patch, for NOLOCK qdiscs, q->seqlock is always held when the dequeue() is invoked, we can drop any additional locking to protect such operation.
--- include/linux/skb_array.h | 5 +++++ net/sched/sch_generic.c | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-)
include/linux/skb_array.h +5 -0
@@ -97,6 +97,11 @@ static inline bool skb_array_empty_any(struct skb_array *a)return ptr_ring_empty_any (&a->ring); } +static inline struct sk_buff *__skb_array_consume (struct skb_array *a) +{ +return __ptr_ring_consume (&a->ring); +} +static inline struct sk_buff *skb_array_consume (struct skb_array *a) {return ptr_ring_consume (&a->ring);
net/sched/sch_generic.c +2 -2
@@ -656,7 +656,7 @@ static struct sk_buff *pfifo_fast_dequeue(struct Qdisc *qdisc)if (__skb_array_empty (q))continue ; - skb =skb_array_consume_bh (q); + skb =__skb_array_consume (q); }if (likely (skb)) {qdisc_qstats_cpu_backlog_dec (qdisc, skb);
@@ -697,7 +697,7 @@ static void pfifo_fast_reset(struct Qdisc *qdisc)if (!q->ring.queue)continue ; -while ((skb =skb_array_consume_bh (q)) !=NULL ) +while ((skb =__skb_array_consume (q)) !=NULL )kfree_skb (skb); }
Signature · 1 line
2.14.3
Re: [PATCH net-next 2/2] pfifo_fast: drop unneeded additional lock on dequeue
Quoted 9 lines · Paolo Abeni
After the previous patch, for NOLOCK qdiscs,
q->seqlockis always held when thedequeue()is invoked, we can drop any additional locking to protect such operation.Signed-off-by Paolo Abeni <[email protected]>--- include/linux/skb_array.h | 5 +++++ net/sched/sch_generic.c | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-)
Is the seqlock taken during qdisc_change_tx_queue_len? We need to prevent that racing with dequeue.
Quoted 40 lines
include/linux/skb_array.h +5
-0@@ -97,6 +97,11 @@ static inline bool skb_array_empty_any(struct skb_array *a)return ptr_ring_empty_any (&a->ring); } +static inline struct sk_buff *__skb_array_consume (struct skb_array *a) +{ +return __ptr_ring_consume (&a->ring); +} +static inline struct sk_buff *skb_array_consume (struct skb_array *a) {return ptr_ring_consume (&a->ring);net/sched/sch_generic.c +2
-2@@ -656,7 +656,7 @@ static struct sk_buff *pfifo_fast_dequeue(struct Qdisc *qdisc)if (__skb_array_empty (q))continue ; - skb =skb_array_consume_bh (q); + skb =__skb_array_consume (q); }if (likely (skb)) {qdisc_qstats_cpu_backlog_dec (qdisc, skb);@@ -697,7 +697,7 @@ static void pfifo_fast_reset(struct Qdisc *qdisc)if (!q->ring.queue)continue ; -while ((skb =skb_array_consume_bh (q)) !=NULL ) +while ((skb =__skb_array_consume (q)) !=NULL )kfree_skb (skb); }Signature · 1 line
2.14.3
Paolo Abeni
maintainerRe: [PATCH net-next 2/2] pfifo_fast: drop unneeded additional lock on dequeue
Quoted 5 lines · Michael S. Tsirkin
Quoted 4 lines · Paolo Abeni
After the previous patch, for NOLOCK qdiscs,
q->seqlockis always held when thedequeue()is invoked, we can drop any additional locking to protect such operation.
Quoted 8 lines
Quoted 5 lines
Signed-off-by Paolo Abeni <[email protected]>--- include/linux/skb_array.h | 5 +++++ net/sched/sch_generic.c | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-)Is the seqlock taken during
qdisc_change_tx_queue_len? We need to prevent that racing with dequeue.
Thanks for the head-up! I missed that code-path.
I'll add the lock in qdisc_change_tx_queue_len() in v2.
Thanks you,
Paolo
Paolo Abeni
maintainerRe: [PATCH net-next 2/2] pfifo_fast: drop unneeded additional lock on dequeue
Quoted 12 lines · Paolo Abeni
Quoted 11 lines · Michael S. Tsirkin
Quoted 9 lines · Paolo Abeni
After the previous patch, for NOLOCK qdiscs,
q->seqlockis always held when thedequeue()is invoked, we can drop any additional locking to protect such operation.Signed-off-by Paolo Abeni <[email protected]>--- include/linux/skb_array.h | 5 +++++ net/sched/sch_generic.c | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-)
Quoted 6 lines
Quoted 2 lines
Is the seqlock taken during
qdisc_change_tx_queue_len? We need to prevent that racing with dequeue.Thanks for the head-up! I missed that code-path.
I'll add the lock in
qdisc_change_tx_queue_len()in v2.
Actually the lock is not needed in qdisc_change_tx_queue_len(): the device is deactivated before calling ops->change_tx_queue_len, so the latter can't race with ops->dequeue().
I think the current patch is safe.
Cheers,
Paolo
Re: [PATCH net-next 2/2] pfifo_fast: drop unneeded additional lock on dequeue
In reply to Paolo AbeniQuoted 15 lines · Paolo Abeni
Quoted 5 lines · Michael S. Tsirkin
Quoted 4 lines · Paolo Abeni
After the previous patch, for NOLOCK qdiscs,
q->seqlockis always held when thedequeue()is invoked, we can drop any additional locking to protect such operation.Quoted 8 lines
Quoted 5 lines
Signed-off-by Paolo Abeni <[email protected]>--- include/linux/skb_array.h | 5 +++++ net/sched/sch_generic.c | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-)Is the seqlock taken during
qdisc_change_tx_queue_len? We need to prevent that racing with dequeue.
Quoted 7 lines
Thanks for the head-up! I missed that code-path.
I'll add the lock in
qdisc_change_tx_queue_len()in v2.Thanks you,
Paolo
When you do, make sure locks nest consistently.
David Miller
maintainerFrom: Paolo Abeni <[email protected]>
Date: Tue, 15 May 2018 16:24:35 +0200
Quoted 4 lines
With the introduction of NOLOCK qdiscs,
pfifo_fastperformances in the uncontended scenario degraded measurably, especially after the commiteb82a9944792("net: sched, fix OOO packets withpfifo_fast").
Quoted 8 lines
This series restore the
pfifo_fastperformances in such scenario back the previous level, mainly reducing the number of atomic operations required to perform theqdisc_run()call. Even performances in the contended scenario increase measurably.Note: This series is on top of:
sched: manipulate
__QDISC_STATE_RUNNINGin qdisc_run_* helpers
Series applied, thank you.