diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-02-23 00:40:54 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-02-23 00:40:54 +0000 |
commit | 529c593ece216e4aaffd36bd940cb94f1fa63129 (patch) | |
tree | 78f1c0b805f5656aa7b0417a043c5346f700a2cf /net/sched | |
parent | 0bd079751d25808d1972baee5c4eaa1db2227257 (diff) |
Merge with 2.3.43. I did ignore all modifications to the qlogicisp.c
driver due to the Origin A64 hacks.
Diffstat (limited to 'net/sched')
-rw-r--r-- | net/sched/sch_atm.c | 39 | ||||
-rw-r--r-- | net/sched/sch_cbq.c | 7 | ||||
-rw-r--r-- | net/sched/sch_generic.c | 216 | ||||
-rw-r--r-- | net/sched/sch_tbf.c | 4 | ||||
-rw-r--r-- | net/sched/sch_teql.c | 29 |
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; } |