Import 2.3.18pre1
[davej-history.git] / net / atm / proc.c
blob7875d9dfd2c4c46aed07a0503909460cce1559fc
1 /* net/atm/proc.c - ATM /proc interface */
3 /* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */
5 /*
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>
23 #include <linux/mm.h>
24 #include <linux/fs.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>
43 #include "ipcommon.h"
44 extern void clip_push(struct atm_vcc *vcc,struct sk_buff *skb);
45 #endif
47 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
48 #include "lec.h"
49 #include "lec_arpc.h"
50 extern struct atm_lane_ops atm_lane_ops; /* in common.c */
51 #endif
54 static ssize_t proc_atm_read(struct file *file,char *buf,size_t count,
55 loff_t *pos);
58 static struct file_operations proc_atm_operations = {
59 NULL, /* lseek */
60 proc_atm_read, /* read */
61 NULL, /* write */
62 NULL, /* readdir */
63 NULL, /* select */
64 NULL, /* ioctl */
65 NULL, /* mmap */
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 */
73 NULL, /* create */
74 NULL, /* lookup */
75 NULL, /* link */
76 NULL, /* unlink */
77 NULL, /* symlink */
78 NULL, /* mkdir */
79 NULL, /* rmdir */
80 NULL, /* mknod */
81 NULL, /* rename */
82 NULL, /* readlink */
83 NULL, /* follow_link */
84 NULL, /* readpage */
85 NULL, /* writepage */
86 NULL, /* bmap */
87 NULL, /* truncate */
88 NULL /* permission */
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)
100 ENTRY(devices);
101 ENTRY(pvc);
102 ENTRY(svc);
103 #ifdef CONFIG_ATM_CLIP
104 ENTRY(arp);
105 #endif
106 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
107 ENTRY(lec);
108 #endif
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");
116 if (ino == INO(pvc))
117 return sprintf(buf,"Itf VPI VCI AAL RX(PCR,Class) "
118 "TX(PCR,Class)\n");
119 if (ino == INO(svc))
120 return sprintf(buf,"Itf VPI VCI State Remote\n");
121 #ifdef CONFIG_ATM_CLIP
122 if (ino == INO(arp))
123 return sprintf(buf,"IPitf TypeEncp Idle IP address "
124 "ATM address\n");
125 #endif
126 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
127 if (ino == INO(lec))
128 return sprintf(buf,"Itf MAC ATM destination"
129 " Status Flags "
130 "VPI/VCI Recv VPI/VCI\n");
131 #endif
132 return -EINVAL;
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)
146 int off,i;
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]);
151 strcat(buf," ");
152 add_stats(buf,"0",&dev->stats.aal0);
153 strcat(buf," ");
154 add_stats(buf,"5",&dev->stats.aal5);
155 strcat(buf,"\n");
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 };
166 int *fields;
167 int len,i,j,pos;
169 len = 0;
170 if (*addr->sas_addr.pub) {
171 strcpy(buf,addr->sas_addr.pub);
172 len = strlen(addr->sas_addr.pub);
173 buf += len;
174 if (*addr->sas_addr.pub) {
175 *buf += '+';
176 len++;
179 else if (!*addr->sas_addr.prv) {
180 strcpy(buf,"(none)");
181 return strlen(buf);
183 if (*addr->sas_addr.prv) {
184 len += 44;
185 pos = 0;
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++]);
190 buf += 2;
192 if (fields[i+1]) *buf++ = '.';
195 return len;
199 static void atmarp_info(struct net_device *dev,struct atmarp_entry *entry,
200 struct clip_vcc *clip_vcc,char *buf)
202 unsigned char *ip;
203 int svc,off,ip_len;
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))/
209 HZ);
210 ip = (unsigned char *) &entry->ip;
211 ip_len = sprintf(buf+off,"%d.%d.%d.%d",ip[0],ip[1],ip[2],ip[3]);
212 off += ip_len;
213 while (ip_len++ < 16) buf[off++] = ' ';
214 if (!clip_vcc)
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));
219 else if (!svc)
220 sprintf(buf+off,"%d.%d.%d\n",clip_vcc->vcc->dev->number,
221 clip_vcc->vcc->vpi,clip_vcc->vcc->vci);
222 else {
223 off += svc_addr(buf+off,&clip_vcc->vcc->remote);
224 strcpy(buf+off,"\n");
229 #endif
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 */
240 int off;
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");
259 #endif
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)
274 char *here;
275 int i;
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]);
287 strcat(here,"\n");
291 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
293 static char*
294 lec_arp_get_status_string(unsigned char status)
296 switch(status) {
297 case ESI_UNKNOWN:
298 return "ESI_UNKNOWN ";
299 case ESI_ARP_PENDING:
300 return "ESI_ARP_PENDING ";
301 case ESI_VC_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";
307 default:
308 return "<Unknown> ";
312 static void
313 lec_info(struct lec_arp_table *entry, char *buf)
315 int j, offset=0;
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);
328 if (entry->vcc) {
329 offset+=sprintf(buf+offset, "%3d %3d ", entry->vcc->vpi,
330 entry->vcc->vci);
331 } else
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");
341 #endif
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)
351 struct atm_dev *dev;
352 struct atm_vcc *vcc;
353 int left;
355 if (ino == INO(devices)) {
356 left = *pos-1;
357 for (dev = atm_devs; dev && left; dev = dev->next) left--;
358 if (!dev) return 0;
359 dev_info(dev,buf);
360 return strlen(buf);
362 if (ino == INO(pvc)) {
363 left = *pos-1;
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--) {
368 pvc_info(vcc,buf);
369 return strlen(buf);
371 return 0;
373 if (ino == INO(svc)) {
374 left = *pos-1;
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--) {
378 svc_info(vcc,buf);
379 return strlen(buf);
381 for (vcc = nodev_vccs; vcc; vcc = vcc->next)
382 if (vcc->family == PF_ATMSVC && !left--) {
383 svc_info(vcc,buf);
384 return strlen(buf);
386 return 0;
388 #ifdef CONFIG_ATM_CLIP
389 if (ino == INO(arp)) {
390 struct neighbour *n;
391 int i,count;
393 count = *pos;
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;
400 if (!entry->vccs) {
401 if (--count) continue;
402 atmarp_info(n->dev,entry,NULL,buf);
403 read_unlock_bh(&clip_tbl.lock);
404 return strlen(buf);
406 for (vcc = entry->vccs; vcc;
407 vcc = vcc->next) {
408 if (--count) continue;
409 atmarp_info(n->dev,entry,vcc,buf);
410 read_unlock_bh(&clip_tbl.lock);
411 return strlen(buf);
414 read_unlock_bh(&clip_tbl.lock);
415 return 0;
417 #endif
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;
422 int i, count, d, e;
423 struct net_device **dev_lec;
425 if (atm_lane_ops.get_lecs == NULL)
426 return 0; /* the lane module is not there yet */
427 else
428 dev_lec = atm_lane_ops.get_lecs();
430 count = *pos;
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;
438 e=sprintf(buf,"%s ",
439 dev_lec[d]->name);
440 lec_info(entry,buf+e);
441 return strlen(buf);
444 for(entry=priv->lec_arp_empty_ones; entry;
445 entry=entry->next) {
446 if (--count) continue;
447 e=sprintf(buf,"%s ",dev_lec[d]->name);
448 lec_info(entry, buf+e);
449 return strlen(buf);
451 for(entry=priv->lec_no_forward; entry;
452 entry=entry->next) {
453 if (--count) continue;
454 e=sprintf(buf,"%s ",dev_lec[d]->name);
455 lec_info(entry, buf+e);
456 return strlen(buf);
458 for(entry=priv->mcast_fwds; entry;
459 entry=entry->next) {
460 if (--count) continue;
461 e=sprintf(buf,"%s ",dev_lec[d]->name);
462 lec_info(entry, buf+e);
463 return strlen(buf);
466 return 0;
468 #endif
469 return -EINVAL;
473 static ssize_t proc_atm_read(struct file *file,char *buf,size_t count,
474 loff_t *pos)
476 struct atm_dev *dev;
477 unsigned long page;
478 int ino = file->f_dentry->d_inode->i_ino;
479 int length;
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)
486 break;
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;
491 if (length >= 0) {
492 if (copy_to_user(buf,(char *) page,length)) length = -EFAULT;
493 (*pos)++;
495 free_page(page);
496 return length;
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)
510 ENTRY(template);
511 int digits,num;
512 int error;
514 error = -ENOMEM;
515 digits = 0;
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,
525 dev->number);
526 error = proc_register(&atm_proc_root,dev->proc_entry);
527 if (!error) return 0;
528 kfree(dev->proc_name);
529 fail1:
530 kfree(dev->proc_entry);
531 fail0:
532 return error;
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)
546 int error;
548 error = proc_register(&proc_root,&atm_proc_root);
549 REG(devices);
550 REG(pvc);
551 REG(svc);
552 #ifdef CONFIG_ATM_CLIP
553 REG(arp);
554 #endif
555 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
556 REG(lec);
557 #endif
558 return error;