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