pkt_sched: Fix qdisc_watchdog() vs. dev_deactivate() race

dev_deactivate() can skip rescheduling of a qdisc by qdisc_watchdog()
or other timer calling netif_schedule() after dev_queue_deactivate().
We prevent this checking aliveness before scheduling the timer. Since
during deactivation the root qdisc is available only as qdisc_sleeping
additional accessor qdisc_root_sleeping() is created.

With feedback from Herbert Xu <herbert@gondor.apana.org.au>

Signed-off-by: Jarek Poplawski <jarkao2@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by Jarek Poplawski and committed by David S. Miller 2540e051 5e739d17

+13
+5
include/net/sch_generic.h
··· 193 193 return qdisc->dev_queue->qdisc; 194 194 } 195 195 196 + static inline struct Qdisc *qdisc_root_sleeping(struct Qdisc *qdisc) 197 + { 198 + return qdisc->dev_queue->qdisc_sleeping; 199 + } 200 + 196 201 /* The qdisc root lock is a mechanism by which to top level 197 202 * of a qdisc tree can be locked from any qdisc node in the 198 203 * forest. This allows changing the configuration of some
+4
net/sched/sch_api.c
··· 444 444 { 445 445 ktime_t time; 446 446 447 + if (test_bit(__QDISC_STATE_DEACTIVATED, 448 + &qdisc_root_sleeping(wd->qdisc)->state)) 449 + return; 450 + 447 451 wd->qdisc->flags |= TCQ_F_THROTTLED; 448 452 time = ktime_set(0, 0); 449 453 time = ktime_add_ns(time, PSCHED_US2NS(expires));
+4
net/sched/sch_cbq.c
··· 521 521 struct cbq_sched_data *q = qdisc_priv(cl->qdisc); 522 522 psched_tdiff_t delay = cl->undertime - q->now; 523 523 524 + if (test_bit(__QDISC_STATE_DEACTIVATED, 525 + &qdisc_root_sleeping(cl->qdisc)->state)) 526 + return; 527 + 524 528 if (!cl->delayed) { 525 529 psched_time_t sched = q->now; 526 530 ktime_t expires;