Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: sched: avoid costly atomic operation in fq_dequeue()

Standard qdisc API to setup a timer implies an atomic operation on every
packet dequeue : qdisc_unthrottled()

It turns out this is not really needed for FQ, as FQ has no concept of
global qdisc throttling, being a qdisc handling many different flows,
some of them can be throttled, while others are not.

Fix is straightforward : add a 'bool throttle' to
qdisc_watchdog_schedule_ns(), and remove calls to qdisc_unthrottled()
in sch_fq.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Eric Dumazet and committed by
David S. Miller
f2600cf0 681d2421

+9 -9
+2 -2
include/net/pkt_sched.h
··· 65 65 }; 66 66 67 67 void qdisc_watchdog_init(struct qdisc_watchdog *wd, struct Qdisc *qdisc); 68 - void qdisc_watchdog_schedule_ns(struct qdisc_watchdog *wd, u64 expires); 68 + void qdisc_watchdog_schedule_ns(struct qdisc_watchdog *wd, u64 expires, bool throttle); 69 69 70 70 static inline void qdisc_watchdog_schedule(struct qdisc_watchdog *wd, 71 71 psched_time_t expires) 72 72 { 73 - qdisc_watchdog_schedule_ns(wd, PSCHED_TICKS2NS(expires)); 73 + qdisc_watchdog_schedule_ns(wd, PSCHED_TICKS2NS(expires), true); 74 74 } 75 75 76 76 void qdisc_watchdog_cancel(struct qdisc_watchdog *wd);
+3 -2
net/sched/sch_api.c
··· 594 594 } 595 595 EXPORT_SYMBOL(qdisc_watchdog_init); 596 596 597 - void qdisc_watchdog_schedule_ns(struct qdisc_watchdog *wd, u64 expires) 597 + void qdisc_watchdog_schedule_ns(struct qdisc_watchdog *wd, u64 expires, bool throttle) 598 598 { 599 599 if (test_bit(__QDISC_STATE_DEACTIVATED, 600 600 &qdisc_root_sleeping(wd->qdisc)->state)) 601 601 return; 602 602 603 - qdisc_throttled(wd->qdisc); 603 + if (throttle) 604 + qdisc_throttled(wd->qdisc); 604 605 605 606 hrtimer_start(&wd->timer, 606 607 ns_to_ktime(expires),
+2 -4
net/sched/sch_fq.c
··· 377 377 if (time_after(jiffies, f->age + q->flow_refill_delay)) 378 378 f->credit = max_t(u32, f->credit, q->quantum); 379 379 q->inactive_flows--; 380 - qdisc_unthrottled(sch); 381 380 } 382 381 383 382 /* Note: this overwrites f->age */ ··· 384 385 385 386 if (unlikely(f == &q->internal)) { 386 387 q->stat_internal_packets++; 387 - qdisc_unthrottled(sch); 388 388 } 389 389 sch->q.qlen++; 390 390 ··· 431 433 if (!head->first) { 432 434 if (q->time_next_delayed_flow != ~0ULL) 433 435 qdisc_watchdog_schedule_ns(&q->watchdog, 434 - q->time_next_delayed_flow); 436 + q->time_next_delayed_flow, 437 + false); 435 438 return NULL; 436 439 } 437 440 } ··· 494 495 } 495 496 out: 496 497 qdisc_bstats_update(sch, skb); 497 - qdisc_unthrottled(sch); 498 498 return skb; 499 499 } 500 500
+2 -1
net/sched/sch_tbf.c
··· 268 268 } 269 269 270 270 qdisc_watchdog_schedule_ns(&q->watchdog, 271 - now + max_t(long, -toks, -ptoks)); 271 + now + max_t(long, -toks, -ptoks), 272 + true); 272 273 273 274 /* Maybe we have a shorter packet in the queue, 274 275 which can be sent now. It sounds cool,