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

📄 af_iucv.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  linux/net/iucv/af_iucv.c * *  IUCV protocol stack for Linux on zSeries * *  Copyright 2006 IBM Corporation * *  Author(s):	Jennifer Hunt <jenhunt@us.ibm.com> */#include <linux/module.h>#include <linux/types.h>#include <linux/list.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/skbuff.h>#include <linux/init.h>#include <linux/poll.h>#include <net/sock.h>#include <asm/ebcdic.h>#include <asm/cpcmd.h>#include <linux/kmod.h>#include <net/iucv/iucv.h>#include <net/iucv/af_iucv.h>#define CONFIG_IUCV_SOCK_DEBUG 1#define IPRMDATA 0x80#define VERSION "1.0"static char iucv_userid[80];static struct proto_ops iucv_sock_ops;static struct proto iucv_proto = {	.name		= "AF_IUCV",	.owner		= THIS_MODULE,	.obj_size	= sizeof(struct iucv_sock),};static void iucv_sock_kill(struct sock *sk);static void iucv_sock_close(struct sock *sk);/* Call Back functions */static void iucv_callback_rx(struct iucv_path *, struct iucv_message *);static void iucv_callback_txdone(struct iucv_path *, struct iucv_message *);static void iucv_callback_connack(struct iucv_path *, u8 ipuser[16]);static int iucv_callback_connreq(struct iucv_path *, u8 ipvmid[8],				 u8 ipuser[16]);static void iucv_callback_connrej(struct iucv_path *, u8 ipuser[16]);static struct iucv_sock_list iucv_sk_list = {	.lock = RW_LOCK_UNLOCKED,	.autobind_name = ATOMIC_INIT(0)};static struct iucv_handler af_iucv_handler = {	.path_pending	  = iucv_callback_connreq,	.path_complete	  = iucv_callback_connack,	.path_severed	  = iucv_callback_connrej,	.message_pending  = iucv_callback_rx,	.message_complete = iucv_callback_txdone};static inline void high_nmcpy(unsigned char *dst, char *src){       memcpy(dst, src, 8);}static inline void low_nmcpy(unsigned char *dst, char *src){       memcpy(&dst[8], src, 8);}/* Timers */static void iucv_sock_timeout(unsigned long arg){	struct sock *sk = (struct sock *)arg;	bh_lock_sock(sk);	sk->sk_err = ETIMEDOUT;	sk->sk_state_change(sk);	bh_unlock_sock(sk);	iucv_sock_kill(sk);	sock_put(sk);}static void iucv_sock_clear_timer(struct sock *sk){	sk_stop_timer(sk, &sk->sk_timer);}static void iucv_sock_init_timer(struct sock *sk){	init_timer(&sk->sk_timer);	sk->sk_timer.function = iucv_sock_timeout;	sk->sk_timer.data = (unsigned long)sk;}static struct sock *__iucv_get_sock_by_name(char *nm){	struct sock *sk;	struct hlist_node *node;	sk_for_each(sk, node, &iucv_sk_list.head)		if (!memcmp(&iucv_sk(sk)->src_name, nm, 8))			return sk;	return NULL;}static void iucv_sock_destruct(struct sock *sk){	skb_queue_purge(&sk->sk_receive_queue);	skb_queue_purge(&sk->sk_write_queue);}/* Cleanup Listen */static void iucv_sock_cleanup_listen(struct sock *parent){	struct sock *sk;	/* Close non-accepted connections */	while ((sk = iucv_accept_dequeue(parent, NULL))) {		iucv_sock_close(sk);		iucv_sock_kill(sk);	}	parent->sk_state = IUCV_CLOSED;	sock_set_flag(parent, SOCK_ZAPPED);}/* Kill socket */static void iucv_sock_kill(struct sock *sk){	if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket)		return;	iucv_sock_unlink(&iucv_sk_list, sk);	sock_set_flag(sk, SOCK_DEAD);	sock_put(sk);}/* Close an IUCV socket */static void iucv_sock_close(struct sock *sk){	unsigned char user_data[16];	struct iucv_sock *iucv = iucv_sk(sk);	int err;	unsigned long timeo;	iucv_sock_clear_timer(sk);	lock_sock(sk);	switch (sk->sk_state) {	case IUCV_LISTEN:		iucv_sock_cleanup_listen(sk);		break;	case IUCV_CONNECTED:	case IUCV_DISCONN:		err = 0;		sk->sk_state = IUCV_CLOSING;		sk->sk_state_change(sk);		if (!skb_queue_empty(&iucv->send_skb_q)) {			if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)				timeo = sk->sk_lingertime;			else				timeo = IUCV_DISCONN_TIMEOUT;			err = iucv_sock_wait_state(sk, IUCV_CLOSED, 0, timeo);		}		sk->sk_state = IUCV_CLOSED;		sk->sk_state_change(sk);		if (iucv->path) {			low_nmcpy(user_data, iucv->src_name);			high_nmcpy(user_data, iucv->dst_name);			ASCEBC(user_data, sizeof(user_data));			err = iucv_path_sever(iucv->path, user_data);			iucv_path_free(iucv->path);			iucv->path = NULL;		}		sk->sk_err = ECONNRESET;		sk->sk_state_change(sk);		skb_queue_purge(&iucv->send_skb_q);		skb_queue_purge(&iucv->backlog_skb_q);		sock_set_flag(sk, SOCK_ZAPPED);		break;	default:		sock_set_flag(sk, SOCK_ZAPPED);		break;	}	release_sock(sk);	iucv_sock_kill(sk);}static void iucv_sock_init(struct sock *sk, struct sock *parent){	if (parent)		sk->sk_type = parent->sk_type;}static struct sock *iucv_sock_alloc(struct socket *sock, int proto, gfp_t prio){	struct sock *sk;	sk = sk_alloc(&init_net, PF_IUCV, prio, &iucv_proto);	if (!sk)		return NULL;	sock_init_data(sock, sk);	INIT_LIST_HEAD(&iucv_sk(sk)->accept_q);	spin_lock_init(&iucv_sk(sk)->accept_q_lock);	skb_queue_head_init(&iucv_sk(sk)->send_skb_q);	INIT_LIST_HEAD(&iucv_sk(sk)->message_q.list);	spin_lock_init(&iucv_sk(sk)->message_q.lock);	skb_queue_head_init(&iucv_sk(sk)->backlog_skb_q);	iucv_sk(sk)->send_tag = 0;	sk->sk_destruct = iucv_sock_destruct;	sk->sk_sndtimeo = IUCV_CONN_TIMEOUT;	sk->sk_allocation = GFP_DMA;	sock_reset_flag(sk, SOCK_ZAPPED);	sk->sk_protocol = proto;	sk->sk_state	= IUCV_OPEN;	iucv_sock_init_timer(sk);	iucv_sock_link(&iucv_sk_list, sk);	return sk;}/* Create an IUCV socket */static int iucv_sock_create(struct net *net, struct socket *sock, int protocol){	struct sock *sk;	if (sock->type != SOCK_STREAM)		return -ESOCKTNOSUPPORT;	sock->state = SS_UNCONNECTED;	sock->ops = &iucv_sock_ops;	sk = iucv_sock_alloc(sock, protocol, GFP_KERNEL);	if (!sk)		return -ENOMEM;	iucv_sock_init(sk, NULL);	return 0;}void iucv_sock_link(struct iucv_sock_list *l, struct sock *sk){	write_lock_bh(&l->lock);	sk_add_node(sk, &l->head);	write_unlock_bh(&l->lock);}void iucv_sock_unlink(struct iucv_sock_list *l, struct sock *sk){	write_lock_bh(&l->lock);	sk_del_node_init(sk);	write_unlock_bh(&l->lock);}void iucv_accept_enqueue(struct sock *parent, struct sock *sk){	unsigned long flags;	struct iucv_sock *par = iucv_sk(parent);	sock_hold(sk);	spin_lock_irqsave(&par->accept_q_lock, flags);	list_add_tail(&iucv_sk(sk)->accept_q, &par->accept_q);	spin_unlock_irqrestore(&par->accept_q_lock, flags);	iucv_sk(sk)->parent = parent;	parent->sk_ack_backlog++;}void iucv_accept_unlink(struct sock *sk){	unsigned long flags;	struct iucv_sock *par = iucv_sk(iucv_sk(sk)->parent);	spin_lock_irqsave(&par->accept_q_lock, flags);	list_del_init(&iucv_sk(sk)->accept_q);	spin_unlock_irqrestore(&par->accept_q_lock, flags);	iucv_sk(sk)->parent->sk_ack_backlog--;	iucv_sk(sk)->parent = NULL;	sock_put(sk);}struct sock *iucv_accept_dequeue(struct sock *parent, struct socket *newsock){	struct iucv_sock *isk, *n;	struct sock *sk;	list_for_each_entry_safe(isk, n, &iucv_sk(parent)->accept_q, accept_q) {		sk = (struct sock *) isk;		lock_sock(sk);		if (sk->sk_state == IUCV_CLOSED) {			iucv_accept_unlink(sk);			release_sock(sk);			continue;		}		if (sk->sk_state == IUCV_CONNECTED ||		    sk->sk_state == IUCV_SEVERED ||		    !newsock) {			iucv_accept_unlink(sk);			if (newsock)				sock_graft(sk, newsock);			if (sk->sk_state == IUCV_SEVERED)				sk->sk_state = IUCV_DISCONN;			release_sock(sk);			return sk;		}		release_sock(sk);	}	return NULL;}int iucv_sock_wait_state(struct sock *sk, int state, int state2,			 unsigned long timeo){	DECLARE_WAITQUEUE(wait, current);	int err = 0;	add_wait_queue(sk->sk_sleep, &wait);	while (sk->sk_state != state && sk->sk_state != state2) {		set_current_state(TASK_INTERRUPTIBLE);		if (!timeo) {			err = -EAGAIN;			break;		}		if (signal_pending(current)) {			err = sock_intr_errno(timeo);			break;		}		release_sock(sk);		timeo = schedule_timeout(timeo);		lock_sock(sk);		err = sock_error(sk);		if (err)			break;	}	set_current_state(TASK_RUNNING);	remove_wait_queue(sk->sk_sleep, &wait);	return err;}/* Bind an unbound socket */static int iucv_sock_bind(struct socket *sock, struct sockaddr *addr,			  int addr_len){	struct sockaddr_iucv *sa = (struct sockaddr_iucv *) addr;	struct sock *sk = sock->sk;	struct iucv_sock *iucv;	int err;	/* Verify the input sockaddr */	if (!addr || addr->sa_family != AF_IUCV)		return -EINVAL;	lock_sock(sk);	if (sk->sk_state != IUCV_OPEN) {		err = -EBADFD;		goto done;	}	write_lock_bh(&iucv_sk_list.lock);	iucv = iucv_sk(sk);	if (__iucv_get_sock_by_name(sa->siucv_name)) {		err = -EADDRINUSE;		goto done_unlock;	}	if (iucv->path) {		err = 0;		goto done_unlock;	}	/* Bind the socket */	memcpy(iucv->src_name, sa->siucv_name, 8);	/* Copy the user id */	memcpy(iucv->src_user_id, iucv_userid, 8);	sk->sk_state = IUCV_BOUND;	err = 0;done_unlock:	/* Release the socket list lock */	write_unlock_bh(&iucv_sk_list.lock);done:	release_sock(sk);	return err;}/* Automatically bind an unbound socket */static int iucv_sock_autobind(struct sock *sk){	struct iucv_sock *iucv = iucv_sk(sk);	char query_buffer[80];	char name[12];	int err = 0;	/* Set the userid and name */	cpcmd("QUERY USERID", query_buffer, sizeof(query_buffer), &err);	if (unlikely(err))		return -EPROTO;	memcpy(iucv->src_user_id, query_buffer, 8);	write_lock_bh(&iucv_sk_list.lock);	sprintf(name, "%08x", atomic_inc_return(&iucv_sk_list.autobind_name));	while (__iucv_get_sock_by_name(name)) {		sprintf(name, "%08x",			atomic_inc_return(&iucv_sk_list.autobind_name));	}	write_unlock_bh(&iucv_sk_list.lock);	memcpy(&iucv->src_name, name, 8);	return err;}/* Connect an unconnected socket */static int iucv_sock_connect(struct socket *sock, struct sockaddr *addr,			     int alen, int flags){	struct sockaddr_iucv *sa = (struct sockaddr_iucv *) addr;	struct sock *sk = sock->sk;	struct iucv_sock *iucv;	unsigned char user_data[16];	int err;	if (addr->sa_family != AF_IUCV || alen < sizeof(struct sockaddr_iucv))		return -EINVAL;	if (sk->sk_state != IUCV_OPEN && sk->sk_state != IUCV_BOUND)		return -EBADFD;	if (sk->sk_type != SOCK_STREAM)		return -EINVAL;	iucv = iucv_sk(sk);	if (sk->sk_state == IUCV_OPEN) {		err = iucv_sock_autobind(sk);		if (unlikely(err))			return err;	}	lock_sock(sk);	/* Set the destination information */	memcpy(iucv_sk(sk)->dst_user_id, sa->siucv_user_id, 8);	memcpy(iucv_sk(sk)->dst_name, sa->siucv_name, 8);	high_nmcpy(user_data, sa->siucv_name);	low_nmcpy(user_data, iucv_sk(sk)->src_name);	ASCEBC(user_data, sizeof(user_data));	iucv = iucv_sk(sk);	/* Create path. */	iucv->path = iucv_path_alloc(IUCV_QUEUELEN_DEFAULT,				     IPRMDATA, GFP_KERNEL);	err = iucv_path_connect(iucv->path, &af_iucv_handler,				sa->siucv_user_id, NULL, user_data, sk);	if (err) {		iucv_path_free(iucv->path);		iucv->path = NULL;		err = -ECONNREFUSED;		goto done;	}	if (sk->sk_state != IUCV_CONNECTED) {		err = iucv_sock_wait_state(sk, IUCV_CONNECTED, IUCV_DISCONN,				sock_sndtimeo(sk, flags & O_NONBLOCK));	}	if (sk->sk_state == IUCV_DISCONN) {		release_sock(sk);		return -ECONNREFUSED;	}done:	release_sock(sk);	return err;}/* Move a socket into listening state. */static int iucv_sock_listen(struct socket *sock, int backlog){	struct sock *sk = sock->sk;	int err;	lock_sock(sk);	err = -EINVAL;	if (sk->sk_state != IUCV_BOUND || sock->type != SOCK_STREAM)		goto done;	sk->sk_max_ack_backlog = backlog;	sk->sk_ack_backlog = 0;	sk->sk_state = IUCV_LISTEN;	err = 0;done:	release_sock(sk);	return err;}/* Accept a pending connection */static int iucv_sock_accept(struct socket *sock, struct socket *newsock,			    int flags){	DECLARE_WAITQUEUE(wait, current);	struct sock *sk = sock->sk, *nsk;	long timeo;	int err = 0;	lock_sock_nested(sk, SINGLE_DEPTH_NESTING);	if (sk->sk_state != IUCV_LISTEN) {		err = -EBADFD;		goto done;	}	timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);	/* Wait for an incoming connection */	add_wait_queue_exclusive(sk->sk_sleep, &wait);	while (!(nsk = iucv_accept_dequeue(sk, newsock))) {		set_current_state(TASK_INTERRUPTIBLE);		if (!timeo) {			err = -EAGAIN;			break;		}		release_sock(sk);		timeo = schedule_timeout(timeo);		lock_sock_nested(sk, SINGLE_DEPTH_NESTING);		if (sk->sk_state != IUCV_LISTEN) {			err = -EBADFD;			break;		}		if (signal_pending(current)) {			err = sock_intr_errno(timeo);			break;		}	}	set_current_state(TASK_RUNNING);	remove_wait_queue(sk->sk_sleep, &wait);	if (err)		goto done;	newsock->state = SS_CONNECTED;done:	release_sock(sk);	return err;}static int iucv_sock_getname(struct socket *sock, struct sockaddr *addr,			     int *len, int peer){	struct sockaddr_iucv *siucv = (struct sockaddr_iucv *) addr;	struct sock *sk = sock->sk;	addr->sa_family = AF_IUCV;	*len = sizeof(struct sockaddr_iucv);	if (peer) {		memcpy(siucv->siucv_user_id, iucv_sk(sk)->dst_user_id, 8);		memcpy(siucv->siucv_name, &iucv_sk(sk)->dst_name, 8);	} else {		memcpy(siucv->siucv_user_id, iucv_sk(sk)->src_user_id, 8);		memcpy(siucv->siucv_name, iucv_sk(sk)->src_name, 8);	}	memset(&siucv->siucv_port, 0, sizeof(siucv->siucv_port));	memset(&siucv->siucv_addr, 0, sizeof(siucv->siucv_addr));	memset(siucv->siucv_nodeid, 0, sizeof(siucv->siucv_nodeid));	return 0;}static int iucv_sock_sendmsg(struct kiocb *iocb, struct socket *sock,			     struct msghdr *msg, size_t len){	struct sock *sk = sock->sk;

⌨️ 快捷键说明

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