diff options
Diffstat (limited to 'drivers/s390/char/vmlogrdr.c')
| -rw-r--r-- | drivers/s390/char/vmlogrdr.c | 139 |
1 files changed, 86 insertions, 53 deletions
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index d8a2289fcb6..a8848db7b09 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -1,9 +1,8 @@ /* - * drivers/s390/char/vmlogrdr.c * character device driver for reading z/VM system service records * * - * Copyright 2004 IBM Corporation + * Copyright IBM Corp. 2004, 2009 * character device driver for reading z/VM system service records, * Version 1.0 * Author(s): Xenia Tkatschow <xenia@us.ibm.com> @@ -16,11 +15,12 @@ #include <linux/module.h> #include <linux/init.h> +#include <linux/slab.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/interrupt.h> #include <linux/spinlock.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include <asm/uaccess.h> #include <asm/cpcmd.h> #include <asm/debug.h> @@ -29,7 +29,6 @@ #include <linux/kmod.h> #include <linux/cdev.h> #include <linux/device.h> -#include <linux/smp_lock.h> #include <linux/string.h> MODULE_AUTHOR @@ -96,6 +95,7 @@ static const struct file_operations vmlogrdr_fops = { .open = vmlogrdr_open, .release = vmlogrdr_release, .read = vmlogrdr_read, + .llseek = no_llseek, }; @@ -213,7 +213,7 @@ static void vmlogrdr_iucv_message_pending(struct iucv_path *path, static int vmlogrdr_get_recording_class_AB(void) { - char cp_command[]="QUERY COMMAND RECORDING "; + static const char cp_command[] = "QUERY COMMAND RECORDING "; char cp_response[80]; char *tail; int len,i; @@ -247,27 +247,25 @@ static int vmlogrdr_recording(struct vmlogrdr_priv_t * logptr, char cp_command[80]; char cp_response[160]; char *onoff, *qid_string; + int rc; - memset(cp_command, 0x00, sizeof(cp_command)); - memset(cp_response, 0x00, sizeof(cp_response)); - - onoff = ((action == 1) ? "ON" : "OFF"); + onoff = ((action == 1) ? "ON" : "OFF"); qid_string = ((recording_class_AB == 1) ? " QID * " : ""); - /* + /* * The recording commands needs to be called with option QID * for guests that have previlege classes A or B. * Purging has to be done as separate step, because recording * can't be switched on as long as records are on the queue. * Doing both at the same time doesn't work. */ - - if (purge) { + if (purge && (action == 1)) { + memset(cp_command, 0x00, sizeof(cp_command)); + memset(cp_response, 0x00, sizeof(cp_response)); snprintf(cp_command, sizeof(cp_command), "RECORDING %s PURGE %s", logptr->recording_name, qid_string); - cpcmd(cp_command, cp_response, sizeof(cp_response), NULL); } @@ -277,19 +275,33 @@ static int vmlogrdr_recording(struct vmlogrdr_priv_t * logptr, logptr->recording_name, onoff, qid_string); - cpcmd(cp_command, cp_response, sizeof(cp_response), NULL); /* The recording command will usually answer with 'Command complete' * on success, but when the specific service was never connected * before then there might be an additional informational message * 'HCPCRC8072I Recording entry not found' before the - * 'Command complete'. So I use strstr rather then the strncmp. + * 'Command complete'. So I use strstr rather then the strncmp. */ if (strstr(cp_response,"Command complete")) - return 0; + rc = 0; else - return -EIO; + rc = -EIO; + /* + * If we turn recording off, we have to purge any remaining records + * afterwards, as a large number of queued records may impact z/VM + * performance. + */ + if (purge && (action == 0)) { + memset(cp_command, 0x00, sizeof(cp_command)); + memset(cp_response, 0x00, sizeof(cp_response)); + snprintf(cp_command, sizeof(cp_command), + "RECORDING %s PURGE %s", + logptr->recording_name, + qid_string); + cpcmd(cp_command, cp_response, sizeof(cp_response), NULL); + } + return rc; } @@ -301,7 +313,7 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp) int ret; dev_num = iminor(inode); - if (dev_num > MAXMINOR) + if (dev_num >= MAXMINOR) return -ENODEV; logptr = &sys_ser[dev_num]; @@ -309,14 +321,12 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp) * only allow for blocking reads to be open */ if (filp->f_flags & O_NONBLOCK) - return -ENOSYS; + return -EOPNOTSUPP; /* Besure this device hasn't already been opened */ - lock_kernel(); spin_lock_bh(&logptr->priv_lock); if (logptr->dev_in_use) { spin_unlock_bh(&logptr->priv_lock); - unlock_kernel(); return -EBUSY; } logptr->dev_in_use = 1; @@ -360,9 +370,8 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp) || (logptr->iucv_path_severed)); if (logptr->iucv_path_severed) goto out_record; - ret = nonseekable_open(inode, filp); - unlock_kernel(); - return ret; + nonseekable_open(inode, filp); + return 0; out_record: if (logptr->autorecording) @@ -372,7 +381,6 @@ out_path: logptr->path = NULL; out_dev: logptr->dev_in_use = 0; - unlock_kernel(); return -EIO; } @@ -504,7 +512,7 @@ static ssize_t vmlogrdr_autopurge_store(struct device * dev, struct device_attribute *attr, const char * buf, size_t count) { - struct vmlogrdr_priv_t *priv = dev->driver_data; + struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev); ssize_t ret = count; switch (buf[0]) { @@ -525,7 +533,7 @@ static ssize_t vmlogrdr_autopurge_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct vmlogrdr_priv_t *priv = dev->driver_data; + struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev); return sprintf(buf, "%u\n", priv->autopurge); } @@ -541,7 +549,7 @@ static ssize_t vmlogrdr_purge_store(struct device * dev, char cp_command[80]; char cp_response[80]; - struct vmlogrdr_priv_t *priv = dev->driver_data; + struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev); if (buf[0] != '1') return -EINVAL; @@ -578,7 +586,7 @@ static ssize_t vmlogrdr_autorecording_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct vmlogrdr_priv_t *priv = dev->driver_data; + struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev); ssize_t ret = count; switch (buf[0]) { @@ -599,7 +607,7 @@ static ssize_t vmlogrdr_autorecording_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct vmlogrdr_priv_t *priv = dev->driver_data; + struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev); return sprintf(buf, "%u\n", priv->autorecording); } @@ -612,7 +620,7 @@ static ssize_t vmlogrdr_recording_store(struct device * dev, struct device_attribute *attr, const char * buf, size_t count) { - struct vmlogrdr_priv_t *priv = dev->driver_data; + struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev); ssize_t ret; switch (buf[0]) { @@ -640,17 +648,26 @@ static ssize_t vmlogrdr_recording_status_show(struct device_driver *driver, char *buf) { - char cp_command[] = "QUERY RECORDING "; + static const char cp_command[] = "QUERY RECORDING "; int len; cpcmd(cp_command, buf, 4096, NULL); len = strlen(buf); return len; } - - static DRIVER_ATTR(recording_status, 0444, vmlogrdr_recording_status_show, NULL); +static struct attribute *vmlogrdr_drv_attrs[] = { + &driver_attr_recording_status.attr, + NULL, +}; +static struct attribute_group vmlogrdr_drv_attr_group = { + .attrs = vmlogrdr_drv_attrs, +}; +static const struct attribute_group *vmlogrdr_drv_attr_groups[] = { + &vmlogrdr_drv_attr_group, + NULL, +}; static struct attribute *vmlogrdr_attrs[] = { &dev_attr_autopurge.attr, @@ -659,18 +676,45 @@ static struct attribute *vmlogrdr_attrs[] = { &dev_attr_recording.attr, NULL, }; - static struct attribute_group vmlogrdr_attr_group = { .attrs = vmlogrdr_attrs, }; +static const struct attribute_group *vmlogrdr_attr_groups[] = { + &vmlogrdr_attr_group, + NULL, +}; + +static int vmlogrdr_pm_prepare(struct device *dev) +{ + int rc; + struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev); + + rc = 0; + if (priv) { + spin_lock_bh(&priv->priv_lock); + if (priv->dev_in_use) + rc = -EBUSY; + spin_unlock_bh(&priv->priv_lock); + } + if (rc) + pr_err("vmlogrdr: device %s is busy. Refuse to suspend.\n", + dev_name(dev)); + return rc; +} + + +static const struct dev_pm_ops vmlogrdr_pm_ops = { + .prepare = vmlogrdr_pm_prepare, +}; static struct class *vmlogrdr_class; static struct device_driver vmlogrdr_driver = { .name = "vmlogrdr", .bus = &iucv_bus, + .pm = &vmlogrdr_pm_ops, + .groups = vmlogrdr_drv_attr_groups, }; - static int vmlogrdr_register_driver(void) { int ret; @@ -684,21 +728,14 @@ static int vmlogrdr_register_driver(void) if (ret) goto out_iucv; - ret = driver_create_file(&vmlogrdr_driver, - &driver_attr_recording_status); - if (ret) - goto out_driver; - vmlogrdr_class = class_create(THIS_MODULE, "vmlogrdr"); if (IS_ERR(vmlogrdr_class)) { ret = PTR_ERR(vmlogrdr_class); vmlogrdr_class = NULL; - goto out_attr; + goto out_driver; } return 0; -out_attr: - driver_remove_file(&vmlogrdr_driver, &driver_attr_recording_status); out_driver: driver_unregister(&vmlogrdr_driver); out_iucv: @@ -712,7 +749,6 @@ static void vmlogrdr_unregister_driver(void) { class_destroy(vmlogrdr_class); vmlogrdr_class = NULL; - driver_remove_file(&vmlogrdr_driver, &driver_attr_recording_status); driver_unregister(&vmlogrdr_driver); iucv_unregister(&vmlogrdr_iucv_handler, 1); } @@ -725,10 +761,12 @@ static int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv) dev = kzalloc(sizeof(struct device), GFP_KERNEL); if (dev) { - dev_set_name(dev, priv->internal_name); + dev_set_name(dev, "%s", priv->internal_name); dev->bus = &iucv_bus; dev->parent = iucv_root; dev->driver = &vmlogrdr_driver; + dev->groups = vmlogrdr_attr_groups; + dev_set_drvdata(dev, priv); /* * The release function could be called after the * module has been unloaded. It's _only_ task is to @@ -740,14 +778,11 @@ static int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv) } else return -ENOMEM; ret = device_register(dev); - if (ret) - return ret; - - ret = sysfs_create_group(&dev->kobj, &vmlogrdr_attr_group); if (ret) { - device_unregister(dev); + put_device(dev); return ret; } + priv->class_device = device_create(vmlogrdr_class, dev, MKDEV(vmlogrdr_major, priv->minor_num), @@ -755,7 +790,6 @@ static int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv) if (IS_ERR(priv->class_device)) { ret = PTR_ERR(priv->class_device); priv->class_device=NULL; - sysfs_remove_group(&dev->kobj, &vmlogrdr_attr_group); device_unregister(dev); return ret; } @@ -768,7 +802,6 @@ static int vmlogrdr_unregister_device(struct vmlogrdr_priv_t *priv) { device_destroy(vmlogrdr_class, MKDEV(vmlogrdr_major, priv->minor_num)); if (priv->device != NULL) { - sysfs_remove_group(&priv->device->kobj, &vmlogrdr_attr_group); device_unregister(priv->device); priv->device=NULL; } |
