1 /* net/atm/proc.c - ATM /proc interface */
3 /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
6 * The mechanism used here isn't designed for speed but rather for convenience
7 * of implementation. We only return one entry per read system call, so we can
8 * be reasonably sure not to overrun the page and race conditions may lead to
9 * the addition or omission of some lines but never to any corruption of a
10 * line's internal structure.
12 * Making the whole thing slightly more efficient is left as an exercise to the
13 * reader. (Suggestions: wrapper which loops to get several entries per system
14 * call; or make --left slightly more clever to avoid O(n^2) characteristics.)
15 * I find it fast enough on my unloaded 266 MHz Pentium 2 :-)
19 #include <linux/config.h>
20 #include <linux/module.h> /* for EXPORT_SYMBOL */
21 #include <linux/string.h>
22 #include <linux/types.h>
25 #include <linux/stat.h>
26 #include <linux/proc_fs.h>
27 #include <linux/errno.h>
28 #include <linux/atm.h>
29 #include <linux/atmdev.h>
30 #include <linux/netdevice.h>
31 #include <linux/atmclip.h>
32 #include <linux/atmarp.h>
33 #include <linux/if_arp.h>
34 #include <linux/init.h> /* for __init */
35 #include <asm/uaccess.h>
36 #include <asm/atomic.h>
37 #include <asm/param.h> /* for HZ */
38 #include "resources.h"
39 #include "common.h" /* atm_proc_init prototype */
40 #include "signaling.h" /* to get sigd - ugly too */
42 #ifdef CONFIG_ATM_CLIP
43 #include <net/atmclip.h>
45 extern void clip_push(struct atm_vcc
*vcc
,struct sk_buff
*skb
);
48 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
51 extern struct atm_lane_ops atm_lane_ops
; /* in common.c */
54 static ssize_t
proc_dev_atm_read(struct file
*file
,char *buf
,size_t count
,
56 static ssize_t
proc_spec_atm_read(struct file
*file
,char *buf
,size_t count
,
59 static struct file_operations proc_dev_atm_operations
= {
60 read
: proc_dev_atm_read
,
63 static struct file_operations proc_spec_atm_operations
= {
64 read
: proc_spec_atm_read
,
67 static void add_stats(char *buf
,const char *aal
,
68 const struct k_atm_aal_stats
*stats
)
70 sprintf(strchr(buf
,0),"%s ( %d %d %d %d %d )",aal
,
71 atomic_read(&stats
->tx
),atomic_read(&stats
->tx_err
),
72 atomic_read(&stats
->rx
),atomic_read(&stats
->rx_err
),
73 atomic_read(&stats
->rx_drop
));
77 static void dev_info(const struct atm_dev
*dev
,char *buf
)
81 off
= sprintf(buf
,"%3d %-8s",dev
->number
,dev
->type
);
82 for (i
= 0; i
< ESI_LEN
; i
++)
83 off
+= sprintf(buf
+off
,"%02x",dev
->esi
[i
]);
85 add_stats(buf
,"0",&dev
->stats
.aal0
);
87 add_stats(buf
,"5",&dev
->stats
.aal5
);
92 #ifdef CONFIG_ATM_CLIP
95 static int svc_addr(char *buf
,struct sockaddr_atmsvc
*addr
)
97 static int code
[] = { 1,2,10,6,1,0 };
98 static int e164
[] = { 1,8,4,6,1,0 };
103 if (*addr
->sas_addr
.pub
) {
104 strcpy(buf
,addr
->sas_addr
.pub
);
105 len
= strlen(addr
->sas_addr
.pub
);
107 if (*addr
->sas_addr
.prv
) {
112 else if (!*addr
->sas_addr
.prv
) {
113 strcpy(buf
,"(none)");
116 if (*addr
->sas_addr
.prv
) {
119 fields
= *addr
->sas_addr
.prv
== ATM_AFI_E164
? e164
: code
;
120 for (i
= 0; fields
[i
]; i
++) {
121 for (j
= fields
[i
]; j
; j
--) {
122 sprintf(buf
,"%02X",addr
->sas_addr
.prv
[pos
++]);
125 if (fields
[i
+1]) *buf
++ = '.';
132 static void atmarp_info(struct net_device
*dev
,struct atmarp_entry
*entry
,
133 struct clip_vcc
*clip_vcc
,char *buf
)
138 svc
= !clip_vcc
|| clip_vcc
->vcc
->family
== AF_ATMSVC
;
139 off
= sprintf(buf
,"%-6s%-4s%-4s%5ld ",dev
->name
,svc
? "SVC" : "PVC",
140 !clip_vcc
|| clip_vcc
->encap
? "LLC" : "NULL",
141 (jiffies
-(clip_vcc
? clip_vcc
->last_use
: entry
->neigh
->used
))/
143 ip
= (unsigned char *) &entry
->ip
;
144 ip_len
= sprintf(buf
+off
,"%d.%d.%d.%d",ip
[0],ip
[1],ip
[2],ip
[3]);
146 while (ip_len
++ < 16) buf
[off
++] = ' ';
148 if (time_before(jiffies
, entry
->expires
))
149 strcpy(buf
+off
,"(resolving)\n");
150 else sprintf(buf
+off
,"(expired, ref %d)\n",
151 atomic_read(&entry
->neigh
->refcnt
));
153 sprintf(buf
+off
,"%d.%d.%d\n",clip_vcc
->vcc
->dev
->number
,
154 clip_vcc
->vcc
->vpi
,clip_vcc
->vcc
->vci
);
156 off
+= svc_addr(buf
+off
,&clip_vcc
->vcc
->remote
);
157 strcpy(buf
+off
,"\n");
165 static void pvc_info(struct atm_vcc
*vcc
,char *buf
)
167 static const char *class_name
[] = { "off","UBR","CBR","VBR","ABR" };
168 static const char *aal_name
[] = {
169 "---", "1", "2", "3/4", /* 0- 3 */
170 "???", "5", "???", "???", /* 4- 7 */
171 "???", "???", "???", "???", /* 8-11 */
172 "???", "0", "???", "???"}; /* 12-15 */
175 off
= sprintf(buf
,"%3d %3d %5d %-3s %7d %-5s %7d %-6s",
176 vcc
->dev
->number
,vcc
->vpi
,vcc
->vci
,
177 vcc
->qos
.aal
>= sizeof(aal_name
)/sizeof(aal_name
[0]) ? "err" :
178 aal_name
[vcc
->qos
.aal
],vcc
->qos
.rxtp
.min_pcr
,
179 class_name
[vcc
->qos
.rxtp
.traffic_class
],vcc
->qos
.txtp
.min_pcr
,
180 class_name
[vcc
->qos
.txtp
.traffic_class
]);
181 #ifdef CONFIG_ATM_CLIP
182 if (vcc
->push
== clip_push
) {
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 off
+= sprintf(buf
+off
,"CLIP, Itf:%s, Encap:",
188 dev
? dev
->name
: "none?");
189 if (clip_vcc
->encap
) off
+= sprintf(buf
+off
,"LLC/SNAP");
190 else off
+= sprintf(buf
+off
,"None");
193 strcpy(buf
+off
,"\n");
197 static const char *vcc_state(struct atm_vcc
*vcc
)
199 static const char *map
[] = { ATM_VS2TXT_MAP
};
201 return map
[ATM_VF2VS(vcc
->flags
)];
205 static void vc_info(struct atm_vcc
*vcc
,char *buf
)
209 here
= buf
+sprintf(buf
,"%p ",vcc
);
210 if (!vcc
->dev
) here
+= sprintf(here
,"Unassigned ");
211 else here
+= sprintf(here
,"%3d %3d %5d ",vcc
->dev
->number
,vcc
->vpi
,
213 switch (vcc
->family
) {
215 here
+= sprintf(here
,"PVC");
218 here
+= sprintf(here
,"SVC");
221 here
+= sprintf(here
,"%3d",vcc
->family
);
223 here
+= sprintf(here
," %04lx %5d %7d/%7d %7d/%7d\n",vcc
->flags
.bits
,
225 atomic_read(&vcc
->tx_inuse
),vcc
->sk
->sndbuf
,
226 atomic_read(&vcc
->rx_inuse
),vcc
->sk
->rcvbuf
);
230 static void svc_info(struct atm_vcc
*vcc
,char *buf
)
236 sprintf(buf
,sizeof(void *) == 4 ? "N/A@%p%10s" : "N/A@%p%2s",
238 else sprintf(buf
,"%3d %3d %5d ",vcc
->dev
->number
,vcc
->vpi
,
240 here
= strchr(buf
,0);
241 here
+= sprintf(here
,"%-10s ",vcc_state(vcc
));
242 here
+= sprintf(here
,"%s%s",vcc
->remote
.sas_addr
.pub
,
243 *vcc
->remote
.sas_addr
.pub
&& *vcc
->remote
.sas_addr
.prv
? "+" : "");
244 if (*vcc
->remote
.sas_addr
.prv
)
245 for (i
= 0; i
< ATM_ESA_LEN
; i
++)
246 here
+= sprintf(here
,"%02x",
247 vcc
->remote
.sas_addr
.prv
[i
]);
252 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
255 lec_arp_get_status_string(unsigned char status
)
259 return "ESI_UNKNOWN ";
260 case ESI_ARP_PENDING
:
261 return "ESI_ARP_PENDING ";
263 return "ESI_VC_PENDING ";
264 case ESI_FLUSH_PENDING
:
265 return "ESI_FLUSH_PENDING ";
266 case ESI_FORWARD_DIRECT
:
267 return "ESI_FORWARD_DIRECT";
274 lec_info(struct lec_arp_table
*entry
, char *buf
)
278 for(j
=0;j
<ETH_ALEN
;j
++) {
279 offset
+=sprintf(buf
+offset
,"%2.2x",0xff&entry
->mac_addr
[j
]);
281 offset
+=sprintf(buf
+offset
, " ");
282 for(j
=0;j
<ATM_ESA_LEN
;j
++) {
283 offset
+=sprintf(buf
+offset
,"%2.2x",0xff&entry
->atm_addr
[j
]);
285 offset
+=sprintf(buf
+offset
, " %s %4.4x",
286 lec_arp_get_status_string(entry
->status
),
287 entry
->flags
&0xffff);
289 offset
+=sprintf(buf
+offset
, "%3d %3d ", entry
->vcc
->vpi
,
292 offset
+=sprintf(buf
+offset
, " ");
293 if (entry
->recv_vcc
) {
294 offset
+=sprintf(buf
+offset
, " %3d %3d",
295 entry
->recv_vcc
->vpi
, entry
->recv_vcc
->vci
);
298 sprintf(buf
+offset
,"\n");
303 static int atm_devices_info(loff_t pos
,char *buf
)
309 return sprintf(buf
,"Itf Type ESI/\"MAC\"addr "
310 "AAL(TX,err,RX,err,drop) ...\n");
313 for (dev
= atm_devs
; dev
&& left
; dev
= dev
->next
) left
--;
320 * FIXME: it isn't safe to walk the VCC list without turning off interrupts.
321 * What is really needed is some lock on the devices. Ditto for ATMARP.
324 static int atm_pvc_info(loff_t pos
,char *buf
)
331 return sprintf(buf
,"Itf VPI VCI AAL RX(PCR,Class) "
335 for (dev
= atm_devs
; dev
; dev
= dev
->next
)
336 for (vcc
= dev
->vccs
; vcc
; vcc
= vcc
->next
)
337 if (vcc
->family
== PF_ATMPVC
&&
338 vcc
->dev
&& !left
--) {
346 static int atm_vc_info(loff_t pos
,char *buf
)
353 return sprintf(buf
,sizeof(void *) == 4 ? "%-8s%s" : "%-16s%s",
354 "Address"," Itf VPI VCI Fam Flags Reply Send buffer"
357 for (dev
= atm_devs
; dev
; dev
= dev
->next
)
358 for (vcc
= dev
->vccs
; vcc
; vcc
= vcc
->next
)
363 for (vcc
= nodev_vccs
; vcc
; vcc
= vcc
->next
)
373 static int atm_svc_info(loff_t pos
,char *buf
)
380 return sprintf(buf
,"Itf VPI VCI State Remote\n");
382 for (dev
= atm_devs
; dev
; dev
= dev
->next
)
383 for (vcc
= dev
->vccs
; vcc
; vcc
= vcc
->next
)
384 if (vcc
->family
== PF_ATMSVC
&& !left
--) {
388 for (vcc
= nodev_vccs
; vcc
; vcc
= vcc
->next
)
389 if (vcc
->family
== PF_ATMSVC
&& !left
--) {
396 #ifdef CONFIG_ATM_CLIP
397 static int atm_arp_info(loff_t pos
,char *buf
)
403 return sprintf(buf
,"IPitf TypeEncp Idle IP address "
407 read_lock_bh(&clip_tbl
.lock
);
408 for (i
= 0; i
<= NEIGH_HASHMASK
; i
++)
409 for (n
= clip_tbl
.hash_buckets
[i
]; n
; n
= n
->next
) {
410 struct atmarp_entry
*entry
= NEIGH2ENTRY(n
);
411 struct clip_vcc
*vcc
;
414 if (--count
) continue;
415 atmarp_info(n
->dev
,entry
,NULL
,buf
);
416 read_unlock_bh(&clip_tbl
.lock
);
419 for (vcc
= entry
->vccs
; vcc
;
421 if (--count
) continue;
422 atmarp_info(n
->dev
,entry
,vcc
,buf
);
423 read_unlock_bh(&clip_tbl
.lock
);
427 read_unlock_bh(&clip_tbl
.lock
);
432 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
433 static int atm_lec_info(loff_t pos
,char *buf
)
435 struct lec_priv
*priv
;
436 struct lec_arp_table
*entry
;
438 struct net_device
**dev_lec
;
441 return sprintf(buf
,"Itf MAC ATM destination"
443 "VPI/VCI Recv VPI/VCI\n");
445 if (atm_lane_ops
.get_lecs
== NULL
)
446 return 0; /* the lane module is not there yet */
448 dev_lec
= atm_lane_ops
.get_lecs();
451 for(d
=0;d
<MAX_LEC_ITF
;d
++) {
452 if (!dev_lec
[d
] || !(priv
=
453 (struct lec_priv
*) dev_lec
[d
]->priv
)) continue;
454 for(i
=0;i
<LEC_ARP_TABLE_SIZE
;i
++) {
455 entry
= priv
->lec_arp_tables
[i
];
456 for(;entry
;entry
=entry
->next
) {
457 if (--count
) continue;
460 lec_info(entry
,buf
+e
);
464 for(entry
=priv
->lec_arp_empty_ones
; entry
;
466 if (--count
) continue;
467 e
=sprintf(buf
,"%s ",dev_lec
[d
]->name
);
468 lec_info(entry
, buf
+e
);
471 for(entry
=priv
->lec_no_forward
; entry
;
473 if (--count
) continue;
474 e
=sprintf(buf
,"%s ",dev_lec
[d
]->name
);
475 lec_info(entry
, buf
+e
);
478 for(entry
=priv
->mcast_fwds
; entry
;
480 if (--count
) continue;
481 e
=sprintf(buf
,"%s ",dev_lec
[d
]->name
);
482 lec_info(entry
, buf
+e
);
491 static ssize_t
proc_dev_atm_read(struct file
*file
,char *buf
,size_t count
,
498 if (count
< 0) return -EINVAL
;
499 page
= get_free_page(GFP_KERNEL
);
500 if (!page
) return -ENOMEM
;
501 dev
= ((struct proc_dir_entry
*) file
->f_dentry
->d_inode
->u
.generic_ip
)
503 if (!dev
->ops
->proc_read
)
506 length
= dev
->ops
->proc_read(dev
,pos
,(char *) page
);
507 if (length
> count
) length
= -EINVAL
;
510 if (copy_to_user(buf
,(char *) page
,length
)) length
= -EFAULT
;
518 static ssize_t
proc_spec_atm_read(struct file
*file
,char *buf
,size_t count
,
523 int (*info
)(loff_t
,char *);
524 info
= ((struct proc_dir_entry
*) file
->f_dentry
->d_inode
->u
.generic_ip
)
527 if (count
< 0) return -EINVAL
;
528 page
= get_free_page(GFP_KERNEL
);
529 if (!page
) return -ENOMEM
;
530 length
= (*info
)(*pos
,(char *) page
);
531 if (length
> count
) length
= -EINVAL
;
533 if (copy_to_user(buf
,(char *) page
,length
)) length
= -EFAULT
;
541 struct proc_dir_entry
*atm_proc_root
;
542 EXPORT_SYMBOL(atm_proc_root
);
545 int atm_proc_dev_register(struct atm_dev
*dev
)
552 for (num
= dev
->number
; num
; num
/= 10) digits
++;
553 if (!digits
) digits
++;
554 dev
->proc_name
= kmalloc(strlen(dev
->type
)+digits
+2,GFP_KERNEL
);
555 if (!dev
->proc_name
) goto fail1
;
556 sprintf(dev
->proc_name
,"%s:%d",dev
->type
, dev
->number
);
557 dev
->proc_entry
= create_proc_entry(dev
->proc_name
, 0, atm_proc_root
);
558 if (!dev
->proc_entry
)
560 dev
->proc_entry
->data
= dev
;
561 dev
->proc_entry
->proc_fops
= &proc_dev_atm_operations
;
562 dev
->proc_entry
->owner
= THIS_MODULE
;
564 kfree(dev
->proc_entry
);
566 kfree(dev
->proc_name
);
572 void atm_proc_dev_deregister(struct atm_dev
*dev
)
574 remove_proc_entry(dev
->proc_name
, atm_proc_root
);
575 kfree(dev
->proc_name
);
579 #define CREATE_ENTRY(name) \
580 name = create_proc_entry(#name,0,atm_proc_root); \
581 if (!name) goto cleanup; \
582 name->data = atm_##name##_info; \
583 name->proc_fops = &proc_spec_atm_operations; \
584 name->owner = THIS_MODULE
587 int __init
atm_proc_init(void)
589 struct proc_dir_entry
*devices
= NULL
,*pvc
= NULL
,*svc
= NULL
;
590 struct proc_dir_entry
*arp
= NULL
,*lec
= NULL
,*vc
= NULL
;
592 atm_proc_root
= proc_mkdir("net/atm",NULL
);
595 CREATE_ENTRY(devices
);
599 #ifdef CONFIG_ATM_CLIP
602 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
608 if (devices
) remove_proc_entry("devices",atm_proc_root
);
609 if (pvc
) remove_proc_entry("pvc",atm_proc_root
);
610 if (svc
) remove_proc_entry("svc",atm_proc_root
);
611 if (arp
) remove_proc_entry("arp",atm_proc_root
);
612 if (lec
) remove_proc_entry("lec",atm_proc_root
);
613 if (vc
) remove_proc_entry("vc",atm_proc_root
);
614 remove_proc_entry("net/atm",NULL
);