18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * IUCV base infrastructure. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2001, 2009 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author(s): 88c2ecf20Sopenharmony_ci * Original source: 98c2ecf20Sopenharmony_ci * Alan Altmark (Alan_Altmark@us.ibm.com) Sept. 2000 108c2ecf20Sopenharmony_ci * Xenia Tkatschow (xenia@us.ibm.com) 118c2ecf20Sopenharmony_ci * 2Gb awareness and general cleanup: 128c2ecf20Sopenharmony_ci * Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com) 138c2ecf20Sopenharmony_ci * Rewritten for af_iucv: 148c2ecf20Sopenharmony_ci * Martin Schwidefsky <schwidefsky@de.ibm.com> 158c2ecf20Sopenharmony_ci * PM functions: 168c2ecf20Sopenharmony_ci * Ursula Braun (ursula.braun@de.ibm.com) 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * Documentation used: 198c2ecf20Sopenharmony_ci * The original source 208c2ecf20Sopenharmony_ci * CP Programming Service, IBM document # SC24-5760 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "iucv" 248c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <linux/kernel_stat.h> 278c2ecf20Sopenharmony_ci#include <linux/module.h> 288c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 298c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 308c2ecf20Sopenharmony_ci#include <linux/kernel.h> 318c2ecf20Sopenharmony_ci#include <linux/slab.h> 328c2ecf20Sopenharmony_ci#include <linux/init.h> 338c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 348c2ecf20Sopenharmony_ci#include <linux/list.h> 358c2ecf20Sopenharmony_ci#include <linux/errno.h> 368c2ecf20Sopenharmony_ci#include <linux/err.h> 378c2ecf20Sopenharmony_ci#include <linux/device.h> 388c2ecf20Sopenharmony_ci#include <linux/cpu.h> 398c2ecf20Sopenharmony_ci#include <linux/reboot.h> 408c2ecf20Sopenharmony_ci#include <net/iucv/iucv.h> 418c2ecf20Sopenharmony_ci#include <linux/atomic.h> 428c2ecf20Sopenharmony_ci#include <asm/ebcdic.h> 438c2ecf20Sopenharmony_ci#include <asm/io.h> 448c2ecf20Sopenharmony_ci#include <asm/irq.h> 458c2ecf20Sopenharmony_ci#include <asm/smp.h> 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* 488c2ecf20Sopenharmony_ci * FLAGS: 498c2ecf20Sopenharmony_ci * All flags are defined in the field IPFLAGS1 of each function 508c2ecf20Sopenharmony_ci * and can be found in CP Programming Services. 518c2ecf20Sopenharmony_ci * IPSRCCLS - Indicates you have specified a source class. 528c2ecf20Sopenharmony_ci * IPTRGCLS - Indicates you have specified a target class. 538c2ecf20Sopenharmony_ci * IPFGPID - Indicates you have specified a pathid. 548c2ecf20Sopenharmony_ci * IPFGMID - Indicates you have specified a message ID. 558c2ecf20Sopenharmony_ci * IPNORPY - Indicates a one-way message. No reply expected. 568c2ecf20Sopenharmony_ci * IPALL - Indicates that all paths are affected. 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_ci#define IUCV_IPSRCCLS 0x01 598c2ecf20Sopenharmony_ci#define IUCV_IPTRGCLS 0x01 608c2ecf20Sopenharmony_ci#define IUCV_IPFGPID 0x02 618c2ecf20Sopenharmony_ci#define IUCV_IPFGMID 0x04 628c2ecf20Sopenharmony_ci#define IUCV_IPNORPY 0x10 638c2ecf20Sopenharmony_ci#define IUCV_IPALL 0x80 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int iucv_bus_match(struct device *dev, struct device_driver *drv) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci return 0; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistruct bus_type iucv_bus = { 718c2ecf20Sopenharmony_ci .name = "iucv", 728c2ecf20Sopenharmony_ci .match = iucv_bus_match, 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_bus); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistruct device *iucv_root; 778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_root); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic int iucv_available; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* General IUCV interrupt structure */ 828c2ecf20Sopenharmony_cistruct iucv_irq_data { 838c2ecf20Sopenharmony_ci u16 ippathid; 848c2ecf20Sopenharmony_ci u8 ipflags1; 858c2ecf20Sopenharmony_ci u8 iptype; 868c2ecf20Sopenharmony_ci u32 res2[9]; 878c2ecf20Sopenharmony_ci}; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistruct iucv_irq_list { 908c2ecf20Sopenharmony_ci struct list_head list; 918c2ecf20Sopenharmony_ci struct iucv_irq_data data; 928c2ecf20Sopenharmony_ci}; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic struct iucv_irq_data *iucv_irq_data[NR_CPUS]; 958c2ecf20Sopenharmony_cistatic cpumask_t iucv_buffer_cpumask = { CPU_BITS_NONE }; 968c2ecf20Sopenharmony_cistatic cpumask_t iucv_irq_cpumask = { CPU_BITS_NONE }; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* 998c2ecf20Sopenharmony_ci * Queue of interrupt buffers lock for delivery via the tasklet 1008c2ecf20Sopenharmony_ci * (fast but can't call smp_call_function). 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_cistatic LIST_HEAD(iucv_task_queue); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* 1058c2ecf20Sopenharmony_ci * The tasklet for fast delivery of iucv interrupts. 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_cistatic void iucv_tasklet_fn(unsigned long); 1088c2ecf20Sopenharmony_cistatic DECLARE_TASKLET_OLD(iucv_tasklet, iucv_tasklet_fn); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/* 1118c2ecf20Sopenharmony_ci * Queue of interrupt buffers for delivery via a work queue 1128c2ecf20Sopenharmony_ci * (slower but can call smp_call_function). 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_cistatic LIST_HEAD(iucv_work_queue); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* 1178c2ecf20Sopenharmony_ci * The work element to deliver path pending interrupts. 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_cistatic void iucv_work_fn(struct work_struct *work); 1208c2ecf20Sopenharmony_cistatic DECLARE_WORK(iucv_work, iucv_work_fn); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/* 1238c2ecf20Sopenharmony_ci * Spinlock protecting task and work queue. 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(iucv_queue_lock); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cienum iucv_command_codes { 1288c2ecf20Sopenharmony_ci IUCV_QUERY = 0, 1298c2ecf20Sopenharmony_ci IUCV_RETRIEVE_BUFFER = 2, 1308c2ecf20Sopenharmony_ci IUCV_SEND = 4, 1318c2ecf20Sopenharmony_ci IUCV_RECEIVE = 5, 1328c2ecf20Sopenharmony_ci IUCV_REPLY = 6, 1338c2ecf20Sopenharmony_ci IUCV_REJECT = 8, 1348c2ecf20Sopenharmony_ci IUCV_PURGE = 9, 1358c2ecf20Sopenharmony_ci IUCV_ACCEPT = 10, 1368c2ecf20Sopenharmony_ci IUCV_CONNECT = 11, 1378c2ecf20Sopenharmony_ci IUCV_DECLARE_BUFFER = 12, 1388c2ecf20Sopenharmony_ci IUCV_QUIESCE = 13, 1398c2ecf20Sopenharmony_ci IUCV_RESUME = 14, 1408c2ecf20Sopenharmony_ci IUCV_SEVER = 15, 1418c2ecf20Sopenharmony_ci IUCV_SETMASK = 16, 1428c2ecf20Sopenharmony_ci IUCV_SETCONTROLMASK = 17, 1438c2ecf20Sopenharmony_ci}; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/* 1468c2ecf20Sopenharmony_ci * Error messages that are used with the iucv_sever function. They get 1478c2ecf20Sopenharmony_ci * converted to EBCDIC. 1488c2ecf20Sopenharmony_ci */ 1498c2ecf20Sopenharmony_cistatic char iucv_error_no_listener[16] = "NO LISTENER"; 1508c2ecf20Sopenharmony_cistatic char iucv_error_no_memory[16] = "NO MEMORY"; 1518c2ecf20Sopenharmony_cistatic char iucv_error_pathid[16] = "INVALID PATHID"; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/* 1548c2ecf20Sopenharmony_ci * iucv_handler_list: List of registered handlers. 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_cistatic LIST_HEAD(iucv_handler_list); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/* 1598c2ecf20Sopenharmony_ci * iucv_path_table: an array of iucv_path structures. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_cistatic struct iucv_path **iucv_path_table; 1628c2ecf20Sopenharmony_cistatic unsigned long iucv_max_pathid; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci/* 1658c2ecf20Sopenharmony_ci * iucv_lock: spinlock protecting iucv_handler_list and iucv_pathid_table 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(iucv_table_lock); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci/* 1708c2ecf20Sopenharmony_ci * iucv_active_cpu: contains the number of the cpu executing the tasklet 1718c2ecf20Sopenharmony_ci * or the work handler. Needed for iucv_path_sever called from tasklet. 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_cistatic int iucv_active_cpu = -1; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/* 1768c2ecf20Sopenharmony_ci * Mutex and wait queue for iucv_register/iucv_unregister. 1778c2ecf20Sopenharmony_ci */ 1788c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(iucv_register_mutex); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci/* 1818c2ecf20Sopenharmony_ci * Counter for number of non-smp capable handlers. 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_cistatic int iucv_nonsmp_handler; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci/* 1868c2ecf20Sopenharmony_ci * IUCV control data structure. Used by iucv_path_accept, iucv_path_connect, 1878c2ecf20Sopenharmony_ci * iucv_path_quiesce and iucv_path_sever. 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_cistruct iucv_cmd_control { 1908c2ecf20Sopenharmony_ci u16 ippathid; 1918c2ecf20Sopenharmony_ci u8 ipflags1; 1928c2ecf20Sopenharmony_ci u8 iprcode; 1938c2ecf20Sopenharmony_ci u16 ipmsglim; 1948c2ecf20Sopenharmony_ci u16 res1; 1958c2ecf20Sopenharmony_ci u8 ipvmid[8]; 1968c2ecf20Sopenharmony_ci u8 ipuser[16]; 1978c2ecf20Sopenharmony_ci u8 iptarget[8]; 1988c2ecf20Sopenharmony_ci} __attribute__ ((packed,aligned(8))); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci/* 2018c2ecf20Sopenharmony_ci * Data in parameter list iucv structure. Used by iucv_message_send, 2028c2ecf20Sopenharmony_ci * iucv_message_send2way and iucv_message_reply. 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_cistruct iucv_cmd_dpl { 2058c2ecf20Sopenharmony_ci u16 ippathid; 2068c2ecf20Sopenharmony_ci u8 ipflags1; 2078c2ecf20Sopenharmony_ci u8 iprcode; 2088c2ecf20Sopenharmony_ci u32 ipmsgid; 2098c2ecf20Sopenharmony_ci u32 iptrgcls; 2108c2ecf20Sopenharmony_ci u8 iprmmsg[8]; 2118c2ecf20Sopenharmony_ci u32 ipsrccls; 2128c2ecf20Sopenharmony_ci u32 ipmsgtag; 2138c2ecf20Sopenharmony_ci u32 ipbfadr2; 2148c2ecf20Sopenharmony_ci u32 ipbfln2f; 2158c2ecf20Sopenharmony_ci u32 res; 2168c2ecf20Sopenharmony_ci} __attribute__ ((packed,aligned(8))); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci/* 2198c2ecf20Sopenharmony_ci * Data in buffer iucv structure. Used by iucv_message_receive, 2208c2ecf20Sopenharmony_ci * iucv_message_reject, iucv_message_send, iucv_message_send2way 2218c2ecf20Sopenharmony_ci * and iucv_declare_cpu. 2228c2ecf20Sopenharmony_ci */ 2238c2ecf20Sopenharmony_cistruct iucv_cmd_db { 2248c2ecf20Sopenharmony_ci u16 ippathid; 2258c2ecf20Sopenharmony_ci u8 ipflags1; 2268c2ecf20Sopenharmony_ci u8 iprcode; 2278c2ecf20Sopenharmony_ci u32 ipmsgid; 2288c2ecf20Sopenharmony_ci u32 iptrgcls; 2298c2ecf20Sopenharmony_ci u32 ipbfadr1; 2308c2ecf20Sopenharmony_ci u32 ipbfln1f; 2318c2ecf20Sopenharmony_ci u32 ipsrccls; 2328c2ecf20Sopenharmony_ci u32 ipmsgtag; 2338c2ecf20Sopenharmony_ci u32 ipbfadr2; 2348c2ecf20Sopenharmony_ci u32 ipbfln2f; 2358c2ecf20Sopenharmony_ci u32 res; 2368c2ecf20Sopenharmony_ci} __attribute__ ((packed,aligned(8))); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci/* 2398c2ecf20Sopenharmony_ci * Purge message iucv structure. Used by iucv_message_purge. 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_cistruct iucv_cmd_purge { 2428c2ecf20Sopenharmony_ci u16 ippathid; 2438c2ecf20Sopenharmony_ci u8 ipflags1; 2448c2ecf20Sopenharmony_ci u8 iprcode; 2458c2ecf20Sopenharmony_ci u32 ipmsgid; 2468c2ecf20Sopenharmony_ci u8 ipaudit[3]; 2478c2ecf20Sopenharmony_ci u8 res1[5]; 2488c2ecf20Sopenharmony_ci u32 res2; 2498c2ecf20Sopenharmony_ci u32 ipsrccls; 2508c2ecf20Sopenharmony_ci u32 ipmsgtag; 2518c2ecf20Sopenharmony_ci u32 res3[3]; 2528c2ecf20Sopenharmony_ci} __attribute__ ((packed,aligned(8))); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci/* 2558c2ecf20Sopenharmony_ci * Set mask iucv structure. Used by iucv_enable_cpu. 2568c2ecf20Sopenharmony_ci */ 2578c2ecf20Sopenharmony_cistruct iucv_cmd_set_mask { 2588c2ecf20Sopenharmony_ci u8 ipmask; 2598c2ecf20Sopenharmony_ci u8 res1[2]; 2608c2ecf20Sopenharmony_ci u8 iprcode; 2618c2ecf20Sopenharmony_ci u32 res2[9]; 2628c2ecf20Sopenharmony_ci} __attribute__ ((packed,aligned(8))); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ciunion iucv_param { 2658c2ecf20Sopenharmony_ci struct iucv_cmd_control ctrl; 2668c2ecf20Sopenharmony_ci struct iucv_cmd_dpl dpl; 2678c2ecf20Sopenharmony_ci struct iucv_cmd_db db; 2688c2ecf20Sopenharmony_ci struct iucv_cmd_purge purge; 2698c2ecf20Sopenharmony_ci struct iucv_cmd_set_mask set_mask; 2708c2ecf20Sopenharmony_ci}; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci/* 2738c2ecf20Sopenharmony_ci * Anchor for per-cpu IUCV command parameter block. 2748c2ecf20Sopenharmony_ci */ 2758c2ecf20Sopenharmony_cistatic union iucv_param *iucv_param[NR_CPUS]; 2768c2ecf20Sopenharmony_cistatic union iucv_param *iucv_param_irq[NR_CPUS]; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci/** 2798c2ecf20Sopenharmony_ci * iucv_call_b2f0 2808c2ecf20Sopenharmony_ci * @code: identifier of IUCV call to CP. 2818c2ecf20Sopenharmony_ci * @parm: pointer to a struct iucv_parm block 2828c2ecf20Sopenharmony_ci * 2838c2ecf20Sopenharmony_ci * Calls CP to execute IUCV commands. 2848c2ecf20Sopenharmony_ci * 2858c2ecf20Sopenharmony_ci * Returns the result of the CP IUCV call. 2868c2ecf20Sopenharmony_ci */ 2878c2ecf20Sopenharmony_cistatic inline int __iucv_call_b2f0(int command, union iucv_param *parm) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci register unsigned long reg0 asm ("0"); 2908c2ecf20Sopenharmony_ci register unsigned long reg1 asm ("1"); 2918c2ecf20Sopenharmony_ci int ccode; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci reg0 = command; 2948c2ecf20Sopenharmony_ci reg1 = (unsigned long)parm; 2958c2ecf20Sopenharmony_ci asm volatile( 2968c2ecf20Sopenharmony_ci " .long 0xb2f01000\n" 2978c2ecf20Sopenharmony_ci " ipm %0\n" 2988c2ecf20Sopenharmony_ci " srl %0,28\n" 2998c2ecf20Sopenharmony_ci : "=d" (ccode), "=m" (*parm), "+d" (reg0), "+a" (reg1) 3008c2ecf20Sopenharmony_ci : "m" (*parm) : "cc"); 3018c2ecf20Sopenharmony_ci return ccode; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic inline int iucv_call_b2f0(int command, union iucv_param *parm) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci int ccode; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci ccode = __iucv_call_b2f0(command, parm); 3098c2ecf20Sopenharmony_ci return ccode == 1 ? parm->ctrl.iprcode : ccode; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci/** 3138c2ecf20Sopenharmony_ci * iucv_query_maxconn 3148c2ecf20Sopenharmony_ci * 3158c2ecf20Sopenharmony_ci * Determines the maximum number of connections that may be established. 3168c2ecf20Sopenharmony_ci * 3178c2ecf20Sopenharmony_ci * Returns the maximum number of connections or -EPERM is IUCV is not 3188c2ecf20Sopenharmony_ci * available. 3198c2ecf20Sopenharmony_ci */ 3208c2ecf20Sopenharmony_cistatic int __iucv_query_maxconn(void *param, unsigned long *max_pathid) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci register unsigned long reg0 asm ("0"); 3238c2ecf20Sopenharmony_ci register unsigned long reg1 asm ("1"); 3248c2ecf20Sopenharmony_ci int ccode; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci reg0 = IUCV_QUERY; 3278c2ecf20Sopenharmony_ci reg1 = (unsigned long) param; 3288c2ecf20Sopenharmony_ci asm volatile ( 3298c2ecf20Sopenharmony_ci " .long 0xb2f01000\n" 3308c2ecf20Sopenharmony_ci " ipm %0\n" 3318c2ecf20Sopenharmony_ci " srl %0,28\n" 3328c2ecf20Sopenharmony_ci : "=d" (ccode), "+d" (reg0), "+d" (reg1) : : "cc"); 3338c2ecf20Sopenharmony_ci *max_pathid = reg1; 3348c2ecf20Sopenharmony_ci return ccode; 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic int iucv_query_maxconn(void) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci unsigned long max_pathid; 3408c2ecf20Sopenharmony_ci void *param; 3418c2ecf20Sopenharmony_ci int ccode; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci param = kzalloc(sizeof(union iucv_param), GFP_KERNEL | GFP_DMA); 3448c2ecf20Sopenharmony_ci if (!param) 3458c2ecf20Sopenharmony_ci return -ENOMEM; 3468c2ecf20Sopenharmony_ci ccode = __iucv_query_maxconn(param, &max_pathid); 3478c2ecf20Sopenharmony_ci if (ccode == 0) 3488c2ecf20Sopenharmony_ci iucv_max_pathid = max_pathid; 3498c2ecf20Sopenharmony_ci kfree(param); 3508c2ecf20Sopenharmony_ci return ccode ? -EPERM : 0; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci/** 3548c2ecf20Sopenharmony_ci * iucv_allow_cpu 3558c2ecf20Sopenharmony_ci * @data: unused 3568c2ecf20Sopenharmony_ci * 3578c2ecf20Sopenharmony_ci * Allow iucv interrupts on this cpu. 3588c2ecf20Sopenharmony_ci */ 3598c2ecf20Sopenharmony_cistatic void iucv_allow_cpu(void *data) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci int cpu = smp_processor_id(); 3628c2ecf20Sopenharmony_ci union iucv_param *parm; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* 3658c2ecf20Sopenharmony_ci * Enable all iucv interrupts. 3668c2ecf20Sopenharmony_ci * ipmask contains bits for the different interrupts 3678c2ecf20Sopenharmony_ci * 0x80 - Flag to allow nonpriority message pending interrupts 3688c2ecf20Sopenharmony_ci * 0x40 - Flag to allow priority message pending interrupts 3698c2ecf20Sopenharmony_ci * 0x20 - Flag to allow nonpriority message completion interrupts 3708c2ecf20Sopenharmony_ci * 0x10 - Flag to allow priority message completion interrupts 3718c2ecf20Sopenharmony_ci * 0x08 - Flag to allow IUCV control interrupts 3728c2ecf20Sopenharmony_ci */ 3738c2ecf20Sopenharmony_ci parm = iucv_param_irq[cpu]; 3748c2ecf20Sopenharmony_ci memset(parm, 0, sizeof(union iucv_param)); 3758c2ecf20Sopenharmony_ci parm->set_mask.ipmask = 0xf8; 3768c2ecf20Sopenharmony_ci iucv_call_b2f0(IUCV_SETMASK, parm); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* 3798c2ecf20Sopenharmony_ci * Enable all iucv control interrupts. 3808c2ecf20Sopenharmony_ci * ipmask contains bits for the different interrupts 3818c2ecf20Sopenharmony_ci * 0x80 - Flag to allow pending connections interrupts 3828c2ecf20Sopenharmony_ci * 0x40 - Flag to allow connection complete interrupts 3838c2ecf20Sopenharmony_ci * 0x20 - Flag to allow connection severed interrupts 3848c2ecf20Sopenharmony_ci * 0x10 - Flag to allow connection quiesced interrupts 3858c2ecf20Sopenharmony_ci * 0x08 - Flag to allow connection resumed interrupts 3868c2ecf20Sopenharmony_ci */ 3878c2ecf20Sopenharmony_ci memset(parm, 0, sizeof(union iucv_param)); 3888c2ecf20Sopenharmony_ci parm->set_mask.ipmask = 0xf8; 3898c2ecf20Sopenharmony_ci iucv_call_b2f0(IUCV_SETCONTROLMASK, parm); 3908c2ecf20Sopenharmony_ci /* Set indication that iucv interrupts are allowed for this cpu. */ 3918c2ecf20Sopenharmony_ci cpumask_set_cpu(cpu, &iucv_irq_cpumask); 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci/** 3958c2ecf20Sopenharmony_ci * iucv_block_cpu 3968c2ecf20Sopenharmony_ci * @data: unused 3978c2ecf20Sopenharmony_ci * 3988c2ecf20Sopenharmony_ci * Block iucv interrupts on this cpu. 3998c2ecf20Sopenharmony_ci */ 4008c2ecf20Sopenharmony_cistatic void iucv_block_cpu(void *data) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci int cpu = smp_processor_id(); 4038c2ecf20Sopenharmony_ci union iucv_param *parm; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci /* Disable all iucv interrupts. */ 4068c2ecf20Sopenharmony_ci parm = iucv_param_irq[cpu]; 4078c2ecf20Sopenharmony_ci memset(parm, 0, sizeof(union iucv_param)); 4088c2ecf20Sopenharmony_ci iucv_call_b2f0(IUCV_SETMASK, parm); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* Clear indication that iucv interrupts are allowed for this cpu. */ 4118c2ecf20Sopenharmony_ci cpumask_clear_cpu(cpu, &iucv_irq_cpumask); 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci/** 4158c2ecf20Sopenharmony_ci * iucv_declare_cpu 4168c2ecf20Sopenharmony_ci * @data: unused 4178c2ecf20Sopenharmony_ci * 4188c2ecf20Sopenharmony_ci * Declare a interrupt buffer on this cpu. 4198c2ecf20Sopenharmony_ci */ 4208c2ecf20Sopenharmony_cistatic void iucv_declare_cpu(void *data) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci int cpu = smp_processor_id(); 4238c2ecf20Sopenharmony_ci union iucv_param *parm; 4248c2ecf20Sopenharmony_ci int rc; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (cpumask_test_cpu(cpu, &iucv_buffer_cpumask)) 4278c2ecf20Sopenharmony_ci return; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* Declare interrupt buffer. */ 4308c2ecf20Sopenharmony_ci parm = iucv_param_irq[cpu]; 4318c2ecf20Sopenharmony_ci memset(parm, 0, sizeof(union iucv_param)); 4328c2ecf20Sopenharmony_ci parm->db.ipbfadr1 = virt_to_phys(iucv_irq_data[cpu]); 4338c2ecf20Sopenharmony_ci rc = iucv_call_b2f0(IUCV_DECLARE_BUFFER, parm); 4348c2ecf20Sopenharmony_ci if (rc) { 4358c2ecf20Sopenharmony_ci char *err = "Unknown"; 4368c2ecf20Sopenharmony_ci switch (rc) { 4378c2ecf20Sopenharmony_ci case 0x03: 4388c2ecf20Sopenharmony_ci err = "Directory error"; 4398c2ecf20Sopenharmony_ci break; 4408c2ecf20Sopenharmony_ci case 0x0a: 4418c2ecf20Sopenharmony_ci err = "Invalid length"; 4428c2ecf20Sopenharmony_ci break; 4438c2ecf20Sopenharmony_ci case 0x13: 4448c2ecf20Sopenharmony_ci err = "Buffer already exists"; 4458c2ecf20Sopenharmony_ci break; 4468c2ecf20Sopenharmony_ci case 0x3e: 4478c2ecf20Sopenharmony_ci err = "Buffer overlap"; 4488c2ecf20Sopenharmony_ci break; 4498c2ecf20Sopenharmony_ci case 0x5c: 4508c2ecf20Sopenharmony_ci err = "Paging or storage error"; 4518c2ecf20Sopenharmony_ci break; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci pr_warn("Defining an interrupt buffer on CPU %i failed with 0x%02x (%s)\n", 4548c2ecf20Sopenharmony_ci cpu, rc, err); 4558c2ecf20Sopenharmony_ci return; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci /* Set indication that an iucv buffer exists for this cpu. */ 4598c2ecf20Sopenharmony_ci cpumask_set_cpu(cpu, &iucv_buffer_cpumask); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (iucv_nonsmp_handler == 0 || cpumask_empty(&iucv_irq_cpumask)) 4628c2ecf20Sopenharmony_ci /* Enable iucv interrupts on this cpu. */ 4638c2ecf20Sopenharmony_ci iucv_allow_cpu(NULL); 4648c2ecf20Sopenharmony_ci else 4658c2ecf20Sopenharmony_ci /* Disable iucv interrupts on this cpu. */ 4668c2ecf20Sopenharmony_ci iucv_block_cpu(NULL); 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci/** 4708c2ecf20Sopenharmony_ci * iucv_retrieve_cpu 4718c2ecf20Sopenharmony_ci * @data: unused 4728c2ecf20Sopenharmony_ci * 4738c2ecf20Sopenharmony_ci * Retrieve interrupt buffer on this cpu. 4748c2ecf20Sopenharmony_ci */ 4758c2ecf20Sopenharmony_cistatic void iucv_retrieve_cpu(void *data) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci int cpu = smp_processor_id(); 4788c2ecf20Sopenharmony_ci union iucv_param *parm; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (!cpumask_test_cpu(cpu, &iucv_buffer_cpumask)) 4818c2ecf20Sopenharmony_ci return; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci /* Block iucv interrupts. */ 4848c2ecf20Sopenharmony_ci iucv_block_cpu(NULL); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci /* Retrieve interrupt buffer. */ 4878c2ecf20Sopenharmony_ci parm = iucv_param_irq[cpu]; 4888c2ecf20Sopenharmony_ci iucv_call_b2f0(IUCV_RETRIEVE_BUFFER, parm); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* Clear indication that an iucv buffer exists for this cpu. */ 4918c2ecf20Sopenharmony_ci cpumask_clear_cpu(cpu, &iucv_buffer_cpumask); 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci/** 4958c2ecf20Sopenharmony_ci * iucv_setmask_smp 4968c2ecf20Sopenharmony_ci * 4978c2ecf20Sopenharmony_ci * Allow iucv interrupts on all cpus. 4988c2ecf20Sopenharmony_ci */ 4998c2ecf20Sopenharmony_cistatic void iucv_setmask_mp(void) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci int cpu; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci get_online_cpus(); 5048c2ecf20Sopenharmony_ci for_each_online_cpu(cpu) 5058c2ecf20Sopenharmony_ci /* Enable all cpus with a declared buffer. */ 5068c2ecf20Sopenharmony_ci if (cpumask_test_cpu(cpu, &iucv_buffer_cpumask) && 5078c2ecf20Sopenharmony_ci !cpumask_test_cpu(cpu, &iucv_irq_cpumask)) 5088c2ecf20Sopenharmony_ci smp_call_function_single(cpu, iucv_allow_cpu, 5098c2ecf20Sopenharmony_ci NULL, 1); 5108c2ecf20Sopenharmony_ci put_online_cpus(); 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci/** 5148c2ecf20Sopenharmony_ci * iucv_setmask_up 5158c2ecf20Sopenharmony_ci * 5168c2ecf20Sopenharmony_ci * Allow iucv interrupts on a single cpu. 5178c2ecf20Sopenharmony_ci */ 5188c2ecf20Sopenharmony_cistatic void iucv_setmask_up(void) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci cpumask_t cpumask; 5218c2ecf20Sopenharmony_ci int cpu; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci /* Disable all cpu but the first in cpu_irq_cpumask. */ 5248c2ecf20Sopenharmony_ci cpumask_copy(&cpumask, &iucv_irq_cpumask); 5258c2ecf20Sopenharmony_ci cpumask_clear_cpu(cpumask_first(&iucv_irq_cpumask), &cpumask); 5268c2ecf20Sopenharmony_ci for_each_cpu(cpu, &cpumask) 5278c2ecf20Sopenharmony_ci smp_call_function_single(cpu, iucv_block_cpu, NULL, 1); 5288c2ecf20Sopenharmony_ci} 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci/** 5318c2ecf20Sopenharmony_ci * iucv_enable 5328c2ecf20Sopenharmony_ci * 5338c2ecf20Sopenharmony_ci * This function makes iucv ready for use. It allocates the pathid 5348c2ecf20Sopenharmony_ci * table, declares an iucv interrupt buffer and enables the iucv 5358c2ecf20Sopenharmony_ci * interrupts. Called when the first user has registered an iucv 5368c2ecf20Sopenharmony_ci * handler. 5378c2ecf20Sopenharmony_ci */ 5388c2ecf20Sopenharmony_cistatic int iucv_enable(void) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci size_t alloc_size; 5418c2ecf20Sopenharmony_ci int cpu, rc; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci get_online_cpus(); 5448c2ecf20Sopenharmony_ci rc = -ENOMEM; 5458c2ecf20Sopenharmony_ci alloc_size = iucv_max_pathid * sizeof(struct iucv_path); 5468c2ecf20Sopenharmony_ci iucv_path_table = kzalloc(alloc_size, GFP_KERNEL); 5478c2ecf20Sopenharmony_ci if (!iucv_path_table) 5488c2ecf20Sopenharmony_ci goto out; 5498c2ecf20Sopenharmony_ci /* Declare per cpu buffers. */ 5508c2ecf20Sopenharmony_ci rc = -EIO; 5518c2ecf20Sopenharmony_ci for_each_online_cpu(cpu) 5528c2ecf20Sopenharmony_ci smp_call_function_single(cpu, iucv_declare_cpu, NULL, 1); 5538c2ecf20Sopenharmony_ci if (cpumask_empty(&iucv_buffer_cpumask)) 5548c2ecf20Sopenharmony_ci /* No cpu could declare an iucv buffer. */ 5558c2ecf20Sopenharmony_ci goto out; 5568c2ecf20Sopenharmony_ci put_online_cpus(); 5578c2ecf20Sopenharmony_ci return 0; 5588c2ecf20Sopenharmony_ciout: 5598c2ecf20Sopenharmony_ci kfree(iucv_path_table); 5608c2ecf20Sopenharmony_ci iucv_path_table = NULL; 5618c2ecf20Sopenharmony_ci put_online_cpus(); 5628c2ecf20Sopenharmony_ci return rc; 5638c2ecf20Sopenharmony_ci} 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci/** 5668c2ecf20Sopenharmony_ci * iucv_disable 5678c2ecf20Sopenharmony_ci * 5688c2ecf20Sopenharmony_ci * This function shuts down iucv. It disables iucv interrupts, retrieves 5698c2ecf20Sopenharmony_ci * the iucv interrupt buffer and frees the pathid table. Called after the 5708c2ecf20Sopenharmony_ci * last user unregister its iucv handler. 5718c2ecf20Sopenharmony_ci */ 5728c2ecf20Sopenharmony_cistatic void iucv_disable(void) 5738c2ecf20Sopenharmony_ci{ 5748c2ecf20Sopenharmony_ci get_online_cpus(); 5758c2ecf20Sopenharmony_ci on_each_cpu(iucv_retrieve_cpu, NULL, 1); 5768c2ecf20Sopenharmony_ci kfree(iucv_path_table); 5778c2ecf20Sopenharmony_ci iucv_path_table = NULL; 5788c2ecf20Sopenharmony_ci put_online_cpus(); 5798c2ecf20Sopenharmony_ci} 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_cistatic int iucv_cpu_dead(unsigned int cpu) 5828c2ecf20Sopenharmony_ci{ 5838c2ecf20Sopenharmony_ci kfree(iucv_param_irq[cpu]); 5848c2ecf20Sopenharmony_ci iucv_param_irq[cpu] = NULL; 5858c2ecf20Sopenharmony_ci kfree(iucv_param[cpu]); 5868c2ecf20Sopenharmony_ci iucv_param[cpu] = NULL; 5878c2ecf20Sopenharmony_ci kfree(iucv_irq_data[cpu]); 5888c2ecf20Sopenharmony_ci iucv_irq_data[cpu] = NULL; 5898c2ecf20Sopenharmony_ci return 0; 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_cistatic int iucv_cpu_prepare(unsigned int cpu) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci /* Note: GFP_DMA used to get memory below 2G */ 5958c2ecf20Sopenharmony_ci iucv_irq_data[cpu] = kmalloc_node(sizeof(struct iucv_irq_data), 5968c2ecf20Sopenharmony_ci GFP_KERNEL|GFP_DMA, cpu_to_node(cpu)); 5978c2ecf20Sopenharmony_ci if (!iucv_irq_data[cpu]) 5988c2ecf20Sopenharmony_ci goto out_free; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci /* Allocate parameter blocks. */ 6018c2ecf20Sopenharmony_ci iucv_param[cpu] = kmalloc_node(sizeof(union iucv_param), 6028c2ecf20Sopenharmony_ci GFP_KERNEL|GFP_DMA, cpu_to_node(cpu)); 6038c2ecf20Sopenharmony_ci if (!iucv_param[cpu]) 6048c2ecf20Sopenharmony_ci goto out_free; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci iucv_param_irq[cpu] = kmalloc_node(sizeof(union iucv_param), 6078c2ecf20Sopenharmony_ci GFP_KERNEL|GFP_DMA, cpu_to_node(cpu)); 6088c2ecf20Sopenharmony_ci if (!iucv_param_irq[cpu]) 6098c2ecf20Sopenharmony_ci goto out_free; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci return 0; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ciout_free: 6148c2ecf20Sopenharmony_ci iucv_cpu_dead(cpu); 6158c2ecf20Sopenharmony_ci return -ENOMEM; 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_cistatic int iucv_cpu_online(unsigned int cpu) 6198c2ecf20Sopenharmony_ci{ 6208c2ecf20Sopenharmony_ci if (!iucv_path_table) 6218c2ecf20Sopenharmony_ci return 0; 6228c2ecf20Sopenharmony_ci iucv_declare_cpu(NULL); 6238c2ecf20Sopenharmony_ci return 0; 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_cistatic int iucv_cpu_down_prep(unsigned int cpu) 6278c2ecf20Sopenharmony_ci{ 6288c2ecf20Sopenharmony_ci cpumask_t cpumask; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (!iucv_path_table) 6318c2ecf20Sopenharmony_ci return 0; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci cpumask_copy(&cpumask, &iucv_buffer_cpumask); 6348c2ecf20Sopenharmony_ci cpumask_clear_cpu(cpu, &cpumask); 6358c2ecf20Sopenharmony_ci if (cpumask_empty(&cpumask)) 6368c2ecf20Sopenharmony_ci /* Can't offline last IUCV enabled cpu. */ 6378c2ecf20Sopenharmony_ci return -EINVAL; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci iucv_retrieve_cpu(NULL); 6408c2ecf20Sopenharmony_ci if (!cpumask_empty(&iucv_irq_cpumask)) 6418c2ecf20Sopenharmony_ci return 0; 6428c2ecf20Sopenharmony_ci smp_call_function_single(cpumask_first(&iucv_buffer_cpumask), 6438c2ecf20Sopenharmony_ci iucv_allow_cpu, NULL, 1); 6448c2ecf20Sopenharmony_ci return 0; 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci/** 6488c2ecf20Sopenharmony_ci * iucv_sever_pathid 6498c2ecf20Sopenharmony_ci * @pathid: path identification number. 6508c2ecf20Sopenharmony_ci * @userdata: 16-bytes of user data. 6518c2ecf20Sopenharmony_ci * 6528c2ecf20Sopenharmony_ci * Sever an iucv path to free up the pathid. Used internally. 6538c2ecf20Sopenharmony_ci */ 6548c2ecf20Sopenharmony_cistatic int iucv_sever_pathid(u16 pathid, u8 *userdata) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci union iucv_param *parm; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci parm = iucv_param_irq[smp_processor_id()]; 6598c2ecf20Sopenharmony_ci memset(parm, 0, sizeof(union iucv_param)); 6608c2ecf20Sopenharmony_ci if (userdata) 6618c2ecf20Sopenharmony_ci memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser)); 6628c2ecf20Sopenharmony_ci parm->ctrl.ippathid = pathid; 6638c2ecf20Sopenharmony_ci return iucv_call_b2f0(IUCV_SEVER, parm); 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci/** 6678c2ecf20Sopenharmony_ci * __iucv_cleanup_queue 6688c2ecf20Sopenharmony_ci * @dummy: unused dummy argument 6698c2ecf20Sopenharmony_ci * 6708c2ecf20Sopenharmony_ci * Nop function called via smp_call_function to force work items from 6718c2ecf20Sopenharmony_ci * pending external iucv interrupts to the work queue. 6728c2ecf20Sopenharmony_ci */ 6738c2ecf20Sopenharmony_cistatic void __iucv_cleanup_queue(void *dummy) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci/** 6788c2ecf20Sopenharmony_ci * iucv_cleanup_queue 6798c2ecf20Sopenharmony_ci * 6808c2ecf20Sopenharmony_ci * Function called after a path has been severed to find all remaining 6818c2ecf20Sopenharmony_ci * work items for the now stale pathid. The caller needs to hold the 6828c2ecf20Sopenharmony_ci * iucv_table_lock. 6838c2ecf20Sopenharmony_ci */ 6848c2ecf20Sopenharmony_cistatic void iucv_cleanup_queue(void) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci struct iucv_irq_list *p, *n; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci /* 6898c2ecf20Sopenharmony_ci * When a path is severed, the pathid can be reused immediately 6908c2ecf20Sopenharmony_ci * on a iucv connect or a connection pending interrupt. Remove 6918c2ecf20Sopenharmony_ci * all entries from the task queue that refer to a stale pathid 6928c2ecf20Sopenharmony_ci * (iucv_path_table[ix] == NULL). Only then do the iucv connect 6938c2ecf20Sopenharmony_ci * or deliver the connection pending interrupt. To get all the 6948c2ecf20Sopenharmony_ci * pending interrupts force them to the work queue by calling 6958c2ecf20Sopenharmony_ci * an empty function on all cpus. 6968c2ecf20Sopenharmony_ci */ 6978c2ecf20Sopenharmony_ci smp_call_function(__iucv_cleanup_queue, NULL, 1); 6988c2ecf20Sopenharmony_ci spin_lock_irq(&iucv_queue_lock); 6998c2ecf20Sopenharmony_ci list_for_each_entry_safe(p, n, &iucv_task_queue, list) { 7008c2ecf20Sopenharmony_ci /* Remove stale work items from the task queue. */ 7018c2ecf20Sopenharmony_ci if (iucv_path_table[p->data.ippathid] == NULL) { 7028c2ecf20Sopenharmony_ci list_del(&p->list); 7038c2ecf20Sopenharmony_ci kfree(p); 7048c2ecf20Sopenharmony_ci } 7058c2ecf20Sopenharmony_ci } 7068c2ecf20Sopenharmony_ci spin_unlock_irq(&iucv_queue_lock); 7078c2ecf20Sopenharmony_ci} 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci/** 7108c2ecf20Sopenharmony_ci * iucv_register: 7118c2ecf20Sopenharmony_ci * @handler: address of iucv handler structure 7128c2ecf20Sopenharmony_ci * @smp: != 0 indicates that the handler can deal with out of order messages 7138c2ecf20Sopenharmony_ci * 7148c2ecf20Sopenharmony_ci * Registers a driver with IUCV. 7158c2ecf20Sopenharmony_ci * 7168c2ecf20Sopenharmony_ci * Returns 0 on success, -ENOMEM if the memory allocation for the pathid 7178c2ecf20Sopenharmony_ci * table failed, or -EIO if IUCV_DECLARE_BUFFER failed on all cpus. 7188c2ecf20Sopenharmony_ci */ 7198c2ecf20Sopenharmony_ciint iucv_register(struct iucv_handler *handler, int smp) 7208c2ecf20Sopenharmony_ci{ 7218c2ecf20Sopenharmony_ci int rc; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci if (!iucv_available) 7248c2ecf20Sopenharmony_ci return -ENOSYS; 7258c2ecf20Sopenharmony_ci mutex_lock(&iucv_register_mutex); 7268c2ecf20Sopenharmony_ci if (!smp) 7278c2ecf20Sopenharmony_ci iucv_nonsmp_handler++; 7288c2ecf20Sopenharmony_ci if (list_empty(&iucv_handler_list)) { 7298c2ecf20Sopenharmony_ci rc = iucv_enable(); 7308c2ecf20Sopenharmony_ci if (rc) 7318c2ecf20Sopenharmony_ci goto out_mutex; 7328c2ecf20Sopenharmony_ci } else if (!smp && iucv_nonsmp_handler == 1) 7338c2ecf20Sopenharmony_ci iucv_setmask_up(); 7348c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&handler->paths); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci spin_lock_bh(&iucv_table_lock); 7378c2ecf20Sopenharmony_ci list_add_tail(&handler->list, &iucv_handler_list); 7388c2ecf20Sopenharmony_ci spin_unlock_bh(&iucv_table_lock); 7398c2ecf20Sopenharmony_ci rc = 0; 7408c2ecf20Sopenharmony_ciout_mutex: 7418c2ecf20Sopenharmony_ci mutex_unlock(&iucv_register_mutex); 7428c2ecf20Sopenharmony_ci return rc; 7438c2ecf20Sopenharmony_ci} 7448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_register); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci/** 7478c2ecf20Sopenharmony_ci * iucv_unregister 7488c2ecf20Sopenharmony_ci * @handler: address of iucv handler structure 7498c2ecf20Sopenharmony_ci * @smp: != 0 indicates that the handler can deal with out of order messages 7508c2ecf20Sopenharmony_ci * 7518c2ecf20Sopenharmony_ci * Unregister driver from IUCV. 7528c2ecf20Sopenharmony_ci */ 7538c2ecf20Sopenharmony_civoid iucv_unregister(struct iucv_handler *handler, int smp) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci struct iucv_path *p, *n; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci mutex_lock(&iucv_register_mutex); 7588c2ecf20Sopenharmony_ci spin_lock_bh(&iucv_table_lock); 7598c2ecf20Sopenharmony_ci /* Remove handler from the iucv_handler_list. */ 7608c2ecf20Sopenharmony_ci list_del_init(&handler->list); 7618c2ecf20Sopenharmony_ci /* Sever all pathids still referring to the handler. */ 7628c2ecf20Sopenharmony_ci list_for_each_entry_safe(p, n, &handler->paths, list) { 7638c2ecf20Sopenharmony_ci iucv_sever_pathid(p->pathid, NULL); 7648c2ecf20Sopenharmony_ci iucv_path_table[p->pathid] = NULL; 7658c2ecf20Sopenharmony_ci list_del(&p->list); 7668c2ecf20Sopenharmony_ci iucv_path_free(p); 7678c2ecf20Sopenharmony_ci } 7688c2ecf20Sopenharmony_ci spin_unlock_bh(&iucv_table_lock); 7698c2ecf20Sopenharmony_ci if (!smp) 7708c2ecf20Sopenharmony_ci iucv_nonsmp_handler--; 7718c2ecf20Sopenharmony_ci if (list_empty(&iucv_handler_list)) 7728c2ecf20Sopenharmony_ci iucv_disable(); 7738c2ecf20Sopenharmony_ci else if (!smp && iucv_nonsmp_handler == 0) 7748c2ecf20Sopenharmony_ci iucv_setmask_mp(); 7758c2ecf20Sopenharmony_ci mutex_unlock(&iucv_register_mutex); 7768c2ecf20Sopenharmony_ci} 7778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_unregister); 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_cistatic int iucv_reboot_event(struct notifier_block *this, 7808c2ecf20Sopenharmony_ci unsigned long event, void *ptr) 7818c2ecf20Sopenharmony_ci{ 7828c2ecf20Sopenharmony_ci int i; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci if (cpumask_empty(&iucv_irq_cpumask)) 7858c2ecf20Sopenharmony_ci return NOTIFY_DONE; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci get_online_cpus(); 7888c2ecf20Sopenharmony_ci on_each_cpu_mask(&iucv_irq_cpumask, iucv_block_cpu, NULL, 1); 7898c2ecf20Sopenharmony_ci preempt_disable(); 7908c2ecf20Sopenharmony_ci for (i = 0; i < iucv_max_pathid; i++) { 7918c2ecf20Sopenharmony_ci if (iucv_path_table[i]) 7928c2ecf20Sopenharmony_ci iucv_sever_pathid(i, NULL); 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci preempt_enable(); 7958c2ecf20Sopenharmony_ci put_online_cpus(); 7968c2ecf20Sopenharmony_ci iucv_disable(); 7978c2ecf20Sopenharmony_ci return NOTIFY_DONE; 7988c2ecf20Sopenharmony_ci} 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_cistatic struct notifier_block iucv_reboot_notifier = { 8018c2ecf20Sopenharmony_ci .notifier_call = iucv_reboot_event, 8028c2ecf20Sopenharmony_ci}; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci/** 8058c2ecf20Sopenharmony_ci * iucv_path_accept 8068c2ecf20Sopenharmony_ci * @path: address of iucv path structure 8078c2ecf20Sopenharmony_ci * @handler: address of iucv handler structure 8088c2ecf20Sopenharmony_ci * @userdata: 16 bytes of data reflected to the communication partner 8098c2ecf20Sopenharmony_ci * @private: private data passed to interrupt handlers for this path 8108c2ecf20Sopenharmony_ci * 8118c2ecf20Sopenharmony_ci * This function is issued after the user received a connection pending 8128c2ecf20Sopenharmony_ci * external interrupt and now wishes to complete the IUCV communication path. 8138c2ecf20Sopenharmony_ci * 8148c2ecf20Sopenharmony_ci * Returns the result of the CP IUCV call. 8158c2ecf20Sopenharmony_ci */ 8168c2ecf20Sopenharmony_ciint iucv_path_accept(struct iucv_path *path, struct iucv_handler *handler, 8178c2ecf20Sopenharmony_ci u8 *userdata, void *private) 8188c2ecf20Sopenharmony_ci{ 8198c2ecf20Sopenharmony_ci union iucv_param *parm; 8208c2ecf20Sopenharmony_ci int rc; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci local_bh_disable(); 8238c2ecf20Sopenharmony_ci if (cpumask_empty(&iucv_buffer_cpumask)) { 8248c2ecf20Sopenharmony_ci rc = -EIO; 8258c2ecf20Sopenharmony_ci goto out; 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci /* Prepare parameter block. */ 8288c2ecf20Sopenharmony_ci parm = iucv_param[smp_processor_id()]; 8298c2ecf20Sopenharmony_ci memset(parm, 0, sizeof(union iucv_param)); 8308c2ecf20Sopenharmony_ci parm->ctrl.ippathid = path->pathid; 8318c2ecf20Sopenharmony_ci parm->ctrl.ipmsglim = path->msglim; 8328c2ecf20Sopenharmony_ci if (userdata) 8338c2ecf20Sopenharmony_ci memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser)); 8348c2ecf20Sopenharmony_ci parm->ctrl.ipflags1 = path->flags; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci rc = iucv_call_b2f0(IUCV_ACCEPT, parm); 8378c2ecf20Sopenharmony_ci if (!rc) { 8388c2ecf20Sopenharmony_ci path->private = private; 8398c2ecf20Sopenharmony_ci path->msglim = parm->ctrl.ipmsglim; 8408c2ecf20Sopenharmony_ci path->flags = parm->ctrl.ipflags1; 8418c2ecf20Sopenharmony_ci } 8428c2ecf20Sopenharmony_ciout: 8438c2ecf20Sopenharmony_ci local_bh_enable(); 8448c2ecf20Sopenharmony_ci return rc; 8458c2ecf20Sopenharmony_ci} 8468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_path_accept); 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci/** 8498c2ecf20Sopenharmony_ci * iucv_path_connect 8508c2ecf20Sopenharmony_ci * @path: address of iucv path structure 8518c2ecf20Sopenharmony_ci * @handler: address of iucv handler structure 8528c2ecf20Sopenharmony_ci * @userid: 8-byte user identification 8538c2ecf20Sopenharmony_ci * @system: 8-byte target system identification 8548c2ecf20Sopenharmony_ci * @userdata: 16 bytes of data reflected to the communication partner 8558c2ecf20Sopenharmony_ci * @private: private data passed to interrupt handlers for this path 8568c2ecf20Sopenharmony_ci * 8578c2ecf20Sopenharmony_ci * This function establishes an IUCV path. Although the connect may complete 8588c2ecf20Sopenharmony_ci * successfully, you are not able to use the path until you receive an IUCV 8598c2ecf20Sopenharmony_ci * Connection Complete external interrupt. 8608c2ecf20Sopenharmony_ci * 8618c2ecf20Sopenharmony_ci * Returns the result of the CP IUCV call. 8628c2ecf20Sopenharmony_ci */ 8638c2ecf20Sopenharmony_ciint iucv_path_connect(struct iucv_path *path, struct iucv_handler *handler, 8648c2ecf20Sopenharmony_ci u8 *userid, u8 *system, u8 *userdata, 8658c2ecf20Sopenharmony_ci void *private) 8668c2ecf20Sopenharmony_ci{ 8678c2ecf20Sopenharmony_ci union iucv_param *parm; 8688c2ecf20Sopenharmony_ci int rc; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci spin_lock_bh(&iucv_table_lock); 8718c2ecf20Sopenharmony_ci iucv_cleanup_queue(); 8728c2ecf20Sopenharmony_ci if (cpumask_empty(&iucv_buffer_cpumask)) { 8738c2ecf20Sopenharmony_ci rc = -EIO; 8748c2ecf20Sopenharmony_ci goto out; 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci parm = iucv_param[smp_processor_id()]; 8778c2ecf20Sopenharmony_ci memset(parm, 0, sizeof(union iucv_param)); 8788c2ecf20Sopenharmony_ci parm->ctrl.ipmsglim = path->msglim; 8798c2ecf20Sopenharmony_ci parm->ctrl.ipflags1 = path->flags; 8808c2ecf20Sopenharmony_ci if (userid) { 8818c2ecf20Sopenharmony_ci memcpy(parm->ctrl.ipvmid, userid, sizeof(parm->ctrl.ipvmid)); 8828c2ecf20Sopenharmony_ci ASCEBC(parm->ctrl.ipvmid, sizeof(parm->ctrl.ipvmid)); 8838c2ecf20Sopenharmony_ci EBC_TOUPPER(parm->ctrl.ipvmid, sizeof(parm->ctrl.ipvmid)); 8848c2ecf20Sopenharmony_ci } 8858c2ecf20Sopenharmony_ci if (system) { 8868c2ecf20Sopenharmony_ci memcpy(parm->ctrl.iptarget, system, 8878c2ecf20Sopenharmony_ci sizeof(parm->ctrl.iptarget)); 8888c2ecf20Sopenharmony_ci ASCEBC(parm->ctrl.iptarget, sizeof(parm->ctrl.iptarget)); 8898c2ecf20Sopenharmony_ci EBC_TOUPPER(parm->ctrl.iptarget, sizeof(parm->ctrl.iptarget)); 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci if (userdata) 8928c2ecf20Sopenharmony_ci memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser)); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci rc = iucv_call_b2f0(IUCV_CONNECT, parm); 8958c2ecf20Sopenharmony_ci if (!rc) { 8968c2ecf20Sopenharmony_ci if (parm->ctrl.ippathid < iucv_max_pathid) { 8978c2ecf20Sopenharmony_ci path->pathid = parm->ctrl.ippathid; 8988c2ecf20Sopenharmony_ci path->msglim = parm->ctrl.ipmsglim; 8998c2ecf20Sopenharmony_ci path->flags = parm->ctrl.ipflags1; 9008c2ecf20Sopenharmony_ci path->handler = handler; 9018c2ecf20Sopenharmony_ci path->private = private; 9028c2ecf20Sopenharmony_ci list_add_tail(&path->list, &handler->paths); 9038c2ecf20Sopenharmony_ci iucv_path_table[path->pathid] = path; 9048c2ecf20Sopenharmony_ci } else { 9058c2ecf20Sopenharmony_ci iucv_sever_pathid(parm->ctrl.ippathid, 9068c2ecf20Sopenharmony_ci iucv_error_pathid); 9078c2ecf20Sopenharmony_ci rc = -EIO; 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci } 9108c2ecf20Sopenharmony_ciout: 9118c2ecf20Sopenharmony_ci spin_unlock_bh(&iucv_table_lock); 9128c2ecf20Sopenharmony_ci return rc; 9138c2ecf20Sopenharmony_ci} 9148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_path_connect); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci/** 9178c2ecf20Sopenharmony_ci * iucv_path_quiesce: 9188c2ecf20Sopenharmony_ci * @path: address of iucv path structure 9198c2ecf20Sopenharmony_ci * @userdata: 16 bytes of data reflected to the communication partner 9208c2ecf20Sopenharmony_ci * 9218c2ecf20Sopenharmony_ci * This function temporarily suspends incoming messages on an IUCV path. 9228c2ecf20Sopenharmony_ci * You can later reactivate the path by invoking the iucv_resume function. 9238c2ecf20Sopenharmony_ci * 9248c2ecf20Sopenharmony_ci * Returns the result from the CP IUCV call. 9258c2ecf20Sopenharmony_ci */ 9268c2ecf20Sopenharmony_ciint iucv_path_quiesce(struct iucv_path *path, u8 *userdata) 9278c2ecf20Sopenharmony_ci{ 9288c2ecf20Sopenharmony_ci union iucv_param *parm; 9298c2ecf20Sopenharmony_ci int rc; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci local_bh_disable(); 9328c2ecf20Sopenharmony_ci if (cpumask_empty(&iucv_buffer_cpumask)) { 9338c2ecf20Sopenharmony_ci rc = -EIO; 9348c2ecf20Sopenharmony_ci goto out; 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci parm = iucv_param[smp_processor_id()]; 9378c2ecf20Sopenharmony_ci memset(parm, 0, sizeof(union iucv_param)); 9388c2ecf20Sopenharmony_ci if (userdata) 9398c2ecf20Sopenharmony_ci memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser)); 9408c2ecf20Sopenharmony_ci parm->ctrl.ippathid = path->pathid; 9418c2ecf20Sopenharmony_ci rc = iucv_call_b2f0(IUCV_QUIESCE, parm); 9428c2ecf20Sopenharmony_ciout: 9438c2ecf20Sopenharmony_ci local_bh_enable(); 9448c2ecf20Sopenharmony_ci return rc; 9458c2ecf20Sopenharmony_ci} 9468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_path_quiesce); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci/** 9498c2ecf20Sopenharmony_ci * iucv_path_resume: 9508c2ecf20Sopenharmony_ci * @path: address of iucv path structure 9518c2ecf20Sopenharmony_ci * @userdata: 16 bytes of data reflected to the communication partner 9528c2ecf20Sopenharmony_ci * 9538c2ecf20Sopenharmony_ci * This function resumes incoming messages on an IUCV path that has 9548c2ecf20Sopenharmony_ci * been stopped with iucv_path_quiesce. 9558c2ecf20Sopenharmony_ci * 9568c2ecf20Sopenharmony_ci * Returns the result from the CP IUCV call. 9578c2ecf20Sopenharmony_ci */ 9588c2ecf20Sopenharmony_ciint iucv_path_resume(struct iucv_path *path, u8 *userdata) 9598c2ecf20Sopenharmony_ci{ 9608c2ecf20Sopenharmony_ci union iucv_param *parm; 9618c2ecf20Sopenharmony_ci int rc; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci local_bh_disable(); 9648c2ecf20Sopenharmony_ci if (cpumask_empty(&iucv_buffer_cpumask)) { 9658c2ecf20Sopenharmony_ci rc = -EIO; 9668c2ecf20Sopenharmony_ci goto out; 9678c2ecf20Sopenharmony_ci } 9688c2ecf20Sopenharmony_ci parm = iucv_param[smp_processor_id()]; 9698c2ecf20Sopenharmony_ci memset(parm, 0, sizeof(union iucv_param)); 9708c2ecf20Sopenharmony_ci if (userdata) 9718c2ecf20Sopenharmony_ci memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser)); 9728c2ecf20Sopenharmony_ci parm->ctrl.ippathid = path->pathid; 9738c2ecf20Sopenharmony_ci rc = iucv_call_b2f0(IUCV_RESUME, parm); 9748c2ecf20Sopenharmony_ciout: 9758c2ecf20Sopenharmony_ci local_bh_enable(); 9768c2ecf20Sopenharmony_ci return rc; 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci/** 9808c2ecf20Sopenharmony_ci * iucv_path_sever 9818c2ecf20Sopenharmony_ci * @path: address of iucv path structure 9828c2ecf20Sopenharmony_ci * @userdata: 16 bytes of data reflected to the communication partner 9838c2ecf20Sopenharmony_ci * 9848c2ecf20Sopenharmony_ci * This function terminates an IUCV path. 9858c2ecf20Sopenharmony_ci * 9868c2ecf20Sopenharmony_ci * Returns the result from the CP IUCV call. 9878c2ecf20Sopenharmony_ci */ 9888c2ecf20Sopenharmony_ciint iucv_path_sever(struct iucv_path *path, u8 *userdata) 9898c2ecf20Sopenharmony_ci{ 9908c2ecf20Sopenharmony_ci int rc; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci preempt_disable(); 9938c2ecf20Sopenharmony_ci if (cpumask_empty(&iucv_buffer_cpumask)) { 9948c2ecf20Sopenharmony_ci rc = -EIO; 9958c2ecf20Sopenharmony_ci goto out; 9968c2ecf20Sopenharmony_ci } 9978c2ecf20Sopenharmony_ci if (iucv_active_cpu != smp_processor_id()) 9988c2ecf20Sopenharmony_ci spin_lock_bh(&iucv_table_lock); 9998c2ecf20Sopenharmony_ci rc = iucv_sever_pathid(path->pathid, userdata); 10008c2ecf20Sopenharmony_ci iucv_path_table[path->pathid] = NULL; 10018c2ecf20Sopenharmony_ci list_del_init(&path->list); 10028c2ecf20Sopenharmony_ci if (iucv_active_cpu != smp_processor_id()) 10038c2ecf20Sopenharmony_ci spin_unlock_bh(&iucv_table_lock); 10048c2ecf20Sopenharmony_ciout: 10058c2ecf20Sopenharmony_ci preempt_enable(); 10068c2ecf20Sopenharmony_ci return rc; 10078c2ecf20Sopenharmony_ci} 10088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_path_sever); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci/** 10118c2ecf20Sopenharmony_ci * iucv_message_purge 10128c2ecf20Sopenharmony_ci * @path: address of iucv path structure 10138c2ecf20Sopenharmony_ci * @msg: address of iucv msg structure 10148c2ecf20Sopenharmony_ci * @srccls: source class of message 10158c2ecf20Sopenharmony_ci * 10168c2ecf20Sopenharmony_ci * Cancels a message you have sent. 10178c2ecf20Sopenharmony_ci * 10188c2ecf20Sopenharmony_ci * Returns the result from the CP IUCV call. 10198c2ecf20Sopenharmony_ci */ 10208c2ecf20Sopenharmony_ciint iucv_message_purge(struct iucv_path *path, struct iucv_message *msg, 10218c2ecf20Sopenharmony_ci u32 srccls) 10228c2ecf20Sopenharmony_ci{ 10238c2ecf20Sopenharmony_ci union iucv_param *parm; 10248c2ecf20Sopenharmony_ci int rc; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci local_bh_disable(); 10278c2ecf20Sopenharmony_ci if (cpumask_empty(&iucv_buffer_cpumask)) { 10288c2ecf20Sopenharmony_ci rc = -EIO; 10298c2ecf20Sopenharmony_ci goto out; 10308c2ecf20Sopenharmony_ci } 10318c2ecf20Sopenharmony_ci parm = iucv_param[smp_processor_id()]; 10328c2ecf20Sopenharmony_ci memset(parm, 0, sizeof(union iucv_param)); 10338c2ecf20Sopenharmony_ci parm->purge.ippathid = path->pathid; 10348c2ecf20Sopenharmony_ci parm->purge.ipmsgid = msg->id; 10358c2ecf20Sopenharmony_ci parm->purge.ipsrccls = srccls; 10368c2ecf20Sopenharmony_ci parm->purge.ipflags1 = IUCV_IPSRCCLS | IUCV_IPFGMID | IUCV_IPFGPID; 10378c2ecf20Sopenharmony_ci rc = iucv_call_b2f0(IUCV_PURGE, parm); 10388c2ecf20Sopenharmony_ci if (!rc) { 10398c2ecf20Sopenharmony_ci msg->audit = (*(u32 *) &parm->purge.ipaudit) >> 8; 10408c2ecf20Sopenharmony_ci msg->tag = parm->purge.ipmsgtag; 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ciout: 10438c2ecf20Sopenharmony_ci local_bh_enable(); 10448c2ecf20Sopenharmony_ci return rc; 10458c2ecf20Sopenharmony_ci} 10468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_message_purge); 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci/** 10498c2ecf20Sopenharmony_ci * iucv_message_receive_iprmdata 10508c2ecf20Sopenharmony_ci * @path: address of iucv path structure 10518c2ecf20Sopenharmony_ci * @msg: address of iucv msg structure 10528c2ecf20Sopenharmony_ci * @flags: how the message is received (IUCV_IPBUFLST) 10538c2ecf20Sopenharmony_ci * @buffer: address of data buffer or address of struct iucv_array 10548c2ecf20Sopenharmony_ci * @size: length of data buffer 10558c2ecf20Sopenharmony_ci * @residual: 10568c2ecf20Sopenharmony_ci * 10578c2ecf20Sopenharmony_ci * Internal function used by iucv_message_receive and __iucv_message_receive 10588c2ecf20Sopenharmony_ci * to receive RMDATA data stored in struct iucv_message. 10598c2ecf20Sopenharmony_ci */ 10608c2ecf20Sopenharmony_cistatic int iucv_message_receive_iprmdata(struct iucv_path *path, 10618c2ecf20Sopenharmony_ci struct iucv_message *msg, 10628c2ecf20Sopenharmony_ci u8 flags, void *buffer, 10638c2ecf20Sopenharmony_ci size_t size, size_t *residual) 10648c2ecf20Sopenharmony_ci{ 10658c2ecf20Sopenharmony_ci struct iucv_array *array; 10668c2ecf20Sopenharmony_ci u8 *rmmsg; 10678c2ecf20Sopenharmony_ci size_t copy; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci /* 10708c2ecf20Sopenharmony_ci * Message is 8 bytes long and has been stored to the 10718c2ecf20Sopenharmony_ci * message descriptor itself. 10728c2ecf20Sopenharmony_ci */ 10738c2ecf20Sopenharmony_ci if (residual) 10748c2ecf20Sopenharmony_ci *residual = abs(size - 8); 10758c2ecf20Sopenharmony_ci rmmsg = msg->rmmsg; 10768c2ecf20Sopenharmony_ci if (flags & IUCV_IPBUFLST) { 10778c2ecf20Sopenharmony_ci /* Copy to struct iucv_array. */ 10788c2ecf20Sopenharmony_ci size = (size < 8) ? size : 8; 10798c2ecf20Sopenharmony_ci for (array = buffer; size > 0; array++) { 10808c2ecf20Sopenharmony_ci copy = min_t(size_t, size, array->length); 10818c2ecf20Sopenharmony_ci memcpy((u8 *)(addr_t) array->address, 10828c2ecf20Sopenharmony_ci rmmsg, copy); 10838c2ecf20Sopenharmony_ci rmmsg += copy; 10848c2ecf20Sopenharmony_ci size -= copy; 10858c2ecf20Sopenharmony_ci } 10868c2ecf20Sopenharmony_ci } else { 10878c2ecf20Sopenharmony_ci /* Copy to direct buffer. */ 10888c2ecf20Sopenharmony_ci memcpy(buffer, rmmsg, min_t(size_t, size, 8)); 10898c2ecf20Sopenharmony_ci } 10908c2ecf20Sopenharmony_ci return 0; 10918c2ecf20Sopenharmony_ci} 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci/** 10948c2ecf20Sopenharmony_ci * __iucv_message_receive 10958c2ecf20Sopenharmony_ci * @path: address of iucv path structure 10968c2ecf20Sopenharmony_ci * @msg: address of iucv msg structure 10978c2ecf20Sopenharmony_ci * @flags: how the message is received (IUCV_IPBUFLST) 10988c2ecf20Sopenharmony_ci * @buffer: address of data buffer or address of struct iucv_array 10998c2ecf20Sopenharmony_ci * @size: length of data buffer 11008c2ecf20Sopenharmony_ci * @residual: 11018c2ecf20Sopenharmony_ci * 11028c2ecf20Sopenharmony_ci * This function receives messages that are being sent to you over 11038c2ecf20Sopenharmony_ci * established paths. This function will deal with RMDATA messages 11048c2ecf20Sopenharmony_ci * embedded in struct iucv_message as well. 11058c2ecf20Sopenharmony_ci * 11068c2ecf20Sopenharmony_ci * Locking: no locking 11078c2ecf20Sopenharmony_ci * 11088c2ecf20Sopenharmony_ci * Returns the result from the CP IUCV call. 11098c2ecf20Sopenharmony_ci */ 11108c2ecf20Sopenharmony_ciint __iucv_message_receive(struct iucv_path *path, struct iucv_message *msg, 11118c2ecf20Sopenharmony_ci u8 flags, void *buffer, size_t size, size_t *residual) 11128c2ecf20Sopenharmony_ci{ 11138c2ecf20Sopenharmony_ci union iucv_param *parm; 11148c2ecf20Sopenharmony_ci int rc; 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci if (msg->flags & IUCV_IPRMDATA) 11178c2ecf20Sopenharmony_ci return iucv_message_receive_iprmdata(path, msg, flags, 11188c2ecf20Sopenharmony_ci buffer, size, residual); 11198c2ecf20Sopenharmony_ci if (cpumask_empty(&iucv_buffer_cpumask)) 11208c2ecf20Sopenharmony_ci return -EIO; 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci parm = iucv_param[smp_processor_id()]; 11238c2ecf20Sopenharmony_ci memset(parm, 0, sizeof(union iucv_param)); 11248c2ecf20Sopenharmony_ci parm->db.ipbfadr1 = (u32)(addr_t) buffer; 11258c2ecf20Sopenharmony_ci parm->db.ipbfln1f = (u32) size; 11268c2ecf20Sopenharmony_ci parm->db.ipmsgid = msg->id; 11278c2ecf20Sopenharmony_ci parm->db.ippathid = path->pathid; 11288c2ecf20Sopenharmony_ci parm->db.iptrgcls = msg->class; 11298c2ecf20Sopenharmony_ci parm->db.ipflags1 = (flags | IUCV_IPFGPID | 11308c2ecf20Sopenharmony_ci IUCV_IPFGMID | IUCV_IPTRGCLS); 11318c2ecf20Sopenharmony_ci rc = iucv_call_b2f0(IUCV_RECEIVE, parm); 11328c2ecf20Sopenharmony_ci if (!rc || rc == 5) { 11338c2ecf20Sopenharmony_ci msg->flags = parm->db.ipflags1; 11348c2ecf20Sopenharmony_ci if (residual) 11358c2ecf20Sopenharmony_ci *residual = parm->db.ipbfln1f; 11368c2ecf20Sopenharmony_ci } 11378c2ecf20Sopenharmony_ci return rc; 11388c2ecf20Sopenharmony_ci} 11398c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__iucv_message_receive); 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci/** 11428c2ecf20Sopenharmony_ci * iucv_message_receive 11438c2ecf20Sopenharmony_ci * @path: address of iucv path structure 11448c2ecf20Sopenharmony_ci * @msg: address of iucv msg structure 11458c2ecf20Sopenharmony_ci * @flags: how the message is received (IUCV_IPBUFLST) 11468c2ecf20Sopenharmony_ci * @buffer: address of data buffer or address of struct iucv_array 11478c2ecf20Sopenharmony_ci * @size: length of data buffer 11488c2ecf20Sopenharmony_ci * @residual: 11498c2ecf20Sopenharmony_ci * 11508c2ecf20Sopenharmony_ci * This function receives messages that are being sent to you over 11518c2ecf20Sopenharmony_ci * established paths. This function will deal with RMDATA messages 11528c2ecf20Sopenharmony_ci * embedded in struct iucv_message as well. 11538c2ecf20Sopenharmony_ci * 11548c2ecf20Sopenharmony_ci * Locking: local_bh_enable/local_bh_disable 11558c2ecf20Sopenharmony_ci * 11568c2ecf20Sopenharmony_ci * Returns the result from the CP IUCV call. 11578c2ecf20Sopenharmony_ci */ 11588c2ecf20Sopenharmony_ciint iucv_message_receive(struct iucv_path *path, struct iucv_message *msg, 11598c2ecf20Sopenharmony_ci u8 flags, void *buffer, size_t size, size_t *residual) 11608c2ecf20Sopenharmony_ci{ 11618c2ecf20Sopenharmony_ci int rc; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci if (msg->flags & IUCV_IPRMDATA) 11648c2ecf20Sopenharmony_ci return iucv_message_receive_iprmdata(path, msg, flags, 11658c2ecf20Sopenharmony_ci buffer, size, residual); 11668c2ecf20Sopenharmony_ci local_bh_disable(); 11678c2ecf20Sopenharmony_ci rc = __iucv_message_receive(path, msg, flags, buffer, size, residual); 11688c2ecf20Sopenharmony_ci local_bh_enable(); 11698c2ecf20Sopenharmony_ci return rc; 11708c2ecf20Sopenharmony_ci} 11718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_message_receive); 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci/** 11748c2ecf20Sopenharmony_ci * iucv_message_reject 11758c2ecf20Sopenharmony_ci * @path: address of iucv path structure 11768c2ecf20Sopenharmony_ci * @msg: address of iucv msg structure 11778c2ecf20Sopenharmony_ci * 11788c2ecf20Sopenharmony_ci * The reject function refuses a specified message. Between the time you 11798c2ecf20Sopenharmony_ci * are notified of a message and the time that you complete the message, 11808c2ecf20Sopenharmony_ci * the message may be rejected. 11818c2ecf20Sopenharmony_ci * 11828c2ecf20Sopenharmony_ci * Returns the result from the CP IUCV call. 11838c2ecf20Sopenharmony_ci */ 11848c2ecf20Sopenharmony_ciint iucv_message_reject(struct iucv_path *path, struct iucv_message *msg) 11858c2ecf20Sopenharmony_ci{ 11868c2ecf20Sopenharmony_ci union iucv_param *parm; 11878c2ecf20Sopenharmony_ci int rc; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci local_bh_disable(); 11908c2ecf20Sopenharmony_ci if (cpumask_empty(&iucv_buffer_cpumask)) { 11918c2ecf20Sopenharmony_ci rc = -EIO; 11928c2ecf20Sopenharmony_ci goto out; 11938c2ecf20Sopenharmony_ci } 11948c2ecf20Sopenharmony_ci parm = iucv_param[smp_processor_id()]; 11958c2ecf20Sopenharmony_ci memset(parm, 0, sizeof(union iucv_param)); 11968c2ecf20Sopenharmony_ci parm->db.ippathid = path->pathid; 11978c2ecf20Sopenharmony_ci parm->db.ipmsgid = msg->id; 11988c2ecf20Sopenharmony_ci parm->db.iptrgcls = msg->class; 11998c2ecf20Sopenharmony_ci parm->db.ipflags1 = (IUCV_IPTRGCLS | IUCV_IPFGMID | IUCV_IPFGPID); 12008c2ecf20Sopenharmony_ci rc = iucv_call_b2f0(IUCV_REJECT, parm); 12018c2ecf20Sopenharmony_ciout: 12028c2ecf20Sopenharmony_ci local_bh_enable(); 12038c2ecf20Sopenharmony_ci return rc; 12048c2ecf20Sopenharmony_ci} 12058c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_message_reject); 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci/** 12088c2ecf20Sopenharmony_ci * iucv_message_reply 12098c2ecf20Sopenharmony_ci * @path: address of iucv path structure 12108c2ecf20Sopenharmony_ci * @msg: address of iucv msg structure 12118c2ecf20Sopenharmony_ci * @flags: how the reply is sent (IUCV_IPRMDATA, IUCV_IPPRTY, IUCV_IPBUFLST) 12128c2ecf20Sopenharmony_ci * @reply: address of reply data buffer or address of struct iucv_array 12138c2ecf20Sopenharmony_ci * @size: length of reply data buffer 12148c2ecf20Sopenharmony_ci * 12158c2ecf20Sopenharmony_ci * This function responds to the two-way messages that you receive. You 12168c2ecf20Sopenharmony_ci * must identify completely the message to which you wish to reply. ie, 12178c2ecf20Sopenharmony_ci * pathid, msgid, and trgcls. Prmmsg signifies the data is moved into 12188c2ecf20Sopenharmony_ci * the parameter list. 12198c2ecf20Sopenharmony_ci * 12208c2ecf20Sopenharmony_ci * Returns the result from the CP IUCV call. 12218c2ecf20Sopenharmony_ci */ 12228c2ecf20Sopenharmony_ciint iucv_message_reply(struct iucv_path *path, struct iucv_message *msg, 12238c2ecf20Sopenharmony_ci u8 flags, void *reply, size_t size) 12248c2ecf20Sopenharmony_ci{ 12258c2ecf20Sopenharmony_ci union iucv_param *parm; 12268c2ecf20Sopenharmony_ci int rc; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci local_bh_disable(); 12298c2ecf20Sopenharmony_ci if (cpumask_empty(&iucv_buffer_cpumask)) { 12308c2ecf20Sopenharmony_ci rc = -EIO; 12318c2ecf20Sopenharmony_ci goto out; 12328c2ecf20Sopenharmony_ci } 12338c2ecf20Sopenharmony_ci parm = iucv_param[smp_processor_id()]; 12348c2ecf20Sopenharmony_ci memset(parm, 0, sizeof(union iucv_param)); 12358c2ecf20Sopenharmony_ci if (flags & IUCV_IPRMDATA) { 12368c2ecf20Sopenharmony_ci parm->dpl.ippathid = path->pathid; 12378c2ecf20Sopenharmony_ci parm->dpl.ipflags1 = flags; 12388c2ecf20Sopenharmony_ci parm->dpl.ipmsgid = msg->id; 12398c2ecf20Sopenharmony_ci parm->dpl.iptrgcls = msg->class; 12408c2ecf20Sopenharmony_ci memcpy(parm->dpl.iprmmsg, reply, min_t(size_t, size, 8)); 12418c2ecf20Sopenharmony_ci } else { 12428c2ecf20Sopenharmony_ci parm->db.ipbfadr1 = (u32)(addr_t) reply; 12438c2ecf20Sopenharmony_ci parm->db.ipbfln1f = (u32) size; 12448c2ecf20Sopenharmony_ci parm->db.ippathid = path->pathid; 12458c2ecf20Sopenharmony_ci parm->db.ipflags1 = flags; 12468c2ecf20Sopenharmony_ci parm->db.ipmsgid = msg->id; 12478c2ecf20Sopenharmony_ci parm->db.iptrgcls = msg->class; 12488c2ecf20Sopenharmony_ci } 12498c2ecf20Sopenharmony_ci rc = iucv_call_b2f0(IUCV_REPLY, parm); 12508c2ecf20Sopenharmony_ciout: 12518c2ecf20Sopenharmony_ci local_bh_enable(); 12528c2ecf20Sopenharmony_ci return rc; 12538c2ecf20Sopenharmony_ci} 12548c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_message_reply); 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci/** 12578c2ecf20Sopenharmony_ci * __iucv_message_send 12588c2ecf20Sopenharmony_ci * @path: address of iucv path structure 12598c2ecf20Sopenharmony_ci * @msg: address of iucv msg structure 12608c2ecf20Sopenharmony_ci * @flags: how the message is sent (IUCV_IPRMDATA, IUCV_IPPRTY, IUCV_IPBUFLST) 12618c2ecf20Sopenharmony_ci * @srccls: source class of message 12628c2ecf20Sopenharmony_ci * @buffer: address of send buffer or address of struct iucv_array 12638c2ecf20Sopenharmony_ci * @size: length of send buffer 12648c2ecf20Sopenharmony_ci * 12658c2ecf20Sopenharmony_ci * This function transmits data to another application. Data to be 12668c2ecf20Sopenharmony_ci * transmitted is in a buffer and this is a one-way message and the 12678c2ecf20Sopenharmony_ci * receiver will not reply to the message. 12688c2ecf20Sopenharmony_ci * 12698c2ecf20Sopenharmony_ci * Locking: no locking 12708c2ecf20Sopenharmony_ci * 12718c2ecf20Sopenharmony_ci * Returns the result from the CP IUCV call. 12728c2ecf20Sopenharmony_ci */ 12738c2ecf20Sopenharmony_ciint __iucv_message_send(struct iucv_path *path, struct iucv_message *msg, 12748c2ecf20Sopenharmony_ci u8 flags, u32 srccls, void *buffer, size_t size) 12758c2ecf20Sopenharmony_ci{ 12768c2ecf20Sopenharmony_ci union iucv_param *parm; 12778c2ecf20Sopenharmony_ci int rc; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci if (cpumask_empty(&iucv_buffer_cpumask)) { 12808c2ecf20Sopenharmony_ci rc = -EIO; 12818c2ecf20Sopenharmony_ci goto out; 12828c2ecf20Sopenharmony_ci } 12838c2ecf20Sopenharmony_ci parm = iucv_param[smp_processor_id()]; 12848c2ecf20Sopenharmony_ci memset(parm, 0, sizeof(union iucv_param)); 12858c2ecf20Sopenharmony_ci if (flags & IUCV_IPRMDATA) { 12868c2ecf20Sopenharmony_ci /* Message of 8 bytes can be placed into the parameter list. */ 12878c2ecf20Sopenharmony_ci parm->dpl.ippathid = path->pathid; 12888c2ecf20Sopenharmony_ci parm->dpl.ipflags1 = flags | IUCV_IPNORPY; 12898c2ecf20Sopenharmony_ci parm->dpl.iptrgcls = msg->class; 12908c2ecf20Sopenharmony_ci parm->dpl.ipsrccls = srccls; 12918c2ecf20Sopenharmony_ci parm->dpl.ipmsgtag = msg->tag; 12928c2ecf20Sopenharmony_ci memcpy(parm->dpl.iprmmsg, buffer, 8); 12938c2ecf20Sopenharmony_ci } else { 12948c2ecf20Sopenharmony_ci parm->db.ipbfadr1 = (u32)(addr_t) buffer; 12958c2ecf20Sopenharmony_ci parm->db.ipbfln1f = (u32) size; 12968c2ecf20Sopenharmony_ci parm->db.ippathid = path->pathid; 12978c2ecf20Sopenharmony_ci parm->db.ipflags1 = flags | IUCV_IPNORPY; 12988c2ecf20Sopenharmony_ci parm->db.iptrgcls = msg->class; 12998c2ecf20Sopenharmony_ci parm->db.ipsrccls = srccls; 13008c2ecf20Sopenharmony_ci parm->db.ipmsgtag = msg->tag; 13018c2ecf20Sopenharmony_ci } 13028c2ecf20Sopenharmony_ci rc = iucv_call_b2f0(IUCV_SEND, parm); 13038c2ecf20Sopenharmony_ci if (!rc) 13048c2ecf20Sopenharmony_ci msg->id = parm->db.ipmsgid; 13058c2ecf20Sopenharmony_ciout: 13068c2ecf20Sopenharmony_ci return rc; 13078c2ecf20Sopenharmony_ci} 13088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__iucv_message_send); 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci/** 13118c2ecf20Sopenharmony_ci * iucv_message_send 13128c2ecf20Sopenharmony_ci * @path: address of iucv path structure 13138c2ecf20Sopenharmony_ci * @msg: address of iucv msg structure 13148c2ecf20Sopenharmony_ci * @flags: how the message is sent (IUCV_IPRMDATA, IUCV_IPPRTY, IUCV_IPBUFLST) 13158c2ecf20Sopenharmony_ci * @srccls: source class of message 13168c2ecf20Sopenharmony_ci * @buffer: address of send buffer or address of struct iucv_array 13178c2ecf20Sopenharmony_ci * @size: length of send buffer 13188c2ecf20Sopenharmony_ci * 13198c2ecf20Sopenharmony_ci * This function transmits data to another application. Data to be 13208c2ecf20Sopenharmony_ci * transmitted is in a buffer and this is a one-way message and the 13218c2ecf20Sopenharmony_ci * receiver will not reply to the message. 13228c2ecf20Sopenharmony_ci * 13238c2ecf20Sopenharmony_ci * Locking: local_bh_enable/local_bh_disable 13248c2ecf20Sopenharmony_ci * 13258c2ecf20Sopenharmony_ci * Returns the result from the CP IUCV call. 13268c2ecf20Sopenharmony_ci */ 13278c2ecf20Sopenharmony_ciint iucv_message_send(struct iucv_path *path, struct iucv_message *msg, 13288c2ecf20Sopenharmony_ci u8 flags, u32 srccls, void *buffer, size_t size) 13298c2ecf20Sopenharmony_ci{ 13308c2ecf20Sopenharmony_ci int rc; 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci local_bh_disable(); 13338c2ecf20Sopenharmony_ci rc = __iucv_message_send(path, msg, flags, srccls, buffer, size); 13348c2ecf20Sopenharmony_ci local_bh_enable(); 13358c2ecf20Sopenharmony_ci return rc; 13368c2ecf20Sopenharmony_ci} 13378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_message_send); 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci/** 13408c2ecf20Sopenharmony_ci * iucv_message_send2way 13418c2ecf20Sopenharmony_ci * @path: address of iucv path structure 13428c2ecf20Sopenharmony_ci * @msg: address of iucv msg structure 13438c2ecf20Sopenharmony_ci * @flags: how the message is sent and the reply is received 13448c2ecf20Sopenharmony_ci * (IUCV_IPRMDATA, IUCV_IPBUFLST, IUCV_IPPRTY, IUCV_ANSLST) 13458c2ecf20Sopenharmony_ci * @srccls: source class of message 13468c2ecf20Sopenharmony_ci * @buffer: address of send buffer or address of struct iucv_array 13478c2ecf20Sopenharmony_ci * @size: length of send buffer 13488c2ecf20Sopenharmony_ci * @ansbuf: address of answer buffer or address of struct iucv_array 13498c2ecf20Sopenharmony_ci * @asize: size of reply buffer 13508c2ecf20Sopenharmony_ci * 13518c2ecf20Sopenharmony_ci * This function transmits data to another application. Data to be 13528c2ecf20Sopenharmony_ci * transmitted is in a buffer. The receiver of the send is expected to 13538c2ecf20Sopenharmony_ci * reply to the message and a buffer is provided into which IUCV moves 13548c2ecf20Sopenharmony_ci * the reply to this message. 13558c2ecf20Sopenharmony_ci * 13568c2ecf20Sopenharmony_ci * Returns the result from the CP IUCV call. 13578c2ecf20Sopenharmony_ci */ 13588c2ecf20Sopenharmony_ciint iucv_message_send2way(struct iucv_path *path, struct iucv_message *msg, 13598c2ecf20Sopenharmony_ci u8 flags, u32 srccls, void *buffer, size_t size, 13608c2ecf20Sopenharmony_ci void *answer, size_t asize, size_t *residual) 13618c2ecf20Sopenharmony_ci{ 13628c2ecf20Sopenharmony_ci union iucv_param *parm; 13638c2ecf20Sopenharmony_ci int rc; 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci local_bh_disable(); 13668c2ecf20Sopenharmony_ci if (cpumask_empty(&iucv_buffer_cpumask)) { 13678c2ecf20Sopenharmony_ci rc = -EIO; 13688c2ecf20Sopenharmony_ci goto out; 13698c2ecf20Sopenharmony_ci } 13708c2ecf20Sopenharmony_ci parm = iucv_param[smp_processor_id()]; 13718c2ecf20Sopenharmony_ci memset(parm, 0, sizeof(union iucv_param)); 13728c2ecf20Sopenharmony_ci if (flags & IUCV_IPRMDATA) { 13738c2ecf20Sopenharmony_ci parm->dpl.ippathid = path->pathid; 13748c2ecf20Sopenharmony_ci parm->dpl.ipflags1 = path->flags; /* priority message */ 13758c2ecf20Sopenharmony_ci parm->dpl.iptrgcls = msg->class; 13768c2ecf20Sopenharmony_ci parm->dpl.ipsrccls = srccls; 13778c2ecf20Sopenharmony_ci parm->dpl.ipmsgtag = msg->tag; 13788c2ecf20Sopenharmony_ci parm->dpl.ipbfadr2 = (u32)(addr_t) answer; 13798c2ecf20Sopenharmony_ci parm->dpl.ipbfln2f = (u32) asize; 13808c2ecf20Sopenharmony_ci memcpy(parm->dpl.iprmmsg, buffer, 8); 13818c2ecf20Sopenharmony_ci } else { 13828c2ecf20Sopenharmony_ci parm->db.ippathid = path->pathid; 13838c2ecf20Sopenharmony_ci parm->db.ipflags1 = path->flags; /* priority message */ 13848c2ecf20Sopenharmony_ci parm->db.iptrgcls = msg->class; 13858c2ecf20Sopenharmony_ci parm->db.ipsrccls = srccls; 13868c2ecf20Sopenharmony_ci parm->db.ipmsgtag = msg->tag; 13878c2ecf20Sopenharmony_ci parm->db.ipbfadr1 = (u32)(addr_t) buffer; 13888c2ecf20Sopenharmony_ci parm->db.ipbfln1f = (u32) size; 13898c2ecf20Sopenharmony_ci parm->db.ipbfadr2 = (u32)(addr_t) answer; 13908c2ecf20Sopenharmony_ci parm->db.ipbfln2f = (u32) asize; 13918c2ecf20Sopenharmony_ci } 13928c2ecf20Sopenharmony_ci rc = iucv_call_b2f0(IUCV_SEND, parm); 13938c2ecf20Sopenharmony_ci if (!rc) 13948c2ecf20Sopenharmony_ci msg->id = parm->db.ipmsgid; 13958c2ecf20Sopenharmony_ciout: 13968c2ecf20Sopenharmony_ci local_bh_enable(); 13978c2ecf20Sopenharmony_ci return rc; 13988c2ecf20Sopenharmony_ci} 13998c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_message_send2way); 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci/** 14028c2ecf20Sopenharmony_ci * iucv_path_pending 14038c2ecf20Sopenharmony_ci * @data: Pointer to external interrupt buffer 14048c2ecf20Sopenharmony_ci * 14058c2ecf20Sopenharmony_ci * Process connection pending work item. Called from tasklet while holding 14068c2ecf20Sopenharmony_ci * iucv_table_lock. 14078c2ecf20Sopenharmony_ci */ 14088c2ecf20Sopenharmony_cistruct iucv_path_pending { 14098c2ecf20Sopenharmony_ci u16 ippathid; 14108c2ecf20Sopenharmony_ci u8 ipflags1; 14118c2ecf20Sopenharmony_ci u8 iptype; 14128c2ecf20Sopenharmony_ci u16 ipmsglim; 14138c2ecf20Sopenharmony_ci u16 res1; 14148c2ecf20Sopenharmony_ci u8 ipvmid[8]; 14158c2ecf20Sopenharmony_ci u8 ipuser[16]; 14168c2ecf20Sopenharmony_ci u32 res3; 14178c2ecf20Sopenharmony_ci u8 ippollfg; 14188c2ecf20Sopenharmony_ci u8 res4[3]; 14198c2ecf20Sopenharmony_ci} __packed; 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_cistatic void iucv_path_pending(struct iucv_irq_data *data) 14228c2ecf20Sopenharmony_ci{ 14238c2ecf20Sopenharmony_ci struct iucv_path_pending *ipp = (void *) data; 14248c2ecf20Sopenharmony_ci struct iucv_handler *handler; 14258c2ecf20Sopenharmony_ci struct iucv_path *path; 14268c2ecf20Sopenharmony_ci char *error; 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci BUG_ON(iucv_path_table[ipp->ippathid]); 14298c2ecf20Sopenharmony_ci /* New pathid, handler found. Create a new path struct. */ 14308c2ecf20Sopenharmony_ci error = iucv_error_no_memory; 14318c2ecf20Sopenharmony_ci path = iucv_path_alloc(ipp->ipmsglim, ipp->ipflags1, GFP_ATOMIC); 14328c2ecf20Sopenharmony_ci if (!path) 14338c2ecf20Sopenharmony_ci goto out_sever; 14348c2ecf20Sopenharmony_ci path->pathid = ipp->ippathid; 14358c2ecf20Sopenharmony_ci iucv_path_table[path->pathid] = path; 14368c2ecf20Sopenharmony_ci EBCASC(ipp->ipvmid, 8); 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci /* Call registered handler until one is found that wants the path. */ 14398c2ecf20Sopenharmony_ci list_for_each_entry(handler, &iucv_handler_list, list) { 14408c2ecf20Sopenharmony_ci if (!handler->path_pending) 14418c2ecf20Sopenharmony_ci continue; 14428c2ecf20Sopenharmony_ci /* 14438c2ecf20Sopenharmony_ci * Add path to handler to allow a call to iucv_path_sever 14448c2ecf20Sopenharmony_ci * inside the path_pending function. If the handler returns 14458c2ecf20Sopenharmony_ci * an error remove the path from the handler again. 14468c2ecf20Sopenharmony_ci */ 14478c2ecf20Sopenharmony_ci list_add(&path->list, &handler->paths); 14488c2ecf20Sopenharmony_ci path->handler = handler; 14498c2ecf20Sopenharmony_ci if (!handler->path_pending(path, ipp->ipvmid, ipp->ipuser)) 14508c2ecf20Sopenharmony_ci return; 14518c2ecf20Sopenharmony_ci list_del(&path->list); 14528c2ecf20Sopenharmony_ci path->handler = NULL; 14538c2ecf20Sopenharmony_ci } 14548c2ecf20Sopenharmony_ci /* No handler wanted the path. */ 14558c2ecf20Sopenharmony_ci iucv_path_table[path->pathid] = NULL; 14568c2ecf20Sopenharmony_ci iucv_path_free(path); 14578c2ecf20Sopenharmony_ci error = iucv_error_no_listener; 14588c2ecf20Sopenharmony_ciout_sever: 14598c2ecf20Sopenharmony_ci iucv_sever_pathid(ipp->ippathid, error); 14608c2ecf20Sopenharmony_ci} 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci/** 14638c2ecf20Sopenharmony_ci * iucv_path_complete 14648c2ecf20Sopenharmony_ci * @data: Pointer to external interrupt buffer 14658c2ecf20Sopenharmony_ci * 14668c2ecf20Sopenharmony_ci * Process connection complete work item. Called from tasklet while holding 14678c2ecf20Sopenharmony_ci * iucv_table_lock. 14688c2ecf20Sopenharmony_ci */ 14698c2ecf20Sopenharmony_cistruct iucv_path_complete { 14708c2ecf20Sopenharmony_ci u16 ippathid; 14718c2ecf20Sopenharmony_ci u8 ipflags1; 14728c2ecf20Sopenharmony_ci u8 iptype; 14738c2ecf20Sopenharmony_ci u16 ipmsglim; 14748c2ecf20Sopenharmony_ci u16 res1; 14758c2ecf20Sopenharmony_ci u8 res2[8]; 14768c2ecf20Sopenharmony_ci u8 ipuser[16]; 14778c2ecf20Sopenharmony_ci u32 res3; 14788c2ecf20Sopenharmony_ci u8 ippollfg; 14798c2ecf20Sopenharmony_ci u8 res4[3]; 14808c2ecf20Sopenharmony_ci} __packed; 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_cistatic void iucv_path_complete(struct iucv_irq_data *data) 14838c2ecf20Sopenharmony_ci{ 14848c2ecf20Sopenharmony_ci struct iucv_path_complete *ipc = (void *) data; 14858c2ecf20Sopenharmony_ci struct iucv_path *path = iucv_path_table[ipc->ippathid]; 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci if (path) 14888c2ecf20Sopenharmony_ci path->flags = ipc->ipflags1; 14898c2ecf20Sopenharmony_ci if (path && path->handler && path->handler->path_complete) 14908c2ecf20Sopenharmony_ci path->handler->path_complete(path, ipc->ipuser); 14918c2ecf20Sopenharmony_ci} 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci/** 14948c2ecf20Sopenharmony_ci * iucv_path_severed 14958c2ecf20Sopenharmony_ci * @data: Pointer to external interrupt buffer 14968c2ecf20Sopenharmony_ci * 14978c2ecf20Sopenharmony_ci * Process connection severed work item. Called from tasklet while holding 14988c2ecf20Sopenharmony_ci * iucv_table_lock. 14998c2ecf20Sopenharmony_ci */ 15008c2ecf20Sopenharmony_cistruct iucv_path_severed { 15018c2ecf20Sopenharmony_ci u16 ippathid; 15028c2ecf20Sopenharmony_ci u8 res1; 15038c2ecf20Sopenharmony_ci u8 iptype; 15048c2ecf20Sopenharmony_ci u32 res2; 15058c2ecf20Sopenharmony_ci u8 res3[8]; 15068c2ecf20Sopenharmony_ci u8 ipuser[16]; 15078c2ecf20Sopenharmony_ci u32 res4; 15088c2ecf20Sopenharmony_ci u8 ippollfg; 15098c2ecf20Sopenharmony_ci u8 res5[3]; 15108c2ecf20Sopenharmony_ci} __packed; 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_cistatic void iucv_path_severed(struct iucv_irq_data *data) 15138c2ecf20Sopenharmony_ci{ 15148c2ecf20Sopenharmony_ci struct iucv_path_severed *ips = (void *) data; 15158c2ecf20Sopenharmony_ci struct iucv_path *path = iucv_path_table[ips->ippathid]; 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci if (!path || !path->handler) /* Already severed */ 15188c2ecf20Sopenharmony_ci return; 15198c2ecf20Sopenharmony_ci if (path->handler->path_severed) 15208c2ecf20Sopenharmony_ci path->handler->path_severed(path, ips->ipuser); 15218c2ecf20Sopenharmony_ci else { 15228c2ecf20Sopenharmony_ci iucv_sever_pathid(path->pathid, NULL); 15238c2ecf20Sopenharmony_ci iucv_path_table[path->pathid] = NULL; 15248c2ecf20Sopenharmony_ci list_del(&path->list); 15258c2ecf20Sopenharmony_ci iucv_path_free(path); 15268c2ecf20Sopenharmony_ci } 15278c2ecf20Sopenharmony_ci} 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci/** 15308c2ecf20Sopenharmony_ci * iucv_path_quiesced 15318c2ecf20Sopenharmony_ci * @data: Pointer to external interrupt buffer 15328c2ecf20Sopenharmony_ci * 15338c2ecf20Sopenharmony_ci * Process connection quiesced work item. Called from tasklet while holding 15348c2ecf20Sopenharmony_ci * iucv_table_lock. 15358c2ecf20Sopenharmony_ci */ 15368c2ecf20Sopenharmony_cistruct iucv_path_quiesced { 15378c2ecf20Sopenharmony_ci u16 ippathid; 15388c2ecf20Sopenharmony_ci u8 res1; 15398c2ecf20Sopenharmony_ci u8 iptype; 15408c2ecf20Sopenharmony_ci u32 res2; 15418c2ecf20Sopenharmony_ci u8 res3[8]; 15428c2ecf20Sopenharmony_ci u8 ipuser[16]; 15438c2ecf20Sopenharmony_ci u32 res4; 15448c2ecf20Sopenharmony_ci u8 ippollfg; 15458c2ecf20Sopenharmony_ci u8 res5[3]; 15468c2ecf20Sopenharmony_ci} __packed; 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_cistatic void iucv_path_quiesced(struct iucv_irq_data *data) 15498c2ecf20Sopenharmony_ci{ 15508c2ecf20Sopenharmony_ci struct iucv_path_quiesced *ipq = (void *) data; 15518c2ecf20Sopenharmony_ci struct iucv_path *path = iucv_path_table[ipq->ippathid]; 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci if (path && path->handler && path->handler->path_quiesced) 15548c2ecf20Sopenharmony_ci path->handler->path_quiesced(path, ipq->ipuser); 15558c2ecf20Sopenharmony_ci} 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci/** 15588c2ecf20Sopenharmony_ci * iucv_path_resumed 15598c2ecf20Sopenharmony_ci * @data: Pointer to external interrupt buffer 15608c2ecf20Sopenharmony_ci * 15618c2ecf20Sopenharmony_ci * Process connection resumed work item. Called from tasklet while holding 15628c2ecf20Sopenharmony_ci * iucv_table_lock. 15638c2ecf20Sopenharmony_ci */ 15648c2ecf20Sopenharmony_cistruct iucv_path_resumed { 15658c2ecf20Sopenharmony_ci u16 ippathid; 15668c2ecf20Sopenharmony_ci u8 res1; 15678c2ecf20Sopenharmony_ci u8 iptype; 15688c2ecf20Sopenharmony_ci u32 res2; 15698c2ecf20Sopenharmony_ci u8 res3[8]; 15708c2ecf20Sopenharmony_ci u8 ipuser[16]; 15718c2ecf20Sopenharmony_ci u32 res4; 15728c2ecf20Sopenharmony_ci u8 ippollfg; 15738c2ecf20Sopenharmony_ci u8 res5[3]; 15748c2ecf20Sopenharmony_ci} __packed; 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_cistatic void iucv_path_resumed(struct iucv_irq_data *data) 15778c2ecf20Sopenharmony_ci{ 15788c2ecf20Sopenharmony_ci struct iucv_path_resumed *ipr = (void *) data; 15798c2ecf20Sopenharmony_ci struct iucv_path *path = iucv_path_table[ipr->ippathid]; 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci if (path && path->handler && path->handler->path_resumed) 15828c2ecf20Sopenharmony_ci path->handler->path_resumed(path, ipr->ipuser); 15838c2ecf20Sopenharmony_ci} 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci/** 15868c2ecf20Sopenharmony_ci * iucv_message_complete 15878c2ecf20Sopenharmony_ci * @data: Pointer to external interrupt buffer 15888c2ecf20Sopenharmony_ci * 15898c2ecf20Sopenharmony_ci * Process message complete work item. Called from tasklet while holding 15908c2ecf20Sopenharmony_ci * iucv_table_lock. 15918c2ecf20Sopenharmony_ci */ 15928c2ecf20Sopenharmony_cistruct iucv_message_complete { 15938c2ecf20Sopenharmony_ci u16 ippathid; 15948c2ecf20Sopenharmony_ci u8 ipflags1; 15958c2ecf20Sopenharmony_ci u8 iptype; 15968c2ecf20Sopenharmony_ci u32 ipmsgid; 15978c2ecf20Sopenharmony_ci u32 ipaudit; 15988c2ecf20Sopenharmony_ci u8 iprmmsg[8]; 15998c2ecf20Sopenharmony_ci u32 ipsrccls; 16008c2ecf20Sopenharmony_ci u32 ipmsgtag; 16018c2ecf20Sopenharmony_ci u32 res; 16028c2ecf20Sopenharmony_ci u32 ipbfln2f; 16038c2ecf20Sopenharmony_ci u8 ippollfg; 16048c2ecf20Sopenharmony_ci u8 res2[3]; 16058c2ecf20Sopenharmony_ci} __packed; 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_cistatic void iucv_message_complete(struct iucv_irq_data *data) 16088c2ecf20Sopenharmony_ci{ 16098c2ecf20Sopenharmony_ci struct iucv_message_complete *imc = (void *) data; 16108c2ecf20Sopenharmony_ci struct iucv_path *path = iucv_path_table[imc->ippathid]; 16118c2ecf20Sopenharmony_ci struct iucv_message msg; 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci if (path && path->handler && path->handler->message_complete) { 16148c2ecf20Sopenharmony_ci msg.flags = imc->ipflags1; 16158c2ecf20Sopenharmony_ci msg.id = imc->ipmsgid; 16168c2ecf20Sopenharmony_ci msg.audit = imc->ipaudit; 16178c2ecf20Sopenharmony_ci memcpy(msg.rmmsg, imc->iprmmsg, 8); 16188c2ecf20Sopenharmony_ci msg.class = imc->ipsrccls; 16198c2ecf20Sopenharmony_ci msg.tag = imc->ipmsgtag; 16208c2ecf20Sopenharmony_ci msg.length = imc->ipbfln2f; 16218c2ecf20Sopenharmony_ci path->handler->message_complete(path, &msg); 16228c2ecf20Sopenharmony_ci } 16238c2ecf20Sopenharmony_ci} 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci/** 16268c2ecf20Sopenharmony_ci * iucv_message_pending 16278c2ecf20Sopenharmony_ci * @data: Pointer to external interrupt buffer 16288c2ecf20Sopenharmony_ci * 16298c2ecf20Sopenharmony_ci * Process message pending work item. Called from tasklet while holding 16308c2ecf20Sopenharmony_ci * iucv_table_lock. 16318c2ecf20Sopenharmony_ci */ 16328c2ecf20Sopenharmony_cistruct iucv_message_pending { 16338c2ecf20Sopenharmony_ci u16 ippathid; 16348c2ecf20Sopenharmony_ci u8 ipflags1; 16358c2ecf20Sopenharmony_ci u8 iptype; 16368c2ecf20Sopenharmony_ci u32 ipmsgid; 16378c2ecf20Sopenharmony_ci u32 iptrgcls; 16388c2ecf20Sopenharmony_ci union { 16398c2ecf20Sopenharmony_ci u32 iprmmsg1_u32; 16408c2ecf20Sopenharmony_ci u8 iprmmsg1[4]; 16418c2ecf20Sopenharmony_ci } ln1msg1; 16428c2ecf20Sopenharmony_ci union { 16438c2ecf20Sopenharmony_ci u32 ipbfln1f; 16448c2ecf20Sopenharmony_ci u8 iprmmsg2[4]; 16458c2ecf20Sopenharmony_ci } ln1msg2; 16468c2ecf20Sopenharmony_ci u32 res1[3]; 16478c2ecf20Sopenharmony_ci u32 ipbfln2f; 16488c2ecf20Sopenharmony_ci u8 ippollfg; 16498c2ecf20Sopenharmony_ci u8 res2[3]; 16508c2ecf20Sopenharmony_ci} __packed; 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_cistatic void iucv_message_pending(struct iucv_irq_data *data) 16538c2ecf20Sopenharmony_ci{ 16548c2ecf20Sopenharmony_ci struct iucv_message_pending *imp = (void *) data; 16558c2ecf20Sopenharmony_ci struct iucv_path *path = iucv_path_table[imp->ippathid]; 16568c2ecf20Sopenharmony_ci struct iucv_message msg; 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci if (path && path->handler && path->handler->message_pending) { 16598c2ecf20Sopenharmony_ci msg.flags = imp->ipflags1; 16608c2ecf20Sopenharmony_ci msg.id = imp->ipmsgid; 16618c2ecf20Sopenharmony_ci msg.class = imp->iptrgcls; 16628c2ecf20Sopenharmony_ci if (imp->ipflags1 & IUCV_IPRMDATA) { 16638c2ecf20Sopenharmony_ci memcpy(msg.rmmsg, imp->ln1msg1.iprmmsg1, 8); 16648c2ecf20Sopenharmony_ci msg.length = 8; 16658c2ecf20Sopenharmony_ci } else 16668c2ecf20Sopenharmony_ci msg.length = imp->ln1msg2.ipbfln1f; 16678c2ecf20Sopenharmony_ci msg.reply_size = imp->ipbfln2f; 16688c2ecf20Sopenharmony_ci path->handler->message_pending(path, &msg); 16698c2ecf20Sopenharmony_ci } 16708c2ecf20Sopenharmony_ci} 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci/** 16738c2ecf20Sopenharmony_ci * iucv_tasklet_fn: 16748c2ecf20Sopenharmony_ci * 16758c2ecf20Sopenharmony_ci * This tasklet loops over the queue of irq buffers created by 16768c2ecf20Sopenharmony_ci * iucv_external_interrupt, calls the appropriate action handler 16778c2ecf20Sopenharmony_ci * and then frees the buffer. 16788c2ecf20Sopenharmony_ci */ 16798c2ecf20Sopenharmony_cistatic void iucv_tasklet_fn(unsigned long ignored) 16808c2ecf20Sopenharmony_ci{ 16818c2ecf20Sopenharmony_ci typedef void iucv_irq_fn(struct iucv_irq_data *); 16828c2ecf20Sopenharmony_ci static iucv_irq_fn *irq_fn[] = { 16838c2ecf20Sopenharmony_ci [0x02] = iucv_path_complete, 16848c2ecf20Sopenharmony_ci [0x03] = iucv_path_severed, 16858c2ecf20Sopenharmony_ci [0x04] = iucv_path_quiesced, 16868c2ecf20Sopenharmony_ci [0x05] = iucv_path_resumed, 16878c2ecf20Sopenharmony_ci [0x06] = iucv_message_complete, 16888c2ecf20Sopenharmony_ci [0x07] = iucv_message_complete, 16898c2ecf20Sopenharmony_ci [0x08] = iucv_message_pending, 16908c2ecf20Sopenharmony_ci [0x09] = iucv_message_pending, 16918c2ecf20Sopenharmony_ci }; 16928c2ecf20Sopenharmony_ci LIST_HEAD(task_queue); 16938c2ecf20Sopenharmony_ci struct iucv_irq_list *p, *n; 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci /* Serialize tasklet, iucv_path_sever and iucv_path_connect. */ 16968c2ecf20Sopenharmony_ci if (!spin_trylock(&iucv_table_lock)) { 16978c2ecf20Sopenharmony_ci tasklet_schedule(&iucv_tasklet); 16988c2ecf20Sopenharmony_ci return; 16998c2ecf20Sopenharmony_ci } 17008c2ecf20Sopenharmony_ci iucv_active_cpu = smp_processor_id(); 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci spin_lock_irq(&iucv_queue_lock); 17038c2ecf20Sopenharmony_ci list_splice_init(&iucv_task_queue, &task_queue); 17048c2ecf20Sopenharmony_ci spin_unlock_irq(&iucv_queue_lock); 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci list_for_each_entry_safe(p, n, &task_queue, list) { 17078c2ecf20Sopenharmony_ci list_del_init(&p->list); 17088c2ecf20Sopenharmony_ci irq_fn[p->data.iptype](&p->data); 17098c2ecf20Sopenharmony_ci kfree(p); 17108c2ecf20Sopenharmony_ci } 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci iucv_active_cpu = -1; 17138c2ecf20Sopenharmony_ci spin_unlock(&iucv_table_lock); 17148c2ecf20Sopenharmony_ci} 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci/** 17178c2ecf20Sopenharmony_ci * iucv_work_fn: 17188c2ecf20Sopenharmony_ci * 17198c2ecf20Sopenharmony_ci * This work function loops over the queue of path pending irq blocks 17208c2ecf20Sopenharmony_ci * created by iucv_external_interrupt, calls the appropriate action 17218c2ecf20Sopenharmony_ci * handler and then frees the buffer. 17228c2ecf20Sopenharmony_ci */ 17238c2ecf20Sopenharmony_cistatic void iucv_work_fn(struct work_struct *work) 17248c2ecf20Sopenharmony_ci{ 17258c2ecf20Sopenharmony_ci LIST_HEAD(work_queue); 17268c2ecf20Sopenharmony_ci struct iucv_irq_list *p, *n; 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci /* Serialize tasklet, iucv_path_sever and iucv_path_connect. */ 17298c2ecf20Sopenharmony_ci spin_lock_bh(&iucv_table_lock); 17308c2ecf20Sopenharmony_ci iucv_active_cpu = smp_processor_id(); 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci spin_lock_irq(&iucv_queue_lock); 17338c2ecf20Sopenharmony_ci list_splice_init(&iucv_work_queue, &work_queue); 17348c2ecf20Sopenharmony_ci spin_unlock_irq(&iucv_queue_lock); 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci iucv_cleanup_queue(); 17378c2ecf20Sopenharmony_ci list_for_each_entry_safe(p, n, &work_queue, list) { 17388c2ecf20Sopenharmony_ci list_del_init(&p->list); 17398c2ecf20Sopenharmony_ci iucv_path_pending(&p->data); 17408c2ecf20Sopenharmony_ci kfree(p); 17418c2ecf20Sopenharmony_ci } 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci iucv_active_cpu = -1; 17448c2ecf20Sopenharmony_ci spin_unlock_bh(&iucv_table_lock); 17458c2ecf20Sopenharmony_ci} 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci/** 17488c2ecf20Sopenharmony_ci * iucv_external_interrupt 17498c2ecf20Sopenharmony_ci * @code: irq code 17508c2ecf20Sopenharmony_ci * 17518c2ecf20Sopenharmony_ci * Handles external interrupts coming in from CP. 17528c2ecf20Sopenharmony_ci * Places the interrupt buffer on a queue and schedules iucv_tasklet_fn(). 17538c2ecf20Sopenharmony_ci */ 17548c2ecf20Sopenharmony_cistatic void iucv_external_interrupt(struct ext_code ext_code, 17558c2ecf20Sopenharmony_ci unsigned int param32, unsigned long param64) 17568c2ecf20Sopenharmony_ci{ 17578c2ecf20Sopenharmony_ci struct iucv_irq_data *p; 17588c2ecf20Sopenharmony_ci struct iucv_irq_list *work; 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci inc_irq_stat(IRQEXT_IUC); 17618c2ecf20Sopenharmony_ci p = iucv_irq_data[smp_processor_id()]; 17628c2ecf20Sopenharmony_ci if (p->ippathid >= iucv_max_pathid) { 17638c2ecf20Sopenharmony_ci WARN_ON(p->ippathid >= iucv_max_pathid); 17648c2ecf20Sopenharmony_ci iucv_sever_pathid(p->ippathid, iucv_error_no_listener); 17658c2ecf20Sopenharmony_ci return; 17668c2ecf20Sopenharmony_ci } 17678c2ecf20Sopenharmony_ci BUG_ON(p->iptype < 0x01 || p->iptype > 0x09); 17688c2ecf20Sopenharmony_ci work = kmalloc(sizeof(struct iucv_irq_list), GFP_ATOMIC); 17698c2ecf20Sopenharmony_ci if (!work) { 17708c2ecf20Sopenharmony_ci pr_warn("iucv_external_interrupt: out of memory\n"); 17718c2ecf20Sopenharmony_ci return; 17728c2ecf20Sopenharmony_ci } 17738c2ecf20Sopenharmony_ci memcpy(&work->data, p, sizeof(work->data)); 17748c2ecf20Sopenharmony_ci spin_lock(&iucv_queue_lock); 17758c2ecf20Sopenharmony_ci if (p->iptype == 0x01) { 17768c2ecf20Sopenharmony_ci /* Path pending interrupt. */ 17778c2ecf20Sopenharmony_ci list_add_tail(&work->list, &iucv_work_queue); 17788c2ecf20Sopenharmony_ci schedule_work(&iucv_work); 17798c2ecf20Sopenharmony_ci } else { 17808c2ecf20Sopenharmony_ci /* The other interrupts. */ 17818c2ecf20Sopenharmony_ci list_add_tail(&work->list, &iucv_task_queue); 17828c2ecf20Sopenharmony_ci tasklet_schedule(&iucv_tasklet); 17838c2ecf20Sopenharmony_ci } 17848c2ecf20Sopenharmony_ci spin_unlock(&iucv_queue_lock); 17858c2ecf20Sopenharmony_ci} 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_cistruct iucv_interface iucv_if = { 17888c2ecf20Sopenharmony_ci .message_receive = iucv_message_receive, 17898c2ecf20Sopenharmony_ci .__message_receive = __iucv_message_receive, 17908c2ecf20Sopenharmony_ci .message_reply = iucv_message_reply, 17918c2ecf20Sopenharmony_ci .message_reject = iucv_message_reject, 17928c2ecf20Sopenharmony_ci .message_send = iucv_message_send, 17938c2ecf20Sopenharmony_ci .__message_send = __iucv_message_send, 17948c2ecf20Sopenharmony_ci .message_send2way = iucv_message_send2way, 17958c2ecf20Sopenharmony_ci .message_purge = iucv_message_purge, 17968c2ecf20Sopenharmony_ci .path_accept = iucv_path_accept, 17978c2ecf20Sopenharmony_ci .path_connect = iucv_path_connect, 17988c2ecf20Sopenharmony_ci .path_quiesce = iucv_path_quiesce, 17998c2ecf20Sopenharmony_ci .path_resume = iucv_path_resume, 18008c2ecf20Sopenharmony_ci .path_sever = iucv_path_sever, 18018c2ecf20Sopenharmony_ci .iucv_register = iucv_register, 18028c2ecf20Sopenharmony_ci .iucv_unregister = iucv_unregister, 18038c2ecf20Sopenharmony_ci .bus = NULL, 18048c2ecf20Sopenharmony_ci .root = NULL, 18058c2ecf20Sopenharmony_ci}; 18068c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iucv_if); 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_cistatic enum cpuhp_state iucv_online; 18098c2ecf20Sopenharmony_ci/** 18108c2ecf20Sopenharmony_ci * iucv_init 18118c2ecf20Sopenharmony_ci * 18128c2ecf20Sopenharmony_ci * Allocates and initializes various data structures. 18138c2ecf20Sopenharmony_ci */ 18148c2ecf20Sopenharmony_cistatic int __init iucv_init(void) 18158c2ecf20Sopenharmony_ci{ 18168c2ecf20Sopenharmony_ci int rc; 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci if (!MACHINE_IS_VM) { 18198c2ecf20Sopenharmony_ci rc = -EPROTONOSUPPORT; 18208c2ecf20Sopenharmony_ci goto out; 18218c2ecf20Sopenharmony_ci } 18228c2ecf20Sopenharmony_ci ctl_set_bit(0, 1); 18238c2ecf20Sopenharmony_ci rc = iucv_query_maxconn(); 18248c2ecf20Sopenharmony_ci if (rc) 18258c2ecf20Sopenharmony_ci goto out_ctl; 18268c2ecf20Sopenharmony_ci rc = register_external_irq(EXT_IRQ_IUCV, iucv_external_interrupt); 18278c2ecf20Sopenharmony_ci if (rc) 18288c2ecf20Sopenharmony_ci goto out_ctl; 18298c2ecf20Sopenharmony_ci iucv_root = root_device_register("iucv"); 18308c2ecf20Sopenharmony_ci if (IS_ERR(iucv_root)) { 18318c2ecf20Sopenharmony_ci rc = PTR_ERR(iucv_root); 18328c2ecf20Sopenharmony_ci goto out_int; 18338c2ecf20Sopenharmony_ci } 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci rc = cpuhp_setup_state(CPUHP_NET_IUCV_PREPARE, "net/iucv:prepare", 18368c2ecf20Sopenharmony_ci iucv_cpu_prepare, iucv_cpu_dead); 18378c2ecf20Sopenharmony_ci if (rc) 18388c2ecf20Sopenharmony_ci goto out_dev; 18398c2ecf20Sopenharmony_ci rc = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "net/iucv:online", 18408c2ecf20Sopenharmony_ci iucv_cpu_online, iucv_cpu_down_prep); 18418c2ecf20Sopenharmony_ci if (rc < 0) 18428c2ecf20Sopenharmony_ci goto out_prep; 18438c2ecf20Sopenharmony_ci iucv_online = rc; 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci rc = register_reboot_notifier(&iucv_reboot_notifier); 18468c2ecf20Sopenharmony_ci if (rc) 18478c2ecf20Sopenharmony_ci goto out_remove_hp; 18488c2ecf20Sopenharmony_ci ASCEBC(iucv_error_no_listener, 16); 18498c2ecf20Sopenharmony_ci ASCEBC(iucv_error_no_memory, 16); 18508c2ecf20Sopenharmony_ci ASCEBC(iucv_error_pathid, 16); 18518c2ecf20Sopenharmony_ci iucv_available = 1; 18528c2ecf20Sopenharmony_ci rc = bus_register(&iucv_bus); 18538c2ecf20Sopenharmony_ci if (rc) 18548c2ecf20Sopenharmony_ci goto out_reboot; 18558c2ecf20Sopenharmony_ci iucv_if.root = iucv_root; 18568c2ecf20Sopenharmony_ci iucv_if.bus = &iucv_bus; 18578c2ecf20Sopenharmony_ci return 0; 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ciout_reboot: 18608c2ecf20Sopenharmony_ci unregister_reboot_notifier(&iucv_reboot_notifier); 18618c2ecf20Sopenharmony_ciout_remove_hp: 18628c2ecf20Sopenharmony_ci cpuhp_remove_state(iucv_online); 18638c2ecf20Sopenharmony_ciout_prep: 18648c2ecf20Sopenharmony_ci cpuhp_remove_state(CPUHP_NET_IUCV_PREPARE); 18658c2ecf20Sopenharmony_ciout_dev: 18668c2ecf20Sopenharmony_ci root_device_unregister(iucv_root); 18678c2ecf20Sopenharmony_ciout_int: 18688c2ecf20Sopenharmony_ci unregister_external_irq(EXT_IRQ_IUCV, iucv_external_interrupt); 18698c2ecf20Sopenharmony_ciout_ctl: 18708c2ecf20Sopenharmony_ci ctl_clear_bit(0, 1); 18718c2ecf20Sopenharmony_ciout: 18728c2ecf20Sopenharmony_ci return rc; 18738c2ecf20Sopenharmony_ci} 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_ci/** 18768c2ecf20Sopenharmony_ci * iucv_exit 18778c2ecf20Sopenharmony_ci * 18788c2ecf20Sopenharmony_ci * Frees everything allocated from iucv_init. 18798c2ecf20Sopenharmony_ci */ 18808c2ecf20Sopenharmony_cistatic void __exit iucv_exit(void) 18818c2ecf20Sopenharmony_ci{ 18828c2ecf20Sopenharmony_ci struct iucv_irq_list *p, *n; 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci spin_lock_irq(&iucv_queue_lock); 18858c2ecf20Sopenharmony_ci list_for_each_entry_safe(p, n, &iucv_task_queue, list) 18868c2ecf20Sopenharmony_ci kfree(p); 18878c2ecf20Sopenharmony_ci list_for_each_entry_safe(p, n, &iucv_work_queue, list) 18888c2ecf20Sopenharmony_ci kfree(p); 18898c2ecf20Sopenharmony_ci spin_unlock_irq(&iucv_queue_lock); 18908c2ecf20Sopenharmony_ci unregister_reboot_notifier(&iucv_reboot_notifier); 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci cpuhp_remove_state_nocalls(iucv_online); 18938c2ecf20Sopenharmony_ci cpuhp_remove_state(CPUHP_NET_IUCV_PREPARE); 18948c2ecf20Sopenharmony_ci root_device_unregister(iucv_root); 18958c2ecf20Sopenharmony_ci bus_unregister(&iucv_bus); 18968c2ecf20Sopenharmony_ci unregister_external_irq(EXT_IRQ_IUCV, iucv_external_interrupt); 18978c2ecf20Sopenharmony_ci} 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_cisubsys_initcall(iucv_init); 19008c2ecf20Sopenharmony_cimodule_exit(iucv_exit); 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ciMODULE_AUTHOR("(C) 2001 IBM Corp. by Fritz Elfert (felfert@millenux.com)"); 19038c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Linux for S/390 IUCV lowlevel driver"); 19048c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1905