Import changeset
[linux-flexiantxendom0-3.2.10.git] / net / atm / proc.c
1 /* net/atm/proc.c - ATM /proc interface */
2
3 /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
4
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.
11  *
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 :-)
16  */
17
18
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 */
41
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
47
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
53
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);
58
59 static struct file_operations proc_dev_atm_operations = {
60         read:           proc_dev_atm_read,
61 };
62
63 static struct file_operations proc_spec_atm_operations = {
64         read:           proc_spec_atm_read,
65 };
66
67 static void add_stats(char *buf,const char *aal,
68   const struct k_atm_aal_stats *stats)
69 {
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));
74 }
75
76
77 static void dev_info(const struct atm_dev *dev,char *buf)
78 {
79         int off,i;
80
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");
89 }
90
91
92 #ifdef CONFIG_ATM_CLIP
93
94
95 static int svc_addr(char *buf,struct sockaddr_atmsvc *addr)
96 {
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;
101
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++;
110                 }
111         }
112         else if (!*addr->sas_addr.prv) {
113                         strcpy(buf,"(none)");
114                         return strlen(buf);
115                 }
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;
124                         }
125                         if (fields[i+1]) *buf++ = '.';
126                 }
127         }
128         return len;
129 }
130
131
132 static void atmarp_info(struct net_device *dev,struct atmarp_entry *entry,
133     struct clip_vcc *clip_vcc,char *buf)
134 {
135         unsigned char *ip;
136         int svc,off,ip_len;
137
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");
158                 }
159 }
160
161
162 #endif
163
164
165 static void pvc_info(struct atm_vcc *vcc,char *buf)
166 {
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;
174
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;
185
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");
191         }
192 #endif
193         strcpy(buf+off,"\n");
194 }
195
196
197 static const char *vcc_state(struct atm_vcc *vcc)
198 {
199         static const char *map[] = { ATM_VS2TXT_MAP };
200
201         return map[ATM_VF2VS(vcc->flags)];
202 }
203
204
205 static void vc_info(struct atm_vcc *vcc,char *buf)
206 {
207         char *here;
208
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);
222         }
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);
227 }
228
229
230 static void svc_info(struct atm_vcc *vcc,char *buf)
231 {
232         char *here;
233         int i;
234
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");
249 }
250
251
252 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
253
254 static char*
255 lec_arp_get_status_string(unsigned char status)
256 {
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>         ";
270   }
271 }
272
273 static void 
274 lec_info(struct lec_arp_table *entry, char *buf)
275 {
276         int j, offset=0;
277
278         for(j=0;j<ETH_ALEN;j++) {
279                 offset+=sprintf(buf+offset,"%2.2x",0xff&entry->mac_addr[j]);
280         }
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]);
284         }
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);
296         }
297
298         sprintf(buf+offset,"\n");
299 }
300
301 #endif
302
303 static int atm_devices_info(loff_t pos,char *buf)
304 {
305         struct atm_dev *dev;
306         int left;
307
308         if (!pos) {
309                 return sprintf(buf,"Itf Type    ESI/\"MAC\"addr "
310                     "AAL(TX,err,RX,err,drop) ...\n");
311         }
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);
317 }
318
319 /*
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.
322  */
323
324 static int atm_pvc_info(loff_t pos,char *buf)
325 {
326         struct atm_dev *dev;
327         struct atm_vcc *vcc;
328         int left;
329
330         if (!pos) {
331                 return sprintf(buf,"Itf VPI VCI   AAL RX(PCR,Class) "
332                     "TX(PCR,Class)\n");
333         }
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);
341                         }
342         return 0;
343 }
344
345
346 static int atm_vc_info(loff_t pos,char *buf)
347 {
348         struct atm_dev *dev;
349         struct atm_vcc *vcc;
350         int left;
351
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);
362                         }
363         for (vcc = nodev_vccs; vcc; vcc = vcc->next)
364                 if (!left--) {
365                         vc_info(vcc,buf);
366                         return strlen(buf);
367                 }
368
369         return 0;
370 }
371
372
373 static int atm_svc_info(loff_t pos,char *buf)
374 {
375         struct atm_dev *dev;
376         struct atm_vcc *vcc;
377         int left;
378
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);
387                         }
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);
392                 }
393         return 0;
394 }
395
396 #ifdef CONFIG_ATM_CLIP
397 static int atm_arp_info(loff_t pos,char *buf)
398 {
399         struct neighbour *n;
400         int i,count;
401
402         if (!pos) {
403                 return sprintf(buf,"IPitf TypeEncp Idle IP address      "
404                     "ATM address\n");
405         }
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;
412
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);
418                         }
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);
425                         }
426                 }
427         read_unlock_bh(&clip_tbl.lock);
428         return 0;
429 }
430 #endif
431
432 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
433 static int atm_lec_info(loff_t pos,char *buf)
434 {
435         struct lec_priv *priv;
436         struct lec_arp_table *entry;
437         int i, count, d, e;
438         struct net_device **dev_lec;
439
440         if (!pos) {
441                 return sprintf(buf,"Itf  MAC          ATM destination"
442                     "                          Status            Flags "
443                     "VPI/VCI Recv VPI/VCI\n");
444         }
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();
449
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);
462                         }
463                 }
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);
470                 }
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);
477                 }
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);
484                 }
485         }
486         return 0;
487 }
488 #endif
489
490
491 static ssize_t proc_dev_atm_read(struct file *file,char *buf,size_t count,
492     loff_t *pos)
493 {
494         struct atm_dev *dev;
495         unsigned long page;
496         int length;
497
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;
508         }
509         if (length >= 0) {
510                 if (copy_to_user(buf,(char *) page,length)) length = -EFAULT;
511                 (*pos)++;
512         }
513         free_page(page);
514         return length;
515 }
516
517
518 static ssize_t proc_spec_atm_read(struct file *file,char *buf,size_t count,
519     loff_t *pos)
520 {
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;
526
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)++;
535         }
536         free_page(page);
537         return length;
538 }
539
540
541 struct proc_dir_entry *atm_proc_root;
542 EXPORT_SYMBOL(atm_proc_root);
543
544
545 int atm_proc_dev_register(struct atm_dev *dev)
546 {
547         int digits,num;
548         int error;
549
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;
569 }
570
571
572 void atm_proc_dev_deregister(struct atm_dev *dev)
573 {
574         remove_proc_entry(dev->proc_name, atm_proc_root);
575         kfree(dev->proc_name);
576 }
577
578
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
585
586
587 int __init atm_proc_init(void)
588 {
589         struct proc_dir_entry *devices = NULL,*pvc = NULL,*svc = NULL;
590         struct proc_dir_entry *arp = NULL,*lec = NULL,*vc = NULL;
591
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;
606
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;
616 }