📄 af_iucv.c
字号:
/* * 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 + -