18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * character device driver for reading z/VM system service records 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2004, 2009 78c2ecf20Sopenharmony_ci * character device driver for reading z/VM system service records, 88c2ecf20Sopenharmony_ci * Version 1.0 98c2ecf20Sopenharmony_ci * Author(s): Xenia Tkatschow <xenia@us.ibm.com> 108c2ecf20Sopenharmony_ci * Stefan Weinhuber <wein@de.ibm.com> 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "vmlogrdr" 158c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/init.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/errno.h> 218c2ecf20Sopenharmony_ci#include <linux/types.h> 228c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 238c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 248c2ecf20Sopenharmony_ci#include <linux/atomic.h> 258c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 268c2ecf20Sopenharmony_ci#include <asm/cpcmd.h> 278c2ecf20Sopenharmony_ci#include <asm/debug.h> 288c2ecf20Sopenharmony_ci#include <asm/ebcdic.h> 298c2ecf20Sopenharmony_ci#include <net/iucv/iucv.h> 308c2ecf20Sopenharmony_ci#include <linux/kmod.h> 318c2ecf20Sopenharmony_ci#include <linux/cdev.h> 328c2ecf20Sopenharmony_ci#include <linux/device.h> 338c2ecf20Sopenharmony_ci#include <linux/string.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ciMODULE_AUTHOR 368c2ecf20Sopenharmony_ci ("(C) 2004 IBM Corporation by Xenia Tkatschow (xenia@us.ibm.com)\n" 378c2ecf20Sopenharmony_ci " Stefan Weinhuber (wein@de.ibm.com)"); 388c2ecf20Sopenharmony_ciMODULE_DESCRIPTION ("Character device driver for reading z/VM " 398c2ecf20Sopenharmony_ci "system service records."); 408c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* 448c2ecf20Sopenharmony_ci * The size of the buffer for iucv data transfer is one page, 458c2ecf20Sopenharmony_ci * but in addition to the data we read from iucv we also 468c2ecf20Sopenharmony_ci * place an integer and some characters into that buffer, 478c2ecf20Sopenharmony_ci * so the maximum size for record data is a little less then 488c2ecf20Sopenharmony_ci * one page. 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci#define NET_BUFFER_SIZE (PAGE_SIZE - sizeof(int) - sizeof(FENCE)) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* 538c2ecf20Sopenharmony_ci * The elements that are concurrently accessed by bottom halves are 548c2ecf20Sopenharmony_ci * connection_established, iucv_path_severed, local_interrupt_buffer 558c2ecf20Sopenharmony_ci * and receive_ready. The first three can be protected by 568c2ecf20Sopenharmony_ci * priv_lock. receive_ready is atomic, so it can be incremented and 578c2ecf20Sopenharmony_ci * decremented without holding a lock. 588c2ecf20Sopenharmony_ci * The variable dev_in_use needs to be protected by the lock, since 598c2ecf20Sopenharmony_ci * it's a flag used by open to make sure that the device is opened only 608c2ecf20Sopenharmony_ci * by one user at the same time. 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_cistruct vmlogrdr_priv_t { 638c2ecf20Sopenharmony_ci char system_service[8]; 648c2ecf20Sopenharmony_ci char internal_name[8]; 658c2ecf20Sopenharmony_ci char recording_name[8]; 668c2ecf20Sopenharmony_ci struct iucv_path *path; 678c2ecf20Sopenharmony_ci int connection_established; 688c2ecf20Sopenharmony_ci int iucv_path_severed; 698c2ecf20Sopenharmony_ci struct iucv_message local_interrupt_buffer; 708c2ecf20Sopenharmony_ci atomic_t receive_ready; 718c2ecf20Sopenharmony_ci int minor_num; 728c2ecf20Sopenharmony_ci char * buffer; 738c2ecf20Sopenharmony_ci char * current_position; 748c2ecf20Sopenharmony_ci int remaining; 758c2ecf20Sopenharmony_ci ulong residual_length; 768c2ecf20Sopenharmony_ci int buffer_free; 778c2ecf20Sopenharmony_ci int dev_in_use; /* 1: already opened, 0: not opened*/ 788c2ecf20Sopenharmony_ci spinlock_t priv_lock; 798c2ecf20Sopenharmony_ci struct device *device; 808c2ecf20Sopenharmony_ci struct device *class_device; 818c2ecf20Sopenharmony_ci int autorecording; 828c2ecf20Sopenharmony_ci int autopurge; 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* 878c2ecf20Sopenharmony_ci * File operation structure for vmlogrdr devices 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_cistatic int vmlogrdr_open(struct inode *, struct file *); 908c2ecf20Sopenharmony_cistatic int vmlogrdr_release(struct inode *, struct file *); 918c2ecf20Sopenharmony_cistatic ssize_t vmlogrdr_read (struct file *filp, char __user *data, 928c2ecf20Sopenharmony_ci size_t count, loff_t * ppos); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic const struct file_operations vmlogrdr_fops = { 958c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 968c2ecf20Sopenharmony_ci .open = vmlogrdr_open, 978c2ecf20Sopenharmony_ci .release = vmlogrdr_release, 988c2ecf20Sopenharmony_ci .read = vmlogrdr_read, 998c2ecf20Sopenharmony_ci .llseek = no_llseek, 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic void vmlogrdr_iucv_path_complete(struct iucv_path *, u8 *ipuser); 1048c2ecf20Sopenharmony_cistatic void vmlogrdr_iucv_path_severed(struct iucv_path *, u8 *ipuser); 1058c2ecf20Sopenharmony_cistatic void vmlogrdr_iucv_message_pending(struct iucv_path *, 1068c2ecf20Sopenharmony_ci struct iucv_message *); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic struct iucv_handler vmlogrdr_iucv_handler = { 1108c2ecf20Sopenharmony_ci .path_complete = vmlogrdr_iucv_path_complete, 1118c2ecf20Sopenharmony_ci .path_severed = vmlogrdr_iucv_path_severed, 1128c2ecf20Sopenharmony_ci .message_pending = vmlogrdr_iucv_message_pending, 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(conn_wait_queue); 1178c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(read_wait_queue); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/* 1208c2ecf20Sopenharmony_ci * pointer to system service private structure 1218c2ecf20Sopenharmony_ci * minor number 0 --> logrec 1228c2ecf20Sopenharmony_ci * minor number 1 --> account 1238c2ecf20Sopenharmony_ci * minor number 2 --> symptom 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic struct vmlogrdr_priv_t sys_ser[] = { 1278c2ecf20Sopenharmony_ci { .system_service = "*LOGREC ", 1288c2ecf20Sopenharmony_ci .internal_name = "logrec", 1298c2ecf20Sopenharmony_ci .recording_name = "EREP", 1308c2ecf20Sopenharmony_ci .minor_num = 0, 1318c2ecf20Sopenharmony_ci .buffer_free = 1, 1328c2ecf20Sopenharmony_ci .priv_lock = __SPIN_LOCK_UNLOCKED(sys_ser[0].priv_lock), 1338c2ecf20Sopenharmony_ci .autorecording = 1, 1348c2ecf20Sopenharmony_ci .autopurge = 1, 1358c2ecf20Sopenharmony_ci }, 1368c2ecf20Sopenharmony_ci { .system_service = "*ACCOUNT", 1378c2ecf20Sopenharmony_ci .internal_name = "account", 1388c2ecf20Sopenharmony_ci .recording_name = "ACCOUNT", 1398c2ecf20Sopenharmony_ci .minor_num = 1, 1408c2ecf20Sopenharmony_ci .buffer_free = 1, 1418c2ecf20Sopenharmony_ci .priv_lock = __SPIN_LOCK_UNLOCKED(sys_ser[1].priv_lock), 1428c2ecf20Sopenharmony_ci .autorecording = 1, 1438c2ecf20Sopenharmony_ci .autopurge = 1, 1448c2ecf20Sopenharmony_ci }, 1458c2ecf20Sopenharmony_ci { .system_service = "*SYMPTOM", 1468c2ecf20Sopenharmony_ci .internal_name = "symptom", 1478c2ecf20Sopenharmony_ci .recording_name = "SYMPTOM", 1488c2ecf20Sopenharmony_ci .minor_num = 2, 1498c2ecf20Sopenharmony_ci .buffer_free = 1, 1508c2ecf20Sopenharmony_ci .priv_lock = __SPIN_LOCK_UNLOCKED(sys_ser[2].priv_lock), 1518c2ecf20Sopenharmony_ci .autorecording = 1, 1528c2ecf20Sopenharmony_ci .autopurge = 1, 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci}; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci#define MAXMINOR ARRAY_SIZE(sys_ser) 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic char FENCE[] = {"EOR"}; 1598c2ecf20Sopenharmony_cistatic int vmlogrdr_major = 0; 1608c2ecf20Sopenharmony_cistatic struct cdev *vmlogrdr_cdev = NULL; 1618c2ecf20Sopenharmony_cistatic int recording_class_AB; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic void vmlogrdr_iucv_path_complete(struct iucv_path *path, u8 *ipuser) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct vmlogrdr_priv_t * logptr = path->private; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci spin_lock(&logptr->priv_lock); 1698c2ecf20Sopenharmony_ci logptr->connection_established = 1; 1708c2ecf20Sopenharmony_ci spin_unlock(&logptr->priv_lock); 1718c2ecf20Sopenharmony_ci wake_up(&conn_wait_queue); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic void vmlogrdr_iucv_path_severed(struct iucv_path *path, u8 *ipuser) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct vmlogrdr_priv_t * logptr = path->private; 1788c2ecf20Sopenharmony_ci u8 reason = (u8) ipuser[8]; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci pr_err("vmlogrdr: connection severed with reason %i\n", reason); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci iucv_path_sever(path, NULL); 1838c2ecf20Sopenharmony_ci kfree(path); 1848c2ecf20Sopenharmony_ci logptr->path = NULL; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci spin_lock(&logptr->priv_lock); 1878c2ecf20Sopenharmony_ci logptr->connection_established = 0; 1888c2ecf20Sopenharmony_ci logptr->iucv_path_severed = 1; 1898c2ecf20Sopenharmony_ci spin_unlock(&logptr->priv_lock); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci wake_up(&conn_wait_queue); 1928c2ecf20Sopenharmony_ci /* just in case we're sleeping waiting for a record */ 1938c2ecf20Sopenharmony_ci wake_up_interruptible(&read_wait_queue); 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic void vmlogrdr_iucv_message_pending(struct iucv_path *path, 1988c2ecf20Sopenharmony_ci struct iucv_message *msg) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct vmlogrdr_priv_t * logptr = path->private; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* 2038c2ecf20Sopenharmony_ci * This function is the bottom half so it should be quick. 2048c2ecf20Sopenharmony_ci * Copy the external interrupt data into our local eib and increment 2058c2ecf20Sopenharmony_ci * the usage count 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_ci spin_lock(&logptr->priv_lock); 2088c2ecf20Sopenharmony_ci memcpy(&logptr->local_interrupt_buffer, msg, sizeof(*msg)); 2098c2ecf20Sopenharmony_ci atomic_inc(&logptr->receive_ready); 2108c2ecf20Sopenharmony_ci spin_unlock(&logptr->priv_lock); 2118c2ecf20Sopenharmony_ci wake_up_interruptible(&read_wait_queue); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int vmlogrdr_get_recording_class_AB(void) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci static const char cp_command[] = "QUERY COMMAND RECORDING "; 2188c2ecf20Sopenharmony_ci char cp_response[80]; 2198c2ecf20Sopenharmony_ci char *tail; 2208c2ecf20Sopenharmony_ci int len,i; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci cpcmd(cp_command, cp_response, sizeof(cp_response), NULL); 2238c2ecf20Sopenharmony_ci len = strnlen(cp_response,sizeof(cp_response)); 2248c2ecf20Sopenharmony_ci // now the parsing 2258c2ecf20Sopenharmony_ci tail=strnchr(cp_response,len,'='); 2268c2ecf20Sopenharmony_ci if (!tail) 2278c2ecf20Sopenharmony_ci return 0; 2288c2ecf20Sopenharmony_ci tail++; 2298c2ecf20Sopenharmony_ci if (!strncmp("ANY",tail,3)) 2308c2ecf20Sopenharmony_ci return 1; 2318c2ecf20Sopenharmony_ci if (!strncmp("NONE",tail,4)) 2328c2ecf20Sopenharmony_ci return 0; 2338c2ecf20Sopenharmony_ci /* 2348c2ecf20Sopenharmony_ci * expect comma separated list of classes here, if one of them 2358c2ecf20Sopenharmony_ci * is A or B return 1 otherwise 0 2368c2ecf20Sopenharmony_ci */ 2378c2ecf20Sopenharmony_ci for (i=tail-cp_response; i<len; i++) 2388c2ecf20Sopenharmony_ci if ( cp_response[i]=='A' || cp_response[i]=='B' ) 2398c2ecf20Sopenharmony_ci return 1; 2408c2ecf20Sopenharmony_ci return 0; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic int vmlogrdr_recording(struct vmlogrdr_priv_t * logptr, 2458c2ecf20Sopenharmony_ci int action, int purge) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci char cp_command[80]; 2498c2ecf20Sopenharmony_ci char cp_response[160]; 2508c2ecf20Sopenharmony_ci char *onoff, *qid_string; 2518c2ecf20Sopenharmony_ci int rc; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci onoff = ((action == 1) ? "ON" : "OFF"); 2548c2ecf20Sopenharmony_ci qid_string = ((recording_class_AB == 1) ? " QID * " : ""); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* 2578c2ecf20Sopenharmony_ci * The recording commands needs to be called with option QID 2588c2ecf20Sopenharmony_ci * for guests that have previlege classes A or B. 2598c2ecf20Sopenharmony_ci * Purging has to be done as separate step, because recording 2608c2ecf20Sopenharmony_ci * can't be switched on as long as records are on the queue. 2618c2ecf20Sopenharmony_ci * Doing both at the same time doesn't work. 2628c2ecf20Sopenharmony_ci */ 2638c2ecf20Sopenharmony_ci if (purge && (action == 1)) { 2648c2ecf20Sopenharmony_ci memset(cp_command, 0x00, sizeof(cp_command)); 2658c2ecf20Sopenharmony_ci memset(cp_response, 0x00, sizeof(cp_response)); 2668c2ecf20Sopenharmony_ci snprintf(cp_command, sizeof(cp_command), 2678c2ecf20Sopenharmony_ci "RECORDING %s PURGE %s", 2688c2ecf20Sopenharmony_ci logptr->recording_name, 2698c2ecf20Sopenharmony_ci qid_string); 2708c2ecf20Sopenharmony_ci cpcmd(cp_command, cp_response, sizeof(cp_response), NULL); 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci memset(cp_command, 0x00, sizeof(cp_command)); 2748c2ecf20Sopenharmony_ci memset(cp_response, 0x00, sizeof(cp_response)); 2758c2ecf20Sopenharmony_ci snprintf(cp_command, sizeof(cp_command), "RECORDING %s %s %s", 2768c2ecf20Sopenharmony_ci logptr->recording_name, 2778c2ecf20Sopenharmony_ci onoff, 2788c2ecf20Sopenharmony_ci qid_string); 2798c2ecf20Sopenharmony_ci cpcmd(cp_command, cp_response, sizeof(cp_response), NULL); 2808c2ecf20Sopenharmony_ci /* The recording command will usually answer with 'Command complete' 2818c2ecf20Sopenharmony_ci * on success, but when the specific service was never connected 2828c2ecf20Sopenharmony_ci * before then there might be an additional informational message 2838c2ecf20Sopenharmony_ci * 'HCPCRC8072I Recording entry not found' before the 2848c2ecf20Sopenharmony_ci * 'Command complete'. So I use strstr rather then the strncmp. 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_ci if (strstr(cp_response,"Command complete")) 2878c2ecf20Sopenharmony_ci rc = 0; 2888c2ecf20Sopenharmony_ci else 2898c2ecf20Sopenharmony_ci rc = -EIO; 2908c2ecf20Sopenharmony_ci /* 2918c2ecf20Sopenharmony_ci * If we turn recording off, we have to purge any remaining records 2928c2ecf20Sopenharmony_ci * afterwards, as a large number of queued records may impact z/VM 2938c2ecf20Sopenharmony_ci * performance. 2948c2ecf20Sopenharmony_ci */ 2958c2ecf20Sopenharmony_ci if (purge && (action == 0)) { 2968c2ecf20Sopenharmony_ci memset(cp_command, 0x00, sizeof(cp_command)); 2978c2ecf20Sopenharmony_ci memset(cp_response, 0x00, sizeof(cp_response)); 2988c2ecf20Sopenharmony_ci snprintf(cp_command, sizeof(cp_command), 2998c2ecf20Sopenharmony_ci "RECORDING %s PURGE %s", 3008c2ecf20Sopenharmony_ci logptr->recording_name, 3018c2ecf20Sopenharmony_ci qid_string); 3028c2ecf20Sopenharmony_ci cpcmd(cp_command, cp_response, sizeof(cp_response), NULL); 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci return rc; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic int vmlogrdr_open (struct inode *inode, struct file *filp) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci int dev_num = 0; 3128c2ecf20Sopenharmony_ci struct vmlogrdr_priv_t * logptr = NULL; 3138c2ecf20Sopenharmony_ci int connect_rc = 0; 3148c2ecf20Sopenharmony_ci int ret; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci dev_num = iminor(inode); 3178c2ecf20Sopenharmony_ci if (dev_num >= MAXMINOR) 3188c2ecf20Sopenharmony_ci return -ENODEV; 3198c2ecf20Sopenharmony_ci logptr = &sys_ser[dev_num]; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* 3228c2ecf20Sopenharmony_ci * only allow for blocking reads to be open 3238c2ecf20Sopenharmony_ci */ 3248c2ecf20Sopenharmony_ci if (filp->f_flags & O_NONBLOCK) 3258c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* Besure this device hasn't already been opened */ 3288c2ecf20Sopenharmony_ci spin_lock_bh(&logptr->priv_lock); 3298c2ecf20Sopenharmony_ci if (logptr->dev_in_use) { 3308c2ecf20Sopenharmony_ci spin_unlock_bh(&logptr->priv_lock); 3318c2ecf20Sopenharmony_ci return -EBUSY; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci logptr->dev_in_use = 1; 3348c2ecf20Sopenharmony_ci logptr->connection_established = 0; 3358c2ecf20Sopenharmony_ci logptr->iucv_path_severed = 0; 3368c2ecf20Sopenharmony_ci atomic_set(&logptr->receive_ready, 0); 3378c2ecf20Sopenharmony_ci logptr->buffer_free = 1; 3388c2ecf20Sopenharmony_ci spin_unlock_bh(&logptr->priv_lock); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* set the file options */ 3418c2ecf20Sopenharmony_ci filp->private_data = logptr; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* start recording for this service*/ 3448c2ecf20Sopenharmony_ci if (logptr->autorecording) { 3458c2ecf20Sopenharmony_ci ret = vmlogrdr_recording(logptr,1,logptr->autopurge); 3468c2ecf20Sopenharmony_ci if (ret) 3478c2ecf20Sopenharmony_ci pr_warn("vmlogrdr: failed to start recording automatically\n"); 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* create connection to the system service */ 3518c2ecf20Sopenharmony_ci logptr->path = iucv_path_alloc(10, 0, GFP_KERNEL); 3528c2ecf20Sopenharmony_ci if (!logptr->path) 3538c2ecf20Sopenharmony_ci goto out_dev; 3548c2ecf20Sopenharmony_ci connect_rc = iucv_path_connect(logptr->path, &vmlogrdr_iucv_handler, 3558c2ecf20Sopenharmony_ci logptr->system_service, NULL, NULL, 3568c2ecf20Sopenharmony_ci logptr); 3578c2ecf20Sopenharmony_ci if (connect_rc) { 3588c2ecf20Sopenharmony_ci pr_err("vmlogrdr: iucv connection to %s " 3598c2ecf20Sopenharmony_ci "failed with rc %i \n", 3608c2ecf20Sopenharmony_ci logptr->system_service, connect_rc); 3618c2ecf20Sopenharmony_ci goto out_path; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* We've issued the connect and now we must wait for a 3658c2ecf20Sopenharmony_ci * ConnectionComplete or ConnectinSevered Interrupt 3668c2ecf20Sopenharmony_ci * before we can continue to process. 3678c2ecf20Sopenharmony_ci */ 3688c2ecf20Sopenharmony_ci wait_event(conn_wait_queue, (logptr->connection_established) 3698c2ecf20Sopenharmony_ci || (logptr->iucv_path_severed)); 3708c2ecf20Sopenharmony_ci if (logptr->iucv_path_severed) 3718c2ecf20Sopenharmony_ci goto out_record; 3728c2ecf20Sopenharmony_ci nonseekable_open(inode, filp); 3738c2ecf20Sopenharmony_ci return 0; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ciout_record: 3768c2ecf20Sopenharmony_ci if (logptr->autorecording) 3778c2ecf20Sopenharmony_ci vmlogrdr_recording(logptr,0,logptr->autopurge); 3788c2ecf20Sopenharmony_ciout_path: 3798c2ecf20Sopenharmony_ci kfree(logptr->path); /* kfree(NULL) is ok. */ 3808c2ecf20Sopenharmony_ci logptr->path = NULL; 3818c2ecf20Sopenharmony_ciout_dev: 3828c2ecf20Sopenharmony_ci logptr->dev_in_use = 0; 3838c2ecf20Sopenharmony_ci return -EIO; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic int vmlogrdr_release (struct inode *inode, struct file *filp) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci int ret; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci struct vmlogrdr_priv_t * logptr = filp->private_data; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci iucv_path_sever(logptr->path, NULL); 3948c2ecf20Sopenharmony_ci kfree(logptr->path); 3958c2ecf20Sopenharmony_ci logptr->path = NULL; 3968c2ecf20Sopenharmony_ci if (logptr->autorecording) { 3978c2ecf20Sopenharmony_ci ret = vmlogrdr_recording(logptr,0,logptr->autopurge); 3988c2ecf20Sopenharmony_ci if (ret) 3998c2ecf20Sopenharmony_ci pr_warn("vmlogrdr: failed to stop recording automatically\n"); 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci logptr->dev_in_use = 0; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci return 0; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic int vmlogrdr_receive_data(struct vmlogrdr_priv_t *priv) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci int rc, *temp; 4108c2ecf20Sopenharmony_ci /* we need to keep track of two data sizes here: 4118c2ecf20Sopenharmony_ci * The number of bytes we need to receive from iucv and 4128c2ecf20Sopenharmony_ci * the total number of bytes we actually write into the buffer. 4138c2ecf20Sopenharmony_ci */ 4148c2ecf20Sopenharmony_ci int user_data_count, iucv_data_count; 4158c2ecf20Sopenharmony_ci char * buffer; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (atomic_read(&priv->receive_ready)) { 4188c2ecf20Sopenharmony_ci spin_lock_bh(&priv->priv_lock); 4198c2ecf20Sopenharmony_ci if (priv->residual_length){ 4208c2ecf20Sopenharmony_ci /* receive second half of a record */ 4218c2ecf20Sopenharmony_ci iucv_data_count = priv->residual_length; 4228c2ecf20Sopenharmony_ci user_data_count = 0; 4238c2ecf20Sopenharmony_ci buffer = priv->buffer; 4248c2ecf20Sopenharmony_ci } else { 4258c2ecf20Sopenharmony_ci /* receive a new record: 4268c2ecf20Sopenharmony_ci * We need to return the total length of the record 4278c2ecf20Sopenharmony_ci * + size of FENCE in the first 4 bytes of the buffer. 4288c2ecf20Sopenharmony_ci */ 4298c2ecf20Sopenharmony_ci iucv_data_count = priv->local_interrupt_buffer.length; 4308c2ecf20Sopenharmony_ci user_data_count = sizeof(int); 4318c2ecf20Sopenharmony_ci temp = (int*)priv->buffer; 4328c2ecf20Sopenharmony_ci *temp= iucv_data_count + sizeof(FENCE); 4338c2ecf20Sopenharmony_ci buffer = priv->buffer + sizeof(int); 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci /* 4368c2ecf20Sopenharmony_ci * If the record is bigger than our buffer, we receive only 4378c2ecf20Sopenharmony_ci * a part of it. We can get the rest later. 4388c2ecf20Sopenharmony_ci */ 4398c2ecf20Sopenharmony_ci if (iucv_data_count > NET_BUFFER_SIZE) 4408c2ecf20Sopenharmony_ci iucv_data_count = NET_BUFFER_SIZE; 4418c2ecf20Sopenharmony_ci rc = iucv_message_receive(priv->path, 4428c2ecf20Sopenharmony_ci &priv->local_interrupt_buffer, 4438c2ecf20Sopenharmony_ci 0, buffer, iucv_data_count, 4448c2ecf20Sopenharmony_ci &priv->residual_length); 4458c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->priv_lock); 4468c2ecf20Sopenharmony_ci /* An rc of 5 indicates that the record was bigger than 4478c2ecf20Sopenharmony_ci * the buffer, which is OK for us. A 9 indicates that the 4488c2ecf20Sopenharmony_ci * record was purged befor we could receive it. 4498c2ecf20Sopenharmony_ci */ 4508c2ecf20Sopenharmony_ci if (rc == 5) 4518c2ecf20Sopenharmony_ci rc = 0; 4528c2ecf20Sopenharmony_ci if (rc == 9) 4538c2ecf20Sopenharmony_ci atomic_set(&priv->receive_ready, 0); 4548c2ecf20Sopenharmony_ci } else { 4558c2ecf20Sopenharmony_ci rc = 1; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci if (!rc) { 4588c2ecf20Sopenharmony_ci priv->buffer_free = 0; 4598c2ecf20Sopenharmony_ci user_data_count += iucv_data_count; 4608c2ecf20Sopenharmony_ci priv->current_position = priv->buffer; 4618c2ecf20Sopenharmony_ci if (priv->residual_length == 0){ 4628c2ecf20Sopenharmony_ci /* the whole record has been captured, 4638c2ecf20Sopenharmony_ci * now add the fence */ 4648c2ecf20Sopenharmony_ci atomic_dec(&priv->receive_ready); 4658c2ecf20Sopenharmony_ci buffer = priv->buffer + user_data_count; 4668c2ecf20Sopenharmony_ci memcpy(buffer, FENCE, sizeof(FENCE)); 4678c2ecf20Sopenharmony_ci user_data_count += sizeof(FENCE); 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci priv->remaining = user_data_count; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci return rc; 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic ssize_t vmlogrdr_read(struct file *filp, char __user *data, 4778c2ecf20Sopenharmony_ci size_t count, loff_t * ppos) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci int rc; 4808c2ecf20Sopenharmony_ci struct vmlogrdr_priv_t * priv = filp->private_data; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci while (priv->buffer_free) { 4838c2ecf20Sopenharmony_ci rc = vmlogrdr_receive_data(priv); 4848c2ecf20Sopenharmony_ci if (rc) { 4858c2ecf20Sopenharmony_ci rc = wait_event_interruptible(read_wait_queue, 4868c2ecf20Sopenharmony_ci atomic_read(&priv->receive_ready)); 4878c2ecf20Sopenharmony_ci if (rc) 4888c2ecf20Sopenharmony_ci return rc; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci /* copy only up to end of record */ 4928c2ecf20Sopenharmony_ci if (count > priv->remaining) 4938c2ecf20Sopenharmony_ci count = priv->remaining; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (copy_to_user(data, priv->current_position, count)) 4968c2ecf20Sopenharmony_ci return -EFAULT; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci *ppos += count; 4998c2ecf20Sopenharmony_ci priv->current_position += count; 5008c2ecf20Sopenharmony_ci priv->remaining -= count; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* if all data has been transferred, set buffer free */ 5038c2ecf20Sopenharmony_ci if (priv->remaining == 0) 5048c2ecf20Sopenharmony_ci priv->buffer_free = 1; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci return count; 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_cistatic ssize_t vmlogrdr_autopurge_store(struct device * dev, 5108c2ecf20Sopenharmony_ci struct device_attribute *attr, 5118c2ecf20Sopenharmony_ci const char * buf, size_t count) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev); 5148c2ecf20Sopenharmony_ci ssize_t ret = count; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci switch (buf[0]) { 5178c2ecf20Sopenharmony_ci case '0': 5188c2ecf20Sopenharmony_ci priv->autopurge=0; 5198c2ecf20Sopenharmony_ci break; 5208c2ecf20Sopenharmony_ci case '1': 5218c2ecf20Sopenharmony_ci priv->autopurge=1; 5228c2ecf20Sopenharmony_ci break; 5238c2ecf20Sopenharmony_ci default: 5248c2ecf20Sopenharmony_ci ret = -EINVAL; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci return ret; 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_cistatic ssize_t vmlogrdr_autopurge_show(struct device *dev, 5318c2ecf20Sopenharmony_ci struct device_attribute *attr, 5328c2ecf20Sopenharmony_ci char *buf) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev); 5358c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", priv->autopurge); 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic DEVICE_ATTR(autopurge, 0644, vmlogrdr_autopurge_show, 5408c2ecf20Sopenharmony_ci vmlogrdr_autopurge_store); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic ssize_t vmlogrdr_purge_store(struct device * dev, 5448c2ecf20Sopenharmony_ci struct device_attribute *attr, 5458c2ecf20Sopenharmony_ci const char * buf, size_t count) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci char cp_command[80]; 5498c2ecf20Sopenharmony_ci char cp_response[80]; 5508c2ecf20Sopenharmony_ci struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci if (buf[0] != '1') 5538c2ecf20Sopenharmony_ci return -EINVAL; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci memset(cp_command, 0x00, sizeof(cp_command)); 5568c2ecf20Sopenharmony_ci memset(cp_response, 0x00, sizeof(cp_response)); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci /* 5598c2ecf20Sopenharmony_ci * The recording command needs to be called with option QID 5608c2ecf20Sopenharmony_ci * for guests that have previlege classes A or B. 5618c2ecf20Sopenharmony_ci * Other guests will not recognize the command and we have to 5628c2ecf20Sopenharmony_ci * issue the same command without the QID parameter. 5638c2ecf20Sopenharmony_ci */ 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci if (recording_class_AB) 5668c2ecf20Sopenharmony_ci snprintf(cp_command, sizeof(cp_command), 5678c2ecf20Sopenharmony_ci "RECORDING %s PURGE QID * ", 5688c2ecf20Sopenharmony_ci priv->recording_name); 5698c2ecf20Sopenharmony_ci else 5708c2ecf20Sopenharmony_ci snprintf(cp_command, sizeof(cp_command), 5718c2ecf20Sopenharmony_ci "RECORDING %s PURGE ", 5728c2ecf20Sopenharmony_ci priv->recording_name); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci cpcmd(cp_command, cp_response, sizeof(cp_response), NULL); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci return count; 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic DEVICE_ATTR(purge, 0200, NULL, vmlogrdr_purge_store); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic ssize_t vmlogrdr_autorecording_store(struct device *dev, 5848c2ecf20Sopenharmony_ci struct device_attribute *attr, 5858c2ecf20Sopenharmony_ci const char *buf, size_t count) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev); 5888c2ecf20Sopenharmony_ci ssize_t ret = count; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci switch (buf[0]) { 5918c2ecf20Sopenharmony_ci case '0': 5928c2ecf20Sopenharmony_ci priv->autorecording=0; 5938c2ecf20Sopenharmony_ci break; 5948c2ecf20Sopenharmony_ci case '1': 5958c2ecf20Sopenharmony_ci priv->autorecording=1; 5968c2ecf20Sopenharmony_ci break; 5978c2ecf20Sopenharmony_ci default: 5988c2ecf20Sopenharmony_ci ret = -EINVAL; 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci return ret; 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_cistatic ssize_t vmlogrdr_autorecording_show(struct device *dev, 6058c2ecf20Sopenharmony_ci struct device_attribute *attr, 6068c2ecf20Sopenharmony_ci char *buf) 6078c2ecf20Sopenharmony_ci{ 6088c2ecf20Sopenharmony_ci struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev); 6098c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", priv->autorecording); 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_cistatic DEVICE_ATTR(autorecording, 0644, vmlogrdr_autorecording_show, 6148c2ecf20Sopenharmony_ci vmlogrdr_autorecording_store); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_cistatic ssize_t vmlogrdr_recording_store(struct device * dev, 6188c2ecf20Sopenharmony_ci struct device_attribute *attr, 6198c2ecf20Sopenharmony_ci const char * buf, size_t count) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev); 6228c2ecf20Sopenharmony_ci ssize_t ret; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci switch (buf[0]) { 6258c2ecf20Sopenharmony_ci case '0': 6268c2ecf20Sopenharmony_ci ret = vmlogrdr_recording(priv,0,0); 6278c2ecf20Sopenharmony_ci break; 6288c2ecf20Sopenharmony_ci case '1': 6298c2ecf20Sopenharmony_ci ret = vmlogrdr_recording(priv,1,0); 6308c2ecf20Sopenharmony_ci break; 6318c2ecf20Sopenharmony_ci default: 6328c2ecf20Sopenharmony_ci ret = -EINVAL; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci if (ret) 6358c2ecf20Sopenharmony_ci return ret; 6368c2ecf20Sopenharmony_ci else 6378c2ecf20Sopenharmony_ci return count; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_cistatic DEVICE_ATTR(recording, 0200, NULL, vmlogrdr_recording_store); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic ssize_t recording_status_show(struct device_driver *driver, char *buf) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci static const char cp_command[] = "QUERY RECORDING "; 6488c2ecf20Sopenharmony_ci int len; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci cpcmd(cp_command, buf, 4096, NULL); 6518c2ecf20Sopenharmony_ci len = strlen(buf); 6528c2ecf20Sopenharmony_ci return len; 6538c2ecf20Sopenharmony_ci} 6548c2ecf20Sopenharmony_cistatic DRIVER_ATTR_RO(recording_status); 6558c2ecf20Sopenharmony_cistatic struct attribute *vmlogrdr_drv_attrs[] = { 6568c2ecf20Sopenharmony_ci &driver_attr_recording_status.attr, 6578c2ecf20Sopenharmony_ci NULL, 6588c2ecf20Sopenharmony_ci}; 6598c2ecf20Sopenharmony_cistatic struct attribute_group vmlogrdr_drv_attr_group = { 6608c2ecf20Sopenharmony_ci .attrs = vmlogrdr_drv_attrs, 6618c2ecf20Sopenharmony_ci}; 6628c2ecf20Sopenharmony_cistatic const struct attribute_group *vmlogrdr_drv_attr_groups[] = { 6638c2ecf20Sopenharmony_ci &vmlogrdr_drv_attr_group, 6648c2ecf20Sopenharmony_ci NULL, 6658c2ecf20Sopenharmony_ci}; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_cistatic struct attribute *vmlogrdr_attrs[] = { 6688c2ecf20Sopenharmony_ci &dev_attr_autopurge.attr, 6698c2ecf20Sopenharmony_ci &dev_attr_purge.attr, 6708c2ecf20Sopenharmony_ci &dev_attr_autorecording.attr, 6718c2ecf20Sopenharmony_ci &dev_attr_recording.attr, 6728c2ecf20Sopenharmony_ci NULL, 6738c2ecf20Sopenharmony_ci}; 6748c2ecf20Sopenharmony_cistatic struct attribute_group vmlogrdr_attr_group = { 6758c2ecf20Sopenharmony_ci .attrs = vmlogrdr_attrs, 6768c2ecf20Sopenharmony_ci}; 6778c2ecf20Sopenharmony_cistatic const struct attribute_group *vmlogrdr_attr_groups[] = { 6788c2ecf20Sopenharmony_ci &vmlogrdr_attr_group, 6798c2ecf20Sopenharmony_ci NULL, 6808c2ecf20Sopenharmony_ci}; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_cistatic int vmlogrdr_pm_prepare(struct device *dev) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci int rc; 6858c2ecf20Sopenharmony_ci struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci rc = 0; 6888c2ecf20Sopenharmony_ci if (priv) { 6898c2ecf20Sopenharmony_ci spin_lock_bh(&priv->priv_lock); 6908c2ecf20Sopenharmony_ci if (priv->dev_in_use) 6918c2ecf20Sopenharmony_ci rc = -EBUSY; 6928c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->priv_lock); 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci if (rc) 6958c2ecf20Sopenharmony_ci pr_err("vmlogrdr: device %s is busy. Refuse to suspend.\n", 6968c2ecf20Sopenharmony_ci dev_name(dev)); 6978c2ecf20Sopenharmony_ci return rc; 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_cistatic const struct dev_pm_ops vmlogrdr_pm_ops = { 7028c2ecf20Sopenharmony_ci .prepare = vmlogrdr_pm_prepare, 7038c2ecf20Sopenharmony_ci}; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_cistatic struct class *vmlogrdr_class; 7068c2ecf20Sopenharmony_cistatic struct device_driver vmlogrdr_driver = { 7078c2ecf20Sopenharmony_ci .name = "vmlogrdr", 7088c2ecf20Sopenharmony_ci .bus = &iucv_bus, 7098c2ecf20Sopenharmony_ci .pm = &vmlogrdr_pm_ops, 7108c2ecf20Sopenharmony_ci .groups = vmlogrdr_drv_attr_groups, 7118c2ecf20Sopenharmony_ci}; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_cistatic int vmlogrdr_register_driver(void) 7148c2ecf20Sopenharmony_ci{ 7158c2ecf20Sopenharmony_ci int ret; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci /* Register with iucv driver */ 7188c2ecf20Sopenharmony_ci ret = iucv_register(&vmlogrdr_iucv_handler, 1); 7198c2ecf20Sopenharmony_ci if (ret) 7208c2ecf20Sopenharmony_ci goto out; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci ret = driver_register(&vmlogrdr_driver); 7238c2ecf20Sopenharmony_ci if (ret) 7248c2ecf20Sopenharmony_ci goto out_iucv; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci vmlogrdr_class = class_create(THIS_MODULE, "vmlogrdr"); 7278c2ecf20Sopenharmony_ci if (IS_ERR(vmlogrdr_class)) { 7288c2ecf20Sopenharmony_ci ret = PTR_ERR(vmlogrdr_class); 7298c2ecf20Sopenharmony_ci vmlogrdr_class = NULL; 7308c2ecf20Sopenharmony_ci goto out_driver; 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci return 0; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ciout_driver: 7358c2ecf20Sopenharmony_ci driver_unregister(&vmlogrdr_driver); 7368c2ecf20Sopenharmony_ciout_iucv: 7378c2ecf20Sopenharmony_ci iucv_unregister(&vmlogrdr_iucv_handler, 1); 7388c2ecf20Sopenharmony_ciout: 7398c2ecf20Sopenharmony_ci return ret; 7408c2ecf20Sopenharmony_ci} 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_cistatic void vmlogrdr_unregister_driver(void) 7448c2ecf20Sopenharmony_ci{ 7458c2ecf20Sopenharmony_ci class_destroy(vmlogrdr_class); 7468c2ecf20Sopenharmony_ci vmlogrdr_class = NULL; 7478c2ecf20Sopenharmony_ci driver_unregister(&vmlogrdr_driver); 7488c2ecf20Sopenharmony_ci iucv_unregister(&vmlogrdr_iucv_handler, 1); 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_cistatic int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv) 7538c2ecf20Sopenharmony_ci{ 7548c2ecf20Sopenharmony_ci struct device *dev; 7558c2ecf20Sopenharmony_ci int ret; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(struct device), GFP_KERNEL); 7588c2ecf20Sopenharmony_ci if (dev) { 7598c2ecf20Sopenharmony_ci dev_set_name(dev, "%s", priv->internal_name); 7608c2ecf20Sopenharmony_ci dev->bus = &iucv_bus; 7618c2ecf20Sopenharmony_ci dev->parent = iucv_root; 7628c2ecf20Sopenharmony_ci dev->driver = &vmlogrdr_driver; 7638c2ecf20Sopenharmony_ci dev->groups = vmlogrdr_attr_groups; 7648c2ecf20Sopenharmony_ci dev_set_drvdata(dev, priv); 7658c2ecf20Sopenharmony_ci /* 7668c2ecf20Sopenharmony_ci * The release function could be called after the 7678c2ecf20Sopenharmony_ci * module has been unloaded. It's _only_ task is to 7688c2ecf20Sopenharmony_ci * free the struct. Therefore, we specify kfree() 7698c2ecf20Sopenharmony_ci * directly here. (Probably a little bit obfuscating 7708c2ecf20Sopenharmony_ci * but legitime ...). 7718c2ecf20Sopenharmony_ci */ 7728c2ecf20Sopenharmony_ci dev->release = (void (*)(struct device *))kfree; 7738c2ecf20Sopenharmony_ci } else 7748c2ecf20Sopenharmony_ci return -ENOMEM; 7758c2ecf20Sopenharmony_ci ret = device_register(dev); 7768c2ecf20Sopenharmony_ci if (ret) { 7778c2ecf20Sopenharmony_ci put_device(dev); 7788c2ecf20Sopenharmony_ci return ret; 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci priv->class_device = device_create(vmlogrdr_class, dev, 7828c2ecf20Sopenharmony_ci MKDEV(vmlogrdr_major, 7838c2ecf20Sopenharmony_ci priv->minor_num), 7848c2ecf20Sopenharmony_ci priv, "%s", dev_name(dev)); 7858c2ecf20Sopenharmony_ci if (IS_ERR(priv->class_device)) { 7868c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->class_device); 7878c2ecf20Sopenharmony_ci priv->class_device=NULL; 7888c2ecf20Sopenharmony_ci device_unregister(dev); 7898c2ecf20Sopenharmony_ci return ret; 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci priv->device = dev; 7928c2ecf20Sopenharmony_ci return 0; 7938c2ecf20Sopenharmony_ci} 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_cistatic int vmlogrdr_unregister_device(struct vmlogrdr_priv_t *priv) 7978c2ecf20Sopenharmony_ci{ 7988c2ecf20Sopenharmony_ci device_destroy(vmlogrdr_class, MKDEV(vmlogrdr_major, priv->minor_num)); 7998c2ecf20Sopenharmony_ci if (priv->device != NULL) { 8008c2ecf20Sopenharmony_ci device_unregister(priv->device); 8018c2ecf20Sopenharmony_ci priv->device=NULL; 8028c2ecf20Sopenharmony_ci } 8038c2ecf20Sopenharmony_ci return 0; 8048c2ecf20Sopenharmony_ci} 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_cistatic int vmlogrdr_register_cdev(dev_t dev) 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci int rc = 0; 8108c2ecf20Sopenharmony_ci vmlogrdr_cdev = cdev_alloc(); 8118c2ecf20Sopenharmony_ci if (!vmlogrdr_cdev) { 8128c2ecf20Sopenharmony_ci return -ENOMEM; 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ci vmlogrdr_cdev->owner = THIS_MODULE; 8158c2ecf20Sopenharmony_ci vmlogrdr_cdev->ops = &vmlogrdr_fops; 8168c2ecf20Sopenharmony_ci rc = cdev_add(vmlogrdr_cdev, dev, MAXMINOR); 8178c2ecf20Sopenharmony_ci if (!rc) 8188c2ecf20Sopenharmony_ci return 0; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci // cleanup: cdev is not fully registered, no cdev_del here! 8218c2ecf20Sopenharmony_ci kobject_put(&vmlogrdr_cdev->kobj); 8228c2ecf20Sopenharmony_ci vmlogrdr_cdev=NULL; 8238c2ecf20Sopenharmony_ci return rc; 8248c2ecf20Sopenharmony_ci} 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_cistatic void vmlogrdr_cleanup(void) 8288c2ecf20Sopenharmony_ci{ 8298c2ecf20Sopenharmony_ci int i; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci if (vmlogrdr_cdev) { 8328c2ecf20Sopenharmony_ci cdev_del(vmlogrdr_cdev); 8338c2ecf20Sopenharmony_ci vmlogrdr_cdev=NULL; 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci for (i=0; i < MAXMINOR; ++i ) { 8368c2ecf20Sopenharmony_ci vmlogrdr_unregister_device(&sys_ser[i]); 8378c2ecf20Sopenharmony_ci free_page((unsigned long)sys_ser[i].buffer); 8388c2ecf20Sopenharmony_ci } 8398c2ecf20Sopenharmony_ci vmlogrdr_unregister_driver(); 8408c2ecf20Sopenharmony_ci if (vmlogrdr_major) { 8418c2ecf20Sopenharmony_ci unregister_chrdev_region(MKDEV(vmlogrdr_major, 0), MAXMINOR); 8428c2ecf20Sopenharmony_ci vmlogrdr_major=0; 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci} 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_cistatic int __init vmlogrdr_init(void) 8488c2ecf20Sopenharmony_ci{ 8498c2ecf20Sopenharmony_ci int rc; 8508c2ecf20Sopenharmony_ci int i; 8518c2ecf20Sopenharmony_ci dev_t dev; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci if (! MACHINE_IS_VM) { 8548c2ecf20Sopenharmony_ci pr_err("not running under VM, driver not loaded.\n"); 8558c2ecf20Sopenharmony_ci return -ENODEV; 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci recording_class_AB = vmlogrdr_get_recording_class_AB(); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci rc = alloc_chrdev_region(&dev, 0, MAXMINOR, "vmlogrdr"); 8618c2ecf20Sopenharmony_ci if (rc) 8628c2ecf20Sopenharmony_ci return rc; 8638c2ecf20Sopenharmony_ci vmlogrdr_major = MAJOR(dev); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci rc=vmlogrdr_register_driver(); 8668c2ecf20Sopenharmony_ci if (rc) 8678c2ecf20Sopenharmony_ci goto cleanup; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci for (i=0; i < MAXMINOR; ++i ) { 8708c2ecf20Sopenharmony_ci sys_ser[i].buffer = (char *) get_zeroed_page(GFP_KERNEL | GFP_DMA); 8718c2ecf20Sopenharmony_ci if (!sys_ser[i].buffer) { 8728c2ecf20Sopenharmony_ci rc = -ENOMEM; 8738c2ecf20Sopenharmony_ci break; 8748c2ecf20Sopenharmony_ci } 8758c2ecf20Sopenharmony_ci sys_ser[i].current_position = sys_ser[i].buffer; 8768c2ecf20Sopenharmony_ci rc=vmlogrdr_register_device(&sys_ser[i]); 8778c2ecf20Sopenharmony_ci if (rc) 8788c2ecf20Sopenharmony_ci break; 8798c2ecf20Sopenharmony_ci } 8808c2ecf20Sopenharmony_ci if (rc) 8818c2ecf20Sopenharmony_ci goto cleanup; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci rc = vmlogrdr_register_cdev(dev); 8848c2ecf20Sopenharmony_ci if (rc) 8858c2ecf20Sopenharmony_ci goto cleanup; 8868c2ecf20Sopenharmony_ci return 0; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_cicleanup: 8898c2ecf20Sopenharmony_ci vmlogrdr_cleanup(); 8908c2ecf20Sopenharmony_ci return rc; 8918c2ecf20Sopenharmony_ci} 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_cistatic void __exit vmlogrdr_exit(void) 8958c2ecf20Sopenharmony_ci{ 8968c2ecf20Sopenharmony_ci vmlogrdr_cleanup(); 8978c2ecf20Sopenharmony_ci return; 8988c2ecf20Sopenharmony_ci} 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_cimodule_init(vmlogrdr_init); 9028c2ecf20Sopenharmony_cimodule_exit(vmlogrdr_exit); 903