summaryrefslogtreecommitdiff
path: root/drivers/s390
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390')
-rw-r--r--drivers/s390/char/sclp.c69
-rw-r--r--drivers/s390/char/sclp.h6
-rw-r--r--drivers/s390/char/sclp_con.c31
-rw-r--r--drivers/s390/char/sclp_vt220.c39
4 files changed, 138 insertions, 7 deletions
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c
index bd6871bf545a..a77febeead1f 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. */
@@ -1013,11 +1044,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 +1163,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;
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 25bcd4c0ed82..e11383f5d3d2 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
@@ -175,6 +175,10 @@ int sclp_service_call(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_con.c b/drivers/s390/char/sclp_con.c
index ecf45c54f8c4..5880def98fc1 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_vt220.c b/drivers/s390/char/sclp_vt220.c
index 5aaaa2ec8df4..4eed38cd0af6 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 */