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