18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Character device driver for reading z/VM *MONITOR service records. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2004, 2009 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "monreader" 118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/errno.h> 178c2ecf20Sopenharmony_ci#include <linux/types.h> 188c2ecf20Sopenharmony_ci#include <linux/kernel.h> 198c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 208c2ecf20Sopenharmony_ci#include <linux/ctype.h> 218c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 228c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 238c2ecf20Sopenharmony_ci#include <linux/poll.h> 248c2ecf20Sopenharmony_ci#include <linux/device.h> 258c2ecf20Sopenharmony_ci#include <linux/slab.h> 268c2ecf20Sopenharmony_ci#include <net/iucv/iucv.h> 278c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 288c2ecf20Sopenharmony_ci#include <asm/ebcdic.h> 298c2ecf20Sopenharmony_ci#include <asm/extmem.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define MON_COLLECT_SAMPLE 0x80 338c2ecf20Sopenharmony_ci#define MON_COLLECT_EVENT 0x40 348c2ecf20Sopenharmony_ci#define MON_SERVICE "*MONITOR" 358c2ecf20Sopenharmony_ci#define MON_IN_USE 0x01 368c2ecf20Sopenharmony_ci#define MON_MSGLIM 255 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic char mon_dcss_name[9] = "MONDCSS\0"; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct mon_msg { 418c2ecf20Sopenharmony_ci u32 pos; 428c2ecf20Sopenharmony_ci u32 mca_offset; 438c2ecf20Sopenharmony_ci struct iucv_message msg; 448c2ecf20Sopenharmony_ci char msglim_reached; 458c2ecf20Sopenharmony_ci char replied_msglim; 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistruct mon_private { 498c2ecf20Sopenharmony_ci struct iucv_path *path; 508c2ecf20Sopenharmony_ci struct mon_msg *msg_array[MON_MSGLIM]; 518c2ecf20Sopenharmony_ci unsigned int write_index; 528c2ecf20Sopenharmony_ci unsigned int read_index; 538c2ecf20Sopenharmony_ci atomic_t msglim_count; 548c2ecf20Sopenharmony_ci atomic_t read_ready; 558c2ecf20Sopenharmony_ci atomic_t iucv_connected; 568c2ecf20Sopenharmony_ci atomic_t iucv_severed; 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic unsigned long mon_in_use = 0; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic unsigned long mon_dcss_start; 628c2ecf20Sopenharmony_cistatic unsigned long mon_dcss_end; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(mon_read_wait_queue); 658c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(mon_conn_wait_queue); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic u8 user_data_connect[16] = { 688c2ecf20Sopenharmony_ci /* Version code, must be 0x01 for shared mode */ 698c2ecf20Sopenharmony_ci 0x01, 708c2ecf20Sopenharmony_ci /* what to collect */ 718c2ecf20Sopenharmony_ci MON_COLLECT_SAMPLE | MON_COLLECT_EVENT, 728c2ecf20Sopenharmony_ci /* DCSS name in EBCDIC, 8 bytes padded with blanks */ 738c2ecf20Sopenharmony_ci 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 748c2ecf20Sopenharmony_ci 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 758c2ecf20Sopenharmony_ci}; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic u8 user_data_sever[16] = { 788c2ecf20Sopenharmony_ci 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 798c2ecf20Sopenharmony_ci 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic struct device *monreader_device; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/****************************************************************************** 858c2ecf20Sopenharmony_ci * helper functions * 868c2ecf20Sopenharmony_ci *****************************************************************************/ 878c2ecf20Sopenharmony_ci/* 888c2ecf20Sopenharmony_ci * Create the 8 bytes EBCDIC DCSS segment name from 898c2ecf20Sopenharmony_ci * an ASCII name, incl. padding 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_cistatic void dcss_mkname(char *ascii_name, char *ebcdic_name) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci int i; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 968c2ecf20Sopenharmony_ci if (ascii_name[i] == '\0') 978c2ecf20Sopenharmony_ci break; 988c2ecf20Sopenharmony_ci ebcdic_name[i] = toupper(ascii_name[i]); 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci for (; i < 8; i++) 1018c2ecf20Sopenharmony_ci ebcdic_name[i] = ' '; 1028c2ecf20Sopenharmony_ci ASCEBC(ebcdic_name, 8); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic inline unsigned long mon_mca_start(struct mon_msg *monmsg) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci return *(u32 *) &monmsg->msg.rmmsg; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic inline unsigned long mon_mca_end(struct mon_msg *monmsg) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci return *(u32 *) &monmsg->msg.rmmsg[4]; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic inline u8 mon_mca_type(struct mon_msg *monmsg, u8 index) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci return *((u8 *) mon_mca_start(monmsg) + monmsg->mca_offset + index); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic inline u32 mon_mca_size(struct mon_msg *monmsg) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci return mon_mca_end(monmsg) - mon_mca_start(monmsg) + 1; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic inline u32 mon_rec_start(struct mon_msg *monmsg) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci return *((u32 *) (mon_mca_start(monmsg) + monmsg->mca_offset + 4)); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic inline u32 mon_rec_end(struct mon_msg *monmsg) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci return *((u32 *) (mon_mca_start(monmsg) + monmsg->mca_offset + 8)); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic int mon_check_mca(struct mon_msg *monmsg) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci if ((mon_rec_end(monmsg) <= mon_rec_start(monmsg)) || 1388c2ecf20Sopenharmony_ci (mon_rec_start(monmsg) < mon_dcss_start) || 1398c2ecf20Sopenharmony_ci (mon_rec_end(monmsg) > mon_dcss_end) || 1408c2ecf20Sopenharmony_ci (mon_mca_type(monmsg, 0) == 0) || 1418c2ecf20Sopenharmony_ci (mon_mca_size(monmsg) % 12 != 0) || 1428c2ecf20Sopenharmony_ci (mon_mca_end(monmsg) <= mon_mca_start(monmsg)) || 1438c2ecf20Sopenharmony_ci (mon_mca_end(monmsg) > mon_dcss_end) || 1448c2ecf20Sopenharmony_ci (mon_mca_start(monmsg) < mon_dcss_start) || 1458c2ecf20Sopenharmony_ci ((mon_mca_type(monmsg, 1) == 0) && (mon_mca_type(monmsg, 2) == 0))) 1468c2ecf20Sopenharmony_ci return -EINVAL; 1478c2ecf20Sopenharmony_ci return 0; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic int mon_send_reply(struct mon_msg *monmsg, 1518c2ecf20Sopenharmony_ci struct mon_private *monpriv) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci int rc; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci rc = iucv_message_reply(monpriv->path, &monmsg->msg, 1568c2ecf20Sopenharmony_ci IUCV_IPRMDATA, NULL, 0); 1578c2ecf20Sopenharmony_ci atomic_dec(&monpriv->msglim_count); 1588c2ecf20Sopenharmony_ci if (likely(!monmsg->msglim_reached)) { 1598c2ecf20Sopenharmony_ci monmsg->pos = 0; 1608c2ecf20Sopenharmony_ci monmsg->mca_offset = 0; 1618c2ecf20Sopenharmony_ci monpriv->read_index = (monpriv->read_index + 1) % 1628c2ecf20Sopenharmony_ci MON_MSGLIM; 1638c2ecf20Sopenharmony_ci atomic_dec(&monpriv->read_ready); 1648c2ecf20Sopenharmony_ci } else 1658c2ecf20Sopenharmony_ci monmsg->replied_msglim = 1; 1668c2ecf20Sopenharmony_ci if (rc) { 1678c2ecf20Sopenharmony_ci pr_err("Reading monitor data failed with rc=%i\n", rc); 1688c2ecf20Sopenharmony_ci return -EIO; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic void mon_free_mem(struct mon_private *monpriv) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci int i; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci for (i = 0; i < MON_MSGLIM; i++) 1788c2ecf20Sopenharmony_ci kfree(monpriv->msg_array[i]); 1798c2ecf20Sopenharmony_ci kfree(monpriv); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic struct mon_private *mon_alloc_mem(void) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci int i; 1858c2ecf20Sopenharmony_ci struct mon_private *monpriv; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci monpriv = kzalloc(sizeof(struct mon_private), GFP_KERNEL); 1888c2ecf20Sopenharmony_ci if (!monpriv) 1898c2ecf20Sopenharmony_ci return NULL; 1908c2ecf20Sopenharmony_ci for (i = 0; i < MON_MSGLIM; i++) { 1918c2ecf20Sopenharmony_ci monpriv->msg_array[i] = kzalloc(sizeof(struct mon_msg), 1928c2ecf20Sopenharmony_ci GFP_KERNEL); 1938c2ecf20Sopenharmony_ci if (!monpriv->msg_array[i]) { 1948c2ecf20Sopenharmony_ci mon_free_mem(monpriv); 1958c2ecf20Sopenharmony_ci return NULL; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci return monpriv; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic inline void mon_next_mca(struct mon_msg *monmsg) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci if (likely((mon_mca_size(monmsg) - monmsg->mca_offset) == 12)) 2048c2ecf20Sopenharmony_ci return; 2058c2ecf20Sopenharmony_ci monmsg->mca_offset += 12; 2068c2ecf20Sopenharmony_ci monmsg->pos = 0; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic struct mon_msg *mon_next_message(struct mon_private *monpriv) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct mon_msg *monmsg; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (!atomic_read(&monpriv->read_ready)) 2148c2ecf20Sopenharmony_ci return NULL; 2158c2ecf20Sopenharmony_ci monmsg = monpriv->msg_array[monpriv->read_index]; 2168c2ecf20Sopenharmony_ci if (unlikely(monmsg->replied_msglim)) { 2178c2ecf20Sopenharmony_ci monmsg->replied_msglim = 0; 2188c2ecf20Sopenharmony_ci monmsg->msglim_reached = 0; 2198c2ecf20Sopenharmony_ci monmsg->pos = 0; 2208c2ecf20Sopenharmony_ci monmsg->mca_offset = 0; 2218c2ecf20Sopenharmony_ci monpriv->read_index = (monpriv->read_index + 1) % 2228c2ecf20Sopenharmony_ci MON_MSGLIM; 2238c2ecf20Sopenharmony_ci atomic_dec(&monpriv->read_ready); 2248c2ecf20Sopenharmony_ci return ERR_PTR(-EOVERFLOW); 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci return monmsg; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci/****************************************************************************** 2318c2ecf20Sopenharmony_ci * IUCV handler * 2328c2ecf20Sopenharmony_ci *****************************************************************************/ 2338c2ecf20Sopenharmony_cistatic void mon_iucv_path_complete(struct iucv_path *path, u8 *ipuser) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci struct mon_private *monpriv = path->private; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci atomic_set(&monpriv->iucv_connected, 1); 2388c2ecf20Sopenharmony_ci wake_up(&mon_conn_wait_queue); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic void mon_iucv_path_severed(struct iucv_path *path, u8 *ipuser) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct mon_private *monpriv = path->private; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci pr_err("z/VM *MONITOR system service disconnected with rc=%i\n", 2468c2ecf20Sopenharmony_ci ipuser[0]); 2478c2ecf20Sopenharmony_ci iucv_path_sever(path, NULL); 2488c2ecf20Sopenharmony_ci atomic_set(&monpriv->iucv_severed, 1); 2498c2ecf20Sopenharmony_ci wake_up(&mon_conn_wait_queue); 2508c2ecf20Sopenharmony_ci wake_up_interruptible(&mon_read_wait_queue); 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic void mon_iucv_message_pending(struct iucv_path *path, 2548c2ecf20Sopenharmony_ci struct iucv_message *msg) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci struct mon_private *monpriv = path->private; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci memcpy(&monpriv->msg_array[monpriv->write_index]->msg, 2598c2ecf20Sopenharmony_ci msg, sizeof(*msg)); 2608c2ecf20Sopenharmony_ci if (atomic_inc_return(&monpriv->msglim_count) == MON_MSGLIM) { 2618c2ecf20Sopenharmony_ci pr_warn("The read queue for monitor data is full\n"); 2628c2ecf20Sopenharmony_ci monpriv->msg_array[monpriv->write_index]->msglim_reached = 1; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci monpriv->write_index = (monpriv->write_index + 1) % MON_MSGLIM; 2658c2ecf20Sopenharmony_ci atomic_inc(&monpriv->read_ready); 2668c2ecf20Sopenharmony_ci wake_up_interruptible(&mon_read_wait_queue); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic struct iucv_handler monreader_iucv_handler = { 2708c2ecf20Sopenharmony_ci .path_complete = mon_iucv_path_complete, 2718c2ecf20Sopenharmony_ci .path_severed = mon_iucv_path_severed, 2728c2ecf20Sopenharmony_ci .message_pending = mon_iucv_message_pending, 2738c2ecf20Sopenharmony_ci}; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci/****************************************************************************** 2768c2ecf20Sopenharmony_ci * file operations * 2778c2ecf20Sopenharmony_ci *****************************************************************************/ 2788c2ecf20Sopenharmony_cistatic int mon_open(struct inode *inode, struct file *filp) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct mon_private *monpriv; 2818c2ecf20Sopenharmony_ci int rc; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* 2848c2ecf20Sopenharmony_ci * only one user allowed 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_ci rc = -EBUSY; 2878c2ecf20Sopenharmony_ci if (test_and_set_bit(MON_IN_USE, &mon_in_use)) 2888c2ecf20Sopenharmony_ci goto out; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci rc = -ENOMEM; 2918c2ecf20Sopenharmony_ci monpriv = mon_alloc_mem(); 2928c2ecf20Sopenharmony_ci if (!monpriv) 2938c2ecf20Sopenharmony_ci goto out_use; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci /* 2968c2ecf20Sopenharmony_ci * Connect to *MONITOR service 2978c2ecf20Sopenharmony_ci */ 2988c2ecf20Sopenharmony_ci monpriv->path = iucv_path_alloc(MON_MSGLIM, IUCV_IPRMDATA, GFP_KERNEL); 2998c2ecf20Sopenharmony_ci if (!monpriv->path) 3008c2ecf20Sopenharmony_ci goto out_priv; 3018c2ecf20Sopenharmony_ci rc = iucv_path_connect(monpriv->path, &monreader_iucv_handler, 3028c2ecf20Sopenharmony_ci MON_SERVICE, NULL, user_data_connect, monpriv); 3038c2ecf20Sopenharmony_ci if (rc) { 3048c2ecf20Sopenharmony_ci pr_err("Connecting to the z/VM *MONITOR system service " 3058c2ecf20Sopenharmony_ci "failed with rc=%i\n", rc); 3068c2ecf20Sopenharmony_ci rc = -EIO; 3078c2ecf20Sopenharmony_ci goto out_path; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci /* 3108c2ecf20Sopenharmony_ci * Wait for connection confirmation 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_ci wait_event(mon_conn_wait_queue, 3138c2ecf20Sopenharmony_ci atomic_read(&monpriv->iucv_connected) || 3148c2ecf20Sopenharmony_ci atomic_read(&monpriv->iucv_severed)); 3158c2ecf20Sopenharmony_ci if (atomic_read(&monpriv->iucv_severed)) { 3168c2ecf20Sopenharmony_ci atomic_set(&monpriv->iucv_severed, 0); 3178c2ecf20Sopenharmony_ci atomic_set(&monpriv->iucv_connected, 0); 3188c2ecf20Sopenharmony_ci rc = -EIO; 3198c2ecf20Sopenharmony_ci goto out_path; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci filp->private_data = monpriv; 3228c2ecf20Sopenharmony_ci dev_set_drvdata(monreader_device, monpriv); 3238c2ecf20Sopenharmony_ci return nonseekable_open(inode, filp); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ciout_path: 3268c2ecf20Sopenharmony_ci iucv_path_free(monpriv->path); 3278c2ecf20Sopenharmony_ciout_priv: 3288c2ecf20Sopenharmony_ci mon_free_mem(monpriv); 3298c2ecf20Sopenharmony_ciout_use: 3308c2ecf20Sopenharmony_ci clear_bit(MON_IN_USE, &mon_in_use); 3318c2ecf20Sopenharmony_ciout: 3328c2ecf20Sopenharmony_ci return rc; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic int mon_close(struct inode *inode, struct file *filp) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci int rc, i; 3388c2ecf20Sopenharmony_ci struct mon_private *monpriv = filp->private_data; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* 3418c2ecf20Sopenharmony_ci * Close IUCV connection and unregister 3428c2ecf20Sopenharmony_ci */ 3438c2ecf20Sopenharmony_ci if (monpriv->path) { 3448c2ecf20Sopenharmony_ci rc = iucv_path_sever(monpriv->path, user_data_sever); 3458c2ecf20Sopenharmony_ci if (rc) 3468c2ecf20Sopenharmony_ci pr_warn("Disconnecting the z/VM *MONITOR system service failed with rc=%i\n", 3478c2ecf20Sopenharmony_ci rc); 3488c2ecf20Sopenharmony_ci iucv_path_free(monpriv->path); 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci atomic_set(&monpriv->iucv_severed, 0); 3528c2ecf20Sopenharmony_ci atomic_set(&monpriv->iucv_connected, 0); 3538c2ecf20Sopenharmony_ci atomic_set(&monpriv->read_ready, 0); 3548c2ecf20Sopenharmony_ci atomic_set(&monpriv->msglim_count, 0); 3558c2ecf20Sopenharmony_ci monpriv->write_index = 0; 3568c2ecf20Sopenharmony_ci monpriv->read_index = 0; 3578c2ecf20Sopenharmony_ci dev_set_drvdata(monreader_device, NULL); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci for (i = 0; i < MON_MSGLIM; i++) 3608c2ecf20Sopenharmony_ci kfree(monpriv->msg_array[i]); 3618c2ecf20Sopenharmony_ci kfree(monpriv); 3628c2ecf20Sopenharmony_ci clear_bit(MON_IN_USE, &mon_in_use); 3638c2ecf20Sopenharmony_ci return 0; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic ssize_t mon_read(struct file *filp, char __user *data, 3678c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci struct mon_private *monpriv = filp->private_data; 3708c2ecf20Sopenharmony_ci struct mon_msg *monmsg; 3718c2ecf20Sopenharmony_ci int ret; 3728c2ecf20Sopenharmony_ci u32 mce_start; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci monmsg = mon_next_message(monpriv); 3758c2ecf20Sopenharmony_ci if (IS_ERR(monmsg)) 3768c2ecf20Sopenharmony_ci return PTR_ERR(monmsg); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (!monmsg) { 3798c2ecf20Sopenharmony_ci if (filp->f_flags & O_NONBLOCK) 3808c2ecf20Sopenharmony_ci return -EAGAIN; 3818c2ecf20Sopenharmony_ci ret = wait_event_interruptible(mon_read_wait_queue, 3828c2ecf20Sopenharmony_ci atomic_read(&monpriv->read_ready) || 3838c2ecf20Sopenharmony_ci atomic_read(&monpriv->iucv_severed)); 3848c2ecf20Sopenharmony_ci if (ret) 3858c2ecf20Sopenharmony_ci return ret; 3868c2ecf20Sopenharmony_ci if (unlikely(atomic_read(&monpriv->iucv_severed))) 3878c2ecf20Sopenharmony_ci return -EIO; 3888c2ecf20Sopenharmony_ci monmsg = monpriv->msg_array[monpriv->read_index]; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (!monmsg->pos) 3928c2ecf20Sopenharmony_ci monmsg->pos = mon_mca_start(monmsg) + monmsg->mca_offset; 3938c2ecf20Sopenharmony_ci if (mon_check_mca(monmsg)) 3948c2ecf20Sopenharmony_ci goto reply; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci /* read monitor control element (12 bytes) first */ 3978c2ecf20Sopenharmony_ci mce_start = mon_mca_start(monmsg) + monmsg->mca_offset; 3988c2ecf20Sopenharmony_ci if ((monmsg->pos >= mce_start) && (monmsg->pos < mce_start + 12)) { 3998c2ecf20Sopenharmony_ci count = min(count, (size_t) mce_start + 12 - monmsg->pos); 4008c2ecf20Sopenharmony_ci ret = copy_to_user(data, (void *) (unsigned long) monmsg->pos, 4018c2ecf20Sopenharmony_ci count); 4028c2ecf20Sopenharmony_ci if (ret) 4038c2ecf20Sopenharmony_ci return -EFAULT; 4048c2ecf20Sopenharmony_ci monmsg->pos += count; 4058c2ecf20Sopenharmony_ci if (monmsg->pos == mce_start + 12) 4068c2ecf20Sopenharmony_ci monmsg->pos = mon_rec_start(monmsg); 4078c2ecf20Sopenharmony_ci goto out_copy; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* read records */ 4118c2ecf20Sopenharmony_ci if (monmsg->pos <= mon_rec_end(monmsg)) { 4128c2ecf20Sopenharmony_ci count = min(count, (size_t) mon_rec_end(monmsg) - monmsg->pos 4138c2ecf20Sopenharmony_ci + 1); 4148c2ecf20Sopenharmony_ci ret = copy_to_user(data, (void *) (unsigned long) monmsg->pos, 4158c2ecf20Sopenharmony_ci count); 4168c2ecf20Sopenharmony_ci if (ret) 4178c2ecf20Sopenharmony_ci return -EFAULT; 4188c2ecf20Sopenharmony_ci monmsg->pos += count; 4198c2ecf20Sopenharmony_ci if (monmsg->pos > mon_rec_end(monmsg)) 4208c2ecf20Sopenharmony_ci mon_next_mca(monmsg); 4218c2ecf20Sopenharmony_ci goto out_copy; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_cireply: 4248c2ecf20Sopenharmony_ci ret = mon_send_reply(monmsg, monpriv); 4258c2ecf20Sopenharmony_ci return ret; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ciout_copy: 4288c2ecf20Sopenharmony_ci *ppos += count; 4298c2ecf20Sopenharmony_ci return count; 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic __poll_t mon_poll(struct file *filp, struct poll_table_struct *p) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci struct mon_private *monpriv = filp->private_data; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci poll_wait(filp, &mon_read_wait_queue, p); 4378c2ecf20Sopenharmony_ci if (unlikely(atomic_read(&monpriv->iucv_severed))) 4388c2ecf20Sopenharmony_ci return EPOLLERR; 4398c2ecf20Sopenharmony_ci if (atomic_read(&monpriv->read_ready)) 4408c2ecf20Sopenharmony_ci return EPOLLIN | EPOLLRDNORM; 4418c2ecf20Sopenharmony_ci return 0; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic const struct file_operations mon_fops = { 4458c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4468c2ecf20Sopenharmony_ci .open = &mon_open, 4478c2ecf20Sopenharmony_ci .release = &mon_close, 4488c2ecf20Sopenharmony_ci .read = &mon_read, 4498c2ecf20Sopenharmony_ci .poll = &mon_poll, 4508c2ecf20Sopenharmony_ci .llseek = noop_llseek, 4518c2ecf20Sopenharmony_ci}; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic struct miscdevice mon_dev = { 4548c2ecf20Sopenharmony_ci .name = "monreader", 4558c2ecf20Sopenharmony_ci .fops = &mon_fops, 4568c2ecf20Sopenharmony_ci .minor = MISC_DYNAMIC_MINOR, 4578c2ecf20Sopenharmony_ci}; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci/****************************************************************************** 4618c2ecf20Sopenharmony_ci * suspend / resume * 4628c2ecf20Sopenharmony_ci *****************************************************************************/ 4638c2ecf20Sopenharmony_cistatic int monreader_freeze(struct device *dev) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci struct mon_private *monpriv = dev_get_drvdata(dev); 4668c2ecf20Sopenharmony_ci int rc; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (!monpriv) 4698c2ecf20Sopenharmony_ci return 0; 4708c2ecf20Sopenharmony_ci if (monpriv->path) { 4718c2ecf20Sopenharmony_ci rc = iucv_path_sever(monpriv->path, user_data_sever); 4728c2ecf20Sopenharmony_ci if (rc) 4738c2ecf20Sopenharmony_ci pr_warn("Disconnecting the z/VM *MONITOR system service failed with rc=%i\n", 4748c2ecf20Sopenharmony_ci rc); 4758c2ecf20Sopenharmony_ci iucv_path_free(monpriv->path); 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci atomic_set(&monpriv->iucv_severed, 0); 4788c2ecf20Sopenharmony_ci atomic_set(&monpriv->iucv_connected, 0); 4798c2ecf20Sopenharmony_ci atomic_set(&monpriv->read_ready, 0); 4808c2ecf20Sopenharmony_ci atomic_set(&monpriv->msglim_count, 0); 4818c2ecf20Sopenharmony_ci monpriv->write_index = 0; 4828c2ecf20Sopenharmony_ci monpriv->read_index = 0; 4838c2ecf20Sopenharmony_ci monpriv->path = NULL; 4848c2ecf20Sopenharmony_ci return 0; 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic int monreader_thaw(struct device *dev) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci struct mon_private *monpriv = dev_get_drvdata(dev); 4908c2ecf20Sopenharmony_ci int rc; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci if (!monpriv) 4938c2ecf20Sopenharmony_ci return 0; 4948c2ecf20Sopenharmony_ci rc = -ENOMEM; 4958c2ecf20Sopenharmony_ci monpriv->path = iucv_path_alloc(MON_MSGLIM, IUCV_IPRMDATA, GFP_KERNEL); 4968c2ecf20Sopenharmony_ci if (!monpriv->path) 4978c2ecf20Sopenharmony_ci goto out; 4988c2ecf20Sopenharmony_ci rc = iucv_path_connect(monpriv->path, &monreader_iucv_handler, 4998c2ecf20Sopenharmony_ci MON_SERVICE, NULL, user_data_connect, monpriv); 5008c2ecf20Sopenharmony_ci if (rc) { 5018c2ecf20Sopenharmony_ci pr_err("Connecting to the z/VM *MONITOR system service " 5028c2ecf20Sopenharmony_ci "failed with rc=%i\n", rc); 5038c2ecf20Sopenharmony_ci goto out_path; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci wait_event(mon_conn_wait_queue, 5068c2ecf20Sopenharmony_ci atomic_read(&monpriv->iucv_connected) || 5078c2ecf20Sopenharmony_ci atomic_read(&monpriv->iucv_severed)); 5088c2ecf20Sopenharmony_ci if (atomic_read(&monpriv->iucv_severed)) 5098c2ecf20Sopenharmony_ci goto out_path; 5108c2ecf20Sopenharmony_ci return 0; 5118c2ecf20Sopenharmony_ciout_path: 5128c2ecf20Sopenharmony_ci rc = -EIO; 5138c2ecf20Sopenharmony_ci iucv_path_free(monpriv->path); 5148c2ecf20Sopenharmony_ci monpriv->path = NULL; 5158c2ecf20Sopenharmony_ciout: 5168c2ecf20Sopenharmony_ci atomic_set(&monpriv->iucv_severed, 1); 5178c2ecf20Sopenharmony_ci return rc; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic int monreader_restore(struct device *dev) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci int rc; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci segment_unload(mon_dcss_name); 5258c2ecf20Sopenharmony_ci rc = segment_load(mon_dcss_name, SEGMENT_SHARED, 5268c2ecf20Sopenharmony_ci &mon_dcss_start, &mon_dcss_end); 5278c2ecf20Sopenharmony_ci if (rc < 0) { 5288c2ecf20Sopenharmony_ci segment_warning(rc, mon_dcss_name); 5298c2ecf20Sopenharmony_ci panic("fatal monreader resume error: no monitor dcss\n"); 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci return monreader_thaw(dev); 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic const struct dev_pm_ops monreader_pm_ops = { 5358c2ecf20Sopenharmony_ci .freeze = monreader_freeze, 5368c2ecf20Sopenharmony_ci .thaw = monreader_thaw, 5378c2ecf20Sopenharmony_ci .restore = monreader_restore, 5388c2ecf20Sopenharmony_ci}; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic struct device_driver monreader_driver = { 5418c2ecf20Sopenharmony_ci .name = "monreader", 5428c2ecf20Sopenharmony_ci .bus = &iucv_bus, 5438c2ecf20Sopenharmony_ci .pm = &monreader_pm_ops, 5448c2ecf20Sopenharmony_ci}; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci/****************************************************************************** 5488c2ecf20Sopenharmony_ci * module init/exit * 5498c2ecf20Sopenharmony_ci *****************************************************************************/ 5508c2ecf20Sopenharmony_cistatic int __init mon_init(void) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci int rc; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if (!MACHINE_IS_VM) { 5558c2ecf20Sopenharmony_ci pr_err("The z/VM *MONITOR record device driver cannot be " 5568c2ecf20Sopenharmony_ci "loaded without z/VM\n"); 5578c2ecf20Sopenharmony_ci return -ENODEV; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci /* 5618c2ecf20Sopenharmony_ci * Register with IUCV and connect to *MONITOR service 5628c2ecf20Sopenharmony_ci */ 5638c2ecf20Sopenharmony_ci rc = iucv_register(&monreader_iucv_handler, 1); 5648c2ecf20Sopenharmony_ci if (rc) { 5658c2ecf20Sopenharmony_ci pr_err("The z/VM *MONITOR record device driver failed to " 5668c2ecf20Sopenharmony_ci "register with IUCV\n"); 5678c2ecf20Sopenharmony_ci return rc; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci rc = driver_register(&monreader_driver); 5718c2ecf20Sopenharmony_ci if (rc) 5728c2ecf20Sopenharmony_ci goto out_iucv; 5738c2ecf20Sopenharmony_ci monreader_device = kzalloc(sizeof(struct device), GFP_KERNEL); 5748c2ecf20Sopenharmony_ci if (!monreader_device) { 5758c2ecf20Sopenharmony_ci rc = -ENOMEM; 5768c2ecf20Sopenharmony_ci goto out_driver; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci dev_set_name(monreader_device, "monreader-dev"); 5808c2ecf20Sopenharmony_ci monreader_device->bus = &iucv_bus; 5818c2ecf20Sopenharmony_ci monreader_device->parent = iucv_root; 5828c2ecf20Sopenharmony_ci monreader_device->driver = &monreader_driver; 5838c2ecf20Sopenharmony_ci monreader_device->release = (void (*)(struct device *))kfree; 5848c2ecf20Sopenharmony_ci rc = device_register(monreader_device); 5858c2ecf20Sopenharmony_ci if (rc) { 5868c2ecf20Sopenharmony_ci put_device(monreader_device); 5878c2ecf20Sopenharmony_ci goto out_driver; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci rc = segment_type(mon_dcss_name); 5918c2ecf20Sopenharmony_ci if (rc < 0) { 5928c2ecf20Sopenharmony_ci segment_warning(rc, mon_dcss_name); 5938c2ecf20Sopenharmony_ci goto out_device; 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci if (rc != SEG_TYPE_SC) { 5968c2ecf20Sopenharmony_ci pr_err("The specified *MONITOR DCSS %s does not have the " 5978c2ecf20Sopenharmony_ci "required type SC\n", mon_dcss_name); 5988c2ecf20Sopenharmony_ci rc = -EINVAL; 5998c2ecf20Sopenharmony_ci goto out_device; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci rc = segment_load(mon_dcss_name, SEGMENT_SHARED, 6038c2ecf20Sopenharmony_ci &mon_dcss_start, &mon_dcss_end); 6048c2ecf20Sopenharmony_ci if (rc < 0) { 6058c2ecf20Sopenharmony_ci segment_warning(rc, mon_dcss_name); 6068c2ecf20Sopenharmony_ci rc = -EINVAL; 6078c2ecf20Sopenharmony_ci goto out_device; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci dcss_mkname(mon_dcss_name, &user_data_connect[8]); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci /* 6128c2ecf20Sopenharmony_ci * misc_register() has to be the last action in module_init(), because 6138c2ecf20Sopenharmony_ci * file operations will be available right after this. 6148c2ecf20Sopenharmony_ci */ 6158c2ecf20Sopenharmony_ci rc = misc_register(&mon_dev); 6168c2ecf20Sopenharmony_ci if (rc < 0 ) 6178c2ecf20Sopenharmony_ci goto out; 6188c2ecf20Sopenharmony_ci return 0; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ciout: 6218c2ecf20Sopenharmony_ci segment_unload(mon_dcss_name); 6228c2ecf20Sopenharmony_ciout_device: 6238c2ecf20Sopenharmony_ci device_unregister(monreader_device); 6248c2ecf20Sopenharmony_ciout_driver: 6258c2ecf20Sopenharmony_ci driver_unregister(&monreader_driver); 6268c2ecf20Sopenharmony_ciout_iucv: 6278c2ecf20Sopenharmony_ci iucv_unregister(&monreader_iucv_handler, 1); 6288c2ecf20Sopenharmony_ci return rc; 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_cistatic void __exit mon_exit(void) 6328c2ecf20Sopenharmony_ci{ 6338c2ecf20Sopenharmony_ci segment_unload(mon_dcss_name); 6348c2ecf20Sopenharmony_ci misc_deregister(&mon_dev); 6358c2ecf20Sopenharmony_ci device_unregister(monreader_device); 6368c2ecf20Sopenharmony_ci driver_unregister(&monreader_driver); 6378c2ecf20Sopenharmony_ci iucv_unregister(&monreader_iucv_handler, 1); 6388c2ecf20Sopenharmony_ci return; 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_cimodule_init(mon_init); 6438c2ecf20Sopenharmony_cimodule_exit(mon_exit); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cimodule_param_string(mondcss, mon_dcss_name, 9, 0444); 6468c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mondcss, "Name of DCSS segment to be used for *MONITOR " 6478c2ecf20Sopenharmony_ci "service, max. 8 chars. Default is MONDCSS"); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ciMODULE_AUTHOR("Gerald Schaefer <geraldsc@de.ibm.com>"); 6508c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Character device driver for reading z/VM " 6518c2ecf20Sopenharmony_ci "monitor service records."); 6528c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 653