⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 common.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* net/atm/common.c - ATM sockets (common part for PVC and SVC) *//* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */#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>#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 */struct hlist_head vcc_hash[VCC_HTABLE_SIZE];DEFINE_RWLOCK(vcc_sklist_lock);static 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_hash = 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);}static 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;	struct sock *sk = sk_atm(vcc);	if (atomic_read(&sk->sk_wmem_alloc) && !atm_may_send(vcc, size)) {		pr_debug("Sorry: wmem_alloc = %d, size = %d, sndbuf = %d\n",			atomic_read(&sk->sk_wmem_alloc), size,			sk->sk_sndbuf);		return NULL;	}	while (!(skb = alloc_skb(size,GFP_KERNEL))) schedule();	pr_debug("AlTx %d += %d\n", atomic_read(&sk->sk_wmem_alloc),		skb->truesize);	atomic_add(skb->truesize, &sk->sk_wmem_alloc);	return skb;}EXPORT_SYMBOL(vcc_hash);EXPORT_SYMBOL(vcc_sklist_lock);EXPORT_SYMBOL(vcc_insert_socket);static void vcc_sock_destruct(struct sock *sk){	if (atomic_read(&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(&sk->sk_wmem_alloc))		printk(KERN_DEBUG "vcc_sock_destruct: wmem leakage (%d bytes) detected.\n", atomic_read(&sk->sk_wmem_alloc));}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);}static struct proto vcc_proto = {	.name	  = "VCC",	.owner	  = THIS_MODULE,	.obj_size = sizeof(struct atm_vcc),};int vcc_create(struct net *net, 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(net, family, GFP_KERNEL, &vcc_proto);	if (!sk)		return -ENOMEM;	sock_init_data(sock, sk);	sk->sk_state_change = vcc_def_wakeup;	sk->sk_write_space = vcc_write_space;	vcc = atm_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(&sk->sk_wmem_alloc, 0);	atomic_set(&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;	return 0;}static void vcc_destroy_socket(struct sock *sk){	struct atm_vcc *vcc = atm_sk(sk);	struct sk_buff *skb;	set_bit(ATM_VF_CLOSE, &vcc->flags);	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(&sk->sk_receive_queue)) != NULL) {			atm_return(vcc,skb->truesize);			kfree_skb(skb);		}		module_put(vcc->dev->ops->owner);		atm_dev_put(vcc->dev);	}	vcc_remove_socket(sk);}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){	struct sock *sk = sk_atm(vcc);	set_bit(ATM_VF_CLOSE, &vcc->flags);	sk->sk_shutdown |= RCV_SHUTDOWN;	sk->sk_err = -reply;	clear_bit(ATM_VF_WAITING, &vcc->flags);	sk->sk_state_change(sk);}EXPORT_SYMBOL(vcc_release_async);void atm_dev_release_vccs(struct atm_dev *dev){	int i;	write_lock_irq(&vcc_sklist_lock);	for (i = 0; i < VCC_HTABLE_SIZE; i++) {		struct hlist_head *head = &vcc_hash[i];		struct hlist_node *node, *tmp;		struct sock *s;		struct atm_vcc *vcc;		sk_for_each_safe(s, node, tmp, head) {			vcc = atm_sk(s);			if (vcc->dev == dev) {				vcc_release_async(vcc, -EPIPE);				sk_del_node_init(s);			}		}	}	write_unlock_irq(&vcc_sklist_lock);}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){	struct sock *sk = sk_atm(vcc);	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 = -ENODEV;	if (!try_module_get(dev->ops->owner))		return error;	vcc->dev = dev;	write_lock_irq(&vcc_sklist_lock);	if (test_bit(ATM_DF_REMOVED, &dev->flags) ||	    (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(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;	pr_debug("VCC %d.%d, AAL %d\n",vpi,vci,vcc->qos.aal);	pr_debug("  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);	pr_debug("  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(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)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -