📄 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 net_proto, struct proto_ops */#include <linux/atm.h> /* ATM stuff */#include <linux/atmdev.h>#include <linux/atmclip.h> /* CLIP_*ENCAP */#include <linux/atmarp.h> /* manifest constants */#include <linux/sonet.h> /* for ioctls */#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 <net/sock.h> /* struct sock */#include <asm/uaccess.h>#include <asm/atomic.h>#include <asm/poll.h>#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)#include <linux/atmlec.h>#include "lec.h"#include "lec_arpc.h"struct atm_lane_ops atm_lane_ops;#endif#ifdef CONFIG_ATM_LANE_MODULEEXPORT_SYMBOL(atm_lane_ops);#endif#if defined(CONFIG_ATM_MPOA) || defined(CONFIG_ATM_MPOA_MODULE)#include <linux/atmmpc.h>#include "mpc.h"struct atm_mpoa_ops atm_mpoa_ops;#endif#ifdef CONFIG_ATM_MPOA_MODULEEXPORT_SYMBOL(atm_mpoa_ops);#ifndef CONFIG_ATM_LANE_MODULEEXPORT_SYMBOL(atm_lane_ops);#endif#endif#if defined(CONFIG_ATM_TCP) || defined(CONFIG_ATM_TCP_MODULE)#include <linux/atm_tcp.h>#ifdef CONFIG_ATM_TCP_MODULEstruct atm_tcp_ops atm_tcp_ops;EXPORT_SYMBOL(atm_tcp_ops);#endif#endif#include "resources.h" /* atm_find_dev */#include "common.h" /* prototypes */#include "protocols.h" /* atm_init_<transport> */#include "addr.h" /* address registry */#ifdef CONFIG_ATM_CLIP#include <net/atmclip.h> /* for clip_create */#endif#include "signaling.h" /* for WAITING and sigd_attach */#if 0#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)#else#define DPRINTK(format,args...)#endifspinlock_t atm_dev_lock = SPIN_LOCK_UNLOCKED;static struct sk_buff *alloc_tx(struct atm_vcc *vcc,unsigned int size){ struct sk_buff *skb; if (atomic_read(&vcc->tx_inuse) && !atm_may_send(vcc,size)) { DPRINTK("Sorry: tx_inuse = %d, size = %d, sndbuf = %d\n", atomic_read(&vcc->tx_inuse),size,vcc->sk->sndbuf); return NULL; } while (!(skb = alloc_skb(size,GFP_KERNEL))) schedule(); DPRINTK("AlTx %d += %d\n",atomic_read(&vcc->tx_inuse),skb->truesize); atomic_add(skb->truesize+ATM_PDU_OVHD,&vcc->tx_inuse); return skb;}int atm_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; if (!(sk = alloc_atm_vcc_sk(family))) return -ENOMEM; vcc = sk->protinfo.af_atm; memset(&vcc->flags,0,sizeof(vcc->flags)); vcc->dev = NULL; vcc->family = sock->ops->family; vcc->alloc_tx = alloc_tx; vcc->callback = 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->tx_inuse,0); atomic_set(&vcc->rx_inuse,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; vcc->timestamp.tv_sec = vcc->timestamp.tv_usec = 0; init_waitqueue_head(&vcc->sleep); skb_queue_head_init(&vcc->recvq); skb_queue_head_init(&vcc->listenq); sk->sleep = &vcc->sleep; sock->sk = sk; return 0;}void atm_release_vcc_sk(struct sock *sk,int free_sk){ struct atm_vcc *vcc; struct sk_buff *skb; vcc = sk->protinfo.af_atm; 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 */ while ((skb = skb_dequeue(&vcc->recvq))) { atm_return(vcc,skb->truesize); if (vcc->dev->ops->free_rx_skb) vcc->dev->ops->free_rx_skb(vcc,skb); else kfree_skb(skb); } spin_lock (&atm_dev_lock); fops_put (vcc->dev->ops); if (atomic_read(&vcc->rx_inuse)) printk(KERN_WARNING "atm_release_vcc: strange ... " "rx_inuse == %d after closing\n", atomic_read(&vcc->rx_inuse)); bind_vcc(vcc,NULL); } else spin_lock (&atm_dev_lock); if (free_sk) free_atm_vcc_sk(sk); spin_unlock (&atm_dev_lock);}int atm_release(struct socket *sock){ if (sock->sk) atm_release_vcc_sk(sock->sk,1); return 0;}void atm_async_release_vcc(struct atm_vcc *vcc,int reply){ set_bit(ATM_VF_CLOSE,&vcc->flags); vcc->reply = reply; wake_up(&vcc->sleep);}EXPORT_SYMBOL(atm_async_release_vcc);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 atm_do_connect_dev(struct atm_vcc *vcc,struct atm_dev *dev,int 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; bind_vcc(vcc,dev); 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) { bind_vcc(vcc,NULL); return error; } 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); fops_get (dev->ops); if (dev->ops->open) { error = dev->ops->open(vcc,vpi,vci); if (error) { fops_put (dev->ops); bind_vcc(vcc,NULL); return error; } } return 0;}static int atm_do_connect(struct atm_vcc *vcc,int itf,int vpi,int vci){ struct atm_dev *dev; int return_val; spin_lock (&atm_dev_lock); dev = atm_find_dev(itf); if (!dev) return_val = -ENODEV; else return_val = atm_do_connect_dev(vcc,dev,vpi,vci); spin_unlock (&atm_dev_lock); return return_val;}int atm_connect_vcc(struct atm_vcc *vcc,int itf,short vpi,int vci){ if (vpi != ATM_VPI_UNSPEC && vci != ATM_VCI_UNSPEC) clear_bit(ATM_VF_PARTIAL,&vcc->flags); else if (test_bit(ATM_VF_PARTIAL,&vcc->flags)) return -EINVAL; printk(KERN_DEBUG "atm_connect (TX: cl %d,bw %d-%d,sdu %d; " "RX: cl %d,bw %d-%d,sdu %d,AAL %s%d)\n", vcc->qos.txtp.traffic_class,vcc->qos.txtp.min_pcr, vcc->qos.txtp.max_pcr,vcc->qos.txtp.max_sdu, vcc->qos.rxtp.traffic_class,vcc->qos.rxtp.min_pcr, vcc->qos.rxtp.max_pcr,vcc->qos.rxtp.max_sdu, vcc->qos.aal == ATM_AAL5 ? "" : vcc->qos.aal == ATM_AAL0 ? "" : " ??? code ",vcc->qos.aal == ATM_AAL0 ? 0 : vcc->qos.aal); if (!test_bit(ATM_VF_HASQOS,&vcc->flags)) return -EBADFD; if (vcc->qos.txtp.traffic_class == ATM_ANYCLASS || vcc->qos.rxtp.traffic_class == ATM_ANYCLASS) return -EINVAL; if (itf != ATM_ITF_ANY) { int error; error = atm_do_connect(vcc,itf,vpi,vci); if (error) return error; } else { struct atm_dev *dev; spin_lock (&atm_dev_lock); for (dev = atm_devs; dev; dev = dev->next) if (!atm_do_connect_dev(vcc,dev,vpi,vci)) break; spin_unlock (&atm_dev_lock); if (!dev) return -ENODEV; } if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC) set_bit(ATM_VF_PARTIAL,&vcc->flags); return 0;}int atm_connect(struct socket *sock,int itf,short vpi,int vci){ int error; DPRINTK("atm_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)) return -EINVAL; error = atm_connect_vcc(ATM_SD(sock),itf,vpi,vci); if (error) return error; if (test_bit(ATM_VF_READY,&ATM_SD(sock)->flags)) sock->state = SS_CONNECTED; return 0;}int atm_recvmsg(struct socket *sock,struct msghdr *m,int total_len, int flags,struct scm_cookie *scm){ DECLARE_WAITQUEUE(wait,current); struct atm_vcc *vcc; struct sk_buff *skb; int eff_len,error; void *buff; int size; if (sock->state != SS_CONNECTED) return -ENOTCONN; if (flags & ~MSG_DONTWAIT) return -EOPNOTSUPP; if (m->msg_iovlen != 1) return -ENOSYS; /* fix this later @@@ */ buff = m->msg_iov->iov_base; size = m->msg_iov->iov_len; vcc = ATM_SD(sock); add_wait_queue(&vcc->sleep,&wait); set_current_state(TASK_INTERRUPTIBLE); error = 1; /* <= 0 is error */ while (!(skb = skb_dequeue(&vcc->recvq))) { if (test_bit(ATM_VF_RELEASED,&vcc->flags) || test_bit(ATM_VF_CLOSE,&vcc->flags)) { error = vcc->reply; break; } if (!test_bit(ATM_VF_READY,&vcc->flags)) { error = 0; break; } if (flags & MSG_DONTWAIT) { error = -EAGAIN; break; } schedule(); set_current_state(TASK_INTERRUPTIBLE); if (signal_pending(current)) { error = -ERESTARTSYS; break; } } set_current_state(TASK_RUNNING); remove_wait_queue(&vcc->sleep,&wait); if (error <= 0) return error; vcc->timestamp = skb->stamp; eff_len = skb->len > size ? size : skb->len; if (vcc->dev->ops->feedback) vcc->dev->ops->feedback(vcc,skb,(unsigned long) skb->data, (unsigned long) buff,eff_len); DPRINTK("RcvM %d -= %d\n",atomic_read(&vcc->rx_inuse),skb->truesize); atm_return(vcc,skb->truesize); if (ATM_SKB(skb)->iovcnt) { /* @@@ hack */ /* iovcnt set, use scatter-gather for receive */ int el, cnt; struct iovec *iov = (struct iovec *)skb->data; unsigned char *p = (unsigned char *)buff; el = eff_len; error = 0; for (cnt = 0; (cnt < ATM_SKB(skb)->iovcnt) && el; cnt++) {/*printk("s-g???: %p -> %p (%d)\n",iov->iov_base,p,iov->iov_len);*/ error = copy_to_user(p,iov->iov_base, (iov->iov_len > el) ? el : iov->iov_len) ? -EFAULT : 0; if (error) break; p += iov->iov_len; el -= (iov->iov_len > el)?el:iov->iov_len; iov++; } if (!vcc->dev->ops->free_rx_skb) kfree_skb(skb); else vcc->dev->ops->free_rx_skb(vcc, skb); return error ? error : eff_len; } error = copy_to_user(buff,skb->data,eff_len) ? -EFAULT : 0; if (!vcc->dev->ops->free_rx_skb) kfree_skb(skb); else vcc->dev->ops->free_rx_skb(vcc, skb); return error ? error : eff_len;}int atm_sendmsg(struct socket *sock,struct msghdr *m,int total_len, struct scm_cookie *scm){ DECLARE_WAITQUEUE(wait,current); struct atm_vcc *vcc; struct sk_buff *skb; int eff,error; const void *buff; int size; if (sock->state != SS_CONNECTED) return -ENOTCONN; if (m->msg_name) return -EISCONN; if (m->msg_iovlen != 1) return -ENOSYS; /* fix this later @@@ */ buff = m->msg_iov->iov_base; size = m->msg_iov->iov_len; vcc = ATM_SD(sock); if (test_bit(ATM_VF_RELEASED,&vcc->flags) || test_bit(ATM_VF_CLOSE,&vcc->flags)) return vcc->reply; if (!test_bit(ATM_VF_READY,&vcc->flags)) return -EPIPE; if (!size) return 0; if (size < 0 || size > vcc->qos.txtp.max_sdu) return -EMSGSIZE; /* verify_area is done by net/socket.c */ eff = (size+3) & ~3; /* align to word boundary */ add_wait_queue(&vcc->sleep,&wait); set_current_state(TASK_INTERRUPTIBLE); error = 0; while (!(skb = vcc->alloc_tx(vcc,eff))) { if (m->msg_flags & MSG_DONTWAIT) { error = -EAGAIN; break; } schedule(); set_current_state(TASK_INTERRUPTIBLE); if (signal_pending(current)) { error = -ERESTARTSYS; break; } if (test_bit(ATM_VF_RELEASED,&vcc->flags) || test_bit(ATM_VF_CLOSE,&vcc->flags)) { error = vcc->reply; break; } if (!test_bit(ATM_VF_READY,&vcc->flags)) { error = -EPIPE; break; } } set_current_state(TASK_RUNNING); remove_wait_queue(&vcc->sleep,&wait); if (error) return error; skb->dev = NULL; /* for paths shared with net_device interfaces */ ATM_SKB(skb)->iovcnt = 0; ATM_SKB(skb)->atm_options = vcc->atm_options; if (copy_from_user(skb_put(skb,size),buff,size)) { kfree_skb(skb); return -EFAULT; } if (eff != size) memset(skb->data+size,0,eff-size); error = vcc->dev->ops->send(vcc,skb); return error ? error : size;}unsigned int atm_poll(struct file *file,struct socket *sock,poll_table *wait){ struct atm_vcc *vcc; unsigned int mask; vcc = ATM_SD(sock); poll_wait(file,&vcc->sleep,wait); mask = 0; if (skb_peek(&vcc->recvq) || skb_peek(&vcc->listenq)) mask |= POLLIN | POLLRDNORM; if (test_bit(ATM_VF_RELEASED,&vcc->flags) || test_bit(ATM_VF_CLOSE,&vcc->flags)) mask |= POLLHUP; if (sock->state != SS_CONNECTING) { if (vcc->qos.txtp.traffic_class != ATM_NONE && vcc->qos.txtp.max_sdu+atomic_read(&vcc->tx_inuse)+ ATM_PDU_OVHD <= vcc->sk->sndbuf) mask |= POLLOUT | POLLWRNORM; } else if (vcc->reply != WAITING) { mask |= POLLOUT | POLLWRNORM; if (vcc->reply) mask |= POLLERR; } return mask;}static void copy_aal_stats(struct k_atm_aal_stats *from, struct atm_aal_stats *to){#define __HANDLE_ITEM(i) to->i = atomic_read(&from->i) __AAL_STAT_ITEMS#undef __HANDLE_ITEM}static void subtract_aal_stats(struct k_atm_aal_stats *from, struct atm_aal_stats *to){#define __HANDLE_ITEM(i) atomic_sub(to->i,&from->i) __AAL_STAT_ITEMS#undef __HANDLE_ITEM}static int fetch_stats(struct atm_dev *dev,struct atm_dev_stats *arg,int zero){ struct atm_dev_stats tmp; int error = 0; copy_aal_stats(&dev->stats.aal0,&tmp.aal0); copy_aal_stats(&dev->stats.aal34,&tmp.aal34); copy_aal_stats(&dev->stats.aal5,&tmp.aal5); if (arg) error = copy_to_user(arg,&tmp,sizeof(tmp)); if (zero && !error) { subtract_aal_stats(&dev->stats.aal0,&tmp.aal0); subtract_aal_stats(&dev->stats.aal34,&tmp.aal34); subtract_aal_stats(&dev->stats.aal5,&tmp.aal5); } return error ? -EFAULT : 0;}int atm_ioctl(struct socket *sock,unsigned int cmd,unsigned long arg){ struct atm_dev *dev; struct atm_vcc *vcc; int *tmp_buf; void *buf; int error,len,size,number, ret_val; ret_val = 0; spin_lock (&atm_dev_lock); vcc = ATM_SD(sock); switch (cmd) { case SIOCOUTQ: if (sock->state != SS_CONNECTED || !test_bit(ATM_VF_READY,&vcc->flags)) { ret_val = -EINVAL; goto done; } ret_val = put_user(vcc->sk->sndbuf- atomic_read(&vcc->tx_inuse)-ATM_PDU_OVHD, (int *) arg) ? -EFAULT : 0; goto done; case SIOCINQ: { struct sk_buff *skb; if (sock->state != SS_CONNECTED) { ret_val = -EINVAL; goto done; } skb = skb_peek(&vcc->recvq); ret_val = put_user(skb ? skb->len : 0,(int *) arg) ? -EFAULT : 0; goto done; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -