📄 common.c
字号:
/* net/atm/common.c - ATM sockets (common part for PVC and SVC) *//* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */#include <linux/config.h>#include <linux/module.h>#include <linux/kmod.h>#include <linux/net.h> /* struct socket, struct proto_ops */#include <linux/atm.h> /* ATM stuff */#include <linux/atmdev.h>#include <linux/socket.h> /* SOL_SOCKET */#include <linux/errno.h> /* error codes */#include <linux/capability.h>#include <linux/mm.h> /* verify_area */#include <linux/sched.h>#include <linux/time.h> /* struct timeval */#include <linux/skbuff.h>#include <linux/bitops.h>#include <linux/init.h>#include <net/sock.h> /* struct sock */#include <asm/uaccess.h>#include <asm/atomic.h>#include <asm/poll.h>#include "resources.h" /* atm_find_dev */#include "common.h" /* prototypes */#include "protocols.h" /* atm_init_<transport> */#include "addr.h" /* address registry */#include "signaling.h" /* for WAITING and sigd_attach */#if 0#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)#else#define DPRINTK(format,args...)#endifstruct hlist_head vcc_hash[VCC_HTABLE_SIZE];rwlock_t vcc_sklist_lock = RW_LOCK_UNLOCKED;void __vcc_insert_socket(struct sock *sk){ struct atm_vcc *vcc = atm_sk(sk); struct hlist_head *head = &vcc_hash[vcc->vci & (VCC_HTABLE_SIZE - 1)]; sk->sk_hashent = vcc->vci & (VCC_HTABLE_SIZE - 1); sk_add_node(sk, head);}void vcc_insert_socket(struct sock *sk){ write_lock_irq(&vcc_sklist_lock); __vcc_insert_socket(sk); write_unlock_irq(&vcc_sklist_lock);}void vcc_remove_socket(struct sock *sk){ write_lock_irq(&vcc_sklist_lock); sk_del_node_init(sk); write_unlock_irq(&vcc_sklist_lock);}static struct sk_buff *alloc_tx(struct atm_vcc *vcc,unsigned int size){ struct sk_buff *skb; if (atomic_read(&vcc->sk->sk_wmem_alloc) && !atm_may_send(vcc, size)) { DPRINTK("Sorry: wmem_alloc = %d, size = %d, sndbuf = %d\n", atomic_read(&vcc->sk->sk_wmem_alloc), size, vcc->sk->sk_sndbuf); return NULL; } while (!(skb = alloc_skb(size,GFP_KERNEL))) schedule(); DPRINTK("AlTx %d += %d\n", atomic_read(&vcc->sk->sk_wmem_alloc), skb->truesize); atomic_add(skb->truesize, &vcc->sk->sk_wmem_alloc); return skb;}EXPORT_SYMBOL(vcc_hash);EXPORT_SYMBOL(vcc_sklist_lock);EXPORT_SYMBOL(vcc_insert_socket);EXPORT_SYMBOL(vcc_remove_socket);static void vcc_sock_destruct(struct sock *sk){ struct atm_vcc *vcc = atm_sk(sk); if (atomic_read(&vcc->sk->sk_rmem_alloc)) printk(KERN_DEBUG "vcc_sock_destruct: rmem leakage (%d bytes) detected.\n", atomic_read(&sk->sk_rmem_alloc)); if (atomic_read(&vcc->sk->sk_wmem_alloc)) printk(KERN_DEBUG "vcc_sock_destruct: wmem leakage (%d bytes) detected.\n", atomic_read(&sk->sk_wmem_alloc)); kfree(sk->sk_protinfo);}static void vcc_def_wakeup(struct sock *sk){ read_lock(&sk->sk_callback_lock); if (sk->sk_sleep && waitqueue_active(sk->sk_sleep)) wake_up(sk->sk_sleep); read_unlock(&sk->sk_callback_lock);}static inline int vcc_writable(struct sock *sk){ struct atm_vcc *vcc = atm_sk(sk); return (vcc->qos.txtp.max_sdu + atomic_read(&sk->sk_wmem_alloc)) <= sk->sk_sndbuf;}static void vcc_write_space(struct sock *sk){ read_lock(&sk->sk_callback_lock); if (vcc_writable(sk)) { if (sk->sk_sleep && waitqueue_active(sk->sk_sleep)) wake_up_interruptible(sk->sk_sleep); sk_wake_async(sk, 2, POLL_OUT); } read_unlock(&sk->sk_callback_lock);} int vcc_create(struct socket *sock, int protocol, int family){ struct sock *sk; struct atm_vcc *vcc; sock->sk = NULL; if (sock->type == SOCK_STREAM) return -EINVAL; sk = sk_alloc(family, GFP_KERNEL, 1, NULL); if (!sk) return -ENOMEM; sock_init_data(sock, sk); sk_set_owner(sk, THIS_MODULE); sk->sk_state_change = vcc_def_wakeup; sk->sk_write_space = vcc_write_space; vcc = sk->sk_protinfo = kmalloc(sizeof(*vcc), GFP_KERNEL); if (!vcc) { sk_free(sk); return -ENOMEM; } memset(vcc, 0, sizeof(*vcc)); vcc->sk = sk; vcc->dev = NULL; memset(&vcc->local,0,sizeof(struct sockaddr_atmsvc)); memset(&vcc->remote,0,sizeof(struct sockaddr_atmsvc)); vcc->qos.txtp.max_sdu = 1 << 16; /* for meta VCs */ atomic_set(&vcc->sk->sk_wmem_alloc, 0); atomic_set(&vcc->sk->sk_rmem_alloc, 0); vcc->push = NULL; vcc->pop = NULL; vcc->push_oam = NULL; vcc->vpi = vcc->vci = 0; /* no VCI/VPI yet */ vcc->atm_options = vcc->aal_options = 0; sk->sk_destruct = vcc_sock_destruct; sock->sk = sk; return 0;}static void vcc_destroy_socket(struct sock *sk){ struct atm_vcc *vcc = atm_sk(sk); struct sk_buff *skb; clear_bit(ATM_VF_READY, &vcc->flags); if (vcc->dev) { if (vcc->dev->ops->close) vcc->dev->ops->close(vcc); if (vcc->push) vcc->push(vcc, NULL); /* atmarpd has no push */ vcc_remove_socket(sk); /* no more receive */ while ((skb = skb_dequeue(&vcc->sk->sk_receive_queue)) != NULL) { atm_return(vcc,skb->truesize); kfree_skb(skb); } module_put(vcc->dev->ops->owner); atm_dev_put(vcc->dev); }}int vcc_release(struct socket *sock){ struct sock *sk = sock->sk; if (sk) { lock_sock(sk); vcc_destroy_socket(sock->sk); release_sock(sk); sock_put(sk); } return 0;}void vcc_release_async(struct atm_vcc *vcc, int reply){ set_bit(ATM_VF_CLOSE, &vcc->flags); vcc->sk->sk_err = -reply; clear_bit(ATM_VF_WAITING, &vcc->flags); vcc->sk->sk_state_change(vcc->sk);}EXPORT_SYMBOL(vcc_release_async);static int adjust_tp(struct atm_trafprm *tp,unsigned char aal){ int max_sdu; if (!tp->traffic_class) return 0; switch (aal) { case ATM_AAL0: max_sdu = ATM_CELL_SIZE-1; break; case ATM_AAL34: max_sdu = ATM_MAX_AAL34_PDU; break; default: printk(KERN_WARNING "ATM: AAL problems ... " "(%d)\n",aal); /* fall through */ case ATM_AAL5: max_sdu = ATM_MAX_AAL5_PDU; } if (!tp->max_sdu) tp->max_sdu = max_sdu; else if (tp->max_sdu > max_sdu) return -EINVAL; if (!tp->max_cdv) tp->max_cdv = ATM_MAX_CDV; return 0;}static int check_ci(struct atm_vcc *vcc, short vpi, int vci){ struct hlist_head *head = &vcc_hash[vci & (VCC_HTABLE_SIZE - 1)]; struct hlist_node *node; struct sock *s; struct atm_vcc *walk; sk_for_each(s, node, head) { walk = atm_sk(s); if (walk->dev != vcc->dev) continue; if (test_bit(ATM_VF_ADDR, &walk->flags) && walk->vpi == vpi && walk->vci == vci && ((walk->qos.txtp.traffic_class != ATM_NONE && vcc->qos.txtp.traffic_class != ATM_NONE) || (walk->qos.rxtp.traffic_class != ATM_NONE && vcc->qos.rxtp.traffic_class != ATM_NONE))) return -EADDRINUSE; } /* allow VCCs with same VPI/VCI iff they don't collide on TX/RX (but we may refuse such sharing for other reasons, e.g. if protocol requires to have both channels) */ return 0;}static int find_ci(struct atm_vcc *vcc, short *vpi, int *vci){ static short p; /* poor man's per-device cache */ static int c; short old_p; int old_c; int err; if (*vpi != ATM_VPI_ANY && *vci != ATM_VCI_ANY) { err = check_ci(vcc, *vpi, *vci); return err; } /* last scan may have left values out of bounds for current device */ if (*vpi != ATM_VPI_ANY) p = *vpi; else if (p >= 1 << vcc->dev->ci_range.vpi_bits) p = 0; if (*vci != ATM_VCI_ANY) c = *vci; else if (c < ATM_NOT_RSV_VCI || c >= 1 << vcc->dev->ci_range.vci_bits) c = ATM_NOT_RSV_VCI; old_p = p; old_c = c; do { if (!check_ci(vcc, p, c)) { *vpi = p; *vci = c; return 0; } if (*vci == ATM_VCI_ANY) { c++; if (c >= 1 << vcc->dev->ci_range.vci_bits) c = ATM_NOT_RSV_VCI; } if ((c == ATM_NOT_RSV_VCI || *vci != ATM_VCI_ANY) && *vpi == ATM_VPI_ANY) { p++; if (p >= 1 << vcc->dev->ci_range.vpi_bits) p = 0; } } while (old_p != p || old_c != c); return -EADDRINUSE;}static int __vcc_connect(struct atm_vcc *vcc, struct atm_dev *dev, short vpi, int vci){ int error; if ((vpi != ATM_VPI_UNSPEC && vpi != ATM_VPI_ANY && vpi >> dev->ci_range.vpi_bits) || (vci != ATM_VCI_UNSPEC && vci != ATM_VCI_ANY && vci >> dev->ci_range.vci_bits)) return -EINVAL; if (vci > 0 && vci < ATM_NOT_RSV_VCI && !capable(CAP_NET_BIND_SERVICE)) return -EPERM; error = 0; if (!try_module_get(dev->ops->owner)) return -ENODEV; vcc->dev = dev; write_lock_irq(&vcc_sklist_lock); if ((error = find_ci(vcc, &vpi, &vci))) { write_unlock_irq(&vcc_sklist_lock); goto fail_module_put; } vcc->vpi = vpi; vcc->vci = vci; __vcc_insert_socket(vcc->sk); write_unlock_irq(&vcc_sklist_lock); switch (vcc->qos.aal) { case ATM_AAL0: error = atm_init_aal0(vcc); vcc->stats = &dev->stats.aal0; break; case ATM_AAL34: error = atm_init_aal34(vcc); vcc->stats = &dev->stats.aal34; break; case ATM_NO_AAL: /* ATM_AAL5 is also used in the "0 for default" case */ vcc->qos.aal = ATM_AAL5; /* fall through */ case ATM_AAL5: error = atm_init_aal5(vcc); vcc->stats = &dev->stats.aal5; break; default: error = -EPROTOTYPE; } if (!error) error = adjust_tp(&vcc->qos.txtp,vcc->qos.aal); if (!error) error = adjust_tp(&vcc->qos.rxtp,vcc->qos.aal); if (error) goto fail; DPRINTK("VCC %d.%d, AAL %d\n",vpi,vci,vcc->qos.aal); DPRINTK(" TX: %d, PCR %d..%d, SDU %d\n",vcc->qos.txtp.traffic_class, vcc->qos.txtp.min_pcr,vcc->qos.txtp.max_pcr,vcc->qos.txtp.max_sdu); DPRINTK(" RX: %d, PCR %d..%d, SDU %d\n",vcc->qos.rxtp.traffic_class, vcc->qos.rxtp.min_pcr,vcc->qos.rxtp.max_pcr,vcc->qos.rxtp.max_sdu); if (dev->ops->open) { if ((error = dev->ops->open(vcc))) goto fail; } return 0;fail: vcc_remove_socket(vcc->sk);fail_module_put: module_put(dev->ops->owner); /* ensure we get dev module ref count correct */ vcc->dev = NULL; return error;}int vcc_connect(struct socket *sock, int itf, short vpi, int vci){ struct atm_dev *dev; struct atm_vcc *vcc = ATM_SD(sock); int error; DPRINTK("vcc_connect (vpi %d, vci %d)\n",vpi,vci); if (sock->state == SS_CONNECTED) return -EISCONN; if (sock->state != SS_UNCONNECTED) return -EINVAL; if (!(vpi || vci))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -