162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* net/atm/proc.c - ATM /proc interface 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * seq_file api usage by romieu@fr.zoreil.com 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Evaluating the efficiency of the whole thing if left as an exercise to 962306a36Sopenharmony_ci * the reader. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/module.h> /* for EXPORT_SYMBOL */ 1362306a36Sopenharmony_ci#include <linux/string.h> 1462306a36Sopenharmony_ci#include <linux/types.h> 1562306a36Sopenharmony_ci#include <linux/mm.h> 1662306a36Sopenharmony_ci#include <linux/fs.h> 1762306a36Sopenharmony_ci#include <linux/stat.h> 1862306a36Sopenharmony_ci#include <linux/proc_fs.h> 1962306a36Sopenharmony_ci#include <linux/seq_file.h> 2062306a36Sopenharmony_ci#include <linux/errno.h> 2162306a36Sopenharmony_ci#include <linux/atm.h> 2262306a36Sopenharmony_ci#include <linux/atmdev.h> 2362306a36Sopenharmony_ci#include <linux/netdevice.h> 2462306a36Sopenharmony_ci#include <linux/atmclip.h> 2562306a36Sopenharmony_ci#include <linux/init.h> /* for __init */ 2662306a36Sopenharmony_ci#include <linux/slab.h> 2762306a36Sopenharmony_ci#include <net/net_namespace.h> 2862306a36Sopenharmony_ci#include <net/atmclip.h> 2962306a36Sopenharmony_ci#include <linux/uaccess.h> 3062306a36Sopenharmony_ci#include <linux/param.h> /* for HZ */ 3162306a36Sopenharmony_ci#include <linux/atomic.h> 3262306a36Sopenharmony_ci#include "resources.h" 3362306a36Sopenharmony_ci#include "common.h" /* atm_proc_init prototype */ 3462306a36Sopenharmony_ci#include "signaling.h" /* to get sigd - ugly too */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic ssize_t proc_dev_atm_read(struct file *file, char __user *buf, 3762306a36Sopenharmony_ci size_t count, loff_t *pos); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic const struct proc_ops atm_dev_proc_ops = { 4062306a36Sopenharmony_ci .proc_read = proc_dev_atm_read, 4162306a36Sopenharmony_ci .proc_lseek = noop_llseek, 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic void add_stats(struct seq_file *seq, const char *aal, 4562306a36Sopenharmony_ci const struct k_atm_aal_stats *stats) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci seq_printf(seq, "%s ( %d %d %d %d %d )", aal, 4862306a36Sopenharmony_ci atomic_read(&stats->tx), atomic_read(&stats->tx_err), 4962306a36Sopenharmony_ci atomic_read(&stats->rx), atomic_read(&stats->rx_err), 5062306a36Sopenharmony_ci atomic_read(&stats->rx_drop)); 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic void atm_dev_info(struct seq_file *seq, const struct atm_dev *dev) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci int i; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci seq_printf(seq, "%3d %-8s", dev->number, dev->type); 5862306a36Sopenharmony_ci for (i = 0; i < ESI_LEN; i++) 5962306a36Sopenharmony_ci seq_printf(seq, "%02x", dev->esi[i]); 6062306a36Sopenharmony_ci seq_puts(seq, " "); 6162306a36Sopenharmony_ci add_stats(seq, "0", &dev->stats.aal0); 6262306a36Sopenharmony_ci seq_puts(seq, " "); 6362306a36Sopenharmony_ci add_stats(seq, "5", &dev->stats.aal5); 6462306a36Sopenharmony_ci seq_printf(seq, "\t[%d]", refcount_read(&dev->refcnt)); 6562306a36Sopenharmony_ci seq_putc(seq, '\n'); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistruct vcc_state { 6962306a36Sopenharmony_ci int bucket; 7062306a36Sopenharmony_ci struct sock *sk; 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic inline int compare_family(struct sock *sk, int family) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci return !family || (sk->sk_family == family); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic int __vcc_walk(struct sock **sock, int family, int *bucket, loff_t l) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct sock *sk = *sock; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (sk == SEQ_START_TOKEN) { 8362306a36Sopenharmony_ci for (*bucket = 0; *bucket < VCC_HTABLE_SIZE; ++*bucket) { 8462306a36Sopenharmony_ci struct hlist_head *head = &vcc_hash[*bucket]; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci sk = hlist_empty(head) ? NULL : __sk_head(head); 8762306a36Sopenharmony_ci if (sk) 8862306a36Sopenharmony_ci break; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci l--; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_citry_again: 9362306a36Sopenharmony_ci for (; sk; sk = sk_next(sk)) { 9462306a36Sopenharmony_ci l -= compare_family(sk, family); 9562306a36Sopenharmony_ci if (l < 0) 9662306a36Sopenharmony_ci goto out; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci if (!sk && ++*bucket < VCC_HTABLE_SIZE) { 9962306a36Sopenharmony_ci sk = sk_head(&vcc_hash[*bucket]); 10062306a36Sopenharmony_ci goto try_again; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci sk = SEQ_START_TOKEN; 10362306a36Sopenharmony_ciout: 10462306a36Sopenharmony_ci *sock = sk; 10562306a36Sopenharmony_ci return (l < 0); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic inline void *vcc_walk(struct seq_file *seq, loff_t l) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct vcc_state *state = seq->private; 11162306a36Sopenharmony_ci int family = (uintptr_t)(pde_data(file_inode(seq->file))); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return __vcc_walk(&state->sk, family, &state->bucket, l) ? 11462306a36Sopenharmony_ci state : NULL; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void *vcc_seq_start(struct seq_file *seq, loff_t *pos) 11862306a36Sopenharmony_ci __acquires(vcc_sklist_lock) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct vcc_state *state = seq->private; 12162306a36Sopenharmony_ci loff_t left = *pos; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci read_lock(&vcc_sklist_lock); 12462306a36Sopenharmony_ci state->sk = SEQ_START_TOKEN; 12562306a36Sopenharmony_ci return left ? vcc_walk(seq, left) : SEQ_START_TOKEN; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic void vcc_seq_stop(struct seq_file *seq, void *v) 12962306a36Sopenharmony_ci __releases(vcc_sklist_lock) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci read_unlock(&vcc_sklist_lock); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic void *vcc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci v = vcc_walk(seq, 1); 13762306a36Sopenharmony_ci (*pos)++; 13862306a36Sopenharmony_ci return v; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic void pvc_info(struct seq_file *seq, struct atm_vcc *vcc) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci static const char *const class_name[] = { 14462306a36Sopenharmony_ci "off", "UBR", "CBR", "VBR", "ABR"}; 14562306a36Sopenharmony_ci static const char *const aal_name[] = { 14662306a36Sopenharmony_ci "---", "1", "2", "3/4", /* 0- 3 */ 14762306a36Sopenharmony_ci "???", "5", "???", "???", /* 4- 7 */ 14862306a36Sopenharmony_ci "???", "???", "???", "???", /* 8-11 */ 14962306a36Sopenharmony_ci "???", "0", "???", "???"}; /* 12-15 */ 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci seq_printf(seq, "%3d %3d %5d %-3s %7d %-5s %7d %-6s", 15262306a36Sopenharmony_ci vcc->dev->number, vcc->vpi, vcc->vci, 15362306a36Sopenharmony_ci vcc->qos.aal >= ARRAY_SIZE(aal_name) ? "err" : 15462306a36Sopenharmony_ci aal_name[vcc->qos.aal], vcc->qos.rxtp.min_pcr, 15562306a36Sopenharmony_ci class_name[vcc->qos.rxtp.traffic_class], 15662306a36Sopenharmony_ci vcc->qos.txtp.min_pcr, 15762306a36Sopenharmony_ci class_name[vcc->qos.txtp.traffic_class]); 15862306a36Sopenharmony_ci if (test_bit(ATM_VF_IS_CLIP, &vcc->flags)) { 15962306a36Sopenharmony_ci struct clip_vcc *clip_vcc = CLIP_VCC(vcc); 16062306a36Sopenharmony_ci struct net_device *dev; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci dev = clip_vcc->entry ? clip_vcc->entry->neigh->dev : NULL; 16362306a36Sopenharmony_ci seq_printf(seq, "CLIP, Itf:%s, Encap:", 16462306a36Sopenharmony_ci dev ? dev->name : "none?"); 16562306a36Sopenharmony_ci seq_printf(seq, "%s", clip_vcc->encap ? "LLC/SNAP" : "None"); 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci seq_putc(seq, '\n'); 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic const char *vcc_state(struct atm_vcc *vcc) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci static const char *const map[] = { ATM_VS2TXT_MAP }; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return map[ATM_VF2VS(vcc->flags)]; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic void vcc_info(struct seq_file *seq, struct atm_vcc *vcc) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci struct sock *sk = sk_atm(vcc); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci seq_printf(seq, "%pK ", vcc); 18262306a36Sopenharmony_ci if (!vcc->dev) 18362306a36Sopenharmony_ci seq_printf(seq, "Unassigned "); 18462306a36Sopenharmony_ci else 18562306a36Sopenharmony_ci seq_printf(seq, "%3d %3d %5d ", vcc->dev->number, vcc->vpi, 18662306a36Sopenharmony_ci vcc->vci); 18762306a36Sopenharmony_ci switch (sk->sk_family) { 18862306a36Sopenharmony_ci case AF_ATMPVC: 18962306a36Sopenharmony_ci seq_printf(seq, "PVC"); 19062306a36Sopenharmony_ci break; 19162306a36Sopenharmony_ci case AF_ATMSVC: 19262306a36Sopenharmony_ci seq_printf(seq, "SVC"); 19362306a36Sopenharmony_ci break; 19462306a36Sopenharmony_ci default: 19562306a36Sopenharmony_ci seq_printf(seq, "%3d", sk->sk_family); 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci seq_printf(seq, " %04lx %5d %7d/%7d %7d/%7d [%d]\n", 19862306a36Sopenharmony_ci vcc->flags, sk->sk_err, 19962306a36Sopenharmony_ci sk_wmem_alloc_get(sk), sk->sk_sndbuf, 20062306a36Sopenharmony_ci sk_rmem_alloc_get(sk), sk->sk_rcvbuf, 20162306a36Sopenharmony_ci refcount_read(&sk->sk_refcnt)); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic void svc_info(struct seq_file *seq, struct atm_vcc *vcc) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci if (!vcc->dev) 20762306a36Sopenharmony_ci seq_printf(seq, sizeof(void *) == 4 ? 20862306a36Sopenharmony_ci "N/A@%pK%10s" : "N/A@%pK%2s", vcc, ""); 20962306a36Sopenharmony_ci else 21062306a36Sopenharmony_ci seq_printf(seq, "%3d %3d %5d ", 21162306a36Sopenharmony_ci vcc->dev->number, vcc->vpi, vcc->vci); 21262306a36Sopenharmony_ci seq_printf(seq, "%-10s ", vcc_state(vcc)); 21362306a36Sopenharmony_ci seq_printf(seq, "%s%s", vcc->remote.sas_addr.pub, 21462306a36Sopenharmony_ci *vcc->remote.sas_addr.pub && *vcc->remote.sas_addr.prv ? "+" : ""); 21562306a36Sopenharmony_ci if (*vcc->remote.sas_addr.prv) { 21662306a36Sopenharmony_ci int i; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci for (i = 0; i < ATM_ESA_LEN; i++) 21962306a36Sopenharmony_ci seq_printf(seq, "%02x", vcc->remote.sas_addr.prv[i]); 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci seq_putc(seq, '\n'); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic int atm_dev_seq_show(struct seq_file *seq, void *v) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci static char atm_dev_banner[] = 22762306a36Sopenharmony_ci "Itf Type ESI/\"MAC\"addr " 22862306a36Sopenharmony_ci "AAL(TX,err,RX,err,drop) ... [refcnt]\n"; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (v == &atm_devs) 23162306a36Sopenharmony_ci seq_puts(seq, atm_dev_banner); 23262306a36Sopenharmony_ci else { 23362306a36Sopenharmony_ci struct atm_dev *dev = list_entry(v, struct atm_dev, dev_list); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci atm_dev_info(seq, dev); 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci return 0; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic const struct seq_operations atm_dev_seq_ops = { 24162306a36Sopenharmony_ci .start = atm_dev_seq_start, 24262306a36Sopenharmony_ci .next = atm_dev_seq_next, 24362306a36Sopenharmony_ci .stop = atm_dev_seq_stop, 24462306a36Sopenharmony_ci .show = atm_dev_seq_show, 24562306a36Sopenharmony_ci}; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic int pvc_seq_show(struct seq_file *seq, void *v) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci static char atm_pvc_banner[] = 25062306a36Sopenharmony_ci "Itf VPI VCI AAL RX(PCR,Class) TX(PCR,Class)\n"; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (v == SEQ_START_TOKEN) 25362306a36Sopenharmony_ci seq_puts(seq, atm_pvc_banner); 25462306a36Sopenharmony_ci else { 25562306a36Sopenharmony_ci struct vcc_state *state = seq->private; 25662306a36Sopenharmony_ci struct atm_vcc *vcc = atm_sk(state->sk); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci pvc_info(seq, vcc); 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci return 0; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic const struct seq_operations pvc_seq_ops = { 26462306a36Sopenharmony_ci .start = vcc_seq_start, 26562306a36Sopenharmony_ci .next = vcc_seq_next, 26662306a36Sopenharmony_ci .stop = vcc_seq_stop, 26762306a36Sopenharmony_ci .show = pvc_seq_show, 26862306a36Sopenharmony_ci}; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic int vcc_seq_show(struct seq_file *seq, void *v) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci if (v == SEQ_START_TOKEN) { 27362306a36Sopenharmony_ci seq_printf(seq, sizeof(void *) == 4 ? "%-8s%s" : "%-16s%s", 27462306a36Sopenharmony_ci "Address ", "Itf VPI VCI Fam Flags Reply " 27562306a36Sopenharmony_ci "Send buffer Recv buffer [refcnt]\n"); 27662306a36Sopenharmony_ci } else { 27762306a36Sopenharmony_ci struct vcc_state *state = seq->private; 27862306a36Sopenharmony_ci struct atm_vcc *vcc = atm_sk(state->sk); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci vcc_info(seq, vcc); 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci return 0; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic const struct seq_operations vcc_seq_ops = { 28662306a36Sopenharmony_ci .start = vcc_seq_start, 28762306a36Sopenharmony_ci .next = vcc_seq_next, 28862306a36Sopenharmony_ci .stop = vcc_seq_stop, 28962306a36Sopenharmony_ci .show = vcc_seq_show, 29062306a36Sopenharmony_ci}; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic int svc_seq_show(struct seq_file *seq, void *v) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci static const char atm_svc_banner[] = 29562306a36Sopenharmony_ci "Itf VPI VCI State Remote\n"; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (v == SEQ_START_TOKEN) 29862306a36Sopenharmony_ci seq_puts(seq, atm_svc_banner); 29962306a36Sopenharmony_ci else { 30062306a36Sopenharmony_ci struct vcc_state *state = seq->private; 30162306a36Sopenharmony_ci struct atm_vcc *vcc = atm_sk(state->sk); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci svc_info(seq, vcc); 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci return 0; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic const struct seq_operations svc_seq_ops = { 30962306a36Sopenharmony_ci .start = vcc_seq_start, 31062306a36Sopenharmony_ci .next = vcc_seq_next, 31162306a36Sopenharmony_ci .stop = vcc_seq_stop, 31262306a36Sopenharmony_ci .show = svc_seq_show, 31362306a36Sopenharmony_ci}; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic ssize_t proc_dev_atm_read(struct file *file, char __user *buf, 31662306a36Sopenharmony_ci size_t count, loff_t *pos) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci struct atm_dev *dev; 31962306a36Sopenharmony_ci unsigned long page; 32062306a36Sopenharmony_ci int length; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (count == 0) 32362306a36Sopenharmony_ci return 0; 32462306a36Sopenharmony_ci page = get_zeroed_page(GFP_KERNEL); 32562306a36Sopenharmony_ci if (!page) 32662306a36Sopenharmony_ci return -ENOMEM; 32762306a36Sopenharmony_ci dev = pde_data(file_inode(file)); 32862306a36Sopenharmony_ci if (!dev->ops->proc_read) 32962306a36Sopenharmony_ci length = -EINVAL; 33062306a36Sopenharmony_ci else { 33162306a36Sopenharmony_ci length = dev->ops->proc_read(dev, pos, (char *)page); 33262306a36Sopenharmony_ci if (length > count) 33362306a36Sopenharmony_ci length = -EINVAL; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci if (length >= 0) { 33662306a36Sopenharmony_ci if (copy_to_user(buf, (char *)page, length)) 33762306a36Sopenharmony_ci length = -EFAULT; 33862306a36Sopenharmony_ci (*pos)++; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci free_page(page); 34162306a36Sopenharmony_ci return length; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistruct proc_dir_entry *atm_proc_root; 34562306a36Sopenharmony_ciEXPORT_SYMBOL(atm_proc_root); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ciint atm_proc_dev_register(struct atm_dev *dev) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci int error; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci /* No proc info */ 35362306a36Sopenharmony_ci if (!dev->ops->proc_read) 35462306a36Sopenharmony_ci return 0; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci error = -ENOMEM; 35762306a36Sopenharmony_ci dev->proc_name = kasprintf(GFP_KERNEL, "%s:%d", dev->type, dev->number); 35862306a36Sopenharmony_ci if (!dev->proc_name) 35962306a36Sopenharmony_ci goto err_out; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci dev->proc_entry = proc_create_data(dev->proc_name, 0, atm_proc_root, 36262306a36Sopenharmony_ci &atm_dev_proc_ops, dev); 36362306a36Sopenharmony_ci if (!dev->proc_entry) 36462306a36Sopenharmony_ci goto err_free_name; 36562306a36Sopenharmony_ci return 0; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cierr_free_name: 36862306a36Sopenharmony_ci kfree(dev->proc_name); 36962306a36Sopenharmony_cierr_out: 37062306a36Sopenharmony_ci return error; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_civoid atm_proc_dev_deregister(struct atm_dev *dev) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci if (!dev->ops->proc_read) 37662306a36Sopenharmony_ci return; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci remove_proc_entry(dev->proc_name, atm_proc_root); 37962306a36Sopenharmony_ci kfree(dev->proc_name); 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ciint __init atm_proc_init(void) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci atm_proc_root = proc_net_mkdir(&init_net, "atm", init_net.proc_net); 38562306a36Sopenharmony_ci if (!atm_proc_root) 38662306a36Sopenharmony_ci return -ENOMEM; 38762306a36Sopenharmony_ci proc_create_seq("devices", 0444, atm_proc_root, &atm_dev_seq_ops); 38862306a36Sopenharmony_ci proc_create_seq_private("pvc", 0444, atm_proc_root, &pvc_seq_ops, 38962306a36Sopenharmony_ci sizeof(struct vcc_state), (void *)(uintptr_t)PF_ATMPVC); 39062306a36Sopenharmony_ci proc_create_seq_private("svc", 0444, atm_proc_root, &svc_seq_ops, 39162306a36Sopenharmony_ci sizeof(struct vcc_state), (void *)(uintptr_t)PF_ATMSVC); 39262306a36Sopenharmony_ci proc_create_seq_private("vc", 0444, atm_proc_root, &vcc_seq_ops, 39362306a36Sopenharmony_ci sizeof(struct vcc_state), NULL); 39462306a36Sopenharmony_ci return 0; 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_civoid atm_proc_exit(void) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci remove_proc_subtree("atm", init_net.proc_net); 40062306a36Sopenharmony_ci} 401