aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/char
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/char')
-rw-r--r--drivers/s390/char/Kconfig10
-rw-r--r--drivers/s390/char/Makefile1
-rw-r--r--drivers/s390/char/monreader.c2
-rw-r--r--drivers/s390/char/sclp.h4
-rw-r--r--drivers/s390/char/sclp_async.c224
-rw-r--r--drivers/s390/char/tape_34xx.c2
-rw-r--r--drivers/s390/char/tape_3590.c4
-rw-r--r--drivers/s390/char/tape_block.c12
-rw-r--r--drivers/s390/char/tape_core.c18
-rw-r--r--drivers/s390/char/tape_std.c2
-rw-r--r--drivers/s390/char/vmlogrdr.c4
-rw-r--r--drivers/s390/char/vmur.c19
-rw-r--r--drivers/s390/char/zcore.c2
13 files changed, 272 insertions, 32 deletions
diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig
index 0769ced52dbd..4e34d3686c23 100644
--- a/drivers/s390/char/Kconfig
+++ b/drivers/s390/char/Kconfig
@@ -82,6 +82,16 @@ config SCLP_CPI
You should only select this option if you know what you are doing,
need this feature and intend to run your kernel in LPAR.
+config SCLP_ASYNC
+ tristate "Support for Call Home via Asynchronous SCLP Records"
+ depends on S390
+ help
+ This option enables the call home function, which is able to inform
+ the service element and connected organisations about a kernel panic.
+ You should only select this option if you know what you are doing,
+ want for inform other people about your kernel panics,
+ need this feature and intend to run your kernel in LPAR.
+
config S390_TAPE
tristate "S/390 tape device support"
depends on CCW
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile
index 7e73e39a1741..efb500ab66c0 100644
--- a/drivers/s390/char/Makefile
+++ b/drivers/s390/char/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_SCLP_TTY) += sclp_tty.o
obj-$(CONFIG_SCLP_CONSOLE) += sclp_con.o
obj-$(CONFIG_SCLP_VT220_TTY) += sclp_vt220.o
obj-$(CONFIG_SCLP_CPI) += sclp_cpi.o
+obj-$(CONFIG_SCLP_ASYNC) += sclp_async.o
obj-$(CONFIG_ZVM_WATCHDOG) += vmwatchdog.o
obj-$(CONFIG_VMLOGRDR) += vmlogrdr.o
diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c
index 3234e90bd7f9..89ece1c235aa 100644
--- a/drivers/s390/char/monreader.c
+++ b/drivers/s390/char/monreader.c
@@ -581,7 +581,7 @@ static int __init mon_init(void)
monreader_device->release = (void (*)(struct device *))kfree;
rc = device_register(monreader_device);
if (rc) {
- kfree(monreader_device);
+ put_device(monreader_device);
goto out_driver;
}
diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h
index 60e7cb07095b..6bb5a6bdfab5 100644
--- a/drivers/s390/char/sclp.h
+++ b/drivers/s390/char/sclp.h
@@ -27,6 +27,7 @@
#define EVTYP_VT220MSG 0x1A
#define EVTYP_CONFMGMDATA 0x04
#define EVTYP_SDIAS 0x1C
+#define EVTYP_ASYNC 0x0A
#define EVTYP_OPCMD_MASK 0x80000000
#define EVTYP_MSG_MASK 0x40000000
@@ -38,6 +39,7 @@
#define EVTYP_VT220MSG_MASK 0x00000040
#define EVTYP_CONFMGMDATA_MASK 0x10000000
#define EVTYP_SDIAS_MASK 0x00000010
+#define EVTYP_ASYNC_MASK 0x00400000
#define GNRLMSGFLGS_DOM 0x8000
#define GNRLMSGFLGS_SNDALRM 0x4000
@@ -85,12 +87,12 @@ struct sccb_header {
} __attribute__((packed));
extern u64 sclp_facilities;
-
#define SCLP_HAS_CHP_INFO (sclp_facilities & 0x8000000000000000ULL)
#define SCLP_HAS_CHP_RECONFIG (sclp_facilities & 0x2000000000000000ULL)
#define SCLP_HAS_CPU_INFO (sclp_facilities & 0x0800000000000000ULL)
#define SCLP_HAS_CPU_RECONFIG (sclp_facilities & 0x0400000000000000ULL)
+
struct gds_subvector {
u8 length;
u8 key;
diff --git a/drivers/s390/char/sclp_async.c b/drivers/s390/char/sclp_async.c
new file mode 100644
index 000000000000..daaec185ed36
--- /dev/null
+++ b/drivers/s390/char/sclp_async.c
@@ -0,0 +1,224 @@
+/*
+ * Enable Asynchronous Notification via SCLP.
+ *
+ * Copyright IBM Corp. 2009
+ * Author(s): Hans-Joachim Picht <hans@linux.vnet.ibm.com>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/kmod.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/proc_fs.h>
+#include <linux/sysctl.h>
+#include <linux/utsname.h>
+#include "sclp.h"
+
+static int callhome_enabled;
+static struct sclp_req *request;
+static struct sclp_async_sccb *sccb;
+static int sclp_async_send_wait(char *message);
+static struct ctl_table_header *callhome_sysctl_header;
+static DEFINE_SPINLOCK(sclp_async_lock);
+static char nodename[64];
+#define SCLP_NORMAL_WRITE 0x00
+
+struct async_evbuf {
+ struct evbuf_header header;
+ u64 reserved;
+ u8 rflags;
+ u8 empty;
+ u8 rtype;
+ u8 otype;
+ char comp_id[12];
+ char data[3000]; /* there is still some space left */
+} __attribute__((packed));
+
+struct sclp_async_sccb {
+ struct sccb_header header;
+ struct async_evbuf evbuf;
+} __attribute__((packed));
+
+static struct sclp_register sclp_async_register = {
+ .send_mask = EVTYP_ASYNC_MASK,
+};
+
+static int call_home_on_panic(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ strncat(data, nodename, strlen(nodename));
+ sclp_async_send_wait(data);
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block call_home_panic_nb = {
+ .notifier_call = call_home_on_panic,
+ .priority = INT_MAX,
+};
+
+static int proc_handler_callhome(ctl_table *ctl, int write, struct file *filp,
+ void __user *buffer, size_t *count,
+ loff_t *ppos)
+{
+ unsigned long val;
+ int len, rc;
+ char buf[2];
+
+ if (!*count | (*ppos && !write)) {
+ *count = 0;
+ return 0;
+ }
+ if (!write) {
+ len = sprintf(buf, "%d\n", callhome_enabled);
+ buf[len] = '\0';
+ rc = copy_to_user(buffer, buf, sizeof(buf));
+ if (rc != 0)
+ return -EFAULT;
+ } else {
+ len = *count;
+ rc = copy_from_user(buf, buffer, sizeof(buf));
+ if (rc != 0)
+ return -EFAULT;
+ if (strict_strtoul(buf, 0, &val) != 0)
+ return -EINVAL;
+ if (val != 0 && val != 1)
+ return -EINVAL;
+ callhome_enabled = val;
+ }
+ *count = len;
+ *ppos += len;
+ return 0;
+}
+
+static struct ctl_table callhome_table[] = {
+ {
+ .procname = "callhome",
+ .mode = 0644,
+ .proc_handler = &proc_handler_callhome,
+ },
+ { .ctl_name = 0 }
+};
+
+static struct ctl_table kern_dir_table[] = {
+ {
+ .ctl_name = CTL_KERN,
+ .procname = "kernel",
+ .maxlen = 0,
+ .mode = 0555,
+ .child = callhome_table,
+ },
+ { .ctl_name = 0 }
+};
+
+/*
+ * Function used to transfer asynchronous notification
+ * records which waits for send completion
+ */
+static int sclp_async_send_wait(char *message)
+{
+ struct async_evbuf *evb;
+ int rc;
+ unsigned long flags;
+
+ if (!callhome_enabled)
+ return 0;
+ sccb->evbuf.header.type = EVTYP_ASYNC;
+ sccb->evbuf.rtype = 0xA5;
+ sccb->evbuf.otype = 0x00;
+ evb = &sccb->evbuf;
+ request->command = SCLP_CMDW_WRITE_EVENT_DATA;
+ request->sccb = sccb;
+ request->status = SCLP_REQ_FILLED;
+ strncpy(sccb->evbuf.data, message, sizeof(sccb->evbuf.data));
+ /*
+ * Retain Queue
+ * e.g. 5639CC140 500 Red Hat RHEL5 Linux for zSeries (RHEL AS)
+ */
+ strncpy(sccb->evbuf.comp_id, "000000000", sizeof(sccb->evbuf.comp_id));
+ sccb->evbuf.header.length = sizeof(sccb->evbuf);
+ sccb->header.length = sizeof(sccb->evbuf) + sizeof(sccb->header);
+ sccb->header.function_code = SCLP_NORMAL_WRITE;
+ rc = sclp_add_request(request);
+ if (rc)
+ return rc;
+ spin_lock_irqsave(&sclp_async_lock, flags);
+ while (request->status != SCLP_REQ_DONE &&
+ request->status != SCLP_REQ_FAILED) {
+ sclp_sync_wait();
+ }
+ spin_unlock_irqrestore(&sclp_async_lock, flags);
+ if (request->status != SCLP_REQ_DONE)
+ return -EIO;
+ rc = ((struct sclp_async_sccb *)
+ request->sccb)->header.response_code;
+ if (rc != 0x0020)
+ return -EIO;
+ if (evb->header.flags != 0x80)
+ return -EIO;
+ return rc;
+}
+
+static int __init sclp_async_init(void)
+{
+ int rc;
+
+ rc = sclp_register(&sclp_async_register);
+ if (rc)
+ return rc;
+ callhome_sysctl_header = register_sysctl_table(kern_dir_table);
+ if (!callhome_sysctl_header) {
+ rc = -ENOMEM;
+ goto out_sclp;
+ }
+ if (!(sclp_async_register.sclp_receive_mask & EVTYP_ASYNC_MASK)) {
+ rc = -EOPNOTSUPP;
+ goto out_sclp;
+ }
+ rc = -ENOMEM;
+ request = kzalloc(sizeof(struct sclp_req), GFP_KERNEL);
+ if (!request)
+ goto out_sys;
+ sccb = (struct sclp_async_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!sccb)
+ goto out_mem;
+ rc = atomic_notifier_chain_register(&panic_notifier_list,
+ &call_home_panic_nb);
+ if (rc)
+ goto out_mem;
+
+ strncpy(nodename, init_utsname()->nodename, 64);
+ return 0;
+
+out_mem:
+ kfree(request);
+ free_page((unsigned long) sccb);
+out_sys:
+ unregister_sysctl_table(callhome_sysctl_header);
+out_sclp:
+ sclp_unregister(&sclp_async_register);
+ return rc;
+
+}
+module_init(sclp_async_init);
+
+static void __exit sclp_async_exit(void)
+{
+ atomic_notifier_chain_unregister(&panic_notifier_list,
+ &call_home_panic_nb);
+ unregister_sysctl_table(callhome_sysctl_header);
+ sclp_unregister(&sclp_async_register);
+ free_page((unsigned long) sccb);
+ kfree(request);
+}
+module_exit(sclp_async_exit);
+
+MODULE_AUTHOR("Copyright IBM Corp. 2009");
+MODULE_AUTHOR("Hans-Joachim Picht <hans@linux.vnet.ibm.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SCLP Asynchronous Notification Records");
diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c
index 5a519fac37b7..2fe45ff77b75 100644
--- a/drivers/s390/char/tape_34xx.c
+++ b/drivers/s390/char/tape_34xx.c
@@ -8,7 +8,7 @@
* Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
-#define KMSG_COMPONENT "tape"
+#define KMSG_COMPONENT "tape_34xx"
#include <linux/module.h>
#include <linux/init.h>
diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c
index 418f72dd39b4..e4cc3aae9162 100644
--- a/drivers/s390/char/tape_3590.c
+++ b/drivers/s390/char/tape_3590.c
@@ -8,7 +8,7 @@
* Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
-#define KMSG_COMPONENT "tape"
+#define KMSG_COMPONENT "tape_3590"
#include <linux/module.h>
#include <linux/init.h>
@@ -39,8 +39,6 @@ EXPORT_SYMBOL(TAPE_DBF_AREA);
* - Read Alternate: implemented
*******************************************************************/
-#define KMSG_COMPONENT "tape"
-
static const char *tape_3590_msg[TAPE_3590_MAX_MSG] = {
[0x00] = "",
[0x10] = "Lost Sense",
diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c
index 47ff695255ea..4cb9e70507ab 100644
--- a/drivers/s390/char/tape_block.c
+++ b/drivers/s390/char/tape_block.c
@@ -302,8 +302,6 @@ tapeblock_revalidate_disk(struct gendisk *disk)
if (!device->blk_data.medium_changed)
return 0;
- dev_info(&device->cdev->dev, "Determining the size of the recorded "
- "area...\n");
rc = tape_mtop(device, MTFSFM, 1);
if (rc)
return rc;
@@ -312,6 +310,8 @@ tapeblock_revalidate_disk(struct gendisk *disk)
if (rc < 0)
return rc;
+ pr_info("%s: Determining the size of the recorded area...\n",
+ dev_name(&device->cdev->dev));
DBF_LH(3, "Image file ends at %d\n", rc);
nr_of_blks = rc;
@@ -330,8 +330,8 @@ tapeblock_revalidate_disk(struct gendisk *disk)
device->bof = rc;
nr_of_blks -= rc;
- dev_info(&device->cdev->dev, "The size of the recorded area is %i "
- "blocks\n", nr_of_blks);
+ pr_info("%s: The size of the recorded area is %i blocks\n",
+ dev_name(&device->cdev->dev), nr_of_blks);
set_capacity(device->blk_data.disk,
nr_of_blks*(TAPEBLOCK_HSEC_SIZE/512));
@@ -366,8 +366,8 @@ tapeblock_open(struct block_device *bdev, fmode_t mode)
if (device->required_tapemarks) {
DBF_EVENT(2, "TBLOCK: missing tapemarks\n");
- dev_warn(&device->cdev->dev, "Opening the tape failed because"
- " of missing end-of-file marks\n");
+ pr_warning("%s: Opening the tape failed because of missing "
+ "end-of-file marks\n", dev_name(&device->cdev->dev));
rc = -EPERM;
goto put_device;
}
diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c
index 1d420d947596..5cd31e071647 100644
--- a/drivers/s390/char/tape_core.c
+++ b/drivers/s390/char/tape_core.c
@@ -214,13 +214,15 @@ tape_med_state_set(struct tape_device *device, enum tape_medium_state newstate)
switch(newstate){
case MS_UNLOADED:
device->tape_generic_status |= GMT_DR_OPEN(~0);
- dev_info(&device->cdev->dev, "The tape cartridge has been "
- "successfully unloaded\n");
+ if (device->medium_state == MS_LOADED)
+ pr_info("%s: The tape cartridge has been successfully "
+ "unloaded\n", dev_name(&device->cdev->dev));
break;
case MS_LOADED:
device->tape_generic_status &= ~GMT_DR_OPEN(~0);
- dev_info(&device->cdev->dev, "A tape cartridge has been "
- "mounted\n");
+ if (device->medium_state == MS_UNLOADED)
+ pr_info("%s: A tape cartridge has been mounted\n",
+ dev_name(&device->cdev->dev));
break;
default:
// print nothing
@@ -358,11 +360,11 @@ tape_generic_online(struct tape_device *device,
out_char:
tapechar_cleanup_device(device);
+out_minor:
+ tape_remove_minor(device);
out_discipline:
device->discipline->cleanup_device(device);
device->discipline = NULL;
-out_minor:
- tape_remove_minor(device);
out:
module_put(discipline->owner);
return rc;
@@ -654,8 +656,8 @@ tape_generic_remove(struct ccw_device *cdev)
*/
DBF_EVENT(3, "(%08x): Drive in use vanished!\n",
device->cdev_id);
- dev_warn(&device->cdev->dev, "A tape unit was detached"
- " while in use\n");
+ pr_warning("%s: A tape unit was detached while in "
+ "use\n", dev_name(&device->cdev->dev));
tape_state_set(device, TS_NOT_OPER);
__tape_discard_requests(device);
spin_unlock_irq(get_ccwdev_lock(device->cdev));
diff --git a/drivers/s390/char/tape_std.c b/drivers/s390/char/tape_std.c
index 1a9420ba518d..750354ad16e5 100644
--- a/drivers/s390/char/tape_std.c
+++ b/drivers/s390/char/tape_std.c
@@ -68,7 +68,7 @@ tape_std_assign(struct tape_device *device)
* to another host (actually this shouldn't happen but it does).
* So we set up a timeout for this call.
*/
- init_timer(&timeout);
+ init_timer_on_stack(&timeout);
timeout.function = tape_std_assign_timeout;
timeout.data = (unsigned long) request;
timeout.expires = jiffies + 2 * HZ;
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
index c20a4fe6da51..d1a142fa3eb4 100644
--- a/drivers/s390/char/vmlogrdr.c
+++ b/drivers/s390/char/vmlogrdr.c
@@ -765,8 +765,10 @@ static int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv)
} else
return -ENOMEM;
ret = device_register(dev);
- if (ret)
+ if (ret) {
+ put_device(dev);
return ret;
+ }
ret = sysfs_create_group(&dev->kobj, &vmlogrdr_attr_group);
if (ret) {
diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c
index 31b902e94f7b..77571b68539a 100644
--- a/drivers/s390/char/vmur.c
+++ b/drivers/s390/char/vmur.c
@@ -1026,9 +1026,15 @@ static int __init ur_init(void)
debug_set_level(vmur_dbf, 6);
+ vmur_class = class_create(THIS_MODULE, "vmur");
+ if (IS_ERR(vmur_class)) {
+ rc = PTR_ERR(vmur_class);
+ goto fail_free_dbf;
+ }
+
rc = ccw_driver_register(&ur_driver);
if (rc)
- goto fail_free_dbf;
+ goto fail_class_destroy;
rc = alloc_chrdev_region(&dev, 0, NUM_MINORS, "vmur");
if (rc) {
@@ -1038,18 +1044,13 @@ static int __init ur_init(void)
}
ur_first_dev_maj_min = MKDEV(MAJOR(dev), 0);
- vmur_class = class_create(THIS_MODULE, "vmur");
- if (IS_ERR(vmur_class)) {
- rc = PTR_ERR(vmur_class);
- goto fail_unregister_region;
- }
pr_info("%s loaded.\n", ur_banner);
return 0;
-fail_unregister_region:
- unregister_chrdev_region(ur_first_dev_maj_min, NUM_MINORS);
fail_unregister_driver:
ccw_driver_unregister(&ur_driver);
+fail_class_destroy:
+ class_destroy(vmur_class);
fail_free_dbf:
debug_unregister(vmur_dbf);
return rc;
@@ -1057,9 +1058,9 @@ fail_free_dbf:
static void __exit ur_exit(void)
{
- class_destroy(vmur_class);
unregister_chrdev_region(ur_first_dev_maj_min, NUM_MINORS);
ccw_driver_unregister(&ur_driver);
+ class_destroy(vmur_class);
debug_unregister(vmur_dbf);
pr_info("%s unloaded.\n", ur_banner);
}
diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c
index 1bbae433fbd8..c431198bdbc4 100644
--- a/drivers/s390/char/zcore.c
+++ b/drivers/s390/char/zcore.c
@@ -275,7 +275,7 @@ struct zcore_header {
u32 num_pages;
u32 pad1;
u64 tod;
- cpuid_t cpu_id;
+ struct cpuid cpu_id;
u32 arch_id;
u32 volnr;
u32 build_arch;