net_sched: fix THROTTLED/RUNNING race

commit fd245a4adb (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 fd245a4adb, 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 3711210576 (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>
This commit is contained in:
Eric Dumazet 2011-03-24 00:13:14 -07:00 committed by David S. Miller
parent 8c64b4cc02
commit ef352e7cdf

View File

@ -25,6 +25,7 @@ struct qdisc_rate_table {
enum qdisc_state_t { enum qdisc_state_t {
__QDISC_STATE_SCHED, __QDISC_STATE_SCHED,
__QDISC_STATE_DEACTIVATED, __QDISC_STATE_DEACTIVATED,
__QDISC_STATE_THROTTLED,
}; };
/* /*
@ -32,7 +33,6 @@ enum qdisc_state_t {
*/ */
enum qdisc___state_t { enum qdisc___state_t {
__QDISC___STATE_RUNNING = 1, __QDISC___STATE_RUNNING = 1,
__QDISC___STATE_THROTTLED = 2,
}; };
struct qdisc_size_table { 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) 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) 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) static inline void qdisc_unthrottled(struct Qdisc *qdisc)
{ {
qdisc->__state &= ~__QDISC___STATE_THROTTLED; clear_bit(__QDISC_STATE_THROTTLED, &qdisc->state);
} }
struct Qdisc_class_ops { struct Qdisc_class_ops {