18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* net/atm/proc.c - ATM /proc interface 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * seq_file api usage by romieu@fr.zoreil.com 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Evaluating the efficiency of the whole thing if left as an exercise to 98c2ecf20Sopenharmony_ci * the reader. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> /* for EXPORT_SYMBOL */ 138c2ecf20Sopenharmony_ci#include <linux/string.h> 148c2ecf20Sopenharmony_ci#include <linux/types.h> 158c2ecf20Sopenharmony_ci#include <linux/mm.h> 168c2ecf20Sopenharmony_ci#include <linux/fs.h> 178c2ecf20Sopenharmony_ci#include <linux/stat.h> 188c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 198c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 208c2ecf20Sopenharmony_ci#include <linux/errno.h> 218c2ecf20Sopenharmony_ci#include <linux/atm.h> 228c2ecf20Sopenharmony_ci#include <linux/atmdev.h> 238c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 248c2ecf20Sopenharmony_ci#include <linux/atmclip.h> 258c2ecf20Sopenharmony_ci#include <linux/init.h> /* for __init */ 268c2ecf20Sopenharmony_ci#include <linux/slab.h> 278c2ecf20Sopenharmony_ci#include <net/net_namespace.h> 288c2ecf20Sopenharmony_ci#include <net/atmclip.h> 298c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 308c2ecf20Sopenharmony_ci#include <linux/param.h> /* for HZ */ 318c2ecf20Sopenharmony_ci#include <linux/atomic.h> 328c2ecf20Sopenharmony_ci#include "resources.h" 338c2ecf20Sopenharmony_ci#include "common.h" /* atm_proc_init prototype */ 348c2ecf20Sopenharmony_ci#include "signaling.h" /* to get sigd - ugly too */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic ssize_t proc_dev_atm_read(struct file *file, char __user *buf, 378c2ecf20Sopenharmony_ci size_t count, loff_t *pos); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic const struct proc_ops atm_dev_proc_ops = { 408c2ecf20Sopenharmony_ci .proc_read = proc_dev_atm_read, 418c2ecf20Sopenharmony_ci .proc_lseek = noop_llseek, 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic void add_stats(struct seq_file *seq, const char *aal, 458c2ecf20Sopenharmony_ci const struct k_atm_aal_stats *stats) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci seq_printf(seq, "%s ( %d %d %d %d %d )", aal, 488c2ecf20Sopenharmony_ci atomic_read(&stats->tx), atomic_read(&stats->tx_err), 498c2ecf20Sopenharmony_ci atomic_read(&stats->rx), atomic_read(&stats->rx_err), 508c2ecf20Sopenharmony_ci atomic_read(&stats->rx_drop)); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic void atm_dev_info(struct seq_file *seq, const struct atm_dev *dev) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci int i; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci seq_printf(seq, "%3d %-8s", dev->number, dev->type); 588c2ecf20Sopenharmony_ci for (i = 0; i < ESI_LEN; i++) 598c2ecf20Sopenharmony_ci seq_printf(seq, "%02x", dev->esi[i]); 608c2ecf20Sopenharmony_ci seq_puts(seq, " "); 618c2ecf20Sopenharmony_ci add_stats(seq, "0", &dev->stats.aal0); 628c2ecf20Sopenharmony_ci seq_puts(seq, " "); 638c2ecf20Sopenharmony_ci add_stats(seq, "5", &dev->stats.aal5); 648c2ecf20Sopenharmony_ci seq_printf(seq, "\t[%d]", refcount_read(&dev->refcnt)); 658c2ecf20Sopenharmony_ci seq_putc(seq, '\n'); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistruct vcc_state { 698c2ecf20Sopenharmony_ci int bucket; 708c2ecf20Sopenharmony_ci struct sock *sk; 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic inline int compare_family(struct sock *sk, int family) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci return !family || (sk->sk_family == family); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic int __vcc_walk(struct sock **sock, int family, int *bucket, loff_t l) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct sock *sk = *sock; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (sk == SEQ_START_TOKEN) { 838c2ecf20Sopenharmony_ci for (*bucket = 0; *bucket < VCC_HTABLE_SIZE; ++*bucket) { 848c2ecf20Sopenharmony_ci struct hlist_head *head = &vcc_hash[*bucket]; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci sk = hlist_empty(head) ? NULL : __sk_head(head); 878c2ecf20Sopenharmony_ci if (sk) 888c2ecf20Sopenharmony_ci break; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci l--; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_citry_again: 938c2ecf20Sopenharmony_ci for (; sk; sk = sk_next(sk)) { 948c2ecf20Sopenharmony_ci l -= compare_family(sk, family); 958c2ecf20Sopenharmony_ci if (l < 0) 968c2ecf20Sopenharmony_ci goto out; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci if (!sk && ++*bucket < VCC_HTABLE_SIZE) { 998c2ecf20Sopenharmony_ci sk = sk_head(&vcc_hash[*bucket]); 1008c2ecf20Sopenharmony_ci goto try_again; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci sk = SEQ_START_TOKEN; 1038c2ecf20Sopenharmony_ciout: 1048c2ecf20Sopenharmony_ci *sock = sk; 1058c2ecf20Sopenharmony_ci return (l < 0); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic inline void *vcc_walk(struct seq_file *seq, loff_t l) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct vcc_state *state = seq->private; 1118c2ecf20Sopenharmony_ci int family = (uintptr_t)(PDE_DATA(file_inode(seq->file))); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return __vcc_walk(&state->sk, family, &state->bucket, l) ? 1148c2ecf20Sopenharmony_ci state : NULL; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic void *vcc_seq_start(struct seq_file *seq, loff_t *pos) 1188c2ecf20Sopenharmony_ci __acquires(vcc_sklist_lock) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct vcc_state *state = seq->private; 1218c2ecf20Sopenharmony_ci loff_t left = *pos; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci read_lock(&vcc_sklist_lock); 1248c2ecf20Sopenharmony_ci state->sk = SEQ_START_TOKEN; 1258c2ecf20Sopenharmony_ci return left ? vcc_walk(seq, left) : SEQ_START_TOKEN; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic void vcc_seq_stop(struct seq_file *seq, void *v) 1298c2ecf20Sopenharmony_ci __releases(vcc_sklist_lock) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci read_unlock(&vcc_sklist_lock); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic void *vcc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci v = vcc_walk(seq, 1); 1378c2ecf20Sopenharmony_ci (*pos)++; 1388c2ecf20Sopenharmony_ci return v; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic void pvc_info(struct seq_file *seq, struct atm_vcc *vcc) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci static const char *const class_name[] = { 1448c2ecf20Sopenharmony_ci "off", "UBR", "CBR", "VBR", "ABR"}; 1458c2ecf20Sopenharmony_ci static const char *const aal_name[] = { 1468c2ecf20Sopenharmony_ci "---", "1", "2", "3/4", /* 0- 3 */ 1478c2ecf20Sopenharmony_ci "???", "5", "???", "???", /* 4- 7 */ 1488c2ecf20Sopenharmony_ci "???", "???", "???", "???", /* 8-11 */ 1498c2ecf20Sopenharmony_ci "???", "0", "???", "???"}; /* 12-15 */ 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci seq_printf(seq, "%3d %3d %5d %-3s %7d %-5s %7d %-6s", 1528c2ecf20Sopenharmony_ci vcc->dev->number, vcc->vpi, vcc->vci, 1538c2ecf20Sopenharmony_ci vcc->qos.aal >= ARRAY_SIZE(aal_name) ? "err" : 1548c2ecf20Sopenharmony_ci aal_name[vcc->qos.aal], vcc->qos.rxtp.min_pcr, 1558c2ecf20Sopenharmony_ci class_name[vcc->qos.rxtp.traffic_class], 1568c2ecf20Sopenharmony_ci vcc->qos.txtp.min_pcr, 1578c2ecf20Sopenharmony_ci class_name[vcc->qos.txtp.traffic_class]); 1588c2ecf20Sopenharmony_ci if (test_bit(ATM_VF_IS_CLIP, &vcc->flags)) { 1598c2ecf20Sopenharmony_ci struct clip_vcc *clip_vcc = CLIP_VCC(vcc); 1608c2ecf20Sopenharmony_ci struct net_device *dev; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci dev = clip_vcc->entry ? clip_vcc->entry->neigh->dev : NULL; 1638c2ecf20Sopenharmony_ci seq_printf(seq, "CLIP, Itf:%s, Encap:", 1648c2ecf20Sopenharmony_ci dev ? dev->name : "none?"); 1658c2ecf20Sopenharmony_ci seq_printf(seq, "%s", clip_vcc->encap ? "LLC/SNAP" : "None"); 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci seq_putc(seq, '\n'); 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic const char *vcc_state(struct atm_vcc *vcc) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci static const char *const map[] = { ATM_VS2TXT_MAP }; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return map[ATM_VF2VS(vcc->flags)]; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic void vcc_info(struct seq_file *seq, struct atm_vcc *vcc) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct sock *sk = sk_atm(vcc); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci seq_printf(seq, "%pK ", vcc); 1828c2ecf20Sopenharmony_ci if (!vcc->dev) 1838c2ecf20Sopenharmony_ci seq_printf(seq, "Unassigned "); 1848c2ecf20Sopenharmony_ci else 1858c2ecf20Sopenharmony_ci seq_printf(seq, "%3d %3d %5d ", vcc->dev->number, vcc->vpi, 1868c2ecf20Sopenharmony_ci vcc->vci); 1878c2ecf20Sopenharmony_ci switch (sk->sk_family) { 1888c2ecf20Sopenharmony_ci case AF_ATMPVC: 1898c2ecf20Sopenharmony_ci seq_printf(seq, "PVC"); 1908c2ecf20Sopenharmony_ci break; 1918c2ecf20Sopenharmony_ci case AF_ATMSVC: 1928c2ecf20Sopenharmony_ci seq_printf(seq, "SVC"); 1938c2ecf20Sopenharmony_ci break; 1948c2ecf20Sopenharmony_ci default: 1958c2ecf20Sopenharmony_ci seq_printf(seq, "%3d", sk->sk_family); 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci seq_printf(seq, " %04lx %5d %7d/%7d %7d/%7d [%d]\n", 1988c2ecf20Sopenharmony_ci vcc->flags, sk->sk_err, 1998c2ecf20Sopenharmony_ci sk_wmem_alloc_get(sk), sk->sk_sndbuf, 2008c2ecf20Sopenharmony_ci sk_rmem_alloc_get(sk), sk->sk_rcvbuf, 2018c2ecf20Sopenharmony_ci refcount_read(&sk->sk_refcnt)); 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic void svc_info(struct seq_file *seq, struct atm_vcc *vcc) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci if (!vcc->dev) 2078c2ecf20Sopenharmony_ci seq_printf(seq, sizeof(void *) == 4 ? 2088c2ecf20Sopenharmony_ci "N/A@%pK%10s" : "N/A@%pK%2s", vcc, ""); 2098c2ecf20Sopenharmony_ci else 2108c2ecf20Sopenharmony_ci seq_printf(seq, "%3d %3d %5d ", 2118c2ecf20Sopenharmony_ci vcc->dev->number, vcc->vpi, vcc->vci); 2128c2ecf20Sopenharmony_ci seq_printf(seq, "%-10s ", vcc_state(vcc)); 2138c2ecf20Sopenharmony_ci seq_printf(seq, "%s%s", vcc->remote.sas_addr.pub, 2148c2ecf20Sopenharmony_ci *vcc->remote.sas_addr.pub && *vcc->remote.sas_addr.prv ? "+" : ""); 2158c2ecf20Sopenharmony_ci if (*vcc->remote.sas_addr.prv) { 2168c2ecf20Sopenharmony_ci int i; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci for (i = 0; i < ATM_ESA_LEN; i++) 2198c2ecf20Sopenharmony_ci seq_printf(seq, "%02x", vcc->remote.sas_addr.prv[i]); 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci seq_putc(seq, '\n'); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic int atm_dev_seq_show(struct seq_file *seq, void *v) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci static char atm_dev_banner[] = 2278c2ecf20Sopenharmony_ci "Itf Type ESI/\"MAC\"addr " 2288c2ecf20Sopenharmony_ci "AAL(TX,err,RX,err,drop) ... [refcnt]\n"; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (v == &atm_devs) 2318c2ecf20Sopenharmony_ci seq_puts(seq, atm_dev_banner); 2328c2ecf20Sopenharmony_ci else { 2338c2ecf20Sopenharmony_ci struct atm_dev *dev = list_entry(v, struct atm_dev, dev_list); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci atm_dev_info(seq, dev); 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci return 0; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic const struct seq_operations atm_dev_seq_ops = { 2418c2ecf20Sopenharmony_ci .start = atm_dev_seq_start, 2428c2ecf20Sopenharmony_ci .next = atm_dev_seq_next, 2438c2ecf20Sopenharmony_ci .stop = atm_dev_seq_stop, 2448c2ecf20Sopenharmony_ci .show = atm_dev_seq_show, 2458c2ecf20Sopenharmony_ci}; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic int pvc_seq_show(struct seq_file *seq, void *v) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci static char atm_pvc_banner[] = 2508c2ecf20Sopenharmony_ci "Itf VPI VCI AAL RX(PCR,Class) TX(PCR,Class)\n"; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (v == SEQ_START_TOKEN) 2538c2ecf20Sopenharmony_ci seq_puts(seq, atm_pvc_banner); 2548c2ecf20Sopenharmony_ci else { 2558c2ecf20Sopenharmony_ci struct vcc_state *state = seq->private; 2568c2ecf20Sopenharmony_ci struct atm_vcc *vcc = atm_sk(state->sk); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci pvc_info(seq, vcc); 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci return 0; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic const struct seq_operations pvc_seq_ops = { 2648c2ecf20Sopenharmony_ci .start = vcc_seq_start, 2658c2ecf20Sopenharmony_ci .next = vcc_seq_next, 2668c2ecf20Sopenharmony_ci .stop = vcc_seq_stop, 2678c2ecf20Sopenharmony_ci .show = pvc_seq_show, 2688c2ecf20Sopenharmony_ci}; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic int vcc_seq_show(struct seq_file *seq, void *v) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci if (v == SEQ_START_TOKEN) { 2738c2ecf20Sopenharmony_ci seq_printf(seq, sizeof(void *) == 4 ? "%-8s%s" : "%-16s%s", 2748c2ecf20Sopenharmony_ci "Address ", "Itf VPI VCI Fam Flags Reply " 2758c2ecf20Sopenharmony_ci "Send buffer Recv buffer [refcnt]\n"); 2768c2ecf20Sopenharmony_ci } else { 2778c2ecf20Sopenharmony_ci struct vcc_state *state = seq->private; 2788c2ecf20Sopenharmony_ci struct atm_vcc *vcc = atm_sk(state->sk); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci vcc_info(seq, vcc); 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci return 0; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic const struct seq_operations vcc_seq_ops = { 2868c2ecf20Sopenharmony_ci .start = vcc_seq_start, 2878c2ecf20Sopenharmony_ci .next = vcc_seq_next, 2888c2ecf20Sopenharmony_ci .stop = vcc_seq_stop, 2898c2ecf20Sopenharmony_ci .show = vcc_seq_show, 2908c2ecf20Sopenharmony_ci}; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic int svc_seq_show(struct seq_file *seq, void *v) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci static const char atm_svc_banner[] = 2958c2ecf20Sopenharmony_ci "Itf VPI VCI State Remote\n"; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (v == SEQ_START_TOKEN) 2988c2ecf20Sopenharmony_ci seq_puts(seq, atm_svc_banner); 2998c2ecf20Sopenharmony_ci else { 3008c2ecf20Sopenharmony_ci struct vcc_state *state = seq->private; 3018c2ecf20Sopenharmony_ci struct atm_vcc *vcc = atm_sk(state->sk); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci svc_info(seq, vcc); 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci return 0; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic const struct seq_operations svc_seq_ops = { 3098c2ecf20Sopenharmony_ci .start = vcc_seq_start, 3108c2ecf20Sopenharmony_ci .next = vcc_seq_next, 3118c2ecf20Sopenharmony_ci .stop = vcc_seq_stop, 3128c2ecf20Sopenharmony_ci .show = svc_seq_show, 3138c2ecf20Sopenharmony_ci}; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic ssize_t proc_dev_atm_read(struct file *file, char __user *buf, 3168c2ecf20Sopenharmony_ci size_t count, loff_t *pos) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct atm_dev *dev; 3198c2ecf20Sopenharmony_ci unsigned long page; 3208c2ecf20Sopenharmony_ci int length; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (count == 0) 3238c2ecf20Sopenharmony_ci return 0; 3248c2ecf20Sopenharmony_ci page = get_zeroed_page(GFP_KERNEL); 3258c2ecf20Sopenharmony_ci if (!page) 3268c2ecf20Sopenharmony_ci return -ENOMEM; 3278c2ecf20Sopenharmony_ci dev = PDE_DATA(file_inode(file)); 3288c2ecf20Sopenharmony_ci if (!dev->ops->proc_read) 3298c2ecf20Sopenharmony_ci length = -EINVAL; 3308c2ecf20Sopenharmony_ci else { 3318c2ecf20Sopenharmony_ci length = dev->ops->proc_read(dev, pos, (char *)page); 3328c2ecf20Sopenharmony_ci if (length > count) 3338c2ecf20Sopenharmony_ci length = -EINVAL; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci if (length >= 0) { 3368c2ecf20Sopenharmony_ci if (copy_to_user(buf, (char *)page, length)) 3378c2ecf20Sopenharmony_ci length = -EFAULT; 3388c2ecf20Sopenharmony_ci (*pos)++; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci free_page(page); 3418c2ecf20Sopenharmony_ci return length; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistruct proc_dir_entry *atm_proc_root; 3458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(atm_proc_root); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ciint atm_proc_dev_register(struct atm_dev *dev) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci int error; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* No proc info */ 3538c2ecf20Sopenharmony_ci if (!dev->ops->proc_read) 3548c2ecf20Sopenharmony_ci return 0; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci error = -ENOMEM; 3578c2ecf20Sopenharmony_ci dev->proc_name = kasprintf(GFP_KERNEL, "%s:%d", dev->type, dev->number); 3588c2ecf20Sopenharmony_ci if (!dev->proc_name) 3598c2ecf20Sopenharmony_ci goto err_out; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci dev->proc_entry = proc_create_data(dev->proc_name, 0, atm_proc_root, 3628c2ecf20Sopenharmony_ci &atm_dev_proc_ops, dev); 3638c2ecf20Sopenharmony_ci if (!dev->proc_entry) 3648c2ecf20Sopenharmony_ci goto err_free_name; 3658c2ecf20Sopenharmony_ci return 0; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cierr_free_name: 3688c2ecf20Sopenharmony_ci kfree(dev->proc_name); 3698c2ecf20Sopenharmony_cierr_out: 3708c2ecf20Sopenharmony_ci return error; 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_civoid atm_proc_dev_deregister(struct atm_dev *dev) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci if (!dev->ops->proc_read) 3768c2ecf20Sopenharmony_ci return; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci remove_proc_entry(dev->proc_name, atm_proc_root); 3798c2ecf20Sopenharmony_ci kfree(dev->proc_name); 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ciint __init atm_proc_init(void) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci atm_proc_root = proc_net_mkdir(&init_net, "atm", init_net.proc_net); 3858c2ecf20Sopenharmony_ci if (!atm_proc_root) 3868c2ecf20Sopenharmony_ci return -ENOMEM; 3878c2ecf20Sopenharmony_ci proc_create_seq("devices", 0444, atm_proc_root, &atm_dev_seq_ops); 3888c2ecf20Sopenharmony_ci proc_create_seq_private("pvc", 0444, atm_proc_root, &pvc_seq_ops, 3898c2ecf20Sopenharmony_ci sizeof(struct vcc_state), (void *)(uintptr_t)PF_ATMPVC); 3908c2ecf20Sopenharmony_ci proc_create_seq_private("svc", 0444, atm_proc_root, &svc_seq_ops, 3918c2ecf20Sopenharmony_ci sizeof(struct vcc_state), (void *)(uintptr_t)PF_ATMSVC); 3928c2ecf20Sopenharmony_ci proc_create_seq_private("vc", 0444, atm_proc_root, &vcc_seq_ops, 3938c2ecf20Sopenharmony_ci sizeof(struct vcc_state), NULL); 3948c2ecf20Sopenharmony_ci return 0; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_civoid atm_proc_exit(void) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci remove_proc_subtree("atm", init_net.proc_net); 4008c2ecf20Sopenharmony_ci} 401