net_sched: fix THROTTLED/RUNNING race
authorEric Dumazet <eric.dumazet@gmail.com>
Thu, 24 Mar 2011 07:13:14 +0000 (00:13 -0700)
committerDavid S. Miller <davem@davemloft.net>
Thu, 24 Mar 2011 07:13:14 +0000 (00:13 -0700)
commit fd245a4adb52 (net_sched: move TCQ_F_THROTTLED flag)
added a race.

qdisc_watchdog() is run from softirq, so special care should be taken or
we can lose one state transition (THROTTLED/RUNNING)

Prior to fd245a4adb52, we were manipulating q->flags (qdisc->flags &=
~TCQ_F_THROTTLED;) and this manipulation could only race with
qdisc_warn_nonwc().

Since we want to avoid atomic ops in qdisc fast path - it was the
meaning of commit 371121057607e (QDISC_STATE_RUNNING dont need atomic
bit ops) - fix is to move THROTTLE bit into 'state' field, this one
being manipulated with SMP and IRQ safe operations.

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/sch_generic.h

index a9505b6a18e3365249cf663363bac9e29a257266..b931f021d7ab4712f3fa553bef09229f5bf5aeb4 100644 (file)
@@ -25,6 +25,7 @@ struct qdisc_rate_table {
 enum qdisc_state_t {
        __QDISC_STATE_SCHED,
        __QDISC_STATE_DEACTIVATED,
+       __QDISC_STATE_THROTTLED,
 };
 
 /*
@@ -32,7 +33,6 @@ enum qdisc_state_t {
  */
 enum qdisc___state_t {
        __QDISC___STATE_RUNNING = 1,
-       __QDISC___STATE_THROTTLED = 2,
 };
 
 struct qdisc_size_table {
@@ -106,17 +106,17 @@ static inline void qdisc_run_end(struct Qdisc *qdisc)
 
 static inline bool qdisc_is_throttled(const struct Qdisc *qdisc)
 {
-       return (qdisc->__state & __QDISC___STATE_THROTTLED) ? true : false;
+       return test_bit(__QDISC_STATE_THROTTLED, &qdisc->state) ? true : false;
 }
 
 static inline void qdisc_throttled(struct Qdisc *qdisc)
 {
-       qdisc->__state |= __QDISC___STATE_THROTTLED;
+       set_bit(__QDISC_STATE_THROTTLED, &qdisc->state);
 }
 
 static inline void qdisc_unthrottled(struct Qdisc *qdisc)
 {
-       qdisc->__state &= ~__QDISC___STATE_THROTTLED;
+       clear_bit(__QDISC_STATE_THROTTLED, &qdisc->state);
 }
 
 struct Qdisc_class_ops {