📄 svc.c
字号:
/* net/atm/svc.c - ATM SVC sockets *//* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */#include <linux/string.h>#include <linux/net.h> /* struct socket, struct proto_ops */#include <linux/errno.h> /* error codes */#include <linux/kernel.h> /* printk */#include <linux/skbuff.h>#include <linux/wait.h>#include <linux/sched.h> /* jiffies and HZ */#include <linux/fcntl.h> /* O_NONBLOCK */#include <linux/init.h>#include <linux/atm.h> /* ATM stuff */#include <linux/atmsap.h>#include <linux/atmsvc.h>#include <linux/atmdev.h>#include <linux/bitops.h>#include <net/sock.h> /* for sock_no_* */#include <asm/uaccess.h>#include "resources.h"#include "common.h" /* common for PVCs and SVCs */#include "signaling.h"#include "addr.h"static int svc_create(struct net *net, struct socket *sock,int protocol);/* * Note: since all this is still nicely synchronized with the signaling demon, * there's no need to protect sleep loops with clis. If signaling is * moved into the kernel, that would change. */static int svc_shutdown(struct socket *sock,int how){ return 0;}static void svc_disconnect(struct atm_vcc *vcc){ DEFINE_WAIT(wait); struct sk_buff *skb; struct sock *sk = sk_atm(vcc); pr_debug("svc_disconnect %p\n",vcc); if (test_bit(ATM_VF_REGIS,&vcc->flags)) { prepare_to_wait(sk->sk_sleep, &wait, TASK_UNINTERRUPTIBLE); sigd_enq(vcc,as_close,NULL,NULL,NULL); while (!test_bit(ATM_VF_RELEASED,&vcc->flags) && sigd) { schedule(); prepare_to_wait(sk->sk_sleep, &wait, TASK_UNINTERRUPTIBLE); } finish_wait(sk->sk_sleep, &wait); } /* beware - socket is still in use by atmsigd until the last as_indicate has been answered */ while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) { atm_return(vcc, skb->truesize); pr_debug("LISTEN REL\n"); sigd_enq2(NULL,as_reject,vcc,NULL,NULL,&vcc->qos,0); dev_kfree_skb(skb); } clear_bit(ATM_VF_REGIS, &vcc->flags); /* ... may retry later */}static int svc_release(struct socket *sock){ struct sock *sk = sock->sk; struct atm_vcc *vcc; if (sk) { vcc = ATM_SD(sock); pr_debug("svc_release %p\n", vcc); clear_bit(ATM_VF_READY, &vcc->flags); /* VCC pointer is used as a reference, so we must not free it (thereby subjecting it to re-use) before all pending connections are closed */ svc_disconnect(vcc); vcc_release(sock); } return 0;}static int svc_bind(struct socket *sock,struct sockaddr *sockaddr, int sockaddr_len){ DEFINE_WAIT(wait); struct sock *sk = sock->sk; struct sockaddr_atmsvc *addr; struct atm_vcc *vcc; int error; if (sockaddr_len != sizeof(struct sockaddr_atmsvc)) return -EINVAL; lock_sock(sk); if (sock->state == SS_CONNECTED) { error = -EISCONN; goto out; } if (sock->state != SS_UNCONNECTED) { error = -EINVAL; goto out; } vcc = ATM_SD(sock); addr = (struct sockaddr_atmsvc *) sockaddr; if (addr->sas_family != AF_ATMSVC) { error = -EAFNOSUPPORT; goto out; } clear_bit(ATM_VF_BOUND,&vcc->flags); /* failing rebind will kill old binding */ /* @@@ check memory (de)allocation on rebind */ if (!test_bit(ATM_VF_HASQOS,&vcc->flags)) { error = -EBADFD; goto out; } vcc->local = *addr; set_bit(ATM_VF_WAITING, &vcc->flags); prepare_to_wait(sk->sk_sleep, &wait, TASK_UNINTERRUPTIBLE); sigd_enq(vcc,as_bind,NULL,NULL,&vcc->local); while (test_bit(ATM_VF_WAITING, &vcc->flags) && sigd) { schedule(); prepare_to_wait(sk->sk_sleep, &wait, TASK_UNINTERRUPTIBLE); } finish_wait(sk->sk_sleep, &wait); clear_bit(ATM_VF_REGIS,&vcc->flags); /* doesn't count */ if (!sigd) { error = -EUNATCH; goto out; } if (!sk->sk_err) set_bit(ATM_VF_BOUND,&vcc->flags); error = -sk->sk_err;out: release_sock(sk); return error;}static int svc_connect(struct socket *sock,struct sockaddr *sockaddr, int sockaddr_len,int flags){ DEFINE_WAIT(wait); struct sock *sk = sock->sk; struct sockaddr_atmsvc *addr; struct atm_vcc *vcc = ATM_SD(sock); int error; pr_debug("svc_connect %p\n",vcc); lock_sock(sk); if (sockaddr_len != sizeof(struct sockaddr_atmsvc)) { error = -EINVAL; goto out; } switch (sock->state) { default: error = -EINVAL; goto out; case SS_CONNECTED: error = -EISCONN; goto out; case SS_CONNECTING: if (test_bit(ATM_VF_WAITING, &vcc->flags)) { error = -EALREADY; goto out; } sock->state = SS_UNCONNECTED; if (sk->sk_err) { error = -sk->sk_err; goto out; } break; case SS_UNCONNECTED: addr = (struct sockaddr_atmsvc *) sockaddr; if (addr->sas_family != AF_ATMSVC) { error = -EAFNOSUPPORT; goto out; } if (!test_bit(ATM_VF_HASQOS, &vcc->flags)) { error = -EBADFD; goto out; } if (vcc->qos.txtp.traffic_class == ATM_ANYCLASS || vcc->qos.rxtp.traffic_class == ATM_ANYCLASS) { error = -EINVAL; goto out; } if (!vcc->qos.txtp.traffic_class && !vcc->qos.rxtp.traffic_class) { error = -EINVAL; goto out; } vcc->remote = *addr; set_bit(ATM_VF_WAITING, &vcc->flags); prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); sigd_enq(vcc,as_connect,NULL,NULL,&vcc->remote); if (flags & O_NONBLOCK) { finish_wait(sk->sk_sleep, &wait); sock->state = SS_CONNECTING; error = -EINPROGRESS; goto out; } error = 0; while (test_bit(ATM_VF_WAITING, &vcc->flags) && sigd) { schedule(); if (!signal_pending(current)) { prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); continue; } pr_debug("*ABORT*\n"); /* * This is tricky: * Kernel ---close--> Demon * Kernel <--close--- Demon * or * Kernel ---close--> Demon * Kernel <--error--- Demon * or * Kernel ---close--> Demon * Kernel <--okay---- Demon * Kernel <--close--- Demon */ sigd_enq(vcc,as_close,NULL,NULL,NULL); while (test_bit(ATM_VF_WAITING, &vcc->flags) && sigd) { prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); schedule(); } if (!sk->sk_err) while (!test_bit(ATM_VF_RELEASED,&vcc->flags) && sigd) { prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE); schedule(); } clear_bit(ATM_VF_REGIS,&vcc->flags); clear_bit(ATM_VF_RELEASED,&vcc->flags); clear_bit(ATM_VF_CLOSE,&vcc->flags); /* we're gone now but may connect later */ error = -EINTR; break; } finish_wait(sk->sk_sleep, &wait); if (error) goto out; if (!sigd) { error = -EUNATCH; goto out; } if (sk->sk_err) { error = -sk->sk_err; goto out; } }/* * Not supported yet * * #ifndef CONFIG_SINGLE_SIGITF */ vcc->qos.txtp.max_pcr = SELECT_TOP_PCR(vcc->qos.txtp); vcc->qos.txtp.pcr = 0; vcc->qos.txtp.min_pcr = 0;/* * #endif */ if (!(error = vcc_connect(sock, vcc->itf, vcc->vpi, vcc->vci))) sock->state = SS_CONNECTED; else (void) svc_disconnect(vcc);out: release_sock(sk); return error;}static int svc_listen(struct socket *sock,int backlog){ DEFINE_WAIT(wait); struct sock *sk = sock->sk; struct atm_vcc *vcc = ATM_SD(sock); int error; pr_debug("svc_listen %p\n",vcc); lock_sock(sk); /* let server handle listen on unbound sockets */ if (test_bit(ATM_VF_SESSION,&vcc->flags)) { error = -EINVAL; goto out; } vcc_insert_socket(sk); set_bit(ATM_VF_WAITING, &vcc->flags); prepare_to_wait(sk->sk_sleep, &wait, TASK_UNINTERRUPTIBLE); sigd_enq(vcc,as_listen,NULL,NULL,&vcc->local); while (test_bit(ATM_VF_WAITING, &vcc->flags) && sigd) { schedule(); prepare_to_wait(sk->sk_sleep, &wait, TASK_UNINTERRUPTIBLE); } finish_wait(sk->sk_sleep, &wait); if (!sigd) { error = -EUNATCH; goto out; } set_bit(ATM_VF_LISTEN,&vcc->flags); sk->sk_max_ack_backlog = backlog > 0 ? backlog : ATM_BACKLOG_DEFAULT; error = -sk->sk_err;out: release_sock(sk); return error;}static int svc_accept(struct socket *sock,struct socket *newsock,int flags){ struct sock *sk = sock->sk; struct sk_buff *skb; struct atmsvc_msg *msg; struct atm_vcc *old_vcc = ATM_SD(sock); struct atm_vcc *new_vcc; int error; lock_sock(sk); error = svc_create(sk->sk_net, newsock,0); if (error) goto out; new_vcc = ATM_SD(newsock);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -