162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* net/atm/common.c - ATM sockets (common part for PVC and SVC) */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/kmod.h> 1062306a36Sopenharmony_ci#include <linux/net.h> /* struct socket, struct proto_ops */ 1162306a36Sopenharmony_ci#include <linux/atm.h> /* ATM stuff */ 1262306a36Sopenharmony_ci#include <linux/atmdev.h> 1362306a36Sopenharmony_ci#include <linux/socket.h> /* SOL_SOCKET */ 1462306a36Sopenharmony_ci#include <linux/errno.h> /* error codes */ 1562306a36Sopenharmony_ci#include <linux/capability.h> 1662306a36Sopenharmony_ci#include <linux/mm.h> 1762306a36Sopenharmony_ci#include <linux/sched/signal.h> 1862306a36Sopenharmony_ci#include <linux/time64.h> /* 64-bit time for seconds */ 1962306a36Sopenharmony_ci#include <linux/skbuff.h> 2062306a36Sopenharmony_ci#include <linux/bitops.h> 2162306a36Sopenharmony_ci#include <linux/init.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <net/sock.h> /* struct sock */ 2462306a36Sopenharmony_ci#include <linux/uaccess.h> 2562306a36Sopenharmony_ci#include <linux/poll.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <linux/atomic.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "resources.h" /* atm_find_dev */ 3062306a36Sopenharmony_ci#include "common.h" /* prototypes */ 3162306a36Sopenharmony_ci#include "protocols.h" /* atm_init_<transport> */ 3262306a36Sopenharmony_ci#include "addr.h" /* address registry */ 3362306a36Sopenharmony_ci#include "signaling.h" /* for WAITING and sigd_attach */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct hlist_head vcc_hash[VCC_HTABLE_SIZE]; 3662306a36Sopenharmony_ciEXPORT_SYMBOL(vcc_hash); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ciDEFINE_RWLOCK(vcc_sklist_lock); 3962306a36Sopenharmony_ciEXPORT_SYMBOL(vcc_sklist_lock); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic ATOMIC_NOTIFIER_HEAD(atm_dev_notify_chain); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic void __vcc_insert_socket(struct sock *sk) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci struct atm_vcc *vcc = atm_sk(sk); 4662306a36Sopenharmony_ci struct hlist_head *head = &vcc_hash[vcc->vci & (VCC_HTABLE_SIZE - 1)]; 4762306a36Sopenharmony_ci sk->sk_hash = vcc->vci & (VCC_HTABLE_SIZE - 1); 4862306a36Sopenharmony_ci sk_add_node(sk, head); 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_civoid vcc_insert_socket(struct sock *sk) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci write_lock_irq(&vcc_sklist_lock); 5462306a36Sopenharmony_ci __vcc_insert_socket(sk); 5562306a36Sopenharmony_ci write_unlock_irq(&vcc_sklist_lock); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ciEXPORT_SYMBOL(vcc_insert_socket); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void vcc_remove_socket(struct sock *sk) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci write_lock_irq(&vcc_sklist_lock); 6262306a36Sopenharmony_ci sk_del_node_init(sk); 6362306a36Sopenharmony_ci write_unlock_irq(&vcc_sklist_lock); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic bool vcc_tx_ready(struct atm_vcc *vcc, unsigned int size) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct sock *sk = sk_atm(vcc); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (sk_wmem_alloc_get(sk) && !atm_may_send(vcc, size)) { 7162306a36Sopenharmony_ci pr_debug("Sorry: wmem_alloc = %d, size = %d, sndbuf = %d\n", 7262306a36Sopenharmony_ci sk_wmem_alloc_get(sk), size, sk->sk_sndbuf); 7362306a36Sopenharmony_ci return false; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci return true; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic void vcc_sock_destruct(struct sock *sk) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci if (atomic_read(&sk->sk_rmem_alloc)) 8162306a36Sopenharmony_ci printk(KERN_DEBUG "%s: rmem leakage (%d bytes) detected.\n", 8262306a36Sopenharmony_ci __func__, atomic_read(&sk->sk_rmem_alloc)); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (refcount_read(&sk->sk_wmem_alloc)) 8562306a36Sopenharmony_ci printk(KERN_DEBUG "%s: wmem leakage (%d bytes) detected.\n", 8662306a36Sopenharmony_ci __func__, refcount_read(&sk->sk_wmem_alloc)); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void vcc_def_wakeup(struct sock *sk) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct socket_wq *wq; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci rcu_read_lock(); 9462306a36Sopenharmony_ci wq = rcu_dereference(sk->sk_wq); 9562306a36Sopenharmony_ci if (skwq_has_sleeper(wq)) 9662306a36Sopenharmony_ci wake_up(&wq->wait); 9762306a36Sopenharmony_ci rcu_read_unlock(); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic inline int vcc_writable(struct sock *sk) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct atm_vcc *vcc = atm_sk(sk); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return (vcc->qos.txtp.max_sdu + 10562306a36Sopenharmony_ci refcount_read(&sk->sk_wmem_alloc)) <= sk->sk_sndbuf; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic void vcc_write_space(struct sock *sk) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct socket_wq *wq; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci rcu_read_lock(); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (vcc_writable(sk)) { 11562306a36Sopenharmony_ci wq = rcu_dereference(sk->sk_wq); 11662306a36Sopenharmony_ci if (skwq_has_sleeper(wq)) 11762306a36Sopenharmony_ci wake_up_interruptible(&wq->wait); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT); 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci rcu_read_unlock(); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic void vcc_release_cb(struct sock *sk) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct atm_vcc *vcc = atm_sk(sk); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (vcc->release_cb) 13062306a36Sopenharmony_ci vcc->release_cb(vcc); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic struct proto vcc_proto = { 13462306a36Sopenharmony_ci .name = "VCC", 13562306a36Sopenharmony_ci .owner = THIS_MODULE, 13662306a36Sopenharmony_ci .obj_size = sizeof(struct atm_vcc), 13762306a36Sopenharmony_ci .release_cb = vcc_release_cb, 13862306a36Sopenharmony_ci}; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ciint vcc_create(struct net *net, struct socket *sock, int protocol, int family, int kern) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct sock *sk; 14362306a36Sopenharmony_ci struct atm_vcc *vcc; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci sock->sk = NULL; 14662306a36Sopenharmony_ci if (sock->type == SOCK_STREAM) 14762306a36Sopenharmony_ci return -EINVAL; 14862306a36Sopenharmony_ci sk = sk_alloc(net, family, GFP_KERNEL, &vcc_proto, kern); 14962306a36Sopenharmony_ci if (!sk) 15062306a36Sopenharmony_ci return -ENOMEM; 15162306a36Sopenharmony_ci sock_init_data(sock, sk); 15262306a36Sopenharmony_ci sk->sk_state_change = vcc_def_wakeup; 15362306a36Sopenharmony_ci sk->sk_write_space = vcc_write_space; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci vcc = atm_sk(sk); 15662306a36Sopenharmony_ci vcc->dev = NULL; 15762306a36Sopenharmony_ci memset(&vcc->local, 0, sizeof(struct sockaddr_atmsvc)); 15862306a36Sopenharmony_ci memset(&vcc->remote, 0, sizeof(struct sockaddr_atmsvc)); 15962306a36Sopenharmony_ci vcc->qos.txtp.max_sdu = 1 << 16; /* for meta VCs */ 16062306a36Sopenharmony_ci refcount_set(&sk->sk_wmem_alloc, 1); 16162306a36Sopenharmony_ci atomic_set(&sk->sk_rmem_alloc, 0); 16262306a36Sopenharmony_ci vcc->push = NULL; 16362306a36Sopenharmony_ci vcc->pop = NULL; 16462306a36Sopenharmony_ci vcc->owner = NULL; 16562306a36Sopenharmony_ci vcc->push_oam = NULL; 16662306a36Sopenharmony_ci vcc->release_cb = NULL; 16762306a36Sopenharmony_ci vcc->vpi = vcc->vci = 0; /* no VCI/VPI yet */ 16862306a36Sopenharmony_ci vcc->atm_options = vcc->aal_options = 0; 16962306a36Sopenharmony_ci sk->sk_destruct = vcc_sock_destruct; 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic void vcc_destroy_socket(struct sock *sk) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct atm_vcc *vcc = atm_sk(sk); 17662306a36Sopenharmony_ci struct sk_buff *skb; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci set_bit(ATM_VF_CLOSE, &vcc->flags); 17962306a36Sopenharmony_ci clear_bit(ATM_VF_READY, &vcc->flags); 18062306a36Sopenharmony_ci if (vcc->dev && vcc->dev->ops->close) 18162306a36Sopenharmony_ci vcc->dev->ops->close(vcc); 18262306a36Sopenharmony_ci if (vcc->push) 18362306a36Sopenharmony_ci vcc->push(vcc, NULL); /* atmarpd has no push */ 18462306a36Sopenharmony_ci module_put(vcc->owner); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) { 18762306a36Sopenharmony_ci atm_return(vcc, skb->truesize); 18862306a36Sopenharmony_ci kfree_skb(skb); 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (vcc->dev && vcc->dev->ops->owner) { 19262306a36Sopenharmony_ci module_put(vcc->dev->ops->owner); 19362306a36Sopenharmony_ci atm_dev_put(vcc->dev); 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci vcc_remove_socket(sk); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ciint vcc_release(struct socket *sock) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct sock *sk = sock->sk; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (sk) { 20462306a36Sopenharmony_ci lock_sock(sk); 20562306a36Sopenharmony_ci vcc_destroy_socket(sock->sk); 20662306a36Sopenharmony_ci release_sock(sk); 20762306a36Sopenharmony_ci sock_put(sk); 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return 0; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_civoid vcc_release_async(struct atm_vcc *vcc, int reply) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct sock *sk = sk_atm(vcc); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci set_bit(ATM_VF_CLOSE, &vcc->flags); 21862306a36Sopenharmony_ci sk->sk_shutdown |= RCV_SHUTDOWN; 21962306a36Sopenharmony_ci sk->sk_err = -reply; 22062306a36Sopenharmony_ci clear_bit(ATM_VF_WAITING, &vcc->flags); 22162306a36Sopenharmony_ci sk->sk_state_change(sk); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ciEXPORT_SYMBOL(vcc_release_async); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_civoid vcc_process_recv_queue(struct atm_vcc *vcc) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct sk_buff_head queue, *rq; 22862306a36Sopenharmony_ci struct sk_buff *skb, *tmp; 22962306a36Sopenharmony_ci unsigned long flags; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci __skb_queue_head_init(&queue); 23262306a36Sopenharmony_ci rq = &sk_atm(vcc)->sk_receive_queue; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci spin_lock_irqsave(&rq->lock, flags); 23562306a36Sopenharmony_ci skb_queue_splice_init(rq, &queue); 23662306a36Sopenharmony_ci spin_unlock_irqrestore(&rq->lock, flags); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci skb_queue_walk_safe(&queue, skb, tmp) { 23962306a36Sopenharmony_ci __skb_unlink(skb, &queue); 24062306a36Sopenharmony_ci vcc->push(vcc, skb); 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ciEXPORT_SYMBOL(vcc_process_recv_queue); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_civoid atm_dev_signal_change(struct atm_dev *dev, char signal) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci pr_debug("%s signal=%d dev=%p number=%d dev->signal=%d\n", 24862306a36Sopenharmony_ci __func__, signal, dev, dev->number, dev->signal); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* atm driver sending invalid signal */ 25162306a36Sopenharmony_ci WARN_ON(signal < ATM_PHY_SIG_LOST || signal > ATM_PHY_SIG_FOUND); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (dev->signal == signal) 25462306a36Sopenharmony_ci return; /* no change */ 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci dev->signal = signal; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci atomic_notifier_call_chain(&atm_dev_notify_chain, signal, dev); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ciEXPORT_SYMBOL(atm_dev_signal_change); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_civoid atm_dev_release_vccs(struct atm_dev *dev) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci int i; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci write_lock_irq(&vcc_sklist_lock); 26762306a36Sopenharmony_ci for (i = 0; i < VCC_HTABLE_SIZE; i++) { 26862306a36Sopenharmony_ci struct hlist_head *head = &vcc_hash[i]; 26962306a36Sopenharmony_ci struct hlist_node *tmp; 27062306a36Sopenharmony_ci struct sock *s; 27162306a36Sopenharmony_ci struct atm_vcc *vcc; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci sk_for_each_safe(s, tmp, head) { 27462306a36Sopenharmony_ci vcc = atm_sk(s); 27562306a36Sopenharmony_ci if (vcc->dev == dev) { 27662306a36Sopenharmony_ci vcc_release_async(vcc, -EPIPE); 27762306a36Sopenharmony_ci sk_del_node_init(s); 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci write_unlock_irq(&vcc_sklist_lock); 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ciEXPORT_SYMBOL(atm_dev_release_vccs); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic int adjust_tp(struct atm_trafprm *tp, unsigned char aal) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci int max_sdu; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (!tp->traffic_class) 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci switch (aal) { 29262306a36Sopenharmony_ci case ATM_AAL0: 29362306a36Sopenharmony_ci max_sdu = ATM_CELL_SIZE-1; 29462306a36Sopenharmony_ci break; 29562306a36Sopenharmony_ci case ATM_AAL34: 29662306a36Sopenharmony_ci max_sdu = ATM_MAX_AAL34_PDU; 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci default: 29962306a36Sopenharmony_ci pr_warn("AAL problems ... (%d)\n", aal); 30062306a36Sopenharmony_ci fallthrough; 30162306a36Sopenharmony_ci case ATM_AAL5: 30262306a36Sopenharmony_ci max_sdu = ATM_MAX_AAL5_PDU; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci if (!tp->max_sdu) 30562306a36Sopenharmony_ci tp->max_sdu = max_sdu; 30662306a36Sopenharmony_ci else if (tp->max_sdu > max_sdu) 30762306a36Sopenharmony_ci return -EINVAL; 30862306a36Sopenharmony_ci if (!tp->max_cdv) 30962306a36Sopenharmony_ci tp->max_cdv = ATM_MAX_CDV; 31062306a36Sopenharmony_ci return 0; 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic int check_ci(const struct atm_vcc *vcc, short vpi, int vci) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci struct hlist_head *head = &vcc_hash[vci & (VCC_HTABLE_SIZE - 1)]; 31662306a36Sopenharmony_ci struct sock *s; 31762306a36Sopenharmony_ci struct atm_vcc *walk; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci sk_for_each(s, head) { 32062306a36Sopenharmony_ci walk = atm_sk(s); 32162306a36Sopenharmony_ci if (walk->dev != vcc->dev) 32262306a36Sopenharmony_ci continue; 32362306a36Sopenharmony_ci if (test_bit(ATM_VF_ADDR, &walk->flags) && walk->vpi == vpi && 32462306a36Sopenharmony_ci walk->vci == vci && ((walk->qos.txtp.traffic_class != 32562306a36Sopenharmony_ci ATM_NONE && vcc->qos.txtp.traffic_class != ATM_NONE) || 32662306a36Sopenharmony_ci (walk->qos.rxtp.traffic_class != ATM_NONE && 32762306a36Sopenharmony_ci vcc->qos.rxtp.traffic_class != ATM_NONE))) 32862306a36Sopenharmony_ci return -EADDRINUSE; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* allow VCCs with same VPI/VCI iff they don't collide on 33262306a36Sopenharmony_ci TX/RX (but we may refuse such sharing for other reasons, 33362306a36Sopenharmony_ci e.g. if protocol requires to have both channels) */ 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci return 0; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic int find_ci(const struct atm_vcc *vcc, short *vpi, int *vci) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci static short p; /* poor man's per-device cache */ 34162306a36Sopenharmony_ci static int c; 34262306a36Sopenharmony_ci short old_p; 34362306a36Sopenharmony_ci int old_c; 34462306a36Sopenharmony_ci int err; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (*vpi != ATM_VPI_ANY && *vci != ATM_VCI_ANY) { 34762306a36Sopenharmony_ci err = check_ci(vcc, *vpi, *vci); 34862306a36Sopenharmony_ci return err; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci /* last scan may have left values out of bounds for current device */ 35162306a36Sopenharmony_ci if (*vpi != ATM_VPI_ANY) 35262306a36Sopenharmony_ci p = *vpi; 35362306a36Sopenharmony_ci else if (p >= 1 << vcc->dev->ci_range.vpi_bits) 35462306a36Sopenharmony_ci p = 0; 35562306a36Sopenharmony_ci if (*vci != ATM_VCI_ANY) 35662306a36Sopenharmony_ci c = *vci; 35762306a36Sopenharmony_ci else if (c < ATM_NOT_RSV_VCI || c >= 1 << vcc->dev->ci_range.vci_bits) 35862306a36Sopenharmony_ci c = ATM_NOT_RSV_VCI; 35962306a36Sopenharmony_ci old_p = p; 36062306a36Sopenharmony_ci old_c = c; 36162306a36Sopenharmony_ci do { 36262306a36Sopenharmony_ci if (!check_ci(vcc, p, c)) { 36362306a36Sopenharmony_ci *vpi = p; 36462306a36Sopenharmony_ci *vci = c; 36562306a36Sopenharmony_ci return 0; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci if (*vci == ATM_VCI_ANY) { 36862306a36Sopenharmony_ci c++; 36962306a36Sopenharmony_ci if (c >= 1 << vcc->dev->ci_range.vci_bits) 37062306a36Sopenharmony_ci c = ATM_NOT_RSV_VCI; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci if ((c == ATM_NOT_RSV_VCI || *vci != ATM_VCI_ANY) && 37362306a36Sopenharmony_ci *vpi == ATM_VPI_ANY) { 37462306a36Sopenharmony_ci p++; 37562306a36Sopenharmony_ci if (p >= 1 << vcc->dev->ci_range.vpi_bits) 37662306a36Sopenharmony_ci p = 0; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci } while (old_p != p || old_c != c); 37962306a36Sopenharmony_ci return -EADDRINUSE; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic int __vcc_connect(struct atm_vcc *vcc, struct atm_dev *dev, short vpi, 38362306a36Sopenharmony_ci int vci) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci struct sock *sk = sk_atm(vcc); 38662306a36Sopenharmony_ci int error; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if ((vpi != ATM_VPI_UNSPEC && vpi != ATM_VPI_ANY && 38962306a36Sopenharmony_ci vpi >> dev->ci_range.vpi_bits) || (vci != ATM_VCI_UNSPEC && 39062306a36Sopenharmony_ci vci != ATM_VCI_ANY && vci >> dev->ci_range.vci_bits)) 39162306a36Sopenharmony_ci return -EINVAL; 39262306a36Sopenharmony_ci if (vci > 0 && vci < ATM_NOT_RSV_VCI && !capable(CAP_NET_BIND_SERVICE)) 39362306a36Sopenharmony_ci return -EPERM; 39462306a36Sopenharmony_ci error = -ENODEV; 39562306a36Sopenharmony_ci if (!try_module_get(dev->ops->owner)) 39662306a36Sopenharmony_ci return error; 39762306a36Sopenharmony_ci vcc->dev = dev; 39862306a36Sopenharmony_ci write_lock_irq(&vcc_sklist_lock); 39962306a36Sopenharmony_ci if (test_bit(ATM_DF_REMOVED, &dev->flags) || 40062306a36Sopenharmony_ci (error = find_ci(vcc, &vpi, &vci))) { 40162306a36Sopenharmony_ci write_unlock_irq(&vcc_sklist_lock); 40262306a36Sopenharmony_ci goto fail_module_put; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci vcc->vpi = vpi; 40562306a36Sopenharmony_ci vcc->vci = vci; 40662306a36Sopenharmony_ci __vcc_insert_socket(sk); 40762306a36Sopenharmony_ci write_unlock_irq(&vcc_sklist_lock); 40862306a36Sopenharmony_ci switch (vcc->qos.aal) { 40962306a36Sopenharmony_ci case ATM_AAL0: 41062306a36Sopenharmony_ci error = atm_init_aal0(vcc); 41162306a36Sopenharmony_ci vcc->stats = &dev->stats.aal0; 41262306a36Sopenharmony_ci break; 41362306a36Sopenharmony_ci case ATM_AAL34: 41462306a36Sopenharmony_ci error = atm_init_aal34(vcc); 41562306a36Sopenharmony_ci vcc->stats = &dev->stats.aal34; 41662306a36Sopenharmony_ci break; 41762306a36Sopenharmony_ci case ATM_NO_AAL: 41862306a36Sopenharmony_ci /* ATM_AAL5 is also used in the "0 for default" case */ 41962306a36Sopenharmony_ci vcc->qos.aal = ATM_AAL5; 42062306a36Sopenharmony_ci fallthrough; 42162306a36Sopenharmony_ci case ATM_AAL5: 42262306a36Sopenharmony_ci error = atm_init_aal5(vcc); 42362306a36Sopenharmony_ci vcc->stats = &dev->stats.aal5; 42462306a36Sopenharmony_ci break; 42562306a36Sopenharmony_ci default: 42662306a36Sopenharmony_ci error = -EPROTOTYPE; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci if (!error) 42962306a36Sopenharmony_ci error = adjust_tp(&vcc->qos.txtp, vcc->qos.aal); 43062306a36Sopenharmony_ci if (!error) 43162306a36Sopenharmony_ci error = adjust_tp(&vcc->qos.rxtp, vcc->qos.aal); 43262306a36Sopenharmony_ci if (error) 43362306a36Sopenharmony_ci goto fail; 43462306a36Sopenharmony_ci pr_debug("VCC %d.%d, AAL %d\n", vpi, vci, vcc->qos.aal); 43562306a36Sopenharmony_ci pr_debug(" TX: %d, PCR %d..%d, SDU %d\n", 43662306a36Sopenharmony_ci vcc->qos.txtp.traffic_class, 43762306a36Sopenharmony_ci vcc->qos.txtp.min_pcr, 43862306a36Sopenharmony_ci vcc->qos.txtp.max_pcr, 43962306a36Sopenharmony_ci vcc->qos.txtp.max_sdu); 44062306a36Sopenharmony_ci pr_debug(" RX: %d, PCR %d..%d, SDU %d\n", 44162306a36Sopenharmony_ci vcc->qos.rxtp.traffic_class, 44262306a36Sopenharmony_ci vcc->qos.rxtp.min_pcr, 44362306a36Sopenharmony_ci vcc->qos.rxtp.max_pcr, 44462306a36Sopenharmony_ci vcc->qos.rxtp.max_sdu); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (dev->ops->open) { 44762306a36Sopenharmony_ci error = dev->ops->open(vcc); 44862306a36Sopenharmony_ci if (error) 44962306a36Sopenharmony_ci goto fail; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci return 0; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cifail: 45462306a36Sopenharmony_ci vcc_remove_socket(sk); 45562306a36Sopenharmony_cifail_module_put: 45662306a36Sopenharmony_ci module_put(dev->ops->owner); 45762306a36Sopenharmony_ci /* ensure we get dev module ref count correct */ 45862306a36Sopenharmony_ci vcc->dev = NULL; 45962306a36Sopenharmony_ci return error; 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ciint vcc_connect(struct socket *sock, int itf, short vpi, int vci) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci struct atm_dev *dev; 46562306a36Sopenharmony_ci struct atm_vcc *vcc = ATM_SD(sock); 46662306a36Sopenharmony_ci int error; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci pr_debug("(vpi %d, vci %d)\n", vpi, vci); 46962306a36Sopenharmony_ci if (sock->state == SS_CONNECTED) 47062306a36Sopenharmony_ci return -EISCONN; 47162306a36Sopenharmony_ci if (sock->state != SS_UNCONNECTED) 47262306a36Sopenharmony_ci return -EINVAL; 47362306a36Sopenharmony_ci if (!(vpi || vci)) 47462306a36Sopenharmony_ci return -EINVAL; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci if (vpi != ATM_VPI_UNSPEC && vci != ATM_VCI_UNSPEC) 47762306a36Sopenharmony_ci clear_bit(ATM_VF_PARTIAL, &vcc->flags); 47862306a36Sopenharmony_ci else 47962306a36Sopenharmony_ci if (test_bit(ATM_VF_PARTIAL, &vcc->flags)) 48062306a36Sopenharmony_ci return -EINVAL; 48162306a36Sopenharmony_ci pr_debug("(TX: cl %d,bw %d-%d,sdu %d; " 48262306a36Sopenharmony_ci "RX: cl %d,bw %d-%d,sdu %d,AAL %s%d)\n", 48362306a36Sopenharmony_ci vcc->qos.txtp.traffic_class, vcc->qos.txtp.min_pcr, 48462306a36Sopenharmony_ci vcc->qos.txtp.max_pcr, vcc->qos.txtp.max_sdu, 48562306a36Sopenharmony_ci vcc->qos.rxtp.traffic_class, vcc->qos.rxtp.min_pcr, 48662306a36Sopenharmony_ci vcc->qos.rxtp.max_pcr, vcc->qos.rxtp.max_sdu, 48762306a36Sopenharmony_ci vcc->qos.aal == ATM_AAL5 ? "" : 48862306a36Sopenharmony_ci vcc->qos.aal == ATM_AAL0 ? "" : " ??? code ", 48962306a36Sopenharmony_ci vcc->qos.aal == ATM_AAL0 ? 0 : vcc->qos.aal); 49062306a36Sopenharmony_ci if (!test_bit(ATM_VF_HASQOS, &vcc->flags)) 49162306a36Sopenharmony_ci return -EBADFD; 49262306a36Sopenharmony_ci if (vcc->qos.txtp.traffic_class == ATM_ANYCLASS || 49362306a36Sopenharmony_ci vcc->qos.rxtp.traffic_class == ATM_ANYCLASS) 49462306a36Sopenharmony_ci return -EINVAL; 49562306a36Sopenharmony_ci if (likely(itf != ATM_ITF_ANY)) { 49662306a36Sopenharmony_ci dev = try_then_request_module(atm_dev_lookup(itf), 49762306a36Sopenharmony_ci "atm-device-%d", itf); 49862306a36Sopenharmony_ci } else { 49962306a36Sopenharmony_ci dev = NULL; 50062306a36Sopenharmony_ci mutex_lock(&atm_dev_mutex); 50162306a36Sopenharmony_ci if (!list_empty(&atm_devs)) { 50262306a36Sopenharmony_ci dev = list_entry(atm_devs.next, 50362306a36Sopenharmony_ci struct atm_dev, dev_list); 50462306a36Sopenharmony_ci atm_dev_hold(dev); 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci mutex_unlock(&atm_dev_mutex); 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci if (!dev) 50962306a36Sopenharmony_ci return -ENODEV; 51062306a36Sopenharmony_ci error = __vcc_connect(vcc, dev, vpi, vci); 51162306a36Sopenharmony_ci if (error) { 51262306a36Sopenharmony_ci atm_dev_put(dev); 51362306a36Sopenharmony_ci return error; 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC) 51662306a36Sopenharmony_ci set_bit(ATM_VF_PARTIAL, &vcc->flags); 51762306a36Sopenharmony_ci if (test_bit(ATM_VF_READY, &ATM_SD(sock)->flags)) 51862306a36Sopenharmony_ci sock->state = SS_CONNECTED; 51962306a36Sopenharmony_ci return 0; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ciint vcc_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, 52362306a36Sopenharmony_ci int flags) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci struct sock *sk = sock->sk; 52662306a36Sopenharmony_ci struct atm_vcc *vcc; 52762306a36Sopenharmony_ci struct sk_buff *skb; 52862306a36Sopenharmony_ci int copied, error = -EINVAL; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (sock->state != SS_CONNECTED) 53162306a36Sopenharmony_ci return -ENOTCONN; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* only handle MSG_DONTWAIT and MSG_PEEK */ 53462306a36Sopenharmony_ci if (flags & ~(MSG_DONTWAIT | MSG_PEEK)) 53562306a36Sopenharmony_ci return -EOPNOTSUPP; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci vcc = ATM_SD(sock); 53862306a36Sopenharmony_ci if (test_bit(ATM_VF_RELEASED, &vcc->flags) || 53962306a36Sopenharmony_ci test_bit(ATM_VF_CLOSE, &vcc->flags) || 54062306a36Sopenharmony_ci !test_bit(ATM_VF_READY, &vcc->flags)) 54162306a36Sopenharmony_ci return 0; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci skb = skb_recv_datagram(sk, flags, &error); 54462306a36Sopenharmony_ci if (!skb) 54562306a36Sopenharmony_ci return error; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci copied = skb->len; 54862306a36Sopenharmony_ci if (copied > size) { 54962306a36Sopenharmony_ci copied = size; 55062306a36Sopenharmony_ci msg->msg_flags |= MSG_TRUNC; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci error = skb_copy_datagram_msg(skb, 0, msg, copied); 55462306a36Sopenharmony_ci if (error) 55562306a36Sopenharmony_ci return error; 55662306a36Sopenharmony_ci sock_recv_cmsgs(msg, sk, skb); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (!(flags & MSG_PEEK)) { 55962306a36Sopenharmony_ci pr_debug("%d -= %d\n", atomic_read(&sk->sk_rmem_alloc), 56062306a36Sopenharmony_ci skb->truesize); 56162306a36Sopenharmony_ci atm_return(vcc, skb->truesize); 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci skb_free_datagram(sk, skb); 56562306a36Sopenharmony_ci return copied; 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ciint vcc_sendmsg(struct socket *sock, struct msghdr *m, size_t size) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci struct sock *sk = sock->sk; 57162306a36Sopenharmony_ci DEFINE_WAIT(wait); 57262306a36Sopenharmony_ci struct atm_vcc *vcc; 57362306a36Sopenharmony_ci struct sk_buff *skb; 57462306a36Sopenharmony_ci int eff, error; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci lock_sock(sk); 57762306a36Sopenharmony_ci if (sock->state != SS_CONNECTED) { 57862306a36Sopenharmony_ci error = -ENOTCONN; 57962306a36Sopenharmony_ci goto out; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci if (m->msg_name) { 58262306a36Sopenharmony_ci error = -EISCONN; 58362306a36Sopenharmony_ci goto out; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci vcc = ATM_SD(sock); 58662306a36Sopenharmony_ci if (test_bit(ATM_VF_RELEASED, &vcc->flags) || 58762306a36Sopenharmony_ci test_bit(ATM_VF_CLOSE, &vcc->flags) || 58862306a36Sopenharmony_ci !test_bit(ATM_VF_READY, &vcc->flags)) { 58962306a36Sopenharmony_ci error = -EPIPE; 59062306a36Sopenharmony_ci send_sig(SIGPIPE, current, 0); 59162306a36Sopenharmony_ci goto out; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci if (!size) { 59462306a36Sopenharmony_ci error = 0; 59562306a36Sopenharmony_ci goto out; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci if (size > vcc->qos.txtp.max_sdu) { 59862306a36Sopenharmony_ci error = -EMSGSIZE; 59962306a36Sopenharmony_ci goto out; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci eff = (size+3) & ~3; /* align to word boundary */ 60362306a36Sopenharmony_ci prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); 60462306a36Sopenharmony_ci error = 0; 60562306a36Sopenharmony_ci while (!vcc_tx_ready(vcc, eff)) { 60662306a36Sopenharmony_ci if (m->msg_flags & MSG_DONTWAIT) { 60762306a36Sopenharmony_ci error = -EAGAIN; 60862306a36Sopenharmony_ci break; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci schedule(); 61162306a36Sopenharmony_ci if (signal_pending(current)) { 61262306a36Sopenharmony_ci error = -ERESTARTSYS; 61362306a36Sopenharmony_ci break; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci if (test_bit(ATM_VF_RELEASED, &vcc->flags) || 61662306a36Sopenharmony_ci test_bit(ATM_VF_CLOSE, &vcc->flags) || 61762306a36Sopenharmony_ci !test_bit(ATM_VF_READY, &vcc->flags)) { 61862306a36Sopenharmony_ci error = -EPIPE; 61962306a36Sopenharmony_ci send_sig(SIGPIPE, current, 0); 62062306a36Sopenharmony_ci break; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci finish_wait(sk_sleep(sk), &wait); 62562306a36Sopenharmony_ci if (error) 62662306a36Sopenharmony_ci goto out; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci skb = alloc_skb(eff, GFP_KERNEL); 62962306a36Sopenharmony_ci if (!skb) { 63062306a36Sopenharmony_ci error = -ENOMEM; 63162306a36Sopenharmony_ci goto out; 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci pr_debug("%d += %d\n", sk_wmem_alloc_get(sk), skb->truesize); 63462306a36Sopenharmony_ci atm_account_tx(vcc, skb); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci skb->dev = NULL; /* for paths shared with net_device interfaces */ 63762306a36Sopenharmony_ci if (!copy_from_iter_full(skb_put(skb, size), size, &m->msg_iter)) { 63862306a36Sopenharmony_ci kfree_skb(skb); 63962306a36Sopenharmony_ci error = -EFAULT; 64062306a36Sopenharmony_ci goto out; 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci if (eff != size) 64362306a36Sopenharmony_ci memset(skb->data + size, 0, eff-size); 64462306a36Sopenharmony_ci error = vcc->dev->ops->send(vcc, skb); 64562306a36Sopenharmony_ci error = error ? error : size; 64662306a36Sopenharmony_ciout: 64762306a36Sopenharmony_ci release_sock(sk); 64862306a36Sopenharmony_ci return error; 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci__poll_t vcc_poll(struct file *file, struct socket *sock, poll_table *wait) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci struct sock *sk = sock->sk; 65462306a36Sopenharmony_ci struct atm_vcc *vcc; 65562306a36Sopenharmony_ci __poll_t mask; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci sock_poll_wait(file, sock, wait); 65862306a36Sopenharmony_ci mask = 0; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci vcc = ATM_SD(sock); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci /* exceptional events */ 66362306a36Sopenharmony_ci if (sk->sk_err) 66462306a36Sopenharmony_ci mask = EPOLLERR; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci if (test_bit(ATM_VF_RELEASED, &vcc->flags) || 66762306a36Sopenharmony_ci test_bit(ATM_VF_CLOSE, &vcc->flags)) 66862306a36Sopenharmony_ci mask |= EPOLLHUP; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci /* readable? */ 67162306a36Sopenharmony_ci if (!skb_queue_empty_lockless(&sk->sk_receive_queue)) 67262306a36Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci /* writable? */ 67562306a36Sopenharmony_ci if (sock->state == SS_CONNECTING && 67662306a36Sopenharmony_ci test_bit(ATM_VF_WAITING, &vcc->flags)) 67762306a36Sopenharmony_ci return mask; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci if (vcc->qos.txtp.traffic_class != ATM_NONE && 68062306a36Sopenharmony_ci vcc_writable(sk)) 68162306a36Sopenharmony_ci mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci return mask; 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cistatic int atm_change_qos(struct atm_vcc *vcc, struct atm_qos *qos) 68762306a36Sopenharmony_ci{ 68862306a36Sopenharmony_ci int error; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci /* 69162306a36Sopenharmony_ci * Don't let the QoS change the already connected AAL type nor the 69262306a36Sopenharmony_ci * traffic class. 69362306a36Sopenharmony_ci */ 69462306a36Sopenharmony_ci if (qos->aal != vcc->qos.aal || 69562306a36Sopenharmony_ci qos->rxtp.traffic_class != vcc->qos.rxtp.traffic_class || 69662306a36Sopenharmony_ci qos->txtp.traffic_class != vcc->qos.txtp.traffic_class) 69762306a36Sopenharmony_ci return -EINVAL; 69862306a36Sopenharmony_ci error = adjust_tp(&qos->txtp, qos->aal); 69962306a36Sopenharmony_ci if (!error) 70062306a36Sopenharmony_ci error = adjust_tp(&qos->rxtp, qos->aal); 70162306a36Sopenharmony_ci if (error) 70262306a36Sopenharmony_ci return error; 70362306a36Sopenharmony_ci if (!vcc->dev->ops->change_qos) 70462306a36Sopenharmony_ci return -EOPNOTSUPP; 70562306a36Sopenharmony_ci if (sk_atm(vcc)->sk_family == AF_ATMPVC) 70662306a36Sopenharmony_ci return vcc->dev->ops->change_qos(vcc, qos, ATM_MF_SET); 70762306a36Sopenharmony_ci return svc_change_qos(vcc, qos); 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_cistatic int check_tp(const struct atm_trafprm *tp) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci /* @@@ Should be merged with adjust_tp */ 71362306a36Sopenharmony_ci if (!tp->traffic_class || tp->traffic_class == ATM_ANYCLASS) 71462306a36Sopenharmony_ci return 0; 71562306a36Sopenharmony_ci if (tp->traffic_class != ATM_UBR && !tp->min_pcr && !tp->pcr && 71662306a36Sopenharmony_ci !tp->max_pcr) 71762306a36Sopenharmony_ci return -EINVAL; 71862306a36Sopenharmony_ci if (tp->min_pcr == ATM_MAX_PCR) 71962306a36Sopenharmony_ci return -EINVAL; 72062306a36Sopenharmony_ci if (tp->min_pcr && tp->max_pcr && tp->max_pcr != ATM_MAX_PCR && 72162306a36Sopenharmony_ci tp->min_pcr > tp->max_pcr) 72262306a36Sopenharmony_ci return -EINVAL; 72362306a36Sopenharmony_ci /* 72462306a36Sopenharmony_ci * We allow pcr to be outside [min_pcr,max_pcr], because later 72562306a36Sopenharmony_ci * adjustment may still push it in the valid range. 72662306a36Sopenharmony_ci */ 72762306a36Sopenharmony_ci return 0; 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_cistatic int check_qos(const struct atm_qos *qos) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci int error; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (!qos->txtp.traffic_class && !qos->rxtp.traffic_class) 73562306a36Sopenharmony_ci return -EINVAL; 73662306a36Sopenharmony_ci if (qos->txtp.traffic_class != qos->rxtp.traffic_class && 73762306a36Sopenharmony_ci qos->txtp.traffic_class && qos->rxtp.traffic_class && 73862306a36Sopenharmony_ci qos->txtp.traffic_class != ATM_ANYCLASS && 73962306a36Sopenharmony_ci qos->rxtp.traffic_class != ATM_ANYCLASS) 74062306a36Sopenharmony_ci return -EINVAL; 74162306a36Sopenharmony_ci error = check_tp(&qos->txtp); 74262306a36Sopenharmony_ci if (error) 74362306a36Sopenharmony_ci return error; 74462306a36Sopenharmony_ci return check_tp(&qos->rxtp); 74562306a36Sopenharmony_ci} 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ciint vcc_setsockopt(struct socket *sock, int level, int optname, 74862306a36Sopenharmony_ci sockptr_t optval, unsigned int optlen) 74962306a36Sopenharmony_ci{ 75062306a36Sopenharmony_ci struct atm_vcc *vcc; 75162306a36Sopenharmony_ci unsigned long value; 75262306a36Sopenharmony_ci int error; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci if (__SO_LEVEL_MATCH(optname, level) && optlen != __SO_SIZE(optname)) 75562306a36Sopenharmony_ci return -EINVAL; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci vcc = ATM_SD(sock); 75862306a36Sopenharmony_ci switch (optname) { 75962306a36Sopenharmony_ci case SO_ATMQOS: 76062306a36Sopenharmony_ci { 76162306a36Sopenharmony_ci struct atm_qos qos; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci if (copy_from_sockptr(&qos, optval, sizeof(qos))) 76462306a36Sopenharmony_ci return -EFAULT; 76562306a36Sopenharmony_ci error = check_qos(&qos); 76662306a36Sopenharmony_ci if (error) 76762306a36Sopenharmony_ci return error; 76862306a36Sopenharmony_ci if (sock->state == SS_CONNECTED) 76962306a36Sopenharmony_ci return atm_change_qos(vcc, &qos); 77062306a36Sopenharmony_ci if (sock->state != SS_UNCONNECTED) 77162306a36Sopenharmony_ci return -EBADFD; 77262306a36Sopenharmony_ci vcc->qos = qos; 77362306a36Sopenharmony_ci set_bit(ATM_VF_HASQOS, &vcc->flags); 77462306a36Sopenharmony_ci return 0; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci case SO_SETCLP: 77762306a36Sopenharmony_ci if (copy_from_sockptr(&value, optval, sizeof(value))) 77862306a36Sopenharmony_ci return -EFAULT; 77962306a36Sopenharmony_ci if (value) 78062306a36Sopenharmony_ci vcc->atm_options |= ATM_ATMOPT_CLP; 78162306a36Sopenharmony_ci else 78262306a36Sopenharmony_ci vcc->atm_options &= ~ATM_ATMOPT_CLP; 78362306a36Sopenharmony_ci return 0; 78462306a36Sopenharmony_ci default: 78562306a36Sopenharmony_ci return -EINVAL; 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci} 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ciint vcc_getsockopt(struct socket *sock, int level, int optname, 79062306a36Sopenharmony_ci char __user *optval, int __user *optlen) 79162306a36Sopenharmony_ci{ 79262306a36Sopenharmony_ci struct atm_vcc *vcc; 79362306a36Sopenharmony_ci int len; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci if (get_user(len, optlen)) 79662306a36Sopenharmony_ci return -EFAULT; 79762306a36Sopenharmony_ci if (__SO_LEVEL_MATCH(optname, level) && len != __SO_SIZE(optname)) 79862306a36Sopenharmony_ci return -EINVAL; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci vcc = ATM_SD(sock); 80162306a36Sopenharmony_ci switch (optname) { 80262306a36Sopenharmony_ci case SO_ATMQOS: 80362306a36Sopenharmony_ci if (!test_bit(ATM_VF_HASQOS, &vcc->flags)) 80462306a36Sopenharmony_ci return -EINVAL; 80562306a36Sopenharmony_ci return copy_to_user(optval, &vcc->qos, sizeof(vcc->qos)) 80662306a36Sopenharmony_ci ? -EFAULT : 0; 80762306a36Sopenharmony_ci case SO_SETCLP: 80862306a36Sopenharmony_ci return put_user(vcc->atm_options & ATM_ATMOPT_CLP ? 1 : 0, 80962306a36Sopenharmony_ci (unsigned long __user *)optval) ? -EFAULT : 0; 81062306a36Sopenharmony_ci case SO_ATMPVC: 81162306a36Sopenharmony_ci { 81262306a36Sopenharmony_ci struct sockaddr_atmpvc pvc; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci if (!vcc->dev || !test_bit(ATM_VF_ADDR, &vcc->flags)) 81562306a36Sopenharmony_ci return -ENOTCONN; 81662306a36Sopenharmony_ci memset(&pvc, 0, sizeof(pvc)); 81762306a36Sopenharmony_ci pvc.sap_family = AF_ATMPVC; 81862306a36Sopenharmony_ci pvc.sap_addr.itf = vcc->dev->number; 81962306a36Sopenharmony_ci pvc.sap_addr.vpi = vcc->vpi; 82062306a36Sopenharmony_ci pvc.sap_addr.vci = vcc->vci; 82162306a36Sopenharmony_ci return copy_to_user(optval, &pvc, sizeof(pvc)) ? -EFAULT : 0; 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci default: 82462306a36Sopenharmony_ci return -EINVAL; 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci} 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ciint register_atmdevice_notifier(struct notifier_block *nb) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci return atomic_notifier_chain_register(&atm_dev_notify_chain, nb); 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(register_atmdevice_notifier); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_civoid unregister_atmdevice_notifier(struct notifier_block *nb) 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci atomic_notifier_chain_unregister(&atm_dev_notify_chain, nb); 83762306a36Sopenharmony_ci} 83862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(unregister_atmdevice_notifier); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_cistatic int __init atm_init(void) 84162306a36Sopenharmony_ci{ 84262306a36Sopenharmony_ci int error; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci error = proto_register(&vcc_proto, 0); 84562306a36Sopenharmony_ci if (error < 0) 84662306a36Sopenharmony_ci goto out; 84762306a36Sopenharmony_ci error = atmpvc_init(); 84862306a36Sopenharmony_ci if (error < 0) { 84962306a36Sopenharmony_ci pr_err("atmpvc_init() failed with %d\n", error); 85062306a36Sopenharmony_ci goto out_unregister_vcc_proto; 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci error = atmsvc_init(); 85362306a36Sopenharmony_ci if (error < 0) { 85462306a36Sopenharmony_ci pr_err("atmsvc_init() failed with %d\n", error); 85562306a36Sopenharmony_ci goto out_atmpvc_exit; 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci error = atm_proc_init(); 85862306a36Sopenharmony_ci if (error < 0) { 85962306a36Sopenharmony_ci pr_err("atm_proc_init() failed with %d\n", error); 86062306a36Sopenharmony_ci goto out_atmsvc_exit; 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci error = atm_sysfs_init(); 86362306a36Sopenharmony_ci if (error < 0) { 86462306a36Sopenharmony_ci pr_err("atm_sysfs_init() failed with %d\n", error); 86562306a36Sopenharmony_ci goto out_atmproc_exit; 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ciout: 86862306a36Sopenharmony_ci return error; 86962306a36Sopenharmony_ciout_atmproc_exit: 87062306a36Sopenharmony_ci atm_proc_exit(); 87162306a36Sopenharmony_ciout_atmsvc_exit: 87262306a36Sopenharmony_ci atmsvc_exit(); 87362306a36Sopenharmony_ciout_atmpvc_exit: 87462306a36Sopenharmony_ci atmsvc_exit(); 87562306a36Sopenharmony_ciout_unregister_vcc_proto: 87662306a36Sopenharmony_ci proto_unregister(&vcc_proto); 87762306a36Sopenharmony_ci goto out; 87862306a36Sopenharmony_ci} 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_cistatic void __exit atm_exit(void) 88162306a36Sopenharmony_ci{ 88262306a36Sopenharmony_ci atm_proc_exit(); 88362306a36Sopenharmony_ci atm_sysfs_exit(); 88462306a36Sopenharmony_ci atmsvc_exit(); 88562306a36Sopenharmony_ci atmpvc_exit(); 88662306a36Sopenharmony_ci proto_unregister(&vcc_proto); 88762306a36Sopenharmony_ci} 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_cisubsys_initcall(atm_init); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_cimodule_exit(atm_exit); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 89462306a36Sopenharmony_ciMODULE_ALIAS_NETPROTO(PF_ATMPVC); 89562306a36Sopenharmony_ciMODULE_ALIAS_NETPROTO(PF_ATMSVC); 896