diff options
Diffstat (limited to 'net/atm/resources.c')
-rw-r--r-- | net/atm/resources.c | 432 |
1 files changed, 432 insertions, 0 deletions
diff --git a/net/atm/resources.c b/net/atm/resources.c new file mode 100644 index 000000000000..33f1685dbb77 --- /dev/null +++ b/net/atm/resources.c | |||
@@ -0,0 +1,432 @@ | |||
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 | |||
11 | #include <linux/config.h> | ||
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/delay.h> | ||
20 | #include <net/sock.h> /* for struct sock */ | ||
21 | |||
22 | #include "common.h" | ||
23 | #include "resources.h" | ||
24 | #include "addr.h" | ||
25 | |||
26 | |||
27 | LIST_HEAD(atm_devs); | ||
28 | DEFINE_SPINLOCK(atm_dev_lock); | ||
29 | |||
30 | static struct atm_dev *__alloc_atm_dev(const char *type) | ||
31 | { | ||
32 | struct atm_dev *dev; | ||
33 | |||
34 | dev = kmalloc(sizeof(*dev), GFP_KERNEL); | ||
35 | if (!dev) | ||
36 | return NULL; | ||
37 | memset(dev, 0, sizeof(*dev)); | ||
38 | dev->type = type; | ||
39 | dev->signal = ATM_PHY_SIG_UNKNOWN; | ||
40 | dev->link_rate = ATM_OC3_PCR; | ||
41 | spin_lock_init(&dev->lock); | ||
42 | INIT_LIST_HEAD(&dev->local); | ||
43 | |||
44 | return dev; | ||
45 | } | ||
46 | |||
47 | static void __free_atm_dev(struct atm_dev *dev) | ||
48 | { | ||
49 | kfree(dev); | ||
50 | } | ||
51 | |||
52 | static struct atm_dev *__atm_dev_lookup(int number) | ||
53 | { | ||
54 | struct atm_dev *dev; | ||
55 | struct list_head *p; | ||
56 | |||
57 | list_for_each(p, &atm_devs) { | ||
58 | dev = list_entry(p, struct atm_dev, dev_list); | ||
59 | if ((dev->ops) && (dev->number == number)) { | ||
60 | atm_dev_hold(dev); | ||
61 | return dev; | ||
62 | } | ||
63 | } | ||
64 | return NULL; | ||
65 | } | ||
66 | |||
67 | struct atm_dev *atm_dev_lookup(int number) | ||
68 | { | ||
69 | struct atm_dev *dev; | ||
70 | |||
71 | spin_lock(&atm_dev_lock); | ||
72 | dev = __atm_dev_lookup(number); | ||
73 | spin_unlock(&atm_dev_lock); | ||
74 | return dev; | ||
75 | } | ||
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 | printk(KERN_ERR "atm_dev_register: no space for dev %s\n", | ||
85 | type); | ||
86 | return NULL; | ||
87 | } | ||
88 | spin_lock(&atm_dev_lock); | ||
89 | if (number != -1) { | ||
90 | if ((inuse = __atm_dev_lookup(number))) { | ||
91 | atm_dev_put(inuse); | ||
92 | spin_unlock(&atm_dev_lock); | ||
93 | __free_atm_dev(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 | list_add_tail(&dev->dev_list, &atm_devs); | ||
113 | spin_unlock(&atm_dev_lock); | ||
114 | |||
115 | if (atm_proc_dev_register(dev) < 0) { | ||
116 | printk(KERN_ERR "atm_dev_register: " | ||
117 | "atm_proc_dev_register failed for dev %s\n", | ||
118 | type); | ||
119 | spin_lock(&atm_dev_lock); | ||
120 | list_del(&dev->dev_list); | ||
121 | spin_unlock(&atm_dev_lock); | ||
122 | __free_atm_dev(dev); | ||
123 | return NULL; | ||
124 | } | ||
125 | |||
126 | return dev; | ||
127 | } | ||
128 | |||
129 | |||
130 | void atm_dev_deregister(struct atm_dev *dev) | ||
131 | { | ||
132 | unsigned long warning_time; | ||
133 | |||
134 | atm_proc_dev_deregister(dev); | ||
135 | |||
136 | spin_lock(&atm_dev_lock); | ||
137 | list_del(&dev->dev_list); | ||
138 | spin_unlock(&atm_dev_lock); | ||
139 | |||
140 | warning_time = jiffies; | ||
141 | while (atomic_read(&dev->refcnt) != 1) { | ||
142 | msleep(250); | ||
143 | if ((jiffies - warning_time) > 10 * HZ) { | ||
144 | printk(KERN_EMERG "atm_dev_deregister: waiting for " | ||
145 | "dev %d to become free. Usage count = %d\n", | ||
146 | dev->number, atomic_read(&dev->refcnt)); | ||
147 | warning_time = jiffies; | ||
148 | } | ||
149 | } | ||
150 | |||
151 | __free_atm_dev(dev); | ||
152 | } | ||
153 | |||
154 | void shutdown_atm_dev(struct atm_dev *dev) | ||
155 | { | ||
156 | if (atomic_read(&dev->refcnt) > 1) { | ||
157 | set_bit(ATM_DF_CLOSE, &dev->flags); | ||
158 | return; | ||
159 | } | ||
160 | if (dev->ops->dev_close) | ||
161 | dev->ops->dev_close(dev); | ||
162 | atm_dev_deregister(dev); | ||
163 | } | ||
164 | |||
165 | |||
166 | static void copy_aal_stats(struct k_atm_aal_stats *from, | ||
167 | struct atm_aal_stats *to) | ||
168 | { | ||
169 | #define __HANDLE_ITEM(i) to->i = atomic_read(&from->i) | ||
170 | __AAL_STAT_ITEMS | ||
171 | #undef __HANDLE_ITEM | ||
172 | } | ||
173 | |||
174 | |||
175 | static void subtract_aal_stats(struct k_atm_aal_stats *from, | ||
176 | struct atm_aal_stats *to) | ||
177 | { | ||
178 | #define __HANDLE_ITEM(i) atomic_sub(to->i, &from->i) | ||
179 | __AAL_STAT_ITEMS | ||
180 | #undef __HANDLE_ITEM | ||
181 | } | ||
182 | |||
183 | |||
184 | static int fetch_stats(struct atm_dev *dev, struct atm_dev_stats __user *arg, int zero) | ||
185 | { | ||
186 | struct atm_dev_stats tmp; | ||
187 | int error = 0; | ||
188 | |||
189 | copy_aal_stats(&dev->stats.aal0, &tmp.aal0); | ||
190 | copy_aal_stats(&dev->stats.aal34, &tmp.aal34); | ||
191 | copy_aal_stats(&dev->stats.aal5, &tmp.aal5); | ||
192 | if (arg) | ||
193 | error = copy_to_user(arg, &tmp, sizeof(tmp)); | ||
194 | if (zero && !error) { | ||
195 | subtract_aal_stats(&dev->stats.aal0, &tmp.aal0); | ||
196 | subtract_aal_stats(&dev->stats.aal34, &tmp.aal34); | ||
197 | subtract_aal_stats(&dev->stats.aal5, &tmp.aal5); | ||
198 | } | ||
199 | return error ? -EFAULT : 0; | ||
200 | } | ||
201 | |||
202 | |||
203 | int atm_dev_ioctl(unsigned int cmd, void __user *arg) | ||
204 | { | ||
205 | void __user *buf; | ||
206 | int error, len, number, size = 0; | ||
207 | struct atm_dev *dev; | ||
208 | struct list_head *p; | ||
209 | int *tmp_buf, *tmp_p; | ||
210 | struct atm_iobuf __user *iobuf = arg; | ||
211 | struct atmif_sioc __user *sioc = arg; | ||
212 | switch (cmd) { | ||
213 | case ATM_GETNAMES: | ||
214 | if (get_user(buf, &iobuf->buffer)) | ||
215 | return -EFAULT; | ||
216 | if (get_user(len, &iobuf->length)) | ||
217 | return -EFAULT; | ||
218 | spin_lock(&atm_dev_lock); | ||
219 | list_for_each(p, &atm_devs) | ||
220 | size += sizeof(int); | ||
221 | if (size > len) { | ||
222 | spin_unlock(&atm_dev_lock); | ||
223 | return -E2BIG; | ||
224 | } | ||
225 | tmp_buf = kmalloc(size, GFP_ATOMIC); | ||
226 | if (!tmp_buf) { | ||
227 | spin_unlock(&atm_dev_lock); | ||
228 | return -ENOMEM; | ||
229 | } | ||
230 | tmp_p = tmp_buf; | ||
231 | list_for_each(p, &atm_devs) { | ||
232 | dev = list_entry(p, struct atm_dev, dev_list); | ||
233 | *tmp_p++ = dev->number; | ||
234 | } | ||
235 | spin_unlock(&atm_dev_lock); | ||
236 | error = ((copy_to_user(buf, tmp_buf, size)) || | ||
237 | put_user(size, &iobuf->length)) | ||
238 | ? -EFAULT : 0; | ||
239 | kfree(tmp_buf); | ||
240 | return error; | ||
241 | default: | ||
242 | break; | ||
243 | } | ||
244 | |||
245 | if (get_user(buf, &sioc->arg)) | ||
246 | return -EFAULT; | ||
247 | if (get_user(len, &sioc->length)) | ||
248 | return -EFAULT; | ||
249 | if (get_user(number, &sioc->number)) | ||
250 | return -EFAULT; | ||
251 | |||
252 | if (!(dev = atm_dev_lookup(number))) | ||
253 | return -ENODEV; | ||
254 | |||
255 | switch (cmd) { | ||
256 | case ATM_GETTYPE: | ||
257 | size = strlen(dev->type) + 1; | ||
258 | if (copy_to_user(buf, dev->type, size)) { | ||
259 | error = -EFAULT; | ||
260 | goto done; | ||
261 | } | ||
262 | break; | ||
263 | case ATM_GETESI: | ||
264 | size = ESI_LEN; | ||
265 | if (copy_to_user(buf, dev->esi, size)) { | ||
266 | error = -EFAULT; | ||
267 | goto done; | ||
268 | } | ||
269 | break; | ||
270 | case ATM_SETESI: | ||
271 | { | ||
272 | int i; | ||
273 | |||
274 | for (i = 0; i < ESI_LEN; i++) | ||
275 | if (dev->esi[i]) { | ||
276 | error = -EEXIST; | ||
277 | goto done; | ||
278 | } | ||
279 | } | ||
280 | /* fall through */ | ||
281 | case ATM_SETESIF: | ||
282 | { | ||
283 | unsigned char esi[ESI_LEN]; | ||
284 | |||
285 | if (!capable(CAP_NET_ADMIN)) { | ||
286 | error = -EPERM; | ||
287 | goto done; | ||
288 | } | ||
289 | if (copy_from_user(esi, buf, ESI_LEN)) { | ||
290 | error = -EFAULT; | ||
291 | goto done; | ||
292 | } | ||
293 | memcpy(dev->esi, esi, ESI_LEN); | ||
294 | error = ESI_LEN; | ||
295 | goto done; | ||
296 | } | ||
297 | case ATM_GETSTATZ: | ||
298 | if (!capable(CAP_NET_ADMIN)) { | ||
299 | error = -EPERM; | ||
300 | goto done; | ||
301 | } | ||
302 | /* fall through */ | ||
303 | case ATM_GETSTAT: | ||
304 | size = sizeof(struct atm_dev_stats); | ||
305 | error = fetch_stats(dev, buf, cmd == ATM_GETSTATZ); | ||
306 | if (error) | ||
307 | goto done; | ||
308 | break; | ||
309 | case ATM_GETCIRANGE: | ||
310 | size = sizeof(struct atm_cirange); | ||
311 | if (copy_to_user(buf, &dev->ci_range, size)) { | ||
312 | error = -EFAULT; | ||
313 | goto done; | ||
314 | } | ||
315 | break; | ||
316 | case ATM_GETLINKRATE: | ||
317 | size = sizeof(int); | ||
318 | if (copy_to_user(buf, &dev->link_rate, size)) { | ||
319 | error = -EFAULT; | ||
320 | goto done; | ||
321 | } | ||
322 | break; | ||
323 | case ATM_RSTADDR: | ||
324 | if (!capable(CAP_NET_ADMIN)) { | ||
325 | error = -EPERM; | ||
326 | goto done; | ||
327 | } | ||
328 | atm_reset_addr(dev); | ||
329 | break; | ||
330 | case ATM_ADDADDR: | ||
331 | case ATM_DELADDR: | ||
332 | if (!capable(CAP_NET_ADMIN)) { | ||
333 | error = -EPERM; | ||
334 | goto done; | ||
335 | } | ||
336 | { | ||
337 | struct sockaddr_atmsvc addr; | ||
338 | |||
339 | if (copy_from_user(&addr, buf, sizeof(addr))) { | ||
340 | error = -EFAULT; | ||
341 | goto done; | ||
342 | } | ||
343 | if (cmd == ATM_ADDADDR) | ||
344 | error = atm_add_addr(dev, &addr); | ||
345 | else | ||
346 | error = atm_del_addr(dev, &addr); | ||
347 | goto done; | ||
348 | } | ||
349 | case ATM_GETADDR: | ||
350 | error = atm_get_addr(dev, buf, len); | ||
351 | if (error < 0) | ||
352 | goto done; | ||
353 | size = error; | ||
354 | /* may return 0, but later on size == 0 means "don't | ||
355 | write the length" */ | ||
356 | error = put_user(size, &sioc->length) | ||
357 | ? -EFAULT : 0; | ||
358 | goto done; | ||
359 | case ATM_SETLOOP: | ||
360 | if (__ATM_LM_XTRMT((int) (unsigned long) buf) && | ||
361 | __ATM_LM_XTLOC((int) (unsigned long) buf) > | ||
362 | __ATM_LM_XTRMT((int) (unsigned long) buf)) { | ||
363 | error = -EINVAL; | ||
364 | goto done; | ||
365 | } | ||
366 | /* fall through */ | ||
367 | case ATM_SETCIRANGE: | ||
368 | case SONET_GETSTATZ: | ||
369 | case SONET_SETDIAG: | ||
370 | case SONET_CLRDIAG: | ||
371 | case SONET_SETFRAMING: | ||
372 | if (!capable(CAP_NET_ADMIN)) { | ||
373 | error = -EPERM; | ||
374 | goto done; | ||
375 | } | ||
376 | /* fall through */ | ||
377 | default: | ||
378 | if (!dev->ops->ioctl) { | ||
379 | error = -EINVAL; | ||
380 | goto done; | ||
381 | } | ||
382 | size = dev->ops->ioctl(dev, cmd, buf); | ||
383 | if (size < 0) { | ||
384 | error = (size == -ENOIOCTLCMD ? -EINVAL : size); | ||
385 | goto done; | ||
386 | } | ||
387 | } | ||
388 | |||
389 | if (size) | ||
390 | error = put_user(size, &sioc->length) | ||
391 | ? -EFAULT : 0; | ||
392 | else | ||
393 | error = 0; | ||
394 | done: | ||
395 | atm_dev_put(dev); | ||
396 | return error; | ||
397 | } | ||
398 | |||
399 | static __inline__ void *dev_get_idx(loff_t left) | ||
400 | { | ||
401 | struct list_head *p; | ||
402 | |||
403 | list_for_each(p, &atm_devs) { | ||
404 | if (!--left) | ||
405 | break; | ||
406 | } | ||
407 | return (p != &atm_devs) ? p : NULL; | ||
408 | } | ||
409 | |||
410 | void *atm_dev_seq_start(struct seq_file *seq, loff_t *pos) | ||
411 | { | ||
412 | spin_lock(&atm_dev_lock); | ||
413 | return *pos ? dev_get_idx(*pos) : (void *) 1; | ||
414 | } | ||
415 | |||
416 | void atm_dev_seq_stop(struct seq_file *seq, void *v) | ||
417 | { | ||
418 | spin_unlock(&atm_dev_lock); | ||
419 | } | ||
420 | |||
421 | void *atm_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) | ||
422 | { | ||
423 | ++*pos; | ||
424 | v = (v == (void *)1) ? atm_devs.next : ((struct list_head *)v)->next; | ||
425 | return (v == &atm_devs) ? NULL : v; | ||
426 | } | ||
427 | |||
428 | |||
429 | EXPORT_SYMBOL(atm_dev_register); | ||
430 | EXPORT_SYMBOL(atm_dev_deregister); | ||
431 | EXPORT_SYMBOL(atm_dev_lookup); | ||
432 | EXPORT_SYMBOL(shutdown_atm_dev); | ||