diff options
Diffstat (limited to 'drivers/s390/char')
-rw-r--r-- | drivers/s390/char/Makefile | 2 | ||||
-rw-r--r-- | drivers/s390/char/sclp.c | 86 | ||||
-rw-r--r-- | drivers/s390/char/sclp.h | 7 | ||||
-rw-r--r-- | drivers/s390/char/sclp_cmd.c | 20 | ||||
-rw-r--r-- | drivers/s390/char/sclp_con.c | 31 | ||||
-rw-r--r-- | drivers/s390/char/sclp_ctl.c | 144 | ||||
-rw-r--r-- | drivers/s390/char/sclp_vt220.c | 39 | ||||
-rw-r--r-- | drivers/s390/char/tape_class.c | 2 | ||||
-rw-r--r-- | drivers/s390/char/vmur.c | 2 | ||||
-rw-r--r-- | drivers/s390/char/vmwatchdog.c | 5 |
10 files changed, 310 insertions, 28 deletions
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index f3c32520744..17821a026c9 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -3,7 +3,7 @@ # obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \ - sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o + sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o sclp_ctl.o obj-$(CONFIG_TN3270) += raw3270.o obj-$(CONFIG_TN3270_CONSOLE) += con3270.o diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index bd6871bf545..3e4fb4e858d 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -50,11 +50,42 @@ static char sclp_init_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); /* Suspend request */ static DECLARE_COMPLETION(sclp_request_queue_flushed); +/* Number of console pages to allocate, used by sclp_con.c and sclp_vt220.c */ +int sclp_console_pages = SCLP_CONSOLE_PAGES; +/* Flag to indicate if buffer pages are dropped on buffer full condition */ +int sclp_console_drop = 0; +/* Number of times the console dropped buffer pages */ +unsigned long sclp_console_full; + static void sclp_suspend_req_cb(struct sclp_req *req, void *data) { complete(&sclp_request_queue_flushed); } +static int __init sclp_setup_console_pages(char *str) +{ + int pages, rc; + + rc = kstrtoint(str, 0, &pages); + if (!rc && pages >= SCLP_CONSOLE_PAGES) + sclp_console_pages = pages; + return 1; +} + +__setup("sclp_con_pages=", sclp_setup_console_pages); + +static int __init sclp_setup_console_drop(char *str) +{ + int drop, rc; + + rc = kstrtoint(str, 0, &drop); + if (!rc && drop) + sclp_console_drop = 1; + return 1; +} + +__setup("sclp_con_drop=", sclp_setup_console_drop); + static struct sclp_req sclp_suspend_req; /* Timer for request retries. */ @@ -117,14 +148,19 @@ static int sclp_init(void); int sclp_service_call(sclp_cmdw_t command, void *sccb) { - int cc; + int cc = 4; /* Initialize for program check handling */ asm volatile( - " .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */ - " ipm %0\n" - " srl %0,28" - : "=&d" (cc) : "d" (command), "a" (__pa(sccb)) + "0: .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */ + "1: ipm %0\n" + " srl %0,28\n" + "2:\n" + EX_TABLE(0b, 2b) + EX_TABLE(1b, 2b) + : "+&d" (cc) : "d" (command), "a" (__pa(sccb)) : "cc", "memory"); + if (cc == 4) + return -EINVAL; if (cc == 3) return -EIO; if (cc == 2) @@ -1013,11 +1049,47 @@ static const struct dev_pm_ops sclp_pm_ops = { .restore = sclp_restore, }; +static ssize_t sclp_show_console_pages(struct device_driver *dev, char *buf) +{ + return sprintf(buf, "%i\n", sclp_console_pages); +} + +static DRIVER_ATTR(con_pages, S_IRUSR, sclp_show_console_pages, NULL); + +static ssize_t sclp_show_con_drop(struct device_driver *dev, char *buf) +{ + return sprintf(buf, "%i\n", sclp_console_drop); +} + +static DRIVER_ATTR(con_drop, S_IRUSR, sclp_show_con_drop, NULL); + +static ssize_t sclp_show_console_full(struct device_driver *dev, char *buf) +{ + return sprintf(buf, "%lu\n", sclp_console_full); +} + +static DRIVER_ATTR(con_full, S_IRUSR, sclp_show_console_full, NULL); + +static struct attribute *sclp_drv_attrs[] = { + &driver_attr_con_pages.attr, + &driver_attr_con_drop.attr, + &driver_attr_con_full.attr, + NULL, +}; +static struct attribute_group sclp_drv_attr_group = { + .attrs = sclp_drv_attrs, +}; +static const struct attribute_group *sclp_drv_attr_groups[] = { + &sclp_drv_attr_group, + NULL, +}; + static struct platform_driver sclp_pdrv = { .driver = { .name = "sclp", .owner = THIS_MODULE, .pm = &sclp_pm_ops, + .groups = sclp_drv_attr_groups, }, }; @@ -1096,10 +1168,12 @@ static __init int sclp_initcall(void) rc = platform_driver_register(&sclp_pdrv); if (rc) return rc; + sclp_pdev = platform_device_register_simple("sclp", -1, NULL, 0); - rc = IS_ERR(sclp_pdev) ? PTR_ERR(sclp_pdev) : 0; + rc = PTR_RET(sclp_pdev); if (rc) goto fail_platform_driver_unregister; + rc = atomic_notifier_chain_register(&panic_notifier_list, &sclp_on_panic_nb); if (rc) diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h index 25bcd4c0ed8..40d1406289e 100644 --- a/drivers/s390/char/sclp.h +++ b/drivers/s390/char/sclp.h @@ -15,7 +15,7 @@ /* maximum number of pages concerning our own memory management */ #define MAX_KMEM_PAGES (sizeof(unsigned long) << 3) -#define MAX_CONSOLE_PAGES 6 +#define SCLP_CONSOLE_PAGES 6 #define EVTYP_OPCMD 0x01 #define EVTYP_MSG 0x02 @@ -171,10 +171,15 @@ int sclp_remove_processed(struct sccb_header *sccb); int sclp_deactivate(void); int sclp_reactivate(void); int sclp_service_call(sclp_cmdw_t command, void *sccb); +int sclp_sync_request(sclp_cmdw_t command, void *sccb); int sclp_sdias_init(void); void sclp_sdias_exit(void); +extern int sclp_console_pages; +extern int sclp_console_drop; +extern unsigned long sclp_console_full; + /* useful inlines */ /* VM uses EBCDIC 037, LPAR+native(SE+HMC) use EBCDIC 500 */ diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index bf07c3a188d..8cd34bf644b 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -195,7 +195,7 @@ static void sclp_sync_callback(struct sclp_req *req, void *data) complete(completion); } -static int do_sync_request(sclp_cmdw_t cmd, void *sccb) +int sclp_sync_request(sclp_cmdw_t cmd, void *sccb) { struct completion completion; struct sclp_req *request; @@ -270,7 +270,7 @@ int sclp_get_cpu_info(struct sclp_cpu_info *info) if (!sccb) return -ENOMEM; sccb->header.length = sizeof(*sccb); - rc = do_sync_request(SCLP_CMDW_READ_CPU_INFO, sccb); + rc = sclp_sync_request(SCLP_CMDW_READ_CPU_INFO, sccb); if (rc) goto out; if (sccb->header.response_code != 0x0010) { @@ -304,7 +304,7 @@ static int do_cpu_configure(sclp_cmdw_t cmd) if (!sccb) return -ENOMEM; sccb->header.length = sizeof(*sccb); - rc = do_sync_request(cmd, sccb); + rc = sclp_sync_request(cmd, sccb); if (rc) goto out; switch (sccb->header.response_code) { @@ -374,7 +374,7 @@ static int do_assign_storage(sclp_cmdw_t cmd, u16 rn) return -ENOMEM; sccb->header.length = PAGE_SIZE; sccb->rn = rn; - rc = do_sync_request(cmd, sccb); + rc = sclp_sync_request(cmd, sccb); if (rc) goto out; switch (sccb->header.response_code) { @@ -429,7 +429,7 @@ static int sclp_attach_storage(u8 id) if (!sccb) return -ENOMEM; sccb->header.length = PAGE_SIZE; - rc = do_sync_request(0x00080001 | id << 8, sccb); + rc = sclp_sync_request(0x00080001 | id << 8, sccb); if (rc) goto out; switch (sccb->header.response_code) { @@ -627,7 +627,7 @@ static int __init sclp_detect_standby_memory(void) for (id = 0; id <= sclp_max_storage_id; id++) { memset(sccb, 0, PAGE_SIZE); sccb->header.length = PAGE_SIZE; - rc = do_sync_request(0x00040001 | id << 8, sccb); + rc = sclp_sync_request(0x00040001 | id << 8, sccb); if (rc) goto out; switch (sccb->header.response_code) { @@ -668,7 +668,7 @@ static int __init sclp_detect_standby_memory(void) if (rc) goto out; sclp_pdev = platform_device_register_simple("sclp_mem", -1, NULL, 0); - rc = IS_ERR(sclp_pdev) ? PTR_ERR(sclp_pdev) : 0; + rc = PTR_RET(sclp_pdev); if (rc) goto out_driver; sclp_add_standby_memory(); @@ -714,7 +714,7 @@ static int do_pci_configure(sclp_cmdw_t cmd, u32 fid) sccb->header.length = PAGE_SIZE; sccb->atype = SCLP_RECONFIG_PCI_ATPYE; sccb->aid = fid; - rc = do_sync_request(cmd, sccb); + rc = sclp_sync_request(cmd, sccb); if (rc) goto out; switch (sccb->header.response_code) { @@ -771,7 +771,7 @@ static int do_chp_configure(sclp_cmdw_t cmd) if (!sccb) return -ENOMEM; sccb->header.length = sizeof(*sccb); - rc = do_sync_request(cmd, sccb); + rc = sclp_sync_request(cmd, sccb); if (rc) goto out; switch (sccb->header.response_code) { @@ -846,7 +846,7 @@ int sclp_chp_read_info(struct sclp_chp_info *info) if (!sccb) return -ENOMEM; sccb->header.length = sizeof(*sccb); - rc = do_sync_request(SCLP_CMDW_READ_CHPATH_INFORMATION, sccb); + rc = sclp_sync_request(SCLP_CMDW_READ_CHPATH_INFORMATION, sccb); if (rc) goto out; if (sccb->header.response_code != 0x0010) { diff --git a/drivers/s390/char/sclp_con.c b/drivers/s390/char/sclp_con.c index ecf45c54f8c..5880def98fc 100644 --- a/drivers/s390/char/sclp_con.c +++ b/drivers/s390/char/sclp_con.c @@ -130,6 +130,31 @@ sclp_console_timeout(unsigned long data) } /* + * Drop oldest console buffer if sclp_con_drop is set + */ +static int +sclp_console_drop_buffer(void) +{ + struct list_head *list; + struct sclp_buffer *buffer; + void *page; + + if (!sclp_console_drop) + return 0; + list = sclp_con_outqueue.next; + if (sclp_con_queue_running) + /* The first element is in I/O */ + list = list->next; + if (list == &sclp_con_outqueue) + return 0; + list_del(list); + buffer = list_entry(list, struct sclp_buffer, list); + page = sclp_unmake_buffer(buffer); + list_add_tail((struct list_head *) page, &sclp_con_pages); + return 1; +} + +/* * Writes the given message to S390 system console */ static void @@ -150,9 +175,13 @@ sclp_console_write(struct console *console, const char *message, do { /* make sure we have a console output buffer */ if (sclp_conbuf == NULL) { + if (list_empty(&sclp_con_pages)) + sclp_console_full++; while (list_empty(&sclp_con_pages)) { if (sclp_con_suspended) goto out; + if (sclp_console_drop_buffer()) + break; spin_unlock_irqrestore(&sclp_con_lock, flags); sclp_sync_wait(); spin_lock_irqsave(&sclp_con_lock, flags); @@ -297,7 +326,7 @@ sclp_console_init(void) return rc; /* Allocate pages for output buffering */ INIT_LIST_HEAD(&sclp_con_pages); - for (i = 0; i < MAX_CONSOLE_PAGES; i++) { + for (i = 0; i < sclp_console_pages; i++) { page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); list_add_tail(page, &sclp_con_pages); } diff --git a/drivers/s390/char/sclp_ctl.c b/drivers/s390/char/sclp_ctl.c new file mode 100644 index 00000000000..648cb86afd4 --- /dev/null +++ b/drivers/s390/char/sclp_ctl.c @@ -0,0 +1,144 @@ +/* + * IOCTL interface for SCLP + * + * Copyright IBM Corp. 2012 + * + * Author: Michael Holzheu <holzheu@linux.vnet.ibm.com> + */ + +#include <linux/compat.h> +#include <linux/uaccess.h> +#include <linux/miscdevice.h> +#include <linux/gfp.h> +#include <linux/module.h> +#include <linux/ioctl.h> +#include <linux/fs.h> +#include <asm/compat.h> +#include <asm/sclp_ctl.h> +#include <asm/sclp.h> + +#include "sclp.h" + +/* + * Supported command words + */ +static unsigned int sclp_ctl_sccb_wlist[] = { + 0x00400002, + 0x00410002, +}; + +/* + * Check if command word is supported + */ +static int sclp_ctl_cmdw_supported(unsigned int cmdw) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sclp_ctl_sccb_wlist); i++) { + if (cmdw == sclp_ctl_sccb_wlist[i]) + return 1; + } + return 0; +} + +static void __user *u64_to_uptr(u64 value) +{ + if (is_compat_task()) + return compat_ptr(value); + else + return (void __user *)(unsigned long)value; +} + +/* + * Start SCLP request + */ +static int sclp_ctl_ioctl_sccb(void __user *user_area) +{ + struct sclp_ctl_sccb ctl_sccb; + struct sccb_header *sccb; + int rc; + + if (copy_from_user(&ctl_sccb, user_area, sizeof(ctl_sccb))) + return -EFAULT; + if (!sclp_ctl_cmdw_supported(ctl_sccb.cmdw)) + return -EOPNOTSUPP; + sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sccb) + return -ENOMEM; + if (copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), sizeof(*sccb))) { + rc = -EFAULT; + goto out_free; + } + if (sccb->length > PAGE_SIZE || sccb->length < 8) + return -EINVAL; + if (copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), sccb->length)) { + rc = -EFAULT; + goto out_free; + } + rc = sclp_sync_request(ctl_sccb.cmdw, sccb); + if (rc) + goto out_free; + if (copy_to_user(u64_to_uptr(ctl_sccb.sccb), sccb, sccb->length)) + rc = -EFAULT; +out_free: + free_page((unsigned long) sccb); + return rc; +} + +/* + * SCLP SCCB ioctl function + */ +static long sclp_ctl_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + void __user *argp; + + if (is_compat_task()) + argp = compat_ptr(arg); + else + argp = (void __user *) arg; + switch (cmd) { + case SCLP_CTL_SCCB: + return sclp_ctl_ioctl_sccb(argp); + default: /* unknown ioctl number */ + return -ENOTTY; + } +} + +/* + * File operations + */ +static const struct file_operations sclp_ctl_fops = { + .owner = THIS_MODULE, + .open = nonseekable_open, + .unlocked_ioctl = sclp_ctl_ioctl, + .compat_ioctl = sclp_ctl_ioctl, + .llseek = no_llseek, +}; + +/* + * Misc device definition + */ +static struct miscdevice sclp_ctl_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "sclp", + .fops = &sclp_ctl_fops, +}; + +/* + * Register sclp_ctl misc device + */ +static int __init sclp_ctl_init(void) +{ + return misc_register(&sclp_ctl_device); +} +module_init(sclp_ctl_init); + +/* + * Deregister sclp_ctl misc device + */ +static void __exit sclp_ctl_exit(void) +{ + misc_deregister(&sclp_ctl_device); +} +module_exit(sclp_ctl_exit); diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index 5aaaa2ec8df..4eed38cd0af 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -362,6 +362,31 @@ sclp_vt220_timeout(unsigned long data) #define BUFFER_MAX_DELAY HZ/20 +/* + * Drop oldest console buffer if sclp_con_drop is set + */ +static int +sclp_vt220_drop_buffer(void) +{ + struct list_head *list; + struct sclp_vt220_request *request; + void *page; + + if (!sclp_console_drop) + return 0; + list = sclp_vt220_outqueue.next; + if (sclp_vt220_queue_running) + /* The first element is in I/O */ + list = list->next; + if (list == &sclp_vt220_outqueue) + return 0; + list_del(list); + request = list_entry(list, struct sclp_vt220_request, list); + page = request->sclp_req.sccb; + list_add_tail((struct list_head *) page, &sclp_vt220_empty); + return 1; +} + /* * Internal implementation of the write function. Write COUNT bytes of data * from memory at BUF @@ -390,12 +415,16 @@ __sclp_vt220_write(const unsigned char *buf, int count, int do_schedule, do { /* Create an sclp output buffer if none exists yet */ if (sclp_vt220_current_request == NULL) { + if (list_empty(&sclp_vt220_empty)) + sclp_console_full++; while (list_empty(&sclp_vt220_empty)) { - spin_unlock_irqrestore(&sclp_vt220_lock, flags); if (may_fail || sclp_vt220_suspended) goto out; - else - sclp_sync_wait(); + if (sclp_vt220_drop_buffer()) + break; + spin_unlock_irqrestore(&sclp_vt220_lock, flags); + + sclp_sync_wait(); spin_lock_irqsave(&sclp_vt220_lock, flags); } page = (void *) sclp_vt220_empty.next; @@ -428,8 +457,8 @@ __sclp_vt220_write(const unsigned char *buf, int count, int do_schedule, sclp_vt220_timer.expires = jiffies + BUFFER_MAX_DELAY; add_timer(&sclp_vt220_timer); } - spin_unlock_irqrestore(&sclp_vt220_lock, flags); out: + spin_unlock_irqrestore(&sclp_vt220_lock, flags); return overall_written; } @@ -803,7 +832,7 @@ sclp_vt220_con_init(void) if (!CONSOLE_IS_SCLP) return 0; - rc = __sclp_vt220_init(MAX_CONSOLE_PAGES); + rc = __sclp_vt220_init(sclp_console_pages); if (rc) return rc; /* Attach linux console */ diff --git a/drivers/s390/char/tape_class.c b/drivers/s390/char/tape_class.c index 54b3c79203f..91c3c642c76 100644 --- a/drivers/s390/char/tape_class.c +++ b/drivers/s390/char/tape_class.c @@ -77,7 +77,7 @@ struct tape_class_device *register_tape_dev( tcd->class_device = device_create(tape_class, device, tcd->char_device->dev, NULL, "%s", tcd->device_name); - rc = IS_ERR(tcd->class_device) ? PTR_ERR(tcd->class_device) : 0; + rc = PTR_RET(tcd->class_device); if (rc) goto fail_with_cdev; rc = sysfs_create_link( diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c index c180e3135b3..64c467998a9 100644 --- a/drivers/s390/char/vmur.c +++ b/drivers/s390/char/vmur.c @@ -89,7 +89,7 @@ static DEFINE_MUTEX(vmur_mutex); * urd references: * - ur_probe gets a urd reference, ur_remove drops the reference * dev_get_drvdata(&cdev->dev) - * - ur_open gets a urd reference, ur_relase drops the reference + * - ur_open gets a urd reference, ur_release drops the reference * (urf->urd) * * cdev references: diff --git a/drivers/s390/char/vmwatchdog.c b/drivers/s390/char/vmwatchdog.c index e9b72311e25..d5eac985976 100644 --- a/drivers/s390/char/vmwatchdog.c +++ b/drivers/s390/char/vmwatchdog.c @@ -112,7 +112,8 @@ static int vmwdt_keepalive(void) static int vmwdt_disable(void) { - int ret = __diag288(wdt_cancel, 0, "", 0); + char cmd[] = {'\0'}; + int ret = __diag288(wdt_cancel, 0, cmd, 0); WARN_ON(ret != 0); clear_bit(VMWDT_RUNNING, &vmwdt_is_open); return ret; @@ -124,7 +125,7 @@ static int __init vmwdt_probe(void) * so we try initializing it with a NOP command ("BEGIN") * that won't cause any harm even if the following disable * fails for some reason */ - static char __initdata ebc_begin[] = { + char ebc_begin[] = { 194, 197, 199, 201, 213 }; if (__diag288(wdt_init, 15, ebc_begin, sizeof(ebc_begin)) != 0) |