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