Root/
1 | /* net/atm/resources.c - Statically allocated resources */ |
2 | |
3 | /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ |
4 | |
5 | /* Fixes |
6 | * Arnaldo Carvalho de Melo <acme@conectiva.com.br> |
7 | * 2002/01 - don't free the whole struct sock on sk->destruct time, |
8 | * use the default destruct function initialized by sock_init_data */ |
9 | |
10 | #define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ |
11 | |
12 | #include <linux/ctype.h> |
13 | #include <linux/string.h> |
14 | #include <linux/atmdev.h> |
15 | #include <linux/sonet.h> |
16 | #include <linux/kernel.h> /* for barrier */ |
17 | #include <linux/module.h> |
18 | #include <linux/bitops.h> |
19 | #include <linux/capability.h> |
20 | #include <linux/delay.h> |
21 | #include <linux/mutex.h> |
22 | #include <linux/slab.h> |
23 | |
24 | #include <net/sock.h> /* for struct sock */ |
25 | |
26 | #include "common.h" |
27 | #include "resources.h" |
28 | #include "addr.h" |
29 | |
30 | |
31 | LIST_HEAD(atm_devs); |
32 | DEFINE_MUTEX(atm_dev_mutex); |
33 | |
34 | static struct atm_dev *__alloc_atm_dev(const char *type) |
35 | { |
36 | struct atm_dev *dev; |
37 | |
38 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); |
39 | if (!dev) |
40 | return NULL; |
41 | dev->type = type; |
42 | dev->signal = ATM_PHY_SIG_UNKNOWN; |
43 | dev->link_rate = ATM_OC3_PCR; |
44 | spin_lock_init(&dev->lock); |
45 | INIT_LIST_HEAD(&dev->local); |
46 | INIT_LIST_HEAD(&dev->lecs); |
47 | |
48 | return dev; |
49 | } |
50 | |
51 | static struct atm_dev *__atm_dev_lookup(int number) |
52 | { |
53 | struct atm_dev *dev; |
54 | struct list_head *p; |
55 | |
56 | list_for_each(p, &atm_devs) { |
57 | dev = list_entry(p, struct atm_dev, dev_list); |
58 | if (dev->number == number) { |
59 | atm_dev_hold(dev); |
60 | return dev; |
61 | } |
62 | } |
63 | return NULL; |
64 | } |
65 | |
66 | struct atm_dev *atm_dev_lookup(int number) |
67 | { |
68 | struct atm_dev *dev; |
69 | |
70 | mutex_lock(&atm_dev_mutex); |
71 | dev = __atm_dev_lookup(number); |
72 | mutex_unlock(&atm_dev_mutex); |
73 | return dev; |
74 | } |
75 | EXPORT_SYMBOL(atm_dev_lookup); |
76 | |
77 | struct atm_dev *atm_dev_register(const char *type, const struct atmdev_ops *ops, |
78 | int number, unsigned long *flags) |
79 | { |
80 | struct atm_dev *dev, *inuse; |
81 | |
82 | dev = __alloc_atm_dev(type); |
83 | if (!dev) { |
84 | pr_err("no space for dev %s\n", type); |
85 | return NULL; |
86 | } |
87 | mutex_lock(&atm_dev_mutex); |
88 | if (number != -1) { |
89 | inuse = __atm_dev_lookup(number); |
90 | if (inuse) { |
91 | atm_dev_put(inuse); |
92 | mutex_unlock(&atm_dev_mutex); |
93 | kfree(dev); |
94 | return NULL; |
95 | } |
96 | dev->number = number; |
97 | } else { |
98 | dev->number = 0; |
99 | while ((inuse = __atm_dev_lookup(dev->number))) { |
100 | atm_dev_put(inuse); |
101 | dev->number++; |
102 | } |
103 | } |
104 | |
105 | dev->ops = ops; |
106 | if (flags) |
107 | dev->flags = *flags; |
108 | else |
109 | memset(&dev->flags, 0, sizeof(dev->flags)); |
110 | memset(&dev->stats, 0, sizeof(dev->stats)); |
111 | atomic_set(&dev->refcnt, 1); |
112 | |
113 | if (atm_proc_dev_register(dev) < 0) { |
114 | pr_err("atm_proc_dev_register failed for dev %s\n", type); |
115 | goto out_fail; |
116 | } |
117 | |
118 | if (atm_register_sysfs(dev) < 0) { |
119 | pr_err("atm_register_sysfs failed for dev %s\n", type); |
120 | atm_proc_dev_deregister(dev); |
121 | goto out_fail; |
122 | } |
123 | |
124 | list_add_tail(&dev->dev_list, &atm_devs); |
125 | |
126 | out: |
127 | mutex_unlock(&atm_dev_mutex); |
128 | return dev; |
129 | |
130 | out_fail: |
131 | kfree(dev); |
132 | dev = NULL; |
133 | goto out; |
134 | } |
135 | EXPORT_SYMBOL(atm_dev_register); |
136 | |
137 | void atm_dev_deregister(struct atm_dev *dev) |
138 | { |
139 | BUG_ON(test_bit(ATM_DF_REMOVED, &dev->flags)); |
140 | set_bit(ATM_DF_REMOVED, &dev->flags); |
141 | |
142 | /* |
143 | * if we remove current device from atm_devs list, new device |
144 | * with same number can appear, such we need deregister proc, |
145 | * release async all vccs and remove them from vccs list too |
146 | */ |
147 | mutex_lock(&atm_dev_mutex); |
148 | list_del(&dev->dev_list); |
149 | mutex_unlock(&atm_dev_mutex); |
150 | |
151 | atm_dev_release_vccs(dev); |
152 | atm_unregister_sysfs(dev); |
153 | atm_proc_dev_deregister(dev); |
154 | |
155 | atm_dev_put(dev); |
156 | } |
157 | EXPORT_SYMBOL(atm_dev_deregister); |
158 | |
159 | static void copy_aal_stats(struct k_atm_aal_stats *from, |
160 | struct atm_aal_stats *to) |
161 | { |
162 | #define __HANDLE_ITEM(i) to->i = atomic_read(&from->i) |
163 | __AAL_STAT_ITEMS |
164 | #undef __HANDLE_ITEM |
165 | } |
166 | |
167 | static void subtract_aal_stats(struct k_atm_aal_stats *from, |
168 | struct atm_aal_stats *to) |
169 | { |
170 | #define __HANDLE_ITEM(i) atomic_sub(to->i, &from->i) |
171 | __AAL_STAT_ITEMS |
172 | #undef __HANDLE_ITEM |
173 | } |
174 | |
175 | static int fetch_stats(struct atm_dev *dev, struct atm_dev_stats __user *arg, |
176 | int zero) |
177 | { |
178 | struct atm_dev_stats tmp; |
179 | int error = 0; |
180 | |
181 | copy_aal_stats(&dev->stats.aal0, &tmp.aal0); |
182 | copy_aal_stats(&dev->stats.aal34, &tmp.aal34); |
183 | copy_aal_stats(&dev->stats.aal5, &tmp.aal5); |
184 | if (arg) |
185 | error = copy_to_user(arg, &tmp, sizeof(tmp)); |
186 | if (zero && !error) { |
187 | subtract_aal_stats(&dev->stats.aal0, &tmp.aal0); |
188 | subtract_aal_stats(&dev->stats.aal34, &tmp.aal34); |
189 | subtract_aal_stats(&dev->stats.aal5, &tmp.aal5); |
190 | } |
191 | return error ? -EFAULT : 0; |
192 | } |
193 | |
194 | int atm_dev_ioctl(unsigned int cmd, void __user *arg, int compat) |
195 | { |
196 | void __user *buf; |
197 | int error, len, number, size = 0; |
198 | struct atm_dev *dev; |
199 | struct list_head *p; |
200 | int *tmp_buf, *tmp_p; |
201 | int __user *sioc_len; |
202 | int __user *iobuf_len; |
203 | |
204 | #ifndef CONFIG_COMPAT |
205 | compat = 0; /* Just so the compiler _knows_ */ |
206 | #endif |
207 | |
208 | switch (cmd) { |
209 | case ATM_GETNAMES: |
210 | if (compat) { |
211 | #ifdef CONFIG_COMPAT |
212 | struct compat_atm_iobuf __user *ciobuf = arg; |
213 | compat_uptr_t cbuf; |
214 | iobuf_len = &ciobuf->length; |
215 | if (get_user(cbuf, &ciobuf->buffer)) |
216 | return -EFAULT; |
217 | buf = compat_ptr(cbuf); |
218 | #endif |
219 | } else { |
220 | struct atm_iobuf __user *iobuf = arg; |
221 | iobuf_len = &iobuf->length; |
222 | if (get_user(buf, &iobuf->buffer)) |
223 | return -EFAULT; |
224 | } |
225 | if (get_user(len, iobuf_len)) |
226 | return -EFAULT; |
227 | mutex_lock(&atm_dev_mutex); |
228 | list_for_each(p, &atm_devs) |
229 | size += sizeof(int); |
230 | if (size > len) { |
231 | mutex_unlock(&atm_dev_mutex); |
232 | return -E2BIG; |
233 | } |
234 | tmp_buf = kmalloc(size, GFP_ATOMIC); |
235 | if (!tmp_buf) { |
236 | mutex_unlock(&atm_dev_mutex); |
237 | return -ENOMEM; |
238 | } |
239 | tmp_p = tmp_buf; |
240 | list_for_each(p, &atm_devs) { |
241 | dev = list_entry(p, struct atm_dev, dev_list); |
242 | *tmp_p++ = dev->number; |
243 | } |
244 | mutex_unlock(&atm_dev_mutex); |
245 | error = ((copy_to_user(buf, tmp_buf, size)) || |
246 | put_user(size, iobuf_len)) |
247 | ? -EFAULT : 0; |
248 | kfree(tmp_buf); |
249 | return error; |
250 | default: |
251 | break; |
252 | } |
253 | |
254 | if (compat) { |
255 | #ifdef CONFIG_COMPAT |
256 | struct compat_atmif_sioc __user *csioc = arg; |
257 | compat_uptr_t carg; |
258 | |
259 | sioc_len = &csioc->length; |
260 | if (get_user(carg, &csioc->arg)) |
261 | return -EFAULT; |
262 | buf = compat_ptr(carg); |
263 | |
264 | if (get_user(len, &csioc->length)) |
265 | return -EFAULT; |
266 | if (get_user(number, &csioc->number)) |
267 | return -EFAULT; |
268 | #endif |
269 | } else { |
270 | struct atmif_sioc __user *sioc = arg; |
271 | |
272 | sioc_len = &sioc->length; |
273 | if (get_user(buf, &sioc->arg)) |
274 | return -EFAULT; |
275 | if (get_user(len, &sioc->length)) |
276 | return -EFAULT; |
277 | if (get_user(number, &sioc->number)) |
278 | return -EFAULT; |
279 | } |
280 | |
281 | dev = try_then_request_module(atm_dev_lookup(number), "atm-device-%d", |
282 | number); |
283 | if (!dev) |
284 | return -ENODEV; |
285 | |
286 | switch (cmd) { |
287 | case ATM_GETTYPE: |
288 | size = strlen(dev->type) + 1; |
289 | if (copy_to_user(buf, dev->type, size)) { |
290 | error = -EFAULT; |
291 | goto done; |
292 | } |
293 | break; |
294 | case ATM_GETESI: |
295 | size = ESI_LEN; |
296 | if (copy_to_user(buf, dev->esi, size)) { |
297 | error = -EFAULT; |
298 | goto done; |
299 | } |
300 | break; |
301 | case ATM_SETESI: |
302 | { |
303 | int i; |
304 | |
305 | for (i = 0; i < ESI_LEN; i++) |
306 | if (dev->esi[i]) { |
307 | error = -EEXIST; |
308 | goto done; |
309 | } |
310 | } |
311 | /* fall through */ |
312 | case ATM_SETESIF: |
313 | { |
314 | unsigned char esi[ESI_LEN]; |
315 | |
316 | if (!capable(CAP_NET_ADMIN)) { |
317 | error = -EPERM; |
318 | goto done; |
319 | } |
320 | if (copy_from_user(esi, buf, ESI_LEN)) { |
321 | error = -EFAULT; |
322 | goto done; |
323 | } |
324 | memcpy(dev->esi, esi, ESI_LEN); |
325 | error = ESI_LEN; |
326 | goto done; |
327 | } |
328 | case ATM_GETSTATZ: |
329 | if (!capable(CAP_NET_ADMIN)) { |
330 | error = -EPERM; |
331 | goto done; |
332 | } |
333 | /* fall through */ |
334 | case ATM_GETSTAT: |
335 | size = sizeof(struct atm_dev_stats); |
336 | error = fetch_stats(dev, buf, cmd == ATM_GETSTATZ); |
337 | if (error) |
338 | goto done; |
339 | break; |
340 | case ATM_GETCIRANGE: |
341 | size = sizeof(struct atm_cirange); |
342 | if (copy_to_user(buf, &dev->ci_range, size)) { |
343 | error = -EFAULT; |
344 | goto done; |
345 | } |
346 | break; |
347 | case ATM_GETLINKRATE: |
348 | size = sizeof(int); |
349 | if (copy_to_user(buf, &dev->link_rate, size)) { |
350 | error = -EFAULT; |
351 | goto done; |
352 | } |
353 | break; |
354 | case ATM_RSTADDR: |
355 | if (!capable(CAP_NET_ADMIN)) { |
356 | error = -EPERM; |
357 | goto done; |
358 | } |
359 | atm_reset_addr(dev, ATM_ADDR_LOCAL); |
360 | break; |
361 | case ATM_ADDADDR: |
362 | case ATM_DELADDR: |
363 | case ATM_ADDLECSADDR: |
364 | case ATM_DELLECSADDR: |
365 | { |
366 | struct sockaddr_atmsvc addr; |
367 | |
368 | if (!capable(CAP_NET_ADMIN)) { |
369 | error = -EPERM; |
370 | goto done; |
371 | } |
372 | |
373 | if (copy_from_user(&addr, buf, sizeof(addr))) { |
374 | error = -EFAULT; |
375 | goto done; |
376 | } |
377 | if (cmd == ATM_ADDADDR || cmd == ATM_ADDLECSADDR) |
378 | error = atm_add_addr(dev, &addr, |
379 | (cmd == ATM_ADDADDR ? |
380 | ATM_ADDR_LOCAL : ATM_ADDR_LECS)); |
381 | else |
382 | error = atm_del_addr(dev, &addr, |
383 | (cmd == ATM_DELADDR ? |
384 | ATM_ADDR_LOCAL : ATM_ADDR_LECS)); |
385 | goto done; |
386 | } |
387 | case ATM_GETADDR: |
388 | case ATM_GETLECSADDR: |
389 | error = atm_get_addr(dev, buf, len, |
390 | (cmd == ATM_GETADDR ? |
391 | ATM_ADDR_LOCAL : ATM_ADDR_LECS)); |
392 | if (error < 0) |
393 | goto done; |
394 | size = error; |
395 | /* may return 0, but later on size == 0 means "don't |
396 | write the length" */ |
397 | error = put_user(size, sioc_len) ? -EFAULT : 0; |
398 | goto done; |
399 | case ATM_SETLOOP: |
400 | if (__ATM_LM_XTRMT((int) (unsigned long) buf) && |
401 | __ATM_LM_XTLOC((int) (unsigned long) buf) > |
402 | __ATM_LM_XTRMT((int) (unsigned long) buf)) { |
403 | error = -EINVAL; |
404 | goto done; |
405 | } |
406 | /* fall through */ |
407 | case ATM_SETCIRANGE: |
408 | case SONET_GETSTATZ: |
409 | case SONET_SETDIAG: |
410 | case SONET_CLRDIAG: |
411 | case SONET_SETFRAMING: |
412 | if (!capable(CAP_NET_ADMIN)) { |
413 | error = -EPERM; |
414 | goto done; |
415 | } |
416 | /* fall through */ |
417 | default: |
418 | if (compat) { |
419 | #ifdef CONFIG_COMPAT |
420 | if (!dev->ops->compat_ioctl) { |
421 | error = -EINVAL; |
422 | goto done; |
423 | } |
424 | size = dev->ops->compat_ioctl(dev, cmd, buf); |
425 | #endif |
426 | } else { |
427 | if (!dev->ops->ioctl) { |
428 | error = -EINVAL; |
429 | goto done; |
430 | } |
431 | size = dev->ops->ioctl(dev, cmd, buf); |
432 | } |
433 | if (size < 0) { |
434 | error = (size == -ENOIOCTLCMD ? -EINVAL : size); |
435 | goto done; |
436 | } |
437 | } |
438 | |
439 | if (size) |
440 | error = put_user(size, sioc_len) ? -EFAULT : 0; |
441 | else |
442 | error = 0; |
443 | done: |
444 | atm_dev_put(dev); |
445 | return error; |
446 | } |
447 | |
448 | void *atm_dev_seq_start(struct seq_file *seq, loff_t *pos) |
449 | { |
450 | mutex_lock(&atm_dev_mutex); |
451 | return seq_list_start_head(&atm_devs, *pos); |
452 | } |
453 | |
454 | void atm_dev_seq_stop(struct seq_file *seq, void *v) |
455 | { |
456 | mutex_unlock(&atm_dev_mutex); |
457 | } |
458 | |
459 | void *atm_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) |
460 | { |
461 | return seq_list_next(v, &atm_devs, pos); |
462 | } |
463 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9