Blame view
net/sched/sch_atm.c
19.5 KB
09c434b8a treewide: Add SPD... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
1da177e4c Linux-2.6.12-rc2 |
2 3 4 |
/* net/sched/sch_atm.c - ATM VC selection "queueing discipline" */ /* Written 1998-2000 by Werner Almesberger, EPFL ICA */ |
1da177e4c Linux-2.6.12-rc2 |
5 |
#include <linux/module.h> |
5a0e3ad6a include cleanup: ... |
6 |
#include <linux/slab.h> |
1da177e4c Linux-2.6.12-rc2 |
7 |
#include <linux/init.h> |
a6b7a4078 net: remove inter... |
8 |
#include <linux/interrupt.h> |
1da177e4c Linux-2.6.12-rc2 |
9 10 11 |
#include <linux/string.h> #include <linux/errno.h> #include <linux/skbuff.h> |
1da177e4c Linux-2.6.12-rc2 |
12 13 |
#include <linux/atmdev.h> #include <linux/atmclip.h> |
1da177e4c Linux-2.6.12-rc2 |
14 |
#include <linux/rtnetlink.h> |
b0188d4db [NET_SCHED]: sch_... |
15 |
#include <linux/file.h> /* for fput */ |
dc5fc579b [NETLINK]: Use nl... |
16 |
#include <net/netlink.h> |
1da177e4c Linux-2.6.12-rc2 |
17 |
#include <net/pkt_sched.h> |
cf1facda2 sched: move tcf_p... |
18 |
#include <net/pkt_cls.h> |
1da177e4c Linux-2.6.12-rc2 |
19 |
|
1da177e4c Linux-2.6.12-rc2 |
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
/* * The ATM queuing discipline provides a framework for invoking classifiers * (aka "filters"), which in turn select classes of this queuing discipline. * Each class maps the flow(s) it is handling to a given VC. Multiple classes * may share the same VC. * * When creating a class, VCs are specified by passing the number of the open * socket descriptor by which the calling process references the VC. The kernel * keeps the VC open at least until all classes using it are removed. * * In this file, most functions are named atm_tc_* to avoid confusion with all * the atm_* in net/atm. This naming convention differs from what's used in the * rest of net/sched. * * Known bugs: * - sometimes messes up the IP stack * - any manipulations besides the few operations described in the README, are * untested and likely to crash the system * - should lock the flow while there is data in the queue (?) */ |
1da177e4c Linux-2.6.12-rc2 |
40 |
#define VCC2FLOW(vcc) ((struct atm_flow_data *) ((vcc)->user_back)) |
1da177e4c Linux-2.6.12-rc2 |
41 |
struct atm_flow_data { |
f7ebdff75 net: sched: sch_a... |
42 |
struct Qdisc_class_common common; |
b0188d4db [NET_SCHED]: sch_... |
43 |
struct Qdisc *q; /* FIFO, TBF, etc. */ |
25d8c0d55 net: rcu-ify tcf_... |
44 |
struct tcf_proto __rcu *filter_list; |
6529eaba3 net: sched: intro... |
45 |
struct tcf_block *block; |
b0188d4db [NET_SCHED]: sch_... |
46 47 |
struct atm_vcc *vcc; /* VCC; NULL if VCC is closed */ void (*old_pop)(struct atm_vcc *vcc, |
786a90366 [PKT_SCHED] sch_a... |
48 |
struct sk_buff *skb); /* chaining */ |
1da177e4c Linux-2.6.12-rc2 |
49 50 |
struct atm_qdisc_data *parent; /* parent qdisc */ struct socket *sock; /* for closing */ |
1da177e4c Linux-2.6.12-rc2 |
51 |
int ref; /* reference count */ |
c1a8f1f1c net: restore gnet... |
52 |
struct gnet_stats_basic_packed bstats; |
1da177e4c Linux-2.6.12-rc2 |
53 |
struct gnet_stats_queue qstats; |
6accec76f sch_atm: Convert ... |
54 |
struct list_head list; |
1da177e4c Linux-2.6.12-rc2 |
55 56 57 |
struct atm_flow_data *excess; /* flow for excess traffic; NULL to set CLP instead */ int hdr_len; |
b90feaff2 net: sched: Repla... |
58 |
unsigned char hdr[]; /* header data; MUST BE LAST */ |
1da177e4c Linux-2.6.12-rc2 |
59 60 61 62 |
}; struct atm_qdisc_data { struct atm_flow_data link; /* unclassified skbs go here */ |
6accec76f sch_atm: Convert ... |
63 |
struct list_head flows; /* NB: "link" is also on this |
1da177e4c Linux-2.6.12-rc2 |
64 |
list */ |
f30ab418a pkt_sched: Remove... |
65 |
struct tasklet_struct task; /* dequeue tasklet */ |
1da177e4c Linux-2.6.12-rc2 |
66 |
}; |
1da177e4c Linux-2.6.12-rc2 |
67 |
/* ------------------------- Class/flow operations ------------------------- */ |
b0188d4db [NET_SCHED]: sch_... |
68 |
static inline struct atm_flow_data *lookup_flow(struct Qdisc *sch, u32 classid) |
1da177e4c Linux-2.6.12-rc2 |
69 |
{ |
786a90366 [PKT_SCHED] sch_a... |
70 |
struct atm_qdisc_data *p = qdisc_priv(sch); |
1da177e4c Linux-2.6.12-rc2 |
71 |
struct atm_flow_data *flow; |
6accec76f sch_atm: Convert ... |
72 |
list_for_each_entry(flow, &p->flows, list) { |
f7ebdff75 net: sched: sch_a... |
73 |
if (flow->common.classid == classid) |
6accec76f sch_atm: Convert ... |
74 75 76 |
return flow; } return NULL; |
1da177e4c Linux-2.6.12-rc2 |
77 |
} |
b0188d4db [NET_SCHED]: sch_... |
78 |
static int atm_tc_graft(struct Qdisc *sch, unsigned long arg, |
653d6fd68 net: sched: sch: ... |
79 80 |
struct Qdisc *new, struct Qdisc **old, struct netlink_ext_ack *extack) |
1da177e4c Linux-2.6.12-rc2 |
81 |
{ |
786a90366 [PKT_SCHED] sch_a... |
82 |
struct atm_qdisc_data *p = qdisc_priv(sch); |
b0188d4db [NET_SCHED]: sch_... |
83 |
struct atm_flow_data *flow = (struct atm_flow_data *)arg; |
786a90366 [PKT_SCHED] sch_a... |
84 85 |
pr_debug("atm_tc_graft(sch %p,[qdisc %p],flow %p,new %p,old %p) ", |
b0188d4db [NET_SCHED]: sch_... |
86 |
sch, p, flow, new, old); |
6accec76f sch_atm: Convert ... |
87 |
if (list_empty(&flow->list)) |
b0188d4db [NET_SCHED]: sch_... |
88 89 90 |
return -EINVAL; if (!new) new = &noop_qdisc; |
b94c8afcb pkt_sched: remove... |
91 92 |
*old = flow->q; flow->q = new; |
b0188d4db [NET_SCHED]: sch_... |
93 94 |
if (*old) qdisc_reset(*old); |
10297b993 [NET] SCHED: Fix ... |
95 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
96 |
} |
b0188d4db [NET_SCHED]: sch_... |
97 |
static struct Qdisc *atm_tc_leaf(struct Qdisc *sch, unsigned long cl) |
1da177e4c Linux-2.6.12-rc2 |
98 |
{ |
b0188d4db [NET_SCHED]: sch_... |
99 |
struct atm_flow_data *flow = (struct atm_flow_data *)cl; |
1da177e4c Linux-2.6.12-rc2 |
100 |
|
786a90366 [PKT_SCHED] sch_a... |
101 102 |
pr_debug("atm_tc_leaf(sch %p,flow %p) ", sch, flow); |
1da177e4c Linux-2.6.12-rc2 |
103 104 |
return flow ? flow->q : NULL; } |
143976ce9 net_sched: remove... |
105 |
static unsigned long atm_tc_find(struct Qdisc *sch, u32 classid) |
1da177e4c Linux-2.6.12-rc2 |
106 |
{ |
786a90366 [PKT_SCHED] sch_a... |
107 |
struct atm_qdisc_data *p __maybe_unused = qdisc_priv(sch); |
1da177e4c Linux-2.6.12-rc2 |
108 |
struct atm_flow_data *flow; |
143976ce9 net_sched: remove... |
109 110 |
pr_debug("%s(sch %p,[qdisc %p],classid %x) ", __func__, sch, p, classid); |
b0188d4db [NET_SCHED]: sch_... |
111 |
flow = lookup_flow(sch, classid); |
143976ce9 net_sched: remove... |
112 113 |
pr_debug("%s: flow %p ", __func__, flow); |
b0188d4db [NET_SCHED]: sch_... |
114 |
return (unsigned long)flow; |
1da177e4c Linux-2.6.12-rc2 |
115 |
} |
1da177e4c Linux-2.6.12-rc2 |
116 |
static unsigned long atm_tc_bind_filter(struct Qdisc *sch, |
b0188d4db [NET_SCHED]: sch_... |
117 |
unsigned long parent, u32 classid) |
1da177e4c Linux-2.6.12-rc2 |
118 |
{ |
143976ce9 net_sched: remove... |
119 120 121 122 123 124 125 126 127 128 129 |
struct atm_qdisc_data *p __maybe_unused = qdisc_priv(sch); struct atm_flow_data *flow; pr_debug("%s(sch %p,[qdisc %p],classid %x) ", __func__, sch, p, classid); flow = lookup_flow(sch, classid); if (flow) flow->ref++; pr_debug("%s: flow %p ", __func__, flow); return (unsigned long)flow; |
1da177e4c Linux-2.6.12-rc2 |
130 |
} |
1da177e4c Linux-2.6.12-rc2 |
131 132 133 134 135 |
/* * atm_tc_put handles all destructions, including the ones that are explicitly * requested (atm_tc_destroy, etc.). The assumption here is that we never drop * anything that still seems to be in use. */ |
1da177e4c Linux-2.6.12-rc2 |
136 137 |
static void atm_tc_put(struct Qdisc *sch, unsigned long cl) { |
786a90366 [PKT_SCHED] sch_a... |
138 |
struct atm_qdisc_data *p = qdisc_priv(sch); |
b0188d4db [NET_SCHED]: sch_... |
139 |
struct atm_flow_data *flow = (struct atm_flow_data *)cl; |
1da177e4c Linux-2.6.12-rc2 |
140 |
|
786a90366 [PKT_SCHED] sch_a... |
141 142 |
pr_debug("atm_tc_put(sch %p,[qdisc %p],flow %p) ", sch, p, flow); |
b0188d4db [NET_SCHED]: sch_... |
143 144 |
if (--flow->ref) return; |
786a90366 [PKT_SCHED] sch_a... |
145 146 |
pr_debug("atm_tc_put: destroying "); |
6accec76f sch_atm: Convert ... |
147 |
list_del_init(&flow->list); |
786a90366 [PKT_SCHED] sch_a... |
148 149 |
pr_debug("atm_tc_put: qdisc %p ", flow->q); |
86bd446b5 net: sched: renam... |
150 |
qdisc_put(flow->q); |
6529eaba3 net: sched: intro... |
151 |
tcf_block_put(flow->block); |
1da177e4c Linux-2.6.12-rc2 |
152 |
if (flow->sock) { |
516e0cc56 [PATCH] f_count m... |
153 154 |
pr_debug("atm_tc_put: f_count %ld ", |
b0188d4db [NET_SCHED]: sch_... |
155 |
file_count(flow->sock->file)); |
1da177e4c Linux-2.6.12-rc2 |
156 157 158 |
flow->vcc->pop = flow->old_pop; sockfd_put(flow->sock); } |
b0188d4db [NET_SCHED]: sch_... |
159 160 161 162 |
if (flow->excess) atm_tc_put(sch, (unsigned long)flow->excess); if (flow != &p->link) kfree(flow); |
1da177e4c Linux-2.6.12-rc2 |
163 164 165 166 167 |
/* * If flow == &p->link, the qdisc no longer works at this point and * needs to be removed. (By the caller of atm_tc_put.) */ } |
b0188d4db [NET_SCHED]: sch_... |
168 |
static void sch_atm_pop(struct atm_vcc *vcc, struct sk_buff *skb) |
1da177e4c Linux-2.6.12-rc2 |
169 170 |
{ struct atm_qdisc_data *p = VCC2FLOW(vcc)->parent; |
786a90366 [PKT_SCHED] sch_a... |
171 172 |
pr_debug("sch_atm_pop(vcc %p,skb %p,[qdisc %p]) ", vcc, skb, p); |
b0188d4db [NET_SCHED]: sch_... |
173 |
VCC2FLOW(vcc)->old_pop(vcc, skb); |
1da177e4c Linux-2.6.12-rc2 |
174 175 176 177 |
tasklet_schedule(&p->task); } static const u8 llc_oui_ip[] = { |
b0188d4db [NET_SCHED]: sch_... |
178 179 180 181 |
0xaa, /* DSAP: non-ISO */ 0xaa, /* SSAP: non-ISO */ 0x03, /* Ctrl: Unnumbered Information Command PDU */ 0x00, /* OUI: EtherType */ |
1da177e4c Linux-2.6.12-rc2 |
182 |
0x00, 0x00, |
b0188d4db [NET_SCHED]: sch_... |
183 184 |
0x08, 0x00 }; /* Ethertype IP (0800) */ |
1da177e4c Linux-2.6.12-rc2 |
185 |
|
27a3421e4 [NET_SCHED]: Use ... |
186 187 188 189 |
static const struct nla_policy atm_policy[TCA_ATM_MAX + 1] = { [TCA_ATM_FD] = { .type = NLA_U32 }, [TCA_ATM_EXCESS] = { .type = NLA_U32 }, }; |
1da177e4c Linux-2.6.12-rc2 |
190 |
static int atm_tc_change(struct Qdisc *sch, u32 classid, u32 parent, |
793d81d6a net: sched: sch: ... |
191 192 |
struct nlattr **tca, unsigned long *arg, struct netlink_ext_ack *extack) |
1da177e4c Linux-2.6.12-rc2 |
193 |
{ |
786a90366 [PKT_SCHED] sch_a... |
194 |
struct atm_qdisc_data *p = qdisc_priv(sch); |
b0188d4db [NET_SCHED]: sch_... |
195 |
struct atm_flow_data *flow = (struct atm_flow_data *)*arg; |
1da177e4c Linux-2.6.12-rc2 |
196 |
struct atm_flow_data *excess = NULL; |
1e90474c3 [NET_SCHED]: Conv... |
197 198 |
struct nlattr *opt = tca[TCA_OPTIONS]; struct nlattr *tb[TCA_ATM_MAX + 1]; |
1da177e4c Linux-2.6.12-rc2 |
199 |
struct socket *sock; |
b0188d4db [NET_SCHED]: sch_... |
200 |
int fd, error, hdr_len; |
1da177e4c Linux-2.6.12-rc2 |
201 |
void *hdr; |
786a90366 [PKT_SCHED] sch_a... |
202 |
pr_debug("atm_tc_change(sch %p,[qdisc %p],classid %x,parent %x," |
b0188d4db [NET_SCHED]: sch_... |
203 204 |
"flow %p,opt %p) ", sch, p, classid, parent, flow, opt); |
1da177e4c Linux-2.6.12-rc2 |
205 206 207 208 209 210 211 212 213 214 215 216 |
/* * The concept of parents doesn't apply for this qdisc. */ if (parent && parent != TC_H_ROOT && parent != sch->handle) return -EINVAL; /* * ATM classes cannot be changed. In order to change properties of the * ATM connection, that socket needs to be modified directly (via the * native ATM API. In order to send a flow to a different VC, the old * class needs to be removed and a new one added. (This may be changed * later.) */ |
b0188d4db [NET_SCHED]: sch_... |
217 218 |
if (flow) return -EBUSY; |
cee63723b [NET_SCHED]: Prop... |
219 |
if (opt == NULL) |
1da177e4c Linux-2.6.12-rc2 |
220 |
return -EINVAL; |
27a3421e4 [NET_SCHED]: Use ... |
221 |
|
8cb081746 netlink: make val... |
222 223 |
error = nla_parse_nested_deprecated(tb, TCA_ATM_MAX, opt, atm_policy, NULL); |
cee63723b [NET_SCHED]: Prop... |
224 225 |
if (error < 0) return error; |
27a3421e4 [NET_SCHED]: Use ... |
226 |
if (!tb[TCA_ATM_FD]) |
1da177e4c Linux-2.6.12-rc2 |
227 |
return -EINVAL; |
1587bac49 [NET_SCHED]: Use ... |
228 |
fd = nla_get_u32(tb[TCA_ATM_FD]); |
786a90366 [PKT_SCHED] sch_a... |
229 230 |
pr_debug("atm_tc_change: fd %d ", fd); |
1e90474c3 [NET_SCHED]: Conv... |
231 232 233 |
if (tb[TCA_ATM_HDR]) { hdr_len = nla_len(tb[TCA_ATM_HDR]); hdr = nla_data(tb[TCA_ATM_HDR]); |
b0188d4db [NET_SCHED]: sch_... |
234 |
} else { |
1da177e4c Linux-2.6.12-rc2 |
235 |
hdr_len = RFC1483LLC_LEN; |
b0188d4db [NET_SCHED]: sch_... |
236 |
hdr = NULL; /* default LLC/SNAP for IP */ |
1da177e4c Linux-2.6.12-rc2 |
237 |
} |
1e90474c3 [NET_SCHED]: Conv... |
238 |
if (!tb[TCA_ATM_EXCESS]) |
b0188d4db [NET_SCHED]: sch_... |
239 |
excess = NULL; |
1da177e4c Linux-2.6.12-rc2 |
240 |
else { |
b0188d4db [NET_SCHED]: sch_... |
241 |
excess = (struct atm_flow_data *) |
143976ce9 net_sched: remove... |
242 |
atm_tc_find(sch, nla_get_u32(tb[TCA_ATM_EXCESS])); |
b0188d4db [NET_SCHED]: sch_... |
243 244 |
if (!excess) return -ENOENT; |
1da177e4c Linux-2.6.12-rc2 |
245 |
} |
f5e5cb755 [NET_SCHED]: sch_... |
246 247 |
pr_debug("atm_tc_change: type %d, payload %d, hdr_len %d ", |
1e90474c3 [NET_SCHED]: Conv... |
248 |
opt->nla_type, nla_len(opt), hdr_len); |
786a90366 [PKT_SCHED] sch_a... |
249 250 |
sock = sockfd_lookup(fd, &error); if (!sock) |
b0188d4db [NET_SCHED]: sch_... |
251 |
return error; /* f_count++ */ |
516e0cc56 [PATCH] f_count m... |
252 253 |
pr_debug("atm_tc_change: f_count %ld ", file_count(sock->file)); |
10297b993 [NET] SCHED: Fix ... |
254 |
if (sock->ops->family != PF_ATMSVC && sock->ops->family != PF_ATMPVC) { |
1da177e4c Linux-2.6.12-rc2 |
255 |
error = -EPROTOTYPE; |
10297b993 [NET] SCHED: Fix ... |
256 |
goto err_out; |
1da177e4c Linux-2.6.12-rc2 |
257 258 259 260 261 |
} /* @@@ should check if the socket is really operational or we'll crash on vcc->send */ if (classid) { if (TC_H_MAJ(classid ^ sch->handle)) { |
786a90366 [PKT_SCHED] sch_a... |
262 263 |
pr_debug("atm_tc_change: classid mismatch "); |
1da177e4c Linux-2.6.12-rc2 |
264 265 266 |
error = -EINVAL; goto err_out; } |
b0188d4db [NET_SCHED]: sch_... |
267 |
} else { |
1da177e4c Linux-2.6.12-rc2 |
268 269 270 271 |
int i; unsigned long cl; for (i = 1; i < 0x8000; i++) { |
b0188d4db [NET_SCHED]: sch_... |
272 |
classid = TC_H_MAKE(sch->handle, 0x8000 | i); |
143976ce9 net_sched: remove... |
273 |
cl = atm_tc_find(sch, classid); |
786a90366 [PKT_SCHED] sch_a... |
274 |
if (!cl) |
b0188d4db [NET_SCHED]: sch_... |
275 |
break; |
1da177e4c Linux-2.6.12-rc2 |
276 277 |
} } |
786a90366 [PKT_SCHED] sch_a... |
278 279 |
pr_debug("atm_tc_change: new id %x ", classid); |
782f79568 [ATM]: Replacing ... |
280 |
flow = kzalloc(sizeof(struct atm_flow_data) + hdr_len, GFP_KERNEL); |
786a90366 [PKT_SCHED] sch_a... |
281 282 |
pr_debug("atm_tc_change: flow %p ", flow); |
1da177e4c Linux-2.6.12-rc2 |
283 284 285 286 |
if (!flow) { error = -ENOBUFS; goto err_out; } |
6529eaba3 net: sched: intro... |
287 |
|
8d1a77f97 net: sch: api: ad... |
288 289 |
error = tcf_block_get(&flow->block, &flow->filter_list, sch, extack); |
6529eaba3 net: sched: intro... |
290 291 292 293 |
if (error) { kfree(flow); goto err_out; } |
a38a98821 net: sch: api: ad... |
294 295 |
flow->q = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, classid, extack); |
786a90366 [PKT_SCHED] sch_a... |
296 |
if (!flow->q) |
1da177e4c Linux-2.6.12-rc2 |
297 |
flow->q = &noop_qdisc; |
786a90366 [PKT_SCHED] sch_a... |
298 299 |
pr_debug("atm_tc_change: qdisc %p ", flow->q); |
1da177e4c Linux-2.6.12-rc2 |
300 |
flow->sock = sock; |
b0188d4db [NET_SCHED]: sch_... |
301 |
flow->vcc = ATM_SD(sock); /* speedup */ |
1da177e4c Linux-2.6.12-rc2 |
302 |
flow->vcc->user_back = flow; |
786a90366 [PKT_SCHED] sch_a... |
303 304 |
pr_debug("atm_tc_change: vcc %p ", flow->vcc); |
1da177e4c Linux-2.6.12-rc2 |
305 306 307 |
flow->old_pop = flow->vcc->pop; flow->parent = p; flow->vcc->pop = sch_atm_pop; |
f7ebdff75 net: sched: sch_a... |
308 |
flow->common.classid = classid; |
1da177e4c Linux-2.6.12-rc2 |
309 310 |
flow->ref = 1; flow->excess = excess; |
6accec76f sch_atm: Convert ... |
311 |
list_add(&flow->list, &p->link.list); |
1da177e4c Linux-2.6.12-rc2 |
312 313 |
flow->hdr_len = hdr_len; if (hdr) |
b0188d4db [NET_SCHED]: sch_... |
314 |
memcpy(flow->hdr, hdr, hdr_len); |
1da177e4c Linux-2.6.12-rc2 |
315 |
else |
b0188d4db [NET_SCHED]: sch_... |
316 317 |
memcpy(flow->hdr, llc_oui_ip, sizeof(llc_oui_ip)); *arg = (unsigned long)flow; |
1da177e4c Linux-2.6.12-rc2 |
318 319 |
return 0; err_out: |
1da177e4c Linux-2.6.12-rc2 |
320 321 322 |
sockfd_put(sock); return error; } |
b0188d4db [NET_SCHED]: sch_... |
323 |
static int atm_tc_delete(struct Qdisc *sch, unsigned long arg) |
1da177e4c Linux-2.6.12-rc2 |
324 |
{ |
786a90366 [PKT_SCHED] sch_a... |
325 |
struct atm_qdisc_data *p = qdisc_priv(sch); |
b0188d4db [NET_SCHED]: sch_... |
326 |
struct atm_flow_data *flow = (struct atm_flow_data *)arg; |
1da177e4c Linux-2.6.12-rc2 |
327 |
|
786a90366 [PKT_SCHED] sch_a... |
328 329 |
pr_debug("atm_tc_delete(sch %p,[qdisc %p],flow %p) ", sch, p, flow); |
6accec76f sch_atm: Convert ... |
330 |
if (list_empty(&flow->list)) |
b0188d4db [NET_SCHED]: sch_... |
331 |
return -EINVAL; |
25d8c0d55 net: rcu-ify tcf_... |
332 |
if (rcu_access_pointer(flow->filter_list) || flow == &p->link) |
b0188d4db [NET_SCHED]: sch_... |
333 |
return -EBUSY; |
1da177e4c Linux-2.6.12-rc2 |
334 335 336 337 338 |
/* * Reference count must be 2: one for "keepalive" (set at class * creation), and one for the reference held when calling delete. */ if (flow->ref < 2) { |
cc7ec456f net_sched: cleanups |
339 340 |
pr_err("atm_tc_delete: flow->ref == %d ", flow->ref); |
1da177e4c Linux-2.6.12-rc2 |
341 342 |
return -EINVAL; } |
b0188d4db [NET_SCHED]: sch_... |
343 344 345 |
if (flow->ref > 2) return -EBUSY; /* catch references via excess, etc. */ atm_tc_put(sch, arg); |
1da177e4c Linux-2.6.12-rc2 |
346 347 |
return 0; } |
b0188d4db [NET_SCHED]: sch_... |
348 |
static void atm_tc_walk(struct Qdisc *sch, struct qdisc_walker *walker) |
1da177e4c Linux-2.6.12-rc2 |
349 |
{ |
786a90366 [PKT_SCHED] sch_a... |
350 |
struct atm_qdisc_data *p = qdisc_priv(sch); |
1da177e4c Linux-2.6.12-rc2 |
351 |
struct atm_flow_data *flow; |
786a90366 [PKT_SCHED] sch_a... |
352 353 |
pr_debug("atm_tc_walk(sch %p,[qdisc %p],walker %p) ", sch, p, walker); |
b0188d4db [NET_SCHED]: sch_... |
354 355 |
if (walker->stop) return; |
6accec76f sch_atm: Convert ... |
356 357 358 359 360 361 |
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; } |
1da177e4c Linux-2.6.12-rc2 |
362 363 364 |
walker->count++; } } |
cbaacc4e8 net: sched: sch: ... |
365 366 |
static struct tcf_block *atm_tc_tcf_block(struct Qdisc *sch, unsigned long cl, struct netlink_ext_ack *extack) |
1da177e4c Linux-2.6.12-rc2 |
367 |
{ |
786a90366 [PKT_SCHED] sch_a... |
368 |
struct atm_qdisc_data *p = qdisc_priv(sch); |
b0188d4db [NET_SCHED]: sch_... |
369 |
struct atm_flow_data *flow = (struct atm_flow_data *)cl; |
1da177e4c Linux-2.6.12-rc2 |
370 |
|
786a90366 [PKT_SCHED] sch_a... |
371 372 |
pr_debug("atm_tc_find_tcf(sch %p,[qdisc %p],flow %p) ", sch, p, flow); |
6529eaba3 net: sched: intro... |
373 |
return flow ? flow->block : p->link.block; |
1da177e4c Linux-2.6.12-rc2 |
374 |
} |
1da177e4c Linux-2.6.12-rc2 |
375 |
/* --------------------------- Qdisc operations ---------------------------- */ |
520ac30f4 net_sched: drop p... |
376 377 |
static int atm_tc_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free) |
1da177e4c Linux-2.6.12-rc2 |
378 |
{ |
786a90366 [PKT_SCHED] sch_a... |
379 |
struct atm_qdisc_data *p = qdisc_priv(sch); |
6accec76f sch_atm: Convert ... |
380 |
struct atm_flow_data *flow; |
1da177e4c Linux-2.6.12-rc2 |
381 382 |
struct tcf_result res; int result; |
99860208b sched: remove NET... |
383 |
int ret = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; |
1da177e4c Linux-2.6.12-rc2 |
384 |
|
786a90366 [PKT_SCHED] sch_a... |
385 386 |
pr_debug("atm_tc_enqueue(skb %p,sch %p,[qdisc %p]) ", skb, sch, p); |
95df1b160 net_sched: remove... |
387 |
result = TC_ACT_OK; /* be nice to gcc */ |
6accec76f sch_atm: Convert ... |
388 |
flow = NULL; |
1da177e4c Linux-2.6.12-rc2 |
389 |
if (TC_H_MAJ(skb->priority) != sch->handle || |
143976ce9 net_sched: remove... |
390 |
!(flow = (struct atm_flow_data *)atm_tc_find(sch, skb->priority))) { |
25d8c0d55 net: rcu-ify tcf_... |
391 |
struct tcf_proto *fl; |
6accec76f sch_atm: Convert ... |
392 |
list_for_each_entry(flow, &p->flows, list) { |
25d8c0d55 net: rcu-ify tcf_... |
393 394 |
fl = rcu_dereference_bh(flow->filter_list); if (fl) { |
87d83093b net: sched: move ... |
395 |
result = tcf_classify(skb, fl, &res, true); |
b0188d4db [NET_SCHED]: sch_... |
396 397 398 399 400 |
if (result < 0) continue; flow = (struct atm_flow_data *)res.class; if (!flow) flow = lookup_flow(sch, res.classid); |
6accec76f sch_atm: Convert ... |
401 |
goto done; |
1da177e4c Linux-2.6.12-rc2 |
402 |
} |
6accec76f sch_atm: Convert ... |
403 404 |
} flow = NULL; |
cc7ec456f net_sched: cleanups |
405 406 |
done: ; |
6accec76f sch_atm: Convert ... |
407 |
} |
cc7ec456f net_sched: cleanups |
408 |
if (!flow) { |
b0188d4db [NET_SCHED]: sch_... |
409 |
flow = &p->link; |
cc7ec456f net_sched: cleanups |
410 |
} else { |
1da177e4c Linux-2.6.12-rc2 |
411 412 |
if (flow->vcc) ATM_SKB(skb)->atm_options = flow->vcc->atm_options; |
b0188d4db [NET_SCHED]: sch_... |
413 |
/*@@@ looks good ... but it's not supposed to work :-) */ |
921008044 [NET_SCHED]: sch_... |
414 415 416 417 |
#ifdef CONFIG_NET_CLS_ACT switch (result) { case TC_ACT_QUEUED: case TC_ACT_STOLEN: |
e25ea21ff net: sched: intro... |
418 |
case TC_ACT_TRAP: |
520ac30f4 net_sched: drop p... |
419 |
__qdisc_drop(skb, to_free); |
378a2f090 net_sched: Add qd... |
420 |
return NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; |
921008044 [NET_SCHED]: sch_... |
421 |
case TC_ACT_SHOT: |
520ac30f4 net_sched: drop p... |
422 |
__qdisc_drop(skb, to_free); |
921008044 [NET_SCHED]: sch_... |
423 |
goto drop; |
95df1b160 net_sched: remove... |
424 |
case TC_ACT_RECLASSIFY: |
73ca4918f [NET_SCHED]: act_... |
425 426 427 428 429 |
if (flow->excess) flow = flow->excess; else ATM_SKB(skb)->atm_options |= ATM_ATMOPT_CLP; break; |
921008044 [NET_SCHED]: sch_... |
430 |
} |
1da177e4c Linux-2.6.12-rc2 |
431 432 |
#endif } |
c3bc7cff8 [NET_SCHED]: Kill... |
433 |
|
520ac30f4 net_sched: drop p... |
434 |
ret = qdisc_enqueue(skb, flow->q, to_free); |
9871e50ed net: Use NET_XMIT... |
435 |
if (ret != NET_XMIT_SUCCESS) { |
921008044 [NET_SCHED]: sch_... |
436 |
drop: __maybe_unused |
378a2f090 net_sched: Add qd... |
437 |
if (net_xmit_drop_count(ret)) { |
25331d6ce net: sched: imple... |
438 |
qdisc_qstats_drop(sch); |
378a2f090 net_sched: Add qd... |
439 440 441 |
if (flow) flow->qstats.drops++; } |
1da177e4c Linux-2.6.12-rc2 |
442 443 |
return ret; } |
1da177e4c Linux-2.6.12-rc2 |
444 445 446 447 448 449 450 451 452 453 454 |
/* * 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 * expects to be able to q->dequeue the packet later on if we return * success at this place. Also, sch->q.qdisc needs to reflect whether * there is a packet egligible for dequeuing or not. Note that the * statistics of the outer qdisc are necessarily wrong because of all * this. There's currently no correct solution for this. */ if (flow == &p->link) { sch->q.qlen++; |
9871e50ed net: Use NET_XMIT... |
455 |
return NET_XMIT_SUCCESS; |
1da177e4c Linux-2.6.12-rc2 |
456 457 |
} tasklet_schedule(&p->task); |
c27f339af net_sched: Add qd... |
458 |
return NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; |
1da177e4c Linux-2.6.12-rc2 |
459 |
} |
1da177e4c Linux-2.6.12-rc2 |
460 461 462 463 464 465 |
/* * Dequeue packets and send them over ATM. Note that we quite deliberately * avoid checking net_device's flow control here, simply because sch_atm * uses its own channels, which have nothing to do with any CLIP/LANE/or * non-ATM interfaces. */ |
1da177e4c Linux-2.6.12-rc2 |
466 467 |
static void sch_atm_dequeue(unsigned long data) { |
b0188d4db [NET_SCHED]: sch_... |
468 |
struct Qdisc *sch = (struct Qdisc *)data; |
786a90366 [PKT_SCHED] sch_a... |
469 |
struct atm_qdisc_data *p = qdisc_priv(sch); |
1da177e4c Linux-2.6.12-rc2 |
470 471 |
struct atm_flow_data *flow; struct sk_buff *skb; |
786a90366 [PKT_SCHED] sch_a... |
472 473 |
pr_debug("sch_atm_dequeue(sch %p,[qdisc %p]) ", sch, p); |
6accec76f sch_atm: Convert ... |
474 475 476 |
list_for_each_entry(flow, &p->flows, list) { if (flow == &p->link) continue; |
1da177e4c Linux-2.6.12-rc2 |
477 478 479 480 |
/* * If traffic is properly shaped, this won't generate nasty * little bursts. Otherwise, it may ... (but that's okay) */ |
03c05f0d4 pkt_sched: Use qd... |
481 482 |
while ((skb = flow->q->ops->peek(flow->q))) { if (!atm_may_send(flow->vcc, skb->truesize)) |
1da177e4c Linux-2.6.12-rc2 |
483 |
break; |
03c05f0d4 pkt_sched: Use qd... |
484 |
|
77be155cb pkt_sched: Add pe... |
485 |
skb = qdisc_dequeue_peeked(flow->q); |
03c05f0d4 pkt_sched: Use qd... |
486 487 |
if (unlikely(!skb)) break; |
2dd875ff3 net_sched: update... |
488 489 |
qdisc_bstats_update(sch, skb); bstats_update(&flow->bstats, skb); |
786a90366 [PKT_SCHED] sch_a... |
490 491 |
pr_debug("atm_tc_dequeue: sending on class %p ", flow); |
1da177e4c Linux-2.6.12-rc2 |
492 |
/* remove any LL header somebody else has attached */ |
eddc9ec53 [SK_BUFF]: Introd... |
493 |
skb_pull(skb, skb_network_offset(skb)); |
1da177e4c Linux-2.6.12-rc2 |
494 495 |
if (skb_headroom(skb) < flow->hdr_len) { struct sk_buff *new; |
b0188d4db [NET_SCHED]: sch_... |
496 |
new = skb_realloc_headroom(skb, flow->hdr_len); |
1da177e4c Linux-2.6.12-rc2 |
497 |
dev_kfree_skb(skb); |
b0188d4db [NET_SCHED]: sch_... |
498 499 |
if (!new) continue; |
1da177e4c Linux-2.6.12-rc2 |
500 501 |
skb = new; } |
786a90366 [PKT_SCHED] sch_a... |
502 503 |
pr_debug("sch_atm_dequeue: ip %p, data %p ", |
eddc9ec53 [SK_BUFF]: Introd... |
504 |
skb_network_header(skb), skb->data); |
1da177e4c Linux-2.6.12-rc2 |
505 |
ATM_SKB(skb)->vcc = flow->vcc; |
b0188d4db [NET_SCHED]: sch_... |
506 507 |
memcpy(skb_push(skb, flow->hdr_len), flow->hdr, flow->hdr_len); |
14afee4b6 net: convert sock... |
508 |
refcount_add(skb->truesize, |
1da177e4c Linux-2.6.12-rc2 |
509 510 |
&sk_atm(flow->vcc)->sk_wmem_alloc); /* atm.atm_options are already set by atm_tc_enqueue */ |
b0188d4db [NET_SCHED]: sch_... |
511 |
flow->vcc->send(flow->vcc, skb); |
1da177e4c Linux-2.6.12-rc2 |
512 |
} |
6accec76f sch_atm: Convert ... |
513 |
} |
1da177e4c Linux-2.6.12-rc2 |
514 |
} |
1da177e4c Linux-2.6.12-rc2 |
515 516 |
static struct sk_buff *atm_tc_dequeue(struct Qdisc *sch) { |
786a90366 [PKT_SCHED] sch_a... |
517 |
struct atm_qdisc_data *p = qdisc_priv(sch); |
1da177e4c Linux-2.6.12-rc2 |
518 |
struct sk_buff *skb; |
786a90366 [PKT_SCHED] sch_a... |
519 520 |
pr_debug("atm_tc_dequeue(sch %p,[qdisc %p]) ", sch, p); |
1da177e4c Linux-2.6.12-rc2 |
521 |
tasklet_schedule(&p->task); |
77be155cb pkt_sched: Add pe... |
522 |
skb = qdisc_dequeue_peeked(p->link.q); |
b0188d4db [NET_SCHED]: sch_... |
523 524 |
if (skb) sch->q.qlen--; |
1da177e4c Linux-2.6.12-rc2 |
525 526 |
return skb; } |
8e3af9789 pkt_sched: Add qd... |
527 528 529 530 531 532 533 534 535 |
static struct sk_buff *atm_tc_peek(struct Qdisc *sch) { struct atm_qdisc_data *p = qdisc_priv(sch); pr_debug("atm_tc_peek(sch %p,[qdisc %p]) ", sch, p); return p->link.q->ops->peek(p->link.q); } |
e63d7dfd2 net: sched: sch: ... |
536 537 |
static int atm_tc_init(struct Qdisc *sch, struct nlattr *opt, struct netlink_ext_ack *extack) |
1da177e4c Linux-2.6.12-rc2 |
538 |
{ |
786a90366 [PKT_SCHED] sch_a... |
539 |
struct atm_qdisc_data *p = qdisc_priv(sch); |
6529eaba3 net: sched: intro... |
540 |
int err; |
1da177e4c Linux-2.6.12-rc2 |
541 |
|
786a90366 [PKT_SCHED] sch_a... |
542 543 |
pr_debug("atm_tc_init(sch %p,[qdisc %p],opt %p) ", sch, p, opt); |
6accec76f sch_atm: Convert ... |
544 545 546 |
INIT_LIST_HEAD(&p->flows); INIT_LIST_HEAD(&p->link.list); list_add(&p->link.list, &p->flows); |
3511c9132 net_sched: remove... |
547 |
p->link.q = qdisc_create_dflt(sch->dev_queue, |
a38a98821 net: sch: api: ad... |
548 |
&pfifo_qdisc_ops, sch->handle, extack); |
786a90366 [PKT_SCHED] sch_a... |
549 |
if (!p->link.q) |
1da177e4c Linux-2.6.12-rc2 |
550 |
p->link.q = &noop_qdisc; |
786a90366 [PKT_SCHED] sch_a... |
551 552 |
pr_debug("atm_tc_init: link (%p) qdisc %p ", &p->link, p->link.q); |
306381aec net_sched: fix a ... |
553 554 555 556 |
p->link.vcc = NULL; p->link.sock = NULL; p->link.common.classid = sch->handle; p->link.ref = 1; |
6529eaba3 net: sched: intro... |
557 |
|
8d1a77f97 net: sch: api: ad... |
558 559 |
err = tcf_block_get(&p->link.block, &p->link.filter_list, sch, extack); |
6529eaba3 net: sched: intro... |
560 561 |
if (err) return err; |
b0188d4db [NET_SCHED]: sch_... |
562 |
tasklet_init(&p->task, sch_atm_dequeue, (unsigned long)sch); |
1da177e4c Linux-2.6.12-rc2 |
563 564 |
return 0; } |
1da177e4c Linux-2.6.12-rc2 |
565 566 |
static void atm_tc_reset(struct Qdisc *sch) { |
786a90366 [PKT_SCHED] sch_a... |
567 |
struct atm_qdisc_data *p = qdisc_priv(sch); |
1da177e4c Linux-2.6.12-rc2 |
568 |
struct atm_flow_data *flow; |
786a90366 [PKT_SCHED] sch_a... |
569 570 |
pr_debug("atm_tc_reset(sch %p,[qdisc %p]) ", sch, p); |
6accec76f sch_atm: Convert ... |
571 |
list_for_each_entry(flow, &p->flows, list) |
b0188d4db [NET_SCHED]: sch_... |
572 |
qdisc_reset(flow->q); |
1da177e4c Linux-2.6.12-rc2 |
573 574 |
sch->q.qlen = 0; } |
1da177e4c Linux-2.6.12-rc2 |
575 576 |
static void atm_tc_destroy(struct Qdisc *sch) { |
786a90366 [PKT_SCHED] sch_a... |
577 |
struct atm_qdisc_data *p = qdisc_priv(sch); |
6accec76f sch_atm: Convert ... |
578 |
struct atm_flow_data *flow, *tmp; |
1da177e4c Linux-2.6.12-rc2 |
579 |
|
786a90366 [PKT_SCHED] sch_a... |
580 581 |
pr_debug("atm_tc_destroy(sch %p,[qdisc %p]) ", sch, p); |
898904226 net_sched: reset ... |
582 |
list_for_each_entry(flow, &p->flows, list) { |
6529eaba3 net: sched: intro... |
583 |
tcf_block_put(flow->block); |
898904226 net_sched: reset ... |
584 585 |
flow->block = NULL; } |
a4aebb83c net-sched: fix fi... |
586 |
|
6accec76f sch_atm: Convert ... |
587 |
list_for_each_entry_safe(flow, tmp, &p->flows, list) { |
1da177e4c Linux-2.6.12-rc2 |
588 |
if (flow->ref > 1) |
cc7ec456f net_sched: cleanups |
589 590 |
pr_err("atm_destroy: %p->ref = %d ", flow, flow->ref); |
b0188d4db [NET_SCHED]: sch_... |
591 |
atm_tc_put(sch, (unsigned long)flow); |
1da177e4c Linux-2.6.12-rc2 |
592 593 594 |
} tasklet_kill(&p->task); } |
1da177e4c Linux-2.6.12-rc2 |
595 |
static int atm_tc_dump_class(struct Qdisc *sch, unsigned long cl, |
b0188d4db [NET_SCHED]: sch_... |
596 |
struct sk_buff *skb, struct tcmsg *tcm) |
1da177e4c Linux-2.6.12-rc2 |
597 |
{ |
786a90366 [PKT_SCHED] sch_a... |
598 |
struct atm_qdisc_data *p = qdisc_priv(sch); |
b0188d4db [NET_SCHED]: sch_... |
599 |
struct atm_flow_data *flow = (struct atm_flow_data *)cl; |
4b3550ef5 [NET_SCHED]: Use ... |
600 |
struct nlattr *nest; |
1da177e4c Linux-2.6.12-rc2 |
601 |
|
786a90366 [PKT_SCHED] sch_a... |
602 603 |
pr_debug("atm_tc_dump_class(sch %p,[qdisc %p],flow %p,skb %p,tcm %p) ", |
b0188d4db [NET_SCHED]: sch_... |
604 |
sch, p, flow, skb, tcm); |
6accec76f sch_atm: Convert ... |
605 |
if (list_empty(&flow->list)) |
b0188d4db [NET_SCHED]: sch_... |
606 |
return -EINVAL; |
f7ebdff75 net: sched: sch_a... |
607 |
tcm->tcm_handle = flow->common.classid; |
cdc7f8e36 [PKT_SCHED]: Dump... |
608 |
tcm->tcm_info = flow->q->handle; |
4b3550ef5 [NET_SCHED]: Use ... |
609 |
|
ae0be8de9 netlink: make nla... |
610 |
nest = nla_nest_start_noflag(skb, TCA_OPTIONS); |
4b3550ef5 [NET_SCHED]: Use ... |
611 612 |
if (nest == NULL) goto nla_put_failure; |
1b34ec43c pkt_sched: Stop u... |
613 614 |
if (nla_put(skb, TCA_ATM_HDR, flow->hdr_len, flow->hdr)) goto nla_put_failure; |
1da177e4c Linux-2.6.12-rc2 |
615 616 617 |
if (flow->vcc) { struct sockaddr_atmpvc pvc; int state; |
8cb3b9c36 net_sched: info l... |
618 |
memset(&pvc, 0, sizeof(pvc)); |
1da177e4c Linux-2.6.12-rc2 |
619 620 621 622 |
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; |
1b34ec43c pkt_sched: Stop u... |
623 624 |
if (nla_put(skb, TCA_ATM_ADDR, sizeof(pvc), &pvc)) goto nla_put_failure; |
1da177e4c Linux-2.6.12-rc2 |
625 |
state = ATM_VF2VS(flow->vcc->flags); |
1b34ec43c pkt_sched: Stop u... |
626 627 628 629 |
if (nla_put_u32(skb, TCA_ATM_STATE, state)) goto nla_put_failure; } if (flow->excess) { |
f7ebdff75 net: sched: sch_a... |
630 |
if (nla_put_u32(skb, TCA_ATM_EXCESS, flow->common.classid)) |
1b34ec43c pkt_sched: Stop u... |
631 632 633 634 |
goto nla_put_failure; } else { if (nla_put_u32(skb, TCA_ATM_EXCESS, 0)) goto nla_put_failure; |
1da177e4c Linux-2.6.12-rc2 |
635 |
} |
d59b7d805 net_sched: return... |
636 |
return nla_nest_end(skb, nest); |
1da177e4c Linux-2.6.12-rc2 |
637 |
|
1e90474c3 [NET_SCHED]: Conv... |
638 |
nla_put_failure: |
4b3550ef5 [NET_SCHED]: Use ... |
639 |
nla_nest_cancel(skb, nest); |
1da177e4c Linux-2.6.12-rc2 |
640 641 642 643 |
return -1; } static int atm_tc_dump_class_stats(struct Qdisc *sch, unsigned long arg, |
b0188d4db [NET_SCHED]: sch_... |
644 |
struct gnet_dump *d) |
1da177e4c Linux-2.6.12-rc2 |
645 |
{ |
b0188d4db [NET_SCHED]: sch_... |
646 |
struct atm_flow_data *flow = (struct atm_flow_data *)arg; |
1da177e4c Linux-2.6.12-rc2 |
647 |
|
edb09eb17 net: sched: do no... |
648 649 |
if (gnet_stats_copy_basic(qdisc_root_sleeping_running(sch), d, NULL, &flow->bstats) < 0 || |
b0ab6f927 net: sched: enabl... |
650 |
gnet_stats_copy_queue(d, NULL, &flow->qstats, flow->q->q.qlen) < 0) |
1da177e4c Linux-2.6.12-rc2 |
651 652 653 654 655 656 657 658 659 |
return -1; return 0; } static int atm_tc_dump(struct Qdisc *sch, struct sk_buff *skb) { return 0; } |
20fea08b5 [NET]: Move Qdisc... |
660 |
static const struct Qdisc_class_ops atm_class_ops = { |
b0188d4db [NET_SCHED]: sch_... |
661 662 |
.graft = atm_tc_graft, .leaf = atm_tc_leaf, |
143976ce9 net_sched: remove... |
663 |
.find = atm_tc_find, |
b0188d4db [NET_SCHED]: sch_... |
664 665 666 |
.change = atm_tc_change, .delete = atm_tc_delete, .walk = atm_tc_walk, |
6529eaba3 net: sched: intro... |
667 |
.tcf_block = atm_tc_tcf_block, |
b0188d4db [NET_SCHED]: sch_... |
668 669 670 671 |
.bind_tcf = atm_tc_bind_filter, .unbind_tcf = atm_tc_put, .dump = atm_tc_dump_class, .dump_stats = atm_tc_dump_class_stats, |
1da177e4c Linux-2.6.12-rc2 |
672 |
}; |
20fea08b5 [NET]: Move Qdisc... |
673 |
static struct Qdisc_ops atm_qdisc_ops __read_mostly = { |
b0188d4db [NET_SCHED]: sch_... |
674 675 676 677 678 |
.cl_ops = &atm_class_ops, .id = "atm", .priv_size = sizeof(struct atm_qdisc_data), .enqueue = atm_tc_enqueue, .dequeue = atm_tc_dequeue, |
8e3af9789 pkt_sched: Add qd... |
679 |
.peek = atm_tc_peek, |
b0188d4db [NET_SCHED]: sch_... |
680 681 682 683 684 |
.init = atm_tc_init, .reset = atm_tc_reset, .destroy = atm_tc_destroy, .dump = atm_tc_dump, .owner = THIS_MODULE, |
1da177e4c Linux-2.6.12-rc2 |
685 |
}; |
1da177e4c Linux-2.6.12-rc2 |
686 687 688 689 |
static int __init atm_init(void) { return register_qdisc(&atm_qdisc_ops); } |
10297b993 [NET] SCHED: Fix ... |
690 |
static void __exit atm_exit(void) |
1da177e4c Linux-2.6.12-rc2 |
691 692 693 694 695 696 697 |
{ unregister_qdisc(&atm_qdisc_ops); } module_init(atm_init) module_exit(atm_exit) MODULE_LICENSE("GPL"); |