xref: /linux/net/iucv/iucv.c (revision effb8357)
1de6cc651SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
22356f4cbSMartin Schwidefsky /*
32356f4cbSMartin Schwidefsky  * IUCV base infrastructure.
42356f4cbSMartin Schwidefsky  *
56c005961SUrsula Braun  * Copyright IBM Corp. 2001, 2009
66c005961SUrsula Braun  *
72356f4cbSMartin Schwidefsky  * Author(s):
82356f4cbSMartin Schwidefsky  *    Original source:
92356f4cbSMartin Schwidefsky  *	Alan Altmark (Alan_Altmark@us.ibm.com)	Sept. 2000
102356f4cbSMartin Schwidefsky  *	Xenia Tkatschow (xenia@us.ibm.com)
112356f4cbSMartin Schwidefsky  *    2Gb awareness and general cleanup:
122356f4cbSMartin Schwidefsky  *	Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com)
132356f4cbSMartin Schwidefsky  *    Rewritten for af_iucv:
142356f4cbSMartin Schwidefsky  *	Martin Schwidefsky <schwidefsky@de.ibm.com>
15672e405bSUrsula Braun  *    PM functions:
16672e405bSUrsula Braun  *	Ursula Braun (ursula.braun@de.ibm.com)
172356f4cbSMartin Schwidefsky  *
182356f4cbSMartin Schwidefsky  * Documentation used:
192356f4cbSMartin Schwidefsky  *    The original source
202356f4cbSMartin Schwidefsky  *    CP Programming Service, IBM document # SC24-5760
212356f4cbSMartin Schwidefsky  */
222356f4cbSMartin Schwidefsky 
238f7c502cSUrsula Braun #define KMSG_COMPONENT "iucv"
248f7c502cSUrsula Braun #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
258f7c502cSUrsula Braun 
26052ff461SHeiko Carstens #include <linux/kernel_stat.h>
272356f4cbSMartin Schwidefsky #include <linux/module.h>
282356f4cbSMartin Schwidefsky #include <linux/moduleparam.h>
292356f4cbSMartin Schwidefsky #include <linux/spinlock.h>
302356f4cbSMartin Schwidefsky #include <linux/kernel.h>
312356f4cbSMartin Schwidefsky #include <linux/slab.h>
322356f4cbSMartin Schwidefsky #include <linux/init.h>
332356f4cbSMartin Schwidefsky #include <linux/interrupt.h>
342356f4cbSMartin Schwidefsky #include <linux/list.h>
352356f4cbSMartin Schwidefsky #include <linux/errno.h>
362356f4cbSMartin Schwidefsky #include <linux/err.h>
372356f4cbSMartin Schwidefsky #include <linux/device.h>
382356f4cbSMartin Schwidefsky #include <linux/cpu.h>
396c005961SUrsula Braun #include <linux/reboot.h>
402356f4cbSMartin Schwidefsky #include <net/iucv/iucv.h>
4160063497SArun Sharma #include <linux/atomic.h>
422356f4cbSMartin Schwidefsky #include <asm/ebcdic.h>
432356f4cbSMartin Schwidefsky #include <asm/io.h>
44d7b250e2SHeiko Carstens #include <asm/irq.h>
452356f4cbSMartin Schwidefsky #include <asm/smp.h>
462356f4cbSMartin Schwidefsky 
472356f4cbSMartin Schwidefsky /*
482356f4cbSMartin Schwidefsky  * FLAGS:
492356f4cbSMartin Schwidefsky  * All flags are defined in the field IPFLAGS1 of each function
502356f4cbSMartin Schwidefsky  * and can be found in CP Programming Services.
512356f4cbSMartin Schwidefsky  * IPSRCCLS - Indicates you have specified a source class.
522356f4cbSMartin Schwidefsky  * IPTRGCLS - Indicates you have specified a target class.
532356f4cbSMartin Schwidefsky  * IPFGPID  - Indicates you have specified a pathid.
542356f4cbSMartin Schwidefsky  * IPFGMID  - Indicates you have specified a message ID.
552356f4cbSMartin Schwidefsky  * IPNORPY  - Indicates a one-way message. No reply expected.
562356f4cbSMartin Schwidefsky  * IPALL    - Indicates that all paths are affected.
572356f4cbSMartin Schwidefsky  */
582356f4cbSMartin Schwidefsky #define IUCV_IPSRCCLS	0x01
592356f4cbSMartin Schwidefsky #define IUCV_IPTRGCLS	0x01
602356f4cbSMartin Schwidefsky #define IUCV_IPFGPID	0x02
612356f4cbSMartin Schwidefsky #define IUCV_IPFGMID	0x04
622356f4cbSMartin Schwidefsky #define IUCV_IPNORPY	0x10
632356f4cbSMartin Schwidefsky #define IUCV_IPALL	0x80
642356f4cbSMartin Schwidefsky 
iucv_bus_match(struct device * dev,struct device_driver * drv)652356f4cbSMartin Schwidefsky static int iucv_bus_match(struct device *dev, struct device_driver *drv)
662356f4cbSMartin Schwidefsky {
672356f4cbSMartin Schwidefsky 	return 0;
682356f4cbSMartin Schwidefsky }
692356f4cbSMartin Schwidefsky 
70f732ba4aSGreg Kroah-Hartman const struct bus_type iucv_bus = {
712356f4cbSMartin Schwidefsky 	.name = "iucv",
722356f4cbSMartin Schwidefsky 	.match = iucv_bus_match,
732356f4cbSMartin Schwidefsky };
74da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_bus);
752356f4cbSMartin Schwidefsky 
76*effb8357SHeiko Carstens static struct device *iucv_root;
77da99f056SHeiko Carstens 
iucv_release_device(struct device * device)784452e8efSHeiko Carstens static void iucv_release_device(struct device *device)
794452e8efSHeiko Carstens {
804452e8efSHeiko Carstens 	kfree(device);
814452e8efSHeiko Carstens }
824452e8efSHeiko Carstens 
iucv_alloc_device(const struct attribute_group ** attrs,struct device_driver * driver,void * priv,const char * fmt,...)834452e8efSHeiko Carstens struct device *iucv_alloc_device(const struct attribute_group **attrs,
844452e8efSHeiko Carstens 				 struct device_driver *driver,
854452e8efSHeiko Carstens 				 void *priv, const char *fmt, ...)
864452e8efSHeiko Carstens {
874452e8efSHeiko Carstens 	struct device *dev;
884452e8efSHeiko Carstens 	va_list vargs;
894452e8efSHeiko Carstens 	int rc;
904452e8efSHeiko Carstens 
914452e8efSHeiko Carstens 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
924452e8efSHeiko Carstens 	if (!dev)
934452e8efSHeiko Carstens 		goto out_error;
944452e8efSHeiko Carstens 	va_start(vargs, fmt);
954452e8efSHeiko Carstens 	rc = dev_set_name(dev, fmt, vargs);
964452e8efSHeiko Carstens 	va_end(vargs);
974452e8efSHeiko Carstens 	if (rc)
984452e8efSHeiko Carstens 		goto out_error;
994452e8efSHeiko Carstens 	dev->bus = &iucv_bus;
1004452e8efSHeiko Carstens 	dev->parent = iucv_root;
1014452e8efSHeiko Carstens 	dev->driver = driver;
1024452e8efSHeiko Carstens 	dev->groups = attrs;
1034452e8efSHeiko Carstens 	dev->release = iucv_release_device;
1044452e8efSHeiko Carstens 	dev_set_drvdata(dev, priv);
1054452e8efSHeiko Carstens 	return dev;
1064452e8efSHeiko Carstens 
1074452e8efSHeiko Carstens out_error:
1084452e8efSHeiko Carstens 	kfree(dev);
1094452e8efSHeiko Carstens 	return NULL;
1104452e8efSHeiko Carstens }
1114452e8efSHeiko Carstens EXPORT_SYMBOL(iucv_alloc_device);
1124452e8efSHeiko Carstens 
1132356f4cbSMartin Schwidefsky static int iucv_available;
1142356f4cbSMartin Schwidefsky 
1152356f4cbSMartin Schwidefsky /* General IUCV interrupt structure */
1162356f4cbSMartin Schwidefsky struct iucv_irq_data {
1172356f4cbSMartin Schwidefsky 	u16 ippathid;
1182356f4cbSMartin Schwidefsky 	u8  ipflags1;
1192356f4cbSMartin Schwidefsky 	u8  iptype;
1203d87debbSAlexandra Winter 	u32 res2[9];
1212356f4cbSMartin Schwidefsky };
1222356f4cbSMartin Schwidefsky 
12304b090d5SMartin Schwidefsky struct iucv_irq_list {
1242356f4cbSMartin Schwidefsky 	struct list_head list;
1252356f4cbSMartin Schwidefsky 	struct iucv_irq_data data;
1262356f4cbSMartin Schwidefsky };
1272356f4cbSMartin Schwidefsky 
12870cf5035SChristoph Lameter static struct iucv_irq_data *iucv_irq_data[NR_CPUS];
129f2019030SKOSAKI Motohiro static cpumask_t iucv_buffer_cpumask = { CPU_BITS_NONE };
130f2019030SKOSAKI Motohiro static cpumask_t iucv_irq_cpumask = { CPU_BITS_NONE };
1312356f4cbSMartin Schwidefsky 
13204b090d5SMartin Schwidefsky /*
13304b090d5SMartin Schwidefsky  * Queue of interrupt buffers lock for delivery via the tasklet
13404b090d5SMartin Schwidefsky  * (fast but can't call smp_call_function).
13504b090d5SMartin Schwidefsky  */
13604b090d5SMartin Schwidefsky static LIST_HEAD(iucv_task_queue);
13704b090d5SMartin Schwidefsky 
13804b090d5SMartin Schwidefsky /*
13904b090d5SMartin Schwidefsky  * The tasklet for fast delivery of iucv interrupts.
14004b090d5SMartin Schwidefsky  */
14104b090d5SMartin Schwidefsky static void iucv_tasklet_fn(unsigned long);
142b13fecb1SKees Cook static DECLARE_TASKLET_OLD(iucv_tasklet, iucv_tasklet_fn);
14304b090d5SMartin Schwidefsky 
14404b090d5SMartin Schwidefsky /*
14504b090d5SMartin Schwidefsky  * Queue of interrupt buffers for delivery via a work queue
14604b090d5SMartin Schwidefsky  * (slower but can call smp_call_function).
14704b090d5SMartin Schwidefsky  */
14804b090d5SMartin Schwidefsky static LIST_HEAD(iucv_work_queue);
14904b090d5SMartin Schwidefsky 
15004b090d5SMartin Schwidefsky /*
15104b090d5SMartin Schwidefsky  * The work element to deliver path pending interrupts.
15204b090d5SMartin Schwidefsky  */
15304b090d5SMartin Schwidefsky static void iucv_work_fn(struct work_struct *work);
15404b090d5SMartin Schwidefsky static DECLARE_WORK(iucv_work, iucv_work_fn);
15504b090d5SMartin Schwidefsky 
15604b090d5SMartin Schwidefsky /*
15704b090d5SMartin Schwidefsky  * Spinlock protecting task and work queue.
15804b090d5SMartin Schwidefsky  */
15904b090d5SMartin Schwidefsky static DEFINE_SPINLOCK(iucv_queue_lock);
1602356f4cbSMartin Schwidefsky 
1612356f4cbSMartin Schwidefsky enum iucv_command_codes {
1622356f4cbSMartin Schwidefsky 	IUCV_QUERY = 0,
1632356f4cbSMartin Schwidefsky 	IUCV_RETRIEVE_BUFFER = 2,
1642356f4cbSMartin Schwidefsky 	IUCV_SEND = 4,
1652356f4cbSMartin Schwidefsky 	IUCV_RECEIVE = 5,
1662356f4cbSMartin Schwidefsky 	IUCV_REPLY = 6,
1672356f4cbSMartin Schwidefsky 	IUCV_REJECT = 8,
1682356f4cbSMartin Schwidefsky 	IUCV_PURGE = 9,
1692356f4cbSMartin Schwidefsky 	IUCV_ACCEPT = 10,
1702356f4cbSMartin Schwidefsky 	IUCV_CONNECT = 11,
1712356f4cbSMartin Schwidefsky 	IUCV_DECLARE_BUFFER = 12,
1722356f4cbSMartin Schwidefsky 	IUCV_QUIESCE = 13,
1732356f4cbSMartin Schwidefsky 	IUCV_RESUME = 14,
1742356f4cbSMartin Schwidefsky 	IUCV_SEVER = 15,
1752356f4cbSMartin Schwidefsky 	IUCV_SETMASK = 16,
176672e405bSUrsula Braun 	IUCV_SETCONTROLMASK = 17,
1772356f4cbSMartin Schwidefsky };
1782356f4cbSMartin Schwidefsky 
1792356f4cbSMartin Schwidefsky /*
1802356f4cbSMartin Schwidefsky  * Error messages that are used with the iucv_sever function. They get
1812356f4cbSMartin Schwidefsky  * converted to EBCDIC.
1822356f4cbSMartin Schwidefsky  */
1832356f4cbSMartin Schwidefsky static char iucv_error_no_listener[16] = "NO LISTENER";
1842356f4cbSMartin Schwidefsky static char iucv_error_no_memory[16] = "NO MEMORY";
1852356f4cbSMartin Schwidefsky static char iucv_error_pathid[16] = "INVALID PATHID";
1862356f4cbSMartin Schwidefsky 
1872356f4cbSMartin Schwidefsky /*
1882356f4cbSMartin Schwidefsky  * iucv_handler_list: List of registered handlers.
1892356f4cbSMartin Schwidefsky  */
1902356f4cbSMartin Schwidefsky static LIST_HEAD(iucv_handler_list);
1912356f4cbSMartin Schwidefsky 
1922356f4cbSMartin Schwidefsky /*
193b4ea9b6aSAlexander Gordeev  * iucv_path_table: array of pointers to iucv_path structures.
1942356f4cbSMartin Schwidefsky  */
1952356f4cbSMartin Schwidefsky static struct iucv_path **iucv_path_table;
1962356f4cbSMartin Schwidefsky static unsigned long iucv_max_pathid;
1972356f4cbSMartin Schwidefsky 
1982356f4cbSMartin Schwidefsky /*
1992356f4cbSMartin Schwidefsky  * iucv_lock: spinlock protecting iucv_handler_list and iucv_pathid_table
2002356f4cbSMartin Schwidefsky  */
2012356f4cbSMartin Schwidefsky static DEFINE_SPINLOCK(iucv_table_lock);
2022356f4cbSMartin Schwidefsky 
2032356f4cbSMartin Schwidefsky /*
20404b090d5SMartin Schwidefsky  * iucv_active_cpu: contains the number of the cpu executing the tasklet
20504b090d5SMartin Schwidefsky  * or the work handler. Needed for iucv_path_sever called from tasklet.
2062356f4cbSMartin Schwidefsky  */
20704b090d5SMartin Schwidefsky static int iucv_active_cpu = -1;
2082356f4cbSMartin Schwidefsky 
2092356f4cbSMartin Schwidefsky /*
2102356f4cbSMartin Schwidefsky  * Mutex and wait queue for iucv_register/iucv_unregister.
2112356f4cbSMartin Schwidefsky  */
2122356f4cbSMartin Schwidefsky static DEFINE_MUTEX(iucv_register_mutex);
2132356f4cbSMartin Schwidefsky 
2142356f4cbSMartin Schwidefsky /*
2152356f4cbSMartin Schwidefsky  * Counter for number of non-smp capable handlers.
2162356f4cbSMartin Schwidefsky  */
2172356f4cbSMartin Schwidefsky static int iucv_nonsmp_handler;
2182356f4cbSMartin Schwidefsky 
2192356f4cbSMartin Schwidefsky /*
2202356f4cbSMartin Schwidefsky  * IUCV control data structure. Used by iucv_path_accept, iucv_path_connect,
2212356f4cbSMartin Schwidefsky  * iucv_path_quiesce and iucv_path_sever.
2222356f4cbSMartin Schwidefsky  */
2232356f4cbSMartin Schwidefsky struct iucv_cmd_control {
2242356f4cbSMartin Schwidefsky 	u16 ippathid;
2252356f4cbSMartin Schwidefsky 	u8  ipflags1;
2262356f4cbSMartin Schwidefsky 	u8  iprcode;
2272356f4cbSMartin Schwidefsky 	u16 ipmsglim;
2282356f4cbSMartin Schwidefsky 	u16 res1;
2292356f4cbSMartin Schwidefsky 	u8  ipvmid[8];
2302356f4cbSMartin Schwidefsky 	u8  ipuser[16];
2312356f4cbSMartin Schwidefsky 	u8  iptarget[8];
2322356f4cbSMartin Schwidefsky } __attribute__ ((packed,aligned(8)));
2332356f4cbSMartin Schwidefsky 
2342356f4cbSMartin Schwidefsky /*
2352356f4cbSMartin Schwidefsky  * Data in parameter list iucv structure. Used by iucv_message_send,
2362356f4cbSMartin Schwidefsky  * iucv_message_send2way and iucv_message_reply.
2372356f4cbSMartin Schwidefsky  */
2382356f4cbSMartin Schwidefsky struct iucv_cmd_dpl {
2392356f4cbSMartin Schwidefsky 	u16 ippathid;
2402356f4cbSMartin Schwidefsky 	u8  ipflags1;
2412356f4cbSMartin Schwidefsky 	u8  iprcode;
2422356f4cbSMartin Schwidefsky 	u32 ipmsgid;
2432356f4cbSMartin Schwidefsky 	u32 iptrgcls;
2442356f4cbSMartin Schwidefsky 	u8  iprmmsg[8];
2452356f4cbSMartin Schwidefsky 	u32 ipsrccls;
2462356f4cbSMartin Schwidefsky 	u32 ipmsgtag;
2472aca9eafSHeiko Carstens 	dma32_t ipbfadr2;
2482356f4cbSMartin Schwidefsky 	u32 ipbfln2f;
2492356f4cbSMartin Schwidefsky 	u32 res;
2502356f4cbSMartin Schwidefsky } __attribute__ ((packed,aligned(8)));
2512356f4cbSMartin Schwidefsky 
2522356f4cbSMartin Schwidefsky /*
2532356f4cbSMartin Schwidefsky  * Data in buffer iucv structure. Used by iucv_message_receive,
2542356f4cbSMartin Schwidefsky  * iucv_message_reject, iucv_message_send, iucv_message_send2way
2552356f4cbSMartin Schwidefsky  * and iucv_declare_cpu.
2562356f4cbSMartin Schwidefsky  */
2572356f4cbSMartin Schwidefsky struct iucv_cmd_db {
2582356f4cbSMartin Schwidefsky 	u16 ippathid;
2592356f4cbSMartin Schwidefsky 	u8  ipflags1;
2602356f4cbSMartin Schwidefsky 	u8  iprcode;
2612356f4cbSMartin Schwidefsky 	u32 ipmsgid;
2622356f4cbSMartin Schwidefsky 	u32 iptrgcls;
2632aca9eafSHeiko Carstens 	dma32_t ipbfadr1;
2642356f4cbSMartin Schwidefsky 	u32 ipbfln1f;
2652356f4cbSMartin Schwidefsky 	u32 ipsrccls;
2662356f4cbSMartin Schwidefsky 	u32 ipmsgtag;
2672aca9eafSHeiko Carstens 	dma32_t ipbfadr2;
2682356f4cbSMartin Schwidefsky 	u32 ipbfln2f;
2692356f4cbSMartin Schwidefsky 	u32 res;
2702356f4cbSMartin Schwidefsky } __attribute__ ((packed,aligned(8)));
2712356f4cbSMartin Schwidefsky 
2722356f4cbSMartin Schwidefsky /*
2732356f4cbSMartin Schwidefsky  * Purge message iucv structure. Used by iucv_message_purge.
2742356f4cbSMartin Schwidefsky  */
2752356f4cbSMartin Schwidefsky struct iucv_cmd_purge {
2762356f4cbSMartin Schwidefsky 	u16 ippathid;
2772356f4cbSMartin Schwidefsky 	u8  ipflags1;
2782356f4cbSMartin Schwidefsky 	u8  iprcode;
2792356f4cbSMartin Schwidefsky 	u32 ipmsgid;
2802356f4cbSMartin Schwidefsky 	u8  ipaudit[3];
2812356f4cbSMartin Schwidefsky 	u8  res1[5];
2822356f4cbSMartin Schwidefsky 	u32 res2;
2832356f4cbSMartin Schwidefsky 	u32 ipsrccls;
2842356f4cbSMartin Schwidefsky 	u32 ipmsgtag;
2852356f4cbSMartin Schwidefsky 	u32 res3[3];
2862356f4cbSMartin Schwidefsky } __attribute__ ((packed,aligned(8)));
2872356f4cbSMartin Schwidefsky 
2882356f4cbSMartin Schwidefsky /*
2892356f4cbSMartin Schwidefsky  * Set mask iucv structure. Used by iucv_enable_cpu.
2902356f4cbSMartin Schwidefsky  */
2912356f4cbSMartin Schwidefsky struct iucv_cmd_set_mask {
2922356f4cbSMartin Schwidefsky 	u8  ipmask;
2932356f4cbSMartin Schwidefsky 	u8  res1[2];
2942356f4cbSMartin Schwidefsky 	u8  iprcode;
2952356f4cbSMartin Schwidefsky 	u32 res2[9];
2962356f4cbSMartin Schwidefsky } __attribute__ ((packed,aligned(8)));
2972356f4cbSMartin Schwidefsky 
2982356f4cbSMartin Schwidefsky union iucv_param {
2992356f4cbSMartin Schwidefsky 	struct iucv_cmd_control ctrl;
3002356f4cbSMartin Schwidefsky 	struct iucv_cmd_dpl dpl;
3012356f4cbSMartin Schwidefsky 	struct iucv_cmd_db db;
3022356f4cbSMartin Schwidefsky 	struct iucv_cmd_purge purge;
3032356f4cbSMartin Schwidefsky 	struct iucv_cmd_set_mask set_mask;
3042356f4cbSMartin Schwidefsky };
3052356f4cbSMartin Schwidefsky 
3062356f4cbSMartin Schwidefsky /*
3072356f4cbSMartin Schwidefsky  * Anchor for per-cpu IUCV command parameter block.
3082356f4cbSMartin Schwidefsky  */
30970cf5035SChristoph Lameter static union iucv_param *iucv_param[NR_CPUS];
31042e1b4c2SUrsula Braun static union iucv_param *iucv_param_irq[NR_CPUS];
3112356f4cbSMartin Schwidefsky 
3122356f4cbSMartin Schwidefsky /**
313682026a5SHeiko Carstens  * __iucv_call_b2f0
314682026a5SHeiko Carstens  * @command: identifier of IUCV call to CP.
3152356f4cbSMartin Schwidefsky  * @parm: pointer to a struct iucv_parm block
3162356f4cbSMartin Schwidefsky  *
3172356f4cbSMartin Schwidefsky  * Calls CP to execute IUCV commands.
3182356f4cbSMartin Schwidefsky  *
3192356f4cbSMartin Schwidefsky  * Returns the result of the CP IUCV call.
3202356f4cbSMartin Schwidefsky  */
__iucv_call_b2f0(int command,union iucv_param * parm)321eb090ad2SHeiko Carstens static inline int __iucv_call_b2f0(int command, union iucv_param *parm)
3222356f4cbSMartin Schwidefsky {
3232210c548SAlexander Gordeev 	unsigned long reg1 = virt_to_phys(parm);
32450348facSHeiko Carstens 	int cc;
3252356f4cbSMartin Schwidefsky 
3262356f4cbSMartin Schwidefsky 	asm volatile(
32750348facSHeiko Carstens 		"	lgr	0,%[reg0]\n"
32850348facSHeiko Carstens 		"	lgr	1,%[reg1]\n"
3292356f4cbSMartin Schwidefsky 		"	.long	0xb2f01000\n"
33050348facSHeiko Carstens 		"	ipm	%[cc]\n"
33150348facSHeiko Carstens 		"	srl	%[cc],28\n"
33250348facSHeiko Carstens 		: [cc] "=&d" (cc), "+m" (*parm)
33350348facSHeiko Carstens 		: [reg0] "d" ((unsigned long)command),
3342210c548SAlexander Gordeev 		  [reg1] "d" (reg1)
33550348facSHeiko Carstens 		: "cc", "0", "1");
33650348facSHeiko Carstens 	return cc;
337eb090ad2SHeiko Carstens }
338eb090ad2SHeiko Carstens 
iucv_call_b2f0(int command,union iucv_param * parm)339eb090ad2SHeiko Carstens static inline int iucv_call_b2f0(int command, union iucv_param *parm)
340eb090ad2SHeiko Carstens {
341eb090ad2SHeiko Carstens 	int ccode;
342eb090ad2SHeiko Carstens 
343eb090ad2SHeiko Carstens 	ccode = __iucv_call_b2f0(command, parm);
344eb090ad2SHeiko Carstens 	return ccode == 1 ? parm->ctrl.iprcode : ccode;
3452356f4cbSMartin Schwidefsky }
3462356f4cbSMartin Schwidefsky 
347682026a5SHeiko Carstens /*
3482356f4cbSMartin Schwidefsky  * iucv_query_maxconn
3492356f4cbSMartin Schwidefsky  *
3502356f4cbSMartin Schwidefsky  * Determines the maximum number of connections that may be established.
3512356f4cbSMartin Schwidefsky  *
3522356f4cbSMartin Schwidefsky  * Returns the maximum number of connections or -EPERM is IUCV is not
3532356f4cbSMartin Schwidefsky  * available.
3542356f4cbSMartin Schwidefsky  */
__iucv_query_maxconn(void * param,unsigned long * max_pathid)355eb090ad2SHeiko Carstens static int __iucv_query_maxconn(void *param, unsigned long *max_pathid)
3562356f4cbSMartin Schwidefsky {
357ab847d03SAlexander Gordeev 	unsigned long reg1 = virt_to_phys(param);
35850348facSHeiko Carstens 	int cc;
3592356f4cbSMartin Schwidefsky 
3602356f4cbSMartin Schwidefsky 	asm volatile (
36150348facSHeiko Carstens 		"	lghi	0,%[cmd]\n"
36250348facSHeiko Carstens 		"	lgr	1,%[reg1]\n"
3632356f4cbSMartin Schwidefsky 		"	.long	0xb2f01000\n"
36450348facSHeiko Carstens 		"	ipm	%[cc]\n"
36550348facSHeiko Carstens 		"	srl	%[cc],28\n"
36650348facSHeiko Carstens 		"	lgr	%[reg1],1\n"
36750348facSHeiko Carstens 		: [cc] "=&d" (cc), [reg1] "+&d" (reg1)
36850348facSHeiko Carstens 		: [cmd] "K" (IUCV_QUERY)
36950348facSHeiko Carstens 		: "cc", "0", "1");
370eb090ad2SHeiko Carstens 	*max_pathid = reg1;
37150348facSHeiko Carstens 	return cc;
372eb090ad2SHeiko Carstens }
373eb090ad2SHeiko Carstens 
iucv_query_maxconn(void)374eb090ad2SHeiko Carstens static int iucv_query_maxconn(void)
375eb090ad2SHeiko Carstens {
376eb090ad2SHeiko Carstens 	unsigned long max_pathid;
377eb090ad2SHeiko Carstens 	void *param;
378eb090ad2SHeiko Carstens 	int ccode;
379eb090ad2SHeiko Carstens 
380eb090ad2SHeiko Carstens 	param = kzalloc(sizeof(union iucv_param), GFP_KERNEL | GFP_DMA);
381eb090ad2SHeiko Carstens 	if (!param)
382eb090ad2SHeiko Carstens 		return -ENOMEM;
383eb090ad2SHeiko Carstens 	ccode = __iucv_query_maxconn(param, &max_pathid);
3842356f4cbSMartin Schwidefsky 	if (ccode == 0)
385eb090ad2SHeiko Carstens 		iucv_max_pathid = max_pathid;
3862356f4cbSMartin Schwidefsky 	kfree(param);
3872356f4cbSMartin Schwidefsky 	return ccode ? -EPERM : 0;
3882356f4cbSMartin Schwidefsky }
3892356f4cbSMartin Schwidefsky 
3902356f4cbSMartin Schwidefsky /**
3912356f4cbSMartin Schwidefsky  * iucv_allow_cpu
3922356f4cbSMartin Schwidefsky  * @data: unused
3932356f4cbSMartin Schwidefsky  *
3942356f4cbSMartin Schwidefsky  * Allow iucv interrupts on this cpu.
3952356f4cbSMartin Schwidefsky  */
iucv_allow_cpu(void * data)3962356f4cbSMartin Schwidefsky static void iucv_allow_cpu(void *data)
3972356f4cbSMartin Schwidefsky {
3982356f4cbSMartin Schwidefsky 	int cpu = smp_processor_id();
3992356f4cbSMartin Schwidefsky 	union iucv_param *parm;
4002356f4cbSMartin Schwidefsky 
4012356f4cbSMartin Schwidefsky 	/*
4022356f4cbSMartin Schwidefsky 	 * Enable all iucv interrupts.
4032356f4cbSMartin Schwidefsky 	 * ipmask contains bits for the different interrupts
4042356f4cbSMartin Schwidefsky 	 *	0x80 - Flag to allow nonpriority message pending interrupts
4052356f4cbSMartin Schwidefsky 	 *	0x40 - Flag to allow priority message pending interrupts
4062356f4cbSMartin Schwidefsky 	 *	0x20 - Flag to allow nonpriority message completion interrupts
4072356f4cbSMartin Schwidefsky 	 *	0x10 - Flag to allow priority message completion interrupts
4082356f4cbSMartin Schwidefsky 	 *	0x08 - Flag to allow IUCV control interrupts
4092356f4cbSMartin Schwidefsky 	 */
41042e1b4c2SUrsula Braun 	parm = iucv_param_irq[cpu];
4112356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
4122356f4cbSMartin Schwidefsky 	parm->set_mask.ipmask = 0xf8;
4132356f4cbSMartin Schwidefsky 	iucv_call_b2f0(IUCV_SETMASK, parm);
4142356f4cbSMartin Schwidefsky 
415672e405bSUrsula Braun 	/*
416672e405bSUrsula Braun 	 * Enable all iucv control interrupts.
417672e405bSUrsula Braun 	 * ipmask contains bits for the different interrupts
418672e405bSUrsula Braun 	 *	0x80 - Flag to allow pending connections interrupts
419672e405bSUrsula Braun 	 *	0x40 - Flag to allow connection complete interrupts
420672e405bSUrsula Braun 	 *	0x20 - Flag to allow connection severed interrupts
421672e405bSUrsula Braun 	 *	0x10 - Flag to allow connection quiesced interrupts
422672e405bSUrsula Braun 	 *	0x08 - Flag to allow connection resumed interrupts
423672e405bSUrsula Braun 	 */
424672e405bSUrsula Braun 	memset(parm, 0, sizeof(union iucv_param));
425672e405bSUrsula Braun 	parm->set_mask.ipmask = 0xf8;
426672e405bSUrsula Braun 	iucv_call_b2f0(IUCV_SETCONTROLMASK, parm);
4272356f4cbSMartin Schwidefsky 	/* Set indication that iucv interrupts are allowed for this cpu. */
428f2019030SKOSAKI Motohiro 	cpumask_set_cpu(cpu, &iucv_irq_cpumask);
4292356f4cbSMartin Schwidefsky }
4302356f4cbSMartin Schwidefsky 
4312356f4cbSMartin Schwidefsky /**
4322356f4cbSMartin Schwidefsky  * iucv_block_cpu
4332356f4cbSMartin Schwidefsky  * @data: unused
4342356f4cbSMartin Schwidefsky  *
4352356f4cbSMartin Schwidefsky  * Block iucv interrupts on this cpu.
4362356f4cbSMartin Schwidefsky  */
iucv_block_cpu(void * data)4372356f4cbSMartin Schwidefsky static void iucv_block_cpu(void *data)
4382356f4cbSMartin Schwidefsky {
4392356f4cbSMartin Schwidefsky 	int cpu = smp_processor_id();
4402356f4cbSMartin Schwidefsky 	union iucv_param *parm;
4412356f4cbSMartin Schwidefsky 
4422356f4cbSMartin Schwidefsky 	/* Disable all iucv interrupts. */
44342e1b4c2SUrsula Braun 	parm = iucv_param_irq[cpu];
4442356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
4452356f4cbSMartin Schwidefsky 	iucv_call_b2f0(IUCV_SETMASK, parm);
4462356f4cbSMartin Schwidefsky 
4472356f4cbSMartin Schwidefsky 	/* Clear indication that iucv interrupts are allowed for this cpu. */
448f2019030SKOSAKI Motohiro 	cpumask_clear_cpu(cpu, &iucv_irq_cpumask);
4492356f4cbSMartin Schwidefsky }
4502356f4cbSMartin Schwidefsky 
4512356f4cbSMartin Schwidefsky /**
4522356f4cbSMartin Schwidefsky  * iucv_declare_cpu
4532356f4cbSMartin Schwidefsky  * @data: unused
4542356f4cbSMartin Schwidefsky  *
4553a4fa0a2SRobert P. J. Day  * Declare a interrupt buffer on this cpu.
4562356f4cbSMartin Schwidefsky  */
iucv_declare_cpu(void * data)4572356f4cbSMartin Schwidefsky static void iucv_declare_cpu(void *data)
4582356f4cbSMartin Schwidefsky {
4592356f4cbSMartin Schwidefsky 	int cpu = smp_processor_id();
4602356f4cbSMartin Schwidefsky 	union iucv_param *parm;
4612356f4cbSMartin Schwidefsky 	int rc;
4622356f4cbSMartin Schwidefsky 
463f2019030SKOSAKI Motohiro 	if (cpumask_test_cpu(cpu, &iucv_buffer_cpumask))
4642356f4cbSMartin Schwidefsky 		return;
4652356f4cbSMartin Schwidefsky 
4662356f4cbSMartin Schwidefsky 	/* Declare interrupt buffer. */
46742e1b4c2SUrsula Braun 	parm = iucv_param_irq[cpu];
4682356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
4692aca9eafSHeiko Carstens 	parm->db.ipbfadr1 = virt_to_dma32(iucv_irq_data[cpu]);
4702356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_DECLARE_BUFFER, parm);
4712356f4cbSMartin Schwidefsky 	if (rc) {
4722356f4cbSMartin Schwidefsky 		char *err = "Unknown";
4732356f4cbSMartin Schwidefsky 		switch (rc) {
4742356f4cbSMartin Schwidefsky 		case 0x03:
4752356f4cbSMartin Schwidefsky 			err = "Directory error";
4762356f4cbSMartin Schwidefsky 			break;
4772356f4cbSMartin Schwidefsky 		case 0x0a:
4782356f4cbSMartin Schwidefsky 			err = "Invalid length";
4792356f4cbSMartin Schwidefsky 			break;
4802356f4cbSMartin Schwidefsky 		case 0x13:
4812356f4cbSMartin Schwidefsky 			err = "Buffer already exists";
4822356f4cbSMartin Schwidefsky 			break;
4832356f4cbSMartin Schwidefsky 		case 0x3e:
4842356f4cbSMartin Schwidefsky 			err = "Buffer overlap";
4852356f4cbSMartin Schwidefsky 			break;
4862356f4cbSMartin Schwidefsky 		case 0x5c:
4872356f4cbSMartin Schwidefsky 			err = "Paging or storage error";
4882356f4cbSMartin Schwidefsky 			break;
4892356f4cbSMartin Schwidefsky 		}
49047c4cfc3SJoe Perches 		pr_warn("Defining an interrupt buffer on CPU %i failed with 0x%02x (%s)\n",
49147c4cfc3SJoe Perches 			cpu, rc, err);
4922356f4cbSMartin Schwidefsky 		return;
4932356f4cbSMartin Schwidefsky 	}
4942356f4cbSMartin Schwidefsky 
4952356f4cbSMartin Schwidefsky 	/* Set indication that an iucv buffer exists for this cpu. */
496f2019030SKOSAKI Motohiro 	cpumask_set_cpu(cpu, &iucv_buffer_cpumask);
4972356f4cbSMartin Schwidefsky 
498f2019030SKOSAKI Motohiro 	if (iucv_nonsmp_handler == 0 || cpumask_empty(&iucv_irq_cpumask))
4992356f4cbSMartin Schwidefsky 		/* Enable iucv interrupts on this cpu. */
5002356f4cbSMartin Schwidefsky 		iucv_allow_cpu(NULL);
5012356f4cbSMartin Schwidefsky 	else
5022356f4cbSMartin Schwidefsky 		/* Disable iucv interrupts on this cpu. */
5032356f4cbSMartin Schwidefsky 		iucv_block_cpu(NULL);
5042356f4cbSMartin Schwidefsky }
5052356f4cbSMartin Schwidefsky 
5062356f4cbSMartin Schwidefsky /**
5072356f4cbSMartin Schwidefsky  * iucv_retrieve_cpu
5082356f4cbSMartin Schwidefsky  * @data: unused
5092356f4cbSMartin Schwidefsky  *
5102356f4cbSMartin Schwidefsky  * Retrieve interrupt buffer on this cpu.
5112356f4cbSMartin Schwidefsky  */
iucv_retrieve_cpu(void * data)5122356f4cbSMartin Schwidefsky static void iucv_retrieve_cpu(void *data)
5132356f4cbSMartin Schwidefsky {
5142356f4cbSMartin Schwidefsky 	int cpu = smp_processor_id();
5152356f4cbSMartin Schwidefsky 	union iucv_param *parm;
5162356f4cbSMartin Schwidefsky 
517f2019030SKOSAKI Motohiro 	if (!cpumask_test_cpu(cpu, &iucv_buffer_cpumask))
5182356f4cbSMartin Schwidefsky 		return;
5192356f4cbSMartin Schwidefsky 
5202356f4cbSMartin Schwidefsky 	/* Block iucv interrupts. */
5212356f4cbSMartin Schwidefsky 	iucv_block_cpu(NULL);
5222356f4cbSMartin Schwidefsky 
5232356f4cbSMartin Schwidefsky 	/* Retrieve interrupt buffer. */
52442e1b4c2SUrsula Braun 	parm = iucv_param_irq[cpu];
5252356f4cbSMartin Schwidefsky 	iucv_call_b2f0(IUCV_RETRIEVE_BUFFER, parm);
5262356f4cbSMartin Schwidefsky 
5272356f4cbSMartin Schwidefsky 	/* Clear indication that an iucv buffer exists for this cpu. */
528f2019030SKOSAKI Motohiro 	cpumask_clear_cpu(cpu, &iucv_buffer_cpumask);
5292356f4cbSMartin Schwidefsky }
5302356f4cbSMartin Schwidefsky 
531682026a5SHeiko Carstens /*
532682026a5SHeiko Carstens  * iucv_setmask_mp
5332356f4cbSMartin Schwidefsky  *
5342356f4cbSMartin Schwidefsky  * Allow iucv interrupts on all cpus.
5352356f4cbSMartin Schwidefsky  */
iucv_setmask_mp(void)5362356f4cbSMartin Schwidefsky static void iucv_setmask_mp(void)
5372356f4cbSMartin Schwidefsky {
5382356f4cbSMartin Schwidefsky 	int cpu;
5392356f4cbSMartin Schwidefsky 
5408c39ed48SSebastian Andrzej Siewior 	cpus_read_lock();
5412356f4cbSMartin Schwidefsky 	for_each_online_cpu(cpu)
5422356f4cbSMartin Schwidefsky 		/* Enable all cpus with a declared buffer. */
543f2019030SKOSAKI Motohiro 		if (cpumask_test_cpu(cpu, &iucv_buffer_cpumask) &&
544f2019030SKOSAKI Motohiro 		    !cpumask_test_cpu(cpu, &iucv_irq_cpumask))
5453bb447fcSHeiko Carstens 			smp_call_function_single(cpu, iucv_allow_cpu,
5468691e5a8SJens Axboe 						 NULL, 1);
5478c39ed48SSebastian Andrzej Siewior 	cpus_read_unlock();
5482356f4cbSMartin Schwidefsky }
5492356f4cbSMartin Schwidefsky 
550682026a5SHeiko Carstens /*
5512356f4cbSMartin Schwidefsky  * iucv_setmask_up
5522356f4cbSMartin Schwidefsky  *
55304b090d5SMartin Schwidefsky  * Allow iucv interrupts on a single cpu.
5542356f4cbSMartin Schwidefsky  */
iucv_setmask_up(void)5552356f4cbSMartin Schwidefsky static void iucv_setmask_up(void)
5562356f4cbSMartin Schwidefsky {
5572356f4cbSMartin Schwidefsky 	static cpumask_t cpumask;
5582356f4cbSMartin Schwidefsky 	int cpu;
5592356f4cbSMartin Schwidefsky 
5602356f4cbSMartin Schwidefsky 	/* Disable all cpu but the first in cpu_irq_cpumask. */
561f2019030SKOSAKI Motohiro 	cpumask_copy(&cpumask, &iucv_irq_cpumask);
562f2019030SKOSAKI Motohiro 	cpumask_clear_cpu(cpumask_first(&iucv_irq_cpumask), &cpumask);
563f2019030SKOSAKI Motohiro 	for_each_cpu(cpu, &cpumask)
5648691e5a8SJens Axboe 		smp_call_function_single(cpu, iucv_block_cpu, NULL, 1);
5652356f4cbSMartin Schwidefsky }
5662356f4cbSMartin Schwidefsky 
567682026a5SHeiko Carstens /*
5682356f4cbSMartin Schwidefsky  * iucv_enable
5692356f4cbSMartin Schwidefsky  *
5702356f4cbSMartin Schwidefsky  * This function makes iucv ready for use. It allocates the pathid
5712356f4cbSMartin Schwidefsky  * table, declares an iucv interrupt buffer and enables the iucv
5722356f4cbSMartin Schwidefsky  * interrupts. Called when the first user has registered an iucv
5732356f4cbSMartin Schwidefsky  * handler.
5742356f4cbSMartin Schwidefsky  */
iucv_enable(void)5752356f4cbSMartin Schwidefsky static int iucv_enable(void)
5762356f4cbSMartin Schwidefsky {
5772356f4cbSMartin Schwidefsky 	size_t alloc_size;
5782356f4cbSMartin Schwidefsky 	int cpu, rc;
5792356f4cbSMartin Schwidefsky 
5808c39ed48SSebastian Andrzej Siewior 	cpus_read_lock();
5812356f4cbSMartin Schwidefsky 	rc = -ENOMEM;
582b4ea9b6aSAlexander Gordeev 	alloc_size = iucv_max_pathid * sizeof(*iucv_path_table);
5832356f4cbSMartin Schwidefsky 	iucv_path_table = kzalloc(alloc_size, GFP_KERNEL);
5842356f4cbSMartin Schwidefsky 	if (!iucv_path_table)
5852356f4cbSMartin Schwidefsky 		goto out;
5862356f4cbSMartin Schwidefsky 	/* Declare per cpu buffers. */
5872356f4cbSMartin Schwidefsky 	rc = -EIO;
5882356f4cbSMartin Schwidefsky 	for_each_online_cpu(cpu)
5898691e5a8SJens Axboe 		smp_call_function_single(cpu, iucv_declare_cpu, NULL, 1);
590f2019030SKOSAKI Motohiro 	if (cpumask_empty(&iucv_buffer_cpumask))
5912356f4cbSMartin Schwidefsky 		/* No cpu could declare an iucv buffer. */
592f1d3e4dcSHeiko Carstens 		goto out;
5938c39ed48SSebastian Andrzej Siewior 	cpus_read_unlock();
5942356f4cbSMartin Schwidefsky 	return 0;
5952356f4cbSMartin Schwidefsky out:
596f1d3e4dcSHeiko Carstens 	kfree(iucv_path_table);
597f1d3e4dcSHeiko Carstens 	iucv_path_table = NULL;
5988c39ed48SSebastian Andrzej Siewior 	cpus_read_unlock();
5992356f4cbSMartin Schwidefsky 	return rc;
6002356f4cbSMartin Schwidefsky }
6012356f4cbSMartin Schwidefsky 
602682026a5SHeiko Carstens /*
6032356f4cbSMartin Schwidefsky  * iucv_disable
6042356f4cbSMartin Schwidefsky  *
6052356f4cbSMartin Schwidefsky  * This function shuts down iucv. It disables iucv interrupts, retrieves
6062356f4cbSMartin Schwidefsky  * the iucv interrupt buffer and frees the pathid table. Called after the
6072356f4cbSMartin Schwidefsky  * last user unregister its iucv handler.
6082356f4cbSMartin Schwidefsky  */
iucv_disable(void)6092356f4cbSMartin Schwidefsky static void iucv_disable(void)
6102356f4cbSMartin Schwidefsky {
6118c39ed48SSebastian Andrzej Siewior 	cpus_read_lock();
61215c8b6c1SJens Axboe 	on_each_cpu(iucv_retrieve_cpu, NULL, 1);
6132356f4cbSMartin Schwidefsky 	kfree(iucv_path_table);
614f1d3e4dcSHeiko Carstens 	iucv_path_table = NULL;
6158c39ed48SSebastian Andrzej Siewior 	cpus_read_unlock();
6162356f4cbSMartin Schwidefsky }
6172356f4cbSMartin Schwidefsky 
iucv_cpu_dead(unsigned int cpu)61838b48292SSebastian Andrzej Siewior static int iucv_cpu_dead(unsigned int cpu)
619a0e247a8SSrivatsa S. Bhat {
620a0e247a8SSrivatsa S. Bhat 	kfree(iucv_param_irq[cpu]);
621a0e247a8SSrivatsa S. Bhat 	iucv_param_irq[cpu] = NULL;
622a0e247a8SSrivatsa S. Bhat 	kfree(iucv_param[cpu]);
623a0e247a8SSrivatsa S. Bhat 	iucv_param[cpu] = NULL;
624a0e247a8SSrivatsa S. Bhat 	kfree(iucv_irq_data[cpu]);
625a0e247a8SSrivatsa S. Bhat 	iucv_irq_data[cpu] = NULL;
62638b48292SSebastian Andrzej Siewior 	return 0;
627a0e247a8SSrivatsa S. Bhat }
628a0e247a8SSrivatsa S. Bhat 
iucv_cpu_prepare(unsigned int cpu)62938b48292SSebastian Andrzej Siewior static int iucv_cpu_prepare(unsigned int cpu)
630a0e247a8SSrivatsa S. Bhat {
631a0e247a8SSrivatsa S. Bhat 	/* Note: GFP_DMA used to get memory below 2G */
632a0e247a8SSrivatsa S. Bhat 	iucv_irq_data[cpu] = kmalloc_node(sizeof(struct iucv_irq_data),
633a0e247a8SSrivatsa S. Bhat 			     GFP_KERNEL|GFP_DMA, cpu_to_node(cpu));
634a0e247a8SSrivatsa S. Bhat 	if (!iucv_irq_data[cpu])
635a0e247a8SSrivatsa S. Bhat 		goto out_free;
636a0e247a8SSrivatsa S. Bhat 
637a0e247a8SSrivatsa S. Bhat 	/* Allocate parameter blocks. */
638a0e247a8SSrivatsa S. Bhat 	iucv_param[cpu] = kmalloc_node(sizeof(union iucv_param),
639a0e247a8SSrivatsa S. Bhat 			  GFP_KERNEL|GFP_DMA, cpu_to_node(cpu));
640a0e247a8SSrivatsa S. Bhat 	if (!iucv_param[cpu])
641a0e247a8SSrivatsa S. Bhat 		goto out_free;
642a0e247a8SSrivatsa S. Bhat 
643a0e247a8SSrivatsa S. Bhat 	iucv_param_irq[cpu] = kmalloc_node(sizeof(union iucv_param),
644a0e247a8SSrivatsa S. Bhat 			  GFP_KERNEL|GFP_DMA, cpu_to_node(cpu));
645a0e247a8SSrivatsa S. Bhat 	if (!iucv_param_irq[cpu])
646a0e247a8SSrivatsa S. Bhat 		goto out_free;
647a0e247a8SSrivatsa S. Bhat 
648a0e247a8SSrivatsa S. Bhat 	return 0;
649a0e247a8SSrivatsa S. Bhat 
650a0e247a8SSrivatsa S. Bhat out_free:
65138b48292SSebastian Andrzej Siewior 	iucv_cpu_dead(cpu);
652a0e247a8SSrivatsa S. Bhat 	return -ENOMEM;
653a0e247a8SSrivatsa S. Bhat }
654a0e247a8SSrivatsa S. Bhat 
iucv_cpu_online(unsigned int cpu)65538b48292SSebastian Andrzej Siewior static int iucv_cpu_online(unsigned int cpu)
65638b48292SSebastian Andrzej Siewior {
65738b48292SSebastian Andrzej Siewior 	if (!iucv_path_table)
65838b48292SSebastian Andrzej Siewior 		return 0;
65938b48292SSebastian Andrzej Siewior 	iucv_declare_cpu(NULL);
66038b48292SSebastian Andrzej Siewior 	return 0;
66138b48292SSebastian Andrzej Siewior }
66238b48292SSebastian Andrzej Siewior 
iucv_cpu_down_prep(unsigned int cpu)66338b48292SSebastian Andrzej Siewior static int iucv_cpu_down_prep(unsigned int cpu)
6642356f4cbSMartin Schwidefsky {
6652356f4cbSMartin Schwidefsky 	cpumask_var_t cpumask;
6662356f4cbSMartin Schwidefsky 	int ret = 0;
667f1d3e4dcSHeiko Carstens 
66838b48292SSebastian Andrzej Siewior 	if (!iucv_path_table)
66938b48292SSebastian Andrzej Siewior 		return 0;
670f2019030SKOSAKI Motohiro 
671f2019030SKOSAKI Motohiro 	if (!alloc_cpumask_var(&cpumask, GFP_KERNEL))
672f2019030SKOSAKI Motohiro 		return -ENOMEM;
6732356f4cbSMartin Schwidefsky 
67438b48292SSebastian Andrzej Siewior 	cpumask_copy(cpumask, &iucv_buffer_cpumask);
6752356f4cbSMartin Schwidefsky 	cpumask_clear_cpu(cpu, cpumask);
67638b48292SSebastian Andrzej Siewior 	if (cpumask_empty(cpumask)) {
67738b48292SSebastian Andrzej Siewior 		/* Can't offline last IUCV enabled cpu. */
67838b48292SSebastian Andrzej Siewior 		ret = -EINVAL;
67938b48292SSebastian Andrzej Siewior 		goto __free_cpumask;
68038b48292SSebastian Andrzej Siewior 	}
68138b48292SSebastian Andrzej Siewior 
68238b48292SSebastian Andrzej Siewior 	iucv_retrieve_cpu(NULL);
6832356f4cbSMartin Schwidefsky 	if (!cpumask_empty(&iucv_irq_cpumask))
6842356f4cbSMartin Schwidefsky 		goto __free_cpumask;
6852356f4cbSMartin Schwidefsky 
6862356f4cbSMartin Schwidefsky 	smp_call_function_single(cpumask_first(&iucv_buffer_cpumask),
6872356f4cbSMartin Schwidefsky 				 iucv_allow_cpu, NULL, 1);
6882356f4cbSMartin Schwidefsky 
6892356f4cbSMartin Schwidefsky __free_cpumask:
6902356f4cbSMartin Schwidefsky 	free_cpumask_var(cpumask);
69191e60eb6SUrsula Braun 	return ret;
6922356f4cbSMartin Schwidefsky }
6932356f4cbSMartin Schwidefsky 
6942356f4cbSMartin Schwidefsky /**
69542e1b4c2SUrsula Braun  * iucv_sever_pathid
6962356f4cbSMartin Schwidefsky  * @pathid: path identification number.
6972356f4cbSMartin Schwidefsky  * @userdata: 16-bytes of user data.
6982356f4cbSMartin Schwidefsky  *
6992356f4cbSMartin Schwidefsky  * Sever an iucv path to free up the pathid. Used internally.
7002356f4cbSMartin Schwidefsky  */
iucv_sever_pathid(u16 pathid,u8 * userdata)7012356f4cbSMartin Schwidefsky static int iucv_sever_pathid(u16 pathid, u8 *userdata)
7022356f4cbSMartin Schwidefsky {
7032356f4cbSMartin Schwidefsky 	union iucv_param *parm;
70404b090d5SMartin Schwidefsky 
7052356f4cbSMartin Schwidefsky 	parm = iucv_param_irq[smp_processor_id()];
7062356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
7072356f4cbSMartin Schwidefsky 	if (userdata)
7082356f4cbSMartin Schwidefsky 		memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser));
7092356f4cbSMartin Schwidefsky 	parm->ctrl.ippathid = pathid;
71004b090d5SMartin Schwidefsky 	return iucv_call_b2f0(IUCV_SEVER, parm);
7112356f4cbSMartin Schwidefsky }
7122356f4cbSMartin Schwidefsky 
7132356f4cbSMartin Schwidefsky /**
7142356f4cbSMartin Schwidefsky  * __iucv_cleanup_queue
71504b090d5SMartin Schwidefsky  * @dummy: unused dummy argument
7162356f4cbSMartin Schwidefsky  *
7172356f4cbSMartin Schwidefsky  * Nop function called via smp_call_function to force work items from
7182356f4cbSMartin Schwidefsky  * pending external iucv interrupts to the work queue.
7192356f4cbSMartin Schwidefsky  */
__iucv_cleanup_queue(void * dummy)7202356f4cbSMartin Schwidefsky static void __iucv_cleanup_queue(void *dummy)
72104b090d5SMartin Schwidefsky {
7222356f4cbSMartin Schwidefsky }
72304b090d5SMartin Schwidefsky 
7242356f4cbSMartin Schwidefsky /**
7252356f4cbSMartin Schwidefsky  * iucv_cleanup_queue
72625985edcSLucas De Marchi  *
72704b090d5SMartin Schwidefsky  * Function called after a path has been severed to find all remaining
72804b090d5SMartin Schwidefsky  * work items for the now stale pathid. The caller needs to hold the
72904b090d5SMartin Schwidefsky  * iucv_table_lock.
73004b090d5SMartin Schwidefsky  */
iucv_cleanup_queue(void)73104b090d5SMartin Schwidefsky static void iucv_cleanup_queue(void)
73204b090d5SMartin Schwidefsky {
7332356f4cbSMartin Schwidefsky 	struct iucv_irq_list *p, *n;
7348691e5a8SJens Axboe 
73504b090d5SMartin Schwidefsky 	/*
73604b090d5SMartin Schwidefsky 	 * When a path is severed, the pathid can be reused immediately
73704b090d5SMartin Schwidefsky 	 * on a iucv connect or a connection pending interrupt. Remove
73804b090d5SMartin Schwidefsky 	 * all entries from the task queue that refer to a stale pathid
7392356f4cbSMartin Schwidefsky 	 * (iucv_path_table[ix] == NULL). Only then do the iucv connect
7402356f4cbSMartin Schwidefsky 	 * or deliver the connection pending interrupt. To get all the
7412356f4cbSMartin Schwidefsky 	 * pending interrupts force them to the work queue by calling
7422356f4cbSMartin Schwidefsky 	 * an empty function on all cpus.
74304b090d5SMartin Schwidefsky 	 */
7442356f4cbSMartin Schwidefsky 	smp_call_function(__iucv_cleanup_queue, NULL, 1);
7452356f4cbSMartin Schwidefsky 	spin_lock_irq(&iucv_queue_lock);
7462356f4cbSMartin Schwidefsky 	list_for_each_entry_safe(p, n, &iucv_task_queue, list) {
7472356f4cbSMartin Schwidefsky 		/* Remove stale work items from the task queue. */
7482356f4cbSMartin Schwidefsky 		if (iucv_path_table[p->data.ippathid] == NULL) {
7492356f4cbSMartin Schwidefsky 			list_del(&p->list);
7502356f4cbSMartin Schwidefsky 			kfree(p);
7512356f4cbSMartin Schwidefsky 		}
7522356f4cbSMartin Schwidefsky 	}
7532356f4cbSMartin Schwidefsky 	spin_unlock_irq(&iucv_queue_lock);
7542356f4cbSMartin Schwidefsky }
7552356f4cbSMartin Schwidefsky 
7562356f4cbSMartin Schwidefsky /**
7572356f4cbSMartin Schwidefsky  * iucv_register:
7582356f4cbSMartin Schwidefsky  * @handler: address of iucv handler structure
7592356f4cbSMartin Schwidefsky  * @smp: != 0 indicates that the handler can deal with out of order messages
7602356f4cbSMartin Schwidefsky  *
7612356f4cbSMartin Schwidefsky  * Registers a driver with IUCV.
7622356f4cbSMartin Schwidefsky  *
7632356f4cbSMartin Schwidefsky  * Returns 0 on success, -ENOMEM if the memory allocation for the pathid
7642356f4cbSMartin Schwidefsky  * table failed, or -EIO if IUCV_DECLARE_BUFFER failed on all cpus.
7652356f4cbSMartin Schwidefsky  */
iucv_register(struct iucv_handler * handler,int smp)7662356f4cbSMartin Schwidefsky int iucv_register(struct iucv_handler *handler, int smp)
7672356f4cbSMartin Schwidefsky {
7682356f4cbSMartin Schwidefsky 	int rc;
7692356f4cbSMartin Schwidefsky 
7702356f4cbSMartin Schwidefsky 	if (!iucv_available)
7712356f4cbSMartin Schwidefsky 		return -ENOSYS;
7722356f4cbSMartin Schwidefsky 	mutex_lock(&iucv_register_mutex);
773435bc9dfSUrsula Braun 	if (!smp)
7742356f4cbSMartin Schwidefsky 		iucv_nonsmp_handler++;
775435bc9dfSUrsula Braun 	if (list_empty(&iucv_handler_list)) {
7762356f4cbSMartin Schwidefsky 		rc = iucv_enable();
7772356f4cbSMartin Schwidefsky 		if (rc)
7782356f4cbSMartin Schwidefsky 			goto out_mutex;
7792356f4cbSMartin Schwidefsky 	} else if (!smp && iucv_nonsmp_handler == 1)
7802356f4cbSMartin Schwidefsky 		iucv_setmask_up();
781da99f056SHeiko Carstens 	INIT_LIST_HEAD(&handler->paths);
7822356f4cbSMartin Schwidefsky 
7832356f4cbSMartin Schwidefsky 	spin_lock_bh(&iucv_table_lock);
7842356f4cbSMartin Schwidefsky 	list_add_tail(&handler->list, &iucv_handler_list);
7852356f4cbSMartin Schwidefsky 	spin_unlock_bh(&iucv_table_lock);
7862356f4cbSMartin Schwidefsky 	rc = 0;
7872356f4cbSMartin Schwidefsky out_mutex:
7882356f4cbSMartin Schwidefsky 	mutex_unlock(&iucv_register_mutex);
7892356f4cbSMartin Schwidefsky 	return rc;
7902356f4cbSMartin Schwidefsky }
7912356f4cbSMartin Schwidefsky EXPORT_SYMBOL(iucv_register);
7922356f4cbSMartin Schwidefsky 
7932356f4cbSMartin Schwidefsky /**
7942356f4cbSMartin Schwidefsky  * iucv_unregister
7952356f4cbSMartin Schwidefsky  * @handler:  address of iucv handler structure
7962356f4cbSMartin Schwidefsky  * @smp: != 0 indicates that the handler can deal with out of order messages
7972356f4cbSMartin Schwidefsky  *
79825985edcSLucas De Marchi  * Unregister driver from IUCV.
7992356f4cbSMartin Schwidefsky  */
iucv_unregister(struct iucv_handler * handler,int smp)8002356f4cbSMartin Schwidefsky void iucv_unregister(struct iucv_handler *handler, int smp)
8012356f4cbSMartin Schwidefsky {
8022356f4cbSMartin Schwidefsky 	struct iucv_path *p, *n;
8032356f4cbSMartin Schwidefsky 
8042356f4cbSMartin Schwidefsky 	mutex_lock(&iucv_register_mutex);
8052356f4cbSMartin Schwidefsky 	spin_lock_bh(&iucv_table_lock);
8062356f4cbSMartin Schwidefsky 	/* Remove handler from the iucv_handler_list. */
8072356f4cbSMartin Schwidefsky 	list_del_init(&handler->list);
8082356f4cbSMartin Schwidefsky 	/* Sever all pathids still referring to the handler. */
8092356f4cbSMartin Schwidefsky 	list_for_each_entry_safe(p, n, &handler->paths, list) {
8102356f4cbSMartin Schwidefsky 		iucv_sever_pathid(p->pathid, NULL);
8112356f4cbSMartin Schwidefsky 		iucv_path_table[p->pathid] = NULL;
8122356f4cbSMartin Schwidefsky 		list_del(&p->list);
8132356f4cbSMartin Schwidefsky 		iucv_path_free(p);
814da99f056SHeiko Carstens 	}
8152356f4cbSMartin Schwidefsky 	spin_unlock_bh(&iucv_table_lock);
8166c005961SUrsula Braun 	if (!smp)
8176c005961SUrsula Braun 		iucv_nonsmp_handler--;
8186c005961SUrsula Braun 	if (list_empty(&iucv_handler_list))
8195db79c06SUrsula Braun 		iucv_disable();
8206c005961SUrsula Braun 	else if (!smp && iucv_nonsmp_handler == 0)
821c0048de2SHendrik Brueckner 		iucv_setmask_mp();
822c0048de2SHendrik Brueckner 	mutex_unlock(&iucv_register_mutex);
823c0048de2SHendrik Brueckner }
8248c39ed48SSebastian Andrzej Siewior EXPORT_SYMBOL(iucv_unregister);
825c0048de2SHendrik Brueckner 
iucv_reboot_event(struct notifier_block * this,unsigned long event,void * ptr)8266c005961SUrsula Braun static int iucv_reboot_event(struct notifier_block *this,
8276c005961SUrsula Braun 			     unsigned long event, void *ptr)
8286c005961SUrsula Braun {
8295db79c06SUrsula Braun 	int i;
8306c005961SUrsula Braun 
8316c005961SUrsula Braun 	if (cpumask_empty(&iucv_irq_cpumask))
8328c39ed48SSebastian Andrzej Siewior 		return NOTIFY_DONE;
8336c005961SUrsula Braun 
8346c005961SUrsula Braun 	cpus_read_lock();
8356c005961SUrsula Braun 	on_each_cpu_mask(&iucv_irq_cpumask, iucv_block_cpu, NULL, 1);
8366c005961SUrsula Braun 	preempt_disable();
8376c005961SUrsula Braun 	for (i = 0; i < iucv_max_pathid; i++) {
8386c005961SUrsula Braun 		if (iucv_path_table[i])
8396c005961SUrsula Braun 			iucv_sever_pathid(i, NULL);
8406c005961SUrsula Braun 	}
8412356f4cbSMartin Schwidefsky 	preempt_enable();
8422356f4cbSMartin Schwidefsky 	cpus_read_unlock();
8432356f4cbSMartin Schwidefsky 	iucv_disable();
8442356f4cbSMartin Schwidefsky 	return NOTIFY_DONE;
8452356f4cbSMartin Schwidefsky }
8462356f4cbSMartin Schwidefsky 
8472356f4cbSMartin Schwidefsky static struct notifier_block iucv_reboot_notifier = {
8482356f4cbSMartin Schwidefsky 	.notifier_call = iucv_reboot_event,
8492356f4cbSMartin Schwidefsky };
8502356f4cbSMartin Schwidefsky 
8512356f4cbSMartin Schwidefsky /**
8522356f4cbSMartin Schwidefsky  * iucv_path_accept
8532356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
85491e60eb6SUrsula Braun  * @handler: address of iucv handler structure
8552356f4cbSMartin Schwidefsky  * @userdata: 16 bytes of data reflected to the communication partner
8562356f4cbSMartin Schwidefsky  * @private: private data passed to interrupt handlers for this path
8572356f4cbSMartin Schwidefsky  *
8582356f4cbSMartin Schwidefsky  * This function is issued after the user received a connection pending
8592356f4cbSMartin Schwidefsky  * external interrupt and now wishes to complete the IUCV communication path.
860f2019030SKOSAKI Motohiro  *
8616c005961SUrsula Braun  * Returns the result of the CP IUCV call.
8626c005961SUrsula Braun  */
iucv_path_accept(struct iucv_path * path,struct iucv_handler * handler,u8 * userdata,void * private)8636c005961SUrsula Braun int iucv_path_accept(struct iucv_path *path, struct iucv_handler *handler,
8642356f4cbSMartin Schwidefsky 		     u8 *userdata, void *private)
86570cf5035SChristoph Lameter {
8662356f4cbSMartin Schwidefsky 	union iucv_param *parm;
8672356f4cbSMartin Schwidefsky 	int rc;
8682356f4cbSMartin Schwidefsky 
8692356f4cbSMartin Schwidefsky 	local_bh_disable();
8702356f4cbSMartin Schwidefsky 	if (cpumask_empty(&iucv_buffer_cpumask)) {
8712356f4cbSMartin Schwidefsky 		rc = -EIO;
8722356f4cbSMartin Schwidefsky 		goto out;
8732356f4cbSMartin Schwidefsky 	}
8742356f4cbSMartin Schwidefsky 	/* Prepare parameter block. */
8752356f4cbSMartin Schwidefsky 	parm = iucv_param[smp_processor_id()];
8762356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
8772356f4cbSMartin Schwidefsky 	parm->ctrl.ippathid = path->pathid;
8782356f4cbSMartin Schwidefsky 	parm->ctrl.ipmsglim = path->msglim;
8796c005961SUrsula Braun 	if (userdata)
8802356f4cbSMartin Schwidefsky 		memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser));
8812356f4cbSMartin Schwidefsky 	parm->ctrl.ipflags1 = path->flags;
8822356f4cbSMartin Schwidefsky 
883da99f056SHeiko Carstens 	rc = iucv_call_b2f0(IUCV_ACCEPT, parm);
8842356f4cbSMartin Schwidefsky 	if (!rc) {
8852356f4cbSMartin Schwidefsky 		path->private = private;
8862356f4cbSMartin Schwidefsky 		path->msglim = parm->ctrl.ipmsglim;
8872356f4cbSMartin Schwidefsky 		path->flags = parm->ctrl.ipflags1;
8882356f4cbSMartin Schwidefsky 	}
8892356f4cbSMartin Schwidefsky out:
8902356f4cbSMartin Schwidefsky 	local_bh_enable();
8912356f4cbSMartin Schwidefsky 	return rc;
8922356f4cbSMartin Schwidefsky }
8932356f4cbSMartin Schwidefsky EXPORT_SYMBOL(iucv_path_accept);
8942356f4cbSMartin Schwidefsky 
8952356f4cbSMartin Schwidefsky /**
8962356f4cbSMartin Schwidefsky  * iucv_path_connect
8972356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
8982356f4cbSMartin Schwidefsky  * @handler: address of iucv handler structure
8992356f4cbSMartin Schwidefsky  * @userid: 8-byte user identification
9002356f4cbSMartin Schwidefsky  * @system: 8-byte target system identification
90191e60eb6SUrsula Braun  * @userdata: 16 bytes of data reflected to the communication partner
9022356f4cbSMartin Schwidefsky  * @private: private data passed to interrupt handlers for this path
9032356f4cbSMartin Schwidefsky  *
9042356f4cbSMartin Schwidefsky  * This function establishes an IUCV path. Although the connect may complete
9052356f4cbSMartin Schwidefsky  * successfully, you are not able to use the path until you receive an IUCV
9062356f4cbSMartin Schwidefsky  * Connection Complete external interrupt.
9072356f4cbSMartin Schwidefsky  *
90804b090d5SMartin Schwidefsky  * Returns the result of the CP IUCV call.
909f2019030SKOSAKI Motohiro  */
iucv_path_connect(struct iucv_path * path,struct iucv_handler * handler,u8 * userid,u8 * system,u8 * userdata,void * private)9106c005961SUrsula Braun int iucv_path_connect(struct iucv_path *path, struct iucv_handler *handler,
9116c005961SUrsula Braun 		      u8 *userid, u8 *system, u8 *userdata,
9126c005961SUrsula Braun 		      void *private)
91370cf5035SChristoph Lameter {
9142356f4cbSMartin Schwidefsky 	union iucv_param *parm;
9152356f4cbSMartin Schwidefsky 	int rc;
9162356f4cbSMartin Schwidefsky 
9172356f4cbSMartin Schwidefsky 	spin_lock_bh(&iucv_table_lock);
9182356f4cbSMartin Schwidefsky 	iucv_cleanup_queue();
9192356f4cbSMartin Schwidefsky 	if (cpumask_empty(&iucv_buffer_cpumask)) {
9202356f4cbSMartin Schwidefsky 		rc = -EIO;
9212356f4cbSMartin Schwidefsky 		goto out;
9222356f4cbSMartin Schwidefsky 	}
9232356f4cbSMartin Schwidefsky 	parm = iucv_param[smp_processor_id()];
9242356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
9252356f4cbSMartin Schwidefsky 	parm->ctrl.ipmsglim = path->msglim;
9262356f4cbSMartin Schwidefsky 	parm->ctrl.ipflags1 = path->flags;
9272356f4cbSMartin Schwidefsky 	if (userid) {
9282356f4cbSMartin Schwidefsky 		memcpy(parm->ctrl.ipvmid, userid, sizeof(parm->ctrl.ipvmid));
9292356f4cbSMartin Schwidefsky 		ASCEBC(parm->ctrl.ipvmid, sizeof(parm->ctrl.ipvmid));
9302356f4cbSMartin Schwidefsky 		EBC_TOUPPER(parm->ctrl.ipvmid, sizeof(parm->ctrl.ipvmid));
9312356f4cbSMartin Schwidefsky 	}
9322356f4cbSMartin Schwidefsky 	if (system) {
9332356f4cbSMartin Schwidefsky 		memcpy(parm->ctrl.iptarget, system,
9342356f4cbSMartin Schwidefsky 		       sizeof(parm->ctrl.iptarget));
9352356f4cbSMartin Schwidefsky 		ASCEBC(parm->ctrl.iptarget, sizeof(parm->ctrl.iptarget));
9362356f4cbSMartin Schwidefsky 		EBC_TOUPPER(parm->ctrl.iptarget, sizeof(parm->ctrl.iptarget));
9372356f4cbSMartin Schwidefsky 	}
9382356f4cbSMartin Schwidefsky 	if (userdata)
9392356f4cbSMartin Schwidefsky 		memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser));
9402356f4cbSMartin Schwidefsky 
9412356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_CONNECT, parm);
9422356f4cbSMartin Schwidefsky 	if (!rc) {
9432356f4cbSMartin Schwidefsky 		if (parm->ctrl.ippathid < iucv_max_pathid) {
9442356f4cbSMartin Schwidefsky 			path->pathid = parm->ctrl.ippathid;
9452356f4cbSMartin Schwidefsky 			path->msglim = parm->ctrl.ipmsglim;
9462356f4cbSMartin Schwidefsky 			path->flags = parm->ctrl.ipflags1;
9476c005961SUrsula Braun 			path->handler = handler;
9482356f4cbSMartin Schwidefsky 			path->private = private;
9492356f4cbSMartin Schwidefsky 			list_add_tail(&path->list, &handler->paths);
9502356f4cbSMartin Schwidefsky 			iucv_path_table[path->pathid] = path;
951da99f056SHeiko Carstens 		} else {
9522356f4cbSMartin Schwidefsky 			iucv_sever_pathid(parm->ctrl.ippathid,
9532356f4cbSMartin Schwidefsky 					  iucv_error_pathid);
9542356f4cbSMartin Schwidefsky 			rc = -EIO;
9552356f4cbSMartin Schwidefsky 		}
9562356f4cbSMartin Schwidefsky 	}
9572356f4cbSMartin Schwidefsky out:
9582356f4cbSMartin Schwidefsky 	spin_unlock_bh(&iucv_table_lock);
9592356f4cbSMartin Schwidefsky 	return rc;
9602356f4cbSMartin Schwidefsky }
9612356f4cbSMartin Schwidefsky EXPORT_SYMBOL(iucv_path_connect);
9622356f4cbSMartin Schwidefsky 
96391e60eb6SUrsula Braun /**
9642356f4cbSMartin Schwidefsky  * iucv_path_quiesce:
9652356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
9662356f4cbSMartin Schwidefsky  * @userdata: 16 bytes of data reflected to the communication partner
9672356f4cbSMartin Schwidefsky  *
9682356f4cbSMartin Schwidefsky  * This function temporarily suspends incoming messages on an IUCV path.
969f2019030SKOSAKI Motohiro  * You can later reactivate the path by invoking the iucv_resume function.
9706c005961SUrsula Braun  *
9716c005961SUrsula Braun  * Returns the result from the CP IUCV call.
9726c005961SUrsula Braun  */
iucv_path_quiesce(struct iucv_path * path,u8 * userdata)97370cf5035SChristoph Lameter int iucv_path_quiesce(struct iucv_path *path, u8 *userdata)
9742356f4cbSMartin Schwidefsky {
9752356f4cbSMartin Schwidefsky 	union iucv_param *parm;
9762356f4cbSMartin Schwidefsky 	int rc;
9772356f4cbSMartin Schwidefsky 
9782356f4cbSMartin Schwidefsky 	local_bh_disable();
9796c005961SUrsula Braun 	if (cpumask_empty(&iucv_buffer_cpumask)) {
9802356f4cbSMartin Schwidefsky 		rc = -EIO;
9812356f4cbSMartin Schwidefsky 		goto out;
9822356f4cbSMartin Schwidefsky 	}
983da99f056SHeiko Carstens 	parm = iucv_param[smp_processor_id()];
9842356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
9852356f4cbSMartin Schwidefsky 	if (userdata)
9862356f4cbSMartin Schwidefsky 		memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser));
9872356f4cbSMartin Schwidefsky 	parm->ctrl.ippathid = path->pathid;
9882356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_QUIESCE, parm);
9892356f4cbSMartin Schwidefsky out:
9902356f4cbSMartin Schwidefsky 	local_bh_enable();
9912356f4cbSMartin Schwidefsky 	return rc;
9922356f4cbSMartin Schwidefsky }
9932356f4cbSMartin Schwidefsky EXPORT_SYMBOL(iucv_path_quiesce);
9942356f4cbSMartin Schwidefsky 
99591e60eb6SUrsula Braun /**
9962356f4cbSMartin Schwidefsky  * iucv_path_resume:
9972356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
9982356f4cbSMartin Schwidefsky  * @userdata: 16 bytes of data reflected to the communication partner
9992356f4cbSMartin Schwidefsky  *
10002356f4cbSMartin Schwidefsky  * This function resumes incoming messages on an IUCV path that has
1001f2019030SKOSAKI Motohiro  * been stopped with iucv_path_quiesce.
10026c005961SUrsula Braun  *
10036c005961SUrsula Braun  * Returns the result from the CP IUCV call.
10046c005961SUrsula Braun  */
iucv_path_resume(struct iucv_path * path,u8 * userdata)100570cf5035SChristoph Lameter int iucv_path_resume(struct iucv_path *path, u8 *userdata)
10062356f4cbSMartin Schwidefsky {
10072356f4cbSMartin Schwidefsky 	union iucv_param *parm;
10082356f4cbSMartin Schwidefsky 	int rc;
10092356f4cbSMartin Schwidefsky 
10102356f4cbSMartin Schwidefsky 	local_bh_disable();
10116c005961SUrsula Braun 	if (cpumask_empty(&iucv_buffer_cpumask)) {
10122356f4cbSMartin Schwidefsky 		rc = -EIO;
10132356f4cbSMartin Schwidefsky 		goto out;
10142356f4cbSMartin Schwidefsky 	}
10152356f4cbSMartin Schwidefsky 	parm = iucv_param[smp_processor_id()];
10162356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
10172356f4cbSMartin Schwidefsky 	if (userdata)
10182356f4cbSMartin Schwidefsky 		memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser));
10192356f4cbSMartin Schwidefsky 	parm->ctrl.ippathid = path->pathid;
10202356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_RESUME, parm);
10212356f4cbSMartin Schwidefsky out:
10222356f4cbSMartin Schwidefsky 	local_bh_enable();
10232356f4cbSMartin Schwidefsky 	return rc;
10242356f4cbSMartin Schwidefsky }
102591e60eb6SUrsula Braun 
10262356f4cbSMartin Schwidefsky /**
10272356f4cbSMartin Schwidefsky  * iucv_path_sever
10282356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
10292356f4cbSMartin Schwidefsky  * @userdata: 16 bytes of data reflected to the communication partner
1030f2019030SKOSAKI Motohiro  *
10316c005961SUrsula Braun  * This function terminates an IUCV path.
10326c005961SUrsula Braun  *
10336c005961SUrsula Braun  * Returns the result from the CP IUCV call.
103404b090d5SMartin Schwidefsky  */
iucv_path_sever(struct iucv_path * path,u8 * userdata)10352356f4cbSMartin Schwidefsky int iucv_path_sever(struct iucv_path *path, u8 *userdata)
10362356f4cbSMartin Schwidefsky {
10372356f4cbSMartin Schwidefsky 	int rc;
10382356f4cbSMartin Schwidefsky 
103904b090d5SMartin Schwidefsky 	preempt_disable();
10402356f4cbSMartin Schwidefsky 	if (cpumask_empty(&iucv_buffer_cpumask)) {
10416c005961SUrsula Braun 		rc = -EIO;
10422356f4cbSMartin Schwidefsky 		goto out;
10432356f4cbSMartin Schwidefsky 	}
10442356f4cbSMartin Schwidefsky 	if (iucv_active_cpu != smp_processor_id())
1045da99f056SHeiko Carstens 		spin_lock_bh(&iucv_table_lock);
10462356f4cbSMartin Schwidefsky 	rc = iucv_sever_pathid(path->pathid, userdata);
10472356f4cbSMartin Schwidefsky 	iucv_path_table[path->pathid] = NULL;
10482356f4cbSMartin Schwidefsky 	list_del_init(&path->list);
10492356f4cbSMartin Schwidefsky 	if (iucv_active_cpu != smp_processor_id())
10502356f4cbSMartin Schwidefsky 		spin_unlock_bh(&iucv_table_lock);
10512356f4cbSMartin Schwidefsky out:
10522356f4cbSMartin Schwidefsky 	preempt_enable();
10532356f4cbSMartin Schwidefsky 	return rc;
10542356f4cbSMartin Schwidefsky }
10552356f4cbSMartin Schwidefsky EXPORT_SYMBOL(iucv_path_sever);
10562356f4cbSMartin Schwidefsky 
10572356f4cbSMartin Schwidefsky /**
10582356f4cbSMartin Schwidefsky  * iucv_message_purge
10592356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
10602356f4cbSMartin Schwidefsky  * @msg: address of iucv msg structure
10612356f4cbSMartin Schwidefsky  * @srccls: source class of message
10622356f4cbSMartin Schwidefsky  *
10632356f4cbSMartin Schwidefsky  * Cancels a message you have sent.
1064f2019030SKOSAKI Motohiro  *
10656c005961SUrsula Braun  * Returns the result from the CP IUCV call.
10666c005961SUrsula Braun  */
iucv_message_purge(struct iucv_path * path,struct iucv_message * msg,u32 srccls)10676c005961SUrsula Braun int iucv_message_purge(struct iucv_path *path, struct iucv_message *msg,
106870cf5035SChristoph Lameter 		       u32 srccls)
10692356f4cbSMartin Schwidefsky {
10702356f4cbSMartin Schwidefsky 	union iucv_param *parm;
10712356f4cbSMartin Schwidefsky 	int rc;
10722356f4cbSMartin Schwidefsky 
10732356f4cbSMartin Schwidefsky 	local_bh_disable();
10742356f4cbSMartin Schwidefsky 	if (cpumask_empty(&iucv_buffer_cpumask)) {
10752356f4cbSMartin Schwidefsky 		rc = -EIO;
10762356f4cbSMartin Schwidefsky 		goto out;
10772356f4cbSMartin Schwidefsky 	}
10782356f4cbSMartin Schwidefsky 	parm = iucv_param[smp_processor_id()];
10796c005961SUrsula Braun 	memset(parm, 0, sizeof(union iucv_param));
10802356f4cbSMartin Schwidefsky 	parm->purge.ippathid = path->pathid;
10812356f4cbSMartin Schwidefsky 	parm->purge.ipmsgid = msg->id;
10822356f4cbSMartin Schwidefsky 	parm->purge.ipsrccls = srccls;
1083da99f056SHeiko Carstens 	parm->purge.ipflags1 = IUCV_IPSRCCLS | IUCV_IPFGMID | IUCV_IPFGPID;
10842356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_PURGE, parm);
10852356f4cbSMartin Schwidefsky 	if (!rc) {
108691d5d45eSHendrik Brueckner 		msg->audit = (*(u32 *) &parm->purge.ipaudit) >> 8;
10872356f4cbSMartin Schwidefsky 		msg->tag = parm->purge.ipmsgtag;
10882356f4cbSMartin Schwidefsky 	}
10892356f4cbSMartin Schwidefsky out:
10902356f4cbSMartin Schwidefsky 	local_bh_enable();
10912356f4cbSMartin Schwidefsky 	return rc;
10922356f4cbSMartin Schwidefsky }
10932356f4cbSMartin Schwidefsky EXPORT_SYMBOL(iucv_message_purge);
109491d5d45eSHendrik Brueckner 
109591d5d45eSHendrik Brueckner /**
10962356f4cbSMartin Schwidefsky  * iucv_message_receive_iprmdata
109791d5d45eSHendrik Brueckner  * @path: address of iucv path structure
109891d5d45eSHendrik Brueckner  * @msg: address of iucv msg structure
109991d5d45eSHendrik Brueckner  * @flags: how the message is received (IUCV_IPBUFLST)
110091d5d45eSHendrik Brueckner  * @buffer: address of data buffer or address of struct iucv_array
11012356f4cbSMartin Schwidefsky  * @size: length of data buffer
11022356f4cbSMartin Schwidefsky  * @residual:
11032356f4cbSMartin Schwidefsky  *
11042356f4cbSMartin Schwidefsky  * Internal function used by iucv_message_receive and __iucv_message_receive
11052356f4cbSMartin Schwidefsky  * to receive RMDATA data stored in struct iucv_message.
11062356f4cbSMartin Schwidefsky  */
iucv_message_receive_iprmdata(struct iucv_path * path,struct iucv_message * msg,u8 flags,void * buffer,size_t size,size_t * residual)11072356f4cbSMartin Schwidefsky static int iucv_message_receive_iprmdata(struct iucv_path *path,
11082356f4cbSMartin Schwidefsky 					 struct iucv_message *msg,
11092356f4cbSMartin Schwidefsky 					 u8 flags, void *buffer,
11102356f4cbSMartin Schwidefsky 					 size_t size, size_t *residual)
11112356f4cbSMartin Schwidefsky {
11122356f4cbSMartin Schwidefsky 	struct iucv_array *array;
11132356f4cbSMartin Schwidefsky 	u8 *rmmsg;
11142356f4cbSMartin Schwidefsky 	size_t copy;
11152356f4cbSMartin Schwidefsky 
11162356f4cbSMartin Schwidefsky 	/*
11172356f4cbSMartin Schwidefsky 	 * Message is 8 bytes long and has been stored to the
11182aca9eafSHeiko Carstens 	 * message descriptor itself.
11192356f4cbSMartin Schwidefsky 	 */
11202356f4cbSMartin Schwidefsky 	if (residual)
11212356f4cbSMartin Schwidefsky 		*residual = abs(size - 8);
11222356f4cbSMartin Schwidefsky 	rmmsg = msg->rmmsg;
11232356f4cbSMartin Schwidefsky 	if (flags & IUCV_IPBUFLST) {
11242356f4cbSMartin Schwidefsky 		/* Copy to struct iucv_array. */
11252356f4cbSMartin Schwidefsky 		size = (size < 8) ? size : 8;
11262356f4cbSMartin Schwidefsky 		for (array = buffer; size > 0; array++) {
11272356f4cbSMartin Schwidefsky 			copy = min_t(size_t, size, array->length);
11282356f4cbSMartin Schwidefsky 			memcpy(dma32_to_virt(array->address), rmmsg, copy);
112991d5d45eSHendrik Brueckner 			rmmsg += copy;
113091d5d45eSHendrik Brueckner 			size -= copy;
113191d5d45eSHendrik Brueckner 		}
113291d5d45eSHendrik Brueckner 	} else {
113391d5d45eSHendrik Brueckner 		/* Copy to direct buffer. */
113491d5d45eSHendrik Brueckner 		memcpy(buffer, rmmsg, min_t(size_t, size, 8));
113591d5d45eSHendrik Brueckner 	}
113691d5d45eSHendrik Brueckner 	return 0;
113791d5d45eSHendrik Brueckner }
113891d5d45eSHendrik Brueckner 
113991d5d45eSHendrik Brueckner /**
114091d5d45eSHendrik Brueckner  * __iucv_message_receive
114191d5d45eSHendrik Brueckner  * @path: address of iucv path structure
114291d5d45eSHendrik Brueckner  * @msg: address of iucv msg structure
114391d5d45eSHendrik Brueckner  * @flags: how the message is received (IUCV_IPBUFLST)
114491d5d45eSHendrik Brueckner  * @buffer: address of data buffer or address of struct iucv_array
114591d5d45eSHendrik Brueckner  * @size: length of data buffer
114691d5d45eSHendrik Brueckner  * @residual:
114791d5d45eSHendrik Brueckner  *
114891d5d45eSHendrik Brueckner  * This function receives messages that are being sent to you over
114991d5d45eSHendrik Brueckner  * established paths. This function will deal with RMDATA messages
115091d5d45eSHendrik Brueckner  * embedded in struct iucv_message as well.
115191d5d45eSHendrik Brueckner  *
115291d5d45eSHendrik Brueckner  * Locking:	no locking
115391d5d45eSHendrik Brueckner  *
115491d5d45eSHendrik Brueckner  * Returns the result from the CP IUCV call.
1155a29f245eSJulian Wiedmann  */
__iucv_message_receive(struct iucv_path * path,struct iucv_message * msg,u8 flags,void * buffer,size_t size,size_t * residual)1156a29f245eSJulian Wiedmann int __iucv_message_receive(struct iucv_path *path, struct iucv_message *msg,
1157a29f245eSJulian Wiedmann 			   u8 flags, void *buffer, size_t size, size_t *residual)
115870cf5035SChristoph Lameter {
11592356f4cbSMartin Schwidefsky 	union iucv_param *parm;
11602aca9eafSHeiko Carstens 	int rc;
11612356f4cbSMartin Schwidefsky 
11622356f4cbSMartin Schwidefsky 	if (msg->flags & IUCV_IPRMDATA)
11632356f4cbSMartin Schwidefsky 		return iucv_message_receive_iprmdata(path, msg, flags,
11642356f4cbSMartin Schwidefsky 						     buffer, size, residual);
11652356f4cbSMartin Schwidefsky 	if (cpumask_empty(&iucv_buffer_cpumask))
11662356f4cbSMartin Schwidefsky 		return -EIO;
11672356f4cbSMartin Schwidefsky 
11682356f4cbSMartin Schwidefsky 	parm = iucv_param[smp_processor_id()];
11692356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
11702356f4cbSMartin Schwidefsky 	parm->db.ipbfadr1 = virt_to_dma32(buffer);
11712356f4cbSMartin Schwidefsky 	parm->db.ipbfln1f = (u32) size;
11722356f4cbSMartin Schwidefsky 	parm->db.ipmsgid = msg->id;
117391d5d45eSHendrik Brueckner 	parm->db.ippathid = path->pathid;
117491d5d45eSHendrik Brueckner 	parm->db.iptrgcls = msg->class;
117591d5d45eSHendrik Brueckner 	parm->db.ipflags1 = (flags | IUCV_IPFGPID |
117691d5d45eSHendrik Brueckner 			     IUCV_IPFGMID | IUCV_IPTRGCLS);
117791d5d45eSHendrik Brueckner 	rc = iucv_call_b2f0(IUCV_RECEIVE, parm);
117891d5d45eSHendrik Brueckner 	if (!rc || rc == 5) {
117991d5d45eSHendrik Brueckner 		msg->flags = parm->db.ipflags1;
118091d5d45eSHendrik Brueckner 		if (residual)
118191d5d45eSHendrik Brueckner 			*residual = parm->db.ipbfln1f;
118291d5d45eSHendrik Brueckner 	}
118391d5d45eSHendrik Brueckner 	return rc;
118491d5d45eSHendrik Brueckner }
118591d5d45eSHendrik Brueckner EXPORT_SYMBOL(__iucv_message_receive);
118691d5d45eSHendrik Brueckner 
118791d5d45eSHendrik Brueckner /**
118891d5d45eSHendrik Brueckner  * iucv_message_receive
118991d5d45eSHendrik Brueckner  * @path: address of iucv path structure
119091d5d45eSHendrik Brueckner  * @msg: address of iucv msg structure
119191d5d45eSHendrik Brueckner  * @flags: how the message is received (IUCV_IPBUFLST)
119291d5d45eSHendrik Brueckner  * @buffer: address of data buffer or address of struct iucv_array
119391d5d45eSHendrik Brueckner  * @size: length of data buffer
119491d5d45eSHendrik Brueckner  * @residual:
119591d5d45eSHendrik Brueckner  *
119691d5d45eSHendrik Brueckner  * This function receives messages that are being sent to you over
119791d5d45eSHendrik Brueckner  * established paths. This function will deal with RMDATA messages
119891d5d45eSHendrik Brueckner  * embedded in struct iucv_message as well.
119991d5d45eSHendrik Brueckner  *
120091d5d45eSHendrik Brueckner  * Locking:	local_bh_enable/local_bh_disable
120191d5d45eSHendrik Brueckner  *
120291d5d45eSHendrik Brueckner  * Returns the result from the CP IUCV call.
120391d5d45eSHendrik Brueckner  */
iucv_message_receive(struct iucv_path * path,struct iucv_message * msg,u8 flags,void * buffer,size_t size,size_t * residual)12042356f4cbSMartin Schwidefsky int iucv_message_receive(struct iucv_path *path, struct iucv_message *msg,
12052356f4cbSMartin Schwidefsky 			 u8 flags, void *buffer, size_t size, size_t *residual)
12062356f4cbSMartin Schwidefsky {
1207da99f056SHeiko Carstens 	int rc;
12082356f4cbSMartin Schwidefsky 
12092356f4cbSMartin Schwidefsky 	if (msg->flags & IUCV_IPRMDATA)
12102356f4cbSMartin Schwidefsky 		return iucv_message_receive_iprmdata(path, msg, flags,
12112356f4cbSMartin Schwidefsky 						     buffer, size, residual);
12122356f4cbSMartin Schwidefsky 	local_bh_disable();
12132356f4cbSMartin Schwidefsky 	rc = __iucv_message_receive(path, msg, flags, buffer, size, residual);
12142356f4cbSMartin Schwidefsky 	local_bh_enable();
12152356f4cbSMartin Schwidefsky 	return rc;
12162356f4cbSMartin Schwidefsky }
12172356f4cbSMartin Schwidefsky EXPORT_SYMBOL(iucv_message_receive);
12182356f4cbSMartin Schwidefsky 
12192356f4cbSMartin Schwidefsky /**
12202356f4cbSMartin Schwidefsky  * iucv_message_reject
12212356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
12222356f4cbSMartin Schwidefsky  * @msg: address of iucv msg structure
12232356f4cbSMartin Schwidefsky  *
12242356f4cbSMartin Schwidefsky  * The reject function refuses a specified message. Between the time you
12252356f4cbSMartin Schwidefsky  * are notified of a message and the time that you complete the message,
1226f2019030SKOSAKI Motohiro  * the message may be rejected.
12276c005961SUrsula Braun  *
12286c005961SUrsula Braun  * Returns the result from the CP IUCV call.
12296c005961SUrsula Braun  */
iucv_message_reject(struct iucv_path * path,struct iucv_message * msg)123070cf5035SChristoph Lameter int iucv_message_reject(struct iucv_path *path, struct iucv_message *msg)
12312356f4cbSMartin Schwidefsky {
12322356f4cbSMartin Schwidefsky 	union iucv_param *parm;
12332356f4cbSMartin Schwidefsky 	int rc;
12342356f4cbSMartin Schwidefsky 
12352356f4cbSMartin Schwidefsky 	local_bh_disable();
12362356f4cbSMartin Schwidefsky 	if (cpumask_empty(&iucv_buffer_cpumask)) {
12376c005961SUrsula Braun 		rc = -EIO;
12382356f4cbSMartin Schwidefsky 		goto out;
12392356f4cbSMartin Schwidefsky 	}
12402356f4cbSMartin Schwidefsky 	parm = iucv_param[smp_processor_id()];
1241da99f056SHeiko Carstens 	memset(parm, 0, sizeof(union iucv_param));
12422356f4cbSMartin Schwidefsky 	parm->db.ippathid = path->pathid;
12432356f4cbSMartin Schwidefsky 	parm->db.ipmsgid = msg->id;
12442356f4cbSMartin Schwidefsky 	parm->db.iptrgcls = msg->class;
12452356f4cbSMartin Schwidefsky 	parm->db.ipflags1 = (IUCV_IPTRGCLS | IUCV_IPFGMID | IUCV_IPFGPID);
12462356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_REJECT, parm);
12472356f4cbSMartin Schwidefsky out:
12482356f4cbSMartin Schwidefsky 	local_bh_enable();
12492356f4cbSMartin Schwidefsky 	return rc;
12502356f4cbSMartin Schwidefsky }
12512356f4cbSMartin Schwidefsky EXPORT_SYMBOL(iucv_message_reject);
12522356f4cbSMartin Schwidefsky 
12532356f4cbSMartin Schwidefsky /**
12542356f4cbSMartin Schwidefsky  * iucv_message_reply
12552356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
12562356f4cbSMartin Schwidefsky  * @msg: address of iucv msg structure
12572356f4cbSMartin Schwidefsky  * @flags: how the reply is sent (IUCV_IPRMDATA, IUCV_IPPRTY, IUCV_IPBUFLST)
12582356f4cbSMartin Schwidefsky  * @reply: address of reply data buffer or address of struct iucv_array
12592356f4cbSMartin Schwidefsky  * @size: length of reply data buffer
12602356f4cbSMartin Schwidefsky  *
12612356f4cbSMartin Schwidefsky  * This function responds to the two-way messages that you receive. You
12622356f4cbSMartin Schwidefsky  * must identify completely the message to which you wish to reply. ie,
12632356f4cbSMartin Schwidefsky  * pathid, msgid, and trgcls. Prmmsg signifies the data is moved into
12642356f4cbSMartin Schwidefsky  * the parameter list.
1265f2019030SKOSAKI Motohiro  *
12666c005961SUrsula Braun  * Returns the result from the CP IUCV call.
12676c005961SUrsula Braun  */
iucv_message_reply(struct iucv_path * path,struct iucv_message * msg,u8 flags,void * reply,size_t size)12686c005961SUrsula Braun int iucv_message_reply(struct iucv_path *path, struct iucv_message *msg,
126970cf5035SChristoph Lameter 		       u8 flags, void *reply, size_t size)
12702356f4cbSMartin Schwidefsky {
12712356f4cbSMartin Schwidefsky 	union iucv_param *parm;
12722356f4cbSMartin Schwidefsky 	int rc;
12732356f4cbSMartin Schwidefsky 
12742356f4cbSMartin Schwidefsky 	local_bh_disable();
12752356f4cbSMartin Schwidefsky 	if (cpumask_empty(&iucv_buffer_cpumask)) {
12762356f4cbSMartin Schwidefsky 		rc = -EIO;
12772356f4cbSMartin Schwidefsky 		goto out;
12782aca9eafSHeiko Carstens 	}
12792356f4cbSMartin Schwidefsky 	parm = iucv_param[smp_processor_id()];
12802356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
12812356f4cbSMartin Schwidefsky 	if (flags & IUCV_IPRMDATA) {
12822356f4cbSMartin Schwidefsky 		parm->dpl.ippathid = path->pathid;
12832356f4cbSMartin Schwidefsky 		parm->dpl.ipflags1 = flags;
12842356f4cbSMartin Schwidefsky 		parm->dpl.ipmsgid = msg->id;
12852356f4cbSMartin Schwidefsky 		parm->dpl.iptrgcls = msg->class;
12866c005961SUrsula Braun 		memcpy(parm->dpl.iprmmsg, reply, min_t(size_t, size, 8));
12872356f4cbSMartin Schwidefsky 	} else {
12882356f4cbSMartin Schwidefsky 		parm->db.ipbfadr1 = virt_to_dma32(reply);
12892356f4cbSMartin Schwidefsky 		parm->db.ipbfln1f = (u32) size;
1290da99f056SHeiko Carstens 		parm->db.ippathid = path->pathid;
12912356f4cbSMartin Schwidefsky 		parm->db.ipflags1 = flags;
12922356f4cbSMartin Schwidefsky 		parm->db.ipmsgid = msg->id;
129391d5d45eSHendrik Brueckner 		parm->db.iptrgcls = msg->class;
12942356f4cbSMartin Schwidefsky 	}
12952356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_REPLY, parm);
12962356f4cbSMartin Schwidefsky out:
12972356f4cbSMartin Schwidefsky 	local_bh_enable();
12982356f4cbSMartin Schwidefsky 	return rc;
12992356f4cbSMartin Schwidefsky }
13002356f4cbSMartin Schwidefsky EXPORT_SYMBOL(iucv_message_reply);
13012356f4cbSMartin Schwidefsky 
13022356f4cbSMartin Schwidefsky /**
13032356f4cbSMartin Schwidefsky  * __iucv_message_send
13042356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
130591d5d45eSHendrik Brueckner  * @msg: address of iucv msg structure
130691d5d45eSHendrik Brueckner  * @flags: how the message is sent (IUCV_IPRMDATA, IUCV_IPPRTY, IUCV_IPBUFLST)
13072356f4cbSMartin Schwidefsky  * @srccls: source class of message
13082356f4cbSMartin Schwidefsky  * @buffer: address of send buffer or address of struct iucv_array
130991d5d45eSHendrik Brueckner  * @size: length of send buffer
13102356f4cbSMartin Schwidefsky  *
13112356f4cbSMartin Schwidefsky  * This function transmits data to another application. Data to be
13122356f4cbSMartin Schwidefsky  * transmitted is in a buffer and this is a one-way message and the
13132356f4cbSMartin Schwidefsky  * receiver will not reply to the message.
13142356f4cbSMartin Schwidefsky  *
1315f2019030SKOSAKI Motohiro  * Locking:	no locking
13166c005961SUrsula Braun  *
13176c005961SUrsula Braun  * Returns the result from the CP IUCV call.
13186c005961SUrsula Braun  */
__iucv_message_send(struct iucv_path * path,struct iucv_message * msg,u8 flags,u32 srccls,void * buffer,size_t size)131970cf5035SChristoph Lameter int __iucv_message_send(struct iucv_path *path, struct iucv_message *msg,
13202356f4cbSMartin Schwidefsky 		      u8 flags, u32 srccls, void *buffer, size_t size)
13212356f4cbSMartin Schwidefsky {
13222356f4cbSMartin Schwidefsky 	union iucv_param *parm;
13232356f4cbSMartin Schwidefsky 	int rc;
13242356f4cbSMartin Schwidefsky 
13252356f4cbSMartin Schwidefsky 	if (cpumask_empty(&iucv_buffer_cpumask)) {
13262356f4cbSMartin Schwidefsky 		rc = -EIO;
13272356f4cbSMartin Schwidefsky 		goto out;
13282356f4cbSMartin Schwidefsky 	}
13292356f4cbSMartin Schwidefsky 	parm = iucv_param[smp_processor_id()];
13302aca9eafSHeiko Carstens 	memset(parm, 0, sizeof(union iucv_param));
13312356f4cbSMartin Schwidefsky 	if (flags & IUCV_IPRMDATA) {
13322356f4cbSMartin Schwidefsky 		/* Message of 8 bytes can be placed into the parameter list. */
13332356f4cbSMartin Schwidefsky 		parm->dpl.ippathid = path->pathid;
13342356f4cbSMartin Schwidefsky 		parm->dpl.ipflags1 = flags | IUCV_IPNORPY;
13352356f4cbSMartin Schwidefsky 		parm->dpl.iptrgcls = msg->class;
13362356f4cbSMartin Schwidefsky 		parm->dpl.ipsrccls = srccls;
13372356f4cbSMartin Schwidefsky 		parm->dpl.ipmsgtag = msg->tag;
13382356f4cbSMartin Schwidefsky 		memcpy(parm->dpl.iprmmsg, buffer, 8);
13392356f4cbSMartin Schwidefsky 	} else {
13402356f4cbSMartin Schwidefsky 		parm->db.ipbfadr1 = virt_to_dma32(buffer);
13416c005961SUrsula Braun 		parm->db.ipbfln1f = (u32) size;
134291d5d45eSHendrik Brueckner 		parm->db.ippathid = path->pathid;
134391d5d45eSHendrik Brueckner 		parm->db.ipflags1 = flags | IUCV_IPNORPY;
134491d5d45eSHendrik Brueckner 		parm->db.iptrgcls = msg->class;
134591d5d45eSHendrik Brueckner 		parm->db.ipsrccls = srccls;
134691d5d45eSHendrik Brueckner 		parm->db.ipmsgtag = msg->tag;
134791d5d45eSHendrik Brueckner 	}
134891d5d45eSHendrik Brueckner 	rc = iucv_call_b2f0(IUCV_SEND, parm);
134991d5d45eSHendrik Brueckner 	if (!rc)
135091d5d45eSHendrik Brueckner 		msg->id = parm->db.ipmsgid;
135191d5d45eSHendrik Brueckner out:
135291d5d45eSHendrik Brueckner 	return rc;
135391d5d45eSHendrik Brueckner }
135491d5d45eSHendrik Brueckner EXPORT_SYMBOL(__iucv_message_send);
135591d5d45eSHendrik Brueckner 
135691d5d45eSHendrik Brueckner /**
135791d5d45eSHendrik Brueckner  * iucv_message_send
135891d5d45eSHendrik Brueckner  * @path: address of iucv path structure
135991d5d45eSHendrik Brueckner  * @msg: address of iucv msg structure
136091d5d45eSHendrik Brueckner  * @flags: how the message is sent (IUCV_IPRMDATA, IUCV_IPPRTY, IUCV_IPBUFLST)
136191d5d45eSHendrik Brueckner  * @srccls: source class of message
136291d5d45eSHendrik Brueckner  * @buffer: address of send buffer or address of struct iucv_array
136391d5d45eSHendrik Brueckner  * @size: length of send buffer
136491d5d45eSHendrik Brueckner  *
136591d5d45eSHendrik Brueckner  * This function transmits data to another application. Data to be
136691d5d45eSHendrik Brueckner  * transmitted is in a buffer and this is a one-way message and the
136791d5d45eSHendrik Brueckner  * receiver will not reply to the message.
136891d5d45eSHendrik Brueckner  *
136991d5d45eSHendrik Brueckner  * Locking:	local_bh_enable/local_bh_disable
13702356f4cbSMartin Schwidefsky  *
13712356f4cbSMartin Schwidefsky  * Returns the result from the CP IUCV call.
13722356f4cbSMartin Schwidefsky  */
iucv_message_send(struct iucv_path * path,struct iucv_message * msg,u8 flags,u32 srccls,void * buffer,size_t size)1373da99f056SHeiko Carstens int iucv_message_send(struct iucv_path *path, struct iucv_message *msg,
13742356f4cbSMartin Schwidefsky 		      u8 flags, u32 srccls, void *buffer, size_t size)
13752356f4cbSMartin Schwidefsky {
13762356f4cbSMartin Schwidefsky 	int rc;
13772356f4cbSMartin Schwidefsky 
13782356f4cbSMartin Schwidefsky 	local_bh_disable();
13792356f4cbSMartin Schwidefsky 	rc = __iucv_message_send(path, msg, flags, srccls, buffer, size);
13802356f4cbSMartin Schwidefsky 	local_bh_enable();
13812356f4cbSMartin Schwidefsky 	return rc;
13822356f4cbSMartin Schwidefsky }
13832356f4cbSMartin Schwidefsky EXPORT_SYMBOL(iucv_message_send);
1384682026a5SHeiko Carstens 
13852356f4cbSMartin Schwidefsky /**
1386682026a5SHeiko Carstens  * iucv_message_send2way
13872356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
13882356f4cbSMartin Schwidefsky  * @msg: address of iucv msg structure
13892356f4cbSMartin Schwidefsky  * @flags: how the message is sent and the reply is received
13902356f4cbSMartin Schwidefsky  *	   (IUCV_IPRMDATA, IUCV_IPBUFLST, IUCV_IPPRTY, IUCV_ANSLST)
13912356f4cbSMartin Schwidefsky  * @srccls: source class of message
13922356f4cbSMartin Schwidefsky  * @buffer: address of send buffer or address of struct iucv_array
13932356f4cbSMartin Schwidefsky  * @size: length of send buffer
13942356f4cbSMartin Schwidefsky  * @answer: address of answer buffer or address of struct iucv_array
13952356f4cbSMartin Schwidefsky  * @asize: size of reply buffer
13962356f4cbSMartin Schwidefsky  * @residual: ignored
13972356f4cbSMartin Schwidefsky  *
13982356f4cbSMartin Schwidefsky  * This function transmits data to another application. Data to be
13992356f4cbSMartin Schwidefsky  * transmitted is in a buffer. The receiver of the send is expected to
14002356f4cbSMartin Schwidefsky  * reply to the message and a buffer is provided into which IUCV moves
14012356f4cbSMartin Schwidefsky  * the reply to this message.
14022356f4cbSMartin Schwidefsky  *
1403f2019030SKOSAKI Motohiro  * Returns the result from the CP IUCV call.
14046c005961SUrsula Braun  */
iucv_message_send2way(struct iucv_path * path,struct iucv_message * msg,u8 flags,u32 srccls,void * buffer,size_t size,void * answer,size_t asize,size_t * residual)14056c005961SUrsula Braun int iucv_message_send2way(struct iucv_path *path, struct iucv_message *msg,
14066c005961SUrsula Braun 			  u8 flags, u32 srccls, void *buffer, size_t size,
140770cf5035SChristoph Lameter 			  void *answer, size_t asize, size_t *residual)
14082356f4cbSMartin Schwidefsky {
14092356f4cbSMartin Schwidefsky 	union iucv_param *parm;
14102356f4cbSMartin Schwidefsky 	int rc;
14112356f4cbSMartin Schwidefsky 
14122356f4cbSMartin Schwidefsky 	local_bh_disable();
14132356f4cbSMartin Schwidefsky 	if (cpumask_empty(&iucv_buffer_cpumask)) {
14142356f4cbSMartin Schwidefsky 		rc = -EIO;
14152aca9eafSHeiko Carstens 		goto out;
14162356f4cbSMartin Schwidefsky 	}
14172356f4cbSMartin Schwidefsky 	parm = iucv_param[smp_processor_id()];
14182356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
14192356f4cbSMartin Schwidefsky 	if (flags & IUCV_IPRMDATA) {
14202356f4cbSMartin Schwidefsky 		parm->dpl.ippathid = path->pathid;
14212356f4cbSMartin Schwidefsky 		parm->dpl.ipflags1 = path->flags;	/* priority message */
14222356f4cbSMartin Schwidefsky 		parm->dpl.iptrgcls = msg->class;
14232356f4cbSMartin Schwidefsky 		parm->dpl.ipsrccls = srccls;
14242aca9eafSHeiko Carstens 		parm->dpl.ipmsgtag = msg->tag;
14252356f4cbSMartin Schwidefsky 		parm->dpl.ipbfadr2 = virt_to_dma32(answer);
14262aca9eafSHeiko Carstens 		parm->dpl.ipbfln2f = (u32) asize;
14272356f4cbSMartin Schwidefsky 		memcpy(parm->dpl.iprmmsg, buffer, 8);
14282356f4cbSMartin Schwidefsky 	} else {
14292356f4cbSMartin Schwidefsky 		parm->db.ippathid = path->pathid;
14302356f4cbSMartin Schwidefsky 		parm->db.ipflags1 = path->flags;	/* priority message */
14312356f4cbSMartin Schwidefsky 		parm->db.iptrgcls = msg->class;
14326c005961SUrsula Braun 		parm->db.ipsrccls = srccls;
14332356f4cbSMartin Schwidefsky 		parm->db.ipmsgtag = msg->tag;
14342356f4cbSMartin Schwidefsky 		parm->db.ipbfadr1 = virt_to_dma32(buffer);
14352356f4cbSMartin Schwidefsky 		parm->db.ipbfln1f = (u32) size;
1436da99f056SHeiko Carstens 		parm->db.ipbfadr2 = virt_to_dma32(answer);
14372356f4cbSMartin Schwidefsky 		parm->db.ipbfln2f = (u32) asize;
14382356f4cbSMartin Schwidefsky 	}
14392356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_SEND, parm);
14402356f4cbSMartin Schwidefsky 	if (!rc)
14412356f4cbSMartin Schwidefsky 		msg->id = parm->db.ipmsgid;
14422356f4cbSMartin Schwidefsky out:
14432356f4cbSMartin Schwidefsky 	local_bh_enable();
14442356f4cbSMartin Schwidefsky 	return rc;
14452356f4cbSMartin Schwidefsky }
14462356f4cbSMartin Schwidefsky EXPORT_SYMBOL(iucv_message_send2way);
14472356f4cbSMartin Schwidefsky 
14482356f4cbSMartin Schwidefsky struct iucv_path_pending {
1449bc10502dSEric Dumazet 	u16 ippathid;
14502356f4cbSMartin Schwidefsky 	u8  ipflags1;
1451682026a5SHeiko Carstens 	u8  iptype;
1452682026a5SHeiko Carstens 	u16 ipmsglim;
1453682026a5SHeiko Carstens 	u16 res1;
1454682026a5SHeiko Carstens 	u8  ipvmid[8];
1455682026a5SHeiko Carstens 	u8  ipuser[16];
1456682026a5SHeiko Carstens 	u32 res3;
1457682026a5SHeiko Carstens 	u8  ippollfg;
14582356f4cbSMartin Schwidefsky 	u8  res4[3];
14592356f4cbSMartin Schwidefsky } __packed;
14602356f4cbSMartin Schwidefsky 
14612356f4cbSMartin Schwidefsky /**
14622356f4cbSMartin Schwidefsky  * iucv_path_pending
14632356f4cbSMartin Schwidefsky  * @data: Pointer to external interrupt buffer
14642356f4cbSMartin Schwidefsky  *
14652356f4cbSMartin Schwidefsky  * Process connection pending work item. Called from tasklet while holding
14662356f4cbSMartin Schwidefsky  * iucv_table_lock.
14672356f4cbSMartin Schwidefsky  */
iucv_path_pending(struct iucv_irq_data * data)14682356f4cbSMartin Schwidefsky static void iucv_path_pending(struct iucv_irq_data *data)
14692356f4cbSMartin Schwidefsky {
14702356f4cbSMartin Schwidefsky 	struct iucv_path_pending *ipp = (void *) data;
14712356f4cbSMartin Schwidefsky 	struct iucv_handler *handler;
14722356f4cbSMartin Schwidefsky 	struct iucv_path *path;
14732356f4cbSMartin Schwidefsky 	char *error;
14742356f4cbSMartin Schwidefsky 
14752356f4cbSMartin Schwidefsky 	BUG_ON(iucv_path_table[ipp->ippathid]);
14762356f4cbSMartin Schwidefsky 	/* New pathid, handler found. Create a new path struct. */
14772356f4cbSMartin Schwidefsky 	error = iucv_error_no_memory;
14782356f4cbSMartin Schwidefsky 	path = iucv_path_alloc(ipp->ipmsglim, ipp->ipflags1, GFP_ATOMIC);
14792356f4cbSMartin Schwidefsky 	if (!path)
14802356f4cbSMartin Schwidefsky 		goto out_sever;
14812356f4cbSMartin Schwidefsky 	path->pathid = ipp->ippathid;
14822356f4cbSMartin Schwidefsky 	iucv_path_table[path->pathid] = path;
14832356f4cbSMartin Schwidefsky 	EBCASC(ipp->ipvmid, 8);
14842356f4cbSMartin Schwidefsky 
14852356f4cbSMartin Schwidefsky 	/* Call registered handler until one is found that wants the path. */
14862356f4cbSMartin Schwidefsky 	list_for_each_entry(handler, &iucv_handler_list, list) {
14872356f4cbSMartin Schwidefsky 		if (!handler->path_pending)
14882356f4cbSMartin Schwidefsky 			continue;
14892356f4cbSMartin Schwidefsky 		/*
14902356f4cbSMartin Schwidefsky 		 * Add path to handler to allow a call to iucv_path_sever
14912356f4cbSMartin Schwidefsky 		 * inside the path_pending function. If the handler returns
14922356f4cbSMartin Schwidefsky 		 * an error remove the path from the handler again.
14932356f4cbSMartin Schwidefsky 		 */
14942356f4cbSMartin Schwidefsky 		list_add(&path->list, &handler->paths);
14952356f4cbSMartin Schwidefsky 		path->handler = handler;
14962356f4cbSMartin Schwidefsky 		if (!handler->path_pending(path, ipp->ipvmid, ipp->ipuser))
14972356f4cbSMartin Schwidefsky 			return;
14982356f4cbSMartin Schwidefsky 		list_del(&path->list);
14992356f4cbSMartin Schwidefsky 		path->handler = NULL;
15002356f4cbSMartin Schwidefsky 	}
15012356f4cbSMartin Schwidefsky 	/* No handler wanted the path. */
15022356f4cbSMartin Schwidefsky 	iucv_path_table[path->pathid] = NULL;
15032356f4cbSMartin Schwidefsky 	iucv_path_free(path);
15042356f4cbSMartin Schwidefsky 	error = iucv_error_no_listener;
15052356f4cbSMartin Schwidefsky out_sever:
15062356f4cbSMartin Schwidefsky 	iucv_sever_pathid(ipp->ippathid, error);
15072356f4cbSMartin Schwidefsky }
15082356f4cbSMartin Schwidefsky 
15092356f4cbSMartin Schwidefsky struct iucv_path_complete {
1510bc10502dSEric Dumazet 	u16 ippathid;
15112356f4cbSMartin Schwidefsky 	u8  ipflags1;
1512682026a5SHeiko Carstens 	u8  iptype;
1513682026a5SHeiko Carstens 	u16 ipmsglim;
1514682026a5SHeiko Carstens 	u16 res1;
1515682026a5SHeiko Carstens 	u8  res2[8];
1516682026a5SHeiko Carstens 	u8  ipuser[16];
1517682026a5SHeiko Carstens 	u32 res3;
1518682026a5SHeiko Carstens 	u8  ippollfg;
15192356f4cbSMartin Schwidefsky 	u8  res4[3];
15202356f4cbSMartin Schwidefsky } __packed;
15212356f4cbSMartin Schwidefsky 
15222356f4cbSMartin Schwidefsky /**
15232356f4cbSMartin Schwidefsky  * iucv_path_complete
1524b8942e3bSHendrik Brueckner  * @data: Pointer to external interrupt buffer
1525b8942e3bSHendrik Brueckner  *
152604b090d5SMartin Schwidefsky  * Process connection complete work item. Called from tasklet while holding
15272356f4cbSMartin Schwidefsky  * iucv_table_lock.
15282356f4cbSMartin Schwidefsky  */
iucv_path_complete(struct iucv_irq_data * data)15292356f4cbSMartin Schwidefsky static void iucv_path_complete(struct iucv_irq_data *data)
15302356f4cbSMartin Schwidefsky {
15312356f4cbSMartin Schwidefsky 	struct iucv_path_complete *ipc = (void *) data;
15322356f4cbSMartin Schwidefsky 	struct iucv_path *path = iucv_path_table[ipc->ippathid];
15332356f4cbSMartin Schwidefsky 
15342356f4cbSMartin Schwidefsky 	if (path)
15352356f4cbSMartin Schwidefsky 		path->flags = ipc->ipflags1;
15362356f4cbSMartin Schwidefsky 	if (path && path->handler && path->handler->path_complete)
15372356f4cbSMartin Schwidefsky 		path->handler->path_complete(path, ipc->ipuser);
15382356f4cbSMartin Schwidefsky }
15392356f4cbSMartin Schwidefsky 
1540bc10502dSEric Dumazet struct iucv_path_severed {
15412356f4cbSMartin Schwidefsky 	u16 ippathid;
1542682026a5SHeiko Carstens 	u8  res1;
1543682026a5SHeiko Carstens 	u8  iptype;
1544682026a5SHeiko Carstens 	u32 res2;
1545682026a5SHeiko Carstens 	u8  res3[8];
1546682026a5SHeiko Carstens 	u8  ipuser[16];
1547682026a5SHeiko Carstens 	u32 res4;
1548682026a5SHeiko Carstens 	u8  ippollfg;
15492356f4cbSMartin Schwidefsky 	u8  res5[3];
15502356f4cbSMartin Schwidefsky } __packed;
15512356f4cbSMartin Schwidefsky 
15522356f4cbSMartin Schwidefsky /**
15532356f4cbSMartin Schwidefsky  * iucv_path_severed
155404b090d5SMartin Schwidefsky  * @data: Pointer to external interrupt buffer
155504b090d5SMartin Schwidefsky  *
15562356f4cbSMartin Schwidefsky  * Process connection severed work item. Called from tasklet while holding
15572356f4cbSMartin Schwidefsky  * iucv_table_lock.
15582356f4cbSMartin Schwidefsky  */
iucv_path_severed(struct iucv_irq_data * data)15592356f4cbSMartin Schwidefsky static void iucv_path_severed(struct iucv_irq_data *data)
15602356f4cbSMartin Schwidefsky {
156142e1b4c2SUrsula Braun 	struct iucv_path_severed *ips = (void *) data;
15622356f4cbSMartin Schwidefsky 	struct iucv_path *path = iucv_path_table[ips->ippathid];
15632356f4cbSMartin Schwidefsky 
15642356f4cbSMartin Schwidefsky 	if (!path || !path->handler)	/* Already severed */
15652356f4cbSMartin Schwidefsky 		return;
15662356f4cbSMartin Schwidefsky 	if (path->handler->path_severed)
15672356f4cbSMartin Schwidefsky 		path->handler->path_severed(path, ips->ipuser);
15682356f4cbSMartin Schwidefsky 	else {
15692356f4cbSMartin Schwidefsky 		iucv_sever_pathid(path->pathid, NULL);
15702356f4cbSMartin Schwidefsky 		iucv_path_table[path->pathid] = NULL;
15712356f4cbSMartin Schwidefsky 		list_del(&path->list);
15722356f4cbSMartin Schwidefsky 		iucv_path_free(path);
15732356f4cbSMartin Schwidefsky 	}
15742356f4cbSMartin Schwidefsky }
15752356f4cbSMartin Schwidefsky 
1576bc10502dSEric Dumazet struct iucv_path_quiesced {
15772356f4cbSMartin Schwidefsky 	u16 ippathid;
1578682026a5SHeiko Carstens 	u8  res1;
1579682026a5SHeiko Carstens 	u8  iptype;
1580682026a5SHeiko Carstens 	u32 res2;
1581682026a5SHeiko Carstens 	u8  res3[8];
1582682026a5SHeiko Carstens 	u8  ipuser[16];
1583682026a5SHeiko Carstens 	u32 res4;
1584682026a5SHeiko Carstens 	u8  ippollfg;
15852356f4cbSMartin Schwidefsky 	u8  res5[3];
15862356f4cbSMartin Schwidefsky } __packed;
15872356f4cbSMartin Schwidefsky 
15882356f4cbSMartin Schwidefsky /**
15892356f4cbSMartin Schwidefsky  * iucv_path_quiesced
159004b090d5SMartin Schwidefsky  * @data: Pointer to external interrupt buffer
15912356f4cbSMartin Schwidefsky  *
15922356f4cbSMartin Schwidefsky  * Process connection quiesced work item. Called from tasklet while holding
15932356f4cbSMartin Schwidefsky  * iucv_table_lock.
15942356f4cbSMartin Schwidefsky  */
iucv_path_quiesced(struct iucv_irq_data * data)15952356f4cbSMartin Schwidefsky static void iucv_path_quiesced(struct iucv_irq_data *data)
15962356f4cbSMartin Schwidefsky {
15972356f4cbSMartin Schwidefsky 	struct iucv_path_quiesced *ipq = (void *) data;
15982356f4cbSMartin Schwidefsky 	struct iucv_path *path = iucv_path_table[ipq->ippathid];
15992356f4cbSMartin Schwidefsky 
16002356f4cbSMartin Schwidefsky 	if (path && path->handler && path->handler->path_quiesced)
16012356f4cbSMartin Schwidefsky 		path->handler->path_quiesced(path, ipq->ipuser);
16022356f4cbSMartin Schwidefsky }
16032356f4cbSMartin Schwidefsky 
1604bc10502dSEric Dumazet struct iucv_path_resumed {
16052356f4cbSMartin Schwidefsky 	u16 ippathid;
1606682026a5SHeiko Carstens 	u8  res1;
1607682026a5SHeiko Carstens 	u8  iptype;
1608682026a5SHeiko Carstens 	u32 res2;
1609682026a5SHeiko Carstens 	u8  res3[8];
1610682026a5SHeiko Carstens 	u8  ipuser[16];
1611682026a5SHeiko Carstens 	u32 res4;
1612682026a5SHeiko Carstens 	u8  ippollfg;
16132356f4cbSMartin Schwidefsky 	u8  res5[3];
16142356f4cbSMartin Schwidefsky } __packed;
16152356f4cbSMartin Schwidefsky 
16162356f4cbSMartin Schwidefsky /**
16172356f4cbSMartin Schwidefsky  * iucv_path_resumed
161804b090d5SMartin Schwidefsky  * @data: Pointer to external interrupt buffer
16192356f4cbSMartin Schwidefsky  *
16202356f4cbSMartin Schwidefsky  * Process connection resumed work item. Called from tasklet while holding
16212356f4cbSMartin Schwidefsky  * iucv_table_lock.
16222356f4cbSMartin Schwidefsky  */
iucv_path_resumed(struct iucv_irq_data * data)16232356f4cbSMartin Schwidefsky static void iucv_path_resumed(struct iucv_irq_data *data)
16242356f4cbSMartin Schwidefsky {
16252356f4cbSMartin Schwidefsky 	struct iucv_path_resumed *ipr = (void *) data;
16262356f4cbSMartin Schwidefsky 	struct iucv_path *path = iucv_path_table[ipr->ippathid];
16272356f4cbSMartin Schwidefsky 
16282356f4cbSMartin Schwidefsky 	if (path && path->handler && path->handler->path_resumed)
16292356f4cbSMartin Schwidefsky 		path->handler->path_resumed(path, ipr->ipuser);
16302356f4cbSMartin Schwidefsky }
16312356f4cbSMartin Schwidefsky 
16322356f4cbSMartin Schwidefsky struct iucv_message_complete {
16332356f4cbSMartin Schwidefsky 	u16 ippathid;
16342356f4cbSMartin Schwidefsky 	u8  ipflags1;
1635bc10502dSEric Dumazet 	u8  iptype;
16362356f4cbSMartin Schwidefsky 	u32 ipmsgid;
1637682026a5SHeiko Carstens 	u32 ipaudit;
1638682026a5SHeiko Carstens 	u8  iprmmsg[8];
1639682026a5SHeiko Carstens 	u32 ipsrccls;
1640682026a5SHeiko Carstens 	u32 ipmsgtag;
1641682026a5SHeiko Carstens 	u32 res;
1642682026a5SHeiko Carstens 	u32 ipbfln2f;
1643682026a5SHeiko Carstens 	u8  ippollfg;
16442356f4cbSMartin Schwidefsky 	u8  res2[3];
16452356f4cbSMartin Schwidefsky } __packed;
16462356f4cbSMartin Schwidefsky 
16472356f4cbSMartin Schwidefsky /**
16482356f4cbSMartin Schwidefsky  * iucv_message_complete
16492356f4cbSMartin Schwidefsky  * @data: Pointer to external interrupt buffer
165004b090d5SMartin Schwidefsky  *
16512356f4cbSMartin Schwidefsky  * Process message complete work item. Called from tasklet while holding
16522356f4cbSMartin Schwidefsky  * iucv_table_lock.
16532356f4cbSMartin Schwidefsky  */
iucv_message_complete(struct iucv_irq_data * data)16542356f4cbSMartin Schwidefsky static void iucv_message_complete(struct iucv_irq_data *data)
16552356f4cbSMartin Schwidefsky {
16562356f4cbSMartin Schwidefsky 	struct iucv_message_complete *imc = (void *) data;
16572356f4cbSMartin Schwidefsky 	struct iucv_path *path = iucv_path_table[imc->ippathid];
16582356f4cbSMartin Schwidefsky 	struct iucv_message msg;
16592356f4cbSMartin Schwidefsky 
16602356f4cbSMartin Schwidefsky 	if (path && path->handler && path->handler->message_complete) {
16612356f4cbSMartin Schwidefsky 		msg.flags = imc->ipflags1;
16622356f4cbSMartin Schwidefsky 		msg.id = imc->ipmsgid;
16632356f4cbSMartin Schwidefsky 		msg.audit = imc->ipaudit;
16642356f4cbSMartin Schwidefsky 		memcpy(msg.rmmsg, imc->iprmmsg, 8);
16652356f4cbSMartin Schwidefsky 		msg.class = imc->ipsrccls;
16662356f4cbSMartin Schwidefsky 		msg.tag = imc->ipmsgtag;
16672356f4cbSMartin Schwidefsky 		msg.length = imc->ipbfln2f;
16685140aaa4SKees Cook 		path->handler->message_complete(path, &msg);
16692356f4cbSMartin Schwidefsky 	}
16702356f4cbSMartin Schwidefsky }
16712356f4cbSMartin Schwidefsky 
16722356f4cbSMartin Schwidefsky struct iucv_message_pending {
16732356f4cbSMartin Schwidefsky 	u16 ippathid;
16742356f4cbSMartin Schwidefsky 	u8  ipflags1;
16752356f4cbSMartin Schwidefsky 	u8  iptype;
16762356f4cbSMartin Schwidefsky 	u32 ipmsgid;
16775140aaa4SKees Cook 	u32 iptrgcls;
16782356f4cbSMartin Schwidefsky 	struct {
16792356f4cbSMartin Schwidefsky 		union {
16802356f4cbSMartin Schwidefsky 			u32 iprmmsg1_u32;
16812356f4cbSMartin Schwidefsky 			u8  iprmmsg1[4];
1682bc10502dSEric Dumazet 		} ln1msg1;
16832356f4cbSMartin Schwidefsky 		union {
1684682026a5SHeiko Carstens 			u32 ipbfln1f;
1685682026a5SHeiko Carstens 			u8  iprmmsg2[4];
1686682026a5SHeiko Carstens 		} ln1msg2;
1687682026a5SHeiko Carstens 	} rmmsg;
1688682026a5SHeiko Carstens 	u32 res1[3];
1689682026a5SHeiko Carstens 	u32 ipbfln2f;
1690682026a5SHeiko Carstens 	u8  ippollfg;
16912356f4cbSMartin Schwidefsky 	u8  res2[3];
16922356f4cbSMartin Schwidefsky } __packed;
16932356f4cbSMartin Schwidefsky 
16942356f4cbSMartin Schwidefsky /**
16952356f4cbSMartin Schwidefsky  * iucv_message_pending
16962356f4cbSMartin Schwidefsky  * @data: Pointer to external interrupt buffer
169704b090d5SMartin Schwidefsky  *
16982356f4cbSMartin Schwidefsky  * Process message pending work item. Called from tasklet while holding
16992356f4cbSMartin Schwidefsky  * iucv_table_lock.
17002356f4cbSMartin Schwidefsky  */
iucv_message_pending(struct iucv_irq_data * data)17012356f4cbSMartin Schwidefsky static void iucv_message_pending(struct iucv_irq_data *data)
17025140aaa4SKees Cook {
17032356f4cbSMartin Schwidefsky 	struct iucv_message_pending *imp = (void *) data;
17042356f4cbSMartin Schwidefsky 	struct iucv_path *path = iucv_path_table[imp->ippathid];
17055140aaa4SKees Cook 	struct iucv_message msg;
17062356f4cbSMartin Schwidefsky 
17072356f4cbSMartin Schwidefsky 	if (path && path->handler && path->handler->message_pending) {
17082356f4cbSMartin Schwidefsky 		msg.flags = imp->ipflags1;
17092356f4cbSMartin Schwidefsky 		msg.id = imp->ipmsgid;
17102356f4cbSMartin Schwidefsky 		msg.class = imp->iptrgcls;
1711682026a5SHeiko Carstens 		if (imp->ipflags1 & IUCV_IPRMDATA) {
171204b090d5SMartin Schwidefsky 			memcpy(msg.rmmsg, &imp->rmmsg, 8);
17132356f4cbSMartin Schwidefsky 			msg.length = 8;
17142356f4cbSMartin Schwidefsky 		} else
17152356f4cbSMartin Schwidefsky 			msg.length = imp->rmmsg.ln1msg2.ipbfln1f;
17162356f4cbSMartin Schwidefsky 		msg.reply_size = imp->ipbfln2f;
17172356f4cbSMartin Schwidefsky 		path->handler->message_pending(path, &msg);
171804b090d5SMartin Schwidefsky 	}
17192356f4cbSMartin Schwidefsky }
17202356f4cbSMartin Schwidefsky 
17212356f4cbSMartin Schwidefsky /*
17222356f4cbSMartin Schwidefsky  * iucv_tasklet_fn:
17232356f4cbSMartin Schwidefsky  *
17242356f4cbSMartin Schwidefsky  * This tasklet loops over the queue of irq buffers created by
17252356f4cbSMartin Schwidefsky  * iucv_external_interrupt, calls the appropriate action handler
17262356f4cbSMartin Schwidefsky  * and then frees the buffer.
17272356f4cbSMartin Schwidefsky  */
iucv_tasklet_fn(unsigned long ignored)17282356f4cbSMartin Schwidefsky static void iucv_tasklet_fn(unsigned long ignored)
17292356f4cbSMartin Schwidefsky {
17302356f4cbSMartin Schwidefsky 	typedef void iucv_irq_fn(struct iucv_irq_data *);
1731b5e78337SDenis Cheng 	static iucv_irq_fn *irq_fn[] = {
173204b090d5SMartin Schwidefsky 		[0x02] = iucv_path_complete,
17332356f4cbSMartin Schwidefsky 		[0x03] = iucv_path_severed,
17342356f4cbSMartin Schwidefsky 		[0x04] = iucv_path_quiesced,
173513fdc9a7SUrsula Braun 		[0x05] = iucv_path_resumed,
173613fdc9a7SUrsula Braun 		[0x06] = iucv_message_complete,
173713fdc9a7SUrsula Braun 		[0x07] = iucv_message_complete,
173813fdc9a7SUrsula Braun 		[0x08] = iucv_message_pending,
173904b090d5SMartin Schwidefsky 		[0x09] = iucv_message_pending,
17402356f4cbSMartin Schwidefsky 	};
174104b090d5SMartin Schwidefsky 	LIST_HEAD(task_queue);
174204b090d5SMartin Schwidefsky 	struct iucv_irq_list *p, *n;
174304b090d5SMartin Schwidefsky 
174404b090d5SMartin Schwidefsky 	/* Serialize tasklet, iucv_path_sever and iucv_path_connect. */
174504b090d5SMartin Schwidefsky 	if (!spin_trylock(&iucv_table_lock)) {
17462356f4cbSMartin Schwidefsky 		tasklet_schedule(&iucv_tasklet);
17472356f4cbSMartin Schwidefsky 		return;
17482356f4cbSMartin Schwidefsky 	}
17492356f4cbSMartin Schwidefsky 	iucv_active_cpu = smp_processor_id();
17502356f4cbSMartin Schwidefsky 
175104b090d5SMartin Schwidefsky 	spin_lock_irq(&iucv_queue_lock);
17522356f4cbSMartin Schwidefsky 	list_splice_init(&iucv_task_queue, &task_queue);
17532356f4cbSMartin Schwidefsky 	spin_unlock_irq(&iucv_queue_lock);
17542356f4cbSMartin Schwidefsky 
1755682026a5SHeiko Carstens 	list_for_each_entry_safe(p, n, &task_queue, list) {
175604b090d5SMartin Schwidefsky 		list_del_init(&p->list);
175704b090d5SMartin Schwidefsky 		irq_fn[p->data.iptype](&p->data);
175804b090d5SMartin Schwidefsky 		kfree(p);
175904b090d5SMartin Schwidefsky 	}
176004b090d5SMartin Schwidefsky 
176104b090d5SMartin Schwidefsky 	iucv_active_cpu = -1;
176204b090d5SMartin Schwidefsky 	spin_unlock(&iucv_table_lock);
176304b090d5SMartin Schwidefsky }
1764b5e78337SDenis Cheng 
176504b090d5SMartin Schwidefsky /*
176604b090d5SMartin Schwidefsky  * iucv_work_fn:
176704b090d5SMartin Schwidefsky  *
176804b090d5SMartin Schwidefsky  * This work function loops over the queue of path pending irq blocks
176904b090d5SMartin Schwidefsky  * created by iucv_external_interrupt, calls the appropriate action
177004b090d5SMartin Schwidefsky  * handler and then frees the buffer.
177104b090d5SMartin Schwidefsky  */
iucv_work_fn(struct work_struct * work)177204b090d5SMartin Schwidefsky static void iucv_work_fn(struct work_struct *work)
177304b090d5SMartin Schwidefsky {
177404b090d5SMartin Schwidefsky 	LIST_HEAD(work_queue);
177504b090d5SMartin Schwidefsky 	struct iucv_irq_list *p, *n;
177604b090d5SMartin Schwidefsky 
177704b090d5SMartin Schwidefsky 	/* Serialize tasklet, iucv_path_sever and iucv_path_connect. */
177804b090d5SMartin Schwidefsky 	spin_lock_bh(&iucv_table_lock);
177904b090d5SMartin Schwidefsky 	iucv_active_cpu = smp_processor_id();
178004b090d5SMartin Schwidefsky 
178104b090d5SMartin Schwidefsky 	spin_lock_irq(&iucv_queue_lock);
178204b090d5SMartin Schwidefsky 	list_splice_init(&iucv_work_queue, &work_queue);
178304b090d5SMartin Schwidefsky 	spin_unlock_irq(&iucv_queue_lock);
178404b090d5SMartin Schwidefsky 
178504b090d5SMartin Schwidefsky 	iucv_cleanup_queue();
1786682026a5SHeiko Carstens 	list_for_each_entry_safe(p, n, &work_queue, list) {
17872356f4cbSMartin Schwidefsky 		list_del_init(&p->list);
17882356f4cbSMartin Schwidefsky 		iucv_path_pending(&p->data);
17892356f4cbSMartin Schwidefsky 		kfree(p);
179004b090d5SMartin Schwidefsky 	}
17912356f4cbSMartin Schwidefsky 
1792fde15c3aSHeiko Carstens 	iucv_active_cpu = -1;
1793f6649a7eSMartin Schwidefsky 	spin_unlock_bh(&iucv_table_lock);
17942356f4cbSMartin Schwidefsky }
17952356f4cbSMartin Schwidefsky 
179604b090d5SMartin Schwidefsky /*
17972356f4cbSMartin Schwidefsky  * iucv_external_interrupt
1798420f42ecSHeiko Carstens  *
179970cf5035SChristoph Lameter  * Handles external interrupts coming in from CP.
18002356f4cbSMartin Schwidefsky  * Places the interrupt buffer on a queue and schedules iucv_tasklet_fn().
1801c2b4afd2SUrsula Braun  */
iucv_external_interrupt(struct ext_code ext_code,unsigned int param32,unsigned long param64)18022356f4cbSMartin Schwidefsky static void iucv_external_interrupt(struct ext_code ext_code,
18032356f4cbSMartin Schwidefsky 				    unsigned int param32, unsigned long param64)
18042356f4cbSMartin Schwidefsky {
1805c2b4afd2SUrsula Braun 	struct iucv_irq_data *p;
180604b090d5SMartin Schwidefsky 	struct iucv_irq_list *work;
18072356f4cbSMartin Schwidefsky 
180847c4cfc3SJoe Perches 	inc_irq_stat(IRQEXT_IUC);
18092356f4cbSMartin Schwidefsky 	p = iucv_irq_data[smp_processor_id()];
18102356f4cbSMartin Schwidefsky 	if (p->ippathid >= iucv_max_pathid) {
18112356f4cbSMartin Schwidefsky 		WARN_ON(p->ippathid >= iucv_max_pathid);
181204b090d5SMartin Schwidefsky 		iucv_sever_pathid(p->ippathid, iucv_error_no_listener);
181304b090d5SMartin Schwidefsky 		return;
181404b090d5SMartin Schwidefsky 	}
18152356f4cbSMartin Schwidefsky 	BUG_ON(p->iptype  < 0x01 || p->iptype > 0x09);
181604b090d5SMartin Schwidefsky 	work = kmalloc(sizeof(struct iucv_irq_list), GFP_ATOMIC);
181704b090d5SMartin Schwidefsky 	if (!work) {
181804b090d5SMartin Schwidefsky 		pr_warn("iucv_external_interrupt: out of memory\n");
181904b090d5SMartin Schwidefsky 		return;
18202356f4cbSMartin Schwidefsky 	}
18212356f4cbSMartin Schwidefsky 	memcpy(&work->data, p, sizeof(work->data));
182204b090d5SMartin Schwidefsky 	spin_lock(&iucv_queue_lock);
182304b090d5SMartin Schwidefsky 	if (p->iptype == 0x01) {
18242356f4cbSMartin Schwidefsky 		/* Path pending interrupt. */
182596d042a6SFrank Blaschka 		list_add_tail(&work->list, &iucv_work_queue);
182696d042a6SFrank Blaschka 		schedule_work(&iucv_work);
182796d042a6SFrank Blaschka 	} else {
182896d042a6SFrank Blaschka 		/* The other interrupts. */
182996d042a6SFrank Blaschka 		list_add_tail(&work->list, &iucv_task_queue);
183096d042a6SFrank Blaschka 		tasklet_schedule(&iucv_tasklet);
183196d042a6SFrank Blaschka 	}
183296d042a6SFrank Blaschka 	spin_unlock(&iucv_queue_lock);
183396d042a6SFrank Blaschka }
183496d042a6SFrank Blaschka 
183596d042a6SFrank Blaschka struct iucv_interface iucv_if = {
183696d042a6SFrank Blaschka 	.message_receive = iucv_message_receive,
183796d042a6SFrank Blaschka 	.__message_receive = __iucv_message_receive,
183896d042a6SFrank Blaschka 	.message_reply = iucv_message_reply,
183996d042a6SFrank Blaschka 	.message_reject = iucv_message_reject,
184096d042a6SFrank Blaschka 	.message_send = iucv_message_send,
184196d042a6SFrank Blaschka 	.__message_send = __iucv_message_send,
184296d042a6SFrank Blaschka 	.message_send2way = iucv_message_send2way,
184396d042a6SFrank Blaschka 	.message_purge = iucv_message_purge,
184496d042a6SFrank Blaschka 	.path_accept = iucv_path_accept,
184596d042a6SFrank Blaschka 	.path_connect = iucv_path_connect,
184638b48292SSebastian Andrzej Siewior 	.path_quiesce = iucv_path_quiesce,
18472356f4cbSMartin Schwidefsky 	.path_resume = iucv_path_resume,
18482356f4cbSMartin Schwidefsky 	.path_sever = iucv_path_sever,
18492356f4cbSMartin Schwidefsky 	.iucv_register = iucv_register,
18502356f4cbSMartin Schwidefsky 	.iucv_unregister = iucv_unregister,
18512356f4cbSMartin Schwidefsky 	.bus = NULL,
1852da99f056SHeiko Carstens 	.root = NULL,
18532356f4cbSMartin Schwidefsky };
18542356f4cbSMartin Schwidefsky EXPORT_SYMBOL(iucv_if);
18552356f4cbSMartin Schwidefsky 
18562356f4cbSMartin Schwidefsky static enum cpuhp_state iucv_online;
18572356f4cbSMartin Schwidefsky /**
18582356f4cbSMartin Schwidefsky  * iucv_init
18592356f4cbSMartin Schwidefsky  *
186099441a38SHeiko Carstens  * Allocates and initializes various data structures.
18612356f4cbSMartin Schwidefsky  */
iucv_init(void)18622356f4cbSMartin Schwidefsky static int __init iucv_init(void)
18635beab991SMartin Schwidefsky {
18641dad093bSThomas Huth 	int rc;
18652356f4cbSMartin Schwidefsky 
18665beab991SMartin Schwidefsky 	if (!MACHINE_IS_VM) {
1867035da16fSMark McLoughlin 		rc = -EPROTONOSUPPORT;
18682356f4cbSMartin Schwidefsky 		goto out;
18692356f4cbSMartin Schwidefsky 	}
18702d7bf367SCornelia Huck 	system_ctl_set_bit(0, CR0_IUCV_BIT);
18712356f4cbSMartin Schwidefsky 	rc = iucv_query_maxconn();
187270cf5035SChristoph Lameter 	if (rc)
187338b48292SSebastian Andrzej Siewior 		goto out_ctl;
187438b48292SSebastian Andrzej Siewior 	rc = register_external_irq(EXT_IRQ_IUCV, iucv_external_interrupt);
18752d7bf367SCornelia Huck 	if (rc)
18769c6bafabSSebastian Andrzej Siewior 		goto out_ctl;
187738b48292SSebastian Andrzej Siewior 	iucv_root = root_device_register("iucv");
187838b48292SSebastian Andrzej Siewior 	if (IS_ERR(iucv_root)) {
187938b48292SSebastian Andrzej Siewior 		rc = PTR_ERR(iucv_root);
18809c6bafabSSebastian Andrzej Siewior 		goto out_int;
188138b48292SSebastian Andrzej Siewior 	}
1882a0e247a8SSrivatsa S. Bhat 
18836c005961SUrsula Braun 	rc = cpuhp_setup_state(CPUHP_NET_IUCV_PREPARE, "net/iucv:prepare",
18846c005961SUrsula Braun 			       iucv_cpu_prepare, iucv_cpu_dead);
18859c6bafabSSebastian Andrzej Siewior 	if (rc)
18862356f4cbSMartin Schwidefsky 		goto out_dev;
18872356f4cbSMartin Schwidefsky 	rc = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "net/iucv:online",
18882356f4cbSMartin Schwidefsky 			       iucv_cpu_online, iucv_cpu_down_prep);
18892356f4cbSMartin Schwidefsky 	if (rc < 0)
18902d7bf367SCornelia Huck 		goto out_prep;
18912d7bf367SCornelia Huck 	iucv_online = rc;
18926c005961SUrsula Braun 
189396d042a6SFrank Blaschka 	rc = register_reboot_notifier(&iucv_reboot_notifier);
189496d042a6SFrank Blaschka 	if (rc)
18952356f4cbSMartin Schwidefsky 		goto out_remove_hp;
18962356f4cbSMartin Schwidefsky 	ASCEBC(iucv_error_no_listener, 16);
18976c005961SUrsula Braun 	ASCEBC(iucv_error_no_memory, 16);
18986c005961SUrsula Braun 	ASCEBC(iucv_error_pathid, 16);
18999c6bafabSSebastian Andrzej Siewior 	iucv_available = 1;
190038b48292SSebastian Andrzej Siewior 	rc = bus_register(&iucv_bus);
19019c6bafabSSebastian Andrzej Siewior 	if (rc)
190238b48292SSebastian Andrzej Siewior 		goto out_reboot;
19039c6bafabSSebastian Andrzej Siewior 	iucv_if.root = iucv_root;
1904035da16fSMark McLoughlin 	iucv_if.bus = &iucv_bus;
19052356f4cbSMartin Schwidefsky 	return 0;
19061dad093bSThomas Huth 
19075beab991SMartin Schwidefsky out_reboot:
19088d5e98f8SHeiko Carstens 	unregister_reboot_notifier(&iucv_reboot_notifier);
19092356f4cbSMartin Schwidefsky out_remove_hp:
19102356f4cbSMartin Schwidefsky 	cpuhp_remove_state(iucv_online);
19112356f4cbSMartin Schwidefsky out_prep:
19122356f4cbSMartin Schwidefsky 	cpuhp_remove_state(CPUHP_NET_IUCV_PREPARE);
19132356f4cbSMartin Schwidefsky out_dev:
19142356f4cbSMartin Schwidefsky 	root_device_unregister(iucv_root);
19152356f4cbSMartin Schwidefsky out_int:
19162356f4cbSMartin Schwidefsky 	unregister_external_irq(EXT_IRQ_IUCV, iucv_external_interrupt);
19172356f4cbSMartin Schwidefsky out_ctl:
1918da99f056SHeiko Carstens 	system_ctl_clear_bit(0, 1);
19192356f4cbSMartin Schwidefsky out:
192004b090d5SMartin Schwidefsky 	return rc;
19212356f4cbSMartin Schwidefsky }
192204b090d5SMartin Schwidefsky 
192304b090d5SMartin Schwidefsky /**
192404b090d5SMartin Schwidefsky  * iucv_exit
19252356f4cbSMartin Schwidefsky  *
19262356f4cbSMartin Schwidefsky  * Frees everything allocated from iucv_init.
192704b090d5SMartin Schwidefsky  */
iucv_exit(void)19286c005961SUrsula Braun static void __exit iucv_exit(void)
192938b48292SSebastian Andrzej Siewior {
193038b48292SSebastian Andrzej Siewior 	struct iucv_irq_list *p, *n;
193138b48292SSebastian Andrzej Siewior 
1932035da16fSMark McLoughlin 	spin_lock_irq(&iucv_queue_lock);
19332356f4cbSMartin Schwidefsky 	list_for_each_entry_safe(p, n, &iucv_task_queue, list)
19341dad093bSThomas Huth 		kfree(p);
19352356f4cbSMartin Schwidefsky 	list_for_each_entry_safe(p, n, &iucv_work_queue, list)
19362356f4cbSMartin Schwidefsky 		kfree(p);
19372356f4cbSMartin Schwidefsky 	spin_unlock_irq(&iucv_queue_lock);
19382356f4cbSMartin Schwidefsky 	unregister_reboot_notifier(&iucv_reboot_notifier);
19392356f4cbSMartin Schwidefsky 
19406a57a219SAhelenia Ziemiańska 	cpuhp_remove_state_nocalls(iucv_online);
19412356f4cbSMartin Schwidefsky 	cpuhp_remove_state(CPUHP_NET_IUCV_PREPARE);
19422356f4cbSMartin Schwidefsky 	root_device_unregister(iucv_root);
1943 	bus_unregister(&iucv_bus);
1944 	unregister_external_irq(EXT_IRQ_IUCV, iucv_external_interrupt);
1945 }
1946 
1947 subsys_initcall(iucv_init);
1948 module_exit(iucv_exit);
1949 
1950 MODULE_AUTHOR("(C) 2001 IBM Corp. by Fritz Elfert <felfert@millenux.com>");
1951 MODULE_DESCRIPTION("Linux for S/390 IUCV lowlevel driver");
1952 MODULE_LICENSE("GPL");
1953