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