Blame view

net/sched/act_api.c 23.8 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
  /*
   * net/sched/act_api.c	Packet action API.
   *
   *		This program is free software; you can redistribute it and/or
   *		modify it under the terms of the GNU General Public License
   *		as published by the Free Software Foundation; either version
   *		2 of the License, or (at your option) any later version.
   *
   * Author:	Jamal Hadi Salim
   *
   *
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
14
  #include <linux/types.h>
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
  #include <linux/string.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
  #include <linux/errno.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
17
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
  #include <linux/skbuff.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
20
  #include <linux/init.h>
  #include <linux/kmod.h>
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
21
  #include <linux/err.h>
3a9a231d9   Paul Gortmaker   net: Fix files ex...
22
  #include <linux/module.h>
b854272b3   Denis V. Lunev   [NET]: Modify all...
23
24
  #include <net/net_namespace.h>
  #include <net/sock.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
26
  #include <net/sch_generic.h>
  #include <net/act_api.h>
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
27
  #include <net/netlink.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28

519c818e8   Eric Dumazet   net: sched: add p...
29
30
  static void free_tcf(struct rcu_head *head)
  {
ec0595cc4   WANG Cong   net_sched: get ri...
31
  	struct tc_action *p = container_of(head, struct tc_action, tcfa_rcu);
519c818e8   Eric Dumazet   net: sched: add p...
32
33
34
35
36
  
  	free_percpu(p->cpu_bstats);
  	free_percpu(p->cpu_qstats);
  	kfree(p);
  }
ec0595cc4   WANG Cong   net_sched: get ri...
37
  static void tcf_hash_destroy(struct tcf_hashinfo *hinfo, struct tc_action *p)
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
38
  {
89819dc01   WANG Cong   net_sched: conver...
39
  	spin_lock_bh(&hinfo->lock);
ec0595cc4   WANG Cong   net_sched: get ri...
40
  	hlist_del(&p->tcfa_head);
89819dc01   WANG Cong   net_sched: conver...
41
  	spin_unlock_bh(&hinfo->lock);
ec0595cc4   WANG Cong   net_sched: get ri...
42
43
  	gen_kill_estimator(&p->tcfa_bstats,
  			   &p->tcfa_rate_est);
89819dc01   WANG Cong   net_sched: conver...
44
  	/*
ec0595cc4   WANG Cong   net_sched: get ri...
45
  	 * gen_estimator est_timer() might access p->tcfa_lock
89819dc01   WANG Cong   net_sched: conver...
46
47
  	 * or bstats, wait a RCU grace period before freeing p
  	 */
ec0595cc4   WANG Cong   net_sched: get ri...
48
  	call_rcu(&p->tcfa_rcu, free_tcf);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
49
  }
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
50

ec0595cc4   WANG Cong   net_sched: get ri...
51
  int __tcf_hash_release(struct tc_action *p, bool bind, bool strict)
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
52
53
54
55
56
  {
  	int ret = 0;
  
  	if (p) {
  		if (bind)
ec0595cc4   WANG Cong   net_sched: get ri...
57
58
  			p->tcfa_bindcnt--;
  		else if (strict && p->tcfa_bindcnt > 0)
55334a5db   WANG Cong   net_sched: act: r...
59
  			return -EPERM;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
60

ec0595cc4   WANG Cong   net_sched: get ri...
61
62
63
64
  		p->tcfa_refcnt--;
  		if (p->tcfa_bindcnt <= 0 && p->tcfa_refcnt <= 0) {
  			if (p->ops->cleanup)
  				p->ops->cleanup(p, bind);
ec0595cc4   WANG Cong   net_sched: get ri...
65
  			tcf_hash_destroy(p->hinfo, p);
1d4150c02   WANG Cong   net_sched: prepar...
66
  			ret = ACT_P_DELETED;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
67
68
  		}
  	}
28e6b67f0   Daniel Borkmann   net: sched: fix r...
69

e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
70
71
  	return ret;
  }
28e6b67f0   Daniel Borkmann   net: sched: fix r...
72
  EXPORT_SYMBOL(__tcf_hash_release);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
73

ddf97ccdd   WANG Cong   net_sched: add ne...
74
  static int tcf_dump_walker(struct tcf_hashinfo *hinfo, struct sk_buff *skb,
a85a970af   WANG Cong   net_sched: move t...
75
  			   struct netlink_callback *cb)
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
76
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
77
  	int err = 0, index = -1, i = 0, s_i = 0, n_i = 0;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
78
  	struct nlattr *nest;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
79

89819dc01   WANG Cong   net_sched: conver...
80
  	spin_lock_bh(&hinfo->lock);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
81
82
83
84
  
  	s_i = cb->args[0];
  
  	for (i = 0; i < (hinfo->hmask + 1); i++) {
a85a970af   WANG Cong   net_sched: move t...
85
  		struct hlist_head *head;
ec0595cc4   WANG Cong   net_sched: get ri...
86
  		struct tc_action *p;
a85a970af   WANG Cong   net_sched: move t...
87

89819dc01   WANG Cong   net_sched: conver...
88
  		head = &hinfo->htab[tcf_hash(i, hinfo->hmask)];
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
89

ec0595cc4   WANG Cong   net_sched: get ri...
90
  		hlist_for_each_entry_rcu(p, head, tcfa_head) {
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
91
92
93
  			index++;
  			if (index < s_i)
  				continue;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
94

a85a970af   WANG Cong   net_sched: move t...
95
  			nest = nla_nest_start(skb, n_i);
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
96
97
  			if (nest == NULL)
  				goto nla_put_failure;
ec0595cc4   WANG Cong   net_sched: get ri...
98
  			err = tcf_action_dump_1(skb, p, 0, 0);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
99
100
  			if (err < 0) {
  				index--;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
101
  				nlmsg_trim(skb, nest);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
102
103
  				goto done;
  			}
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
104
  			nla_nest_end(skb, nest);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
105
106
107
108
109
110
  			n_i++;
  			if (n_i >= TCA_ACT_MAX_PRIO)
  				goto done;
  		}
  	}
  done:
89819dc01   WANG Cong   net_sched: conver...
111
  	spin_unlock_bh(&hinfo->lock);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
112
113
114
  	if (n_i)
  		cb->args[0] += n_i;
  	return n_i;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
115
  nla_put_failure:
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
116
  	nla_nest_cancel(skb, nest);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
117
118
  	goto done;
  }
ddf97ccdd   WANG Cong   net_sched: add ne...
119
  static int tcf_del_walker(struct tcf_hashinfo *hinfo, struct sk_buff *skb,
a85a970af   WANG Cong   net_sched: move t...
120
  			  const struct tc_action_ops *ops)
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
121
  {
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
122
  	struct nlattr *nest;
cc7ec456f   Eric Dumazet   net_sched: cleanups
123
  	int i = 0, n_i = 0;
55334a5db   WANG Cong   net_sched: act: r...
124
  	int ret = -EINVAL;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
125

a85a970af   WANG Cong   net_sched: move t...
126
  	nest = nla_nest_start(skb, 0);
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
127
128
  	if (nest == NULL)
  		goto nla_put_failure;
a85a970af   WANG Cong   net_sched: move t...
129
  	if (nla_put_string(skb, TCA_KIND, ops->kind))
1b34ec43c   David S. Miller   pkt_sched: Stop u...
130
  		goto nla_put_failure;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
131
  	for (i = 0; i < (hinfo->hmask + 1); i++) {
a85a970af   WANG Cong   net_sched: move t...
132
133
  		struct hlist_head *head;
  		struct hlist_node *n;
ec0595cc4   WANG Cong   net_sched: get ri...
134
  		struct tc_action *p;
a85a970af   WANG Cong   net_sched: move t...
135

89819dc01   WANG Cong   net_sched: conver...
136
  		head = &hinfo->htab[tcf_hash(i, hinfo->hmask)];
ec0595cc4   WANG Cong   net_sched: get ri...
137
138
  		hlist_for_each_entry_safe(p, n, head, tcfa_head) {
  			ret = __tcf_hash_release(p, false, true);
55334a5db   WANG Cong   net_sched: act: r...
139
  			if (ret == ACT_P_DELETED) {
ec0595cc4   WANG Cong   net_sched: get ri...
140
  				module_put(p->ops->owner);
805c1f4ae   Jamal Hadi Salim   net_sched: act: a...
141
  				n_i++;
55334a5db   WANG Cong   net_sched: act: r...
142
143
  			} else if (ret < 0)
  				goto nla_put_failure;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
144
145
  		}
  	}
1b34ec43c   David S. Miller   pkt_sched: Stop u...
146
147
  	if (nla_put_u32(skb, TCA_FCNT, n_i))
  		goto nla_put_failure;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
148
  	nla_nest_end(skb, nest);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
149
150
  
  	return n_i;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
151
  nla_put_failure:
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
152
  	nla_nest_cancel(skb, nest);
55334a5db   WANG Cong   net_sched: act: r...
153
  	return ret;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
154
  }
ddf97ccdd   WANG Cong   net_sched: add ne...
155
156
  int tcf_generic_walker(struct tc_action_net *tn, struct sk_buff *skb,
  		       struct netlink_callback *cb, int type,
a85a970af   WANG Cong   net_sched: move t...
157
  		       const struct tc_action_ops *ops)
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
158
  {
ddf97ccdd   WANG Cong   net_sched: add ne...
159
  	struct tcf_hashinfo *hinfo = tn->hinfo;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
160
  	if (type == RTM_DELACTION) {
a85a970af   WANG Cong   net_sched: move t...
161
  		return tcf_del_walker(hinfo, skb, ops);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
162
  	} else if (type == RTM_GETACTION) {
a85a970af   WANG Cong   net_sched: move t...
163
  		return tcf_dump_walker(hinfo, skb, cb);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
164
  	} else {
6ff9c3644   stephen hemminger   net sched: printk...
165
166
  		WARN(1, "tcf_generic_walker: unknown action %d
  ", type);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
167
168
169
  		return -EINVAL;
  	}
  }
ddf97ccdd   WANG Cong   net_sched: add ne...
170
  EXPORT_SYMBOL(tcf_generic_walker);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
171

ec0595cc4   WANG Cong   net_sched: get ri...
172
  static struct tc_action *tcf_hash_lookup(u32 index, struct tcf_hashinfo *hinfo)
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
173
  {
ec0595cc4   WANG Cong   net_sched: get ri...
174
  	struct tc_action *p = NULL;
89819dc01   WANG Cong   net_sched: conver...
175
  	struct hlist_head *head;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
176

89819dc01   WANG Cong   net_sched: conver...
177
178
  	spin_lock_bh(&hinfo->lock);
  	head = &hinfo->htab[tcf_hash(index, hinfo->hmask)];
ec0595cc4   WANG Cong   net_sched: get ri...
179
180
  	hlist_for_each_entry_rcu(p, head, tcfa_head)
  		if (p->tcfa_index == index)
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
181
  			break;
89819dc01   WANG Cong   net_sched: conver...
182
  	spin_unlock_bh(&hinfo->lock);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
183
184
185
  
  	return p;
  }
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
186

ddf97ccdd   WANG Cong   net_sched: add ne...
187
  u32 tcf_hash_new_index(struct tc_action_net *tn)
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
188
  {
ddf97ccdd   WANG Cong   net_sched: add ne...
189
  	struct tcf_hashinfo *hinfo = tn->hinfo;
ddafd34f4   WANG Cong   net_sched: act: m...
190
  	u32 val = hinfo->index;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
191
192
193
194
195
  
  	do {
  		if (++val == 0)
  			val = 1;
  	} while (tcf_hash_lookup(val, hinfo));
ddafd34f4   WANG Cong   net_sched: act: m...
196
  	hinfo->index = val;
17569faed   Yang Yingliang   net_sched: remove...
197
  	return val;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
198
199
  }
  EXPORT_SYMBOL(tcf_hash_new_index);
a85a970af   WANG Cong   net_sched: move t...
200
  int tcf_hash_search(struct tc_action_net *tn, struct tc_action **a, u32 index)
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
201
  {
ddf97ccdd   WANG Cong   net_sched: add ne...
202
  	struct tcf_hashinfo *hinfo = tn->hinfo;
ec0595cc4   WANG Cong   net_sched: get ri...
203
  	struct tc_action *p = tcf_hash_lookup(index, hinfo);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
204
205
  
  	if (p) {
ec0595cc4   WANG Cong   net_sched: get ri...
206
  		*a = p;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
207
208
209
210
  		return 1;
  	}
  	return 0;
  }
6e6a50c25   WANG Cong   net_sched: act: e...
211
  EXPORT_SYMBOL(tcf_hash_search);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
212

a85a970af   WANG Cong   net_sched: move t...
213
  bool tcf_hash_check(struct tc_action_net *tn, u32 index, struct tc_action **a,
b2313077e   WANG Cong   net_sched: make t...
214
  		    int bind)
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
215
  {
ddf97ccdd   WANG Cong   net_sched: add ne...
216
  	struct tcf_hashinfo *hinfo = tn->hinfo;
ec0595cc4   WANG Cong   net_sched: get ri...
217
  	struct tc_action *p = NULL;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
218
  	if (index && (p = tcf_hash_lookup(index, hinfo)) != NULL) {
76aab2c1e   Jamal Hadi Salim   pkt_sched: Fix ac...
219
  		if (bind)
ec0595cc4   WANG Cong   net_sched: get ri...
220
221
222
  			p->tcfa_bindcnt++;
  		p->tcfa_refcnt++;
  		*a = p;
b2313077e   WANG Cong   net_sched: make t...
223
  		return true;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
224
  	}
b2313077e   WANG Cong   net_sched: make t...
225
  	return false;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
226
227
  }
  EXPORT_SYMBOL(tcf_hash_check);
86062033f   WANG Cong   net_sched: act: h...
228
229
  void tcf_hash_cleanup(struct tc_action *a, struct nlattr *est)
  {
86062033f   WANG Cong   net_sched: act: h...
230
  	if (est)
ec0595cc4   WANG Cong   net_sched: get ri...
231
232
233
  		gen_kill_estimator(&a->tcfa_bstats,
  				   &a->tcfa_rate_est);
  	call_rcu(&a->tcfa_rcu, free_tcf);
86062033f   WANG Cong   net_sched: act: h...
234
235
  }
  EXPORT_SYMBOL(tcf_hash_cleanup);
ddf97ccdd   WANG Cong   net_sched: add ne...
236
  int tcf_hash_create(struct tc_action_net *tn, u32 index, struct nlattr *est,
a85a970af   WANG Cong   net_sched: move t...
237
238
  		    struct tc_action **a, const struct tc_action_ops *ops,
  		    int bind, bool cpustats)
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
239
  {
ec0595cc4   WANG Cong   net_sched: get ri...
240
  	struct tc_action *p = kzalloc(ops->size, GFP_KERNEL);
ddf97ccdd   WANG Cong   net_sched: add ne...
241
  	struct tcf_hashinfo *hinfo = tn->hinfo;
519c818e8   Eric Dumazet   net: sched: add p...
242
  	int err = -ENOMEM;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
243
244
  
  	if (unlikely(!p))
86062033f   WANG Cong   net_sched: act: h...
245
  		return -ENOMEM;
ec0595cc4   WANG Cong   net_sched: get ri...
246
  	p->tcfa_refcnt = 1;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
247
  	if (bind)
ec0595cc4   WANG Cong   net_sched: get ri...
248
  		p->tcfa_bindcnt = 1;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
249

519c818e8   Eric Dumazet   net: sched: add p...
250
251
252
253
254
255
256
257
258
259
260
261
262
263
  	if (cpustats) {
  		p->cpu_bstats = netdev_alloc_pcpu_stats(struct gnet_stats_basic_cpu);
  		if (!p->cpu_bstats) {
  err1:
  			kfree(p);
  			return err;
  		}
  		p->cpu_qstats = alloc_percpu(struct gnet_stats_queue);
  		if (!p->cpu_qstats) {
  err2:
  			free_percpu(p->cpu_bstats);
  			goto err1;
  		}
  	}
ec0595cc4   WANG Cong   net_sched: get ri...
264
265
266
267
268
269
  	spin_lock_init(&p->tcfa_lock);
  	INIT_HLIST_NODE(&p->tcfa_head);
  	p->tcfa_index = index ? index : tcf_hash_new_index(tn);
  	p->tcfa_tm.install = jiffies;
  	p->tcfa_tm.lastuse = jiffies;
  	p->tcfa_tm.firstuse = 0;
0e991ec6a   Stephen Hemminger   tc: propogate err...
270
  	if (est) {
ec0595cc4   WANG Cong   net_sched: get ri...
271
272
273
  		err = gen_new_estimator(&p->tcfa_bstats, p->cpu_bstats,
  					&p->tcfa_rate_est,
  					&p->tcfa_lock, NULL, est);
0e991ec6a   Stephen Hemminger   tc: propogate err...
274
  		if (err) {
519c818e8   Eric Dumazet   net: sched: add p...
275
276
  			free_percpu(p->cpu_qstats);
  			goto err2;
0e991ec6a   Stephen Hemminger   tc: propogate err...
277
278
  		}
  	}
ec0595cc4   WANG Cong   net_sched: get ri...
279
280
281
282
  	p->hinfo = hinfo;
  	p->ops = ops;
  	INIT_LIST_HEAD(&p->list);
  	*a = p;
86062033f   WANG Cong   net_sched: act: h...
283
  	return 0;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
284
285
  }
  EXPORT_SYMBOL(tcf_hash_create);
ddf97ccdd   WANG Cong   net_sched: add ne...
286
  void tcf_hash_insert(struct tc_action_net *tn, struct tc_action *a)
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
287
  {
ddf97ccdd   WANG Cong   net_sched: add ne...
288
  	struct tcf_hashinfo *hinfo = tn->hinfo;
ec0595cc4   WANG Cong   net_sched: get ri...
289
  	unsigned int h = tcf_hash(a->tcfa_index, hinfo->hmask);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
290

89819dc01   WANG Cong   net_sched: conver...
291
  	spin_lock_bh(&hinfo->lock);
ec0595cc4   WANG Cong   net_sched: get ri...
292
  	hlist_add_head(&a->tcfa_head, &hinfo->htab[h]);
89819dc01   WANG Cong   net_sched: conver...
293
  	spin_unlock_bh(&hinfo->lock);
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
294
295
  }
  EXPORT_SYMBOL(tcf_hash_insert);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296

ddf97ccdd   WANG Cong   net_sched: add ne...
297
298
  void tcf_hashinfo_destroy(const struct tc_action_ops *ops,
  			  struct tcf_hashinfo *hinfo)
1d4150c02   WANG Cong   net_sched: prepar...
299
  {
1d4150c02   WANG Cong   net_sched: prepar...
300
301
302
  	int i;
  
  	for (i = 0; i < hinfo->hmask + 1; i++) {
ec0595cc4   WANG Cong   net_sched: get ri...
303
  		struct tc_action *p;
1d4150c02   WANG Cong   net_sched: prepar...
304
  		struct hlist_node *n;
ec0595cc4   WANG Cong   net_sched: get ri...
305
  		hlist_for_each_entry_safe(p, n, &hinfo->htab[i], tcfa_head) {
1d4150c02   WANG Cong   net_sched: prepar...
306
  			int ret;
ec0595cc4   WANG Cong   net_sched: get ri...
307
  			ret = __tcf_hash_release(p, false, true);
1d4150c02   WANG Cong   net_sched: prepar...
308
309
310
311
312
313
314
315
  			if (ret == ACT_P_DELETED)
  				module_put(ops->owner);
  			else if (ret < 0)
  				return;
  		}
  	}
  	kfree(hinfo->htab);
  }
ddf97ccdd   WANG Cong   net_sched: add ne...
316
  EXPORT_SYMBOL(tcf_hashinfo_destroy);
1d4150c02   WANG Cong   net_sched: prepar...
317

1f747c26c   WANG Cong   net_sched: conver...
318
  static LIST_HEAD(act_base);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319
  static DEFINE_RWLOCK(act_mod_lock);
ddf97ccdd   WANG Cong   net_sched: add ne...
320
321
  int tcf_register_action(struct tc_action_ops *act,
  			struct pernet_operations *ops)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
322
  {
1f747c26c   WANG Cong   net_sched: conver...
323
  	struct tc_action_ops *a;
ddf97ccdd   WANG Cong   net_sched: add ne...
324
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
325

ddf97ccdd   WANG Cong   net_sched: add ne...
326
  	if (!act->act || !act->dump || !act->init || !act->walk || !act->lookup)
76c82d7a3   Jamal Hadi Salim   net_sched: Fail i...
327
  		return -EINVAL;
ab102b80c   WANG Cong   net_sched: reorde...
328
329
330
331
332
333
334
  	/* We have to register pernet ops before making the action ops visible,
  	 * otherwise tcf_action_init_1() could get a partially initialized
  	 * netns.
  	 */
  	ret = register_pernet_subsys(ops);
  	if (ret)
  		return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
  	write_lock(&act_mod_lock);
1f747c26c   WANG Cong   net_sched: conver...
336
  	list_for_each_entry(a, &act_base, head) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
337
338
  		if (act->type == a->type || (strcmp(act->kind, a->kind) == 0)) {
  			write_unlock(&act_mod_lock);
ab102b80c   WANG Cong   net_sched: reorde...
339
  			unregister_pernet_subsys(ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
341
342
  			return -EEXIST;
  		}
  	}
1f747c26c   WANG Cong   net_sched: conver...
343
  	list_add_tail(&act->head, &act_base);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344
  	write_unlock(&act_mod_lock);
ddf97ccdd   WANG Cong   net_sched: add ne...
345

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
346
347
  	return 0;
  }
62e3ba1b5   Patrick McHardy   [NET_SCHED]: Move...
348
  EXPORT_SYMBOL(tcf_register_action);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
349

ddf97ccdd   WANG Cong   net_sched: add ne...
350
351
  int tcf_unregister_action(struct tc_action_ops *act,
  			  struct pernet_operations *ops)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
352
  {
1f747c26c   WANG Cong   net_sched: conver...
353
  	struct tc_action_ops *a;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
354
355
356
  	int err = -ENOENT;
  
  	write_lock(&act_mod_lock);
a792866ad   Eric Dumazet   net_sched: fix re...
357
358
359
360
  	list_for_each_entry(a, &act_base, head) {
  		if (a == act) {
  			list_del(&act->head);
  			err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
  			break;
a792866ad   Eric Dumazet   net_sched: fix re...
362
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
364
  	}
  	write_unlock(&act_mod_lock);
ab102b80c   WANG Cong   net_sched: reorde...
365
366
  	if (!err)
  		unregister_pernet_subsys(ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
367
368
  	return err;
  }
62e3ba1b5   Patrick McHardy   [NET_SCHED]: Move...
369
  EXPORT_SYMBOL(tcf_unregister_action);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
370
371
372
373
  
  /* lookup by name */
  static struct tc_action_ops *tc_lookup_action_n(char *kind)
  {
a792866ad   Eric Dumazet   net_sched: fix re...
374
  	struct tc_action_ops *a, *res = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
376
377
  
  	if (kind) {
  		read_lock(&act_mod_lock);
1f747c26c   WANG Cong   net_sched: conver...
378
  		list_for_each_entry(a, &act_base, head) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
  			if (strcmp(kind, a->kind) == 0) {
a792866ad   Eric Dumazet   net_sched: fix re...
380
381
  				if (try_module_get(a->owner))
  					res = a;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
383
384
385
386
  				break;
  			}
  		}
  		read_unlock(&act_mod_lock);
  	}
a792866ad   Eric Dumazet   net_sched: fix re...
387
  	return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
388
  }
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
389
390
  /* lookup by nlattr */
  static struct tc_action_ops *tc_lookup_action(struct nlattr *kind)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
391
  {
a792866ad   Eric Dumazet   net_sched: fix re...
392
  	struct tc_action_ops *a, *res = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
393
394
395
  
  	if (kind) {
  		read_lock(&act_mod_lock);
1f747c26c   WANG Cong   net_sched: conver...
396
  		list_for_each_entry(a, &act_base, head) {
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
397
  			if (nla_strcmp(kind, a->kind) == 0) {
a792866ad   Eric Dumazet   net_sched: fix re...
398
399
  				if (try_module_get(a->owner))
  					res = a;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
400
401
402
403
404
  				break;
  			}
  		}
  		read_unlock(&act_mod_lock);
  	}
a792866ad   Eric Dumazet   net_sched: fix re...
405
  	return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
407

22dc13c83   WANG Cong   net_sched: conver...
408
409
  int tcf_action_exec(struct sk_buff *skb, struct tc_action **actions,
  		    int nr_actions, struct tcf_result *res)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
410
  {
22dc13c83   WANG Cong   net_sched: conver...
411
  	int ret = -1, i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
413
414
  
  	if (skb->tc_verd & TC_NCLS) {
  		skb->tc_verd = CLR_TC_NCLS(skb->tc_verd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
415
416
417
  		ret = TC_ACT_OK;
  		goto exec_done;
  	}
22dc13c83   WANG Cong   net_sched: conver...
418
419
  	for (i = 0; i < nr_actions; i++) {
  		const struct tc_action *a = actions[i];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
420
  repeat:
63acd6807   Jamal Hadi Salim   net_sched: Remove...
421
  		ret = a->ops->act(skb, a, res);
63acd6807   Jamal Hadi Salim   net_sched: Remove...
422
423
424
425
  		if (ret == TC_ACT_REPEAT)
  			goto repeat;	/* we need a ttl - JHS */
  		if (ret != TC_ACT_PIPE)
  			goto exec_done;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
426
427
  	}
  exec_done:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
428
429
  	return ret;
  }
62e3ba1b5   Patrick McHardy   [NET_SCHED]: Move...
430
  EXPORT_SYMBOL(tcf_action_exec);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
431

55334a5db   WANG Cong   net_sched: act: r...
432
  int tcf_action_destroy(struct list_head *actions, int bind)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
433
  {
33be62715   WANG Cong   net_sched: act: u...
434
  	struct tc_action *a, *tmp;
55334a5db   WANG Cong   net_sched: act: r...
435
  	int ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
436

33be62715   WANG Cong   net_sched: act: u...
437
  	list_for_each_entry_safe(a, tmp, actions, list) {
28e6b67f0   Daniel Borkmann   net: sched: fix r...
438
  		ret = __tcf_hash_release(a, bind, true);
55334a5db   WANG Cong   net_sched: act: r...
439
  		if (ret == ACT_P_DELETED)
63acd6807   Jamal Hadi Salim   net_sched: Remove...
440
  			module_put(a->ops->owner);
55334a5db   WANG Cong   net_sched: act: r...
441
442
  		else if (ret < 0)
  			return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
443
  	}
55334a5db   WANG Cong   net_sched: act: r...
444
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
445
446
447
448
449
  }
  
  int
  tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
450
451
452
453
454
455
456
  	return a->ops->dump(skb, a, bind, ref);
  }
  
  int
  tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
  {
  	int err = -EINVAL;
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
457
  	unsigned char *b = skb_tail_pointer(skb);
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
458
  	struct nlattr *nest;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
459

1b34ec43c   David S. Miller   pkt_sched: Stop u...
460
461
  	if (nla_put_string(skb, TCA_KIND, a->ops->kind))
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462
  	if (tcf_action_copy_stats(skb, a, 0))
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
463
  		goto nla_put_failure;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
464
465
466
  	nest = nla_nest_start(skb, TCA_OPTIONS);
  	if (nest == NULL)
  		goto nla_put_failure;
cc7ec456f   Eric Dumazet   net_sched: cleanups
467
468
  	err = tcf_action_dump_old(skb, a, bind, ref);
  	if (err > 0) {
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
469
  		nla_nest_end(skb, nest);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
470
471
  		return err;
  	}
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
472
  nla_put_failure:
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
473
  	nlmsg_trim(skb, b);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
474
475
  	return -1;
  }
62e3ba1b5   Patrick McHardy   [NET_SCHED]: Move...
476
  EXPORT_SYMBOL(tcf_action_dump_1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
477

0b0f43fe2   Jamal Hadi Salim   net sched: indent...
478
479
  int tcf_action_dump(struct sk_buff *skb, struct list_head *actions,
  		    int bind, int ref)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
480
481
482
  {
  	struct tc_action *a;
  	int err = -EINVAL;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
483
  	struct nlattr *nest;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
484

33be62715   WANG Cong   net_sched: act: u...
485
  	list_for_each_entry(a, actions, list) {
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
486
487
488
  		nest = nla_nest_start(skb, a->order);
  		if (nest == NULL)
  			goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
490
  		err = tcf_action_dump_1(skb, a, bind, ref);
  		if (err < 0)
4fe683f50   Thomas Graf   [PKT_SCHED]: Fix ...
491
  			goto errout;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
492
  		nla_nest_end(skb, nest);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
493
494
495
  	}
  
  	return 0;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
496
  nla_put_failure:
4fe683f50   Thomas Graf   [PKT_SCHED]: Fix ...
497
498
  	err = -EINVAL;
  errout:
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
499
  	nla_nest_cancel(skb, nest);
4fe683f50   Thomas Graf   [PKT_SCHED]: Fix ...
500
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501
  }
c1b52739e   Benjamin LaHaise   pkt_sched: namesp...
502
503
504
  struct tc_action *tcf_action_init_1(struct net *net, struct nlattr *nla,
  				    struct nlattr *est, char *name, int ovr,
  				    int bind)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505
506
507
508
  {
  	struct tc_action *a;
  	struct tc_action_ops *a_o;
  	char act_name[IFNAMSIZ];
cc7ec456f   Eric Dumazet   net_sched: cleanups
509
  	struct nlattr *tb[TCA_ACT_MAX + 1];
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
510
  	struct nlattr *kind;
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
511
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
512

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513
  	if (name == NULL) {
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
514
515
  		err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL);
  		if (err < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
516
  			goto err_out;
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
517
  		err = -EINVAL;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
518
  		kind = tb[TCA_ACT_KIND];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
519
520
  		if (kind == NULL)
  			goto err_out;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
521
  		if (nla_strlcpy(act_name, kind, IFNAMSIZ) >= IFNAMSIZ)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
522
523
  			goto err_out;
  	} else {
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
524
  		err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
525
526
527
528
529
530
  		if (strlcpy(act_name, name, IFNAMSIZ) >= IFNAMSIZ)
  			goto err_out;
  	}
  
  	a_o = tc_lookup_action_n(act_name);
  	if (a_o == NULL) {
95a5afca4   Johannes Berg   net: Remove CONFI...
531
  #ifdef CONFIG_MODULES
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
532
  		rtnl_unlock();
4bba39259   Patrick McHardy   [PKT_SCHED]: Pref...
533
  		request_module("act_%s", act_name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
534
535
536
537
538
539
540
541
542
543
544
  		rtnl_lock();
  
  		a_o = tc_lookup_action_n(act_name);
  
  		/* We dropped the RTNL semaphore in order to
  		 * perform the module load.  So, even if we
  		 * succeeded in loading the module we have to
  		 * tell the caller to replay the request.  We
  		 * indicate this using -EAGAIN.
  		 */
  		if (a_o != NULL) {
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
545
  			err = -EAGAIN;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
546
547
548
  			goto err_mod;
  		}
  #endif
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
549
  		err = -ENOENT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
550
551
  		goto err_out;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
552
553
  	/* backward compatibility for policer */
  	if (name == NULL)
a85a970af   WANG Cong   net_sched: move t...
554
  		err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, &a, ovr, bind);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555
  	else
a85a970af   WANG Cong   net_sched: move t...
556
  		err = a_o->init(net, nla, est, &a, ovr, bind);
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
557
  	if (err < 0)
a85a970af   WANG Cong   net_sched: move t...
558
  		goto err_mod;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
559
560
  
  	/* module count goes up only when brand new policy is created
cc7ec456f   Eric Dumazet   net_sched: cleanups
561
562
563
  	 * if it exists and is only bound to in a_o->init() then
  	 * ACT_P_CREATED is not returned (a zero is).
  	 */
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
564
  	if (err != ACT_P_CREATED)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
565
  		module_put(a_o->owner);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
566

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
567
  	return a;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
568
569
570
  err_mod:
  	module_put(a_o->owner);
  err_out:
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
571
  	return ERR_PTR(err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
572
  }
aecc5cefc   Jamal Hadi Salim   net sched actions...
573
574
575
576
577
578
579
580
581
582
  static void cleanup_a(struct list_head *actions, int ovr)
  {
  	struct tc_action *a;
  
  	if (!ovr)
  		return;
  
  	list_for_each_entry(a, actions, list)
  		a->tcfa_refcnt--;
  }
5a7a5555a   Jamal Hadi Salim   net sched: stylis...
583
584
  int tcf_action_init(struct net *net, struct nlattr *nla, struct nlattr *est,
  		    char *name, int ovr, int bind, struct list_head *actions)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
585
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
586
  	struct nlattr *tb[TCA_ACT_MAX_PRIO + 1];
33be62715   WANG Cong   net_sched: act: u...
587
  	struct tc_action *act;
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
588
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
589
  	int i;
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
590
591
  	err = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL);
  	if (err < 0)
33be62715   WANG Cong   net_sched: act: u...
592
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
593

7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
594
  	for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) {
c1b52739e   Benjamin LaHaise   pkt_sched: namesp...
595
  		act = tcf_action_init_1(net, tb[i], est, name, ovr, bind);
33be62715   WANG Cong   net_sched: act: u...
596
597
  		if (IS_ERR(act)) {
  			err = PTR_ERR(act);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598
  			goto err;
33be62715   WANG Cong   net_sched: act: u...
599
  		}
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
600
  		act->order = i;
aecc5cefc   Jamal Hadi Salim   net sched actions...
601
602
  		if (ovr)
  			act->tcfa_refcnt++;
33be62715   WANG Cong   net_sched: act: u...
603
  		list_add_tail(&act->list, actions);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
604
  	}
aecc5cefc   Jamal Hadi Salim   net sched actions...
605
606
607
608
609
  
  	/* Remove the temp refcnt which was necessary to protect against
  	 * destroying an existing action which was being replaced
  	 */
  	cleanup_a(actions, ovr);
33be62715   WANG Cong   net_sched: act: u...
610
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
611
612
  
  err:
33be62715   WANG Cong   net_sched: act: u...
613
614
  	tcf_action_destroy(actions, bind);
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
615
  }
ec0595cc4   WANG Cong   net_sched: get ri...
616
  int tcf_action_copy_stats(struct sk_buff *skb, struct tc_action *p,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
617
618
619
620
  			  int compat_mode)
  {
  	int err = 0;
  	struct gnet_dump d;
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
621

7eb8896df   WANG Cong   net_sched: act: r...
622
  	if (p == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
623
624
625
  		goto errout;
  
  	/* compat_mode being true specifies a call that is supposed
06fe9fb41   Dirk Hohndel   tree-wide: fix a ...
626
  	 * to add additional backward compatibility statistic TLVs.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
627
628
  	 */
  	if (compat_mode) {
ec0595cc4   WANG Cong   net_sched: get ri...
629
  		if (p->type == TCA_OLD_COMPAT)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
630
  			err = gnet_stats_start_copy_compat(skb, 0,
9854518ea   Nicolas Dichtel   sched: align nlat...
631
632
  							   TCA_STATS,
  							   TCA_XSTATS,
ec0595cc4   WANG Cong   net_sched: get ri...
633
  							   &p->tcfa_lock, &d,
9854518ea   Nicolas Dichtel   sched: align nlat...
634
  							   TCA_PAD);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
635
636
637
638
  		else
  			return 0;
  	} else
  		err = gnet_stats_start_copy(skb, TCA_ACT_STATS,
ec0595cc4   WANG Cong   net_sched: get ri...
639
  					    &p->tcfa_lock, &d, TCA_ACT_PAD);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
640
641
642
  
  	if (err < 0)
  		goto errout;
ec0595cc4   WANG Cong   net_sched: get ri...
643
644
645
  	if (gnet_stats_copy_basic(NULL, &d, p->cpu_bstats, &p->tcfa_bstats) < 0 ||
  	    gnet_stats_copy_rate_est(&d, &p->tcfa_bstats,
  				     &p->tcfa_rate_est) < 0 ||
519c818e8   Eric Dumazet   net: sched: add p...
646
  	    gnet_stats_copy_queue(&d, p->cpu_qstats,
ec0595cc4   WANG Cong   net_sched: get ri...
647
648
  				  &p->tcfa_qstats,
  				  p->tcfa_qstats.qlen) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
649
650
651
652
653
654
655
656
657
658
  		goto errout;
  
  	if (gnet_stats_finish_copy(&d) < 0)
  		goto errout;
  
  	return 0;
  
  errout:
  	return -1;
  }
0b0f43fe2   Jamal Hadi Salim   net sched: indent...
659
660
661
  static int tca_get_fill(struct sk_buff *skb, struct list_head *actions,
  			u32 portid, u32 seq, u16 flags, int event, int bind,
  			int ref)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
662
663
664
  {
  	struct tcamsg *t;
  	struct nlmsghdr *nlh;
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
665
  	unsigned char *b = skb_tail_pointer(skb);
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
666
  	struct nlattr *nest;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
667

15e473046   Eric W. Biederman   netlink: Rename p...
668
  	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*t), flags);
8b00a53c6   David S. Miller   pkt_sched: act_ap...
669
670
671
  	if (!nlh)
  		goto out_nlmsg_trim;
  	t = nlmsg_data(nlh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
672
  	t->tca_family = AF_UNSPEC;
9ef1d4c7c   Patrick McHardy   [NETLINK]: Missin...
673
674
  	t->tca__pad1 = 0;
  	t->tca__pad2 = 0;
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
675

4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
676
677
  	nest = nla_nest_start(skb, TCA_ACT_TAB);
  	if (nest == NULL)
8b00a53c6   David S. Miller   pkt_sched: act_ap...
678
  		goto out_nlmsg_trim;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
679

33be62715   WANG Cong   net_sched: act: u...
680
  	if (tcf_action_dump(skb, actions, bind, ref) < 0)
8b00a53c6   David S. Miller   pkt_sched: act_ap...
681
  		goto out_nlmsg_trim;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
682

4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
683
  	nla_nest_end(skb, nest);
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
684

27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
685
  	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
686
  	return skb->len;
8b00a53c6   David S. Miller   pkt_sched: act_ap...
687
  out_nlmsg_trim:
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
688
  	nlmsg_trim(skb, b);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
689
690
691
692
  	return -1;
  }
  
  static int
15e473046   Eric W. Biederman   netlink: Rename p...
693
  act_get_notify(struct net *net, u32 portid, struct nlmsghdr *n,
33be62715   WANG Cong   net_sched: act: u...
694
  	       struct list_head *actions, int event)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
695
696
  {
  	struct sk_buff *skb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
697
698
699
700
  
  	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
  	if (!skb)
  		return -ENOBUFS;
0b0f43fe2   Jamal Hadi Salim   net sched: indent...
701
702
  	if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, event,
  			 0, 0) <= 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
703
704
705
  		kfree_skb(skb);
  		return -EINVAL;
  	}
2942e9005   Thomas Graf   [RTNETLINK]: Use ...
706

15e473046   Eric W. Biederman   netlink: Rename p...
707
  	return rtnl_unicast(skb, net, portid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
708
  }
ddf97ccdd   WANG Cong   net_sched: add ne...
709
710
  static struct tc_action *tcf_action_get_1(struct net *net, struct nlattr *nla,
  					  struct nlmsghdr *n, u32 portid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
711
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
712
  	struct nlattr *tb[TCA_ACT_MAX + 1];
a85a970af   WANG Cong   net_sched: move t...
713
  	const struct tc_action_ops *ops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
714
715
  	struct tc_action *a;
  	int index;
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
716
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
717

cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
718
719
  	err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL);
  	if (err < 0)
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
720
  		goto err_out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
721

cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
722
  	err = -EINVAL;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
723
724
  	if (tb[TCA_ACT_INDEX] == NULL ||
  	    nla_len(tb[TCA_ACT_INDEX]) < sizeof(index))
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
725
  		goto err_out;
1587bac49   Patrick McHardy   [NET_SCHED]: Use ...
726
  	index = nla_get_u32(tb[TCA_ACT_INDEX]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
727

ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
728
  	err = -EINVAL;
a85a970af   WANG Cong   net_sched: move t...
729
730
731
  	ops = tc_lookup_action(tb[TCA_ACT_KIND]);
  	if (!ops) /* could happen in batch of actions */
  		goto err_out;
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
732
  	err = -ENOENT;
a85a970af   WANG Cong   net_sched: move t...
733
  	if (ops->lookup(net, &a, index) == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
734
  		goto err_mod;
a85a970af   WANG Cong   net_sched: move t...
735
  	module_put(ops->owner);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
736
  	return a;
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
737

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
738
  err_mod:
a85a970af   WANG Cong   net_sched: move t...
739
  	module_put(ops->owner);
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
740
741
  err_out:
  	return ERR_PTR(err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
742
  }
7316ae88c   Tom Goff   net_sched: make t...
743
  static int tca_action_flush(struct net *net, struct nlattr *nla,
15e473046   Eric W. Biederman   netlink: Rename p...
744
  			    struct nlmsghdr *n, u32 portid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
745
746
747
748
749
750
  {
  	struct sk_buff *skb;
  	unsigned char *b;
  	struct nlmsghdr *nlh;
  	struct tcamsg *t;
  	struct netlink_callback dcb;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
751
  	struct nlattr *nest;
cc7ec456f   Eric Dumazet   net_sched: cleanups
752
  	struct nlattr *tb[TCA_ACT_MAX + 1];
a85a970af   WANG Cong   net_sched: move t...
753
  	const struct tc_action_ops *ops;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
754
  	struct nlattr *kind;
36723873b   Jamal Hadi Salim   net-sched: fix Ac...
755
  	int err = -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
756

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
757
758
  	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
  	if (!skb) {
6ff9c3644   stephen hemminger   net sched: printk...
759
760
  		pr_debug("tca_action_flush: failed skb alloc
  ");
36723873b   Jamal Hadi Salim   net-sched: fix Ac...
761
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
762
  	}
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
763
  	b = skb_tail_pointer(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
764

cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
765
766
  	err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL);
  	if (err < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
767
  		goto err_out;
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
768
  	err = -EINVAL;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
769
  	kind = tb[TCA_ACT_KIND];
a85a970af   WANG Cong   net_sched: move t...
770
771
  	ops = tc_lookup_action(kind);
  	if (!ops) /*some idjot trying to flush unknown action */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
772
  		goto err_out;
0b0f43fe2   Jamal Hadi Salim   net sched: indent...
773
774
  	nlh = nlmsg_put(skb, portid, n->nlmsg_seq, RTM_DELACTION,
  			sizeof(*t), 0);
8b00a53c6   David S. Miller   pkt_sched: act_ap...
775
776
777
  	if (!nlh)
  		goto out_module_put;
  	t = nlmsg_data(nlh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
778
  	t->tca_family = AF_UNSPEC;
9ef1d4c7c   Patrick McHardy   [NETLINK]: Missin...
779
780
  	t->tca__pad1 = 0;
  	t->tca__pad2 = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
781

4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
782
783
  	nest = nla_nest_start(skb, TCA_ACT_TAB);
  	if (nest == NULL)
8b00a53c6   David S. Miller   pkt_sched: act_ap...
784
  		goto out_module_put;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
785

a85a970af   WANG Cong   net_sched: move t...
786
  	err = ops->walk(net, skb, &dcb, RTM_DELACTION, ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
787
  	if (err < 0)
8b00a53c6   David S. Miller   pkt_sched: act_ap...
788
  		goto out_module_put;
f97017cde   Jamal Hadi Salim   net-sched: Fix ac...
789
790
  	if (err == 0)
  		goto noflush_out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
791

4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
792
  	nla_nest_end(skb, nest);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
793

27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
794
  	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
795
  	nlh->nlmsg_flags |= NLM_F_ROOT;
a85a970af   WANG Cong   net_sched: move t...
796
  	module_put(ops->owner);
15e473046   Eric W. Biederman   netlink: Rename p...
797
  	err = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
cc7ec456f   Eric Dumazet   net_sched: cleanups
798
  			     n->nlmsg_flags & NLM_F_ECHO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
799
800
801
802
  	if (err > 0)
  		return 0;
  
  	return err;
8b00a53c6   David S. Miller   pkt_sched: act_ap...
803
  out_module_put:
a85a970af   WANG Cong   net_sched: move t...
804
  	module_put(ops->owner);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
805
  err_out:
f97017cde   Jamal Hadi Salim   net-sched: Fix ac...
806
  noflush_out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
807
  	kfree_skb(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
808
809
810
811
  	return err;
  }
  
  static int
a56e19538   WANG Cong   net_sched: act: c...
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
  tcf_del_notify(struct net *net, struct nlmsghdr *n, struct list_head *actions,
  	       u32 portid)
  {
  	int ret;
  	struct sk_buff *skb;
  
  	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
  	if (!skb)
  		return -ENOBUFS;
  
  	if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, RTM_DELACTION,
  			 0, 1) <= 0) {
  		kfree_skb(skb);
  		return -EINVAL;
  	}
  
  	/* now do the delete */
55334a5db   WANG Cong   net_sched: act: r...
829
830
831
832
833
  	ret = tcf_action_destroy(actions, 0);
  	if (ret < 0) {
  		kfree_skb(skb);
  		return ret;
  	}
a56e19538   WANG Cong   net_sched: act: c...
834
835
836
837
838
839
840
841
842
  
  	ret = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
  			     n->nlmsg_flags & NLM_F_ECHO);
  	if (ret > 0)
  		return 0;
  	return ret;
  }
  
  static int
7316ae88c   Tom Goff   net_sched: make t...
843
  tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
15e473046   Eric W. Biederman   netlink: Rename p...
844
  	      u32 portid, int event)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
845
  {
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
846
  	int i, ret;
cc7ec456f   Eric Dumazet   net_sched: cleanups
847
  	struct nlattr *tb[TCA_ACT_MAX_PRIO + 1];
33be62715   WANG Cong   net_sched: act: u...
848
849
  	struct tc_action *act;
  	LIST_HEAD(actions);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
850

cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
851
852
853
  	ret = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL);
  	if (ret < 0)
  		return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
854

cc7ec456f   Eric Dumazet   net_sched: cleanups
855
  	if (event == RTM_DELACTION && n->nlmsg_flags & NLM_F_ROOT) {
f97017cde   Jamal Hadi Salim   net-sched: Fix ac...
856
  		if (tb[1] != NULL)
15e473046   Eric W. Biederman   netlink: Rename p...
857
  			return tca_action_flush(net, tb[1], n, portid);
f97017cde   Jamal Hadi Salim   net-sched: Fix ac...
858
859
  		else
  			return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
860
  	}
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
861
  	for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) {
ddf97ccdd   WANG Cong   net_sched: add ne...
862
  		act = tcf_action_get_1(net, tb[i], n, portid);
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
863
864
  		if (IS_ERR(act)) {
  			ret = PTR_ERR(act);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
865
  			goto err;
ab27cfb85   Patrick McHardy   [NET_SCHED]: act_...
866
  		}
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
867
  		act->order = i;
33be62715   WANG Cong   net_sched: act: u...
868
  		list_add_tail(&act->list, &actions);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
869
870
871
  	}
  
  	if (event == RTM_GETACTION)
33be62715   WANG Cong   net_sched: act: u...
872
  		ret = act_get_notify(net, portid, n, &actions, event);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
873
  	else { /* delete */
a56e19538   WANG Cong   net_sched: act: c...
874
875
  		ret = tcf_del_notify(net, n, &actions, portid);
  		if (ret)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
876
  			goto err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
877
878
879
  		return ret;
  	}
  err:
b260a714a   Jamal Hadi Salim   net sched actions...
880
881
  	if (event != RTM_GETACTION)
  		tcf_action_destroy(&actions, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
882
883
  	return ret;
  }
a56e19538   WANG Cong   net_sched: act: c...
884
885
886
  static int
  tcf_add_notify(struct net *net, struct nlmsghdr *n, struct list_head *actions,
  	       u32 portid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
887
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
888
  	struct sk_buff *skb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
889
890
891
892
893
  	int err = 0;
  
  	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
  	if (!skb)
  		return -ENOBUFS;
a56e19538   WANG Cong   net_sched: act: c...
894
895
896
897
898
  	if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, n->nlmsg_flags,
  			 RTM_NEWACTION, 0, 0) <= 0) {
  		kfree_skb(skb);
  		return -EINVAL;
  	}
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
899

a56e19538   WANG Cong   net_sched: act: c...
900
901
  	err = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
  			     n->nlmsg_flags & NLM_F_ECHO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
902
903
904
  	if (err > 0)
  		err = 0;
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
905
  }
5a7a5555a   Jamal Hadi Salim   net sched: stylis...
906
907
  static int tcf_action_add(struct net *net, struct nlattr *nla,
  			  struct nlmsghdr *n, u32 portid, int ovr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
908
909
  {
  	int ret = 0;
33be62715   WANG Cong   net_sched: act: u...
910
  	LIST_HEAD(actions);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
911

33be62715   WANG Cong   net_sched: act: u...
912
913
  	ret = tcf_action_init(net, nla, NULL, NULL, ovr, 0, &actions);
  	if (ret)
f07fed82a   WANG Cong   net_sched: remove...
914
  		return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
915

f07fed82a   WANG Cong   net_sched: remove...
916
  	return tcf_add_notify(net, n, &actions, portid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
917
  }
661d2967b   Thomas Graf   rtnetlink: Remove...
918
  static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
919
  {
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
920
  	struct net *net = sock_net(skb->sk);
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
921
  	struct nlattr *tca[TCA_ACT_MAX + 1];
15e473046   Eric W. Biederman   netlink: Rename p...
922
  	u32 portid = skb ? NETLINK_CB(skb).portid : 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
923
  	int ret = 0, ovr = 0;
0b0f43fe2   Jamal Hadi Salim   net sched: indent...
924
925
  	if ((n->nlmsg_type != RTM_GETACTION) &&
  	    !netlink_capable(skb, CAP_NET_ADMIN))
dfc47ef86   Eric W. Biederman   net: Push capable...
926
  		return -EPERM;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
927
928
929
930
931
  	ret = nlmsg_parse(n, sizeof(struct tcamsg), tca, TCA_ACT_MAX, NULL);
  	if (ret < 0)
  		return ret;
  
  	if (tca[TCA_ACT_TAB] == NULL) {
6ff9c3644   stephen hemminger   net sched: printk...
932
933
  		pr_notice("tc_ctl_action: received NO action attribs
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
934
935
  		return -EINVAL;
  	}
cc7ec456f   Eric Dumazet   net_sched: cleanups
936
  	/* n->nlmsg_flags & NLM_F_CREATE */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
937
938
939
  	switch (n->nlmsg_type) {
  	case RTM_NEWACTION:
  		/* we are going to assume all other flags
25985edce   Lucas De Marchi   Fix common misspe...
940
  		 * imply create only if it doesn't exist
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
941
942
943
944
  		 * Note that CREATE | EXCL implies that
  		 * but since we want avoid ambiguity (eg when flags
  		 * is zero) then just set this
  		 */
cc7ec456f   Eric Dumazet   net_sched: cleanups
945
  		if (n->nlmsg_flags & NLM_F_REPLACE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
946
947
  			ovr = 1;
  replay:
15e473046   Eric W. Biederman   netlink: Rename p...
948
  		ret = tcf_action_add(net, tca[TCA_ACT_TAB], n, portid, ovr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
949
950
951
952
  		if (ret == -EAGAIN)
  			goto replay;
  		break;
  	case RTM_DELACTION:
7316ae88c   Tom Goff   net_sched: make t...
953
  		ret = tca_action_gd(net, tca[TCA_ACT_TAB], n,
15e473046   Eric W. Biederman   netlink: Rename p...
954
  				    portid, RTM_DELACTION);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
955
956
  		break;
  	case RTM_GETACTION:
7316ae88c   Tom Goff   net_sched: make t...
957
  		ret = tca_action_gd(net, tca[TCA_ACT_TAB], n,
15e473046   Eric W. Biederman   netlink: Rename p...
958
  				    portid, RTM_GETACTION);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
959
960
961
962
963
964
965
  		break;
  	default:
  		BUG();
  	}
  
  	return ret;
  }
5a7a5555a   Jamal Hadi Salim   net sched: stylis...
966
  static struct nlattr *find_dump_kind(const struct nlmsghdr *n)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
967
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
968
  	struct nlattr *tb1, *tb2[TCA_ACT_MAX + 1];
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
969
970
971
  	struct nlattr *tb[TCA_ACT_MAX_PRIO + 1];
  	struct nlattr *nla[TCAA_MAX + 1];
  	struct nlattr *kind;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
972

c96c9471d   Patrick McHardy   [NET_SCHED]: act_...
973
  	if (nlmsg_parse(n, sizeof(struct tcamsg), nla, TCAA_MAX, NULL) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
974
  		return NULL;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
975
  	tb1 = nla[TCA_ACT_TAB];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
976
977
  	if (tb1 == NULL)
  		return NULL;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
978
979
  	if (nla_parse(tb, TCA_ACT_MAX_PRIO, nla_data(tb1),
  		      NLMSG_ALIGN(nla_len(tb1)), NULL) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
980
  		return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
981

6d834e04e   Patrick McHardy   [NET_SCHED]: act_...
982
983
  	if (tb[1] == NULL)
  		return NULL;
4700e9ce6   Johannes Berg   net_sched actions...
984
  	if (nla_parse_nested(tb2, TCA_ACT_MAX, tb[1], NULL) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
985
  		return NULL;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
986
  	kind = tb2[TCA_ACT_KIND];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
987

26dab8930   Thomas Graf   [PKT_SCHED]: Fix ...
988
  	return kind;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
989
  }
5a7a5555a   Jamal Hadi Salim   net sched: stylis...
990
  static int tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
991
  {
ddf97ccdd   WANG Cong   net_sched: add ne...
992
  	struct net *net = sock_net(skb->sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
993
  	struct nlmsghdr *nlh;
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
994
  	unsigned char *b = skb_tail_pointer(skb);
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
995
  	struct nlattr *nest;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
996
  	struct tc_action_ops *a_o;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
997
  	int ret = 0;
8b00a53c6   David S. Miller   pkt_sched: act_ap...
998
  	struct tcamsg *t = (struct tcamsg *) nlmsg_data(cb->nlh);
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
999
  	struct nlattr *kind = find_dump_kind(cb->nlh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1000
1001
  
  	if (kind == NULL) {
6ff9c3644   stephen hemminger   net sched: printk...
1002
1003
  		pr_info("tc_dump_action: action bad kind
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1004
1005
  		return 0;
  	}
26dab8930   Thomas Graf   [PKT_SCHED]: Fix ...
1006
  	a_o = tc_lookup_action(kind);
cc7ec456f   Eric Dumazet   net_sched: cleanups
1007
  	if (a_o == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1008
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1009

15e473046   Eric W. Biederman   netlink: Rename p...
1010
  	nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
8b00a53c6   David S. Miller   pkt_sched: act_ap...
1011
1012
1013
1014
  			cb->nlh->nlmsg_type, sizeof(*t), 0);
  	if (!nlh)
  		goto out_module_put;
  	t = nlmsg_data(nlh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1015
  	t->tca_family = AF_UNSPEC;
9ef1d4c7c   Patrick McHardy   [NETLINK]: Missin...
1016
1017
  	t->tca__pad1 = 0;
  	t->tca__pad2 = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1018

4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
1019
1020
  	nest = nla_nest_start(skb, TCA_ACT_TAB);
  	if (nest == NULL)
8b00a53c6   David S. Miller   pkt_sched: act_ap...
1021
  		goto out_module_put;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1022

a85a970af   WANG Cong   net_sched: move t...
1023
  	ret = a_o->walk(net, skb, cb, RTM_GETACTION, a_o);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1024
  	if (ret < 0)
8b00a53c6   David S. Miller   pkt_sched: act_ap...
1025
  		goto out_module_put;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1026
1027
  
  	if (ret > 0) {
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
1028
  		nla_nest_end(skb, nest);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1029
1030
  		ret = skb->len;
  	} else
ebecaa666   Jamal Hadi Salim   net sched actions...
1031
  		nlmsg_trim(skb, b);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1032

27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
1033
  	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
15e473046   Eric W. Biederman   netlink: Rename p...
1034
  	if (NETLINK_CB(cb->skb).portid && ret)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1035
1036
1037
  		nlh->nlmsg_flags |= NLM_F_MULTI;
  	module_put(a_o->owner);
  	return skb->len;
8b00a53c6   David S. Miller   pkt_sched: act_ap...
1038
  out_module_put:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1039
  	module_put(a_o->owner);
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
1040
  	nlmsg_trim(skb, b);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1041
1042
1043
1044
1045
  	return skb->len;
  }
  
  static int __init tc_action_init(void)
  {
c7ac8679b   Greg Rose   rtnetlink: Comput...
1046
1047
1048
1049
  	rtnl_register(PF_UNSPEC, RTM_NEWACTION, tc_ctl_action, NULL, NULL);
  	rtnl_register(PF_UNSPEC, RTM_DELACTION, tc_ctl_action, NULL, NULL);
  	rtnl_register(PF_UNSPEC, RTM_GETACTION, tc_ctl_action, tc_dump_action,
  		      NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1050

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1051
1052
1053
1054
  	return 0;
  }
  
  subsys_initcall(tc_action_init);