patch-2.3.15 linux/net/atm/proc.c
Next file: linux/net/atm/protocols.h
Previous file: linux/net/atm/mpoa_proc.c
Back to the patch index
Back to the overall index
- Lines: 618
- Date:
Mon Aug 23 09:56:32 1999
- Orig file:
v2.3.14/linux/net/atm/proc.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.3.14/linux/net/atm/proc.c linux/net/atm/proc.c
@@ -0,0 +1,617 @@
+/* net/atm/proc.c - ATM /proc interface */
+
+/* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */
+
+/*
+ * The mechanism used here isn't designed for speed but rather for convenience
+ * of implementation. We only return one entry per read system call, so we can
+ * be reasonably sure not to overrun the page and race conditions may lead to
+ * the addition or omission of some lines but never to any corruption of a
+ * line's internal structure.
+ *
+ * Making the whole thing slightly more efficient is left as an exercise to the
+ * reader. (Suggestions: wrapper which loops to get several entries per system
+ * call; or make --left slightly more clever to avoid O(n^2) characteristics.)
+ * I find it fast enough on my unloaded 266 MHz Pentium 2 :-)
+ */
+
+
+#include <linux/config.h>
+#include <linux/module.h> /* for EXPORT_SYMBOL */
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+#include <linux/errno.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <linux/netdevice.h>
+#include <linux/atmclip.h>
+#include <linux/atmarp.h>
+#include <linux/if_arp.h>
+#include <linux/init.h> /* for __initfunc */
+#include <asm/uaccess.h>
+#include <asm/param.h> /* for HZ */
+#include "resources.h"
+#include "common.h" /* atm_proc_init prototype */
+#include "signaling.h" /* to get sigd - ugly too */
+
+#ifdef CONFIG_AREQUIPA
+#include <linux/arequipa.h>
+void atm_push_arequipa(struct atm_vcc *vcc,struct sk_buff *skb);
+#endif
+
+#ifdef CONFIG_ATM_CLIP
+#include <net/atmclip.h>
+#include "ipcommon.h"
+extern void clip_push(struct atm_vcc *vcc,struct sk_buff *skb);
+#endif
+
+#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+#include "lec.h"
+#include "lec_arpc.h"
+extern struct atm_lane_ops atm_lane_ops; /* in common.c */
+#endif
+
+
+static ssize_t proc_atm_read(struct file *file,char *buf,size_t count,
+ loff_t *pos);
+
+
+static struct file_operations proc_atm_operations = {
+ NULL, /* lseek */
+ proc_atm_read, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* select */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release */
+ NULL /* can't fsync */
+};
+
+struct inode_operations proc_atm_inode_operations = {
+ &proc_atm_operations, /* default ATM directory file-ops */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+
+#define ENTRY(name) static struct proc_dir_entry atm_proc_entry_##name = \
+ { 0, sizeof(#name)-1, #name, S_IFREG | S_IRUGO, 1, 0, 0, 0, \
+ &proc_atm_inode_operations, NULL }
+#define REG(name) if (!error) error = proc_register(&atm_proc_root, \
+ &atm_proc_entry_##name)
+#define INO(name) (atm_proc_entry_##name.low_ino)
+
+
+ENTRY(devices);
+ENTRY(pvc);
+ENTRY(svc);
+#ifdef CONFIG_ATM_CLIP
+ENTRY(arp);
+#endif
+#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+ENTRY(lec);
+#endif
+#ifdef CONFIG_AREQUIPA
+ENTRY(arequipa);
+#endif
+
+
+static int atm_header(ino_t ino,char *buf)
+{
+ if (ino == INO(devices))
+ return sprintf(buf,"Itf Type ESI/\"MAC\"addr "
+ "AAL(TX,err,RX,err,drop) ...\n");
+ if (ino == INO(pvc))
+ return sprintf(buf,"Itf VPI VCI AAL RX(PCR,Class) "
+ "TX(PCR,Class)\n");
+ if (ino == INO(svc))
+ return sprintf(buf,"Itf VPI VCI State Remote\n");
+#ifdef CONFIG_ATM_CLIP
+ if (ino == INO(arp))
+ return sprintf(buf,"IPitf TypeEncp Idle IP address "
+ "ATM address\n");
+#endif
+#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+ if (ino == INO(lec))
+ return sprintf(buf,"Itf MAC ATM destination"
+ " Status Flags "
+ "VPI/VCI Recv VPI/VCI\n");
+#endif
+#ifdef CONFIG_AREQUIPA
+ if (ino == INO(arequipa))
+ return sprintf(buf,"Itf VPI VCI State Sock# Inode\n");
+#endif
+ return -EINVAL;
+}
+
+
+static void add_stats(char *buf,const char *aal,
+ const struct atm_aal_stats *stats)
+{
+ sprintf(strchr(buf,0),"%s ( %ld %ld %ld %ld %ld )",aal,stats->tx,
+ stats->tx_err,stats->rx,stats->rx_err,stats->rx_drop);
+}
+
+
+static void dev_info(const struct atm_dev *dev,char *buf)
+{
+ int off,i;
+
+ off = sprintf(buf,"%3d %-8s",dev->number,dev->type);
+ for (i = 0; i < ESI_LEN; i++)
+ off += sprintf(buf+off,"%02x",dev->esi[i]);
+ strcat(buf," ");
+ add_stats(buf,"0",&dev->stats.aal0);
+ strcat(buf," ");
+ add_stats(buf,"5",&dev->stats.aal5);
+ strcat(buf,"\n");
+}
+
+
+#ifdef CONFIG_ATM_CLIP
+
+
+static int svc_addr(char *buf,struct sockaddr_atmsvc *addr)
+{
+ static int code[] = { 1,2,10,6,1,0 };
+ static int e164[] = { 1,8,4,6,1,0 };
+ int *fields;
+ int len,i,j,pos;
+
+ len = 0;
+ if (*addr->sas_addr.pub) {
+ strcpy(buf,addr->sas_addr.pub);
+ len = strlen(addr->sas_addr.pub);
+ buf += len;
+ if (*addr->sas_addr.pub) {
+ *buf += '+';
+ len++;
+ }
+ }
+ else if (!*addr->sas_addr.prv) {
+ strcpy(buf,"(none)");
+ return strlen(buf);
+ }
+ if (*addr->sas_addr.prv) {
+ len += 44;
+ pos = 0;
+ fields = *addr->sas_addr.prv == ATM_AFI_E164 ? e164 : code;
+ for (i = 0; fields[i]; i++) {
+ for (j = fields[i]; j; j--) {
+ sprintf(buf,"%02X",addr->sas_addr.prv[pos++]);
+ buf += 2;
+ }
+ if (fields[i+1]) *buf++ = '.';
+ }
+ }
+ return len;
+}
+
+
+static void atmarp_info(struct net_device *dev,struct atmarp_entry *entry,
+ struct clip_vcc *clip_vcc,char *buf)
+{
+ unsigned char *ip;
+ int svc,off,ip_len;
+
+ svc = !clip_vcc || clip_vcc->vcc->family == AF_ATMSVC;
+ off = sprintf(buf,"%-6s%-4s%-4s%5ld ",dev->name,svc ? "SVC" : "PVC",
+ !clip_vcc || clip_vcc->encap ? "LLC" : "NULL",
+ (jiffies-(clip_vcc ? clip_vcc->last_use : entry->neigh->used))/
+ HZ);
+ ip = (unsigned char *) &entry->ip;
+ ip_len = sprintf(buf+off,"%d.%d.%d.%d",ip[0],ip[1],ip[2],ip[3]);
+ off += ip_len;
+ while (ip_len++ < 16) buf[off++] = ' ';
+ if (!clip_vcc)
+ if (time_before(jiffies, entry->expires))
+ strcpy(buf+off,"(resolving)\n");
+ else sprintf(buf+off,"(expired, ref %d)\n",
+ atomic_read(&entry->neigh->refcnt));
+ else if (!svc)
+ sprintf(buf+off,"%d.%d.%d\n",clip_vcc->vcc->dev->number,
+ clip_vcc->vcc->vpi,clip_vcc->vcc->vci);
+ else {
+ off += svc_addr(buf+off,&clip_vcc->vcc->remote);
+ strcpy(buf+off,"\n");
+ }
+}
+
+
+#endif
+
+
+static void pvc_info(struct atm_vcc *vcc,char *buf)
+{
+ static const char *class_name[] = { "off","UBR","CBR","VBR","ABR" };
+ static const char *aal_name[] = {
+ "---", "1", "2", "3/4", /* 0- 3 */
+ "???", "5", "???", "???", /* 4- 7 */
+ "???", "???", "???", "???", /* 8-11 */
+ "???", "0", "???", "???"}; /* 12-15 */
+ int off;
+
+ off = sprintf(buf,"%3d %3d %5d %-3s %7d %-5s %7d %-6s",
+ vcc->dev->number,vcc->vpi,vcc->vci,
+ vcc->qos.aal >= sizeof(aal_name)/sizeof(aal_name[0]) ? "err" :
+ aal_name[vcc->qos.aal],vcc->qos.rxtp.min_pcr,
+ class_name[vcc->qos.rxtp.traffic_class],vcc->qos.txtp.min_pcr,
+ class_name[vcc->qos.txtp.traffic_class]);
+#ifdef CONFIG_ATM_CLIP
+ if (vcc->push == clip_push) {
+ struct clip_vcc *clip_vcc = CLIP_VCC(vcc);
+ struct net_device *dev;
+
+ dev = clip_vcc->entry ? clip_vcc->entry->neigh->dev : NULL;
+ off += sprintf(buf+off,"CLIP, Itf:%s, Encap:",
+ dev ? dev->name : "none?");
+ if (clip_vcc->encap) off += sprintf(buf+off,"LLC/SNAP");
+ else off += sprintf(buf+off,"None");
+ }
+#endif
+ strcpy(buf+off,"\n");
+}
+
+
+static const char *vcc_state(struct atm_vcc *vcc)
+{
+ static const char *map[] = { ATM_VS2TXT_MAP };
+
+ return map[ATM_VF2VS(vcc->flags)];
+}
+
+
+static void svc_info(struct atm_vcc *vcc,char *buf)
+{
+ char *here;
+ int i;
+
+ if (!vcc->dev) sprintf(buf,"Unassigned ");
+ else sprintf(buf,"%3d %3d %5d ",vcc->dev->number,vcc->vpi,vcc->vci);
+ here = strchr(buf,0);
+ here += sprintf(here,"%-10s ",vcc_state(vcc));
+ here += sprintf(here,"%s%s",vcc->remote.sas_addr.pub,
+ *vcc->remote.sas_addr.pub && *vcc->remote.sas_addr.prv ? "+" : "");
+ if (*vcc->remote.sas_addr.prv)
+ for (i = 0; i < ATM_ESA_LEN; i++)
+ here += sprintf(here,"%02x",
+ vcc->remote.sas_addr.prv[i]);
+ strcat(here,"\n");
+}
+
+
+#ifdef CONFIG_AREQUIPA
+
+
+static const char *arequipa_state(const struct atm_vcc *vcc)
+{
+ if (!(vcc->flags & ATM_VF_REGIS) && vcc->family != PF_ATMPVC)
+ return "DOOMED";
+ if (vcc->upper) return "ATTACHED";
+ return "DANGLING";
+}
+
+
+static void arequipa_info(struct atm_vcc *vcc,char *buf)
+{
+ char *here;
+
+ if (!vcc->dev) sprintf(buf,"Unassigned ");
+ else sprintf(buf,"%3d %3d %5d ",vcc->dev->number,vcc->vpi,vcc->vci);
+ here = strchr(buf,0);
+ here += sprintf(here,"%-8s ",arequipa_state(vcc));
+ if (vcc->upper)
+ here += sprintf(here,"%5d %ld",vcc->upper->num,
+ vcc->upper->socket && SOCK_INODE(vcc->upper->socket) ?
+ SOCK_INODE(vcc->upper->socket)->i_ino : 0);
+ strcat(here,"\n");
+}
+
+
+#endif
+
+
+#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+
+static char*
+lec_arp_get_status_string(unsigned char status)
+{
+ switch(status) {
+ case ESI_UNKNOWN:
+ return "ESI_UNKNOWN ";
+ case ESI_ARP_PENDING:
+ return "ESI_ARP_PENDING ";
+ case ESI_VC_PENDING:
+ return "ESI_VC_PENDING ";
+ case ESI_FLUSH_PENDING:
+ return "ESI_FLUSH_PENDING ";
+ case ESI_FORWARD_DIRECT:
+ return "ESI_FORWARD_DIRECT";
+ default:
+ return "<Unknown> ";
+ }
+}
+
+static void
+lec_info(struct lec_arp_table *entry, char *buf)
+{
+ int j, offset=0;
+
+
+ for(j=0;j<ETH_ALEN;j++) {
+ offset+=sprintf(buf+offset,"%2.2x",0xff&entry->mac_addr[j]);
+ }
+ offset+=sprintf(buf+offset, " ");
+ for(j=0;j<ATM_ESA_LEN;j++) {
+ offset+=sprintf(buf+offset,"%2.2x",0xff&entry->atm_addr[j]);
+ }
+ offset+=sprintf(buf+offset, " %s %4.4x",
+ lec_arp_get_status_string(entry->status),
+ entry->flags&0xffff);
+ if (entry->vcc) {
+ offset+=sprintf(buf+offset, "%3d %3d ", entry->vcc->vpi,
+ entry->vcc->vci);
+ } else
+ offset+=sprintf(buf+offset, " ");
+ if (entry->recv_vcc) {
+ offset+=sprintf(buf+offset, " %3d %3d",
+ entry->recv_vcc->vpi, entry->recv_vcc->vci);
+ }
+
+ sprintf(buf+offset,"\n");
+}
+
+#endif
+
+
+/*
+ * FIXME: it isn't safe to walk the VCC list without turning off interrupts.
+ * What is really needed is some lock on the devices. Ditto for ATMARP.
+ */
+
+static int atm_info(ino_t ino,loff_t *pos,char *buf)
+{
+ struct atm_dev *dev;
+ struct atm_vcc *vcc;
+ int left;
+
+ if (ino == INO(devices)) {
+ left = *pos-1;
+ for (dev = atm_devs; dev && left; dev = dev->next) left--;
+ if (!dev) return 0;
+ dev_info(dev,buf);
+ return strlen(buf);
+ }
+ if (ino == INO(pvc)) {
+ left = *pos-1;
+ for (dev = atm_devs; dev; dev = dev->next)
+ for (vcc = dev->vccs; vcc; vcc = vcc->next)
+ if (vcc->family == PF_ATMPVC &&
+ vcc->dev && !left--) {
+ pvc_info(vcc,buf);
+ return strlen(buf);
+ }
+ return 0;
+ }
+ if (ino == INO(svc)) {
+ left = *pos-1;
+ for (dev = atm_devs; dev; dev = dev->next)
+ for (vcc = dev->vccs; vcc; vcc = vcc->next)
+ if (vcc->family == PF_ATMSVC && !left--) {
+ svc_info(vcc,buf);
+ return strlen(buf);
+ }
+ for (vcc = nodev_vccs; vcc; vcc = vcc->next)
+ if (vcc->family == PF_ATMSVC && !left--) {
+ svc_info(vcc,buf);
+ return strlen(buf);
+ }
+ return 0;
+ }
+#ifdef CONFIG_ATM_CLIP
+ if (ino == INO(arp)) {
+ struct neighbour *n;
+ int i,count;
+
+ count = *pos;
+ read_lock_bh(&clip_tbl.lock);
+ for (i = 0; i <= NEIGH_HASHMASK; i++)
+ for (n = clip_tbl.hash_buckets[i]; n; n = n->next) {
+ struct atmarp_entry *entry = NEIGH2ENTRY(n);
+ struct clip_vcc *vcc;
+
+ if (!entry->vccs) {
+ if (--count) continue;
+ atmarp_info(n->dev,entry,NULL,buf);
+ read_unlock_bh(&clip_tbl.lock);
+ return strlen(buf);
+ }
+ for (vcc = entry->vccs; vcc;
+ vcc = vcc->next) {
+ if (--count) continue;
+ atmarp_info(n->dev,entry,vcc,buf);
+ read_unlock_bh(&clip_tbl.lock);
+ return strlen(buf);
+ }
+ }
+ read_unlock_bh(&clip_tbl.lock);
+ return 0;
+ }
+#endif
+#ifdef CONFIG_AREQUIPA
+ if (ino == INO(arequipa)) {
+ left = *pos-1;
+ for (dev = atm_devs; dev; dev = dev->next)
+ for (vcc = dev->vccs; vcc; vcc = vcc->next)
+ if (vcc->push == atm_push_arequipa && !left--) {
+ arequipa_info(vcc,buf);
+ return strlen(buf);
+ }
+ return 0;
+ }
+#endif
+#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+ if (ino == INO(lec)) {
+ struct lec_priv *priv;
+ struct lec_arp_table *entry;
+ int i, count, d, e;
+ struct net_device **dev_lec;
+
+ if (atm_lane_ops.get_lecs == NULL)
+ return 0; /* the lane module is not there yet */
+ else
+ dev_lec = atm_lane_ops.get_lecs();
+
+ count = *pos;
+ for(d=0;d<MAX_LEC_ITF;d++) {
+ if (!dev_lec[d] || !(priv =
+ (struct lec_priv *) dev_lec[d]->priv)) continue;
+ for(i=0;i<LEC_ARP_TABLE_SIZE;i++) {
+ entry = priv->lec_arp_tables[i];
+ for(;entry;entry=entry->next) {
+ if (--count) continue;
+ e=sprintf(buf,"%s ",
+ dev_lec[d]->name);
+ lec_info(entry,buf+e);
+ return strlen(buf);
+ }
+ }
+ for(entry=priv->lec_arp_empty_ones; entry;
+ entry=entry->next) {
+ if (--count) continue;
+ e=sprintf(buf,"%s ",dev_lec[d]->name);
+ lec_info(entry, buf+e);
+ return strlen(buf);
+ }
+ for(entry=priv->lec_no_forward; entry;
+ entry=entry->next) {
+ if (--count) continue;
+ e=sprintf(buf,"%s ",dev_lec[d]->name);
+ lec_info(entry, buf+e);
+ return strlen(buf);
+ }
+ for(entry=priv->mcast_fwds; entry;
+ entry=entry->next) {
+ if (--count) continue;
+ e=sprintf(buf,"%s ",dev_lec[d]->name);
+ lec_info(entry, buf+e);
+ return strlen(buf);
+ }
+ }
+ return 0;
+ }
+#endif
+ return -EINVAL;
+}
+
+
+static ssize_t proc_atm_read(struct file *file,char *buf,size_t count,
+ loff_t *pos)
+{
+ struct atm_dev *dev;
+ unsigned long page;
+ int ino = file->f_dentry->d_inode->i_ino;
+ int length;
+
+ if (count < 0) return -EINVAL;
+ page = get_free_page(GFP_KERNEL);
+ if (!page) return -ENOMEM;
+ for (dev = atm_devs; dev; dev = dev->next)
+ if (dev->ops->proc_read && dev->proc_entry->low_ino == ino)
+ break;
+ if (dev) length = dev->ops->proc_read(dev,pos,(char *) page);
+ else if (*pos) length = atm_info(ino,pos,(char *) page);
+ else length = atm_header(ino,(char *) page);
+ if (length > count) length = -EINVAL;
+ if (length >= 0) {
+ if (copy_to_user(buf,(char *) page,length)) length = -EFAULT;
+ (*pos)++;
+ }
+ free_page(page);
+ return length;
+}
+
+
+struct proc_dir_entry atm_proc_root = { 0, 3, "atm",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0, 0, &proc_dir_inode_operations,
+ NULL, NULL, NULL, NULL, NULL };
+
+
+EXPORT_SYMBOL(atm_proc_root);
+
+
+int atm_proc_dev_register(struct atm_dev *dev)
+{
+ ENTRY(template);
+ int digits,num;
+ int error;
+
+ error = -ENOMEM;
+ digits = 0;
+ for (num = dev->number; num; num /= 10) digits++;
+ if (!digits) digits++;
+ dev->proc_entry = kmalloc(sizeof(*dev->proc_entry),GFP_KERNEL);
+ if (!dev->proc_entry) goto fail0;
+ dev->proc_name = kmalloc(strlen(dev->type)+digits+2,GFP_KERNEL);
+ if (!dev->proc_name) goto fail1;
+ *dev->proc_entry = atm_proc_entry_template;
+ dev->proc_entry->name = dev->proc_name;
+ dev->proc_entry->namelen = sprintf(dev->proc_name,"%s:%d",dev->type,
+ dev->number);
+ error = proc_register(&atm_proc_root,dev->proc_entry);
+ if (!error) return 0;
+ kfree(dev->proc_name);
+fail1:
+ kfree(dev->proc_entry);
+fail0:
+ return error;
+}
+
+
+void atm_proc_dev_deregister(struct atm_dev *dev)
+{
+ proc_unregister(&atm_proc_root,dev->proc_entry->low_ino);
+ kfree(dev->proc_entry);
+ kfree(dev->proc_name);
+}
+
+
+__initfunc(int atm_proc_init(void))
+{
+ int error;
+
+ error = proc_register(&proc_root,&atm_proc_root);
+ REG(devices);
+ REG(pvc);
+ REG(svc);
+#ifdef CONFIG_ATM_CLIP
+ REG(arp);
+#endif
+#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+ REG(lec);
+#endif
+#ifdef CONFIG_AREQUIPA
+ REG(arequipa);
+#endif
+ return error;
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)