18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * IUCV base infrastructure.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2001, 2009
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author(s):
88c2ecf20Sopenharmony_ci *    Original source:
98c2ecf20Sopenharmony_ci *	Alan Altmark (Alan_Altmark@us.ibm.com)	Sept. 2000
108c2ecf20Sopenharmony_ci *	Xenia Tkatschow (xenia@us.ibm.com)
118c2ecf20Sopenharmony_ci *    2Gb awareness and general cleanup:
128c2ecf20Sopenharmony_ci *	Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com)
138c2ecf20Sopenharmony_ci *    Rewritten for af_iucv:
148c2ecf20Sopenharmony_ci *	Martin Schwidefsky <schwidefsky@de.ibm.com>
158c2ecf20Sopenharmony_ci *    PM functions:
168c2ecf20Sopenharmony_ci *	Ursula Braun (ursula.braun@de.ibm.com)
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * Documentation used:
198c2ecf20Sopenharmony_ci *    The original source
208c2ecf20Sopenharmony_ci *    CP Programming Service, IBM document # SC24-5760
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "iucv"
248c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include <linux/kernel_stat.h>
278c2ecf20Sopenharmony_ci#include <linux/module.h>
288c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
298c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
308c2ecf20Sopenharmony_ci#include <linux/kernel.h>
318c2ecf20Sopenharmony_ci#include <linux/slab.h>
328c2ecf20Sopenharmony_ci#include <linux/init.h>
338c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
348c2ecf20Sopenharmony_ci#include <linux/list.h>
358c2ecf20Sopenharmony_ci#include <linux/errno.h>
368c2ecf20Sopenharmony_ci#include <linux/err.h>
378c2ecf20Sopenharmony_ci#include <linux/device.h>
388c2ecf20Sopenharmony_ci#include <linux/cpu.h>
398c2ecf20Sopenharmony_ci#include <linux/reboot.h>
408c2ecf20Sopenharmony_ci#include <net/iucv/iucv.h>
418c2ecf20Sopenharmony_ci#include <linux/atomic.h>
428c2ecf20Sopenharmony_ci#include <asm/ebcdic.h>
438c2ecf20Sopenharmony_ci#include <asm/io.h>
448c2ecf20Sopenharmony_ci#include <asm/irq.h>
458c2ecf20Sopenharmony_ci#include <asm/smp.h>
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/*
488c2ecf20Sopenharmony_ci * FLAGS:
498c2ecf20Sopenharmony_ci * All flags are defined in the field IPFLAGS1 of each function
508c2ecf20Sopenharmony_ci * and can be found in CP Programming Services.
518c2ecf20Sopenharmony_ci * IPSRCCLS - Indicates you have specified a source class.
528c2ecf20Sopenharmony_ci * IPTRGCLS - Indicates you have specified a target class.
538c2ecf20Sopenharmony_ci * IPFGPID  - Indicates you have specified a pathid.
548c2ecf20Sopenharmony_ci * IPFGMID  - Indicates you have specified a message ID.
558c2ecf20Sopenharmony_ci * IPNORPY  - Indicates a one-way message. No reply expected.
568c2ecf20Sopenharmony_ci * IPALL    - Indicates that all paths are affected.
578c2ecf20Sopenharmony_ci */
588c2ecf20Sopenharmony_ci#define IUCV_IPSRCCLS	0x01
598c2ecf20Sopenharmony_ci#define IUCV_IPTRGCLS	0x01
608c2ecf20Sopenharmony_ci#define IUCV_IPFGPID	0x02
618c2ecf20Sopenharmony_ci#define IUCV_IPFGMID	0x04
628c2ecf20Sopenharmony_ci#define IUCV_IPNORPY	0x10
638c2ecf20Sopenharmony_ci#define IUCV_IPALL	0x80
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic int iucv_bus_match(struct device *dev, struct device_driver *drv)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	return 0;
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistruct bus_type iucv_bus = {
718c2ecf20Sopenharmony_ci	.name = "iucv",
728c2ecf20Sopenharmony_ci	.match = iucv_bus_match,
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_bus);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistruct device *iucv_root;
778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_root);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic int iucv_available;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/* General IUCV interrupt structure */
828c2ecf20Sopenharmony_cistruct iucv_irq_data {
838c2ecf20Sopenharmony_ci	u16 ippathid;
848c2ecf20Sopenharmony_ci	u8  ipflags1;
858c2ecf20Sopenharmony_ci	u8  iptype;
868c2ecf20Sopenharmony_ci	u32 res2[9];
878c2ecf20Sopenharmony_ci};
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistruct iucv_irq_list {
908c2ecf20Sopenharmony_ci	struct list_head list;
918c2ecf20Sopenharmony_ci	struct iucv_irq_data data;
928c2ecf20Sopenharmony_ci};
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic struct iucv_irq_data *iucv_irq_data[NR_CPUS];
958c2ecf20Sopenharmony_cistatic cpumask_t iucv_buffer_cpumask = { CPU_BITS_NONE };
968c2ecf20Sopenharmony_cistatic cpumask_t iucv_irq_cpumask = { CPU_BITS_NONE };
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/*
998c2ecf20Sopenharmony_ci * Queue of interrupt buffers lock for delivery via the tasklet
1008c2ecf20Sopenharmony_ci * (fast but can't call smp_call_function).
1018c2ecf20Sopenharmony_ci */
1028c2ecf20Sopenharmony_cistatic LIST_HEAD(iucv_task_queue);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci/*
1058c2ecf20Sopenharmony_ci * The tasklet for fast delivery of iucv interrupts.
1068c2ecf20Sopenharmony_ci */
1078c2ecf20Sopenharmony_cistatic void iucv_tasklet_fn(unsigned long);
1088c2ecf20Sopenharmony_cistatic DECLARE_TASKLET_OLD(iucv_tasklet, iucv_tasklet_fn);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci/*
1118c2ecf20Sopenharmony_ci * Queue of interrupt buffers for delivery via a work queue
1128c2ecf20Sopenharmony_ci * (slower but can call smp_call_function).
1138c2ecf20Sopenharmony_ci */
1148c2ecf20Sopenharmony_cistatic LIST_HEAD(iucv_work_queue);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/*
1178c2ecf20Sopenharmony_ci * The work element to deliver path pending interrupts.
1188c2ecf20Sopenharmony_ci */
1198c2ecf20Sopenharmony_cistatic void iucv_work_fn(struct work_struct *work);
1208c2ecf20Sopenharmony_cistatic DECLARE_WORK(iucv_work, iucv_work_fn);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci/*
1238c2ecf20Sopenharmony_ci * Spinlock protecting task and work queue.
1248c2ecf20Sopenharmony_ci */
1258c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(iucv_queue_lock);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cienum iucv_command_codes {
1288c2ecf20Sopenharmony_ci	IUCV_QUERY = 0,
1298c2ecf20Sopenharmony_ci	IUCV_RETRIEVE_BUFFER = 2,
1308c2ecf20Sopenharmony_ci	IUCV_SEND = 4,
1318c2ecf20Sopenharmony_ci	IUCV_RECEIVE = 5,
1328c2ecf20Sopenharmony_ci	IUCV_REPLY = 6,
1338c2ecf20Sopenharmony_ci	IUCV_REJECT = 8,
1348c2ecf20Sopenharmony_ci	IUCV_PURGE = 9,
1358c2ecf20Sopenharmony_ci	IUCV_ACCEPT = 10,
1368c2ecf20Sopenharmony_ci	IUCV_CONNECT = 11,
1378c2ecf20Sopenharmony_ci	IUCV_DECLARE_BUFFER = 12,
1388c2ecf20Sopenharmony_ci	IUCV_QUIESCE = 13,
1398c2ecf20Sopenharmony_ci	IUCV_RESUME = 14,
1408c2ecf20Sopenharmony_ci	IUCV_SEVER = 15,
1418c2ecf20Sopenharmony_ci	IUCV_SETMASK = 16,
1428c2ecf20Sopenharmony_ci	IUCV_SETCONTROLMASK = 17,
1438c2ecf20Sopenharmony_ci};
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci/*
1468c2ecf20Sopenharmony_ci * Error messages that are used with the iucv_sever function. They get
1478c2ecf20Sopenharmony_ci * converted to EBCDIC.
1488c2ecf20Sopenharmony_ci */
1498c2ecf20Sopenharmony_cistatic char iucv_error_no_listener[16] = "NO LISTENER";
1508c2ecf20Sopenharmony_cistatic char iucv_error_no_memory[16] = "NO MEMORY";
1518c2ecf20Sopenharmony_cistatic char iucv_error_pathid[16] = "INVALID PATHID";
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci/*
1548c2ecf20Sopenharmony_ci * iucv_handler_list: List of registered handlers.
1558c2ecf20Sopenharmony_ci */
1568c2ecf20Sopenharmony_cistatic LIST_HEAD(iucv_handler_list);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci/*
1598c2ecf20Sopenharmony_ci * iucv_path_table: an array of iucv_path structures.
1608c2ecf20Sopenharmony_ci */
1618c2ecf20Sopenharmony_cistatic struct iucv_path **iucv_path_table;
1628c2ecf20Sopenharmony_cistatic unsigned long iucv_max_pathid;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci/*
1658c2ecf20Sopenharmony_ci * iucv_lock: spinlock protecting iucv_handler_list and iucv_pathid_table
1668c2ecf20Sopenharmony_ci */
1678c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(iucv_table_lock);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci/*
1708c2ecf20Sopenharmony_ci * iucv_active_cpu: contains the number of the cpu executing the tasklet
1718c2ecf20Sopenharmony_ci * or the work handler. Needed for iucv_path_sever called from tasklet.
1728c2ecf20Sopenharmony_ci */
1738c2ecf20Sopenharmony_cistatic int iucv_active_cpu = -1;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci/*
1768c2ecf20Sopenharmony_ci * Mutex and wait queue for iucv_register/iucv_unregister.
1778c2ecf20Sopenharmony_ci */
1788c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(iucv_register_mutex);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci/*
1818c2ecf20Sopenharmony_ci * Counter for number of non-smp capable handlers.
1828c2ecf20Sopenharmony_ci */
1838c2ecf20Sopenharmony_cistatic int iucv_nonsmp_handler;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci/*
1868c2ecf20Sopenharmony_ci * IUCV control data structure. Used by iucv_path_accept, iucv_path_connect,
1878c2ecf20Sopenharmony_ci * iucv_path_quiesce and iucv_path_sever.
1888c2ecf20Sopenharmony_ci */
1898c2ecf20Sopenharmony_cistruct iucv_cmd_control {
1908c2ecf20Sopenharmony_ci	u16 ippathid;
1918c2ecf20Sopenharmony_ci	u8  ipflags1;
1928c2ecf20Sopenharmony_ci	u8  iprcode;
1938c2ecf20Sopenharmony_ci	u16 ipmsglim;
1948c2ecf20Sopenharmony_ci	u16 res1;
1958c2ecf20Sopenharmony_ci	u8  ipvmid[8];
1968c2ecf20Sopenharmony_ci	u8  ipuser[16];
1978c2ecf20Sopenharmony_ci	u8  iptarget[8];
1988c2ecf20Sopenharmony_ci} __attribute__ ((packed,aligned(8)));
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci/*
2018c2ecf20Sopenharmony_ci * Data in parameter list iucv structure. Used by iucv_message_send,
2028c2ecf20Sopenharmony_ci * iucv_message_send2way and iucv_message_reply.
2038c2ecf20Sopenharmony_ci */
2048c2ecf20Sopenharmony_cistruct iucv_cmd_dpl {
2058c2ecf20Sopenharmony_ci	u16 ippathid;
2068c2ecf20Sopenharmony_ci	u8  ipflags1;
2078c2ecf20Sopenharmony_ci	u8  iprcode;
2088c2ecf20Sopenharmony_ci	u32 ipmsgid;
2098c2ecf20Sopenharmony_ci	u32 iptrgcls;
2108c2ecf20Sopenharmony_ci	u8  iprmmsg[8];
2118c2ecf20Sopenharmony_ci	u32 ipsrccls;
2128c2ecf20Sopenharmony_ci	u32 ipmsgtag;
2138c2ecf20Sopenharmony_ci	u32 ipbfadr2;
2148c2ecf20Sopenharmony_ci	u32 ipbfln2f;
2158c2ecf20Sopenharmony_ci	u32 res;
2168c2ecf20Sopenharmony_ci} __attribute__ ((packed,aligned(8)));
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci/*
2198c2ecf20Sopenharmony_ci * Data in buffer iucv structure. Used by iucv_message_receive,
2208c2ecf20Sopenharmony_ci * iucv_message_reject, iucv_message_send, iucv_message_send2way
2218c2ecf20Sopenharmony_ci * and iucv_declare_cpu.
2228c2ecf20Sopenharmony_ci */
2238c2ecf20Sopenharmony_cistruct iucv_cmd_db {
2248c2ecf20Sopenharmony_ci	u16 ippathid;
2258c2ecf20Sopenharmony_ci	u8  ipflags1;
2268c2ecf20Sopenharmony_ci	u8  iprcode;
2278c2ecf20Sopenharmony_ci	u32 ipmsgid;
2288c2ecf20Sopenharmony_ci	u32 iptrgcls;
2298c2ecf20Sopenharmony_ci	u32 ipbfadr1;
2308c2ecf20Sopenharmony_ci	u32 ipbfln1f;
2318c2ecf20Sopenharmony_ci	u32 ipsrccls;
2328c2ecf20Sopenharmony_ci	u32 ipmsgtag;
2338c2ecf20Sopenharmony_ci	u32 ipbfadr2;
2348c2ecf20Sopenharmony_ci	u32 ipbfln2f;
2358c2ecf20Sopenharmony_ci	u32 res;
2368c2ecf20Sopenharmony_ci} __attribute__ ((packed,aligned(8)));
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci/*
2398c2ecf20Sopenharmony_ci * Purge message iucv structure. Used by iucv_message_purge.
2408c2ecf20Sopenharmony_ci */
2418c2ecf20Sopenharmony_cistruct iucv_cmd_purge {
2428c2ecf20Sopenharmony_ci	u16 ippathid;
2438c2ecf20Sopenharmony_ci	u8  ipflags1;
2448c2ecf20Sopenharmony_ci	u8  iprcode;
2458c2ecf20Sopenharmony_ci	u32 ipmsgid;
2468c2ecf20Sopenharmony_ci	u8  ipaudit[3];
2478c2ecf20Sopenharmony_ci	u8  res1[5];
2488c2ecf20Sopenharmony_ci	u32 res2;
2498c2ecf20Sopenharmony_ci	u32 ipsrccls;
2508c2ecf20Sopenharmony_ci	u32 ipmsgtag;
2518c2ecf20Sopenharmony_ci	u32 res3[3];
2528c2ecf20Sopenharmony_ci} __attribute__ ((packed,aligned(8)));
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci/*
2558c2ecf20Sopenharmony_ci * Set mask iucv structure. Used by iucv_enable_cpu.
2568c2ecf20Sopenharmony_ci */
2578c2ecf20Sopenharmony_cistruct iucv_cmd_set_mask {
2588c2ecf20Sopenharmony_ci	u8  ipmask;
2598c2ecf20Sopenharmony_ci	u8  res1[2];
2608c2ecf20Sopenharmony_ci	u8  iprcode;
2618c2ecf20Sopenharmony_ci	u32 res2[9];
2628c2ecf20Sopenharmony_ci} __attribute__ ((packed,aligned(8)));
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ciunion iucv_param {
2658c2ecf20Sopenharmony_ci	struct iucv_cmd_control ctrl;
2668c2ecf20Sopenharmony_ci	struct iucv_cmd_dpl dpl;
2678c2ecf20Sopenharmony_ci	struct iucv_cmd_db db;
2688c2ecf20Sopenharmony_ci	struct iucv_cmd_purge purge;
2698c2ecf20Sopenharmony_ci	struct iucv_cmd_set_mask set_mask;
2708c2ecf20Sopenharmony_ci};
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci/*
2738c2ecf20Sopenharmony_ci * Anchor for per-cpu IUCV command parameter block.
2748c2ecf20Sopenharmony_ci */
2758c2ecf20Sopenharmony_cistatic union iucv_param *iucv_param[NR_CPUS];
2768c2ecf20Sopenharmony_cistatic union iucv_param *iucv_param_irq[NR_CPUS];
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci/**
2798c2ecf20Sopenharmony_ci * iucv_call_b2f0
2808c2ecf20Sopenharmony_ci * @code: identifier of IUCV call to CP.
2818c2ecf20Sopenharmony_ci * @parm: pointer to a struct iucv_parm block
2828c2ecf20Sopenharmony_ci *
2838c2ecf20Sopenharmony_ci * Calls CP to execute IUCV commands.
2848c2ecf20Sopenharmony_ci *
2858c2ecf20Sopenharmony_ci * Returns the result of the CP IUCV call.
2868c2ecf20Sopenharmony_ci */
2878c2ecf20Sopenharmony_cistatic inline int __iucv_call_b2f0(int command, union iucv_param *parm)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	register unsigned long reg0 asm ("0");
2908c2ecf20Sopenharmony_ci	register unsigned long reg1 asm ("1");
2918c2ecf20Sopenharmony_ci	int ccode;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	reg0 = command;
2948c2ecf20Sopenharmony_ci	reg1 = (unsigned long)parm;
2958c2ecf20Sopenharmony_ci	asm volatile(
2968c2ecf20Sopenharmony_ci		"	.long 0xb2f01000\n"
2978c2ecf20Sopenharmony_ci		"	ipm	%0\n"
2988c2ecf20Sopenharmony_ci		"	srl	%0,28\n"
2998c2ecf20Sopenharmony_ci		: "=d" (ccode), "=m" (*parm), "+d" (reg0), "+a" (reg1)
3008c2ecf20Sopenharmony_ci		:  "m" (*parm) : "cc");
3018c2ecf20Sopenharmony_ci	return ccode;
3028c2ecf20Sopenharmony_ci}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_cistatic inline int iucv_call_b2f0(int command, union iucv_param *parm)
3058c2ecf20Sopenharmony_ci{
3068c2ecf20Sopenharmony_ci	int ccode;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	ccode = __iucv_call_b2f0(command, parm);
3098c2ecf20Sopenharmony_ci	return ccode == 1 ? parm->ctrl.iprcode : ccode;
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci/**
3138c2ecf20Sopenharmony_ci * iucv_query_maxconn
3148c2ecf20Sopenharmony_ci *
3158c2ecf20Sopenharmony_ci * Determines the maximum number of connections that may be established.
3168c2ecf20Sopenharmony_ci *
3178c2ecf20Sopenharmony_ci * Returns the maximum number of connections or -EPERM is IUCV is not
3188c2ecf20Sopenharmony_ci * available.
3198c2ecf20Sopenharmony_ci */
3208c2ecf20Sopenharmony_cistatic int __iucv_query_maxconn(void *param, unsigned long *max_pathid)
3218c2ecf20Sopenharmony_ci{
3228c2ecf20Sopenharmony_ci	register unsigned long reg0 asm ("0");
3238c2ecf20Sopenharmony_ci	register unsigned long reg1 asm ("1");
3248c2ecf20Sopenharmony_ci	int ccode;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	reg0 = IUCV_QUERY;
3278c2ecf20Sopenharmony_ci	reg1 = (unsigned long) param;
3288c2ecf20Sopenharmony_ci	asm volatile (
3298c2ecf20Sopenharmony_ci		"	.long	0xb2f01000\n"
3308c2ecf20Sopenharmony_ci		"	ipm	%0\n"
3318c2ecf20Sopenharmony_ci		"	srl	%0,28\n"
3328c2ecf20Sopenharmony_ci		: "=d" (ccode), "+d" (reg0), "+d" (reg1) : : "cc");
3338c2ecf20Sopenharmony_ci	*max_pathid = reg1;
3348c2ecf20Sopenharmony_ci	return ccode;
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic int iucv_query_maxconn(void)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	unsigned long max_pathid;
3408c2ecf20Sopenharmony_ci	void *param;
3418c2ecf20Sopenharmony_ci	int ccode;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	param = kzalloc(sizeof(union iucv_param), GFP_KERNEL | GFP_DMA);
3448c2ecf20Sopenharmony_ci	if (!param)
3458c2ecf20Sopenharmony_ci		return -ENOMEM;
3468c2ecf20Sopenharmony_ci	ccode = __iucv_query_maxconn(param, &max_pathid);
3478c2ecf20Sopenharmony_ci	if (ccode == 0)
3488c2ecf20Sopenharmony_ci		iucv_max_pathid = max_pathid;
3498c2ecf20Sopenharmony_ci	kfree(param);
3508c2ecf20Sopenharmony_ci	return ccode ? -EPERM : 0;
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci/**
3548c2ecf20Sopenharmony_ci * iucv_allow_cpu
3558c2ecf20Sopenharmony_ci * @data: unused
3568c2ecf20Sopenharmony_ci *
3578c2ecf20Sopenharmony_ci * Allow iucv interrupts on this cpu.
3588c2ecf20Sopenharmony_ci */
3598c2ecf20Sopenharmony_cistatic void iucv_allow_cpu(void *data)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	int cpu = smp_processor_id();
3628c2ecf20Sopenharmony_ci	union iucv_param *parm;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	/*
3658c2ecf20Sopenharmony_ci	 * Enable all iucv interrupts.
3668c2ecf20Sopenharmony_ci	 * ipmask contains bits for the different interrupts
3678c2ecf20Sopenharmony_ci	 *	0x80 - Flag to allow nonpriority message pending interrupts
3688c2ecf20Sopenharmony_ci	 *	0x40 - Flag to allow priority message pending interrupts
3698c2ecf20Sopenharmony_ci	 *	0x20 - Flag to allow nonpriority message completion interrupts
3708c2ecf20Sopenharmony_ci	 *	0x10 - Flag to allow priority message completion interrupts
3718c2ecf20Sopenharmony_ci	 *	0x08 - Flag to allow IUCV control interrupts
3728c2ecf20Sopenharmony_ci	 */
3738c2ecf20Sopenharmony_ci	parm = iucv_param_irq[cpu];
3748c2ecf20Sopenharmony_ci	memset(parm, 0, sizeof(union iucv_param));
3758c2ecf20Sopenharmony_ci	parm->set_mask.ipmask = 0xf8;
3768c2ecf20Sopenharmony_ci	iucv_call_b2f0(IUCV_SETMASK, parm);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	/*
3798c2ecf20Sopenharmony_ci	 * Enable all iucv control interrupts.
3808c2ecf20Sopenharmony_ci	 * ipmask contains bits for the different interrupts
3818c2ecf20Sopenharmony_ci	 *	0x80 - Flag to allow pending connections interrupts
3828c2ecf20Sopenharmony_ci	 *	0x40 - Flag to allow connection complete interrupts
3838c2ecf20Sopenharmony_ci	 *	0x20 - Flag to allow connection severed interrupts
3848c2ecf20Sopenharmony_ci	 *	0x10 - Flag to allow connection quiesced interrupts
3858c2ecf20Sopenharmony_ci	 *	0x08 - Flag to allow connection resumed interrupts
3868c2ecf20Sopenharmony_ci	 */
3878c2ecf20Sopenharmony_ci	memset(parm, 0, sizeof(union iucv_param));
3888c2ecf20Sopenharmony_ci	parm->set_mask.ipmask = 0xf8;
3898c2ecf20Sopenharmony_ci	iucv_call_b2f0(IUCV_SETCONTROLMASK, parm);
3908c2ecf20Sopenharmony_ci	/* Set indication that iucv interrupts are allowed for this cpu. */
3918c2ecf20Sopenharmony_ci	cpumask_set_cpu(cpu, &iucv_irq_cpumask);
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci/**
3958c2ecf20Sopenharmony_ci * iucv_block_cpu
3968c2ecf20Sopenharmony_ci * @data: unused
3978c2ecf20Sopenharmony_ci *
3988c2ecf20Sopenharmony_ci * Block iucv interrupts on this cpu.
3998c2ecf20Sopenharmony_ci */
4008c2ecf20Sopenharmony_cistatic void iucv_block_cpu(void *data)
4018c2ecf20Sopenharmony_ci{
4028c2ecf20Sopenharmony_ci	int cpu = smp_processor_id();
4038c2ecf20Sopenharmony_ci	union iucv_param *parm;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	/* Disable all iucv interrupts. */
4068c2ecf20Sopenharmony_ci	parm = iucv_param_irq[cpu];
4078c2ecf20Sopenharmony_ci	memset(parm, 0, sizeof(union iucv_param));
4088c2ecf20Sopenharmony_ci	iucv_call_b2f0(IUCV_SETMASK, parm);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	/* Clear indication that iucv interrupts are allowed for this cpu. */
4118c2ecf20Sopenharmony_ci	cpumask_clear_cpu(cpu, &iucv_irq_cpumask);
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci/**
4158c2ecf20Sopenharmony_ci * iucv_declare_cpu
4168c2ecf20Sopenharmony_ci * @data: unused
4178c2ecf20Sopenharmony_ci *
4188c2ecf20Sopenharmony_ci * Declare a interrupt buffer on this cpu.
4198c2ecf20Sopenharmony_ci */
4208c2ecf20Sopenharmony_cistatic void iucv_declare_cpu(void *data)
4218c2ecf20Sopenharmony_ci{
4228c2ecf20Sopenharmony_ci	int cpu = smp_processor_id();
4238c2ecf20Sopenharmony_ci	union iucv_param *parm;
4248c2ecf20Sopenharmony_ci	int rc;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	if (cpumask_test_cpu(cpu, &iucv_buffer_cpumask))
4278c2ecf20Sopenharmony_ci		return;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	/* Declare interrupt buffer. */
4308c2ecf20Sopenharmony_ci	parm = iucv_param_irq[cpu];
4318c2ecf20Sopenharmony_ci	memset(parm, 0, sizeof(union iucv_param));
4328c2ecf20Sopenharmony_ci	parm->db.ipbfadr1 = virt_to_phys(iucv_irq_data[cpu]);
4338c2ecf20Sopenharmony_ci	rc = iucv_call_b2f0(IUCV_DECLARE_BUFFER, parm);
4348c2ecf20Sopenharmony_ci	if (rc) {
4358c2ecf20Sopenharmony_ci		char *err = "Unknown";
4368c2ecf20Sopenharmony_ci		switch (rc) {
4378c2ecf20Sopenharmony_ci		case 0x03:
4388c2ecf20Sopenharmony_ci			err = "Directory error";
4398c2ecf20Sopenharmony_ci			break;
4408c2ecf20Sopenharmony_ci		case 0x0a:
4418c2ecf20Sopenharmony_ci			err = "Invalid length";
4428c2ecf20Sopenharmony_ci			break;
4438c2ecf20Sopenharmony_ci		case 0x13:
4448c2ecf20Sopenharmony_ci			err = "Buffer already exists";
4458c2ecf20Sopenharmony_ci			break;
4468c2ecf20Sopenharmony_ci		case 0x3e:
4478c2ecf20Sopenharmony_ci			err = "Buffer overlap";
4488c2ecf20Sopenharmony_ci			break;
4498c2ecf20Sopenharmony_ci		case 0x5c:
4508c2ecf20Sopenharmony_ci			err = "Paging or storage error";
4518c2ecf20Sopenharmony_ci			break;
4528c2ecf20Sopenharmony_ci		}
4538c2ecf20Sopenharmony_ci		pr_warn("Defining an interrupt buffer on CPU %i failed with 0x%02x (%s)\n",
4548c2ecf20Sopenharmony_ci			cpu, rc, err);
4558c2ecf20Sopenharmony_ci		return;
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	/* Set indication that an iucv buffer exists for this cpu. */
4598c2ecf20Sopenharmony_ci	cpumask_set_cpu(cpu, &iucv_buffer_cpumask);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	if (iucv_nonsmp_handler == 0 || cpumask_empty(&iucv_irq_cpumask))
4628c2ecf20Sopenharmony_ci		/* Enable iucv interrupts on this cpu. */
4638c2ecf20Sopenharmony_ci		iucv_allow_cpu(NULL);
4648c2ecf20Sopenharmony_ci	else
4658c2ecf20Sopenharmony_ci		/* Disable iucv interrupts on this cpu. */
4668c2ecf20Sopenharmony_ci		iucv_block_cpu(NULL);
4678c2ecf20Sopenharmony_ci}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci/**
4708c2ecf20Sopenharmony_ci * iucv_retrieve_cpu
4718c2ecf20Sopenharmony_ci * @data: unused
4728c2ecf20Sopenharmony_ci *
4738c2ecf20Sopenharmony_ci * Retrieve interrupt buffer on this cpu.
4748c2ecf20Sopenharmony_ci */
4758c2ecf20Sopenharmony_cistatic void iucv_retrieve_cpu(void *data)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	int cpu = smp_processor_id();
4788c2ecf20Sopenharmony_ci	union iucv_param *parm;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	if (!cpumask_test_cpu(cpu, &iucv_buffer_cpumask))
4818c2ecf20Sopenharmony_ci		return;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	/* Block iucv interrupts. */
4848c2ecf20Sopenharmony_ci	iucv_block_cpu(NULL);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	/* Retrieve interrupt buffer. */
4878c2ecf20Sopenharmony_ci	parm = iucv_param_irq[cpu];
4888c2ecf20Sopenharmony_ci	iucv_call_b2f0(IUCV_RETRIEVE_BUFFER, parm);
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	/* Clear indication that an iucv buffer exists for this cpu. */
4918c2ecf20Sopenharmony_ci	cpumask_clear_cpu(cpu, &iucv_buffer_cpumask);
4928c2ecf20Sopenharmony_ci}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci/**
4958c2ecf20Sopenharmony_ci * iucv_setmask_smp
4968c2ecf20Sopenharmony_ci *
4978c2ecf20Sopenharmony_ci * Allow iucv interrupts on all cpus.
4988c2ecf20Sopenharmony_ci */
4998c2ecf20Sopenharmony_cistatic void iucv_setmask_mp(void)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	int cpu;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	get_online_cpus();
5048c2ecf20Sopenharmony_ci	for_each_online_cpu(cpu)
5058c2ecf20Sopenharmony_ci		/* Enable all cpus with a declared buffer. */
5068c2ecf20Sopenharmony_ci		if (cpumask_test_cpu(cpu, &iucv_buffer_cpumask) &&
5078c2ecf20Sopenharmony_ci		    !cpumask_test_cpu(cpu, &iucv_irq_cpumask))
5088c2ecf20Sopenharmony_ci			smp_call_function_single(cpu, iucv_allow_cpu,
5098c2ecf20Sopenharmony_ci						 NULL, 1);
5108c2ecf20Sopenharmony_ci	put_online_cpus();
5118c2ecf20Sopenharmony_ci}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci/**
5148c2ecf20Sopenharmony_ci * iucv_setmask_up
5158c2ecf20Sopenharmony_ci *
5168c2ecf20Sopenharmony_ci * Allow iucv interrupts on a single cpu.
5178c2ecf20Sopenharmony_ci */
5188c2ecf20Sopenharmony_cistatic void iucv_setmask_up(void)
5198c2ecf20Sopenharmony_ci{
5208c2ecf20Sopenharmony_ci	cpumask_t cpumask;
5218c2ecf20Sopenharmony_ci	int cpu;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	/* Disable all cpu but the first in cpu_irq_cpumask. */
5248c2ecf20Sopenharmony_ci	cpumask_copy(&cpumask, &iucv_irq_cpumask);
5258c2ecf20Sopenharmony_ci	cpumask_clear_cpu(cpumask_first(&iucv_irq_cpumask), &cpumask);
5268c2ecf20Sopenharmony_ci	for_each_cpu(cpu, &cpumask)
5278c2ecf20Sopenharmony_ci		smp_call_function_single(cpu, iucv_block_cpu, NULL, 1);
5288c2ecf20Sopenharmony_ci}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci/**
5318c2ecf20Sopenharmony_ci * iucv_enable
5328c2ecf20Sopenharmony_ci *
5338c2ecf20Sopenharmony_ci * This function makes iucv ready for use. It allocates the pathid
5348c2ecf20Sopenharmony_ci * table, declares an iucv interrupt buffer and enables the iucv
5358c2ecf20Sopenharmony_ci * interrupts. Called when the first user has registered an iucv
5368c2ecf20Sopenharmony_ci * handler.
5378c2ecf20Sopenharmony_ci */
5388c2ecf20Sopenharmony_cistatic int iucv_enable(void)
5398c2ecf20Sopenharmony_ci{
5408c2ecf20Sopenharmony_ci	size_t alloc_size;
5418c2ecf20Sopenharmony_ci	int cpu, rc;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	get_online_cpus();
5448c2ecf20Sopenharmony_ci	rc = -ENOMEM;
5458c2ecf20Sopenharmony_ci	alloc_size = iucv_max_pathid * sizeof(struct iucv_path);
5468c2ecf20Sopenharmony_ci	iucv_path_table = kzalloc(alloc_size, GFP_KERNEL);
5478c2ecf20Sopenharmony_ci	if (!iucv_path_table)
5488c2ecf20Sopenharmony_ci		goto out;
5498c2ecf20Sopenharmony_ci	/* Declare per cpu buffers. */
5508c2ecf20Sopenharmony_ci	rc = -EIO;
5518c2ecf20Sopenharmony_ci	for_each_online_cpu(cpu)
5528c2ecf20Sopenharmony_ci		smp_call_function_single(cpu, iucv_declare_cpu, NULL, 1);
5538c2ecf20Sopenharmony_ci	if (cpumask_empty(&iucv_buffer_cpumask))
5548c2ecf20Sopenharmony_ci		/* No cpu could declare an iucv buffer. */
5558c2ecf20Sopenharmony_ci		goto out;
5568c2ecf20Sopenharmony_ci	put_online_cpus();
5578c2ecf20Sopenharmony_ci	return 0;
5588c2ecf20Sopenharmony_ciout:
5598c2ecf20Sopenharmony_ci	kfree(iucv_path_table);
5608c2ecf20Sopenharmony_ci	iucv_path_table = NULL;
5618c2ecf20Sopenharmony_ci	put_online_cpus();
5628c2ecf20Sopenharmony_ci	return rc;
5638c2ecf20Sopenharmony_ci}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci/**
5668c2ecf20Sopenharmony_ci * iucv_disable
5678c2ecf20Sopenharmony_ci *
5688c2ecf20Sopenharmony_ci * This function shuts down iucv. It disables iucv interrupts, retrieves
5698c2ecf20Sopenharmony_ci * the iucv interrupt buffer and frees the pathid table. Called after the
5708c2ecf20Sopenharmony_ci * last user unregister its iucv handler.
5718c2ecf20Sopenharmony_ci */
5728c2ecf20Sopenharmony_cistatic void iucv_disable(void)
5738c2ecf20Sopenharmony_ci{
5748c2ecf20Sopenharmony_ci	get_online_cpus();
5758c2ecf20Sopenharmony_ci	on_each_cpu(iucv_retrieve_cpu, NULL, 1);
5768c2ecf20Sopenharmony_ci	kfree(iucv_path_table);
5778c2ecf20Sopenharmony_ci	iucv_path_table = NULL;
5788c2ecf20Sopenharmony_ci	put_online_cpus();
5798c2ecf20Sopenharmony_ci}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_cistatic int iucv_cpu_dead(unsigned int cpu)
5828c2ecf20Sopenharmony_ci{
5838c2ecf20Sopenharmony_ci	kfree(iucv_param_irq[cpu]);
5848c2ecf20Sopenharmony_ci	iucv_param_irq[cpu] = NULL;
5858c2ecf20Sopenharmony_ci	kfree(iucv_param[cpu]);
5868c2ecf20Sopenharmony_ci	iucv_param[cpu] = NULL;
5878c2ecf20Sopenharmony_ci	kfree(iucv_irq_data[cpu]);
5888c2ecf20Sopenharmony_ci	iucv_irq_data[cpu] = NULL;
5898c2ecf20Sopenharmony_ci	return 0;
5908c2ecf20Sopenharmony_ci}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_cistatic int iucv_cpu_prepare(unsigned int cpu)
5938c2ecf20Sopenharmony_ci{
5948c2ecf20Sopenharmony_ci	/* Note: GFP_DMA used to get memory below 2G */
5958c2ecf20Sopenharmony_ci	iucv_irq_data[cpu] = kmalloc_node(sizeof(struct iucv_irq_data),
5968c2ecf20Sopenharmony_ci			     GFP_KERNEL|GFP_DMA, cpu_to_node(cpu));
5978c2ecf20Sopenharmony_ci	if (!iucv_irq_data[cpu])
5988c2ecf20Sopenharmony_ci		goto out_free;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	/* Allocate parameter blocks. */
6018c2ecf20Sopenharmony_ci	iucv_param[cpu] = kmalloc_node(sizeof(union iucv_param),
6028c2ecf20Sopenharmony_ci			  GFP_KERNEL|GFP_DMA, cpu_to_node(cpu));
6038c2ecf20Sopenharmony_ci	if (!iucv_param[cpu])
6048c2ecf20Sopenharmony_ci		goto out_free;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	iucv_param_irq[cpu] = kmalloc_node(sizeof(union iucv_param),
6078c2ecf20Sopenharmony_ci			  GFP_KERNEL|GFP_DMA, cpu_to_node(cpu));
6088c2ecf20Sopenharmony_ci	if (!iucv_param_irq[cpu])
6098c2ecf20Sopenharmony_ci		goto out_free;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	return 0;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ciout_free:
6148c2ecf20Sopenharmony_ci	iucv_cpu_dead(cpu);
6158c2ecf20Sopenharmony_ci	return -ENOMEM;
6168c2ecf20Sopenharmony_ci}
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_cistatic int iucv_cpu_online(unsigned int cpu)
6198c2ecf20Sopenharmony_ci{
6208c2ecf20Sopenharmony_ci	if (!iucv_path_table)
6218c2ecf20Sopenharmony_ci		return 0;
6228c2ecf20Sopenharmony_ci	iucv_declare_cpu(NULL);
6238c2ecf20Sopenharmony_ci	return 0;
6248c2ecf20Sopenharmony_ci}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_cistatic int iucv_cpu_down_prep(unsigned int cpu)
6278c2ecf20Sopenharmony_ci{
6288c2ecf20Sopenharmony_ci	cpumask_t cpumask;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	if (!iucv_path_table)
6318c2ecf20Sopenharmony_ci		return 0;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	cpumask_copy(&cpumask, &iucv_buffer_cpumask);
6348c2ecf20Sopenharmony_ci	cpumask_clear_cpu(cpu, &cpumask);
6358c2ecf20Sopenharmony_ci	if (cpumask_empty(&cpumask))
6368c2ecf20Sopenharmony_ci		/* Can't offline last IUCV enabled cpu. */
6378c2ecf20Sopenharmony_ci		return -EINVAL;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	iucv_retrieve_cpu(NULL);
6408c2ecf20Sopenharmony_ci	if (!cpumask_empty(&iucv_irq_cpumask))
6418c2ecf20Sopenharmony_ci		return 0;
6428c2ecf20Sopenharmony_ci	smp_call_function_single(cpumask_first(&iucv_buffer_cpumask),
6438c2ecf20Sopenharmony_ci				 iucv_allow_cpu, NULL, 1);
6448c2ecf20Sopenharmony_ci	return 0;
6458c2ecf20Sopenharmony_ci}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci/**
6488c2ecf20Sopenharmony_ci * iucv_sever_pathid
6498c2ecf20Sopenharmony_ci * @pathid: path identification number.
6508c2ecf20Sopenharmony_ci * @userdata: 16-bytes of user data.
6518c2ecf20Sopenharmony_ci *
6528c2ecf20Sopenharmony_ci * Sever an iucv path to free up the pathid. Used internally.
6538c2ecf20Sopenharmony_ci */
6548c2ecf20Sopenharmony_cistatic int iucv_sever_pathid(u16 pathid, u8 *userdata)
6558c2ecf20Sopenharmony_ci{
6568c2ecf20Sopenharmony_ci	union iucv_param *parm;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	parm = iucv_param_irq[smp_processor_id()];
6598c2ecf20Sopenharmony_ci	memset(parm, 0, sizeof(union iucv_param));
6608c2ecf20Sopenharmony_ci	if (userdata)
6618c2ecf20Sopenharmony_ci		memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser));
6628c2ecf20Sopenharmony_ci	parm->ctrl.ippathid = pathid;
6638c2ecf20Sopenharmony_ci	return iucv_call_b2f0(IUCV_SEVER, parm);
6648c2ecf20Sopenharmony_ci}
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci/**
6678c2ecf20Sopenharmony_ci * __iucv_cleanup_queue
6688c2ecf20Sopenharmony_ci * @dummy: unused dummy argument
6698c2ecf20Sopenharmony_ci *
6708c2ecf20Sopenharmony_ci * Nop function called via smp_call_function to force work items from
6718c2ecf20Sopenharmony_ci * pending external iucv interrupts to the work queue.
6728c2ecf20Sopenharmony_ci */
6738c2ecf20Sopenharmony_cistatic void __iucv_cleanup_queue(void *dummy)
6748c2ecf20Sopenharmony_ci{
6758c2ecf20Sopenharmony_ci}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci/**
6788c2ecf20Sopenharmony_ci * iucv_cleanup_queue
6798c2ecf20Sopenharmony_ci *
6808c2ecf20Sopenharmony_ci * Function called after a path has been severed to find all remaining
6818c2ecf20Sopenharmony_ci * work items for the now stale pathid. The caller needs to hold the
6828c2ecf20Sopenharmony_ci * iucv_table_lock.
6838c2ecf20Sopenharmony_ci */
6848c2ecf20Sopenharmony_cistatic void iucv_cleanup_queue(void)
6858c2ecf20Sopenharmony_ci{
6868c2ecf20Sopenharmony_ci	struct iucv_irq_list *p, *n;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	/*
6898c2ecf20Sopenharmony_ci	 * When a path is severed, the pathid can be reused immediately
6908c2ecf20Sopenharmony_ci	 * on a iucv connect or a connection pending interrupt. Remove
6918c2ecf20Sopenharmony_ci	 * all entries from the task queue that refer to a stale pathid
6928c2ecf20Sopenharmony_ci	 * (iucv_path_table[ix] == NULL). Only then do the iucv connect
6938c2ecf20Sopenharmony_ci	 * or deliver the connection pending interrupt. To get all the
6948c2ecf20Sopenharmony_ci	 * pending interrupts force them to the work queue by calling
6958c2ecf20Sopenharmony_ci	 * an empty function on all cpus.
6968c2ecf20Sopenharmony_ci	 */
6978c2ecf20Sopenharmony_ci	smp_call_function(__iucv_cleanup_queue, NULL, 1);
6988c2ecf20Sopenharmony_ci	spin_lock_irq(&iucv_queue_lock);
6998c2ecf20Sopenharmony_ci	list_for_each_entry_safe(p, n, &iucv_task_queue, list) {
7008c2ecf20Sopenharmony_ci		/* Remove stale work items from the task queue. */
7018c2ecf20Sopenharmony_ci		if (iucv_path_table[p->data.ippathid] == NULL) {
7028c2ecf20Sopenharmony_ci			list_del(&p->list);
7038c2ecf20Sopenharmony_ci			kfree(p);
7048c2ecf20Sopenharmony_ci		}
7058c2ecf20Sopenharmony_ci	}
7068c2ecf20Sopenharmony_ci	spin_unlock_irq(&iucv_queue_lock);
7078c2ecf20Sopenharmony_ci}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci/**
7108c2ecf20Sopenharmony_ci * iucv_register:
7118c2ecf20Sopenharmony_ci * @handler: address of iucv handler structure
7128c2ecf20Sopenharmony_ci * @smp: != 0 indicates that the handler can deal with out of order messages
7138c2ecf20Sopenharmony_ci *
7148c2ecf20Sopenharmony_ci * Registers a driver with IUCV.
7158c2ecf20Sopenharmony_ci *
7168c2ecf20Sopenharmony_ci * Returns 0 on success, -ENOMEM if the memory allocation for the pathid
7178c2ecf20Sopenharmony_ci * table failed, or -EIO if IUCV_DECLARE_BUFFER failed on all cpus.
7188c2ecf20Sopenharmony_ci */
7198c2ecf20Sopenharmony_ciint iucv_register(struct iucv_handler *handler, int smp)
7208c2ecf20Sopenharmony_ci{
7218c2ecf20Sopenharmony_ci	int rc;
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	if (!iucv_available)
7248c2ecf20Sopenharmony_ci		return -ENOSYS;
7258c2ecf20Sopenharmony_ci	mutex_lock(&iucv_register_mutex);
7268c2ecf20Sopenharmony_ci	if (!smp)
7278c2ecf20Sopenharmony_ci		iucv_nonsmp_handler++;
7288c2ecf20Sopenharmony_ci	if (list_empty(&iucv_handler_list)) {
7298c2ecf20Sopenharmony_ci		rc = iucv_enable();
7308c2ecf20Sopenharmony_ci		if (rc)
7318c2ecf20Sopenharmony_ci			goto out_mutex;
7328c2ecf20Sopenharmony_ci	} else if (!smp && iucv_nonsmp_handler == 1)
7338c2ecf20Sopenharmony_ci		iucv_setmask_up();
7348c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&handler->paths);
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	spin_lock_bh(&iucv_table_lock);
7378c2ecf20Sopenharmony_ci	list_add_tail(&handler->list, &iucv_handler_list);
7388c2ecf20Sopenharmony_ci	spin_unlock_bh(&iucv_table_lock);
7398c2ecf20Sopenharmony_ci	rc = 0;
7408c2ecf20Sopenharmony_ciout_mutex:
7418c2ecf20Sopenharmony_ci	mutex_unlock(&iucv_register_mutex);
7428c2ecf20Sopenharmony_ci	return rc;
7438c2ecf20Sopenharmony_ci}
7448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_register);
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci/**
7478c2ecf20Sopenharmony_ci * iucv_unregister
7488c2ecf20Sopenharmony_ci * @handler:  address of iucv handler structure
7498c2ecf20Sopenharmony_ci * @smp: != 0 indicates that the handler can deal with out of order messages
7508c2ecf20Sopenharmony_ci *
7518c2ecf20Sopenharmony_ci * Unregister driver from IUCV.
7528c2ecf20Sopenharmony_ci */
7538c2ecf20Sopenharmony_civoid iucv_unregister(struct iucv_handler *handler, int smp)
7548c2ecf20Sopenharmony_ci{
7558c2ecf20Sopenharmony_ci	struct iucv_path *p, *n;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	mutex_lock(&iucv_register_mutex);
7588c2ecf20Sopenharmony_ci	spin_lock_bh(&iucv_table_lock);
7598c2ecf20Sopenharmony_ci	/* Remove handler from the iucv_handler_list. */
7608c2ecf20Sopenharmony_ci	list_del_init(&handler->list);
7618c2ecf20Sopenharmony_ci	/* Sever all pathids still referring to the handler. */
7628c2ecf20Sopenharmony_ci	list_for_each_entry_safe(p, n, &handler->paths, list) {
7638c2ecf20Sopenharmony_ci		iucv_sever_pathid(p->pathid, NULL);
7648c2ecf20Sopenharmony_ci		iucv_path_table[p->pathid] = NULL;
7658c2ecf20Sopenharmony_ci		list_del(&p->list);
7668c2ecf20Sopenharmony_ci		iucv_path_free(p);
7678c2ecf20Sopenharmony_ci	}
7688c2ecf20Sopenharmony_ci	spin_unlock_bh(&iucv_table_lock);
7698c2ecf20Sopenharmony_ci	if (!smp)
7708c2ecf20Sopenharmony_ci		iucv_nonsmp_handler--;
7718c2ecf20Sopenharmony_ci	if (list_empty(&iucv_handler_list))
7728c2ecf20Sopenharmony_ci		iucv_disable();
7738c2ecf20Sopenharmony_ci	else if (!smp && iucv_nonsmp_handler == 0)
7748c2ecf20Sopenharmony_ci		iucv_setmask_mp();
7758c2ecf20Sopenharmony_ci	mutex_unlock(&iucv_register_mutex);
7768c2ecf20Sopenharmony_ci}
7778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_unregister);
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_cistatic int iucv_reboot_event(struct notifier_block *this,
7808c2ecf20Sopenharmony_ci			     unsigned long event, void *ptr)
7818c2ecf20Sopenharmony_ci{
7828c2ecf20Sopenharmony_ci	int i;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	if (cpumask_empty(&iucv_irq_cpumask))
7858c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	get_online_cpus();
7888c2ecf20Sopenharmony_ci	on_each_cpu_mask(&iucv_irq_cpumask, iucv_block_cpu, NULL, 1);
7898c2ecf20Sopenharmony_ci	preempt_disable();
7908c2ecf20Sopenharmony_ci	for (i = 0; i < iucv_max_pathid; i++) {
7918c2ecf20Sopenharmony_ci		if (iucv_path_table[i])
7928c2ecf20Sopenharmony_ci			iucv_sever_pathid(i, NULL);
7938c2ecf20Sopenharmony_ci	}
7948c2ecf20Sopenharmony_ci	preempt_enable();
7958c2ecf20Sopenharmony_ci	put_online_cpus();
7968c2ecf20Sopenharmony_ci	iucv_disable();
7978c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
7988c2ecf20Sopenharmony_ci}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_cistatic struct notifier_block iucv_reboot_notifier = {
8018c2ecf20Sopenharmony_ci	.notifier_call = iucv_reboot_event,
8028c2ecf20Sopenharmony_ci};
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci/**
8058c2ecf20Sopenharmony_ci * iucv_path_accept
8068c2ecf20Sopenharmony_ci * @path: address of iucv path structure
8078c2ecf20Sopenharmony_ci * @handler: address of iucv handler structure
8088c2ecf20Sopenharmony_ci * @userdata: 16 bytes of data reflected to the communication partner
8098c2ecf20Sopenharmony_ci * @private: private data passed to interrupt handlers for this path
8108c2ecf20Sopenharmony_ci *
8118c2ecf20Sopenharmony_ci * This function is issued after the user received a connection pending
8128c2ecf20Sopenharmony_ci * external interrupt and now wishes to complete the IUCV communication path.
8138c2ecf20Sopenharmony_ci *
8148c2ecf20Sopenharmony_ci * Returns the result of the CP IUCV call.
8158c2ecf20Sopenharmony_ci */
8168c2ecf20Sopenharmony_ciint iucv_path_accept(struct iucv_path *path, struct iucv_handler *handler,
8178c2ecf20Sopenharmony_ci		     u8 *userdata, void *private)
8188c2ecf20Sopenharmony_ci{
8198c2ecf20Sopenharmony_ci	union iucv_param *parm;
8208c2ecf20Sopenharmony_ci	int rc;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	local_bh_disable();
8238c2ecf20Sopenharmony_ci	if (cpumask_empty(&iucv_buffer_cpumask)) {
8248c2ecf20Sopenharmony_ci		rc = -EIO;
8258c2ecf20Sopenharmony_ci		goto out;
8268c2ecf20Sopenharmony_ci	}
8278c2ecf20Sopenharmony_ci	/* Prepare parameter block. */
8288c2ecf20Sopenharmony_ci	parm = iucv_param[smp_processor_id()];
8298c2ecf20Sopenharmony_ci	memset(parm, 0, sizeof(union iucv_param));
8308c2ecf20Sopenharmony_ci	parm->ctrl.ippathid = path->pathid;
8318c2ecf20Sopenharmony_ci	parm->ctrl.ipmsglim = path->msglim;
8328c2ecf20Sopenharmony_ci	if (userdata)
8338c2ecf20Sopenharmony_ci		memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser));
8348c2ecf20Sopenharmony_ci	parm->ctrl.ipflags1 = path->flags;
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	rc = iucv_call_b2f0(IUCV_ACCEPT, parm);
8378c2ecf20Sopenharmony_ci	if (!rc) {
8388c2ecf20Sopenharmony_ci		path->private = private;
8398c2ecf20Sopenharmony_ci		path->msglim = parm->ctrl.ipmsglim;
8408c2ecf20Sopenharmony_ci		path->flags = parm->ctrl.ipflags1;
8418c2ecf20Sopenharmony_ci	}
8428c2ecf20Sopenharmony_ciout:
8438c2ecf20Sopenharmony_ci	local_bh_enable();
8448c2ecf20Sopenharmony_ci	return rc;
8458c2ecf20Sopenharmony_ci}
8468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_path_accept);
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci/**
8498c2ecf20Sopenharmony_ci * iucv_path_connect
8508c2ecf20Sopenharmony_ci * @path: address of iucv path structure
8518c2ecf20Sopenharmony_ci * @handler: address of iucv handler structure
8528c2ecf20Sopenharmony_ci * @userid: 8-byte user identification
8538c2ecf20Sopenharmony_ci * @system: 8-byte target system identification
8548c2ecf20Sopenharmony_ci * @userdata: 16 bytes of data reflected to the communication partner
8558c2ecf20Sopenharmony_ci * @private: private data passed to interrupt handlers for this path
8568c2ecf20Sopenharmony_ci *
8578c2ecf20Sopenharmony_ci * This function establishes an IUCV path. Although the connect may complete
8588c2ecf20Sopenharmony_ci * successfully, you are not able to use the path until you receive an IUCV
8598c2ecf20Sopenharmony_ci * Connection Complete external interrupt.
8608c2ecf20Sopenharmony_ci *
8618c2ecf20Sopenharmony_ci * Returns the result of the CP IUCV call.
8628c2ecf20Sopenharmony_ci */
8638c2ecf20Sopenharmony_ciint iucv_path_connect(struct iucv_path *path, struct iucv_handler *handler,
8648c2ecf20Sopenharmony_ci		      u8 *userid, u8 *system, u8 *userdata,
8658c2ecf20Sopenharmony_ci		      void *private)
8668c2ecf20Sopenharmony_ci{
8678c2ecf20Sopenharmony_ci	union iucv_param *parm;
8688c2ecf20Sopenharmony_ci	int rc;
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	spin_lock_bh(&iucv_table_lock);
8718c2ecf20Sopenharmony_ci	iucv_cleanup_queue();
8728c2ecf20Sopenharmony_ci	if (cpumask_empty(&iucv_buffer_cpumask)) {
8738c2ecf20Sopenharmony_ci		rc = -EIO;
8748c2ecf20Sopenharmony_ci		goto out;
8758c2ecf20Sopenharmony_ci	}
8768c2ecf20Sopenharmony_ci	parm = iucv_param[smp_processor_id()];
8778c2ecf20Sopenharmony_ci	memset(parm, 0, sizeof(union iucv_param));
8788c2ecf20Sopenharmony_ci	parm->ctrl.ipmsglim = path->msglim;
8798c2ecf20Sopenharmony_ci	parm->ctrl.ipflags1 = path->flags;
8808c2ecf20Sopenharmony_ci	if (userid) {
8818c2ecf20Sopenharmony_ci		memcpy(parm->ctrl.ipvmid, userid, sizeof(parm->ctrl.ipvmid));
8828c2ecf20Sopenharmony_ci		ASCEBC(parm->ctrl.ipvmid, sizeof(parm->ctrl.ipvmid));
8838c2ecf20Sopenharmony_ci		EBC_TOUPPER(parm->ctrl.ipvmid, sizeof(parm->ctrl.ipvmid));
8848c2ecf20Sopenharmony_ci	}
8858c2ecf20Sopenharmony_ci	if (system) {
8868c2ecf20Sopenharmony_ci		memcpy(parm->ctrl.iptarget, system,
8878c2ecf20Sopenharmony_ci		       sizeof(parm->ctrl.iptarget));
8888c2ecf20Sopenharmony_ci		ASCEBC(parm->ctrl.iptarget, sizeof(parm->ctrl.iptarget));
8898c2ecf20Sopenharmony_ci		EBC_TOUPPER(parm->ctrl.iptarget, sizeof(parm->ctrl.iptarget));
8908c2ecf20Sopenharmony_ci	}
8918c2ecf20Sopenharmony_ci	if (userdata)
8928c2ecf20Sopenharmony_ci		memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser));
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	rc = iucv_call_b2f0(IUCV_CONNECT, parm);
8958c2ecf20Sopenharmony_ci	if (!rc) {
8968c2ecf20Sopenharmony_ci		if (parm->ctrl.ippathid < iucv_max_pathid) {
8978c2ecf20Sopenharmony_ci			path->pathid = parm->ctrl.ippathid;
8988c2ecf20Sopenharmony_ci			path->msglim = parm->ctrl.ipmsglim;
8998c2ecf20Sopenharmony_ci			path->flags = parm->ctrl.ipflags1;
9008c2ecf20Sopenharmony_ci			path->handler = handler;
9018c2ecf20Sopenharmony_ci			path->private = private;
9028c2ecf20Sopenharmony_ci			list_add_tail(&path->list, &handler->paths);
9038c2ecf20Sopenharmony_ci			iucv_path_table[path->pathid] = path;
9048c2ecf20Sopenharmony_ci		} else {
9058c2ecf20Sopenharmony_ci			iucv_sever_pathid(parm->ctrl.ippathid,
9068c2ecf20Sopenharmony_ci					  iucv_error_pathid);
9078c2ecf20Sopenharmony_ci			rc = -EIO;
9088c2ecf20Sopenharmony_ci		}
9098c2ecf20Sopenharmony_ci	}
9108c2ecf20Sopenharmony_ciout:
9118c2ecf20Sopenharmony_ci	spin_unlock_bh(&iucv_table_lock);
9128c2ecf20Sopenharmony_ci	return rc;
9138c2ecf20Sopenharmony_ci}
9148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_path_connect);
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci/**
9178c2ecf20Sopenharmony_ci * iucv_path_quiesce:
9188c2ecf20Sopenharmony_ci * @path: address of iucv path structure
9198c2ecf20Sopenharmony_ci * @userdata: 16 bytes of data reflected to the communication partner
9208c2ecf20Sopenharmony_ci *
9218c2ecf20Sopenharmony_ci * This function temporarily suspends incoming messages on an IUCV path.
9228c2ecf20Sopenharmony_ci * You can later reactivate the path by invoking the iucv_resume function.
9238c2ecf20Sopenharmony_ci *
9248c2ecf20Sopenharmony_ci * Returns the result from the CP IUCV call.
9258c2ecf20Sopenharmony_ci */
9268c2ecf20Sopenharmony_ciint iucv_path_quiesce(struct iucv_path *path, u8 *userdata)
9278c2ecf20Sopenharmony_ci{
9288c2ecf20Sopenharmony_ci	union iucv_param *parm;
9298c2ecf20Sopenharmony_ci	int rc;
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	local_bh_disable();
9328c2ecf20Sopenharmony_ci	if (cpumask_empty(&iucv_buffer_cpumask)) {
9338c2ecf20Sopenharmony_ci		rc = -EIO;
9348c2ecf20Sopenharmony_ci		goto out;
9358c2ecf20Sopenharmony_ci	}
9368c2ecf20Sopenharmony_ci	parm = iucv_param[smp_processor_id()];
9378c2ecf20Sopenharmony_ci	memset(parm, 0, sizeof(union iucv_param));
9388c2ecf20Sopenharmony_ci	if (userdata)
9398c2ecf20Sopenharmony_ci		memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser));
9408c2ecf20Sopenharmony_ci	parm->ctrl.ippathid = path->pathid;
9418c2ecf20Sopenharmony_ci	rc = iucv_call_b2f0(IUCV_QUIESCE, parm);
9428c2ecf20Sopenharmony_ciout:
9438c2ecf20Sopenharmony_ci	local_bh_enable();
9448c2ecf20Sopenharmony_ci	return rc;
9458c2ecf20Sopenharmony_ci}
9468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_path_quiesce);
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci/**
9498c2ecf20Sopenharmony_ci * iucv_path_resume:
9508c2ecf20Sopenharmony_ci * @path: address of iucv path structure
9518c2ecf20Sopenharmony_ci * @userdata: 16 bytes of data reflected to the communication partner
9528c2ecf20Sopenharmony_ci *
9538c2ecf20Sopenharmony_ci * This function resumes incoming messages on an IUCV path that has
9548c2ecf20Sopenharmony_ci * been stopped with iucv_path_quiesce.
9558c2ecf20Sopenharmony_ci *
9568c2ecf20Sopenharmony_ci * Returns the result from the CP IUCV call.
9578c2ecf20Sopenharmony_ci */
9588c2ecf20Sopenharmony_ciint iucv_path_resume(struct iucv_path *path, u8 *userdata)
9598c2ecf20Sopenharmony_ci{
9608c2ecf20Sopenharmony_ci	union iucv_param *parm;
9618c2ecf20Sopenharmony_ci	int rc;
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	local_bh_disable();
9648c2ecf20Sopenharmony_ci	if (cpumask_empty(&iucv_buffer_cpumask)) {
9658c2ecf20Sopenharmony_ci		rc = -EIO;
9668c2ecf20Sopenharmony_ci		goto out;
9678c2ecf20Sopenharmony_ci	}
9688c2ecf20Sopenharmony_ci	parm = iucv_param[smp_processor_id()];
9698c2ecf20Sopenharmony_ci	memset(parm, 0, sizeof(union iucv_param));
9708c2ecf20Sopenharmony_ci	if (userdata)
9718c2ecf20Sopenharmony_ci		memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser));
9728c2ecf20Sopenharmony_ci	parm->ctrl.ippathid = path->pathid;
9738c2ecf20Sopenharmony_ci	rc = iucv_call_b2f0(IUCV_RESUME, parm);
9748c2ecf20Sopenharmony_ciout:
9758c2ecf20Sopenharmony_ci	local_bh_enable();
9768c2ecf20Sopenharmony_ci	return rc;
9778c2ecf20Sopenharmony_ci}
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci/**
9808c2ecf20Sopenharmony_ci * iucv_path_sever
9818c2ecf20Sopenharmony_ci * @path: address of iucv path structure
9828c2ecf20Sopenharmony_ci * @userdata: 16 bytes of data reflected to the communication partner
9838c2ecf20Sopenharmony_ci *
9848c2ecf20Sopenharmony_ci * This function terminates an IUCV path.
9858c2ecf20Sopenharmony_ci *
9868c2ecf20Sopenharmony_ci * Returns the result from the CP IUCV call.
9878c2ecf20Sopenharmony_ci */
9888c2ecf20Sopenharmony_ciint iucv_path_sever(struct iucv_path *path, u8 *userdata)
9898c2ecf20Sopenharmony_ci{
9908c2ecf20Sopenharmony_ci	int rc;
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci	preempt_disable();
9938c2ecf20Sopenharmony_ci	if (cpumask_empty(&iucv_buffer_cpumask)) {
9948c2ecf20Sopenharmony_ci		rc = -EIO;
9958c2ecf20Sopenharmony_ci		goto out;
9968c2ecf20Sopenharmony_ci	}
9978c2ecf20Sopenharmony_ci	if (iucv_active_cpu != smp_processor_id())
9988c2ecf20Sopenharmony_ci		spin_lock_bh(&iucv_table_lock);
9998c2ecf20Sopenharmony_ci	rc = iucv_sever_pathid(path->pathid, userdata);
10008c2ecf20Sopenharmony_ci	iucv_path_table[path->pathid] = NULL;
10018c2ecf20Sopenharmony_ci	list_del_init(&path->list);
10028c2ecf20Sopenharmony_ci	if (iucv_active_cpu != smp_processor_id())
10038c2ecf20Sopenharmony_ci		spin_unlock_bh(&iucv_table_lock);
10048c2ecf20Sopenharmony_ciout:
10058c2ecf20Sopenharmony_ci	preempt_enable();
10068c2ecf20Sopenharmony_ci	return rc;
10078c2ecf20Sopenharmony_ci}
10088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_path_sever);
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci/**
10118c2ecf20Sopenharmony_ci * iucv_message_purge
10128c2ecf20Sopenharmony_ci * @path: address of iucv path structure
10138c2ecf20Sopenharmony_ci * @msg: address of iucv msg structure
10148c2ecf20Sopenharmony_ci * @srccls: source class of message
10158c2ecf20Sopenharmony_ci *
10168c2ecf20Sopenharmony_ci * Cancels a message you have sent.
10178c2ecf20Sopenharmony_ci *
10188c2ecf20Sopenharmony_ci * Returns the result from the CP IUCV call.
10198c2ecf20Sopenharmony_ci */
10208c2ecf20Sopenharmony_ciint iucv_message_purge(struct iucv_path *path, struct iucv_message *msg,
10218c2ecf20Sopenharmony_ci		       u32 srccls)
10228c2ecf20Sopenharmony_ci{
10238c2ecf20Sopenharmony_ci	union iucv_param *parm;
10248c2ecf20Sopenharmony_ci	int rc;
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci	local_bh_disable();
10278c2ecf20Sopenharmony_ci	if (cpumask_empty(&iucv_buffer_cpumask)) {
10288c2ecf20Sopenharmony_ci		rc = -EIO;
10298c2ecf20Sopenharmony_ci		goto out;
10308c2ecf20Sopenharmony_ci	}
10318c2ecf20Sopenharmony_ci	parm = iucv_param[smp_processor_id()];
10328c2ecf20Sopenharmony_ci	memset(parm, 0, sizeof(union iucv_param));
10338c2ecf20Sopenharmony_ci	parm->purge.ippathid = path->pathid;
10348c2ecf20Sopenharmony_ci	parm->purge.ipmsgid = msg->id;
10358c2ecf20Sopenharmony_ci	parm->purge.ipsrccls = srccls;
10368c2ecf20Sopenharmony_ci	parm->purge.ipflags1 = IUCV_IPSRCCLS | IUCV_IPFGMID | IUCV_IPFGPID;
10378c2ecf20Sopenharmony_ci	rc = iucv_call_b2f0(IUCV_PURGE, parm);
10388c2ecf20Sopenharmony_ci	if (!rc) {
10398c2ecf20Sopenharmony_ci		msg->audit = (*(u32 *) &parm->purge.ipaudit) >> 8;
10408c2ecf20Sopenharmony_ci		msg->tag = parm->purge.ipmsgtag;
10418c2ecf20Sopenharmony_ci	}
10428c2ecf20Sopenharmony_ciout:
10438c2ecf20Sopenharmony_ci	local_bh_enable();
10448c2ecf20Sopenharmony_ci	return rc;
10458c2ecf20Sopenharmony_ci}
10468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_message_purge);
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci/**
10498c2ecf20Sopenharmony_ci * iucv_message_receive_iprmdata
10508c2ecf20Sopenharmony_ci * @path: address of iucv path structure
10518c2ecf20Sopenharmony_ci * @msg: address of iucv msg structure
10528c2ecf20Sopenharmony_ci * @flags: how the message is received (IUCV_IPBUFLST)
10538c2ecf20Sopenharmony_ci * @buffer: address of data buffer or address of struct iucv_array
10548c2ecf20Sopenharmony_ci * @size: length of data buffer
10558c2ecf20Sopenharmony_ci * @residual:
10568c2ecf20Sopenharmony_ci *
10578c2ecf20Sopenharmony_ci * Internal function used by iucv_message_receive and __iucv_message_receive
10588c2ecf20Sopenharmony_ci * to receive RMDATA data stored in struct iucv_message.
10598c2ecf20Sopenharmony_ci */
10608c2ecf20Sopenharmony_cistatic int iucv_message_receive_iprmdata(struct iucv_path *path,
10618c2ecf20Sopenharmony_ci					 struct iucv_message *msg,
10628c2ecf20Sopenharmony_ci					 u8 flags, void *buffer,
10638c2ecf20Sopenharmony_ci					 size_t size, size_t *residual)
10648c2ecf20Sopenharmony_ci{
10658c2ecf20Sopenharmony_ci	struct iucv_array *array;
10668c2ecf20Sopenharmony_ci	u8 *rmmsg;
10678c2ecf20Sopenharmony_ci	size_t copy;
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	/*
10708c2ecf20Sopenharmony_ci	 * Message is 8 bytes long and has been stored to the
10718c2ecf20Sopenharmony_ci	 * message descriptor itself.
10728c2ecf20Sopenharmony_ci	 */
10738c2ecf20Sopenharmony_ci	if (residual)
10748c2ecf20Sopenharmony_ci		*residual = abs(size - 8);
10758c2ecf20Sopenharmony_ci	rmmsg = msg->rmmsg;
10768c2ecf20Sopenharmony_ci	if (flags & IUCV_IPBUFLST) {
10778c2ecf20Sopenharmony_ci		/* Copy to struct iucv_array. */
10788c2ecf20Sopenharmony_ci		size = (size < 8) ? size : 8;
10798c2ecf20Sopenharmony_ci		for (array = buffer; size > 0; array++) {
10808c2ecf20Sopenharmony_ci			copy = min_t(size_t, size, array->length);
10818c2ecf20Sopenharmony_ci			memcpy((u8 *)(addr_t) array->address,
10828c2ecf20Sopenharmony_ci				rmmsg, copy);
10838c2ecf20Sopenharmony_ci			rmmsg += copy;
10848c2ecf20Sopenharmony_ci			size -= copy;
10858c2ecf20Sopenharmony_ci		}
10868c2ecf20Sopenharmony_ci	} else {
10878c2ecf20Sopenharmony_ci		/* Copy to direct buffer. */
10888c2ecf20Sopenharmony_ci		memcpy(buffer, rmmsg, min_t(size_t, size, 8));
10898c2ecf20Sopenharmony_ci	}
10908c2ecf20Sopenharmony_ci	return 0;
10918c2ecf20Sopenharmony_ci}
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci/**
10948c2ecf20Sopenharmony_ci * __iucv_message_receive
10958c2ecf20Sopenharmony_ci * @path: address of iucv path structure
10968c2ecf20Sopenharmony_ci * @msg: address of iucv msg structure
10978c2ecf20Sopenharmony_ci * @flags: how the message is received (IUCV_IPBUFLST)
10988c2ecf20Sopenharmony_ci * @buffer: address of data buffer or address of struct iucv_array
10998c2ecf20Sopenharmony_ci * @size: length of data buffer
11008c2ecf20Sopenharmony_ci * @residual:
11018c2ecf20Sopenharmony_ci *
11028c2ecf20Sopenharmony_ci * This function receives messages that are being sent to you over
11038c2ecf20Sopenharmony_ci * established paths. This function will deal with RMDATA messages
11048c2ecf20Sopenharmony_ci * embedded in struct iucv_message as well.
11058c2ecf20Sopenharmony_ci *
11068c2ecf20Sopenharmony_ci * Locking:	no locking
11078c2ecf20Sopenharmony_ci *
11088c2ecf20Sopenharmony_ci * Returns the result from the CP IUCV call.
11098c2ecf20Sopenharmony_ci */
11108c2ecf20Sopenharmony_ciint __iucv_message_receive(struct iucv_path *path, struct iucv_message *msg,
11118c2ecf20Sopenharmony_ci			   u8 flags, void *buffer, size_t size, size_t *residual)
11128c2ecf20Sopenharmony_ci{
11138c2ecf20Sopenharmony_ci	union iucv_param *parm;
11148c2ecf20Sopenharmony_ci	int rc;
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	if (msg->flags & IUCV_IPRMDATA)
11178c2ecf20Sopenharmony_ci		return iucv_message_receive_iprmdata(path, msg, flags,
11188c2ecf20Sopenharmony_ci						     buffer, size, residual);
11198c2ecf20Sopenharmony_ci	if (cpumask_empty(&iucv_buffer_cpumask))
11208c2ecf20Sopenharmony_ci		return -EIO;
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	parm = iucv_param[smp_processor_id()];
11238c2ecf20Sopenharmony_ci	memset(parm, 0, sizeof(union iucv_param));
11248c2ecf20Sopenharmony_ci	parm->db.ipbfadr1 = (u32)(addr_t) buffer;
11258c2ecf20Sopenharmony_ci	parm->db.ipbfln1f = (u32) size;
11268c2ecf20Sopenharmony_ci	parm->db.ipmsgid = msg->id;
11278c2ecf20Sopenharmony_ci	parm->db.ippathid = path->pathid;
11288c2ecf20Sopenharmony_ci	parm->db.iptrgcls = msg->class;
11298c2ecf20Sopenharmony_ci	parm->db.ipflags1 = (flags | IUCV_IPFGPID |
11308c2ecf20Sopenharmony_ci			     IUCV_IPFGMID | IUCV_IPTRGCLS);
11318c2ecf20Sopenharmony_ci	rc = iucv_call_b2f0(IUCV_RECEIVE, parm);
11328c2ecf20Sopenharmony_ci	if (!rc || rc == 5) {
11338c2ecf20Sopenharmony_ci		msg->flags = parm->db.ipflags1;
11348c2ecf20Sopenharmony_ci		if (residual)
11358c2ecf20Sopenharmony_ci			*residual = parm->db.ipbfln1f;
11368c2ecf20Sopenharmony_ci	}
11378c2ecf20Sopenharmony_ci	return rc;
11388c2ecf20Sopenharmony_ci}
11398c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__iucv_message_receive);
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ci/**
11428c2ecf20Sopenharmony_ci * iucv_message_receive
11438c2ecf20Sopenharmony_ci * @path: address of iucv path structure
11448c2ecf20Sopenharmony_ci * @msg: address of iucv msg structure
11458c2ecf20Sopenharmony_ci * @flags: how the message is received (IUCV_IPBUFLST)
11468c2ecf20Sopenharmony_ci * @buffer: address of data buffer or address of struct iucv_array
11478c2ecf20Sopenharmony_ci * @size: length of data buffer
11488c2ecf20Sopenharmony_ci * @residual:
11498c2ecf20Sopenharmony_ci *
11508c2ecf20Sopenharmony_ci * This function receives messages that are being sent to you over
11518c2ecf20Sopenharmony_ci * established paths. This function will deal with RMDATA messages
11528c2ecf20Sopenharmony_ci * embedded in struct iucv_message as well.
11538c2ecf20Sopenharmony_ci *
11548c2ecf20Sopenharmony_ci * Locking:	local_bh_enable/local_bh_disable
11558c2ecf20Sopenharmony_ci *
11568c2ecf20Sopenharmony_ci * Returns the result from the CP IUCV call.
11578c2ecf20Sopenharmony_ci */
11588c2ecf20Sopenharmony_ciint iucv_message_receive(struct iucv_path *path, struct iucv_message *msg,
11598c2ecf20Sopenharmony_ci			 u8 flags, void *buffer, size_t size, size_t *residual)
11608c2ecf20Sopenharmony_ci{
11618c2ecf20Sopenharmony_ci	int rc;
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	if (msg->flags & IUCV_IPRMDATA)
11648c2ecf20Sopenharmony_ci		return iucv_message_receive_iprmdata(path, msg, flags,
11658c2ecf20Sopenharmony_ci						     buffer, size, residual);
11668c2ecf20Sopenharmony_ci	local_bh_disable();
11678c2ecf20Sopenharmony_ci	rc = __iucv_message_receive(path, msg, flags, buffer, size, residual);
11688c2ecf20Sopenharmony_ci	local_bh_enable();
11698c2ecf20Sopenharmony_ci	return rc;
11708c2ecf20Sopenharmony_ci}
11718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_message_receive);
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci/**
11748c2ecf20Sopenharmony_ci * iucv_message_reject
11758c2ecf20Sopenharmony_ci * @path: address of iucv path structure
11768c2ecf20Sopenharmony_ci * @msg: address of iucv msg structure
11778c2ecf20Sopenharmony_ci *
11788c2ecf20Sopenharmony_ci * The reject function refuses a specified message. Between the time you
11798c2ecf20Sopenharmony_ci * are notified of a message and the time that you complete the message,
11808c2ecf20Sopenharmony_ci * the message may be rejected.
11818c2ecf20Sopenharmony_ci *
11828c2ecf20Sopenharmony_ci * Returns the result from the CP IUCV call.
11838c2ecf20Sopenharmony_ci */
11848c2ecf20Sopenharmony_ciint iucv_message_reject(struct iucv_path *path, struct iucv_message *msg)
11858c2ecf20Sopenharmony_ci{
11868c2ecf20Sopenharmony_ci	union iucv_param *parm;
11878c2ecf20Sopenharmony_ci	int rc;
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci	local_bh_disable();
11908c2ecf20Sopenharmony_ci	if (cpumask_empty(&iucv_buffer_cpumask)) {
11918c2ecf20Sopenharmony_ci		rc = -EIO;
11928c2ecf20Sopenharmony_ci		goto out;
11938c2ecf20Sopenharmony_ci	}
11948c2ecf20Sopenharmony_ci	parm = iucv_param[smp_processor_id()];
11958c2ecf20Sopenharmony_ci	memset(parm, 0, sizeof(union iucv_param));
11968c2ecf20Sopenharmony_ci	parm->db.ippathid = path->pathid;
11978c2ecf20Sopenharmony_ci	parm->db.ipmsgid = msg->id;
11988c2ecf20Sopenharmony_ci	parm->db.iptrgcls = msg->class;
11998c2ecf20Sopenharmony_ci	parm->db.ipflags1 = (IUCV_IPTRGCLS | IUCV_IPFGMID | IUCV_IPFGPID);
12008c2ecf20Sopenharmony_ci	rc = iucv_call_b2f0(IUCV_REJECT, parm);
12018c2ecf20Sopenharmony_ciout:
12028c2ecf20Sopenharmony_ci	local_bh_enable();
12038c2ecf20Sopenharmony_ci	return rc;
12048c2ecf20Sopenharmony_ci}
12058c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_message_reject);
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci/**
12088c2ecf20Sopenharmony_ci * iucv_message_reply
12098c2ecf20Sopenharmony_ci * @path: address of iucv path structure
12108c2ecf20Sopenharmony_ci * @msg: address of iucv msg structure
12118c2ecf20Sopenharmony_ci * @flags: how the reply is sent (IUCV_IPRMDATA, IUCV_IPPRTY, IUCV_IPBUFLST)
12128c2ecf20Sopenharmony_ci * @reply: address of reply data buffer or address of struct iucv_array
12138c2ecf20Sopenharmony_ci * @size: length of reply data buffer
12148c2ecf20Sopenharmony_ci *
12158c2ecf20Sopenharmony_ci * This function responds to the two-way messages that you receive. You
12168c2ecf20Sopenharmony_ci * must identify completely the message to which you wish to reply. ie,
12178c2ecf20Sopenharmony_ci * pathid, msgid, and trgcls. Prmmsg signifies the data is moved into
12188c2ecf20Sopenharmony_ci * the parameter list.
12198c2ecf20Sopenharmony_ci *
12208c2ecf20Sopenharmony_ci * Returns the result from the CP IUCV call.
12218c2ecf20Sopenharmony_ci */
12228c2ecf20Sopenharmony_ciint iucv_message_reply(struct iucv_path *path, struct iucv_message *msg,
12238c2ecf20Sopenharmony_ci		       u8 flags, void *reply, size_t size)
12248c2ecf20Sopenharmony_ci{
12258c2ecf20Sopenharmony_ci	union iucv_param *parm;
12268c2ecf20Sopenharmony_ci	int rc;
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	local_bh_disable();
12298c2ecf20Sopenharmony_ci	if (cpumask_empty(&iucv_buffer_cpumask)) {
12308c2ecf20Sopenharmony_ci		rc = -EIO;
12318c2ecf20Sopenharmony_ci		goto out;
12328c2ecf20Sopenharmony_ci	}
12338c2ecf20Sopenharmony_ci	parm = iucv_param[smp_processor_id()];
12348c2ecf20Sopenharmony_ci	memset(parm, 0, sizeof(union iucv_param));
12358c2ecf20Sopenharmony_ci	if (flags & IUCV_IPRMDATA) {
12368c2ecf20Sopenharmony_ci		parm->dpl.ippathid = path->pathid;
12378c2ecf20Sopenharmony_ci		parm->dpl.ipflags1 = flags;
12388c2ecf20Sopenharmony_ci		parm->dpl.ipmsgid = msg->id;
12398c2ecf20Sopenharmony_ci		parm->dpl.iptrgcls = msg->class;
12408c2ecf20Sopenharmony_ci		memcpy(parm->dpl.iprmmsg, reply, min_t(size_t, size, 8));
12418c2ecf20Sopenharmony_ci	} else {
12428c2ecf20Sopenharmony_ci		parm->db.ipbfadr1 = (u32)(addr_t) reply;
12438c2ecf20Sopenharmony_ci		parm->db.ipbfln1f = (u32) size;
12448c2ecf20Sopenharmony_ci		parm->db.ippathid = path->pathid;
12458c2ecf20Sopenharmony_ci		parm->db.ipflags1 = flags;
12468c2ecf20Sopenharmony_ci		parm->db.ipmsgid = msg->id;
12478c2ecf20Sopenharmony_ci		parm->db.iptrgcls = msg->class;
12488c2ecf20Sopenharmony_ci	}
12498c2ecf20Sopenharmony_ci	rc = iucv_call_b2f0(IUCV_REPLY, parm);
12508c2ecf20Sopenharmony_ciout:
12518c2ecf20Sopenharmony_ci	local_bh_enable();
12528c2ecf20Sopenharmony_ci	return rc;
12538c2ecf20Sopenharmony_ci}
12548c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_message_reply);
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci/**
12578c2ecf20Sopenharmony_ci * __iucv_message_send
12588c2ecf20Sopenharmony_ci * @path: address of iucv path structure
12598c2ecf20Sopenharmony_ci * @msg: address of iucv msg structure
12608c2ecf20Sopenharmony_ci * @flags: how the message is sent (IUCV_IPRMDATA, IUCV_IPPRTY, IUCV_IPBUFLST)
12618c2ecf20Sopenharmony_ci * @srccls: source class of message
12628c2ecf20Sopenharmony_ci * @buffer: address of send buffer or address of struct iucv_array
12638c2ecf20Sopenharmony_ci * @size: length of send buffer
12648c2ecf20Sopenharmony_ci *
12658c2ecf20Sopenharmony_ci * This function transmits data to another application. Data to be
12668c2ecf20Sopenharmony_ci * transmitted is in a buffer and this is a one-way message and the
12678c2ecf20Sopenharmony_ci * receiver will not reply to the message.
12688c2ecf20Sopenharmony_ci *
12698c2ecf20Sopenharmony_ci * Locking:	no locking
12708c2ecf20Sopenharmony_ci *
12718c2ecf20Sopenharmony_ci * Returns the result from the CP IUCV call.
12728c2ecf20Sopenharmony_ci */
12738c2ecf20Sopenharmony_ciint __iucv_message_send(struct iucv_path *path, struct iucv_message *msg,
12748c2ecf20Sopenharmony_ci		      u8 flags, u32 srccls, void *buffer, size_t size)
12758c2ecf20Sopenharmony_ci{
12768c2ecf20Sopenharmony_ci	union iucv_param *parm;
12778c2ecf20Sopenharmony_ci	int rc;
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci	if (cpumask_empty(&iucv_buffer_cpumask)) {
12808c2ecf20Sopenharmony_ci		rc = -EIO;
12818c2ecf20Sopenharmony_ci		goto out;
12828c2ecf20Sopenharmony_ci	}
12838c2ecf20Sopenharmony_ci	parm = iucv_param[smp_processor_id()];
12848c2ecf20Sopenharmony_ci	memset(parm, 0, sizeof(union iucv_param));
12858c2ecf20Sopenharmony_ci	if (flags & IUCV_IPRMDATA) {
12868c2ecf20Sopenharmony_ci		/* Message of 8 bytes can be placed into the parameter list. */
12878c2ecf20Sopenharmony_ci		parm->dpl.ippathid = path->pathid;
12888c2ecf20Sopenharmony_ci		parm->dpl.ipflags1 = flags | IUCV_IPNORPY;
12898c2ecf20Sopenharmony_ci		parm->dpl.iptrgcls = msg->class;
12908c2ecf20Sopenharmony_ci		parm->dpl.ipsrccls = srccls;
12918c2ecf20Sopenharmony_ci		parm->dpl.ipmsgtag = msg->tag;
12928c2ecf20Sopenharmony_ci		memcpy(parm->dpl.iprmmsg, buffer, 8);
12938c2ecf20Sopenharmony_ci	} else {
12948c2ecf20Sopenharmony_ci		parm->db.ipbfadr1 = (u32)(addr_t) buffer;
12958c2ecf20Sopenharmony_ci		parm->db.ipbfln1f = (u32) size;
12968c2ecf20Sopenharmony_ci		parm->db.ippathid = path->pathid;
12978c2ecf20Sopenharmony_ci		parm->db.ipflags1 = flags | IUCV_IPNORPY;
12988c2ecf20Sopenharmony_ci		parm->db.iptrgcls = msg->class;
12998c2ecf20Sopenharmony_ci		parm->db.ipsrccls = srccls;
13008c2ecf20Sopenharmony_ci		parm->db.ipmsgtag = msg->tag;
13018c2ecf20Sopenharmony_ci	}
13028c2ecf20Sopenharmony_ci	rc = iucv_call_b2f0(IUCV_SEND, parm);
13038c2ecf20Sopenharmony_ci	if (!rc)
13048c2ecf20Sopenharmony_ci		msg->id = parm->db.ipmsgid;
13058c2ecf20Sopenharmony_ciout:
13068c2ecf20Sopenharmony_ci	return rc;
13078c2ecf20Sopenharmony_ci}
13088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__iucv_message_send);
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci/**
13118c2ecf20Sopenharmony_ci * iucv_message_send
13128c2ecf20Sopenharmony_ci * @path: address of iucv path structure
13138c2ecf20Sopenharmony_ci * @msg: address of iucv msg structure
13148c2ecf20Sopenharmony_ci * @flags: how the message is sent (IUCV_IPRMDATA, IUCV_IPPRTY, IUCV_IPBUFLST)
13158c2ecf20Sopenharmony_ci * @srccls: source class of message
13168c2ecf20Sopenharmony_ci * @buffer: address of send buffer or address of struct iucv_array
13178c2ecf20Sopenharmony_ci * @size: length of send buffer
13188c2ecf20Sopenharmony_ci *
13198c2ecf20Sopenharmony_ci * This function transmits data to another application. Data to be
13208c2ecf20Sopenharmony_ci * transmitted is in a buffer and this is a one-way message and the
13218c2ecf20Sopenharmony_ci * receiver will not reply to the message.
13228c2ecf20Sopenharmony_ci *
13238c2ecf20Sopenharmony_ci * Locking:	local_bh_enable/local_bh_disable
13248c2ecf20Sopenharmony_ci *
13258c2ecf20Sopenharmony_ci * Returns the result from the CP IUCV call.
13268c2ecf20Sopenharmony_ci */
13278c2ecf20Sopenharmony_ciint iucv_message_send(struct iucv_path *path, struct iucv_message *msg,
13288c2ecf20Sopenharmony_ci		      u8 flags, u32 srccls, void *buffer, size_t size)
13298c2ecf20Sopenharmony_ci{
13308c2ecf20Sopenharmony_ci	int rc;
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ci	local_bh_disable();
13338c2ecf20Sopenharmony_ci	rc = __iucv_message_send(path, msg, flags, srccls, buffer, size);
13348c2ecf20Sopenharmony_ci	local_bh_enable();
13358c2ecf20Sopenharmony_ci	return rc;
13368c2ecf20Sopenharmony_ci}
13378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_message_send);
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci/**
13408c2ecf20Sopenharmony_ci * iucv_message_send2way
13418c2ecf20Sopenharmony_ci * @path: address of iucv path structure
13428c2ecf20Sopenharmony_ci * @msg: address of iucv msg structure
13438c2ecf20Sopenharmony_ci * @flags: how the message is sent and the reply is received
13448c2ecf20Sopenharmony_ci *	   (IUCV_IPRMDATA, IUCV_IPBUFLST, IUCV_IPPRTY, IUCV_ANSLST)
13458c2ecf20Sopenharmony_ci * @srccls: source class of message
13468c2ecf20Sopenharmony_ci * @buffer: address of send buffer or address of struct iucv_array
13478c2ecf20Sopenharmony_ci * @size: length of send buffer
13488c2ecf20Sopenharmony_ci * @ansbuf: address of answer buffer or address of struct iucv_array
13498c2ecf20Sopenharmony_ci * @asize: size of reply buffer
13508c2ecf20Sopenharmony_ci *
13518c2ecf20Sopenharmony_ci * This function transmits data to another application. Data to be
13528c2ecf20Sopenharmony_ci * transmitted is in a buffer. The receiver of the send is expected to
13538c2ecf20Sopenharmony_ci * reply to the message and a buffer is provided into which IUCV moves
13548c2ecf20Sopenharmony_ci * the reply to this message.
13558c2ecf20Sopenharmony_ci *
13568c2ecf20Sopenharmony_ci * Returns the result from the CP IUCV call.
13578c2ecf20Sopenharmony_ci */
13588c2ecf20Sopenharmony_ciint iucv_message_send2way(struct iucv_path *path, struct iucv_message *msg,
13598c2ecf20Sopenharmony_ci			  u8 flags, u32 srccls, void *buffer, size_t size,
13608c2ecf20Sopenharmony_ci			  void *answer, size_t asize, size_t *residual)
13618c2ecf20Sopenharmony_ci{
13628c2ecf20Sopenharmony_ci	union iucv_param *parm;
13638c2ecf20Sopenharmony_ci	int rc;
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci	local_bh_disable();
13668c2ecf20Sopenharmony_ci	if (cpumask_empty(&iucv_buffer_cpumask)) {
13678c2ecf20Sopenharmony_ci		rc = -EIO;
13688c2ecf20Sopenharmony_ci		goto out;
13698c2ecf20Sopenharmony_ci	}
13708c2ecf20Sopenharmony_ci	parm = iucv_param[smp_processor_id()];
13718c2ecf20Sopenharmony_ci	memset(parm, 0, sizeof(union iucv_param));
13728c2ecf20Sopenharmony_ci	if (flags & IUCV_IPRMDATA) {
13738c2ecf20Sopenharmony_ci		parm->dpl.ippathid = path->pathid;
13748c2ecf20Sopenharmony_ci		parm->dpl.ipflags1 = path->flags;	/* priority message */
13758c2ecf20Sopenharmony_ci		parm->dpl.iptrgcls = msg->class;
13768c2ecf20Sopenharmony_ci		parm->dpl.ipsrccls = srccls;
13778c2ecf20Sopenharmony_ci		parm->dpl.ipmsgtag = msg->tag;
13788c2ecf20Sopenharmony_ci		parm->dpl.ipbfadr2 = (u32)(addr_t) answer;
13798c2ecf20Sopenharmony_ci		parm->dpl.ipbfln2f = (u32) asize;
13808c2ecf20Sopenharmony_ci		memcpy(parm->dpl.iprmmsg, buffer, 8);
13818c2ecf20Sopenharmony_ci	} else {
13828c2ecf20Sopenharmony_ci		parm->db.ippathid = path->pathid;
13838c2ecf20Sopenharmony_ci		parm->db.ipflags1 = path->flags;	/* priority message */
13848c2ecf20Sopenharmony_ci		parm->db.iptrgcls = msg->class;
13858c2ecf20Sopenharmony_ci		parm->db.ipsrccls = srccls;
13868c2ecf20Sopenharmony_ci		parm->db.ipmsgtag = msg->tag;
13878c2ecf20Sopenharmony_ci		parm->db.ipbfadr1 = (u32)(addr_t) buffer;
13888c2ecf20Sopenharmony_ci		parm->db.ipbfln1f = (u32) size;
13898c2ecf20Sopenharmony_ci		parm->db.ipbfadr2 = (u32)(addr_t) answer;
13908c2ecf20Sopenharmony_ci		parm->db.ipbfln2f = (u32) asize;
13918c2ecf20Sopenharmony_ci	}
13928c2ecf20Sopenharmony_ci	rc = iucv_call_b2f0(IUCV_SEND, parm);
13938c2ecf20Sopenharmony_ci	if (!rc)
13948c2ecf20Sopenharmony_ci		msg->id = parm->db.ipmsgid;
13958c2ecf20Sopenharmony_ciout:
13968c2ecf20Sopenharmony_ci	local_bh_enable();
13978c2ecf20Sopenharmony_ci	return rc;
13988c2ecf20Sopenharmony_ci}
13998c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_message_send2way);
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ci/**
14028c2ecf20Sopenharmony_ci * iucv_path_pending
14038c2ecf20Sopenharmony_ci * @data: Pointer to external interrupt buffer
14048c2ecf20Sopenharmony_ci *
14058c2ecf20Sopenharmony_ci * Process connection pending work item. Called from tasklet while holding
14068c2ecf20Sopenharmony_ci * iucv_table_lock.
14078c2ecf20Sopenharmony_ci */
14088c2ecf20Sopenharmony_cistruct iucv_path_pending {
14098c2ecf20Sopenharmony_ci	u16 ippathid;
14108c2ecf20Sopenharmony_ci	u8  ipflags1;
14118c2ecf20Sopenharmony_ci	u8  iptype;
14128c2ecf20Sopenharmony_ci	u16 ipmsglim;
14138c2ecf20Sopenharmony_ci	u16 res1;
14148c2ecf20Sopenharmony_ci	u8  ipvmid[8];
14158c2ecf20Sopenharmony_ci	u8  ipuser[16];
14168c2ecf20Sopenharmony_ci	u32 res3;
14178c2ecf20Sopenharmony_ci	u8  ippollfg;
14188c2ecf20Sopenharmony_ci	u8  res4[3];
14198c2ecf20Sopenharmony_ci} __packed;
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_cistatic void iucv_path_pending(struct iucv_irq_data *data)
14228c2ecf20Sopenharmony_ci{
14238c2ecf20Sopenharmony_ci	struct iucv_path_pending *ipp = (void *) data;
14248c2ecf20Sopenharmony_ci	struct iucv_handler *handler;
14258c2ecf20Sopenharmony_ci	struct iucv_path *path;
14268c2ecf20Sopenharmony_ci	char *error;
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci	BUG_ON(iucv_path_table[ipp->ippathid]);
14298c2ecf20Sopenharmony_ci	/* New pathid, handler found. Create a new path struct. */
14308c2ecf20Sopenharmony_ci	error = iucv_error_no_memory;
14318c2ecf20Sopenharmony_ci	path = iucv_path_alloc(ipp->ipmsglim, ipp->ipflags1, GFP_ATOMIC);
14328c2ecf20Sopenharmony_ci	if (!path)
14338c2ecf20Sopenharmony_ci		goto out_sever;
14348c2ecf20Sopenharmony_ci	path->pathid = ipp->ippathid;
14358c2ecf20Sopenharmony_ci	iucv_path_table[path->pathid] = path;
14368c2ecf20Sopenharmony_ci	EBCASC(ipp->ipvmid, 8);
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_ci	/* Call registered handler until one is found that wants the path. */
14398c2ecf20Sopenharmony_ci	list_for_each_entry(handler, &iucv_handler_list, list) {
14408c2ecf20Sopenharmony_ci		if (!handler->path_pending)
14418c2ecf20Sopenharmony_ci			continue;
14428c2ecf20Sopenharmony_ci		/*
14438c2ecf20Sopenharmony_ci		 * Add path to handler to allow a call to iucv_path_sever
14448c2ecf20Sopenharmony_ci		 * inside the path_pending function. If the handler returns
14458c2ecf20Sopenharmony_ci		 * an error remove the path from the handler again.
14468c2ecf20Sopenharmony_ci		 */
14478c2ecf20Sopenharmony_ci		list_add(&path->list, &handler->paths);
14488c2ecf20Sopenharmony_ci		path->handler = handler;
14498c2ecf20Sopenharmony_ci		if (!handler->path_pending(path, ipp->ipvmid, ipp->ipuser))
14508c2ecf20Sopenharmony_ci			return;
14518c2ecf20Sopenharmony_ci		list_del(&path->list);
14528c2ecf20Sopenharmony_ci		path->handler = NULL;
14538c2ecf20Sopenharmony_ci	}
14548c2ecf20Sopenharmony_ci	/* No handler wanted the path. */
14558c2ecf20Sopenharmony_ci	iucv_path_table[path->pathid] = NULL;
14568c2ecf20Sopenharmony_ci	iucv_path_free(path);
14578c2ecf20Sopenharmony_ci	error = iucv_error_no_listener;
14588c2ecf20Sopenharmony_ciout_sever:
14598c2ecf20Sopenharmony_ci	iucv_sever_pathid(ipp->ippathid, error);
14608c2ecf20Sopenharmony_ci}
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci/**
14638c2ecf20Sopenharmony_ci * iucv_path_complete
14648c2ecf20Sopenharmony_ci * @data: Pointer to external interrupt buffer
14658c2ecf20Sopenharmony_ci *
14668c2ecf20Sopenharmony_ci * Process connection complete work item. Called from tasklet while holding
14678c2ecf20Sopenharmony_ci * iucv_table_lock.
14688c2ecf20Sopenharmony_ci */
14698c2ecf20Sopenharmony_cistruct iucv_path_complete {
14708c2ecf20Sopenharmony_ci	u16 ippathid;
14718c2ecf20Sopenharmony_ci	u8  ipflags1;
14728c2ecf20Sopenharmony_ci	u8  iptype;
14738c2ecf20Sopenharmony_ci	u16 ipmsglim;
14748c2ecf20Sopenharmony_ci	u16 res1;
14758c2ecf20Sopenharmony_ci	u8  res2[8];
14768c2ecf20Sopenharmony_ci	u8  ipuser[16];
14778c2ecf20Sopenharmony_ci	u32 res3;
14788c2ecf20Sopenharmony_ci	u8  ippollfg;
14798c2ecf20Sopenharmony_ci	u8  res4[3];
14808c2ecf20Sopenharmony_ci} __packed;
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_cistatic void iucv_path_complete(struct iucv_irq_data *data)
14838c2ecf20Sopenharmony_ci{
14848c2ecf20Sopenharmony_ci	struct iucv_path_complete *ipc = (void *) data;
14858c2ecf20Sopenharmony_ci	struct iucv_path *path = iucv_path_table[ipc->ippathid];
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_ci	if (path)
14888c2ecf20Sopenharmony_ci		path->flags = ipc->ipflags1;
14898c2ecf20Sopenharmony_ci	if (path && path->handler && path->handler->path_complete)
14908c2ecf20Sopenharmony_ci		path->handler->path_complete(path, ipc->ipuser);
14918c2ecf20Sopenharmony_ci}
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci/**
14948c2ecf20Sopenharmony_ci * iucv_path_severed
14958c2ecf20Sopenharmony_ci * @data: Pointer to external interrupt buffer
14968c2ecf20Sopenharmony_ci *
14978c2ecf20Sopenharmony_ci * Process connection severed work item. Called from tasklet while holding
14988c2ecf20Sopenharmony_ci * iucv_table_lock.
14998c2ecf20Sopenharmony_ci */
15008c2ecf20Sopenharmony_cistruct iucv_path_severed {
15018c2ecf20Sopenharmony_ci	u16 ippathid;
15028c2ecf20Sopenharmony_ci	u8  res1;
15038c2ecf20Sopenharmony_ci	u8  iptype;
15048c2ecf20Sopenharmony_ci	u32 res2;
15058c2ecf20Sopenharmony_ci	u8  res3[8];
15068c2ecf20Sopenharmony_ci	u8  ipuser[16];
15078c2ecf20Sopenharmony_ci	u32 res4;
15088c2ecf20Sopenharmony_ci	u8  ippollfg;
15098c2ecf20Sopenharmony_ci	u8  res5[3];
15108c2ecf20Sopenharmony_ci} __packed;
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_cistatic void iucv_path_severed(struct iucv_irq_data *data)
15138c2ecf20Sopenharmony_ci{
15148c2ecf20Sopenharmony_ci	struct iucv_path_severed *ips = (void *) data;
15158c2ecf20Sopenharmony_ci	struct iucv_path *path = iucv_path_table[ips->ippathid];
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_ci	if (!path || !path->handler)	/* Already severed */
15188c2ecf20Sopenharmony_ci		return;
15198c2ecf20Sopenharmony_ci	if (path->handler->path_severed)
15208c2ecf20Sopenharmony_ci		path->handler->path_severed(path, ips->ipuser);
15218c2ecf20Sopenharmony_ci	else {
15228c2ecf20Sopenharmony_ci		iucv_sever_pathid(path->pathid, NULL);
15238c2ecf20Sopenharmony_ci		iucv_path_table[path->pathid] = NULL;
15248c2ecf20Sopenharmony_ci		list_del(&path->list);
15258c2ecf20Sopenharmony_ci		iucv_path_free(path);
15268c2ecf20Sopenharmony_ci	}
15278c2ecf20Sopenharmony_ci}
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci/**
15308c2ecf20Sopenharmony_ci * iucv_path_quiesced
15318c2ecf20Sopenharmony_ci * @data: Pointer to external interrupt buffer
15328c2ecf20Sopenharmony_ci *
15338c2ecf20Sopenharmony_ci * Process connection quiesced work item. Called from tasklet while holding
15348c2ecf20Sopenharmony_ci * iucv_table_lock.
15358c2ecf20Sopenharmony_ci */
15368c2ecf20Sopenharmony_cistruct iucv_path_quiesced {
15378c2ecf20Sopenharmony_ci	u16 ippathid;
15388c2ecf20Sopenharmony_ci	u8  res1;
15398c2ecf20Sopenharmony_ci	u8  iptype;
15408c2ecf20Sopenharmony_ci	u32 res2;
15418c2ecf20Sopenharmony_ci	u8  res3[8];
15428c2ecf20Sopenharmony_ci	u8  ipuser[16];
15438c2ecf20Sopenharmony_ci	u32 res4;
15448c2ecf20Sopenharmony_ci	u8  ippollfg;
15458c2ecf20Sopenharmony_ci	u8  res5[3];
15468c2ecf20Sopenharmony_ci} __packed;
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_cistatic void iucv_path_quiesced(struct iucv_irq_data *data)
15498c2ecf20Sopenharmony_ci{
15508c2ecf20Sopenharmony_ci	struct iucv_path_quiesced *ipq = (void *) data;
15518c2ecf20Sopenharmony_ci	struct iucv_path *path = iucv_path_table[ipq->ippathid];
15528c2ecf20Sopenharmony_ci
15538c2ecf20Sopenharmony_ci	if (path && path->handler && path->handler->path_quiesced)
15548c2ecf20Sopenharmony_ci		path->handler->path_quiesced(path, ipq->ipuser);
15558c2ecf20Sopenharmony_ci}
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci/**
15588c2ecf20Sopenharmony_ci * iucv_path_resumed
15598c2ecf20Sopenharmony_ci * @data: Pointer to external interrupt buffer
15608c2ecf20Sopenharmony_ci *
15618c2ecf20Sopenharmony_ci * Process connection resumed work item. Called from tasklet while holding
15628c2ecf20Sopenharmony_ci * iucv_table_lock.
15638c2ecf20Sopenharmony_ci */
15648c2ecf20Sopenharmony_cistruct iucv_path_resumed {
15658c2ecf20Sopenharmony_ci	u16 ippathid;
15668c2ecf20Sopenharmony_ci	u8  res1;
15678c2ecf20Sopenharmony_ci	u8  iptype;
15688c2ecf20Sopenharmony_ci	u32 res2;
15698c2ecf20Sopenharmony_ci	u8  res3[8];
15708c2ecf20Sopenharmony_ci	u8  ipuser[16];
15718c2ecf20Sopenharmony_ci	u32 res4;
15728c2ecf20Sopenharmony_ci	u8  ippollfg;
15738c2ecf20Sopenharmony_ci	u8  res5[3];
15748c2ecf20Sopenharmony_ci} __packed;
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_cistatic void iucv_path_resumed(struct iucv_irq_data *data)
15778c2ecf20Sopenharmony_ci{
15788c2ecf20Sopenharmony_ci	struct iucv_path_resumed *ipr = (void *) data;
15798c2ecf20Sopenharmony_ci	struct iucv_path *path = iucv_path_table[ipr->ippathid];
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci	if (path && path->handler && path->handler->path_resumed)
15828c2ecf20Sopenharmony_ci		path->handler->path_resumed(path, ipr->ipuser);
15838c2ecf20Sopenharmony_ci}
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_ci/**
15868c2ecf20Sopenharmony_ci * iucv_message_complete
15878c2ecf20Sopenharmony_ci * @data: Pointer to external interrupt buffer
15888c2ecf20Sopenharmony_ci *
15898c2ecf20Sopenharmony_ci * Process message complete work item. Called from tasklet while holding
15908c2ecf20Sopenharmony_ci * iucv_table_lock.
15918c2ecf20Sopenharmony_ci */
15928c2ecf20Sopenharmony_cistruct iucv_message_complete {
15938c2ecf20Sopenharmony_ci	u16 ippathid;
15948c2ecf20Sopenharmony_ci	u8  ipflags1;
15958c2ecf20Sopenharmony_ci	u8  iptype;
15968c2ecf20Sopenharmony_ci	u32 ipmsgid;
15978c2ecf20Sopenharmony_ci	u32 ipaudit;
15988c2ecf20Sopenharmony_ci	u8  iprmmsg[8];
15998c2ecf20Sopenharmony_ci	u32 ipsrccls;
16008c2ecf20Sopenharmony_ci	u32 ipmsgtag;
16018c2ecf20Sopenharmony_ci	u32 res;
16028c2ecf20Sopenharmony_ci	u32 ipbfln2f;
16038c2ecf20Sopenharmony_ci	u8  ippollfg;
16048c2ecf20Sopenharmony_ci	u8  res2[3];
16058c2ecf20Sopenharmony_ci} __packed;
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_cistatic void iucv_message_complete(struct iucv_irq_data *data)
16088c2ecf20Sopenharmony_ci{
16098c2ecf20Sopenharmony_ci	struct iucv_message_complete *imc = (void *) data;
16108c2ecf20Sopenharmony_ci	struct iucv_path *path = iucv_path_table[imc->ippathid];
16118c2ecf20Sopenharmony_ci	struct iucv_message msg;
16128c2ecf20Sopenharmony_ci
16138c2ecf20Sopenharmony_ci	if (path && path->handler && path->handler->message_complete) {
16148c2ecf20Sopenharmony_ci		msg.flags = imc->ipflags1;
16158c2ecf20Sopenharmony_ci		msg.id = imc->ipmsgid;
16168c2ecf20Sopenharmony_ci		msg.audit = imc->ipaudit;
16178c2ecf20Sopenharmony_ci		memcpy(msg.rmmsg, imc->iprmmsg, 8);
16188c2ecf20Sopenharmony_ci		msg.class = imc->ipsrccls;
16198c2ecf20Sopenharmony_ci		msg.tag = imc->ipmsgtag;
16208c2ecf20Sopenharmony_ci		msg.length = imc->ipbfln2f;
16218c2ecf20Sopenharmony_ci		path->handler->message_complete(path, &msg);
16228c2ecf20Sopenharmony_ci	}
16238c2ecf20Sopenharmony_ci}
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_ci/**
16268c2ecf20Sopenharmony_ci * iucv_message_pending
16278c2ecf20Sopenharmony_ci * @data: Pointer to external interrupt buffer
16288c2ecf20Sopenharmony_ci *
16298c2ecf20Sopenharmony_ci * Process message pending work item. Called from tasklet while holding
16308c2ecf20Sopenharmony_ci * iucv_table_lock.
16318c2ecf20Sopenharmony_ci */
16328c2ecf20Sopenharmony_cistruct iucv_message_pending {
16338c2ecf20Sopenharmony_ci	u16 ippathid;
16348c2ecf20Sopenharmony_ci	u8  ipflags1;
16358c2ecf20Sopenharmony_ci	u8  iptype;
16368c2ecf20Sopenharmony_ci	u32 ipmsgid;
16378c2ecf20Sopenharmony_ci	u32 iptrgcls;
16388c2ecf20Sopenharmony_ci	union {
16398c2ecf20Sopenharmony_ci		u32 iprmmsg1_u32;
16408c2ecf20Sopenharmony_ci		u8  iprmmsg1[4];
16418c2ecf20Sopenharmony_ci	} ln1msg1;
16428c2ecf20Sopenharmony_ci	union {
16438c2ecf20Sopenharmony_ci		u32 ipbfln1f;
16448c2ecf20Sopenharmony_ci		u8  iprmmsg2[4];
16458c2ecf20Sopenharmony_ci	} ln1msg2;
16468c2ecf20Sopenharmony_ci	u32 res1[3];
16478c2ecf20Sopenharmony_ci	u32 ipbfln2f;
16488c2ecf20Sopenharmony_ci	u8  ippollfg;
16498c2ecf20Sopenharmony_ci	u8  res2[3];
16508c2ecf20Sopenharmony_ci} __packed;
16518c2ecf20Sopenharmony_ci
16528c2ecf20Sopenharmony_cistatic void iucv_message_pending(struct iucv_irq_data *data)
16538c2ecf20Sopenharmony_ci{
16548c2ecf20Sopenharmony_ci	struct iucv_message_pending *imp = (void *) data;
16558c2ecf20Sopenharmony_ci	struct iucv_path *path = iucv_path_table[imp->ippathid];
16568c2ecf20Sopenharmony_ci	struct iucv_message msg;
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_ci	if (path && path->handler && path->handler->message_pending) {
16598c2ecf20Sopenharmony_ci		msg.flags = imp->ipflags1;
16608c2ecf20Sopenharmony_ci		msg.id = imp->ipmsgid;
16618c2ecf20Sopenharmony_ci		msg.class = imp->iptrgcls;
16628c2ecf20Sopenharmony_ci		if (imp->ipflags1 & IUCV_IPRMDATA) {
16638c2ecf20Sopenharmony_ci			memcpy(msg.rmmsg, imp->ln1msg1.iprmmsg1, 8);
16648c2ecf20Sopenharmony_ci			msg.length = 8;
16658c2ecf20Sopenharmony_ci		} else
16668c2ecf20Sopenharmony_ci			msg.length = imp->ln1msg2.ipbfln1f;
16678c2ecf20Sopenharmony_ci		msg.reply_size = imp->ipbfln2f;
16688c2ecf20Sopenharmony_ci		path->handler->message_pending(path, &msg);
16698c2ecf20Sopenharmony_ci	}
16708c2ecf20Sopenharmony_ci}
16718c2ecf20Sopenharmony_ci
16728c2ecf20Sopenharmony_ci/**
16738c2ecf20Sopenharmony_ci * iucv_tasklet_fn:
16748c2ecf20Sopenharmony_ci *
16758c2ecf20Sopenharmony_ci * This tasklet loops over the queue of irq buffers created by
16768c2ecf20Sopenharmony_ci * iucv_external_interrupt, calls the appropriate action handler
16778c2ecf20Sopenharmony_ci * and then frees the buffer.
16788c2ecf20Sopenharmony_ci */
16798c2ecf20Sopenharmony_cistatic void iucv_tasklet_fn(unsigned long ignored)
16808c2ecf20Sopenharmony_ci{
16818c2ecf20Sopenharmony_ci	typedef void iucv_irq_fn(struct iucv_irq_data *);
16828c2ecf20Sopenharmony_ci	static iucv_irq_fn *irq_fn[] = {
16838c2ecf20Sopenharmony_ci		[0x02] = iucv_path_complete,
16848c2ecf20Sopenharmony_ci		[0x03] = iucv_path_severed,
16858c2ecf20Sopenharmony_ci		[0x04] = iucv_path_quiesced,
16868c2ecf20Sopenharmony_ci		[0x05] = iucv_path_resumed,
16878c2ecf20Sopenharmony_ci		[0x06] = iucv_message_complete,
16888c2ecf20Sopenharmony_ci		[0x07] = iucv_message_complete,
16898c2ecf20Sopenharmony_ci		[0x08] = iucv_message_pending,
16908c2ecf20Sopenharmony_ci		[0x09] = iucv_message_pending,
16918c2ecf20Sopenharmony_ci	};
16928c2ecf20Sopenharmony_ci	LIST_HEAD(task_queue);
16938c2ecf20Sopenharmony_ci	struct iucv_irq_list *p, *n;
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci	/* Serialize tasklet, iucv_path_sever and iucv_path_connect. */
16968c2ecf20Sopenharmony_ci	if (!spin_trylock(&iucv_table_lock)) {
16978c2ecf20Sopenharmony_ci		tasklet_schedule(&iucv_tasklet);
16988c2ecf20Sopenharmony_ci		return;
16998c2ecf20Sopenharmony_ci	}
17008c2ecf20Sopenharmony_ci	iucv_active_cpu = smp_processor_id();
17018c2ecf20Sopenharmony_ci
17028c2ecf20Sopenharmony_ci	spin_lock_irq(&iucv_queue_lock);
17038c2ecf20Sopenharmony_ci	list_splice_init(&iucv_task_queue, &task_queue);
17048c2ecf20Sopenharmony_ci	spin_unlock_irq(&iucv_queue_lock);
17058c2ecf20Sopenharmony_ci
17068c2ecf20Sopenharmony_ci	list_for_each_entry_safe(p, n, &task_queue, list) {
17078c2ecf20Sopenharmony_ci		list_del_init(&p->list);
17088c2ecf20Sopenharmony_ci		irq_fn[p->data.iptype](&p->data);
17098c2ecf20Sopenharmony_ci		kfree(p);
17108c2ecf20Sopenharmony_ci	}
17118c2ecf20Sopenharmony_ci
17128c2ecf20Sopenharmony_ci	iucv_active_cpu = -1;
17138c2ecf20Sopenharmony_ci	spin_unlock(&iucv_table_lock);
17148c2ecf20Sopenharmony_ci}
17158c2ecf20Sopenharmony_ci
17168c2ecf20Sopenharmony_ci/**
17178c2ecf20Sopenharmony_ci * iucv_work_fn:
17188c2ecf20Sopenharmony_ci *
17198c2ecf20Sopenharmony_ci * This work function loops over the queue of path pending irq blocks
17208c2ecf20Sopenharmony_ci * created by iucv_external_interrupt, calls the appropriate action
17218c2ecf20Sopenharmony_ci * handler and then frees the buffer.
17228c2ecf20Sopenharmony_ci */
17238c2ecf20Sopenharmony_cistatic void iucv_work_fn(struct work_struct *work)
17248c2ecf20Sopenharmony_ci{
17258c2ecf20Sopenharmony_ci	LIST_HEAD(work_queue);
17268c2ecf20Sopenharmony_ci	struct iucv_irq_list *p, *n;
17278c2ecf20Sopenharmony_ci
17288c2ecf20Sopenharmony_ci	/* Serialize tasklet, iucv_path_sever and iucv_path_connect. */
17298c2ecf20Sopenharmony_ci	spin_lock_bh(&iucv_table_lock);
17308c2ecf20Sopenharmony_ci	iucv_active_cpu = smp_processor_id();
17318c2ecf20Sopenharmony_ci
17328c2ecf20Sopenharmony_ci	spin_lock_irq(&iucv_queue_lock);
17338c2ecf20Sopenharmony_ci	list_splice_init(&iucv_work_queue, &work_queue);
17348c2ecf20Sopenharmony_ci	spin_unlock_irq(&iucv_queue_lock);
17358c2ecf20Sopenharmony_ci
17368c2ecf20Sopenharmony_ci	iucv_cleanup_queue();
17378c2ecf20Sopenharmony_ci	list_for_each_entry_safe(p, n, &work_queue, list) {
17388c2ecf20Sopenharmony_ci		list_del_init(&p->list);
17398c2ecf20Sopenharmony_ci		iucv_path_pending(&p->data);
17408c2ecf20Sopenharmony_ci		kfree(p);
17418c2ecf20Sopenharmony_ci	}
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci	iucv_active_cpu = -1;
17448c2ecf20Sopenharmony_ci	spin_unlock_bh(&iucv_table_lock);
17458c2ecf20Sopenharmony_ci}
17468c2ecf20Sopenharmony_ci
17478c2ecf20Sopenharmony_ci/**
17488c2ecf20Sopenharmony_ci * iucv_external_interrupt
17498c2ecf20Sopenharmony_ci * @code: irq code
17508c2ecf20Sopenharmony_ci *
17518c2ecf20Sopenharmony_ci * Handles external interrupts coming in from CP.
17528c2ecf20Sopenharmony_ci * Places the interrupt buffer on a queue and schedules iucv_tasklet_fn().
17538c2ecf20Sopenharmony_ci */
17548c2ecf20Sopenharmony_cistatic void iucv_external_interrupt(struct ext_code ext_code,
17558c2ecf20Sopenharmony_ci				    unsigned int param32, unsigned long param64)
17568c2ecf20Sopenharmony_ci{
17578c2ecf20Sopenharmony_ci	struct iucv_irq_data *p;
17588c2ecf20Sopenharmony_ci	struct iucv_irq_list *work;
17598c2ecf20Sopenharmony_ci
17608c2ecf20Sopenharmony_ci	inc_irq_stat(IRQEXT_IUC);
17618c2ecf20Sopenharmony_ci	p = iucv_irq_data[smp_processor_id()];
17628c2ecf20Sopenharmony_ci	if (p->ippathid >= iucv_max_pathid) {
17638c2ecf20Sopenharmony_ci		WARN_ON(p->ippathid >= iucv_max_pathid);
17648c2ecf20Sopenharmony_ci		iucv_sever_pathid(p->ippathid, iucv_error_no_listener);
17658c2ecf20Sopenharmony_ci		return;
17668c2ecf20Sopenharmony_ci	}
17678c2ecf20Sopenharmony_ci	BUG_ON(p->iptype  < 0x01 || p->iptype > 0x09);
17688c2ecf20Sopenharmony_ci	work = kmalloc(sizeof(struct iucv_irq_list), GFP_ATOMIC);
17698c2ecf20Sopenharmony_ci	if (!work) {
17708c2ecf20Sopenharmony_ci		pr_warn("iucv_external_interrupt: out of memory\n");
17718c2ecf20Sopenharmony_ci		return;
17728c2ecf20Sopenharmony_ci	}
17738c2ecf20Sopenharmony_ci	memcpy(&work->data, p, sizeof(work->data));
17748c2ecf20Sopenharmony_ci	spin_lock(&iucv_queue_lock);
17758c2ecf20Sopenharmony_ci	if (p->iptype == 0x01) {
17768c2ecf20Sopenharmony_ci		/* Path pending interrupt. */
17778c2ecf20Sopenharmony_ci		list_add_tail(&work->list, &iucv_work_queue);
17788c2ecf20Sopenharmony_ci		schedule_work(&iucv_work);
17798c2ecf20Sopenharmony_ci	} else {
17808c2ecf20Sopenharmony_ci		/* The other interrupts. */
17818c2ecf20Sopenharmony_ci		list_add_tail(&work->list, &iucv_task_queue);
17828c2ecf20Sopenharmony_ci		tasklet_schedule(&iucv_tasklet);
17838c2ecf20Sopenharmony_ci	}
17848c2ecf20Sopenharmony_ci	spin_unlock(&iucv_queue_lock);
17858c2ecf20Sopenharmony_ci}
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_cistruct iucv_interface iucv_if = {
17888c2ecf20Sopenharmony_ci	.message_receive = iucv_message_receive,
17898c2ecf20Sopenharmony_ci	.__message_receive = __iucv_message_receive,
17908c2ecf20Sopenharmony_ci	.message_reply = iucv_message_reply,
17918c2ecf20Sopenharmony_ci	.message_reject = iucv_message_reject,
17928c2ecf20Sopenharmony_ci	.message_send = iucv_message_send,
17938c2ecf20Sopenharmony_ci	.__message_send = __iucv_message_send,
17948c2ecf20Sopenharmony_ci	.message_send2way = iucv_message_send2way,
17958c2ecf20Sopenharmony_ci	.message_purge = iucv_message_purge,
17968c2ecf20Sopenharmony_ci	.path_accept = iucv_path_accept,
17978c2ecf20Sopenharmony_ci	.path_connect = iucv_path_connect,
17988c2ecf20Sopenharmony_ci	.path_quiesce = iucv_path_quiesce,
17998c2ecf20Sopenharmony_ci	.path_resume = iucv_path_resume,
18008c2ecf20Sopenharmony_ci	.path_sever = iucv_path_sever,
18018c2ecf20Sopenharmony_ci	.iucv_register = iucv_register,
18028c2ecf20Sopenharmony_ci	.iucv_unregister = iucv_unregister,
18038c2ecf20Sopenharmony_ci	.bus = NULL,
18048c2ecf20Sopenharmony_ci	.root = NULL,
18058c2ecf20Sopenharmony_ci};
18068c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_if);
18078c2ecf20Sopenharmony_ci
18088c2ecf20Sopenharmony_cistatic enum cpuhp_state iucv_online;
18098c2ecf20Sopenharmony_ci/**
18108c2ecf20Sopenharmony_ci * iucv_init
18118c2ecf20Sopenharmony_ci *
18128c2ecf20Sopenharmony_ci * Allocates and initializes various data structures.
18138c2ecf20Sopenharmony_ci */
18148c2ecf20Sopenharmony_cistatic int __init iucv_init(void)
18158c2ecf20Sopenharmony_ci{
18168c2ecf20Sopenharmony_ci	int rc;
18178c2ecf20Sopenharmony_ci
18188c2ecf20Sopenharmony_ci	if (!MACHINE_IS_VM) {
18198c2ecf20Sopenharmony_ci		rc = -EPROTONOSUPPORT;
18208c2ecf20Sopenharmony_ci		goto out;
18218c2ecf20Sopenharmony_ci	}
18228c2ecf20Sopenharmony_ci	ctl_set_bit(0, 1);
18238c2ecf20Sopenharmony_ci	rc = iucv_query_maxconn();
18248c2ecf20Sopenharmony_ci	if (rc)
18258c2ecf20Sopenharmony_ci		goto out_ctl;
18268c2ecf20Sopenharmony_ci	rc = register_external_irq(EXT_IRQ_IUCV, iucv_external_interrupt);
18278c2ecf20Sopenharmony_ci	if (rc)
18288c2ecf20Sopenharmony_ci		goto out_ctl;
18298c2ecf20Sopenharmony_ci	iucv_root = root_device_register("iucv");
18308c2ecf20Sopenharmony_ci	if (IS_ERR(iucv_root)) {
18318c2ecf20Sopenharmony_ci		rc = PTR_ERR(iucv_root);
18328c2ecf20Sopenharmony_ci		goto out_int;
18338c2ecf20Sopenharmony_ci	}
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_ci	rc = cpuhp_setup_state(CPUHP_NET_IUCV_PREPARE, "net/iucv:prepare",
18368c2ecf20Sopenharmony_ci			       iucv_cpu_prepare, iucv_cpu_dead);
18378c2ecf20Sopenharmony_ci	if (rc)
18388c2ecf20Sopenharmony_ci		goto out_dev;
18398c2ecf20Sopenharmony_ci	rc = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "net/iucv:online",
18408c2ecf20Sopenharmony_ci			       iucv_cpu_online, iucv_cpu_down_prep);
18418c2ecf20Sopenharmony_ci	if (rc < 0)
18428c2ecf20Sopenharmony_ci		goto out_prep;
18438c2ecf20Sopenharmony_ci	iucv_online = rc;
18448c2ecf20Sopenharmony_ci
18458c2ecf20Sopenharmony_ci	rc = register_reboot_notifier(&iucv_reboot_notifier);
18468c2ecf20Sopenharmony_ci	if (rc)
18478c2ecf20Sopenharmony_ci		goto out_remove_hp;
18488c2ecf20Sopenharmony_ci	ASCEBC(iucv_error_no_listener, 16);
18498c2ecf20Sopenharmony_ci	ASCEBC(iucv_error_no_memory, 16);
18508c2ecf20Sopenharmony_ci	ASCEBC(iucv_error_pathid, 16);
18518c2ecf20Sopenharmony_ci	iucv_available = 1;
18528c2ecf20Sopenharmony_ci	rc = bus_register(&iucv_bus);
18538c2ecf20Sopenharmony_ci	if (rc)
18548c2ecf20Sopenharmony_ci		goto out_reboot;
18558c2ecf20Sopenharmony_ci	iucv_if.root = iucv_root;
18568c2ecf20Sopenharmony_ci	iucv_if.bus = &iucv_bus;
18578c2ecf20Sopenharmony_ci	return 0;
18588c2ecf20Sopenharmony_ci
18598c2ecf20Sopenharmony_ciout_reboot:
18608c2ecf20Sopenharmony_ci	unregister_reboot_notifier(&iucv_reboot_notifier);
18618c2ecf20Sopenharmony_ciout_remove_hp:
18628c2ecf20Sopenharmony_ci	cpuhp_remove_state(iucv_online);
18638c2ecf20Sopenharmony_ciout_prep:
18648c2ecf20Sopenharmony_ci	cpuhp_remove_state(CPUHP_NET_IUCV_PREPARE);
18658c2ecf20Sopenharmony_ciout_dev:
18668c2ecf20Sopenharmony_ci	root_device_unregister(iucv_root);
18678c2ecf20Sopenharmony_ciout_int:
18688c2ecf20Sopenharmony_ci	unregister_external_irq(EXT_IRQ_IUCV, iucv_external_interrupt);
18698c2ecf20Sopenharmony_ciout_ctl:
18708c2ecf20Sopenharmony_ci	ctl_clear_bit(0, 1);
18718c2ecf20Sopenharmony_ciout:
18728c2ecf20Sopenharmony_ci	return rc;
18738c2ecf20Sopenharmony_ci}
18748c2ecf20Sopenharmony_ci
18758c2ecf20Sopenharmony_ci/**
18768c2ecf20Sopenharmony_ci * iucv_exit
18778c2ecf20Sopenharmony_ci *
18788c2ecf20Sopenharmony_ci * Frees everything allocated from iucv_init.
18798c2ecf20Sopenharmony_ci */
18808c2ecf20Sopenharmony_cistatic void __exit iucv_exit(void)
18818c2ecf20Sopenharmony_ci{
18828c2ecf20Sopenharmony_ci	struct iucv_irq_list *p, *n;
18838c2ecf20Sopenharmony_ci
18848c2ecf20Sopenharmony_ci	spin_lock_irq(&iucv_queue_lock);
18858c2ecf20Sopenharmony_ci	list_for_each_entry_safe(p, n, &iucv_task_queue, list)
18868c2ecf20Sopenharmony_ci		kfree(p);
18878c2ecf20Sopenharmony_ci	list_for_each_entry_safe(p, n, &iucv_work_queue, list)
18888c2ecf20Sopenharmony_ci		kfree(p);
18898c2ecf20Sopenharmony_ci	spin_unlock_irq(&iucv_queue_lock);
18908c2ecf20Sopenharmony_ci	unregister_reboot_notifier(&iucv_reboot_notifier);
18918c2ecf20Sopenharmony_ci
18928c2ecf20Sopenharmony_ci	cpuhp_remove_state_nocalls(iucv_online);
18938c2ecf20Sopenharmony_ci	cpuhp_remove_state(CPUHP_NET_IUCV_PREPARE);
18948c2ecf20Sopenharmony_ci	root_device_unregister(iucv_root);
18958c2ecf20Sopenharmony_ci	bus_unregister(&iucv_bus);
18968c2ecf20Sopenharmony_ci	unregister_external_irq(EXT_IRQ_IUCV, iucv_external_interrupt);
18978c2ecf20Sopenharmony_ci}
18988c2ecf20Sopenharmony_ci
18998c2ecf20Sopenharmony_cisubsys_initcall(iucv_init);
19008c2ecf20Sopenharmony_cimodule_exit(iucv_exit);
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_ciMODULE_AUTHOR("(C) 2001 IBM Corp. by Fritz Elfert (felfert@millenux.com)");
19038c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Linux for S/390 IUCV lowlevel driver");
19048c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1905