18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* net/atm/common.c - ATM sockets (common part for PVC and SVC) */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/kmod.h> 108c2ecf20Sopenharmony_ci#include <linux/net.h> /* struct socket, struct proto_ops */ 118c2ecf20Sopenharmony_ci#include <linux/atm.h> /* ATM stuff */ 128c2ecf20Sopenharmony_ci#include <linux/atmdev.h> 138c2ecf20Sopenharmony_ci#include <linux/socket.h> /* SOL_SOCKET */ 148c2ecf20Sopenharmony_ci#include <linux/errno.h> /* error codes */ 158c2ecf20Sopenharmony_ci#include <linux/capability.h> 168c2ecf20Sopenharmony_ci#include <linux/mm.h> 178c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 188c2ecf20Sopenharmony_ci#include <linux/time64.h> /* 64-bit time for seconds */ 198c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 208c2ecf20Sopenharmony_ci#include <linux/bitops.h> 218c2ecf20Sopenharmony_ci#include <linux/init.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <net/sock.h> /* struct sock */ 248c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 258c2ecf20Sopenharmony_ci#include <linux/poll.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <linux/atomic.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "resources.h" /* atm_find_dev */ 308c2ecf20Sopenharmony_ci#include "common.h" /* prototypes */ 318c2ecf20Sopenharmony_ci#include "protocols.h" /* atm_init_<transport> */ 328c2ecf20Sopenharmony_ci#include "addr.h" /* address registry */ 338c2ecf20Sopenharmony_ci#include "signaling.h" /* for WAITING and sigd_attach */ 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistruct hlist_head vcc_hash[VCC_HTABLE_SIZE]; 368c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vcc_hash); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ciDEFINE_RWLOCK(vcc_sklist_lock); 398c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vcc_sklist_lock); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic ATOMIC_NOTIFIER_HEAD(atm_dev_notify_chain); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic void __vcc_insert_socket(struct sock *sk) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct atm_vcc *vcc = atm_sk(sk); 468c2ecf20Sopenharmony_ci struct hlist_head *head = &vcc_hash[vcc->vci & (VCC_HTABLE_SIZE - 1)]; 478c2ecf20Sopenharmony_ci sk->sk_hash = vcc->vci & (VCC_HTABLE_SIZE - 1); 488c2ecf20Sopenharmony_ci sk_add_node(sk, head); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_civoid vcc_insert_socket(struct sock *sk) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci write_lock_irq(&vcc_sklist_lock); 548c2ecf20Sopenharmony_ci __vcc_insert_socket(sk); 558c2ecf20Sopenharmony_ci write_unlock_irq(&vcc_sklist_lock); 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vcc_insert_socket); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic void vcc_remove_socket(struct sock *sk) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci write_lock_irq(&vcc_sklist_lock); 628c2ecf20Sopenharmony_ci sk_del_node_init(sk); 638c2ecf20Sopenharmony_ci write_unlock_irq(&vcc_sklist_lock); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic bool vcc_tx_ready(struct atm_vcc *vcc, unsigned int size) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct sock *sk = sk_atm(vcc); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (sk_wmem_alloc_get(sk) && !atm_may_send(vcc, size)) { 718c2ecf20Sopenharmony_ci pr_debug("Sorry: wmem_alloc = %d, size = %d, sndbuf = %d\n", 728c2ecf20Sopenharmony_ci sk_wmem_alloc_get(sk), size, sk->sk_sndbuf); 738c2ecf20Sopenharmony_ci return false; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci return true; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic void vcc_sock_destruct(struct sock *sk) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci if (atomic_read(&sk->sk_rmem_alloc)) 818c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: rmem leakage (%d bytes) detected.\n", 828c2ecf20Sopenharmony_ci __func__, atomic_read(&sk->sk_rmem_alloc)); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (refcount_read(&sk->sk_wmem_alloc)) 858c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: wmem leakage (%d bytes) detected.\n", 868c2ecf20Sopenharmony_ci __func__, refcount_read(&sk->sk_wmem_alloc)); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic void vcc_def_wakeup(struct sock *sk) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct socket_wq *wq; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci rcu_read_lock(); 948c2ecf20Sopenharmony_ci wq = rcu_dereference(sk->sk_wq); 958c2ecf20Sopenharmony_ci if (skwq_has_sleeper(wq)) 968c2ecf20Sopenharmony_ci wake_up(&wq->wait); 978c2ecf20Sopenharmony_ci rcu_read_unlock(); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic inline int vcc_writable(struct sock *sk) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct atm_vcc *vcc = atm_sk(sk); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return (vcc->qos.txtp.max_sdu + 1058c2ecf20Sopenharmony_ci refcount_read(&sk->sk_wmem_alloc)) <= sk->sk_sndbuf; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic void vcc_write_space(struct sock *sk) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct socket_wq *wq; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci rcu_read_lock(); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (vcc_writable(sk)) { 1158c2ecf20Sopenharmony_ci wq = rcu_dereference(sk->sk_wq); 1168c2ecf20Sopenharmony_ci if (skwq_has_sleeper(wq)) 1178c2ecf20Sopenharmony_ci wake_up_interruptible(&wq->wait); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT); 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci rcu_read_unlock(); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic void vcc_release_cb(struct sock *sk) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct atm_vcc *vcc = atm_sk(sk); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (vcc->release_cb) 1308c2ecf20Sopenharmony_ci vcc->release_cb(vcc); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic struct proto vcc_proto = { 1348c2ecf20Sopenharmony_ci .name = "VCC", 1358c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1368c2ecf20Sopenharmony_ci .obj_size = sizeof(struct atm_vcc), 1378c2ecf20Sopenharmony_ci .release_cb = vcc_release_cb, 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ciint vcc_create(struct net *net, struct socket *sock, int protocol, int family, int kern) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct sock *sk; 1438c2ecf20Sopenharmony_ci struct atm_vcc *vcc; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci sock->sk = NULL; 1468c2ecf20Sopenharmony_ci if (sock->type == SOCK_STREAM) 1478c2ecf20Sopenharmony_ci return -EINVAL; 1488c2ecf20Sopenharmony_ci sk = sk_alloc(net, family, GFP_KERNEL, &vcc_proto, kern); 1498c2ecf20Sopenharmony_ci if (!sk) 1508c2ecf20Sopenharmony_ci return -ENOMEM; 1518c2ecf20Sopenharmony_ci sock_init_data(sock, sk); 1528c2ecf20Sopenharmony_ci sk->sk_state_change = vcc_def_wakeup; 1538c2ecf20Sopenharmony_ci sk->sk_write_space = vcc_write_space; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci vcc = atm_sk(sk); 1568c2ecf20Sopenharmony_ci vcc->dev = NULL; 1578c2ecf20Sopenharmony_ci memset(&vcc->local, 0, sizeof(struct sockaddr_atmsvc)); 1588c2ecf20Sopenharmony_ci memset(&vcc->remote, 0, sizeof(struct sockaddr_atmsvc)); 1598c2ecf20Sopenharmony_ci vcc->qos.txtp.max_sdu = 1 << 16; /* for meta VCs */ 1608c2ecf20Sopenharmony_ci refcount_set(&sk->sk_wmem_alloc, 1); 1618c2ecf20Sopenharmony_ci atomic_set(&sk->sk_rmem_alloc, 0); 1628c2ecf20Sopenharmony_ci vcc->push = NULL; 1638c2ecf20Sopenharmony_ci vcc->pop = NULL; 1648c2ecf20Sopenharmony_ci vcc->owner = NULL; 1658c2ecf20Sopenharmony_ci vcc->push_oam = NULL; 1668c2ecf20Sopenharmony_ci vcc->release_cb = NULL; 1678c2ecf20Sopenharmony_ci vcc->vpi = vcc->vci = 0; /* no VCI/VPI yet */ 1688c2ecf20Sopenharmony_ci vcc->atm_options = vcc->aal_options = 0; 1698c2ecf20Sopenharmony_ci sk->sk_destruct = vcc_sock_destruct; 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic void vcc_destroy_socket(struct sock *sk) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct atm_vcc *vcc = atm_sk(sk); 1768c2ecf20Sopenharmony_ci struct sk_buff *skb; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci set_bit(ATM_VF_CLOSE, &vcc->flags); 1798c2ecf20Sopenharmony_ci clear_bit(ATM_VF_READY, &vcc->flags); 1808c2ecf20Sopenharmony_ci if (vcc->dev && vcc->dev->ops->close) 1818c2ecf20Sopenharmony_ci vcc->dev->ops->close(vcc); 1828c2ecf20Sopenharmony_ci if (vcc->push) 1838c2ecf20Sopenharmony_ci vcc->push(vcc, NULL); /* atmarpd has no push */ 1848c2ecf20Sopenharmony_ci module_put(vcc->owner); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) { 1878c2ecf20Sopenharmony_ci atm_return(vcc, skb->truesize); 1888c2ecf20Sopenharmony_ci kfree_skb(skb); 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (vcc->dev && vcc->dev->ops->owner) { 1928c2ecf20Sopenharmony_ci module_put(vcc->dev->ops->owner); 1938c2ecf20Sopenharmony_ci atm_dev_put(vcc->dev); 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci vcc_remove_socket(sk); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ciint vcc_release(struct socket *sock) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct sock *sk = sock->sk; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (sk) { 2048c2ecf20Sopenharmony_ci lock_sock(sk); 2058c2ecf20Sopenharmony_ci vcc_destroy_socket(sock->sk); 2068c2ecf20Sopenharmony_ci release_sock(sk); 2078c2ecf20Sopenharmony_ci sock_put(sk); 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return 0; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_civoid vcc_release_async(struct atm_vcc *vcc, int reply) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct sock *sk = sk_atm(vcc); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci set_bit(ATM_VF_CLOSE, &vcc->flags); 2188c2ecf20Sopenharmony_ci sk->sk_shutdown |= RCV_SHUTDOWN; 2198c2ecf20Sopenharmony_ci sk->sk_err = -reply; 2208c2ecf20Sopenharmony_ci clear_bit(ATM_VF_WAITING, &vcc->flags); 2218c2ecf20Sopenharmony_ci sk->sk_state_change(sk); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vcc_release_async); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_civoid vcc_process_recv_queue(struct atm_vcc *vcc) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct sk_buff_head queue, *rq; 2288c2ecf20Sopenharmony_ci struct sk_buff *skb, *tmp; 2298c2ecf20Sopenharmony_ci unsigned long flags; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci __skb_queue_head_init(&queue); 2328c2ecf20Sopenharmony_ci rq = &sk_atm(vcc)->sk_receive_queue; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci spin_lock_irqsave(&rq->lock, flags); 2358c2ecf20Sopenharmony_ci skb_queue_splice_init(rq, &queue); 2368c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rq->lock, flags); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci skb_queue_walk_safe(&queue, skb, tmp) { 2398c2ecf20Sopenharmony_ci __skb_unlink(skb, &queue); 2408c2ecf20Sopenharmony_ci vcc->push(vcc, skb); 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vcc_process_recv_queue); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_civoid atm_dev_signal_change(struct atm_dev *dev, char signal) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci pr_debug("%s signal=%d dev=%p number=%d dev->signal=%d\n", 2488c2ecf20Sopenharmony_ci __func__, signal, dev, dev->number, dev->signal); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* atm driver sending invalid signal */ 2518c2ecf20Sopenharmony_ci WARN_ON(signal < ATM_PHY_SIG_LOST || signal > ATM_PHY_SIG_FOUND); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (dev->signal == signal) 2548c2ecf20Sopenharmony_ci return; /* no change */ 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci dev->signal = signal; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci atomic_notifier_call_chain(&atm_dev_notify_chain, signal, dev); 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(atm_dev_signal_change); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_civoid atm_dev_release_vccs(struct atm_dev *dev) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci int i; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci write_lock_irq(&vcc_sklist_lock); 2678c2ecf20Sopenharmony_ci for (i = 0; i < VCC_HTABLE_SIZE; i++) { 2688c2ecf20Sopenharmony_ci struct hlist_head *head = &vcc_hash[i]; 2698c2ecf20Sopenharmony_ci struct hlist_node *tmp; 2708c2ecf20Sopenharmony_ci struct sock *s; 2718c2ecf20Sopenharmony_ci struct atm_vcc *vcc; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci sk_for_each_safe(s, tmp, head) { 2748c2ecf20Sopenharmony_ci vcc = atm_sk(s); 2758c2ecf20Sopenharmony_ci if (vcc->dev == dev) { 2768c2ecf20Sopenharmony_ci vcc_release_async(vcc, -EPIPE); 2778c2ecf20Sopenharmony_ci sk_del_node_init(s); 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci write_unlock_irq(&vcc_sklist_lock); 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(atm_dev_release_vccs); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic int adjust_tp(struct atm_trafprm *tp, unsigned char aal) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci int max_sdu; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (!tp->traffic_class) 2908c2ecf20Sopenharmony_ci return 0; 2918c2ecf20Sopenharmony_ci switch (aal) { 2928c2ecf20Sopenharmony_ci case ATM_AAL0: 2938c2ecf20Sopenharmony_ci max_sdu = ATM_CELL_SIZE-1; 2948c2ecf20Sopenharmony_ci break; 2958c2ecf20Sopenharmony_ci case ATM_AAL34: 2968c2ecf20Sopenharmony_ci max_sdu = ATM_MAX_AAL34_PDU; 2978c2ecf20Sopenharmony_ci break; 2988c2ecf20Sopenharmony_ci default: 2998c2ecf20Sopenharmony_ci pr_warn("AAL problems ... (%d)\n", aal); 3008c2ecf20Sopenharmony_ci fallthrough; 3018c2ecf20Sopenharmony_ci case ATM_AAL5: 3028c2ecf20Sopenharmony_ci max_sdu = ATM_MAX_AAL5_PDU; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci if (!tp->max_sdu) 3058c2ecf20Sopenharmony_ci tp->max_sdu = max_sdu; 3068c2ecf20Sopenharmony_ci else if (tp->max_sdu > max_sdu) 3078c2ecf20Sopenharmony_ci return -EINVAL; 3088c2ecf20Sopenharmony_ci if (!tp->max_cdv) 3098c2ecf20Sopenharmony_ci tp->max_cdv = ATM_MAX_CDV; 3108c2ecf20Sopenharmony_ci return 0; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic int check_ci(const struct atm_vcc *vcc, short vpi, int vci) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct hlist_head *head = &vcc_hash[vci & (VCC_HTABLE_SIZE - 1)]; 3168c2ecf20Sopenharmony_ci struct sock *s; 3178c2ecf20Sopenharmony_ci struct atm_vcc *walk; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci sk_for_each(s, head) { 3208c2ecf20Sopenharmony_ci walk = atm_sk(s); 3218c2ecf20Sopenharmony_ci if (walk->dev != vcc->dev) 3228c2ecf20Sopenharmony_ci continue; 3238c2ecf20Sopenharmony_ci if (test_bit(ATM_VF_ADDR, &walk->flags) && walk->vpi == vpi && 3248c2ecf20Sopenharmony_ci walk->vci == vci && ((walk->qos.txtp.traffic_class != 3258c2ecf20Sopenharmony_ci ATM_NONE && vcc->qos.txtp.traffic_class != ATM_NONE) || 3268c2ecf20Sopenharmony_ci (walk->qos.rxtp.traffic_class != ATM_NONE && 3278c2ecf20Sopenharmony_ci vcc->qos.rxtp.traffic_class != ATM_NONE))) 3288c2ecf20Sopenharmony_ci return -EADDRINUSE; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* allow VCCs with same VPI/VCI iff they don't collide on 3328c2ecf20Sopenharmony_ci TX/RX (but we may refuse such sharing for other reasons, 3338c2ecf20Sopenharmony_ci e.g. if protocol requires to have both channels) */ 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci return 0; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic int find_ci(const struct atm_vcc *vcc, short *vpi, int *vci) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci static short p; /* poor man's per-device cache */ 3418c2ecf20Sopenharmony_ci static int c; 3428c2ecf20Sopenharmony_ci short old_p; 3438c2ecf20Sopenharmony_ci int old_c; 3448c2ecf20Sopenharmony_ci int err; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (*vpi != ATM_VPI_ANY && *vci != ATM_VCI_ANY) { 3478c2ecf20Sopenharmony_ci err = check_ci(vcc, *vpi, *vci); 3488c2ecf20Sopenharmony_ci return err; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci /* last scan may have left values out of bounds for current device */ 3518c2ecf20Sopenharmony_ci if (*vpi != ATM_VPI_ANY) 3528c2ecf20Sopenharmony_ci p = *vpi; 3538c2ecf20Sopenharmony_ci else if (p >= 1 << vcc->dev->ci_range.vpi_bits) 3548c2ecf20Sopenharmony_ci p = 0; 3558c2ecf20Sopenharmony_ci if (*vci != ATM_VCI_ANY) 3568c2ecf20Sopenharmony_ci c = *vci; 3578c2ecf20Sopenharmony_ci else if (c < ATM_NOT_RSV_VCI || c >= 1 << vcc->dev->ci_range.vci_bits) 3588c2ecf20Sopenharmony_ci c = ATM_NOT_RSV_VCI; 3598c2ecf20Sopenharmony_ci old_p = p; 3608c2ecf20Sopenharmony_ci old_c = c; 3618c2ecf20Sopenharmony_ci do { 3628c2ecf20Sopenharmony_ci if (!check_ci(vcc, p, c)) { 3638c2ecf20Sopenharmony_ci *vpi = p; 3648c2ecf20Sopenharmony_ci *vci = c; 3658c2ecf20Sopenharmony_ci return 0; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci if (*vci == ATM_VCI_ANY) { 3688c2ecf20Sopenharmony_ci c++; 3698c2ecf20Sopenharmony_ci if (c >= 1 << vcc->dev->ci_range.vci_bits) 3708c2ecf20Sopenharmony_ci c = ATM_NOT_RSV_VCI; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci if ((c == ATM_NOT_RSV_VCI || *vci != ATM_VCI_ANY) && 3738c2ecf20Sopenharmony_ci *vpi == ATM_VPI_ANY) { 3748c2ecf20Sopenharmony_ci p++; 3758c2ecf20Sopenharmony_ci if (p >= 1 << vcc->dev->ci_range.vpi_bits) 3768c2ecf20Sopenharmony_ci p = 0; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci } while (old_p != p || old_c != c); 3798c2ecf20Sopenharmony_ci return -EADDRINUSE; 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic int __vcc_connect(struct atm_vcc *vcc, struct atm_dev *dev, short vpi, 3838c2ecf20Sopenharmony_ci int vci) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci struct sock *sk = sk_atm(vcc); 3868c2ecf20Sopenharmony_ci int error; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if ((vpi != ATM_VPI_UNSPEC && vpi != ATM_VPI_ANY && 3898c2ecf20Sopenharmony_ci vpi >> dev->ci_range.vpi_bits) || (vci != ATM_VCI_UNSPEC && 3908c2ecf20Sopenharmony_ci vci != ATM_VCI_ANY && vci >> dev->ci_range.vci_bits)) 3918c2ecf20Sopenharmony_ci return -EINVAL; 3928c2ecf20Sopenharmony_ci if (vci > 0 && vci < ATM_NOT_RSV_VCI && !capable(CAP_NET_BIND_SERVICE)) 3938c2ecf20Sopenharmony_ci return -EPERM; 3948c2ecf20Sopenharmony_ci error = -ENODEV; 3958c2ecf20Sopenharmony_ci if (!try_module_get(dev->ops->owner)) 3968c2ecf20Sopenharmony_ci return error; 3978c2ecf20Sopenharmony_ci vcc->dev = dev; 3988c2ecf20Sopenharmony_ci write_lock_irq(&vcc_sklist_lock); 3998c2ecf20Sopenharmony_ci if (test_bit(ATM_DF_REMOVED, &dev->flags) || 4008c2ecf20Sopenharmony_ci (error = find_ci(vcc, &vpi, &vci))) { 4018c2ecf20Sopenharmony_ci write_unlock_irq(&vcc_sklist_lock); 4028c2ecf20Sopenharmony_ci goto fail_module_put; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci vcc->vpi = vpi; 4058c2ecf20Sopenharmony_ci vcc->vci = vci; 4068c2ecf20Sopenharmony_ci __vcc_insert_socket(sk); 4078c2ecf20Sopenharmony_ci write_unlock_irq(&vcc_sklist_lock); 4088c2ecf20Sopenharmony_ci switch (vcc->qos.aal) { 4098c2ecf20Sopenharmony_ci case ATM_AAL0: 4108c2ecf20Sopenharmony_ci error = atm_init_aal0(vcc); 4118c2ecf20Sopenharmony_ci vcc->stats = &dev->stats.aal0; 4128c2ecf20Sopenharmony_ci break; 4138c2ecf20Sopenharmony_ci case ATM_AAL34: 4148c2ecf20Sopenharmony_ci error = atm_init_aal34(vcc); 4158c2ecf20Sopenharmony_ci vcc->stats = &dev->stats.aal34; 4168c2ecf20Sopenharmony_ci break; 4178c2ecf20Sopenharmony_ci case ATM_NO_AAL: 4188c2ecf20Sopenharmony_ci /* ATM_AAL5 is also used in the "0 for default" case */ 4198c2ecf20Sopenharmony_ci vcc->qos.aal = ATM_AAL5; 4208c2ecf20Sopenharmony_ci fallthrough; 4218c2ecf20Sopenharmony_ci case ATM_AAL5: 4228c2ecf20Sopenharmony_ci error = atm_init_aal5(vcc); 4238c2ecf20Sopenharmony_ci vcc->stats = &dev->stats.aal5; 4248c2ecf20Sopenharmony_ci break; 4258c2ecf20Sopenharmony_ci default: 4268c2ecf20Sopenharmony_ci error = -EPROTOTYPE; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci if (!error) 4298c2ecf20Sopenharmony_ci error = adjust_tp(&vcc->qos.txtp, vcc->qos.aal); 4308c2ecf20Sopenharmony_ci if (!error) 4318c2ecf20Sopenharmony_ci error = adjust_tp(&vcc->qos.rxtp, vcc->qos.aal); 4328c2ecf20Sopenharmony_ci if (error) 4338c2ecf20Sopenharmony_ci goto fail; 4348c2ecf20Sopenharmony_ci pr_debug("VCC %d.%d, AAL %d\n", vpi, vci, vcc->qos.aal); 4358c2ecf20Sopenharmony_ci pr_debug(" TX: %d, PCR %d..%d, SDU %d\n", 4368c2ecf20Sopenharmony_ci vcc->qos.txtp.traffic_class, 4378c2ecf20Sopenharmony_ci vcc->qos.txtp.min_pcr, 4388c2ecf20Sopenharmony_ci vcc->qos.txtp.max_pcr, 4398c2ecf20Sopenharmony_ci vcc->qos.txtp.max_sdu); 4408c2ecf20Sopenharmony_ci pr_debug(" RX: %d, PCR %d..%d, SDU %d\n", 4418c2ecf20Sopenharmony_ci vcc->qos.rxtp.traffic_class, 4428c2ecf20Sopenharmony_ci vcc->qos.rxtp.min_pcr, 4438c2ecf20Sopenharmony_ci vcc->qos.rxtp.max_pcr, 4448c2ecf20Sopenharmony_ci vcc->qos.rxtp.max_sdu); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (dev->ops->open) { 4478c2ecf20Sopenharmony_ci error = dev->ops->open(vcc); 4488c2ecf20Sopenharmony_ci if (error) 4498c2ecf20Sopenharmony_ci goto fail; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci return 0; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cifail: 4548c2ecf20Sopenharmony_ci vcc_remove_socket(sk); 4558c2ecf20Sopenharmony_cifail_module_put: 4568c2ecf20Sopenharmony_ci module_put(dev->ops->owner); 4578c2ecf20Sopenharmony_ci /* ensure we get dev module ref count correct */ 4588c2ecf20Sopenharmony_ci vcc->dev = NULL; 4598c2ecf20Sopenharmony_ci return error; 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ciint vcc_connect(struct socket *sock, int itf, short vpi, int vci) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci struct atm_dev *dev; 4658c2ecf20Sopenharmony_ci struct atm_vcc *vcc = ATM_SD(sock); 4668c2ecf20Sopenharmony_ci int error; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci pr_debug("(vpi %d, vci %d)\n", vpi, vci); 4698c2ecf20Sopenharmony_ci if (sock->state == SS_CONNECTED) 4708c2ecf20Sopenharmony_ci return -EISCONN; 4718c2ecf20Sopenharmony_ci if (sock->state != SS_UNCONNECTED) 4728c2ecf20Sopenharmony_ci return -EINVAL; 4738c2ecf20Sopenharmony_ci if (!(vpi || vci)) 4748c2ecf20Sopenharmony_ci return -EINVAL; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (vpi != ATM_VPI_UNSPEC && vci != ATM_VCI_UNSPEC) 4778c2ecf20Sopenharmony_ci clear_bit(ATM_VF_PARTIAL, &vcc->flags); 4788c2ecf20Sopenharmony_ci else 4798c2ecf20Sopenharmony_ci if (test_bit(ATM_VF_PARTIAL, &vcc->flags)) 4808c2ecf20Sopenharmony_ci return -EINVAL; 4818c2ecf20Sopenharmony_ci pr_debug("(TX: cl %d,bw %d-%d,sdu %d; " 4828c2ecf20Sopenharmony_ci "RX: cl %d,bw %d-%d,sdu %d,AAL %s%d)\n", 4838c2ecf20Sopenharmony_ci vcc->qos.txtp.traffic_class, vcc->qos.txtp.min_pcr, 4848c2ecf20Sopenharmony_ci vcc->qos.txtp.max_pcr, vcc->qos.txtp.max_sdu, 4858c2ecf20Sopenharmony_ci vcc->qos.rxtp.traffic_class, vcc->qos.rxtp.min_pcr, 4868c2ecf20Sopenharmony_ci vcc->qos.rxtp.max_pcr, vcc->qos.rxtp.max_sdu, 4878c2ecf20Sopenharmony_ci vcc->qos.aal == ATM_AAL5 ? "" : 4888c2ecf20Sopenharmony_ci vcc->qos.aal == ATM_AAL0 ? "" : " ??? code ", 4898c2ecf20Sopenharmony_ci vcc->qos.aal == ATM_AAL0 ? 0 : vcc->qos.aal); 4908c2ecf20Sopenharmony_ci if (!test_bit(ATM_VF_HASQOS, &vcc->flags)) 4918c2ecf20Sopenharmony_ci return -EBADFD; 4928c2ecf20Sopenharmony_ci if (vcc->qos.txtp.traffic_class == ATM_ANYCLASS || 4938c2ecf20Sopenharmony_ci vcc->qos.rxtp.traffic_class == ATM_ANYCLASS) 4948c2ecf20Sopenharmony_ci return -EINVAL; 4958c2ecf20Sopenharmony_ci if (likely(itf != ATM_ITF_ANY)) { 4968c2ecf20Sopenharmony_ci dev = try_then_request_module(atm_dev_lookup(itf), 4978c2ecf20Sopenharmony_ci "atm-device-%d", itf); 4988c2ecf20Sopenharmony_ci } else { 4998c2ecf20Sopenharmony_ci dev = NULL; 5008c2ecf20Sopenharmony_ci mutex_lock(&atm_dev_mutex); 5018c2ecf20Sopenharmony_ci if (!list_empty(&atm_devs)) { 5028c2ecf20Sopenharmony_ci dev = list_entry(atm_devs.next, 5038c2ecf20Sopenharmony_ci struct atm_dev, dev_list); 5048c2ecf20Sopenharmony_ci atm_dev_hold(dev); 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci mutex_unlock(&atm_dev_mutex); 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci if (!dev) 5098c2ecf20Sopenharmony_ci return -ENODEV; 5108c2ecf20Sopenharmony_ci error = __vcc_connect(vcc, dev, vpi, vci); 5118c2ecf20Sopenharmony_ci if (error) { 5128c2ecf20Sopenharmony_ci atm_dev_put(dev); 5138c2ecf20Sopenharmony_ci return error; 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC) 5168c2ecf20Sopenharmony_ci set_bit(ATM_VF_PARTIAL, &vcc->flags); 5178c2ecf20Sopenharmony_ci if (test_bit(ATM_VF_READY, &ATM_SD(sock)->flags)) 5188c2ecf20Sopenharmony_ci sock->state = SS_CONNECTED; 5198c2ecf20Sopenharmony_ci return 0; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ciint vcc_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, 5238c2ecf20Sopenharmony_ci int flags) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci struct sock *sk = sock->sk; 5268c2ecf20Sopenharmony_ci struct atm_vcc *vcc; 5278c2ecf20Sopenharmony_ci struct sk_buff *skb; 5288c2ecf20Sopenharmony_ci int copied, error = -EINVAL; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (sock->state != SS_CONNECTED) 5318c2ecf20Sopenharmony_ci return -ENOTCONN; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci /* only handle MSG_DONTWAIT and MSG_PEEK */ 5348c2ecf20Sopenharmony_ci if (flags & ~(MSG_DONTWAIT | MSG_PEEK)) 5358c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci vcc = ATM_SD(sock); 5388c2ecf20Sopenharmony_ci if (test_bit(ATM_VF_RELEASED, &vcc->flags) || 5398c2ecf20Sopenharmony_ci test_bit(ATM_VF_CLOSE, &vcc->flags) || 5408c2ecf20Sopenharmony_ci !test_bit(ATM_VF_READY, &vcc->flags)) 5418c2ecf20Sopenharmony_ci return 0; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &error); 5448c2ecf20Sopenharmony_ci if (!skb) 5458c2ecf20Sopenharmony_ci return error; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci copied = skb->len; 5488c2ecf20Sopenharmony_ci if (copied > size) { 5498c2ecf20Sopenharmony_ci copied = size; 5508c2ecf20Sopenharmony_ci msg->msg_flags |= MSG_TRUNC; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci error = skb_copy_datagram_msg(skb, 0, msg, copied); 5548c2ecf20Sopenharmony_ci if (error) 5558c2ecf20Sopenharmony_ci return error; 5568c2ecf20Sopenharmony_ci sock_recv_ts_and_drops(msg, sk, skb); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (!(flags & MSG_PEEK)) { 5598c2ecf20Sopenharmony_ci pr_debug("%d -= %d\n", atomic_read(&sk->sk_rmem_alloc), 5608c2ecf20Sopenharmony_ci skb->truesize); 5618c2ecf20Sopenharmony_ci atm_return(vcc, skb->truesize); 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci skb_free_datagram(sk, skb); 5658c2ecf20Sopenharmony_ci return copied; 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ciint vcc_sendmsg(struct socket *sock, struct msghdr *m, size_t size) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci struct sock *sk = sock->sk; 5718c2ecf20Sopenharmony_ci DEFINE_WAIT(wait); 5728c2ecf20Sopenharmony_ci struct atm_vcc *vcc; 5738c2ecf20Sopenharmony_ci struct sk_buff *skb; 5748c2ecf20Sopenharmony_ci int eff, error; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci lock_sock(sk); 5778c2ecf20Sopenharmony_ci if (sock->state != SS_CONNECTED) { 5788c2ecf20Sopenharmony_ci error = -ENOTCONN; 5798c2ecf20Sopenharmony_ci goto out; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci if (m->msg_name) { 5828c2ecf20Sopenharmony_ci error = -EISCONN; 5838c2ecf20Sopenharmony_ci goto out; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci vcc = ATM_SD(sock); 5868c2ecf20Sopenharmony_ci if (test_bit(ATM_VF_RELEASED, &vcc->flags) || 5878c2ecf20Sopenharmony_ci test_bit(ATM_VF_CLOSE, &vcc->flags) || 5888c2ecf20Sopenharmony_ci !test_bit(ATM_VF_READY, &vcc->flags)) { 5898c2ecf20Sopenharmony_ci error = -EPIPE; 5908c2ecf20Sopenharmony_ci send_sig(SIGPIPE, current, 0); 5918c2ecf20Sopenharmony_ci goto out; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci if (!size) { 5948c2ecf20Sopenharmony_ci error = 0; 5958c2ecf20Sopenharmony_ci goto out; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci if (size > vcc->qos.txtp.max_sdu) { 5988c2ecf20Sopenharmony_ci error = -EMSGSIZE; 5998c2ecf20Sopenharmony_ci goto out; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci eff = (size+3) & ~3; /* align to word boundary */ 6038c2ecf20Sopenharmony_ci prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); 6048c2ecf20Sopenharmony_ci error = 0; 6058c2ecf20Sopenharmony_ci while (!vcc_tx_ready(vcc, eff)) { 6068c2ecf20Sopenharmony_ci if (m->msg_flags & MSG_DONTWAIT) { 6078c2ecf20Sopenharmony_ci error = -EAGAIN; 6088c2ecf20Sopenharmony_ci break; 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci schedule(); 6118c2ecf20Sopenharmony_ci if (signal_pending(current)) { 6128c2ecf20Sopenharmony_ci error = -ERESTARTSYS; 6138c2ecf20Sopenharmony_ci break; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci if (test_bit(ATM_VF_RELEASED, &vcc->flags) || 6168c2ecf20Sopenharmony_ci test_bit(ATM_VF_CLOSE, &vcc->flags) || 6178c2ecf20Sopenharmony_ci !test_bit(ATM_VF_READY, &vcc->flags)) { 6188c2ecf20Sopenharmony_ci error = -EPIPE; 6198c2ecf20Sopenharmony_ci send_sig(SIGPIPE, current, 0); 6208c2ecf20Sopenharmony_ci break; 6218c2ecf20Sopenharmony_ci } 6228c2ecf20Sopenharmony_ci prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci finish_wait(sk_sleep(sk), &wait); 6258c2ecf20Sopenharmony_ci if (error) 6268c2ecf20Sopenharmony_ci goto out; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci skb = alloc_skb(eff, GFP_KERNEL); 6298c2ecf20Sopenharmony_ci if (!skb) { 6308c2ecf20Sopenharmony_ci error = -ENOMEM; 6318c2ecf20Sopenharmony_ci goto out; 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci pr_debug("%d += %d\n", sk_wmem_alloc_get(sk), skb->truesize); 6348c2ecf20Sopenharmony_ci atm_account_tx(vcc, skb); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci skb->dev = NULL; /* for paths shared with net_device interfaces */ 6378c2ecf20Sopenharmony_ci if (!copy_from_iter_full(skb_put(skb, size), size, &m->msg_iter)) { 6388c2ecf20Sopenharmony_ci kfree_skb(skb); 6398c2ecf20Sopenharmony_ci error = -EFAULT; 6408c2ecf20Sopenharmony_ci goto out; 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci if (eff != size) 6438c2ecf20Sopenharmony_ci memset(skb->data + size, 0, eff-size); 6448c2ecf20Sopenharmony_ci error = vcc->dev->ops->send(vcc, skb); 6458c2ecf20Sopenharmony_ci error = error ? error : size; 6468c2ecf20Sopenharmony_ciout: 6478c2ecf20Sopenharmony_ci release_sock(sk); 6488c2ecf20Sopenharmony_ci return error; 6498c2ecf20Sopenharmony_ci} 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci__poll_t vcc_poll(struct file *file, struct socket *sock, poll_table *wait) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci struct sock *sk = sock->sk; 6548c2ecf20Sopenharmony_ci struct atm_vcc *vcc; 6558c2ecf20Sopenharmony_ci __poll_t mask; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci sock_poll_wait(file, sock, wait); 6588c2ecf20Sopenharmony_ci mask = 0; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci vcc = ATM_SD(sock); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci /* exceptional events */ 6638c2ecf20Sopenharmony_ci if (sk->sk_err) 6648c2ecf20Sopenharmony_ci mask = EPOLLERR; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci if (test_bit(ATM_VF_RELEASED, &vcc->flags) || 6678c2ecf20Sopenharmony_ci test_bit(ATM_VF_CLOSE, &vcc->flags)) 6688c2ecf20Sopenharmony_ci mask |= EPOLLHUP; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci /* readable? */ 6718c2ecf20Sopenharmony_ci if (!skb_queue_empty_lockless(&sk->sk_receive_queue)) 6728c2ecf20Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci /* writable? */ 6758c2ecf20Sopenharmony_ci if (sock->state == SS_CONNECTING && 6768c2ecf20Sopenharmony_ci test_bit(ATM_VF_WAITING, &vcc->flags)) 6778c2ecf20Sopenharmony_ci return mask; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (vcc->qos.txtp.traffic_class != ATM_NONE && 6808c2ecf20Sopenharmony_ci vcc_writable(sk)) 6818c2ecf20Sopenharmony_ci mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci return mask; 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_cistatic int atm_change_qos(struct atm_vcc *vcc, struct atm_qos *qos) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci int error; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci /* 6918c2ecf20Sopenharmony_ci * Don't let the QoS change the already connected AAL type nor the 6928c2ecf20Sopenharmony_ci * traffic class. 6938c2ecf20Sopenharmony_ci */ 6948c2ecf20Sopenharmony_ci if (qos->aal != vcc->qos.aal || 6958c2ecf20Sopenharmony_ci qos->rxtp.traffic_class != vcc->qos.rxtp.traffic_class || 6968c2ecf20Sopenharmony_ci qos->txtp.traffic_class != vcc->qos.txtp.traffic_class) 6978c2ecf20Sopenharmony_ci return -EINVAL; 6988c2ecf20Sopenharmony_ci error = adjust_tp(&qos->txtp, qos->aal); 6998c2ecf20Sopenharmony_ci if (!error) 7008c2ecf20Sopenharmony_ci error = adjust_tp(&qos->rxtp, qos->aal); 7018c2ecf20Sopenharmony_ci if (error) 7028c2ecf20Sopenharmony_ci return error; 7038c2ecf20Sopenharmony_ci if (!vcc->dev->ops->change_qos) 7048c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 7058c2ecf20Sopenharmony_ci if (sk_atm(vcc)->sk_family == AF_ATMPVC) 7068c2ecf20Sopenharmony_ci return vcc->dev->ops->change_qos(vcc, qos, ATM_MF_SET); 7078c2ecf20Sopenharmony_ci return svc_change_qos(vcc, qos); 7088c2ecf20Sopenharmony_ci} 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_cistatic int check_tp(const struct atm_trafprm *tp) 7118c2ecf20Sopenharmony_ci{ 7128c2ecf20Sopenharmony_ci /* @@@ Should be merged with adjust_tp */ 7138c2ecf20Sopenharmony_ci if (!tp->traffic_class || tp->traffic_class == ATM_ANYCLASS) 7148c2ecf20Sopenharmony_ci return 0; 7158c2ecf20Sopenharmony_ci if (tp->traffic_class != ATM_UBR && !tp->min_pcr && !tp->pcr && 7168c2ecf20Sopenharmony_ci !tp->max_pcr) 7178c2ecf20Sopenharmony_ci return -EINVAL; 7188c2ecf20Sopenharmony_ci if (tp->min_pcr == ATM_MAX_PCR) 7198c2ecf20Sopenharmony_ci return -EINVAL; 7208c2ecf20Sopenharmony_ci if (tp->min_pcr && tp->max_pcr && tp->max_pcr != ATM_MAX_PCR && 7218c2ecf20Sopenharmony_ci tp->min_pcr > tp->max_pcr) 7228c2ecf20Sopenharmony_ci return -EINVAL; 7238c2ecf20Sopenharmony_ci /* 7248c2ecf20Sopenharmony_ci * We allow pcr to be outside [min_pcr,max_pcr], because later 7258c2ecf20Sopenharmony_ci * adjustment may still push it in the valid range. 7268c2ecf20Sopenharmony_ci */ 7278c2ecf20Sopenharmony_ci return 0; 7288c2ecf20Sopenharmony_ci} 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_cistatic int check_qos(const struct atm_qos *qos) 7318c2ecf20Sopenharmony_ci{ 7328c2ecf20Sopenharmony_ci int error; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci if (!qos->txtp.traffic_class && !qos->rxtp.traffic_class) 7358c2ecf20Sopenharmony_ci return -EINVAL; 7368c2ecf20Sopenharmony_ci if (qos->txtp.traffic_class != qos->rxtp.traffic_class && 7378c2ecf20Sopenharmony_ci qos->txtp.traffic_class && qos->rxtp.traffic_class && 7388c2ecf20Sopenharmony_ci qos->txtp.traffic_class != ATM_ANYCLASS && 7398c2ecf20Sopenharmony_ci qos->rxtp.traffic_class != ATM_ANYCLASS) 7408c2ecf20Sopenharmony_ci return -EINVAL; 7418c2ecf20Sopenharmony_ci error = check_tp(&qos->txtp); 7428c2ecf20Sopenharmony_ci if (error) 7438c2ecf20Sopenharmony_ci return error; 7448c2ecf20Sopenharmony_ci return check_tp(&qos->rxtp); 7458c2ecf20Sopenharmony_ci} 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ciint vcc_setsockopt(struct socket *sock, int level, int optname, 7488c2ecf20Sopenharmony_ci sockptr_t optval, unsigned int optlen) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci struct atm_vcc *vcc; 7518c2ecf20Sopenharmony_ci unsigned long value; 7528c2ecf20Sopenharmony_ci int error; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci if (__SO_LEVEL_MATCH(optname, level) && optlen != __SO_SIZE(optname)) 7558c2ecf20Sopenharmony_ci return -EINVAL; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci vcc = ATM_SD(sock); 7588c2ecf20Sopenharmony_ci switch (optname) { 7598c2ecf20Sopenharmony_ci case SO_ATMQOS: 7608c2ecf20Sopenharmony_ci { 7618c2ecf20Sopenharmony_ci struct atm_qos qos; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci if (copy_from_sockptr(&qos, optval, sizeof(qos))) 7648c2ecf20Sopenharmony_ci return -EFAULT; 7658c2ecf20Sopenharmony_ci error = check_qos(&qos); 7668c2ecf20Sopenharmony_ci if (error) 7678c2ecf20Sopenharmony_ci return error; 7688c2ecf20Sopenharmony_ci if (sock->state == SS_CONNECTED) 7698c2ecf20Sopenharmony_ci return atm_change_qos(vcc, &qos); 7708c2ecf20Sopenharmony_ci if (sock->state != SS_UNCONNECTED) 7718c2ecf20Sopenharmony_ci return -EBADFD; 7728c2ecf20Sopenharmony_ci vcc->qos = qos; 7738c2ecf20Sopenharmony_ci set_bit(ATM_VF_HASQOS, &vcc->flags); 7748c2ecf20Sopenharmony_ci return 0; 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci case SO_SETCLP: 7778c2ecf20Sopenharmony_ci if (copy_from_sockptr(&value, optval, sizeof(value))) 7788c2ecf20Sopenharmony_ci return -EFAULT; 7798c2ecf20Sopenharmony_ci if (value) 7808c2ecf20Sopenharmony_ci vcc->atm_options |= ATM_ATMOPT_CLP; 7818c2ecf20Sopenharmony_ci else 7828c2ecf20Sopenharmony_ci vcc->atm_options &= ~ATM_ATMOPT_CLP; 7838c2ecf20Sopenharmony_ci return 0; 7848c2ecf20Sopenharmony_ci default: 7858c2ecf20Sopenharmony_ci return -EINVAL; 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ciint vcc_getsockopt(struct socket *sock, int level, int optname, 7908c2ecf20Sopenharmony_ci char __user *optval, int __user *optlen) 7918c2ecf20Sopenharmony_ci{ 7928c2ecf20Sopenharmony_ci struct atm_vcc *vcc; 7938c2ecf20Sopenharmony_ci int len; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci if (get_user(len, optlen)) 7968c2ecf20Sopenharmony_ci return -EFAULT; 7978c2ecf20Sopenharmony_ci if (__SO_LEVEL_MATCH(optname, level) && len != __SO_SIZE(optname)) 7988c2ecf20Sopenharmony_ci return -EINVAL; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci vcc = ATM_SD(sock); 8018c2ecf20Sopenharmony_ci switch (optname) { 8028c2ecf20Sopenharmony_ci case SO_ATMQOS: 8038c2ecf20Sopenharmony_ci if (!test_bit(ATM_VF_HASQOS, &vcc->flags)) 8048c2ecf20Sopenharmony_ci return -EINVAL; 8058c2ecf20Sopenharmony_ci return copy_to_user(optval, &vcc->qos, sizeof(vcc->qos)) 8068c2ecf20Sopenharmony_ci ? -EFAULT : 0; 8078c2ecf20Sopenharmony_ci case SO_SETCLP: 8088c2ecf20Sopenharmony_ci return put_user(vcc->atm_options & ATM_ATMOPT_CLP ? 1 : 0, 8098c2ecf20Sopenharmony_ci (unsigned long __user *)optval) ? -EFAULT : 0; 8108c2ecf20Sopenharmony_ci case SO_ATMPVC: 8118c2ecf20Sopenharmony_ci { 8128c2ecf20Sopenharmony_ci struct sockaddr_atmpvc pvc; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci if (!vcc->dev || !test_bit(ATM_VF_ADDR, &vcc->flags)) 8158c2ecf20Sopenharmony_ci return -ENOTCONN; 8168c2ecf20Sopenharmony_ci memset(&pvc, 0, sizeof(pvc)); 8178c2ecf20Sopenharmony_ci pvc.sap_family = AF_ATMPVC; 8188c2ecf20Sopenharmony_ci pvc.sap_addr.itf = vcc->dev->number; 8198c2ecf20Sopenharmony_ci pvc.sap_addr.vpi = vcc->vpi; 8208c2ecf20Sopenharmony_ci pvc.sap_addr.vci = vcc->vci; 8218c2ecf20Sopenharmony_ci return copy_to_user(optval, &pvc, sizeof(pvc)) ? -EFAULT : 0; 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci default: 8248c2ecf20Sopenharmony_ci return -EINVAL; 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci} 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ciint register_atmdevice_notifier(struct notifier_block *nb) 8298c2ecf20Sopenharmony_ci{ 8308c2ecf20Sopenharmony_ci return atomic_notifier_chain_register(&atm_dev_notify_chain, nb); 8318c2ecf20Sopenharmony_ci} 8328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(register_atmdevice_notifier); 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_civoid unregister_atmdevice_notifier(struct notifier_block *nb) 8358c2ecf20Sopenharmony_ci{ 8368c2ecf20Sopenharmony_ci atomic_notifier_chain_unregister(&atm_dev_notify_chain, nb); 8378c2ecf20Sopenharmony_ci} 8388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(unregister_atmdevice_notifier); 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_cistatic int __init atm_init(void) 8418c2ecf20Sopenharmony_ci{ 8428c2ecf20Sopenharmony_ci int error; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci error = proto_register(&vcc_proto, 0); 8458c2ecf20Sopenharmony_ci if (error < 0) 8468c2ecf20Sopenharmony_ci goto out; 8478c2ecf20Sopenharmony_ci error = atmpvc_init(); 8488c2ecf20Sopenharmony_ci if (error < 0) { 8498c2ecf20Sopenharmony_ci pr_err("atmpvc_init() failed with %d\n", error); 8508c2ecf20Sopenharmony_ci goto out_unregister_vcc_proto; 8518c2ecf20Sopenharmony_ci } 8528c2ecf20Sopenharmony_ci error = atmsvc_init(); 8538c2ecf20Sopenharmony_ci if (error < 0) { 8548c2ecf20Sopenharmony_ci pr_err("atmsvc_init() failed with %d\n", error); 8558c2ecf20Sopenharmony_ci goto out_atmpvc_exit; 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci error = atm_proc_init(); 8588c2ecf20Sopenharmony_ci if (error < 0) { 8598c2ecf20Sopenharmony_ci pr_err("atm_proc_init() failed with %d\n", error); 8608c2ecf20Sopenharmony_ci goto out_atmsvc_exit; 8618c2ecf20Sopenharmony_ci } 8628c2ecf20Sopenharmony_ci error = atm_sysfs_init(); 8638c2ecf20Sopenharmony_ci if (error < 0) { 8648c2ecf20Sopenharmony_ci pr_err("atm_sysfs_init() failed with %d\n", error); 8658c2ecf20Sopenharmony_ci goto out_atmproc_exit; 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ciout: 8688c2ecf20Sopenharmony_ci return error; 8698c2ecf20Sopenharmony_ciout_atmproc_exit: 8708c2ecf20Sopenharmony_ci atm_proc_exit(); 8718c2ecf20Sopenharmony_ciout_atmsvc_exit: 8728c2ecf20Sopenharmony_ci atmsvc_exit(); 8738c2ecf20Sopenharmony_ciout_atmpvc_exit: 8748c2ecf20Sopenharmony_ci atmsvc_exit(); 8758c2ecf20Sopenharmony_ciout_unregister_vcc_proto: 8768c2ecf20Sopenharmony_ci proto_unregister(&vcc_proto); 8778c2ecf20Sopenharmony_ci goto out; 8788c2ecf20Sopenharmony_ci} 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_cistatic void __exit atm_exit(void) 8818c2ecf20Sopenharmony_ci{ 8828c2ecf20Sopenharmony_ci atm_proc_exit(); 8838c2ecf20Sopenharmony_ci atm_sysfs_exit(); 8848c2ecf20Sopenharmony_ci atmsvc_exit(); 8858c2ecf20Sopenharmony_ci atmpvc_exit(); 8868c2ecf20Sopenharmony_ci proto_unregister(&vcc_proto); 8878c2ecf20Sopenharmony_ci} 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_cisubsys_initcall(atm_init); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_cimodule_exit(atm_exit); 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 8948c2ecf20Sopenharmony_ciMODULE_ALIAS_NETPROTO(PF_ATMPVC); 8958c2ecf20Sopenharmony_ciMODULE_ALIAS_NETPROTO(PF_ATMSVC); 896