Ok. I didn't make 2.4.0 in 2000. Tough. I tried, but we had some
[davej-history.git] / net / atm / proc.c
blobef0091735aa765931f3718845c3f6e1874bfcbf7
1 /* net/atm/proc.c - ATM /proc interface */
3 /* Written 1995-2000 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/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>
44 #include "ipcommon.h"
45 extern void clip_push(struct atm_vcc *vcc,struct sk_buff *skb);
46 #endif
48 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
49 #include "lec.h"
50 #include "lec_arpc.h"
51 extern struct atm_lane_ops atm_lane_ops; /* in common.c */
52 #endif
54 static ssize_t proc_dev_atm_read(struct file *file,char *buf,size_t count,
55 loff_t *pos);
56 static ssize_t proc_spec_atm_read(struct file *file,char *buf,size_t count,
57 loff_t *pos);
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)
79 int off,i;
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]);
84 strcat(buf," ");
85 add_stats(buf,"0",&dev->stats.aal0);
86 strcat(buf," ");
87 add_stats(buf,"5",&dev->stats.aal5);
88 strcat(buf,"\n");
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 };
99 int *fields;
100 int len,i,j,pos;
102 len = 0;
103 if (*addr->sas_addr.pub) {
104 strcpy(buf,addr->sas_addr.pub);
105 len = strlen(addr->sas_addr.pub);
106 buf += len;
107 if (*addr->sas_addr.prv) {
108 *buf++ = '+';
109 len++;
112 else if (!*addr->sas_addr.prv) {
113 strcpy(buf,"(none)");
114 return strlen(buf);
116 if (*addr->sas_addr.prv) {
117 len += 44;
118 pos = 0;
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++]);
123 buf += 2;
125 if (fields[i+1]) *buf++ = '.';
128 return len;
132 static void atmarp_info(struct net_device *dev,struct atmarp_entry *entry,
133 struct clip_vcc *clip_vcc,char *buf)
135 unsigned char *ip;
136 int svc,off,ip_len;
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))/
142 HZ);
143 ip = (unsigned char *) &entry->ip;
144 ip_len = sprintf(buf+off,"%d.%d.%d.%d",ip[0],ip[1],ip[2],ip[3]);
145 off += ip_len;
146 while (ip_len++ < 16) buf[off++] = ' ';
147 if (!clip_vcc)
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));
152 else if (!svc)
153 sprintf(buf+off,"%d.%d.%d\n",clip_vcc->vcc->dev->number,
154 clip_vcc->vcc->vpi,clip_vcc->vcc->vci);
155 else {
156 off += svc_addr(buf+off,&clip_vcc->vcc->remote);
157 strcpy(buf+off,"\n");
162 #endif
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 */
173 int off;
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");
192 #endif
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)
207 char *here;
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,
212 vcc->vci);
213 switch (vcc->family) {
214 case AF_ATMPVC:
215 here += sprintf(here,"PVC");
216 break;
217 case AF_ATMSVC:
218 here += sprintf(here,"SVC");
219 break;
220 default:
221 here += sprintf(here,"%3d",vcc->family);
223 here += sprintf(here," %04lx %5d %7d/%7d %7d/%7d\n",vcc->flags.bits,
224 vcc->reply,
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)
232 char *here;
233 int i;
235 if (!vcc->dev)
236 sprintf(buf,sizeof(void *) == 4 ? "N/A@%p%10s" : "N/A@%p%2s",
237 vcc,"");
238 else sprintf(buf,"%3d %3d %5d ",vcc->dev->number,vcc->vpi,
239 vcc->vci);
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]);
248 strcat(here,"\n");
252 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
254 static char*
255 lec_arp_get_status_string(unsigned char status)
257 switch(status) {
258 case ESI_UNKNOWN:
259 return "ESI_UNKNOWN ";
260 case ESI_ARP_PENDING:
261 return "ESI_ARP_PENDING ";
262 case ESI_VC_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";
268 default:
269 return "<Unknown> ";
273 static void
274 lec_info(struct lec_arp_table *entry, char *buf)
276 int j, offset=0;
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);
288 if (entry->vcc) {
289 offset+=sprintf(buf+offset, "%3d %3d ", entry->vcc->vpi,
290 entry->vcc->vci);
291 } else
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");
301 #endif
303 static int atm_devices_info(loff_t pos,char *buf)
305 struct atm_dev *dev;
306 int left;
308 if (!pos) {
309 return sprintf(buf,"Itf Type ESI/\"MAC\"addr "
310 "AAL(TX,err,RX,err,drop) ...\n");
312 left = pos-1;
313 for (dev = atm_devs; dev && left; dev = dev->next) left--;
314 if (!dev) return 0;
315 dev_info(dev,buf);
316 return strlen(buf);
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)
326 struct atm_dev *dev;
327 struct atm_vcc *vcc;
328 int left;
330 if (!pos) {
331 return sprintf(buf,"Itf VPI VCI AAL RX(PCR,Class) "
332 "TX(PCR,Class)\n");
334 left = pos-1;
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--) {
339 pvc_info(vcc,buf);
340 return strlen(buf);
342 return 0;
346 static int atm_vc_info(loff_t pos,char *buf)
348 struct atm_dev *dev;
349 struct atm_vcc *vcc;
350 int left;
352 if (!pos)
353 return sprintf(buf,sizeof(void *) == 4 ? "%-8s%s" : "%-16s%s",
354 "Address"," Itf VPI VCI Fam Flags Reply Send buffer"
355 " Recv buffer\n");
356 left = pos-1;
357 for (dev = atm_devs; dev; dev = dev->next)
358 for (vcc = dev->vccs; vcc; vcc = vcc->next)
359 if (!left--) {
360 vc_info(vcc,buf);
361 return strlen(buf);
363 for (vcc = nodev_vccs; vcc; vcc = vcc->next)
364 if (!left--) {
365 vc_info(vcc,buf);
366 return strlen(buf);
369 return 0;
373 static int atm_svc_info(loff_t pos,char *buf)
375 struct atm_dev *dev;
376 struct atm_vcc *vcc;
377 int left;
379 if (!pos)
380 return sprintf(buf,"Itf VPI VCI State Remote\n");
381 left = pos-1;
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--) {
385 svc_info(vcc,buf);
386 return strlen(buf);
388 for (vcc = nodev_vccs; vcc; vcc = vcc->next)
389 if (vcc->family == PF_ATMSVC && !left--) {
390 svc_info(vcc,buf);
391 return strlen(buf);
393 return 0;
396 #ifdef CONFIG_ATM_CLIP
397 static int atm_arp_info(loff_t pos,char *buf)
399 struct neighbour *n;
400 int i,count;
402 if (!pos) {
403 return sprintf(buf,"IPitf TypeEncp Idle IP address "
404 "ATM address\n");
406 count = pos;
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;
413 if (!entry->vccs) {
414 if (--count) continue;
415 atmarp_info(n->dev,entry,NULL,buf);
416 read_unlock_bh(&clip_tbl.lock);
417 return strlen(buf);
419 for (vcc = entry->vccs; vcc;
420 vcc = vcc->next) {
421 if (--count) continue;
422 atmarp_info(n->dev,entry,vcc,buf);
423 read_unlock_bh(&clip_tbl.lock);
424 return strlen(buf);
427 read_unlock_bh(&clip_tbl.lock);
428 return 0;
430 #endif
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;
437 int i, count, d, e;
438 struct net_device **dev_lec;
440 if (!pos) {
441 return sprintf(buf,"Itf MAC ATM destination"
442 " Status Flags "
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 */
447 else
448 dev_lec = atm_lane_ops.get_lecs();
450 count = pos;
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;
458 e=sprintf(buf,"%s ",
459 dev_lec[d]->name);
460 lec_info(entry,buf+e);
461 return strlen(buf);
464 for(entry=priv->lec_arp_empty_ones; entry;
465 entry=entry->next) {
466 if (--count) continue;
467 e=sprintf(buf,"%s ",dev_lec[d]->name);
468 lec_info(entry, buf+e);
469 return strlen(buf);
471 for(entry=priv->lec_no_forward; entry;
472 entry=entry->next) {
473 if (--count) continue;
474 e=sprintf(buf,"%s ",dev_lec[d]->name);
475 lec_info(entry, buf+e);
476 return strlen(buf);
478 for(entry=priv->mcast_fwds; entry;
479 entry=entry->next) {
480 if (--count) continue;
481 e=sprintf(buf,"%s ",dev_lec[d]->name);
482 lec_info(entry, buf+e);
483 return strlen(buf);
486 return 0;
488 #endif
491 static ssize_t proc_dev_atm_read(struct file *file,char *buf,size_t count,
492 loff_t *pos)
494 struct atm_dev *dev;
495 unsigned long page;
496 int length;
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)
502 ->data;
503 if (!dev->ops->proc_read)
504 length = -EINVAL;
505 else {
506 length = dev->ops->proc_read(dev,pos,(char *) page);
507 if (length > count) length = -EINVAL;
509 if (length >= 0) {
510 if (copy_to_user(buf,(char *) page,length)) length = -EFAULT;
511 (*pos)++;
513 free_page(page);
514 return length;
518 static ssize_t proc_spec_atm_read(struct file *file,char *buf,size_t count,
519 loff_t *pos)
521 unsigned long page;
522 int length;
523 int (*info)(loff_t,char *);
524 info = ((struct proc_dir_entry *) file->f_dentry->d_inode->u.generic_ip)
525 ->data;
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;
532 if (length >= 0) {
533 if (copy_to_user(buf,(char *) page,length)) length = -EFAULT;
534 (*pos)++;
536 free_page(page);
537 return length;
541 struct proc_dir_entry *atm_proc_root;
542 EXPORT_SYMBOL(atm_proc_root);
545 int atm_proc_dev_register(struct atm_dev *dev)
547 int digits,num;
548 int error;
550 error = -ENOMEM;
551 digits = 0;
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)
559 goto fail0;
560 dev->proc_entry->data = dev;
561 dev->proc_entry->proc_fops = &proc_dev_atm_operations;
562 dev->proc_entry->owner = THIS_MODULE;
563 return 0;
564 kfree(dev->proc_entry);
565 fail0:
566 kfree(dev->proc_name);
567 fail1:
568 return error;
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);
593 if (!atm_proc_root)
594 return -ENOMEM;
595 CREATE_ENTRY(devices);
596 CREATE_ENTRY(pvc);
597 CREATE_ENTRY(svc);
598 CREATE_ENTRY(vc);
599 #ifdef CONFIG_ATM_CLIP
600 CREATE_ENTRY(arp);
601 #endif
602 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
603 CREATE_ENTRY(lec);
604 #endif
605 return 0;
607 cleanup:
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);
615 return -ENOMEM;