1 /* net/atm/proc.c - ATM /proc interface */
3 /* Written 1995-1999 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/param.h> /* for HZ */
37 #include "resources.h"
38 #include "common.h" /* atm_proc_init prototype */
39 #include "signaling.h" /* to get sigd - ugly too */
41 #ifdef CONFIG_ATM_CLIP
42 #include <net/atmclip.h>
44 extern void clip_push(struct atm_vcc
*vcc
,struct sk_buff
*skb
);
47 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
50 extern struct atm_lane_ops atm_lane_ops
; /* in common.c */
54 static ssize_t
proc_atm_read(struct file
*file
,char *buf
,size_t count
,
58 static struct file_operations proc_atm_operations
= {
60 proc_atm_read
, /* read */
66 NULL
, /* no special open code */
67 NULL
, /* no special release */
68 NULL
/* can't fsync */
71 struct inode_operations proc_atm_inode_operations
= {
72 &proc_atm_operations
, /* default ATM directory file-ops */
83 NULL
, /* follow_link */
92 #define ENTRY(name) static struct proc_dir_entry atm_proc_entry_##name = \
93 { 0, sizeof(#name)-1, #name, S_IFREG | S_IRUGO, 1, 0, 0, 0, \
94 &proc_atm_inode_operations, NULL }
95 #define REG(name) if (!error) error = proc_register(&atm_proc_root, \
96 &atm_proc_entry_##name)
97 #define INO(name) (atm_proc_entry_##name.low_ino)
103 #ifdef CONFIG_ATM_CLIP
106 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
111 static int atm_header(ino_t ino
,char *buf
)
113 if (ino
== INO(devices
))
114 return sprintf(buf
,"Itf Type ESI/\"MAC\"addr "
115 "AAL(TX,err,RX,err,drop) ...\n");
117 return sprintf(buf
,"Itf VPI VCI AAL RX(PCR,Class) "
120 return sprintf(buf
,"Itf VPI VCI State Remote\n");
121 #ifdef CONFIG_ATM_CLIP
123 return sprintf(buf
,"IPitf TypeEncp Idle IP address "
126 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
128 return sprintf(buf
,"Itf MAC ATM destination"
130 "VPI/VCI Recv VPI/VCI\n");
136 static void add_stats(char *buf
,const char *aal
,
137 const struct atm_aal_stats
*stats
)
139 sprintf(strchr(buf
,0),"%s ( %ld %ld %ld %ld %ld )",aal
,stats
->tx
,
140 stats
->tx_err
,stats
->rx
,stats
->rx_err
,stats
->rx_drop
);
144 static void dev_info(const struct atm_dev
*dev
,char *buf
)
148 off
= sprintf(buf
,"%3d %-8s",dev
->number
,dev
->type
);
149 for (i
= 0; i
< ESI_LEN
; i
++)
150 off
+= sprintf(buf
+off
,"%02x",dev
->esi
[i
]);
152 add_stats(buf
,"0",&dev
->stats
.aal0
);
154 add_stats(buf
,"5",&dev
->stats
.aal5
);
159 #ifdef CONFIG_ATM_CLIP
162 static int svc_addr(char *buf
,struct sockaddr_atmsvc
*addr
)
164 static int code
[] = { 1,2,10,6,1,0 };
165 static int e164
[] = { 1,8,4,6,1,0 };
170 if (*addr
->sas_addr
.pub
) {
171 strcpy(buf
,addr
->sas_addr
.pub
);
172 len
= strlen(addr
->sas_addr
.pub
);
174 if (*addr
->sas_addr
.pub
) {
179 else if (!*addr
->sas_addr
.prv
) {
180 strcpy(buf
,"(none)");
183 if (*addr
->sas_addr
.prv
) {
186 fields
= *addr
->sas_addr
.prv
== ATM_AFI_E164
? e164
: code
;
187 for (i
= 0; fields
[i
]; i
++) {
188 for (j
= fields
[i
]; j
; j
--) {
189 sprintf(buf
,"%02X",addr
->sas_addr
.prv
[pos
++]);
192 if (fields
[i
+1]) *buf
++ = '.';
199 static void atmarp_info(struct net_device
*dev
,struct atmarp_entry
*entry
,
200 struct clip_vcc
*clip_vcc
,char *buf
)
205 svc
= !clip_vcc
|| clip_vcc
->vcc
->family
== AF_ATMSVC
;
206 off
= sprintf(buf
,"%-6s%-4s%-4s%5ld ",dev
->name
,svc
? "SVC" : "PVC",
207 !clip_vcc
|| clip_vcc
->encap
? "LLC" : "NULL",
208 (jiffies
-(clip_vcc
? clip_vcc
->last_use
: entry
->neigh
->used
))/
210 ip
= (unsigned char *) &entry
->ip
;
211 ip_len
= sprintf(buf
+off
,"%d.%d.%d.%d",ip
[0],ip
[1],ip
[2],ip
[3]);
213 while (ip_len
++ < 16) buf
[off
++] = ' ';
215 if (time_before(jiffies
, entry
->expires
))
216 strcpy(buf
+off
,"(resolving)\n");
217 else sprintf(buf
+off
,"(expired, ref %d)\n",
218 atomic_read(&entry
->neigh
->refcnt
));
220 sprintf(buf
+off
,"%d.%d.%d\n",clip_vcc
->vcc
->dev
->number
,
221 clip_vcc
->vcc
->vpi
,clip_vcc
->vcc
->vci
);
223 off
+= svc_addr(buf
+off
,&clip_vcc
->vcc
->remote
);
224 strcpy(buf
+off
,"\n");
232 static void pvc_info(struct atm_vcc
*vcc
,char *buf
)
234 static const char *class_name
[] = { "off","UBR","CBR","VBR","ABR" };
235 static const char *aal_name
[] = {
236 "---", "1", "2", "3/4", /* 0- 3 */
237 "???", "5", "???", "???", /* 4- 7 */
238 "???", "???", "???", "???", /* 8-11 */
239 "???", "0", "???", "???"}; /* 12-15 */
242 off
= sprintf(buf
,"%3d %3d %5d %-3s %7d %-5s %7d %-6s",
243 vcc
->dev
->number
,vcc
->vpi
,vcc
->vci
,
244 vcc
->qos
.aal
>= sizeof(aal_name
)/sizeof(aal_name
[0]) ? "err" :
245 aal_name
[vcc
->qos
.aal
],vcc
->qos
.rxtp
.min_pcr
,
246 class_name
[vcc
->qos
.rxtp
.traffic_class
],vcc
->qos
.txtp
.min_pcr
,
247 class_name
[vcc
->qos
.txtp
.traffic_class
]);
248 #ifdef CONFIG_ATM_CLIP
249 if (vcc
->push
== clip_push
) {
250 struct clip_vcc
*clip_vcc
= CLIP_VCC(vcc
);
251 struct net_device
*dev
;
253 dev
= clip_vcc
->entry
? clip_vcc
->entry
->neigh
->dev
: NULL
;
254 off
+= sprintf(buf
+off
,"CLIP, Itf:%s, Encap:",
255 dev
? dev
->name
: "none?");
256 if (clip_vcc
->encap
) off
+= sprintf(buf
+off
,"LLC/SNAP");
257 else off
+= sprintf(buf
+off
,"None");
260 strcpy(buf
+off
,"\n");
264 static const char *vcc_state(struct atm_vcc
*vcc
)
266 static const char *map
[] = { ATM_VS2TXT_MAP
};
268 return map
[ATM_VF2VS(vcc
->flags
)];
272 static void svc_info(struct atm_vcc
*vcc
,char *buf
)
277 if (!vcc
->dev
) sprintf(buf
,"Unassigned ");
278 else sprintf(buf
,"%3d %3d %5d ",vcc
->dev
->number
,vcc
->vpi
,vcc
->vci
);
279 here
= strchr(buf
,0);
280 here
+= sprintf(here
,"%-10s ",vcc_state(vcc
));
281 here
+= sprintf(here
,"%s%s",vcc
->remote
.sas_addr
.pub
,
282 *vcc
->remote
.sas_addr
.pub
&& *vcc
->remote
.sas_addr
.prv
? "+" : "");
283 if (*vcc
->remote
.sas_addr
.prv
)
284 for (i
= 0; i
< ATM_ESA_LEN
; i
++)
285 here
+= sprintf(here
,"%02x",
286 vcc
->remote
.sas_addr
.prv
[i
]);
291 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
294 lec_arp_get_status_string(unsigned char status
)
298 return "ESI_UNKNOWN ";
299 case ESI_ARP_PENDING
:
300 return "ESI_ARP_PENDING ";
302 return "ESI_VC_PENDING ";
303 case ESI_FLUSH_PENDING
:
304 return "ESI_FLUSH_PENDING ";
305 case ESI_FORWARD_DIRECT
:
306 return "ESI_FORWARD_DIRECT";
313 lec_info(struct lec_arp_table
*entry
, char *buf
)
318 for(j
=0;j
<ETH_ALEN
;j
++) {
319 offset
+=sprintf(buf
+offset
,"%2.2x",0xff&entry
->mac_addr
[j
]);
321 offset
+=sprintf(buf
+offset
, " ");
322 for(j
=0;j
<ATM_ESA_LEN
;j
++) {
323 offset
+=sprintf(buf
+offset
,"%2.2x",0xff&entry
->atm_addr
[j
]);
325 offset
+=sprintf(buf
+offset
, " %s %4.4x",
326 lec_arp_get_status_string(entry
->status
),
327 entry
->flags
&0xffff);
329 offset
+=sprintf(buf
+offset
, "%3d %3d ", entry
->vcc
->vpi
,
332 offset
+=sprintf(buf
+offset
, " ");
333 if (entry
->recv_vcc
) {
334 offset
+=sprintf(buf
+offset
, " %3d %3d",
335 entry
->recv_vcc
->vpi
, entry
->recv_vcc
->vci
);
338 sprintf(buf
+offset
,"\n");
345 * FIXME: it isn't safe to walk the VCC list without turning off interrupts.
346 * What is really needed is some lock on the devices. Ditto for ATMARP.
349 static int atm_info(ino_t ino
,loff_t
*pos
,char *buf
)
355 if (ino
== INO(devices
)) {
357 for (dev
= atm_devs
; dev
&& left
; dev
= dev
->next
) left
--;
362 if (ino
== INO(pvc
)) {
364 for (dev
= atm_devs
; dev
; dev
= dev
->next
)
365 for (vcc
= dev
->vccs
; vcc
; vcc
= vcc
->next
)
366 if (vcc
->family
== PF_ATMPVC
&&
367 vcc
->dev
&& !left
--) {
373 if (ino
== INO(svc
)) {
375 for (dev
= atm_devs
; dev
; dev
= dev
->next
)
376 for (vcc
= dev
->vccs
; vcc
; vcc
= vcc
->next
)
377 if (vcc
->family
== PF_ATMSVC
&& !left
--) {
381 for (vcc
= nodev_vccs
; vcc
; vcc
= vcc
->next
)
382 if (vcc
->family
== PF_ATMSVC
&& !left
--) {
388 #ifdef CONFIG_ATM_CLIP
389 if (ino
== INO(arp
)) {
394 read_lock_bh(&clip_tbl
.lock
);
395 for (i
= 0; i
<= NEIGH_HASHMASK
; i
++)
396 for (n
= clip_tbl
.hash_buckets
[i
]; n
; n
= n
->next
) {
397 struct atmarp_entry
*entry
= NEIGH2ENTRY(n
);
398 struct clip_vcc
*vcc
;
401 if (--count
) continue;
402 atmarp_info(n
->dev
,entry
,NULL
,buf
);
403 read_unlock_bh(&clip_tbl
.lock
);
406 for (vcc
= entry
->vccs
; vcc
;
408 if (--count
) continue;
409 atmarp_info(n
->dev
,entry
,vcc
,buf
);
410 read_unlock_bh(&clip_tbl
.lock
);
414 read_unlock_bh(&clip_tbl
.lock
);
418 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
419 if (ino
== INO(lec
)) {
420 struct lec_priv
*priv
;
421 struct lec_arp_table
*entry
;
423 struct net_device
**dev_lec
;
425 if (atm_lane_ops
.get_lecs
== NULL
)
426 return 0; /* the lane module is not there yet */
428 dev_lec
= atm_lane_ops
.get_lecs();
431 for(d
=0;d
<MAX_LEC_ITF
;d
++) {
432 if (!dev_lec
[d
] || !(priv
=
433 (struct lec_priv
*) dev_lec
[d
]->priv
)) continue;
434 for(i
=0;i
<LEC_ARP_TABLE_SIZE
;i
++) {
435 entry
= priv
->lec_arp_tables
[i
];
436 for(;entry
;entry
=entry
->next
) {
437 if (--count
) continue;
440 lec_info(entry
,buf
+e
);
444 for(entry
=priv
->lec_arp_empty_ones
; entry
;
446 if (--count
) continue;
447 e
=sprintf(buf
,"%s ",dev_lec
[d
]->name
);
448 lec_info(entry
, buf
+e
);
451 for(entry
=priv
->lec_no_forward
; entry
;
453 if (--count
) continue;
454 e
=sprintf(buf
,"%s ",dev_lec
[d
]->name
);
455 lec_info(entry
, buf
+e
);
458 for(entry
=priv
->mcast_fwds
; entry
;
460 if (--count
) continue;
461 e
=sprintf(buf
,"%s ",dev_lec
[d
]->name
);
462 lec_info(entry
, buf
+e
);
473 static ssize_t
proc_atm_read(struct file
*file
,char *buf
,size_t count
,
478 int ino
= file
->f_dentry
->d_inode
->i_ino
;
481 if (count
< 0) return -EINVAL
;
482 page
= get_free_page(GFP_KERNEL
);
483 if (!page
) return -ENOMEM
;
484 for (dev
= atm_devs
; dev
; dev
= dev
->next
)
485 if (dev
->ops
->proc_read
&& dev
->proc_entry
->low_ino
== ino
)
487 if (dev
) length
= dev
->ops
->proc_read(dev
,pos
,(char *) page
);
488 else if (*pos
) length
= atm_info(ino
,pos
,(char *) page
);
489 else length
= atm_header(ino
,(char *) page
);
490 if (length
> count
) length
= -EINVAL
;
492 if (copy_to_user(buf
,(char *) page
,length
)) length
= -EFAULT
;
500 struct proc_dir_entry atm_proc_root
= { 0, 3, "atm",
501 S_IFDIR
| S_IRUGO
| S_IXUGO
, 2, 0, 0, 0, &proc_dir_inode_operations
,
502 NULL
, NULL
, NULL
, NULL
, NULL
};
505 EXPORT_SYMBOL(atm_proc_root
);
508 int atm_proc_dev_register(struct atm_dev
*dev
)
516 for (num
= dev
->number
; num
; num
/= 10) digits
++;
517 if (!digits
) digits
++;
518 dev
->proc_entry
= kmalloc(sizeof(*dev
->proc_entry
),GFP_KERNEL
);
519 if (!dev
->proc_entry
) goto fail0
;
520 dev
->proc_name
= kmalloc(strlen(dev
->type
)+digits
+2,GFP_KERNEL
);
521 if (!dev
->proc_name
) goto fail1
;
522 *dev
->proc_entry
= atm_proc_entry_template
;
523 dev
->proc_entry
->name
= dev
->proc_name
;
524 dev
->proc_entry
->namelen
= sprintf(dev
->proc_name
,"%s:%d",dev
->type
,
526 error
= proc_register(&atm_proc_root
,dev
->proc_entry
);
527 if (!error
) return 0;
528 kfree(dev
->proc_name
);
530 kfree(dev
->proc_entry
);
536 void atm_proc_dev_deregister(struct atm_dev
*dev
)
538 proc_unregister(&atm_proc_root
,dev
->proc_entry
->low_ino
);
539 kfree(dev
->proc_entry
);
540 kfree(dev
->proc_name
);
544 int __init
atm_proc_init(void)
548 error
= proc_register(&proc_root
,&atm_proc_root
);
552 #ifdef CONFIG_ATM_CLIP
555 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)