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 #if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
43 #include <net/atmclip.h>
44 #include "ipcommon.h"
45 #endif
46 
47 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
48 #include "lec.h"
49 #include "lec_arpc.h"
50 #endif
51 
52 static ssize_t proc_dev_atm_read(struct file *file,char *buf,size_t count,
53     loff_t *pos);
54 static ssize_t proc_spec_atm_read(struct file *file,char *buf,size_t count,
55     loff_t *pos);
56 
57 static struct file_operations proc_dev_atm_operations = {
58 	read:		proc_dev_atm_read,
59 };
60 
61 static struct file_operations proc_spec_atm_operations = {
62 	read:		proc_spec_atm_read,
63 };
64 
add_stats(char * buf,const char * aal,const struct k_atm_aal_stats * stats)65 static void add_stats(char *buf,const char *aal,
66   const struct k_atm_aal_stats *stats)
67 {
68 	sprintf(strchr(buf,0),"%s ( %d %d %d %d %d )",aal,
69 	    atomic_read(&stats->tx),atomic_read(&stats->tx_err),
70 	    atomic_read(&stats->rx),atomic_read(&stats->rx_err),
71 	    atomic_read(&stats->rx_drop));
72 }
73 
74 
dev_info(const struct atm_dev * dev,char * buf)75 static void dev_info(const struct atm_dev *dev,char *buf)
76 {
77 	int off,i;
78 
79 	off = sprintf(buf,"%3d %-8s",dev->number,dev->type);
80 	for (i = 0; i < ESI_LEN; i++)
81 		off += sprintf(buf+off,"%02x",dev->esi[i]);
82 	strcat(buf,"  ");
83 	add_stats(buf,"0",&dev->stats.aal0);
84 	strcat(buf,"  ");
85 	add_stats(buf,"5",&dev->stats.aal5);
86 	sprintf(strchr(buf,0), "\t[%d]", atomic_read(&dev->refcnt));
87 	strcat(buf,"\n");
88 }
89 
90 
91 #if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
92 
93 #define SEQ_NO_VCC_TOKEN   ((void *) 2)
94 
svc_addr(struct seq_file * seq,struct sockaddr_atmsvc * addr)95 static void svc_addr(struct seq_file *seq, 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 
100 	if (*addr->sas_addr.pub) {
101 		seq_printf(seq, "%s", addr->sas_addr.pub);
102 		if (*addr->sas_addr.prv)
103 			seq_putc(seq, '+');
104 	} else if (!*addr->sas_addr.prv) {
105 		seq_printf(seq, "%s", "(none)");
106 		return;
107 	}
108 	if (*addr->sas_addr.prv) {
109 		unsigned char *prv = addr->sas_addr.prv;
110 		int *fields;
111 		int i, j;
112 
113 		fields = *prv == ATM_AFI_E164 ? e164 : code;
114 		for (i = 0; fields[i]; i++) {
115 			for (j = fields[i]; j; j--)
116 				seq_printf(seq, "%02X", *prv++);
117 			if (fields[i+1])
118 				seq_putc(seq, '.');
119 		}
120 	}
121 }
122 
123 
atmarp_info(struct seq_file * seq,struct net_device * dev,struct atmarp_entry * entry,struct clip_vcc * clip_vcc)124 static void atmarp_info(struct seq_file *seq, struct net_device *dev,struct
125 			atmarp_entry *entry, struct clip_vcc *clip_vcc) {
126 	unsigned long exp;
127 	char buf[17];
128 	int svc, llc, off;
129 
130 	svc = ((clip_vcc == SEQ_NO_VCC_TOKEN) ||
131 	       (clip_vcc->vcc->sk->family == AF_ATMSVC));
132 
133 	llc = ((clip_vcc == SEQ_NO_VCC_TOKEN) ||
134 	       (clip_vcc->encap));
135 
136 	if (clip_vcc == SEQ_NO_VCC_TOKEN)
137 		exp = entry->neigh->used;
138 	else
139 		exp = clip_vcc->last_use;
140 
141 	exp = (jiffies - exp) / HZ;
142 
143 	seq_printf(seq, "%-6s%-4s%-4s%5ld ",
144 		   dev->name,
145 		   svc ? "SVC" : "PVC",
146 		   llc ? "LLC" : "NULL",
147 		   exp);
148 
149 	off = snprintf(buf, sizeof(buf)-1, "%d.%d.%d.%d", NIPQUAD(entry->ip));
150 	while (off < 16)
151 		buf[off++] = ' ';
152 	buf[off] = '\0';
153 	seq_printf(seq, "%s", buf);
154 
155 	if (clip_vcc == SEQ_NO_VCC_TOKEN) {
156 		if (time_before(jiffies, entry->expires))
157 			seq_printf(seq, "(resolving)\n");
158 		else
159 			seq_printf(seq, "(expired, ref %d)\n",
160 				   atomic_read(&entry->neigh->refcnt));
161 	} else if (!svc) {
162 		seq_printf(seq, "%d.%d.%d\n",
163 			   clip_vcc->vcc->dev->number,
164 			   clip_vcc->vcc->vpi,
165 			   clip_vcc->vcc->vci);
166 	} else {
167 		svc_addr(seq, &clip_vcc->vcc->remote);
168 		seq_putc(seq, '\n');
169 	}
170 }
171 
172 struct clip_seq_state {
173 	/* This member must be first. */
174 	struct neigh_seq_state ns;
175 
176 	/* Local to clip specific iteration. */
177 	struct clip_vcc *vcc;
178 };
179 
clip_seq_next_vcc(struct atmarp_entry * e,struct clip_vcc * curr)180 static struct clip_vcc *clip_seq_next_vcc(struct atmarp_entry *e,
181 					  struct clip_vcc *curr)
182 {
183 	if (!curr) {
184 		curr = e->vccs;
185 		if (!curr)
186 			return SEQ_NO_VCC_TOKEN;
187 		return curr;
188 	}
189 
190 	if (curr == SEQ_NO_VCC_TOKEN)
191 		return NULL;
192 
193 	curr = curr->next;
194 
195 	return curr;
196 }
197 
clip_seq_vcc_walk(struct clip_seq_state * state,struct atmarp_entry * e,loff_t * pos)198 static void *clip_seq_vcc_walk(struct clip_seq_state *state,
199 			       struct atmarp_entry *e, loff_t *pos)
200 {
201 	struct clip_vcc *vcc = state->vcc;
202 
203 	vcc = clip_seq_next_vcc(e, vcc);
204 	if (vcc && pos != NULL) {
205 		while (*pos) {
206 			vcc = clip_seq_next_vcc(e, vcc);
207 			if (!vcc)
208 				break;
209 			--(*pos);
210 		}
211 	}
212 	state->vcc = vcc;
213 
214 	return vcc;
215 }
216 
clip_seq_sub_iter(struct neigh_seq_state * _state,struct neighbour * n,loff_t * pos)217 static void *clip_seq_sub_iter(struct neigh_seq_state *_state,
218 			       struct neighbour *n, loff_t *pos)
219 {
220 	struct clip_seq_state *state = (struct clip_seq_state *) _state;
221 
222 	return clip_seq_vcc_walk(state, NEIGH2ENTRY(n), pos);
223 }
224 
clip_seq_start(struct seq_file * seq,loff_t * pos)225 static void *clip_seq_start(struct seq_file *seq, loff_t *pos)
226 {
227 	return neigh_seq_start(seq, pos, clip_tbl_hook, NEIGH_SEQ_NEIGH_ONLY);
228 }
229 
clip_seq_show(struct seq_file * seq,void * v)230 static int clip_seq_show(struct seq_file *seq, void *v)
231 {
232 	static char atm_arp_banner[] =
233 		"IPitf TypeEncp Idle IP address      ATM address\n";
234 
235 	if (v == SEQ_START_TOKEN) {
236 		seq_puts(seq, atm_arp_banner);
237 	} else {
238 		struct clip_seq_state *state = seq->private;
239 		struct neighbour *n = v;
240 		struct clip_vcc *vcc = state->vcc;
241 
242 		atmarp_info(seq, n->dev, NEIGH2ENTRY(n), vcc);
243 	}
244 	return 0;
245 }
246 
247 static struct seq_operations arp_seq_ops = {
248 	.start	= clip_seq_start,
249 	.next	= neigh_seq_next,
250 	.stop	= neigh_seq_stop,
251 	.show	= clip_seq_show,
252 };
253 
arp_seq_open(struct inode * inode,struct file * file)254 static int arp_seq_open(struct inode *inode, struct file *file)
255 {
256 	struct clip_seq_state *state;
257 	struct seq_file *seq;
258 	int rc = -EAGAIN;
259 
260 	if (!clip_tbl_hook)
261 		goto out;
262 
263 	state = kmalloc(sizeof(*state), GFP_KERNEL);
264 	if (!state) {
265 		rc = -ENOMEM;
266 		goto out_kfree;
267 	}
268 	memset(state, 0, sizeof(*state));
269 	state->ns.neigh_sub_iter = clip_seq_sub_iter;
270 
271 	rc = seq_open(file, &arp_seq_ops);
272 	if (rc)
273 		goto out_kfree;
274 
275 	seq = file->private_data;
276 	seq->private = state;
277 out:
278 	return rc;
279 
280 out_kfree:
281 	kfree(state);
282 	goto out;
283 }
284 
285 static struct file_operations arp_seq_fops = {
286 	.open		= arp_seq_open,
287 	.read		= seq_read,
288 	.llseek		= seq_lseek,
289 	.release	= seq_release_private,
290 	.owner		= THIS_MODULE,
291 };
292 #endif
293 
294 
pvc_info(struct atm_vcc * vcc,char * buf,int clip_info)295 static void pvc_info(struct atm_vcc *vcc, char *buf, int clip_info)
296 {
297 	static const char *class_name[] = { "off","UBR","CBR","VBR","ABR" };
298 	static const char *aal_name[] = {
299 		"---",	"1",	"2",	"3/4",	/*  0- 3 */
300 		"???",	"5",	"???",	"???",	/*  4- 7 */
301 		"???",	"???",	"???",	"???",	/*  8-11 */
302 		"???",	"0",	"???",	"???"};	/* 12-15 */
303 	int off;
304 
305 	off = sprintf(buf,"%3d %3d %5d %-3s %7d %-5s %7d %-6s",
306 	    vcc->dev->number,vcc->vpi,vcc->vci,
307 	    vcc->qos.aal >= sizeof(aal_name)/sizeof(aal_name[0]) ? "err" :
308 	    aal_name[vcc->qos.aal],vcc->qos.rxtp.min_pcr,
309 	    class_name[vcc->qos.rxtp.traffic_class],vcc->qos.txtp.min_pcr,
310 	    class_name[vcc->qos.txtp.traffic_class]);
311 #if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
312 	if (clip_info && (vcc->push == atm_clip_ops->clip_push)) {
313 		struct clip_vcc *clip_vcc = CLIP_VCC(vcc);
314 		struct net_device *dev;
315 
316 		dev = clip_vcc->entry ? clip_vcc->entry->neigh->dev : NULL;
317 		off += sprintf(buf+off,"CLIP, Itf:%s, Encap:",
318 		    dev ? dev->name : "none?");
319 		if (clip_vcc->encap)
320 			off += sprintf(buf+off,"LLC/SNAP");
321 		else
322 			off += sprintf(buf+off,"None");
323 	}
324 #endif
325 	strcpy(buf+off,"\n");
326 }
327 
328 
vcc_state(struct atm_vcc * vcc)329 static const char *vcc_state(struct atm_vcc *vcc)
330 {
331 	static const char *map[] = { ATM_VS2TXT_MAP };
332 
333 	return map[ATM_VF2VS(vcc->flags)];
334 }
335 
336 
vc_info(struct atm_vcc * vcc,char * buf)337 static void vc_info(struct atm_vcc *vcc,char *buf)
338 {
339 	char *here;
340 
341 	here = buf+sprintf(buf,"%p ",vcc);
342 	if (!vcc->dev) here += sprintf(here,"Unassigned    ");
343 	else here += sprintf(here,"%3d %3d %5d ",vcc->dev->number,vcc->vpi,
344 		    vcc->vci);
345 	switch (vcc->sk->family) {
346 		case AF_ATMPVC:
347 			here += sprintf(here,"PVC");
348 			break;
349 		case AF_ATMSVC:
350 			here += sprintf(here,"SVC");
351 			break;
352 		default:
353 			here += sprintf(here,"%3d",vcc->sk->family);
354 	}
355 	here += sprintf(here," %04lx  %5d %7d/%7d %7d/%7d\n",vcc->flags.bits,
356 	    vcc->reply,
357 	    atomic_read(&vcc->sk->wmem_alloc),vcc->sk->sndbuf,
358 	    atomic_read(&vcc->sk->rmem_alloc),vcc->sk->rcvbuf);
359 }
360 
361 
svc_info(struct atm_vcc * vcc,char * buf)362 static void svc_info(struct atm_vcc *vcc,char *buf)
363 {
364 	char *here;
365 	int i;
366 
367 	if (!vcc->dev)
368 		sprintf(buf,sizeof(void *) == 4 ? "N/A@%p%10s" : "N/A@%p%2s",
369 		    vcc,"");
370 	else sprintf(buf,"%3d %3d %5d         ",vcc->dev->number,vcc->vpi,
371 		    vcc->vci);
372 	here = strchr(buf,0);
373 	here += sprintf(here,"%-10s ",vcc_state(vcc));
374 	here += sprintf(here,"%s%s",vcc->remote.sas_addr.pub,
375 	    *vcc->remote.sas_addr.pub && *vcc->remote.sas_addr.prv ? "+" : "");
376 	if (*vcc->remote.sas_addr.prv)
377 		for (i = 0; i < ATM_ESA_LEN; i++)
378 			here += sprintf(here,"%02x",
379 			    vcc->remote.sas_addr.prv[i]);
380 	strcat(here,"\n");
381 }
382 
383 
384 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
385 
386 static char*
lec_arp_get_status_string(unsigned char status)387 lec_arp_get_status_string(unsigned char status)
388 {
389   switch(status) {
390   case ESI_UNKNOWN:
391     return "ESI_UNKNOWN       ";
392   case ESI_ARP_PENDING:
393     return "ESI_ARP_PENDING   ";
394   case ESI_VC_PENDING:
395     return "ESI_VC_PENDING    ";
396   case ESI_FLUSH_PENDING:
397     return "ESI_FLUSH_PENDING ";
398   case ESI_FORWARD_DIRECT:
399     return "ESI_FORWARD_DIRECT";
400   default:
401     return "<Unknown>         ";
402   }
403 }
404 
405 static void
lec_info(struct lec_arp_table * entry,char * buf)406 lec_info(struct lec_arp_table *entry, char *buf)
407 {
408         int j, offset=0;
409 
410         for(j=0;j<ETH_ALEN;j++) {
411                 offset+=sprintf(buf+offset,"%2.2x",0xff&entry->mac_addr[j]);
412         }
413         offset+=sprintf(buf+offset, " ");
414         for(j=0;j<ATM_ESA_LEN;j++) {
415                 offset+=sprintf(buf+offset,"%2.2x",0xff&entry->atm_addr[j]);
416         }
417         offset+=sprintf(buf+offset, " %s %4.4x",
418                         lec_arp_get_status_string(entry->status),
419                         entry->flags&0xffff);
420         if (entry->vcc) {
421                 offset+=sprintf(buf+offset, "%3d %3d ", entry->vcc->vpi,
422                                 entry->vcc->vci);
423         } else
424                 offset+=sprintf(buf+offset, "        ");
425         if (entry->recv_vcc) {
426                 offset+=sprintf(buf+offset, "     %3d %3d",
427                                 entry->recv_vcc->vpi, entry->recv_vcc->vci);
428         }
429 
430         sprintf(buf+offset,"\n");
431 }
432 
433 #endif
434 
atm_devices_info(loff_t pos,char * buf)435 static int atm_devices_info(loff_t pos,char *buf)
436 {
437 	struct atm_dev *dev;
438 	struct list_head *p;
439 	int left;
440 
441 	if (!pos) {
442 		return sprintf(buf,"Itf Type    ESI/\"MAC\"addr "
443 		    "AAL(TX,err,RX,err,drop) ...               [refcnt]\n");
444 	}
445 	left = pos-1;
446 	spin_lock(&atm_dev_lock);
447 	list_for_each(p, &atm_devs) {
448 		dev = list_entry(p, struct atm_dev, dev_list);
449 		if (left-- == 0) {
450 			dev_info(dev,buf);
451 			spin_unlock(&atm_dev_lock);
452 			return strlen(buf);
453 		}
454 	}
455 	spin_unlock(&atm_dev_lock);
456 	return 0;
457 }
458 
459 /*
460  * FIXME: it isn't safe to walk the VCC list without turning off interrupts.
461  * What is really needed is some lock on the devices. Ditto for ATMARP.
462  */
463 
atm_pvc_info(loff_t pos,char * buf)464 static int atm_pvc_info(loff_t pos,char *buf)
465 {
466 	struct sock *s;
467 	struct atm_vcc *vcc;
468 	int left, clip_info = 0;
469 
470 	if (!pos) {
471 		return sprintf(buf,"Itf VPI VCI   AAL RX(PCR,Class) "
472 		    "TX(PCR,Class)\n");
473 	}
474 	left = pos-1;
475 #if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
476 	if (try_atm_clip_ops())
477 		clip_info = 1;
478 #endif
479 	read_lock(&vcc_sklist_lock);
480 	for(s = vcc_sklist; s; s = s->next) {
481 		vcc = s->protinfo.af_atm;
482 		if (vcc->sk->family == PF_ATMPVC && vcc->dev && !left--) {
483 			pvc_info(vcc,buf,clip_info);
484 			read_unlock(&vcc_sklist_lock);
485 #if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
486 			if (clip_info && atm_clip_ops->owner)
487 				__MOD_DEC_USE_COUNT(atm_clip_ops->owner);
488 #endif
489 			return strlen(buf);
490 		}
491 	}
492 	read_unlock(&vcc_sklist_lock);
493 #if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
494 	if (clip_info && atm_clip_ops->owner)
495 			__MOD_DEC_USE_COUNT(atm_clip_ops->owner);
496 #endif
497 	return 0;
498 }
499 
500 
atm_vc_info(loff_t pos,char * buf)501 static int atm_vc_info(loff_t pos,char *buf)
502 {
503 	struct atm_vcc *vcc;
504 	struct sock *s;
505 	int left;
506 
507 	if (!pos)
508 		return sprintf(buf,sizeof(void *) == 4 ? "%-8s%s" : "%-16s%s",
509 		    "Address"," Itf VPI VCI   Fam Flags Reply Send buffer"
510 		    "     Recv buffer\n");
511 	left = pos-1;
512 	read_lock(&vcc_sklist_lock);
513 	for(s = vcc_sklist; s; s = s->next) {
514 		vcc = s->protinfo.af_atm;
515 		if (!left--) {
516 			vc_info(vcc,buf);
517 			read_unlock(&vcc_sklist_lock);
518 			return strlen(buf);
519 		}
520 	}
521 	read_unlock(&vcc_sklist_lock);
522 
523 	return 0;
524 }
525 
526 
atm_svc_info(loff_t pos,char * buf)527 static int atm_svc_info(loff_t pos,char *buf)
528 {
529 	struct sock *s;
530 	struct atm_vcc *vcc;
531 	int left;
532 
533 	if (!pos)
534 		return sprintf(buf,"Itf VPI VCI           State      Remote\n");
535 	left = pos-1;
536 	read_lock(&vcc_sklist_lock);
537 	for(s = vcc_sklist; s; s = s->next) {
538 		vcc = s->protinfo.af_atm;
539 		if (vcc->sk->family == PF_ATMSVC && !left--) {
540 			svc_info(vcc,buf);
541 			read_unlock(&vcc_sklist_lock);
542 			return strlen(buf);
543 		}
544 	}
545 	read_unlock(&vcc_sklist_lock);
546 
547 	return 0;
548 }
549 
550 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
atm_lec_info(loff_t pos,char * buf)551 static int atm_lec_info(loff_t pos,char *buf)
552 {
553 	unsigned long flags;
554 	struct lec_priv *priv;
555 	struct lec_arp_table *entry;
556 	int i, count, d, e;
557 	struct net_device *dev;
558 
559 	if (!pos) {
560 		return sprintf(buf,"Itf  MAC          ATM destination"
561 		    "                          Status            Flags "
562 		    "VPI/VCI Recv VPI/VCI\n");
563 	}
564 	if (!try_atm_lane_ops())
565 		return 0; /* the lane module is not there yet */
566 
567 	count = pos;
568 	for(d = 0; d < MAX_LEC_ITF; d++) {
569 		dev = atm_lane_ops->get_lec(d);
570 		if (!dev || !(priv = (struct lec_priv *) dev->priv))
571 			continue;
572 		spin_lock_irqsave(&priv->lec_arp_lock, flags);
573 		for(i = 0; i < LEC_ARP_TABLE_SIZE; i++) {
574 			for(entry = priv->lec_arp_tables[i]; entry; entry = entry->next) {
575 				if (--count)
576 					continue;
577 				e = sprintf(buf,"%s ", dev->name);
578 				lec_info(entry, buf+e);
579 				spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
580 				dev_put(dev);
581 				if (atm_lane_ops->owner)
582 					__MOD_DEC_USE_COUNT(atm_lane_ops->owner);
583 				return strlen(buf);
584 			}
585 		}
586 		for(entry = priv->lec_arp_empty_ones; entry; entry = entry->next) {
587 			if (--count)
588 				continue;
589 			e = sprintf(buf,"%s ", dev->name);
590 			lec_info(entry, buf+e);
591 			spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
592 			dev_put(dev);
593 			if (atm_lane_ops->owner)
594 				__MOD_DEC_USE_COUNT(atm_lane_ops->owner);
595 			return strlen(buf);
596 		}
597 		for(entry = priv->lec_no_forward; entry; entry=entry->next) {
598 			if (--count)
599 				continue;
600 			e = sprintf(buf,"%s ", dev->name);
601 			lec_info(entry, buf+e);
602 			spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
603 			dev_put(dev);
604 			if (atm_lane_ops->owner)
605 				__MOD_DEC_USE_COUNT(atm_lane_ops->owner);
606 			return strlen(buf);
607 		}
608 		for(entry = priv->mcast_fwds; entry; entry = entry->next) {
609 			if (--count)
610 				continue;
611 			e = sprintf(buf,"%s ", dev->name);
612 			lec_info(entry, buf+e);
613 			spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
614 			dev_put(dev);
615 			if (atm_lane_ops->owner)
616 				__MOD_DEC_USE_COUNT(atm_lane_ops->owner);
617 			return strlen(buf);
618 		}
619 		spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
620 		dev_put(dev);
621 	}
622 	if (atm_lane_ops->owner)
623 		__MOD_DEC_USE_COUNT(atm_lane_ops->owner);
624 	return 0;
625 }
626 #endif
627 
628 
proc_dev_atm_read(struct file * file,char * buf,size_t count,loff_t * pos)629 static ssize_t proc_dev_atm_read(struct file *file,char *buf,size_t count,
630     loff_t *pos)
631 {
632 	struct atm_dev *dev;
633 	unsigned long page;
634 	int length;
635 
636 	if (count == 0) return 0;
637 	page = get_free_page(GFP_KERNEL);
638 	if (!page) return -ENOMEM;
639 	dev = ((struct proc_dir_entry *) file->f_dentry->d_inode->u.generic_ip)
640 	    ->data;
641 	if (!dev->ops->proc_read)
642 		length = -EINVAL;
643 	else {
644 		length = dev->ops->proc_read(dev,pos,(char *) page);
645 		if (length > count) length = -EINVAL;
646 	}
647 	if (length >= 0) {
648 		if (copy_to_user(buf,(char *) page,length)) length = -EFAULT;
649 		(*pos)++;
650 	}
651 	free_page(page);
652 	return length;
653 }
654 
655 
proc_spec_atm_read(struct file * file,char * buf,size_t count,loff_t * pos)656 static ssize_t proc_spec_atm_read(struct file *file,char *buf,size_t count,
657     loff_t *pos)
658 {
659 	unsigned long page;
660 	int length;
661 	int (*info)(loff_t,char *);
662 	info = ((struct proc_dir_entry *) file->f_dentry->d_inode->u.generic_ip)
663 	    ->data;
664 
665 	if (count == 0) return 0;
666 	page = get_free_page(GFP_KERNEL);
667 	if (!page) return -ENOMEM;
668 	length = (*info)(*pos,(char *) page);
669 	if (length > count) length = -EINVAL;
670 	if (length >= 0) {
671 		if (copy_to_user(buf,(char *) page,length)) length = -EFAULT;
672 		(*pos)++;
673 	}
674 	free_page(page);
675 	return length;
676 }
677 
678 
679 struct proc_dir_entry *atm_proc_root;
680 EXPORT_SYMBOL(atm_proc_root);
681 
682 
atm_proc_dev_register(struct atm_dev * dev)683 int atm_proc_dev_register(struct atm_dev *dev)
684 {
685 	int digits,num;
686 	int error;
687 
688 	error = -ENOMEM;
689 	digits = 0;
690 	for (num = dev->number; num; num /= 10) digits++;
691 	if (!digits) digits++;
692 
693 	dev->proc_name = kmalloc(strlen(dev->type) + digits + 2, GFP_ATOMIC);
694 	if (!dev->proc_name)
695 		goto fail1;
696 	sprintf(dev->proc_name,"%s:%d",dev->type, dev->number);
697 
698 	dev->proc_entry = create_proc_entry(dev->proc_name, 0, atm_proc_root);
699 	if (!dev->proc_entry)
700 		goto fail0;
701 	dev->proc_entry->data = dev;
702 	dev->proc_entry->proc_fops = &proc_dev_atm_operations;
703 	dev->proc_entry->owner = THIS_MODULE;
704 	return 0;
705 fail0:
706 	kfree(dev->proc_name);
707 fail1:
708 	return error;
709 }
710 
711 
atm_proc_dev_deregister(struct atm_dev * dev)712 void atm_proc_dev_deregister(struct atm_dev *dev)
713 {
714 	remove_proc_entry(dev->proc_name, atm_proc_root);
715 	kfree(dev->proc_name);
716 }
717 
718 
719 #define CREATE_ENTRY(name) \
720     name = create_proc_entry(#name,0,atm_proc_root); \
721     if (!name) goto cleanup; \
722     name->data = atm_##name##_info; \
723     name->proc_fops = &proc_spec_atm_operations; \
724     name->owner = THIS_MODULE
725 
726 static struct proc_dir_entry *devices = NULL, *pvc = NULL,
727 		*svc = NULL, *arp = NULL, *lec = NULL, *vc = NULL;
728 
atm_proc_cleanup(void)729 static void atm_proc_cleanup(void)
730 {
731 	if (devices)
732 		remove_proc_entry("devices",atm_proc_root);
733 	if (pvc)
734 		remove_proc_entry("pvc",atm_proc_root);
735 	if (svc)
736 		remove_proc_entry("svc",atm_proc_root);
737 	if (arp)
738 		remove_proc_entry("arp",atm_proc_root);
739 	if (lec)
740 		remove_proc_entry("lec",atm_proc_root);
741 	if (vc)
742 		remove_proc_entry("vc",atm_proc_root);
743 	remove_proc_entry("net/atm",NULL);
744 }
745 
atm_proc_init(void)746 int atm_proc_init(void)
747 {
748 	atm_proc_root = proc_mkdir("net/atm",NULL);
749 	if (!atm_proc_root)
750 		return -ENOMEM;
751 	CREATE_ENTRY(devices);
752 	CREATE_ENTRY(pvc);
753 	CREATE_ENTRY(svc);
754 	CREATE_ENTRY(vc);
755 #if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
756 	arp  = create_proc_entry("arp", S_IRUGO, atm_proc_root);
757 	if (!arp)
758 		goto cleanup;
759 	arp->proc_fops = &arp_seq_fops;
760 #endif
761 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
762 	CREATE_ENTRY(lec);
763 #endif
764 	return 0;
765 
766 cleanup:
767 	atm_proc_cleanup();
768 	return -ENOMEM;
769 }
770 
atm_proc_exit(void)771 void atm_proc_exit(void)
772 {
773 	atm_proc_cleanup();
774 }
775