Contents of /alx-src/tags/kernel26-2.6.12-alx-r9/net/atm/proc.c
Parent Directory | Revision Log
Revision 630 -
(show annotations)
(download)
Wed Mar 4 11:03:09 2009 UTC (15 years, 3 months ago) by niro
File MIME type: text/plain
File size: 12081 byte(s)
Wed Mar 4 11:03:09 2009 UTC (15 years, 3 months ago) by niro
File MIME type: text/plain
File size: 12081 byte(s)
Tag kernel26-2.6.12-alx-r9
1 | /* net/atm/proc.c - ATM /proc interface |
2 | * |
3 | * Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA |
4 | * |
5 | * seq_file api usage by romieu@fr.zoreil.com |
6 | * |
7 | * Evaluating the efficiency of the whole thing if left as an exercise to |
8 | * the reader. |
9 | */ |
10 | |
11 | #include <linux/config.h> |
12 | #include <linux/module.h> /* for EXPORT_SYMBOL */ |
13 | #include <linux/string.h> |
14 | #include <linux/types.h> |
15 | #include <linux/mm.h> |
16 | #include <linux/fs.h> |
17 | #include <linux/stat.h> |
18 | #include <linux/proc_fs.h> |
19 | #include <linux/seq_file.h> |
20 | #include <linux/errno.h> |
21 | #include <linux/atm.h> |
22 | #include <linux/atmdev.h> |
23 | #include <linux/netdevice.h> |
24 | #include <linux/atmclip.h> |
25 | #include <linux/init.h> /* for __init */ |
26 | #include <net/atmclip.h> |
27 | #include <asm/uaccess.h> |
28 | #include <asm/atomic.h> |
29 | #include <asm/param.h> /* for HZ */ |
30 | #include "resources.h" |
31 | #include "common.h" /* atm_proc_init prototype */ |
32 | #include "signaling.h" /* to get sigd - ugly too */ |
33 | |
34 | static ssize_t proc_dev_atm_read(struct file *file,char __user *buf,size_t count, |
35 | loff_t *pos); |
36 | |
37 | static struct file_operations proc_atm_dev_ops = { |
38 | .owner = THIS_MODULE, |
39 | .read = proc_dev_atm_read, |
40 | }; |
41 | |
42 | static void add_stats(struct seq_file *seq, const char *aal, |
43 | const struct k_atm_aal_stats *stats) |
44 | { |
45 | seq_printf(seq, "%s ( %d %d %d %d %d )", aal, |
46 | atomic_read(&stats->tx),atomic_read(&stats->tx_err), |
47 | atomic_read(&stats->rx),atomic_read(&stats->rx_err), |
48 | atomic_read(&stats->rx_drop)); |
49 | } |
50 | |
51 | static void atm_dev_info(struct seq_file *seq, const struct atm_dev *dev) |
52 | { |
53 | int i; |
54 | |
55 | seq_printf(seq, "%3d %-8s", dev->number, dev->type); |
56 | for (i = 0; i < ESI_LEN; i++) |
57 | seq_printf(seq, "%02x", dev->esi[i]); |
58 | seq_puts(seq, " "); |
59 | add_stats(seq, "0", &dev->stats.aal0); |
60 | seq_puts(seq, " "); |
61 | add_stats(seq, "5", &dev->stats.aal5); |
62 | seq_printf(seq, "\t[%d]", atomic_read(&dev->refcnt)); |
63 | seq_putc(seq, '\n'); |
64 | } |
65 | |
66 | struct vcc_state { |
67 | int bucket; |
68 | struct sock *sk; |
69 | int family; |
70 | }; |
71 | |
72 | static inline int compare_family(struct sock *sk, int family) |
73 | { |
74 | return !family || (sk->sk_family == family); |
75 | } |
76 | |
77 | static int __vcc_walk(struct sock **sock, int family, int *bucket, loff_t l) |
78 | { |
79 | struct sock *sk = *sock; |
80 | |
81 | if (sk == (void *)1) { |
82 | for (*bucket = 0; *bucket < VCC_HTABLE_SIZE; ++*bucket) { |
83 | struct hlist_head *head = &vcc_hash[*bucket]; |
84 | |
85 | sk = hlist_empty(head) ? NULL : __sk_head(head); |
86 | if (sk) |
87 | break; |
88 | } |
89 | l--; |
90 | } |
91 | try_again: |
92 | for (; sk; sk = sk_next(sk)) { |
93 | l -= compare_family(sk, family); |
94 | if (l < 0) |
95 | goto out; |
96 | } |
97 | if (!sk && ++*bucket < VCC_HTABLE_SIZE) { |
98 | sk = sk_head(&vcc_hash[*bucket]); |
99 | goto try_again; |
100 | } |
101 | sk = (void *)1; |
102 | out: |
103 | *sock = sk; |
104 | return (l < 0); |
105 | } |
106 | |
107 | static inline void *vcc_walk(struct vcc_state *state, loff_t l) |
108 | { |
109 | return __vcc_walk(&state->sk, state->family, &state->bucket, l) ? |
110 | state : NULL; |
111 | } |
112 | |
113 | static int __vcc_seq_open(struct inode *inode, struct file *file, |
114 | int family, struct seq_operations *ops) |
115 | { |
116 | struct vcc_state *state; |
117 | struct seq_file *seq; |
118 | int rc = -ENOMEM; |
119 | |
120 | state = kmalloc(sizeof(*state), GFP_KERNEL); |
121 | if (!state) |
122 | goto out; |
123 | |
124 | rc = seq_open(file, ops); |
125 | if (rc) |
126 | goto out_kfree; |
127 | |
128 | state->family = family; |
129 | |
130 | seq = file->private_data; |
131 | seq->private = state; |
132 | out: |
133 | return rc; |
134 | out_kfree: |
135 | kfree(state); |
136 | goto out; |
137 | } |
138 | |
139 | static int vcc_seq_release(struct inode *inode, struct file *file) |
140 | { |
141 | return seq_release_private(inode, file); |
142 | } |
143 | |
144 | static void *vcc_seq_start(struct seq_file *seq, loff_t *pos) |
145 | { |
146 | struct vcc_state *state = seq->private; |
147 | loff_t left = *pos; |
148 | |
149 | read_lock(&vcc_sklist_lock); |
150 | state->sk = (void *)1; |
151 | return left ? vcc_walk(state, left) : (void *)1; |
152 | } |
153 | |
154 | static void vcc_seq_stop(struct seq_file *seq, void *v) |
155 | { |
156 | read_unlock(&vcc_sklist_lock); |
157 | } |
158 | |
159 | static void *vcc_seq_next(struct seq_file *seq, void *v, loff_t *pos) |
160 | { |
161 | struct vcc_state *state = seq->private; |
162 | |
163 | v = vcc_walk(state, 1); |
164 | *pos += !!PTR_ERR(v); |
165 | return v; |
166 | } |
167 | |
168 | static void pvc_info(struct seq_file *seq, struct atm_vcc *vcc) |
169 | { |
170 | static const char *class_name[] = { "off","UBR","CBR","VBR","ABR" }; |
171 | static const char *aal_name[] = { |
172 | "---", "1", "2", "3/4", /* 0- 3 */ |
173 | "???", "5", "???", "???", /* 4- 7 */ |
174 | "???", "???", "???", "???", /* 8-11 */ |
175 | "???", "0", "???", "???"}; /* 12-15 */ |
176 | |
177 | seq_printf(seq, "%3d %3d %5d %-3s %7d %-5s %7d %-6s", |
178 | vcc->dev->number,vcc->vpi,vcc->vci, |
179 | vcc->qos.aal >= sizeof(aal_name)/sizeof(aal_name[0]) ? "err" : |
180 | aal_name[vcc->qos.aal],vcc->qos.rxtp.min_pcr, |
181 | class_name[vcc->qos.rxtp.traffic_class],vcc->qos.txtp.min_pcr, |
182 | class_name[vcc->qos.txtp.traffic_class]); |
183 | if (test_bit(ATM_VF_IS_CLIP, &vcc->flags)) { |
184 | struct clip_vcc *clip_vcc = CLIP_VCC(vcc); |
185 | struct net_device *dev; |
186 | |
187 | dev = clip_vcc->entry ? clip_vcc->entry->neigh->dev : NULL; |
188 | seq_printf(seq, "CLIP, Itf:%s, Encap:", |
189 | dev ? dev->name : "none?"); |
190 | seq_printf(seq, "%s", clip_vcc->encap ? "LLC/SNAP" : "None"); |
191 | } |
192 | seq_putc(seq, '\n'); |
193 | } |
194 | |
195 | static const char *vcc_state(struct atm_vcc *vcc) |
196 | { |
197 | static const char *map[] = { ATM_VS2TXT_MAP }; |
198 | |
199 | return map[ATM_VF2VS(vcc->flags)]; |
200 | } |
201 | |
202 | static void vcc_info(struct seq_file *seq, struct atm_vcc *vcc) |
203 | { |
204 | struct sock *sk = sk_atm(vcc); |
205 | |
206 | seq_printf(seq, "%p ", vcc); |
207 | if (!vcc->dev) |
208 | seq_printf(seq, "Unassigned "); |
209 | else |
210 | seq_printf(seq, "%3d %3d %5d ", vcc->dev->number, vcc->vpi, |
211 | vcc->vci); |
212 | switch (sk->sk_family) { |
213 | case AF_ATMPVC: |
214 | seq_printf(seq, "PVC"); |
215 | break; |
216 | case AF_ATMSVC: |
217 | seq_printf(seq, "SVC"); |
218 | break; |
219 | default: |
220 | seq_printf(seq, "%3d", sk->sk_family); |
221 | } |
222 | seq_printf(seq, " %04lx %5d %7d/%7d %7d/%7d [%d]\n", vcc->flags, sk->sk_err, |
223 | atomic_read(&sk->sk_wmem_alloc), sk->sk_sndbuf, |
224 | atomic_read(&sk->sk_rmem_alloc), sk->sk_rcvbuf, |
225 | atomic_read(&sk->sk_refcnt)); |
226 | } |
227 | |
228 | static void svc_info(struct seq_file *seq, struct atm_vcc *vcc) |
229 | { |
230 | if (!vcc->dev) |
231 | seq_printf(seq, sizeof(void *) == 4 ? |
232 | "N/A@%p%10s" : "N/A@%p%2s", vcc, ""); |
233 | else |
234 | seq_printf(seq, "%3d %3d %5d ", |
235 | vcc->dev->number, vcc->vpi, vcc->vci); |
236 | seq_printf(seq, "%-10s ", vcc_state(vcc)); |
237 | seq_printf(seq, "%s%s", vcc->remote.sas_addr.pub, |
238 | *vcc->remote.sas_addr.pub && *vcc->remote.sas_addr.prv ? "+" : ""); |
239 | if (*vcc->remote.sas_addr.prv) { |
240 | int i; |
241 | |
242 | for (i = 0; i < ATM_ESA_LEN; i++) |
243 | seq_printf(seq, "%02x", vcc->remote.sas_addr.prv[i]); |
244 | } |
245 | seq_putc(seq, '\n'); |
246 | } |
247 | |
248 | static int atm_dev_seq_show(struct seq_file *seq, void *v) |
249 | { |
250 | static char atm_dev_banner[] = |
251 | "Itf Type ESI/\"MAC\"addr " |
252 | "AAL(TX,err,RX,err,drop) ... [refcnt]\n"; |
253 | |
254 | if (v == (void *)1) |
255 | seq_puts(seq, atm_dev_banner); |
256 | else { |
257 | struct atm_dev *dev = list_entry(v, struct atm_dev, dev_list); |
258 | |
259 | atm_dev_info(seq, dev); |
260 | } |
261 | return 0; |
262 | } |
263 | |
264 | static struct seq_operations atm_dev_seq_ops = { |
265 | .start = atm_dev_seq_start, |
266 | .next = atm_dev_seq_next, |
267 | .stop = atm_dev_seq_stop, |
268 | .show = atm_dev_seq_show, |
269 | }; |
270 | |
271 | static int atm_dev_seq_open(struct inode *inode, struct file *file) |
272 | { |
273 | return seq_open(file, &atm_dev_seq_ops); |
274 | } |
275 | |
276 | static struct file_operations devices_seq_fops = { |
277 | .open = atm_dev_seq_open, |
278 | .read = seq_read, |
279 | .llseek = seq_lseek, |
280 | .release = seq_release, |
281 | }; |
282 | |
283 | static int pvc_seq_show(struct seq_file *seq, void *v) |
284 | { |
285 | static char atm_pvc_banner[] = |
286 | "Itf VPI VCI AAL RX(PCR,Class) TX(PCR,Class)\n"; |
287 | |
288 | if (v == (void *)1) |
289 | seq_puts(seq, atm_pvc_banner); |
290 | else { |
291 | struct vcc_state *state = seq->private; |
292 | struct atm_vcc *vcc = atm_sk(state->sk); |
293 | |
294 | pvc_info(seq, vcc); |
295 | } |
296 | return 0; |
297 | } |
298 | |
299 | static struct seq_operations pvc_seq_ops = { |
300 | .start = vcc_seq_start, |
301 | .next = vcc_seq_next, |
302 | .stop = vcc_seq_stop, |
303 | .show = pvc_seq_show, |
304 | }; |
305 | |
306 | static int pvc_seq_open(struct inode *inode, struct file *file) |
307 | { |
308 | return __vcc_seq_open(inode, file, PF_ATMPVC, &pvc_seq_ops); |
309 | } |
310 | |
311 | static struct file_operations pvc_seq_fops = { |
312 | .open = pvc_seq_open, |
313 | .read = seq_read, |
314 | .llseek = seq_lseek, |
315 | .release = vcc_seq_release, |
316 | }; |
317 | |
318 | static int vcc_seq_show(struct seq_file *seq, void *v) |
319 | { |
320 | if (v == (void *)1) { |
321 | seq_printf(seq, sizeof(void *) == 4 ? "%-8s%s" : "%-16s%s", |
322 | "Address ", "Itf VPI VCI Fam Flags Reply " |
323 | "Send buffer Recv buffer [refcnt]\n"); |
324 | } else { |
325 | struct vcc_state *state = seq->private; |
326 | struct atm_vcc *vcc = atm_sk(state->sk); |
327 | |
328 | vcc_info(seq, vcc); |
329 | } |
330 | return 0; |
331 | } |
332 | |
333 | static struct seq_operations vcc_seq_ops = { |
334 | .start = vcc_seq_start, |
335 | .next = vcc_seq_next, |
336 | .stop = vcc_seq_stop, |
337 | .show = vcc_seq_show, |
338 | }; |
339 | |
340 | static int vcc_seq_open(struct inode *inode, struct file *file) |
341 | { |
342 | return __vcc_seq_open(inode, file, 0, &vcc_seq_ops); |
343 | } |
344 | |
345 | static struct file_operations vcc_seq_fops = { |
346 | .open = vcc_seq_open, |
347 | .read = seq_read, |
348 | .llseek = seq_lseek, |
349 | .release = vcc_seq_release, |
350 | }; |
351 | |
352 | static int svc_seq_show(struct seq_file *seq, void *v) |
353 | { |
354 | static char atm_svc_banner[] = |
355 | "Itf VPI VCI State Remote\n"; |
356 | |
357 | if (v == (void *)1) |
358 | seq_puts(seq, atm_svc_banner); |
359 | else { |
360 | struct vcc_state *state = seq->private; |
361 | struct atm_vcc *vcc = atm_sk(state->sk); |
362 | |
363 | svc_info(seq, vcc); |
364 | } |
365 | return 0; |
366 | } |
367 | |
368 | static struct seq_operations svc_seq_ops = { |
369 | .start = vcc_seq_start, |
370 | .next = vcc_seq_next, |
371 | .stop = vcc_seq_stop, |
372 | .show = svc_seq_show, |
373 | }; |
374 | |
375 | static int svc_seq_open(struct inode *inode, struct file *file) |
376 | { |
377 | return __vcc_seq_open(inode, file, PF_ATMSVC, &svc_seq_ops); |
378 | } |
379 | |
380 | static struct file_operations svc_seq_fops = { |
381 | .open = svc_seq_open, |
382 | .read = seq_read, |
383 | .llseek = seq_lseek, |
384 | .release = vcc_seq_release, |
385 | }; |
386 | |
387 | static ssize_t proc_dev_atm_read(struct file *file, char __user *buf, |
388 | size_t count, loff_t *pos) |
389 | { |
390 | struct atm_dev *dev; |
391 | unsigned long page; |
392 | int length; |
393 | |
394 | if (count == 0) return 0; |
395 | page = get_zeroed_page(GFP_KERNEL); |
396 | if (!page) return -ENOMEM; |
397 | dev = PDE(file->f_dentry->d_inode)->data; |
398 | if (!dev->ops->proc_read) |
399 | length = -EINVAL; |
400 | else { |
401 | length = dev->ops->proc_read(dev,pos,(char *) page); |
402 | if (length > count) length = -EINVAL; |
403 | } |
404 | if (length >= 0) { |
405 | if (copy_to_user(buf,(char *) page,length)) length = -EFAULT; |
406 | (*pos)++; |
407 | } |
408 | free_page(page); |
409 | return length; |
410 | } |
411 | |
412 | |
413 | struct proc_dir_entry *atm_proc_root; |
414 | EXPORT_SYMBOL(atm_proc_root); |
415 | |
416 | |
417 | int atm_proc_dev_register(struct atm_dev *dev) |
418 | { |
419 | int digits,num; |
420 | int error; |
421 | |
422 | /* No proc info */ |
423 | if (!dev->ops->proc_read) |
424 | return 0; |
425 | |
426 | error = -ENOMEM; |
427 | digits = 0; |
428 | for (num = dev->number; num; num /= 10) digits++; |
429 | if (!digits) digits++; |
430 | |
431 | dev->proc_name = kmalloc(strlen(dev->type) + digits + 2, GFP_KERNEL); |
432 | if (!dev->proc_name) |
433 | goto err_out; |
434 | sprintf(dev->proc_name,"%s:%d",dev->type, dev->number); |
435 | |
436 | dev->proc_entry = create_proc_entry(dev->proc_name, 0, atm_proc_root); |
437 | if (!dev->proc_entry) |
438 | goto err_free_name; |
439 | dev->proc_entry->data = dev; |
440 | dev->proc_entry->proc_fops = &proc_atm_dev_ops; |
441 | dev->proc_entry->owner = THIS_MODULE; |
442 | return 0; |
443 | err_free_name: |
444 | kfree(dev->proc_name); |
445 | err_out: |
446 | return error; |
447 | } |
448 | |
449 | |
450 | void atm_proc_dev_deregister(struct atm_dev *dev) |
451 | { |
452 | if (!dev->ops->proc_read) |
453 | return; |
454 | |
455 | remove_proc_entry(dev->proc_name, atm_proc_root); |
456 | kfree(dev->proc_name); |
457 | } |
458 | |
459 | static struct atm_proc_entry { |
460 | char *name; |
461 | struct file_operations *proc_fops; |
462 | struct proc_dir_entry *dirent; |
463 | } atm_proc_ents[] = { |
464 | { .name = "devices", .proc_fops = &devices_seq_fops }, |
465 | { .name = "pvc", .proc_fops = &pvc_seq_fops }, |
466 | { .name = "svc", .proc_fops = &svc_seq_fops }, |
467 | { .name = "vc", .proc_fops = &vcc_seq_fops }, |
468 | { .name = NULL, .proc_fops = NULL } |
469 | }; |
470 | |
471 | static void atm_proc_dirs_remove(void) |
472 | { |
473 | static struct atm_proc_entry *e; |
474 | |
475 | for (e = atm_proc_ents; e->name; e++) { |
476 | if (e->dirent) |
477 | remove_proc_entry(e->name, atm_proc_root); |
478 | } |
479 | remove_proc_entry("net/atm", NULL); |
480 | } |
481 | |
482 | int __init atm_proc_init(void) |
483 | { |
484 | static struct atm_proc_entry *e; |
485 | int ret; |
486 | |
487 | atm_proc_root = proc_mkdir("net/atm",NULL); |
488 | if (!atm_proc_root) |
489 | goto err_out; |
490 | for (e = atm_proc_ents; e->name; e++) { |
491 | struct proc_dir_entry *dirent; |
492 | |
493 | dirent = create_proc_entry(e->name, S_IRUGO, atm_proc_root); |
494 | if (!dirent) |
495 | goto err_out_remove; |
496 | dirent->proc_fops = e->proc_fops; |
497 | dirent->owner = THIS_MODULE; |
498 | e->dirent = dirent; |
499 | } |
500 | ret = 0; |
501 | out: |
502 | return ret; |
503 | |
504 | err_out_remove: |
505 | atm_proc_dirs_remove(); |
506 | err_out: |
507 | ret = -ENOMEM; |
508 | goto out; |
509 | } |
510 | |
511 | void __exit atm_proc_exit(void) |
512 | { |
513 | atm_proc_dirs_remove(); |
514 | } |