16f05e69eSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * character device driver for reading z/VM system service records
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds *
69f62fa16SStefan Weinhuber * Copyright IBM Corp. 2004, 2009
71da177e4SLinus Torvalds * character device driver for reading z/VM system service records,
81da177e4SLinus Torvalds * Version 1.0
91da177e4SLinus Torvalds * Author(s): Xenia Tkatschow <xenia@us.ibm.com>
101da177e4SLinus Torvalds * Stefan Weinhuber <wein@de.ibm.com>
111da177e4SLinus Torvalds *
121da177e4SLinus Torvalds */
135466c2e4SMartin Schwidefsky
145466c2e4SMartin Schwidefsky #define KMSG_COMPONENT "vmlogrdr"
155466c2e4SMartin Schwidefsky #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
165466c2e4SMartin Schwidefsky
171da177e4SLinus Torvalds #include <linux/module.h>
181da177e4SLinus Torvalds #include <linux/init.h>
195a0e3ad6STejun Heo #include <linux/slab.h>
201da177e4SLinus Torvalds #include <linux/errno.h>
211da177e4SLinus Torvalds #include <linux/types.h>
221da177e4SLinus Torvalds #include <linux/interrupt.h>
231da177e4SLinus Torvalds #include <linux/spinlock.h>
2460063497SArun Sharma #include <linux/atomic.h>
257c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
261da177e4SLinus Torvalds #include <asm/cpcmd.h>
271da177e4SLinus Torvalds #include <asm/debug.h>
281da177e4SLinus Torvalds #include <asm/ebcdic.h>
29c9101c5bSMartin Schwidefsky #include <net/iucv/iucv.h>
301da177e4SLinus Torvalds #include <linux/kmod.h>
311da177e4SLinus Torvalds #include <linux/cdev.h>
321da177e4SLinus Torvalds #include <linux/device.h>
331da177e4SLinus Torvalds #include <linux/string.h>
341da177e4SLinus Torvalds
351da177e4SLinus Torvalds MODULE_AUTHOR
361da177e4SLinus Torvalds ("(C) 2004 IBM Corporation by Xenia Tkatschow (xenia@us.ibm.com)\n"
371da177e4SLinus Torvalds " Stefan Weinhuber (wein@de.ibm.com)");
381da177e4SLinus Torvalds MODULE_DESCRIPTION ("Character device driver for reading z/VM "
391da177e4SLinus Torvalds "system service records.");
401da177e4SLinus Torvalds MODULE_LICENSE("GPL");
411da177e4SLinus Torvalds
421da177e4SLinus Torvalds
431da177e4SLinus Torvalds /*
441da177e4SLinus Torvalds * The size of the buffer for iucv data transfer is one page,
451da177e4SLinus Torvalds * but in addition to the data we read from iucv we also
461da177e4SLinus Torvalds * place an integer and some characters into that buffer,
471da177e4SLinus Torvalds * so the maximum size for record data is a little less then
481da177e4SLinus Torvalds * one page.
491da177e4SLinus Torvalds */
501da177e4SLinus Torvalds #define NET_BUFFER_SIZE (PAGE_SIZE - sizeof(int) - sizeof(FENCE))
511da177e4SLinus Torvalds
521da177e4SLinus Torvalds /*
531da177e4SLinus Torvalds * The elements that are concurrently accessed by bottom halves are
541da177e4SLinus Torvalds * connection_established, iucv_path_severed, local_interrupt_buffer
551da177e4SLinus Torvalds * and receive_ready. The first three can be protected by
561da177e4SLinus Torvalds * priv_lock. receive_ready is atomic, so it can be incremented and
571da177e4SLinus Torvalds * decremented without holding a lock.
581da177e4SLinus Torvalds * The variable dev_in_use needs to be protected by the lock, since
591da177e4SLinus Torvalds * it's a flag used by open to make sure that the device is opened only
601da177e4SLinus Torvalds * by one user at the same time.
611da177e4SLinus Torvalds */
621da177e4SLinus Torvalds struct vmlogrdr_priv_t {
631da177e4SLinus Torvalds char system_service[8];
641da177e4SLinus Torvalds char internal_name[8];
651da177e4SLinus Torvalds char recording_name[8];
66c9101c5bSMartin Schwidefsky struct iucv_path *path;
671da177e4SLinus Torvalds int connection_established;
681da177e4SLinus Torvalds int iucv_path_severed;
69c9101c5bSMartin Schwidefsky struct iucv_message local_interrupt_buffer;
701da177e4SLinus Torvalds atomic_t receive_ready;
711da177e4SLinus Torvalds int minor_num;
721da177e4SLinus Torvalds char * buffer;
731da177e4SLinus Torvalds char * current_position;
741da177e4SLinus Torvalds int remaining;
751da177e4SLinus Torvalds ulong residual_length;
761da177e4SLinus Torvalds int buffer_free;
771da177e4SLinus Torvalds int dev_in_use; /* 1: already opened, 0: not opened*/
781da177e4SLinus Torvalds spinlock_t priv_lock;
791da177e4SLinus Torvalds struct device *device;
807f021ce1SCornelia Huck struct device *class_device;
811da177e4SLinus Torvalds int autorecording;
821da177e4SLinus Torvalds int autopurge;
831da177e4SLinus Torvalds };
841da177e4SLinus Torvalds
851da177e4SLinus Torvalds
861da177e4SLinus Torvalds /*
871da177e4SLinus Torvalds * File operation structure for vmlogrdr devices
881da177e4SLinus Torvalds */
891da177e4SLinus Torvalds static int vmlogrdr_open(struct inode *, struct file *);
901da177e4SLinus Torvalds static int vmlogrdr_release(struct inode *, struct file *);
91d2c993d8SHeiko Carstens static ssize_t vmlogrdr_read (struct file *filp, char __user *data,
92d2c993d8SHeiko Carstens size_t count, loff_t * ppos);
931da177e4SLinus Torvalds
94d54b1fdbSArjan van de Ven static const struct file_operations vmlogrdr_fops = {
951da177e4SLinus Torvalds .owner = THIS_MODULE,
961da177e4SLinus Torvalds .open = vmlogrdr_open,
971da177e4SLinus Torvalds .release = vmlogrdr_release,
981da177e4SLinus Torvalds .read = vmlogrdr_read,
996038f373SArnd Bergmann .llseek = no_llseek,
1001da177e4SLinus Torvalds };
1011da177e4SLinus Torvalds
1021da177e4SLinus Torvalds
10391e60eb6SUrsula Braun static void vmlogrdr_iucv_path_complete(struct iucv_path *, u8 *ipuser);
10491e60eb6SUrsula Braun static void vmlogrdr_iucv_path_severed(struct iucv_path *, u8 *ipuser);
105c9101c5bSMartin Schwidefsky static void vmlogrdr_iucv_message_pending(struct iucv_path *,
106c9101c5bSMartin Schwidefsky struct iucv_message *);
107c9101c5bSMartin Schwidefsky
108c9101c5bSMartin Schwidefsky
109c9101c5bSMartin Schwidefsky static struct iucv_handler vmlogrdr_iucv_handler = {
110c9101c5bSMartin Schwidefsky .path_complete = vmlogrdr_iucv_path_complete,
111c9101c5bSMartin Schwidefsky .path_severed = vmlogrdr_iucv_path_severed,
112c9101c5bSMartin Schwidefsky .message_pending = vmlogrdr_iucv_message_pending,
1131da177e4SLinus Torvalds };
1141da177e4SLinus Torvalds
1151da177e4SLinus Torvalds
1162b67fc46SHeiko Carstens static DECLARE_WAIT_QUEUE_HEAD(conn_wait_queue);
1172b67fc46SHeiko Carstens static DECLARE_WAIT_QUEUE_HEAD(read_wait_queue);
1181da177e4SLinus Torvalds
1191da177e4SLinus Torvalds /*
1201da177e4SLinus Torvalds * pointer to system service private structure
1211da177e4SLinus Torvalds * minor number 0 --> logrec
1221da177e4SLinus Torvalds * minor number 1 --> account
1231da177e4SLinus Torvalds * minor number 2 --> symptom
1241da177e4SLinus Torvalds */
1251da177e4SLinus Torvalds
1261da177e4SLinus Torvalds static struct vmlogrdr_priv_t sys_ser[] = {
1271da177e4SLinus Torvalds { .system_service = "*LOGREC ",
1281da177e4SLinus Torvalds .internal_name = "logrec",
1291da177e4SLinus Torvalds .recording_name = "EREP",
1301da177e4SLinus Torvalds .minor_num = 0,
1311da177e4SLinus Torvalds .buffer_free = 1,
132cb629a01SMilind Arun Choudhary .priv_lock = __SPIN_LOCK_UNLOCKED(sys_ser[0].priv_lock),
1331da177e4SLinus Torvalds .autorecording = 1,
1341da177e4SLinus Torvalds .autopurge = 1,
1351da177e4SLinus Torvalds },
1361da177e4SLinus Torvalds { .system_service = "*ACCOUNT",
1371da177e4SLinus Torvalds .internal_name = "account",
1381da177e4SLinus Torvalds .recording_name = "ACCOUNT",
1391da177e4SLinus Torvalds .minor_num = 1,
1401da177e4SLinus Torvalds .buffer_free = 1,
141cb629a01SMilind Arun Choudhary .priv_lock = __SPIN_LOCK_UNLOCKED(sys_ser[1].priv_lock),
1421da177e4SLinus Torvalds .autorecording = 1,
1431da177e4SLinus Torvalds .autopurge = 1,
1441da177e4SLinus Torvalds },
1451da177e4SLinus Torvalds { .system_service = "*SYMPTOM",
1461da177e4SLinus Torvalds .internal_name = "symptom",
1471da177e4SLinus Torvalds .recording_name = "SYMPTOM",
1481da177e4SLinus Torvalds .minor_num = 2,
1491da177e4SLinus Torvalds .buffer_free = 1,
150cb629a01SMilind Arun Choudhary .priv_lock = __SPIN_LOCK_UNLOCKED(sys_ser[2].priv_lock),
1511da177e4SLinus Torvalds .autorecording = 1,
1521da177e4SLinus Torvalds .autopurge = 1,
1531da177e4SLinus Torvalds }
1541da177e4SLinus Torvalds };
1551da177e4SLinus Torvalds
15654be9d12Szhong jiang #define MAXMINOR ARRAY_SIZE(sys_ser)
1571da177e4SLinus Torvalds
1581da177e4SLinus Torvalds static char FENCE[] = {"EOR"};
1591da177e4SLinus Torvalds static int vmlogrdr_major = 0;
1601da177e4SLinus Torvalds static struct cdev *vmlogrdr_cdev = NULL;
1611da177e4SLinus Torvalds static int recording_class_AB;
1621da177e4SLinus Torvalds
1631da177e4SLinus Torvalds
vmlogrdr_iucv_path_complete(struct iucv_path * path,u8 * ipuser)16491e60eb6SUrsula Braun static void vmlogrdr_iucv_path_complete(struct iucv_path *path, u8 *ipuser)
1651da177e4SLinus Torvalds {
166c9101c5bSMartin Schwidefsky struct vmlogrdr_priv_t * logptr = path->private;
167c9101c5bSMartin Schwidefsky
1681da177e4SLinus Torvalds spin_lock(&logptr->priv_lock);
1691da177e4SLinus Torvalds logptr->connection_established = 1;
1701da177e4SLinus Torvalds spin_unlock(&logptr->priv_lock);
1711da177e4SLinus Torvalds wake_up(&conn_wait_queue);
1721da177e4SLinus Torvalds }
1731da177e4SLinus Torvalds
1741da177e4SLinus Torvalds
vmlogrdr_iucv_path_severed(struct iucv_path * path,u8 * ipuser)17591e60eb6SUrsula Braun static void vmlogrdr_iucv_path_severed(struct iucv_path *path, u8 *ipuser)
1761da177e4SLinus Torvalds {
177c9101c5bSMartin Schwidefsky struct vmlogrdr_priv_t * logptr = path->private;
178c9101c5bSMartin Schwidefsky u8 reason = (u8) ipuser[8];
1791da177e4SLinus Torvalds
1805466c2e4SMartin Schwidefsky pr_err("vmlogrdr: connection severed with reason %i\n", reason);
1811da177e4SLinus Torvalds
182c9101c5bSMartin Schwidefsky iucv_path_sever(path, NULL);
183c9101c5bSMartin Schwidefsky kfree(path);
184c9101c5bSMartin Schwidefsky logptr->path = NULL;
185c9101c5bSMartin Schwidefsky
1861da177e4SLinus Torvalds spin_lock(&logptr->priv_lock);
1871da177e4SLinus Torvalds logptr->connection_established = 0;
1881da177e4SLinus Torvalds logptr->iucv_path_severed = 1;
1891da177e4SLinus Torvalds spin_unlock(&logptr->priv_lock);
1901da177e4SLinus Torvalds
1911da177e4SLinus Torvalds wake_up(&conn_wait_queue);
1921da177e4SLinus Torvalds /* just in case we're sleeping waiting for a record */
1931da177e4SLinus Torvalds wake_up_interruptible(&read_wait_queue);
1941da177e4SLinus Torvalds }
1951da177e4SLinus Torvalds
1961da177e4SLinus Torvalds
vmlogrdr_iucv_message_pending(struct iucv_path * path,struct iucv_message * msg)197c9101c5bSMartin Schwidefsky static void vmlogrdr_iucv_message_pending(struct iucv_path *path,
198c9101c5bSMartin Schwidefsky struct iucv_message *msg)
1991da177e4SLinus Torvalds {
200c9101c5bSMartin Schwidefsky struct vmlogrdr_priv_t * logptr = path->private;
2011da177e4SLinus Torvalds
2021da177e4SLinus Torvalds /*
2031da177e4SLinus Torvalds * This function is the bottom half so it should be quick.
2041da177e4SLinus Torvalds * Copy the external interrupt data into our local eib and increment
2051da177e4SLinus Torvalds * the usage count
2061da177e4SLinus Torvalds */
2071da177e4SLinus Torvalds spin_lock(&logptr->priv_lock);
208c9101c5bSMartin Schwidefsky memcpy(&logptr->local_interrupt_buffer, msg, sizeof(*msg));
2091da177e4SLinus Torvalds atomic_inc(&logptr->receive_ready);
2101da177e4SLinus Torvalds spin_unlock(&logptr->priv_lock);
2111da177e4SLinus Torvalds wake_up_interruptible(&read_wait_queue);
2121da177e4SLinus Torvalds }
2131da177e4SLinus Torvalds
2141da177e4SLinus Torvalds
vmlogrdr_get_recording_class_AB(void)215c9101c5bSMartin Schwidefsky static int vmlogrdr_get_recording_class_AB(void)
216c9101c5bSMartin Schwidefsky {
217bf2106aeSJoe Perches static const char cp_command[] = "QUERY COMMAND RECORDING ";
2181da177e4SLinus Torvalds char cp_response[80];
2191da177e4SLinus Torvalds char *tail;
2201da177e4SLinus Torvalds int len,i;
2211da177e4SLinus Torvalds
2226b979de3SChristian Borntraeger cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
2231da177e4SLinus Torvalds len = strnlen(cp_response,sizeof(cp_response));
2241da177e4SLinus Torvalds // now the parsing
2251da177e4SLinus Torvalds tail=strnchr(cp_response,len,'=');
2261da177e4SLinus Torvalds if (!tail)
2271da177e4SLinus Torvalds return 0;
2281da177e4SLinus Torvalds tail++;
2291da177e4SLinus Torvalds if (!strncmp("ANY",tail,3))
2301da177e4SLinus Torvalds return 1;
2311da177e4SLinus Torvalds if (!strncmp("NONE",tail,4))
2321da177e4SLinus Torvalds return 0;
2331da177e4SLinus Torvalds /*
2341da177e4SLinus Torvalds * expect comma separated list of classes here, if one of them
2351da177e4SLinus Torvalds * is A or B return 1 otherwise 0
2361da177e4SLinus Torvalds */
2371da177e4SLinus Torvalds for (i=tail-cp_response; i<len; i++)
2381da177e4SLinus Torvalds if ( cp_response[i]=='A' || cp_response[i]=='B' )
2391da177e4SLinus Torvalds return 1;
2401da177e4SLinus Torvalds return 0;
2411da177e4SLinus Torvalds }
2421da177e4SLinus Torvalds
2431da177e4SLinus Torvalds
vmlogrdr_recording(struct vmlogrdr_priv_t * logptr,int action,int purge)244c9101c5bSMartin Schwidefsky static int vmlogrdr_recording(struct vmlogrdr_priv_t * logptr,
245c9101c5bSMartin Schwidefsky int action, int purge)
246c9101c5bSMartin Schwidefsky {
2471da177e4SLinus Torvalds
2481da177e4SLinus Torvalds char cp_command[80];
2491da177e4SLinus Torvalds char cp_response[160];
2501da177e4SLinus Torvalds char *onoff, *qid_string;
251ca768b66SStefan Weinhuber int rc;
2521da177e4SLinus Torvalds
2531da177e4SLinus Torvalds onoff = ((action == 1) ? "ON" : "OFF");
2541da177e4SLinus Torvalds qid_string = ((recording_class_AB == 1) ? " QID * " : "");
2551da177e4SLinus Torvalds
2561da177e4SLinus Torvalds /*
2571da177e4SLinus Torvalds * The recording commands needs to be called with option QID
2581da177e4SLinus Torvalds * for guests that have previlege classes A or B.
2591da177e4SLinus Torvalds * Purging has to be done as separate step, because recording
2601da177e4SLinus Torvalds * can't be switched on as long as records are on the queue.
2611da177e4SLinus Torvalds * Doing both at the same time doesn't work.
2621da177e4SLinus Torvalds */
263ca768b66SStefan Weinhuber if (purge && (action == 1)) {
264ca768b66SStefan Weinhuber memset(cp_command, 0x00, sizeof(cp_command));
265ca768b66SStefan Weinhuber memset(cp_response, 0x00, sizeof(cp_response));
2661da177e4SLinus Torvalds snprintf(cp_command, sizeof(cp_command),
2671da177e4SLinus Torvalds "RECORDING %s PURGE %s",
2681da177e4SLinus Torvalds logptr->recording_name,
2691da177e4SLinus Torvalds qid_string);
2706b979de3SChristian Borntraeger cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
2711da177e4SLinus Torvalds }
2721da177e4SLinus Torvalds
2731da177e4SLinus Torvalds memset(cp_command, 0x00, sizeof(cp_command));
2741da177e4SLinus Torvalds memset(cp_response, 0x00, sizeof(cp_response));
2751da177e4SLinus Torvalds snprintf(cp_command, sizeof(cp_command), "RECORDING %s %s %s",
2761da177e4SLinus Torvalds logptr->recording_name,
2771da177e4SLinus Torvalds onoff,
2781da177e4SLinus Torvalds qid_string);
2796b979de3SChristian Borntraeger cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
2801da177e4SLinus Torvalds /* The recording command will usually answer with 'Command complete'
2811da177e4SLinus Torvalds * on success, but when the specific service was never connected
2821da177e4SLinus Torvalds * before then there might be an additional informational message
2831da177e4SLinus Torvalds * 'HCPCRC8072I Recording entry not found' before the
2841da177e4SLinus Torvalds * 'Command complete'. So I use strstr rather then the strncmp.
2851da177e4SLinus Torvalds */
2861da177e4SLinus Torvalds if (strstr(cp_response,"Command complete"))
287ca768b66SStefan Weinhuber rc = 0;
2881da177e4SLinus Torvalds else
289ca768b66SStefan Weinhuber rc = -EIO;
290ca768b66SStefan Weinhuber /*
291ca768b66SStefan Weinhuber * If we turn recording off, we have to purge any remaining records
292ca768b66SStefan Weinhuber * afterwards, as a large number of queued records may impact z/VM
293ca768b66SStefan Weinhuber * performance.
294ca768b66SStefan Weinhuber */
295ca768b66SStefan Weinhuber if (purge && (action == 0)) {
296ca768b66SStefan Weinhuber memset(cp_command, 0x00, sizeof(cp_command));
297ca768b66SStefan Weinhuber memset(cp_response, 0x00, sizeof(cp_response));
298ca768b66SStefan Weinhuber snprintf(cp_command, sizeof(cp_command),
299ca768b66SStefan Weinhuber "RECORDING %s PURGE %s",
300ca768b66SStefan Weinhuber logptr->recording_name,
301ca768b66SStefan Weinhuber qid_string);
302ca768b66SStefan Weinhuber cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
303ca768b66SStefan Weinhuber }
3041da177e4SLinus Torvalds
305ca768b66SStefan Weinhuber return rc;
3061da177e4SLinus Torvalds }
3071da177e4SLinus Torvalds
3081da177e4SLinus Torvalds
vmlogrdr_open(struct inode * inode,struct file * filp)309c9101c5bSMartin Schwidefsky static int vmlogrdr_open (struct inode *inode, struct file *filp)
3101da177e4SLinus Torvalds {
3111da177e4SLinus Torvalds int dev_num = 0;
3121da177e4SLinus Torvalds struct vmlogrdr_priv_t * logptr = NULL;
3131da177e4SLinus Torvalds int connect_rc = 0;
3141da177e4SLinus Torvalds int ret;
3151da177e4SLinus Torvalds
3161da177e4SLinus Torvalds dev_num = iminor(inode);
3179784bd4fSHeiko Carstens if (dev_num >= MAXMINOR)
3181da177e4SLinus Torvalds return -ENODEV;
3191da177e4SLinus Torvalds logptr = &sys_ser[dev_num];
3201da177e4SLinus Torvalds
3211da177e4SLinus Torvalds /*
3221da177e4SLinus Torvalds * only allow for blocking reads to be open
3231da177e4SLinus Torvalds */
3241da177e4SLinus Torvalds if (filp->f_flags & O_NONBLOCK)
325745e967aSHeiko Carstens return -EOPNOTSUPP;
3261da177e4SLinus Torvalds
3271da177e4SLinus Torvalds /* Besure this device hasn't already been opened */
3281da177e4SLinus Torvalds spin_lock_bh(&logptr->priv_lock);
3291da177e4SLinus Torvalds if (logptr->dev_in_use) {
3301da177e4SLinus Torvalds spin_unlock_bh(&logptr->priv_lock);
3311da177e4SLinus Torvalds return -EBUSY;
3321da177e4SLinus Torvalds }
333c9101c5bSMartin Schwidefsky logptr->dev_in_use = 1;
334c9101c5bSMartin Schwidefsky logptr->connection_established = 0;
335c9101c5bSMartin Schwidefsky logptr->iucv_path_severed = 0;
3361da177e4SLinus Torvalds atomic_set(&logptr->receive_ready, 0);
3371da177e4SLinus Torvalds logptr->buffer_free = 1;
338c9101c5bSMartin Schwidefsky spin_unlock_bh(&logptr->priv_lock);
3391da177e4SLinus Torvalds
3401da177e4SLinus Torvalds /* set the file options */
3411da177e4SLinus Torvalds filp->private_data = logptr;
3421da177e4SLinus Torvalds
3431da177e4SLinus Torvalds /* start recording for this service*/
344c9101c5bSMartin Schwidefsky if (logptr->autorecording) {
3451da177e4SLinus Torvalds ret = vmlogrdr_recording(logptr,1,logptr->autopurge);
3461da177e4SLinus Torvalds if (ret)
347baebc70aSJoe Perches pr_warn("vmlogrdr: failed to start recording automatically\n");
3481da177e4SLinus Torvalds }
3491da177e4SLinus Torvalds
3501da177e4SLinus Torvalds /* create connection to the system service */
351c9101c5bSMartin Schwidefsky logptr->path = iucv_path_alloc(10, 0, GFP_KERNEL);
352c9101c5bSMartin Schwidefsky if (!logptr->path)
353c9101c5bSMartin Schwidefsky goto out_dev;
354c9101c5bSMartin Schwidefsky connect_rc = iucv_path_connect(logptr->path, &vmlogrdr_iucv_handler,
355c9101c5bSMartin Schwidefsky logptr->system_service, NULL, NULL,
356c9101c5bSMartin Schwidefsky logptr);
3571da177e4SLinus Torvalds if (connect_rc) {
3585466c2e4SMartin Schwidefsky pr_err("vmlogrdr: iucv connection to %s "
3595466c2e4SMartin Schwidefsky "failed with rc %i \n",
3605466c2e4SMartin Schwidefsky logptr->system_service, connect_rc);
361c9101c5bSMartin Schwidefsky goto out_path;
3621da177e4SLinus Torvalds }
3631da177e4SLinus Torvalds
3641da177e4SLinus Torvalds /* We've issued the connect and now we must wait for a
3651da177e4SLinus Torvalds * ConnectionComplete or ConnectinSevered Interrupt
3661da177e4SLinus Torvalds * before we can continue to process.
3671da177e4SLinus Torvalds */
3681da177e4SLinus Torvalds wait_event(conn_wait_queue, (logptr->connection_established)
3691da177e4SLinus Torvalds || (logptr->iucv_path_severed));
370c9101c5bSMartin Schwidefsky if (logptr->iucv_path_severed)
371c9101c5bSMartin Schwidefsky goto out_record;
3723b47f9d5SMartin Schwidefsky nonseekable_open(inode, filp);
3733b47f9d5SMartin Schwidefsky return 0;
3741da177e4SLinus Torvalds
375c9101c5bSMartin Schwidefsky out_record:
3761da177e4SLinus Torvalds if (logptr->autorecording)
3771da177e4SLinus Torvalds vmlogrdr_recording(logptr,0,logptr->autopurge);
378c9101c5bSMartin Schwidefsky out_path:
379c9101c5bSMartin Schwidefsky kfree(logptr->path); /* kfree(NULL) is ok. */
380c9101c5bSMartin Schwidefsky logptr->path = NULL;
381c9101c5bSMartin Schwidefsky out_dev:
3821da177e4SLinus Torvalds logptr->dev_in_use = 0;
3831da177e4SLinus Torvalds return -EIO;
3841da177e4SLinus Torvalds }
3851da177e4SLinus Torvalds
3861da177e4SLinus Torvalds
vmlogrdr_release(struct inode * inode,struct file * filp)387c9101c5bSMartin Schwidefsky static int vmlogrdr_release (struct inode *inode, struct file *filp)
3881da177e4SLinus Torvalds {
3891da177e4SLinus Torvalds int ret;
3901da177e4SLinus Torvalds
3911da177e4SLinus Torvalds struct vmlogrdr_priv_t * logptr = filp->private_data;
3921da177e4SLinus Torvalds
39366b494a7SUrsula Braun iucv_path_sever(logptr->path, NULL);
39466b494a7SUrsula Braun kfree(logptr->path);
39566b494a7SUrsula Braun logptr->path = NULL;
3961da177e4SLinus Torvalds if (logptr->autorecording) {
3971da177e4SLinus Torvalds ret = vmlogrdr_recording(logptr,0,logptr->autopurge);
3981da177e4SLinus Torvalds if (ret)
399baebc70aSJoe Perches pr_warn("vmlogrdr: failed to stop recording automatically\n");
4001da177e4SLinus Torvalds }
4011da177e4SLinus Torvalds logptr->dev_in_use = 0;
4021da177e4SLinus Torvalds
4031da177e4SLinus Torvalds return 0;
4041da177e4SLinus Torvalds }
4051da177e4SLinus Torvalds
4061da177e4SLinus Torvalds
vmlogrdr_receive_data(struct vmlogrdr_priv_t * priv)407c9101c5bSMartin Schwidefsky static int vmlogrdr_receive_data(struct vmlogrdr_priv_t *priv)
408c9101c5bSMartin Schwidefsky {
4091da177e4SLinus Torvalds int rc, *temp;
4101da177e4SLinus Torvalds /* we need to keep track of two data sizes here:
4111da177e4SLinus Torvalds * The number of bytes we need to receive from iucv and
4121da177e4SLinus Torvalds * the total number of bytes we actually write into the buffer.
4131da177e4SLinus Torvalds */
4141da177e4SLinus Torvalds int user_data_count, iucv_data_count;
4151da177e4SLinus Torvalds char * buffer;
4161da177e4SLinus Torvalds
4171da177e4SLinus Torvalds if (atomic_read(&priv->receive_ready)) {
4181da177e4SLinus Torvalds spin_lock_bh(&priv->priv_lock);
4191da177e4SLinus Torvalds if (priv->residual_length){
4201da177e4SLinus Torvalds /* receive second half of a record */
4211da177e4SLinus Torvalds iucv_data_count = priv->residual_length;
4221da177e4SLinus Torvalds user_data_count = 0;
4231da177e4SLinus Torvalds buffer = priv->buffer;
4241da177e4SLinus Torvalds } else {
4251da177e4SLinus Torvalds /* receive a new record:
4261da177e4SLinus Torvalds * We need to return the total length of the record
4271da177e4SLinus Torvalds * + size of FENCE in the first 4 bytes of the buffer.
4281da177e4SLinus Torvalds */
429c9101c5bSMartin Schwidefsky iucv_data_count = priv->local_interrupt_buffer.length;
4301da177e4SLinus Torvalds user_data_count = sizeof(int);
4311da177e4SLinus Torvalds temp = (int*)priv->buffer;
4321da177e4SLinus Torvalds *temp= iucv_data_count + sizeof(FENCE);
4331da177e4SLinus Torvalds buffer = priv->buffer + sizeof(int);
4341da177e4SLinus Torvalds }
4351da177e4SLinus Torvalds /*
436025dfdafSFrederik Schwarzer * If the record is bigger than our buffer, we receive only
4371da177e4SLinus Torvalds * a part of it. We can get the rest later.
4381da177e4SLinus Torvalds */
4391da177e4SLinus Torvalds if (iucv_data_count > NET_BUFFER_SIZE)
4401da177e4SLinus Torvalds iucv_data_count = NET_BUFFER_SIZE;
441c9101c5bSMartin Schwidefsky rc = iucv_message_receive(priv->path,
442c9101c5bSMartin Schwidefsky &priv->local_interrupt_buffer,
443c9101c5bSMartin Schwidefsky 0, buffer, iucv_data_count,
4441da177e4SLinus Torvalds &priv->residual_length);
4451da177e4SLinus Torvalds spin_unlock_bh(&priv->priv_lock);
446025dfdafSFrederik Schwarzer /* An rc of 5 indicates that the record was bigger than
4471da177e4SLinus Torvalds * the buffer, which is OK for us. A 9 indicates that the
4481da177e4SLinus Torvalds * record was purged befor we could receive it.
4491da177e4SLinus Torvalds */
4501da177e4SLinus Torvalds if (rc == 5)
4511da177e4SLinus Torvalds rc = 0;
4521da177e4SLinus Torvalds if (rc == 9)
4531da177e4SLinus Torvalds atomic_set(&priv->receive_ready, 0);
4541da177e4SLinus Torvalds } else {
4551da177e4SLinus Torvalds rc = 1;
4561da177e4SLinus Torvalds }
4571da177e4SLinus Torvalds if (!rc) {
4581da177e4SLinus Torvalds priv->buffer_free = 0;
4591da177e4SLinus Torvalds user_data_count += iucv_data_count;
4601da177e4SLinus Torvalds priv->current_position = priv->buffer;
4611da177e4SLinus Torvalds if (priv->residual_length == 0){
4621da177e4SLinus Torvalds /* the whole record has been captured,
4631da177e4SLinus Torvalds * now add the fence */
4641da177e4SLinus Torvalds atomic_dec(&priv->receive_ready);
4651da177e4SLinus Torvalds buffer = priv->buffer + user_data_count;
4661da177e4SLinus Torvalds memcpy(buffer, FENCE, sizeof(FENCE));
4671da177e4SLinus Torvalds user_data_count += sizeof(FENCE);
4681da177e4SLinus Torvalds }
4691da177e4SLinus Torvalds priv->remaining = user_data_count;
4701da177e4SLinus Torvalds }
4711da177e4SLinus Torvalds
4721da177e4SLinus Torvalds return rc;
4731da177e4SLinus Torvalds }
4741da177e4SLinus Torvalds
4751da177e4SLinus Torvalds
vmlogrdr_read(struct file * filp,char __user * data,size_t count,loff_t * ppos)476c9101c5bSMartin Schwidefsky static ssize_t vmlogrdr_read(struct file *filp, char __user *data,
477c9101c5bSMartin Schwidefsky size_t count, loff_t * ppos)
4781da177e4SLinus Torvalds {
4791da177e4SLinus Torvalds int rc;
4801da177e4SLinus Torvalds struct vmlogrdr_priv_t * priv = filp->private_data;
4811da177e4SLinus Torvalds
4821da177e4SLinus Torvalds while (priv->buffer_free) {
4831da177e4SLinus Torvalds rc = vmlogrdr_receive_data(priv);
4841da177e4SLinus Torvalds if (rc) {
4851da177e4SLinus Torvalds rc = wait_event_interruptible(read_wait_queue,
4861da177e4SLinus Torvalds atomic_read(&priv->receive_ready));
4871da177e4SLinus Torvalds if (rc)
4881da177e4SLinus Torvalds return rc;
4891da177e4SLinus Torvalds }
4901da177e4SLinus Torvalds }
4911da177e4SLinus Torvalds /* copy only up to end of record */
4921da177e4SLinus Torvalds if (count > priv->remaining)
4931da177e4SLinus Torvalds count = priv->remaining;
4941da177e4SLinus Torvalds
4951da177e4SLinus Torvalds if (copy_to_user(data, priv->current_position, count))
4961da177e4SLinus Torvalds return -EFAULT;
4971da177e4SLinus Torvalds
4981da177e4SLinus Torvalds *ppos += count;
4991da177e4SLinus Torvalds priv->current_position += count;
5001da177e4SLinus Torvalds priv->remaining -= count;
5011da177e4SLinus Torvalds
5021da177e4SLinus Torvalds /* if all data has been transferred, set buffer free */
5031da177e4SLinus Torvalds if (priv->remaining == 0)
5041da177e4SLinus Torvalds priv->buffer_free = 1;
5051da177e4SLinus Torvalds
5061da177e4SLinus Torvalds return count;
5071da177e4SLinus Torvalds }
5081da177e4SLinus Torvalds
vmlogrdr_autopurge_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)509c9101c5bSMartin Schwidefsky static ssize_t vmlogrdr_autopurge_store(struct device * dev,
510c9101c5bSMartin Schwidefsky struct device_attribute *attr,
511c9101c5bSMartin Schwidefsky const char * buf, size_t count)
512c9101c5bSMartin Schwidefsky {
513dff59b64SGreg Kroah-Hartman struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
5141da177e4SLinus Torvalds ssize_t ret = count;
5151da177e4SLinus Torvalds
5161da177e4SLinus Torvalds switch (buf[0]) {
5171da177e4SLinus Torvalds case '0':
5181da177e4SLinus Torvalds priv->autopurge=0;
5191da177e4SLinus Torvalds break;
5201da177e4SLinus Torvalds case '1':
5211da177e4SLinus Torvalds priv->autopurge=1;
5221da177e4SLinus Torvalds break;
5231da177e4SLinus Torvalds default:
5241da177e4SLinus Torvalds ret = -EINVAL;
5251da177e4SLinus Torvalds }
5261da177e4SLinus Torvalds return ret;
5271da177e4SLinus Torvalds }
5281da177e4SLinus Torvalds
5291da177e4SLinus Torvalds
vmlogrdr_autopurge_show(struct device * dev,struct device_attribute * attr,char * buf)530c9101c5bSMartin Schwidefsky static ssize_t vmlogrdr_autopurge_show(struct device *dev,
531c9101c5bSMartin Schwidefsky struct device_attribute *attr,
532c9101c5bSMartin Schwidefsky char *buf)
533c9101c5bSMartin Schwidefsky {
534dff59b64SGreg Kroah-Hartman struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
5351da177e4SLinus Torvalds return sprintf(buf, "%u\n", priv->autopurge);
5361da177e4SLinus Torvalds }
5371da177e4SLinus Torvalds
5381da177e4SLinus Torvalds
5391da177e4SLinus Torvalds static DEVICE_ATTR(autopurge, 0644, vmlogrdr_autopurge_show,
5401da177e4SLinus Torvalds vmlogrdr_autopurge_store);
5411da177e4SLinus Torvalds
5421da177e4SLinus Torvalds
vmlogrdr_purge_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)543c9101c5bSMartin Schwidefsky static ssize_t vmlogrdr_purge_store(struct device * dev,
544c9101c5bSMartin Schwidefsky struct device_attribute *attr,
545c9101c5bSMartin Schwidefsky const char * buf, size_t count)
546c9101c5bSMartin Schwidefsky {
5471da177e4SLinus Torvalds
5481da177e4SLinus Torvalds char cp_command[80];
5491da177e4SLinus Torvalds char cp_response[80];
550dff59b64SGreg Kroah-Hartman struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
5511da177e4SLinus Torvalds
5521da177e4SLinus Torvalds if (buf[0] != '1')
5531da177e4SLinus Torvalds return -EINVAL;
5541da177e4SLinus Torvalds
5551da177e4SLinus Torvalds memset(cp_command, 0x00, sizeof(cp_command));
5561da177e4SLinus Torvalds memset(cp_response, 0x00, sizeof(cp_response));
5571da177e4SLinus Torvalds
5581da177e4SLinus Torvalds /*
5591da177e4SLinus Torvalds * The recording command needs to be called with option QID
5601da177e4SLinus Torvalds * for guests that have previlege classes A or B.
5611da177e4SLinus Torvalds * Other guests will not recognize the command and we have to
5621da177e4SLinus Torvalds * issue the same command without the QID parameter.
5631da177e4SLinus Torvalds */
5641da177e4SLinus Torvalds
5651da177e4SLinus Torvalds if (recording_class_AB)
5661da177e4SLinus Torvalds snprintf(cp_command, sizeof(cp_command),
5671da177e4SLinus Torvalds "RECORDING %s PURGE QID * ",
5681da177e4SLinus Torvalds priv->recording_name);
5691da177e4SLinus Torvalds else
5701da177e4SLinus Torvalds snprintf(cp_command, sizeof(cp_command),
5711da177e4SLinus Torvalds "RECORDING %s PURGE ",
5721da177e4SLinus Torvalds priv->recording_name);
5731da177e4SLinus Torvalds
5746b979de3SChristian Borntraeger cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
5751da177e4SLinus Torvalds
5761da177e4SLinus Torvalds return count;
5771da177e4SLinus Torvalds }
5781da177e4SLinus Torvalds
5791da177e4SLinus Torvalds
5801da177e4SLinus Torvalds static DEVICE_ATTR(purge, 0200, NULL, vmlogrdr_purge_store);
5811da177e4SLinus Torvalds
5821da177e4SLinus Torvalds
vmlogrdr_autorecording_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)583c9101c5bSMartin Schwidefsky static ssize_t vmlogrdr_autorecording_store(struct device *dev,
584c9101c5bSMartin Schwidefsky struct device_attribute *attr,
585c9101c5bSMartin Schwidefsky const char *buf, size_t count)
586c9101c5bSMartin Schwidefsky {
587dff59b64SGreg Kroah-Hartman struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
5881da177e4SLinus Torvalds ssize_t ret = count;
5891da177e4SLinus Torvalds
5901da177e4SLinus Torvalds switch (buf[0]) {
5911da177e4SLinus Torvalds case '0':
5921da177e4SLinus Torvalds priv->autorecording=0;
5931da177e4SLinus Torvalds break;
5941da177e4SLinus Torvalds case '1':
5951da177e4SLinus Torvalds priv->autorecording=1;
5961da177e4SLinus Torvalds break;
5971da177e4SLinus Torvalds default:
5981da177e4SLinus Torvalds ret = -EINVAL;
5991da177e4SLinus Torvalds }
6001da177e4SLinus Torvalds return ret;
6011da177e4SLinus Torvalds }
6021da177e4SLinus Torvalds
6031da177e4SLinus Torvalds
vmlogrdr_autorecording_show(struct device * dev,struct device_attribute * attr,char * buf)604c9101c5bSMartin Schwidefsky static ssize_t vmlogrdr_autorecording_show(struct device *dev,
605c9101c5bSMartin Schwidefsky struct device_attribute *attr,
606c9101c5bSMartin Schwidefsky char *buf)
607c9101c5bSMartin Schwidefsky {
608dff59b64SGreg Kroah-Hartman struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
6091da177e4SLinus Torvalds return sprintf(buf, "%u\n", priv->autorecording);
6101da177e4SLinus Torvalds }
6111da177e4SLinus Torvalds
6121da177e4SLinus Torvalds
6131da177e4SLinus Torvalds static DEVICE_ATTR(autorecording, 0644, vmlogrdr_autorecording_show,
6141da177e4SLinus Torvalds vmlogrdr_autorecording_store);
6151da177e4SLinus Torvalds
6161da177e4SLinus Torvalds
vmlogrdr_recording_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)617c9101c5bSMartin Schwidefsky static ssize_t vmlogrdr_recording_store(struct device * dev,
618c9101c5bSMartin Schwidefsky struct device_attribute *attr,
619c9101c5bSMartin Schwidefsky const char * buf, size_t count)
620c9101c5bSMartin Schwidefsky {
621dff59b64SGreg Kroah-Hartman struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
6221da177e4SLinus Torvalds ssize_t ret;
6231da177e4SLinus Torvalds
6241da177e4SLinus Torvalds switch (buf[0]) {
6251da177e4SLinus Torvalds case '0':
6261da177e4SLinus Torvalds ret = vmlogrdr_recording(priv,0,0);
6271da177e4SLinus Torvalds break;
6281da177e4SLinus Torvalds case '1':
6291da177e4SLinus Torvalds ret = vmlogrdr_recording(priv,1,0);
6301da177e4SLinus Torvalds break;
6311da177e4SLinus Torvalds default:
6321da177e4SLinus Torvalds ret = -EINVAL;
6331da177e4SLinus Torvalds }
6341da177e4SLinus Torvalds if (ret)
6351da177e4SLinus Torvalds return ret;
6361da177e4SLinus Torvalds else
6371da177e4SLinus Torvalds return count;
6381da177e4SLinus Torvalds
6391da177e4SLinus Torvalds }
6401da177e4SLinus Torvalds
6411da177e4SLinus Torvalds
6421da177e4SLinus Torvalds static DEVICE_ATTR(recording, 0200, NULL, vmlogrdr_recording_store);
6431da177e4SLinus Torvalds
6441da177e4SLinus Torvalds
recording_status_show(struct device_driver * driver,char * buf)64536369569SGreg Kroah-Hartman static ssize_t recording_status_show(struct device_driver *driver, char *buf)
646c9101c5bSMartin Schwidefsky {
647bf2106aeSJoe Perches static const char cp_command[] = "QUERY RECORDING ";
6481da177e4SLinus Torvalds int len;
6491da177e4SLinus Torvalds
6506b979de3SChristian Borntraeger cpcmd(cp_command, buf, 4096, NULL);
6511da177e4SLinus Torvalds len = strlen(buf);
6521da177e4SLinus Torvalds return len;
6531da177e4SLinus Torvalds }
65436369569SGreg Kroah-Hartman static DRIVER_ATTR_RO(recording_status);
65572f6e3a8SSebastian Ott static struct attribute *vmlogrdr_drv_attrs[] = {
65672f6e3a8SSebastian Ott &driver_attr_recording_status.attr,
65772f6e3a8SSebastian Ott NULL,
65872f6e3a8SSebastian Ott };
65972f6e3a8SSebastian Ott static struct attribute_group vmlogrdr_drv_attr_group = {
66072f6e3a8SSebastian Ott .attrs = vmlogrdr_drv_attrs,
66172f6e3a8SSebastian Ott };
66272f6e3a8SSebastian Ott static const struct attribute_group *vmlogrdr_drv_attr_groups[] = {
66372f6e3a8SSebastian Ott &vmlogrdr_drv_attr_group,
66472f6e3a8SSebastian Ott NULL,
66572f6e3a8SSebastian Ott };
6661da177e4SLinus Torvalds
6671da177e4SLinus Torvalds static struct attribute *vmlogrdr_attrs[] = {
6681da177e4SLinus Torvalds &dev_attr_autopurge.attr,
6691da177e4SLinus Torvalds &dev_attr_purge.attr,
6701da177e4SLinus Torvalds &dev_attr_autorecording.attr,
6711da177e4SLinus Torvalds &dev_attr_recording.attr,
6721da177e4SLinus Torvalds NULL,
6731da177e4SLinus Torvalds };
67476e0377bSSebastian Ott static struct attribute_group vmlogrdr_attr_group = {
67576e0377bSSebastian Ott .attrs = vmlogrdr_attrs,
67676e0377bSSebastian Ott };
67776e0377bSSebastian Ott static const struct attribute_group *vmlogrdr_attr_groups[] = {
67876e0377bSSebastian Ott &vmlogrdr_attr_group,
67976e0377bSSebastian Ott NULL,
68076e0377bSSebastian Ott };
6811da177e4SLinus Torvalds
68256b22935Sgregkh@suse.de static struct class *vmlogrdr_class;
6831da177e4SLinus Torvalds static struct device_driver vmlogrdr_driver = {
6841da177e4SLinus Torvalds .name = "vmlogrdr",
6851da177e4SLinus Torvalds .bus = &iucv_bus,
68672f6e3a8SSebastian Ott .groups = vmlogrdr_drv_attr_groups,
6871da177e4SLinus Torvalds };
6881da177e4SLinus Torvalds
vmlogrdr_register_driver(void)689c9101c5bSMartin Schwidefsky static int vmlogrdr_register_driver(void)
690c9101c5bSMartin Schwidefsky {
6911da177e4SLinus Torvalds int ret;
6921da177e4SLinus Torvalds
693c9101c5bSMartin Schwidefsky /* Register with iucv driver */
694c9101c5bSMartin Schwidefsky ret = iucv_register(&vmlogrdr_iucv_handler, 1);
6952f6f2521SMartin Schwidefsky if (ret)
696c9101c5bSMartin Schwidefsky goto out;
697c9101c5bSMartin Schwidefsky
6981da177e4SLinus Torvalds ret = driver_register(&vmlogrdr_driver);
6992f6f2521SMartin Schwidefsky if (ret)
700c9101c5bSMartin Schwidefsky goto out_iucv;
7011da177e4SLinus Torvalds
702*1aaba11dSGreg Kroah-Hartman vmlogrdr_class = class_create("vmlogrdr");
7031da177e4SLinus Torvalds if (IS_ERR(vmlogrdr_class)) {
7041da177e4SLinus Torvalds ret = PTR_ERR(vmlogrdr_class);
7051da177e4SLinus Torvalds vmlogrdr_class = NULL;
70672f6e3a8SSebastian Ott goto out_driver;
7071da177e4SLinus Torvalds }
7081da177e4SLinus Torvalds return 0;
7091da177e4SLinus Torvalds
710c9101c5bSMartin Schwidefsky out_driver:
7111da177e4SLinus Torvalds driver_unregister(&vmlogrdr_driver);
712c9101c5bSMartin Schwidefsky out_iucv:
713c9101c5bSMartin Schwidefsky iucv_unregister(&vmlogrdr_iucv_handler, 1);
714c9101c5bSMartin Schwidefsky out:
7151da177e4SLinus Torvalds return ret;
7161da177e4SLinus Torvalds }
7171da177e4SLinus Torvalds
7181da177e4SLinus Torvalds
vmlogrdr_unregister_driver(void)719c9101c5bSMartin Schwidefsky static void vmlogrdr_unregister_driver(void)
720c9101c5bSMartin Schwidefsky {
72156b22935Sgregkh@suse.de class_destroy(vmlogrdr_class);
7221da177e4SLinus Torvalds vmlogrdr_class = NULL;
7231da177e4SLinus Torvalds driver_unregister(&vmlogrdr_driver);
724c9101c5bSMartin Schwidefsky iucv_unregister(&vmlogrdr_iucv_handler, 1);
7251da177e4SLinus Torvalds }
7261da177e4SLinus Torvalds
7271da177e4SLinus Torvalds
vmlogrdr_register_device(struct vmlogrdr_priv_t * priv)728c9101c5bSMartin Schwidefsky static int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv)
729c9101c5bSMartin Schwidefsky {
7301da177e4SLinus Torvalds struct device *dev;
7311da177e4SLinus Torvalds int ret;
7321da177e4SLinus Torvalds
73388abaab4SEric Sesterhenn dev = kzalloc(sizeof(struct device), GFP_KERNEL);
7341da177e4SLinus Torvalds if (dev) {
735ef283688SKees Cook dev_set_name(dev, "%s", priv->internal_name);
7361da177e4SLinus Torvalds dev->bus = &iucv_bus;
7371da177e4SLinus Torvalds dev->parent = iucv_root;
7381da177e4SLinus Torvalds dev->driver = &vmlogrdr_driver;
73976e0377bSSebastian Ott dev->groups = vmlogrdr_attr_groups;
7404f0076f7SMartin Schwidefsky dev_set_drvdata(dev, priv);
7411da177e4SLinus Torvalds /*
7421da177e4SLinus Torvalds * The release function could be called after the
7431da177e4SLinus Torvalds * module has been unloaded. It's _only_ task is to
7441da177e4SLinus Torvalds * free the struct. Therefore, we specify kfree()
7451da177e4SLinus Torvalds * directly here. (Probably a little bit obfuscating
7461da177e4SLinus Torvalds * but legitime ...).
7471da177e4SLinus Torvalds */
7481da177e4SLinus Torvalds dev->release = (void (*)(struct device *))kfree;
7491da177e4SLinus Torvalds } else
7501da177e4SLinus Torvalds return -ENOMEM;
7511da177e4SLinus Torvalds ret = device_register(dev);
752c6304933SSebastian Ott if (ret) {
753c6304933SSebastian Ott put_device(dev);
7541da177e4SLinus Torvalds return ret;
755c6304933SSebastian Ott }
7561da177e4SLinus Torvalds
757ea9e42f6SGreg Kroah-Hartman priv->class_device = device_create(vmlogrdr_class, dev,
7587f021ce1SCornelia Huck MKDEV(vmlogrdr_major,
7597f021ce1SCornelia Huck priv->minor_num),
7602a0217d5SKay Sievers priv, "%s", dev_name(dev));
7611da177e4SLinus Torvalds if (IS_ERR(priv->class_device)) {
7621da177e4SLinus Torvalds ret = PTR_ERR(priv->class_device);
7631da177e4SLinus Torvalds priv->class_device=NULL;
7641da177e4SLinus Torvalds device_unregister(dev);
7651da177e4SLinus Torvalds return ret;
7661da177e4SLinus Torvalds }
7671da177e4SLinus Torvalds priv->device = dev;
7681da177e4SLinus Torvalds return 0;
7691da177e4SLinus Torvalds }
7701da177e4SLinus Torvalds
7711da177e4SLinus Torvalds
vmlogrdr_unregister_device(struct vmlogrdr_priv_t * priv)772c9101c5bSMartin Schwidefsky static int vmlogrdr_unregister_device(struct vmlogrdr_priv_t *priv)
773c9101c5bSMartin Schwidefsky {
7747f021ce1SCornelia Huck device_destroy(vmlogrdr_class, MKDEV(vmlogrdr_major, priv->minor_num));
7751da177e4SLinus Torvalds if (priv->device != NULL) {
7761da177e4SLinus Torvalds device_unregister(priv->device);
7771da177e4SLinus Torvalds priv->device=NULL;
7781da177e4SLinus Torvalds }
7791da177e4SLinus Torvalds return 0;
7801da177e4SLinus Torvalds }
7811da177e4SLinus Torvalds
7821da177e4SLinus Torvalds
vmlogrdr_register_cdev(dev_t dev)783c9101c5bSMartin Schwidefsky static int vmlogrdr_register_cdev(dev_t dev)
784c9101c5bSMartin Schwidefsky {
7851da177e4SLinus Torvalds int rc = 0;
7861da177e4SLinus Torvalds vmlogrdr_cdev = cdev_alloc();
7871da177e4SLinus Torvalds if (!vmlogrdr_cdev) {
7881da177e4SLinus Torvalds return -ENOMEM;
7891da177e4SLinus Torvalds }
7901da177e4SLinus Torvalds vmlogrdr_cdev->owner = THIS_MODULE;
7911da177e4SLinus Torvalds vmlogrdr_cdev->ops = &vmlogrdr_fops;
792b08e19deSJean Delvare rc = cdev_add(vmlogrdr_cdev, dev, MAXMINOR);
7931da177e4SLinus Torvalds if (!rc)
7941da177e4SLinus Torvalds return 0;
7951da177e4SLinus Torvalds
7961da177e4SLinus Torvalds // cleanup: cdev is not fully registered, no cdev_del here!
7971da177e4SLinus Torvalds kobject_put(&vmlogrdr_cdev->kobj);
7981da177e4SLinus Torvalds vmlogrdr_cdev=NULL;
7991da177e4SLinus Torvalds return rc;
8001da177e4SLinus Torvalds }
8011da177e4SLinus Torvalds
8021da177e4SLinus Torvalds
vmlogrdr_cleanup(void)803c9101c5bSMartin Schwidefsky static void vmlogrdr_cleanup(void)
804c9101c5bSMartin Schwidefsky {
8051da177e4SLinus Torvalds int i;
806c9101c5bSMartin Schwidefsky
8071da177e4SLinus Torvalds if (vmlogrdr_cdev) {
8081da177e4SLinus Torvalds cdev_del(vmlogrdr_cdev);
8091da177e4SLinus Torvalds vmlogrdr_cdev=NULL;
8101da177e4SLinus Torvalds }
8111da177e4SLinus Torvalds for (i=0; i < MAXMINOR; ++i ) {
8121da177e4SLinus Torvalds vmlogrdr_unregister_device(&sys_ser[i]);
8131da177e4SLinus Torvalds free_page((unsigned long)sys_ser[i].buffer);
8141da177e4SLinus Torvalds }
8151da177e4SLinus Torvalds vmlogrdr_unregister_driver();
8161da177e4SLinus Torvalds if (vmlogrdr_major) {
8171da177e4SLinus Torvalds unregister_chrdev_region(MKDEV(vmlogrdr_major, 0), MAXMINOR);
8181da177e4SLinus Torvalds vmlogrdr_major=0;
8191da177e4SLinus Torvalds }
8201da177e4SLinus Torvalds }
8211da177e4SLinus Torvalds
8221da177e4SLinus Torvalds
vmlogrdr_init(void)823f60d8910SChristian Borntraeger static int __init vmlogrdr_init(void)
8241da177e4SLinus Torvalds {
8251da177e4SLinus Torvalds int rc;
8261da177e4SLinus Torvalds int i;
8271da177e4SLinus Torvalds dev_t dev;
8281da177e4SLinus Torvalds
8291da177e4SLinus Torvalds if (! MACHINE_IS_VM) {
8305466c2e4SMartin Schwidefsky pr_err("not running under VM, driver not loaded.\n");
8311da177e4SLinus Torvalds return -ENODEV;
8321da177e4SLinus Torvalds }
8331da177e4SLinus Torvalds
8341da177e4SLinus Torvalds recording_class_AB = vmlogrdr_get_recording_class_AB();
8351da177e4SLinus Torvalds
8361da177e4SLinus Torvalds rc = alloc_chrdev_region(&dev, 0, MAXMINOR, "vmlogrdr");
8371da177e4SLinus Torvalds if (rc)
8381da177e4SLinus Torvalds return rc;
8391da177e4SLinus Torvalds vmlogrdr_major = MAJOR(dev);
8401da177e4SLinus Torvalds
8411da177e4SLinus Torvalds rc=vmlogrdr_register_driver();
8421da177e4SLinus Torvalds if (rc)
8431da177e4SLinus Torvalds goto cleanup;
8441da177e4SLinus Torvalds
8451da177e4SLinus Torvalds for (i=0; i < MAXMINOR; ++i ) {
8465457e03dSGerald Schaefer sys_ser[i].buffer = (char *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
8471da177e4SLinus Torvalds if (!sys_ser[i].buffer) {
8483cb2cea1SMarcin Slusarz rc = -ENOMEM;
8491da177e4SLinus Torvalds break;
8501da177e4SLinus Torvalds }
8511da177e4SLinus Torvalds sys_ser[i].current_position = sys_ser[i].buffer;
8521da177e4SLinus Torvalds rc=vmlogrdr_register_device(&sys_ser[i]);
8531da177e4SLinus Torvalds if (rc)
8541da177e4SLinus Torvalds break;
8551da177e4SLinus Torvalds }
8561da177e4SLinus Torvalds if (rc)
8571da177e4SLinus Torvalds goto cleanup;
8581da177e4SLinus Torvalds
8591da177e4SLinus Torvalds rc = vmlogrdr_register_cdev(dev);
8601da177e4SLinus Torvalds if (rc)
8611da177e4SLinus Torvalds goto cleanup;
8621da177e4SLinus Torvalds return 0;
8631da177e4SLinus Torvalds
8641da177e4SLinus Torvalds cleanup:
8651da177e4SLinus Torvalds vmlogrdr_cleanup();
8661da177e4SLinus Torvalds return rc;
8671da177e4SLinus Torvalds }
8681da177e4SLinus Torvalds
8691da177e4SLinus Torvalds
vmlogrdr_exit(void)870f60d8910SChristian Borntraeger static void __exit vmlogrdr_exit(void)
8711da177e4SLinus Torvalds {
8721da177e4SLinus Torvalds vmlogrdr_cleanup();
8731da177e4SLinus Torvalds return;
8741da177e4SLinus Torvalds }
8751da177e4SLinus Torvalds
8761da177e4SLinus Torvalds
8771da177e4SLinus Torvalds module_init(vmlogrdr_init);
8781da177e4SLinus Torvalds module_exit(vmlogrdr_exit);
879