18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  IUCV protocol stack for Linux on zSeries
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright IBM Corp. 2006, 2009
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  Author(s):	Jennifer Hunt <jenhunt@us.ibm.com>
88c2ecf20Sopenharmony_ci *		Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
98c2ecf20Sopenharmony_ci *  PM functions:
108c2ecf20Sopenharmony_ci *		Ursula Braun <ursula.braun@de.ibm.com>
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "af_iucv"
148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
188c2ecf20Sopenharmony_ci#include <linux/types.h>
198c2ecf20Sopenharmony_ci#include <linux/limits.h>
208c2ecf20Sopenharmony_ci#include <linux/list.h>
218c2ecf20Sopenharmony_ci#include <linux/errno.h>
228c2ecf20Sopenharmony_ci#include <linux/kernel.h>
238c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
248c2ecf20Sopenharmony_ci#include <linux/slab.h>
258c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
268c2ecf20Sopenharmony_ci#include <linux/init.h>
278c2ecf20Sopenharmony_ci#include <linux/poll.h>
288c2ecf20Sopenharmony_ci#include <linux/security.h>
298c2ecf20Sopenharmony_ci#include <net/sock.h>
308c2ecf20Sopenharmony_ci#include <asm/ebcdic.h>
318c2ecf20Sopenharmony_ci#include <asm/cpcmd.h>
328c2ecf20Sopenharmony_ci#include <linux/kmod.h>
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include <net/iucv/af_iucv.h>
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define VERSION "1.2"
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic char iucv_userid[80];
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic struct proto iucv_proto = {
418c2ecf20Sopenharmony_ci	.name		= "AF_IUCV",
428c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
438c2ecf20Sopenharmony_ci	.obj_size	= sizeof(struct iucv_sock),
448c2ecf20Sopenharmony_ci};
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic struct iucv_interface *pr_iucv;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/* special AF_IUCV IPRM messages */
498c2ecf20Sopenharmony_cistatic const u8 iprm_shutdown[8] =
508c2ecf20Sopenharmony_ci	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#define TRGCLS_SIZE	sizeof_field(struct iucv_message, class)
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#define __iucv_sock_wait(sk, condition, timeo, ret)			\
558c2ecf20Sopenharmony_cido {									\
568c2ecf20Sopenharmony_ci	DEFINE_WAIT(__wait);						\
578c2ecf20Sopenharmony_ci	long __timeo = timeo;						\
588c2ecf20Sopenharmony_ci	ret = 0;							\
598c2ecf20Sopenharmony_ci	prepare_to_wait(sk_sleep(sk), &__wait, TASK_INTERRUPTIBLE);	\
608c2ecf20Sopenharmony_ci	while (!(condition)) {						\
618c2ecf20Sopenharmony_ci		if (!__timeo) {						\
628c2ecf20Sopenharmony_ci			ret = -EAGAIN;					\
638c2ecf20Sopenharmony_ci			break;						\
648c2ecf20Sopenharmony_ci		}							\
658c2ecf20Sopenharmony_ci		if (signal_pending(current)) {				\
668c2ecf20Sopenharmony_ci			ret = sock_intr_errno(__timeo);			\
678c2ecf20Sopenharmony_ci			break;						\
688c2ecf20Sopenharmony_ci		}							\
698c2ecf20Sopenharmony_ci		release_sock(sk);					\
708c2ecf20Sopenharmony_ci		__timeo = schedule_timeout(__timeo);			\
718c2ecf20Sopenharmony_ci		lock_sock(sk);						\
728c2ecf20Sopenharmony_ci		ret = sock_error(sk);					\
738c2ecf20Sopenharmony_ci		if (ret)						\
748c2ecf20Sopenharmony_ci			break;						\
758c2ecf20Sopenharmony_ci	}								\
768c2ecf20Sopenharmony_ci	finish_wait(sk_sleep(sk), &__wait);				\
778c2ecf20Sopenharmony_ci} while (0)
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci#define iucv_sock_wait(sk, condition, timeo)				\
808c2ecf20Sopenharmony_ci({									\
818c2ecf20Sopenharmony_ci	int __ret = 0;							\
828c2ecf20Sopenharmony_ci	if (!(condition))						\
838c2ecf20Sopenharmony_ci		__iucv_sock_wait(sk, condition, timeo, __ret);		\
848c2ecf20Sopenharmony_ci	__ret;								\
858c2ecf20Sopenharmony_ci})
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic struct sock *iucv_accept_dequeue(struct sock *parent,
888c2ecf20Sopenharmony_ci					struct socket *newsock);
898c2ecf20Sopenharmony_cistatic void iucv_sock_kill(struct sock *sk);
908c2ecf20Sopenharmony_cistatic void iucv_sock_close(struct sock *sk);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic void afiucv_hs_callback_txnotify(struct sk_buff *, enum iucv_tx_notify);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci/* Call Back functions */
958c2ecf20Sopenharmony_cistatic void iucv_callback_rx(struct iucv_path *, struct iucv_message *);
968c2ecf20Sopenharmony_cistatic void iucv_callback_txdone(struct iucv_path *, struct iucv_message *);
978c2ecf20Sopenharmony_cistatic void iucv_callback_connack(struct iucv_path *, u8 *);
988c2ecf20Sopenharmony_cistatic int iucv_callback_connreq(struct iucv_path *, u8 *, u8 *);
998c2ecf20Sopenharmony_cistatic void iucv_callback_connrej(struct iucv_path *, u8 *);
1008c2ecf20Sopenharmony_cistatic void iucv_callback_shutdown(struct iucv_path *, u8 *);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic struct iucv_sock_list iucv_sk_list = {
1038c2ecf20Sopenharmony_ci	.lock = __RW_LOCK_UNLOCKED(iucv_sk_list.lock),
1048c2ecf20Sopenharmony_ci	.autobind_name = ATOMIC_INIT(0)
1058c2ecf20Sopenharmony_ci};
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic struct iucv_handler af_iucv_handler = {
1088c2ecf20Sopenharmony_ci	.path_pending	  = iucv_callback_connreq,
1098c2ecf20Sopenharmony_ci	.path_complete	  = iucv_callback_connack,
1108c2ecf20Sopenharmony_ci	.path_severed	  = iucv_callback_connrej,
1118c2ecf20Sopenharmony_ci	.message_pending  = iucv_callback_rx,
1128c2ecf20Sopenharmony_ci	.message_complete = iucv_callback_txdone,
1138c2ecf20Sopenharmony_ci	.path_quiesced	  = iucv_callback_shutdown,
1148c2ecf20Sopenharmony_ci};
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic inline void high_nmcpy(unsigned char *dst, char *src)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci       memcpy(dst, src, 8);
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic inline void low_nmcpy(unsigned char *dst, char *src)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci       memcpy(&dst[8], src, 8);
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci/**
1278c2ecf20Sopenharmony_ci * iucv_msg_length() - Returns the length of an iucv message.
1288c2ecf20Sopenharmony_ci * @msg:	Pointer to struct iucv_message, MUST NOT be NULL
1298c2ecf20Sopenharmony_ci *
1308c2ecf20Sopenharmony_ci * The function returns the length of the specified iucv message @msg of data
1318c2ecf20Sopenharmony_ci * stored in a buffer and of data stored in the parameter list (PRMDATA).
1328c2ecf20Sopenharmony_ci *
1338c2ecf20Sopenharmony_ci * For IUCV_IPRMDATA, AF_IUCV uses the following convention to transport socket
1348c2ecf20Sopenharmony_ci * data:
1358c2ecf20Sopenharmony_ci *	PRMDATA[0..6]	socket data (max 7 bytes);
1368c2ecf20Sopenharmony_ci *	PRMDATA[7]	socket data length value (len is 0xff - PRMDATA[7])
1378c2ecf20Sopenharmony_ci *
1388c2ecf20Sopenharmony_ci * The socket data length is computed by subtracting the socket data length
1398c2ecf20Sopenharmony_ci * value from 0xFF.
1408c2ecf20Sopenharmony_ci * If the socket data len is greater 7, then PRMDATA can be used for special
1418c2ecf20Sopenharmony_ci * notifications (see iucv_sock_shutdown); and further,
1428c2ecf20Sopenharmony_ci * if the socket data len is > 7, the function returns 8.
1438c2ecf20Sopenharmony_ci *
1448c2ecf20Sopenharmony_ci * Use this function to allocate socket buffers to store iucv message data.
1458c2ecf20Sopenharmony_ci */
1468c2ecf20Sopenharmony_cistatic inline size_t iucv_msg_length(struct iucv_message *msg)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	size_t datalen;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (msg->flags & IUCV_IPRMDATA) {
1518c2ecf20Sopenharmony_ci		datalen = 0xff - msg->rmmsg[7];
1528c2ecf20Sopenharmony_ci		return (datalen < 8) ? datalen : 8;
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci	return msg->length;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci/**
1588c2ecf20Sopenharmony_ci * iucv_sock_in_state() - check for specific states
1598c2ecf20Sopenharmony_ci * @sk:		sock structure
1608c2ecf20Sopenharmony_ci * @state:	first iucv sk state
1618c2ecf20Sopenharmony_ci * @state:	second iucv sk state
1628c2ecf20Sopenharmony_ci *
1638c2ecf20Sopenharmony_ci * Returns true if the socket in either in the first or second state.
1648c2ecf20Sopenharmony_ci */
1658c2ecf20Sopenharmony_cistatic int iucv_sock_in_state(struct sock *sk, int state, int state2)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	return (sk->sk_state == state || sk->sk_state == state2);
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci/**
1718c2ecf20Sopenharmony_ci * iucv_below_msglim() - function to check if messages can be sent
1728c2ecf20Sopenharmony_ci * @sk:		sock structure
1738c2ecf20Sopenharmony_ci *
1748c2ecf20Sopenharmony_ci * Returns true if the send queue length is lower than the message limit.
1758c2ecf20Sopenharmony_ci * Always returns true if the socket is not connected (no iucv path for
1768c2ecf20Sopenharmony_ci * checking the message limit).
1778c2ecf20Sopenharmony_ci */
1788c2ecf20Sopenharmony_cistatic inline int iucv_below_msglim(struct sock *sk)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	struct iucv_sock *iucv = iucv_sk(sk);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	if (sk->sk_state != IUCV_CONNECTED)
1838c2ecf20Sopenharmony_ci		return 1;
1848c2ecf20Sopenharmony_ci	if (iucv->transport == AF_IUCV_TRANS_IUCV)
1858c2ecf20Sopenharmony_ci		return (skb_queue_len(&iucv->send_skb_q) < iucv->path->msglim);
1868c2ecf20Sopenharmony_ci	else
1878c2ecf20Sopenharmony_ci		return ((atomic_read(&iucv->msg_sent) < iucv->msglimit_peer) &&
1888c2ecf20Sopenharmony_ci			(atomic_read(&iucv->pendings) <= 0));
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci/**
1928c2ecf20Sopenharmony_ci * iucv_sock_wake_msglim() - Wake up thread waiting on msg limit
1938c2ecf20Sopenharmony_ci */
1948c2ecf20Sopenharmony_cistatic void iucv_sock_wake_msglim(struct sock *sk)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	struct socket_wq *wq;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	rcu_read_lock();
1998c2ecf20Sopenharmony_ci	wq = rcu_dereference(sk->sk_wq);
2008c2ecf20Sopenharmony_ci	if (skwq_has_sleeper(wq))
2018c2ecf20Sopenharmony_ci		wake_up_interruptible_all(&wq->wait);
2028c2ecf20Sopenharmony_ci	sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT);
2038c2ecf20Sopenharmony_ci	rcu_read_unlock();
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci/**
2078c2ecf20Sopenharmony_ci * afiucv_hs_send() - send a message through HiperSockets transport
2088c2ecf20Sopenharmony_ci */
2098c2ecf20Sopenharmony_cistatic int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock,
2108c2ecf20Sopenharmony_ci		   struct sk_buff *skb, u8 flags)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	struct iucv_sock *iucv = iucv_sk(sock);
2138c2ecf20Sopenharmony_ci	struct af_iucv_trans_hdr *phs_hdr;
2148c2ecf20Sopenharmony_ci	struct sk_buff *nskb;
2158c2ecf20Sopenharmony_ci	int err, confirm_recv = 0;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	phs_hdr = skb_push(skb, sizeof(*phs_hdr));
2188c2ecf20Sopenharmony_ci	memset(phs_hdr, 0, sizeof(*phs_hdr));
2198c2ecf20Sopenharmony_ci	skb_reset_network_header(skb);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	phs_hdr->magic = ETH_P_AF_IUCV;
2228c2ecf20Sopenharmony_ci	phs_hdr->version = 1;
2238c2ecf20Sopenharmony_ci	phs_hdr->flags = flags;
2248c2ecf20Sopenharmony_ci	if (flags == AF_IUCV_FLAG_SYN)
2258c2ecf20Sopenharmony_ci		phs_hdr->window = iucv->msglimit;
2268c2ecf20Sopenharmony_ci	else if ((flags == AF_IUCV_FLAG_WIN) || !flags) {
2278c2ecf20Sopenharmony_ci		confirm_recv = atomic_read(&iucv->msg_recv);
2288c2ecf20Sopenharmony_ci		phs_hdr->window = confirm_recv;
2298c2ecf20Sopenharmony_ci		if (confirm_recv)
2308c2ecf20Sopenharmony_ci			phs_hdr->flags = phs_hdr->flags | AF_IUCV_FLAG_WIN;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci	memcpy(phs_hdr->destUserID, iucv->dst_user_id, 8);
2338c2ecf20Sopenharmony_ci	memcpy(phs_hdr->destAppName, iucv->dst_name, 8);
2348c2ecf20Sopenharmony_ci	memcpy(phs_hdr->srcUserID, iucv->src_user_id, 8);
2358c2ecf20Sopenharmony_ci	memcpy(phs_hdr->srcAppName, iucv->src_name, 8);
2368c2ecf20Sopenharmony_ci	ASCEBC(phs_hdr->destUserID, sizeof(phs_hdr->destUserID));
2378c2ecf20Sopenharmony_ci	ASCEBC(phs_hdr->destAppName, sizeof(phs_hdr->destAppName));
2388c2ecf20Sopenharmony_ci	ASCEBC(phs_hdr->srcUserID, sizeof(phs_hdr->srcUserID));
2398c2ecf20Sopenharmony_ci	ASCEBC(phs_hdr->srcAppName, sizeof(phs_hdr->srcAppName));
2408c2ecf20Sopenharmony_ci	if (imsg)
2418c2ecf20Sopenharmony_ci		memcpy(&phs_hdr->iucv_hdr, imsg, sizeof(struct iucv_message));
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	skb->dev = iucv->hs_dev;
2448c2ecf20Sopenharmony_ci	if (!skb->dev) {
2458c2ecf20Sopenharmony_ci		err = -ENODEV;
2468c2ecf20Sopenharmony_ci		goto err_free;
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	dev_hard_header(skb, skb->dev, ETH_P_AF_IUCV, NULL, NULL, skb->len);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	if (!(skb->dev->flags & IFF_UP) || !netif_carrier_ok(skb->dev)) {
2528c2ecf20Sopenharmony_ci		err = -ENETDOWN;
2538c2ecf20Sopenharmony_ci		goto err_free;
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci	if (skb->len > skb->dev->mtu) {
2568c2ecf20Sopenharmony_ci		if (sock->sk_type == SOCK_SEQPACKET) {
2578c2ecf20Sopenharmony_ci			err = -EMSGSIZE;
2588c2ecf20Sopenharmony_ci			goto err_free;
2598c2ecf20Sopenharmony_ci		}
2608c2ecf20Sopenharmony_ci		skb_trim(skb, skb->dev->mtu);
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci	skb->protocol = cpu_to_be16(ETH_P_AF_IUCV);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	__skb_header_release(skb);
2658c2ecf20Sopenharmony_ci	nskb = skb_clone(skb, GFP_ATOMIC);
2668c2ecf20Sopenharmony_ci	if (!nskb) {
2678c2ecf20Sopenharmony_ci		err = -ENOMEM;
2688c2ecf20Sopenharmony_ci		goto err_free;
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	skb_queue_tail(&iucv->send_skb_q, nskb);
2728c2ecf20Sopenharmony_ci	err = dev_queue_xmit(skb);
2738c2ecf20Sopenharmony_ci	if (net_xmit_eval(err)) {
2748c2ecf20Sopenharmony_ci		skb_unlink(nskb, &iucv->send_skb_q);
2758c2ecf20Sopenharmony_ci		kfree_skb(nskb);
2768c2ecf20Sopenharmony_ci	} else {
2778c2ecf20Sopenharmony_ci		atomic_sub(confirm_recv, &iucv->msg_recv);
2788c2ecf20Sopenharmony_ci		WARN_ON(atomic_read(&iucv->msg_recv) < 0);
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci	return net_xmit_eval(err);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cierr_free:
2838c2ecf20Sopenharmony_ci	kfree_skb(skb);
2848c2ecf20Sopenharmony_ci	return err;
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_cistatic struct sock *__iucv_get_sock_by_name(char *nm)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	struct sock *sk;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	sk_for_each(sk, &iucv_sk_list.head)
2928c2ecf20Sopenharmony_ci		if (!memcmp(&iucv_sk(sk)->src_name, nm, 8))
2938c2ecf20Sopenharmony_ci			return sk;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	return NULL;
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic void iucv_sock_destruct(struct sock *sk)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	skb_queue_purge(&sk->sk_receive_queue);
3018c2ecf20Sopenharmony_ci	skb_queue_purge(&sk->sk_error_queue);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	sk_mem_reclaim(sk);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	if (!sock_flag(sk, SOCK_DEAD)) {
3068c2ecf20Sopenharmony_ci		pr_err("Attempt to release alive iucv socket %p\n", sk);
3078c2ecf20Sopenharmony_ci		return;
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	WARN_ON(atomic_read(&sk->sk_rmem_alloc));
3118c2ecf20Sopenharmony_ci	WARN_ON(refcount_read(&sk->sk_wmem_alloc));
3128c2ecf20Sopenharmony_ci	WARN_ON(sk->sk_wmem_queued);
3138c2ecf20Sopenharmony_ci	WARN_ON(sk->sk_forward_alloc);
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci/* Cleanup Listen */
3178c2ecf20Sopenharmony_cistatic void iucv_sock_cleanup_listen(struct sock *parent)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	struct sock *sk;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	/* Close non-accepted connections */
3228c2ecf20Sopenharmony_ci	while ((sk = iucv_accept_dequeue(parent, NULL))) {
3238c2ecf20Sopenharmony_ci		iucv_sock_close(sk);
3248c2ecf20Sopenharmony_ci		iucv_sock_kill(sk);
3258c2ecf20Sopenharmony_ci	}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	parent->sk_state = IUCV_CLOSED;
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic void iucv_sock_link(struct iucv_sock_list *l, struct sock *sk)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	write_lock_bh(&l->lock);
3338c2ecf20Sopenharmony_ci	sk_add_node(sk, &l->head);
3348c2ecf20Sopenharmony_ci	write_unlock_bh(&l->lock);
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic void iucv_sock_unlink(struct iucv_sock_list *l, struct sock *sk)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	write_lock_bh(&l->lock);
3408c2ecf20Sopenharmony_ci	sk_del_node_init(sk);
3418c2ecf20Sopenharmony_ci	write_unlock_bh(&l->lock);
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci/* Kill socket (only if zapped and orphaned) */
3458c2ecf20Sopenharmony_cistatic void iucv_sock_kill(struct sock *sk)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket)
3488c2ecf20Sopenharmony_ci		return;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	iucv_sock_unlink(&iucv_sk_list, sk);
3518c2ecf20Sopenharmony_ci	sock_set_flag(sk, SOCK_DEAD);
3528c2ecf20Sopenharmony_ci	sock_put(sk);
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci/* Terminate an IUCV path */
3568c2ecf20Sopenharmony_cistatic void iucv_sever_path(struct sock *sk, int with_user_data)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	unsigned char user_data[16];
3598c2ecf20Sopenharmony_ci	struct iucv_sock *iucv = iucv_sk(sk);
3608c2ecf20Sopenharmony_ci	struct iucv_path *path = iucv->path;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	/* Whoever resets the path pointer, must sever and free it. */
3638c2ecf20Sopenharmony_ci	if (xchg(&iucv->path, NULL)) {
3648c2ecf20Sopenharmony_ci		if (with_user_data) {
3658c2ecf20Sopenharmony_ci			low_nmcpy(user_data, iucv->src_name);
3668c2ecf20Sopenharmony_ci			high_nmcpy(user_data, iucv->dst_name);
3678c2ecf20Sopenharmony_ci			ASCEBC(user_data, sizeof(user_data));
3688c2ecf20Sopenharmony_ci			pr_iucv->path_sever(path, user_data);
3698c2ecf20Sopenharmony_ci		} else
3708c2ecf20Sopenharmony_ci			pr_iucv->path_sever(path, NULL);
3718c2ecf20Sopenharmony_ci		iucv_path_free(path);
3728c2ecf20Sopenharmony_ci	}
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci/* Send controlling flags through an IUCV socket for HIPER transport */
3768c2ecf20Sopenharmony_cistatic int iucv_send_ctrl(struct sock *sk, u8 flags)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	struct iucv_sock *iucv = iucv_sk(sk);
3798c2ecf20Sopenharmony_ci	int err = 0;
3808c2ecf20Sopenharmony_ci	int blen;
3818c2ecf20Sopenharmony_ci	struct sk_buff *skb;
3828c2ecf20Sopenharmony_ci	u8 shutdown = 0;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	blen = sizeof(struct af_iucv_trans_hdr) +
3858c2ecf20Sopenharmony_ci	       LL_RESERVED_SPACE(iucv->hs_dev);
3868c2ecf20Sopenharmony_ci	if (sk->sk_shutdown & SEND_SHUTDOWN) {
3878c2ecf20Sopenharmony_ci		/* controlling flags should be sent anyway */
3888c2ecf20Sopenharmony_ci		shutdown = sk->sk_shutdown;
3898c2ecf20Sopenharmony_ci		sk->sk_shutdown &= RCV_SHUTDOWN;
3908c2ecf20Sopenharmony_ci	}
3918c2ecf20Sopenharmony_ci	skb = sock_alloc_send_skb(sk, blen, 1, &err);
3928c2ecf20Sopenharmony_ci	if (skb) {
3938c2ecf20Sopenharmony_ci		skb_reserve(skb, blen);
3948c2ecf20Sopenharmony_ci		err = afiucv_hs_send(NULL, sk, skb, flags);
3958c2ecf20Sopenharmony_ci	}
3968c2ecf20Sopenharmony_ci	if (shutdown)
3978c2ecf20Sopenharmony_ci		sk->sk_shutdown = shutdown;
3988c2ecf20Sopenharmony_ci	return err;
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci/* Close an IUCV socket */
4028c2ecf20Sopenharmony_cistatic void iucv_sock_close(struct sock *sk)
4038c2ecf20Sopenharmony_ci{
4048c2ecf20Sopenharmony_ci	struct iucv_sock *iucv = iucv_sk(sk);
4058c2ecf20Sopenharmony_ci	unsigned long timeo;
4068c2ecf20Sopenharmony_ci	int err = 0;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	lock_sock(sk);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	switch (sk->sk_state) {
4118c2ecf20Sopenharmony_ci	case IUCV_LISTEN:
4128c2ecf20Sopenharmony_ci		iucv_sock_cleanup_listen(sk);
4138c2ecf20Sopenharmony_ci		break;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	case IUCV_CONNECTED:
4168c2ecf20Sopenharmony_ci		if (iucv->transport == AF_IUCV_TRANS_HIPER) {
4178c2ecf20Sopenharmony_ci			err = iucv_send_ctrl(sk, AF_IUCV_FLAG_FIN);
4188c2ecf20Sopenharmony_ci			sk->sk_state = IUCV_DISCONN;
4198c2ecf20Sopenharmony_ci			sk->sk_state_change(sk);
4208c2ecf20Sopenharmony_ci		}
4218c2ecf20Sopenharmony_ci		fallthrough;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	case IUCV_DISCONN:
4248c2ecf20Sopenharmony_ci		sk->sk_state = IUCV_CLOSING;
4258c2ecf20Sopenharmony_ci		sk->sk_state_change(sk);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci		if (!err && !skb_queue_empty(&iucv->send_skb_q)) {
4288c2ecf20Sopenharmony_ci			if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
4298c2ecf20Sopenharmony_ci				timeo = sk->sk_lingertime;
4308c2ecf20Sopenharmony_ci			else
4318c2ecf20Sopenharmony_ci				timeo = IUCV_DISCONN_TIMEOUT;
4328c2ecf20Sopenharmony_ci			iucv_sock_wait(sk,
4338c2ecf20Sopenharmony_ci					iucv_sock_in_state(sk, IUCV_CLOSED, 0),
4348c2ecf20Sopenharmony_ci					timeo);
4358c2ecf20Sopenharmony_ci		}
4368c2ecf20Sopenharmony_ci		fallthrough;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	case IUCV_CLOSING:
4398c2ecf20Sopenharmony_ci		sk->sk_state = IUCV_CLOSED;
4408c2ecf20Sopenharmony_ci		sk->sk_state_change(sk);
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci		sk->sk_err = ECONNRESET;
4438c2ecf20Sopenharmony_ci		sk->sk_state_change(sk);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci		skb_queue_purge(&iucv->send_skb_q);
4468c2ecf20Sopenharmony_ci		skb_queue_purge(&iucv->backlog_skb_q);
4478c2ecf20Sopenharmony_ci		fallthrough;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	default:
4508c2ecf20Sopenharmony_ci		iucv_sever_path(sk, 1);
4518c2ecf20Sopenharmony_ci	}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	if (iucv->hs_dev) {
4548c2ecf20Sopenharmony_ci		dev_put(iucv->hs_dev);
4558c2ecf20Sopenharmony_ci		iucv->hs_dev = NULL;
4568c2ecf20Sopenharmony_ci		sk->sk_bound_dev_if = 0;
4578c2ecf20Sopenharmony_ci	}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	/* mark socket for deletion by iucv_sock_kill() */
4608c2ecf20Sopenharmony_ci	sock_set_flag(sk, SOCK_ZAPPED);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	release_sock(sk);
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic void iucv_sock_init(struct sock *sk, struct sock *parent)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	if (parent) {
4688c2ecf20Sopenharmony_ci		sk->sk_type = parent->sk_type;
4698c2ecf20Sopenharmony_ci		security_sk_clone(parent, sk);
4708c2ecf20Sopenharmony_ci	}
4718c2ecf20Sopenharmony_ci}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_cistatic struct sock *iucv_sock_alloc(struct socket *sock, int proto, gfp_t prio, int kern)
4748c2ecf20Sopenharmony_ci{
4758c2ecf20Sopenharmony_ci	struct sock *sk;
4768c2ecf20Sopenharmony_ci	struct iucv_sock *iucv;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	sk = sk_alloc(&init_net, PF_IUCV, prio, &iucv_proto, kern);
4798c2ecf20Sopenharmony_ci	if (!sk)
4808c2ecf20Sopenharmony_ci		return NULL;
4818c2ecf20Sopenharmony_ci	iucv = iucv_sk(sk);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	sock_init_data(sock, sk);
4848c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&iucv->accept_q);
4858c2ecf20Sopenharmony_ci	spin_lock_init(&iucv->accept_q_lock);
4868c2ecf20Sopenharmony_ci	skb_queue_head_init(&iucv->send_skb_q);
4878c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&iucv->message_q.list);
4888c2ecf20Sopenharmony_ci	spin_lock_init(&iucv->message_q.lock);
4898c2ecf20Sopenharmony_ci	skb_queue_head_init(&iucv->backlog_skb_q);
4908c2ecf20Sopenharmony_ci	iucv->send_tag = 0;
4918c2ecf20Sopenharmony_ci	atomic_set(&iucv->pendings, 0);
4928c2ecf20Sopenharmony_ci	iucv->flags = 0;
4938c2ecf20Sopenharmony_ci	iucv->msglimit = 0;
4948c2ecf20Sopenharmony_ci	atomic_set(&iucv->msg_sent, 0);
4958c2ecf20Sopenharmony_ci	atomic_set(&iucv->msg_recv, 0);
4968c2ecf20Sopenharmony_ci	iucv->path = NULL;
4978c2ecf20Sopenharmony_ci	iucv->sk_txnotify = afiucv_hs_callback_txnotify;
4988c2ecf20Sopenharmony_ci	memset(&iucv->src_user_id , 0, 32);
4998c2ecf20Sopenharmony_ci	if (pr_iucv)
5008c2ecf20Sopenharmony_ci		iucv->transport = AF_IUCV_TRANS_IUCV;
5018c2ecf20Sopenharmony_ci	else
5028c2ecf20Sopenharmony_ci		iucv->transport = AF_IUCV_TRANS_HIPER;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	sk->sk_destruct = iucv_sock_destruct;
5058c2ecf20Sopenharmony_ci	sk->sk_sndtimeo = IUCV_CONN_TIMEOUT;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	sock_reset_flag(sk, SOCK_ZAPPED);
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	sk->sk_protocol = proto;
5108c2ecf20Sopenharmony_ci	sk->sk_state	= IUCV_OPEN;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	iucv_sock_link(&iucv_sk_list, sk);
5138c2ecf20Sopenharmony_ci	return sk;
5148c2ecf20Sopenharmony_ci}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_cistatic void iucv_accept_enqueue(struct sock *parent, struct sock *sk)
5178c2ecf20Sopenharmony_ci{
5188c2ecf20Sopenharmony_ci	unsigned long flags;
5198c2ecf20Sopenharmony_ci	struct iucv_sock *par = iucv_sk(parent);
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	sock_hold(sk);
5228c2ecf20Sopenharmony_ci	spin_lock_irqsave(&par->accept_q_lock, flags);
5238c2ecf20Sopenharmony_ci	list_add_tail(&iucv_sk(sk)->accept_q, &par->accept_q);
5248c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&par->accept_q_lock, flags);
5258c2ecf20Sopenharmony_ci	iucv_sk(sk)->parent = parent;
5268c2ecf20Sopenharmony_ci	sk_acceptq_added(parent);
5278c2ecf20Sopenharmony_ci}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_cistatic void iucv_accept_unlink(struct sock *sk)
5308c2ecf20Sopenharmony_ci{
5318c2ecf20Sopenharmony_ci	unsigned long flags;
5328c2ecf20Sopenharmony_ci	struct iucv_sock *par = iucv_sk(iucv_sk(sk)->parent);
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	spin_lock_irqsave(&par->accept_q_lock, flags);
5358c2ecf20Sopenharmony_ci	list_del_init(&iucv_sk(sk)->accept_q);
5368c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&par->accept_q_lock, flags);
5378c2ecf20Sopenharmony_ci	sk_acceptq_removed(iucv_sk(sk)->parent);
5388c2ecf20Sopenharmony_ci	iucv_sk(sk)->parent = NULL;
5398c2ecf20Sopenharmony_ci	sock_put(sk);
5408c2ecf20Sopenharmony_ci}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_cistatic struct sock *iucv_accept_dequeue(struct sock *parent,
5438c2ecf20Sopenharmony_ci					struct socket *newsock)
5448c2ecf20Sopenharmony_ci{
5458c2ecf20Sopenharmony_ci	struct iucv_sock *isk, *n;
5468c2ecf20Sopenharmony_ci	struct sock *sk;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	list_for_each_entry_safe(isk, n, &iucv_sk(parent)->accept_q, accept_q) {
5498c2ecf20Sopenharmony_ci		sk = (struct sock *) isk;
5508c2ecf20Sopenharmony_ci		lock_sock(sk);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci		if (sk->sk_state == IUCV_CLOSED) {
5538c2ecf20Sopenharmony_ci			iucv_accept_unlink(sk);
5548c2ecf20Sopenharmony_ci			release_sock(sk);
5558c2ecf20Sopenharmony_ci			continue;
5568c2ecf20Sopenharmony_ci		}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci		if (sk->sk_state == IUCV_CONNECTED ||
5598c2ecf20Sopenharmony_ci		    sk->sk_state == IUCV_DISCONN ||
5608c2ecf20Sopenharmony_ci		    !newsock) {
5618c2ecf20Sopenharmony_ci			iucv_accept_unlink(sk);
5628c2ecf20Sopenharmony_ci			if (newsock)
5638c2ecf20Sopenharmony_ci				sock_graft(sk, newsock);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci			release_sock(sk);
5668c2ecf20Sopenharmony_ci			return sk;
5678c2ecf20Sopenharmony_ci		}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci		release_sock(sk);
5708c2ecf20Sopenharmony_ci	}
5718c2ecf20Sopenharmony_ci	return NULL;
5728c2ecf20Sopenharmony_ci}
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_cistatic void __iucv_auto_name(struct iucv_sock *iucv)
5758c2ecf20Sopenharmony_ci{
5768c2ecf20Sopenharmony_ci	char name[12];
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	sprintf(name, "%08x", atomic_inc_return(&iucv_sk_list.autobind_name));
5798c2ecf20Sopenharmony_ci	while (__iucv_get_sock_by_name(name)) {
5808c2ecf20Sopenharmony_ci		sprintf(name, "%08x",
5818c2ecf20Sopenharmony_ci			atomic_inc_return(&iucv_sk_list.autobind_name));
5828c2ecf20Sopenharmony_ci	}
5838c2ecf20Sopenharmony_ci	memcpy(iucv->src_name, name, 8);
5848c2ecf20Sopenharmony_ci}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci/* Bind an unbound socket */
5878c2ecf20Sopenharmony_cistatic int iucv_sock_bind(struct socket *sock, struct sockaddr *addr,
5888c2ecf20Sopenharmony_ci			  int addr_len)
5898c2ecf20Sopenharmony_ci{
5908c2ecf20Sopenharmony_ci	struct sockaddr_iucv *sa = (struct sockaddr_iucv *) addr;
5918c2ecf20Sopenharmony_ci	char uid[sizeof(sa->siucv_user_id)];
5928c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
5938c2ecf20Sopenharmony_ci	struct iucv_sock *iucv;
5948c2ecf20Sopenharmony_ci	int err = 0;
5958c2ecf20Sopenharmony_ci	struct net_device *dev;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	/* Verify the input sockaddr */
5988c2ecf20Sopenharmony_ci	if (addr_len < sizeof(struct sockaddr_iucv) ||
5998c2ecf20Sopenharmony_ci	    addr->sa_family != AF_IUCV)
6008c2ecf20Sopenharmony_ci		return -EINVAL;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	lock_sock(sk);
6038c2ecf20Sopenharmony_ci	if (sk->sk_state != IUCV_OPEN) {
6048c2ecf20Sopenharmony_ci		err = -EBADFD;
6058c2ecf20Sopenharmony_ci		goto done;
6068c2ecf20Sopenharmony_ci	}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	write_lock_bh(&iucv_sk_list.lock);
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	iucv = iucv_sk(sk);
6118c2ecf20Sopenharmony_ci	if (__iucv_get_sock_by_name(sa->siucv_name)) {
6128c2ecf20Sopenharmony_ci		err = -EADDRINUSE;
6138c2ecf20Sopenharmony_ci		goto done_unlock;
6148c2ecf20Sopenharmony_ci	}
6158c2ecf20Sopenharmony_ci	if (iucv->path)
6168c2ecf20Sopenharmony_ci		goto done_unlock;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	/* Bind the socket */
6198c2ecf20Sopenharmony_ci	if (pr_iucv)
6208c2ecf20Sopenharmony_ci		if (!memcmp(sa->siucv_user_id, iucv_userid, 8))
6218c2ecf20Sopenharmony_ci			goto vm_bind; /* VM IUCV transport */
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	/* try hiper transport */
6248c2ecf20Sopenharmony_ci	memcpy(uid, sa->siucv_user_id, sizeof(uid));
6258c2ecf20Sopenharmony_ci	ASCEBC(uid, 8);
6268c2ecf20Sopenharmony_ci	rcu_read_lock();
6278c2ecf20Sopenharmony_ci	for_each_netdev_rcu(&init_net, dev) {
6288c2ecf20Sopenharmony_ci		if (!memcmp(dev->perm_addr, uid, 8)) {
6298c2ecf20Sopenharmony_ci			memcpy(iucv->src_user_id, sa->siucv_user_id, 8);
6308c2ecf20Sopenharmony_ci			/* Check for unitialized siucv_name */
6318c2ecf20Sopenharmony_ci			if (strncmp(sa->siucv_name, "        ", 8) == 0)
6328c2ecf20Sopenharmony_ci				__iucv_auto_name(iucv);
6338c2ecf20Sopenharmony_ci			else
6348c2ecf20Sopenharmony_ci				memcpy(iucv->src_name, sa->siucv_name, 8);
6358c2ecf20Sopenharmony_ci			sk->sk_bound_dev_if = dev->ifindex;
6368c2ecf20Sopenharmony_ci			iucv->hs_dev = dev;
6378c2ecf20Sopenharmony_ci			dev_hold(dev);
6388c2ecf20Sopenharmony_ci			sk->sk_state = IUCV_BOUND;
6398c2ecf20Sopenharmony_ci			iucv->transport = AF_IUCV_TRANS_HIPER;
6408c2ecf20Sopenharmony_ci			if (!iucv->msglimit)
6418c2ecf20Sopenharmony_ci				iucv->msglimit = IUCV_HIPER_MSGLIM_DEFAULT;
6428c2ecf20Sopenharmony_ci			rcu_read_unlock();
6438c2ecf20Sopenharmony_ci			goto done_unlock;
6448c2ecf20Sopenharmony_ci		}
6458c2ecf20Sopenharmony_ci	}
6468c2ecf20Sopenharmony_ci	rcu_read_unlock();
6478c2ecf20Sopenharmony_civm_bind:
6488c2ecf20Sopenharmony_ci	if (pr_iucv) {
6498c2ecf20Sopenharmony_ci		/* use local userid for backward compat */
6508c2ecf20Sopenharmony_ci		memcpy(iucv->src_name, sa->siucv_name, 8);
6518c2ecf20Sopenharmony_ci		memcpy(iucv->src_user_id, iucv_userid, 8);
6528c2ecf20Sopenharmony_ci		sk->sk_state = IUCV_BOUND;
6538c2ecf20Sopenharmony_ci		iucv->transport = AF_IUCV_TRANS_IUCV;
6548c2ecf20Sopenharmony_ci		sk->sk_allocation |= GFP_DMA;
6558c2ecf20Sopenharmony_ci		if (!iucv->msglimit)
6568c2ecf20Sopenharmony_ci			iucv->msglimit = IUCV_QUEUELEN_DEFAULT;
6578c2ecf20Sopenharmony_ci		goto done_unlock;
6588c2ecf20Sopenharmony_ci	}
6598c2ecf20Sopenharmony_ci	/* found no dev to bind */
6608c2ecf20Sopenharmony_ci	err = -ENODEV;
6618c2ecf20Sopenharmony_cidone_unlock:
6628c2ecf20Sopenharmony_ci	/* Release the socket list lock */
6638c2ecf20Sopenharmony_ci	write_unlock_bh(&iucv_sk_list.lock);
6648c2ecf20Sopenharmony_cidone:
6658c2ecf20Sopenharmony_ci	release_sock(sk);
6668c2ecf20Sopenharmony_ci	return err;
6678c2ecf20Sopenharmony_ci}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci/* Automatically bind an unbound socket */
6708c2ecf20Sopenharmony_cistatic int iucv_sock_autobind(struct sock *sk)
6718c2ecf20Sopenharmony_ci{
6728c2ecf20Sopenharmony_ci	struct iucv_sock *iucv = iucv_sk(sk);
6738c2ecf20Sopenharmony_ci	int err = 0;
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	if (unlikely(!pr_iucv))
6768c2ecf20Sopenharmony_ci		return -EPROTO;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	memcpy(iucv->src_user_id, iucv_userid, 8);
6798c2ecf20Sopenharmony_ci	iucv->transport = AF_IUCV_TRANS_IUCV;
6808c2ecf20Sopenharmony_ci	sk->sk_allocation |= GFP_DMA;
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	write_lock_bh(&iucv_sk_list.lock);
6838c2ecf20Sopenharmony_ci	__iucv_auto_name(iucv);
6848c2ecf20Sopenharmony_ci	write_unlock_bh(&iucv_sk_list.lock);
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	if (!iucv->msglimit)
6878c2ecf20Sopenharmony_ci		iucv->msglimit = IUCV_QUEUELEN_DEFAULT;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	return err;
6908c2ecf20Sopenharmony_ci}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_cistatic int afiucv_path_connect(struct socket *sock, struct sockaddr *addr)
6938c2ecf20Sopenharmony_ci{
6948c2ecf20Sopenharmony_ci	struct sockaddr_iucv *sa = (struct sockaddr_iucv *) addr;
6958c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
6968c2ecf20Sopenharmony_ci	struct iucv_sock *iucv = iucv_sk(sk);
6978c2ecf20Sopenharmony_ci	unsigned char user_data[16];
6988c2ecf20Sopenharmony_ci	int err;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	high_nmcpy(user_data, sa->siucv_name);
7018c2ecf20Sopenharmony_ci	low_nmcpy(user_data, iucv->src_name);
7028c2ecf20Sopenharmony_ci	ASCEBC(user_data, sizeof(user_data));
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	/* Create path. */
7058c2ecf20Sopenharmony_ci	iucv->path = iucv_path_alloc(iucv->msglimit,
7068c2ecf20Sopenharmony_ci				     IUCV_IPRMDATA, GFP_KERNEL);
7078c2ecf20Sopenharmony_ci	if (!iucv->path) {
7088c2ecf20Sopenharmony_ci		err = -ENOMEM;
7098c2ecf20Sopenharmony_ci		goto done;
7108c2ecf20Sopenharmony_ci	}
7118c2ecf20Sopenharmony_ci	err = pr_iucv->path_connect(iucv->path, &af_iucv_handler,
7128c2ecf20Sopenharmony_ci				    sa->siucv_user_id, NULL, user_data,
7138c2ecf20Sopenharmony_ci				    sk);
7148c2ecf20Sopenharmony_ci	if (err) {
7158c2ecf20Sopenharmony_ci		iucv_path_free(iucv->path);
7168c2ecf20Sopenharmony_ci		iucv->path = NULL;
7178c2ecf20Sopenharmony_ci		switch (err) {
7188c2ecf20Sopenharmony_ci		case 0x0b:	/* Target communicator is not logged on */
7198c2ecf20Sopenharmony_ci			err = -ENETUNREACH;
7208c2ecf20Sopenharmony_ci			break;
7218c2ecf20Sopenharmony_ci		case 0x0d:	/* Max connections for this guest exceeded */
7228c2ecf20Sopenharmony_ci		case 0x0e:	/* Max connections for target guest exceeded */
7238c2ecf20Sopenharmony_ci			err = -EAGAIN;
7248c2ecf20Sopenharmony_ci			break;
7258c2ecf20Sopenharmony_ci		case 0x0f:	/* Missing IUCV authorization */
7268c2ecf20Sopenharmony_ci			err = -EACCES;
7278c2ecf20Sopenharmony_ci			break;
7288c2ecf20Sopenharmony_ci		default:
7298c2ecf20Sopenharmony_ci			err = -ECONNREFUSED;
7308c2ecf20Sopenharmony_ci			break;
7318c2ecf20Sopenharmony_ci		}
7328c2ecf20Sopenharmony_ci	}
7338c2ecf20Sopenharmony_cidone:
7348c2ecf20Sopenharmony_ci	return err;
7358c2ecf20Sopenharmony_ci}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci/* Connect an unconnected socket */
7388c2ecf20Sopenharmony_cistatic int iucv_sock_connect(struct socket *sock, struct sockaddr *addr,
7398c2ecf20Sopenharmony_ci			     int alen, int flags)
7408c2ecf20Sopenharmony_ci{
7418c2ecf20Sopenharmony_ci	struct sockaddr_iucv *sa = (struct sockaddr_iucv *) addr;
7428c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
7438c2ecf20Sopenharmony_ci	struct iucv_sock *iucv = iucv_sk(sk);
7448c2ecf20Sopenharmony_ci	int err;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	if (alen < sizeof(struct sockaddr_iucv) || addr->sa_family != AF_IUCV)
7478c2ecf20Sopenharmony_ci		return -EINVAL;
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	if (sk->sk_state != IUCV_OPEN && sk->sk_state != IUCV_BOUND)
7508c2ecf20Sopenharmony_ci		return -EBADFD;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	if (sk->sk_state == IUCV_OPEN &&
7538c2ecf20Sopenharmony_ci	    iucv->transport == AF_IUCV_TRANS_HIPER)
7548c2ecf20Sopenharmony_ci		return -EBADFD; /* explicit bind required */
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	if (sk->sk_type != SOCK_STREAM && sk->sk_type != SOCK_SEQPACKET)
7578c2ecf20Sopenharmony_ci		return -EINVAL;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	if (sk->sk_state == IUCV_OPEN) {
7608c2ecf20Sopenharmony_ci		err = iucv_sock_autobind(sk);
7618c2ecf20Sopenharmony_ci		if (unlikely(err))
7628c2ecf20Sopenharmony_ci			return err;
7638c2ecf20Sopenharmony_ci	}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	lock_sock(sk);
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	/* Set the destination information */
7688c2ecf20Sopenharmony_ci	memcpy(iucv->dst_user_id, sa->siucv_user_id, 8);
7698c2ecf20Sopenharmony_ci	memcpy(iucv->dst_name, sa->siucv_name, 8);
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	if (iucv->transport == AF_IUCV_TRANS_HIPER)
7728c2ecf20Sopenharmony_ci		err = iucv_send_ctrl(sock->sk, AF_IUCV_FLAG_SYN);
7738c2ecf20Sopenharmony_ci	else
7748c2ecf20Sopenharmony_ci		err = afiucv_path_connect(sock, addr);
7758c2ecf20Sopenharmony_ci	if (err)
7768c2ecf20Sopenharmony_ci		goto done;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	if (sk->sk_state != IUCV_CONNECTED)
7798c2ecf20Sopenharmony_ci		err = iucv_sock_wait(sk, iucv_sock_in_state(sk, IUCV_CONNECTED,
7808c2ecf20Sopenharmony_ci							    IUCV_DISCONN),
7818c2ecf20Sopenharmony_ci				     sock_sndtimeo(sk, flags & O_NONBLOCK));
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	if (sk->sk_state == IUCV_DISCONN || sk->sk_state == IUCV_CLOSED)
7848c2ecf20Sopenharmony_ci		err = -ECONNREFUSED;
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	if (err && iucv->transport == AF_IUCV_TRANS_IUCV)
7878c2ecf20Sopenharmony_ci		iucv_sever_path(sk, 0);
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_cidone:
7908c2ecf20Sopenharmony_ci	release_sock(sk);
7918c2ecf20Sopenharmony_ci	return err;
7928c2ecf20Sopenharmony_ci}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci/* Move a socket into listening state. */
7958c2ecf20Sopenharmony_cistatic int iucv_sock_listen(struct socket *sock, int backlog)
7968c2ecf20Sopenharmony_ci{
7978c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
7988c2ecf20Sopenharmony_ci	int err;
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	lock_sock(sk);
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	err = -EINVAL;
8038c2ecf20Sopenharmony_ci	if (sk->sk_state != IUCV_BOUND)
8048c2ecf20Sopenharmony_ci		goto done;
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	if (sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET)
8078c2ecf20Sopenharmony_ci		goto done;
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	sk->sk_max_ack_backlog = backlog;
8108c2ecf20Sopenharmony_ci	sk->sk_ack_backlog = 0;
8118c2ecf20Sopenharmony_ci	sk->sk_state = IUCV_LISTEN;
8128c2ecf20Sopenharmony_ci	err = 0;
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_cidone:
8158c2ecf20Sopenharmony_ci	release_sock(sk);
8168c2ecf20Sopenharmony_ci	return err;
8178c2ecf20Sopenharmony_ci}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci/* Accept a pending connection */
8208c2ecf20Sopenharmony_cistatic int iucv_sock_accept(struct socket *sock, struct socket *newsock,
8218c2ecf20Sopenharmony_ci			    int flags, bool kern)
8228c2ecf20Sopenharmony_ci{
8238c2ecf20Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
8248c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk, *nsk;
8258c2ecf20Sopenharmony_ci	long timeo;
8268c2ecf20Sopenharmony_ci	int err = 0;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	if (sk->sk_state != IUCV_LISTEN) {
8318c2ecf20Sopenharmony_ci		err = -EBADFD;
8328c2ecf20Sopenharmony_ci		goto done;
8338c2ecf20Sopenharmony_ci	}
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	/* Wait for an incoming connection */
8388c2ecf20Sopenharmony_ci	add_wait_queue_exclusive(sk_sleep(sk), &wait);
8398c2ecf20Sopenharmony_ci	while (!(nsk = iucv_accept_dequeue(sk, newsock))) {
8408c2ecf20Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
8418c2ecf20Sopenharmony_ci		if (!timeo) {
8428c2ecf20Sopenharmony_ci			err = -EAGAIN;
8438c2ecf20Sopenharmony_ci			break;
8448c2ecf20Sopenharmony_ci		}
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci		release_sock(sk);
8478c2ecf20Sopenharmony_ci		timeo = schedule_timeout(timeo);
8488c2ecf20Sopenharmony_ci		lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci		if (sk->sk_state != IUCV_LISTEN) {
8518c2ecf20Sopenharmony_ci			err = -EBADFD;
8528c2ecf20Sopenharmony_ci			break;
8538c2ecf20Sopenharmony_ci		}
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci		if (signal_pending(current)) {
8568c2ecf20Sopenharmony_ci			err = sock_intr_errno(timeo);
8578c2ecf20Sopenharmony_ci			break;
8588c2ecf20Sopenharmony_ci		}
8598c2ecf20Sopenharmony_ci	}
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	set_current_state(TASK_RUNNING);
8628c2ecf20Sopenharmony_ci	remove_wait_queue(sk_sleep(sk), &wait);
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	if (err)
8658c2ecf20Sopenharmony_ci		goto done;
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	newsock->state = SS_CONNECTED;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_cidone:
8708c2ecf20Sopenharmony_ci	release_sock(sk);
8718c2ecf20Sopenharmony_ci	return err;
8728c2ecf20Sopenharmony_ci}
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_cistatic int iucv_sock_getname(struct socket *sock, struct sockaddr *addr,
8758c2ecf20Sopenharmony_ci			     int peer)
8768c2ecf20Sopenharmony_ci{
8778c2ecf20Sopenharmony_ci	struct sockaddr_iucv *siucv = (struct sockaddr_iucv *) addr;
8788c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
8798c2ecf20Sopenharmony_ci	struct iucv_sock *iucv = iucv_sk(sk);
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	addr->sa_family = AF_IUCV;
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	if (peer) {
8848c2ecf20Sopenharmony_ci		memcpy(siucv->siucv_user_id, iucv->dst_user_id, 8);
8858c2ecf20Sopenharmony_ci		memcpy(siucv->siucv_name, iucv->dst_name, 8);
8868c2ecf20Sopenharmony_ci	} else {
8878c2ecf20Sopenharmony_ci		memcpy(siucv->siucv_user_id, iucv->src_user_id, 8);
8888c2ecf20Sopenharmony_ci		memcpy(siucv->siucv_name, iucv->src_name, 8);
8898c2ecf20Sopenharmony_ci	}
8908c2ecf20Sopenharmony_ci	memset(&siucv->siucv_port, 0, sizeof(siucv->siucv_port));
8918c2ecf20Sopenharmony_ci	memset(&siucv->siucv_addr, 0, sizeof(siucv->siucv_addr));
8928c2ecf20Sopenharmony_ci	memset(&siucv->siucv_nodeid, 0, sizeof(siucv->siucv_nodeid));
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	return sizeof(struct sockaddr_iucv);
8958c2ecf20Sopenharmony_ci}
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci/**
8988c2ecf20Sopenharmony_ci * iucv_send_iprm() - Send socket data in parameter list of an iucv message.
8998c2ecf20Sopenharmony_ci * @path:	IUCV path
9008c2ecf20Sopenharmony_ci * @msg:	Pointer to a struct iucv_message
9018c2ecf20Sopenharmony_ci * @skb:	The socket data to send, skb->len MUST BE <= 7
9028c2ecf20Sopenharmony_ci *
9038c2ecf20Sopenharmony_ci * Send the socket data in the parameter list in the iucv message
9048c2ecf20Sopenharmony_ci * (IUCV_IPRMDATA). The socket data is stored at index 0 to 6 in the parameter
9058c2ecf20Sopenharmony_ci * list and the socket data len at index 7 (last byte).
9068c2ecf20Sopenharmony_ci * See also iucv_msg_length().
9078c2ecf20Sopenharmony_ci *
9088c2ecf20Sopenharmony_ci * Returns the error code from the iucv_message_send() call.
9098c2ecf20Sopenharmony_ci */
9108c2ecf20Sopenharmony_cistatic int iucv_send_iprm(struct iucv_path *path, struct iucv_message *msg,
9118c2ecf20Sopenharmony_ci			  struct sk_buff *skb)
9128c2ecf20Sopenharmony_ci{
9138c2ecf20Sopenharmony_ci	u8 prmdata[8];
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	memcpy(prmdata, (void *) skb->data, skb->len);
9168c2ecf20Sopenharmony_ci	prmdata[7] = 0xff - (u8) skb->len;
9178c2ecf20Sopenharmony_ci	return pr_iucv->message_send(path, msg, IUCV_IPRMDATA, 0,
9188c2ecf20Sopenharmony_ci				 (void *) prmdata, 8);
9198c2ecf20Sopenharmony_ci}
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_cistatic int iucv_sock_sendmsg(struct socket *sock, struct msghdr *msg,
9228c2ecf20Sopenharmony_ci			     size_t len)
9238c2ecf20Sopenharmony_ci{
9248c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
9258c2ecf20Sopenharmony_ci	struct iucv_sock *iucv = iucv_sk(sk);
9268c2ecf20Sopenharmony_ci	size_t headroom = 0;
9278c2ecf20Sopenharmony_ci	size_t linear;
9288c2ecf20Sopenharmony_ci	struct sk_buff *skb;
9298c2ecf20Sopenharmony_ci	struct iucv_message txmsg = {0};
9308c2ecf20Sopenharmony_ci	struct cmsghdr *cmsg;
9318c2ecf20Sopenharmony_ci	int cmsg_done;
9328c2ecf20Sopenharmony_ci	long timeo;
9338c2ecf20Sopenharmony_ci	char user_id[9];
9348c2ecf20Sopenharmony_ci	char appl_id[9];
9358c2ecf20Sopenharmony_ci	int err;
9368c2ecf20Sopenharmony_ci	int noblock = msg->msg_flags & MSG_DONTWAIT;
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	err = sock_error(sk);
9398c2ecf20Sopenharmony_ci	if (err)
9408c2ecf20Sopenharmony_ci		return err;
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	if (msg->msg_flags & MSG_OOB)
9438c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	/* SOCK_SEQPACKET: we do not support segmented records */
9468c2ecf20Sopenharmony_ci	if (sk->sk_type == SOCK_SEQPACKET && !(msg->msg_flags & MSG_EOR))
9478c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	lock_sock(sk);
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	if (sk->sk_shutdown & SEND_SHUTDOWN) {
9528c2ecf20Sopenharmony_ci		err = -EPIPE;
9538c2ecf20Sopenharmony_ci		goto out;
9548c2ecf20Sopenharmony_ci	}
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	/* Return if the socket is not in connected state */
9578c2ecf20Sopenharmony_ci	if (sk->sk_state != IUCV_CONNECTED) {
9588c2ecf20Sopenharmony_ci		err = -ENOTCONN;
9598c2ecf20Sopenharmony_ci		goto out;
9608c2ecf20Sopenharmony_ci	}
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	/* initialize defaults */
9638c2ecf20Sopenharmony_ci	cmsg_done   = 0;	/* check for duplicate headers */
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	/* iterate over control messages */
9668c2ecf20Sopenharmony_ci	for_each_cmsghdr(cmsg, msg) {
9678c2ecf20Sopenharmony_ci		if (!CMSG_OK(msg, cmsg)) {
9688c2ecf20Sopenharmony_ci			err = -EINVAL;
9698c2ecf20Sopenharmony_ci			goto out;
9708c2ecf20Sopenharmony_ci		}
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci		if (cmsg->cmsg_level != SOL_IUCV)
9738c2ecf20Sopenharmony_ci			continue;
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci		if (cmsg->cmsg_type & cmsg_done) {
9768c2ecf20Sopenharmony_ci			err = -EINVAL;
9778c2ecf20Sopenharmony_ci			goto out;
9788c2ecf20Sopenharmony_ci		}
9798c2ecf20Sopenharmony_ci		cmsg_done |= cmsg->cmsg_type;
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci		switch (cmsg->cmsg_type) {
9828c2ecf20Sopenharmony_ci		case SCM_IUCV_TRGCLS:
9838c2ecf20Sopenharmony_ci			if (cmsg->cmsg_len != CMSG_LEN(TRGCLS_SIZE)) {
9848c2ecf20Sopenharmony_ci				err = -EINVAL;
9858c2ecf20Sopenharmony_ci				goto out;
9868c2ecf20Sopenharmony_ci			}
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci			/* set iucv message target class */
9898c2ecf20Sopenharmony_ci			memcpy(&txmsg.class,
9908c2ecf20Sopenharmony_ci				(void *) CMSG_DATA(cmsg), TRGCLS_SIZE);
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci			break;
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci		default:
9958c2ecf20Sopenharmony_ci			err = -EINVAL;
9968c2ecf20Sopenharmony_ci			goto out;
9978c2ecf20Sopenharmony_ci		}
9988c2ecf20Sopenharmony_ci	}
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci	/* allocate one skb for each iucv message:
10018c2ecf20Sopenharmony_ci	 * this is fine for SOCK_SEQPACKET (unless we want to support
10028c2ecf20Sopenharmony_ci	 * segmented records using the MSG_EOR flag), but
10038c2ecf20Sopenharmony_ci	 * for SOCK_STREAM we might want to improve it in future */
10048c2ecf20Sopenharmony_ci	if (iucv->transport == AF_IUCV_TRANS_HIPER) {
10058c2ecf20Sopenharmony_ci		headroom = sizeof(struct af_iucv_trans_hdr) +
10068c2ecf20Sopenharmony_ci			   LL_RESERVED_SPACE(iucv->hs_dev);
10078c2ecf20Sopenharmony_ci		linear = len;
10088c2ecf20Sopenharmony_ci	} else {
10098c2ecf20Sopenharmony_ci		if (len < PAGE_SIZE) {
10108c2ecf20Sopenharmony_ci			linear = len;
10118c2ecf20Sopenharmony_ci		} else {
10128c2ecf20Sopenharmony_ci			/* In nonlinear "classic" iucv skb,
10138c2ecf20Sopenharmony_ci			 * reserve space for iucv_array
10148c2ecf20Sopenharmony_ci			 */
10158c2ecf20Sopenharmony_ci			headroom = sizeof(struct iucv_array) *
10168c2ecf20Sopenharmony_ci				   (MAX_SKB_FRAGS + 1);
10178c2ecf20Sopenharmony_ci			linear = PAGE_SIZE - headroom;
10188c2ecf20Sopenharmony_ci		}
10198c2ecf20Sopenharmony_ci	}
10208c2ecf20Sopenharmony_ci	skb = sock_alloc_send_pskb(sk, headroom + linear, len - linear,
10218c2ecf20Sopenharmony_ci				   noblock, &err, 0);
10228c2ecf20Sopenharmony_ci	if (!skb)
10238c2ecf20Sopenharmony_ci		goto out;
10248c2ecf20Sopenharmony_ci	if (headroom)
10258c2ecf20Sopenharmony_ci		skb_reserve(skb, headroom);
10268c2ecf20Sopenharmony_ci	skb_put(skb, linear);
10278c2ecf20Sopenharmony_ci	skb->len = len;
10288c2ecf20Sopenharmony_ci	skb->data_len = len - linear;
10298c2ecf20Sopenharmony_ci	err = skb_copy_datagram_from_iter(skb, 0, &msg->msg_iter, len);
10308c2ecf20Sopenharmony_ci	if (err)
10318c2ecf20Sopenharmony_ci		goto fail;
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	/* wait if outstanding messages for iucv path has reached */
10348c2ecf20Sopenharmony_ci	timeo = sock_sndtimeo(sk, noblock);
10358c2ecf20Sopenharmony_ci	err = iucv_sock_wait(sk, iucv_below_msglim(sk), timeo);
10368c2ecf20Sopenharmony_ci	if (err)
10378c2ecf20Sopenharmony_ci		goto fail;
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	/* return -ECONNRESET if the socket is no longer connected */
10408c2ecf20Sopenharmony_ci	if (sk->sk_state != IUCV_CONNECTED) {
10418c2ecf20Sopenharmony_ci		err = -ECONNRESET;
10428c2ecf20Sopenharmony_ci		goto fail;
10438c2ecf20Sopenharmony_ci	}
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci	/* increment and save iucv message tag for msg_completion cbk */
10468c2ecf20Sopenharmony_ci	txmsg.tag = iucv->send_tag++;
10478c2ecf20Sopenharmony_ci	IUCV_SKB_CB(skb)->tag = txmsg.tag;
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	if (iucv->transport == AF_IUCV_TRANS_HIPER) {
10508c2ecf20Sopenharmony_ci		atomic_inc(&iucv->msg_sent);
10518c2ecf20Sopenharmony_ci		err = afiucv_hs_send(&txmsg, sk, skb, 0);
10528c2ecf20Sopenharmony_ci		if (err) {
10538c2ecf20Sopenharmony_ci			atomic_dec(&iucv->msg_sent);
10548c2ecf20Sopenharmony_ci			goto out;
10558c2ecf20Sopenharmony_ci		}
10568c2ecf20Sopenharmony_ci	} else { /* Classic VM IUCV transport */
10578c2ecf20Sopenharmony_ci		skb_queue_tail(&iucv->send_skb_q, skb);
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci		if (((iucv->path->flags & IUCV_IPRMDATA) & iucv->flags) &&
10608c2ecf20Sopenharmony_ci		    skb->len <= 7) {
10618c2ecf20Sopenharmony_ci			err = iucv_send_iprm(iucv->path, &txmsg, skb);
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci			/* on success: there is no message_complete callback */
10648c2ecf20Sopenharmony_ci			/* for an IPRMDATA msg; remove skb from send queue   */
10658c2ecf20Sopenharmony_ci			if (err == 0) {
10668c2ecf20Sopenharmony_ci				skb_unlink(skb, &iucv->send_skb_q);
10678c2ecf20Sopenharmony_ci				kfree_skb(skb);
10688c2ecf20Sopenharmony_ci			}
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci			/* this error should never happen since the	*/
10718c2ecf20Sopenharmony_ci			/* IUCV_IPRMDATA path flag is set... sever path */
10728c2ecf20Sopenharmony_ci			if (err == 0x15) {
10738c2ecf20Sopenharmony_ci				pr_iucv->path_sever(iucv->path, NULL);
10748c2ecf20Sopenharmony_ci				skb_unlink(skb, &iucv->send_skb_q);
10758c2ecf20Sopenharmony_ci				err = -EPIPE;
10768c2ecf20Sopenharmony_ci				goto fail;
10778c2ecf20Sopenharmony_ci			}
10788c2ecf20Sopenharmony_ci		} else if (skb_is_nonlinear(skb)) {
10798c2ecf20Sopenharmony_ci			struct iucv_array *iba = (struct iucv_array *)skb->head;
10808c2ecf20Sopenharmony_ci			int i;
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci			/* skip iucv_array lying in the headroom */
10838c2ecf20Sopenharmony_ci			iba[0].address = (u32)(addr_t)skb->data;
10848c2ecf20Sopenharmony_ci			iba[0].length = (u32)skb_headlen(skb);
10858c2ecf20Sopenharmony_ci			for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
10868c2ecf20Sopenharmony_ci				skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci				iba[i + 1].address =
10898c2ecf20Sopenharmony_ci					(u32)(addr_t)skb_frag_address(frag);
10908c2ecf20Sopenharmony_ci				iba[i + 1].length = (u32)skb_frag_size(frag);
10918c2ecf20Sopenharmony_ci			}
10928c2ecf20Sopenharmony_ci			err = pr_iucv->message_send(iucv->path, &txmsg,
10938c2ecf20Sopenharmony_ci						    IUCV_IPBUFLST, 0,
10948c2ecf20Sopenharmony_ci						    (void *)iba, skb->len);
10958c2ecf20Sopenharmony_ci		} else { /* non-IPRM Linear skb */
10968c2ecf20Sopenharmony_ci			err = pr_iucv->message_send(iucv->path, &txmsg,
10978c2ecf20Sopenharmony_ci					0, 0, (void *)skb->data, skb->len);
10988c2ecf20Sopenharmony_ci		}
10998c2ecf20Sopenharmony_ci		if (err) {
11008c2ecf20Sopenharmony_ci			if (err == 3) {
11018c2ecf20Sopenharmony_ci				user_id[8] = 0;
11028c2ecf20Sopenharmony_ci				memcpy(user_id, iucv->dst_user_id, 8);
11038c2ecf20Sopenharmony_ci				appl_id[8] = 0;
11048c2ecf20Sopenharmony_ci				memcpy(appl_id, iucv->dst_name, 8);
11058c2ecf20Sopenharmony_ci				pr_err(
11068c2ecf20Sopenharmony_ci		"Application %s on z/VM guest %s exceeds message limit\n",
11078c2ecf20Sopenharmony_ci					appl_id, user_id);
11088c2ecf20Sopenharmony_ci				err = -EAGAIN;
11098c2ecf20Sopenharmony_ci			} else {
11108c2ecf20Sopenharmony_ci				err = -EPIPE;
11118c2ecf20Sopenharmony_ci			}
11128c2ecf20Sopenharmony_ci			skb_unlink(skb, &iucv->send_skb_q);
11138c2ecf20Sopenharmony_ci			goto fail;
11148c2ecf20Sopenharmony_ci		}
11158c2ecf20Sopenharmony_ci	}
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	release_sock(sk);
11188c2ecf20Sopenharmony_ci	return len;
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_cifail:
11218c2ecf20Sopenharmony_ci	kfree_skb(skb);
11228c2ecf20Sopenharmony_ciout:
11238c2ecf20Sopenharmony_ci	release_sock(sk);
11248c2ecf20Sopenharmony_ci	return err;
11258c2ecf20Sopenharmony_ci}
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_cistatic struct sk_buff *alloc_iucv_recv_skb(unsigned long len)
11288c2ecf20Sopenharmony_ci{
11298c2ecf20Sopenharmony_ci	size_t headroom, linear;
11308c2ecf20Sopenharmony_ci	struct sk_buff *skb;
11318c2ecf20Sopenharmony_ci	int err;
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci	if (len < PAGE_SIZE) {
11348c2ecf20Sopenharmony_ci		headroom = 0;
11358c2ecf20Sopenharmony_ci		linear = len;
11368c2ecf20Sopenharmony_ci	} else {
11378c2ecf20Sopenharmony_ci		headroom = sizeof(struct iucv_array) * (MAX_SKB_FRAGS + 1);
11388c2ecf20Sopenharmony_ci		linear = PAGE_SIZE - headroom;
11398c2ecf20Sopenharmony_ci	}
11408c2ecf20Sopenharmony_ci	skb = alloc_skb_with_frags(headroom + linear, len - linear,
11418c2ecf20Sopenharmony_ci				   0, &err, GFP_ATOMIC | GFP_DMA);
11428c2ecf20Sopenharmony_ci	WARN_ONCE(!skb,
11438c2ecf20Sopenharmony_ci		  "alloc of recv iucv skb len=%lu failed with errcode=%d\n",
11448c2ecf20Sopenharmony_ci		  len, err);
11458c2ecf20Sopenharmony_ci	if (skb) {
11468c2ecf20Sopenharmony_ci		if (headroom)
11478c2ecf20Sopenharmony_ci			skb_reserve(skb, headroom);
11488c2ecf20Sopenharmony_ci		skb_put(skb, linear);
11498c2ecf20Sopenharmony_ci		skb->len = len;
11508c2ecf20Sopenharmony_ci		skb->data_len = len - linear;
11518c2ecf20Sopenharmony_ci	}
11528c2ecf20Sopenharmony_ci	return skb;
11538c2ecf20Sopenharmony_ci}
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci/* iucv_process_message() - Receive a single outstanding IUCV message
11568c2ecf20Sopenharmony_ci *
11578c2ecf20Sopenharmony_ci * Locking: must be called with message_q.lock held
11588c2ecf20Sopenharmony_ci */
11598c2ecf20Sopenharmony_cistatic void iucv_process_message(struct sock *sk, struct sk_buff *skb,
11608c2ecf20Sopenharmony_ci				 struct iucv_path *path,
11618c2ecf20Sopenharmony_ci				 struct iucv_message *msg)
11628c2ecf20Sopenharmony_ci{
11638c2ecf20Sopenharmony_ci	int rc;
11648c2ecf20Sopenharmony_ci	unsigned int len;
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci	len = iucv_msg_length(msg);
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	/* store msg target class in the second 4 bytes of skb ctrl buffer */
11698c2ecf20Sopenharmony_ci	/* Note: the first 4 bytes are reserved for msg tag */
11708c2ecf20Sopenharmony_ci	IUCV_SKB_CB(skb)->class = msg->class;
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_ci	/* check for special IPRM messages (e.g. iucv_sock_shutdown) */
11738c2ecf20Sopenharmony_ci	if ((msg->flags & IUCV_IPRMDATA) && len > 7) {
11748c2ecf20Sopenharmony_ci		if (memcmp(msg->rmmsg, iprm_shutdown, 8) == 0) {
11758c2ecf20Sopenharmony_ci			skb->data = NULL;
11768c2ecf20Sopenharmony_ci			skb->len = 0;
11778c2ecf20Sopenharmony_ci		}
11788c2ecf20Sopenharmony_ci	} else {
11798c2ecf20Sopenharmony_ci		if (skb_is_nonlinear(skb)) {
11808c2ecf20Sopenharmony_ci			struct iucv_array *iba = (struct iucv_array *)skb->head;
11818c2ecf20Sopenharmony_ci			int i;
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci			iba[0].address = (u32)(addr_t)skb->data;
11848c2ecf20Sopenharmony_ci			iba[0].length = (u32)skb_headlen(skb);
11858c2ecf20Sopenharmony_ci			for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
11868c2ecf20Sopenharmony_ci				skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci				iba[i + 1].address =
11898c2ecf20Sopenharmony_ci					(u32)(addr_t)skb_frag_address(frag);
11908c2ecf20Sopenharmony_ci				iba[i + 1].length = (u32)skb_frag_size(frag);
11918c2ecf20Sopenharmony_ci			}
11928c2ecf20Sopenharmony_ci			rc = pr_iucv->message_receive(path, msg,
11938c2ecf20Sopenharmony_ci					      IUCV_IPBUFLST,
11948c2ecf20Sopenharmony_ci					      (void *)iba, len, NULL);
11958c2ecf20Sopenharmony_ci		} else {
11968c2ecf20Sopenharmony_ci			rc = pr_iucv->message_receive(path, msg,
11978c2ecf20Sopenharmony_ci					      msg->flags & IUCV_IPRMDATA,
11988c2ecf20Sopenharmony_ci					      skb->data, len, NULL);
11998c2ecf20Sopenharmony_ci		}
12008c2ecf20Sopenharmony_ci		if (rc) {
12018c2ecf20Sopenharmony_ci			kfree_skb(skb);
12028c2ecf20Sopenharmony_ci			return;
12038c2ecf20Sopenharmony_ci		}
12048c2ecf20Sopenharmony_ci		WARN_ON_ONCE(skb->len != len);
12058c2ecf20Sopenharmony_ci	}
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci	IUCV_SKB_CB(skb)->offset = 0;
12088c2ecf20Sopenharmony_ci	if (sk_filter(sk, skb)) {
12098c2ecf20Sopenharmony_ci		atomic_inc(&sk->sk_drops);	/* skb rejected by filter */
12108c2ecf20Sopenharmony_ci		kfree_skb(skb);
12118c2ecf20Sopenharmony_ci		return;
12128c2ecf20Sopenharmony_ci	}
12138c2ecf20Sopenharmony_ci	if (__sock_queue_rcv_skb(sk, skb))	/* handle rcv queue full */
12148c2ecf20Sopenharmony_ci		skb_queue_tail(&iucv_sk(sk)->backlog_skb_q, skb);
12158c2ecf20Sopenharmony_ci}
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci/* iucv_process_message_q() - Process outstanding IUCV messages
12188c2ecf20Sopenharmony_ci *
12198c2ecf20Sopenharmony_ci * Locking: must be called with message_q.lock held
12208c2ecf20Sopenharmony_ci */
12218c2ecf20Sopenharmony_cistatic void iucv_process_message_q(struct sock *sk)
12228c2ecf20Sopenharmony_ci{
12238c2ecf20Sopenharmony_ci	struct iucv_sock *iucv = iucv_sk(sk);
12248c2ecf20Sopenharmony_ci	struct sk_buff *skb;
12258c2ecf20Sopenharmony_ci	struct sock_msg_q *p, *n;
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ci	list_for_each_entry_safe(p, n, &iucv->message_q.list, list) {
12288c2ecf20Sopenharmony_ci		skb = alloc_iucv_recv_skb(iucv_msg_length(&p->msg));
12298c2ecf20Sopenharmony_ci		if (!skb)
12308c2ecf20Sopenharmony_ci			break;
12318c2ecf20Sopenharmony_ci		iucv_process_message(sk, skb, p->path, &p->msg);
12328c2ecf20Sopenharmony_ci		list_del(&p->list);
12338c2ecf20Sopenharmony_ci		kfree(p);
12348c2ecf20Sopenharmony_ci		if (!skb_queue_empty(&iucv->backlog_skb_q))
12358c2ecf20Sopenharmony_ci			break;
12368c2ecf20Sopenharmony_ci	}
12378c2ecf20Sopenharmony_ci}
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_cistatic int iucv_sock_recvmsg(struct socket *sock, struct msghdr *msg,
12408c2ecf20Sopenharmony_ci			     size_t len, int flags)
12418c2ecf20Sopenharmony_ci{
12428c2ecf20Sopenharmony_ci	int noblock = flags & MSG_DONTWAIT;
12438c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
12448c2ecf20Sopenharmony_ci	struct iucv_sock *iucv = iucv_sk(sk);
12458c2ecf20Sopenharmony_ci	unsigned int copied, rlen;
12468c2ecf20Sopenharmony_ci	struct sk_buff *skb, *rskb, *cskb;
12478c2ecf20Sopenharmony_ci	int err = 0;
12488c2ecf20Sopenharmony_ci	u32 offset;
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci	if ((sk->sk_state == IUCV_DISCONN) &&
12518c2ecf20Sopenharmony_ci	    skb_queue_empty(&iucv->backlog_skb_q) &&
12528c2ecf20Sopenharmony_ci	    skb_queue_empty(&sk->sk_receive_queue) &&
12538c2ecf20Sopenharmony_ci	    list_empty(&iucv->message_q.list))
12548c2ecf20Sopenharmony_ci		return 0;
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	if (flags & (MSG_OOB))
12578c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci	/* receive/dequeue next skb:
12608c2ecf20Sopenharmony_ci	 * the function understands MSG_PEEK and, thus, does not dequeue skb */
12618c2ecf20Sopenharmony_ci	skb = skb_recv_datagram(sk, flags, noblock, &err);
12628c2ecf20Sopenharmony_ci	if (!skb) {
12638c2ecf20Sopenharmony_ci		if (sk->sk_shutdown & RCV_SHUTDOWN)
12648c2ecf20Sopenharmony_ci			return 0;
12658c2ecf20Sopenharmony_ci		return err;
12668c2ecf20Sopenharmony_ci	}
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_ci	offset = IUCV_SKB_CB(skb)->offset;
12698c2ecf20Sopenharmony_ci	rlen   = skb->len - offset;		/* real length of skb */
12708c2ecf20Sopenharmony_ci	copied = min_t(unsigned int, rlen, len);
12718c2ecf20Sopenharmony_ci	if (!rlen)
12728c2ecf20Sopenharmony_ci		sk->sk_shutdown = sk->sk_shutdown | RCV_SHUTDOWN;
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ci	cskb = skb;
12758c2ecf20Sopenharmony_ci	if (skb_copy_datagram_msg(cskb, offset, msg, copied)) {
12768c2ecf20Sopenharmony_ci		if (!(flags & MSG_PEEK))
12778c2ecf20Sopenharmony_ci			skb_queue_head(&sk->sk_receive_queue, skb);
12788c2ecf20Sopenharmony_ci		return -EFAULT;
12798c2ecf20Sopenharmony_ci	}
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_ci	/* SOCK_SEQPACKET: set MSG_TRUNC if recv buf size is too small */
12828c2ecf20Sopenharmony_ci	if (sk->sk_type == SOCK_SEQPACKET) {
12838c2ecf20Sopenharmony_ci		if (copied < rlen)
12848c2ecf20Sopenharmony_ci			msg->msg_flags |= MSG_TRUNC;
12858c2ecf20Sopenharmony_ci		/* each iucv message contains a complete record */
12868c2ecf20Sopenharmony_ci		msg->msg_flags |= MSG_EOR;
12878c2ecf20Sopenharmony_ci	}
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	/* create control message to store iucv msg target class:
12908c2ecf20Sopenharmony_ci	 * get the trgcls from the control buffer of the skb due to
12918c2ecf20Sopenharmony_ci	 * fragmentation of original iucv message. */
12928c2ecf20Sopenharmony_ci	err = put_cmsg(msg, SOL_IUCV, SCM_IUCV_TRGCLS,
12938c2ecf20Sopenharmony_ci		       sizeof(IUCV_SKB_CB(skb)->class),
12948c2ecf20Sopenharmony_ci		       (void *)&IUCV_SKB_CB(skb)->class);
12958c2ecf20Sopenharmony_ci	if (err) {
12968c2ecf20Sopenharmony_ci		if (!(flags & MSG_PEEK))
12978c2ecf20Sopenharmony_ci			skb_queue_head(&sk->sk_receive_queue, skb);
12988c2ecf20Sopenharmony_ci		return err;
12998c2ecf20Sopenharmony_ci	}
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci	/* Mark read part of skb as used */
13028c2ecf20Sopenharmony_ci	if (!(flags & MSG_PEEK)) {
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci		/* SOCK_STREAM: re-queue skb if it contains unreceived data */
13058c2ecf20Sopenharmony_ci		if (sk->sk_type == SOCK_STREAM) {
13068c2ecf20Sopenharmony_ci			if (copied < rlen) {
13078c2ecf20Sopenharmony_ci				IUCV_SKB_CB(skb)->offset = offset + copied;
13088c2ecf20Sopenharmony_ci				skb_queue_head(&sk->sk_receive_queue, skb);
13098c2ecf20Sopenharmony_ci				goto done;
13108c2ecf20Sopenharmony_ci			}
13118c2ecf20Sopenharmony_ci		}
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci		kfree_skb(skb);
13148c2ecf20Sopenharmony_ci		if (iucv->transport == AF_IUCV_TRANS_HIPER) {
13158c2ecf20Sopenharmony_ci			atomic_inc(&iucv->msg_recv);
13168c2ecf20Sopenharmony_ci			if (atomic_read(&iucv->msg_recv) > iucv->msglimit) {
13178c2ecf20Sopenharmony_ci				WARN_ON(1);
13188c2ecf20Sopenharmony_ci				iucv_sock_close(sk);
13198c2ecf20Sopenharmony_ci				return -EFAULT;
13208c2ecf20Sopenharmony_ci			}
13218c2ecf20Sopenharmony_ci		}
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci		/* Queue backlog skbs */
13248c2ecf20Sopenharmony_ci		spin_lock_bh(&iucv->message_q.lock);
13258c2ecf20Sopenharmony_ci		rskb = skb_dequeue(&iucv->backlog_skb_q);
13268c2ecf20Sopenharmony_ci		while (rskb) {
13278c2ecf20Sopenharmony_ci			IUCV_SKB_CB(rskb)->offset = 0;
13288c2ecf20Sopenharmony_ci			if (__sock_queue_rcv_skb(sk, rskb)) {
13298c2ecf20Sopenharmony_ci				/* handle rcv queue full */
13308c2ecf20Sopenharmony_ci				skb_queue_head(&iucv->backlog_skb_q,
13318c2ecf20Sopenharmony_ci						rskb);
13328c2ecf20Sopenharmony_ci				break;
13338c2ecf20Sopenharmony_ci			}
13348c2ecf20Sopenharmony_ci			rskb = skb_dequeue(&iucv->backlog_skb_q);
13358c2ecf20Sopenharmony_ci		}
13368c2ecf20Sopenharmony_ci		if (skb_queue_empty(&iucv->backlog_skb_q)) {
13378c2ecf20Sopenharmony_ci			if (!list_empty(&iucv->message_q.list))
13388c2ecf20Sopenharmony_ci				iucv_process_message_q(sk);
13398c2ecf20Sopenharmony_ci			if (atomic_read(&iucv->msg_recv) >=
13408c2ecf20Sopenharmony_ci							iucv->msglimit / 2) {
13418c2ecf20Sopenharmony_ci				err = iucv_send_ctrl(sk, AF_IUCV_FLAG_WIN);
13428c2ecf20Sopenharmony_ci				if (err) {
13438c2ecf20Sopenharmony_ci					sk->sk_state = IUCV_DISCONN;
13448c2ecf20Sopenharmony_ci					sk->sk_state_change(sk);
13458c2ecf20Sopenharmony_ci				}
13468c2ecf20Sopenharmony_ci			}
13478c2ecf20Sopenharmony_ci		}
13488c2ecf20Sopenharmony_ci		spin_unlock_bh(&iucv->message_q.lock);
13498c2ecf20Sopenharmony_ci	}
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_cidone:
13528c2ecf20Sopenharmony_ci	/* SOCK_SEQPACKET: return real length if MSG_TRUNC is set */
13538c2ecf20Sopenharmony_ci	if (sk->sk_type == SOCK_SEQPACKET && (flags & MSG_TRUNC))
13548c2ecf20Sopenharmony_ci		copied = rlen;
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_ci	return copied;
13578c2ecf20Sopenharmony_ci}
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_cistatic inline __poll_t iucv_accept_poll(struct sock *parent)
13608c2ecf20Sopenharmony_ci{
13618c2ecf20Sopenharmony_ci	struct iucv_sock *isk, *n;
13628c2ecf20Sopenharmony_ci	struct sock *sk;
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_ci	list_for_each_entry_safe(isk, n, &iucv_sk(parent)->accept_q, accept_q) {
13658c2ecf20Sopenharmony_ci		sk = (struct sock *) isk;
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_ci		if (sk->sk_state == IUCV_CONNECTED)
13688c2ecf20Sopenharmony_ci			return EPOLLIN | EPOLLRDNORM;
13698c2ecf20Sopenharmony_ci	}
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ci	return 0;
13728c2ecf20Sopenharmony_ci}
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_cistatic __poll_t iucv_sock_poll(struct file *file, struct socket *sock,
13758c2ecf20Sopenharmony_ci			       poll_table *wait)
13768c2ecf20Sopenharmony_ci{
13778c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
13788c2ecf20Sopenharmony_ci	__poll_t mask = 0;
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	sock_poll_wait(file, sock, wait);
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	if (sk->sk_state == IUCV_LISTEN)
13838c2ecf20Sopenharmony_ci		return iucv_accept_poll(sk);
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci	if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
13868c2ecf20Sopenharmony_ci		mask |= EPOLLERR |
13878c2ecf20Sopenharmony_ci			(sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? EPOLLPRI : 0);
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_ci	if (sk->sk_shutdown & RCV_SHUTDOWN)
13908c2ecf20Sopenharmony_ci		mask |= EPOLLRDHUP;
13918c2ecf20Sopenharmony_ci
13928c2ecf20Sopenharmony_ci	if (sk->sk_shutdown == SHUTDOWN_MASK)
13938c2ecf20Sopenharmony_ci		mask |= EPOLLHUP;
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci	if (!skb_queue_empty(&sk->sk_receive_queue) ||
13968c2ecf20Sopenharmony_ci	    (sk->sk_shutdown & RCV_SHUTDOWN))
13978c2ecf20Sopenharmony_ci		mask |= EPOLLIN | EPOLLRDNORM;
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	if (sk->sk_state == IUCV_CLOSED)
14008c2ecf20Sopenharmony_ci		mask |= EPOLLHUP;
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_ci	if (sk->sk_state == IUCV_DISCONN)
14038c2ecf20Sopenharmony_ci		mask |= EPOLLIN;
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci	if (sock_writeable(sk) && iucv_below_msglim(sk))
14068c2ecf20Sopenharmony_ci		mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND;
14078c2ecf20Sopenharmony_ci	else
14088c2ecf20Sopenharmony_ci		sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci	return mask;
14118c2ecf20Sopenharmony_ci}
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_cistatic int iucv_sock_shutdown(struct socket *sock, int how)
14148c2ecf20Sopenharmony_ci{
14158c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
14168c2ecf20Sopenharmony_ci	struct iucv_sock *iucv = iucv_sk(sk);
14178c2ecf20Sopenharmony_ci	struct iucv_message txmsg;
14188c2ecf20Sopenharmony_ci	int err = 0;
14198c2ecf20Sopenharmony_ci
14208c2ecf20Sopenharmony_ci	how++;
14218c2ecf20Sopenharmony_ci
14228c2ecf20Sopenharmony_ci	if ((how & ~SHUTDOWN_MASK) || !how)
14238c2ecf20Sopenharmony_ci		return -EINVAL;
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci	lock_sock(sk);
14268c2ecf20Sopenharmony_ci	switch (sk->sk_state) {
14278c2ecf20Sopenharmony_ci	case IUCV_LISTEN:
14288c2ecf20Sopenharmony_ci	case IUCV_DISCONN:
14298c2ecf20Sopenharmony_ci	case IUCV_CLOSING:
14308c2ecf20Sopenharmony_ci	case IUCV_CLOSED:
14318c2ecf20Sopenharmony_ci		err = -ENOTCONN;
14328c2ecf20Sopenharmony_ci		goto fail;
14338c2ecf20Sopenharmony_ci	default:
14348c2ecf20Sopenharmony_ci		break;
14358c2ecf20Sopenharmony_ci	}
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci	if ((how == SEND_SHUTDOWN || how == SHUTDOWN_MASK) &&
14388c2ecf20Sopenharmony_ci	    sk->sk_state == IUCV_CONNECTED) {
14398c2ecf20Sopenharmony_ci		if (iucv->transport == AF_IUCV_TRANS_IUCV) {
14408c2ecf20Sopenharmony_ci			txmsg.class = 0;
14418c2ecf20Sopenharmony_ci			txmsg.tag = 0;
14428c2ecf20Sopenharmony_ci			err = pr_iucv->message_send(iucv->path, &txmsg,
14438c2ecf20Sopenharmony_ci				IUCV_IPRMDATA, 0, (void *) iprm_shutdown, 8);
14448c2ecf20Sopenharmony_ci			if (err) {
14458c2ecf20Sopenharmony_ci				switch (err) {
14468c2ecf20Sopenharmony_ci				case 1:
14478c2ecf20Sopenharmony_ci					err = -ENOTCONN;
14488c2ecf20Sopenharmony_ci					break;
14498c2ecf20Sopenharmony_ci				case 2:
14508c2ecf20Sopenharmony_ci					err = -ECONNRESET;
14518c2ecf20Sopenharmony_ci					break;
14528c2ecf20Sopenharmony_ci				default:
14538c2ecf20Sopenharmony_ci					err = -ENOTCONN;
14548c2ecf20Sopenharmony_ci					break;
14558c2ecf20Sopenharmony_ci				}
14568c2ecf20Sopenharmony_ci			}
14578c2ecf20Sopenharmony_ci		} else
14588c2ecf20Sopenharmony_ci			iucv_send_ctrl(sk, AF_IUCV_FLAG_SHT);
14598c2ecf20Sopenharmony_ci	}
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci	sk->sk_shutdown |= how;
14628c2ecf20Sopenharmony_ci	if (how == RCV_SHUTDOWN || how == SHUTDOWN_MASK) {
14638c2ecf20Sopenharmony_ci		if ((iucv->transport == AF_IUCV_TRANS_IUCV) &&
14648c2ecf20Sopenharmony_ci		    iucv->path) {
14658c2ecf20Sopenharmony_ci			err = pr_iucv->path_quiesce(iucv->path, NULL);
14668c2ecf20Sopenharmony_ci			if (err)
14678c2ecf20Sopenharmony_ci				err = -ENOTCONN;
14688c2ecf20Sopenharmony_ci/*			skb_queue_purge(&sk->sk_receive_queue); */
14698c2ecf20Sopenharmony_ci		}
14708c2ecf20Sopenharmony_ci		skb_queue_purge(&sk->sk_receive_queue);
14718c2ecf20Sopenharmony_ci	}
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci	/* Wake up anyone sleeping in poll */
14748c2ecf20Sopenharmony_ci	sk->sk_state_change(sk);
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_cifail:
14778c2ecf20Sopenharmony_ci	release_sock(sk);
14788c2ecf20Sopenharmony_ci	return err;
14798c2ecf20Sopenharmony_ci}
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_cistatic int iucv_sock_release(struct socket *sock)
14828c2ecf20Sopenharmony_ci{
14838c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
14848c2ecf20Sopenharmony_ci	int err = 0;
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci	if (!sk)
14878c2ecf20Sopenharmony_ci		return 0;
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci	iucv_sock_close(sk);
14908c2ecf20Sopenharmony_ci
14918c2ecf20Sopenharmony_ci	sock_orphan(sk);
14928c2ecf20Sopenharmony_ci	iucv_sock_kill(sk);
14938c2ecf20Sopenharmony_ci	return err;
14948c2ecf20Sopenharmony_ci}
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci/* getsockopt and setsockopt */
14978c2ecf20Sopenharmony_cistatic int iucv_sock_setsockopt(struct socket *sock, int level, int optname,
14988c2ecf20Sopenharmony_ci				sockptr_t optval, unsigned int optlen)
14998c2ecf20Sopenharmony_ci{
15008c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
15018c2ecf20Sopenharmony_ci	struct iucv_sock *iucv = iucv_sk(sk);
15028c2ecf20Sopenharmony_ci	int val;
15038c2ecf20Sopenharmony_ci	int rc;
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci	if (level != SOL_IUCV)
15068c2ecf20Sopenharmony_ci		return -ENOPROTOOPT;
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_ci	if (optlen < sizeof(int))
15098c2ecf20Sopenharmony_ci		return -EINVAL;
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ci	if (copy_from_sockptr(&val, optval, sizeof(int)))
15128c2ecf20Sopenharmony_ci		return -EFAULT;
15138c2ecf20Sopenharmony_ci
15148c2ecf20Sopenharmony_ci	rc = 0;
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci	lock_sock(sk);
15178c2ecf20Sopenharmony_ci	switch (optname) {
15188c2ecf20Sopenharmony_ci	case SO_IPRMDATA_MSG:
15198c2ecf20Sopenharmony_ci		if (val)
15208c2ecf20Sopenharmony_ci			iucv->flags |= IUCV_IPRMDATA;
15218c2ecf20Sopenharmony_ci		else
15228c2ecf20Sopenharmony_ci			iucv->flags &= ~IUCV_IPRMDATA;
15238c2ecf20Sopenharmony_ci		break;
15248c2ecf20Sopenharmony_ci	case SO_MSGLIMIT:
15258c2ecf20Sopenharmony_ci		switch (sk->sk_state) {
15268c2ecf20Sopenharmony_ci		case IUCV_OPEN:
15278c2ecf20Sopenharmony_ci		case IUCV_BOUND:
15288c2ecf20Sopenharmony_ci			if (val < 1 || val > U16_MAX)
15298c2ecf20Sopenharmony_ci				rc = -EINVAL;
15308c2ecf20Sopenharmony_ci			else
15318c2ecf20Sopenharmony_ci				iucv->msglimit = val;
15328c2ecf20Sopenharmony_ci			break;
15338c2ecf20Sopenharmony_ci		default:
15348c2ecf20Sopenharmony_ci			rc = -EINVAL;
15358c2ecf20Sopenharmony_ci			break;
15368c2ecf20Sopenharmony_ci		}
15378c2ecf20Sopenharmony_ci		break;
15388c2ecf20Sopenharmony_ci	default:
15398c2ecf20Sopenharmony_ci		rc = -ENOPROTOOPT;
15408c2ecf20Sopenharmony_ci		break;
15418c2ecf20Sopenharmony_ci	}
15428c2ecf20Sopenharmony_ci	release_sock(sk);
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci	return rc;
15458c2ecf20Sopenharmony_ci}
15468c2ecf20Sopenharmony_ci
15478c2ecf20Sopenharmony_cistatic int iucv_sock_getsockopt(struct socket *sock, int level, int optname,
15488c2ecf20Sopenharmony_ci				char __user *optval, int __user *optlen)
15498c2ecf20Sopenharmony_ci{
15508c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
15518c2ecf20Sopenharmony_ci	struct iucv_sock *iucv = iucv_sk(sk);
15528c2ecf20Sopenharmony_ci	unsigned int val;
15538c2ecf20Sopenharmony_ci	int len;
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci	if (level != SOL_IUCV)
15568c2ecf20Sopenharmony_ci		return -ENOPROTOOPT;
15578c2ecf20Sopenharmony_ci
15588c2ecf20Sopenharmony_ci	if (get_user(len, optlen))
15598c2ecf20Sopenharmony_ci		return -EFAULT;
15608c2ecf20Sopenharmony_ci
15618c2ecf20Sopenharmony_ci	if (len < 0)
15628c2ecf20Sopenharmony_ci		return -EINVAL;
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci	len = min_t(unsigned int, len, sizeof(int));
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_ci	switch (optname) {
15678c2ecf20Sopenharmony_ci	case SO_IPRMDATA_MSG:
15688c2ecf20Sopenharmony_ci		val = (iucv->flags & IUCV_IPRMDATA) ? 1 : 0;
15698c2ecf20Sopenharmony_ci		break;
15708c2ecf20Sopenharmony_ci	case SO_MSGLIMIT:
15718c2ecf20Sopenharmony_ci		lock_sock(sk);
15728c2ecf20Sopenharmony_ci		val = (iucv->path != NULL) ? iucv->path->msglim	/* connected */
15738c2ecf20Sopenharmony_ci					   : iucv->msglimit;	/* default */
15748c2ecf20Sopenharmony_ci		release_sock(sk);
15758c2ecf20Sopenharmony_ci		break;
15768c2ecf20Sopenharmony_ci	case SO_MSGSIZE:
15778c2ecf20Sopenharmony_ci		if (sk->sk_state == IUCV_OPEN)
15788c2ecf20Sopenharmony_ci			return -EBADFD;
15798c2ecf20Sopenharmony_ci		val = (iucv->hs_dev) ? iucv->hs_dev->mtu -
15808c2ecf20Sopenharmony_ci				sizeof(struct af_iucv_trans_hdr) - ETH_HLEN :
15818c2ecf20Sopenharmony_ci				0x7fffffff;
15828c2ecf20Sopenharmony_ci		break;
15838c2ecf20Sopenharmony_ci	default:
15848c2ecf20Sopenharmony_ci		return -ENOPROTOOPT;
15858c2ecf20Sopenharmony_ci	}
15868c2ecf20Sopenharmony_ci
15878c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
15888c2ecf20Sopenharmony_ci		return -EFAULT;
15898c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &val, len))
15908c2ecf20Sopenharmony_ci		return -EFAULT;
15918c2ecf20Sopenharmony_ci
15928c2ecf20Sopenharmony_ci	return 0;
15938c2ecf20Sopenharmony_ci}
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci/* Callback wrappers - called from iucv base support */
15978c2ecf20Sopenharmony_cistatic int iucv_callback_connreq(struct iucv_path *path,
15988c2ecf20Sopenharmony_ci				 u8 ipvmid[8], u8 ipuser[16])
15998c2ecf20Sopenharmony_ci{
16008c2ecf20Sopenharmony_ci	unsigned char user_data[16];
16018c2ecf20Sopenharmony_ci	unsigned char nuser_data[16];
16028c2ecf20Sopenharmony_ci	unsigned char src_name[8];
16038c2ecf20Sopenharmony_ci	struct sock *sk, *nsk;
16048c2ecf20Sopenharmony_ci	struct iucv_sock *iucv, *niucv;
16058c2ecf20Sopenharmony_ci	int err;
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_ci	memcpy(src_name, ipuser, 8);
16088c2ecf20Sopenharmony_ci	EBCASC(src_name, 8);
16098c2ecf20Sopenharmony_ci	/* Find out if this path belongs to af_iucv. */
16108c2ecf20Sopenharmony_ci	read_lock(&iucv_sk_list.lock);
16118c2ecf20Sopenharmony_ci	iucv = NULL;
16128c2ecf20Sopenharmony_ci	sk = NULL;
16138c2ecf20Sopenharmony_ci	sk_for_each(sk, &iucv_sk_list.head)
16148c2ecf20Sopenharmony_ci		if (sk->sk_state == IUCV_LISTEN &&
16158c2ecf20Sopenharmony_ci		    !memcmp(&iucv_sk(sk)->src_name, src_name, 8)) {
16168c2ecf20Sopenharmony_ci			/*
16178c2ecf20Sopenharmony_ci			 * Found a listening socket with
16188c2ecf20Sopenharmony_ci			 * src_name == ipuser[0-7].
16198c2ecf20Sopenharmony_ci			 */
16208c2ecf20Sopenharmony_ci			iucv = iucv_sk(sk);
16218c2ecf20Sopenharmony_ci			break;
16228c2ecf20Sopenharmony_ci		}
16238c2ecf20Sopenharmony_ci	read_unlock(&iucv_sk_list.lock);
16248c2ecf20Sopenharmony_ci	if (!iucv)
16258c2ecf20Sopenharmony_ci		/* No socket found, not one of our paths. */
16268c2ecf20Sopenharmony_ci		return -EINVAL;
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci	bh_lock_sock(sk);
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_ci	/* Check if parent socket is listening */
16318c2ecf20Sopenharmony_ci	low_nmcpy(user_data, iucv->src_name);
16328c2ecf20Sopenharmony_ci	high_nmcpy(user_data, iucv->dst_name);
16338c2ecf20Sopenharmony_ci	ASCEBC(user_data, sizeof(user_data));
16348c2ecf20Sopenharmony_ci	if (sk->sk_state != IUCV_LISTEN) {
16358c2ecf20Sopenharmony_ci		err = pr_iucv->path_sever(path, user_data);
16368c2ecf20Sopenharmony_ci		iucv_path_free(path);
16378c2ecf20Sopenharmony_ci		goto fail;
16388c2ecf20Sopenharmony_ci	}
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_ci	/* Check for backlog size */
16418c2ecf20Sopenharmony_ci	if (sk_acceptq_is_full(sk)) {
16428c2ecf20Sopenharmony_ci		err = pr_iucv->path_sever(path, user_data);
16438c2ecf20Sopenharmony_ci		iucv_path_free(path);
16448c2ecf20Sopenharmony_ci		goto fail;
16458c2ecf20Sopenharmony_ci	}
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ci	/* Create the new socket */
16488c2ecf20Sopenharmony_ci	nsk = iucv_sock_alloc(NULL, sk->sk_protocol, GFP_ATOMIC, 0);
16498c2ecf20Sopenharmony_ci	if (!nsk) {
16508c2ecf20Sopenharmony_ci		err = pr_iucv->path_sever(path, user_data);
16518c2ecf20Sopenharmony_ci		iucv_path_free(path);
16528c2ecf20Sopenharmony_ci		goto fail;
16538c2ecf20Sopenharmony_ci	}
16548c2ecf20Sopenharmony_ci
16558c2ecf20Sopenharmony_ci	niucv = iucv_sk(nsk);
16568c2ecf20Sopenharmony_ci	iucv_sock_init(nsk, sk);
16578c2ecf20Sopenharmony_ci	niucv->transport = AF_IUCV_TRANS_IUCV;
16588c2ecf20Sopenharmony_ci	nsk->sk_allocation |= GFP_DMA;
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_ci	/* Set the new iucv_sock */
16618c2ecf20Sopenharmony_ci	memcpy(niucv->dst_name, ipuser + 8, 8);
16628c2ecf20Sopenharmony_ci	EBCASC(niucv->dst_name, 8);
16638c2ecf20Sopenharmony_ci	memcpy(niucv->dst_user_id, ipvmid, 8);
16648c2ecf20Sopenharmony_ci	memcpy(niucv->src_name, iucv->src_name, 8);
16658c2ecf20Sopenharmony_ci	memcpy(niucv->src_user_id, iucv->src_user_id, 8);
16668c2ecf20Sopenharmony_ci	niucv->path = path;
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci	/* Call iucv_accept */
16698c2ecf20Sopenharmony_ci	high_nmcpy(nuser_data, ipuser + 8);
16708c2ecf20Sopenharmony_ci	memcpy(nuser_data + 8, niucv->src_name, 8);
16718c2ecf20Sopenharmony_ci	ASCEBC(nuser_data + 8, 8);
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ci	/* set message limit for path based on msglimit of accepting socket */
16748c2ecf20Sopenharmony_ci	niucv->msglimit = iucv->msglimit;
16758c2ecf20Sopenharmony_ci	path->msglim = iucv->msglimit;
16768c2ecf20Sopenharmony_ci	err = pr_iucv->path_accept(path, &af_iucv_handler, nuser_data, nsk);
16778c2ecf20Sopenharmony_ci	if (err) {
16788c2ecf20Sopenharmony_ci		iucv_sever_path(nsk, 1);
16798c2ecf20Sopenharmony_ci		iucv_sock_kill(nsk);
16808c2ecf20Sopenharmony_ci		goto fail;
16818c2ecf20Sopenharmony_ci	}
16828c2ecf20Sopenharmony_ci
16838c2ecf20Sopenharmony_ci	iucv_accept_enqueue(sk, nsk);
16848c2ecf20Sopenharmony_ci
16858c2ecf20Sopenharmony_ci	/* Wake up accept */
16868c2ecf20Sopenharmony_ci	nsk->sk_state = IUCV_CONNECTED;
16878c2ecf20Sopenharmony_ci	sk->sk_data_ready(sk);
16888c2ecf20Sopenharmony_ci	err = 0;
16898c2ecf20Sopenharmony_cifail:
16908c2ecf20Sopenharmony_ci	bh_unlock_sock(sk);
16918c2ecf20Sopenharmony_ci	return 0;
16928c2ecf20Sopenharmony_ci}
16938c2ecf20Sopenharmony_ci
16948c2ecf20Sopenharmony_cistatic void iucv_callback_connack(struct iucv_path *path, u8 ipuser[16])
16958c2ecf20Sopenharmony_ci{
16968c2ecf20Sopenharmony_ci	struct sock *sk = path->private;
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci	sk->sk_state = IUCV_CONNECTED;
16998c2ecf20Sopenharmony_ci	sk->sk_state_change(sk);
17008c2ecf20Sopenharmony_ci}
17018c2ecf20Sopenharmony_ci
17028c2ecf20Sopenharmony_cistatic void iucv_callback_rx(struct iucv_path *path, struct iucv_message *msg)
17038c2ecf20Sopenharmony_ci{
17048c2ecf20Sopenharmony_ci	struct sock *sk = path->private;
17058c2ecf20Sopenharmony_ci	struct iucv_sock *iucv = iucv_sk(sk);
17068c2ecf20Sopenharmony_ci	struct sk_buff *skb;
17078c2ecf20Sopenharmony_ci	struct sock_msg_q *save_msg;
17088c2ecf20Sopenharmony_ci	int len;
17098c2ecf20Sopenharmony_ci
17108c2ecf20Sopenharmony_ci	if (sk->sk_shutdown & RCV_SHUTDOWN) {
17118c2ecf20Sopenharmony_ci		pr_iucv->message_reject(path, msg);
17128c2ecf20Sopenharmony_ci		return;
17138c2ecf20Sopenharmony_ci	}
17148c2ecf20Sopenharmony_ci
17158c2ecf20Sopenharmony_ci	spin_lock(&iucv->message_q.lock);
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci	if (!list_empty(&iucv->message_q.list) ||
17188c2ecf20Sopenharmony_ci	    !skb_queue_empty(&iucv->backlog_skb_q))
17198c2ecf20Sopenharmony_ci		goto save_message;
17208c2ecf20Sopenharmony_ci
17218c2ecf20Sopenharmony_ci	len = atomic_read(&sk->sk_rmem_alloc);
17228c2ecf20Sopenharmony_ci	len += SKB_TRUESIZE(iucv_msg_length(msg));
17238c2ecf20Sopenharmony_ci	if (len > sk->sk_rcvbuf)
17248c2ecf20Sopenharmony_ci		goto save_message;
17258c2ecf20Sopenharmony_ci
17268c2ecf20Sopenharmony_ci	skb = alloc_iucv_recv_skb(iucv_msg_length(msg));
17278c2ecf20Sopenharmony_ci	if (!skb)
17288c2ecf20Sopenharmony_ci		goto save_message;
17298c2ecf20Sopenharmony_ci
17308c2ecf20Sopenharmony_ci	iucv_process_message(sk, skb, path, msg);
17318c2ecf20Sopenharmony_ci	goto out_unlock;
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_cisave_message:
17348c2ecf20Sopenharmony_ci	save_msg = kzalloc(sizeof(struct sock_msg_q), GFP_ATOMIC | GFP_DMA);
17358c2ecf20Sopenharmony_ci	if (!save_msg)
17368c2ecf20Sopenharmony_ci		goto out_unlock;
17378c2ecf20Sopenharmony_ci	save_msg->path = path;
17388c2ecf20Sopenharmony_ci	save_msg->msg = *msg;
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci	list_add_tail(&save_msg->list, &iucv->message_q.list);
17418c2ecf20Sopenharmony_ci
17428c2ecf20Sopenharmony_ciout_unlock:
17438c2ecf20Sopenharmony_ci	spin_unlock(&iucv->message_q.lock);
17448c2ecf20Sopenharmony_ci}
17458c2ecf20Sopenharmony_ci
17468c2ecf20Sopenharmony_cistatic void iucv_callback_txdone(struct iucv_path *path,
17478c2ecf20Sopenharmony_ci				 struct iucv_message *msg)
17488c2ecf20Sopenharmony_ci{
17498c2ecf20Sopenharmony_ci	struct sock *sk = path->private;
17508c2ecf20Sopenharmony_ci	struct sk_buff *this = NULL;
17518c2ecf20Sopenharmony_ci	struct sk_buff_head *list = &iucv_sk(sk)->send_skb_q;
17528c2ecf20Sopenharmony_ci	struct sk_buff *list_skb;
17538c2ecf20Sopenharmony_ci	unsigned long flags;
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_ci	bh_lock_sock(sk);
17568c2ecf20Sopenharmony_ci
17578c2ecf20Sopenharmony_ci	spin_lock_irqsave(&list->lock, flags);
17588c2ecf20Sopenharmony_ci	skb_queue_walk(list, list_skb) {
17598c2ecf20Sopenharmony_ci		if (msg->tag == IUCV_SKB_CB(list_skb)->tag) {
17608c2ecf20Sopenharmony_ci			this = list_skb;
17618c2ecf20Sopenharmony_ci			break;
17628c2ecf20Sopenharmony_ci		}
17638c2ecf20Sopenharmony_ci	}
17648c2ecf20Sopenharmony_ci	if (this)
17658c2ecf20Sopenharmony_ci		__skb_unlink(this, list);
17668c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&list->lock, flags);
17678c2ecf20Sopenharmony_ci
17688c2ecf20Sopenharmony_ci	if (this) {
17698c2ecf20Sopenharmony_ci		kfree_skb(this);
17708c2ecf20Sopenharmony_ci		/* wake up any process waiting for sending */
17718c2ecf20Sopenharmony_ci		iucv_sock_wake_msglim(sk);
17728c2ecf20Sopenharmony_ci	}
17738c2ecf20Sopenharmony_ci
17748c2ecf20Sopenharmony_ci	if (sk->sk_state == IUCV_CLOSING) {
17758c2ecf20Sopenharmony_ci		if (skb_queue_empty(&iucv_sk(sk)->send_skb_q)) {
17768c2ecf20Sopenharmony_ci			sk->sk_state = IUCV_CLOSED;
17778c2ecf20Sopenharmony_ci			sk->sk_state_change(sk);
17788c2ecf20Sopenharmony_ci		}
17798c2ecf20Sopenharmony_ci	}
17808c2ecf20Sopenharmony_ci	bh_unlock_sock(sk);
17818c2ecf20Sopenharmony_ci
17828c2ecf20Sopenharmony_ci}
17838c2ecf20Sopenharmony_ci
17848c2ecf20Sopenharmony_cistatic void iucv_callback_connrej(struct iucv_path *path, u8 ipuser[16])
17858c2ecf20Sopenharmony_ci{
17868c2ecf20Sopenharmony_ci	struct sock *sk = path->private;
17878c2ecf20Sopenharmony_ci
17888c2ecf20Sopenharmony_ci	if (sk->sk_state == IUCV_CLOSED)
17898c2ecf20Sopenharmony_ci		return;
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_ci	bh_lock_sock(sk);
17928c2ecf20Sopenharmony_ci	iucv_sever_path(sk, 1);
17938c2ecf20Sopenharmony_ci	sk->sk_state = IUCV_DISCONN;
17948c2ecf20Sopenharmony_ci
17958c2ecf20Sopenharmony_ci	sk->sk_state_change(sk);
17968c2ecf20Sopenharmony_ci	bh_unlock_sock(sk);
17978c2ecf20Sopenharmony_ci}
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_ci/* called if the other communication side shuts down its RECV direction;
18008c2ecf20Sopenharmony_ci * in turn, the callback sets SEND_SHUTDOWN to disable sending of data.
18018c2ecf20Sopenharmony_ci */
18028c2ecf20Sopenharmony_cistatic void iucv_callback_shutdown(struct iucv_path *path, u8 ipuser[16])
18038c2ecf20Sopenharmony_ci{
18048c2ecf20Sopenharmony_ci	struct sock *sk = path->private;
18058c2ecf20Sopenharmony_ci
18068c2ecf20Sopenharmony_ci	bh_lock_sock(sk);
18078c2ecf20Sopenharmony_ci	if (sk->sk_state != IUCV_CLOSED) {
18088c2ecf20Sopenharmony_ci		sk->sk_shutdown |= SEND_SHUTDOWN;
18098c2ecf20Sopenharmony_ci		sk->sk_state_change(sk);
18108c2ecf20Sopenharmony_ci	}
18118c2ecf20Sopenharmony_ci	bh_unlock_sock(sk);
18128c2ecf20Sopenharmony_ci}
18138c2ecf20Sopenharmony_ci
18148c2ecf20Sopenharmony_ci/***************** HiperSockets transport callbacks ********************/
18158c2ecf20Sopenharmony_cistatic void afiucv_swap_src_dest(struct sk_buff *skb)
18168c2ecf20Sopenharmony_ci{
18178c2ecf20Sopenharmony_ci	struct af_iucv_trans_hdr *trans_hdr = iucv_trans_hdr(skb);
18188c2ecf20Sopenharmony_ci	char tmpID[8];
18198c2ecf20Sopenharmony_ci	char tmpName[8];
18208c2ecf20Sopenharmony_ci
18218c2ecf20Sopenharmony_ci	ASCEBC(trans_hdr->destUserID, sizeof(trans_hdr->destUserID));
18228c2ecf20Sopenharmony_ci	ASCEBC(trans_hdr->destAppName, sizeof(trans_hdr->destAppName));
18238c2ecf20Sopenharmony_ci	ASCEBC(trans_hdr->srcUserID, sizeof(trans_hdr->srcUserID));
18248c2ecf20Sopenharmony_ci	ASCEBC(trans_hdr->srcAppName, sizeof(trans_hdr->srcAppName));
18258c2ecf20Sopenharmony_ci	memcpy(tmpID, trans_hdr->srcUserID, 8);
18268c2ecf20Sopenharmony_ci	memcpy(tmpName, trans_hdr->srcAppName, 8);
18278c2ecf20Sopenharmony_ci	memcpy(trans_hdr->srcUserID, trans_hdr->destUserID, 8);
18288c2ecf20Sopenharmony_ci	memcpy(trans_hdr->srcAppName, trans_hdr->destAppName, 8);
18298c2ecf20Sopenharmony_ci	memcpy(trans_hdr->destUserID, tmpID, 8);
18308c2ecf20Sopenharmony_ci	memcpy(trans_hdr->destAppName, tmpName, 8);
18318c2ecf20Sopenharmony_ci	skb_push(skb, ETH_HLEN);
18328c2ecf20Sopenharmony_ci	memset(skb->data, 0, ETH_HLEN);
18338c2ecf20Sopenharmony_ci}
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_ci/**
18368c2ecf20Sopenharmony_ci * afiucv_hs_callback_syn - react on received SYN
18378c2ecf20Sopenharmony_ci **/
18388c2ecf20Sopenharmony_cistatic int afiucv_hs_callback_syn(struct sock *sk, struct sk_buff *skb)
18398c2ecf20Sopenharmony_ci{
18408c2ecf20Sopenharmony_ci	struct af_iucv_trans_hdr *trans_hdr = iucv_trans_hdr(skb);
18418c2ecf20Sopenharmony_ci	struct sock *nsk;
18428c2ecf20Sopenharmony_ci	struct iucv_sock *iucv, *niucv;
18438c2ecf20Sopenharmony_ci	int err;
18448c2ecf20Sopenharmony_ci
18458c2ecf20Sopenharmony_ci	iucv = iucv_sk(sk);
18468c2ecf20Sopenharmony_ci	if (!iucv) {
18478c2ecf20Sopenharmony_ci		/* no sock - connection refused */
18488c2ecf20Sopenharmony_ci		afiucv_swap_src_dest(skb);
18498c2ecf20Sopenharmony_ci		trans_hdr->flags = AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_FIN;
18508c2ecf20Sopenharmony_ci		err = dev_queue_xmit(skb);
18518c2ecf20Sopenharmony_ci		goto out;
18528c2ecf20Sopenharmony_ci	}
18538c2ecf20Sopenharmony_ci
18548c2ecf20Sopenharmony_ci	nsk = iucv_sock_alloc(NULL, sk->sk_protocol, GFP_ATOMIC, 0);
18558c2ecf20Sopenharmony_ci	bh_lock_sock(sk);
18568c2ecf20Sopenharmony_ci	if ((sk->sk_state != IUCV_LISTEN) ||
18578c2ecf20Sopenharmony_ci	    sk_acceptq_is_full(sk) ||
18588c2ecf20Sopenharmony_ci	    !nsk) {
18598c2ecf20Sopenharmony_ci		/* error on server socket - connection refused */
18608c2ecf20Sopenharmony_ci		afiucv_swap_src_dest(skb);
18618c2ecf20Sopenharmony_ci		trans_hdr->flags = AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_FIN;
18628c2ecf20Sopenharmony_ci		err = dev_queue_xmit(skb);
18638c2ecf20Sopenharmony_ci		iucv_sock_kill(nsk);
18648c2ecf20Sopenharmony_ci		bh_unlock_sock(sk);
18658c2ecf20Sopenharmony_ci		goto out;
18668c2ecf20Sopenharmony_ci	}
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_ci	niucv = iucv_sk(nsk);
18698c2ecf20Sopenharmony_ci	iucv_sock_init(nsk, sk);
18708c2ecf20Sopenharmony_ci	niucv->transport = AF_IUCV_TRANS_HIPER;
18718c2ecf20Sopenharmony_ci	niucv->msglimit = iucv->msglimit;
18728c2ecf20Sopenharmony_ci	if (!trans_hdr->window)
18738c2ecf20Sopenharmony_ci		niucv->msglimit_peer = IUCV_HIPER_MSGLIM_DEFAULT;
18748c2ecf20Sopenharmony_ci	else
18758c2ecf20Sopenharmony_ci		niucv->msglimit_peer = trans_hdr->window;
18768c2ecf20Sopenharmony_ci	memcpy(niucv->dst_name, trans_hdr->srcAppName, 8);
18778c2ecf20Sopenharmony_ci	memcpy(niucv->dst_user_id, trans_hdr->srcUserID, 8);
18788c2ecf20Sopenharmony_ci	memcpy(niucv->src_name, iucv->src_name, 8);
18798c2ecf20Sopenharmony_ci	memcpy(niucv->src_user_id, iucv->src_user_id, 8);
18808c2ecf20Sopenharmony_ci	nsk->sk_bound_dev_if = sk->sk_bound_dev_if;
18818c2ecf20Sopenharmony_ci	niucv->hs_dev = iucv->hs_dev;
18828c2ecf20Sopenharmony_ci	dev_hold(niucv->hs_dev);
18838c2ecf20Sopenharmony_ci	afiucv_swap_src_dest(skb);
18848c2ecf20Sopenharmony_ci	trans_hdr->flags = AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_ACK;
18858c2ecf20Sopenharmony_ci	trans_hdr->window = niucv->msglimit;
18868c2ecf20Sopenharmony_ci	/* if receiver acks the xmit connection is established */
18878c2ecf20Sopenharmony_ci	err = dev_queue_xmit(skb);
18888c2ecf20Sopenharmony_ci	if (!err) {
18898c2ecf20Sopenharmony_ci		iucv_accept_enqueue(sk, nsk);
18908c2ecf20Sopenharmony_ci		nsk->sk_state = IUCV_CONNECTED;
18918c2ecf20Sopenharmony_ci		sk->sk_data_ready(sk);
18928c2ecf20Sopenharmony_ci	} else
18938c2ecf20Sopenharmony_ci		iucv_sock_kill(nsk);
18948c2ecf20Sopenharmony_ci	bh_unlock_sock(sk);
18958c2ecf20Sopenharmony_ci
18968c2ecf20Sopenharmony_ciout:
18978c2ecf20Sopenharmony_ci	return NET_RX_SUCCESS;
18988c2ecf20Sopenharmony_ci}
18998c2ecf20Sopenharmony_ci
19008c2ecf20Sopenharmony_ci/**
19018c2ecf20Sopenharmony_ci * afiucv_hs_callback_synack() - react on received SYN-ACK
19028c2ecf20Sopenharmony_ci **/
19038c2ecf20Sopenharmony_cistatic int afiucv_hs_callback_synack(struct sock *sk, struct sk_buff *skb)
19048c2ecf20Sopenharmony_ci{
19058c2ecf20Sopenharmony_ci	struct iucv_sock *iucv = iucv_sk(sk);
19068c2ecf20Sopenharmony_ci
19078c2ecf20Sopenharmony_ci	if (!iucv)
19088c2ecf20Sopenharmony_ci		goto out;
19098c2ecf20Sopenharmony_ci	if (sk->sk_state != IUCV_BOUND)
19108c2ecf20Sopenharmony_ci		goto out;
19118c2ecf20Sopenharmony_ci	bh_lock_sock(sk);
19128c2ecf20Sopenharmony_ci	iucv->msglimit_peer = iucv_trans_hdr(skb)->window;
19138c2ecf20Sopenharmony_ci	sk->sk_state = IUCV_CONNECTED;
19148c2ecf20Sopenharmony_ci	sk->sk_state_change(sk);
19158c2ecf20Sopenharmony_ci	bh_unlock_sock(sk);
19168c2ecf20Sopenharmony_ciout:
19178c2ecf20Sopenharmony_ci	kfree_skb(skb);
19188c2ecf20Sopenharmony_ci	return NET_RX_SUCCESS;
19198c2ecf20Sopenharmony_ci}
19208c2ecf20Sopenharmony_ci
19218c2ecf20Sopenharmony_ci/**
19228c2ecf20Sopenharmony_ci * afiucv_hs_callback_synfin() - react on received SYN_FIN
19238c2ecf20Sopenharmony_ci **/
19248c2ecf20Sopenharmony_cistatic int afiucv_hs_callback_synfin(struct sock *sk, struct sk_buff *skb)
19258c2ecf20Sopenharmony_ci{
19268c2ecf20Sopenharmony_ci	struct iucv_sock *iucv = iucv_sk(sk);
19278c2ecf20Sopenharmony_ci
19288c2ecf20Sopenharmony_ci	if (!iucv)
19298c2ecf20Sopenharmony_ci		goto out;
19308c2ecf20Sopenharmony_ci	if (sk->sk_state != IUCV_BOUND)
19318c2ecf20Sopenharmony_ci		goto out;
19328c2ecf20Sopenharmony_ci	bh_lock_sock(sk);
19338c2ecf20Sopenharmony_ci	sk->sk_state = IUCV_DISCONN;
19348c2ecf20Sopenharmony_ci	sk->sk_state_change(sk);
19358c2ecf20Sopenharmony_ci	bh_unlock_sock(sk);
19368c2ecf20Sopenharmony_ciout:
19378c2ecf20Sopenharmony_ci	kfree_skb(skb);
19388c2ecf20Sopenharmony_ci	return NET_RX_SUCCESS;
19398c2ecf20Sopenharmony_ci}
19408c2ecf20Sopenharmony_ci
19418c2ecf20Sopenharmony_ci/**
19428c2ecf20Sopenharmony_ci * afiucv_hs_callback_fin() - react on received FIN
19438c2ecf20Sopenharmony_ci **/
19448c2ecf20Sopenharmony_cistatic int afiucv_hs_callback_fin(struct sock *sk, struct sk_buff *skb)
19458c2ecf20Sopenharmony_ci{
19468c2ecf20Sopenharmony_ci	struct iucv_sock *iucv = iucv_sk(sk);
19478c2ecf20Sopenharmony_ci
19488c2ecf20Sopenharmony_ci	/* other end of connection closed */
19498c2ecf20Sopenharmony_ci	if (!iucv)
19508c2ecf20Sopenharmony_ci		goto out;
19518c2ecf20Sopenharmony_ci	bh_lock_sock(sk);
19528c2ecf20Sopenharmony_ci	if (sk->sk_state == IUCV_CONNECTED) {
19538c2ecf20Sopenharmony_ci		sk->sk_state = IUCV_DISCONN;
19548c2ecf20Sopenharmony_ci		sk->sk_state_change(sk);
19558c2ecf20Sopenharmony_ci	}
19568c2ecf20Sopenharmony_ci	bh_unlock_sock(sk);
19578c2ecf20Sopenharmony_ciout:
19588c2ecf20Sopenharmony_ci	kfree_skb(skb);
19598c2ecf20Sopenharmony_ci	return NET_RX_SUCCESS;
19608c2ecf20Sopenharmony_ci}
19618c2ecf20Sopenharmony_ci
19628c2ecf20Sopenharmony_ci/**
19638c2ecf20Sopenharmony_ci * afiucv_hs_callback_win() - react on received WIN
19648c2ecf20Sopenharmony_ci **/
19658c2ecf20Sopenharmony_cistatic int afiucv_hs_callback_win(struct sock *sk, struct sk_buff *skb)
19668c2ecf20Sopenharmony_ci{
19678c2ecf20Sopenharmony_ci	struct iucv_sock *iucv = iucv_sk(sk);
19688c2ecf20Sopenharmony_ci
19698c2ecf20Sopenharmony_ci	if (!iucv)
19708c2ecf20Sopenharmony_ci		return NET_RX_SUCCESS;
19718c2ecf20Sopenharmony_ci
19728c2ecf20Sopenharmony_ci	if (sk->sk_state != IUCV_CONNECTED)
19738c2ecf20Sopenharmony_ci		return NET_RX_SUCCESS;
19748c2ecf20Sopenharmony_ci
19758c2ecf20Sopenharmony_ci	atomic_sub(iucv_trans_hdr(skb)->window, &iucv->msg_sent);
19768c2ecf20Sopenharmony_ci	iucv_sock_wake_msglim(sk);
19778c2ecf20Sopenharmony_ci	return NET_RX_SUCCESS;
19788c2ecf20Sopenharmony_ci}
19798c2ecf20Sopenharmony_ci
19808c2ecf20Sopenharmony_ci/**
19818c2ecf20Sopenharmony_ci * afiucv_hs_callback_rx() - react on received data
19828c2ecf20Sopenharmony_ci **/
19838c2ecf20Sopenharmony_cistatic int afiucv_hs_callback_rx(struct sock *sk, struct sk_buff *skb)
19848c2ecf20Sopenharmony_ci{
19858c2ecf20Sopenharmony_ci	struct iucv_sock *iucv = iucv_sk(sk);
19868c2ecf20Sopenharmony_ci
19878c2ecf20Sopenharmony_ci	if (!iucv) {
19888c2ecf20Sopenharmony_ci		kfree_skb(skb);
19898c2ecf20Sopenharmony_ci		return NET_RX_SUCCESS;
19908c2ecf20Sopenharmony_ci	}
19918c2ecf20Sopenharmony_ci
19928c2ecf20Sopenharmony_ci	if (sk->sk_state != IUCV_CONNECTED) {
19938c2ecf20Sopenharmony_ci		kfree_skb(skb);
19948c2ecf20Sopenharmony_ci		return NET_RX_SUCCESS;
19958c2ecf20Sopenharmony_ci	}
19968c2ecf20Sopenharmony_ci
19978c2ecf20Sopenharmony_ci	if (sk->sk_shutdown & RCV_SHUTDOWN) {
19988c2ecf20Sopenharmony_ci		kfree_skb(skb);
19998c2ecf20Sopenharmony_ci		return NET_RX_SUCCESS;
20008c2ecf20Sopenharmony_ci	}
20018c2ecf20Sopenharmony_ci
20028c2ecf20Sopenharmony_ci	/* write stuff from iucv_msg to skb cb */
20038c2ecf20Sopenharmony_ci	skb_pull(skb, sizeof(struct af_iucv_trans_hdr));
20048c2ecf20Sopenharmony_ci	skb_reset_transport_header(skb);
20058c2ecf20Sopenharmony_ci	skb_reset_network_header(skb);
20068c2ecf20Sopenharmony_ci	IUCV_SKB_CB(skb)->offset = 0;
20078c2ecf20Sopenharmony_ci	if (sk_filter(sk, skb)) {
20088c2ecf20Sopenharmony_ci		atomic_inc(&sk->sk_drops);	/* skb rejected by filter */
20098c2ecf20Sopenharmony_ci		kfree_skb(skb);
20108c2ecf20Sopenharmony_ci		return NET_RX_SUCCESS;
20118c2ecf20Sopenharmony_ci	}
20128c2ecf20Sopenharmony_ci
20138c2ecf20Sopenharmony_ci	spin_lock(&iucv->message_q.lock);
20148c2ecf20Sopenharmony_ci	if (skb_queue_empty(&iucv->backlog_skb_q)) {
20158c2ecf20Sopenharmony_ci		if (__sock_queue_rcv_skb(sk, skb))
20168c2ecf20Sopenharmony_ci			/* handle rcv queue full */
20178c2ecf20Sopenharmony_ci			skb_queue_tail(&iucv->backlog_skb_q, skb);
20188c2ecf20Sopenharmony_ci	} else
20198c2ecf20Sopenharmony_ci		skb_queue_tail(&iucv_sk(sk)->backlog_skb_q, skb);
20208c2ecf20Sopenharmony_ci	spin_unlock(&iucv->message_q.lock);
20218c2ecf20Sopenharmony_ci	return NET_RX_SUCCESS;
20228c2ecf20Sopenharmony_ci}
20238c2ecf20Sopenharmony_ci
20248c2ecf20Sopenharmony_ci/**
20258c2ecf20Sopenharmony_ci * afiucv_hs_rcv() - base function for arriving data through HiperSockets
20268c2ecf20Sopenharmony_ci *                   transport
20278c2ecf20Sopenharmony_ci *                   called from netif RX softirq
20288c2ecf20Sopenharmony_ci **/
20298c2ecf20Sopenharmony_cistatic int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev,
20308c2ecf20Sopenharmony_ci	struct packet_type *pt, struct net_device *orig_dev)
20318c2ecf20Sopenharmony_ci{
20328c2ecf20Sopenharmony_ci	struct sock *sk;
20338c2ecf20Sopenharmony_ci	struct iucv_sock *iucv;
20348c2ecf20Sopenharmony_ci	struct af_iucv_trans_hdr *trans_hdr;
20358c2ecf20Sopenharmony_ci	int err = NET_RX_SUCCESS;
20368c2ecf20Sopenharmony_ci	char nullstring[8];
20378c2ecf20Sopenharmony_ci
20388c2ecf20Sopenharmony_ci	if (!pskb_may_pull(skb, sizeof(*trans_hdr))) {
20398c2ecf20Sopenharmony_ci		kfree_skb(skb);
20408c2ecf20Sopenharmony_ci		return NET_RX_SUCCESS;
20418c2ecf20Sopenharmony_ci	}
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_ci	trans_hdr = iucv_trans_hdr(skb);
20448c2ecf20Sopenharmony_ci	EBCASC(trans_hdr->destAppName, sizeof(trans_hdr->destAppName));
20458c2ecf20Sopenharmony_ci	EBCASC(trans_hdr->destUserID, sizeof(trans_hdr->destUserID));
20468c2ecf20Sopenharmony_ci	EBCASC(trans_hdr->srcAppName, sizeof(trans_hdr->srcAppName));
20478c2ecf20Sopenharmony_ci	EBCASC(trans_hdr->srcUserID, sizeof(trans_hdr->srcUserID));
20488c2ecf20Sopenharmony_ci	memset(nullstring, 0, sizeof(nullstring));
20498c2ecf20Sopenharmony_ci	iucv = NULL;
20508c2ecf20Sopenharmony_ci	sk = NULL;
20518c2ecf20Sopenharmony_ci	read_lock(&iucv_sk_list.lock);
20528c2ecf20Sopenharmony_ci	sk_for_each(sk, &iucv_sk_list.head) {
20538c2ecf20Sopenharmony_ci		if (trans_hdr->flags == AF_IUCV_FLAG_SYN) {
20548c2ecf20Sopenharmony_ci			if ((!memcmp(&iucv_sk(sk)->src_name,
20558c2ecf20Sopenharmony_ci				     trans_hdr->destAppName, 8)) &&
20568c2ecf20Sopenharmony_ci			    (!memcmp(&iucv_sk(sk)->src_user_id,
20578c2ecf20Sopenharmony_ci				     trans_hdr->destUserID, 8)) &&
20588c2ecf20Sopenharmony_ci			    (!memcmp(&iucv_sk(sk)->dst_name, nullstring, 8)) &&
20598c2ecf20Sopenharmony_ci			    (!memcmp(&iucv_sk(sk)->dst_user_id,
20608c2ecf20Sopenharmony_ci				     nullstring, 8))) {
20618c2ecf20Sopenharmony_ci				iucv = iucv_sk(sk);
20628c2ecf20Sopenharmony_ci				break;
20638c2ecf20Sopenharmony_ci			}
20648c2ecf20Sopenharmony_ci		} else {
20658c2ecf20Sopenharmony_ci			if ((!memcmp(&iucv_sk(sk)->src_name,
20668c2ecf20Sopenharmony_ci				     trans_hdr->destAppName, 8)) &&
20678c2ecf20Sopenharmony_ci			    (!memcmp(&iucv_sk(sk)->src_user_id,
20688c2ecf20Sopenharmony_ci				     trans_hdr->destUserID, 8)) &&
20698c2ecf20Sopenharmony_ci			    (!memcmp(&iucv_sk(sk)->dst_name,
20708c2ecf20Sopenharmony_ci				     trans_hdr->srcAppName, 8)) &&
20718c2ecf20Sopenharmony_ci			    (!memcmp(&iucv_sk(sk)->dst_user_id,
20728c2ecf20Sopenharmony_ci				     trans_hdr->srcUserID, 8))) {
20738c2ecf20Sopenharmony_ci				iucv = iucv_sk(sk);
20748c2ecf20Sopenharmony_ci				break;
20758c2ecf20Sopenharmony_ci			}
20768c2ecf20Sopenharmony_ci		}
20778c2ecf20Sopenharmony_ci	}
20788c2ecf20Sopenharmony_ci	read_unlock(&iucv_sk_list.lock);
20798c2ecf20Sopenharmony_ci	if (!iucv)
20808c2ecf20Sopenharmony_ci		sk = NULL;
20818c2ecf20Sopenharmony_ci
20828c2ecf20Sopenharmony_ci	/* no sock
20838c2ecf20Sopenharmony_ci	how should we send with no sock
20848c2ecf20Sopenharmony_ci	1) send without sock no send rc checking?
20858c2ecf20Sopenharmony_ci	2) introduce default sock to handle this cases
20868c2ecf20Sopenharmony_ci
20878c2ecf20Sopenharmony_ci	 SYN -> send SYN|ACK in good case, send SYN|FIN in bad case
20888c2ecf20Sopenharmony_ci	 data -> send FIN
20898c2ecf20Sopenharmony_ci	 SYN|ACK, SYN|FIN, FIN -> no action? */
20908c2ecf20Sopenharmony_ci
20918c2ecf20Sopenharmony_ci	switch (trans_hdr->flags) {
20928c2ecf20Sopenharmony_ci	case AF_IUCV_FLAG_SYN:
20938c2ecf20Sopenharmony_ci		/* connect request */
20948c2ecf20Sopenharmony_ci		err = afiucv_hs_callback_syn(sk, skb);
20958c2ecf20Sopenharmony_ci		break;
20968c2ecf20Sopenharmony_ci	case (AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_ACK):
20978c2ecf20Sopenharmony_ci		/* connect request confirmed */
20988c2ecf20Sopenharmony_ci		err = afiucv_hs_callback_synack(sk, skb);
20998c2ecf20Sopenharmony_ci		break;
21008c2ecf20Sopenharmony_ci	case (AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_FIN):
21018c2ecf20Sopenharmony_ci		/* connect request refused */
21028c2ecf20Sopenharmony_ci		err = afiucv_hs_callback_synfin(sk, skb);
21038c2ecf20Sopenharmony_ci		break;
21048c2ecf20Sopenharmony_ci	case (AF_IUCV_FLAG_FIN):
21058c2ecf20Sopenharmony_ci		/* close request */
21068c2ecf20Sopenharmony_ci		err = afiucv_hs_callback_fin(sk, skb);
21078c2ecf20Sopenharmony_ci		break;
21088c2ecf20Sopenharmony_ci	case (AF_IUCV_FLAG_WIN):
21098c2ecf20Sopenharmony_ci		err = afiucv_hs_callback_win(sk, skb);
21108c2ecf20Sopenharmony_ci		if (skb->len == sizeof(struct af_iucv_trans_hdr)) {
21118c2ecf20Sopenharmony_ci			kfree_skb(skb);
21128c2ecf20Sopenharmony_ci			break;
21138c2ecf20Sopenharmony_ci		}
21148c2ecf20Sopenharmony_ci		fallthrough;	/* and receive non-zero length data */
21158c2ecf20Sopenharmony_ci	case (AF_IUCV_FLAG_SHT):
21168c2ecf20Sopenharmony_ci		/* shutdown request */
21178c2ecf20Sopenharmony_ci		fallthrough;	/* and receive zero length data */
21188c2ecf20Sopenharmony_ci	case 0:
21198c2ecf20Sopenharmony_ci		/* plain data frame */
21208c2ecf20Sopenharmony_ci		IUCV_SKB_CB(skb)->class = trans_hdr->iucv_hdr.class;
21218c2ecf20Sopenharmony_ci		err = afiucv_hs_callback_rx(sk, skb);
21228c2ecf20Sopenharmony_ci		break;
21238c2ecf20Sopenharmony_ci	default:
21248c2ecf20Sopenharmony_ci		kfree_skb(skb);
21258c2ecf20Sopenharmony_ci	}
21268c2ecf20Sopenharmony_ci
21278c2ecf20Sopenharmony_ci	return err;
21288c2ecf20Sopenharmony_ci}
21298c2ecf20Sopenharmony_ci
21308c2ecf20Sopenharmony_ci/**
21318c2ecf20Sopenharmony_ci * afiucv_hs_callback_txnotify() - handle send notifcations from HiperSockets
21328c2ecf20Sopenharmony_ci *                                 transport
21338c2ecf20Sopenharmony_ci **/
21348c2ecf20Sopenharmony_cistatic void afiucv_hs_callback_txnotify(struct sk_buff *skb,
21358c2ecf20Sopenharmony_ci					enum iucv_tx_notify n)
21368c2ecf20Sopenharmony_ci{
21378c2ecf20Sopenharmony_ci	struct sock *isk = skb->sk;
21388c2ecf20Sopenharmony_ci	struct sock *sk = NULL;
21398c2ecf20Sopenharmony_ci	struct iucv_sock *iucv = NULL;
21408c2ecf20Sopenharmony_ci	struct sk_buff_head *list;
21418c2ecf20Sopenharmony_ci	struct sk_buff *list_skb;
21428c2ecf20Sopenharmony_ci	struct sk_buff *nskb;
21438c2ecf20Sopenharmony_ci	unsigned long flags;
21448c2ecf20Sopenharmony_ci
21458c2ecf20Sopenharmony_ci	read_lock_irqsave(&iucv_sk_list.lock, flags);
21468c2ecf20Sopenharmony_ci	sk_for_each(sk, &iucv_sk_list.head)
21478c2ecf20Sopenharmony_ci		if (sk == isk) {
21488c2ecf20Sopenharmony_ci			iucv = iucv_sk(sk);
21498c2ecf20Sopenharmony_ci			break;
21508c2ecf20Sopenharmony_ci		}
21518c2ecf20Sopenharmony_ci	read_unlock_irqrestore(&iucv_sk_list.lock, flags);
21528c2ecf20Sopenharmony_ci
21538c2ecf20Sopenharmony_ci	if (!iucv || sock_flag(sk, SOCK_ZAPPED))
21548c2ecf20Sopenharmony_ci		return;
21558c2ecf20Sopenharmony_ci
21568c2ecf20Sopenharmony_ci	list = &iucv->send_skb_q;
21578c2ecf20Sopenharmony_ci	spin_lock_irqsave(&list->lock, flags);
21588c2ecf20Sopenharmony_ci	skb_queue_walk_safe(list, list_skb, nskb) {
21598c2ecf20Sopenharmony_ci		if (skb_shinfo(list_skb) == skb_shinfo(skb)) {
21608c2ecf20Sopenharmony_ci			switch (n) {
21618c2ecf20Sopenharmony_ci			case TX_NOTIFY_OK:
21628c2ecf20Sopenharmony_ci				__skb_unlink(list_skb, list);
21638c2ecf20Sopenharmony_ci				kfree_skb(list_skb);
21648c2ecf20Sopenharmony_ci				iucv_sock_wake_msglim(sk);
21658c2ecf20Sopenharmony_ci				break;
21668c2ecf20Sopenharmony_ci			case TX_NOTIFY_PENDING:
21678c2ecf20Sopenharmony_ci				atomic_inc(&iucv->pendings);
21688c2ecf20Sopenharmony_ci				break;
21698c2ecf20Sopenharmony_ci			case TX_NOTIFY_DELAYED_OK:
21708c2ecf20Sopenharmony_ci				__skb_unlink(list_skb, list);
21718c2ecf20Sopenharmony_ci				atomic_dec(&iucv->pendings);
21728c2ecf20Sopenharmony_ci				if (atomic_read(&iucv->pendings) <= 0)
21738c2ecf20Sopenharmony_ci					iucv_sock_wake_msglim(sk);
21748c2ecf20Sopenharmony_ci				kfree_skb(list_skb);
21758c2ecf20Sopenharmony_ci				break;
21768c2ecf20Sopenharmony_ci			case TX_NOTIFY_UNREACHABLE:
21778c2ecf20Sopenharmony_ci			case TX_NOTIFY_DELAYED_UNREACHABLE:
21788c2ecf20Sopenharmony_ci			case TX_NOTIFY_TPQFULL: /* not yet used */
21798c2ecf20Sopenharmony_ci			case TX_NOTIFY_GENERALERROR:
21808c2ecf20Sopenharmony_ci			case TX_NOTIFY_DELAYED_GENERALERROR:
21818c2ecf20Sopenharmony_ci				__skb_unlink(list_skb, list);
21828c2ecf20Sopenharmony_ci				kfree_skb(list_skb);
21838c2ecf20Sopenharmony_ci				if (sk->sk_state == IUCV_CONNECTED) {
21848c2ecf20Sopenharmony_ci					sk->sk_state = IUCV_DISCONN;
21858c2ecf20Sopenharmony_ci					sk->sk_state_change(sk);
21868c2ecf20Sopenharmony_ci				}
21878c2ecf20Sopenharmony_ci				break;
21888c2ecf20Sopenharmony_ci			}
21898c2ecf20Sopenharmony_ci			break;
21908c2ecf20Sopenharmony_ci		}
21918c2ecf20Sopenharmony_ci	}
21928c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&list->lock, flags);
21938c2ecf20Sopenharmony_ci
21948c2ecf20Sopenharmony_ci	if (sk->sk_state == IUCV_CLOSING) {
21958c2ecf20Sopenharmony_ci		if (skb_queue_empty(&iucv_sk(sk)->send_skb_q)) {
21968c2ecf20Sopenharmony_ci			sk->sk_state = IUCV_CLOSED;
21978c2ecf20Sopenharmony_ci			sk->sk_state_change(sk);
21988c2ecf20Sopenharmony_ci		}
21998c2ecf20Sopenharmony_ci	}
22008c2ecf20Sopenharmony_ci
22018c2ecf20Sopenharmony_ci}
22028c2ecf20Sopenharmony_ci
22038c2ecf20Sopenharmony_ci/*
22048c2ecf20Sopenharmony_ci * afiucv_netdev_event: handle netdev notifier chain events
22058c2ecf20Sopenharmony_ci */
22068c2ecf20Sopenharmony_cistatic int afiucv_netdev_event(struct notifier_block *this,
22078c2ecf20Sopenharmony_ci			       unsigned long event, void *ptr)
22088c2ecf20Sopenharmony_ci{
22098c2ecf20Sopenharmony_ci	struct net_device *event_dev = netdev_notifier_info_to_dev(ptr);
22108c2ecf20Sopenharmony_ci	struct sock *sk;
22118c2ecf20Sopenharmony_ci	struct iucv_sock *iucv;
22128c2ecf20Sopenharmony_ci
22138c2ecf20Sopenharmony_ci	switch (event) {
22148c2ecf20Sopenharmony_ci	case NETDEV_REBOOT:
22158c2ecf20Sopenharmony_ci	case NETDEV_GOING_DOWN:
22168c2ecf20Sopenharmony_ci		sk_for_each(sk, &iucv_sk_list.head) {
22178c2ecf20Sopenharmony_ci			iucv = iucv_sk(sk);
22188c2ecf20Sopenharmony_ci			if ((iucv->hs_dev == event_dev) &&
22198c2ecf20Sopenharmony_ci			    (sk->sk_state == IUCV_CONNECTED)) {
22208c2ecf20Sopenharmony_ci				if (event == NETDEV_GOING_DOWN)
22218c2ecf20Sopenharmony_ci					iucv_send_ctrl(sk, AF_IUCV_FLAG_FIN);
22228c2ecf20Sopenharmony_ci				sk->sk_state = IUCV_DISCONN;
22238c2ecf20Sopenharmony_ci				sk->sk_state_change(sk);
22248c2ecf20Sopenharmony_ci			}
22258c2ecf20Sopenharmony_ci		}
22268c2ecf20Sopenharmony_ci		break;
22278c2ecf20Sopenharmony_ci	case NETDEV_DOWN:
22288c2ecf20Sopenharmony_ci	case NETDEV_UNREGISTER:
22298c2ecf20Sopenharmony_ci	default:
22308c2ecf20Sopenharmony_ci		break;
22318c2ecf20Sopenharmony_ci	}
22328c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
22338c2ecf20Sopenharmony_ci}
22348c2ecf20Sopenharmony_ci
22358c2ecf20Sopenharmony_cistatic struct notifier_block afiucv_netdev_notifier = {
22368c2ecf20Sopenharmony_ci	.notifier_call = afiucv_netdev_event,
22378c2ecf20Sopenharmony_ci};
22388c2ecf20Sopenharmony_ci
22398c2ecf20Sopenharmony_cistatic const struct proto_ops iucv_sock_ops = {
22408c2ecf20Sopenharmony_ci	.family		= PF_IUCV,
22418c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
22428c2ecf20Sopenharmony_ci	.release	= iucv_sock_release,
22438c2ecf20Sopenharmony_ci	.bind		= iucv_sock_bind,
22448c2ecf20Sopenharmony_ci	.connect	= iucv_sock_connect,
22458c2ecf20Sopenharmony_ci	.listen		= iucv_sock_listen,
22468c2ecf20Sopenharmony_ci	.accept		= iucv_sock_accept,
22478c2ecf20Sopenharmony_ci	.getname	= iucv_sock_getname,
22488c2ecf20Sopenharmony_ci	.sendmsg	= iucv_sock_sendmsg,
22498c2ecf20Sopenharmony_ci	.recvmsg	= iucv_sock_recvmsg,
22508c2ecf20Sopenharmony_ci	.poll		= iucv_sock_poll,
22518c2ecf20Sopenharmony_ci	.ioctl		= sock_no_ioctl,
22528c2ecf20Sopenharmony_ci	.mmap		= sock_no_mmap,
22538c2ecf20Sopenharmony_ci	.socketpair	= sock_no_socketpair,
22548c2ecf20Sopenharmony_ci	.shutdown	= iucv_sock_shutdown,
22558c2ecf20Sopenharmony_ci	.setsockopt	= iucv_sock_setsockopt,
22568c2ecf20Sopenharmony_ci	.getsockopt	= iucv_sock_getsockopt,
22578c2ecf20Sopenharmony_ci};
22588c2ecf20Sopenharmony_ci
22598c2ecf20Sopenharmony_cistatic int iucv_sock_create(struct net *net, struct socket *sock, int protocol,
22608c2ecf20Sopenharmony_ci			    int kern)
22618c2ecf20Sopenharmony_ci{
22628c2ecf20Sopenharmony_ci	struct sock *sk;
22638c2ecf20Sopenharmony_ci
22648c2ecf20Sopenharmony_ci	if (protocol && protocol != PF_IUCV)
22658c2ecf20Sopenharmony_ci		return -EPROTONOSUPPORT;
22668c2ecf20Sopenharmony_ci
22678c2ecf20Sopenharmony_ci	sock->state = SS_UNCONNECTED;
22688c2ecf20Sopenharmony_ci
22698c2ecf20Sopenharmony_ci	switch (sock->type) {
22708c2ecf20Sopenharmony_ci	case SOCK_STREAM:
22718c2ecf20Sopenharmony_ci	case SOCK_SEQPACKET:
22728c2ecf20Sopenharmony_ci		/* currently, proto ops can handle both sk types */
22738c2ecf20Sopenharmony_ci		sock->ops = &iucv_sock_ops;
22748c2ecf20Sopenharmony_ci		break;
22758c2ecf20Sopenharmony_ci	default:
22768c2ecf20Sopenharmony_ci		return -ESOCKTNOSUPPORT;
22778c2ecf20Sopenharmony_ci	}
22788c2ecf20Sopenharmony_ci
22798c2ecf20Sopenharmony_ci	sk = iucv_sock_alloc(sock, protocol, GFP_KERNEL, kern);
22808c2ecf20Sopenharmony_ci	if (!sk)
22818c2ecf20Sopenharmony_ci		return -ENOMEM;
22828c2ecf20Sopenharmony_ci
22838c2ecf20Sopenharmony_ci	iucv_sock_init(sk, NULL);
22848c2ecf20Sopenharmony_ci
22858c2ecf20Sopenharmony_ci	return 0;
22868c2ecf20Sopenharmony_ci}
22878c2ecf20Sopenharmony_ci
22888c2ecf20Sopenharmony_cistatic const struct net_proto_family iucv_sock_family_ops = {
22898c2ecf20Sopenharmony_ci	.family	= AF_IUCV,
22908c2ecf20Sopenharmony_ci	.owner	= THIS_MODULE,
22918c2ecf20Sopenharmony_ci	.create	= iucv_sock_create,
22928c2ecf20Sopenharmony_ci};
22938c2ecf20Sopenharmony_ci
22948c2ecf20Sopenharmony_cistatic struct packet_type iucv_packet_type = {
22958c2ecf20Sopenharmony_ci	.type = cpu_to_be16(ETH_P_AF_IUCV),
22968c2ecf20Sopenharmony_ci	.func = afiucv_hs_rcv,
22978c2ecf20Sopenharmony_ci};
22988c2ecf20Sopenharmony_ci
22998c2ecf20Sopenharmony_cistatic int afiucv_iucv_init(void)
23008c2ecf20Sopenharmony_ci{
23018c2ecf20Sopenharmony_ci	return pr_iucv->iucv_register(&af_iucv_handler, 0);
23028c2ecf20Sopenharmony_ci}
23038c2ecf20Sopenharmony_ci
23048c2ecf20Sopenharmony_cistatic void afiucv_iucv_exit(void)
23058c2ecf20Sopenharmony_ci{
23068c2ecf20Sopenharmony_ci	pr_iucv->iucv_unregister(&af_iucv_handler, 0);
23078c2ecf20Sopenharmony_ci}
23088c2ecf20Sopenharmony_ci
23098c2ecf20Sopenharmony_cistatic int __init afiucv_init(void)
23108c2ecf20Sopenharmony_ci{
23118c2ecf20Sopenharmony_ci	int err;
23128c2ecf20Sopenharmony_ci
23138c2ecf20Sopenharmony_ci	if (MACHINE_IS_VM && IS_ENABLED(CONFIG_IUCV)) {
23148c2ecf20Sopenharmony_ci		cpcmd("QUERY USERID", iucv_userid, sizeof(iucv_userid), &err);
23158c2ecf20Sopenharmony_ci		if (unlikely(err)) {
23168c2ecf20Sopenharmony_ci			WARN_ON(err);
23178c2ecf20Sopenharmony_ci			err = -EPROTONOSUPPORT;
23188c2ecf20Sopenharmony_ci			goto out;
23198c2ecf20Sopenharmony_ci		}
23208c2ecf20Sopenharmony_ci
23218c2ecf20Sopenharmony_ci		pr_iucv = &iucv_if;
23228c2ecf20Sopenharmony_ci	} else {
23238c2ecf20Sopenharmony_ci		memset(&iucv_userid, 0, sizeof(iucv_userid));
23248c2ecf20Sopenharmony_ci		pr_iucv = NULL;
23258c2ecf20Sopenharmony_ci	}
23268c2ecf20Sopenharmony_ci
23278c2ecf20Sopenharmony_ci	err = proto_register(&iucv_proto, 0);
23288c2ecf20Sopenharmony_ci	if (err)
23298c2ecf20Sopenharmony_ci		goto out;
23308c2ecf20Sopenharmony_ci	err = sock_register(&iucv_sock_family_ops);
23318c2ecf20Sopenharmony_ci	if (err)
23328c2ecf20Sopenharmony_ci		goto out_proto;
23338c2ecf20Sopenharmony_ci
23348c2ecf20Sopenharmony_ci	if (pr_iucv) {
23358c2ecf20Sopenharmony_ci		err = afiucv_iucv_init();
23368c2ecf20Sopenharmony_ci		if (err)
23378c2ecf20Sopenharmony_ci			goto out_sock;
23388c2ecf20Sopenharmony_ci	}
23398c2ecf20Sopenharmony_ci
23408c2ecf20Sopenharmony_ci	err = register_netdevice_notifier(&afiucv_netdev_notifier);
23418c2ecf20Sopenharmony_ci	if (err)
23428c2ecf20Sopenharmony_ci		goto out_notifier;
23438c2ecf20Sopenharmony_ci
23448c2ecf20Sopenharmony_ci	dev_add_pack(&iucv_packet_type);
23458c2ecf20Sopenharmony_ci	return 0;
23468c2ecf20Sopenharmony_ci
23478c2ecf20Sopenharmony_ciout_notifier:
23488c2ecf20Sopenharmony_ci	if (pr_iucv)
23498c2ecf20Sopenharmony_ci		afiucv_iucv_exit();
23508c2ecf20Sopenharmony_ciout_sock:
23518c2ecf20Sopenharmony_ci	sock_unregister(PF_IUCV);
23528c2ecf20Sopenharmony_ciout_proto:
23538c2ecf20Sopenharmony_ci	proto_unregister(&iucv_proto);
23548c2ecf20Sopenharmony_ciout:
23558c2ecf20Sopenharmony_ci	return err;
23568c2ecf20Sopenharmony_ci}
23578c2ecf20Sopenharmony_ci
23588c2ecf20Sopenharmony_cistatic void __exit afiucv_exit(void)
23598c2ecf20Sopenharmony_ci{
23608c2ecf20Sopenharmony_ci	if (pr_iucv)
23618c2ecf20Sopenharmony_ci		afiucv_iucv_exit();
23628c2ecf20Sopenharmony_ci
23638c2ecf20Sopenharmony_ci	unregister_netdevice_notifier(&afiucv_netdev_notifier);
23648c2ecf20Sopenharmony_ci	dev_remove_pack(&iucv_packet_type);
23658c2ecf20Sopenharmony_ci	sock_unregister(PF_IUCV);
23668c2ecf20Sopenharmony_ci	proto_unregister(&iucv_proto);
23678c2ecf20Sopenharmony_ci}
23688c2ecf20Sopenharmony_ci
23698c2ecf20Sopenharmony_cimodule_init(afiucv_init);
23708c2ecf20Sopenharmony_cimodule_exit(afiucv_exit);
23718c2ecf20Sopenharmony_ci
23728c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jennifer Hunt <jenhunt@us.ibm.com>");
23738c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IUCV Sockets ver " VERSION);
23748c2ecf20Sopenharmony_ciMODULE_VERSION(VERSION);
23758c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
23768c2ecf20Sopenharmony_ciMODULE_ALIAS_NETPROTO(PF_IUCV);
2377