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