xref: /linux/net/iucv/iucv.c (revision 70cf5035)
12356f4cbSMartin Schwidefsky /*
22356f4cbSMartin Schwidefsky  * IUCV base infrastructure.
32356f4cbSMartin Schwidefsky  *
42356f4cbSMartin Schwidefsky  * Copyright 2001, 2006 IBM Deutschland Entwicklung GmbH, IBM Corporation
52356f4cbSMartin Schwidefsky  * Author(s):
62356f4cbSMartin Schwidefsky  *    Original source:
72356f4cbSMartin Schwidefsky  *	Alan Altmark (Alan_Altmark@us.ibm.com)	Sept. 2000
82356f4cbSMartin Schwidefsky  *	Xenia Tkatschow (xenia@us.ibm.com)
92356f4cbSMartin Schwidefsky  *    2Gb awareness and general cleanup:
102356f4cbSMartin Schwidefsky  *	Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com)
112356f4cbSMartin Schwidefsky  *    Rewritten for af_iucv:
122356f4cbSMartin Schwidefsky  *	Martin Schwidefsky <schwidefsky@de.ibm.com>
132356f4cbSMartin Schwidefsky  *
142356f4cbSMartin Schwidefsky  * Documentation used:
152356f4cbSMartin Schwidefsky  *    The original source
162356f4cbSMartin Schwidefsky  *    CP Programming Service, IBM document # SC24-5760
172356f4cbSMartin Schwidefsky  *
182356f4cbSMartin Schwidefsky  * This program is free software; you can redistribute it and/or modify
192356f4cbSMartin Schwidefsky  * it under the terms of the GNU General Public License as published by
202356f4cbSMartin Schwidefsky  * the Free Software Foundation; either version 2, or (at your option)
212356f4cbSMartin Schwidefsky  * any later version.
222356f4cbSMartin Schwidefsky  *
232356f4cbSMartin Schwidefsky  * This program is distributed in the hope that it will be useful,
242356f4cbSMartin Schwidefsky  * but WITHOUT ANY WARRANTY; without even the implied warranty of
252356f4cbSMartin Schwidefsky  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
262356f4cbSMartin Schwidefsky  * GNU General Public License for more details.
272356f4cbSMartin Schwidefsky  *
282356f4cbSMartin Schwidefsky  * You should have received a copy of the GNU General Public License
292356f4cbSMartin Schwidefsky  * along with this program; if not, write to the Free Software
302356f4cbSMartin Schwidefsky  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
312356f4cbSMartin Schwidefsky  */
322356f4cbSMartin Schwidefsky 
332356f4cbSMartin Schwidefsky #include <linux/module.h>
342356f4cbSMartin Schwidefsky #include <linux/moduleparam.h>
352356f4cbSMartin Schwidefsky #include <linux/spinlock.h>
362356f4cbSMartin Schwidefsky #include <linux/kernel.h>
372356f4cbSMartin Schwidefsky #include <linux/slab.h>
382356f4cbSMartin Schwidefsky #include <linux/init.h>
392356f4cbSMartin Schwidefsky #include <linux/interrupt.h>
402356f4cbSMartin Schwidefsky #include <linux/list.h>
412356f4cbSMartin Schwidefsky #include <linux/errno.h>
422356f4cbSMartin Schwidefsky #include <linux/err.h>
432356f4cbSMartin Schwidefsky #include <linux/device.h>
442356f4cbSMartin Schwidefsky #include <linux/cpu.h>
452356f4cbSMartin Schwidefsky #include <net/iucv/iucv.h>
462356f4cbSMartin Schwidefsky #include <asm/atomic.h>
472356f4cbSMartin Schwidefsky #include <asm/ebcdic.h>
482356f4cbSMartin Schwidefsky #include <asm/io.h>
492356f4cbSMartin Schwidefsky #include <asm/s390_ext.h>
502356f4cbSMartin Schwidefsky #include <asm/s390_rdev.h>
512356f4cbSMartin Schwidefsky #include <asm/smp.h>
522356f4cbSMartin Schwidefsky 
532356f4cbSMartin Schwidefsky /*
542356f4cbSMartin Schwidefsky  * FLAGS:
552356f4cbSMartin Schwidefsky  * All flags are defined in the field IPFLAGS1 of each function
562356f4cbSMartin Schwidefsky  * and can be found in CP Programming Services.
572356f4cbSMartin Schwidefsky  * IPSRCCLS - Indicates you have specified a source class.
582356f4cbSMartin Schwidefsky  * IPTRGCLS - Indicates you have specified a target class.
592356f4cbSMartin Schwidefsky  * IPFGPID  - Indicates you have specified a pathid.
602356f4cbSMartin Schwidefsky  * IPFGMID  - Indicates you have specified a message ID.
612356f4cbSMartin Schwidefsky  * IPNORPY  - Indicates a one-way message. No reply expected.
622356f4cbSMartin Schwidefsky  * IPALL    - Indicates that all paths are affected.
632356f4cbSMartin Schwidefsky  */
642356f4cbSMartin Schwidefsky #define IUCV_IPSRCCLS	0x01
652356f4cbSMartin Schwidefsky #define IUCV_IPTRGCLS	0x01
662356f4cbSMartin Schwidefsky #define IUCV_IPFGPID	0x02
672356f4cbSMartin Schwidefsky #define IUCV_IPFGMID	0x04
682356f4cbSMartin Schwidefsky #define IUCV_IPNORPY	0x10
692356f4cbSMartin Schwidefsky #define IUCV_IPALL	0x80
702356f4cbSMartin Schwidefsky 
712356f4cbSMartin Schwidefsky static int iucv_bus_match(struct device *dev, struct device_driver *drv)
722356f4cbSMartin Schwidefsky {
732356f4cbSMartin Schwidefsky 	return 0;
742356f4cbSMartin Schwidefsky }
752356f4cbSMartin Schwidefsky 
762356f4cbSMartin Schwidefsky struct bus_type iucv_bus = {
772356f4cbSMartin Schwidefsky 	.name = "iucv",
782356f4cbSMartin Schwidefsky 	.match = iucv_bus_match,
792356f4cbSMartin Schwidefsky };
80da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_bus);
812356f4cbSMartin Schwidefsky 
822356f4cbSMartin Schwidefsky struct device *iucv_root;
83da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_root);
84da99f056SHeiko Carstens 
852356f4cbSMartin Schwidefsky static int iucv_available;
862356f4cbSMartin Schwidefsky 
872356f4cbSMartin Schwidefsky /* General IUCV interrupt structure */
882356f4cbSMartin Schwidefsky struct iucv_irq_data {
892356f4cbSMartin Schwidefsky 	u16 ippathid;
902356f4cbSMartin Schwidefsky 	u8  ipflags1;
912356f4cbSMartin Schwidefsky 	u8  iptype;
922356f4cbSMartin Schwidefsky 	u32 res2[8];
932356f4cbSMartin Schwidefsky };
942356f4cbSMartin Schwidefsky 
9504b090d5SMartin Schwidefsky struct iucv_irq_list {
962356f4cbSMartin Schwidefsky 	struct list_head list;
972356f4cbSMartin Schwidefsky 	struct iucv_irq_data data;
982356f4cbSMartin Schwidefsky };
992356f4cbSMartin Schwidefsky 
100*70cf5035SChristoph Lameter static struct iucv_irq_data *iucv_irq_data[NR_CPUS];
1012356f4cbSMartin Schwidefsky static cpumask_t iucv_buffer_cpumask = CPU_MASK_NONE;
1022356f4cbSMartin Schwidefsky static cpumask_t iucv_irq_cpumask = CPU_MASK_NONE;
1032356f4cbSMartin Schwidefsky 
10404b090d5SMartin Schwidefsky /*
10504b090d5SMartin Schwidefsky  * Queue of interrupt buffers lock for delivery via the tasklet
10604b090d5SMartin Schwidefsky  * (fast but can't call smp_call_function).
10704b090d5SMartin Schwidefsky  */
10804b090d5SMartin Schwidefsky static LIST_HEAD(iucv_task_queue);
10904b090d5SMartin Schwidefsky 
11004b090d5SMartin Schwidefsky /*
11104b090d5SMartin Schwidefsky  * The tasklet for fast delivery of iucv interrupts.
11204b090d5SMartin Schwidefsky  */
11304b090d5SMartin Schwidefsky static void iucv_tasklet_fn(unsigned long);
11404b090d5SMartin Schwidefsky static DECLARE_TASKLET(iucv_tasklet, iucv_tasklet_fn,0);
11504b090d5SMartin Schwidefsky 
11604b090d5SMartin Schwidefsky /*
11704b090d5SMartin Schwidefsky  * Queue of interrupt buffers for delivery via a work queue
11804b090d5SMartin Schwidefsky  * (slower but can call smp_call_function).
11904b090d5SMartin Schwidefsky  */
12004b090d5SMartin Schwidefsky static LIST_HEAD(iucv_work_queue);
12104b090d5SMartin Schwidefsky 
12204b090d5SMartin Schwidefsky /*
12304b090d5SMartin Schwidefsky  * The work element to deliver path pending interrupts.
12404b090d5SMartin Schwidefsky  */
12504b090d5SMartin Schwidefsky static void iucv_work_fn(struct work_struct *work);
12604b090d5SMartin Schwidefsky static DECLARE_WORK(iucv_work, iucv_work_fn);
12704b090d5SMartin Schwidefsky 
12804b090d5SMartin Schwidefsky /*
12904b090d5SMartin Schwidefsky  * Spinlock protecting task and work queue.
13004b090d5SMartin Schwidefsky  */
13104b090d5SMartin Schwidefsky static DEFINE_SPINLOCK(iucv_queue_lock);
1322356f4cbSMartin Schwidefsky 
1332356f4cbSMartin Schwidefsky enum iucv_command_codes {
1342356f4cbSMartin Schwidefsky 	IUCV_QUERY = 0,
1352356f4cbSMartin Schwidefsky 	IUCV_RETRIEVE_BUFFER = 2,
1362356f4cbSMartin Schwidefsky 	IUCV_SEND = 4,
1372356f4cbSMartin Schwidefsky 	IUCV_RECEIVE = 5,
1382356f4cbSMartin Schwidefsky 	IUCV_REPLY = 6,
1392356f4cbSMartin Schwidefsky 	IUCV_REJECT = 8,
1402356f4cbSMartin Schwidefsky 	IUCV_PURGE = 9,
1412356f4cbSMartin Schwidefsky 	IUCV_ACCEPT = 10,
1422356f4cbSMartin Schwidefsky 	IUCV_CONNECT = 11,
1432356f4cbSMartin Schwidefsky 	IUCV_DECLARE_BUFFER = 12,
1442356f4cbSMartin Schwidefsky 	IUCV_QUIESCE = 13,
1452356f4cbSMartin Schwidefsky 	IUCV_RESUME = 14,
1462356f4cbSMartin Schwidefsky 	IUCV_SEVER = 15,
1472356f4cbSMartin Schwidefsky 	IUCV_SETMASK = 16,
1482356f4cbSMartin Schwidefsky };
1492356f4cbSMartin Schwidefsky 
1502356f4cbSMartin Schwidefsky /*
1512356f4cbSMartin Schwidefsky  * Error messages that are used with the iucv_sever function. They get
1522356f4cbSMartin Schwidefsky  * converted to EBCDIC.
1532356f4cbSMartin Schwidefsky  */
1542356f4cbSMartin Schwidefsky static char iucv_error_no_listener[16] = "NO LISTENER";
1552356f4cbSMartin Schwidefsky static char iucv_error_no_memory[16] = "NO MEMORY";
1562356f4cbSMartin Schwidefsky static char iucv_error_pathid[16] = "INVALID PATHID";
1572356f4cbSMartin Schwidefsky 
1582356f4cbSMartin Schwidefsky /*
1592356f4cbSMartin Schwidefsky  * iucv_handler_list: List of registered handlers.
1602356f4cbSMartin Schwidefsky  */
1612356f4cbSMartin Schwidefsky static LIST_HEAD(iucv_handler_list);
1622356f4cbSMartin Schwidefsky 
1632356f4cbSMartin Schwidefsky /*
1642356f4cbSMartin Schwidefsky  * iucv_path_table: an array of iucv_path structures.
1652356f4cbSMartin Schwidefsky  */
1662356f4cbSMartin Schwidefsky static struct iucv_path **iucv_path_table;
1672356f4cbSMartin Schwidefsky static unsigned long iucv_max_pathid;
1682356f4cbSMartin Schwidefsky 
1692356f4cbSMartin Schwidefsky /*
1702356f4cbSMartin Schwidefsky  * iucv_lock: spinlock protecting iucv_handler_list and iucv_pathid_table
1712356f4cbSMartin Schwidefsky  */
1722356f4cbSMartin Schwidefsky static DEFINE_SPINLOCK(iucv_table_lock);
1732356f4cbSMartin Schwidefsky 
1742356f4cbSMartin Schwidefsky /*
17504b090d5SMartin Schwidefsky  * iucv_active_cpu: contains the number of the cpu executing the tasklet
17604b090d5SMartin Schwidefsky  * or the work handler. Needed for iucv_path_sever called from tasklet.
1772356f4cbSMartin Schwidefsky  */
17804b090d5SMartin Schwidefsky static int iucv_active_cpu = -1;
1792356f4cbSMartin Schwidefsky 
1802356f4cbSMartin Schwidefsky /*
1812356f4cbSMartin Schwidefsky  * Mutex and wait queue for iucv_register/iucv_unregister.
1822356f4cbSMartin Schwidefsky  */
1832356f4cbSMartin Schwidefsky static DEFINE_MUTEX(iucv_register_mutex);
1842356f4cbSMartin Schwidefsky 
1852356f4cbSMartin Schwidefsky /*
1862356f4cbSMartin Schwidefsky  * Counter for number of non-smp capable handlers.
1872356f4cbSMartin Schwidefsky  */
1882356f4cbSMartin Schwidefsky static int iucv_nonsmp_handler;
1892356f4cbSMartin Schwidefsky 
1902356f4cbSMartin Schwidefsky /*
1912356f4cbSMartin Schwidefsky  * IUCV control data structure. Used by iucv_path_accept, iucv_path_connect,
1922356f4cbSMartin Schwidefsky  * iucv_path_quiesce and iucv_path_sever.
1932356f4cbSMartin Schwidefsky  */
1942356f4cbSMartin Schwidefsky struct iucv_cmd_control {
1952356f4cbSMartin Schwidefsky 	u16 ippathid;
1962356f4cbSMartin Schwidefsky 	u8  ipflags1;
1972356f4cbSMartin Schwidefsky 	u8  iprcode;
1982356f4cbSMartin Schwidefsky 	u16 ipmsglim;
1992356f4cbSMartin Schwidefsky 	u16 res1;
2002356f4cbSMartin Schwidefsky 	u8  ipvmid[8];
2012356f4cbSMartin Schwidefsky 	u8  ipuser[16];
2022356f4cbSMartin Schwidefsky 	u8  iptarget[8];
2032356f4cbSMartin Schwidefsky } __attribute__ ((packed,aligned(8)));
2042356f4cbSMartin Schwidefsky 
2052356f4cbSMartin Schwidefsky /*
2062356f4cbSMartin Schwidefsky  * Data in parameter list iucv structure. Used by iucv_message_send,
2072356f4cbSMartin Schwidefsky  * iucv_message_send2way and iucv_message_reply.
2082356f4cbSMartin Schwidefsky  */
2092356f4cbSMartin Schwidefsky struct iucv_cmd_dpl {
2102356f4cbSMartin Schwidefsky 	u16 ippathid;
2112356f4cbSMartin Schwidefsky 	u8  ipflags1;
2122356f4cbSMartin Schwidefsky 	u8  iprcode;
2132356f4cbSMartin Schwidefsky 	u32 ipmsgid;
2142356f4cbSMartin Schwidefsky 	u32 iptrgcls;
2152356f4cbSMartin Schwidefsky 	u8  iprmmsg[8];
2162356f4cbSMartin Schwidefsky 	u32 ipsrccls;
2172356f4cbSMartin Schwidefsky 	u32 ipmsgtag;
2182356f4cbSMartin Schwidefsky 	u32 ipbfadr2;
2192356f4cbSMartin Schwidefsky 	u32 ipbfln2f;
2202356f4cbSMartin Schwidefsky 	u32 res;
2212356f4cbSMartin Schwidefsky } __attribute__ ((packed,aligned(8)));
2222356f4cbSMartin Schwidefsky 
2232356f4cbSMartin Schwidefsky /*
2242356f4cbSMartin Schwidefsky  * Data in buffer iucv structure. Used by iucv_message_receive,
2252356f4cbSMartin Schwidefsky  * iucv_message_reject, iucv_message_send, iucv_message_send2way
2262356f4cbSMartin Schwidefsky  * and iucv_declare_cpu.
2272356f4cbSMartin Schwidefsky  */
2282356f4cbSMartin Schwidefsky struct iucv_cmd_db {
2292356f4cbSMartin Schwidefsky 	u16 ippathid;
2302356f4cbSMartin Schwidefsky 	u8  ipflags1;
2312356f4cbSMartin Schwidefsky 	u8  iprcode;
2322356f4cbSMartin Schwidefsky 	u32 ipmsgid;
2332356f4cbSMartin Schwidefsky 	u32 iptrgcls;
2342356f4cbSMartin Schwidefsky 	u32 ipbfadr1;
2352356f4cbSMartin Schwidefsky 	u32 ipbfln1f;
2362356f4cbSMartin Schwidefsky 	u32 ipsrccls;
2372356f4cbSMartin Schwidefsky 	u32 ipmsgtag;
2382356f4cbSMartin Schwidefsky 	u32 ipbfadr2;
2392356f4cbSMartin Schwidefsky 	u32 ipbfln2f;
2402356f4cbSMartin Schwidefsky 	u32 res;
2412356f4cbSMartin Schwidefsky } __attribute__ ((packed,aligned(8)));
2422356f4cbSMartin Schwidefsky 
2432356f4cbSMartin Schwidefsky /*
2442356f4cbSMartin Schwidefsky  * Purge message iucv structure. Used by iucv_message_purge.
2452356f4cbSMartin Schwidefsky  */
2462356f4cbSMartin Schwidefsky struct iucv_cmd_purge {
2472356f4cbSMartin Schwidefsky 	u16 ippathid;
2482356f4cbSMartin Schwidefsky 	u8  ipflags1;
2492356f4cbSMartin Schwidefsky 	u8  iprcode;
2502356f4cbSMartin Schwidefsky 	u32 ipmsgid;
2512356f4cbSMartin Schwidefsky 	u8  ipaudit[3];
2522356f4cbSMartin Schwidefsky 	u8  res1[5];
2532356f4cbSMartin Schwidefsky 	u32 res2;
2542356f4cbSMartin Schwidefsky 	u32 ipsrccls;
2552356f4cbSMartin Schwidefsky 	u32 ipmsgtag;
2562356f4cbSMartin Schwidefsky 	u32 res3[3];
2572356f4cbSMartin Schwidefsky } __attribute__ ((packed,aligned(8)));
2582356f4cbSMartin Schwidefsky 
2592356f4cbSMartin Schwidefsky /*
2602356f4cbSMartin Schwidefsky  * Set mask iucv structure. Used by iucv_enable_cpu.
2612356f4cbSMartin Schwidefsky  */
2622356f4cbSMartin Schwidefsky struct iucv_cmd_set_mask {
2632356f4cbSMartin Schwidefsky 	u8  ipmask;
2642356f4cbSMartin Schwidefsky 	u8  res1[2];
2652356f4cbSMartin Schwidefsky 	u8  iprcode;
2662356f4cbSMartin Schwidefsky 	u32 res2[9];
2672356f4cbSMartin Schwidefsky } __attribute__ ((packed,aligned(8)));
2682356f4cbSMartin Schwidefsky 
2692356f4cbSMartin Schwidefsky union iucv_param {
2702356f4cbSMartin Schwidefsky 	struct iucv_cmd_control ctrl;
2712356f4cbSMartin Schwidefsky 	struct iucv_cmd_dpl dpl;
2722356f4cbSMartin Schwidefsky 	struct iucv_cmd_db db;
2732356f4cbSMartin Schwidefsky 	struct iucv_cmd_purge purge;
2742356f4cbSMartin Schwidefsky 	struct iucv_cmd_set_mask set_mask;
2752356f4cbSMartin Schwidefsky };
2762356f4cbSMartin Schwidefsky 
2772356f4cbSMartin Schwidefsky /*
2782356f4cbSMartin Schwidefsky  * Anchor for per-cpu IUCV command parameter block.
2792356f4cbSMartin Schwidefsky  */
280*70cf5035SChristoph Lameter static union iucv_param *iucv_param[NR_CPUS];
2812356f4cbSMartin Schwidefsky 
2822356f4cbSMartin Schwidefsky /**
2832356f4cbSMartin Schwidefsky  * iucv_call_b2f0
2842356f4cbSMartin Schwidefsky  * @code: identifier of IUCV call to CP.
2852356f4cbSMartin Schwidefsky  * @parm: pointer to a struct iucv_parm block
2862356f4cbSMartin Schwidefsky  *
2872356f4cbSMartin Schwidefsky  * Calls CP to execute IUCV commands.
2882356f4cbSMartin Schwidefsky  *
2892356f4cbSMartin Schwidefsky  * Returns the result of the CP IUCV call.
2902356f4cbSMartin Schwidefsky  */
2912356f4cbSMartin Schwidefsky static inline int iucv_call_b2f0(int command, union iucv_param *parm)
2922356f4cbSMartin Schwidefsky {
2932356f4cbSMartin Schwidefsky 	register unsigned long reg0 asm ("0");
2942356f4cbSMartin Schwidefsky 	register unsigned long reg1 asm ("1");
2952356f4cbSMartin Schwidefsky 	int ccode;
2962356f4cbSMartin Schwidefsky 
2972356f4cbSMartin Schwidefsky 	reg0 = command;
2982356f4cbSMartin Schwidefsky 	reg1 = virt_to_phys(parm);
2992356f4cbSMartin Schwidefsky 	asm volatile(
3002356f4cbSMartin Schwidefsky 		"	.long 0xb2f01000\n"
3012356f4cbSMartin Schwidefsky 		"	ipm	%0\n"
3022356f4cbSMartin Schwidefsky 		"	srl	%0,28\n"
3032356f4cbSMartin Schwidefsky 		: "=d" (ccode), "=m" (*parm), "+d" (reg0), "+a" (reg1)
3042356f4cbSMartin Schwidefsky 		:  "m" (*parm) : "cc");
3052356f4cbSMartin Schwidefsky 	return (ccode == 1) ? parm->ctrl.iprcode : ccode;
3062356f4cbSMartin Schwidefsky }
3072356f4cbSMartin Schwidefsky 
3082356f4cbSMartin Schwidefsky /**
3092356f4cbSMartin Schwidefsky  * iucv_query_maxconn
3102356f4cbSMartin Schwidefsky  *
3112356f4cbSMartin Schwidefsky  * Determines the maximum number of connections that may be established.
3122356f4cbSMartin Schwidefsky  *
3132356f4cbSMartin Schwidefsky  * Returns the maximum number of connections or -EPERM is IUCV is not
3142356f4cbSMartin Schwidefsky  * available.
3152356f4cbSMartin Schwidefsky  */
3162356f4cbSMartin Schwidefsky static int iucv_query_maxconn(void)
3172356f4cbSMartin Schwidefsky {
3182356f4cbSMartin Schwidefsky 	register unsigned long reg0 asm ("0");
3192356f4cbSMartin Schwidefsky 	register unsigned long reg1 asm ("1");
3202356f4cbSMartin Schwidefsky 	void *param;
3212356f4cbSMartin Schwidefsky 	int ccode;
3222356f4cbSMartin Schwidefsky 
3232356f4cbSMartin Schwidefsky 	param = kzalloc(sizeof(union iucv_param), GFP_KERNEL|GFP_DMA);
3242356f4cbSMartin Schwidefsky 	if (!param)
3252356f4cbSMartin Schwidefsky 		return -ENOMEM;
3262356f4cbSMartin Schwidefsky 	reg0 = IUCV_QUERY;
3272356f4cbSMartin Schwidefsky 	reg1 = (unsigned long) param;
3282356f4cbSMartin Schwidefsky 	asm volatile (
3292356f4cbSMartin Schwidefsky 		"	.long	0xb2f01000\n"
3302356f4cbSMartin Schwidefsky 		"	ipm	%0\n"
3312356f4cbSMartin Schwidefsky 		"	srl	%0,28\n"
3322356f4cbSMartin Schwidefsky 		: "=d" (ccode), "+d" (reg0), "+d" (reg1) : : "cc");
3332356f4cbSMartin Schwidefsky 	if (ccode == 0)
3342356f4cbSMartin Schwidefsky 		iucv_max_pathid = reg0;
3352356f4cbSMartin Schwidefsky 	kfree(param);
3362356f4cbSMartin Schwidefsky 	return ccode ? -EPERM : 0;
3372356f4cbSMartin Schwidefsky }
3382356f4cbSMartin Schwidefsky 
3392356f4cbSMartin Schwidefsky /**
3402356f4cbSMartin Schwidefsky  * iucv_allow_cpu
3412356f4cbSMartin Schwidefsky  * @data: unused
3422356f4cbSMartin Schwidefsky  *
3432356f4cbSMartin Schwidefsky  * Allow iucv interrupts on this cpu.
3442356f4cbSMartin Schwidefsky  */
3452356f4cbSMartin Schwidefsky static void iucv_allow_cpu(void *data)
3462356f4cbSMartin Schwidefsky {
3472356f4cbSMartin Schwidefsky 	int cpu = smp_processor_id();
3482356f4cbSMartin Schwidefsky 	union iucv_param *parm;
3492356f4cbSMartin Schwidefsky 
3502356f4cbSMartin Schwidefsky 	/*
3512356f4cbSMartin Schwidefsky 	 * Enable all iucv interrupts.
3522356f4cbSMartin Schwidefsky 	 * ipmask contains bits for the different interrupts
3532356f4cbSMartin Schwidefsky 	 *	0x80 - Flag to allow nonpriority message pending interrupts
3542356f4cbSMartin Schwidefsky 	 *	0x40 - Flag to allow priority message pending interrupts
3552356f4cbSMartin Schwidefsky 	 *	0x20 - Flag to allow nonpriority message completion interrupts
3562356f4cbSMartin Schwidefsky 	 *	0x10 - Flag to allow priority message completion interrupts
3572356f4cbSMartin Schwidefsky 	 *	0x08 - Flag to allow IUCV control interrupts
3582356f4cbSMartin Schwidefsky 	 */
359*70cf5035SChristoph Lameter 	parm = iucv_param[cpu];
3602356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
3612356f4cbSMartin Schwidefsky 	parm->set_mask.ipmask = 0xf8;
3622356f4cbSMartin Schwidefsky 	iucv_call_b2f0(IUCV_SETMASK, parm);
3632356f4cbSMartin Schwidefsky 
3642356f4cbSMartin Schwidefsky 	/* Set indication that iucv interrupts are allowed for this cpu. */
3652356f4cbSMartin Schwidefsky 	cpu_set(cpu, iucv_irq_cpumask);
3662356f4cbSMartin Schwidefsky }
3672356f4cbSMartin Schwidefsky 
3682356f4cbSMartin Schwidefsky /**
3692356f4cbSMartin Schwidefsky  * iucv_block_cpu
3702356f4cbSMartin Schwidefsky  * @data: unused
3712356f4cbSMartin Schwidefsky  *
3722356f4cbSMartin Schwidefsky  * Block iucv interrupts on this cpu.
3732356f4cbSMartin Schwidefsky  */
3742356f4cbSMartin Schwidefsky static void iucv_block_cpu(void *data)
3752356f4cbSMartin Schwidefsky {
3762356f4cbSMartin Schwidefsky 	int cpu = smp_processor_id();
3772356f4cbSMartin Schwidefsky 	union iucv_param *parm;
3782356f4cbSMartin Schwidefsky 
3792356f4cbSMartin Schwidefsky 	/* Disable all iucv interrupts. */
380*70cf5035SChristoph Lameter 	parm = iucv_param[cpu];
3812356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
3822356f4cbSMartin Schwidefsky 	iucv_call_b2f0(IUCV_SETMASK, parm);
3832356f4cbSMartin Schwidefsky 
3842356f4cbSMartin Schwidefsky 	/* Clear indication that iucv interrupts are allowed for this cpu. */
3852356f4cbSMartin Schwidefsky 	cpu_clear(cpu, iucv_irq_cpumask);
3862356f4cbSMartin Schwidefsky }
3872356f4cbSMartin Schwidefsky 
3882356f4cbSMartin Schwidefsky /**
3892356f4cbSMartin Schwidefsky  * iucv_declare_cpu
3902356f4cbSMartin Schwidefsky  * @data: unused
3912356f4cbSMartin Schwidefsky  *
3923a4fa0a2SRobert P. J. Day  * Declare a interrupt buffer on this cpu.
3932356f4cbSMartin Schwidefsky  */
3942356f4cbSMartin Schwidefsky static void iucv_declare_cpu(void *data)
3952356f4cbSMartin Schwidefsky {
3962356f4cbSMartin Schwidefsky 	int cpu = smp_processor_id();
3972356f4cbSMartin Schwidefsky 	union iucv_param *parm;
3982356f4cbSMartin Schwidefsky 	int rc;
3992356f4cbSMartin Schwidefsky 
4002356f4cbSMartin Schwidefsky 	if (cpu_isset(cpu, iucv_buffer_cpumask))
4012356f4cbSMartin Schwidefsky 		return;
4022356f4cbSMartin Schwidefsky 
4032356f4cbSMartin Schwidefsky 	/* Declare interrupt buffer. */
404*70cf5035SChristoph Lameter 	parm = iucv_param[cpu];
4052356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
406*70cf5035SChristoph Lameter 	parm->db.ipbfadr1 = virt_to_phys(iucv_irq_data[cpu]);
4072356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_DECLARE_BUFFER, parm);
4082356f4cbSMartin Schwidefsky 	if (rc) {
4092356f4cbSMartin Schwidefsky 		char *err = "Unknown";
4102356f4cbSMartin Schwidefsky 		switch (rc) {
4112356f4cbSMartin Schwidefsky 		case 0x03:
4122356f4cbSMartin Schwidefsky 			err = "Directory error";
4132356f4cbSMartin Schwidefsky 			break;
4142356f4cbSMartin Schwidefsky 		case 0x0a:
4152356f4cbSMartin Schwidefsky 			err = "Invalid length";
4162356f4cbSMartin Schwidefsky 			break;
4172356f4cbSMartin Schwidefsky 		case 0x13:
4182356f4cbSMartin Schwidefsky 			err = "Buffer already exists";
4192356f4cbSMartin Schwidefsky 			break;
4202356f4cbSMartin Schwidefsky 		case 0x3e:
4212356f4cbSMartin Schwidefsky 			err = "Buffer overlap";
4222356f4cbSMartin Schwidefsky 			break;
4232356f4cbSMartin Schwidefsky 		case 0x5c:
4242356f4cbSMartin Schwidefsky 			err = "Paging or storage error";
4252356f4cbSMartin Schwidefsky 			break;
4262356f4cbSMartin Schwidefsky 		}
4272356f4cbSMartin Schwidefsky 		printk(KERN_WARNING "iucv_register: iucv_declare_buffer "
4282356f4cbSMartin Schwidefsky 		       "on cpu %i returned error 0x%02x (%s)\n", cpu, rc, err);
4292356f4cbSMartin Schwidefsky 		return;
4302356f4cbSMartin Schwidefsky 	}
4312356f4cbSMartin Schwidefsky 
4322356f4cbSMartin Schwidefsky 	/* Set indication that an iucv buffer exists for this cpu. */
4332356f4cbSMartin Schwidefsky 	cpu_set(cpu, iucv_buffer_cpumask);
4342356f4cbSMartin Schwidefsky 
4352356f4cbSMartin Schwidefsky 	if (iucv_nonsmp_handler == 0 || cpus_empty(iucv_irq_cpumask))
4362356f4cbSMartin Schwidefsky 		/* Enable iucv interrupts on this cpu. */
4372356f4cbSMartin Schwidefsky 		iucv_allow_cpu(NULL);
4382356f4cbSMartin Schwidefsky 	else
4392356f4cbSMartin Schwidefsky 		/* Disable iucv interrupts on this cpu. */
4402356f4cbSMartin Schwidefsky 		iucv_block_cpu(NULL);
4412356f4cbSMartin Schwidefsky }
4422356f4cbSMartin Schwidefsky 
4432356f4cbSMartin Schwidefsky /**
4442356f4cbSMartin Schwidefsky  * iucv_retrieve_cpu
4452356f4cbSMartin Schwidefsky  * @data: unused
4462356f4cbSMartin Schwidefsky  *
4472356f4cbSMartin Schwidefsky  * Retrieve interrupt buffer on this cpu.
4482356f4cbSMartin Schwidefsky  */
4492356f4cbSMartin Schwidefsky static void iucv_retrieve_cpu(void *data)
4502356f4cbSMartin Schwidefsky {
4512356f4cbSMartin Schwidefsky 	int cpu = smp_processor_id();
4522356f4cbSMartin Schwidefsky 	union iucv_param *parm;
4532356f4cbSMartin Schwidefsky 
4542356f4cbSMartin Schwidefsky 	if (!cpu_isset(cpu, iucv_buffer_cpumask))
4552356f4cbSMartin Schwidefsky 		return;
4562356f4cbSMartin Schwidefsky 
4572356f4cbSMartin Schwidefsky 	/* Block iucv interrupts. */
4582356f4cbSMartin Schwidefsky 	iucv_block_cpu(NULL);
4592356f4cbSMartin Schwidefsky 
4602356f4cbSMartin Schwidefsky 	/* Retrieve interrupt buffer. */
461*70cf5035SChristoph Lameter 	parm = iucv_param[cpu];
4622356f4cbSMartin Schwidefsky 	iucv_call_b2f0(IUCV_RETRIEVE_BUFFER, parm);
4632356f4cbSMartin Schwidefsky 
4642356f4cbSMartin Schwidefsky 	/* Clear indication that an iucv buffer exists for this cpu. */
4652356f4cbSMartin Schwidefsky 	cpu_clear(cpu, iucv_buffer_cpumask);
4662356f4cbSMartin Schwidefsky }
4672356f4cbSMartin Schwidefsky 
4682356f4cbSMartin Schwidefsky /**
4692356f4cbSMartin Schwidefsky  * iucv_setmask_smp
4702356f4cbSMartin Schwidefsky  *
4712356f4cbSMartin Schwidefsky  * Allow iucv interrupts on all cpus.
4722356f4cbSMartin Schwidefsky  */
4732356f4cbSMartin Schwidefsky static void iucv_setmask_mp(void)
4742356f4cbSMartin Schwidefsky {
4752356f4cbSMartin Schwidefsky 	int cpu;
4762356f4cbSMartin Schwidefsky 
47704b090d5SMartin Schwidefsky 	preempt_disable();
4782356f4cbSMartin Schwidefsky 	for_each_online_cpu(cpu)
4792356f4cbSMartin Schwidefsky 		/* Enable all cpus with a declared buffer. */
4802356f4cbSMartin Schwidefsky 		if (cpu_isset(cpu, iucv_buffer_cpumask) &&
4812356f4cbSMartin Schwidefsky 		    !cpu_isset(cpu, iucv_irq_cpumask))
4823bb447fcSHeiko Carstens 			smp_call_function_single(cpu, iucv_allow_cpu,
4833bb447fcSHeiko Carstens 						 NULL, 0, 1);
48404b090d5SMartin Schwidefsky 	preempt_enable();
4852356f4cbSMartin Schwidefsky }
4862356f4cbSMartin Schwidefsky 
4872356f4cbSMartin Schwidefsky /**
4882356f4cbSMartin Schwidefsky  * iucv_setmask_up
4892356f4cbSMartin Schwidefsky  *
49004b090d5SMartin Schwidefsky  * Allow iucv interrupts on a single cpu.
4912356f4cbSMartin Schwidefsky  */
4922356f4cbSMartin Schwidefsky static void iucv_setmask_up(void)
4932356f4cbSMartin Schwidefsky {
4942356f4cbSMartin Schwidefsky 	cpumask_t cpumask;
4952356f4cbSMartin Schwidefsky 	int cpu;
4962356f4cbSMartin Schwidefsky 
4972356f4cbSMartin Schwidefsky 	/* Disable all cpu but the first in cpu_irq_cpumask. */
4982356f4cbSMartin Schwidefsky 	cpumask = iucv_irq_cpumask;
4992356f4cbSMartin Schwidefsky 	cpu_clear(first_cpu(iucv_irq_cpumask), cpumask);
5002356f4cbSMartin Schwidefsky 	for_each_cpu_mask(cpu, cpumask)
5013bb447fcSHeiko Carstens 		smp_call_function_single(cpu, iucv_block_cpu, NULL, 0, 1);
5022356f4cbSMartin Schwidefsky }
5032356f4cbSMartin Schwidefsky 
5042356f4cbSMartin Schwidefsky /**
5052356f4cbSMartin Schwidefsky  * iucv_enable
5062356f4cbSMartin Schwidefsky  *
5072356f4cbSMartin Schwidefsky  * This function makes iucv ready for use. It allocates the pathid
5082356f4cbSMartin Schwidefsky  * table, declares an iucv interrupt buffer and enables the iucv
5092356f4cbSMartin Schwidefsky  * interrupts. Called when the first user has registered an iucv
5102356f4cbSMartin Schwidefsky  * handler.
5112356f4cbSMartin Schwidefsky  */
5122356f4cbSMartin Schwidefsky static int iucv_enable(void)
5132356f4cbSMartin Schwidefsky {
5142356f4cbSMartin Schwidefsky 	size_t alloc_size;
5152356f4cbSMartin Schwidefsky 	int cpu, rc;
5162356f4cbSMartin Schwidefsky 
5172356f4cbSMartin Schwidefsky 	rc = -ENOMEM;
5182356f4cbSMartin Schwidefsky 	alloc_size = iucv_max_pathid * sizeof(struct iucv_path);
5192356f4cbSMartin Schwidefsky 	iucv_path_table = kzalloc(alloc_size, GFP_KERNEL);
5202356f4cbSMartin Schwidefsky 	if (!iucv_path_table)
5212356f4cbSMartin Schwidefsky 		goto out;
5222356f4cbSMartin Schwidefsky 	/* Declare per cpu buffers. */
5232356f4cbSMartin Schwidefsky 	rc = -EIO;
52404b090d5SMartin Schwidefsky 	preempt_disable();
5252356f4cbSMartin Schwidefsky 	for_each_online_cpu(cpu)
5263bb447fcSHeiko Carstens 		smp_call_function_single(cpu, iucv_declare_cpu, NULL, 0, 1);
52704b090d5SMartin Schwidefsky 	preempt_enable();
5282356f4cbSMartin Schwidefsky 	if (cpus_empty(iucv_buffer_cpumask))
5292356f4cbSMartin Schwidefsky 		/* No cpu could declare an iucv buffer. */
5302356f4cbSMartin Schwidefsky 		goto out_path;
5312356f4cbSMartin Schwidefsky 	return 0;
5322356f4cbSMartin Schwidefsky 
5332356f4cbSMartin Schwidefsky out_path:
5342356f4cbSMartin Schwidefsky 	kfree(iucv_path_table);
5352356f4cbSMartin Schwidefsky out:
5362356f4cbSMartin Schwidefsky 	return rc;
5372356f4cbSMartin Schwidefsky }
5382356f4cbSMartin Schwidefsky 
5392356f4cbSMartin Schwidefsky /**
5402356f4cbSMartin Schwidefsky  * iucv_disable
5412356f4cbSMartin Schwidefsky  *
5422356f4cbSMartin Schwidefsky  * This function shuts down iucv. It disables iucv interrupts, retrieves
5432356f4cbSMartin Schwidefsky  * the iucv interrupt buffer and frees the pathid table. Called after the
5442356f4cbSMartin Schwidefsky  * last user unregister its iucv handler.
5452356f4cbSMartin Schwidefsky  */
5462356f4cbSMartin Schwidefsky static void iucv_disable(void)
5472356f4cbSMartin Schwidefsky {
5482356f4cbSMartin Schwidefsky 	on_each_cpu(iucv_retrieve_cpu, NULL, 0, 1);
5492356f4cbSMartin Schwidefsky 	kfree(iucv_path_table);
5502356f4cbSMartin Schwidefsky }
5512356f4cbSMartin Schwidefsky 
5522356f4cbSMartin Schwidefsky static int __cpuinit iucv_cpu_notify(struct notifier_block *self,
5532356f4cbSMartin Schwidefsky 				     unsigned long action, void *hcpu)
5542356f4cbSMartin Schwidefsky {
5552356f4cbSMartin Schwidefsky 	cpumask_t cpumask;
5562356f4cbSMartin Schwidefsky 	long cpu = (long) hcpu;
5572356f4cbSMartin Schwidefsky 
5582356f4cbSMartin Schwidefsky 	switch (action) {
5592356f4cbSMartin Schwidefsky 	case CPU_UP_PREPARE:
5608bb78442SRafael J. Wysocki 	case CPU_UP_PREPARE_FROZEN:
561*70cf5035SChristoph Lameter 		iucv_irq_data[cpu] = kmalloc_node(sizeof(struct iucv_irq_data),
562*70cf5035SChristoph Lameter 					GFP_KERNEL|GFP_DMA, cpu_to_node(cpu));
563*70cf5035SChristoph Lameter 		if (!iucv_irq_data[cpu])
5642356f4cbSMartin Schwidefsky 			return NOTIFY_BAD;
565*70cf5035SChristoph Lameter 		iucv_param[cpu] = kmalloc_node(sizeof(union iucv_param),
566*70cf5035SChristoph Lameter 				     GFP_KERNEL|GFP_DMA, cpu_to_node(cpu));
567*70cf5035SChristoph Lameter 		if (!iucv_param[cpu])
5682356f4cbSMartin Schwidefsky 			return NOTIFY_BAD;
5692356f4cbSMartin Schwidefsky 		break;
5702356f4cbSMartin Schwidefsky 	case CPU_UP_CANCELED:
5718bb78442SRafael J. Wysocki 	case CPU_UP_CANCELED_FROZEN:
5722356f4cbSMartin Schwidefsky 	case CPU_DEAD:
5738bb78442SRafael J. Wysocki 	case CPU_DEAD_FROZEN:
574*70cf5035SChristoph Lameter 		kfree(iucv_param[cpu]);
575*70cf5035SChristoph Lameter 		iucv_param[cpu] = NULL;
576*70cf5035SChristoph Lameter 		kfree(iucv_irq_data[cpu]);
577*70cf5035SChristoph Lameter 		iucv_irq_data[cpu] = NULL;
5782356f4cbSMartin Schwidefsky 		break;
5792356f4cbSMartin Schwidefsky 	case CPU_ONLINE:
5808bb78442SRafael J. Wysocki 	case CPU_ONLINE_FROZEN:
5812356f4cbSMartin Schwidefsky 	case CPU_DOWN_FAILED:
5828bb78442SRafael J. Wysocki 	case CPU_DOWN_FAILED_FROZEN:
5833bb447fcSHeiko Carstens 		smp_call_function_single(cpu, iucv_declare_cpu, NULL, 0, 1);
5842356f4cbSMartin Schwidefsky 		break;
5852356f4cbSMartin Schwidefsky 	case CPU_DOWN_PREPARE:
5868bb78442SRafael J. Wysocki 	case CPU_DOWN_PREPARE_FROZEN:
5872356f4cbSMartin Schwidefsky 		cpumask = iucv_buffer_cpumask;
5882356f4cbSMartin Schwidefsky 		cpu_clear(cpu, cpumask);
5892356f4cbSMartin Schwidefsky 		if (cpus_empty(cpumask))
5902356f4cbSMartin Schwidefsky 			/* Can't offline last IUCV enabled cpu. */
5912356f4cbSMartin Schwidefsky 			return NOTIFY_BAD;
5923bb447fcSHeiko Carstens 		smp_call_function_single(cpu, iucv_retrieve_cpu, NULL, 0, 1);
5932356f4cbSMartin Schwidefsky 		if (cpus_empty(iucv_irq_cpumask))
5943bb447fcSHeiko Carstens 			smp_call_function_single(first_cpu(iucv_buffer_cpumask),
5953bb447fcSHeiko Carstens 						 iucv_allow_cpu, NULL, 0, 1);
5962356f4cbSMartin Schwidefsky 		break;
5972356f4cbSMartin Schwidefsky 	}
5982356f4cbSMartin Schwidefsky 	return NOTIFY_OK;
5992356f4cbSMartin Schwidefsky }
6002356f4cbSMartin Schwidefsky 
601da99f056SHeiko Carstens static struct notifier_block __cpuinitdata iucv_cpu_notifier = {
6022356f4cbSMartin Schwidefsky 	.notifier_call = iucv_cpu_notify,
6032356f4cbSMartin Schwidefsky };
6042356f4cbSMartin Schwidefsky 
6052356f4cbSMartin Schwidefsky /**
6062356f4cbSMartin Schwidefsky  * iucv_sever_pathid
6072356f4cbSMartin Schwidefsky  * @pathid: path identification number.
6082356f4cbSMartin Schwidefsky  * @userdata: 16-bytes of user data.
6092356f4cbSMartin Schwidefsky  *
6102356f4cbSMartin Schwidefsky  * Sever an iucv path to free up the pathid. Used internally.
6112356f4cbSMartin Schwidefsky  */
6122356f4cbSMartin Schwidefsky static int iucv_sever_pathid(u16 pathid, u8 userdata[16])
6132356f4cbSMartin Schwidefsky {
6142356f4cbSMartin Schwidefsky 	union iucv_param *parm;
6152356f4cbSMartin Schwidefsky 
616*70cf5035SChristoph Lameter 	parm = iucv_param[smp_processor_id()];
6172356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
6182356f4cbSMartin Schwidefsky 	if (userdata)
6192356f4cbSMartin Schwidefsky 		memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser));
6202356f4cbSMartin Schwidefsky 	parm->ctrl.ippathid = pathid;
6212356f4cbSMartin Schwidefsky 	return iucv_call_b2f0(IUCV_SEVER, parm);
6222356f4cbSMartin Schwidefsky }
6232356f4cbSMartin Schwidefsky 
62404b090d5SMartin Schwidefsky #ifdef CONFIG_SMP
6252356f4cbSMartin Schwidefsky /**
62604b090d5SMartin Schwidefsky  * __iucv_cleanup_queue
6272356f4cbSMartin Schwidefsky  * @dummy: unused dummy argument
6282356f4cbSMartin Schwidefsky  *
6292356f4cbSMartin Schwidefsky  * Nop function called via smp_call_function to force work items from
6302356f4cbSMartin Schwidefsky  * pending external iucv interrupts to the work queue.
6312356f4cbSMartin Schwidefsky  */
63204b090d5SMartin Schwidefsky static void __iucv_cleanup_queue(void *dummy)
6332356f4cbSMartin Schwidefsky {
6342356f4cbSMartin Schwidefsky }
63504b090d5SMartin Schwidefsky #endif
6362356f4cbSMartin Schwidefsky 
6372356f4cbSMartin Schwidefsky /**
63804b090d5SMartin Schwidefsky  * iucv_cleanup_queue
6392356f4cbSMartin Schwidefsky  *
6402356f4cbSMartin Schwidefsky  * Function called after a path has been severed to find all remaining
6412356f4cbSMartin Schwidefsky  * work items for the now stale pathid. The caller needs to hold the
6422356f4cbSMartin Schwidefsky  * iucv_table_lock.
6432356f4cbSMartin Schwidefsky  */
64404b090d5SMartin Schwidefsky static void iucv_cleanup_queue(void)
6452356f4cbSMartin Schwidefsky {
64604b090d5SMartin Schwidefsky 	struct iucv_irq_list *p, *n;
6472356f4cbSMartin Schwidefsky 
6482356f4cbSMartin Schwidefsky 	/*
64904b090d5SMartin Schwidefsky 	 * When a path is severed, the pathid can be reused immediatly
65004b090d5SMartin Schwidefsky 	 * on a iucv connect or a connection pending interrupt. Remove
65104b090d5SMartin Schwidefsky 	 * all entries from the task queue that refer to a stale pathid
65204b090d5SMartin Schwidefsky 	 * (iucv_path_table[ix] == NULL). Only then do the iucv connect
65304b090d5SMartin Schwidefsky 	 * or deliver the connection pending interrupt. To get all the
65404b090d5SMartin Schwidefsky 	 * pending interrupts force them to the work queue by calling
65504b090d5SMartin Schwidefsky 	 * an empty function on all cpus.
6562356f4cbSMartin Schwidefsky 	 */
65704b090d5SMartin Schwidefsky 	smp_call_function(__iucv_cleanup_queue, NULL, 0, 1);
65804b090d5SMartin Schwidefsky 	spin_lock_irq(&iucv_queue_lock);
65904b090d5SMartin Schwidefsky 	list_for_each_entry_safe(p, n, &iucv_task_queue, list) {
66004b090d5SMartin Schwidefsky 		/* Remove stale work items from the task queue. */
66104b090d5SMartin Schwidefsky 		if (iucv_path_table[p->data.ippathid] == NULL) {
6622356f4cbSMartin Schwidefsky 			list_del(&p->list);
6632356f4cbSMartin Schwidefsky 			kfree(p);
6642356f4cbSMartin Schwidefsky 		}
6652356f4cbSMartin Schwidefsky 	}
66604b090d5SMartin Schwidefsky 	spin_unlock_irq(&iucv_queue_lock);
6672356f4cbSMartin Schwidefsky }
6682356f4cbSMartin Schwidefsky 
6692356f4cbSMartin Schwidefsky /**
6702356f4cbSMartin Schwidefsky  * iucv_register:
6712356f4cbSMartin Schwidefsky  * @handler: address of iucv handler structure
6722356f4cbSMartin Schwidefsky  * @smp: != 0 indicates that the handler can deal with out of order messages
6732356f4cbSMartin Schwidefsky  *
6742356f4cbSMartin Schwidefsky  * Registers a driver with IUCV.
6752356f4cbSMartin Schwidefsky  *
6762356f4cbSMartin Schwidefsky  * Returns 0 on success, -ENOMEM if the memory allocation for the pathid
6772356f4cbSMartin Schwidefsky  * table failed, or -EIO if IUCV_DECLARE_BUFFER failed on all cpus.
6782356f4cbSMartin Schwidefsky  */
6792356f4cbSMartin Schwidefsky int iucv_register(struct iucv_handler *handler, int smp)
6802356f4cbSMartin Schwidefsky {
6812356f4cbSMartin Schwidefsky 	int rc;
6822356f4cbSMartin Schwidefsky 
6832356f4cbSMartin Schwidefsky 	if (!iucv_available)
6842356f4cbSMartin Schwidefsky 		return -ENOSYS;
6852356f4cbSMartin Schwidefsky 	mutex_lock(&iucv_register_mutex);
6862356f4cbSMartin Schwidefsky 	if (!smp)
6872356f4cbSMartin Schwidefsky 		iucv_nonsmp_handler++;
6882356f4cbSMartin Schwidefsky 	if (list_empty(&iucv_handler_list)) {
6892356f4cbSMartin Schwidefsky 		rc = iucv_enable();
6902356f4cbSMartin Schwidefsky 		if (rc)
6912356f4cbSMartin Schwidefsky 			goto out_mutex;
6922356f4cbSMartin Schwidefsky 	} else if (!smp && iucv_nonsmp_handler == 1)
6932356f4cbSMartin Schwidefsky 		iucv_setmask_up();
6942356f4cbSMartin Schwidefsky 	INIT_LIST_HEAD(&handler->paths);
6952356f4cbSMartin Schwidefsky 
6962356f4cbSMartin Schwidefsky 	spin_lock_irq(&iucv_table_lock);
6972356f4cbSMartin Schwidefsky 	list_add_tail(&handler->list, &iucv_handler_list);
6982356f4cbSMartin Schwidefsky 	spin_unlock_irq(&iucv_table_lock);
6992356f4cbSMartin Schwidefsky 	rc = 0;
7002356f4cbSMartin Schwidefsky out_mutex:
7012356f4cbSMartin Schwidefsky 	mutex_unlock(&iucv_register_mutex);
7022356f4cbSMartin Schwidefsky 	return rc;
7032356f4cbSMartin Schwidefsky }
704da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_register);
7052356f4cbSMartin Schwidefsky 
7062356f4cbSMartin Schwidefsky /**
7072356f4cbSMartin Schwidefsky  * iucv_unregister
7082356f4cbSMartin Schwidefsky  * @handler:  address of iucv handler structure
7092356f4cbSMartin Schwidefsky  * @smp: != 0 indicates that the handler can deal with out of order messages
7102356f4cbSMartin Schwidefsky  *
7112356f4cbSMartin Schwidefsky  * Unregister driver from IUCV.
7122356f4cbSMartin Schwidefsky  */
7132356f4cbSMartin Schwidefsky void iucv_unregister(struct iucv_handler *handler, int smp)
7142356f4cbSMartin Schwidefsky {
7152356f4cbSMartin Schwidefsky 	struct iucv_path *p, *n;
7162356f4cbSMartin Schwidefsky 
7172356f4cbSMartin Schwidefsky 	mutex_lock(&iucv_register_mutex);
7182356f4cbSMartin Schwidefsky 	spin_lock_bh(&iucv_table_lock);
7192356f4cbSMartin Schwidefsky 	/* Remove handler from the iucv_handler_list. */
7202356f4cbSMartin Schwidefsky 	list_del_init(&handler->list);
7212356f4cbSMartin Schwidefsky 	/* Sever all pathids still refering to the handler. */
7222356f4cbSMartin Schwidefsky 	list_for_each_entry_safe(p, n, &handler->paths, list) {
7232356f4cbSMartin Schwidefsky 		iucv_sever_pathid(p->pathid, NULL);
7242356f4cbSMartin Schwidefsky 		iucv_path_table[p->pathid] = NULL;
7252356f4cbSMartin Schwidefsky 		list_del(&p->list);
7262356f4cbSMartin Schwidefsky 		iucv_path_free(p);
7272356f4cbSMartin Schwidefsky 	}
7282356f4cbSMartin Schwidefsky 	spin_unlock_bh(&iucv_table_lock);
7292356f4cbSMartin Schwidefsky 	if (!smp)
7302356f4cbSMartin Schwidefsky 		iucv_nonsmp_handler--;
7312356f4cbSMartin Schwidefsky 	if (list_empty(&iucv_handler_list))
7322356f4cbSMartin Schwidefsky 		iucv_disable();
7332356f4cbSMartin Schwidefsky 	else if (!smp && iucv_nonsmp_handler == 0)
7342356f4cbSMartin Schwidefsky 		iucv_setmask_mp();
7352356f4cbSMartin Schwidefsky 	mutex_unlock(&iucv_register_mutex);
7362356f4cbSMartin Schwidefsky }
737da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_unregister);
7382356f4cbSMartin Schwidefsky 
7392356f4cbSMartin Schwidefsky /**
7402356f4cbSMartin Schwidefsky  * iucv_path_accept
7412356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
7422356f4cbSMartin Schwidefsky  * @handler: address of iucv handler structure
7432356f4cbSMartin Schwidefsky  * @userdata: 16 bytes of data reflected to the communication partner
7442356f4cbSMartin Schwidefsky  * @private: private data passed to interrupt handlers for this path
7452356f4cbSMartin Schwidefsky  *
7462356f4cbSMartin Schwidefsky  * This function is issued after the user received a connection pending
7472356f4cbSMartin Schwidefsky  * external interrupt and now wishes to complete the IUCV communication path.
7482356f4cbSMartin Schwidefsky  *
7492356f4cbSMartin Schwidefsky  * Returns the result of the CP IUCV call.
7502356f4cbSMartin Schwidefsky  */
7512356f4cbSMartin Schwidefsky int iucv_path_accept(struct iucv_path *path, struct iucv_handler *handler,
7522356f4cbSMartin Schwidefsky 		     u8 userdata[16], void *private)
7532356f4cbSMartin Schwidefsky {
7542356f4cbSMartin Schwidefsky 	union iucv_param *parm;
7552356f4cbSMartin Schwidefsky 	int rc;
7562356f4cbSMartin Schwidefsky 
7572356f4cbSMartin Schwidefsky 	local_bh_disable();
7582356f4cbSMartin Schwidefsky 	/* Prepare parameter block. */
759*70cf5035SChristoph Lameter 	parm = iucv_param[smp_processor_id()];
7602356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
7612356f4cbSMartin Schwidefsky 	parm->ctrl.ippathid = path->pathid;
7622356f4cbSMartin Schwidefsky 	parm->ctrl.ipmsglim = path->msglim;
7632356f4cbSMartin Schwidefsky 	if (userdata)
7642356f4cbSMartin Schwidefsky 		memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser));
7652356f4cbSMartin Schwidefsky 	parm->ctrl.ipflags1 = path->flags;
7662356f4cbSMartin Schwidefsky 
7672356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_ACCEPT, parm);
7682356f4cbSMartin Schwidefsky 	if (!rc) {
7692356f4cbSMartin Schwidefsky 		path->private = private;
7702356f4cbSMartin Schwidefsky 		path->msglim = parm->ctrl.ipmsglim;
7712356f4cbSMartin Schwidefsky 		path->flags = parm->ctrl.ipflags1;
7722356f4cbSMartin Schwidefsky 	}
7732356f4cbSMartin Schwidefsky 	local_bh_enable();
7742356f4cbSMartin Schwidefsky 	return rc;
7752356f4cbSMartin Schwidefsky }
776da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_path_accept);
7772356f4cbSMartin Schwidefsky 
7782356f4cbSMartin Schwidefsky /**
7792356f4cbSMartin Schwidefsky  * iucv_path_connect
7802356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
7812356f4cbSMartin Schwidefsky  * @handler: address of iucv handler structure
7822356f4cbSMartin Schwidefsky  * @userid: 8-byte user identification
7832356f4cbSMartin Schwidefsky  * @system: 8-byte target system identification
7842356f4cbSMartin Schwidefsky  * @userdata: 16 bytes of data reflected to the communication partner
7852356f4cbSMartin Schwidefsky  * @private: private data passed to interrupt handlers for this path
7862356f4cbSMartin Schwidefsky  *
7872356f4cbSMartin Schwidefsky  * This function establishes an IUCV path. Although the connect may complete
7882356f4cbSMartin Schwidefsky  * successfully, you are not able to use the path until you receive an IUCV
7892356f4cbSMartin Schwidefsky  * Connection Complete external interrupt.
7902356f4cbSMartin Schwidefsky  *
7912356f4cbSMartin Schwidefsky  * Returns the result of the CP IUCV call.
7922356f4cbSMartin Schwidefsky  */
7932356f4cbSMartin Schwidefsky int iucv_path_connect(struct iucv_path *path, struct iucv_handler *handler,
7942356f4cbSMartin Schwidefsky 		      u8 userid[8], u8 system[8], u8 userdata[16],
7952356f4cbSMartin Schwidefsky 		      void *private)
7962356f4cbSMartin Schwidefsky {
7972356f4cbSMartin Schwidefsky 	union iucv_param *parm;
7982356f4cbSMartin Schwidefsky 	int rc;
7992356f4cbSMartin Schwidefsky 
80004b090d5SMartin Schwidefsky 	BUG_ON(in_atomic());
8012356f4cbSMartin Schwidefsky 	spin_lock_bh(&iucv_table_lock);
80204b090d5SMartin Schwidefsky 	iucv_cleanup_queue();
803*70cf5035SChristoph Lameter 	parm = iucv_param[smp_processor_id()];
8042356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
8052356f4cbSMartin Schwidefsky 	parm->ctrl.ipmsglim = path->msglim;
8062356f4cbSMartin Schwidefsky 	parm->ctrl.ipflags1 = path->flags;
8072356f4cbSMartin Schwidefsky 	if (userid) {
8082356f4cbSMartin Schwidefsky 		memcpy(parm->ctrl.ipvmid, userid, sizeof(parm->ctrl.ipvmid));
8092356f4cbSMartin Schwidefsky 		ASCEBC(parm->ctrl.ipvmid, sizeof(parm->ctrl.ipvmid));
8102356f4cbSMartin Schwidefsky 		EBC_TOUPPER(parm->ctrl.ipvmid, sizeof(parm->ctrl.ipvmid));
8112356f4cbSMartin Schwidefsky 	}
8122356f4cbSMartin Schwidefsky 	if (system) {
8132356f4cbSMartin Schwidefsky 		memcpy(parm->ctrl.iptarget, system,
8142356f4cbSMartin Schwidefsky 		       sizeof(parm->ctrl.iptarget));
8152356f4cbSMartin Schwidefsky 		ASCEBC(parm->ctrl.iptarget, sizeof(parm->ctrl.iptarget));
8162356f4cbSMartin Schwidefsky 		EBC_TOUPPER(parm->ctrl.iptarget, sizeof(parm->ctrl.iptarget));
8172356f4cbSMartin Schwidefsky 	}
8182356f4cbSMartin Schwidefsky 	if (userdata)
8192356f4cbSMartin Schwidefsky 		memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser));
8202356f4cbSMartin Schwidefsky 
8212356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_CONNECT, parm);
8222356f4cbSMartin Schwidefsky 	if (!rc) {
8232356f4cbSMartin Schwidefsky 		if (parm->ctrl.ippathid < iucv_max_pathid) {
8242356f4cbSMartin Schwidefsky 			path->pathid = parm->ctrl.ippathid;
8252356f4cbSMartin Schwidefsky 			path->msglim = parm->ctrl.ipmsglim;
8262356f4cbSMartin Schwidefsky 			path->flags = parm->ctrl.ipflags1;
8272356f4cbSMartin Schwidefsky 			path->handler = handler;
8282356f4cbSMartin Schwidefsky 			path->private = private;
8292356f4cbSMartin Schwidefsky 			list_add_tail(&path->list, &handler->paths);
8302356f4cbSMartin Schwidefsky 			iucv_path_table[path->pathid] = path;
8312356f4cbSMartin Schwidefsky 		} else {
8322356f4cbSMartin Schwidefsky 			iucv_sever_pathid(parm->ctrl.ippathid,
8332356f4cbSMartin Schwidefsky 					  iucv_error_pathid);
8342356f4cbSMartin Schwidefsky 			rc = -EIO;
8352356f4cbSMartin Schwidefsky 		}
8362356f4cbSMartin Schwidefsky 	}
8372356f4cbSMartin Schwidefsky 	spin_unlock_bh(&iucv_table_lock);
8382356f4cbSMartin Schwidefsky 	return rc;
8392356f4cbSMartin Schwidefsky }
840da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_path_connect);
8412356f4cbSMartin Schwidefsky 
8422356f4cbSMartin Schwidefsky /**
8432356f4cbSMartin Schwidefsky  * iucv_path_quiesce:
8442356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
8452356f4cbSMartin Schwidefsky  * @userdata: 16 bytes of data reflected to the communication partner
8462356f4cbSMartin Schwidefsky  *
8472356f4cbSMartin Schwidefsky  * This function temporarily suspends incoming messages on an IUCV path.
8482356f4cbSMartin Schwidefsky  * You can later reactivate the path by invoking the iucv_resume function.
8492356f4cbSMartin Schwidefsky  *
8502356f4cbSMartin Schwidefsky  * Returns the result from the CP IUCV call.
8512356f4cbSMartin Schwidefsky  */
8522356f4cbSMartin Schwidefsky int iucv_path_quiesce(struct iucv_path *path, u8 userdata[16])
8532356f4cbSMartin Schwidefsky {
8542356f4cbSMartin Schwidefsky 	union iucv_param *parm;
8552356f4cbSMartin Schwidefsky 	int rc;
8562356f4cbSMartin Schwidefsky 
8572356f4cbSMartin Schwidefsky 	local_bh_disable();
858*70cf5035SChristoph Lameter 	parm = iucv_param[smp_processor_id()];
8592356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
8602356f4cbSMartin Schwidefsky 	if (userdata)
8612356f4cbSMartin Schwidefsky 		memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser));
8622356f4cbSMartin Schwidefsky 	parm->ctrl.ippathid = path->pathid;
8632356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_QUIESCE, parm);
8642356f4cbSMartin Schwidefsky 	local_bh_enable();
8652356f4cbSMartin Schwidefsky 	return rc;
8662356f4cbSMartin Schwidefsky }
867da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_path_quiesce);
8682356f4cbSMartin Schwidefsky 
8692356f4cbSMartin Schwidefsky /**
8702356f4cbSMartin Schwidefsky  * iucv_path_resume:
8712356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
8722356f4cbSMartin Schwidefsky  * @userdata: 16 bytes of data reflected to the communication partner
8732356f4cbSMartin Schwidefsky  *
8742356f4cbSMartin Schwidefsky  * This function resumes incoming messages on an IUCV path that has
8752356f4cbSMartin Schwidefsky  * been stopped with iucv_path_quiesce.
8762356f4cbSMartin Schwidefsky  *
8772356f4cbSMartin Schwidefsky  * Returns the result from the CP IUCV call.
8782356f4cbSMartin Schwidefsky  */
8792356f4cbSMartin Schwidefsky int iucv_path_resume(struct iucv_path *path, u8 userdata[16])
8802356f4cbSMartin Schwidefsky {
8812356f4cbSMartin Schwidefsky 	union iucv_param *parm;
8822356f4cbSMartin Schwidefsky 	int rc;
8832356f4cbSMartin Schwidefsky 
8842356f4cbSMartin Schwidefsky 	local_bh_disable();
885*70cf5035SChristoph Lameter 	parm = iucv_param[smp_processor_id()];
8862356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
8872356f4cbSMartin Schwidefsky 	if (userdata)
8882356f4cbSMartin Schwidefsky 		memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser));
8892356f4cbSMartin Schwidefsky 	parm->ctrl.ippathid = path->pathid;
8902356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_RESUME, parm);
8912356f4cbSMartin Schwidefsky 	local_bh_enable();
8922356f4cbSMartin Schwidefsky 	return rc;
8932356f4cbSMartin Schwidefsky }
8942356f4cbSMartin Schwidefsky 
8952356f4cbSMartin Schwidefsky /**
8962356f4cbSMartin Schwidefsky  * iucv_path_sever
8972356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
8982356f4cbSMartin Schwidefsky  * @userdata: 16 bytes of data reflected to the communication partner
8992356f4cbSMartin Schwidefsky  *
9002356f4cbSMartin Schwidefsky  * This function terminates an IUCV path.
9012356f4cbSMartin Schwidefsky  *
9022356f4cbSMartin Schwidefsky  * Returns the result from the CP IUCV call.
9032356f4cbSMartin Schwidefsky  */
9042356f4cbSMartin Schwidefsky int iucv_path_sever(struct iucv_path *path, u8 userdata[16])
9052356f4cbSMartin Schwidefsky {
9062356f4cbSMartin Schwidefsky 	int rc;
9072356f4cbSMartin Schwidefsky 
9082356f4cbSMartin Schwidefsky 	preempt_disable();
90904b090d5SMartin Schwidefsky 	if (iucv_active_cpu != smp_processor_id())
9102356f4cbSMartin Schwidefsky 		spin_lock_bh(&iucv_table_lock);
9112356f4cbSMartin Schwidefsky 	rc = iucv_sever_pathid(path->pathid, userdata);
9122356f4cbSMartin Schwidefsky 	if (!rc) {
9132356f4cbSMartin Schwidefsky 		iucv_path_table[path->pathid] = NULL;
9142356f4cbSMartin Schwidefsky 		list_del_init(&path->list);
9152356f4cbSMartin Schwidefsky 	}
91604b090d5SMartin Schwidefsky 	if (iucv_active_cpu != smp_processor_id())
9172356f4cbSMartin Schwidefsky 		spin_unlock_bh(&iucv_table_lock);
9182356f4cbSMartin Schwidefsky 	preempt_enable();
9192356f4cbSMartin Schwidefsky 	return rc;
9202356f4cbSMartin Schwidefsky }
921da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_path_sever);
9222356f4cbSMartin Schwidefsky 
9232356f4cbSMartin Schwidefsky /**
9242356f4cbSMartin Schwidefsky  * iucv_message_purge
9252356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
9262356f4cbSMartin Schwidefsky  * @msg: address of iucv msg structure
9272356f4cbSMartin Schwidefsky  * @srccls: source class of message
9282356f4cbSMartin Schwidefsky  *
9292356f4cbSMartin Schwidefsky  * Cancels a message you have sent.
9302356f4cbSMartin Schwidefsky  *
9312356f4cbSMartin Schwidefsky  * Returns the result from the CP IUCV call.
9322356f4cbSMartin Schwidefsky  */
9332356f4cbSMartin Schwidefsky int iucv_message_purge(struct iucv_path *path, struct iucv_message *msg,
9342356f4cbSMartin Schwidefsky 		       u32 srccls)
9352356f4cbSMartin Schwidefsky {
9362356f4cbSMartin Schwidefsky 	union iucv_param *parm;
9372356f4cbSMartin Schwidefsky 	int rc;
9382356f4cbSMartin Schwidefsky 
9392356f4cbSMartin Schwidefsky 	local_bh_disable();
940*70cf5035SChristoph Lameter 	parm = iucv_param[smp_processor_id()];
9412356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
9422356f4cbSMartin Schwidefsky 	parm->purge.ippathid = path->pathid;
9432356f4cbSMartin Schwidefsky 	parm->purge.ipmsgid = msg->id;
9442356f4cbSMartin Schwidefsky 	parm->purge.ipsrccls = srccls;
9452356f4cbSMartin Schwidefsky 	parm->purge.ipflags1 = IUCV_IPSRCCLS | IUCV_IPFGMID | IUCV_IPFGPID;
9462356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_PURGE, parm);
9472356f4cbSMartin Schwidefsky 	if (!rc) {
9482356f4cbSMartin Schwidefsky 		msg->audit = (*(u32 *) &parm->purge.ipaudit) >> 8;
9492356f4cbSMartin Schwidefsky 		msg->tag = parm->purge.ipmsgtag;
9502356f4cbSMartin Schwidefsky 	}
9512356f4cbSMartin Schwidefsky 	local_bh_enable();
9522356f4cbSMartin Schwidefsky 	return rc;
9532356f4cbSMartin Schwidefsky }
954da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_message_purge);
9552356f4cbSMartin Schwidefsky 
9562356f4cbSMartin Schwidefsky /**
9572356f4cbSMartin Schwidefsky  * iucv_message_receive
9582356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
9592356f4cbSMartin Schwidefsky  * @msg: address of iucv msg structure
9602356f4cbSMartin Schwidefsky  * @flags: how the message is received (IUCV_IPBUFLST)
9612356f4cbSMartin Schwidefsky  * @buffer: address of data buffer or address of struct iucv_array
9622356f4cbSMartin Schwidefsky  * @size: length of data buffer
9632356f4cbSMartin Schwidefsky  * @residual:
9642356f4cbSMartin Schwidefsky  *
9652356f4cbSMartin Schwidefsky  * This function receives messages that are being sent to you over
9662356f4cbSMartin Schwidefsky  * established paths. This function will deal with RMDATA messages
9672356f4cbSMartin Schwidefsky  * embedded in struct iucv_message as well.
9682356f4cbSMartin Schwidefsky  *
9692356f4cbSMartin Schwidefsky  * Returns the result from the CP IUCV call.
9702356f4cbSMartin Schwidefsky  */
9712356f4cbSMartin Schwidefsky int iucv_message_receive(struct iucv_path *path, struct iucv_message *msg,
9722356f4cbSMartin Schwidefsky 			 u8 flags, void *buffer, size_t size, size_t *residual)
9732356f4cbSMartin Schwidefsky {
9742356f4cbSMartin Schwidefsky 	union iucv_param *parm;
9752356f4cbSMartin Schwidefsky 	struct iucv_array *array;
9762356f4cbSMartin Schwidefsky 	u8 *rmmsg;
9772356f4cbSMartin Schwidefsky 	size_t copy;
9782356f4cbSMartin Schwidefsky 	int rc;
9792356f4cbSMartin Schwidefsky 
9802356f4cbSMartin Schwidefsky 	if (msg->flags & IUCV_IPRMDATA) {
9812356f4cbSMartin Schwidefsky 		/*
9822356f4cbSMartin Schwidefsky 		 * Message is 8 bytes long and has been stored to the
9832356f4cbSMartin Schwidefsky 		 * message descriptor itself.
9842356f4cbSMartin Schwidefsky 		 */
9852356f4cbSMartin Schwidefsky 		rc = (size < 8) ? 5 : 0;
9862356f4cbSMartin Schwidefsky 		if (residual)
9872356f4cbSMartin Schwidefsky 			*residual = abs(size - 8);
9882356f4cbSMartin Schwidefsky 		rmmsg = msg->rmmsg;
9892356f4cbSMartin Schwidefsky 		if (flags & IUCV_IPBUFLST) {
9902356f4cbSMartin Schwidefsky 			/* Copy to struct iucv_array. */
9912356f4cbSMartin Schwidefsky 			size = (size < 8) ? size : 8;
9922356f4cbSMartin Schwidefsky 			for (array = buffer; size > 0; array++) {
9932356f4cbSMartin Schwidefsky 				copy = min_t(size_t, size, array->length);
9942356f4cbSMartin Schwidefsky 				memcpy((u8 *)(addr_t) array->address,
9952356f4cbSMartin Schwidefsky 				       rmmsg, copy);
9962356f4cbSMartin Schwidefsky 				rmmsg += copy;
9972356f4cbSMartin Schwidefsky 				size -= copy;
9982356f4cbSMartin Schwidefsky 			}
9992356f4cbSMartin Schwidefsky 		} else {
10002356f4cbSMartin Schwidefsky 			/* Copy to direct buffer. */
10012356f4cbSMartin Schwidefsky 			memcpy(buffer, rmmsg, min_t(size_t, size, 8));
10022356f4cbSMartin Schwidefsky 		}
10032356f4cbSMartin Schwidefsky 		return 0;
10042356f4cbSMartin Schwidefsky 	}
10052356f4cbSMartin Schwidefsky 
10062356f4cbSMartin Schwidefsky 	local_bh_disable();
1007*70cf5035SChristoph Lameter 	parm = iucv_param[smp_processor_id()];
10082356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
10092356f4cbSMartin Schwidefsky 	parm->db.ipbfadr1 = (u32)(addr_t) buffer;
10102356f4cbSMartin Schwidefsky 	parm->db.ipbfln1f = (u32) size;
10112356f4cbSMartin Schwidefsky 	parm->db.ipmsgid = msg->id;
10122356f4cbSMartin Schwidefsky 	parm->db.ippathid = path->pathid;
10132356f4cbSMartin Schwidefsky 	parm->db.iptrgcls = msg->class;
10142356f4cbSMartin Schwidefsky 	parm->db.ipflags1 = (flags | IUCV_IPFGPID |
10152356f4cbSMartin Schwidefsky 			     IUCV_IPFGMID | IUCV_IPTRGCLS);
10162356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_RECEIVE, parm);
10172356f4cbSMartin Schwidefsky 	if (!rc || rc == 5) {
10182356f4cbSMartin Schwidefsky 		msg->flags = parm->db.ipflags1;
10192356f4cbSMartin Schwidefsky 		if (residual)
10202356f4cbSMartin Schwidefsky 			*residual = parm->db.ipbfln1f;
10212356f4cbSMartin Schwidefsky 	}
10222356f4cbSMartin Schwidefsky 	local_bh_enable();
10232356f4cbSMartin Schwidefsky 	return rc;
10242356f4cbSMartin Schwidefsky }
1025da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_message_receive);
10262356f4cbSMartin Schwidefsky 
10272356f4cbSMartin Schwidefsky /**
10282356f4cbSMartin Schwidefsky  * iucv_message_reject
10292356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
10302356f4cbSMartin Schwidefsky  * @msg: address of iucv msg structure
10312356f4cbSMartin Schwidefsky  *
10322356f4cbSMartin Schwidefsky  * The reject function refuses a specified message. Between the time you
10332356f4cbSMartin Schwidefsky  * are notified of a message and the time that you complete the message,
10342356f4cbSMartin Schwidefsky  * the message may be rejected.
10352356f4cbSMartin Schwidefsky  *
10362356f4cbSMartin Schwidefsky  * Returns the result from the CP IUCV call.
10372356f4cbSMartin Schwidefsky  */
10382356f4cbSMartin Schwidefsky int iucv_message_reject(struct iucv_path *path, struct iucv_message *msg)
10392356f4cbSMartin Schwidefsky {
10402356f4cbSMartin Schwidefsky 	union iucv_param *parm;
10412356f4cbSMartin Schwidefsky 	int rc;
10422356f4cbSMartin Schwidefsky 
10432356f4cbSMartin Schwidefsky 	local_bh_disable();
1044*70cf5035SChristoph Lameter 	parm = iucv_param[smp_processor_id()];
10452356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
10462356f4cbSMartin Schwidefsky 	parm->db.ippathid = path->pathid;
10472356f4cbSMartin Schwidefsky 	parm->db.ipmsgid = msg->id;
10482356f4cbSMartin Schwidefsky 	parm->db.iptrgcls = msg->class;
10492356f4cbSMartin Schwidefsky 	parm->db.ipflags1 = (IUCV_IPTRGCLS | IUCV_IPFGMID | IUCV_IPFGPID);
10502356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_REJECT, parm);
10512356f4cbSMartin Schwidefsky 	local_bh_enable();
10522356f4cbSMartin Schwidefsky 	return rc;
10532356f4cbSMartin Schwidefsky }
1054da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_message_reject);
10552356f4cbSMartin Schwidefsky 
10562356f4cbSMartin Schwidefsky /**
10572356f4cbSMartin Schwidefsky  * iucv_message_reply
10582356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
10592356f4cbSMartin Schwidefsky  * @msg: address of iucv msg structure
10602356f4cbSMartin Schwidefsky  * @flags: how the reply is sent (IUCV_IPRMDATA, IUCV_IPPRTY, IUCV_IPBUFLST)
10612356f4cbSMartin Schwidefsky  * @reply: address of reply data buffer or address of struct iucv_array
10622356f4cbSMartin Schwidefsky  * @size: length of reply data buffer
10632356f4cbSMartin Schwidefsky  *
10642356f4cbSMartin Schwidefsky  * This function responds to the two-way messages that you receive. You
10652356f4cbSMartin Schwidefsky  * must identify completely the message to which you wish to reply. ie,
10662356f4cbSMartin Schwidefsky  * pathid, msgid, and trgcls. Prmmsg signifies the data is moved into
10672356f4cbSMartin Schwidefsky  * the parameter list.
10682356f4cbSMartin Schwidefsky  *
10692356f4cbSMartin Schwidefsky  * Returns the result from the CP IUCV call.
10702356f4cbSMartin Schwidefsky  */
10712356f4cbSMartin Schwidefsky int iucv_message_reply(struct iucv_path *path, struct iucv_message *msg,
10722356f4cbSMartin Schwidefsky 		       u8 flags, void *reply, size_t size)
10732356f4cbSMartin Schwidefsky {
10742356f4cbSMartin Schwidefsky 	union iucv_param *parm;
10752356f4cbSMartin Schwidefsky 	int rc;
10762356f4cbSMartin Schwidefsky 
10772356f4cbSMartin Schwidefsky 	local_bh_disable();
1078*70cf5035SChristoph Lameter 	parm = iucv_param[smp_processor_id()];
10792356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
10802356f4cbSMartin Schwidefsky 	if (flags & IUCV_IPRMDATA) {
10812356f4cbSMartin Schwidefsky 		parm->dpl.ippathid = path->pathid;
10822356f4cbSMartin Schwidefsky 		parm->dpl.ipflags1 = flags;
10832356f4cbSMartin Schwidefsky 		parm->dpl.ipmsgid = msg->id;
10842356f4cbSMartin Schwidefsky 		parm->dpl.iptrgcls = msg->class;
10852356f4cbSMartin Schwidefsky 		memcpy(parm->dpl.iprmmsg, reply, min_t(size_t, size, 8));
10862356f4cbSMartin Schwidefsky 	} else {
10872356f4cbSMartin Schwidefsky 		parm->db.ipbfadr1 = (u32)(addr_t) reply;
10882356f4cbSMartin Schwidefsky 		parm->db.ipbfln1f = (u32) size;
10892356f4cbSMartin Schwidefsky 		parm->db.ippathid = path->pathid;
10902356f4cbSMartin Schwidefsky 		parm->db.ipflags1 = flags;
10912356f4cbSMartin Schwidefsky 		parm->db.ipmsgid = msg->id;
10922356f4cbSMartin Schwidefsky 		parm->db.iptrgcls = msg->class;
10932356f4cbSMartin Schwidefsky 	}
10942356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_REPLY, parm);
10952356f4cbSMartin Schwidefsky 	local_bh_enable();
10962356f4cbSMartin Schwidefsky 	return rc;
10972356f4cbSMartin Schwidefsky }
1098da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_message_reply);
10992356f4cbSMartin Schwidefsky 
11002356f4cbSMartin Schwidefsky /**
11012356f4cbSMartin Schwidefsky  * iucv_message_send
11022356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
11032356f4cbSMartin Schwidefsky  * @msg: address of iucv msg structure
11042356f4cbSMartin Schwidefsky  * @flags: how the message is sent (IUCV_IPRMDATA, IUCV_IPPRTY, IUCV_IPBUFLST)
11052356f4cbSMartin Schwidefsky  * @srccls: source class of message
11062356f4cbSMartin Schwidefsky  * @buffer: address of send buffer or address of struct iucv_array
11072356f4cbSMartin Schwidefsky  * @size: length of send buffer
11082356f4cbSMartin Schwidefsky  *
11092356f4cbSMartin Schwidefsky  * This function transmits data to another application. Data to be
11102356f4cbSMartin Schwidefsky  * transmitted is in a buffer and this is a one-way message and the
11112356f4cbSMartin Schwidefsky  * receiver will not reply to the message.
11122356f4cbSMartin Schwidefsky  *
11132356f4cbSMartin Schwidefsky  * Returns the result from the CP IUCV call.
11142356f4cbSMartin Schwidefsky  */
11152356f4cbSMartin Schwidefsky int iucv_message_send(struct iucv_path *path, struct iucv_message *msg,
11162356f4cbSMartin Schwidefsky 		      u8 flags, u32 srccls, void *buffer, size_t size)
11172356f4cbSMartin Schwidefsky {
11182356f4cbSMartin Schwidefsky 	union iucv_param *parm;
11192356f4cbSMartin Schwidefsky 	int rc;
11202356f4cbSMartin Schwidefsky 
11212356f4cbSMartin Schwidefsky 	local_bh_disable();
1122*70cf5035SChristoph Lameter 	parm = iucv_param[smp_processor_id()];
11232356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
11242356f4cbSMartin Schwidefsky 	if (flags & IUCV_IPRMDATA) {
11252356f4cbSMartin Schwidefsky 		/* Message of 8 bytes can be placed into the parameter list. */
11262356f4cbSMartin Schwidefsky 		parm->dpl.ippathid = path->pathid;
11272356f4cbSMartin Schwidefsky 		parm->dpl.ipflags1 = flags | IUCV_IPNORPY;
11282356f4cbSMartin Schwidefsky 		parm->dpl.iptrgcls = msg->class;
11292356f4cbSMartin Schwidefsky 		parm->dpl.ipsrccls = srccls;
11302356f4cbSMartin Schwidefsky 		parm->dpl.ipmsgtag = msg->tag;
11312356f4cbSMartin Schwidefsky 		memcpy(parm->dpl.iprmmsg, buffer, 8);
11322356f4cbSMartin Schwidefsky 	} else {
11332356f4cbSMartin Schwidefsky 		parm->db.ipbfadr1 = (u32)(addr_t) buffer;
11342356f4cbSMartin Schwidefsky 		parm->db.ipbfln1f = (u32) size;
11352356f4cbSMartin Schwidefsky 		parm->db.ippathid = path->pathid;
11362356f4cbSMartin Schwidefsky 		parm->db.ipflags1 = flags | IUCV_IPNORPY;
11372356f4cbSMartin Schwidefsky 		parm->db.iptrgcls = msg->class;
11382356f4cbSMartin Schwidefsky 		parm->db.ipsrccls = srccls;
11392356f4cbSMartin Schwidefsky 		parm->db.ipmsgtag = msg->tag;
11402356f4cbSMartin Schwidefsky 	}
11412356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_SEND, parm);
11422356f4cbSMartin Schwidefsky 	if (!rc)
11432356f4cbSMartin Schwidefsky 		msg->id = parm->db.ipmsgid;
11442356f4cbSMartin Schwidefsky 	local_bh_enable();
11452356f4cbSMartin Schwidefsky 	return rc;
11462356f4cbSMartin Schwidefsky }
1147da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_message_send);
11482356f4cbSMartin Schwidefsky 
11492356f4cbSMartin Schwidefsky /**
11502356f4cbSMartin Schwidefsky  * iucv_message_send2way
11512356f4cbSMartin Schwidefsky  * @path: address of iucv path structure
11522356f4cbSMartin Schwidefsky  * @msg: address of iucv msg structure
11532356f4cbSMartin Schwidefsky  * @flags: how the message is sent and the reply is received
11542356f4cbSMartin Schwidefsky  *	   (IUCV_IPRMDATA, IUCV_IPBUFLST, IUCV_IPPRTY, IUCV_ANSLST)
11552356f4cbSMartin Schwidefsky  * @srccls: source class of message
11562356f4cbSMartin Schwidefsky  * @buffer: address of send buffer or address of struct iucv_array
11572356f4cbSMartin Schwidefsky  * @size: length of send buffer
11582356f4cbSMartin Schwidefsky  * @ansbuf: address of answer buffer or address of struct iucv_array
11592356f4cbSMartin Schwidefsky  * @asize: size of reply buffer
11602356f4cbSMartin Schwidefsky  *
11612356f4cbSMartin Schwidefsky  * This function transmits data to another application. Data to be
11622356f4cbSMartin Schwidefsky  * transmitted is in a buffer. The receiver of the send is expected to
11632356f4cbSMartin Schwidefsky  * reply to the message and a buffer is provided into which IUCV moves
11642356f4cbSMartin Schwidefsky  * the reply to this message.
11652356f4cbSMartin Schwidefsky  *
11662356f4cbSMartin Schwidefsky  * Returns the result from the CP IUCV call.
11672356f4cbSMartin Schwidefsky  */
11682356f4cbSMartin Schwidefsky int iucv_message_send2way(struct iucv_path *path, struct iucv_message *msg,
11692356f4cbSMartin Schwidefsky 			  u8 flags, u32 srccls, void *buffer, size_t size,
11702356f4cbSMartin Schwidefsky 			  void *answer, size_t asize, size_t *residual)
11712356f4cbSMartin Schwidefsky {
11722356f4cbSMartin Schwidefsky 	union iucv_param *parm;
11732356f4cbSMartin Schwidefsky 	int rc;
11742356f4cbSMartin Schwidefsky 
11752356f4cbSMartin Schwidefsky 	local_bh_disable();
1176*70cf5035SChristoph Lameter 	parm = iucv_param[smp_processor_id()];
11772356f4cbSMartin Schwidefsky 	memset(parm, 0, sizeof(union iucv_param));
11782356f4cbSMartin Schwidefsky 	if (flags & IUCV_IPRMDATA) {
11792356f4cbSMartin Schwidefsky 		parm->dpl.ippathid = path->pathid;
11802356f4cbSMartin Schwidefsky 		parm->dpl.ipflags1 = path->flags;	/* priority message */
11812356f4cbSMartin Schwidefsky 		parm->dpl.iptrgcls = msg->class;
11822356f4cbSMartin Schwidefsky 		parm->dpl.ipsrccls = srccls;
11832356f4cbSMartin Schwidefsky 		parm->dpl.ipmsgtag = msg->tag;
11842356f4cbSMartin Schwidefsky 		parm->dpl.ipbfadr2 = (u32)(addr_t) answer;
11852356f4cbSMartin Schwidefsky 		parm->dpl.ipbfln2f = (u32) asize;
11862356f4cbSMartin Schwidefsky 		memcpy(parm->dpl.iprmmsg, buffer, 8);
11872356f4cbSMartin Schwidefsky 	} else {
11882356f4cbSMartin Schwidefsky 		parm->db.ippathid = path->pathid;
11892356f4cbSMartin Schwidefsky 		parm->db.ipflags1 = path->flags;	/* priority message */
11902356f4cbSMartin Schwidefsky 		parm->db.iptrgcls = msg->class;
11912356f4cbSMartin Schwidefsky 		parm->db.ipsrccls = srccls;
11922356f4cbSMartin Schwidefsky 		parm->db.ipmsgtag = msg->tag;
11932356f4cbSMartin Schwidefsky 		parm->db.ipbfadr1 = (u32)(addr_t) buffer;
11942356f4cbSMartin Schwidefsky 		parm->db.ipbfln1f = (u32) size;
11952356f4cbSMartin Schwidefsky 		parm->db.ipbfadr2 = (u32)(addr_t) answer;
11962356f4cbSMartin Schwidefsky 		parm->db.ipbfln2f = (u32) asize;
11972356f4cbSMartin Schwidefsky 	}
11982356f4cbSMartin Schwidefsky 	rc = iucv_call_b2f0(IUCV_SEND, parm);
11992356f4cbSMartin Schwidefsky 	if (!rc)
12002356f4cbSMartin Schwidefsky 		msg->id = parm->db.ipmsgid;
12012356f4cbSMartin Schwidefsky 	local_bh_enable();
12022356f4cbSMartin Schwidefsky 	return rc;
12032356f4cbSMartin Schwidefsky }
1204da99f056SHeiko Carstens EXPORT_SYMBOL(iucv_message_send2way);
12052356f4cbSMartin Schwidefsky 
12062356f4cbSMartin Schwidefsky /**
12072356f4cbSMartin Schwidefsky  * iucv_path_pending
12082356f4cbSMartin Schwidefsky  * @data: Pointer to external interrupt buffer
12092356f4cbSMartin Schwidefsky  *
12102356f4cbSMartin Schwidefsky  * Process connection pending work item. Called from tasklet while holding
12112356f4cbSMartin Schwidefsky  * iucv_table_lock.
12122356f4cbSMartin Schwidefsky  */
12132356f4cbSMartin Schwidefsky struct iucv_path_pending {
12142356f4cbSMartin Schwidefsky 	u16 ippathid;
12152356f4cbSMartin Schwidefsky 	u8  ipflags1;
12162356f4cbSMartin Schwidefsky 	u8  iptype;
12172356f4cbSMartin Schwidefsky 	u16 ipmsglim;
12182356f4cbSMartin Schwidefsky 	u16 res1;
12192356f4cbSMartin Schwidefsky 	u8  ipvmid[8];
12202356f4cbSMartin Schwidefsky 	u8  ipuser[16];
12212356f4cbSMartin Schwidefsky 	u32 res3;
12222356f4cbSMartin Schwidefsky 	u8  ippollfg;
12232356f4cbSMartin Schwidefsky 	u8  res4[3];
12242356f4cbSMartin Schwidefsky } __attribute__ ((packed));
12252356f4cbSMartin Schwidefsky 
12262356f4cbSMartin Schwidefsky static void iucv_path_pending(struct iucv_irq_data *data)
12272356f4cbSMartin Schwidefsky {
12282356f4cbSMartin Schwidefsky 	struct iucv_path_pending *ipp = (void *) data;
12292356f4cbSMartin Schwidefsky 	struct iucv_handler *handler;
12302356f4cbSMartin Schwidefsky 	struct iucv_path *path;
12312356f4cbSMartin Schwidefsky 	char *error;
12322356f4cbSMartin Schwidefsky 
12332356f4cbSMartin Schwidefsky 	BUG_ON(iucv_path_table[ipp->ippathid]);
12342356f4cbSMartin Schwidefsky 	/* New pathid, handler found. Create a new path struct. */
12352356f4cbSMartin Schwidefsky 	error = iucv_error_no_memory;
12362356f4cbSMartin Schwidefsky 	path = iucv_path_alloc(ipp->ipmsglim, ipp->ipflags1, GFP_ATOMIC);
12372356f4cbSMartin Schwidefsky 	if (!path)
12382356f4cbSMartin Schwidefsky 		goto out_sever;
12392356f4cbSMartin Schwidefsky 	path->pathid = ipp->ippathid;
12402356f4cbSMartin Schwidefsky 	iucv_path_table[path->pathid] = path;
12412356f4cbSMartin Schwidefsky 	EBCASC(ipp->ipvmid, 8);
12422356f4cbSMartin Schwidefsky 
12432356f4cbSMartin Schwidefsky 	/* Call registered handler until one is found that wants the path. */
12442356f4cbSMartin Schwidefsky 	list_for_each_entry(handler, &iucv_handler_list, list) {
12452356f4cbSMartin Schwidefsky 		if (!handler->path_pending)
12462356f4cbSMartin Schwidefsky 			continue;
12472356f4cbSMartin Schwidefsky 		/*
12482356f4cbSMartin Schwidefsky 		 * Add path to handler to allow a call to iucv_path_sever
12492356f4cbSMartin Schwidefsky 		 * inside the path_pending function. If the handler returns
12502356f4cbSMartin Schwidefsky 		 * an error remove the path from the handler again.
12512356f4cbSMartin Schwidefsky 		 */
12522356f4cbSMartin Schwidefsky 		list_add(&path->list, &handler->paths);
12532356f4cbSMartin Schwidefsky 		path->handler = handler;
12542356f4cbSMartin Schwidefsky 		if (!handler->path_pending(path, ipp->ipvmid, ipp->ipuser))
12552356f4cbSMartin Schwidefsky 			return;
12562356f4cbSMartin Schwidefsky 		list_del(&path->list);
12572356f4cbSMartin Schwidefsky 		path->handler = NULL;
12582356f4cbSMartin Schwidefsky 	}
12592356f4cbSMartin Schwidefsky 	/* No handler wanted the path. */
12602356f4cbSMartin Schwidefsky 	iucv_path_table[path->pathid] = NULL;
12612356f4cbSMartin Schwidefsky 	iucv_path_free(path);
12622356f4cbSMartin Schwidefsky 	error = iucv_error_no_listener;
12632356f4cbSMartin Schwidefsky out_sever:
12642356f4cbSMartin Schwidefsky 	iucv_sever_pathid(ipp->ippathid, error);
12652356f4cbSMartin Schwidefsky }
12662356f4cbSMartin Schwidefsky 
12672356f4cbSMartin Schwidefsky /**
12682356f4cbSMartin Schwidefsky  * iucv_path_complete
12692356f4cbSMartin Schwidefsky  * @data: Pointer to external interrupt buffer
12702356f4cbSMartin Schwidefsky  *
12712356f4cbSMartin Schwidefsky  * Process connection complete work item. Called from tasklet while holding
12722356f4cbSMartin Schwidefsky  * iucv_table_lock.
12732356f4cbSMartin Schwidefsky  */
12742356f4cbSMartin Schwidefsky struct iucv_path_complete {
12752356f4cbSMartin Schwidefsky 	u16 ippathid;
12762356f4cbSMartin Schwidefsky 	u8  ipflags1;
12772356f4cbSMartin Schwidefsky 	u8  iptype;
12782356f4cbSMartin Schwidefsky 	u16 ipmsglim;
12792356f4cbSMartin Schwidefsky 	u16 res1;
12802356f4cbSMartin Schwidefsky 	u8  res2[8];
12812356f4cbSMartin Schwidefsky 	u8  ipuser[16];
12822356f4cbSMartin Schwidefsky 	u32 res3;
12832356f4cbSMartin Schwidefsky 	u8  ippollfg;
12842356f4cbSMartin Schwidefsky 	u8  res4[3];
12852356f4cbSMartin Schwidefsky } __attribute__ ((packed));
12862356f4cbSMartin Schwidefsky 
12872356f4cbSMartin Schwidefsky static void iucv_path_complete(struct iucv_irq_data *data)
12882356f4cbSMartin Schwidefsky {
12892356f4cbSMartin Schwidefsky 	struct iucv_path_complete *ipc = (void *) data;
12902356f4cbSMartin Schwidefsky 	struct iucv_path *path = iucv_path_table[ipc->ippathid];
12912356f4cbSMartin Schwidefsky 
129204b090d5SMartin Schwidefsky 	if (path && path->handler && path->handler->path_complete)
12932356f4cbSMartin Schwidefsky 		path->handler->path_complete(path, ipc->ipuser);
12942356f4cbSMartin Schwidefsky }
12952356f4cbSMartin Schwidefsky 
12962356f4cbSMartin Schwidefsky /**
12972356f4cbSMartin Schwidefsky  * iucv_path_severed
12982356f4cbSMartin Schwidefsky  * @data: Pointer to external interrupt buffer
12992356f4cbSMartin Schwidefsky  *
13002356f4cbSMartin Schwidefsky  * Process connection severed work item. Called from tasklet while holding
13012356f4cbSMartin Schwidefsky  * iucv_table_lock.
13022356f4cbSMartin Schwidefsky  */
13032356f4cbSMartin Schwidefsky struct iucv_path_severed {
13042356f4cbSMartin Schwidefsky 	u16 ippathid;
13052356f4cbSMartin Schwidefsky 	u8  res1;
13062356f4cbSMartin Schwidefsky 	u8  iptype;
13072356f4cbSMartin Schwidefsky 	u32 res2;
13082356f4cbSMartin Schwidefsky 	u8  res3[8];
13092356f4cbSMartin Schwidefsky 	u8  ipuser[16];
13102356f4cbSMartin Schwidefsky 	u32 res4;
13112356f4cbSMartin Schwidefsky 	u8  ippollfg;
13122356f4cbSMartin Schwidefsky 	u8  res5[3];
13132356f4cbSMartin Schwidefsky } __attribute__ ((packed));
13142356f4cbSMartin Schwidefsky 
13152356f4cbSMartin Schwidefsky static void iucv_path_severed(struct iucv_irq_data *data)
13162356f4cbSMartin Schwidefsky {
13172356f4cbSMartin Schwidefsky 	struct iucv_path_severed *ips = (void *) data;
13182356f4cbSMartin Schwidefsky 	struct iucv_path *path = iucv_path_table[ips->ippathid];
13192356f4cbSMartin Schwidefsky 
132004b090d5SMartin Schwidefsky 	if (!path || !path->handler)	/* Already severed */
132104b090d5SMartin Schwidefsky 		return;
13222356f4cbSMartin Schwidefsky 	if (path->handler->path_severed)
13232356f4cbSMartin Schwidefsky 		path->handler->path_severed(path, ips->ipuser);
13242356f4cbSMartin Schwidefsky 	else {
13252356f4cbSMartin Schwidefsky 		iucv_sever_pathid(path->pathid, NULL);
13262356f4cbSMartin Schwidefsky 		iucv_path_table[path->pathid] = NULL;
13272356f4cbSMartin Schwidefsky 		list_del_init(&path->list);
13282356f4cbSMartin Schwidefsky 		iucv_path_free(path);
13292356f4cbSMartin Schwidefsky 	}
13302356f4cbSMartin Schwidefsky }
13312356f4cbSMartin Schwidefsky 
13322356f4cbSMartin Schwidefsky /**
13332356f4cbSMartin Schwidefsky  * iucv_path_quiesced
13342356f4cbSMartin Schwidefsky  * @data: Pointer to external interrupt buffer
13352356f4cbSMartin Schwidefsky  *
13362356f4cbSMartin Schwidefsky  * Process connection quiesced work item. Called from tasklet while holding
13372356f4cbSMartin Schwidefsky  * iucv_table_lock.
13382356f4cbSMartin Schwidefsky  */
13392356f4cbSMartin Schwidefsky struct iucv_path_quiesced {
13402356f4cbSMartin Schwidefsky 	u16 ippathid;
13412356f4cbSMartin Schwidefsky 	u8  res1;
13422356f4cbSMartin Schwidefsky 	u8  iptype;
13432356f4cbSMartin Schwidefsky 	u32 res2;
13442356f4cbSMartin Schwidefsky 	u8  res3[8];
13452356f4cbSMartin Schwidefsky 	u8  ipuser[16];
13462356f4cbSMartin Schwidefsky 	u32 res4;
13472356f4cbSMartin Schwidefsky 	u8  ippollfg;
13482356f4cbSMartin Schwidefsky 	u8  res5[3];
13492356f4cbSMartin Schwidefsky } __attribute__ ((packed));
13502356f4cbSMartin Schwidefsky 
13512356f4cbSMartin Schwidefsky static void iucv_path_quiesced(struct iucv_irq_data *data)
13522356f4cbSMartin Schwidefsky {
13532356f4cbSMartin Schwidefsky 	struct iucv_path_quiesced *ipq = (void *) data;
13542356f4cbSMartin Schwidefsky 	struct iucv_path *path = iucv_path_table[ipq->ippathid];
13552356f4cbSMartin Schwidefsky 
135604b090d5SMartin Schwidefsky 	if (path && path->handler && path->handler->path_quiesced)
13572356f4cbSMartin Schwidefsky 		path->handler->path_quiesced(path, ipq->ipuser);
13582356f4cbSMartin Schwidefsky }
13592356f4cbSMartin Schwidefsky 
13602356f4cbSMartin Schwidefsky /**
13612356f4cbSMartin Schwidefsky  * iucv_path_resumed
13622356f4cbSMartin Schwidefsky  * @data: Pointer to external interrupt buffer
13632356f4cbSMartin Schwidefsky  *
13642356f4cbSMartin Schwidefsky  * Process connection resumed work item. Called from tasklet while holding
13652356f4cbSMartin Schwidefsky  * iucv_table_lock.
13662356f4cbSMartin Schwidefsky  */
13672356f4cbSMartin Schwidefsky struct iucv_path_resumed {
13682356f4cbSMartin Schwidefsky 	u16 ippathid;
13692356f4cbSMartin Schwidefsky 	u8  res1;
13702356f4cbSMartin Schwidefsky 	u8  iptype;
13712356f4cbSMartin Schwidefsky 	u32 res2;
13722356f4cbSMartin Schwidefsky 	u8  res3[8];
13732356f4cbSMartin Schwidefsky 	u8  ipuser[16];
13742356f4cbSMartin Schwidefsky 	u32 res4;
13752356f4cbSMartin Schwidefsky 	u8  ippollfg;
13762356f4cbSMartin Schwidefsky 	u8  res5[3];
13772356f4cbSMartin Schwidefsky } __attribute__ ((packed));
13782356f4cbSMartin Schwidefsky 
13792356f4cbSMartin Schwidefsky static void iucv_path_resumed(struct iucv_irq_data *data)
13802356f4cbSMartin Schwidefsky {
13812356f4cbSMartin Schwidefsky 	struct iucv_path_resumed *ipr = (void *) data;
13822356f4cbSMartin Schwidefsky 	struct iucv_path *path = iucv_path_table[ipr->ippathid];
13832356f4cbSMartin Schwidefsky 
138404b090d5SMartin Schwidefsky 	if (path && path->handler && path->handler->path_resumed)
13852356f4cbSMartin Schwidefsky 		path->handler->path_resumed(path, ipr->ipuser);
13862356f4cbSMartin Schwidefsky }
13872356f4cbSMartin Schwidefsky 
13882356f4cbSMartin Schwidefsky /**
13892356f4cbSMartin Schwidefsky  * iucv_message_complete
13902356f4cbSMartin Schwidefsky  * @data: Pointer to external interrupt buffer
13912356f4cbSMartin Schwidefsky  *
13922356f4cbSMartin Schwidefsky  * Process message complete work item. Called from tasklet while holding
13932356f4cbSMartin Schwidefsky  * iucv_table_lock.
13942356f4cbSMartin Schwidefsky  */
13952356f4cbSMartin Schwidefsky struct iucv_message_complete {
13962356f4cbSMartin Schwidefsky 	u16 ippathid;
13972356f4cbSMartin Schwidefsky 	u8  ipflags1;
13982356f4cbSMartin Schwidefsky 	u8  iptype;
13992356f4cbSMartin Schwidefsky 	u32 ipmsgid;
14002356f4cbSMartin Schwidefsky 	u32 ipaudit;
14012356f4cbSMartin Schwidefsky 	u8  iprmmsg[8];
14022356f4cbSMartin Schwidefsky 	u32 ipsrccls;
14032356f4cbSMartin Schwidefsky 	u32 ipmsgtag;
14042356f4cbSMartin Schwidefsky 	u32 res;
14052356f4cbSMartin Schwidefsky 	u32 ipbfln2f;
14062356f4cbSMartin Schwidefsky 	u8  ippollfg;
14072356f4cbSMartin Schwidefsky 	u8  res2[3];
14082356f4cbSMartin Schwidefsky } __attribute__ ((packed));
14092356f4cbSMartin Schwidefsky 
14102356f4cbSMartin Schwidefsky static void iucv_message_complete(struct iucv_irq_data *data)
14112356f4cbSMartin Schwidefsky {
14122356f4cbSMartin Schwidefsky 	struct iucv_message_complete *imc = (void *) data;
14132356f4cbSMartin Schwidefsky 	struct iucv_path *path = iucv_path_table[imc->ippathid];
14142356f4cbSMartin Schwidefsky 	struct iucv_message msg;
14152356f4cbSMartin Schwidefsky 
141604b090d5SMartin Schwidefsky 	if (path && path->handler && path->handler->message_complete) {
14172356f4cbSMartin Schwidefsky 		msg.flags = imc->ipflags1;
14182356f4cbSMartin Schwidefsky 		msg.id = imc->ipmsgid;
14192356f4cbSMartin Schwidefsky 		msg.audit = imc->ipaudit;
14202356f4cbSMartin Schwidefsky 		memcpy(msg.rmmsg, imc->iprmmsg, 8);
14212356f4cbSMartin Schwidefsky 		msg.class = imc->ipsrccls;
14222356f4cbSMartin Schwidefsky 		msg.tag = imc->ipmsgtag;
14232356f4cbSMartin Schwidefsky 		msg.length = imc->ipbfln2f;
14242356f4cbSMartin Schwidefsky 		path->handler->message_complete(path, &msg);
14252356f4cbSMartin Schwidefsky 	}
14262356f4cbSMartin Schwidefsky }
14272356f4cbSMartin Schwidefsky 
14282356f4cbSMartin Schwidefsky /**
14292356f4cbSMartin Schwidefsky  * iucv_message_pending
14302356f4cbSMartin Schwidefsky  * @data: Pointer to external interrupt buffer
14312356f4cbSMartin Schwidefsky  *
14322356f4cbSMartin Schwidefsky  * Process message pending work item. Called from tasklet while holding
14332356f4cbSMartin Schwidefsky  * iucv_table_lock.
14342356f4cbSMartin Schwidefsky  */
14352356f4cbSMartin Schwidefsky struct iucv_message_pending {
14362356f4cbSMartin Schwidefsky 	u16 ippathid;
14372356f4cbSMartin Schwidefsky 	u8  ipflags1;
14382356f4cbSMartin Schwidefsky 	u8  iptype;
14392356f4cbSMartin Schwidefsky 	u32 ipmsgid;
14402356f4cbSMartin Schwidefsky 	u32 iptrgcls;
14412356f4cbSMartin Schwidefsky 	union {
14422356f4cbSMartin Schwidefsky 		u32 iprmmsg1_u32;
14432356f4cbSMartin Schwidefsky 		u8  iprmmsg1[4];
14442356f4cbSMartin Schwidefsky 	} ln1msg1;
14452356f4cbSMartin Schwidefsky 	union {
14462356f4cbSMartin Schwidefsky 		u32 ipbfln1f;
14472356f4cbSMartin Schwidefsky 		u8  iprmmsg2[4];
14482356f4cbSMartin Schwidefsky 	} ln1msg2;
14492356f4cbSMartin Schwidefsky 	u32 res1[3];
14502356f4cbSMartin Schwidefsky 	u32 ipbfln2f;
14512356f4cbSMartin Schwidefsky 	u8  ippollfg;
14522356f4cbSMartin Schwidefsky 	u8  res2[3];
14532356f4cbSMartin Schwidefsky } __attribute__ ((packed));
14542356f4cbSMartin Schwidefsky 
14552356f4cbSMartin Schwidefsky static void iucv_message_pending(struct iucv_irq_data *data)
14562356f4cbSMartin Schwidefsky {
14572356f4cbSMartin Schwidefsky 	struct iucv_message_pending *imp = (void *) data;
14582356f4cbSMartin Schwidefsky 	struct iucv_path *path = iucv_path_table[imp->ippathid];
14592356f4cbSMartin Schwidefsky 	struct iucv_message msg;
14602356f4cbSMartin Schwidefsky 
146104b090d5SMartin Schwidefsky 	if (path && path->handler && path->handler->message_pending) {
14622356f4cbSMartin Schwidefsky 		msg.flags = imp->ipflags1;
14632356f4cbSMartin Schwidefsky 		msg.id = imp->ipmsgid;
14642356f4cbSMartin Schwidefsky 		msg.class = imp->iptrgcls;
14652356f4cbSMartin Schwidefsky 		if (imp->ipflags1 & IUCV_IPRMDATA) {
14662356f4cbSMartin Schwidefsky 			memcpy(msg.rmmsg, imp->ln1msg1.iprmmsg1, 8);
14672356f4cbSMartin Schwidefsky 			msg.length = 8;
14682356f4cbSMartin Schwidefsky 		} else
14692356f4cbSMartin Schwidefsky 			msg.length = imp->ln1msg2.ipbfln1f;
14702356f4cbSMartin Schwidefsky 		msg.reply_size = imp->ipbfln2f;
14712356f4cbSMartin Schwidefsky 		path->handler->message_pending(path, &msg);
14722356f4cbSMartin Schwidefsky 	}
14732356f4cbSMartin Schwidefsky }
14742356f4cbSMartin Schwidefsky 
14752356f4cbSMartin Schwidefsky /**
147604b090d5SMartin Schwidefsky  * iucv_tasklet_fn:
14772356f4cbSMartin Schwidefsky  *
14782356f4cbSMartin Schwidefsky  * This tasklet loops over the queue of irq buffers created by
14792356f4cbSMartin Schwidefsky  * iucv_external_interrupt, calls the appropriate action handler
14802356f4cbSMartin Schwidefsky  * and then frees the buffer.
14812356f4cbSMartin Schwidefsky  */
148204b090d5SMartin Schwidefsky static void iucv_tasklet_fn(unsigned long ignored)
14832356f4cbSMartin Schwidefsky {
14842356f4cbSMartin Schwidefsky 	typedef void iucv_irq_fn(struct iucv_irq_data *);
14852356f4cbSMartin Schwidefsky 	static iucv_irq_fn *irq_fn[] = {
14862356f4cbSMartin Schwidefsky 		[0x02] = iucv_path_complete,
14872356f4cbSMartin Schwidefsky 		[0x03] = iucv_path_severed,
14882356f4cbSMartin Schwidefsky 		[0x04] = iucv_path_quiesced,
14892356f4cbSMartin Schwidefsky 		[0x05] = iucv_path_resumed,
14902356f4cbSMartin Schwidefsky 		[0x06] = iucv_message_complete,
14912356f4cbSMartin Schwidefsky 		[0x07] = iucv_message_complete,
14922356f4cbSMartin Schwidefsky 		[0x08] = iucv_message_pending,
14932356f4cbSMartin Schwidefsky 		[0x09] = iucv_message_pending,
14942356f4cbSMartin Schwidefsky 	};
149504b090d5SMartin Schwidefsky 	struct list_head task_queue = LIST_HEAD_INIT(task_queue);
149604b090d5SMartin Schwidefsky 	struct iucv_irq_list *p, *n;
14972356f4cbSMartin Schwidefsky 
14982356f4cbSMartin Schwidefsky 	/* Serialize tasklet, iucv_path_sever and iucv_path_connect. */
149913fdc9a7SUrsula Braun 	if (!spin_trylock(&iucv_table_lock)) {
150013fdc9a7SUrsula Braun 		tasklet_schedule(&iucv_tasklet);
150113fdc9a7SUrsula Braun 		return;
150213fdc9a7SUrsula Braun 	}
150304b090d5SMartin Schwidefsky 	iucv_active_cpu = smp_processor_id();
15042356f4cbSMartin Schwidefsky 
150504b090d5SMartin Schwidefsky 	spin_lock_irq(&iucv_queue_lock);
150604b090d5SMartin Schwidefsky 	list_splice_init(&iucv_task_queue, &task_queue);
150704b090d5SMartin Schwidefsky 	spin_unlock_irq(&iucv_queue_lock);
150804b090d5SMartin Schwidefsky 
150904b090d5SMartin Schwidefsky 	list_for_each_entry_safe(p, n, &task_queue, list) {
15102356f4cbSMartin Schwidefsky 		list_del_init(&p->list);
15112356f4cbSMartin Schwidefsky 		irq_fn[p->data.iptype](&p->data);
15122356f4cbSMartin Schwidefsky 		kfree(p);
15132356f4cbSMartin Schwidefsky 	}
15142356f4cbSMartin Schwidefsky 
151504b090d5SMartin Schwidefsky 	iucv_active_cpu = -1;
15162356f4cbSMartin Schwidefsky 	spin_unlock(&iucv_table_lock);
15172356f4cbSMartin Schwidefsky }
15182356f4cbSMartin Schwidefsky 
15192356f4cbSMartin Schwidefsky /**
152004b090d5SMartin Schwidefsky  * iucv_work_fn:
152104b090d5SMartin Schwidefsky  *
152204b090d5SMartin Schwidefsky  * This work function loops over the queue of path pending irq blocks
152304b090d5SMartin Schwidefsky  * created by iucv_external_interrupt, calls the appropriate action
152404b090d5SMartin Schwidefsky  * handler and then frees the buffer.
152504b090d5SMartin Schwidefsky  */
152604b090d5SMartin Schwidefsky static void iucv_work_fn(struct work_struct *work)
152704b090d5SMartin Schwidefsky {
152804b090d5SMartin Schwidefsky 	typedef void iucv_irq_fn(struct iucv_irq_data *);
152904b090d5SMartin Schwidefsky 	struct list_head work_queue = LIST_HEAD_INIT(work_queue);
153004b090d5SMartin Schwidefsky 	struct iucv_irq_list *p, *n;
153104b090d5SMartin Schwidefsky 
153204b090d5SMartin Schwidefsky 	/* Serialize tasklet, iucv_path_sever and iucv_path_connect. */
153304b090d5SMartin Schwidefsky 	spin_lock_bh(&iucv_table_lock);
153404b090d5SMartin Schwidefsky 	iucv_active_cpu = smp_processor_id();
153504b090d5SMartin Schwidefsky 
153604b090d5SMartin Schwidefsky 	spin_lock_irq(&iucv_queue_lock);
153704b090d5SMartin Schwidefsky 	list_splice_init(&iucv_work_queue, &work_queue);
153804b090d5SMartin Schwidefsky 	spin_unlock_irq(&iucv_queue_lock);
153904b090d5SMartin Schwidefsky 
154004b090d5SMartin Schwidefsky 	iucv_cleanup_queue();
154104b090d5SMartin Schwidefsky 	list_for_each_entry_safe(p, n, &work_queue, list) {
154204b090d5SMartin Schwidefsky 		list_del_init(&p->list);
154304b090d5SMartin Schwidefsky 		iucv_path_pending(&p->data);
154404b090d5SMartin Schwidefsky 		kfree(p);
154504b090d5SMartin Schwidefsky 	}
154604b090d5SMartin Schwidefsky 
154704b090d5SMartin Schwidefsky 	iucv_active_cpu = -1;
154804b090d5SMartin Schwidefsky 	spin_unlock_bh(&iucv_table_lock);
154904b090d5SMartin Schwidefsky }
155004b090d5SMartin Schwidefsky 
155104b090d5SMartin Schwidefsky /**
15522356f4cbSMartin Schwidefsky  * iucv_external_interrupt
15532356f4cbSMartin Schwidefsky  * @code: irq code
15542356f4cbSMartin Schwidefsky  *
15552356f4cbSMartin Schwidefsky  * Handles external interrupts coming in from CP.
155604b090d5SMartin Schwidefsky  * Places the interrupt buffer on a queue and schedules iucv_tasklet_fn().
15572356f4cbSMartin Schwidefsky  */
15582356f4cbSMartin Schwidefsky static void iucv_external_interrupt(u16 code)
15592356f4cbSMartin Schwidefsky {
15602356f4cbSMartin Schwidefsky 	struct iucv_irq_data *p;
156104b090d5SMartin Schwidefsky 	struct iucv_irq_list *work;
15622356f4cbSMartin Schwidefsky 
1563*70cf5035SChristoph Lameter 	p = iucv_irq_data[smp_processor_id()];
15642356f4cbSMartin Schwidefsky 	if (p->ippathid >= iucv_max_pathid) {
15652356f4cbSMartin Schwidefsky 		printk(KERN_WARNING "iucv_do_int: Got interrupt with "
15662356f4cbSMartin Schwidefsky 		       "pathid %d > max_connections (%ld)\n",
15672356f4cbSMartin Schwidefsky 		       p->ippathid, iucv_max_pathid - 1);
15682356f4cbSMartin Schwidefsky 		iucv_sever_pathid(p->ippathid, iucv_error_no_listener);
15692356f4cbSMartin Schwidefsky 		return;
15702356f4cbSMartin Schwidefsky 	}
15712356f4cbSMartin Schwidefsky 	if (p->iptype  < 0x01 || p->iptype > 0x09) {
15722356f4cbSMartin Schwidefsky 		printk(KERN_ERR "iucv_do_int: unknown iucv interrupt\n");
15732356f4cbSMartin Schwidefsky 		return;
15742356f4cbSMartin Schwidefsky 	}
157504b090d5SMartin Schwidefsky 	work = kmalloc(sizeof(struct iucv_irq_list), GFP_ATOMIC);
15762356f4cbSMartin Schwidefsky 	if (!work) {
15772356f4cbSMartin Schwidefsky 		printk(KERN_WARNING "iucv_external_interrupt: out of memory\n");
15782356f4cbSMartin Schwidefsky 		return;
15792356f4cbSMartin Schwidefsky 	}
15802356f4cbSMartin Schwidefsky 	memcpy(&work->data, p, sizeof(work->data));
158104b090d5SMartin Schwidefsky 	spin_lock(&iucv_queue_lock);
158204b090d5SMartin Schwidefsky 	if (p->iptype == 0x01) {
158304b090d5SMartin Schwidefsky 		/* Path pending interrupt. */
15842356f4cbSMartin Schwidefsky 		list_add_tail(&work->list, &iucv_work_queue);
158504b090d5SMartin Schwidefsky 		schedule_work(&iucv_work);
158604b090d5SMartin Schwidefsky 	} else {
158704b090d5SMartin Schwidefsky 		/* The other interrupts. */
158804b090d5SMartin Schwidefsky 		list_add_tail(&work->list, &iucv_task_queue);
15892356f4cbSMartin Schwidefsky 		tasklet_schedule(&iucv_tasklet);
15902356f4cbSMartin Schwidefsky 	}
159104b090d5SMartin Schwidefsky 	spin_unlock(&iucv_queue_lock);
159204b090d5SMartin Schwidefsky }
15932356f4cbSMartin Schwidefsky 
15942356f4cbSMartin Schwidefsky /**
15952356f4cbSMartin Schwidefsky  * iucv_init
15962356f4cbSMartin Schwidefsky  *
15972356f4cbSMartin Schwidefsky  * Allocates and initializes various data structures.
15982356f4cbSMartin Schwidefsky  */
1599da99f056SHeiko Carstens static int __init iucv_init(void)
16002356f4cbSMartin Schwidefsky {
16012356f4cbSMartin Schwidefsky 	int rc;
1602*70cf5035SChristoph Lameter 	int cpu;
16032356f4cbSMartin Schwidefsky 
16042356f4cbSMartin Schwidefsky 	if (!MACHINE_IS_VM) {
16052356f4cbSMartin Schwidefsky 		rc = -EPROTONOSUPPORT;
16062356f4cbSMartin Schwidefsky 		goto out;
16072356f4cbSMartin Schwidefsky 	}
16082356f4cbSMartin Schwidefsky 	rc = iucv_query_maxconn();
16092356f4cbSMartin Schwidefsky 	if (rc)
16102356f4cbSMartin Schwidefsky 		goto out;
16112356f4cbSMartin Schwidefsky 	rc = register_external_interrupt(0x4000, iucv_external_interrupt);
16122356f4cbSMartin Schwidefsky 	if (rc)
16132356f4cbSMartin Schwidefsky 		goto out;
16142356f4cbSMartin Schwidefsky 	rc = bus_register(&iucv_bus);
16152356f4cbSMartin Schwidefsky 	if (rc)
16162356f4cbSMartin Schwidefsky 		goto out_int;
16172356f4cbSMartin Schwidefsky 	iucv_root = s390_root_dev_register("iucv");
16182356f4cbSMartin Schwidefsky 	if (IS_ERR(iucv_root)) {
16192356f4cbSMartin Schwidefsky 		rc = PTR_ERR(iucv_root);
16202356f4cbSMartin Schwidefsky 		goto out_bus;
16212356f4cbSMartin Schwidefsky 	}
1622*70cf5035SChristoph Lameter 
1623*70cf5035SChristoph Lameter 	for_each_online_cpu(cpu) {
1624da99f056SHeiko Carstens 		/* Note: GFP_DMA used to get memory below 2G */
1625*70cf5035SChristoph Lameter 		iucv_irq_data[cpu] = kmalloc_node(sizeof(struct iucv_irq_data),
1626*70cf5035SChristoph Lameter 				     GFP_KERNEL|GFP_DMA, cpu_to_node(cpu));
1627*70cf5035SChristoph Lameter 		if (!iucv_irq_data[cpu]) {
16282356f4cbSMartin Schwidefsky 			rc = -ENOMEM;
1629*70cf5035SChristoph Lameter 			goto out_free;
16302356f4cbSMartin Schwidefsky 		}
1631*70cf5035SChristoph Lameter 
16322356f4cbSMartin Schwidefsky 		/* Allocate parameter blocks. */
1633*70cf5035SChristoph Lameter 		iucv_param[cpu] = kmalloc_node(sizeof(union iucv_param),
1634*70cf5035SChristoph Lameter 				  GFP_KERNEL|GFP_DMA, cpu_to_node(cpu));
1635*70cf5035SChristoph Lameter 		if (!iucv_param[cpu]) {
16362356f4cbSMartin Schwidefsky 			rc = -ENOMEM;
1637*70cf5035SChristoph Lameter 			goto out_free;
1638*70cf5035SChristoph Lameter 		}
16392356f4cbSMartin Schwidefsky 	}
16402356f4cbSMartin Schwidefsky 	register_hotcpu_notifier(&iucv_cpu_notifier);
16412356f4cbSMartin Schwidefsky 	ASCEBC(iucv_error_no_listener, 16);
16422356f4cbSMartin Schwidefsky 	ASCEBC(iucv_error_no_memory, 16);
16432356f4cbSMartin Schwidefsky 	ASCEBC(iucv_error_pathid, 16);
16442356f4cbSMartin Schwidefsky 	iucv_available = 1;
16452356f4cbSMartin Schwidefsky 	return 0;
16462356f4cbSMartin Schwidefsky 
1647*70cf5035SChristoph Lameter out_free:
1648*70cf5035SChristoph Lameter 	for_each_possible_cpu(cpu) {
1649*70cf5035SChristoph Lameter 		kfree(iucv_param[cpu]);
1650*70cf5035SChristoph Lameter 		iucv_param[cpu] = NULL;
1651*70cf5035SChristoph Lameter 		kfree(iucv_irq_data[cpu]);
1652*70cf5035SChristoph Lameter 		iucv_irq_data[cpu] = NULL;
1653*70cf5035SChristoph Lameter 	}
16542356f4cbSMartin Schwidefsky 	s390_root_dev_unregister(iucv_root);
16552356f4cbSMartin Schwidefsky out_bus:
16562356f4cbSMartin Schwidefsky 	bus_unregister(&iucv_bus);
16572356f4cbSMartin Schwidefsky out_int:
16582356f4cbSMartin Schwidefsky 	unregister_external_interrupt(0x4000, iucv_external_interrupt);
16592356f4cbSMartin Schwidefsky out:
16602356f4cbSMartin Schwidefsky 	return rc;
16612356f4cbSMartin Schwidefsky }
16622356f4cbSMartin Schwidefsky 
16632356f4cbSMartin Schwidefsky /**
16642356f4cbSMartin Schwidefsky  * iucv_exit
16652356f4cbSMartin Schwidefsky  *
16662356f4cbSMartin Schwidefsky  * Frees everything allocated from iucv_init.
16672356f4cbSMartin Schwidefsky  */
1668da99f056SHeiko Carstens static void __exit iucv_exit(void)
16692356f4cbSMartin Schwidefsky {
167004b090d5SMartin Schwidefsky 	struct iucv_irq_list *p, *n;
1671*70cf5035SChristoph Lameter 	int cpu;
16722356f4cbSMartin Schwidefsky 
167304b090d5SMartin Schwidefsky 	spin_lock_irq(&iucv_queue_lock);
167404b090d5SMartin Schwidefsky 	list_for_each_entry_safe(p, n, &iucv_task_queue, list)
167504b090d5SMartin Schwidefsky 		kfree(p);
16762356f4cbSMartin Schwidefsky 	list_for_each_entry_safe(p, n, &iucv_work_queue, list)
16772356f4cbSMartin Schwidefsky 		kfree(p);
167804b090d5SMartin Schwidefsky 	spin_unlock_irq(&iucv_queue_lock);
16792356f4cbSMartin Schwidefsky 	unregister_hotcpu_notifier(&iucv_cpu_notifier);
1680*70cf5035SChristoph Lameter 	for_each_possible_cpu(cpu) {
1681*70cf5035SChristoph Lameter 		kfree(iucv_param[cpu]);
1682*70cf5035SChristoph Lameter 		iucv_param[cpu] = NULL;
1683*70cf5035SChristoph Lameter 		kfree(iucv_irq_data[cpu]);
1684*70cf5035SChristoph Lameter 		iucv_irq_data[cpu] = NULL;
1685*70cf5035SChristoph Lameter 	}
16862356f4cbSMartin Schwidefsky 	s390_root_dev_unregister(iucv_root);
16872356f4cbSMartin Schwidefsky 	bus_unregister(&iucv_bus);
16882356f4cbSMartin Schwidefsky 	unregister_external_interrupt(0x4000, iucv_external_interrupt);
16892356f4cbSMartin Schwidefsky }
16902356f4cbSMartin Schwidefsky 
16912356f4cbSMartin Schwidefsky subsys_initcall(iucv_init);
16922356f4cbSMartin Schwidefsky module_exit(iucv_exit);
16932356f4cbSMartin Schwidefsky 
16942356f4cbSMartin Schwidefsky MODULE_AUTHOR("(C) 2001 IBM Corp. by Fritz Elfert (felfert@millenux.com)");
16952356f4cbSMartin Schwidefsky MODULE_DESCRIPTION("Linux for S/390 IUCV lowlevel driver");
16962356f4cbSMartin Schwidefsky MODULE_LICENSE("GPL");
1697