aboutsummaryrefslogtreecommitdiff
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.c213
1 files changed, 97 insertions, 116 deletions
diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c
index 33527341638..8449b337f9e 100644
--- a/net/sched/sch_atm.c
+++ b/net/sched/sch_atm.c
@@ -3,7 +3,9 @@
/* Written 1998-2000 by Werner Almesberger, EPFL ICA */
#include <linux/module.h>
+#include <linux/slab.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/skbuff.h>
@@ -14,8 +16,6 @@
#include <net/netlink.h>
#include <net/pkt_sched.h>
-extern struct socket *sockfd_lookup(int fd, int *err); /* @@@ fix this */
-
/*
* The ATM queuing discipline provides a framework for invoking classifiers
* (aka "filters"), which in turn select classes of this queuing discipline.
@@ -49,9 +49,9 @@ struct atm_flow_data {
struct socket *sock; /* for closing */
u32 classid; /* x:y type ID */
int ref; /* reference count */
- struct gnet_stats_basic bstats;
+ struct gnet_stats_basic_packed bstats;
struct gnet_stats_queue qstats;
- struct atm_flow_data *next;
+ struct list_head list;
struct atm_flow_data *excess; /* flow for excess traffic;
NULL to set CLP instead */
int hdr_len;
@@ -60,34 +60,23 @@ struct atm_flow_data {
struct atm_qdisc_data {
struct atm_flow_data link; /* unclassified skbs go here */
- struct atm_flow_data *flows; /* NB: "link" is also on this
+ struct list_head flows; /* NB: "link" is also on this
list */
- struct tasklet_struct task; /* requeue tasklet */
+ struct tasklet_struct task; /* dequeue tasklet */
};
/* ------------------------- Class/flow operations ------------------------- */
-static int find_flow(struct atm_qdisc_data *qdisc, struct atm_flow_data *flow)
-{
- struct atm_flow_data *walk;
-
- pr_debug("find_flow(qdisc %p,flow %p)\n", qdisc, flow);
- for (walk = qdisc->flows; walk; walk = walk->next)
- if (walk == flow)
- return 1;
- pr_debug("find_flow: not found\n");
- return 0;
-}
-
static inline struct atm_flow_data *lookup_flow(struct Qdisc *sch, u32 classid)
{
struct atm_qdisc_data *p = qdisc_priv(sch);
struct atm_flow_data *flow;
- for (flow = p->flows; flow; flow = flow->next)
+ list_for_each_entry(flow, &p->flows, list) {
if (flow->classid == classid)
- break;
- return flow;
+ return flow;
+ }
+ return NULL;
}
static int atm_tc_graft(struct Qdisc *sch, unsigned long arg,
@@ -98,11 +87,12 @@ static int atm_tc_graft(struct Qdisc *sch, unsigned long arg,
pr_debug("atm_tc_graft(sch %p,[qdisc %p],flow %p,new %p,old %p)\n",
sch, p, flow, new, old);
- if (!find_flow(p, flow))
+ if (list_empty(&flow->list))
return -EINVAL;
if (!new)
new = &noop_qdisc;
- *old = xchg(&flow->q, new);
+ *old = flow->q;
+ flow->q = new;
if (*old)
qdisc_reset(*old);
return 0;
@@ -144,25 +134,17 @@ static void atm_tc_put(struct Qdisc *sch, unsigned long cl)
{
struct atm_qdisc_data *p = qdisc_priv(sch);
struct atm_flow_data *flow = (struct atm_flow_data *)cl;
- struct atm_flow_data **prev;
pr_debug("atm_tc_put(sch %p,[qdisc %p],flow %p)\n", sch, p, flow);
if (--flow->ref)
return;
pr_debug("atm_tc_put: destroying\n");
- for (prev = &p->flows; *prev; prev = &(*prev)->next)
- if (*prev == flow)
- break;
- if (!*prev) {
- printk(KERN_CRIT "atm_tc_put: class %p not found\n", flow);
- return;
- }
- *prev = flow->next;
+ list_del_init(&flow->list);
pr_debug("atm_tc_put: qdisc %p\n", flow->q);
qdisc_destroy(flow->q);
- tcf_destroy_chain(flow->filter_list);
+ tcf_destroy_chain(&flow->filter_list);
if (flow->sock) {
- pr_debug("atm_tc_put: f_count %d\n",
+ pr_debug("atm_tc_put: f_count %ld\n",
file_count(flow->sock->file));
flow->vcc->pop = flow->old_pop;
sockfd_put(flow->sock);
@@ -259,7 +241,7 @@ static int atm_tc_change(struct Qdisc *sch, u32 classid, u32 parent,
sock = sockfd_lookup(fd, &error);
if (!sock)
return error; /* f_count++ */
- pr_debug("atm_tc_change: f_count %d\n", file_count(sock->file));
+ pr_debug("atm_tc_change: f_count %ld\n", file_count(sock->file));
if (sock->ops->family != PF_ATMSVC && sock->ops->family != PF_ATMPVC) {
error = -EPROTOTYPE;
goto err_out;
@@ -272,10 +254,6 @@ static int atm_tc_change(struct Qdisc *sch, u32 classid, u32 parent,
error = -EINVAL;
goto err_out;
}
- if (find_flow(p, flow)) {
- error = -EEXIST;
- goto err_out;
- }
} else {
int i;
unsigned long cl;
@@ -296,7 +274,7 @@ static int atm_tc_change(struct Qdisc *sch, u32 classid, u32 parent,
goto err_out;
}
flow->filter_list = NULL;
- flow->q = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, classid);
+ flow->q = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, classid);
if (!flow->q)
flow->q = &noop_qdisc;
pr_debug("atm_tc_change: qdisc %p\n", flow->q);
@@ -310,8 +288,7 @@ static int atm_tc_change(struct Qdisc *sch, u32 classid, u32 parent,
flow->classid = classid;
flow->ref = 1;
flow->excess = excess;
- flow->next = p->link.next;
- p->link.next = flow;
+ list_add(&flow->list, &p->link.list);
flow->hdr_len = hdr_len;
if (hdr)
memcpy(flow->hdr, hdr, hdr_len);
@@ -332,7 +309,7 @@ static int atm_tc_delete(struct Qdisc *sch, unsigned long arg)
struct atm_flow_data *flow = (struct atm_flow_data *)arg;
pr_debug("atm_tc_delete(sch %p,[qdisc %p],flow %p)\n", sch, p, flow);
- if (!find_flow(qdisc_priv(sch), flow))
+ if (list_empty(&flow->list))
return -EINVAL;
if (flow->filter_list || flow == &p->link)
return -EBUSY;
@@ -341,7 +318,7 @@ static int atm_tc_delete(struct Qdisc *sch, unsigned long arg)
* creation), and one for the reference held when calling delete.
*/
if (flow->ref < 2) {
- printk(KERN_ERR "atm_tc_delete: flow->ref == %d\n", flow->ref);
+ pr_err("atm_tc_delete: flow->ref == %d\n", flow->ref);
return -EINVAL;
}
if (flow->ref > 2)
@@ -358,12 +335,12 @@ static void atm_tc_walk(struct Qdisc *sch, struct qdisc_walker *walker)
pr_debug("atm_tc_walk(sch %p,[qdisc %p],walker %p)\n", sch, p, walker);
if (walker->stop)
return;
- for (flow = p->flows; flow; flow = flow->next) {
- if (walker->count >= walker->skip)
- if (walker->fn(sch, (unsigned long)flow, walker) < 0) {
- walker->stop = 1;
- break;
- }
+ list_for_each_entry(flow, &p->flows, list) {
+ if (walker->count >= walker->skip &&
+ walker->fn(sch, (unsigned long)flow, walker) < 0) {
+ walker->stop = 1;
+ break;
+ }
walker->count++;
}
}
@@ -382,16 +359,17 @@ static struct tcf_proto **atm_tc_find_tcf(struct Qdisc *sch, unsigned long cl)
static int atm_tc_enqueue(struct sk_buff *skb, struct Qdisc *sch)
{
struct atm_qdisc_data *p = qdisc_priv(sch);
- struct atm_flow_data *flow = NULL; /* @@@ */
+ struct atm_flow_data *flow;
struct tcf_result res;
int result;
int ret = NET_XMIT_POLICED;
pr_debug("atm_tc_enqueue(skb %p,sch %p,[qdisc %p])\n", skb, sch, p);
result = TC_POLICE_OK; /* be nice to gcc */
+ flow = NULL;
if (TC_H_MAJ(skb->priority) != sch->handle ||
- !(flow = (struct atm_flow_data *)atm_tc_get(sch, skb->priority)))
- for (flow = p->flows; flow; flow = flow->next)
+ !(flow = (struct atm_flow_data *)atm_tc_get(sch, skb->priority))) {
+ list_for_each_entry(flow, &p->flows, list) {
if (flow->filter_list) {
result = tc_classify_compat(skb,
flow->filter_list,
@@ -401,11 +379,16 @@ static int atm_tc_enqueue(struct sk_buff *skb, struct Qdisc *sch)
flow = (struct atm_flow_data *)res.class;
if (!flow)
flow = lookup_flow(sch, res.classid);
- break;
+ goto done;
}
- if (!flow)
+ }
+ flow = NULL;
+done:
+ ;
+ }
+ if (!flow) {
flow = &p->link;
- else {
+ } else {
if (flow->vcc)
ATM_SKB(skb)->atm_options = flow->vcc->atm_options;
/*@@@ looks good ... but it's not supposed to work :-) */
@@ -414,7 +397,7 @@ static int atm_tc_enqueue(struct sk_buff *skb, struct Qdisc *sch)
case TC_ACT_QUEUED:
case TC_ACT_STOLEN:
kfree_skb(skb);
- return NET_XMIT_SUCCESS;
+ return NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
case TC_ACT_SHOT:
kfree_skb(skb);
goto drop;
@@ -428,18 +411,16 @@ static int atm_tc_enqueue(struct sk_buff *skb, struct Qdisc *sch)
#endif
}
- ret = flow->q->enqueue(skb, flow->q);
- if (ret != 0) {
+ ret = qdisc_enqueue(skb, flow->q);
+ if (ret != NET_XMIT_SUCCESS) {
drop: __maybe_unused
- sch->qstats.drops++;
- if (flow)
- flow->qstats.drops++;
+ if (net_xmit_drop_count(ret)) {
+ sch->qstats.drops++;
+ if (flow)
+ flow->qstats.drops++;
+ }
return ret;
}
- sch->bstats.bytes += skb->len;
- sch->bstats.packets++;
- flow->bstats.bytes += skb->len;
- flow->bstats.packets++;
/*
* Okay, this may seem weird. We pretend we've dropped the packet if
* it goes via ATM. The reason for this is that the outer qdisc
@@ -451,10 +432,10 @@ drop: __maybe_unused
*/
if (flow == &p->link) {
sch->q.qlen++;
- return 0;
+ return NET_XMIT_SUCCESS;
}
tasklet_schedule(&p->task);
- return NET_XMIT_BYPASS;
+ return NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
}
/*
@@ -472,16 +453,23 @@ static void sch_atm_dequeue(unsigned long data)
struct sk_buff *skb;
pr_debug("sch_atm_dequeue(sch %p,[qdisc %p])\n", sch, p);
- for (flow = p->link.next; flow; flow = flow->next)
+ list_for_each_entry(flow, &p->flows, list) {
+ if (flow == &p->link)
+ continue;
/*
* If traffic is properly shaped, this won't generate nasty
* little bursts. Otherwise, it may ... (but that's okay)
*/
- while ((skb = flow->q->dequeue(flow->q))) {
- if (!atm_may_send(flow->vcc, skb->truesize)) {
- (void)flow->q->ops->requeue(skb, flow->q);
+ while ((skb = flow->q->ops->peek(flow->q))) {
+ if (!atm_may_send(flow->vcc, skb->truesize))
break;
- }
+
+ skb = qdisc_dequeue_peeked(flow->q);
+ if (unlikely(!skb))
+ break;
+
+ qdisc_bstats_update(sch, skb);
+ bstats_update(&flow->bstats, skb);
pr_debug("atm_tc_dequeue: sending on class %p\n", flow);
/* remove any LL header somebody else has attached */
skb_pull(skb, skb_network_offset(skb));
@@ -504,6 +492,7 @@ static void sch_atm_dequeue(unsigned long data)
/* atm.atm_options are already set by atm_tc_enqueue */
flow->vcc->send(flow->vcc, skb);
}
+ }
}
static struct sk_buff *atm_tc_dequeue(struct Qdisc *sch)
@@ -513,27 +502,19 @@ static struct sk_buff *atm_tc_dequeue(struct Qdisc *sch)
pr_debug("atm_tc_dequeue(sch %p,[qdisc %p])\n", sch, p);
tasklet_schedule(&p->task);
- skb = p->link.q->dequeue(p->link.q);
+ skb = qdisc_dequeue_peeked(p->link.q);
if (skb)
sch->q.qlen--;
return skb;
}
-static int atm_tc_requeue(struct sk_buff *skb, struct Qdisc *sch)
+static struct sk_buff *atm_tc_peek(struct Qdisc *sch)
{
struct atm_qdisc_data *p = qdisc_priv(sch);
- int ret;
- pr_debug("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++;
- sch->qstats.requeues++;
- } else {
- sch->qstats.drops++;
- p->link.qstats.drops++;
- }
- return ret;
+ pr_debug("atm_tc_peek(sch %p,[qdisc %p])\n", sch, p);
+
+ return p->link.q->ops->peek(p->link.q);
}
static unsigned int atm_tc_drop(struct Qdisc *sch)
@@ -543,9 +524,10 @@ static unsigned int atm_tc_drop(struct Qdisc *sch)
unsigned int len;
pr_debug("atm_tc_drop(sch %p,[qdisc %p])\n", sch, p);
- for (flow = p->flows; flow; flow = flow->next)
+ list_for_each_entry(flow, &p->flows, list) {
if (flow->q->ops->drop && (len = flow->q->ops->drop(flow->q)))
return len;
+ }
return 0;
}
@@ -554,8 +536,11 @@ static int atm_tc_init(struct Qdisc *sch, struct nlattr *opt)
struct atm_qdisc_data *p = qdisc_priv(sch);
pr_debug("atm_tc_init(sch %p,[qdisc %p],opt %p)\n", sch, p, opt);
- p->flows = &p->link;
- p->link.q = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, sch->handle);
+ INIT_LIST_HEAD(&p->flows);
+ INIT_LIST_HEAD(&p->link.list);
+ list_add(&p->link.list, &p->flows);
+ p->link.q = qdisc_create_dflt(sch->dev_queue,
+ &pfifo_qdisc_ops, sch->handle);
if (!p->link.q)
p->link.q = &noop_qdisc;
pr_debug("atm_tc_init: link (%p) qdisc %p\n", &p->link, p->link.q);
@@ -564,7 +549,6 @@ static int atm_tc_init(struct Qdisc *sch, struct nlattr *opt)
p->link.sock = NULL;
p->link.classid = sch->handle;
p->link.ref = 1;
- p->link.next = NULL;
tasklet_init(&p->task, sch_atm_dequeue, (unsigned long)sch);
return 0;
}
@@ -575,7 +559,7 @@ static void atm_tc_reset(struct Qdisc *sch)
struct atm_flow_data *flow;
pr_debug("atm_tc_reset(sch %p,[qdisc %p])\n", sch, p);
- for (flow = p->flows; flow; flow = flow->next)
+ list_for_each_entry(flow, &p->flows, list)
qdisc_reset(flow->q);
sch->q.qlen = 0;
}
@@ -583,23 +567,16 @@ static void atm_tc_reset(struct Qdisc *sch)
static void atm_tc_destroy(struct Qdisc *sch)
{
struct atm_qdisc_data *p = qdisc_priv(sch);
- struct atm_flow_data *flow;
+ struct atm_flow_data *flow, *tmp;
pr_debug("atm_tc_destroy(sch %p,[qdisc %p])\n", sch, p);
- /* races ? */
- while ((flow = p->flows)) {
- tcf_destroy_chain(flow->filter_list);
- flow->filter_list = NULL;
+ list_for_each_entry(flow, &p->flows, list)
+ tcf_destroy_chain(&flow->filter_list);
+
+ list_for_each_entry_safe(flow, tmp, &p->flows, list) {
if (flow->ref > 1)
- printk(KERN_ERR "atm_destroy: %p->ref = %d\n", flow,
- flow->ref);
+ pr_err("atm_destroy: %p->ref = %d\n", flow, flow->ref);
atm_tc_put(sch, (unsigned long)flow);
- if (p->flows == flow) {
- printk(KERN_ERR "atm_destroy: putting flow %p didn't "
- "kill it\n", flow);
- p->flows = flow->next; /* brute force */
- break;
- }
}
tasklet_kill(&p->task);
}
@@ -613,7 +590,7 @@ static int atm_tc_dump_class(struct Qdisc *sch, unsigned long cl,
pr_debug("atm_tc_dump_class(sch %p,[qdisc %p],flow %p,skb %p,tcm %p)\n",
sch, p, flow, skb, tcm);
- if (!find_flow(p, flow))
+ if (list_empty(&flow->list))
return -EINVAL;
tcm->tcm_handle = flow->classid;
tcm->tcm_info = flow->q->handle;
@@ -622,27 +599,31 @@ static int atm_tc_dump_class(struct Qdisc *sch, unsigned long cl,
if (nest == NULL)
goto nla_put_failure;
- NLA_PUT(skb, TCA_ATM_HDR, flow->hdr_len, flow->hdr);
+ if (nla_put(skb, TCA_ATM_HDR, flow->hdr_len, flow->hdr))
+ goto nla_put_failure;
if (flow->vcc) {
struct sockaddr_atmpvc pvc;
int state;
+ memset(&pvc, 0, sizeof(pvc));
pvc.sap_family = AF_ATMPVC;
pvc.sap_addr.itf = flow->vcc->dev ? flow->vcc->dev->number : -1;
pvc.sap_addr.vpi = flow->vcc->vpi;
pvc.sap_addr.vci = flow->vcc->vci;
- NLA_PUT(skb, TCA_ATM_ADDR, sizeof(pvc), &pvc);
+ if (nla_put(skb, TCA_ATM_ADDR, sizeof(pvc), &pvc))
+ goto nla_put_failure;
state = ATM_VF2VS(flow->vcc->flags);
- NLA_PUT_U32(skb, TCA_ATM_STATE, state);
+ if (nla_put_u32(skb, TCA_ATM_STATE, state))
+ goto nla_put_failure;
}
- if (flow->excess)
- NLA_PUT_U32(skb, TCA_ATM_EXCESS, flow->classid);
- else {
- NLA_PUT_U32(skb, TCA_ATM_EXCESS, 0);
+ if (flow->excess) {
+ if (nla_put_u32(skb, TCA_ATM_EXCESS, flow->classid))
+ goto nla_put_failure;
+ } else {
+ if (nla_put_u32(skb, TCA_ATM_EXCESS, 0))
+ goto nla_put_failure;
}
-
- nla_nest_end(skb, nest);
- return skb->len;
+ return nla_nest_end(skb, nest);
nla_put_failure:
nla_nest_cancel(skb, nest);
@@ -689,7 +670,7 @@ static struct Qdisc_ops atm_qdisc_ops __read_mostly = {
.priv_size = sizeof(struct atm_qdisc_data),
.enqueue = atm_tc_enqueue,
.dequeue = atm_tc_dequeue,
- .requeue = atm_tc_requeue,
+ .peek = atm_tc_peek,
.drop = atm_tc_drop,
.init = atm_tc_init,
.reset = atm_tc_reset,