xref: /openbmc/linux/net/iucv/iucv.c (revision 26d0dfbb16fcb17d128a79dc70f3020ea6992af0)
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 
702356f4cbSMartin Schwidefsky 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 
762356f4cbSMartin Schwidefsky struct device *iucv_root;
77da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_root);
78da99f056SHeiko Carstens 
792356f4cbSMartin Schwidefsky static int iucv_available;
802356f4cbSMartin Schwidefsky 
812356f4cbSMartin Schwidefsky /* General IUCV interrupt structure */
822356f4cbSMartin Schwidefsky struct iucv_irq_data {
832356f4cbSMartin Schwidefsky 	u16 ippathid;
842356f4cbSMartin Schwidefsky 	u8  ipflags1;
852356f4cbSMartin Schwidefsky 	u8  iptype;
863d87debbSAlexandra Winter 	u32 res2[9];
872356f4cbSMartin Schwidefsky };
882356f4cbSMartin Schwidefsky 
8904b090d5SMartin Schwidefsky struct iucv_irq_list {
902356f4cbSMartin Schwidefsky 	struct list_head list;
912356f4cbSMartin Schwidefsky 	struct iucv_irq_data data;
922356f4cbSMartin Schwidefsky };
932356f4cbSMartin Schwidefsky 
9470cf5035SChristoph Lameter static struct iucv_irq_data *iucv_irq_data[NR_CPUS];
95f2019030SKOSAKI Motohiro static cpumask_t iucv_buffer_cpumask = { CPU_BITS_NONE };
96f2019030SKOSAKI Motohiro static cpumask_t iucv_irq_cpumask = { CPU_BITS_NONE };
972356f4cbSMartin Schwidefsky 
9804b090d5SMartin Schwidefsky /*
9904b090d5SMartin Schwidefsky  * Queue of interrupt buffers lock for delivery via the tasklet
10004b090d5SMartin Schwidefsky  * (fast but can't call smp_call_function).
10104b090d5SMartin Schwidefsky  */
10204b090d5SMartin Schwidefsky static LIST_HEAD(iucv_task_queue);
10304b090d5SMartin Schwidefsky 
10404b090d5SMartin Schwidefsky /*
10504b090d5SMartin Schwidefsky  * The tasklet for fast delivery of iucv interrupts.
10604b090d5SMartin Schwidefsky  */
10704b090d5SMartin Schwidefsky static void iucv_tasklet_fn(unsigned long);
108b13fecb1SKees Cook static DECLARE_TASKLET_OLD(iucv_tasklet, iucv_tasklet_fn);
10904b090d5SMartin Schwidefsky 
11004b090d5SMartin Schwidefsky /*
11104b090d5SMartin Schwidefsky  * Queue of interrupt buffers for delivery via a work queue
11204b090d5SMartin Schwidefsky  * (slower but can call smp_call_function).
11304b090d5SMartin Schwidefsky  */
11404b090d5SMartin Schwidefsky static LIST_HEAD(iucv_work_queue);
11504b090d5SMartin Schwidefsky 
11604b090d5SMartin Schwidefsky /*
11704b090d5SMartin Schwidefsky  * The work element to deliver path pending interrupts.
11804b090d5SMartin Schwidefsky  */
11904b090d5SMartin Schwidefsky static void iucv_work_fn(struct work_struct *work);
12004b090d5SMartin Schwidefsky static DECLARE_WORK(iucv_work, iucv_work_fn);
12104b090d5SMartin Schwidefsky 
12204b090d5SMartin Schwidefsky /*
12304b090d5SMartin Schwidefsky  * Spinlock protecting task and work queue.
12404b090d5SMartin Schwidefsky  */
12504b090d5SMartin Schwidefsky static DEFINE_SPINLOCK(iucv_queue_lock);
1262356f4cbSMartin Schwidefsky 
1272356f4cbSMartin Schwidefsky enum iucv_command_codes {
1282356f4cbSMartin Schwidefsky 	IUCV_QUERY = 0,
1292356f4cbSMartin Schwidefsky 	IUCV_RETRIEVE_BUFFER = 2,
1302356f4cbSMartin Schwidefsky 	IUCV_SEND = 4,
1312356f4cbSMartin Schwidefsky 	IUCV_RECEIVE = 5,
1322356f4cbSMartin Schwidefsky 	IUCV_REPLY = 6,
1332356f4cbSMartin Schwidefsky 	IUCV_REJECT = 8,
1342356f4cbSMartin Schwidefsky 	IUCV_PURGE = 9,
1352356f4cbSMartin Schwidefsky 	IUCV_ACCEPT = 10,
1362356f4cbSMartin Schwidefsky 	IUCV_CONNECT = 11,
1372356f4cbSMartin Schwidefsky 	IUCV_DECLARE_BUFFER = 12,
1382356f4cbSMartin Schwidefsky 	IUCV_QUIESCE = 13,
1392356f4cbSMartin Schwidefsky 	IUCV_RESUME = 14,
1402356f4cbSMartin Schwidefsky 	IUCV_SEVER = 15,
1412356f4cbSMartin Schwidefsky 	IUCV_SETMASK = 16,
142672e405bSUrsula Braun 	IUCV_SETCONTROLMASK = 17,
1432356f4cbSMartin Schwidefsky };
1442356f4cbSMartin Schwidefsky 
1452356f4cbSMartin Schwidefsky /*
1462356f4cbSMartin Schwidefsky  * Error messages that are used with the iucv_sever function. They get
1472356f4cbSMartin Schwidefsky  * converted to EBCDIC.
1482356f4cbSMartin Schwidefsky  */
1492356f4cbSMartin Schwidefsky static char iucv_error_no_listener[16] = "NO LISTENER";
1502356f4cbSMartin Schwidefsky static char iucv_error_no_memory[16] = "NO MEMORY";
1512356f4cbSMartin Schwidefsky static char iucv_error_pathid[16] = "INVALID PATHID";
1522356f4cbSMartin Schwidefsky 
1532356f4cbSMartin Schwidefsky /*
1542356f4cbSMartin Schwidefsky  * iucv_handler_list: List of registered handlers.
1552356f4cbSMartin Schwidefsky  */
1562356f4cbSMartin Schwidefsky static LIST_HEAD(iucv_handler_list);
1572356f4cbSMartin Schwidefsky 
1582356f4cbSMartin Schwidefsky /*
15961698b98SAlexander Gordeev  * iucv_path_table: array of pointers to iucv_path structures.
1602356f4cbSMartin Schwidefsky  */
1612356f4cbSMartin Schwidefsky static struct iucv_path **iucv_path_table;
1622356f4cbSMartin Schwidefsky static unsigned long iucv_max_pathid;
1632356f4cbSMartin Schwidefsky 
1642356f4cbSMartin Schwidefsky /*
1652356f4cbSMartin Schwidefsky  * iucv_lock: spinlock protecting iucv_handler_list and iucv_pathid_table
1662356f4cbSMartin Schwidefsky  */
1672356f4cbSMartin Schwidefsky static DEFINE_SPINLOCK(iucv_table_lock);
1682356f4cbSMartin Schwidefsky 
1692356f4cbSMartin Schwidefsky /*
17004b090d5SMartin Schwidefsky  * iucv_active_cpu: contains the number of the cpu executing the tasklet
17104b090d5SMartin Schwidefsky  * or the work handler. Needed for iucv_path_sever called from tasklet.
1722356f4cbSMartin Schwidefsky  */
17304b090d5SMartin Schwidefsky static int iucv_active_cpu = -1;
1742356f4cbSMartin Schwidefsky 
1752356f4cbSMartin Schwidefsky /*
1762356f4cbSMartin Schwidefsky  * Mutex and wait queue for iucv_register/iucv_unregister.
1772356f4cbSMartin Schwidefsky  */
1782356f4cbSMartin Schwidefsky static DEFINE_MUTEX(iucv_register_mutex);
1792356f4cbSMartin Schwidefsky 
1802356f4cbSMartin Schwidefsky /*
1812356f4cbSMartin Schwidefsky  * Counter for number of non-smp capable handlers.
1822356f4cbSMartin Schwidefsky  */
1832356f4cbSMartin Schwidefsky static int iucv_nonsmp_handler;
1842356f4cbSMartin Schwidefsky 
1852356f4cbSMartin Schwidefsky /*
1862356f4cbSMartin Schwidefsky  * IUCV control data structure. Used by iucv_path_accept, iucv_path_connect,
1872356f4cbSMartin Schwidefsky  * iucv_path_quiesce and iucv_path_sever.
1882356f4cbSMartin Schwidefsky  */
1892356f4cbSMartin Schwidefsky struct iucv_cmd_control {
1902356f4cbSMartin Schwidefsky 	u16 ippathid;
1912356f4cbSMartin Schwidefsky 	u8  ipflags1;
1922356f4cbSMartin Schwidefsky 	u8  iprcode;
1932356f4cbSMartin Schwidefsky 	u16 ipmsglim;
1942356f4cbSMartin Schwidefsky 	u16 res1;
1952356f4cbSMartin Schwidefsky 	u8  ipvmid[8];
1962356f4cbSMartin Schwidefsky 	u8  ipuser[16];
1972356f4cbSMartin Schwidefsky 	u8  iptarget[8];
1982356f4cbSMartin Schwidefsky } __attribute__ ((packed,aligned(8)));
1992356f4cbSMartin Schwidefsky 
2002356f4cbSMartin Schwidefsky /*
2012356f4cbSMartin Schwidefsky  * Data in parameter list iucv structure. Used by iucv_message_send,
2022356f4cbSMartin Schwidefsky  * iucv_message_send2way and iucv_message_reply.
2032356f4cbSMartin Schwidefsky  */
2042356f4cbSMartin Schwidefsky struct iucv_cmd_dpl {
2052356f4cbSMartin Schwidefsky 	u16 ippathid;
2062356f4cbSMartin Schwidefsky 	u8  ipflags1;
2072356f4cbSMartin Schwidefsky 	u8  iprcode;
2082356f4cbSMartin Schwidefsky 	u32 ipmsgid;
2092356f4cbSMartin Schwidefsky 	u32 iptrgcls;
2102356f4cbSMartin Schwidefsky 	u8  iprmmsg[8];
2112356f4cbSMartin Schwidefsky 	u32 ipsrccls;
2122356f4cbSMartin Schwidefsky 	u32 ipmsgtag;
2132356f4cbSMartin Schwidefsky 	u32 ipbfadr2;
2142356f4cbSMartin Schwidefsky 	u32 ipbfln2f;
2152356f4cbSMartin Schwidefsky 	u32 res;
2162356f4cbSMartin Schwidefsky } __attribute__ ((packed,aligned(8)));
2172356f4cbSMartin Schwidefsky 
2182356f4cbSMartin Schwidefsky /*
2192356f4cbSMartin Schwidefsky  * Data in buffer iucv structure. Used by iucv_message_receive,
2202356f4cbSMartin Schwidefsky  * iucv_message_reject, iucv_message_send, iucv_message_send2way
2212356f4cbSMartin Schwidefsky  * and iucv_declare_cpu.
2222356f4cbSMartin Schwidefsky  */
2232356f4cbSMartin Schwidefsky struct iucv_cmd_db {
2242356f4cbSMartin Schwidefsky 	u16 ippathid;
2252356f4cbSMartin Schwidefsky 	u8  ipflags1;
2262356f4cbSMartin Schwidefsky 	u8  iprcode;
2272356f4cbSMartin Schwidefsky 	u32 ipmsgid;
2282356f4cbSMartin Schwidefsky 	u32 iptrgcls;
2292356f4cbSMartin Schwidefsky 	u32 ipbfadr1;
2302356f4cbSMartin Schwidefsky 	u32 ipbfln1f;
2312356f4cbSMartin Schwidefsky 	u32 ipsrccls;
2322356f4cbSMartin Schwidefsky 	u32 ipmsgtag;
2332356f4cbSMartin Schwidefsky 	u32 ipbfadr2;
2342356f4cbSMartin Schwidefsky 	u32 ipbfln2f;
2352356f4cbSMartin Schwidefsky 	u32 res;
2362356f4cbSMartin Schwidefsky } __attribute__ ((packed,aligned(8)));
2372356f4cbSMartin Schwidefsky 
2382356f4cbSMartin Schwidefsky /*
2392356f4cbSMartin Schwidefsky  * Purge message iucv structure. Used by iucv_message_purge.
2402356f4cbSMartin Schwidefsky  */
2412356f4cbSMartin Schwidefsky struct iucv_cmd_purge {
2422356f4cbSMartin Schwidefsky 	u16 ippathid;
2432356f4cbSMartin Schwidefsky 	u8  ipflags1;
2442356f4cbSMartin Schwidefsky 	u8  iprcode;
2452356f4cbSMartin Schwidefsky 	u32 ipmsgid;
2462356f4cbSMartin Schwidefsky 	u8  ipaudit[3];
2472356f4cbSMartin Schwidefsky 	u8  res1[5];
2482356f4cbSMartin Schwidefsky 	u32 res2;
2492356f4cbSMartin Schwidefsky 	u32 ipsrccls;
2502356f4cbSMartin Schwidefsky 	u32 ipmsgtag;
2512356f4cbSMartin Schwidefsky 	u32 res3[3];
2522356f4cbSMartin Schwidefsky } __attribute__ ((packed,aligned(8)));
2532356f4cbSMartin Schwidefsky 
2542356f4cbSMartin Schwidefsky /*
2552356f4cbSMartin Schwidefsky  * Set mask iucv structure. Used by iucv_enable_cpu.
2562356f4cbSMartin Schwidefsky  */
2572356f4cbSMartin Schwidefsky struct iucv_cmd_set_mask {
2582356f4cbSMartin Schwidefsky 	u8  ipmask;
2592356f4cbSMartin Schwidefsky 	u8  res1[2];
2602356f4cbSMartin Schwidefsky 	u8  iprcode;
2612356f4cbSMartin Schwidefsky 	u32 res2[9];
2622356f4cbSMartin Schwidefsky } __attribute__ ((packed,aligned(8)));
2632356f4cbSMartin Schwidefsky 
2642356f4cbSMartin Schwidefsky union iucv_param {
2652356f4cbSMartin Schwidefsky 	struct iucv_cmd_control ctrl;
2662356f4cbSMartin Schwidefsky 	struct iucv_cmd_dpl dpl;
2672356f4cbSMartin Schwidefsky 	struct iucv_cmd_db db;
2682356f4cbSMartin Schwidefsky 	struct iucv_cmd_purge purge;
2692356f4cbSMartin Schwidefsky 	struct iucv_cmd_set_mask set_mask;
2702356f4cbSMartin Schwidefsky };
2712356f4cbSMartin Schwidefsky 
2722356f4cbSMartin Schwidefsky /*
2732356f4cbSMartin Schwidefsky  * Anchor for per-cpu IUCV command parameter block.
2742356f4cbSMartin Schwidefsky  */
27570cf5035SChristoph Lameter static union iucv_param *iucv_param[NR_CPUS];
27642e1b4c2SUrsula Braun static union iucv_param *iucv_param_irq[NR_CPUS];
2772356f4cbSMartin Schwidefsky 
2782356f4cbSMartin Schwidefsky /**
279682026a5SHeiko Carstens  * __iucv_call_b2f0
280682026a5SHeiko Carstens  * @command: identifier of IUCV call to CP.
2812356f4cbSMartin Schwidefsky  * @parm: pointer to a struct iucv_parm block
2822356f4cbSMartin Schwidefsky  *
2832356f4cbSMartin Schwidefsky  * Calls CP to execute IUCV commands.
2842356f4cbSMartin Schwidefsky  *
2852356f4cbSMartin Schwidefsky  * Returns the result of the CP IUCV call.
2862356f4cbSMartin Schwidefsky  */
__iucv_call_b2f0(int command,union iucv_param * parm)287eb090ad2SHeiko Carstens static inline int __iucv_call_b2f0(int command, union iucv_param *parm)
2882356f4cbSMartin Schwidefsky {
28950348facSHeiko Carstens 	int cc;
2902356f4cbSMartin Schwidefsky 
2912356f4cbSMartin Schwidefsky 	asm volatile(
29250348facSHeiko Carstens 		"	lgr	0,%[reg0]\n"
29350348facSHeiko Carstens 		"	lgr	1,%[reg1]\n"
2942356f4cbSMartin Schwidefsky 		"	.long	0xb2f01000\n"
29550348facSHeiko Carstens 		"	ipm	%[cc]\n"
29650348facSHeiko Carstens 		"	srl	%[cc],28\n"
29750348facSHeiko Carstens 		: [cc] "=&d" (cc), "+m" (*parm)
29850348facSHeiko Carstens 		: [reg0] "d" ((unsigned long)command),
29950348facSHeiko Carstens 		  [reg1] "d" ((unsigned long)parm)
30050348facSHeiko Carstens 		: "cc", "0", "1");
30150348facSHeiko Carstens 	return cc;
302eb090ad2SHeiko Carstens }
303eb090ad2SHeiko Carstens 
iucv_call_b2f0(int command,union iucv_param * parm)304eb090ad2SHeiko Carstens static inline int iucv_call_b2f0(int command, union iucv_param *parm)
305eb090ad2SHeiko Carstens {
306eb090ad2SHeiko Carstens 	int ccode;
307eb090ad2SHeiko Carstens 
308eb090ad2SHeiko Carstens 	ccode = __iucv_call_b2f0(command, parm);
309eb090ad2SHeiko Carstens 	return ccode == 1 ? parm->ctrl.iprcode : ccode;
3102356f4cbSMartin Schwidefsky }
3112356f4cbSMartin Schwidefsky 
312682026a5SHeiko Carstens /*
3132356f4cbSMartin Schwidefsky  * iucv_query_maxconn
3142356f4cbSMartin Schwidefsky  *
3152356f4cbSMartin Schwidefsky  * Determines the maximum number of connections that may be established.
3162356f4cbSMartin Schwidefsky  *
3172356f4cbSMartin Schwidefsky  * Returns the maximum number of connections or -EPERM is IUCV is not
3182356f4cbSMartin Schwidefsky  * available.
3192356f4cbSMartin Schwidefsky  */
__iucv_query_maxconn(void * param,unsigned long * max_pathid)320eb090ad2SHeiko Carstens static int __iucv_query_maxconn(void *param, unsigned long *max_pathid)
3212356f4cbSMartin Schwidefsky {
322ab847d03SAlexander Gordeev 	unsigned long reg1 = virt_to_phys(param);
32350348facSHeiko Carstens 	int cc;
3242356f4cbSMartin Schwidefsky 
3252356f4cbSMartin Schwidefsky 	asm volatile (
32650348facSHeiko Carstens 		"	lghi	0,%[cmd]\n"
32750348facSHeiko Carstens 		"	lgr	1,%[reg1]\n"
3282356f4cbSMartin Schwidefsky 		"	.long	0xb2f01000\n"
32950348facSHeiko Carstens 		"	ipm	%[cc]\n"
33050348facSHeiko Carstens 		"	srl	%[cc],28\n"
33150348facSHeiko Carstens 		"	lgr	%[reg1],1\n"
33250348facSHeiko Carstens 		: [cc] "=&d" (cc), [reg1] "+&d" (reg1)
33350348facSHeiko Carstens 		: [cmd] "K" (IUCV_QUERY)
33450348facSHeiko Carstens 		: "cc", "0", "1");
335eb090ad2SHeiko Carstens 	*max_pathid = reg1;
33650348facSHeiko Carstens 	return cc;
337eb090ad2SHeiko Carstens }
338eb090ad2SHeiko Carstens 
iucv_query_maxconn(void)339eb090ad2SHeiko Carstens static int iucv_query_maxconn(void)
340eb090ad2SHeiko Carstens {
341eb090ad2SHeiko Carstens 	unsigned long max_pathid;
342eb090ad2SHeiko Carstens 	void *param;
343eb090ad2SHeiko Carstens 	int ccode;
344eb090ad2SHeiko Carstens 
345eb090ad2SHeiko Carstens 	param = kzalloc(sizeof(union iucv_param), GFP_KERNEL | GFP_DMA);
346eb090ad2SHeiko Carstens 	if (!param)
347eb090ad2SHeiko Carstens 		return -ENOMEM;
348eb090ad2SHeiko Carstens 	ccode = __iucv_query_maxconn(param, &max_pathid);
3492356f4cbSMartin Schwidefsky 	if (ccode == 0)
350eb090ad2SHeiko Carstens 		iucv_max_pathid = max_pathid;
3512356f4cbSMartin Schwidefsky 	kfree(param);
3522356f4cbSMartin Schwidefsky 	return ccode ? -EPERM : 0;
3532356f4cbSMartin Schwidefsky }
3542356f4cbSMartin Schwidefsky 
3552356f4cbSMartin Schwidefsky /**
3562356f4cbSMartin Schwidefsky  * iucv_allow_cpu
3572356f4cbSMartin Schwidefsky  * @data: unused
3582356f4cbSMartin Schwidefsky  *
3592356f4cbSMartin Schwidefsky  * Allow iucv interrupts on this cpu.
3602356f4cbSMartin Schwidefsky  */
iucv_allow_cpu(void * data)3612356f4cbSMartin Schwidefsky static void iucv_allow_cpu(void *data)
3622356f4cbSMartin Schwidefsky {
3632356f4cbSMartin Schwidefsky 	int cpu = smp_processor_id();
3642356f4cbSMartin Schwidefsky 	union iucv_param *parm;
3652356f4cbSMartin Schwidefsky 
3662356f4cbSMartin Schwidefsky 	/*
3672356f4cbSMartin Schwidefsky 	 * Enable all iucv interrupts.
3682356f4cbSMartin Schwidefsky 	 * ipmask contains bits for the different interrupts
3692356f4cbSMartin Schwidefsky 	 *	0x80 - Flag to allow nonpriority message pending interrupts
3702356f4cbSMartin Schwidefsky 	 *	0x40 - Flag to allow priority message pending interrupts
3712356f4cbSMartin Schwidefsky 	 *	0x20 - Flag to allow nonpriority message completion interrupts
3722356f4cbSMartin Schwidefsky 	 *	0x10 - Flag to allow priority message completion interrupts
3732356f4cbSMartin Schwidefsky 	 *	0x08 - Flag to allow IUCV control interrupts
3742356f4cbSMartin Schwidefsky 	 */
37542e1b4c2SUrsula Braun 	parm = iucv_param_irq[cpu];
3762356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
3772356f4cbSMartin Schwidefsky 	parm->set_mask.ipmask = 0xf8;
3782356f4cbSMartin Schwidefsky 	iucv_call_b2f0(IUCV_SETMASK, parm);
3792356f4cbSMartin Schwidefsky 
380672e405bSUrsula Braun 	/*
381672e405bSUrsula Braun 	 * Enable all iucv control interrupts.
382672e405bSUrsula Braun 	 * ipmask contains bits for the different interrupts
383672e405bSUrsula Braun 	 *	0x80 - Flag to allow pending connections interrupts
384672e405bSUrsula Braun 	 *	0x40 - Flag to allow connection complete interrupts
385672e405bSUrsula Braun 	 *	0x20 - Flag to allow connection severed interrupts
386672e405bSUrsula Braun 	 *	0x10 - Flag to allow connection quiesced interrupts
387672e405bSUrsula Braun 	 *	0x08 - Flag to allow connection resumed interrupts
388672e405bSUrsula Braun 	 */
389672e405bSUrsula Braun 	memset(parm, 0, sizeof(union iucv_param));
390672e405bSUrsula Braun 	parm->set_mask.ipmask = 0xf8;
391672e405bSUrsula Braun 	iucv_call_b2f0(IUCV_SETCONTROLMASK, parm);
3922356f4cbSMartin Schwidefsky 	/* Set indication that iucv interrupts are allowed for this cpu. */
393f2019030SKOSAKI Motohiro 	cpumask_set_cpu(cpu, &iucv_irq_cpumask);
3942356f4cbSMartin Schwidefsky }
3952356f4cbSMartin Schwidefsky 
3962356f4cbSMartin Schwidefsky /**
3972356f4cbSMartin Schwidefsky  * iucv_block_cpu
3982356f4cbSMartin Schwidefsky  * @data: unused
3992356f4cbSMartin Schwidefsky  *
4002356f4cbSMartin Schwidefsky  * Block iucv interrupts on this cpu.
4012356f4cbSMartin Schwidefsky  */
iucv_block_cpu(void * data)4022356f4cbSMartin Schwidefsky static void iucv_block_cpu(void *data)
4032356f4cbSMartin Schwidefsky {
4042356f4cbSMartin Schwidefsky 	int cpu = smp_processor_id();
4052356f4cbSMartin Schwidefsky 	union iucv_param *parm;
4062356f4cbSMartin Schwidefsky 
4072356f4cbSMartin Schwidefsky 	/* Disable all iucv interrupts. */
40842e1b4c2SUrsula Braun 	parm = iucv_param_irq[cpu];
4092356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
4102356f4cbSMartin Schwidefsky 	iucv_call_b2f0(IUCV_SETMASK, parm);
4112356f4cbSMartin Schwidefsky 
4122356f4cbSMartin Schwidefsky 	/* Clear indication that iucv interrupts are allowed for this cpu. */
413f2019030SKOSAKI Motohiro 	cpumask_clear_cpu(cpu, &iucv_irq_cpumask);
4142356f4cbSMartin Schwidefsky }
4152356f4cbSMartin Schwidefsky 
4162356f4cbSMartin Schwidefsky /**
4172356f4cbSMartin Schwidefsky  * iucv_declare_cpu
4182356f4cbSMartin Schwidefsky  * @data: unused
4192356f4cbSMartin Schwidefsky  *
4203a4fa0a2SRobert P. J. Day  * Declare a interrupt buffer on this cpu.
4212356f4cbSMartin Schwidefsky  */
iucv_declare_cpu(void * data)4222356f4cbSMartin Schwidefsky static void iucv_declare_cpu(void *data)
4232356f4cbSMartin Schwidefsky {
4242356f4cbSMartin Schwidefsky 	int cpu = smp_processor_id();
4252356f4cbSMartin Schwidefsky 	union iucv_param *parm;
4262356f4cbSMartin Schwidefsky 	int rc;
4272356f4cbSMartin Schwidefsky 
428f2019030SKOSAKI Motohiro 	if (cpumask_test_cpu(cpu, &iucv_buffer_cpumask))
4292356f4cbSMartin Schwidefsky 		return;
4302356f4cbSMartin Schwidefsky 
4312356f4cbSMartin Schwidefsky 	/* Declare interrupt buffer. */
43242e1b4c2SUrsula Braun 	parm = iucv_param_irq[cpu];
4332356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
43470cf5035SChristoph Lameter 	parm->db.ipbfadr1 = virt_to_phys(iucv_irq_data[cpu]);
4352356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_DECLARE_BUFFER, parm);
4362356f4cbSMartin Schwidefsky 	if (rc) {
4372356f4cbSMartin Schwidefsky 		char *err = "Unknown";
4382356f4cbSMartin Schwidefsky 		switch (rc) {
4392356f4cbSMartin Schwidefsky 		case 0x03:
4402356f4cbSMartin Schwidefsky 			err = "Directory error";
4412356f4cbSMartin Schwidefsky 			break;
4422356f4cbSMartin Schwidefsky 		case 0x0a:
4432356f4cbSMartin Schwidefsky 			err = "Invalid length";
4442356f4cbSMartin Schwidefsky 			break;
4452356f4cbSMartin Schwidefsky 		case 0x13:
4462356f4cbSMartin Schwidefsky 			err = "Buffer already exists";
4472356f4cbSMartin Schwidefsky 			break;
4482356f4cbSMartin Schwidefsky 		case 0x3e:
4492356f4cbSMartin Schwidefsky 			err = "Buffer overlap";
4502356f4cbSMartin Schwidefsky 			break;
4512356f4cbSMartin Schwidefsky 		case 0x5c:
4522356f4cbSMartin Schwidefsky 			err = "Paging or storage error";
4532356f4cbSMartin Schwidefsky 			break;
4542356f4cbSMartin Schwidefsky 		}
45547c4cfc3SJoe Perches 		pr_warn("Defining an interrupt buffer on CPU %i failed with 0x%02x (%s)\n",
45647c4cfc3SJoe Perches 			cpu, rc, err);
4572356f4cbSMartin Schwidefsky 		return;
4582356f4cbSMartin Schwidefsky 	}
4592356f4cbSMartin Schwidefsky 
4602356f4cbSMartin Schwidefsky 	/* Set indication that an iucv buffer exists for this cpu. */
461f2019030SKOSAKI Motohiro 	cpumask_set_cpu(cpu, &iucv_buffer_cpumask);
4622356f4cbSMartin Schwidefsky 
463f2019030SKOSAKI Motohiro 	if (iucv_nonsmp_handler == 0 || cpumask_empty(&iucv_irq_cpumask))
4642356f4cbSMartin Schwidefsky 		/* Enable iucv interrupts on this cpu. */
4652356f4cbSMartin Schwidefsky 		iucv_allow_cpu(NULL);
4662356f4cbSMartin Schwidefsky 	else
4672356f4cbSMartin Schwidefsky 		/* Disable iucv interrupts on this cpu. */
4682356f4cbSMartin Schwidefsky 		iucv_block_cpu(NULL);
4692356f4cbSMartin Schwidefsky }
4702356f4cbSMartin Schwidefsky 
4712356f4cbSMartin Schwidefsky /**
4722356f4cbSMartin Schwidefsky  * iucv_retrieve_cpu
4732356f4cbSMartin Schwidefsky  * @data: unused
4742356f4cbSMartin Schwidefsky  *
4752356f4cbSMartin Schwidefsky  * Retrieve interrupt buffer on this cpu.
4762356f4cbSMartin Schwidefsky  */
iucv_retrieve_cpu(void * data)4772356f4cbSMartin Schwidefsky static void iucv_retrieve_cpu(void *data)
4782356f4cbSMartin Schwidefsky {
4792356f4cbSMartin Schwidefsky 	int cpu = smp_processor_id();
4802356f4cbSMartin Schwidefsky 	union iucv_param *parm;
4812356f4cbSMartin Schwidefsky 
482f2019030SKOSAKI Motohiro 	if (!cpumask_test_cpu(cpu, &iucv_buffer_cpumask))
4832356f4cbSMartin Schwidefsky 		return;
4842356f4cbSMartin Schwidefsky 
4852356f4cbSMartin Schwidefsky 	/* Block iucv interrupts. */
4862356f4cbSMartin Schwidefsky 	iucv_block_cpu(NULL);
4872356f4cbSMartin Schwidefsky 
4882356f4cbSMartin Schwidefsky 	/* Retrieve interrupt buffer. */
48942e1b4c2SUrsula Braun 	parm = iucv_param_irq[cpu];
4902356f4cbSMartin Schwidefsky 	iucv_call_b2f0(IUCV_RETRIEVE_BUFFER, parm);
4912356f4cbSMartin Schwidefsky 
4922356f4cbSMartin Schwidefsky 	/* Clear indication that an iucv buffer exists for this cpu. */
493f2019030SKOSAKI Motohiro 	cpumask_clear_cpu(cpu, &iucv_buffer_cpumask);
4942356f4cbSMartin Schwidefsky }
4952356f4cbSMartin Schwidefsky 
496682026a5SHeiko Carstens /*
497682026a5SHeiko Carstens  * iucv_setmask_mp
4982356f4cbSMartin Schwidefsky  *
4992356f4cbSMartin Schwidefsky  * Allow iucv interrupts on all cpus.
5002356f4cbSMartin Schwidefsky  */
iucv_setmask_mp(void)5012356f4cbSMartin Schwidefsky static void iucv_setmask_mp(void)
5022356f4cbSMartin Schwidefsky {
5032356f4cbSMartin Schwidefsky 	int cpu;
5042356f4cbSMartin Schwidefsky 
5058c39ed48SSebastian Andrzej Siewior 	cpus_read_lock();
5062356f4cbSMartin Schwidefsky 	for_each_online_cpu(cpu)
5072356f4cbSMartin Schwidefsky 		/* Enable all cpus with a declared buffer. */
508f2019030SKOSAKI Motohiro 		if (cpumask_test_cpu(cpu, &iucv_buffer_cpumask) &&
509f2019030SKOSAKI Motohiro 		    !cpumask_test_cpu(cpu, &iucv_irq_cpumask))
5103bb447fcSHeiko Carstens 			smp_call_function_single(cpu, iucv_allow_cpu,
5118691e5a8SJens Axboe 						 NULL, 1);
5128c39ed48SSebastian Andrzej Siewior 	cpus_read_unlock();
5132356f4cbSMartin Schwidefsky }
5142356f4cbSMartin Schwidefsky 
515682026a5SHeiko Carstens /*
5162356f4cbSMartin Schwidefsky  * iucv_setmask_up
5172356f4cbSMartin Schwidefsky  *
51804b090d5SMartin Schwidefsky  * Allow iucv interrupts on a single cpu.
5192356f4cbSMartin Schwidefsky  */
iucv_setmask_up(void)5202356f4cbSMartin Schwidefsky static void iucv_setmask_up(void)
5212356f4cbSMartin Schwidefsky {
522724e7965SDawei Li 	static cpumask_t cpumask;
5232356f4cbSMartin Schwidefsky 	int cpu;
5242356f4cbSMartin Schwidefsky 
5252356f4cbSMartin Schwidefsky 	/* Disable all cpu but the first in cpu_irq_cpumask. */
526f2019030SKOSAKI Motohiro 	cpumask_copy(&cpumask, &iucv_irq_cpumask);
527f2019030SKOSAKI Motohiro 	cpumask_clear_cpu(cpumask_first(&iucv_irq_cpumask), &cpumask);
528f2019030SKOSAKI Motohiro 	for_each_cpu(cpu, &cpumask)
5298691e5a8SJens Axboe 		smp_call_function_single(cpu, iucv_block_cpu, NULL, 1);
5302356f4cbSMartin Schwidefsky }
5312356f4cbSMartin Schwidefsky 
532682026a5SHeiko Carstens /*
5332356f4cbSMartin Schwidefsky  * iucv_enable
5342356f4cbSMartin Schwidefsky  *
5352356f4cbSMartin Schwidefsky  * This function makes iucv ready for use. It allocates the pathid
5362356f4cbSMartin Schwidefsky  * table, declares an iucv interrupt buffer and enables the iucv
5372356f4cbSMartin Schwidefsky  * interrupts. Called when the first user has registered an iucv
5382356f4cbSMartin Schwidefsky  * handler.
5392356f4cbSMartin Schwidefsky  */
iucv_enable(void)5402356f4cbSMartin Schwidefsky static int iucv_enable(void)
5412356f4cbSMartin Schwidefsky {
5422356f4cbSMartin Schwidefsky 	size_t alloc_size;
5432356f4cbSMartin Schwidefsky 	int cpu, rc;
5442356f4cbSMartin Schwidefsky 
5458c39ed48SSebastian Andrzej Siewior 	cpus_read_lock();
5462356f4cbSMartin Schwidefsky 	rc = -ENOMEM;
54761698b98SAlexander Gordeev 	alloc_size = iucv_max_pathid * sizeof(*iucv_path_table);
5482356f4cbSMartin Schwidefsky 	iucv_path_table = kzalloc(alloc_size, GFP_KERNEL);
5492356f4cbSMartin Schwidefsky 	if (!iucv_path_table)
5502356f4cbSMartin Schwidefsky 		goto out;
5512356f4cbSMartin Schwidefsky 	/* Declare per cpu buffers. */
5522356f4cbSMartin Schwidefsky 	rc = -EIO;
5532356f4cbSMartin Schwidefsky 	for_each_online_cpu(cpu)
5548691e5a8SJens Axboe 		smp_call_function_single(cpu, iucv_declare_cpu, NULL, 1);
555f2019030SKOSAKI Motohiro 	if (cpumask_empty(&iucv_buffer_cpumask))
5562356f4cbSMartin Schwidefsky 		/* No cpu could declare an iucv buffer. */
557f1d3e4dcSHeiko Carstens 		goto out;
5588c39ed48SSebastian Andrzej Siewior 	cpus_read_unlock();
5592356f4cbSMartin Schwidefsky 	return 0;
5602356f4cbSMartin Schwidefsky out:
561f1d3e4dcSHeiko Carstens 	kfree(iucv_path_table);
562f1d3e4dcSHeiko Carstens 	iucv_path_table = NULL;
5638c39ed48SSebastian Andrzej Siewior 	cpus_read_unlock();
5642356f4cbSMartin Schwidefsky 	return rc;
5652356f4cbSMartin Schwidefsky }
5662356f4cbSMartin Schwidefsky 
567682026a5SHeiko Carstens /*
5682356f4cbSMartin Schwidefsky  * iucv_disable
5692356f4cbSMartin Schwidefsky  *
5702356f4cbSMartin Schwidefsky  * This function shuts down iucv. It disables iucv interrupts, retrieves
5712356f4cbSMartin Schwidefsky  * the iucv interrupt buffer and frees the pathid table. Called after the
5722356f4cbSMartin Schwidefsky  * last user unregister its iucv handler.
5732356f4cbSMartin Schwidefsky  */
iucv_disable(void)5742356f4cbSMartin Schwidefsky static void iucv_disable(void)
5752356f4cbSMartin Schwidefsky {
5768c39ed48SSebastian Andrzej Siewior 	cpus_read_lock();
57715c8b6c1SJens Axboe 	on_each_cpu(iucv_retrieve_cpu, NULL, 1);
5782356f4cbSMartin Schwidefsky 	kfree(iucv_path_table);
579f1d3e4dcSHeiko Carstens 	iucv_path_table = NULL;
5808c39ed48SSebastian Andrzej Siewior 	cpus_read_unlock();
5812356f4cbSMartin Schwidefsky }
5822356f4cbSMartin Schwidefsky 
iucv_cpu_dead(unsigned int cpu)58338b48292SSebastian Andrzej Siewior static int iucv_cpu_dead(unsigned int cpu)
584a0e247a8SSrivatsa S. Bhat {
585a0e247a8SSrivatsa S. Bhat 	kfree(iucv_param_irq[cpu]);
586a0e247a8SSrivatsa S. Bhat 	iucv_param_irq[cpu] = NULL;
587a0e247a8SSrivatsa S. Bhat 	kfree(iucv_param[cpu]);
588a0e247a8SSrivatsa S. Bhat 	iucv_param[cpu] = NULL;
589a0e247a8SSrivatsa S. Bhat 	kfree(iucv_irq_data[cpu]);
590a0e247a8SSrivatsa S. Bhat 	iucv_irq_data[cpu] = NULL;
59138b48292SSebastian Andrzej Siewior 	return 0;
592a0e247a8SSrivatsa S. Bhat }
593a0e247a8SSrivatsa S. Bhat 
iucv_cpu_prepare(unsigned int cpu)59438b48292SSebastian Andrzej Siewior static int iucv_cpu_prepare(unsigned int cpu)
595a0e247a8SSrivatsa S. Bhat {
596a0e247a8SSrivatsa S. Bhat 	/* Note: GFP_DMA used to get memory below 2G */
597a0e247a8SSrivatsa S. Bhat 	iucv_irq_data[cpu] = kmalloc_node(sizeof(struct iucv_irq_data),
598a0e247a8SSrivatsa S. Bhat 			     GFP_KERNEL|GFP_DMA, cpu_to_node(cpu));
599a0e247a8SSrivatsa S. Bhat 	if (!iucv_irq_data[cpu])
600a0e247a8SSrivatsa S. Bhat 		goto out_free;
601a0e247a8SSrivatsa S. Bhat 
602a0e247a8SSrivatsa S. Bhat 	/* Allocate parameter blocks. */
603a0e247a8SSrivatsa S. Bhat 	iucv_param[cpu] = kmalloc_node(sizeof(union iucv_param),
604a0e247a8SSrivatsa S. Bhat 			  GFP_KERNEL|GFP_DMA, cpu_to_node(cpu));
605a0e247a8SSrivatsa S. Bhat 	if (!iucv_param[cpu])
606a0e247a8SSrivatsa S. Bhat 		goto out_free;
607a0e247a8SSrivatsa S. Bhat 
608a0e247a8SSrivatsa S. Bhat 	iucv_param_irq[cpu] = kmalloc_node(sizeof(union iucv_param),
609a0e247a8SSrivatsa S. Bhat 			  GFP_KERNEL|GFP_DMA, cpu_to_node(cpu));
610a0e247a8SSrivatsa S. Bhat 	if (!iucv_param_irq[cpu])
611a0e247a8SSrivatsa S. Bhat 		goto out_free;
612a0e247a8SSrivatsa S. Bhat 
613a0e247a8SSrivatsa S. Bhat 	return 0;
614a0e247a8SSrivatsa S. Bhat 
615a0e247a8SSrivatsa S. Bhat out_free:
61638b48292SSebastian Andrzej Siewior 	iucv_cpu_dead(cpu);
617a0e247a8SSrivatsa S. Bhat 	return -ENOMEM;
618a0e247a8SSrivatsa S. Bhat }
619a0e247a8SSrivatsa S. Bhat 
iucv_cpu_online(unsigned int cpu)62038b48292SSebastian Andrzej Siewior static int iucv_cpu_online(unsigned int cpu)
62138b48292SSebastian Andrzej Siewior {
62238b48292SSebastian Andrzej Siewior 	if (!iucv_path_table)
62338b48292SSebastian Andrzej Siewior 		return 0;
62438b48292SSebastian Andrzej Siewior 	iucv_declare_cpu(NULL);
62538b48292SSebastian Andrzej Siewior 	return 0;
62638b48292SSebastian Andrzej Siewior }
62738b48292SSebastian Andrzej Siewior 
iucv_cpu_down_prep(unsigned int cpu)62838b48292SSebastian Andrzej Siewior static int iucv_cpu_down_prep(unsigned int cpu)
6292356f4cbSMartin Schwidefsky {
630724e7965SDawei Li 	cpumask_var_t cpumask;
631724e7965SDawei Li 	int ret = 0;
6322356f4cbSMartin Schwidefsky 
633f1d3e4dcSHeiko Carstens 	if (!iucv_path_table)
63438b48292SSebastian Andrzej Siewior 		return 0;
63538b48292SSebastian Andrzej Siewior 
636724e7965SDawei Li 	if (!alloc_cpumask_var(&cpumask, GFP_KERNEL))
637724e7965SDawei Li 		return -ENOMEM;
638724e7965SDawei Li 
639724e7965SDawei Li 	cpumask_copy(cpumask, &iucv_buffer_cpumask);
640724e7965SDawei Li 	cpumask_clear_cpu(cpu, cpumask);
641724e7965SDawei Li 	if (cpumask_empty(cpumask)) {
6422356f4cbSMartin Schwidefsky 		/* Can't offline last IUCV enabled cpu. */
643724e7965SDawei Li 		ret = -EINVAL;
644724e7965SDawei Li 		goto __free_cpumask;
645724e7965SDawei Li 	}
6462356f4cbSMartin Schwidefsky 
64738b48292SSebastian Andrzej Siewior 	iucv_retrieve_cpu(NULL);
64838b48292SSebastian Andrzej Siewior 	if (!cpumask_empty(&iucv_irq_cpumask))
649724e7965SDawei Li 		goto __free_cpumask;
650724e7965SDawei Li 
65138b48292SSebastian Andrzej Siewior 	smp_call_function_single(cpumask_first(&iucv_buffer_cpumask),
65238b48292SSebastian Andrzej Siewior 				 iucv_allow_cpu, NULL, 1);
653724e7965SDawei Li 
654724e7965SDawei Li __free_cpumask:
655724e7965SDawei Li 	free_cpumask_var(cpumask);
656724e7965SDawei Li 	return ret;
65738b48292SSebastian Andrzej Siewior }
6582356f4cbSMartin Schwidefsky 
6592356f4cbSMartin Schwidefsky /**
6602356f4cbSMartin Schwidefsky  * iucv_sever_pathid
6612356f4cbSMartin Schwidefsky  * @pathid: path identification number.
6622356f4cbSMartin Schwidefsky  * @userdata: 16-bytes of user data.
6632356f4cbSMartin Schwidefsky  *
6642356f4cbSMartin Schwidefsky  * Sever an iucv path to free up the pathid. Used internally.
6652356f4cbSMartin Schwidefsky  */
iucv_sever_pathid(u16 pathid,u8 * userdata)66691e60eb6SUrsula Braun static int iucv_sever_pathid(u16 pathid, u8 *userdata)
6672356f4cbSMartin Schwidefsky {
6682356f4cbSMartin Schwidefsky 	union iucv_param *parm;
6692356f4cbSMartin Schwidefsky 
67042e1b4c2SUrsula Braun 	parm = iucv_param_irq[smp_processor_id()];
6712356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
6722356f4cbSMartin Schwidefsky 	if (userdata)
6732356f4cbSMartin Schwidefsky 		memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser));
6742356f4cbSMartin Schwidefsky 	parm->ctrl.ippathid = pathid;
6752356f4cbSMartin Schwidefsky 	return iucv_call_b2f0(IUCV_SEVER, parm);
6762356f4cbSMartin Schwidefsky }
6772356f4cbSMartin Schwidefsky 
6782356f4cbSMartin Schwidefsky /**
67904b090d5SMartin Schwidefsky  * __iucv_cleanup_queue
6802356f4cbSMartin Schwidefsky  * @dummy: unused dummy argument
6812356f4cbSMartin Schwidefsky  *
6822356f4cbSMartin Schwidefsky  * Nop function called via smp_call_function to force work items from
6832356f4cbSMartin Schwidefsky  * pending external iucv interrupts to the work queue.
6842356f4cbSMartin Schwidefsky  */
__iucv_cleanup_queue(void * dummy)68504b090d5SMartin Schwidefsky static void __iucv_cleanup_queue(void *dummy)
6862356f4cbSMartin Schwidefsky {
6872356f4cbSMartin Schwidefsky }
6882356f4cbSMartin Schwidefsky 
6892356f4cbSMartin Schwidefsky /**
69004b090d5SMartin Schwidefsky  * iucv_cleanup_queue
6912356f4cbSMartin Schwidefsky  *
6922356f4cbSMartin Schwidefsky  * Function called after a path has been severed to find all remaining
6932356f4cbSMartin Schwidefsky  * work items for the now stale pathid. The caller needs to hold the
6942356f4cbSMartin Schwidefsky  * iucv_table_lock.
6952356f4cbSMartin Schwidefsky  */
iucv_cleanup_queue(void)69604b090d5SMartin Schwidefsky static void iucv_cleanup_queue(void)
6972356f4cbSMartin Schwidefsky {
69804b090d5SMartin Schwidefsky 	struct iucv_irq_list *p, *n;
6992356f4cbSMartin Schwidefsky 
7002356f4cbSMartin Schwidefsky 	/*
70125985edcSLucas De Marchi 	 * When a path is severed, the pathid can be reused immediately
70204b090d5SMartin Schwidefsky 	 * on a iucv connect or a connection pending interrupt. Remove
70304b090d5SMartin Schwidefsky 	 * all entries from the task queue that refer to a stale pathid
70404b090d5SMartin Schwidefsky 	 * (iucv_path_table[ix] == NULL). Only then do the iucv connect
70504b090d5SMartin Schwidefsky 	 * or deliver the connection pending interrupt. To get all the
70604b090d5SMartin Schwidefsky 	 * pending interrupts force them to the work queue by calling
70704b090d5SMartin Schwidefsky 	 * an empty function on all cpus.
7082356f4cbSMartin Schwidefsky 	 */
7098691e5a8SJens Axboe 	smp_call_function(__iucv_cleanup_queue, NULL, 1);
71004b090d5SMartin Schwidefsky 	spin_lock_irq(&iucv_queue_lock);
71104b090d5SMartin Schwidefsky 	list_for_each_entry_safe(p, n, &iucv_task_queue, list) {
71204b090d5SMartin Schwidefsky 		/* Remove stale work items from the task queue. */
71304b090d5SMartin Schwidefsky 		if (iucv_path_table[p->data.ippathid] == NULL) {
7142356f4cbSMartin Schwidefsky 			list_del(&p->list);
7152356f4cbSMartin Schwidefsky 			kfree(p);
7162356f4cbSMartin Schwidefsky 		}
7172356f4cbSMartin Schwidefsky 	}
71804b090d5SMartin Schwidefsky 	spin_unlock_irq(&iucv_queue_lock);
7192356f4cbSMartin Schwidefsky }
7202356f4cbSMartin Schwidefsky 
7212356f4cbSMartin Schwidefsky /**
7222356f4cbSMartin Schwidefsky  * iucv_register:
7232356f4cbSMartin Schwidefsky  * @handler: address of iucv handler structure
7242356f4cbSMartin Schwidefsky  * @smp: != 0 indicates that the handler can deal with out of order messages
7252356f4cbSMartin Schwidefsky  *
7262356f4cbSMartin Schwidefsky  * Registers a driver with IUCV.
7272356f4cbSMartin Schwidefsky  *
7282356f4cbSMartin Schwidefsky  * Returns 0 on success, -ENOMEM if the memory allocation for the pathid
7292356f4cbSMartin Schwidefsky  * table failed, or -EIO if IUCV_DECLARE_BUFFER failed on all cpus.
7302356f4cbSMartin Schwidefsky  */
iucv_register(struct iucv_handler * handler,int smp)7312356f4cbSMartin Schwidefsky int iucv_register(struct iucv_handler *handler, int smp)
7322356f4cbSMartin Schwidefsky {
7332356f4cbSMartin Schwidefsky 	int rc;
7342356f4cbSMartin Schwidefsky 
7352356f4cbSMartin Schwidefsky 	if (!iucv_available)
7362356f4cbSMartin Schwidefsky 		return -ENOSYS;
7372356f4cbSMartin Schwidefsky 	mutex_lock(&iucv_register_mutex);
7382356f4cbSMartin Schwidefsky 	if (!smp)
7392356f4cbSMartin Schwidefsky 		iucv_nonsmp_handler++;
7402356f4cbSMartin Schwidefsky 	if (list_empty(&iucv_handler_list)) {
7412356f4cbSMartin Schwidefsky 		rc = iucv_enable();
7422356f4cbSMartin Schwidefsky 		if (rc)
7432356f4cbSMartin Schwidefsky 			goto out_mutex;
7442356f4cbSMartin Schwidefsky 	} else if (!smp && iucv_nonsmp_handler == 1)
7452356f4cbSMartin Schwidefsky 		iucv_setmask_up();
7462356f4cbSMartin Schwidefsky 	INIT_LIST_HEAD(&handler->paths);
7472356f4cbSMartin Schwidefsky 
748435bc9dfSUrsula Braun 	spin_lock_bh(&iucv_table_lock);
7492356f4cbSMartin Schwidefsky 	list_add_tail(&handler->list, &iucv_handler_list);
750435bc9dfSUrsula Braun 	spin_unlock_bh(&iucv_table_lock);
7512356f4cbSMartin Schwidefsky 	rc = 0;
7522356f4cbSMartin Schwidefsky out_mutex:
7532356f4cbSMartin Schwidefsky 	mutex_unlock(&iucv_register_mutex);
7542356f4cbSMartin Schwidefsky 	return rc;
7552356f4cbSMartin Schwidefsky }
756da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_register);
7572356f4cbSMartin Schwidefsky 
7582356f4cbSMartin Schwidefsky /**
7592356f4cbSMartin Schwidefsky  * iucv_unregister
7602356f4cbSMartin Schwidefsky  * @handler:  address of iucv handler structure
7612356f4cbSMartin Schwidefsky  * @smp: != 0 indicates that the handler can deal with out of order messages
7622356f4cbSMartin Schwidefsky  *
7632356f4cbSMartin Schwidefsky  * Unregister driver from IUCV.
7642356f4cbSMartin Schwidefsky  */
iucv_unregister(struct iucv_handler * handler,int smp)7652356f4cbSMartin Schwidefsky void iucv_unregister(struct iucv_handler *handler, int smp)
7662356f4cbSMartin Schwidefsky {
7672356f4cbSMartin Schwidefsky 	struct iucv_path *p, *n;
7682356f4cbSMartin Schwidefsky 
7692356f4cbSMartin Schwidefsky 	mutex_lock(&iucv_register_mutex);
7702356f4cbSMartin Schwidefsky 	spin_lock_bh(&iucv_table_lock);
7712356f4cbSMartin Schwidefsky 	/* Remove handler from the iucv_handler_list. */
7722356f4cbSMartin Schwidefsky 	list_del_init(&handler->list);
77325985edcSLucas De Marchi 	/* Sever all pathids still referring to the handler. */
7742356f4cbSMartin Schwidefsky 	list_for_each_entry_safe(p, n, &handler->paths, list) {
7752356f4cbSMartin Schwidefsky 		iucv_sever_pathid(p->pathid, NULL);
7762356f4cbSMartin Schwidefsky 		iucv_path_table[p->pathid] = NULL;
7772356f4cbSMartin Schwidefsky 		list_del(&p->list);
7782356f4cbSMartin Schwidefsky 		iucv_path_free(p);
7792356f4cbSMartin Schwidefsky 	}
7802356f4cbSMartin Schwidefsky 	spin_unlock_bh(&iucv_table_lock);
7812356f4cbSMartin Schwidefsky 	if (!smp)
7822356f4cbSMartin Schwidefsky 		iucv_nonsmp_handler--;
7832356f4cbSMartin Schwidefsky 	if (list_empty(&iucv_handler_list))
7842356f4cbSMartin Schwidefsky 		iucv_disable();
7852356f4cbSMartin Schwidefsky 	else if (!smp && iucv_nonsmp_handler == 0)
7862356f4cbSMartin Schwidefsky 		iucv_setmask_mp();
7872356f4cbSMartin Schwidefsky 	mutex_unlock(&iucv_register_mutex);
7882356f4cbSMartin Schwidefsky }
789da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_unregister);
7902356f4cbSMartin Schwidefsky 
iucv_reboot_event(struct notifier_block * this,unsigned long event,void * ptr)7916c005961SUrsula Braun static int iucv_reboot_event(struct notifier_block *this,
7926c005961SUrsula Braun 			     unsigned long event, void *ptr)
7936c005961SUrsula Braun {
7945db79c06SUrsula Braun 	int i;
7956c005961SUrsula Braun 
796c0048de2SHendrik Brueckner 	if (cpumask_empty(&iucv_irq_cpumask))
797c0048de2SHendrik Brueckner 		return NOTIFY_DONE;
798c0048de2SHendrik Brueckner 
7998c39ed48SSebastian Andrzej Siewior 	cpus_read_lock();
800c0048de2SHendrik Brueckner 	on_each_cpu_mask(&iucv_irq_cpumask, iucv_block_cpu, NULL, 1);
8016c005961SUrsula Braun 	preempt_disable();
8026c005961SUrsula Braun 	for (i = 0; i < iucv_max_pathid; i++) {
8036c005961SUrsula Braun 		if (iucv_path_table[i])
8045db79c06SUrsula Braun 			iucv_sever_pathid(i, NULL);
8056c005961SUrsula Braun 	}
8066c005961SUrsula Braun 	preempt_enable();
8078c39ed48SSebastian Andrzej Siewior 	cpus_read_unlock();
8086c005961SUrsula Braun 	iucv_disable();
8096c005961SUrsula Braun 	return NOTIFY_DONE;
8106c005961SUrsula Braun }
8116c005961SUrsula Braun 
8126c005961SUrsula Braun static struct notifier_block iucv_reboot_notifier = {
8136c005961SUrsula Braun 	.notifier_call = iucv_reboot_event,
8146c005961SUrsula Braun };
8156c005961SUrsula Braun 
8162356f4cbSMartin Schwidefsky /**
8172356f4cbSMartin Schwidefsky  * iucv_path_accept
8182356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
8192356f4cbSMartin Schwidefsky  * @handler: address of iucv handler structure
8202356f4cbSMartin Schwidefsky  * @userdata: 16 bytes of data reflected to the communication partner
8212356f4cbSMartin Schwidefsky  * @private: private data passed to interrupt handlers for this path
8222356f4cbSMartin Schwidefsky  *
8232356f4cbSMartin Schwidefsky  * This function is issued after the user received a connection pending
8242356f4cbSMartin Schwidefsky  * external interrupt and now wishes to complete the IUCV communication path.
8252356f4cbSMartin Schwidefsky  *
8262356f4cbSMartin Schwidefsky  * Returns the result of the CP IUCV call.
8272356f4cbSMartin Schwidefsky  */
iucv_path_accept(struct iucv_path * path,struct iucv_handler * handler,u8 * userdata,void * private)8282356f4cbSMartin Schwidefsky int iucv_path_accept(struct iucv_path *path, struct iucv_handler *handler,
82991e60eb6SUrsula Braun 		     u8 *userdata, void *private)
8302356f4cbSMartin Schwidefsky {
8312356f4cbSMartin Schwidefsky 	union iucv_param *parm;
8322356f4cbSMartin Schwidefsky 	int rc;
8332356f4cbSMartin Schwidefsky 
8342356f4cbSMartin Schwidefsky 	local_bh_disable();
835f2019030SKOSAKI Motohiro 	if (cpumask_empty(&iucv_buffer_cpumask)) {
8366c005961SUrsula Braun 		rc = -EIO;
8376c005961SUrsula Braun 		goto out;
8386c005961SUrsula Braun 	}
8392356f4cbSMartin Schwidefsky 	/* Prepare parameter block. */
84070cf5035SChristoph Lameter 	parm = iucv_param[smp_processor_id()];
8412356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
8422356f4cbSMartin Schwidefsky 	parm->ctrl.ippathid = path->pathid;
8432356f4cbSMartin Schwidefsky 	parm->ctrl.ipmsglim = path->msglim;
8442356f4cbSMartin Schwidefsky 	if (userdata)
8452356f4cbSMartin Schwidefsky 		memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser));
8462356f4cbSMartin Schwidefsky 	parm->ctrl.ipflags1 = path->flags;
8472356f4cbSMartin Schwidefsky 
8482356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_ACCEPT, parm);
8492356f4cbSMartin Schwidefsky 	if (!rc) {
8502356f4cbSMartin Schwidefsky 		path->private = private;
8512356f4cbSMartin Schwidefsky 		path->msglim = parm->ctrl.ipmsglim;
8522356f4cbSMartin Schwidefsky 		path->flags = parm->ctrl.ipflags1;
8532356f4cbSMartin Schwidefsky 	}
8546c005961SUrsula Braun out:
8552356f4cbSMartin Schwidefsky 	local_bh_enable();
8562356f4cbSMartin Schwidefsky 	return rc;
8572356f4cbSMartin Schwidefsky }
858da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_path_accept);
8592356f4cbSMartin Schwidefsky 
8602356f4cbSMartin Schwidefsky /**
8612356f4cbSMartin Schwidefsky  * iucv_path_connect
8622356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
8632356f4cbSMartin Schwidefsky  * @handler: address of iucv handler structure
8642356f4cbSMartin Schwidefsky  * @userid: 8-byte user identification
8652356f4cbSMartin Schwidefsky  * @system: 8-byte target system identification
8662356f4cbSMartin Schwidefsky  * @userdata: 16 bytes of data reflected to the communication partner
8672356f4cbSMartin Schwidefsky  * @private: private data passed to interrupt handlers for this path
8682356f4cbSMartin Schwidefsky  *
8692356f4cbSMartin Schwidefsky  * This function establishes an IUCV path. Although the connect may complete
8702356f4cbSMartin Schwidefsky  * successfully, you are not able to use the path until you receive an IUCV
8712356f4cbSMartin Schwidefsky  * Connection Complete external interrupt.
8722356f4cbSMartin Schwidefsky  *
8732356f4cbSMartin Schwidefsky  * Returns the result of the CP IUCV call.
8742356f4cbSMartin Schwidefsky  */
iucv_path_connect(struct iucv_path * path,struct iucv_handler * handler,u8 * userid,u8 * system,u8 * userdata,void * private)8752356f4cbSMartin Schwidefsky int iucv_path_connect(struct iucv_path *path, struct iucv_handler *handler,
87691e60eb6SUrsula Braun 		      u8 *userid, u8 *system, u8 *userdata,
8772356f4cbSMartin Schwidefsky 		      void *private)
8782356f4cbSMartin Schwidefsky {
8792356f4cbSMartin Schwidefsky 	union iucv_param *parm;
8802356f4cbSMartin Schwidefsky 	int rc;
8812356f4cbSMartin Schwidefsky 
8822356f4cbSMartin Schwidefsky 	spin_lock_bh(&iucv_table_lock);
88304b090d5SMartin Schwidefsky 	iucv_cleanup_queue();
884f2019030SKOSAKI Motohiro 	if (cpumask_empty(&iucv_buffer_cpumask)) {
8856c005961SUrsula Braun 		rc = -EIO;
8866c005961SUrsula Braun 		goto out;
8876c005961SUrsula Braun 	}
88870cf5035SChristoph Lameter 	parm = iucv_param[smp_processor_id()];
8892356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
8902356f4cbSMartin Schwidefsky 	parm->ctrl.ipmsglim = path->msglim;
8912356f4cbSMartin Schwidefsky 	parm->ctrl.ipflags1 = path->flags;
8922356f4cbSMartin Schwidefsky 	if (userid) {
8932356f4cbSMartin Schwidefsky 		memcpy(parm->ctrl.ipvmid, userid, sizeof(parm->ctrl.ipvmid));
8942356f4cbSMartin Schwidefsky 		ASCEBC(parm->ctrl.ipvmid, sizeof(parm->ctrl.ipvmid));
8952356f4cbSMartin Schwidefsky 		EBC_TOUPPER(parm->ctrl.ipvmid, sizeof(parm->ctrl.ipvmid));
8962356f4cbSMartin Schwidefsky 	}
8972356f4cbSMartin Schwidefsky 	if (system) {
8982356f4cbSMartin Schwidefsky 		memcpy(parm->ctrl.iptarget, system,
8992356f4cbSMartin Schwidefsky 		       sizeof(parm->ctrl.iptarget));
9002356f4cbSMartin Schwidefsky 		ASCEBC(parm->ctrl.iptarget, sizeof(parm->ctrl.iptarget));
9012356f4cbSMartin Schwidefsky 		EBC_TOUPPER(parm->ctrl.iptarget, sizeof(parm->ctrl.iptarget));
9022356f4cbSMartin Schwidefsky 	}
9032356f4cbSMartin Schwidefsky 	if (userdata)
9042356f4cbSMartin Schwidefsky 		memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser));
9052356f4cbSMartin Schwidefsky 
9062356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_CONNECT, parm);
9072356f4cbSMartin Schwidefsky 	if (!rc) {
9082356f4cbSMartin Schwidefsky 		if (parm->ctrl.ippathid < iucv_max_pathid) {
9092356f4cbSMartin Schwidefsky 			path->pathid = parm->ctrl.ippathid;
9102356f4cbSMartin Schwidefsky 			path->msglim = parm->ctrl.ipmsglim;
9112356f4cbSMartin Schwidefsky 			path->flags = parm->ctrl.ipflags1;
9122356f4cbSMartin Schwidefsky 			path->handler = handler;
9132356f4cbSMartin Schwidefsky 			path->private = private;
9142356f4cbSMartin Schwidefsky 			list_add_tail(&path->list, &handler->paths);
9152356f4cbSMartin Schwidefsky 			iucv_path_table[path->pathid] = path;
9162356f4cbSMartin Schwidefsky 		} else {
9172356f4cbSMartin Schwidefsky 			iucv_sever_pathid(parm->ctrl.ippathid,
9182356f4cbSMartin Schwidefsky 					  iucv_error_pathid);
9192356f4cbSMartin Schwidefsky 			rc = -EIO;
9202356f4cbSMartin Schwidefsky 		}
9212356f4cbSMartin Schwidefsky 	}
9226c005961SUrsula Braun out:
9232356f4cbSMartin Schwidefsky 	spin_unlock_bh(&iucv_table_lock);
9242356f4cbSMartin Schwidefsky 	return rc;
9252356f4cbSMartin Schwidefsky }
926da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_path_connect);
9272356f4cbSMartin Schwidefsky 
9282356f4cbSMartin Schwidefsky /**
9292356f4cbSMartin Schwidefsky  * iucv_path_quiesce:
9302356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
9312356f4cbSMartin Schwidefsky  * @userdata: 16 bytes of data reflected to the communication partner
9322356f4cbSMartin Schwidefsky  *
9332356f4cbSMartin Schwidefsky  * This function temporarily suspends incoming messages on an IUCV path.
9342356f4cbSMartin Schwidefsky  * You can later reactivate the path by invoking the iucv_resume function.
9352356f4cbSMartin Schwidefsky  *
9362356f4cbSMartin Schwidefsky  * Returns the result from the CP IUCV call.
9372356f4cbSMartin Schwidefsky  */
iucv_path_quiesce(struct iucv_path * path,u8 * userdata)93891e60eb6SUrsula Braun int iucv_path_quiesce(struct iucv_path *path, u8 *userdata)
9392356f4cbSMartin Schwidefsky {
9402356f4cbSMartin Schwidefsky 	union iucv_param *parm;
9412356f4cbSMartin Schwidefsky 	int rc;
9422356f4cbSMartin Schwidefsky 
9432356f4cbSMartin Schwidefsky 	local_bh_disable();
944f2019030SKOSAKI Motohiro 	if (cpumask_empty(&iucv_buffer_cpumask)) {
9456c005961SUrsula Braun 		rc = -EIO;
9466c005961SUrsula Braun 		goto out;
9476c005961SUrsula Braun 	}
94870cf5035SChristoph Lameter 	parm = iucv_param[smp_processor_id()];
9492356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
9502356f4cbSMartin Schwidefsky 	if (userdata)
9512356f4cbSMartin Schwidefsky 		memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser));
9522356f4cbSMartin Schwidefsky 	parm->ctrl.ippathid = path->pathid;
9532356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_QUIESCE, parm);
9546c005961SUrsula Braun out:
9552356f4cbSMartin Schwidefsky 	local_bh_enable();
9562356f4cbSMartin Schwidefsky 	return rc;
9572356f4cbSMartin Schwidefsky }
958da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_path_quiesce);
9592356f4cbSMartin Schwidefsky 
9602356f4cbSMartin Schwidefsky /**
9612356f4cbSMartin Schwidefsky  * iucv_path_resume:
9622356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
9632356f4cbSMartin Schwidefsky  * @userdata: 16 bytes of data reflected to the communication partner
9642356f4cbSMartin Schwidefsky  *
9652356f4cbSMartin Schwidefsky  * This function resumes incoming messages on an IUCV path that has
9662356f4cbSMartin Schwidefsky  * been stopped with iucv_path_quiesce.
9672356f4cbSMartin Schwidefsky  *
9682356f4cbSMartin Schwidefsky  * Returns the result from the CP IUCV call.
9692356f4cbSMartin Schwidefsky  */
iucv_path_resume(struct iucv_path * path,u8 * userdata)97091e60eb6SUrsula Braun int iucv_path_resume(struct iucv_path *path, u8 *userdata)
9712356f4cbSMartin Schwidefsky {
9722356f4cbSMartin Schwidefsky 	union iucv_param *parm;
9732356f4cbSMartin Schwidefsky 	int rc;
9742356f4cbSMartin Schwidefsky 
9752356f4cbSMartin Schwidefsky 	local_bh_disable();
976f2019030SKOSAKI Motohiro 	if (cpumask_empty(&iucv_buffer_cpumask)) {
9776c005961SUrsula Braun 		rc = -EIO;
9786c005961SUrsula Braun 		goto out;
9796c005961SUrsula Braun 	}
98070cf5035SChristoph Lameter 	parm = iucv_param[smp_processor_id()];
9812356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
9822356f4cbSMartin Schwidefsky 	if (userdata)
9832356f4cbSMartin Schwidefsky 		memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser));
9842356f4cbSMartin Schwidefsky 	parm->ctrl.ippathid = path->pathid;
9852356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_RESUME, parm);
9866c005961SUrsula Braun out:
9872356f4cbSMartin Schwidefsky 	local_bh_enable();
9882356f4cbSMartin Schwidefsky 	return rc;
9892356f4cbSMartin Schwidefsky }
9902356f4cbSMartin Schwidefsky 
9912356f4cbSMartin Schwidefsky /**
9922356f4cbSMartin Schwidefsky  * iucv_path_sever
9932356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
9942356f4cbSMartin Schwidefsky  * @userdata: 16 bytes of data reflected to the communication partner
9952356f4cbSMartin Schwidefsky  *
9962356f4cbSMartin Schwidefsky  * This function terminates an IUCV path.
9972356f4cbSMartin Schwidefsky  *
9982356f4cbSMartin Schwidefsky  * Returns the result from the CP IUCV call.
9992356f4cbSMartin Schwidefsky  */
iucv_path_sever(struct iucv_path * path,u8 * userdata)100091e60eb6SUrsula Braun int iucv_path_sever(struct iucv_path *path, u8 *userdata)
10012356f4cbSMartin Schwidefsky {
10022356f4cbSMartin Schwidefsky 	int rc;
10032356f4cbSMartin Schwidefsky 
10042356f4cbSMartin Schwidefsky 	preempt_disable();
1005f2019030SKOSAKI Motohiro 	if (cpumask_empty(&iucv_buffer_cpumask)) {
10066c005961SUrsula Braun 		rc = -EIO;
10076c005961SUrsula Braun 		goto out;
10086c005961SUrsula Braun 	}
100904b090d5SMartin Schwidefsky 	if (iucv_active_cpu != smp_processor_id())
10102356f4cbSMartin Schwidefsky 		spin_lock_bh(&iucv_table_lock);
10112356f4cbSMartin Schwidefsky 	rc = iucv_sever_pathid(path->pathid, userdata);
10122356f4cbSMartin Schwidefsky 	iucv_path_table[path->pathid] = NULL;
10132356f4cbSMartin Schwidefsky 	list_del_init(&path->list);
101404b090d5SMartin Schwidefsky 	if (iucv_active_cpu != smp_processor_id())
10152356f4cbSMartin Schwidefsky 		spin_unlock_bh(&iucv_table_lock);
10166c005961SUrsula Braun out:
10172356f4cbSMartin Schwidefsky 	preempt_enable();
10182356f4cbSMartin Schwidefsky 	return rc;
10192356f4cbSMartin Schwidefsky }
1020da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_path_sever);
10212356f4cbSMartin Schwidefsky 
10222356f4cbSMartin Schwidefsky /**
10232356f4cbSMartin Schwidefsky  * iucv_message_purge
10242356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
10252356f4cbSMartin Schwidefsky  * @msg: address of iucv msg structure
10262356f4cbSMartin Schwidefsky  * @srccls: source class of message
10272356f4cbSMartin Schwidefsky  *
10282356f4cbSMartin Schwidefsky  * Cancels a message you have sent.
10292356f4cbSMartin Schwidefsky  *
10302356f4cbSMartin Schwidefsky  * Returns the result from the CP IUCV call.
10312356f4cbSMartin Schwidefsky  */
iucv_message_purge(struct iucv_path * path,struct iucv_message * msg,u32 srccls)10322356f4cbSMartin Schwidefsky int iucv_message_purge(struct iucv_path *path, struct iucv_message *msg,
10332356f4cbSMartin Schwidefsky 		       u32 srccls)
10342356f4cbSMartin Schwidefsky {
10352356f4cbSMartin Schwidefsky 	union iucv_param *parm;
10362356f4cbSMartin Schwidefsky 	int rc;
10372356f4cbSMartin Schwidefsky 
10382356f4cbSMartin Schwidefsky 	local_bh_disable();
1039f2019030SKOSAKI Motohiro 	if (cpumask_empty(&iucv_buffer_cpumask)) {
10406c005961SUrsula Braun 		rc = -EIO;
10416c005961SUrsula Braun 		goto out;
10426c005961SUrsula Braun 	}
104370cf5035SChristoph Lameter 	parm = iucv_param[smp_processor_id()];
10442356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
10452356f4cbSMartin Schwidefsky 	parm->purge.ippathid = path->pathid;
10462356f4cbSMartin Schwidefsky 	parm->purge.ipmsgid = msg->id;
10472356f4cbSMartin Schwidefsky 	parm->purge.ipsrccls = srccls;
10482356f4cbSMartin Schwidefsky 	parm->purge.ipflags1 = IUCV_IPSRCCLS | IUCV_IPFGMID | IUCV_IPFGPID;
10492356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_PURGE, parm);
10502356f4cbSMartin Schwidefsky 	if (!rc) {
10512356f4cbSMartin Schwidefsky 		msg->audit = (*(u32 *) &parm->purge.ipaudit) >> 8;
10522356f4cbSMartin Schwidefsky 		msg->tag = parm->purge.ipmsgtag;
10532356f4cbSMartin Schwidefsky 	}
10546c005961SUrsula Braun out:
10552356f4cbSMartin Schwidefsky 	local_bh_enable();
10562356f4cbSMartin Schwidefsky 	return rc;
10572356f4cbSMartin Schwidefsky }
1058da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_message_purge);
10592356f4cbSMartin Schwidefsky 
10602356f4cbSMartin Schwidefsky /**
106191d5d45eSHendrik Brueckner  * iucv_message_receive_iprmdata
10622356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
10632356f4cbSMartin Schwidefsky  * @msg: address of iucv msg structure
10642356f4cbSMartin Schwidefsky  * @flags: how the message is received (IUCV_IPBUFLST)
10652356f4cbSMartin Schwidefsky  * @buffer: address of data buffer or address of struct iucv_array
10662356f4cbSMartin Schwidefsky  * @size: length of data buffer
10672356f4cbSMartin Schwidefsky  * @residual:
10682356f4cbSMartin Schwidefsky  *
106991d5d45eSHendrik Brueckner  * Internal function used by iucv_message_receive and __iucv_message_receive
107091d5d45eSHendrik Brueckner  * to receive RMDATA data stored in struct iucv_message.
10712356f4cbSMartin Schwidefsky  */
iucv_message_receive_iprmdata(struct iucv_path * path,struct iucv_message * msg,u8 flags,void * buffer,size_t size,size_t * residual)107291d5d45eSHendrik Brueckner static int iucv_message_receive_iprmdata(struct iucv_path *path,
107391d5d45eSHendrik Brueckner 					 struct iucv_message *msg,
107491d5d45eSHendrik Brueckner 					 u8 flags, void *buffer,
107591d5d45eSHendrik Brueckner 					 size_t size, size_t *residual)
10762356f4cbSMartin Schwidefsky {
10772356f4cbSMartin Schwidefsky 	struct iucv_array *array;
10782356f4cbSMartin Schwidefsky 	u8 *rmmsg;
10792356f4cbSMartin Schwidefsky 	size_t copy;
10802356f4cbSMartin Schwidefsky 
10812356f4cbSMartin Schwidefsky 	/*
10822356f4cbSMartin Schwidefsky 	 * Message is 8 bytes long and has been stored to the
10832356f4cbSMartin Schwidefsky 	 * message descriptor itself.
10842356f4cbSMartin Schwidefsky 	 */
10852356f4cbSMartin Schwidefsky 	if (residual)
10862356f4cbSMartin Schwidefsky 		*residual = abs(size - 8);
10872356f4cbSMartin Schwidefsky 	rmmsg = msg->rmmsg;
10882356f4cbSMartin Schwidefsky 	if (flags & IUCV_IPBUFLST) {
10892356f4cbSMartin Schwidefsky 		/* Copy to struct iucv_array. */
10902356f4cbSMartin Schwidefsky 		size = (size < 8) ? size : 8;
10912356f4cbSMartin Schwidefsky 		for (array = buffer; size > 0; array++) {
10922356f4cbSMartin Schwidefsky 			copy = min_t(size_t, size, array->length);
1093*d8d4da5cSAlexander Gordeev 			memcpy(phys_to_virt(array->address), rmmsg, copy);
10942356f4cbSMartin Schwidefsky 			rmmsg += copy;
10952356f4cbSMartin Schwidefsky 			size -= copy;
10962356f4cbSMartin Schwidefsky 		}
10972356f4cbSMartin Schwidefsky 	} else {
10982356f4cbSMartin Schwidefsky 		/* Copy to direct buffer. */
10992356f4cbSMartin Schwidefsky 		memcpy(buffer, rmmsg, min_t(size_t, size, 8));
11002356f4cbSMartin Schwidefsky 	}
11012356f4cbSMartin Schwidefsky 	return 0;
11022356f4cbSMartin Schwidefsky }
11032356f4cbSMartin Schwidefsky 
110491d5d45eSHendrik Brueckner /**
110591d5d45eSHendrik Brueckner  * __iucv_message_receive
110691d5d45eSHendrik Brueckner  * @path: address of iucv path structure
110791d5d45eSHendrik Brueckner  * @msg: address of iucv msg structure
110891d5d45eSHendrik Brueckner  * @flags: how the message is received (IUCV_IPBUFLST)
110991d5d45eSHendrik Brueckner  * @buffer: address of data buffer or address of struct iucv_array
111091d5d45eSHendrik Brueckner  * @size: length of data buffer
111191d5d45eSHendrik Brueckner  * @residual:
111291d5d45eSHendrik Brueckner  *
111391d5d45eSHendrik Brueckner  * This function receives messages that are being sent to you over
111491d5d45eSHendrik Brueckner  * established paths. This function will deal with RMDATA messages
111591d5d45eSHendrik Brueckner  * embedded in struct iucv_message as well.
111691d5d45eSHendrik Brueckner  *
111791d5d45eSHendrik Brueckner  * Locking:	no locking
111891d5d45eSHendrik Brueckner  *
111991d5d45eSHendrik Brueckner  * Returns the result from the CP IUCV call.
112091d5d45eSHendrik Brueckner  */
__iucv_message_receive(struct iucv_path * path,struct iucv_message * msg,u8 flags,void * buffer,size_t size,size_t * residual)112191d5d45eSHendrik Brueckner int __iucv_message_receive(struct iucv_path *path, struct iucv_message *msg,
112291d5d45eSHendrik Brueckner 			   u8 flags, void *buffer, size_t size, size_t *residual)
112391d5d45eSHendrik Brueckner {
112491d5d45eSHendrik Brueckner 	union iucv_param *parm;
112591d5d45eSHendrik Brueckner 	int rc;
112691d5d45eSHendrik Brueckner 
112791d5d45eSHendrik Brueckner 	if (msg->flags & IUCV_IPRMDATA)
112891d5d45eSHendrik Brueckner 		return iucv_message_receive_iprmdata(path, msg, flags,
112991d5d45eSHendrik Brueckner 						     buffer, size, residual);
1130a29f245eSJulian Wiedmann 	if (cpumask_empty(&iucv_buffer_cpumask))
1131a29f245eSJulian Wiedmann 		return -EIO;
1132a29f245eSJulian Wiedmann 
113370cf5035SChristoph Lameter 	parm = iucv_param[smp_processor_id()];
11342356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
11352356f4cbSMartin Schwidefsky 	parm->db.ipbfadr1 = (u32)(addr_t) buffer;
11362356f4cbSMartin Schwidefsky 	parm->db.ipbfln1f = (u32) size;
11372356f4cbSMartin Schwidefsky 	parm->db.ipmsgid = msg->id;
11382356f4cbSMartin Schwidefsky 	parm->db.ippathid = path->pathid;
11392356f4cbSMartin Schwidefsky 	parm->db.iptrgcls = msg->class;
11402356f4cbSMartin Schwidefsky 	parm->db.ipflags1 = (flags | IUCV_IPFGPID |
11412356f4cbSMartin Schwidefsky 			     IUCV_IPFGMID | IUCV_IPTRGCLS);
11422356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_RECEIVE, parm);
11432356f4cbSMartin Schwidefsky 	if (!rc || rc == 5) {
11442356f4cbSMartin Schwidefsky 		msg->flags = parm->db.ipflags1;
11452356f4cbSMartin Schwidefsky 		if (residual)
11462356f4cbSMartin Schwidefsky 			*residual = parm->db.ipbfln1f;
11472356f4cbSMartin Schwidefsky 	}
114891d5d45eSHendrik Brueckner 	return rc;
114991d5d45eSHendrik Brueckner }
115091d5d45eSHendrik Brueckner EXPORT_SYMBOL(__iucv_message_receive);
115191d5d45eSHendrik Brueckner 
115291d5d45eSHendrik Brueckner /**
115391d5d45eSHendrik Brueckner  * iucv_message_receive
115491d5d45eSHendrik Brueckner  * @path: address of iucv path structure
115591d5d45eSHendrik Brueckner  * @msg: address of iucv msg structure
115691d5d45eSHendrik Brueckner  * @flags: how the message is received (IUCV_IPBUFLST)
115791d5d45eSHendrik Brueckner  * @buffer: address of data buffer or address of struct iucv_array
115891d5d45eSHendrik Brueckner  * @size: length of data buffer
115991d5d45eSHendrik Brueckner  * @residual:
116091d5d45eSHendrik Brueckner  *
116191d5d45eSHendrik Brueckner  * This function receives messages that are being sent to you over
116291d5d45eSHendrik Brueckner  * established paths. This function will deal with RMDATA messages
116391d5d45eSHendrik Brueckner  * embedded in struct iucv_message as well.
116491d5d45eSHendrik Brueckner  *
116591d5d45eSHendrik Brueckner  * Locking:	local_bh_enable/local_bh_disable
116691d5d45eSHendrik Brueckner  *
116791d5d45eSHendrik Brueckner  * Returns the result from the CP IUCV call.
116891d5d45eSHendrik Brueckner  */
iucv_message_receive(struct iucv_path * path,struct iucv_message * msg,u8 flags,void * buffer,size_t size,size_t * residual)116991d5d45eSHendrik Brueckner int iucv_message_receive(struct iucv_path *path, struct iucv_message *msg,
117091d5d45eSHendrik Brueckner 			 u8 flags, void *buffer, size_t size, size_t *residual)
117191d5d45eSHendrik Brueckner {
117291d5d45eSHendrik Brueckner 	int rc;
117391d5d45eSHendrik Brueckner 
117491d5d45eSHendrik Brueckner 	if (msg->flags & IUCV_IPRMDATA)
117591d5d45eSHendrik Brueckner 		return iucv_message_receive_iprmdata(path, msg, flags,
117691d5d45eSHendrik Brueckner 						     buffer, size, residual);
117791d5d45eSHendrik Brueckner 	local_bh_disable();
117891d5d45eSHendrik Brueckner 	rc = __iucv_message_receive(path, msg, flags, buffer, size, residual);
11792356f4cbSMartin Schwidefsky 	local_bh_enable();
11802356f4cbSMartin Schwidefsky 	return rc;
11812356f4cbSMartin Schwidefsky }
1182da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_message_receive);
11832356f4cbSMartin Schwidefsky 
11842356f4cbSMartin Schwidefsky /**
11852356f4cbSMartin Schwidefsky  * iucv_message_reject
11862356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
11872356f4cbSMartin Schwidefsky  * @msg: address of iucv msg structure
11882356f4cbSMartin Schwidefsky  *
11892356f4cbSMartin Schwidefsky  * The reject function refuses a specified message. Between the time you
11902356f4cbSMartin Schwidefsky  * are notified of a message and the time that you complete the message,
11912356f4cbSMartin Schwidefsky  * the message may be rejected.
11922356f4cbSMartin Schwidefsky  *
11932356f4cbSMartin Schwidefsky  * Returns the result from the CP IUCV call.
11942356f4cbSMartin Schwidefsky  */
iucv_message_reject(struct iucv_path * path,struct iucv_message * msg)11952356f4cbSMartin Schwidefsky int iucv_message_reject(struct iucv_path *path, struct iucv_message *msg)
11962356f4cbSMartin Schwidefsky {
11972356f4cbSMartin Schwidefsky 	union iucv_param *parm;
11982356f4cbSMartin Schwidefsky 	int rc;
11992356f4cbSMartin Schwidefsky 
12002356f4cbSMartin Schwidefsky 	local_bh_disable();
1201f2019030SKOSAKI Motohiro 	if (cpumask_empty(&iucv_buffer_cpumask)) {
12026c005961SUrsula Braun 		rc = -EIO;
12036c005961SUrsula Braun 		goto out;
12046c005961SUrsula Braun 	}
120570cf5035SChristoph Lameter 	parm = iucv_param[smp_processor_id()];
12062356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
12072356f4cbSMartin Schwidefsky 	parm->db.ippathid = path->pathid;
12082356f4cbSMartin Schwidefsky 	parm->db.ipmsgid = msg->id;
12092356f4cbSMartin Schwidefsky 	parm->db.iptrgcls = msg->class;
12102356f4cbSMartin Schwidefsky 	parm->db.ipflags1 = (IUCV_IPTRGCLS | IUCV_IPFGMID | IUCV_IPFGPID);
12112356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_REJECT, parm);
12126c005961SUrsula Braun out:
12132356f4cbSMartin Schwidefsky 	local_bh_enable();
12142356f4cbSMartin Schwidefsky 	return rc;
12152356f4cbSMartin Schwidefsky }
1216da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_message_reject);
12172356f4cbSMartin Schwidefsky 
12182356f4cbSMartin Schwidefsky /**
12192356f4cbSMartin Schwidefsky  * iucv_message_reply
12202356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
12212356f4cbSMartin Schwidefsky  * @msg: address of iucv msg structure
12222356f4cbSMartin Schwidefsky  * @flags: how the reply is sent (IUCV_IPRMDATA, IUCV_IPPRTY, IUCV_IPBUFLST)
12232356f4cbSMartin Schwidefsky  * @reply: address of reply data buffer or address of struct iucv_array
12242356f4cbSMartin Schwidefsky  * @size: length of reply data buffer
12252356f4cbSMartin Schwidefsky  *
12262356f4cbSMartin Schwidefsky  * This function responds to the two-way messages that you receive. You
12272356f4cbSMartin Schwidefsky  * must identify completely the message to which you wish to reply. ie,
12282356f4cbSMartin Schwidefsky  * pathid, msgid, and trgcls. Prmmsg signifies the data is moved into
12292356f4cbSMartin Schwidefsky  * the parameter list.
12302356f4cbSMartin Schwidefsky  *
12312356f4cbSMartin Schwidefsky  * Returns the result from the CP IUCV call.
12322356f4cbSMartin Schwidefsky  */
iucv_message_reply(struct iucv_path * path,struct iucv_message * msg,u8 flags,void * reply,size_t size)12332356f4cbSMartin Schwidefsky int iucv_message_reply(struct iucv_path *path, struct iucv_message *msg,
12342356f4cbSMartin Schwidefsky 		       u8 flags, void *reply, size_t size)
12352356f4cbSMartin Schwidefsky {
12362356f4cbSMartin Schwidefsky 	union iucv_param *parm;
12372356f4cbSMartin Schwidefsky 	int rc;
12382356f4cbSMartin Schwidefsky 
12392356f4cbSMartin Schwidefsky 	local_bh_disable();
1240f2019030SKOSAKI Motohiro 	if (cpumask_empty(&iucv_buffer_cpumask)) {
12416c005961SUrsula Braun 		rc = -EIO;
12426c005961SUrsula Braun 		goto out;
12436c005961SUrsula Braun 	}
124470cf5035SChristoph Lameter 	parm = iucv_param[smp_processor_id()];
12452356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
12462356f4cbSMartin Schwidefsky 	if (flags & IUCV_IPRMDATA) {
12472356f4cbSMartin Schwidefsky 		parm->dpl.ippathid = path->pathid;
12482356f4cbSMartin Schwidefsky 		parm->dpl.ipflags1 = flags;
12492356f4cbSMartin Schwidefsky 		parm->dpl.ipmsgid = msg->id;
12502356f4cbSMartin Schwidefsky 		parm->dpl.iptrgcls = msg->class;
12512356f4cbSMartin Schwidefsky 		memcpy(parm->dpl.iprmmsg, reply, min_t(size_t, size, 8));
12522356f4cbSMartin Schwidefsky 	} else {
12532356f4cbSMartin Schwidefsky 		parm->db.ipbfadr1 = (u32)(addr_t) reply;
12542356f4cbSMartin Schwidefsky 		parm->db.ipbfln1f = (u32) size;
12552356f4cbSMartin Schwidefsky 		parm->db.ippathid = path->pathid;
12562356f4cbSMartin Schwidefsky 		parm->db.ipflags1 = flags;
12572356f4cbSMartin Schwidefsky 		parm->db.ipmsgid = msg->id;
12582356f4cbSMartin Schwidefsky 		parm->db.iptrgcls = msg->class;
12592356f4cbSMartin Schwidefsky 	}
12602356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_REPLY, parm);
12616c005961SUrsula Braun out:
12622356f4cbSMartin Schwidefsky 	local_bh_enable();
12632356f4cbSMartin Schwidefsky 	return rc;
12642356f4cbSMartin Schwidefsky }
1265da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_message_reply);
12662356f4cbSMartin Schwidefsky 
12672356f4cbSMartin Schwidefsky /**
126891d5d45eSHendrik Brueckner  * __iucv_message_send
12692356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
12702356f4cbSMartin Schwidefsky  * @msg: address of iucv msg structure
12712356f4cbSMartin Schwidefsky  * @flags: how the message is sent (IUCV_IPRMDATA, IUCV_IPPRTY, IUCV_IPBUFLST)
12722356f4cbSMartin Schwidefsky  * @srccls: source class of message
12732356f4cbSMartin Schwidefsky  * @buffer: address of send buffer or address of struct iucv_array
12742356f4cbSMartin Schwidefsky  * @size: length of send buffer
12752356f4cbSMartin Schwidefsky  *
12762356f4cbSMartin Schwidefsky  * This function transmits data to another application. Data to be
12772356f4cbSMartin Schwidefsky  * transmitted is in a buffer and this is a one-way message and the
12782356f4cbSMartin Schwidefsky  * receiver will not reply to the message.
12792356f4cbSMartin Schwidefsky  *
128091d5d45eSHendrik Brueckner  * Locking:	no locking
128191d5d45eSHendrik Brueckner  *
12822356f4cbSMartin Schwidefsky  * Returns the result from the CP IUCV call.
12832356f4cbSMartin Schwidefsky  */
__iucv_message_send(struct iucv_path * path,struct iucv_message * msg,u8 flags,u32 srccls,void * buffer,size_t size)128491d5d45eSHendrik Brueckner int __iucv_message_send(struct iucv_path *path, struct iucv_message *msg,
12852356f4cbSMartin Schwidefsky 		      u8 flags, u32 srccls, void *buffer, size_t size)
12862356f4cbSMartin Schwidefsky {
12872356f4cbSMartin Schwidefsky 	union iucv_param *parm;
12882356f4cbSMartin Schwidefsky 	int rc;
12892356f4cbSMartin Schwidefsky 
1290f2019030SKOSAKI Motohiro 	if (cpumask_empty(&iucv_buffer_cpumask)) {
12916c005961SUrsula Braun 		rc = -EIO;
12926c005961SUrsula Braun 		goto out;
12936c005961SUrsula Braun 	}
129470cf5035SChristoph Lameter 	parm = iucv_param[smp_processor_id()];
12952356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
12962356f4cbSMartin Schwidefsky 	if (flags & IUCV_IPRMDATA) {
12972356f4cbSMartin Schwidefsky 		/* Message of 8 bytes can be placed into the parameter list. */
12982356f4cbSMartin Schwidefsky 		parm->dpl.ippathid = path->pathid;
12992356f4cbSMartin Schwidefsky 		parm->dpl.ipflags1 = flags | IUCV_IPNORPY;
13002356f4cbSMartin Schwidefsky 		parm->dpl.iptrgcls = msg->class;
13012356f4cbSMartin Schwidefsky 		parm->dpl.ipsrccls = srccls;
13022356f4cbSMartin Schwidefsky 		parm->dpl.ipmsgtag = msg->tag;
13032356f4cbSMartin Schwidefsky 		memcpy(parm->dpl.iprmmsg, buffer, 8);
13042356f4cbSMartin Schwidefsky 	} else {
13052356f4cbSMartin Schwidefsky 		parm->db.ipbfadr1 = (u32)(addr_t) buffer;
13062356f4cbSMartin Schwidefsky 		parm->db.ipbfln1f = (u32) size;
13072356f4cbSMartin Schwidefsky 		parm->db.ippathid = path->pathid;
13082356f4cbSMartin Schwidefsky 		parm->db.ipflags1 = flags | IUCV_IPNORPY;
13092356f4cbSMartin Schwidefsky 		parm->db.iptrgcls = msg->class;
13102356f4cbSMartin Schwidefsky 		parm->db.ipsrccls = srccls;
13112356f4cbSMartin Schwidefsky 		parm->db.ipmsgtag = msg->tag;
13122356f4cbSMartin Schwidefsky 	}
13132356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_SEND, parm);
13142356f4cbSMartin Schwidefsky 	if (!rc)
13152356f4cbSMartin Schwidefsky 		msg->id = parm->db.ipmsgid;
13166c005961SUrsula Braun out:
131791d5d45eSHendrik Brueckner 	return rc;
131891d5d45eSHendrik Brueckner }
131991d5d45eSHendrik Brueckner EXPORT_SYMBOL(__iucv_message_send);
132091d5d45eSHendrik Brueckner 
132191d5d45eSHendrik Brueckner /**
132291d5d45eSHendrik Brueckner  * iucv_message_send
132391d5d45eSHendrik Brueckner  * @path: address of iucv path structure
132491d5d45eSHendrik Brueckner  * @msg: address of iucv msg structure
132591d5d45eSHendrik Brueckner  * @flags: how the message is sent (IUCV_IPRMDATA, IUCV_IPPRTY, IUCV_IPBUFLST)
132691d5d45eSHendrik Brueckner  * @srccls: source class of message
132791d5d45eSHendrik Brueckner  * @buffer: address of send buffer or address of struct iucv_array
132891d5d45eSHendrik Brueckner  * @size: length of send buffer
132991d5d45eSHendrik Brueckner  *
133091d5d45eSHendrik Brueckner  * This function transmits data to another application. Data to be
133191d5d45eSHendrik Brueckner  * transmitted is in a buffer and this is a one-way message and the
133291d5d45eSHendrik Brueckner  * receiver will not reply to the message.
133391d5d45eSHendrik Brueckner  *
133491d5d45eSHendrik Brueckner  * Locking:	local_bh_enable/local_bh_disable
133591d5d45eSHendrik Brueckner  *
133691d5d45eSHendrik Brueckner  * Returns the result from the CP IUCV call.
133791d5d45eSHendrik Brueckner  */
iucv_message_send(struct iucv_path * path,struct iucv_message * msg,u8 flags,u32 srccls,void * buffer,size_t size)133891d5d45eSHendrik Brueckner int iucv_message_send(struct iucv_path *path, struct iucv_message *msg,
133991d5d45eSHendrik Brueckner 		      u8 flags, u32 srccls, void *buffer, size_t size)
134091d5d45eSHendrik Brueckner {
134191d5d45eSHendrik Brueckner 	int rc;
134291d5d45eSHendrik Brueckner 
134391d5d45eSHendrik Brueckner 	local_bh_disable();
134491d5d45eSHendrik Brueckner 	rc = __iucv_message_send(path, msg, flags, srccls, buffer, size);
13452356f4cbSMartin Schwidefsky 	local_bh_enable();
13462356f4cbSMartin Schwidefsky 	return rc;
13472356f4cbSMartin Schwidefsky }
1348da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_message_send);
13492356f4cbSMartin Schwidefsky 
13502356f4cbSMartin Schwidefsky /**
13512356f4cbSMartin Schwidefsky  * iucv_message_send2way
13522356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
13532356f4cbSMartin Schwidefsky  * @msg: address of iucv msg structure
13542356f4cbSMartin Schwidefsky  * @flags: how the message is sent and the reply is received
13552356f4cbSMartin Schwidefsky  *	   (IUCV_IPRMDATA, IUCV_IPBUFLST, IUCV_IPPRTY, IUCV_ANSLST)
13562356f4cbSMartin Schwidefsky  * @srccls: source class of message
13572356f4cbSMartin Schwidefsky  * @buffer: address of send buffer or address of struct iucv_array
13582356f4cbSMartin Schwidefsky  * @size: length of send buffer
1359682026a5SHeiko Carstens  * @answer: address of answer buffer or address of struct iucv_array
13602356f4cbSMartin Schwidefsky  * @asize: size of reply buffer
1361682026a5SHeiko Carstens  * @residual: ignored
13622356f4cbSMartin Schwidefsky  *
13632356f4cbSMartin Schwidefsky  * This function transmits data to another application. Data to be
13642356f4cbSMartin Schwidefsky  * transmitted is in a buffer. The receiver of the send is expected to
13652356f4cbSMartin Schwidefsky  * reply to the message and a buffer is provided into which IUCV moves
13662356f4cbSMartin Schwidefsky  * the reply to this message.
13672356f4cbSMartin Schwidefsky  *
13682356f4cbSMartin Schwidefsky  * Returns the result from the CP IUCV call.
13692356f4cbSMartin Schwidefsky  */
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)13702356f4cbSMartin Schwidefsky int iucv_message_send2way(struct iucv_path *path, struct iucv_message *msg,
13712356f4cbSMartin Schwidefsky 			  u8 flags, u32 srccls, void *buffer, size_t size,
13722356f4cbSMartin Schwidefsky 			  void *answer, size_t asize, size_t *residual)
13732356f4cbSMartin Schwidefsky {
13742356f4cbSMartin Schwidefsky 	union iucv_param *parm;
13752356f4cbSMartin Schwidefsky 	int rc;
13762356f4cbSMartin Schwidefsky 
13772356f4cbSMartin Schwidefsky 	local_bh_disable();
1378f2019030SKOSAKI Motohiro 	if (cpumask_empty(&iucv_buffer_cpumask)) {
13796c005961SUrsula Braun 		rc = -EIO;
13806c005961SUrsula Braun 		goto out;
13816c005961SUrsula Braun 	}
138270cf5035SChristoph Lameter 	parm = iucv_param[smp_processor_id()];
13832356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
13842356f4cbSMartin Schwidefsky 	if (flags & IUCV_IPRMDATA) {
13852356f4cbSMartin Schwidefsky 		parm->dpl.ippathid = path->pathid;
13862356f4cbSMartin Schwidefsky 		parm->dpl.ipflags1 = path->flags;	/* priority message */
13872356f4cbSMartin Schwidefsky 		parm->dpl.iptrgcls = msg->class;
13882356f4cbSMartin Schwidefsky 		parm->dpl.ipsrccls = srccls;
13892356f4cbSMartin Schwidefsky 		parm->dpl.ipmsgtag = msg->tag;
13902356f4cbSMartin Schwidefsky 		parm->dpl.ipbfadr2 = (u32)(addr_t) answer;
13912356f4cbSMartin Schwidefsky 		parm->dpl.ipbfln2f = (u32) asize;
13922356f4cbSMartin Schwidefsky 		memcpy(parm->dpl.iprmmsg, buffer, 8);
13932356f4cbSMartin Schwidefsky 	} else {
13942356f4cbSMartin Schwidefsky 		parm->db.ippathid = path->pathid;
13952356f4cbSMartin Schwidefsky 		parm->db.ipflags1 = path->flags;	/* priority message */
13962356f4cbSMartin Schwidefsky 		parm->db.iptrgcls = msg->class;
13972356f4cbSMartin Schwidefsky 		parm->db.ipsrccls = srccls;
13982356f4cbSMartin Schwidefsky 		parm->db.ipmsgtag = msg->tag;
13992356f4cbSMartin Schwidefsky 		parm->db.ipbfadr1 = (u32)(addr_t) buffer;
14002356f4cbSMartin Schwidefsky 		parm->db.ipbfln1f = (u32) size;
14012356f4cbSMartin Schwidefsky 		parm->db.ipbfadr2 = (u32)(addr_t) answer;
14022356f4cbSMartin Schwidefsky 		parm->db.ipbfln2f = (u32) asize;
14032356f4cbSMartin Schwidefsky 	}
14042356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_SEND, parm);
14052356f4cbSMartin Schwidefsky 	if (!rc)
14062356f4cbSMartin Schwidefsky 		msg->id = parm->db.ipmsgid;
14076c005961SUrsula Braun out:
14082356f4cbSMartin Schwidefsky 	local_bh_enable();
14092356f4cbSMartin Schwidefsky 	return rc;
14102356f4cbSMartin Schwidefsky }
1411da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_message_send2way);
14122356f4cbSMartin Schwidefsky 
14132356f4cbSMartin Schwidefsky struct iucv_path_pending {
14142356f4cbSMartin Schwidefsky 	u16 ippathid;
14152356f4cbSMartin Schwidefsky 	u8  ipflags1;
14162356f4cbSMartin Schwidefsky 	u8  iptype;
14172356f4cbSMartin Schwidefsky 	u16 ipmsglim;
14182356f4cbSMartin Schwidefsky 	u16 res1;
14192356f4cbSMartin Schwidefsky 	u8  ipvmid[8];
14202356f4cbSMartin Schwidefsky 	u8  ipuser[16];
14212356f4cbSMartin Schwidefsky 	u32 res3;
14222356f4cbSMartin Schwidefsky 	u8  ippollfg;
14232356f4cbSMartin Schwidefsky 	u8  res4[3];
1424bc10502dSEric Dumazet } __packed;
14252356f4cbSMartin Schwidefsky 
1426682026a5SHeiko Carstens /**
1427682026a5SHeiko Carstens  * iucv_path_pending
1428682026a5SHeiko Carstens  * @data: Pointer to external interrupt buffer
1429682026a5SHeiko Carstens  *
1430682026a5SHeiko Carstens  * Process connection pending work item. Called from tasklet while holding
1431682026a5SHeiko Carstens  * iucv_table_lock.
1432682026a5SHeiko Carstens  */
iucv_path_pending(struct iucv_irq_data * data)14332356f4cbSMartin Schwidefsky static void iucv_path_pending(struct iucv_irq_data *data)
14342356f4cbSMartin Schwidefsky {
14352356f4cbSMartin Schwidefsky 	struct iucv_path_pending *ipp = (void *) data;
14362356f4cbSMartin Schwidefsky 	struct iucv_handler *handler;
14372356f4cbSMartin Schwidefsky 	struct iucv_path *path;
14382356f4cbSMartin Schwidefsky 	char *error;
14392356f4cbSMartin Schwidefsky 
14402356f4cbSMartin Schwidefsky 	BUG_ON(iucv_path_table[ipp->ippathid]);
14412356f4cbSMartin Schwidefsky 	/* New pathid, handler found. Create a new path struct. */
14422356f4cbSMartin Schwidefsky 	error = iucv_error_no_memory;
14432356f4cbSMartin Schwidefsky 	path = iucv_path_alloc(ipp->ipmsglim, ipp->ipflags1, GFP_ATOMIC);
14442356f4cbSMartin Schwidefsky 	if (!path)
14452356f4cbSMartin Schwidefsky 		goto out_sever;
14462356f4cbSMartin Schwidefsky 	path->pathid = ipp->ippathid;
14472356f4cbSMartin Schwidefsky 	iucv_path_table[path->pathid] = path;
14482356f4cbSMartin Schwidefsky 	EBCASC(ipp->ipvmid, 8);
14492356f4cbSMartin Schwidefsky 
14502356f4cbSMartin Schwidefsky 	/* Call registered handler until one is found that wants the path. */
14512356f4cbSMartin Schwidefsky 	list_for_each_entry(handler, &iucv_handler_list, list) {
14522356f4cbSMartin Schwidefsky 		if (!handler->path_pending)
14532356f4cbSMartin Schwidefsky 			continue;
14542356f4cbSMartin Schwidefsky 		/*
14552356f4cbSMartin Schwidefsky 		 * Add path to handler to allow a call to iucv_path_sever
14562356f4cbSMartin Schwidefsky 		 * inside the path_pending function. If the handler returns
14572356f4cbSMartin Schwidefsky 		 * an error remove the path from the handler again.
14582356f4cbSMartin Schwidefsky 		 */
14592356f4cbSMartin Schwidefsky 		list_add(&path->list, &handler->paths);
14602356f4cbSMartin Schwidefsky 		path->handler = handler;
14612356f4cbSMartin Schwidefsky 		if (!handler->path_pending(path, ipp->ipvmid, ipp->ipuser))
14622356f4cbSMartin Schwidefsky 			return;
14632356f4cbSMartin Schwidefsky 		list_del(&path->list);
14642356f4cbSMartin Schwidefsky 		path->handler = NULL;
14652356f4cbSMartin Schwidefsky 	}
14662356f4cbSMartin Schwidefsky 	/* No handler wanted the path. */
14672356f4cbSMartin Schwidefsky 	iucv_path_table[path->pathid] = NULL;
14682356f4cbSMartin Schwidefsky 	iucv_path_free(path);
14692356f4cbSMartin Schwidefsky 	error = iucv_error_no_listener;
14702356f4cbSMartin Schwidefsky out_sever:
14712356f4cbSMartin Schwidefsky 	iucv_sever_pathid(ipp->ippathid, error);
14722356f4cbSMartin Schwidefsky }
14732356f4cbSMartin Schwidefsky 
14742356f4cbSMartin Schwidefsky struct iucv_path_complete {
14752356f4cbSMartin Schwidefsky 	u16 ippathid;
14762356f4cbSMartin Schwidefsky 	u8  ipflags1;
14772356f4cbSMartin Schwidefsky 	u8  iptype;
14782356f4cbSMartin Schwidefsky 	u16 ipmsglim;
14792356f4cbSMartin Schwidefsky 	u16 res1;
14802356f4cbSMartin Schwidefsky 	u8  res2[8];
14812356f4cbSMartin Schwidefsky 	u8  ipuser[16];
14822356f4cbSMartin Schwidefsky 	u32 res3;
14832356f4cbSMartin Schwidefsky 	u8  ippollfg;
14842356f4cbSMartin Schwidefsky 	u8  res4[3];
1485bc10502dSEric Dumazet } __packed;
14862356f4cbSMartin Schwidefsky 
1487682026a5SHeiko Carstens /**
1488682026a5SHeiko Carstens  * iucv_path_complete
1489682026a5SHeiko Carstens  * @data: Pointer to external interrupt buffer
1490682026a5SHeiko Carstens  *
1491682026a5SHeiko Carstens  * Process connection complete work item. Called from tasklet while holding
1492682026a5SHeiko Carstens  * iucv_table_lock.
1493682026a5SHeiko Carstens  */
iucv_path_complete(struct iucv_irq_data * data)14942356f4cbSMartin Schwidefsky static void iucv_path_complete(struct iucv_irq_data *data)
14952356f4cbSMartin Schwidefsky {
14962356f4cbSMartin Schwidefsky 	struct iucv_path_complete *ipc = (void *) data;
14972356f4cbSMartin Schwidefsky 	struct iucv_path *path = iucv_path_table[ipc->ippathid];
14982356f4cbSMartin Schwidefsky 
1499b8942e3bSHendrik Brueckner 	if (path)
1500b8942e3bSHendrik Brueckner 		path->flags = ipc->ipflags1;
150104b090d5SMartin Schwidefsky 	if (path && path->handler && path->handler->path_complete)
15022356f4cbSMartin Schwidefsky 		path->handler->path_complete(path, ipc->ipuser);
15032356f4cbSMartin Schwidefsky }
15042356f4cbSMartin Schwidefsky 
15052356f4cbSMartin Schwidefsky struct iucv_path_severed {
15062356f4cbSMartin Schwidefsky 	u16 ippathid;
15072356f4cbSMartin Schwidefsky 	u8  res1;
15082356f4cbSMartin Schwidefsky 	u8  iptype;
15092356f4cbSMartin Schwidefsky 	u32 res2;
15102356f4cbSMartin Schwidefsky 	u8  res3[8];
15112356f4cbSMartin Schwidefsky 	u8  ipuser[16];
15122356f4cbSMartin Schwidefsky 	u32 res4;
15132356f4cbSMartin Schwidefsky 	u8  ippollfg;
15142356f4cbSMartin Schwidefsky 	u8  res5[3];
1515bc10502dSEric Dumazet } __packed;
15162356f4cbSMartin Schwidefsky 
1517682026a5SHeiko Carstens /**
1518682026a5SHeiko Carstens  * iucv_path_severed
1519682026a5SHeiko Carstens  * @data: Pointer to external interrupt buffer
1520682026a5SHeiko Carstens  *
1521682026a5SHeiko Carstens  * Process connection severed work item. Called from tasklet while holding
1522682026a5SHeiko Carstens  * iucv_table_lock.
1523682026a5SHeiko Carstens  */
iucv_path_severed(struct iucv_irq_data * data)15242356f4cbSMartin Schwidefsky static void iucv_path_severed(struct iucv_irq_data *data)
15252356f4cbSMartin Schwidefsky {
15262356f4cbSMartin Schwidefsky 	struct iucv_path_severed *ips = (void *) data;
15272356f4cbSMartin Schwidefsky 	struct iucv_path *path = iucv_path_table[ips->ippathid];
15282356f4cbSMartin Schwidefsky 
152904b090d5SMartin Schwidefsky 	if (!path || !path->handler)	/* Already severed */
153004b090d5SMartin Schwidefsky 		return;
15312356f4cbSMartin Schwidefsky 	if (path->handler->path_severed)
15322356f4cbSMartin Schwidefsky 		path->handler->path_severed(path, ips->ipuser);
15332356f4cbSMartin Schwidefsky 	else {
15342356f4cbSMartin Schwidefsky 		iucv_sever_pathid(path->pathid, NULL);
15352356f4cbSMartin Schwidefsky 		iucv_path_table[path->pathid] = NULL;
153642e1b4c2SUrsula Braun 		list_del(&path->list);
15372356f4cbSMartin Schwidefsky 		iucv_path_free(path);
15382356f4cbSMartin Schwidefsky 	}
15392356f4cbSMartin Schwidefsky }
15402356f4cbSMartin Schwidefsky 
15412356f4cbSMartin Schwidefsky struct iucv_path_quiesced {
15422356f4cbSMartin Schwidefsky 	u16 ippathid;
15432356f4cbSMartin Schwidefsky 	u8  res1;
15442356f4cbSMartin Schwidefsky 	u8  iptype;
15452356f4cbSMartin Schwidefsky 	u32 res2;
15462356f4cbSMartin Schwidefsky 	u8  res3[8];
15472356f4cbSMartin Schwidefsky 	u8  ipuser[16];
15482356f4cbSMartin Schwidefsky 	u32 res4;
15492356f4cbSMartin Schwidefsky 	u8  ippollfg;
15502356f4cbSMartin Schwidefsky 	u8  res5[3];
1551bc10502dSEric Dumazet } __packed;
15522356f4cbSMartin Schwidefsky 
1553682026a5SHeiko Carstens /**
1554682026a5SHeiko Carstens  * iucv_path_quiesced
1555682026a5SHeiko Carstens  * @data: Pointer to external interrupt buffer
1556682026a5SHeiko Carstens  *
1557682026a5SHeiko Carstens  * Process connection quiesced work item. Called from tasklet while holding
1558682026a5SHeiko Carstens  * iucv_table_lock.
1559682026a5SHeiko Carstens  */
iucv_path_quiesced(struct iucv_irq_data * data)15602356f4cbSMartin Schwidefsky static void iucv_path_quiesced(struct iucv_irq_data *data)
15612356f4cbSMartin Schwidefsky {
15622356f4cbSMartin Schwidefsky 	struct iucv_path_quiesced *ipq = (void *) data;
15632356f4cbSMartin Schwidefsky 	struct iucv_path *path = iucv_path_table[ipq->ippathid];
15642356f4cbSMartin Schwidefsky 
156504b090d5SMartin Schwidefsky 	if (path && path->handler && path->handler->path_quiesced)
15662356f4cbSMartin Schwidefsky 		path->handler->path_quiesced(path, ipq->ipuser);
15672356f4cbSMartin Schwidefsky }
15682356f4cbSMartin Schwidefsky 
15692356f4cbSMartin Schwidefsky struct iucv_path_resumed {
15702356f4cbSMartin Schwidefsky 	u16 ippathid;
15712356f4cbSMartin Schwidefsky 	u8  res1;
15722356f4cbSMartin Schwidefsky 	u8  iptype;
15732356f4cbSMartin Schwidefsky 	u32 res2;
15742356f4cbSMartin Schwidefsky 	u8  res3[8];
15752356f4cbSMartin Schwidefsky 	u8  ipuser[16];
15762356f4cbSMartin Schwidefsky 	u32 res4;
15772356f4cbSMartin Schwidefsky 	u8  ippollfg;
15782356f4cbSMartin Schwidefsky 	u8  res5[3];
1579bc10502dSEric Dumazet } __packed;
15802356f4cbSMartin Schwidefsky 
1581682026a5SHeiko Carstens /**
1582682026a5SHeiko Carstens  * iucv_path_resumed
1583682026a5SHeiko Carstens  * @data: Pointer to external interrupt buffer
1584682026a5SHeiko Carstens  *
1585682026a5SHeiko Carstens  * Process connection resumed work item. Called from tasklet while holding
1586682026a5SHeiko Carstens  * iucv_table_lock.
1587682026a5SHeiko Carstens  */
iucv_path_resumed(struct iucv_irq_data * data)15882356f4cbSMartin Schwidefsky static void iucv_path_resumed(struct iucv_irq_data *data)
15892356f4cbSMartin Schwidefsky {
15902356f4cbSMartin Schwidefsky 	struct iucv_path_resumed *ipr = (void *) data;
15912356f4cbSMartin Schwidefsky 	struct iucv_path *path = iucv_path_table[ipr->ippathid];
15922356f4cbSMartin Schwidefsky 
159304b090d5SMartin Schwidefsky 	if (path && path->handler && path->handler->path_resumed)
15942356f4cbSMartin Schwidefsky 		path->handler->path_resumed(path, ipr->ipuser);
15952356f4cbSMartin Schwidefsky }
15962356f4cbSMartin Schwidefsky 
15972356f4cbSMartin Schwidefsky struct iucv_message_complete {
15982356f4cbSMartin Schwidefsky 	u16 ippathid;
15992356f4cbSMartin Schwidefsky 	u8  ipflags1;
16002356f4cbSMartin Schwidefsky 	u8  iptype;
16012356f4cbSMartin Schwidefsky 	u32 ipmsgid;
16022356f4cbSMartin Schwidefsky 	u32 ipaudit;
16032356f4cbSMartin Schwidefsky 	u8  iprmmsg[8];
16042356f4cbSMartin Schwidefsky 	u32 ipsrccls;
16052356f4cbSMartin Schwidefsky 	u32 ipmsgtag;
16062356f4cbSMartin Schwidefsky 	u32 res;
16072356f4cbSMartin Schwidefsky 	u32 ipbfln2f;
16082356f4cbSMartin Schwidefsky 	u8  ippollfg;
16092356f4cbSMartin Schwidefsky 	u8  res2[3];
1610bc10502dSEric Dumazet } __packed;
16112356f4cbSMartin Schwidefsky 
1612682026a5SHeiko Carstens /**
1613682026a5SHeiko Carstens  * iucv_message_complete
1614682026a5SHeiko Carstens  * @data: Pointer to external interrupt buffer
1615682026a5SHeiko Carstens  *
1616682026a5SHeiko Carstens  * Process message complete work item. Called from tasklet while holding
1617682026a5SHeiko Carstens  * iucv_table_lock.
1618682026a5SHeiko Carstens  */
iucv_message_complete(struct iucv_irq_data * data)16192356f4cbSMartin Schwidefsky static void iucv_message_complete(struct iucv_irq_data *data)
16202356f4cbSMartin Schwidefsky {
16212356f4cbSMartin Schwidefsky 	struct iucv_message_complete *imc = (void *) data;
16222356f4cbSMartin Schwidefsky 	struct iucv_path *path = iucv_path_table[imc->ippathid];
16232356f4cbSMartin Schwidefsky 	struct iucv_message msg;
16242356f4cbSMartin Schwidefsky 
162504b090d5SMartin Schwidefsky 	if (path && path->handler && path->handler->message_complete) {
16262356f4cbSMartin Schwidefsky 		msg.flags = imc->ipflags1;
16272356f4cbSMartin Schwidefsky 		msg.id = imc->ipmsgid;
16282356f4cbSMartin Schwidefsky 		msg.audit = imc->ipaudit;
16292356f4cbSMartin Schwidefsky 		memcpy(msg.rmmsg, imc->iprmmsg, 8);
16302356f4cbSMartin Schwidefsky 		msg.class = imc->ipsrccls;
16312356f4cbSMartin Schwidefsky 		msg.tag = imc->ipmsgtag;
16322356f4cbSMartin Schwidefsky 		msg.length = imc->ipbfln2f;
16332356f4cbSMartin Schwidefsky 		path->handler->message_complete(path, &msg);
16342356f4cbSMartin Schwidefsky 	}
16352356f4cbSMartin Schwidefsky }
16362356f4cbSMartin Schwidefsky 
16372356f4cbSMartin Schwidefsky struct iucv_message_pending {
16382356f4cbSMartin Schwidefsky 	u16 ippathid;
16392356f4cbSMartin Schwidefsky 	u8  ipflags1;
16402356f4cbSMartin Schwidefsky 	u8  iptype;
16412356f4cbSMartin Schwidefsky 	u32 ipmsgid;
16422356f4cbSMartin Schwidefsky 	u32 iptrgcls;
16435140aaa4SKees Cook 	struct {
16442356f4cbSMartin Schwidefsky 		union {
16452356f4cbSMartin Schwidefsky 			u32 iprmmsg1_u32;
16462356f4cbSMartin Schwidefsky 			u8  iprmmsg1[4];
16472356f4cbSMartin Schwidefsky 		} ln1msg1;
16482356f4cbSMartin Schwidefsky 		union {
16492356f4cbSMartin Schwidefsky 			u32 ipbfln1f;
16502356f4cbSMartin Schwidefsky 			u8  iprmmsg2[4];
16512356f4cbSMartin Schwidefsky 		} ln1msg2;
16525140aaa4SKees Cook 	} rmmsg;
16532356f4cbSMartin Schwidefsky 	u32 res1[3];
16542356f4cbSMartin Schwidefsky 	u32 ipbfln2f;
16552356f4cbSMartin Schwidefsky 	u8  ippollfg;
16562356f4cbSMartin Schwidefsky 	u8  res2[3];
1657bc10502dSEric Dumazet } __packed;
16582356f4cbSMartin Schwidefsky 
1659682026a5SHeiko Carstens /**
1660682026a5SHeiko Carstens  * iucv_message_pending
1661682026a5SHeiko Carstens  * @data: Pointer to external interrupt buffer
1662682026a5SHeiko Carstens  *
1663682026a5SHeiko Carstens  * Process message pending work item. Called from tasklet while holding
1664682026a5SHeiko Carstens  * iucv_table_lock.
1665682026a5SHeiko Carstens  */
iucv_message_pending(struct iucv_irq_data * data)16662356f4cbSMartin Schwidefsky static void iucv_message_pending(struct iucv_irq_data *data)
16672356f4cbSMartin Schwidefsky {
16682356f4cbSMartin Schwidefsky 	struct iucv_message_pending *imp = (void *) data;
16692356f4cbSMartin Schwidefsky 	struct iucv_path *path = iucv_path_table[imp->ippathid];
16702356f4cbSMartin Schwidefsky 	struct iucv_message msg;
16712356f4cbSMartin Schwidefsky 
167204b090d5SMartin Schwidefsky 	if (path && path->handler && path->handler->message_pending) {
16732356f4cbSMartin Schwidefsky 		msg.flags = imp->ipflags1;
16742356f4cbSMartin Schwidefsky 		msg.id = imp->ipmsgid;
16752356f4cbSMartin Schwidefsky 		msg.class = imp->iptrgcls;
16762356f4cbSMartin Schwidefsky 		if (imp->ipflags1 & IUCV_IPRMDATA) {
16775140aaa4SKees Cook 			memcpy(msg.rmmsg, &imp->rmmsg, 8);
16782356f4cbSMartin Schwidefsky 			msg.length = 8;
16792356f4cbSMartin Schwidefsky 		} else
16805140aaa4SKees Cook 			msg.length = imp->rmmsg.ln1msg2.ipbfln1f;
16812356f4cbSMartin Schwidefsky 		msg.reply_size = imp->ipbfln2f;
16822356f4cbSMartin Schwidefsky 		path->handler->message_pending(path, &msg);
16832356f4cbSMartin Schwidefsky 	}
16842356f4cbSMartin Schwidefsky }
16852356f4cbSMartin Schwidefsky 
1686682026a5SHeiko Carstens /*
168704b090d5SMartin Schwidefsky  * iucv_tasklet_fn:
16882356f4cbSMartin Schwidefsky  *
16892356f4cbSMartin Schwidefsky  * This tasklet loops over the queue of irq buffers created by
16902356f4cbSMartin Schwidefsky  * iucv_external_interrupt, calls the appropriate action handler
16912356f4cbSMartin Schwidefsky  * and then frees the buffer.
16922356f4cbSMartin Schwidefsky  */
iucv_tasklet_fn(unsigned long ignored)169304b090d5SMartin Schwidefsky static void iucv_tasklet_fn(unsigned long ignored)
16942356f4cbSMartin Schwidefsky {
16952356f4cbSMartin Schwidefsky 	typedef void iucv_irq_fn(struct iucv_irq_data *);
16962356f4cbSMartin Schwidefsky 	static iucv_irq_fn *irq_fn[] = {
16972356f4cbSMartin Schwidefsky 		[0x02] = iucv_path_complete,
16982356f4cbSMartin Schwidefsky 		[0x03] = iucv_path_severed,
16992356f4cbSMartin Schwidefsky 		[0x04] = iucv_path_quiesced,
17002356f4cbSMartin Schwidefsky 		[0x05] = iucv_path_resumed,
17012356f4cbSMartin Schwidefsky 		[0x06] = iucv_message_complete,
17022356f4cbSMartin Schwidefsky 		[0x07] = iucv_message_complete,
17032356f4cbSMartin Schwidefsky 		[0x08] = iucv_message_pending,
17042356f4cbSMartin Schwidefsky 		[0x09] = iucv_message_pending,
17052356f4cbSMartin Schwidefsky 	};
1706b5e78337SDenis Cheng 	LIST_HEAD(task_queue);
170704b090d5SMartin Schwidefsky 	struct iucv_irq_list *p, *n;
17082356f4cbSMartin Schwidefsky 
17092356f4cbSMartin Schwidefsky 	/* Serialize tasklet, iucv_path_sever and iucv_path_connect. */
171013fdc9a7SUrsula Braun 	if (!spin_trylock(&iucv_table_lock)) {
171113fdc9a7SUrsula Braun 		tasklet_schedule(&iucv_tasklet);
171213fdc9a7SUrsula Braun 		return;
171313fdc9a7SUrsula Braun 	}
171404b090d5SMartin Schwidefsky 	iucv_active_cpu = smp_processor_id();
17152356f4cbSMartin Schwidefsky 
171604b090d5SMartin Schwidefsky 	spin_lock_irq(&iucv_queue_lock);
171704b090d5SMartin Schwidefsky 	list_splice_init(&iucv_task_queue, &task_queue);
171804b090d5SMartin Schwidefsky 	spin_unlock_irq(&iucv_queue_lock);
171904b090d5SMartin Schwidefsky 
172004b090d5SMartin Schwidefsky 	list_for_each_entry_safe(p, n, &task_queue, list) {
17212356f4cbSMartin Schwidefsky 		list_del_init(&p->list);
17222356f4cbSMartin Schwidefsky 		irq_fn[p->data.iptype](&p->data);
17232356f4cbSMartin Schwidefsky 		kfree(p);
17242356f4cbSMartin Schwidefsky 	}
17252356f4cbSMartin Schwidefsky 
172604b090d5SMartin Schwidefsky 	iucv_active_cpu = -1;
17272356f4cbSMartin Schwidefsky 	spin_unlock(&iucv_table_lock);
17282356f4cbSMartin Schwidefsky }
17292356f4cbSMartin Schwidefsky 
1730682026a5SHeiko Carstens /*
173104b090d5SMartin Schwidefsky  * iucv_work_fn:
173204b090d5SMartin Schwidefsky  *
173304b090d5SMartin Schwidefsky  * This work function loops over the queue of path pending irq blocks
173404b090d5SMartin Schwidefsky  * created by iucv_external_interrupt, calls the appropriate action
173504b090d5SMartin Schwidefsky  * handler and then frees the buffer.
173604b090d5SMartin Schwidefsky  */
iucv_work_fn(struct work_struct * work)173704b090d5SMartin Schwidefsky static void iucv_work_fn(struct work_struct *work)
173804b090d5SMartin Schwidefsky {
1739b5e78337SDenis Cheng 	LIST_HEAD(work_queue);
174004b090d5SMartin Schwidefsky 	struct iucv_irq_list *p, *n;
174104b090d5SMartin Schwidefsky 
174204b090d5SMartin Schwidefsky 	/* Serialize tasklet, iucv_path_sever and iucv_path_connect. */
174304b090d5SMartin Schwidefsky 	spin_lock_bh(&iucv_table_lock);
174404b090d5SMartin Schwidefsky 	iucv_active_cpu = smp_processor_id();
174504b090d5SMartin Schwidefsky 
174604b090d5SMartin Schwidefsky 	spin_lock_irq(&iucv_queue_lock);
174704b090d5SMartin Schwidefsky 	list_splice_init(&iucv_work_queue, &work_queue);
174804b090d5SMartin Schwidefsky 	spin_unlock_irq(&iucv_queue_lock);
174904b090d5SMartin Schwidefsky 
175004b090d5SMartin Schwidefsky 	iucv_cleanup_queue();
175104b090d5SMartin Schwidefsky 	list_for_each_entry_safe(p, n, &work_queue, list) {
175204b090d5SMartin Schwidefsky 		list_del_init(&p->list);
175304b090d5SMartin Schwidefsky 		iucv_path_pending(&p->data);
175404b090d5SMartin Schwidefsky 		kfree(p);
175504b090d5SMartin Schwidefsky 	}
175604b090d5SMartin Schwidefsky 
175704b090d5SMartin Schwidefsky 	iucv_active_cpu = -1;
175804b090d5SMartin Schwidefsky 	spin_unlock_bh(&iucv_table_lock);
175904b090d5SMartin Schwidefsky }
176004b090d5SMartin Schwidefsky 
1761682026a5SHeiko Carstens /*
17622356f4cbSMartin Schwidefsky  * iucv_external_interrupt
17632356f4cbSMartin Schwidefsky  *
17642356f4cbSMartin Schwidefsky  * Handles external interrupts coming in from CP.
176504b090d5SMartin Schwidefsky  * Places the interrupt buffer on a queue and schedules iucv_tasklet_fn().
17662356f4cbSMartin Schwidefsky  */
iucv_external_interrupt(struct ext_code ext_code,unsigned int param32,unsigned long param64)1767fde15c3aSHeiko Carstens static void iucv_external_interrupt(struct ext_code ext_code,
1768f6649a7eSMartin Schwidefsky 				    unsigned int param32, unsigned long param64)
17692356f4cbSMartin Schwidefsky {
17702356f4cbSMartin Schwidefsky 	struct iucv_irq_data *p;
177104b090d5SMartin Schwidefsky 	struct iucv_irq_list *work;
17722356f4cbSMartin Schwidefsky 
1773420f42ecSHeiko Carstens 	inc_irq_stat(IRQEXT_IUC);
177470cf5035SChristoph Lameter 	p = iucv_irq_data[smp_processor_id()];
17752356f4cbSMartin Schwidefsky 	if (p->ippathid >= iucv_max_pathid) {
1776c2b4afd2SUrsula Braun 		WARN_ON(p->ippathid >= iucv_max_pathid);
17772356f4cbSMartin Schwidefsky 		iucv_sever_pathid(p->ippathid, iucv_error_no_listener);
17782356f4cbSMartin Schwidefsky 		return;
17792356f4cbSMartin Schwidefsky 	}
1780c2b4afd2SUrsula Braun 	BUG_ON(p->iptype  < 0x01 || p->iptype > 0x09);
178104b090d5SMartin Schwidefsky 	work = kmalloc(sizeof(struct iucv_irq_list), GFP_ATOMIC);
17822356f4cbSMartin Schwidefsky 	if (!work) {
178347c4cfc3SJoe Perches 		pr_warn("iucv_external_interrupt: out of memory\n");
17842356f4cbSMartin Schwidefsky 		return;
17852356f4cbSMartin Schwidefsky 	}
17862356f4cbSMartin Schwidefsky 	memcpy(&work->data, p, sizeof(work->data));
178704b090d5SMartin Schwidefsky 	spin_lock(&iucv_queue_lock);
178804b090d5SMartin Schwidefsky 	if (p->iptype == 0x01) {
178904b090d5SMartin Schwidefsky 		/* Path pending interrupt. */
17902356f4cbSMartin Schwidefsky 		list_add_tail(&work->list, &iucv_work_queue);
179104b090d5SMartin Schwidefsky 		schedule_work(&iucv_work);
179204b090d5SMartin Schwidefsky 	} else {
179304b090d5SMartin Schwidefsky 		/* The other interrupts. */
179404b090d5SMartin Schwidefsky 		list_add_tail(&work->list, &iucv_task_queue);
17952356f4cbSMartin Schwidefsky 		tasklet_schedule(&iucv_tasklet);
17962356f4cbSMartin Schwidefsky 	}
179704b090d5SMartin Schwidefsky 	spin_unlock(&iucv_queue_lock);
179804b090d5SMartin Schwidefsky }
17992356f4cbSMartin Schwidefsky 
180096d042a6SFrank Blaschka struct iucv_interface iucv_if = {
180196d042a6SFrank Blaschka 	.message_receive = iucv_message_receive,
180296d042a6SFrank Blaschka 	.__message_receive = __iucv_message_receive,
180396d042a6SFrank Blaschka 	.message_reply = iucv_message_reply,
180496d042a6SFrank Blaschka 	.message_reject = iucv_message_reject,
180596d042a6SFrank Blaschka 	.message_send = iucv_message_send,
180696d042a6SFrank Blaschka 	.__message_send = __iucv_message_send,
180796d042a6SFrank Blaschka 	.message_send2way = iucv_message_send2way,
180896d042a6SFrank Blaschka 	.message_purge = iucv_message_purge,
180996d042a6SFrank Blaschka 	.path_accept = iucv_path_accept,
181096d042a6SFrank Blaschka 	.path_connect = iucv_path_connect,
181196d042a6SFrank Blaschka 	.path_quiesce = iucv_path_quiesce,
181296d042a6SFrank Blaschka 	.path_resume = iucv_path_resume,
181396d042a6SFrank Blaschka 	.path_sever = iucv_path_sever,
181496d042a6SFrank Blaschka 	.iucv_register = iucv_register,
181596d042a6SFrank Blaschka 	.iucv_unregister = iucv_unregister,
181696d042a6SFrank Blaschka 	.bus = NULL,
181796d042a6SFrank Blaschka 	.root = NULL,
181896d042a6SFrank Blaschka };
181996d042a6SFrank Blaschka EXPORT_SYMBOL(iucv_if);
182096d042a6SFrank Blaschka 
182138b48292SSebastian Andrzej Siewior static enum cpuhp_state iucv_online;
18222356f4cbSMartin Schwidefsky /**
18232356f4cbSMartin Schwidefsky  * iucv_init
18242356f4cbSMartin Schwidefsky  *
18252356f4cbSMartin Schwidefsky  * Allocates and initializes various data structures.
18262356f4cbSMartin Schwidefsky  */
iucv_init(void)1827da99f056SHeiko Carstens static int __init iucv_init(void)
18282356f4cbSMartin Schwidefsky {
18292356f4cbSMartin Schwidefsky 	int rc;
18302356f4cbSMartin Schwidefsky 
18312356f4cbSMartin Schwidefsky 	if (!MACHINE_IS_VM) {
18322356f4cbSMartin Schwidefsky 		rc = -EPROTONOSUPPORT;
18332356f4cbSMartin Schwidefsky 		goto out;
18342356f4cbSMartin Schwidefsky 	}
18355beab991SMartin Schwidefsky 	ctl_set_bit(0, 1);
18362356f4cbSMartin Schwidefsky 	rc = iucv_query_maxconn();
18372356f4cbSMartin Schwidefsky 	if (rc)
18385beab991SMartin Schwidefsky 		goto out_ctl;
18391dad093bSThomas Huth 	rc = register_external_irq(EXT_IRQ_IUCV, iucv_external_interrupt);
18402356f4cbSMartin Schwidefsky 	if (rc)
18415beab991SMartin Schwidefsky 		goto out_ctl;
1842035da16fSMark McLoughlin 	iucv_root = root_device_register("iucv");
18432356f4cbSMartin Schwidefsky 	if (IS_ERR(iucv_root)) {
18442356f4cbSMartin Schwidefsky 		rc = PTR_ERR(iucv_root);
18452d7bf367SCornelia Huck 		goto out_int;
18462356f4cbSMartin Schwidefsky 	}
184770cf5035SChristoph Lameter 
184838b48292SSebastian Andrzej Siewior 	rc = cpuhp_setup_state(CPUHP_NET_IUCV_PREPARE, "net/iucv:prepare",
184938b48292SSebastian Andrzej Siewior 			       iucv_cpu_prepare, iucv_cpu_dead);
18502d7bf367SCornelia Huck 	if (rc)
18519c6bafabSSebastian Andrzej Siewior 		goto out_dev;
185238b48292SSebastian Andrzej Siewior 	rc = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "net/iucv:online",
185338b48292SSebastian Andrzej Siewior 			       iucv_cpu_online, iucv_cpu_down_prep);
185438b48292SSebastian Andrzej Siewior 	if (rc < 0)
18559c6bafabSSebastian Andrzej Siewior 		goto out_prep;
185638b48292SSebastian Andrzej Siewior 	iucv_online = rc;
1857a0e247a8SSrivatsa S. Bhat 
18586c005961SUrsula Braun 	rc = register_reboot_notifier(&iucv_reboot_notifier);
18596c005961SUrsula Braun 	if (rc)
18609c6bafabSSebastian Andrzej Siewior 		goto out_remove_hp;
18612356f4cbSMartin Schwidefsky 	ASCEBC(iucv_error_no_listener, 16);
18622356f4cbSMartin Schwidefsky 	ASCEBC(iucv_error_no_memory, 16);
18632356f4cbSMartin Schwidefsky 	ASCEBC(iucv_error_pathid, 16);
18642356f4cbSMartin Schwidefsky 	iucv_available = 1;
18652d7bf367SCornelia Huck 	rc = bus_register(&iucv_bus);
18662d7bf367SCornelia Huck 	if (rc)
18676c005961SUrsula Braun 		goto out_reboot;
186896d042a6SFrank Blaschka 	iucv_if.root = iucv_root;
186996d042a6SFrank Blaschka 	iucv_if.bus = &iucv_bus;
18702356f4cbSMartin Schwidefsky 	return 0;
18712356f4cbSMartin Schwidefsky 
18726c005961SUrsula Braun out_reboot:
18736c005961SUrsula Braun 	unregister_reboot_notifier(&iucv_reboot_notifier);
18749c6bafabSSebastian Andrzej Siewior out_remove_hp:
187538b48292SSebastian Andrzej Siewior 	cpuhp_remove_state(iucv_online);
18769c6bafabSSebastian Andrzej Siewior out_prep:
187738b48292SSebastian Andrzej Siewior 	cpuhp_remove_state(CPUHP_NET_IUCV_PREPARE);
18789c6bafabSSebastian Andrzej Siewior out_dev:
1879035da16fSMark McLoughlin 	root_device_unregister(iucv_root);
18802356f4cbSMartin Schwidefsky out_int:
18811dad093bSThomas Huth 	unregister_external_irq(EXT_IRQ_IUCV, iucv_external_interrupt);
18825beab991SMartin Schwidefsky out_ctl:
18835beab991SMartin Schwidefsky 	ctl_clear_bit(0, 1);
18842356f4cbSMartin Schwidefsky out:
18852356f4cbSMartin Schwidefsky 	return rc;
18862356f4cbSMartin Schwidefsky }
18872356f4cbSMartin Schwidefsky 
18882356f4cbSMartin Schwidefsky /**
18892356f4cbSMartin Schwidefsky  * iucv_exit
18902356f4cbSMartin Schwidefsky  *
18912356f4cbSMartin Schwidefsky  * Frees everything allocated from iucv_init.
18922356f4cbSMartin Schwidefsky  */
iucv_exit(void)1893da99f056SHeiko Carstens static void __exit iucv_exit(void)
18942356f4cbSMartin Schwidefsky {
189504b090d5SMartin Schwidefsky 	struct iucv_irq_list *p, *n;
18962356f4cbSMartin Schwidefsky 
189704b090d5SMartin Schwidefsky 	spin_lock_irq(&iucv_queue_lock);
189804b090d5SMartin Schwidefsky 	list_for_each_entry_safe(p, n, &iucv_task_queue, list)
189904b090d5SMartin Schwidefsky 		kfree(p);
19002356f4cbSMartin Schwidefsky 	list_for_each_entry_safe(p, n, &iucv_work_queue, list)
19012356f4cbSMartin Schwidefsky 		kfree(p);
190204b090d5SMartin Schwidefsky 	spin_unlock_irq(&iucv_queue_lock);
19036c005961SUrsula Braun 	unregister_reboot_notifier(&iucv_reboot_notifier);
190438b48292SSebastian Andrzej Siewior 
190538b48292SSebastian Andrzej Siewior 	cpuhp_remove_state_nocalls(iucv_online);
190638b48292SSebastian Andrzej Siewior 	cpuhp_remove_state(CPUHP_NET_IUCV_PREPARE);
1907035da16fSMark McLoughlin 	root_device_unregister(iucv_root);
19082356f4cbSMartin Schwidefsky 	bus_unregister(&iucv_bus);
19091dad093bSThomas Huth 	unregister_external_irq(EXT_IRQ_IUCV, iucv_external_interrupt);
19102356f4cbSMartin Schwidefsky }
19112356f4cbSMartin Schwidefsky 
19122356f4cbSMartin Schwidefsky subsys_initcall(iucv_init);
19132356f4cbSMartin Schwidefsky module_exit(iucv_exit);
19142356f4cbSMartin Schwidefsky 
19152356f4cbSMartin Schwidefsky MODULE_AUTHOR("(C) 2001 IBM Corp. by Fritz Elfert (felfert@millenux.com)");
19162356f4cbSMartin Schwidefsky MODULE_DESCRIPTION("Linux for S/390 IUCV lowlevel driver");
19172356f4cbSMartin Schwidefsky MODULE_LICENSE("GPL");
1918