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 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 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,"",&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 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 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 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 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 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 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 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 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 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 "???", "", "???", "???"}; /* 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 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 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 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* 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 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 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 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 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 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) 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 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 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 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 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 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 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 771 void atm_proc_exit(void) 772 { 773 atm_proc_cleanup(); 774 } 775
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.