1 /* net/atm/proc.c - ATM /proc interface 2 * 3 * Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA 4 * 5 * seq_file api usage by romieu@fr.zoreil.com 6 * 7 * Evaluating the efficiency of the whole thing if left as an exercise to 8 * the reader. 9 */ 10 11 #include <linux/module.h> /* for EXPORT_SYMBOL */ 12 #include <linux/string.h> 13 #include <linux/types.h> 14 #include <linux/mm.h> 15 #include <linux/fs.h> 16 #include <linux/stat.h> 17 #include <linux/proc_fs.h> 18 #include <linux/seq_file.h> 19 #include <linux/errno.h> 20 #include <linux/atm.h> 21 #include <linux/atmdev.h> 22 #include <linux/netdevice.h> 23 #include <linux/atmclip.h> 24 #include <linux/init.h> /* for __init */ 25 #include <linux/slab.h> 26 #include <net/net_namespace.h> 27 #include <net/atmclip.h> 28 #include <linux/uaccess.h> 29 #include <linux/param.h> /* for HZ */ 30 #include <asm/atomic.h> 31 #include "resources.h" 32 #include "common.h" /* atm_proc_init prototype */ 33 #include "signaling.h" /* to get sigd - ugly too */ 34 35 static ssize_t proc_dev_atm_read(struct file *file, char __user *buf, 36 size_t count, loff_t *pos); 37 38 static const struct file_operations proc_atm_dev_ops = { 39 .owner = THIS_MODULE, 40 .read = proc_dev_atm_read, 41 }; 42 43 static void add_stats(struct seq_file *seq, const char *aal, 44 const struct k_atm_aal_stats *stats) 45 { 46 seq_printf(seq, "%s ( %d %d %d %d %d )", aal, 47 atomic_read(&stats->tx), atomic_read(&stats->tx_err), 48 atomic_read(&stats->rx), atomic_read(&stats->rx_err), 49 atomic_read(&stats->rx_drop)); 50 } 51 52 static void atm_dev_info(struct seq_file *seq, const struct atm_dev *dev) 53 { 54 int i; 55 56 seq_printf(seq, "%3d %-8s", dev->number, dev->type); 57 for (i = 0; i < ESI_LEN; i++) 58 seq_printf(seq, "%02x", dev->esi[i]); 59 seq_puts(seq, " "); 60 add_stats(seq, "0", &dev->stats.aal0); 61 seq_puts(seq, " "); 62 add_stats(seq, "5", &dev->stats.aal5); 63 seq_printf(seq, "\t[%d]", atomic_read(&dev->refcnt)); 64 seq_putc(seq, '\n'); 65 } 66 67 struct vcc_state { 68 int bucket; 69 struct sock *sk; 70 int family; 71 }; 72 73 static inline int compare_family(struct sock *sk, int family) 74 { 75 return !family || (sk->sk_family == family); 76 } 77 78 static int __vcc_walk(struct sock **sock, int family, int *bucket, loff_t l) 79 { 80 struct sock *sk = *sock; 81 82 if (sk == SEQ_START_TOKEN) { 83 for (*bucket = 0; *bucket < VCC_HTABLE_SIZE; ++*bucket) { 84 struct hlist_head *head = &vcc_hash[*bucket]; 85 86 sk = hlist_empty(head) ? NULL : __sk_head(head); 87 if (sk) 88 break; 89 } 90 l--; 91 } 92 try_again: 93 for (; sk; sk = sk_next(sk)) { 94 l -= compare_family(sk, family); 95 if (l < 0) 96 goto out; 97 } 98 if (!sk && ++*bucket < VCC_HTABLE_SIZE) { 99 sk = sk_head(&vcc_hash[*bucket]); 100 goto try_again; 101 } 102 sk = SEQ_START_TOKEN; 103 out: 104 *sock = sk; 105 return (l < 0); 106 } 107 108 static inline void *vcc_walk(struct vcc_state *state, loff_t l) 109 { 110 return __vcc_walk(&state->sk, state->family, &state->bucket, l) ? 111 state : NULL; 112 } 113 114 static int __vcc_seq_open(struct inode *inode, struct file *file, 115 int family, const struct seq_operations *ops) 116 { 117 struct vcc_state *state; 118 119 state = __seq_open_private(file, ops, sizeof(*state)); 120 if (state == NULL) 121 return -ENOMEM; 122 123 state->family = family; 124 return 0; 125 } 126 127 static void *vcc_seq_start(struct seq_file *seq, loff_t *pos) 128 __acquires(vcc_sklist_lock) 129 { 130 struct vcc_state *state = seq->private; 131 loff_t left = *pos; 132 133 read_lock(&vcc_sklist_lock); 134 state->sk = SEQ_START_TOKEN; 135 return left ? vcc_walk(state, left) : SEQ_START_TOKEN; 136 } 137 138 static void vcc_seq_stop(struct seq_file *seq, void *v) 139 __releases(vcc_sklist_lock) 140 { 141 read_unlock(&vcc_sklist_lock); 142 } 143 144 static void *vcc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 145 { 146 struct vcc_state *state = seq->private; 147 148 v = vcc_walk(state, 1); 149 *pos += !!PTR_ERR(v); 150 return v; 151 } 152 153 static void pvc_info(struct seq_file *seq, struct atm_vcc *vcc) 154 { 155 static const char *const class_name[] = { 156 "off", "UBR", "CBR", "VBR", "ABR"}; 157 static const char *const aal_name[] = { 158 "---", "1", "2", "3/4", /* 0- 3 */ 159 "???", "5", "???", "???", /* 4- 7 */ 160 "???", "???", "???", "???", /* 8-11 */ 161 "???", "0", "???", "???"}; /* 12-15 */ 162 163 seq_printf(seq, "%3d %3d %5d %-3s %7d %-5s %7d %-6s", 164 vcc->dev->number, vcc->vpi, vcc->vci, 165 vcc->qos.aal >= ARRAY_SIZE(aal_name) ? "err" : 166 aal_name[vcc->qos.aal], vcc->qos.rxtp.min_pcr, 167 class_name[vcc->qos.rxtp.traffic_class], 168 vcc->qos.txtp.min_pcr, 169 class_name[vcc->qos.txtp.traffic_class]); 170 if (test_bit(ATM_VF_IS_CLIP, &vcc->flags)) { 171 struct clip_vcc *clip_vcc = CLIP_VCC(vcc); 172 struct net_device *dev; 173 174 dev = clip_vcc->entry ? clip_vcc->entry->neigh->dev : NULL; 175 seq_printf(seq, "CLIP, Itf:%s, Encap:", 176 dev ? dev->name : "none?"); 177 seq_printf(seq, "%s", clip_vcc->encap ? "LLC/SNAP" : "None"); 178 } 179 seq_putc(seq, '\n'); 180 } 181 182 static const char *vcc_state(struct atm_vcc *vcc) 183 { 184 static const char *const map[] = { ATM_VS2TXT_MAP }; 185 186 return map[ATM_VF2VS(vcc->flags)]; 187 } 188 189 static void vcc_info(struct seq_file *seq, struct atm_vcc *vcc) 190 { 191 struct sock *sk = sk_atm(vcc); 192 193 seq_printf(seq, "%p ", vcc); 194 if (!vcc->dev) 195 seq_printf(seq, "Unassigned "); 196 else 197 seq_printf(seq, "%3d %3d %5d ", vcc->dev->number, vcc->vpi, 198 vcc->vci); 199 switch (sk->sk_family) { 200 case AF_ATMPVC: 201 seq_printf(seq, "PVC"); 202 break; 203 case AF_ATMSVC: 204 seq_printf(seq, "SVC"); 205 break; 206 default: 207 seq_printf(seq, "%3d", sk->sk_family); 208 } 209 seq_printf(seq, " %04lx %5d %7d/%7d %7d/%7d [%d]\n", 210 vcc->flags, sk->sk_err, 211 sk_wmem_alloc_get(sk), sk->sk_sndbuf, 212 sk_rmem_alloc_get(sk), sk->sk_rcvbuf, 213 atomic_read(&sk->sk_refcnt)); 214 } 215 216 static void svc_info(struct seq_file *seq, struct atm_vcc *vcc) 217 { 218 if (!vcc->dev) 219 seq_printf(seq, sizeof(void *) == 4 ? 220 "N/A@%p%10s" : "N/A@%p%2s", vcc, ""); 221 else 222 seq_printf(seq, "%3d %3d %5d ", 223 vcc->dev->number, vcc->vpi, vcc->vci); 224 seq_printf(seq, "%-10s ", vcc_state(vcc)); 225 seq_printf(seq, "%s%s", vcc->remote.sas_addr.pub, 226 *vcc->remote.sas_addr.pub && *vcc->remote.sas_addr.prv ? "+" : ""); 227 if (*vcc->remote.sas_addr.prv) { 228 int i; 229 230 for (i = 0; i < ATM_ESA_LEN; i++) 231 seq_printf(seq, "%02x", vcc->remote.sas_addr.prv[i]); 232 } 233 seq_putc(seq, '\n'); 234 } 235 236 static int atm_dev_seq_show(struct seq_file *seq, void *v) 237 { 238 static char atm_dev_banner[] = 239 "Itf Type ESI/\"MAC\"addr " 240 "AAL(TX,err,RX,err,drop) ... [refcnt]\n"; 241 242 if (v == &atm_devs) 243 seq_puts(seq, atm_dev_banner); 244 else { 245 struct atm_dev *dev = list_entry(v, struct atm_dev, dev_list); 246 247 atm_dev_info(seq, dev); 248 } 249 return 0; 250 } 251 252 static const struct seq_operations atm_dev_seq_ops = { 253 .start = atm_dev_seq_start, 254 .next = atm_dev_seq_next, 255 .stop = atm_dev_seq_stop, 256 .show = atm_dev_seq_show, 257 }; 258 259 static int atm_dev_seq_open(struct inode *inode, struct file *file) 260 { 261 return seq_open(file, &atm_dev_seq_ops); 262 } 263 264 static const struct file_operations devices_seq_fops = { 265 .open = atm_dev_seq_open, 266 .read = seq_read, 267 .llseek = seq_lseek, 268 .release = seq_release, 269 }; 270 271 static int pvc_seq_show(struct seq_file *seq, void *v) 272 { 273 static char atm_pvc_banner[] = 274 "Itf VPI VCI AAL RX(PCR,Class) TX(PCR,Class)\n"; 275 276 if (v == SEQ_START_TOKEN) 277 seq_puts(seq, atm_pvc_banner); 278 else { 279 struct vcc_state *state = seq->private; 280 struct atm_vcc *vcc = atm_sk(state->sk); 281 282 pvc_info(seq, vcc); 283 } 284 return 0; 285 } 286 287 static const struct seq_operations pvc_seq_ops = { 288 .start = vcc_seq_start, 289 .next = vcc_seq_next, 290 .stop = vcc_seq_stop, 291 .show = pvc_seq_show, 292 }; 293 294 static int pvc_seq_open(struct inode *inode, struct file *file) 295 { 296 return __vcc_seq_open(inode, file, PF_ATMPVC, &pvc_seq_ops); 297 } 298 299 static const struct file_operations pvc_seq_fops = { 300 .open = pvc_seq_open, 301 .read = seq_read, 302 .llseek = seq_lseek, 303 .release = seq_release_private, 304 }; 305 306 static int vcc_seq_show(struct seq_file *seq, void *v) 307 { 308 if (v == SEQ_START_TOKEN) { 309 seq_printf(seq, sizeof(void *) == 4 ? "%-8s%s" : "%-16s%s", 310 "Address ", "Itf VPI VCI Fam Flags Reply " 311 "Send buffer Recv buffer [refcnt]\n"); 312 } else { 313 struct vcc_state *state = seq->private; 314 struct atm_vcc *vcc = atm_sk(state->sk); 315 316 vcc_info(seq, vcc); 317 } 318 return 0; 319 } 320 321 static const struct seq_operations vcc_seq_ops = { 322 .start = vcc_seq_start, 323 .next = vcc_seq_next, 324 .stop = vcc_seq_stop, 325 .show = vcc_seq_show, 326 }; 327 328 static int vcc_seq_open(struct inode *inode, struct file *file) 329 { 330 return __vcc_seq_open(inode, file, 0, &vcc_seq_ops); 331 } 332 333 static const struct file_operations vcc_seq_fops = { 334 .open = vcc_seq_open, 335 .read = seq_read, 336 .llseek = seq_lseek, 337 .release = seq_release_private, 338 }; 339 340 static int svc_seq_show(struct seq_file *seq, void *v) 341 { 342 static const char atm_svc_banner[] = 343 "Itf VPI VCI State Remote\n"; 344 345 if (v == SEQ_START_TOKEN) 346 seq_puts(seq, atm_svc_banner); 347 else { 348 struct vcc_state *state = seq->private; 349 struct atm_vcc *vcc = atm_sk(state->sk); 350 351 svc_info(seq, vcc); 352 } 353 return 0; 354 } 355 356 static const struct seq_operations svc_seq_ops = { 357 .start = vcc_seq_start, 358 .next = vcc_seq_next, 359 .stop = vcc_seq_stop, 360 .show = svc_seq_show, 361 }; 362 363 static int svc_seq_open(struct inode *inode, struct file *file) 364 { 365 return __vcc_seq_open(inode, file, PF_ATMSVC, &svc_seq_ops); 366 } 367 368 static const struct file_operations svc_seq_fops = { 369 .open = svc_seq_open, 370 .read = seq_read, 371 .llseek = seq_lseek, 372 .release = seq_release_private, 373 }; 374 375 static ssize_t proc_dev_atm_read(struct file *file, char __user *buf, 376 size_t count, loff_t *pos) 377 { 378 struct atm_dev *dev; 379 unsigned long page; 380 int length; 381 382 if (count == 0) 383 return 0; 384 page = get_zeroed_page(GFP_KERNEL); 385 if (!page) 386 return -ENOMEM; 387 dev = PDE(file->f_path.dentry->d_inode)->data; 388 if (!dev->ops->proc_read) 389 length = -EINVAL; 390 else { 391 length = dev->ops->proc_read(dev, pos, (char *)page); 392 if (length > count) 393 length = -EINVAL; 394 } 395 if (length >= 0) { 396 if (copy_to_user(buf, (char *)page, length)) 397 length = -EFAULT; 398 (*pos)++; 399 } 400 free_page(page); 401 return length; 402 } 403 404 struct proc_dir_entry *atm_proc_root; 405 EXPORT_SYMBOL(atm_proc_root); 406 407 408 int atm_proc_dev_register(struct atm_dev *dev) 409 { 410 int error; 411 412 /* No proc info */ 413 if (!dev->ops->proc_read) 414 return 0; 415 416 error = -ENOMEM; 417 dev->proc_name = kasprintf(GFP_KERNEL, "%s:%d", dev->type, dev->number); 418 if (!dev->proc_name) 419 goto err_out; 420 421 dev->proc_entry = proc_create_data(dev->proc_name, 0, atm_proc_root, 422 &proc_atm_dev_ops, dev); 423 if (!dev->proc_entry) 424 goto err_free_name; 425 return 0; 426 427 err_free_name: 428 kfree(dev->proc_name); 429 err_out: 430 return error; 431 } 432 433 void atm_proc_dev_deregister(struct atm_dev *dev) 434 { 435 if (!dev->ops->proc_read) 436 return; 437 438 remove_proc_entry(dev->proc_name, atm_proc_root); 439 kfree(dev->proc_name); 440 } 441 442 static struct atm_proc_entry { 443 char *name; 444 const struct file_operations *proc_fops; 445 struct proc_dir_entry *dirent; 446 } atm_proc_ents[] = { 447 { .name = "devices", .proc_fops = &devices_seq_fops }, 448 { .name = "pvc", .proc_fops = &pvc_seq_fops }, 449 { .name = "svc", .proc_fops = &svc_seq_fops }, 450 { .name = "vc", .proc_fops = &vcc_seq_fops }, 451 { .name = NULL, .proc_fops = NULL } 452 }; 453 454 static void atm_proc_dirs_remove(void) 455 { 456 static struct atm_proc_entry *e; 457 458 for (e = atm_proc_ents; e->name; e++) { 459 if (e->dirent) 460 remove_proc_entry(e->name, atm_proc_root); 461 } 462 proc_net_remove(&init_net, "atm"); 463 } 464 465 int __init atm_proc_init(void) 466 { 467 static struct atm_proc_entry *e; 468 int ret; 469 470 atm_proc_root = proc_net_mkdir(&init_net, "atm", init_net.proc_net); 471 if (!atm_proc_root) 472 goto err_out; 473 for (e = atm_proc_ents; e->name; e++) { 474 struct proc_dir_entry *dirent; 475 476 dirent = proc_create(e->name, S_IRUGO, 477 atm_proc_root, e->proc_fops); 478 if (!dirent) 479 goto err_out_remove; 480 e->dirent = dirent; 481 } 482 ret = 0; 483 out: 484 return ret; 485 486 err_out_remove: 487 atm_proc_dirs_remove(); 488 err_out: 489 ret = -ENOMEM; 490 goto out; 491 } 492 493 void atm_proc_exit(void) 494 { 495 atm_proc_dirs_remove(); 496 } 497