summaryrefslogtreecommitdiffstats
path: root/net/sched
diff options
context:
space:
mode:
Diffstat (limited to 'net/sched')
-rw-r--r--net/sched/sch_atm.c39
-rw-r--r--net/sched/sch_cbq.c7
-rw-r--r--net/sched/sch_generic.c216
-rw-r--r--net/sched/sch_tbf.c4
-rw-r--r--net/sched/sch_teql.c29
5 files changed, 120 insertions, 175 deletions
diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c
index 79901319d..e81541cea 100644
--- a/net/sched/sch_atm.c
+++ b/net/sched/sch_atm.c
@@ -1,6 +1,6 @@
/* net/sched/sch_atm.c - ATM VC selection "queueing discipline" */
-/* Written 1998,1999 by Werner Almesberger, EPFL ICA */
+/* Written 1998-2000 by Werner Almesberger, EPFL ICA */
#include <linux/config.h>
@@ -56,12 +56,14 @@ extern struct socket *sockfd_lookup(int fd, int *err); /* @@@ fix this */
#define PRIV(sch) ((struct atm_qdisc_data *) (sch)->data)
+#define VCC2FLOW(vcc) ((struct atm_flow_data *) ((vcc)->user_back))
struct atm_flow_data {
struct Qdisc *q; /* FIFO, TBF, etc. */
struct tcf_proto *filter_list;
struct atm_vcc *vcc; /* VCC; NULL if VCC is closed */
+ void (*old_pop)(struct atm_vcc *vcc,struct sk_buff *skb); /* chaining */
struct socket *sock; /* for closing */
u32 classid; /* x:y type ID */
int ref; /* reference count */
@@ -133,7 +135,7 @@ static struct Qdisc *atm_tc_leaf(struct Qdisc *sch,unsigned long cl)
static unsigned long atm_tc_get(struct Qdisc *sch,u32 classid)
{
- struct atm_qdisc_data *p = PRIV(sch);
+ struct atm_qdisc_data *p __attribute__((unused)) = PRIV(sch);
struct atm_flow_data *flow;
DPRINTK("atm_tc_get(sch %p,[qdisc %p],classid %x)\n",sch,p,classid);
@@ -184,6 +186,7 @@ static void atm_tc_put(struct Qdisc *sch, unsigned long cl)
}
if (flow->sock) {
DPRINTK("atm_tc_put: f_count %d\n",file_count(flow->sock->file));
+ flow->vcc->pop = flow->old_pop;
sockfd_put(flow->sock);
}
if (flow->excess) atm_tc_put(sch,(unsigned long) flow->excess);
@@ -195,6 +198,13 @@ static void atm_tc_put(struct Qdisc *sch, unsigned long cl)
}
+static void sch_atm_pop(struct atm_vcc *vcc,struct sk_buff *skb)
+{
+ VCC2FLOW(vcc)->old_pop(vcc,skb);
+ mark_bh(NET_BH); /* may allow to send more */
+}
+
+
static int atm_tc_change(struct Qdisc *sch, u32 classid, u32 parent,
struct rtattr **tca, unsigned long *arg)
{
@@ -289,7 +299,10 @@ static int atm_tc_change(struct Qdisc *sch, u32 classid, u32 parent,
DPRINTK("atm_tc_change: qdisc %p\n",flow->q);
flow->sock = sock;
flow->vcc = ATM_SD(sock); /* speedup */
+ flow->vcc->user_back = flow;
DPRINTK("atm_tc_change: vcc %p\n",flow->vcc);
+ flow->old_pop = flow->vcc->pop;
+ flow->vcc->pop = sch_atm_pop;
flow->classid = classid;
flow->ref = 1;
flow->excess = excess;
@@ -440,6 +453,10 @@ static struct sk_buff *atm_tc_dequeue(struct Qdisc *sch)
* little bursts. Otherwise, it may ... @@@
*/
while ((skb = flow->q->dequeue(flow->q))) {
+ if (!atm_may_send(flow->vcc,skb->truesize)) {
+ flow->q->ops->requeue(skb,flow->q);
+ break;
+ }
sch->q.qlen--;
D2PRINTK("atm_tc_deqeueue: sending on class %p\n",flow);
/* remove any LL header somebody else has attached */
@@ -468,6 +485,22 @@ static struct sk_buff *atm_tc_dequeue(struct Qdisc *sch)
}
+static int atm_tc_requeue(struct sk_buff *skb,struct Qdisc *sch)
+{
+ struct atm_qdisc_data *p = PRIV(sch);
+ int ret;
+
+ D2PRINTK("atm_tc_requeue(skb %p,sch %p,[qdisc %p])\n",skb,sch,p);
+ ret = p->link.q->ops->requeue(skb,p->link.q);
+ if (!ret) sch->q.qlen++;
+ else {
+ sch->stats.drops++;
+ p->link.stats.drops++;
+ }
+ return ret;
+}
+
+
static int atm_tc_drop(struct Qdisc *sch)
{
struct atm_qdisc_data *p = PRIV(sch);
@@ -616,7 +649,7 @@ struct Qdisc_ops atm_qdisc_ops =
atm_tc_enqueue, /* enqueue */
atm_tc_dequeue, /* dequeue */
- atm_tc_enqueue, /* requeue; we're cheating a little */
+ atm_tc_requeue, /* requeue */
atm_tc_drop, /* drop */
atm_tc_init, /* init */
diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c
index 0308a02f1..d3c32be20 100644
--- a/net/sched/sch_cbq.c
+++ b/net/sched/sch_cbq.c
@@ -597,8 +597,9 @@ static void cbq_ovl_drop(struct cbq_class *cl)
static void cbq_watchdog(unsigned long arg)
{
struct Qdisc *sch = (struct Qdisc*)arg;
+
sch->flags &= ~TCQ_F_THROTTLED;
- qdisc_wakeup(sch->dev);
+ netif_schedule(sch->dev);
}
static unsigned long cbq_undelay_prio(struct cbq_sched_data *q, int prio)
@@ -666,7 +667,7 @@ static void cbq_undelay(unsigned long arg)
}
sch->flags &= ~TCQ_F_THROTTLED;
- qdisc_wakeup(sch->dev);
+ netif_schedule(sch->dev);
}
@@ -1052,7 +1053,7 @@ cbq_dequeue(struct Qdisc *sch)
if (sch->q.qlen) {
sch->stats.overlimits++;
- if (q->wd_expires && !sch->dev->tbusy) {
+ if (q->wd_expires && !test_bit(LINK_STATE_XOFF, &sch->dev->state)) {
long delay = PSCHED_US2JIFFIE(q->wd_expires);
del_timer(&q->wd_timer);
if (delay <= 0)
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index 65e4c3e36..2a9f9e69e 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -34,9 +34,6 @@
/* Main transmission queue. */
-struct Qdisc_head qdisc_head = { &qdisc_head, &qdisc_head };
-spinlock_t qdisc_runqueue_lock = SPIN_LOCK_UNLOCKED;
-
/* Main qdisc structure lock.
However, modifications
@@ -55,11 +52,7 @@ spinlock_t qdisc_runqueue_lock = SPIN_LOCK_UNLOCKED;
*/
rwlock_t qdisc_tree_lock = RW_LOCK_UNLOCKED;
-/* Anti deadlock rules:
-
- qdisc_runqueue_lock protects main transmission list qdisc_head.
- Run list is accessed only under this spinlock.
-
+/*
dev->queue_lock serializes queue accesses for this device
AND dev->qdisc pointer itself.
@@ -67,10 +60,6 @@ rwlock_t qdisc_tree_lock = RW_LOCK_UNLOCKED;
dev->queue_lock and dev->xmit_lock are mutually exclusive,
if one is grabbed, another must be free.
-
- qdisc_runqueue_lock may be requested under dev->queue_lock,
- but neither dev->queue_lock nor dev->xmit_lock may be requested
- under qdisc_runqueue_lock.
*/
@@ -99,17 +88,19 @@ int qdisc_restart(struct net_device *dev)
/* And release queue */
spin_unlock(&dev->queue_lock);
- if (netdev_nit)
- dev_queue_xmit_nit(skb, dev);
+ if (!test_bit(LINK_STATE_XOFF, &dev->state)) {
+ if (netdev_nit)
+ dev_queue_xmit_nit(skb, dev);
- if (dev->hard_start_xmit(skb, dev) == 0) {
- dev->xmit_lock_owner = -1;
- spin_unlock(&dev->xmit_lock);
+ if (dev->hard_start_xmit(skb, dev) == 0) {
+ dev->xmit_lock_owner = -1;
+ spin_unlock(&dev->xmit_lock);
- spin_lock(&dev->queue_lock);
- dev->qdisc->tx_last = jiffies;
- return -1;
+ spin_lock(&dev->queue_lock);
+ return -1;
+ }
}
+
/* Release the driver */
dev->xmit_lock_owner = -1;
spin_unlock(&dev->xmit_lock);
@@ -126,14 +117,10 @@ int qdisc_restart(struct net_device *dev)
if (dev->xmit_lock_owner == smp_processor_id()) {
kfree_skb(skb);
if (net_ratelimit())
- printk(KERN_DEBUG "Dead loop on virtual %s, fix it urgently!\n", dev->name);
+ printk(KERN_DEBUG "Dead loop on netdevice %s, fix it urgently!\n", dev->name);
return -1;
}
-
- /* Otherwise, packet is requeued
- and will be sent by the next net_bh run.
- */
- mark_bh(NET_BH);
+ netdev_rx_stat[smp_processor_id()].cpu_collision++;
}
/* Device kicked us out :(
@@ -147,139 +134,68 @@ int qdisc_restart(struct net_device *dev)
*/
q->ops->requeue(skb, q);
- return -1;
+ netif_schedule(dev);
+ return 1;
}
return q->q.qlen;
}
-static __inline__ void
-qdisc_stop_run(struct Qdisc *q)
+static void dev_watchdog(unsigned long arg)
{
- q->h.forw->back = q->h.back;
- q->h.back->forw = q->h.forw;
- q->h.forw = NULL;
-}
+ struct net_device *dev = (struct net_device *)arg;
+
+ spin_lock(&dev->xmit_lock);
+ if (dev->qdisc != &noop_qdisc) {
+ if (test_bit(LINK_STATE_XOFF, &dev->state) &&
+ (jiffies - dev->trans_start) > dev->watchdog_timeo) {
+ printk(KERN_INFO "NETDEV WATCHDOG: %s: transmit timed out\n", dev->name);
+ dev->tx_timeout(dev);
+ }
+ if (!del_timer(&dev->watchdog_timer))
+ dev_hold(dev);
-extern __inline__ void
-qdisc_continue_run(struct Qdisc *q)
-{
- if (!qdisc_on_runqueue(q) && q->dev) {
- q->h.forw = &qdisc_head;
- q->h.back = qdisc_head.back;
- qdisc_head.back->forw = &q->h;
- qdisc_head.back = &q->h;
+ dev->watchdog_timer.expires = jiffies + dev->watchdog_timeo;
+ add_timer(&dev->watchdog_timer);
}
+ spin_unlock(&dev->xmit_lock);
+
+ dev_put(dev);
}
-static __inline__ int
-qdisc_init_run(struct Qdisc_head *lh)
+static void dev_watchdog_init(struct net_device *dev)
{
- if (qdisc_head.forw != &qdisc_head) {
- *lh = qdisc_head;
- lh->forw->back = lh;
- lh->back->forw = lh;
- qdisc_head.forw = &qdisc_head;
- qdisc_head.back = &qdisc_head;
- return 1;
- }
- return 0;
+ init_timer(&dev->watchdog_timer);
+ dev->watchdog_timer.data = (unsigned long)dev;
+ dev->watchdog_timer.function = dev_watchdog;
}
-/* Scan transmission queue and kick devices.
-
- Deficiency: slow devices (ppp) and fast ones (100Mb ethernet)
- share one queue. This means that if we have a lot of loaded ppp channels,
- we will scan a long list on every 100Mb EOI.
- I have no idea how to solve it using only "anonymous" Linux mark_bh().
-
- To change queue from device interrupt? Ough... only not this...
-
- This function is called only from net_bh.
- */
-
-void qdisc_run_queues(void)
+static void dev_watchdog_up(struct net_device *dev)
{
- struct Qdisc_head lh, *h;
-
- spin_lock(&qdisc_runqueue_lock);
- if (!qdisc_init_run(&lh))
- goto out;
-
- while ((h = lh.forw) != &lh) {
- int res;
- struct net_device *dev;
- struct Qdisc *q = (struct Qdisc*)h;
-
- qdisc_stop_run(q);
-
- dev = q->dev;
-
- res = -1;
- if (spin_trylock(&dev->queue_lock)) {
- spin_unlock(&qdisc_runqueue_lock);
- while (!dev->tbusy && (res = qdisc_restart(dev)) < 0)
- /* NOTHING */;
- spin_lock(&qdisc_runqueue_lock);
- spin_unlock(&dev->queue_lock);
- }
-
- /* If qdisc is not empty add it to the tail of list */
- if (res)
- qdisc_continue_run(dev->qdisc);
+ spin_lock_bh(&dev->xmit_lock);
+
+ if (dev->tx_timeout) {
+ if (dev->watchdog_timeo <= 0)
+ dev->watchdog_timeo = 5*HZ;
+ if (!del_timer(&dev->watchdog_timer))
+ dev_hold(dev);
+ dev->watchdog_timer.expires = jiffies + dev->watchdog_timeo;
+ add_timer(&dev->watchdog_timer);
}
-out:
- spin_unlock(&qdisc_runqueue_lock);
+ spin_unlock_bh(&dev->xmit_lock);
}
-/* Periodic watchdog timer to recover from hard/soft device bugs. */
-
-static void dev_do_watchdog(unsigned long dummy);
-
-static struct timer_list dev_watchdog =
- { NULL, NULL, 0L, 0L, &dev_do_watchdog };
-
-/* This function is called only from timer */
-
-static void dev_do_watchdog(unsigned long dummy)
+static void dev_watchdog_down(struct net_device *dev)
{
- struct Qdisc_head lh, *h;
+ spin_lock_bh(&dev->xmit_lock);
- if (!spin_trylock(&qdisc_runqueue_lock)) {
- /* No hurry with watchdog. */
- mod_timer(&dev_watchdog, jiffies + HZ/10);
- return;
+ if (dev->tx_timeout) {
+ if (del_timer(&dev->watchdog_timer))
+ __dev_put(dev);
}
-
- if (!qdisc_init_run(&lh))
- goto out;
-
- while ((h = lh.forw) != &lh) {
- struct net_device *dev;
- struct Qdisc *q = (struct Qdisc*)h;
-
- qdisc_stop_run(q);
-
- dev = q->dev;
-
- if (spin_trylock(&dev->queue_lock)) {
- spin_unlock(&qdisc_runqueue_lock);
- q = dev->qdisc;
- if (dev->tbusy && jiffies - q->tx_last > q->tx_timeo)
- qdisc_restart(dev);
- spin_lock(&qdisc_runqueue_lock);
- spin_unlock(&dev->queue_lock);
- }
-
- qdisc_continue_run(dev->qdisc);
- }
-
-out:
- mod_timer(&dev_watchdog, jiffies + 5*HZ);
- spin_unlock(&qdisc_runqueue_lock);
+ spin_unlock_bh(&dev->xmit_lock);
}
-
/* "NOOP" scheduler: the best scheduler, recommended for all interfaces
under all circumstances. It is difficult to invent anything faster or
cheaper.
@@ -321,7 +237,6 @@ struct Qdisc_ops noop_qdisc_ops =
struct Qdisc noop_qdisc =
{
- { NULL },
noop_enqueue,
noop_dequeue,
TCQ_F_BUILTIN,
@@ -344,7 +259,6 @@ struct Qdisc_ops noqueue_qdisc_ops =
struct Qdisc noqueue_qdisc =
{
- { NULL },
NULL,
noop_dequeue,
TCQ_F_BUILTIN,
@@ -476,6 +390,7 @@ struct Qdisc * qdisc_create_dflt(struct net_device *dev, struct Qdisc_ops *ops)
void qdisc_reset(struct Qdisc *qdisc)
{
struct Qdisc_ops *ops = qdisc->ops;
+
if (ops->reset)
ops->reset(qdisc);
}
@@ -540,15 +455,10 @@ void dev_activate(struct net_device *dev)
}
spin_lock_bh(&dev->queue_lock);
- spin_lock(&qdisc_runqueue_lock);
if ((dev->qdisc = dev->qdisc_sleeping) != &noqueue_qdisc) {
- dev->qdisc->tx_timeo = 5*HZ;
- dev->qdisc->tx_last = jiffies - dev->qdisc->tx_timeo;
- if (!del_timer(&dev_watchdog))
- dev_watchdog.expires = jiffies + 5*HZ;
- add_timer(&dev_watchdog);
+ dev->trans_start = jiffies;
+ dev_watchdog_up(dev);
}
- spin_unlock(&qdisc_runqueue_lock);
spin_unlock_bh(&dev->queue_lock);
}
@@ -557,17 +467,20 @@ void dev_deactivate(struct net_device *dev)
struct Qdisc *qdisc;
spin_lock_bh(&dev->queue_lock);
- spin_lock(&qdisc_runqueue_lock);
qdisc = dev->qdisc;
dev->qdisc = &noop_qdisc;
qdisc_reset(qdisc);
- if (qdisc_on_runqueue(qdisc))
- qdisc_stop_run(qdisc);
- spin_unlock(&qdisc_runqueue_lock);
spin_unlock_bh(&dev->queue_lock);
+ dev_watchdog_down(dev);
+
+ if (test_bit(LINK_STATE_SCHED, &dev->state)) {
+ current->policy |= SCHED_YIELD;
+ schedule();
+ }
+
spin_unlock_wait(&dev->xmit_lock);
}
@@ -580,6 +493,8 @@ void dev_init_scheduler(struct net_device *dev)
dev->qdisc_sleeping = &noop_qdisc;
dev->qdisc_list = NULL;
write_unlock(&qdisc_tree_lock);
+
+ dev_watchdog_init(dev);
}
void dev_shutdown(struct net_device *dev)
@@ -599,6 +514,7 @@ void dev_shutdown(struct net_device *dev)
}
#endif
BUG_TRAP(dev->qdisc_list == NULL);
+ BUG_TRAP(dev->watchdog_timer.prev == NULL);
dev->qdisc_list = NULL;
spin_unlock_bh(&dev->queue_lock);
write_unlock(&qdisc_tree_lock);
diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c
index 3a44f6dd7..2681d7129 100644
--- a/net/sched/sch_tbf.c
+++ b/net/sched/sch_tbf.c
@@ -186,7 +186,7 @@ static void tbf_watchdog(unsigned long arg)
struct Qdisc *sch = (struct Qdisc*)arg;
sch->flags &= ~TCQ_F_THROTTLED;
- qdisc_wakeup(sch->dev);
+ netif_schedule(sch->dev);
}
static struct sk_buff *
@@ -226,7 +226,7 @@ tbf_dequeue(struct Qdisc* sch)
return skb;
}
- if (!sch->dev->tbusy) {
+ if (!test_bit(LINK_STATE_XOFF, &sch->dev->state)) {
long delay = PSCHED_US2JIFFIE(max(-toks, -ptoks));
if (delay == 0)
diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c
index ede1e96cd..e576dbb11 100644
--- a/net/sched/sch_teql.c
+++ b/net/sched/sch_teql.c
@@ -126,10 +126,7 @@ teql_dequeue(struct Qdisc* sch)
struct net_device *m = dat->m->dev.qdisc->dev;
if (m) {
dat->m->slaves = sch;
- spin_lock(&m->queue_lock);
- m->tbusy = 0;
- qdisc_restart(m);
- spin_unlock(&m->queue_lock);
+ netif_wake_queue(m);
}
}
sch->q.qlen = dat->q.qlen + dat->m->dev.qdisc->q.qlen;
@@ -285,8 +282,6 @@ static int teql_master_xmit(struct sk_buff *skb, struct net_device *dev)
int len = skb->len;
struct sk_buff *skb_res = NULL;
- dev->tbusy = 1;
-
start = master->slaves;
restart:
@@ -301,23 +296,22 @@ restart:
if (slave->qdisc_sleeping != q)
continue;
- if (slave->tbusy) {
+ if (test_bit(LINK_STATE_XOFF, &slave->state) ||
+ test_bit(LINK_STATE_DOWN, &slave->state)) {
busy = 1;
continue;
}
- if (!qdisc_on_runqueue(q))
- qdisc_run(q);
-
switch (teql_resolve(skb, skb_res, slave)) {
case 0:
if (spin_trylock(&slave->xmit_lock)) {
slave->xmit_lock_owner = smp_processor_id();
- if (slave->hard_start_xmit(skb, slave) == 0) {
+ if (!test_bit(LINK_STATE_XOFF, &slave->state) &&
+ slave->hard_start_xmit(skb, slave) == 0) {
slave->xmit_lock_owner = -1;
spin_unlock(&slave->xmit_lock);
master->slaves = NEXT_SLAVE(q);
- dev->tbusy = 0;
+ netif_wake_queue(dev);
master->stats.tx_packets++;
master->stats.tx_bytes += len;
return 0;
@@ -325,12 +319,11 @@ restart:
slave->xmit_lock_owner = -1;
spin_unlock(&slave->xmit_lock);
}
- if (dev->tbusy)
+ if (test_bit(LINK_STATE_XOFF, &dev->state))
busy = 1;
break;
case 1:
master->slaves = NEXT_SLAVE(q);
- dev->tbusy = 0;
return 0;
default:
nores = 1;
@@ -344,9 +337,10 @@ restart:
goto restart;
}
- dev->tbusy = busy;
- if (busy)
+ if (busy) {
+ netif_stop_queue(dev);
return 1;
+ }
master->stats.tx_errors++;
drop:
@@ -393,13 +387,14 @@ static int teql_master_open(struct net_device *dev)
m->dev.mtu = mtu;
m->dev.flags = (m->dev.flags&~FMASK) | flags;
- m->dev.tbusy = 0;
+ netif_start_queue(&m->dev);
MOD_INC_USE_COUNT;
return 0;
}
static int teql_master_close(struct net_device *dev)
{
+ netif_stop_queue(dev);
MOD_DEC_USE_COUNT;
return 0;
}