summaryrefslogtreecommitdiffstats
path: root/net/sched/sch_atm.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/sched/sch_atm.c')
-rw-r--r--net/sched/sch_atm.c39
1 files changed, 36 insertions, 3 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 */