From: Jiri Pirko <jpirko@redhat.com> Date: Mon, 22 Dec 2008 15:38:54 +0100 Subject: [net] add preemption point in qdisc_run Message-id: 20081222153854.56c180d8@psychotron.englab.brq.redhat.com O-Subject: [RHEL5.4 patch] BZ471398 net: add preemption point in qdisc_run Bugzilla: 471398 RH-Acked-by: Michal Schmidt <mschmidt@redhat.com> RH-Acked-by: Neil Horman <nhorman@redhat.com> RH-Acked-by: Herbert Xu <herbert.xu@redhat.com> CVE: CVE-2008-5713 BZ471398 https://bugzilla.redhat.com/show_bug.cgi?id=471398 Description: The qdisc_run loop is currently unbounded and runs entirely in a softirq. This is bad as it may create an unbounded softirq run. This patch fixes this by calling need_resched and breaking out if necessary. It also adds a break out if the jiffies value changes since that would indicate we've been transmitting for too long which starves other softirqs. Upstream: http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=2ba2506ca7ca62c56edaa334b0fe61eb5eab6ab0 Brew: https://brewweb.devel.redhat.com/taskinfo?taskID=1624510 Test: Booted and tested with reproducer on nec-em8.rhts.bos.redhat.com (16cpu, x86_64). Used hp-ml370g4-01.rhts.bos.redhat.com as a "netserver". With -92 kernel I was able to trigger the soft lookup safely like this: ./np hp-ml370g4-01.rhts.bos.redhat.com 40 With patched kernel I was unable to trigger the soft lookup even after few reproducer runs. Jirka diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 88c6a99..d0df5b5 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -183,11 +183,25 @@ requeue: void __qdisc_run(struct net_device *dev) { + unsigned long start_time = jiffies; + if (unlikely(dev->qdisc == &noop_qdisc)) goto out; - while (qdisc_restart(dev) < 0 && !netif_queue_stopped(dev)) - /* NOTHING */; + while (qdisc_restart(dev) < 0) { + if (netif_queue_stopped(dev)) + break; + + /* + * Postpone processing if + * 1. another process needs the CPU; + * 2. we've been doing it for too long. + */ + if (need_resched() || jiffies != start_time) { + netif_schedule(dev); + break; + } + } out: clear_bit(__LINK_STATE_QDISC_RUNNING, &dev->state);