1 /* net/atm/proc.c - ATM /proc interface
3 * Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA
5 * seq_file api usage by romieu@fr.zoreil.com
7 * Evaluating the efficiency of the whole thing if left as an exercise to
11 #include <linux/module.h> /* for EXPORT_SYMBOL */
12 #include <linux/string.h>
13 #include <linux/types.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 */
33 static ssize_t
proc_dev_atm_read(struct file
*file
,char __user
*buf
,size_t count
,
36 static struct file_operations proc_atm_dev_ops
= {
38 .read
= proc_dev_atm_read
,
41 static void add_stats(struct seq_file
*seq
, const char *aal
,
42 const struct k_atm_aal_stats
*stats
)
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
));
50 static void atm_dev_info(struct seq_file
*seq
, const struct atm_dev
*dev
)
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
]);
58 add_stats(seq
, "0", &dev
->stats
.aal0
);
60 add_stats(seq
, "5", &dev
->stats
.aal5
);
61 seq_printf(seq
, "\t[%d]", atomic_read(&dev
->refcnt
));
71 static inline int compare_family(struct sock
*sk
, int family
)
73 return !family
|| (sk
->sk_family
== family
);
76 static int __vcc_walk(struct sock
**sock
, int family
, int *bucket
, loff_t l
)
78 struct sock
*sk
= *sock
;
80 if (sk
== (void *)1) {
81 for (*bucket
= 0; *bucket
< VCC_HTABLE_SIZE
; ++*bucket
) {
82 struct hlist_head
*head
= &vcc_hash
[*bucket
];
84 sk
= hlist_empty(head
) ? NULL
: __sk_head(head
);
91 for (; sk
; sk
= sk_next(sk
)) {
92 l
-= compare_family(sk
, family
);
96 if (!sk
&& ++*bucket
< VCC_HTABLE_SIZE
) {
97 sk
= sk_head(&vcc_hash
[*bucket
]);
106 static inline void *vcc_walk(struct vcc_state
*state
, loff_t l
)
108 return __vcc_walk(&state
->sk
, state
->family
, &state
->bucket
, l
) ?
112 static int __vcc_seq_open(struct inode
*inode
, struct file
*file
,
113 int family
, struct seq_operations
*ops
)
115 struct vcc_state
*state
;
116 struct seq_file
*seq
;
119 state
= kmalloc(sizeof(*state
), GFP_KERNEL
);
123 rc
= seq_open(file
, ops
);
127 state
->family
= family
;
129 seq
= file
->private_data
;
130 seq
->private = state
;
138 static int vcc_seq_release(struct inode
*inode
, struct file
*file
)
140 return seq_release_private(inode
, file
);
143 static void *vcc_seq_start(struct seq_file
*seq
, loff_t
*pos
)
145 struct vcc_state
*state
= seq
->private;
148 read_lock(&vcc_sklist_lock
);
149 state
->sk
= (void *)1;
150 return left
? vcc_walk(state
, left
) : (void *)1;
153 static void vcc_seq_stop(struct seq_file
*seq
, void *v
)
155 read_unlock(&vcc_sklist_lock
);
158 static void *vcc_seq_next(struct seq_file
*seq
, void *v
, loff_t
*pos
)
160 struct vcc_state
*state
= seq
->private;
162 v
= vcc_walk(state
, 1);
163 *pos
+= !!PTR_ERR(v
);
167 static void pvc_info(struct seq_file
*seq
, struct atm_vcc
*vcc
)
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 */
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
;
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");
194 static const char *vcc_state(struct atm_vcc
*vcc
)
196 static const char *map
[] = { ATM_VS2TXT_MAP
};
198 return map
[ATM_VF2VS(vcc
->flags
)];
201 static void vcc_info(struct seq_file
*seq
, struct atm_vcc
*vcc
)
203 struct sock
*sk
= sk_atm(vcc
);
205 seq_printf(seq
, "%p ", vcc
);
207 seq_printf(seq
, "Unassigned ");
209 seq_printf(seq
, "%3d %3d %5d ", vcc
->dev
->number
, vcc
->vpi
,
211 switch (sk
->sk_family
) {
213 seq_printf(seq
, "PVC");
216 seq_printf(seq
, "SVC");
219 seq_printf(seq
, "%3d", sk
->sk_family
);
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
));
227 static void svc_info(struct seq_file
*seq
, struct atm_vcc
*vcc
)
230 seq_printf(seq
, sizeof(void *) == 4 ?
231 "N/A@%p%10s" : "N/A@%p%2s", vcc
, "");
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
) {
241 for (i
= 0; i
< ATM_ESA_LEN
; i
++)
242 seq_printf(seq
, "%02x", vcc
->remote
.sas_addr
.prv
[i
]);
247 static int atm_dev_seq_show(struct seq_file
*seq
, void *v
)
249 static char atm_dev_banner
[] =
250 "Itf Type ESI/\"MAC\"addr "
251 "AAL(TX,err,RX,err,drop) ... [refcnt]\n";
254 seq_puts(seq
, atm_dev_banner
);
256 struct atm_dev
*dev
= list_entry(v
, struct atm_dev
, dev_list
);
258 atm_dev_info(seq
, dev
);
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
,
270 static int atm_dev_seq_open(struct inode
*inode
, struct file
*file
)
272 return seq_open(file
, &atm_dev_seq_ops
);
275 static struct file_operations devices_seq_fops
= {
276 .open
= atm_dev_seq_open
,
279 .release
= seq_release
,
282 static int pvc_seq_show(struct seq_file
*seq
, void *v
)
284 static char atm_pvc_banner
[] =
285 "Itf VPI VCI AAL RX(PCR,Class) TX(PCR,Class)\n";
288 seq_puts(seq
, atm_pvc_banner
);
290 struct vcc_state
*state
= seq
->private;
291 struct atm_vcc
*vcc
= atm_sk(state
->sk
);
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
,
305 static int pvc_seq_open(struct inode
*inode
, struct file
*file
)
307 return __vcc_seq_open(inode
, file
, PF_ATMPVC
, &pvc_seq_ops
);
310 static struct file_operations pvc_seq_fops
= {
311 .open
= pvc_seq_open
,
314 .release
= vcc_seq_release
,
317 static int vcc_seq_show(struct seq_file
*seq
, void *v
)
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");
324 struct vcc_state
*state
= seq
->private;
325 struct atm_vcc
*vcc
= atm_sk(state
->sk
);
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
,
339 static int vcc_seq_open(struct inode
*inode
, struct file
*file
)
341 return __vcc_seq_open(inode
, file
, 0, &vcc_seq_ops
);
344 static struct file_operations vcc_seq_fops
= {
345 .open
= vcc_seq_open
,
348 .release
= vcc_seq_release
,
351 static int svc_seq_show(struct seq_file
*seq
, void *v
)
353 static char atm_svc_banner
[] =
354 "Itf VPI VCI State Remote\n";
357 seq_puts(seq
, atm_svc_banner
);
359 struct vcc_state
*state
= seq
->private;
360 struct atm_vcc
*vcc
= atm_sk(state
->sk
);
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
,
374 static int svc_seq_open(struct inode
*inode
, struct file
*file
)
376 return __vcc_seq_open(inode
, file
, PF_ATMSVC
, &svc_seq_ops
);
379 static struct file_operations svc_seq_fops
= {
380 .open
= svc_seq_open
,
383 .release
= vcc_seq_release
,
386 static ssize_t
proc_dev_atm_read(struct file
*file
, char __user
*buf
,
387 size_t count
, loff_t
*pos
)
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
)
400 length
= dev
->ops
->proc_read(dev
,pos
,(char *) page
);
401 if (length
> count
) length
= -EINVAL
;
404 if (copy_to_user(buf
,(char *) page
,length
)) length
= -EFAULT
;
412 struct proc_dir_entry
*atm_proc_root
;
413 EXPORT_SYMBOL(atm_proc_root
);
416 int atm_proc_dev_register(struct atm_dev
*dev
)
422 if (!dev
->ops
->proc_read
)
427 for (num
= dev
->number
; num
; num
/= 10) digits
++;
428 if (!digits
) digits
++;
430 dev
->proc_name
= kmalloc(strlen(dev
->type
) + digits
+ 2, GFP_KERNEL
);
433 sprintf(dev
->proc_name
,"%s:%d",dev
->type
, dev
->number
);
435 dev
->proc_entry
= create_proc_entry(dev
->proc_name
, 0, atm_proc_root
);
436 if (!dev
->proc_entry
)
438 dev
->proc_entry
->data
= dev
;
439 dev
->proc_entry
->proc_fops
= &proc_atm_dev_ops
;
440 dev
->proc_entry
->owner
= THIS_MODULE
;
443 kfree(dev
->proc_name
);
449 void atm_proc_dev_deregister(struct atm_dev
*dev
)
451 if (!dev
->ops
->proc_read
)
454 remove_proc_entry(dev
->proc_name
, atm_proc_root
);
455 kfree(dev
->proc_name
);
458 static struct atm_proc_entry
{
460 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
}
470 static void atm_proc_dirs_remove(void)
472 static struct atm_proc_entry
*e
;
474 for (e
= atm_proc_ents
; e
->name
; e
++) {
476 remove_proc_entry(e
->name
, atm_proc_root
);
478 remove_proc_entry("net/atm", NULL
);
481 int __init
atm_proc_init(void)
483 static struct atm_proc_entry
*e
;
486 atm_proc_root
= proc_mkdir("net/atm",NULL
);
489 for (e
= atm_proc_ents
; e
->name
; e
++) {
490 struct proc_dir_entry
*dirent
;
492 dirent
= create_proc_entry(e
->name
, S_IRUGO
, atm_proc_root
);
495 dirent
->proc_fops
= e
->proc_fops
;
496 dirent
->owner
= THIS_MODULE
;
504 atm_proc_dirs_remove();
510 void atm_proc_exit(void)
512 atm_proc_dirs_remove();