109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2eac3731bSJennifer Hunt /*
3eac3731bSJennifer Hunt * IUCV protocol stack for Linux on zSeries
4eac3731bSJennifer Hunt *
5c23cad92SUrsula Braun * Copyright IBM Corp. 2006, 2009
6eac3731bSJennifer Hunt *
7eac3731bSJennifer Hunt * Author(s): Jennifer Hunt <jenhunt@us.ibm.com>
8c23cad92SUrsula Braun * Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
9c23cad92SUrsula Braun * PM functions:
10c23cad92SUrsula Braun * Ursula Braun <ursula.braun@de.ibm.com>
11eac3731bSJennifer Hunt */
12eac3731bSJennifer Hunt
138f7c502cSUrsula Braun #define KMSG_COMPONENT "af_iucv"
148f7c502cSUrsula Braun #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
158f7c502cSUrsula Braun
16b6459415SJakub Kicinski #include <linux/filter.h>
17eac3731bSJennifer Hunt #include <linux/module.h>
18238965b7SJulian Wiedmann #include <linux/netdevice.h>
19eac3731bSJennifer Hunt #include <linux/types.h>
200d1c7664SJulian Wiedmann #include <linux/limits.h>
21eac3731bSJennifer Hunt #include <linux/list.h>
22eac3731bSJennifer Hunt #include <linux/errno.h>
23eac3731bSJennifer Hunt #include <linux/kernel.h>
24174cd4b1SIngo Molnar #include <linux/sched/signal.h>
25eac3731bSJennifer Hunt #include <linux/slab.h>
26eac3731bSJennifer Hunt #include <linux/skbuff.h>
27eac3731bSJennifer Hunt #include <linux/init.h>
28eac3731bSJennifer Hunt #include <linux/poll.h>
2902f06918SPaul Moore #include <linux/security.h>
30eac3731bSJennifer Hunt #include <net/sock.h>
31eac3731bSJennifer Hunt #include <asm/ebcdic.h>
32eac3731bSJennifer Hunt #include <asm/cpcmd.h>
33eac3731bSJennifer Hunt #include <linux/kmod.h>
34eac3731bSJennifer Hunt
35eac3731bSJennifer Hunt #include <net/iucv/af_iucv.h>
36eac3731bSJennifer Hunt
373881ac44SUrsula Braun #define VERSION "1.2"
38eac3731bSJennifer Hunt
39eac3731bSJennifer Hunt static char iucv_userid[80];
40eac3731bSJennifer Hunt
41eac3731bSJennifer Hunt static struct proto iucv_proto = {
42eac3731bSJennifer Hunt .name = "AF_IUCV",
43eac3731bSJennifer Hunt .owner = THIS_MODULE,
44eac3731bSJennifer Hunt .obj_size = sizeof(struct iucv_sock),
45eac3731bSJennifer Hunt };
46eac3731bSJennifer Hunt
476fcd61f7SFrank Blaschka static struct iucv_interface *pr_iucv;
4887c272c6SJulian Wiedmann static struct iucv_handler af_iucv_handler;
496fcd61f7SFrank Blaschka
50b8942e3bSHendrik Brueckner /* special AF_IUCV IPRM messages */
51b8942e3bSHendrik Brueckner static const u8 iprm_shutdown[8] =
52b8942e3bSHendrik Brueckner {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
53b8942e3bSHendrik Brueckner
54c593642cSPankaj Bharadiya #define TRGCLS_SIZE sizeof_field(struct iucv_message, class)
5544b1e6b5SHendrik Brueckner
560ea920d2SHendrik Brueckner #define __iucv_sock_wait(sk, condition, timeo, ret) \
570ea920d2SHendrik Brueckner do { \
580ea920d2SHendrik Brueckner DEFINE_WAIT(__wait); \
590ea920d2SHendrik Brueckner long __timeo = timeo; \
600ea920d2SHendrik Brueckner ret = 0; \
61aa395145SEric Dumazet prepare_to_wait(sk_sleep(sk), &__wait, TASK_INTERRUPTIBLE); \
62d9973179SHendrik Brueckner while (!(condition)) { \
630ea920d2SHendrik Brueckner if (!__timeo) { \
640ea920d2SHendrik Brueckner ret = -EAGAIN; \
650ea920d2SHendrik Brueckner break; \
660ea920d2SHendrik Brueckner } \
670ea920d2SHendrik Brueckner if (signal_pending(current)) { \
680ea920d2SHendrik Brueckner ret = sock_intr_errno(__timeo); \
690ea920d2SHendrik Brueckner break; \
700ea920d2SHendrik Brueckner } \
710ea920d2SHendrik Brueckner release_sock(sk); \
720ea920d2SHendrik Brueckner __timeo = schedule_timeout(__timeo); \
730ea920d2SHendrik Brueckner lock_sock(sk); \
740ea920d2SHendrik Brueckner ret = sock_error(sk); \
750ea920d2SHendrik Brueckner if (ret) \
760ea920d2SHendrik Brueckner break; \
770ea920d2SHendrik Brueckner } \
78aa395145SEric Dumazet finish_wait(sk_sleep(sk), &__wait); \
790ea920d2SHendrik Brueckner } while (0)
800ea920d2SHendrik Brueckner
810ea920d2SHendrik Brueckner #define iucv_sock_wait(sk, condition, timeo) \
820ea920d2SHendrik Brueckner ({ \
830ea920d2SHendrik Brueckner int __ret = 0; \
840ea920d2SHendrik Brueckner if (!(condition)) \
850ea920d2SHendrik Brueckner __iucv_sock_wait(sk, condition, timeo, __ret); \
860ea920d2SHendrik Brueckner __ret; \
870ea920d2SHendrik Brueckner })
8844b1e6b5SHendrik Brueckner
89e9a36ca5SJulian Wiedmann static struct sock *iucv_accept_dequeue(struct sock *parent,
90e9a36ca5SJulian Wiedmann struct socket *newsock);
9157f20448SHeiko Carstens static void iucv_sock_kill(struct sock *sk);
9257f20448SHeiko Carstens static void iucv_sock_close(struct sock *sk);
9357f20448SHeiko Carstens
9480bc97aaSJulian Wiedmann static void afiucv_hs_callback_txnotify(struct sock *sk, enum iucv_tx_notify);
953881ac44SUrsula Braun
96eac3731bSJennifer Hunt static struct iucv_sock_list iucv_sk_list = {
973db8ce35SRobert P. J. Day .lock = __RW_LOCK_UNLOCKED(iucv_sk_list.lock),
98eac3731bSJennifer Hunt .autobind_name = ATOMIC_INIT(0)
99eac3731bSJennifer Hunt };
100eac3731bSJennifer Hunt
high_nmcpy(unsigned char * dst,char * src)101eac3731bSJennifer Hunt static inline void high_nmcpy(unsigned char *dst, char *src)
102eac3731bSJennifer Hunt {
103eac3731bSJennifer Hunt memcpy(dst, src, 8);
104eac3731bSJennifer Hunt }
105eac3731bSJennifer Hunt
low_nmcpy(unsigned char * dst,char * src)106eac3731bSJennifer Hunt static inline void low_nmcpy(unsigned char *dst, char *src)
107eac3731bSJennifer Hunt {
108eac3731bSJennifer Hunt memcpy(&dst[8], src, 8);
109eac3731bSJennifer Hunt }
110eac3731bSJennifer Hunt
111b8942e3bSHendrik Brueckner /**
112b8942e3bSHendrik Brueckner * iucv_msg_length() - Returns the length of an iucv message.
113b8942e3bSHendrik Brueckner * @msg: Pointer to struct iucv_message, MUST NOT be NULL
114b8942e3bSHendrik Brueckner *
115b8942e3bSHendrik Brueckner * The function returns the length of the specified iucv message @msg of data
116b8942e3bSHendrik Brueckner * stored in a buffer and of data stored in the parameter list (PRMDATA).
117b8942e3bSHendrik Brueckner *
118b8942e3bSHendrik Brueckner * For IUCV_IPRMDATA, AF_IUCV uses the following convention to transport socket
119b8942e3bSHendrik Brueckner * data:
120b8942e3bSHendrik Brueckner * PRMDATA[0..6] socket data (max 7 bytes);
121b8942e3bSHendrik Brueckner * PRMDATA[7] socket data length value (len is 0xff - PRMDATA[7])
122b8942e3bSHendrik Brueckner *
12325985edcSLucas De Marchi * The socket data length is computed by subtracting the socket data length
124b8942e3bSHendrik Brueckner * value from 0xFF.
125b8942e3bSHendrik Brueckner * If the socket data len is greater 7, then PRMDATA can be used for special
126b8942e3bSHendrik Brueckner * notifications (see iucv_sock_shutdown); and further,
127b8942e3bSHendrik Brueckner * if the socket data len is > 7, the function returns 8.
128b8942e3bSHendrik Brueckner *
129b8942e3bSHendrik Brueckner * Use this function to allocate socket buffers to store iucv message data.
130b8942e3bSHendrik Brueckner */
iucv_msg_length(struct iucv_message * msg)131b8942e3bSHendrik Brueckner static inline size_t iucv_msg_length(struct iucv_message *msg)
132b8942e3bSHendrik Brueckner {
133b8942e3bSHendrik Brueckner size_t datalen;
134b8942e3bSHendrik Brueckner
135b8942e3bSHendrik Brueckner if (msg->flags & IUCV_IPRMDATA) {
136b8942e3bSHendrik Brueckner datalen = 0xff - msg->rmmsg[7];
137b8942e3bSHendrik Brueckner return (datalen < 8) ? datalen : 8;
138b8942e3bSHendrik Brueckner }
139b8942e3bSHendrik Brueckner return msg->length;
140b8942e3bSHendrik Brueckner }
141b8942e3bSHendrik Brueckner
1420ea920d2SHendrik Brueckner /**
1430ea920d2SHendrik Brueckner * iucv_sock_in_state() - check for specific states
1440ea920d2SHendrik Brueckner * @sk: sock structure
1450ea920d2SHendrik Brueckner * @state: first iucv sk state
1467c8e1a91SHeiko Carstens * @state2: second iucv sk state
1470ea920d2SHendrik Brueckner *
1480ea920d2SHendrik Brueckner * Returns true if the socket in either in the first or second state.
1490ea920d2SHendrik Brueckner */
iucv_sock_in_state(struct sock * sk,int state,int state2)1500ea920d2SHendrik Brueckner static int iucv_sock_in_state(struct sock *sk, int state, int state2)
1510ea920d2SHendrik Brueckner {
1520ea920d2SHendrik Brueckner return (sk->sk_state == state || sk->sk_state == state2);
1530ea920d2SHendrik Brueckner }
1540ea920d2SHendrik Brueckner
1550ea920d2SHendrik Brueckner /**
1560ea920d2SHendrik Brueckner * iucv_below_msglim() - function to check if messages can be sent
1570ea920d2SHendrik Brueckner * @sk: sock structure
1580ea920d2SHendrik Brueckner *
1590ea920d2SHendrik Brueckner * Returns true if the send queue length is lower than the message limit.
1600ea920d2SHendrik Brueckner * Always returns true if the socket is not connected (no iucv path for
1610ea920d2SHendrik Brueckner * checking the message limit).
1620ea920d2SHendrik Brueckner */
iucv_below_msglim(struct sock * sk)1630ea920d2SHendrik Brueckner static inline int iucv_below_msglim(struct sock *sk)
1640ea920d2SHendrik Brueckner {
1650ea920d2SHendrik Brueckner struct iucv_sock *iucv = iucv_sk(sk);
1660ea920d2SHendrik Brueckner
1670ea920d2SHendrik Brueckner if (sk->sk_state != IUCV_CONNECTED)
1680ea920d2SHendrik Brueckner return 1;
1693881ac44SUrsula Braun if (iucv->transport == AF_IUCV_TRANS_IUCV)
170ef6af7bdSJulian Wiedmann return (atomic_read(&iucv->skbs_in_xmit) < iucv->path->msglim);
1713881ac44SUrsula Braun else
1723881ac44SUrsula Braun return ((atomic_read(&iucv->msg_sent) < iucv->msglimit_peer) &&
1733881ac44SUrsula Braun (atomic_read(&iucv->pendings) <= 0));
1740ea920d2SHendrik Brueckner }
1750ea920d2SHendrik Brueckner
1767c8e1a91SHeiko Carstens /*
1770ea920d2SHendrik Brueckner * iucv_sock_wake_msglim() - Wake up thread waiting on msg limit
1780ea920d2SHendrik Brueckner */
iucv_sock_wake_msglim(struct sock * sk)1790ea920d2SHendrik Brueckner static void iucv_sock_wake_msglim(struct sock *sk)
1800ea920d2SHendrik Brueckner {
18143815482SEric Dumazet struct socket_wq *wq;
18243815482SEric Dumazet
18343815482SEric Dumazet rcu_read_lock();
18443815482SEric Dumazet wq = rcu_dereference(sk->sk_wq);
1851ce0bf50SHerbert Xu if (skwq_has_sleeper(wq))
18643815482SEric Dumazet wake_up_interruptible_all(&wq->wait);
1870ea920d2SHendrik Brueckner sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT);
18843815482SEric Dumazet rcu_read_unlock();
1890ea920d2SHendrik Brueckner }
1900ea920d2SHendrik Brueckner
1917c8e1a91SHeiko Carstens /*
1923881ac44SUrsula Braun * afiucv_hs_send() - send a message through HiperSockets transport
1933881ac44SUrsula Braun */
afiucv_hs_send(struct iucv_message * imsg,struct sock * sock,struct sk_buff * skb,u8 flags)1943881ac44SUrsula Braun static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock,
1953881ac44SUrsula Braun struct sk_buff *skb, u8 flags)
1963881ac44SUrsula Braun {
1973881ac44SUrsula Braun struct iucv_sock *iucv = iucv_sk(sock);
1983881ac44SUrsula Braun struct af_iucv_trans_hdr *phs_hdr;
1993881ac44SUrsula Braun int err, confirm_recv = 0;
2003881ac44SUrsula Braun
201cd11d112SJulian Wiedmann phs_hdr = skb_push(skb, sizeof(*phs_hdr));
202cd11d112SJulian Wiedmann memset(phs_hdr, 0, sizeof(*phs_hdr));
2033881ac44SUrsula Braun skb_reset_network_header(skb);
2043881ac44SUrsula Braun
2053881ac44SUrsula Braun phs_hdr->magic = ETH_P_AF_IUCV;
2063881ac44SUrsula Braun phs_hdr->version = 1;
2073881ac44SUrsula Braun phs_hdr->flags = flags;
2083881ac44SUrsula Braun if (flags == AF_IUCV_FLAG_SYN)
2093881ac44SUrsula Braun phs_hdr->window = iucv->msglimit;
2103881ac44SUrsula Braun else if ((flags == AF_IUCV_FLAG_WIN) || !flags) {
2113881ac44SUrsula Braun confirm_recv = atomic_read(&iucv->msg_recv);
2123881ac44SUrsula Braun phs_hdr->window = confirm_recv;
2133881ac44SUrsula Braun if (confirm_recv)
2143881ac44SUrsula Braun phs_hdr->flags = phs_hdr->flags | AF_IUCV_FLAG_WIN;
2153881ac44SUrsula Braun }
2163881ac44SUrsula Braun memcpy(phs_hdr->destUserID, iucv->dst_user_id, 8);
2173881ac44SUrsula Braun memcpy(phs_hdr->destAppName, iucv->dst_name, 8);
2183881ac44SUrsula Braun memcpy(phs_hdr->srcUserID, iucv->src_user_id, 8);
2193881ac44SUrsula Braun memcpy(phs_hdr->srcAppName, iucv->src_name, 8);
2203881ac44SUrsula Braun ASCEBC(phs_hdr->destUserID, sizeof(phs_hdr->destUserID));
2213881ac44SUrsula Braun ASCEBC(phs_hdr->destAppName, sizeof(phs_hdr->destAppName));
2223881ac44SUrsula Braun ASCEBC(phs_hdr->srcUserID, sizeof(phs_hdr->srcUserID));
2233881ac44SUrsula Braun ASCEBC(phs_hdr->srcAppName, sizeof(phs_hdr->srcAppName));
2243881ac44SUrsula Braun if (imsg)
2253881ac44SUrsula Braun memcpy(&phs_hdr->iucv_hdr, imsg, sizeof(struct iucv_message));
2263881ac44SUrsula Braun
227800c5eb7SUrsula Braun skb->dev = iucv->hs_dev;
228b2f54394SJulian Wiedmann if (!skb->dev) {
229b2f54394SJulian Wiedmann err = -ENODEV;
230b2f54394SJulian Wiedmann goto err_free;
231b2f54394SJulian Wiedmann }
232238965b7SJulian Wiedmann
233238965b7SJulian Wiedmann dev_hard_header(skb, skb->dev, ETH_P_AF_IUCV, NULL, NULL, skb->len);
234238965b7SJulian Wiedmann
235b2f54394SJulian Wiedmann if (!(skb->dev->flags & IFF_UP) || !netif_carrier_ok(skb->dev)) {
236b2f54394SJulian Wiedmann err = -ENETDOWN;
237b2f54394SJulian Wiedmann goto err_free;
238b2f54394SJulian Wiedmann }
2393881ac44SUrsula Braun if (skb->len > skb->dev->mtu) {
240b2f54394SJulian Wiedmann if (sock->sk_type == SOCK_SEQPACKET) {
241b2f54394SJulian Wiedmann err = -EMSGSIZE;
242b2f54394SJulian Wiedmann goto err_free;
243b2f54394SJulian Wiedmann }
2442c3b4456SJulian Wiedmann err = pskb_trim(skb, skb->dev->mtu);
2452c3b4456SJulian Wiedmann if (err)
2462c3b4456SJulian Wiedmann goto err_free;
2473881ac44SUrsula Braun }
2482e56c26bSHans Wippel skb->protocol = cpu_to_be16(ETH_P_AF_IUCV);
249238965b7SJulian Wiedmann
250ef6af7bdSJulian Wiedmann atomic_inc(&iucv->skbs_in_xmit);
2513881ac44SUrsula Braun err = dev_queue_xmit(skb);
252800c5eb7SUrsula Braun if (net_xmit_eval(err)) {
253ef6af7bdSJulian Wiedmann atomic_dec(&iucv->skbs_in_xmit);
2543881ac44SUrsula Braun } else {
2553881ac44SUrsula Braun atomic_sub(confirm_recv, &iucv->msg_recv);
2563881ac44SUrsula Braun WARN_ON(atomic_read(&iucv->msg_recv) < 0);
2573881ac44SUrsula Braun }
258800c5eb7SUrsula Braun return net_xmit_eval(err);
259b2f54394SJulian Wiedmann
260b2f54394SJulian Wiedmann err_free:
261b2f54394SJulian Wiedmann kfree_skb(skb);
262b2f54394SJulian Wiedmann return err;
2633881ac44SUrsula Braun }
2643881ac44SUrsula Braun
__iucv_get_sock_by_name(char * nm)265eac3731bSJennifer Hunt static struct sock *__iucv_get_sock_by_name(char *nm)
266eac3731bSJennifer Hunt {
267eac3731bSJennifer Hunt struct sock *sk;
268eac3731bSJennifer Hunt
269b67bfe0dSSasha Levin sk_for_each(sk, &iucv_sk_list.head)
270eac3731bSJennifer Hunt if (!memcmp(&iucv_sk(sk)->src_name, nm, 8))
271eac3731bSJennifer Hunt return sk;
272eac3731bSJennifer Hunt
273eac3731bSJennifer Hunt return NULL;
274eac3731bSJennifer Hunt }
275eac3731bSJennifer Hunt
iucv_sock_destruct(struct sock * sk)276eac3731bSJennifer Hunt static void iucv_sock_destruct(struct sock *sk)
277eac3731bSJennifer Hunt {
278eac3731bSJennifer Hunt skb_queue_purge(&sk->sk_receive_queue);
27982492a35SUrsula Braun skb_queue_purge(&sk->sk_error_queue);
28082492a35SUrsula Braun
28182492a35SUrsula Braun if (!sock_flag(sk, SOCK_DEAD)) {
28282492a35SUrsula Braun pr_err("Attempt to release alive iucv socket %p\n", sk);
28382492a35SUrsula Braun return;
28482492a35SUrsula Braun }
28582492a35SUrsula Braun
28682492a35SUrsula Braun WARN_ON(atomic_read(&sk->sk_rmem_alloc));
287b2c9c5dfSDavid S. Miller WARN_ON(refcount_read(&sk->sk_wmem_alloc));
28882492a35SUrsula Braun WARN_ON(sk->sk_wmem_queued);
28982492a35SUrsula Braun WARN_ON(sk->sk_forward_alloc);
290eac3731bSJennifer Hunt }
291eac3731bSJennifer Hunt
292eac3731bSJennifer Hunt /* Cleanup Listen */
iucv_sock_cleanup_listen(struct sock * parent)293eac3731bSJennifer Hunt static void iucv_sock_cleanup_listen(struct sock *parent)
294eac3731bSJennifer Hunt {
295eac3731bSJennifer Hunt struct sock *sk;
296eac3731bSJennifer Hunt
297eac3731bSJennifer Hunt /* Close non-accepted connections */
298eac3731bSJennifer Hunt while ((sk = iucv_accept_dequeue(parent, NULL))) {
299eac3731bSJennifer Hunt iucv_sock_close(sk);
300eac3731bSJennifer Hunt iucv_sock_kill(sk);
301eac3731bSJennifer Hunt }
302eac3731bSJennifer Hunt
303eac3731bSJennifer Hunt parent->sk_state = IUCV_CLOSED;
304eac3731bSJennifer Hunt }
305eac3731bSJennifer Hunt
iucv_sock_link(struct iucv_sock_list * l,struct sock * sk)306e9a36ca5SJulian Wiedmann static void iucv_sock_link(struct iucv_sock_list *l, struct sock *sk)
307e9a36ca5SJulian Wiedmann {
308e9a36ca5SJulian Wiedmann write_lock_bh(&l->lock);
309e9a36ca5SJulian Wiedmann sk_add_node(sk, &l->head);
310e9a36ca5SJulian Wiedmann write_unlock_bh(&l->lock);
311e9a36ca5SJulian Wiedmann }
312e9a36ca5SJulian Wiedmann
iucv_sock_unlink(struct iucv_sock_list * l,struct sock * sk)313e9a36ca5SJulian Wiedmann static void iucv_sock_unlink(struct iucv_sock_list *l, struct sock *sk)
314e9a36ca5SJulian Wiedmann {
315e9a36ca5SJulian Wiedmann write_lock_bh(&l->lock);
316e9a36ca5SJulian Wiedmann sk_del_node_init(sk);
317e9a36ca5SJulian Wiedmann write_unlock_bh(&l->lock);
318e9a36ca5SJulian Wiedmann }
319e9a36ca5SJulian Wiedmann
3207514bab0SHendrik Brueckner /* Kill socket (only if zapped and orphaned) */
iucv_sock_kill(struct sock * sk)321eac3731bSJennifer Hunt static void iucv_sock_kill(struct sock *sk)
322eac3731bSJennifer Hunt {
323eac3731bSJennifer Hunt if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket)
324eac3731bSJennifer Hunt return;
325eac3731bSJennifer Hunt
326eac3731bSJennifer Hunt iucv_sock_unlink(&iucv_sk_list, sk);
327eac3731bSJennifer Hunt sock_set_flag(sk, SOCK_DEAD);
328eac3731bSJennifer Hunt sock_put(sk);
329eac3731bSJennifer Hunt }
330eac3731bSJennifer Hunt
3317d316b94SUrsula Braun /* Terminate an IUCV path */
iucv_sever_path(struct sock * sk,int with_user_data)3327d316b94SUrsula Braun static void iucv_sever_path(struct sock *sk, int with_user_data)
3337d316b94SUrsula Braun {
3347d316b94SUrsula Braun unsigned char user_data[16];
3357d316b94SUrsula Braun struct iucv_sock *iucv = iucv_sk(sk);
3367d316b94SUrsula Braun struct iucv_path *path = iucv->path;
3377d316b94SUrsula Braun
33801437282SAlexandra Winter /* Whoever resets the path pointer, must sever and free it. */
33901437282SAlexandra Winter if (xchg(&iucv->path, NULL)) {
3407d316b94SUrsula Braun if (with_user_data) {
3417d316b94SUrsula Braun low_nmcpy(user_data, iucv->src_name);
3427d316b94SUrsula Braun high_nmcpy(user_data, iucv->dst_name);
3437d316b94SUrsula Braun ASCEBC(user_data, sizeof(user_data));
3447d316b94SUrsula Braun pr_iucv->path_sever(path, user_data);
3457d316b94SUrsula Braun } else
3467d316b94SUrsula Braun pr_iucv->path_sever(path, NULL);
3477d316b94SUrsula Braun iucv_path_free(path);
3487d316b94SUrsula Braun }
3497d316b94SUrsula Braun }
3507d316b94SUrsula Braun
3514e0ad322SUrsula Braun /* Send controlling flags through an IUCV socket for HIPER transport */
iucv_send_ctrl(struct sock * sk,u8 flags)3529fbd87d4SUrsula Braun static int iucv_send_ctrl(struct sock *sk, u8 flags)
3539fbd87d4SUrsula Braun {
354238965b7SJulian Wiedmann struct iucv_sock *iucv = iucv_sk(sk);
3559fbd87d4SUrsula Braun int err = 0;
3569fbd87d4SUrsula Braun int blen;
3579fbd87d4SUrsula Braun struct sk_buff *skb;
3584e0ad322SUrsula Braun u8 shutdown = 0;
3599fbd87d4SUrsula Braun
360238965b7SJulian Wiedmann blen = sizeof(struct af_iucv_trans_hdr) +
361238965b7SJulian Wiedmann LL_RESERVED_SPACE(iucv->hs_dev);
3624e0ad322SUrsula Braun if (sk->sk_shutdown & SEND_SHUTDOWN) {
3634e0ad322SUrsula Braun /* controlling flags should be sent anyway */
3644e0ad322SUrsula Braun shutdown = sk->sk_shutdown;
3654e0ad322SUrsula Braun sk->sk_shutdown &= RCV_SHUTDOWN;
3664e0ad322SUrsula Braun }
3679fbd87d4SUrsula Braun skb = sock_alloc_send_skb(sk, blen, 1, &err);
3689fbd87d4SUrsula Braun if (skb) {
3699fbd87d4SUrsula Braun skb_reserve(skb, blen);
3709fbd87d4SUrsula Braun err = afiucv_hs_send(NULL, sk, skb, flags);
3719fbd87d4SUrsula Braun }
3724e0ad322SUrsula Braun if (shutdown)
3734e0ad322SUrsula Braun sk->sk_shutdown = shutdown;
3749fbd87d4SUrsula Braun return err;
3759fbd87d4SUrsula Braun }
3769fbd87d4SUrsula Braun
377eac3731bSJennifer Hunt /* Close an IUCV socket */
iucv_sock_close(struct sock * sk)378eac3731bSJennifer Hunt static void iucv_sock_close(struct sock *sk)
379eac3731bSJennifer Hunt {
380eac3731bSJennifer Hunt struct iucv_sock *iucv = iucv_sk(sk);
381561e0360SJennifer Hunt unsigned long timeo;
382800c5eb7SUrsula Braun int err = 0;
383eac3731bSJennifer Hunt
384eac3731bSJennifer Hunt lock_sock(sk);
385eac3731bSJennifer Hunt
386eac3731bSJennifer Hunt switch (sk->sk_state) {
387eac3731bSJennifer Hunt case IUCV_LISTEN:
388eac3731bSJennifer Hunt iucv_sock_cleanup_listen(sk);
389eac3731bSJennifer Hunt break;
390eac3731bSJennifer Hunt
391eac3731bSJennifer Hunt case IUCV_CONNECTED:
3923881ac44SUrsula Braun if (iucv->transport == AF_IUCV_TRANS_HIPER) {
3939fbd87d4SUrsula Braun err = iucv_send_ctrl(sk, AF_IUCV_FLAG_FIN);
3943881ac44SUrsula Braun sk->sk_state = IUCV_DISCONN;
3953881ac44SUrsula Braun sk->sk_state_change(sk);
3963881ac44SUrsula Braun }
397df561f66SGustavo A. R. Silva fallthrough;
39805bba1edSGustavo A. R. Silva
39905bba1edSGustavo A. R. Silva case IUCV_DISCONN:
400561e0360SJennifer Hunt sk->sk_state = IUCV_CLOSING;
401561e0360SJennifer Hunt sk->sk_state_change(sk);
402561e0360SJennifer Hunt
403ef6af7bdSJulian Wiedmann if (!err && atomic_read(&iucv->skbs_in_xmit) > 0) {
404561e0360SJennifer Hunt if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
405561e0360SJennifer Hunt timeo = sk->sk_lingertime;
406561e0360SJennifer Hunt else
407561e0360SJennifer Hunt timeo = IUCV_DISCONN_TIMEOUT;
4089f6298a6SUrsula Braun iucv_sock_wait(sk,
4090ea920d2SHendrik Brueckner iucv_sock_in_state(sk, IUCV_CLOSED, 0),
4100ea920d2SHendrik Brueckner timeo);
411561e0360SJennifer Hunt }
412df561f66SGustavo A. R. Silva fallthrough;
413561e0360SJennifer Hunt
41405bba1edSGustavo A. R. Silva case IUCV_CLOSING:
415561e0360SJennifer Hunt sk->sk_state = IUCV_CLOSED;
416561e0360SJennifer Hunt sk->sk_state_change(sk);
417561e0360SJennifer Hunt
418eac3731bSJennifer Hunt sk->sk_err = ECONNRESET;
419eac3731bSJennifer Hunt sk->sk_state_change(sk);
420eac3731bSJennifer Hunt
421800c5eb7SUrsula Braun skb_queue_purge(&iucv->send_skb_q);
422561e0360SJennifer Hunt skb_queue_purge(&iucv->backlog_skb_q);
423df561f66SGustavo A. R. Silva fallthrough;
424eac3731bSJennifer Hunt
42505bba1edSGustavo A. R. Silva default:
4267d316b94SUrsula Braun iucv_sever_path(sk, 1);
4273ff50b79SStephen Hemminger }
428eac3731bSJennifer Hunt
429800c5eb7SUrsula Braun if (iucv->hs_dev) {
430800c5eb7SUrsula Braun dev_put(iucv->hs_dev);
431800c5eb7SUrsula Braun iucv->hs_dev = NULL;
432800c5eb7SUrsula Braun sk->sk_bound_dev_if = 0;
433800c5eb7SUrsula Braun }
434800c5eb7SUrsula Braun
4357514bab0SHendrik Brueckner /* mark socket for deletion by iucv_sock_kill() */
4367514bab0SHendrik Brueckner sock_set_flag(sk, SOCK_ZAPPED);
4377514bab0SHendrik Brueckner
438eac3731bSJennifer Hunt release_sock(sk);
439eac3731bSJennifer Hunt }
440eac3731bSJennifer Hunt
iucv_sock_init(struct sock * sk,struct sock * parent)441eac3731bSJennifer Hunt static void iucv_sock_init(struct sock *sk, struct sock *parent)
442eac3731bSJennifer Hunt {
44302f06918SPaul Moore if (parent) {
444eac3731bSJennifer Hunt sk->sk_type = parent->sk_type;
44502f06918SPaul Moore security_sk_clone(parent, sk);
44602f06918SPaul Moore }
447eac3731bSJennifer Hunt }
448eac3731bSJennifer Hunt
iucv_sock_alloc(struct socket * sock,int proto,gfp_t prio,int kern)44911aa9c28SEric W. Biederman static struct sock *iucv_sock_alloc(struct socket *sock, int proto, gfp_t prio, int kern)
450eac3731bSJennifer Hunt {
451eac3731bSJennifer Hunt struct sock *sk;
452493d3971SUrsula Braun struct iucv_sock *iucv;
453eac3731bSJennifer Hunt
45411aa9c28SEric W. Biederman sk = sk_alloc(&init_net, PF_IUCV, prio, &iucv_proto, kern);
455eac3731bSJennifer Hunt if (!sk)
456eac3731bSJennifer Hunt return NULL;
457493d3971SUrsula Braun iucv = iucv_sk(sk);
458eac3731bSJennifer Hunt
459eac3731bSJennifer Hunt sock_init_data(sock, sk);
460493d3971SUrsula Braun INIT_LIST_HEAD(&iucv->accept_q);
461493d3971SUrsula Braun spin_lock_init(&iucv->accept_q_lock);
462493d3971SUrsula Braun skb_queue_head_init(&iucv->send_skb_q);
463493d3971SUrsula Braun INIT_LIST_HEAD(&iucv->message_q.list);
464493d3971SUrsula Braun spin_lock_init(&iucv->message_q.lock);
465493d3971SUrsula Braun skb_queue_head_init(&iucv->backlog_skb_q);
466493d3971SUrsula Braun iucv->send_tag = 0;
4673881ac44SUrsula Braun atomic_set(&iucv->pendings, 0);
468493d3971SUrsula Braun iucv->flags = 0;
4693881ac44SUrsula Braun iucv->msglimit = 0;
470ef6af7bdSJulian Wiedmann atomic_set(&iucv->skbs_in_xmit, 0);
4713881ac44SUrsula Braun atomic_set(&iucv->msg_sent, 0);
4723881ac44SUrsula Braun atomic_set(&iucv->msg_recv, 0);
473493d3971SUrsula Braun iucv->path = NULL;
4743881ac44SUrsula Braun iucv->sk_txnotify = afiucv_hs_callback_txnotify;
475b5d8cf0aSKees Cook memset(&iucv->init, 0, sizeof(iucv->init));
4763881ac44SUrsula Braun if (pr_iucv)
4773881ac44SUrsula Braun iucv->transport = AF_IUCV_TRANS_IUCV;
4783881ac44SUrsula Braun else
4793881ac44SUrsula Braun iucv->transport = AF_IUCV_TRANS_HIPER;
480eac3731bSJennifer Hunt
481eac3731bSJennifer Hunt sk->sk_destruct = iucv_sock_destruct;
482eac3731bSJennifer Hunt sk->sk_sndtimeo = IUCV_CONN_TIMEOUT;
483eac3731bSJennifer Hunt
484eac3731bSJennifer Hunt sock_reset_flag(sk, SOCK_ZAPPED);
485eac3731bSJennifer Hunt
486eac3731bSJennifer Hunt sk->sk_protocol = proto;
487eac3731bSJennifer Hunt sk->sk_state = IUCV_OPEN;
488eac3731bSJennifer Hunt
489eac3731bSJennifer Hunt iucv_sock_link(&iucv_sk_list, sk);
490eac3731bSJennifer Hunt return sk;
491eac3731bSJennifer Hunt }
492eac3731bSJennifer Hunt
iucv_accept_enqueue(struct sock * parent,struct sock * sk)493e9a36ca5SJulian Wiedmann static void iucv_accept_enqueue(struct sock *parent, struct sock *sk)
494eac3731bSJennifer Hunt {
495febca281SUrsula Braun unsigned long flags;
496febca281SUrsula Braun struct iucv_sock *par = iucv_sk(parent);
497febca281SUrsula Braun
498eac3731bSJennifer Hunt sock_hold(sk);
499febca281SUrsula Braun spin_lock_irqsave(&par->accept_q_lock, flags);
500febca281SUrsula Braun list_add_tail(&iucv_sk(sk)->accept_q, &par->accept_q);
501febca281SUrsula Braun spin_unlock_irqrestore(&par->accept_q_lock, flags);
502eac3731bSJennifer Hunt iucv_sk(sk)->parent = parent;
50349f5eba7SHendrik Brueckner sk_acceptq_added(parent);
504eac3731bSJennifer Hunt }
505eac3731bSJennifer Hunt
iucv_accept_unlink(struct sock * sk)506e9a36ca5SJulian Wiedmann static void iucv_accept_unlink(struct sock *sk)
507eac3731bSJennifer Hunt {
508febca281SUrsula Braun unsigned long flags;
509febca281SUrsula Braun struct iucv_sock *par = iucv_sk(iucv_sk(sk)->parent);
510febca281SUrsula Braun
511febca281SUrsula Braun spin_lock_irqsave(&par->accept_q_lock, flags);
512eac3731bSJennifer Hunt list_del_init(&iucv_sk(sk)->accept_q);
513febca281SUrsula Braun spin_unlock_irqrestore(&par->accept_q_lock, flags);
51449f5eba7SHendrik Brueckner sk_acceptq_removed(iucv_sk(sk)->parent);
515eac3731bSJennifer Hunt iucv_sk(sk)->parent = NULL;
516eac3731bSJennifer Hunt sock_put(sk);
517eac3731bSJennifer Hunt }
518eac3731bSJennifer Hunt
iucv_accept_dequeue(struct sock * parent,struct socket * newsock)519e9a36ca5SJulian Wiedmann static struct sock *iucv_accept_dequeue(struct sock *parent,
520e9a36ca5SJulian Wiedmann struct socket *newsock)
521eac3731bSJennifer Hunt {
522eac3731bSJennifer Hunt struct iucv_sock *isk, *n;
523eac3731bSJennifer Hunt struct sock *sk;
524eac3731bSJennifer Hunt
525eac3731bSJennifer Hunt list_for_each_entry_safe(isk, n, &iucv_sk(parent)->accept_q, accept_q) {
526eac3731bSJennifer Hunt sk = (struct sock *) isk;
527eac3731bSJennifer Hunt lock_sock(sk);
528eac3731bSJennifer Hunt
529eac3731bSJennifer Hunt if (sk->sk_state == IUCV_CLOSED) {
530eac3731bSJennifer Hunt iucv_accept_unlink(sk);
531febca281SUrsula Braun release_sock(sk);
532eac3731bSJennifer Hunt continue;
533eac3731bSJennifer Hunt }
534eac3731bSJennifer Hunt
535eac3731bSJennifer Hunt if (sk->sk_state == IUCV_CONNECTED ||
536aac6399cSUrsula Braun sk->sk_state == IUCV_DISCONN ||
537eac3731bSJennifer Hunt !newsock) {
538eac3731bSJennifer Hunt iucv_accept_unlink(sk);
539eac3731bSJennifer Hunt if (newsock)
540eac3731bSJennifer Hunt sock_graft(sk, newsock);
541eac3731bSJennifer Hunt
542eac3731bSJennifer Hunt release_sock(sk);
543eac3731bSJennifer Hunt return sk;
544eac3731bSJennifer Hunt }
545eac3731bSJennifer Hunt
546eac3731bSJennifer Hunt release_sock(sk);
547eac3731bSJennifer Hunt }
548eac3731bSJennifer Hunt return NULL;
549eac3731bSJennifer Hunt }
550eac3731bSJennifer Hunt
__iucv_auto_name(struct iucv_sock * iucv)55153a4b499SPhilipp Hachtmann static void __iucv_auto_name(struct iucv_sock *iucv)
55253a4b499SPhilipp Hachtmann {
55353a4b499SPhilipp Hachtmann char name[12];
55453a4b499SPhilipp Hachtmann
55553a4b499SPhilipp Hachtmann sprintf(name, "%08x", atomic_inc_return(&iucv_sk_list.autobind_name));
55653a4b499SPhilipp Hachtmann while (__iucv_get_sock_by_name(name)) {
55753a4b499SPhilipp Hachtmann sprintf(name, "%08x",
55853a4b499SPhilipp Hachtmann atomic_inc_return(&iucv_sk_list.autobind_name));
55953a4b499SPhilipp Hachtmann }
56053a4b499SPhilipp Hachtmann memcpy(iucv->src_name, name, 8);
56153a4b499SPhilipp Hachtmann }
56253a4b499SPhilipp Hachtmann
563eac3731bSJennifer Hunt /* Bind an unbound socket */
iucv_sock_bind(struct socket * sock,struct sockaddr * addr,int addr_len)564eac3731bSJennifer Hunt static int iucv_sock_bind(struct socket *sock, struct sockaddr *addr,
565eac3731bSJennifer Hunt int addr_len)
566eac3731bSJennifer Hunt {
56797f8841eSJulian Wiedmann DECLARE_SOCKADDR(struct sockaddr_iucv *, sa, addr);
568398999baSJulian Wiedmann char uid[sizeof(sa->siucv_user_id)];
569eac3731bSJennifer Hunt struct sock *sk = sock->sk;
570eac3731bSJennifer Hunt struct iucv_sock *iucv;
5713881ac44SUrsula Braun int err = 0;
5723881ac44SUrsula Braun struct net_device *dev;
573eac3731bSJennifer Hunt
574eac3731bSJennifer Hunt /* Verify the input sockaddr */
575e3c42b61SMateusz Jurczyk if (addr_len < sizeof(struct sockaddr_iucv) ||
576e3c42b61SMateusz Jurczyk addr->sa_family != AF_IUCV)
57752a82e23SUrsula Braun return -EINVAL;
57852a82e23SUrsula Braun
579eac3731bSJennifer Hunt lock_sock(sk);
580eac3731bSJennifer Hunt if (sk->sk_state != IUCV_OPEN) {
581eac3731bSJennifer Hunt err = -EBADFD;
582eac3731bSJennifer Hunt goto done;
583eac3731bSJennifer Hunt }
584eac3731bSJennifer Hunt
585eac3731bSJennifer Hunt write_lock_bh(&iucv_sk_list.lock);
586eac3731bSJennifer Hunt
587eac3731bSJennifer Hunt iucv = iucv_sk(sk);
588eac3731bSJennifer Hunt if (__iucv_get_sock_by_name(sa->siucv_name)) {
589eac3731bSJennifer Hunt err = -EADDRINUSE;
590eac3731bSJennifer Hunt goto done_unlock;
591eac3731bSJennifer Hunt }
5923881ac44SUrsula Braun if (iucv->path)
593eac3731bSJennifer Hunt goto done_unlock;
594eac3731bSJennifer Hunt
595eac3731bSJennifer Hunt /* Bind the socket */
5963881ac44SUrsula Braun if (pr_iucv)
5973881ac44SUrsula Braun if (!memcmp(sa->siucv_user_id, iucv_userid, 8))
5983881ac44SUrsula Braun goto vm_bind; /* VM IUCV transport */
5993881ac44SUrsula Braun
6003881ac44SUrsula Braun /* try hiper transport */
6013881ac44SUrsula Braun memcpy(uid, sa->siucv_user_id, sizeof(uid));
6023881ac44SUrsula Braun ASCEBC(uid, 8);
6033881ac44SUrsula Braun rcu_read_lock();
6043881ac44SUrsula Braun for_each_netdev_rcu(&init_net, dev) {
6053881ac44SUrsula Braun if (!memcmp(dev->perm_addr, uid, 8)) {
6063881ac44SUrsula Braun memcpy(iucv->src_user_id, sa->siucv_user_id, 8);
607bf05d48dSBhaskar Chowdhury /* Check for uninitialized siucv_name */
60853a4b499SPhilipp Hachtmann if (strncmp(sa->siucv_name, " ", 8) == 0)
60953a4b499SPhilipp Hachtmann __iucv_auto_name(iucv);
61053a4b499SPhilipp Hachtmann else
61153a4b499SPhilipp Hachtmann memcpy(iucv->src_name, sa->siucv_name, 8);
612816abbadSUrsula Braun sk->sk_bound_dev_if = dev->ifindex;
613800c5eb7SUrsula Braun iucv->hs_dev = dev;
614800c5eb7SUrsula Braun dev_hold(dev);
6153881ac44SUrsula Braun sk->sk_state = IUCV_BOUND;
6163881ac44SUrsula Braun iucv->transport = AF_IUCV_TRANS_HIPER;
6173881ac44SUrsula Braun if (!iucv->msglimit)
6183881ac44SUrsula Braun iucv->msglimit = IUCV_HIPER_MSGLIM_DEFAULT;
6193881ac44SUrsula Braun rcu_read_unlock();
6203881ac44SUrsula Braun goto done_unlock;
6213881ac44SUrsula Braun }
6223881ac44SUrsula Braun }
6233881ac44SUrsula Braun rcu_read_unlock();
6243881ac44SUrsula Braun vm_bind:
6253881ac44SUrsula Braun if (pr_iucv) {
6263881ac44SUrsula Braun /* use local userid for backward compat */
6273881ac44SUrsula Braun memcpy(iucv->src_name, sa->siucv_name, 8);
628eac3731bSJennifer Hunt memcpy(iucv->src_user_id, iucv_userid, 8);
629eac3731bSJennifer Hunt sk->sk_state = IUCV_BOUND;
6303881ac44SUrsula Braun iucv->transport = AF_IUCV_TRANS_IUCV;
631fdbf6326SJulian Wiedmann sk->sk_allocation |= GFP_DMA;
6323881ac44SUrsula Braun if (!iucv->msglimit)
6333881ac44SUrsula Braun iucv->msglimit = IUCV_QUEUELEN_DEFAULT;
6343881ac44SUrsula Braun goto done_unlock;
6353881ac44SUrsula Braun }
6363881ac44SUrsula Braun /* found no dev to bind */
6373881ac44SUrsula Braun err = -ENODEV;
638eac3731bSJennifer Hunt done_unlock:
639eac3731bSJennifer Hunt /* Release the socket list lock */
640eac3731bSJennifer Hunt write_unlock_bh(&iucv_sk_list.lock);
641eac3731bSJennifer Hunt done:
642eac3731bSJennifer Hunt release_sock(sk);
643eac3731bSJennifer Hunt return err;
644eac3731bSJennifer Hunt }
645eac3731bSJennifer Hunt
646eac3731bSJennifer Hunt /* Automatically bind an unbound socket */
iucv_sock_autobind(struct sock * sk)647eac3731bSJennifer Hunt static int iucv_sock_autobind(struct sock *sk)
648eac3731bSJennifer Hunt {
649eac3731bSJennifer Hunt struct iucv_sock *iucv = iucv_sk(sk);
650eac3731bSJennifer Hunt int err = 0;
651eac3731bSJennifer Hunt
652aac6399cSUrsula Braun if (unlikely(!pr_iucv))
653eac3731bSJennifer Hunt return -EPROTO;
654eac3731bSJennifer Hunt
655aac6399cSUrsula Braun memcpy(iucv->src_user_id, iucv_userid, 8);
656fdbf6326SJulian Wiedmann iucv->transport = AF_IUCV_TRANS_IUCV;
657fdbf6326SJulian Wiedmann sk->sk_allocation |= GFP_DMA;
658eac3731bSJennifer Hunt
659eac3731bSJennifer Hunt write_lock_bh(&iucv_sk_list.lock);
66053a4b499SPhilipp Hachtmann __iucv_auto_name(iucv);
661eac3731bSJennifer Hunt write_unlock_bh(&iucv_sk_list.lock);
662eac3731bSJennifer Hunt
6633881ac44SUrsula Braun if (!iucv->msglimit)
6643881ac44SUrsula Braun iucv->msglimit = IUCV_QUEUELEN_DEFAULT;
6653881ac44SUrsula Braun
666eac3731bSJennifer Hunt return err;
667eac3731bSJennifer Hunt }
668eac3731bSJennifer Hunt
afiucv_path_connect(struct socket * sock,struct sockaddr * addr)6693881ac44SUrsula Braun static int afiucv_path_connect(struct socket *sock, struct sockaddr *addr)
670eac3731bSJennifer Hunt {
67197f8841eSJulian Wiedmann DECLARE_SOCKADDR(struct sockaddr_iucv *, sa, addr);
672eac3731bSJennifer Hunt struct sock *sk = sock->sk;
673493d3971SUrsula Braun struct iucv_sock *iucv = iucv_sk(sk);
674eac3731bSJennifer Hunt unsigned char user_data[16];
675eac3731bSJennifer Hunt int err;
676eac3731bSJennifer Hunt
677eac3731bSJennifer Hunt high_nmcpy(user_data, sa->siucv_name);
678493d3971SUrsula Braun low_nmcpy(user_data, iucv->src_name);
679eac3731bSJennifer Hunt ASCEBC(user_data, sizeof(user_data));
680eac3731bSJennifer Hunt
681eac3731bSJennifer Hunt /* Create path. */
68209488e2eSHendrik Brueckner iucv->path = iucv_path_alloc(iucv->msglimit,
683b8942e3bSHendrik Brueckner IUCV_IPRMDATA, GFP_KERNEL);
684d4444722SUrsula Braun if (!iucv->path) {
685d4444722SUrsula Braun err = -ENOMEM;
686d4444722SUrsula Braun goto done;
687d4444722SUrsula Braun }
6886fcd61f7SFrank Blaschka err = pr_iucv->path_connect(iucv->path, &af_iucv_handler,
6896fcd61f7SFrank Blaschka sa->siucv_user_id, NULL, user_data,
6906fcd61f7SFrank Blaschka sk);
691eac3731bSJennifer Hunt if (err) {
692eac3731bSJennifer Hunt iucv_path_free(iucv->path);
693eac3731bSJennifer Hunt iucv->path = NULL;
69455cdea9eSHendrik Brueckner switch (err) {
69555cdea9eSHendrik Brueckner case 0x0b: /* Target communicator is not logged on */
69655cdea9eSHendrik Brueckner err = -ENETUNREACH;
69755cdea9eSHendrik Brueckner break;
69855cdea9eSHendrik Brueckner case 0x0d: /* Max connections for this guest exceeded */
69955cdea9eSHendrik Brueckner case 0x0e: /* Max connections for target guest exceeded */
70055cdea9eSHendrik Brueckner err = -EAGAIN;
70155cdea9eSHendrik Brueckner break;
70255cdea9eSHendrik Brueckner case 0x0f: /* Missing IUCV authorization */
70355cdea9eSHendrik Brueckner err = -EACCES;
70455cdea9eSHendrik Brueckner break;
70555cdea9eSHendrik Brueckner default:
706eac3731bSJennifer Hunt err = -ECONNREFUSED;
70755cdea9eSHendrik Brueckner break;
70855cdea9eSHendrik Brueckner }
7093881ac44SUrsula Braun }
7103881ac44SUrsula Braun done:
7113881ac44SUrsula Braun return err;
712eac3731bSJennifer Hunt }
713eac3731bSJennifer Hunt
7143881ac44SUrsula Braun /* Connect an unconnected socket */
iucv_sock_connect(struct socket * sock,struct sockaddr * addr,int alen,int flags)7153881ac44SUrsula Braun static int iucv_sock_connect(struct socket *sock, struct sockaddr *addr,
7163881ac44SUrsula Braun int alen, int flags)
7173881ac44SUrsula Braun {
71897f8841eSJulian Wiedmann DECLARE_SOCKADDR(struct sockaddr_iucv *, sa, addr);
7193881ac44SUrsula Braun struct sock *sk = sock->sk;
7203881ac44SUrsula Braun struct iucv_sock *iucv = iucv_sk(sk);
7213881ac44SUrsula Braun int err;
7223881ac44SUrsula Braun
723e3c42b61SMateusz Jurczyk if (alen < sizeof(struct sockaddr_iucv) || addr->sa_family != AF_IUCV)
7243881ac44SUrsula Braun return -EINVAL;
7253881ac44SUrsula Braun
7263881ac44SUrsula Braun if (sk->sk_state != IUCV_OPEN && sk->sk_state != IUCV_BOUND)
7273881ac44SUrsula Braun return -EBADFD;
7283881ac44SUrsula Braun
7293881ac44SUrsula Braun if (sk->sk_state == IUCV_OPEN &&
7303881ac44SUrsula Braun iucv->transport == AF_IUCV_TRANS_HIPER)
7313881ac44SUrsula Braun return -EBADFD; /* explicit bind required */
7323881ac44SUrsula Braun
7333881ac44SUrsula Braun if (sk->sk_type != SOCK_STREAM && sk->sk_type != SOCK_SEQPACKET)
7343881ac44SUrsula Braun return -EINVAL;
7353881ac44SUrsula Braun
7363881ac44SUrsula Braun if (sk->sk_state == IUCV_OPEN) {
7373881ac44SUrsula Braun err = iucv_sock_autobind(sk);
7383881ac44SUrsula Braun if (unlikely(err))
7393881ac44SUrsula Braun return err;
7403881ac44SUrsula Braun }
7413881ac44SUrsula Braun
7423881ac44SUrsula Braun lock_sock(sk);
7433881ac44SUrsula Braun
7443881ac44SUrsula Braun /* Set the destination information */
7453881ac44SUrsula Braun memcpy(iucv->dst_user_id, sa->siucv_user_id, 8);
7463881ac44SUrsula Braun memcpy(iucv->dst_name, sa->siucv_name, 8);
7473881ac44SUrsula Braun
7483881ac44SUrsula Braun if (iucv->transport == AF_IUCV_TRANS_HIPER)
7499fbd87d4SUrsula Braun err = iucv_send_ctrl(sock->sk, AF_IUCV_FLAG_SYN);
7503881ac44SUrsula Braun else
7513881ac44SUrsula Braun err = afiucv_path_connect(sock, addr);
7523881ac44SUrsula Braun if (err)
7533881ac44SUrsula Braun goto done;
7543881ac44SUrsula Braun
7553881ac44SUrsula Braun if (sk->sk_state != IUCV_CONNECTED)
7560ea920d2SHendrik Brueckner err = iucv_sock_wait(sk, iucv_sock_in_state(sk, IUCV_CONNECTED,
7570ea920d2SHendrik Brueckner IUCV_DISCONN),
758eac3731bSJennifer Hunt sock_sndtimeo(sk, flags & O_NONBLOCK));
759eac3731bSJennifer Hunt
7603881ac44SUrsula Braun if (sk->sk_state == IUCV_DISCONN || sk->sk_state == IUCV_CLOSED)
761b8942e3bSHendrik Brueckner err = -ECONNREFUSED;
76218becbc5SUrsula Braun
7637d316b94SUrsula Braun if (err && iucv->transport == AF_IUCV_TRANS_IUCV)
7647d316b94SUrsula Braun iucv_sever_path(sk, 0);
76518becbc5SUrsula Braun
766eac3731bSJennifer Hunt done:
767eac3731bSJennifer Hunt release_sock(sk);
768eac3731bSJennifer Hunt return err;
769eac3731bSJennifer Hunt }
770eac3731bSJennifer Hunt
771eac3731bSJennifer Hunt /* Move a socket into listening state. */
iucv_sock_listen(struct socket * sock,int backlog)772eac3731bSJennifer Hunt static int iucv_sock_listen(struct socket *sock, int backlog)
773eac3731bSJennifer Hunt {
774eac3731bSJennifer Hunt struct sock *sk = sock->sk;
775eac3731bSJennifer Hunt int err;
776eac3731bSJennifer Hunt
777eac3731bSJennifer Hunt lock_sock(sk);
778eac3731bSJennifer Hunt
779eac3731bSJennifer Hunt err = -EINVAL;
780aa8e71f5SHendrik Brueckner if (sk->sk_state != IUCV_BOUND)
781aa8e71f5SHendrik Brueckner goto done;
782aa8e71f5SHendrik Brueckner
783aa8e71f5SHendrik Brueckner if (sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET)
784eac3731bSJennifer Hunt goto done;
785eac3731bSJennifer Hunt
786eac3731bSJennifer Hunt sk->sk_max_ack_backlog = backlog;
787eac3731bSJennifer Hunt sk->sk_ack_backlog = 0;
788eac3731bSJennifer Hunt sk->sk_state = IUCV_LISTEN;
789eac3731bSJennifer Hunt err = 0;
790eac3731bSJennifer Hunt
791eac3731bSJennifer Hunt done:
792eac3731bSJennifer Hunt release_sock(sk);
793eac3731bSJennifer Hunt return err;
794eac3731bSJennifer Hunt }
795eac3731bSJennifer Hunt
796eac3731bSJennifer Hunt /* Accept a pending connection */
iucv_sock_accept(struct socket * sock,struct socket * newsock,int flags,bool kern)797eac3731bSJennifer Hunt static int iucv_sock_accept(struct socket *sock, struct socket *newsock,
798cdfbabfbSDavid Howells int flags, bool kern)
799eac3731bSJennifer Hunt {
800eac3731bSJennifer Hunt DECLARE_WAITQUEUE(wait, current);
801eac3731bSJennifer Hunt struct sock *sk = sock->sk, *nsk;
802eac3731bSJennifer Hunt long timeo;
803eac3731bSJennifer Hunt int err = 0;
804eac3731bSJennifer Hunt
805561e0360SJennifer Hunt lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
806eac3731bSJennifer Hunt
807eac3731bSJennifer Hunt if (sk->sk_state != IUCV_LISTEN) {
808eac3731bSJennifer Hunt err = -EBADFD;
809eac3731bSJennifer Hunt goto done;
810eac3731bSJennifer Hunt }
811eac3731bSJennifer Hunt
812eac3731bSJennifer Hunt timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
813eac3731bSJennifer Hunt
814eac3731bSJennifer Hunt /* Wait for an incoming connection */
815aa395145SEric Dumazet add_wait_queue_exclusive(sk_sleep(sk), &wait);
816eac3731bSJennifer Hunt while (!(nsk = iucv_accept_dequeue(sk, newsock))) {
817eac3731bSJennifer Hunt set_current_state(TASK_INTERRUPTIBLE);
818eac3731bSJennifer Hunt if (!timeo) {
819eac3731bSJennifer Hunt err = -EAGAIN;
820eac3731bSJennifer Hunt break;
821eac3731bSJennifer Hunt }
822eac3731bSJennifer Hunt
823eac3731bSJennifer Hunt release_sock(sk);
824eac3731bSJennifer Hunt timeo = schedule_timeout(timeo);
825561e0360SJennifer Hunt lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
826eac3731bSJennifer Hunt
827eac3731bSJennifer Hunt if (sk->sk_state != IUCV_LISTEN) {
828eac3731bSJennifer Hunt err = -EBADFD;
829eac3731bSJennifer Hunt break;
830eac3731bSJennifer Hunt }
831eac3731bSJennifer Hunt
832eac3731bSJennifer Hunt if (signal_pending(current)) {
833eac3731bSJennifer Hunt err = sock_intr_errno(timeo);
834eac3731bSJennifer Hunt break;
835eac3731bSJennifer Hunt }
836eac3731bSJennifer Hunt }
837eac3731bSJennifer Hunt
838eac3731bSJennifer Hunt set_current_state(TASK_RUNNING);
839aa395145SEric Dumazet remove_wait_queue(sk_sleep(sk), &wait);
840eac3731bSJennifer Hunt
841eac3731bSJennifer Hunt if (err)
842eac3731bSJennifer Hunt goto done;
843eac3731bSJennifer Hunt
844eac3731bSJennifer Hunt newsock->state = SS_CONNECTED;
845eac3731bSJennifer Hunt
846eac3731bSJennifer Hunt done:
847eac3731bSJennifer Hunt release_sock(sk);
848eac3731bSJennifer Hunt return err;
849eac3731bSJennifer Hunt }
850eac3731bSJennifer Hunt
iucv_sock_getname(struct socket * sock,struct sockaddr * addr,int peer)851eac3731bSJennifer Hunt static int iucv_sock_getname(struct socket *sock, struct sockaddr *addr,
8529b2c45d4SDenys Vlasenko int peer)
853eac3731bSJennifer Hunt {
85497f8841eSJulian Wiedmann DECLARE_SOCKADDR(struct sockaddr_iucv *, siucv, addr);
855eac3731bSJennifer Hunt struct sock *sk = sock->sk;
856493d3971SUrsula Braun struct iucv_sock *iucv = iucv_sk(sk);
857eac3731bSJennifer Hunt
858eac3731bSJennifer Hunt addr->sa_family = AF_IUCV;
859eac3731bSJennifer Hunt
860eac3731bSJennifer Hunt if (peer) {
861493d3971SUrsula Braun memcpy(siucv->siucv_user_id, iucv->dst_user_id, 8);
862493d3971SUrsula Braun memcpy(siucv->siucv_name, iucv->dst_name, 8);
863eac3731bSJennifer Hunt } else {
864493d3971SUrsula Braun memcpy(siucv->siucv_user_id, iucv->src_user_id, 8);
865493d3971SUrsula Braun memcpy(siucv->siucv_name, iucv->src_name, 8);
866eac3731bSJennifer Hunt }
867eac3731bSJennifer Hunt memset(&siucv->siucv_port, 0, sizeof(siucv->siucv_port));
868eac3731bSJennifer Hunt memset(&siucv->siucv_addr, 0, sizeof(siucv->siucv_addr));
869493d3971SUrsula Braun memset(&siucv->siucv_nodeid, 0, sizeof(siucv->siucv_nodeid));
870eac3731bSJennifer Hunt
8719b2c45d4SDenys Vlasenko return sizeof(struct sockaddr_iucv);
872eac3731bSJennifer Hunt }
873eac3731bSJennifer Hunt
874b8942e3bSHendrik Brueckner /**
875b8942e3bSHendrik Brueckner * iucv_send_iprm() - Send socket data in parameter list of an iucv message.
876b8942e3bSHendrik Brueckner * @path: IUCV path
877b8942e3bSHendrik Brueckner * @msg: Pointer to a struct iucv_message
878b8942e3bSHendrik Brueckner * @skb: The socket data to send, skb->len MUST BE <= 7
879b8942e3bSHendrik Brueckner *
880b8942e3bSHendrik Brueckner * Send the socket data in the parameter list in the iucv message
881b8942e3bSHendrik Brueckner * (IUCV_IPRMDATA). The socket data is stored at index 0 to 6 in the parameter
882b8942e3bSHendrik Brueckner * list and the socket data len at index 7 (last byte).
883b8942e3bSHendrik Brueckner * See also iucv_msg_length().
884b8942e3bSHendrik Brueckner *
885b8942e3bSHendrik Brueckner * Returns the error code from the iucv_message_send() call.
886b8942e3bSHendrik Brueckner */
iucv_send_iprm(struct iucv_path * path,struct iucv_message * msg,struct sk_buff * skb)887b8942e3bSHendrik Brueckner static int iucv_send_iprm(struct iucv_path *path, struct iucv_message *msg,
888b8942e3bSHendrik Brueckner struct sk_buff *skb)
889b8942e3bSHendrik Brueckner {
890b8942e3bSHendrik Brueckner u8 prmdata[8];
891b8942e3bSHendrik Brueckner
892b8942e3bSHendrik Brueckner memcpy(prmdata, (void *) skb->data, skb->len);
893b8942e3bSHendrik Brueckner prmdata[7] = 0xff - (u8) skb->len;
8946fcd61f7SFrank Blaschka return pr_iucv->message_send(path, msg, IUCV_IPRMDATA, 0,
895b8942e3bSHendrik Brueckner (void *) prmdata, 8);
896b8942e3bSHendrik Brueckner }
897b8942e3bSHendrik Brueckner
iucv_sock_sendmsg(struct socket * sock,struct msghdr * msg,size_t len)8981b784140SYing Xue static int iucv_sock_sendmsg(struct socket *sock, struct msghdr *msg,
8991b784140SYing Xue size_t len)
900eac3731bSJennifer Hunt {
901eac3731bSJennifer Hunt struct sock *sk = sock->sk;
902eac3731bSJennifer Hunt struct iucv_sock *iucv = iucv_sk(sk);
903dc5367bcSJulian Wiedmann size_t headroom = 0;
904dc5367bcSJulian Wiedmann size_t linear;
905eac3731bSJennifer Hunt struct sk_buff *skb;
9060506eb01SEugene Crosser struct iucv_message txmsg = {0};
90744b1e6b5SHendrik Brueckner struct cmsghdr *cmsg;
90844b1e6b5SHendrik Brueckner int cmsg_done;
9090ea920d2SHendrik Brueckner long timeo;
9108f7c502cSUrsula Braun char user_id[9];
9118f7c502cSUrsula Braun char appl_id[9];
912eac3731bSJennifer Hunt int err;
9130ea920d2SHendrik Brueckner int noblock = msg->msg_flags & MSG_DONTWAIT;
914eac3731bSJennifer Hunt
915eac3731bSJennifer Hunt err = sock_error(sk);
916eac3731bSJennifer Hunt if (err)
917eac3731bSJennifer Hunt return err;
918eac3731bSJennifer Hunt
919eac3731bSJennifer Hunt if (msg->msg_flags & MSG_OOB)
920eac3731bSJennifer Hunt return -EOPNOTSUPP;
921eac3731bSJennifer Hunt
922aa8e71f5SHendrik Brueckner /* SOCK_SEQPACKET: we do not support segmented records */
923aa8e71f5SHendrik Brueckner if (sk->sk_type == SOCK_SEQPACKET && !(msg->msg_flags & MSG_EOR))
924aa8e71f5SHendrik Brueckner return -EOPNOTSUPP;
925aa8e71f5SHendrik Brueckner
926eac3731bSJennifer Hunt lock_sock(sk);
927eac3731bSJennifer Hunt
928eac3731bSJennifer Hunt if (sk->sk_shutdown & SEND_SHUTDOWN) {
929eac3731bSJennifer Hunt err = -EPIPE;
930eac3731bSJennifer Hunt goto out;
931eac3731bSJennifer Hunt }
932eac3731bSJennifer Hunt
933bb664f49SHendrik Brueckner /* Return if the socket is not in connected state */
934bb664f49SHendrik Brueckner if (sk->sk_state != IUCV_CONNECTED) {
935bb664f49SHendrik Brueckner err = -ENOTCONN;
936bb664f49SHendrik Brueckner goto out;
937bb664f49SHendrik Brueckner }
938bb664f49SHendrik Brueckner
93944b1e6b5SHendrik Brueckner /* initialize defaults */
94044b1e6b5SHendrik Brueckner cmsg_done = 0; /* check for duplicate headers */
94144b1e6b5SHendrik Brueckner
94244b1e6b5SHendrik Brueckner /* iterate over control messages */
943f95b414eSGu Zheng for_each_cmsghdr(cmsg, msg) {
94444b1e6b5SHendrik Brueckner if (!CMSG_OK(msg, cmsg)) {
94544b1e6b5SHendrik Brueckner err = -EINVAL;
94644b1e6b5SHendrik Brueckner goto out;
94744b1e6b5SHendrik Brueckner }
94844b1e6b5SHendrik Brueckner
94944b1e6b5SHendrik Brueckner if (cmsg->cmsg_level != SOL_IUCV)
95044b1e6b5SHendrik Brueckner continue;
95144b1e6b5SHendrik Brueckner
95244b1e6b5SHendrik Brueckner if (cmsg->cmsg_type & cmsg_done) {
95344b1e6b5SHendrik Brueckner err = -EINVAL;
95444b1e6b5SHendrik Brueckner goto out;
95544b1e6b5SHendrik Brueckner }
95644b1e6b5SHendrik Brueckner cmsg_done |= cmsg->cmsg_type;
95744b1e6b5SHendrik Brueckner
95844b1e6b5SHendrik Brueckner switch (cmsg->cmsg_type) {
95944b1e6b5SHendrik Brueckner case SCM_IUCV_TRGCLS:
96044b1e6b5SHendrik Brueckner if (cmsg->cmsg_len != CMSG_LEN(TRGCLS_SIZE)) {
96144b1e6b5SHendrik Brueckner err = -EINVAL;
96244b1e6b5SHendrik Brueckner goto out;
96344b1e6b5SHendrik Brueckner }
96444b1e6b5SHendrik Brueckner
96544b1e6b5SHendrik Brueckner /* set iucv message target class */
96644b1e6b5SHendrik Brueckner memcpy(&txmsg.class,
96744b1e6b5SHendrik Brueckner (void *) CMSG_DATA(cmsg), TRGCLS_SIZE);
96844b1e6b5SHendrik Brueckner
96944b1e6b5SHendrik Brueckner break;
97044b1e6b5SHendrik Brueckner
97144b1e6b5SHendrik Brueckner default:
97244b1e6b5SHendrik Brueckner err = -EINVAL;
97344b1e6b5SHendrik Brueckner goto out;
97444b1e6b5SHendrik Brueckner }
97544b1e6b5SHendrik Brueckner }
97644b1e6b5SHendrik Brueckner
977aa8e71f5SHendrik Brueckner /* allocate one skb for each iucv message:
978aa8e71f5SHendrik Brueckner * this is fine for SOCK_SEQPACKET (unless we want to support
979aa8e71f5SHendrik Brueckner * segmented records using the MSG_EOR flag), but
980aa8e71f5SHendrik Brueckner * for SOCK_STREAM we might want to improve it in future */
981dc5367bcSJulian Wiedmann if (iucv->transport == AF_IUCV_TRANS_HIPER) {
982238965b7SJulian Wiedmann headroom = sizeof(struct af_iucv_trans_hdr) +
983238965b7SJulian Wiedmann LL_RESERVED_SPACE(iucv->hs_dev);
9842c3b4456SJulian Wiedmann linear = min(len, PAGE_SIZE - headroom);
985dc5367bcSJulian Wiedmann } else {
986dc5367bcSJulian Wiedmann if (len < PAGE_SIZE) {
987e5374399SEugene Crosser linear = len;
988e5374399SEugene Crosser } else {
989e5374399SEugene Crosser /* In nonlinear "classic" iucv skb,
990e5374399SEugene Crosser * reserve space for iucv_array
991e5374399SEugene Crosser */
992dc5367bcSJulian Wiedmann headroom = sizeof(struct iucv_array) *
993e5374399SEugene Crosser (MAX_SKB_FRAGS + 1);
994e5374399SEugene Crosser linear = PAGE_SIZE - headroom;
995e5374399SEugene Crosser }
996dc5367bcSJulian Wiedmann }
997e5374399SEugene Crosser skb = sock_alloc_send_pskb(sk, headroom + linear, len - linear,
998e5374399SEugene Crosser noblock, &err, 0);
999ed4ac422SEugene Crosser if (!skb)
1000561e0360SJennifer Hunt goto out;
1001e5374399SEugene Crosser if (headroom)
1002e5374399SEugene Crosser skb_reserve(skb, headroom);
1003e5374399SEugene Crosser skb_put(skb, linear);
1004e5374399SEugene Crosser skb->len = len;
1005e5374399SEugene Crosser skb->data_len = len - linear;
1006e5374399SEugene Crosser err = skb_copy_datagram_from_iter(skb, 0, &msg->msg_iter, len);
1007e5374399SEugene Crosser if (err)
1008eac3731bSJennifer Hunt goto fail;
1009eac3731bSJennifer Hunt
10100ea920d2SHendrik Brueckner /* wait if outstanding messages for iucv path has reached */
10110ea920d2SHendrik Brueckner timeo = sock_sndtimeo(sk, noblock);
10120ea920d2SHendrik Brueckner err = iucv_sock_wait(sk, iucv_below_msglim(sk), timeo);
10130ea920d2SHendrik Brueckner if (err)
10140ea920d2SHendrik Brueckner goto fail;
10150ea920d2SHendrik Brueckner
10160ea920d2SHendrik Brueckner /* return -ECONNRESET if the socket is no longer connected */
10170ea920d2SHendrik Brueckner if (sk->sk_state != IUCV_CONNECTED) {
10180ea920d2SHendrik Brueckner err = -ECONNRESET;
10190ea920d2SHendrik Brueckner goto fail;
10200ea920d2SHendrik Brueckner }
10210ea920d2SHendrik Brueckner
102244b1e6b5SHendrik Brueckner /* increment and save iucv message tag for msg_completion cbk */
1023eac3731bSJennifer Hunt txmsg.tag = iucv->send_tag++;
1024f9c41a62SUrsula Braun IUCV_SKB_CB(skb)->tag = txmsg.tag;
1025800c5eb7SUrsula Braun
10263881ac44SUrsula Braun if (iucv->transport == AF_IUCV_TRANS_HIPER) {
10273881ac44SUrsula Braun atomic_inc(&iucv->msg_sent);
10283881ac44SUrsula Braun err = afiucv_hs_send(&txmsg, sk, skb, 0);
10293881ac44SUrsula Braun if (err) {
10303881ac44SUrsula Braun atomic_dec(&iucv->msg_sent);
1031b2f54394SJulian Wiedmann goto out;
10323881ac44SUrsula Braun }
1033e5374399SEugene Crosser } else { /* Classic VM IUCV transport */
1034eac3731bSJennifer Hunt skb_queue_tail(&iucv->send_skb_q, skb);
1035ef6af7bdSJulian Wiedmann atomic_inc(&iucv->skbs_in_xmit);
1036b8942e3bSHendrik Brueckner
1037e5374399SEugene Crosser if (((iucv->path->flags & IUCV_IPRMDATA) & iucv->flags) &&
1038e5374399SEugene Crosser skb->len <= 7) {
1039b8942e3bSHendrik Brueckner err = iucv_send_iprm(iucv->path, &txmsg, skb);
1040b8942e3bSHendrik Brueckner
1041e5374399SEugene Crosser /* on success: there is no message_complete callback */
1042e5374399SEugene Crosser /* for an IPRMDATA msg; remove skb from send queue */
1043b8942e3bSHendrik Brueckner if (err == 0) {
1044ef6af7bdSJulian Wiedmann atomic_dec(&iucv->skbs_in_xmit);
1045b8942e3bSHendrik Brueckner skb_unlink(skb, &iucv->send_skb_q);
104610d6393dSJulian Wiedmann consume_skb(skb);
1047b8942e3bSHendrik Brueckner }
1048b8942e3bSHendrik Brueckner
1049e5374399SEugene Crosser /* this error should never happen since the */
1050e5374399SEugene Crosser /* IUCV_IPRMDATA path flag is set... sever path */
1051b8942e3bSHendrik Brueckner if (err == 0x15) {
10526fcd61f7SFrank Blaschka pr_iucv->path_sever(iucv->path, NULL);
1053ef6af7bdSJulian Wiedmann atomic_dec(&iucv->skbs_in_xmit);
1054b8942e3bSHendrik Brueckner skb_unlink(skb, &iucv->send_skb_q);
1055b8942e3bSHendrik Brueckner err = -EPIPE;
1056b8942e3bSHendrik Brueckner goto fail;
1057b8942e3bSHendrik Brueckner }
1058e5374399SEugene Crosser } else if (skb_is_nonlinear(skb)) {
1059e5374399SEugene Crosser struct iucv_array *iba = (struct iucv_array *)skb->head;
1060e5374399SEugene Crosser int i;
1061e5374399SEugene Crosser
1062e5374399SEugene Crosser /* skip iucv_array lying in the headroom */
1063e5374399SEugene Crosser iba[0].address = (u32)(addr_t)skb->data;
1064e5374399SEugene Crosser iba[0].length = (u32)skb_headlen(skb);
1065e5374399SEugene Crosser for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
1066e5374399SEugene Crosser skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
1067e5374399SEugene Crosser
1068e5374399SEugene Crosser iba[i + 1].address =
1069e5374399SEugene Crosser (u32)(addr_t)skb_frag_address(frag);
1070e5374399SEugene Crosser iba[i + 1].length = (u32)skb_frag_size(frag);
1071e5374399SEugene Crosser }
1072e5374399SEugene Crosser err = pr_iucv->message_send(iucv->path, &txmsg,
1073e5374399SEugene Crosser IUCV_IPBUFLST, 0,
1074e5374399SEugene Crosser (void *)iba, skb->len);
1075e5374399SEugene Crosser } else { /* non-IPRM Linear skb */
1076e5374399SEugene Crosser err = pr_iucv->message_send(iucv->path, &txmsg,
1077e5374399SEugene Crosser 0, 0, (void *)skb->data, skb->len);
1078e5374399SEugene Crosser }
1079eac3731bSJennifer Hunt if (err) {
10808f7c502cSUrsula Braun if (err == 3) {
10818f7c502cSUrsula Braun user_id[8] = 0;
10828f7c502cSUrsula Braun memcpy(user_id, iucv->dst_user_id, 8);
10838f7c502cSUrsula Braun appl_id[8] = 0;
10848f7c502cSUrsula Braun memcpy(appl_id, iucv->dst_name, 8);
1085e5374399SEugene Crosser pr_err(
1086e5374399SEugene Crosser "Application %s on z/VM guest %s exceeds message limit\n",
1087bb664f49SHendrik Brueckner appl_id, user_id);
10880ea920d2SHendrik Brueckner err = -EAGAIN;
1089e5374399SEugene Crosser } else {
1090eac3731bSJennifer Hunt err = -EPIPE;
1091e5374399SEugene Crosser }
1092ef6af7bdSJulian Wiedmann
1093ef6af7bdSJulian Wiedmann atomic_dec(&iucv->skbs_in_xmit);
10940ea920d2SHendrik Brueckner skb_unlink(skb, &iucv->send_skb_q);
1095eac3731bSJennifer Hunt goto fail;
1096eac3731bSJennifer Hunt }
1097e5374399SEugene Crosser }
1098eac3731bSJennifer Hunt
1099eac3731bSJennifer Hunt release_sock(sk);
1100eac3731bSJennifer Hunt return len;
1101eac3731bSJennifer Hunt
1102eac3731bSJennifer Hunt fail:
1103eac3731bSJennifer Hunt kfree_skb(skb);
1104eac3731bSJennifer Hunt out:
1105eac3731bSJennifer Hunt release_sock(sk);
1106eac3731bSJennifer Hunt return err;
1107eac3731bSJennifer Hunt }
1108eac3731bSJennifer Hunt
alloc_iucv_recv_skb(unsigned long len)1109a006353aSEugene Crosser static struct sk_buff *alloc_iucv_recv_skb(unsigned long len)
1110a006353aSEugene Crosser {
1111a006353aSEugene Crosser size_t headroom, linear;
1112a006353aSEugene Crosser struct sk_buff *skb;
1113a006353aSEugene Crosser int err;
1114a006353aSEugene Crosser
1115a006353aSEugene Crosser if (len < PAGE_SIZE) {
1116a006353aSEugene Crosser headroom = 0;
1117a006353aSEugene Crosser linear = len;
1118a006353aSEugene Crosser } else {
1119a006353aSEugene Crosser headroom = sizeof(struct iucv_array) * (MAX_SKB_FRAGS + 1);
1120a006353aSEugene Crosser linear = PAGE_SIZE - headroom;
1121a006353aSEugene Crosser }
1122a006353aSEugene Crosser skb = alloc_skb_with_frags(headroom + linear, len - linear,
1123a006353aSEugene Crosser 0, &err, GFP_ATOMIC | GFP_DMA);
1124a006353aSEugene Crosser WARN_ONCE(!skb,
1125a006353aSEugene Crosser "alloc of recv iucv skb len=%lu failed with errcode=%d\n",
1126a006353aSEugene Crosser len, err);
1127a006353aSEugene Crosser if (skb) {
1128a006353aSEugene Crosser if (headroom)
1129a006353aSEugene Crosser skb_reserve(skb, headroom);
1130a006353aSEugene Crosser skb_put(skb, linear);
1131a006353aSEugene Crosser skb->len = len;
1132a006353aSEugene Crosser skb->data_len = len - linear;
1133a006353aSEugene Crosser }
1134a006353aSEugene Crosser return skb;
1135a006353aSEugene Crosser }
1136a006353aSEugene Crosser
1137bf95d20fSHendrik Brueckner /* iucv_process_message() - Receive a single outstanding IUCV message
1138bf95d20fSHendrik Brueckner *
1139bf95d20fSHendrik Brueckner * Locking: must be called with message_q.lock held
1140bf95d20fSHendrik Brueckner */
iucv_process_message(struct sock * sk,struct sk_buff * skb,struct iucv_path * path,struct iucv_message * msg)1141f0703c80SUrsula Braun static void iucv_process_message(struct sock *sk, struct sk_buff *skb,
1142f0703c80SUrsula Braun struct iucv_path *path,
1143f0703c80SUrsula Braun struct iucv_message *msg)
1144f0703c80SUrsula Braun {
1145f0703c80SUrsula Braun int rc;
1146b8942e3bSHendrik Brueckner unsigned int len;
1147f0703c80SUrsula Braun
1148b8942e3bSHendrik Brueckner len = iucv_msg_length(msg);
1149b8942e3bSHendrik Brueckner
115044b1e6b5SHendrik Brueckner /* store msg target class in the second 4 bytes of skb ctrl buffer */
115144b1e6b5SHendrik Brueckner /* Note: the first 4 bytes are reserved for msg tag */
1152f9c41a62SUrsula Braun IUCV_SKB_CB(skb)->class = msg->class;
115344b1e6b5SHendrik Brueckner
1154b8942e3bSHendrik Brueckner /* check for special IPRM messages (e.g. iucv_sock_shutdown) */
1155b8942e3bSHendrik Brueckner if ((msg->flags & IUCV_IPRMDATA) && len > 7) {
1156b8942e3bSHendrik Brueckner if (memcmp(msg->rmmsg, iprm_shutdown, 8) == 0) {
1157f0703c80SUrsula Braun skb->data = NULL;
1158f0703c80SUrsula Braun skb->len = 0;
1159b8942e3bSHendrik Brueckner }
1160f0703c80SUrsula Braun } else {
1161a006353aSEugene Crosser if (skb_is_nonlinear(skb)) {
1162a006353aSEugene Crosser struct iucv_array *iba = (struct iucv_array *)skb->head;
1163a006353aSEugene Crosser int i;
1164a006353aSEugene Crosser
1165a006353aSEugene Crosser iba[0].address = (u32)(addr_t)skb->data;
1166a006353aSEugene Crosser iba[0].length = (u32)skb_headlen(skb);
1167a006353aSEugene Crosser for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
1168a006353aSEugene Crosser skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
1169a006353aSEugene Crosser
1170a006353aSEugene Crosser iba[i + 1].address =
1171a006353aSEugene Crosser (u32)(addr_t)skb_frag_address(frag);
1172a006353aSEugene Crosser iba[i + 1].length = (u32)skb_frag_size(frag);
1173a006353aSEugene Crosser }
1174a006353aSEugene Crosser rc = pr_iucv->message_receive(path, msg,
1175a006353aSEugene Crosser IUCV_IPBUFLST,
1176a006353aSEugene Crosser (void *)iba, len, NULL);
1177a006353aSEugene Crosser } else {
11786fcd61f7SFrank Blaschka rc = pr_iucv->message_receive(path, msg,
11796fcd61f7SFrank Blaschka msg->flags & IUCV_IPRMDATA,
1180b8942e3bSHendrik Brueckner skb->data, len, NULL);
1181a006353aSEugene Crosser }
1182f0703c80SUrsula Braun if (rc) {
1183f0703c80SUrsula Braun kfree_skb(skb);
1184f0703c80SUrsula Braun return;
1185f0703c80SUrsula Braun }
1186a006353aSEugene Crosser WARN_ON_ONCE(skb->len != len);
1187f0703c80SUrsula Braun }
1188f0703c80SUrsula Braun
1189f9c41a62SUrsula Braun IUCV_SKB_CB(skb)->offset = 0;
11908c68b1a0SUrsula Braun if (sk_filter(sk, skb)) {
11918c68b1a0SUrsula Braun atomic_inc(&sk->sk_drops); /* skb rejected by filter */
11928c68b1a0SUrsula Braun kfree_skb(skb);
11938c68b1a0SUrsula Braun return;
11948c68b1a0SUrsula Braun }
11958c68b1a0SUrsula Braun if (__sock_queue_rcv_skb(sk, skb)) /* handle rcv queue full */
11968c68b1a0SUrsula Braun skb_queue_tail(&iucv_sk(sk)->backlog_skb_q, skb);
1197f0703c80SUrsula Braun }
1198f0703c80SUrsula Braun
1199bf95d20fSHendrik Brueckner /* iucv_process_message_q() - Process outstanding IUCV messages
1200bf95d20fSHendrik Brueckner *
1201bf95d20fSHendrik Brueckner * Locking: must be called with message_q.lock held
1202bf95d20fSHendrik Brueckner */
iucv_process_message_q(struct sock * sk)1203f0703c80SUrsula Braun static void iucv_process_message_q(struct sock *sk)
1204f0703c80SUrsula Braun {
1205f0703c80SUrsula Braun struct iucv_sock *iucv = iucv_sk(sk);
1206f0703c80SUrsula Braun struct sk_buff *skb;
1207f0703c80SUrsula Braun struct sock_msg_q *p, *n;
1208f0703c80SUrsula Braun
1209f0703c80SUrsula Braun list_for_each_entry_safe(p, n, &iucv->message_q.list, list) {
1210a006353aSEugene Crosser skb = alloc_iucv_recv_skb(iucv_msg_length(&p->msg));
1211f0703c80SUrsula Braun if (!skb)
1212f0703c80SUrsula Braun break;
1213f0703c80SUrsula Braun iucv_process_message(sk, skb, p->path, &p->msg);
1214f0703c80SUrsula Braun list_del(&p->list);
1215f0703c80SUrsula Braun kfree(p);
1216f0703c80SUrsula Braun if (!skb_queue_empty(&iucv->backlog_skb_q))
1217f0703c80SUrsula Braun break;
1218f0703c80SUrsula Braun }
1219f0703c80SUrsula Braun }
1220f0703c80SUrsula Braun
iucv_sock_recvmsg(struct socket * sock,struct msghdr * msg,size_t len,int flags)12211b784140SYing Xue static int iucv_sock_recvmsg(struct socket *sock, struct msghdr *msg,
12221b784140SYing Xue size_t len, int flags)
1223eac3731bSJennifer Hunt {
1224eac3731bSJennifer Hunt struct sock *sk = sock->sk;
1225561e0360SJennifer Hunt struct iucv_sock *iucv = iucv_sk(sk);
1226aa8e71f5SHendrik Brueckner unsigned int copied, rlen;
12279fbd87d4SUrsula Braun struct sk_buff *skb, *rskb, *cskb;
1228eac3731bSJennifer Hunt int err = 0;
1229f9c41a62SUrsula Braun u32 offset;
1230eac3731bSJennifer Hunt
1231aac6399cSUrsula Braun if ((sk->sk_state == IUCV_DISCONN) &&
1232561e0360SJennifer Hunt skb_queue_empty(&iucv->backlog_skb_q) &&
1233f0703c80SUrsula Braun skb_queue_empty(&sk->sk_receive_queue) &&
1234f0703c80SUrsula Braun list_empty(&iucv->message_q.list))
1235561e0360SJennifer Hunt return 0;
1236561e0360SJennifer Hunt
1237eac3731bSJennifer Hunt if (flags & (MSG_OOB))
1238eac3731bSJennifer Hunt return -EOPNOTSUPP;
1239eac3731bSJennifer Hunt
124060d3705fSHendrik Brueckner /* receive/dequeue next skb:
1241*42251c2dSSidraya Jayagond * the function understands MSG_PEEK and, thus, does not dequeue skb
1242*42251c2dSSidraya Jayagond * only refcount is increased.
1243*42251c2dSSidraya Jayagond */
1244f4b41f06SOliver Hartkopp skb = skb_recv_datagram(sk, flags, &err);
1245eac3731bSJennifer Hunt if (!skb) {
1246eac3731bSJennifer Hunt if (sk->sk_shutdown & RCV_SHUTDOWN)
1247eac3731bSJennifer Hunt return 0;
1248eac3731bSJennifer Hunt return err;
1249eac3731bSJennifer Hunt }
1250eac3731bSJennifer Hunt
1251f9c41a62SUrsula Braun offset = IUCV_SKB_CB(skb)->offset;
1252f9c41a62SUrsula Braun rlen = skb->len - offset; /* real length of skb */
1253aa8e71f5SHendrik Brueckner copied = min_t(unsigned int, rlen, len);
125482492a35SUrsula Braun if (!rlen)
125582492a35SUrsula Braun sk->sk_shutdown = sk->sk_shutdown | RCV_SHUTDOWN;
1256eac3731bSJennifer Hunt
1257561e0360SJennifer Hunt cskb = skb;
125851f3d02bSDavid S. Miller if (skb_copy_datagram_msg(cskb, offset, msg, copied)) {
1259*42251c2dSSidraya Jayagond err = -EFAULT;
1260*42251c2dSSidraya Jayagond goto err_out;
1261eac3731bSJennifer Hunt }
1262eac3731bSJennifer Hunt
1263aa8e71f5SHendrik Brueckner /* SOCK_SEQPACKET: set MSG_TRUNC if recv buf size is too small */
1264aa8e71f5SHendrik Brueckner if (sk->sk_type == SOCK_SEQPACKET) {
1265aa8e71f5SHendrik Brueckner if (copied < rlen)
1266aa8e71f5SHendrik Brueckner msg->msg_flags |= MSG_TRUNC;
1267aa8e71f5SHendrik Brueckner /* each iucv message contains a complete record */
1268aa8e71f5SHendrik Brueckner msg->msg_flags |= MSG_EOR;
1269aa8e71f5SHendrik Brueckner }
1270eac3731bSJennifer Hunt
127144b1e6b5SHendrik Brueckner /* create control message to store iucv msg target class:
127244b1e6b5SHendrik Brueckner * get the trgcls from the control buffer of the skb due to
127344b1e6b5SHendrik Brueckner * fragmentation of original iucv message. */
127444b1e6b5SHendrik Brueckner err = put_cmsg(msg, SOL_IUCV, SCM_IUCV_TRGCLS,
1275f9c41a62SUrsula Braun sizeof(IUCV_SKB_CB(skb)->class),
1276f9c41a62SUrsula Braun (void *)&IUCV_SKB_CB(skb)->class);
1277*42251c2dSSidraya Jayagond if (err)
1278*42251c2dSSidraya Jayagond goto err_out;
127944b1e6b5SHendrik Brueckner
1280eac3731bSJennifer Hunt /* Mark read part of skb as used */
1281eac3731bSJennifer Hunt if (!(flags & MSG_PEEK)) {
1282eac3731bSJennifer Hunt
1283aa8e71f5SHendrik Brueckner /* SOCK_STREAM: re-queue skb if it contains unreceived data */
1284aa8e71f5SHendrik Brueckner if (sk->sk_type == SOCK_STREAM) {
1285f9c41a62SUrsula Braun if (copied < rlen) {
1286f9c41a62SUrsula Braun IUCV_SKB_CB(skb)->offset = offset + copied;
12872f139a5dSUrsula Braun skb_queue_head(&sk->sk_receive_queue, skb);
1288eac3731bSJennifer Hunt goto done;
1289eac3731bSJennifer Hunt }
1290aa8e71f5SHendrik Brueckner }
1291eac3731bSJennifer Hunt
129210d6393dSJulian Wiedmann consume_skb(skb);
1293800c5eb7SUrsula Braun if (iucv->transport == AF_IUCV_TRANS_HIPER) {
12943881ac44SUrsula Braun atomic_inc(&iucv->msg_recv);
1295800c5eb7SUrsula Braun if (atomic_read(&iucv->msg_recv) > iucv->msglimit) {
1296800c5eb7SUrsula Braun WARN_ON(1);
1297800c5eb7SUrsula Braun iucv_sock_close(sk);
1298800c5eb7SUrsula Braun return -EFAULT;
1299800c5eb7SUrsula Braun }
1300800c5eb7SUrsula Braun }
1301561e0360SJennifer Hunt
1302561e0360SJennifer Hunt /* Queue backlog skbs */
1303bf95d20fSHendrik Brueckner spin_lock_bh(&iucv->message_q.lock);
1304f0703c80SUrsula Braun rskb = skb_dequeue(&iucv->backlog_skb_q);
1305561e0360SJennifer Hunt while (rskb) {
1306f9c41a62SUrsula Braun IUCV_SKB_CB(rskb)->offset = 0;
13078c68b1a0SUrsula Braun if (__sock_queue_rcv_skb(sk, rskb)) {
13088c68b1a0SUrsula Braun /* handle rcv queue full */
1309f0703c80SUrsula Braun skb_queue_head(&iucv->backlog_skb_q,
1310561e0360SJennifer Hunt rskb);
1311561e0360SJennifer Hunt break;
1312561e0360SJennifer Hunt }
13138c68b1a0SUrsula Braun rskb = skb_dequeue(&iucv->backlog_skb_q);
1314561e0360SJennifer Hunt }
1315f0703c80SUrsula Braun if (skb_queue_empty(&iucv->backlog_skb_q)) {
1316f0703c80SUrsula Braun if (!list_empty(&iucv->message_q.list))
1317f0703c80SUrsula Braun iucv_process_message_q(sk);
13183881ac44SUrsula Braun if (atomic_read(&iucv->msg_recv) >=
13193881ac44SUrsula Braun iucv->msglimit / 2) {
13209fbd87d4SUrsula Braun err = iucv_send_ctrl(sk, AF_IUCV_FLAG_WIN);
13213881ac44SUrsula Braun if (err) {
13223881ac44SUrsula Braun sk->sk_state = IUCV_DISCONN;
13233881ac44SUrsula Braun sk->sk_state_change(sk);
13243881ac44SUrsula Braun }
13253881ac44SUrsula Braun }
1326f0703c80SUrsula Braun }
1327bf95d20fSHendrik Brueckner spin_unlock_bh(&iucv->message_q.lock);
132860d3705fSHendrik Brueckner }
1329eac3731bSJennifer Hunt
1330eac3731bSJennifer Hunt done:
1331aa8e71f5SHendrik Brueckner /* SOCK_SEQPACKET: return real length if MSG_TRUNC is set */
1332aa8e71f5SHendrik Brueckner if (sk->sk_type == SOCK_SEQPACKET && (flags & MSG_TRUNC))
1333aa8e71f5SHendrik Brueckner copied = rlen;
1334*42251c2dSSidraya Jayagond if (flags & MSG_PEEK)
1335*42251c2dSSidraya Jayagond skb_unref(skb);
1336aa8e71f5SHendrik Brueckner
1337aa8e71f5SHendrik Brueckner return copied;
1338*42251c2dSSidraya Jayagond
1339*42251c2dSSidraya Jayagond err_out:
1340*42251c2dSSidraya Jayagond if (!(flags & MSG_PEEK))
1341*42251c2dSSidraya Jayagond skb_queue_head(&sk->sk_receive_queue, skb);
1342*42251c2dSSidraya Jayagond else
1343*42251c2dSSidraya Jayagond skb_unref(skb);
1344*42251c2dSSidraya Jayagond
1345*42251c2dSSidraya Jayagond return err;
1346eac3731bSJennifer Hunt }
1347eac3731bSJennifer Hunt
iucv_accept_poll(struct sock * parent)1348ade994f4SAl Viro static inline __poll_t iucv_accept_poll(struct sock *parent)
1349eac3731bSJennifer Hunt {
1350eac3731bSJennifer Hunt struct iucv_sock *isk, *n;
1351eac3731bSJennifer Hunt struct sock *sk;
1352eac3731bSJennifer Hunt
1353eac3731bSJennifer Hunt list_for_each_entry_safe(isk, n, &iucv_sk(parent)->accept_q, accept_q) {
1354eac3731bSJennifer Hunt sk = (struct sock *) isk;
1355eac3731bSJennifer Hunt
1356eac3731bSJennifer Hunt if (sk->sk_state == IUCV_CONNECTED)
1357a9a08845SLinus Torvalds return EPOLLIN | EPOLLRDNORM;
1358eac3731bSJennifer Hunt }
1359eac3731bSJennifer Hunt
1360eac3731bSJennifer Hunt return 0;
1361eac3731bSJennifer Hunt }
1362eac3731bSJennifer Hunt
iucv_sock_poll(struct file * file,struct socket * sock,poll_table * wait)1363e9a36ca5SJulian Wiedmann static __poll_t iucv_sock_poll(struct file *file, struct socket *sock,
1364a11e1d43SLinus Torvalds poll_table *wait)
1365eac3731bSJennifer Hunt {
1366eac3731bSJennifer Hunt struct sock *sk = sock->sk;
1367ade994f4SAl Viro __poll_t mask = 0;
1368eac3731bSJennifer Hunt
136989ab066dSKarsten Graul sock_poll_wait(file, sock, wait);
1370a11e1d43SLinus Torvalds
1371eac3731bSJennifer Hunt if (sk->sk_state == IUCV_LISTEN)
1372eac3731bSJennifer Hunt return iucv_accept_poll(sk);
1373eac3731bSJennifer Hunt
1374eac3731bSJennifer Hunt if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
1375a9a08845SLinus Torvalds mask |= EPOLLERR |
1376a9a08845SLinus Torvalds (sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? EPOLLPRI : 0);
1377eac3731bSJennifer Hunt
1378eac3731bSJennifer Hunt if (sk->sk_shutdown & RCV_SHUTDOWN)
1379a9a08845SLinus Torvalds mask |= EPOLLRDHUP;
1380eac3731bSJennifer Hunt
1381eac3731bSJennifer Hunt if (sk->sk_shutdown == SHUTDOWN_MASK)
1382a9a08845SLinus Torvalds mask |= EPOLLHUP;
1383eac3731bSJennifer Hunt
1384eac3731bSJennifer Hunt if (!skb_queue_empty(&sk->sk_receive_queue) ||
1385eac3731bSJennifer Hunt (sk->sk_shutdown & RCV_SHUTDOWN))
1386a9a08845SLinus Torvalds mask |= EPOLLIN | EPOLLRDNORM;
1387eac3731bSJennifer Hunt
1388eac3731bSJennifer Hunt if (sk->sk_state == IUCV_CLOSED)
1389a9a08845SLinus Torvalds mask |= EPOLLHUP;
1390eac3731bSJennifer Hunt
1391aac6399cSUrsula Braun if (sk->sk_state == IUCV_DISCONN)
1392a9a08845SLinus Torvalds mask |= EPOLLIN;
1393561e0360SJennifer Hunt
13947f1b0ea4SUrsula Braun if (sock_writeable(sk) && iucv_below_msglim(sk))
1395a9a08845SLinus Torvalds mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND;
1396eac3731bSJennifer Hunt else
13979cd3e072SEric Dumazet sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
1398eac3731bSJennifer Hunt
1399eac3731bSJennifer Hunt return mask;
1400eac3731bSJennifer Hunt }
1401eac3731bSJennifer Hunt
iucv_sock_shutdown(struct socket * sock,int how)1402eac3731bSJennifer Hunt static int iucv_sock_shutdown(struct socket *sock, int how)
1403eac3731bSJennifer Hunt {
1404eac3731bSJennifer Hunt struct sock *sk = sock->sk;
1405eac3731bSJennifer Hunt struct iucv_sock *iucv = iucv_sk(sk);
1406eac3731bSJennifer Hunt struct iucv_message txmsg;
1407eac3731bSJennifer Hunt int err = 0;
1408eac3731bSJennifer Hunt
1409eac3731bSJennifer Hunt how++;
1410eac3731bSJennifer Hunt
1411eac3731bSJennifer Hunt if ((how & ~SHUTDOWN_MASK) || !how)
1412eac3731bSJennifer Hunt return -EINVAL;
1413eac3731bSJennifer Hunt
1414eac3731bSJennifer Hunt lock_sock(sk);
1415eac3731bSJennifer Hunt switch (sk->sk_state) {
141682492a35SUrsula Braun case IUCV_LISTEN:
1417e14ad5faSHendrik Brueckner case IUCV_DISCONN:
1418e14ad5faSHendrik Brueckner case IUCV_CLOSING:
1419eac3731bSJennifer Hunt case IUCV_CLOSED:
1420eac3731bSJennifer Hunt err = -ENOTCONN;
1421eac3731bSJennifer Hunt goto fail;
1422eac3731bSJennifer Hunt default:
1423eac3731bSJennifer Hunt break;
1424eac3731bSJennifer Hunt }
1425eac3731bSJennifer Hunt
14264031eeafSUrsula Braun if ((how == SEND_SHUTDOWN || how == SHUTDOWN_MASK) &&
14274031eeafSUrsula Braun sk->sk_state == IUCV_CONNECTED) {
142882492a35SUrsula Braun if (iucv->transport == AF_IUCV_TRANS_IUCV) {
1429eac3731bSJennifer Hunt txmsg.class = 0;
1430eac3731bSJennifer Hunt txmsg.tag = 0;
143182492a35SUrsula Braun err = pr_iucv->message_send(iucv->path, &txmsg,
143282492a35SUrsula Braun IUCV_IPRMDATA, 0, (void *) iprm_shutdown, 8);
1433eac3731bSJennifer Hunt if (err) {
1434eac3731bSJennifer Hunt switch (err) {
1435eac3731bSJennifer Hunt case 1:
1436eac3731bSJennifer Hunt err = -ENOTCONN;
1437eac3731bSJennifer Hunt break;
1438eac3731bSJennifer Hunt case 2:
1439eac3731bSJennifer Hunt err = -ECONNRESET;
1440eac3731bSJennifer Hunt break;
1441eac3731bSJennifer Hunt default:
1442eac3731bSJennifer Hunt err = -ENOTCONN;
1443eac3731bSJennifer Hunt break;
1444eac3731bSJennifer Hunt }
1445eac3731bSJennifer Hunt }
144682492a35SUrsula Braun } else
144782492a35SUrsula Braun iucv_send_ctrl(sk, AF_IUCV_FLAG_SHT);
1448eac3731bSJennifer Hunt }
1449eac3731bSJennifer Hunt
145082492a35SUrsula Braun sk->sk_shutdown |= how;
1451eac3731bSJennifer Hunt if (how == RCV_SHUTDOWN || how == SHUTDOWN_MASK) {
14521042cab8SUrsula Braun if ((iucv->transport == AF_IUCV_TRANS_IUCV) &&
14531042cab8SUrsula Braun iucv->path) {
14546fcd61f7SFrank Blaschka err = pr_iucv->path_quiesce(iucv->path, NULL);
1455eac3731bSJennifer Hunt if (err)
1456eac3731bSJennifer Hunt err = -ENOTCONN;
145782492a35SUrsula Braun /* skb_queue_purge(&sk->sk_receive_queue); */
145882492a35SUrsula Braun }
1459eac3731bSJennifer Hunt skb_queue_purge(&sk->sk_receive_queue);
1460eac3731bSJennifer Hunt }
1461eac3731bSJennifer Hunt
1462eac3731bSJennifer Hunt /* Wake up anyone sleeping in poll */
1463eac3731bSJennifer Hunt sk->sk_state_change(sk);
1464eac3731bSJennifer Hunt
1465eac3731bSJennifer Hunt fail:
1466eac3731bSJennifer Hunt release_sock(sk);
1467eac3731bSJennifer Hunt return err;
1468eac3731bSJennifer Hunt }
1469eac3731bSJennifer Hunt
iucv_sock_release(struct socket * sock)1470eac3731bSJennifer Hunt static int iucv_sock_release(struct socket *sock)
1471eac3731bSJennifer Hunt {
1472eac3731bSJennifer Hunt struct sock *sk = sock->sk;
1473eac3731bSJennifer Hunt int err = 0;
1474eac3731bSJennifer Hunt
1475eac3731bSJennifer Hunt if (!sk)
1476eac3731bSJennifer Hunt return 0;
1477eac3731bSJennifer Hunt
1478eac3731bSJennifer Hunt iucv_sock_close(sk);
1479eac3731bSJennifer Hunt
1480eac3731bSJennifer Hunt sock_orphan(sk);
1481eac3731bSJennifer Hunt iucv_sock_kill(sk);
1482eac3731bSJennifer Hunt return err;
1483eac3731bSJennifer Hunt }
1484eac3731bSJennifer Hunt
14859d5c5d8fSHendrik Brueckner /* getsockopt and setsockopt */
iucv_sock_setsockopt(struct socket * sock,int level,int optname,sockptr_t optval,unsigned int optlen)14869d5c5d8fSHendrik Brueckner static int iucv_sock_setsockopt(struct socket *sock, int level, int optname,
1487a7b75c5aSChristoph Hellwig sockptr_t optval, unsigned int optlen)
14889d5c5d8fSHendrik Brueckner {
14899d5c5d8fSHendrik Brueckner struct sock *sk = sock->sk;
14909d5c5d8fSHendrik Brueckner struct iucv_sock *iucv = iucv_sk(sk);
14919d5c5d8fSHendrik Brueckner int val;
14929d5c5d8fSHendrik Brueckner int rc;
14939d5c5d8fSHendrik Brueckner
14949d5c5d8fSHendrik Brueckner if (level != SOL_IUCV)
14959d5c5d8fSHendrik Brueckner return -ENOPROTOOPT;
14969d5c5d8fSHendrik Brueckner
14979d5c5d8fSHendrik Brueckner if (optlen < sizeof(int))
14989d5c5d8fSHendrik Brueckner return -EINVAL;
14999d5c5d8fSHendrik Brueckner
1500a7b75c5aSChristoph Hellwig if (copy_from_sockptr(&val, optval, sizeof(int)))
15019d5c5d8fSHendrik Brueckner return -EFAULT;
15029d5c5d8fSHendrik Brueckner
15039d5c5d8fSHendrik Brueckner rc = 0;
15049d5c5d8fSHendrik Brueckner
15059d5c5d8fSHendrik Brueckner lock_sock(sk);
15069d5c5d8fSHendrik Brueckner switch (optname) {
15079d5c5d8fSHendrik Brueckner case SO_IPRMDATA_MSG:
15089d5c5d8fSHendrik Brueckner if (val)
15099d5c5d8fSHendrik Brueckner iucv->flags |= IUCV_IPRMDATA;
15109d5c5d8fSHendrik Brueckner else
15119d5c5d8fSHendrik Brueckner iucv->flags &= ~IUCV_IPRMDATA;
15129d5c5d8fSHendrik Brueckner break;
151309488e2eSHendrik Brueckner case SO_MSGLIMIT:
151409488e2eSHendrik Brueckner switch (sk->sk_state) {
151509488e2eSHendrik Brueckner case IUCV_OPEN:
151609488e2eSHendrik Brueckner case IUCV_BOUND:
15170d1c7664SJulian Wiedmann if (val < 1 || val > U16_MAX)
151809488e2eSHendrik Brueckner rc = -EINVAL;
151909488e2eSHendrik Brueckner else
152009488e2eSHendrik Brueckner iucv->msglimit = val;
152109488e2eSHendrik Brueckner break;
152209488e2eSHendrik Brueckner default:
152309488e2eSHendrik Brueckner rc = -EINVAL;
152409488e2eSHendrik Brueckner break;
152509488e2eSHendrik Brueckner }
152609488e2eSHendrik Brueckner break;
15279d5c5d8fSHendrik Brueckner default:
15289d5c5d8fSHendrik Brueckner rc = -ENOPROTOOPT;
15299d5c5d8fSHendrik Brueckner break;
15309d5c5d8fSHendrik Brueckner }
15319d5c5d8fSHendrik Brueckner release_sock(sk);
15329d5c5d8fSHendrik Brueckner
15339d5c5d8fSHendrik Brueckner return rc;
15349d5c5d8fSHendrik Brueckner }
15359d5c5d8fSHendrik Brueckner
iucv_sock_getsockopt(struct socket * sock,int level,int optname,char __user * optval,int __user * optlen)15369d5c5d8fSHendrik Brueckner static int iucv_sock_getsockopt(struct socket *sock, int level, int optname,
15379d5c5d8fSHendrik Brueckner char __user *optval, int __user *optlen)
15389d5c5d8fSHendrik Brueckner {
15399d5c5d8fSHendrik Brueckner struct sock *sk = sock->sk;
15409d5c5d8fSHendrik Brueckner struct iucv_sock *iucv = iucv_sk(sk);
154151363b87SUrsula Braun unsigned int val;
154251363b87SUrsula Braun int len;
15439d5c5d8fSHendrik Brueckner
15449d5c5d8fSHendrik Brueckner if (level != SOL_IUCV)
15459d5c5d8fSHendrik Brueckner return -ENOPROTOOPT;
15469d5c5d8fSHendrik Brueckner
15479d5c5d8fSHendrik Brueckner if (get_user(len, optlen))
15489d5c5d8fSHendrik Brueckner return -EFAULT;
15499d5c5d8fSHendrik Brueckner
15509d5c5d8fSHendrik Brueckner if (len < 0)
15519d5c5d8fSHendrik Brueckner return -EINVAL;
15529d5c5d8fSHendrik Brueckner
15539d5c5d8fSHendrik Brueckner len = min_t(unsigned int, len, sizeof(int));
15549d5c5d8fSHendrik Brueckner
15559d5c5d8fSHendrik Brueckner switch (optname) {
15569d5c5d8fSHendrik Brueckner case SO_IPRMDATA_MSG:
15579d5c5d8fSHendrik Brueckner val = (iucv->flags & IUCV_IPRMDATA) ? 1 : 0;
15589d5c5d8fSHendrik Brueckner break;
155909488e2eSHendrik Brueckner case SO_MSGLIMIT:
156009488e2eSHendrik Brueckner lock_sock(sk);
156109488e2eSHendrik Brueckner val = (iucv->path != NULL) ? iucv->path->msglim /* connected */
156209488e2eSHendrik Brueckner : iucv->msglimit; /* default */
156309488e2eSHendrik Brueckner release_sock(sk);
156409488e2eSHendrik Brueckner break;
156551363b87SUrsula Braun case SO_MSGSIZE:
156651363b87SUrsula Braun if (sk->sk_state == IUCV_OPEN)
156751363b87SUrsula Braun return -EBADFD;
156851363b87SUrsula Braun val = (iucv->hs_dev) ? iucv->hs_dev->mtu -
156951363b87SUrsula Braun sizeof(struct af_iucv_trans_hdr) - ETH_HLEN :
157051363b87SUrsula Braun 0x7fffffff;
157151363b87SUrsula Braun break;
15729d5c5d8fSHendrik Brueckner default:
15739d5c5d8fSHendrik Brueckner return -ENOPROTOOPT;
15749d5c5d8fSHendrik Brueckner }
15759d5c5d8fSHendrik Brueckner
15769d5c5d8fSHendrik Brueckner if (put_user(len, optlen))
15779d5c5d8fSHendrik Brueckner return -EFAULT;
15789d5c5d8fSHendrik Brueckner if (copy_to_user(optval, &val, len))
15799d5c5d8fSHendrik Brueckner return -EFAULT;
15809d5c5d8fSHendrik Brueckner
15819d5c5d8fSHendrik Brueckner return 0;
15829d5c5d8fSHendrik Brueckner }
15839d5c5d8fSHendrik Brueckner
15849d5c5d8fSHendrik Brueckner
1585eac3731bSJennifer Hunt /* Callback wrappers - called from iucv base support */
iucv_callback_connreq(struct iucv_path * path,u8 ipvmid[8],u8 ipuser[16])1586eac3731bSJennifer Hunt static int iucv_callback_connreq(struct iucv_path *path,
1587eac3731bSJennifer Hunt u8 ipvmid[8], u8 ipuser[16])
1588eac3731bSJennifer Hunt {
1589eac3731bSJennifer Hunt unsigned char user_data[16];
1590eac3731bSJennifer Hunt unsigned char nuser_data[16];
1591eac3731bSJennifer Hunt unsigned char src_name[8];
1592eac3731bSJennifer Hunt struct sock *sk, *nsk;
1593eac3731bSJennifer Hunt struct iucv_sock *iucv, *niucv;
1594eac3731bSJennifer Hunt int err;
1595eac3731bSJennifer Hunt
1596eac3731bSJennifer Hunt memcpy(src_name, ipuser, 8);
1597eac3731bSJennifer Hunt EBCASC(src_name, 8);
1598eac3731bSJennifer Hunt /* Find out if this path belongs to af_iucv. */
1599eac3731bSJennifer Hunt read_lock(&iucv_sk_list.lock);
1600eac3731bSJennifer Hunt iucv = NULL;
1601febca281SUrsula Braun sk = NULL;
1602b67bfe0dSSasha Levin sk_for_each(sk, &iucv_sk_list.head)
1603eac3731bSJennifer Hunt if (sk->sk_state == IUCV_LISTEN &&
1604eac3731bSJennifer Hunt !memcmp(&iucv_sk(sk)->src_name, src_name, 8)) {
1605eac3731bSJennifer Hunt /*
1606eac3731bSJennifer Hunt * Found a listening socket with
1607eac3731bSJennifer Hunt * src_name == ipuser[0-7].
1608eac3731bSJennifer Hunt */
1609eac3731bSJennifer Hunt iucv = iucv_sk(sk);
1610eac3731bSJennifer Hunt break;
1611eac3731bSJennifer Hunt }
1612eac3731bSJennifer Hunt read_unlock(&iucv_sk_list.lock);
1613eac3731bSJennifer Hunt if (!iucv)
1614eac3731bSJennifer Hunt /* No socket found, not one of our paths. */
1615eac3731bSJennifer Hunt return -EINVAL;
1616eac3731bSJennifer Hunt
1617eac3731bSJennifer Hunt bh_lock_sock(sk);
1618eac3731bSJennifer Hunt
1619eac3731bSJennifer Hunt /* Check if parent socket is listening */
1620eac3731bSJennifer Hunt low_nmcpy(user_data, iucv->src_name);
1621eac3731bSJennifer Hunt high_nmcpy(user_data, iucv->dst_name);
1622eac3731bSJennifer Hunt ASCEBC(user_data, sizeof(user_data));
1623eac3731bSJennifer Hunt if (sk->sk_state != IUCV_LISTEN) {
16246fcd61f7SFrank Blaschka err = pr_iucv->path_sever(path, user_data);
162565dbd7c2SHendrik Brueckner iucv_path_free(path);
1626eac3731bSJennifer Hunt goto fail;
1627eac3731bSJennifer Hunt }
1628eac3731bSJennifer Hunt
1629eac3731bSJennifer Hunt /* Check for backlog size */
1630eac3731bSJennifer Hunt if (sk_acceptq_is_full(sk)) {
16316fcd61f7SFrank Blaschka err = pr_iucv->path_sever(path, user_data);
163265dbd7c2SHendrik Brueckner iucv_path_free(path);
1633eac3731bSJennifer Hunt goto fail;
1634eac3731bSJennifer Hunt }
1635eac3731bSJennifer Hunt
1636eac3731bSJennifer Hunt /* Create the new socket */
1637c5dab094SJulian Wiedmann nsk = iucv_sock_alloc(NULL, sk->sk_protocol, GFP_ATOMIC, 0);
1638eac3731bSJennifer Hunt if (!nsk) {
16396fcd61f7SFrank Blaschka err = pr_iucv->path_sever(path, user_data);
164065dbd7c2SHendrik Brueckner iucv_path_free(path);
1641eac3731bSJennifer Hunt goto fail;
1642eac3731bSJennifer Hunt }
1643eac3731bSJennifer Hunt
1644eac3731bSJennifer Hunt niucv = iucv_sk(nsk);
1645eac3731bSJennifer Hunt iucv_sock_init(nsk, sk);
1646fdbf6326SJulian Wiedmann niucv->transport = AF_IUCV_TRANS_IUCV;
1647fdbf6326SJulian Wiedmann nsk->sk_allocation |= GFP_DMA;
1648eac3731bSJennifer Hunt
1649eac3731bSJennifer Hunt /* Set the new iucv_sock */
1650eac3731bSJennifer Hunt memcpy(niucv->dst_name, ipuser + 8, 8);
1651eac3731bSJennifer Hunt EBCASC(niucv->dst_name, 8);
1652eac3731bSJennifer Hunt memcpy(niucv->dst_user_id, ipvmid, 8);
1653eac3731bSJennifer Hunt memcpy(niucv->src_name, iucv->src_name, 8);
1654eac3731bSJennifer Hunt memcpy(niucv->src_user_id, iucv->src_user_id, 8);
1655eac3731bSJennifer Hunt niucv->path = path;
1656eac3731bSJennifer Hunt
1657eac3731bSJennifer Hunt /* Call iucv_accept */
1658eac3731bSJennifer Hunt high_nmcpy(nuser_data, ipuser + 8);
1659eac3731bSJennifer Hunt memcpy(nuser_data + 8, niucv->src_name, 8);
1660eac3731bSJennifer Hunt ASCEBC(nuser_data + 8, 8);
1661eac3731bSJennifer Hunt
166209488e2eSHendrik Brueckner /* set message limit for path based on msglimit of accepting socket */
166309488e2eSHendrik Brueckner niucv->msglimit = iucv->msglimit;
166409488e2eSHendrik Brueckner path->msglim = iucv->msglimit;
16656fcd61f7SFrank Blaschka err = pr_iucv->path_accept(path, &af_iucv_handler, nuser_data, nsk);
1666eac3731bSJennifer Hunt if (err) {
16677d316b94SUrsula Braun iucv_sever_path(nsk, 1);
166865dbd7c2SHendrik Brueckner iucv_sock_kill(nsk);
1669eac3731bSJennifer Hunt goto fail;
1670eac3731bSJennifer Hunt }
1671eac3731bSJennifer Hunt
1672eac3731bSJennifer Hunt iucv_accept_enqueue(sk, nsk);
1673eac3731bSJennifer Hunt
1674eac3731bSJennifer Hunt /* Wake up accept */
1675eac3731bSJennifer Hunt nsk->sk_state = IUCV_CONNECTED;
1676676d2369SDavid S. Miller sk->sk_data_ready(sk);
1677eac3731bSJennifer Hunt err = 0;
1678eac3731bSJennifer Hunt fail:
1679eac3731bSJennifer Hunt bh_unlock_sock(sk);
1680eac3731bSJennifer Hunt return 0;
1681eac3731bSJennifer Hunt }
1682eac3731bSJennifer Hunt
iucv_callback_connack(struct iucv_path * path,u8 ipuser[16])1683eac3731bSJennifer Hunt static void iucv_callback_connack(struct iucv_path *path, u8 ipuser[16])
1684eac3731bSJennifer Hunt {
1685eac3731bSJennifer Hunt struct sock *sk = path->private;
1686eac3731bSJennifer Hunt
1687eac3731bSJennifer Hunt sk->sk_state = IUCV_CONNECTED;
1688eac3731bSJennifer Hunt sk->sk_state_change(sk);
1689eac3731bSJennifer Hunt }
1690eac3731bSJennifer Hunt
iucv_callback_rx(struct iucv_path * path,struct iucv_message * msg)1691eac3731bSJennifer Hunt static void iucv_callback_rx(struct iucv_path *path, struct iucv_message *msg)
1692eac3731bSJennifer Hunt {
1693eac3731bSJennifer Hunt struct sock *sk = path->private;
1694561e0360SJennifer Hunt struct iucv_sock *iucv = iucv_sk(sk);
1695f0703c80SUrsula Braun struct sk_buff *skb;
1696f0703c80SUrsula Braun struct sock_msg_q *save_msg;
1697f0703c80SUrsula Braun int len;
1698561e0360SJennifer Hunt
1699fe86e54eSHendrik Brueckner if (sk->sk_shutdown & RCV_SHUTDOWN) {
17006fcd61f7SFrank Blaschka pr_iucv->message_reject(path, msg);
1701eac3731bSJennifer Hunt return;
1702fe86e54eSHendrik Brueckner }
1703eac3731bSJennifer Hunt
17043fa6b5adSHendrik Brueckner spin_lock(&iucv->message_q.lock);
1705eac3731bSJennifer Hunt
1706f0703c80SUrsula Braun if (!list_empty(&iucv->message_q.list) ||
1707f0703c80SUrsula Braun !skb_queue_empty(&iucv->backlog_skb_q))
1708f0703c80SUrsula Braun goto save_message;
1709f0703c80SUrsula Braun
1710f0703c80SUrsula Braun len = atomic_read(&sk->sk_rmem_alloc);
171187fb4b7bSEric Dumazet len += SKB_TRUESIZE(iucv_msg_length(msg));
1712f0703c80SUrsula Braun if (len > sk->sk_rcvbuf)
1713f0703c80SUrsula Braun goto save_message;
1714f0703c80SUrsula Braun
1715a006353aSEugene Crosser skb = alloc_iucv_recv_skb(iucv_msg_length(msg));
1716f0703c80SUrsula Braun if (!skb)
1717f0703c80SUrsula Braun goto save_message;
1718eac3731bSJennifer Hunt
1719f0703c80SUrsula Braun iucv_process_message(sk, skb, path, msg);
17203fa6b5adSHendrik Brueckner goto out_unlock;
1721eac3731bSJennifer Hunt
1722f0703c80SUrsula Braun save_message:
1723f0703c80SUrsula Braun save_msg = kzalloc(sizeof(struct sock_msg_q), GFP_ATOMIC | GFP_DMA);
1724d4444722SUrsula Braun if (!save_msg)
1725a56635a5SJulia Lawall goto out_unlock;
1726f0703c80SUrsula Braun save_msg->path = path;
1727f0703c80SUrsula Braun save_msg->msg = *msg;
1728f0703c80SUrsula Braun
1729f0703c80SUrsula Braun list_add_tail(&save_msg->list, &iucv->message_q.list);
17303fa6b5adSHendrik Brueckner
17313fa6b5adSHendrik Brueckner out_unlock:
1732f0703c80SUrsula Braun spin_unlock(&iucv->message_q.lock);
1733eac3731bSJennifer Hunt }
1734eac3731bSJennifer Hunt
iucv_callback_txdone(struct iucv_path * path,struct iucv_message * msg)1735eac3731bSJennifer Hunt static void iucv_callback_txdone(struct iucv_path *path,
1736eac3731bSJennifer Hunt struct iucv_message *msg)
1737eac3731bSJennifer Hunt {
1738eac3731bSJennifer Hunt struct sock *sk = path->private;
1739f2a77991SUrsula Braun struct sk_buff *this = NULL;
1740ef6af7bdSJulian Wiedmann struct sk_buff_head *list;
17419e733177SDavid S. Miller struct sk_buff *list_skb;
1742ef6af7bdSJulian Wiedmann struct iucv_sock *iucv;
1743eac3731bSJennifer Hunt unsigned long flags;
1744eac3731bSJennifer Hunt
1745ef6af7bdSJulian Wiedmann iucv = iucv_sk(sk);
1746ef6af7bdSJulian Wiedmann list = &iucv->send_skb_q;
1747ef6af7bdSJulian Wiedmann
17487d316b94SUrsula Braun bh_lock_sock(sk);
1749eac3731bSJennifer Hunt
17509e733177SDavid S. Miller spin_lock_irqsave(&list->lock, flags);
17519e733177SDavid S. Miller skb_queue_walk(list, list_skb) {
1752f5738e2eSUrsula Braun if (msg->tag == IUCV_SKB_CB(list_skb)->tag) {
1753eac3731bSJennifer Hunt this = list_skb;
1754f2a77991SUrsula Braun break;
1755f2a77991SUrsula Braun }
1756f2a77991SUrsula Braun }
1757ef6af7bdSJulian Wiedmann if (this) {
1758ef6af7bdSJulian Wiedmann atomic_dec(&iucv->skbs_in_xmit);
1759d4444722SUrsula Braun __skb_unlink(this, list);
1760ef6af7bdSJulian Wiedmann }
1761ef6af7bdSJulian Wiedmann
1762eac3731bSJennifer Hunt spin_unlock_irqrestore(&list->lock, flags);
1763eac3731bSJennifer Hunt
17640ea920d2SHendrik Brueckner if (this) {
176510d6393dSJulian Wiedmann consume_skb(this);
17660ea920d2SHendrik Brueckner /* wake up any process waiting for sending */
17670ea920d2SHendrik Brueckner iucv_sock_wake_msglim(sk);
17680ea920d2SHendrik Brueckner }
1769eac3731bSJennifer Hunt
1770561e0360SJennifer Hunt if (sk->sk_state == IUCV_CLOSING) {
1771ef6af7bdSJulian Wiedmann if (atomic_read(&iucv->skbs_in_xmit) == 0) {
1772561e0360SJennifer Hunt sk->sk_state = IUCV_CLOSED;
1773561e0360SJennifer Hunt sk->sk_state_change(sk);
1774561e0360SJennifer Hunt }
1775561e0360SJennifer Hunt }
17767d316b94SUrsula Braun bh_unlock_sock(sk);
1777561e0360SJennifer Hunt
1778561e0360SJennifer Hunt }
1779561e0360SJennifer Hunt
iucv_callback_connrej(struct iucv_path * path,u8 ipuser[16])1780eac3731bSJennifer Hunt static void iucv_callback_connrej(struct iucv_path *path, u8 ipuser[16])
1781eac3731bSJennifer Hunt {
1782eac3731bSJennifer Hunt struct sock *sk = path->private;
1783eac3731bSJennifer Hunt
17847d316b94SUrsula Braun if (sk->sk_state == IUCV_CLOSED)
17857d316b94SUrsula Braun return;
17867d316b94SUrsula Braun
17877d316b94SUrsula Braun bh_lock_sock(sk);
17887d316b94SUrsula Braun iucv_sever_path(sk, 1);
1789eac3731bSJennifer Hunt sk->sk_state = IUCV_DISCONN;
1790eac3731bSJennifer Hunt
1791eac3731bSJennifer Hunt sk->sk_state_change(sk);
17927d316b94SUrsula Braun bh_unlock_sock(sk);
1793eac3731bSJennifer Hunt }
1794eac3731bSJennifer Hunt
1795af88b52dSHendrik Brueckner /* called if the other communication side shuts down its RECV direction;
1796af88b52dSHendrik Brueckner * in turn, the callback sets SEND_SHUTDOWN to disable sending of data.
1797af88b52dSHendrik Brueckner */
iucv_callback_shutdown(struct iucv_path * path,u8 ipuser[16])1798af88b52dSHendrik Brueckner static void iucv_callback_shutdown(struct iucv_path *path, u8 ipuser[16])
1799af88b52dSHendrik Brueckner {
1800af88b52dSHendrik Brueckner struct sock *sk = path->private;
1801af88b52dSHendrik Brueckner
1802af88b52dSHendrik Brueckner bh_lock_sock(sk);
1803af88b52dSHendrik Brueckner if (sk->sk_state != IUCV_CLOSED) {
1804af88b52dSHendrik Brueckner sk->sk_shutdown |= SEND_SHUTDOWN;
1805af88b52dSHendrik Brueckner sk->sk_state_change(sk);
1806af88b52dSHendrik Brueckner }
1807af88b52dSHendrik Brueckner bh_unlock_sock(sk);
1808af88b52dSHendrik Brueckner }
1809af88b52dSHendrik Brueckner
181087c272c6SJulian Wiedmann static struct iucv_handler af_iucv_handler = {
181187c272c6SJulian Wiedmann .path_pending = iucv_callback_connreq,
181287c272c6SJulian Wiedmann .path_complete = iucv_callback_connack,
181387c272c6SJulian Wiedmann .path_severed = iucv_callback_connrej,
181487c272c6SJulian Wiedmann .message_pending = iucv_callback_rx,
181587c272c6SJulian Wiedmann .message_complete = iucv_callback_txdone,
181687c272c6SJulian Wiedmann .path_quiesced = iucv_callback_shutdown,
181787c272c6SJulian Wiedmann };
181887c272c6SJulian Wiedmann
18193881ac44SUrsula Braun /***************** HiperSockets transport callbacks ********************/
afiucv_swap_src_dest(struct sk_buff * skb)18203881ac44SUrsula Braun static void afiucv_swap_src_dest(struct sk_buff *skb)
18213881ac44SUrsula Braun {
1822cd11d112SJulian Wiedmann struct af_iucv_trans_hdr *trans_hdr = iucv_trans_hdr(skb);
18233881ac44SUrsula Braun char tmpID[8];
18243881ac44SUrsula Braun char tmpName[8];
18253881ac44SUrsula Braun
18263881ac44SUrsula Braun ASCEBC(trans_hdr->destUserID, sizeof(trans_hdr->destUserID));
18273881ac44SUrsula Braun ASCEBC(trans_hdr->destAppName, sizeof(trans_hdr->destAppName));
18283881ac44SUrsula Braun ASCEBC(trans_hdr->srcUserID, sizeof(trans_hdr->srcUserID));
18293881ac44SUrsula Braun ASCEBC(trans_hdr->srcAppName, sizeof(trans_hdr->srcAppName));
18303881ac44SUrsula Braun memcpy(tmpID, trans_hdr->srcUserID, 8);
18313881ac44SUrsula Braun memcpy(tmpName, trans_hdr->srcAppName, 8);
18323881ac44SUrsula Braun memcpy(trans_hdr->srcUserID, trans_hdr->destUserID, 8);
18333881ac44SUrsula Braun memcpy(trans_hdr->srcAppName, trans_hdr->destAppName, 8);
18343881ac44SUrsula Braun memcpy(trans_hdr->destUserID, tmpID, 8);
18353881ac44SUrsula Braun memcpy(trans_hdr->destAppName, tmpName, 8);
18363881ac44SUrsula Braun skb_push(skb, ETH_HLEN);
18373881ac44SUrsula Braun memset(skb->data, 0, ETH_HLEN);
18383881ac44SUrsula Braun }
18393881ac44SUrsula Braun
18407c8e1a91SHeiko Carstens /*
18413881ac44SUrsula Braun * afiucv_hs_callback_syn - react on received SYN
18427c8e1a91SHeiko Carstens */
afiucv_hs_callback_syn(struct sock * sk,struct sk_buff * skb)18433881ac44SUrsula Braun static int afiucv_hs_callback_syn(struct sock *sk, struct sk_buff *skb)
18443881ac44SUrsula Braun {
1845cd11d112SJulian Wiedmann struct af_iucv_trans_hdr *trans_hdr = iucv_trans_hdr(skb);
18463881ac44SUrsula Braun struct sock *nsk;
18473881ac44SUrsula Braun struct iucv_sock *iucv, *niucv;
18483881ac44SUrsula Braun int err;
18493881ac44SUrsula Braun
18503881ac44SUrsula Braun iucv = iucv_sk(sk);
18513881ac44SUrsula Braun if (!iucv) {
18523881ac44SUrsula Braun /* no sock - connection refused */
18533881ac44SUrsula Braun afiucv_swap_src_dest(skb);
18543881ac44SUrsula Braun trans_hdr->flags = AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_FIN;
18553881ac44SUrsula Braun err = dev_queue_xmit(skb);
18563881ac44SUrsula Braun goto out;
18573881ac44SUrsula Braun }
18583881ac44SUrsula Braun
1859c5dab094SJulian Wiedmann nsk = iucv_sock_alloc(NULL, sk->sk_protocol, GFP_ATOMIC, 0);
18603881ac44SUrsula Braun bh_lock_sock(sk);
18613881ac44SUrsula Braun if ((sk->sk_state != IUCV_LISTEN) ||
18623881ac44SUrsula Braun sk_acceptq_is_full(sk) ||
18633881ac44SUrsula Braun !nsk) {
18643881ac44SUrsula Braun /* error on server socket - connection refused */
18653881ac44SUrsula Braun afiucv_swap_src_dest(skb);
18663881ac44SUrsula Braun trans_hdr->flags = AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_FIN;
18673881ac44SUrsula Braun err = dev_queue_xmit(skb);
18684d520f62SUrsula Braun iucv_sock_kill(nsk);
18693881ac44SUrsula Braun bh_unlock_sock(sk);
18703881ac44SUrsula Braun goto out;
18713881ac44SUrsula Braun }
18723881ac44SUrsula Braun
18733881ac44SUrsula Braun niucv = iucv_sk(nsk);
18743881ac44SUrsula Braun iucv_sock_init(nsk, sk);
18753881ac44SUrsula Braun niucv->transport = AF_IUCV_TRANS_HIPER;
18763881ac44SUrsula Braun niucv->msglimit = iucv->msglimit;
18773881ac44SUrsula Braun if (!trans_hdr->window)
18783881ac44SUrsula Braun niucv->msglimit_peer = IUCV_HIPER_MSGLIM_DEFAULT;
18793881ac44SUrsula Braun else
18803881ac44SUrsula Braun niucv->msglimit_peer = trans_hdr->window;
18813881ac44SUrsula Braun memcpy(niucv->dst_name, trans_hdr->srcAppName, 8);
18823881ac44SUrsula Braun memcpy(niucv->dst_user_id, trans_hdr->srcUserID, 8);
18833881ac44SUrsula Braun memcpy(niucv->src_name, iucv->src_name, 8);
18843881ac44SUrsula Braun memcpy(niucv->src_user_id, iucv->src_user_id, 8);
18853881ac44SUrsula Braun nsk->sk_bound_dev_if = sk->sk_bound_dev_if;
1886800c5eb7SUrsula Braun niucv->hs_dev = iucv->hs_dev;
1887800c5eb7SUrsula Braun dev_hold(niucv->hs_dev);
18883881ac44SUrsula Braun afiucv_swap_src_dest(skb);
18893881ac44SUrsula Braun trans_hdr->flags = AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_ACK;
18903881ac44SUrsula Braun trans_hdr->window = niucv->msglimit;
18913881ac44SUrsula Braun /* if receiver acks the xmit connection is established */
18923881ac44SUrsula Braun err = dev_queue_xmit(skb);
18933881ac44SUrsula Braun if (!err) {
18943881ac44SUrsula Braun iucv_accept_enqueue(sk, nsk);
18953881ac44SUrsula Braun nsk->sk_state = IUCV_CONNECTED;
1896676d2369SDavid S. Miller sk->sk_data_ready(sk);
18973881ac44SUrsula Braun } else
18983881ac44SUrsula Braun iucv_sock_kill(nsk);
18993881ac44SUrsula Braun bh_unlock_sock(sk);
19003881ac44SUrsula Braun
19013881ac44SUrsula Braun out:
19023881ac44SUrsula Braun return NET_RX_SUCCESS;
19033881ac44SUrsula Braun }
19043881ac44SUrsula Braun
19057c8e1a91SHeiko Carstens /*
19063881ac44SUrsula Braun * afiucv_hs_callback_synack() - react on received SYN-ACK
19077c8e1a91SHeiko Carstens */
afiucv_hs_callback_synack(struct sock * sk,struct sk_buff * skb)19083881ac44SUrsula Braun static int afiucv_hs_callback_synack(struct sock *sk, struct sk_buff *skb)
19093881ac44SUrsula Braun {
19103881ac44SUrsula Braun struct iucv_sock *iucv = iucv_sk(sk);
19113881ac44SUrsula Braun
191210d6393dSJulian Wiedmann if (!iucv || sk->sk_state != IUCV_BOUND) {
191310d6393dSJulian Wiedmann kfree_skb(skb);
191410d6393dSJulian Wiedmann return NET_RX_SUCCESS;
191510d6393dSJulian Wiedmann }
191610d6393dSJulian Wiedmann
19173881ac44SUrsula Braun bh_lock_sock(sk);
1918cd11d112SJulian Wiedmann iucv->msglimit_peer = iucv_trans_hdr(skb)->window;
19193881ac44SUrsula Braun sk->sk_state = IUCV_CONNECTED;
19203881ac44SUrsula Braun sk->sk_state_change(sk);
19213881ac44SUrsula Braun bh_unlock_sock(sk);
192210d6393dSJulian Wiedmann consume_skb(skb);
19233881ac44SUrsula Braun return NET_RX_SUCCESS;
19243881ac44SUrsula Braun }
19253881ac44SUrsula Braun
19267c8e1a91SHeiko Carstens /*
19273881ac44SUrsula Braun * afiucv_hs_callback_synfin() - react on received SYN_FIN
19287c8e1a91SHeiko Carstens */
afiucv_hs_callback_synfin(struct sock * sk,struct sk_buff * skb)19293881ac44SUrsula Braun static int afiucv_hs_callback_synfin(struct sock *sk, struct sk_buff *skb)
19303881ac44SUrsula Braun {
19313881ac44SUrsula Braun struct iucv_sock *iucv = iucv_sk(sk);
19323881ac44SUrsula Braun
193310d6393dSJulian Wiedmann if (!iucv || sk->sk_state != IUCV_BOUND) {
193410d6393dSJulian Wiedmann kfree_skb(skb);
193510d6393dSJulian Wiedmann return NET_RX_SUCCESS;
193610d6393dSJulian Wiedmann }
193710d6393dSJulian Wiedmann
19383881ac44SUrsula Braun bh_lock_sock(sk);
19393881ac44SUrsula Braun sk->sk_state = IUCV_DISCONN;
19403881ac44SUrsula Braun sk->sk_state_change(sk);
19413881ac44SUrsula Braun bh_unlock_sock(sk);
194210d6393dSJulian Wiedmann consume_skb(skb);
19433881ac44SUrsula Braun return NET_RX_SUCCESS;
19443881ac44SUrsula Braun }
19453881ac44SUrsula Braun
19467c8e1a91SHeiko Carstens /*
19473881ac44SUrsula Braun * afiucv_hs_callback_fin() - react on received FIN
19487c8e1a91SHeiko Carstens */
afiucv_hs_callback_fin(struct sock * sk,struct sk_buff * skb)19493881ac44SUrsula Braun static int afiucv_hs_callback_fin(struct sock *sk, struct sk_buff *skb)
19503881ac44SUrsula Braun {
19513881ac44SUrsula Braun struct iucv_sock *iucv = iucv_sk(sk);
19523881ac44SUrsula Braun
19533881ac44SUrsula Braun /* other end of connection closed */
195410d6393dSJulian Wiedmann if (!iucv) {
195510d6393dSJulian Wiedmann kfree_skb(skb);
195610d6393dSJulian Wiedmann return NET_RX_SUCCESS;
195710d6393dSJulian Wiedmann }
195810d6393dSJulian Wiedmann
19593881ac44SUrsula Braun bh_lock_sock(sk);
1960800c5eb7SUrsula Braun if (sk->sk_state == IUCV_CONNECTED) {
19613881ac44SUrsula Braun sk->sk_state = IUCV_DISCONN;
19623881ac44SUrsula Braun sk->sk_state_change(sk);
19633881ac44SUrsula Braun }
1964800c5eb7SUrsula Braun bh_unlock_sock(sk);
196510d6393dSJulian Wiedmann consume_skb(skb);
19663881ac44SUrsula Braun return NET_RX_SUCCESS;
19673881ac44SUrsula Braun }
19683881ac44SUrsula Braun
19697c8e1a91SHeiko Carstens /*
19703881ac44SUrsula Braun * afiucv_hs_callback_win() - react on received WIN
19717c8e1a91SHeiko Carstens */
afiucv_hs_callback_win(struct sock * sk,struct sk_buff * skb)19723881ac44SUrsula Braun static int afiucv_hs_callback_win(struct sock *sk, struct sk_buff *skb)
19733881ac44SUrsula Braun {
19743881ac44SUrsula Braun struct iucv_sock *iucv = iucv_sk(sk);
19753881ac44SUrsula Braun
19763881ac44SUrsula Braun if (!iucv)
19773881ac44SUrsula Braun return NET_RX_SUCCESS;
19783881ac44SUrsula Braun
19793881ac44SUrsula Braun if (sk->sk_state != IUCV_CONNECTED)
19803881ac44SUrsula Braun return NET_RX_SUCCESS;
19813881ac44SUrsula Braun
1982cd11d112SJulian Wiedmann atomic_sub(iucv_trans_hdr(skb)->window, &iucv->msg_sent);
19833881ac44SUrsula Braun iucv_sock_wake_msglim(sk);
19843881ac44SUrsula Braun return NET_RX_SUCCESS;
19853881ac44SUrsula Braun }
19863881ac44SUrsula Braun
19877c8e1a91SHeiko Carstens /*
19883881ac44SUrsula Braun * afiucv_hs_callback_rx() - react on received data
19897c8e1a91SHeiko Carstens */
afiucv_hs_callback_rx(struct sock * sk,struct sk_buff * skb)19903881ac44SUrsula Braun static int afiucv_hs_callback_rx(struct sock *sk, struct sk_buff *skb)
19913881ac44SUrsula Braun {
19923881ac44SUrsula Braun struct iucv_sock *iucv = iucv_sk(sk);
19933881ac44SUrsula Braun
19943881ac44SUrsula Braun if (!iucv) {
19953881ac44SUrsula Braun kfree_skb(skb);
19963881ac44SUrsula Braun return NET_RX_SUCCESS;
19973881ac44SUrsula Braun }
19983881ac44SUrsula Braun
19993881ac44SUrsula Braun if (sk->sk_state != IUCV_CONNECTED) {
20003881ac44SUrsula Braun kfree_skb(skb);
20013881ac44SUrsula Braun return NET_RX_SUCCESS;
20023881ac44SUrsula Braun }
20033881ac44SUrsula Braun
200482492a35SUrsula Braun if (sk->sk_shutdown & RCV_SHUTDOWN) {
200582492a35SUrsula Braun kfree_skb(skb);
200682492a35SUrsula Braun return NET_RX_SUCCESS;
200782492a35SUrsula Braun }
200882492a35SUrsula Braun
20093881ac44SUrsula Braun /* write stuff from iucv_msg to skb cb */
20103881ac44SUrsula Braun skb_pull(skb, sizeof(struct af_iucv_trans_hdr));
20113881ac44SUrsula Braun skb_reset_transport_header(skb);
20123881ac44SUrsula Braun skb_reset_network_header(skb);
2013f9c41a62SUrsula Braun IUCV_SKB_CB(skb)->offset = 0;
20148c68b1a0SUrsula Braun if (sk_filter(sk, skb)) {
20158c68b1a0SUrsula Braun atomic_inc(&sk->sk_drops); /* skb rejected by filter */
20168c68b1a0SUrsula Braun kfree_skb(skb);
20178c68b1a0SUrsula Braun return NET_RX_SUCCESS;
20188c68b1a0SUrsula Braun }
20198c68b1a0SUrsula Braun
20203881ac44SUrsula Braun spin_lock(&iucv->message_q.lock);
20213881ac44SUrsula Braun if (skb_queue_empty(&iucv->backlog_skb_q)) {
20228c68b1a0SUrsula Braun if (__sock_queue_rcv_skb(sk, skb))
20233881ac44SUrsula Braun /* handle rcv queue full */
20243881ac44SUrsula Braun skb_queue_tail(&iucv->backlog_skb_q, skb);
20253881ac44SUrsula Braun } else
20263881ac44SUrsula Braun skb_queue_tail(&iucv_sk(sk)->backlog_skb_q, skb);
20273881ac44SUrsula Braun spin_unlock(&iucv->message_q.lock);
20283881ac44SUrsula Braun return NET_RX_SUCCESS;
20293881ac44SUrsula Braun }
20303881ac44SUrsula Braun
20317c8e1a91SHeiko Carstens /*
20323881ac44SUrsula Braun * afiucv_hs_rcv() - base function for arriving data through HiperSockets
20333881ac44SUrsula Braun * transport
20343881ac44SUrsula Braun * called from netif RX softirq
20357c8e1a91SHeiko Carstens */
afiucv_hs_rcv(struct sk_buff * skb,struct net_device * dev,struct packet_type * pt,struct net_device * orig_dev)20363881ac44SUrsula Braun static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev,
20373881ac44SUrsula Braun struct packet_type *pt, struct net_device *orig_dev)
20383881ac44SUrsula Braun {
20393881ac44SUrsula Braun struct sock *sk;
20403881ac44SUrsula Braun struct iucv_sock *iucv;
20413881ac44SUrsula Braun struct af_iucv_trans_hdr *trans_hdr;
204222244099SJulian Wiedmann int err = NET_RX_SUCCESS;
20433881ac44SUrsula Braun char nullstring[8];
20443881ac44SUrsula Braun
2045cd11d112SJulian Wiedmann if (!pskb_may_pull(skb, sizeof(*trans_hdr))) {
2046979f66b3SEugene Crosser kfree_skb(skb);
2047979f66b3SEugene Crosser return NET_RX_SUCCESS;
2048979f66b3SEugene Crosser }
2049cd11d112SJulian Wiedmann
2050cd11d112SJulian Wiedmann trans_hdr = iucv_trans_hdr(skb);
20513881ac44SUrsula Braun EBCASC(trans_hdr->destAppName, sizeof(trans_hdr->destAppName));
20523881ac44SUrsula Braun EBCASC(trans_hdr->destUserID, sizeof(trans_hdr->destUserID));
20533881ac44SUrsula Braun EBCASC(trans_hdr->srcAppName, sizeof(trans_hdr->srcAppName));
20543881ac44SUrsula Braun EBCASC(trans_hdr->srcUserID, sizeof(trans_hdr->srcUserID));
20553881ac44SUrsula Braun memset(nullstring, 0, sizeof(nullstring));
20563881ac44SUrsula Braun iucv = NULL;
20573881ac44SUrsula Braun sk = NULL;
20583881ac44SUrsula Braun read_lock(&iucv_sk_list.lock);
2059b67bfe0dSSasha Levin sk_for_each(sk, &iucv_sk_list.head) {
20603881ac44SUrsula Braun if (trans_hdr->flags == AF_IUCV_FLAG_SYN) {
20613881ac44SUrsula Braun if ((!memcmp(&iucv_sk(sk)->src_name,
20623881ac44SUrsula Braun trans_hdr->destAppName, 8)) &&
20633881ac44SUrsula Braun (!memcmp(&iucv_sk(sk)->src_user_id,
20643881ac44SUrsula Braun trans_hdr->destUserID, 8)) &&
20653881ac44SUrsula Braun (!memcmp(&iucv_sk(sk)->dst_name, nullstring, 8)) &&
20663881ac44SUrsula Braun (!memcmp(&iucv_sk(sk)->dst_user_id,
20673881ac44SUrsula Braun nullstring, 8))) {
20683881ac44SUrsula Braun iucv = iucv_sk(sk);
20693881ac44SUrsula Braun break;
20703881ac44SUrsula Braun }
20713881ac44SUrsula Braun } else {
20723881ac44SUrsula Braun if ((!memcmp(&iucv_sk(sk)->src_name,
20733881ac44SUrsula Braun trans_hdr->destAppName, 8)) &&
20743881ac44SUrsula Braun (!memcmp(&iucv_sk(sk)->src_user_id,
20753881ac44SUrsula Braun trans_hdr->destUserID, 8)) &&
20763881ac44SUrsula Braun (!memcmp(&iucv_sk(sk)->dst_name,
20773881ac44SUrsula Braun trans_hdr->srcAppName, 8)) &&
20783881ac44SUrsula Braun (!memcmp(&iucv_sk(sk)->dst_user_id,
20793881ac44SUrsula Braun trans_hdr->srcUserID, 8))) {
20803881ac44SUrsula Braun iucv = iucv_sk(sk);
20813881ac44SUrsula Braun break;
20823881ac44SUrsula Braun }
20833881ac44SUrsula Braun }
20843881ac44SUrsula Braun }
20853881ac44SUrsula Braun read_unlock(&iucv_sk_list.lock);
20863881ac44SUrsula Braun if (!iucv)
20873881ac44SUrsula Braun sk = NULL;
20883881ac44SUrsula Braun
20893881ac44SUrsula Braun /* no sock
20903881ac44SUrsula Braun how should we send with no sock
20913881ac44SUrsula Braun 1) send without sock no send rc checking?
20923881ac44SUrsula Braun 2) introduce default sock to handle this cases
20933881ac44SUrsula Braun
20943881ac44SUrsula Braun SYN -> send SYN|ACK in good case, send SYN|FIN in bad case
20953881ac44SUrsula Braun data -> send FIN
20963881ac44SUrsula Braun SYN|ACK, SYN|FIN, FIN -> no action? */
20973881ac44SUrsula Braun
20983881ac44SUrsula Braun switch (trans_hdr->flags) {
20993881ac44SUrsula Braun case AF_IUCV_FLAG_SYN:
21003881ac44SUrsula Braun /* connect request */
21013881ac44SUrsula Braun err = afiucv_hs_callback_syn(sk, skb);
21023881ac44SUrsula Braun break;
21033881ac44SUrsula Braun case (AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_ACK):
21043881ac44SUrsula Braun /* connect request confirmed */
21053881ac44SUrsula Braun err = afiucv_hs_callback_synack(sk, skb);
21063881ac44SUrsula Braun break;
21073881ac44SUrsula Braun case (AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_FIN):
21083881ac44SUrsula Braun /* connect request refused */
21093881ac44SUrsula Braun err = afiucv_hs_callback_synfin(sk, skb);
21103881ac44SUrsula Braun break;
21113881ac44SUrsula Braun case (AF_IUCV_FLAG_FIN):
21123881ac44SUrsula Braun /* close request */
21133881ac44SUrsula Braun err = afiucv_hs_callback_fin(sk, skb);
21143881ac44SUrsula Braun break;
21153881ac44SUrsula Braun case (AF_IUCV_FLAG_WIN):
21163881ac44SUrsula Braun err = afiucv_hs_callback_win(sk, skb);
2117800c5eb7SUrsula Braun if (skb->len == sizeof(struct af_iucv_trans_hdr)) {
211810d6393dSJulian Wiedmann consume_skb(skb);
21193881ac44SUrsula Braun break;
2120800c5eb7SUrsula Braun }
2121df561f66SGustavo A. R. Silva fallthrough; /* and receive non-zero length data */
212282492a35SUrsula Braun case (AF_IUCV_FLAG_SHT):
212382492a35SUrsula Braun /* shutdown request */
2124df561f66SGustavo A. R. Silva fallthrough; /* and receive zero length data */
21253881ac44SUrsula Braun case 0:
21263881ac44SUrsula Braun /* plain data frame */
2127f9c41a62SUrsula Braun IUCV_SKB_CB(skb)->class = trans_hdr->iucv_hdr.class;
21283881ac44SUrsula Braun err = afiucv_hs_callback_rx(sk, skb);
21293881ac44SUrsula Braun break;
21303881ac44SUrsula Braun default:
213122244099SJulian Wiedmann kfree_skb(skb);
21323881ac44SUrsula Braun }
21333881ac44SUrsula Braun
21343881ac44SUrsula Braun return err;
21353881ac44SUrsula Braun }
21363881ac44SUrsula Braun
21377c8e1a91SHeiko Carstens /*
2138bf05d48dSBhaskar Chowdhury * afiucv_hs_callback_txnotify() - handle send notifications from HiperSockets
21393881ac44SUrsula Braun * transport
21407c8e1a91SHeiko Carstens */
afiucv_hs_callback_txnotify(struct sock * sk,enum iucv_tx_notify n)214180bc97aaSJulian Wiedmann static void afiucv_hs_callback_txnotify(struct sock *sk, enum iucv_tx_notify n)
21423881ac44SUrsula Braun {
214380bc97aaSJulian Wiedmann struct iucv_sock *iucv = iucv_sk(sk);
21443881ac44SUrsula Braun
2145c464444fSJulian Wiedmann if (sock_flag(sk, SOCK_ZAPPED))
21463881ac44SUrsula Braun return;
21473881ac44SUrsula Braun
21483881ac44SUrsula Braun switch (n) {
21493881ac44SUrsula Braun case TX_NOTIFY_OK:
2150ef6af7bdSJulian Wiedmann atomic_dec(&iucv->skbs_in_xmit);
21513881ac44SUrsula Braun iucv_sock_wake_msglim(sk);
21523881ac44SUrsula Braun break;
21533881ac44SUrsula Braun case TX_NOTIFY_PENDING:
21543881ac44SUrsula Braun atomic_inc(&iucv->pendings);
21553881ac44SUrsula Braun break;
21563881ac44SUrsula Braun case TX_NOTIFY_DELAYED_OK:
2157ef6af7bdSJulian Wiedmann atomic_dec(&iucv->skbs_in_xmit);
215880bc97aaSJulian Wiedmann if (atomic_dec_return(&iucv->pendings) <= 0)
21593881ac44SUrsula Braun iucv_sock_wake_msglim(sk);
21603881ac44SUrsula Braun break;
216180bc97aaSJulian Wiedmann default:
2162ef6af7bdSJulian Wiedmann atomic_dec(&iucv->skbs_in_xmit);
2163800c5eb7SUrsula Braun if (sk->sk_state == IUCV_CONNECTED) {
21643881ac44SUrsula Braun sk->sk_state = IUCV_DISCONN;
21653881ac44SUrsula Braun sk->sk_state_change(sk);
2166800c5eb7SUrsula Braun }
21673881ac44SUrsula Braun }
21683881ac44SUrsula Braun
216942bd48e0SUrsula Braun if (sk->sk_state == IUCV_CLOSING) {
2170ef6af7bdSJulian Wiedmann if (atomic_read(&iucv->skbs_in_xmit) == 0) {
217142bd48e0SUrsula Braun sk->sk_state = IUCV_CLOSED;
217242bd48e0SUrsula Braun sk->sk_state_change(sk);
217342bd48e0SUrsula Braun }
217442bd48e0SUrsula Braun }
21753881ac44SUrsula Braun }
21769fbd87d4SUrsula Braun
21779fbd87d4SUrsula Braun /*
21789fbd87d4SUrsula Braun * afiucv_netdev_event: handle netdev notifier chain events
21799fbd87d4SUrsula Braun */
afiucv_netdev_event(struct notifier_block * this,unsigned long event,void * ptr)21809fbd87d4SUrsula Braun static int afiucv_netdev_event(struct notifier_block *this,
21819fbd87d4SUrsula Braun unsigned long event, void *ptr)
21829fbd87d4SUrsula Braun {
2183351638e7SJiri Pirko struct net_device *event_dev = netdev_notifier_info_to_dev(ptr);
21849fbd87d4SUrsula Braun struct sock *sk;
21859fbd87d4SUrsula Braun struct iucv_sock *iucv;
21869fbd87d4SUrsula Braun
21879fbd87d4SUrsula Braun switch (event) {
21889fbd87d4SUrsula Braun case NETDEV_REBOOT:
21899fbd87d4SUrsula Braun case NETDEV_GOING_DOWN:
2190b67bfe0dSSasha Levin sk_for_each(sk, &iucv_sk_list.head) {
21919fbd87d4SUrsula Braun iucv = iucv_sk(sk);
21929fbd87d4SUrsula Braun if ((iucv->hs_dev == event_dev) &&
21939fbd87d4SUrsula Braun (sk->sk_state == IUCV_CONNECTED)) {
21949fbd87d4SUrsula Braun if (event == NETDEV_GOING_DOWN)
21959fbd87d4SUrsula Braun iucv_send_ctrl(sk, AF_IUCV_FLAG_FIN);
21969fbd87d4SUrsula Braun sk->sk_state = IUCV_DISCONN;
21979fbd87d4SUrsula Braun sk->sk_state_change(sk);
21989fbd87d4SUrsula Braun }
21999fbd87d4SUrsula Braun }
22009fbd87d4SUrsula Braun break;
22019fbd87d4SUrsula Braun case NETDEV_DOWN:
22029fbd87d4SUrsula Braun case NETDEV_UNREGISTER:
22039fbd87d4SUrsula Braun default:
22049fbd87d4SUrsula Braun break;
22059fbd87d4SUrsula Braun }
22069fbd87d4SUrsula Braun return NOTIFY_DONE;
22079fbd87d4SUrsula Braun }
22089fbd87d4SUrsula Braun
22099fbd87d4SUrsula Braun static struct notifier_block afiucv_netdev_notifier = {
22109fbd87d4SUrsula Braun .notifier_call = afiucv_netdev_event,
22119fbd87d4SUrsula Braun };
22129fbd87d4SUrsula Braun
22135708e868SAlexey Dobriyan static const struct proto_ops iucv_sock_ops = {
2214eac3731bSJennifer Hunt .family = PF_IUCV,
2215eac3731bSJennifer Hunt .owner = THIS_MODULE,
2216eac3731bSJennifer Hunt .release = iucv_sock_release,
2217eac3731bSJennifer Hunt .bind = iucv_sock_bind,
2218eac3731bSJennifer Hunt .connect = iucv_sock_connect,
2219eac3731bSJennifer Hunt .listen = iucv_sock_listen,
2220eac3731bSJennifer Hunt .accept = iucv_sock_accept,
2221eac3731bSJennifer Hunt .getname = iucv_sock_getname,
2222eac3731bSJennifer Hunt .sendmsg = iucv_sock_sendmsg,
2223eac3731bSJennifer Hunt .recvmsg = iucv_sock_recvmsg,
2224a11e1d43SLinus Torvalds .poll = iucv_sock_poll,
2225eac3731bSJennifer Hunt .ioctl = sock_no_ioctl,
2226eac3731bSJennifer Hunt .mmap = sock_no_mmap,
2227eac3731bSJennifer Hunt .socketpair = sock_no_socketpair,
2228eac3731bSJennifer Hunt .shutdown = iucv_sock_shutdown,
22299d5c5d8fSHendrik Brueckner .setsockopt = iucv_sock_setsockopt,
22309d5c5d8fSHendrik Brueckner .getsockopt = iucv_sock_getsockopt,
2231eac3731bSJennifer Hunt };
2232eac3731bSJennifer Hunt
iucv_sock_create(struct net * net,struct socket * sock,int protocol,int kern)2233e9a36ca5SJulian Wiedmann static int iucv_sock_create(struct net *net, struct socket *sock, int protocol,
2234e9a36ca5SJulian Wiedmann int kern)
2235e9a36ca5SJulian Wiedmann {
2236e9a36ca5SJulian Wiedmann struct sock *sk;
2237e9a36ca5SJulian Wiedmann
2238e9a36ca5SJulian Wiedmann if (protocol && protocol != PF_IUCV)
2239e9a36ca5SJulian Wiedmann return -EPROTONOSUPPORT;
2240e9a36ca5SJulian Wiedmann
2241e9a36ca5SJulian Wiedmann sock->state = SS_UNCONNECTED;
2242e9a36ca5SJulian Wiedmann
2243e9a36ca5SJulian Wiedmann switch (sock->type) {
2244e9a36ca5SJulian Wiedmann case SOCK_STREAM:
2245e9a36ca5SJulian Wiedmann case SOCK_SEQPACKET:
2246e9a36ca5SJulian Wiedmann /* currently, proto ops can handle both sk types */
2247e9a36ca5SJulian Wiedmann sock->ops = &iucv_sock_ops;
2248e9a36ca5SJulian Wiedmann break;
2249e9a36ca5SJulian Wiedmann default:
2250e9a36ca5SJulian Wiedmann return -ESOCKTNOSUPPORT;
2251e9a36ca5SJulian Wiedmann }
2252e9a36ca5SJulian Wiedmann
2253e9a36ca5SJulian Wiedmann sk = iucv_sock_alloc(sock, protocol, GFP_KERNEL, kern);
2254e9a36ca5SJulian Wiedmann if (!sk)
2255e9a36ca5SJulian Wiedmann return -ENOMEM;
2256e9a36ca5SJulian Wiedmann
2257e9a36ca5SJulian Wiedmann iucv_sock_init(sk, NULL);
2258e9a36ca5SJulian Wiedmann
2259e9a36ca5SJulian Wiedmann return 0;
2260e9a36ca5SJulian Wiedmann }
2261e9a36ca5SJulian Wiedmann
2262ec1b4cf7SStephen Hemminger static const struct net_proto_family iucv_sock_family_ops = {
2263eac3731bSJennifer Hunt .family = AF_IUCV,
2264eac3731bSJennifer Hunt .owner = THIS_MODULE,
2265eac3731bSJennifer Hunt .create = iucv_sock_create,
2266eac3731bSJennifer Hunt };
2267eac3731bSJennifer Hunt
22683881ac44SUrsula Braun static struct packet_type iucv_packet_type = {
22693881ac44SUrsula Braun .type = cpu_to_be16(ETH_P_AF_IUCV),
22703881ac44SUrsula Braun .func = afiucv_hs_rcv,
22713881ac44SUrsula Braun };
22723881ac44SUrsula Braun
afiucv_init(void)2273da99f056SHeiko Carstens static int __init afiucv_init(void)
2274eac3731bSJennifer Hunt {
2275eac3731bSJennifer Hunt int err;
2276eac3731bSJennifer Hunt
22774eb9eda6SJulian Wiedmann if (MACHINE_IS_VM && IS_ENABLED(CONFIG_IUCV)) {
2278eac3731bSJennifer Hunt cpcmd("QUERY USERID", iucv_userid, sizeof(iucv_userid), &err);
2279eac3731bSJennifer Hunt if (unlikely(err)) {
2280c2b4afd2SUrsula Braun WARN_ON(err);
2281eac3731bSJennifer Hunt err = -EPROTONOSUPPORT;
2282eac3731bSJennifer Hunt goto out;
2283eac3731bSJennifer Hunt }
2284eac3731bSJennifer Hunt
22854eb9eda6SJulian Wiedmann pr_iucv = &iucv_if;
22863881ac44SUrsula Braun } else {
22873881ac44SUrsula Braun memset(&iucv_userid, 0, sizeof(iucv_userid));
22883881ac44SUrsula Braun pr_iucv = NULL;
22896fcd61f7SFrank Blaschka }
22906fcd61f7SFrank Blaschka
2291eac3731bSJennifer Hunt err = proto_register(&iucv_proto, 0);
2292eac3731bSJennifer Hunt if (err)
22936fcd61f7SFrank Blaschka goto out;
2294eac3731bSJennifer Hunt err = sock_register(&iucv_sock_family_ops);
2295eac3731bSJennifer Hunt if (err)
2296eac3731bSJennifer Hunt goto out_proto;
22976fcd61f7SFrank Blaschka
22983881ac44SUrsula Braun if (pr_iucv) {
2299ff8424beSJulian Wiedmann err = pr_iucv->iucv_register(&af_iucv_handler, 0);
2300c23cad92SUrsula Braun if (err)
2301c23cad92SUrsula Braun goto out_sock;
230206996c1dSJulian Wiedmann }
230306996c1dSJulian Wiedmann
230406996c1dSJulian Wiedmann err = register_netdevice_notifier(&afiucv_netdev_notifier);
230506996c1dSJulian Wiedmann if (err)
230606996c1dSJulian Wiedmann goto out_notifier;
230706996c1dSJulian Wiedmann
23083881ac44SUrsula Braun dev_add_pack(&iucv_packet_type);
2309eac3731bSJennifer Hunt return 0;
2310eac3731bSJennifer Hunt
231106996c1dSJulian Wiedmann out_notifier:
231206996c1dSJulian Wiedmann if (pr_iucv)
2313ff8424beSJulian Wiedmann pr_iucv->iucv_unregister(&af_iucv_handler, 0);
2314c23cad92SUrsula Braun out_sock:
2315c23cad92SUrsula Braun sock_unregister(PF_IUCV);
2316eac3731bSJennifer Hunt out_proto:
2317eac3731bSJennifer Hunt proto_unregister(&iucv_proto);
2318eac3731bSJennifer Hunt out:
2319eac3731bSJennifer Hunt return err;
2320eac3731bSJennifer Hunt }
2321eac3731bSJennifer Hunt
afiucv_exit(void)2322eac3731bSJennifer Hunt static void __exit afiucv_exit(void)
2323eac3731bSJennifer Hunt {
23244eb9eda6SJulian Wiedmann if (pr_iucv)
2325ff8424beSJulian Wiedmann pr_iucv->iucv_unregister(&af_iucv_handler, 0);
232606996c1dSJulian Wiedmann
23279fbd87d4SUrsula Braun unregister_netdevice_notifier(&afiucv_netdev_notifier);
23283881ac44SUrsula Braun dev_remove_pack(&iucv_packet_type);
2329eac3731bSJennifer Hunt sock_unregister(PF_IUCV);
2330eac3731bSJennifer Hunt proto_unregister(&iucv_proto);
2331eac3731bSJennifer Hunt }
2332eac3731bSJennifer Hunt
2333eac3731bSJennifer Hunt module_init(afiucv_init);
2334eac3731bSJennifer Hunt module_exit(afiucv_exit);
2335eac3731bSJennifer Hunt
2336eac3731bSJennifer Hunt MODULE_AUTHOR("Jennifer Hunt <jenhunt@us.ibm.com>");
2337eac3731bSJennifer Hunt MODULE_DESCRIPTION("IUCV Sockets ver " VERSION);
2338eac3731bSJennifer Hunt MODULE_VERSION(VERSION);
2339eac3731bSJennifer Hunt MODULE_LICENSE("GPL");
2340eac3731bSJennifer Hunt MODULE_ALIAS_NETPROTO(PF_IUCV);
2341