aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2013-05-24 06:30:03 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2013-06-26 15:10:03 -0400
commit25b41a7b67ee4f4d12cee8a4b8b5929e36c27e29 (patch)
tree519be1cacdb7a23ff314ade0cec813c6d59b1af9
parent92820a5f99748b02a3713a314d81e2fd0b6b2f80 (diff)
s390/sclp: add parameter to specify number of buffer pages
Add a kernel parameter to be able to specify the number of pages to be used as output buffer by the line-mode sclp driver and the vt220 sclp driver. The current number of output pages is 6, if the service element is unavailable the boot messages alone can fill up the output buffer. If this happens the system blocks until the service element is working again. For a large LPAR with many devices it is sensible to have the ability to increase the output buffer size. To help to debug this situation add a counter for the page-pool-empty situation and make it available as a sclp driver attribute. To avoid the system to stall until the service element works again add another kernel parameter to allow to drop output buffers. Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-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)));
50/* Suspend request */ 50/* Suspend request */
51static DECLARE_COMPLETION(sclp_request_queue_flushed); 51static DECLARE_COMPLETION(sclp_request_queue_flushed);
52 52
53/* Number of console pages to allocate, used by sclp_con.c and sclp_vt220.c */
54int sclp_console_pages = SCLP_CONSOLE_PAGES;
55/* Flag to indicate if buffer pages are dropped on buffer full condition */
56int sclp_console_drop = 0;
57/* Number of times the console dropped buffer pages */
58unsigned long sclp_console_full;
59
53static void sclp_suspend_req_cb(struct sclp_req *req, void *data) 60static void sclp_suspend_req_cb(struct sclp_req *req, void *data)
54{ 61{
55 complete(&sclp_request_queue_flushed); 62 complete(&sclp_request_queue_flushed);
56} 63}
57 64
65static int __init sclp_setup_console_pages(char *str)
66{
67 int pages, rc;
68
69 rc = kstrtoint(str, 0, &pages);
70 if (!rc && pages >= SCLP_CONSOLE_PAGES)
71 sclp_console_pages = pages;
72 return 1;
73}
74
75__setup("sclp_con_pages=", sclp_setup_console_pages);
76
77static int __init sclp_setup_console_drop(char *str)
78{
79 int drop, rc;
80
81 rc = kstrtoint(str, 0, &drop);
82 if (!rc && drop)
83 sclp_console_drop = 1;
84 return 1;
85}
86
87__setup("sclp_con_drop=", sclp_setup_console_drop);
88
58static struct sclp_req sclp_suspend_req; 89static struct sclp_req sclp_suspend_req;
59 90
60/* Timer for request retries. */ 91/* Timer for request retries. */
@@ -1013,11 +1044,47 @@ static const struct dev_pm_ops sclp_pm_ops = {
1013 .restore = sclp_restore, 1044 .restore = sclp_restore,
1014}; 1045};
1015 1046
1047static ssize_t sclp_show_console_pages(struct device_driver *dev, char *buf)
1048{
1049 return sprintf(buf, "%i\n", sclp_console_pages);
1050}
1051
1052static DRIVER_ATTR(con_pages, S_IRUSR, sclp_show_console_pages, NULL);
1053
1054static ssize_t sclp_show_con_drop(struct device_driver *dev, char *buf)
1055{
1056 return sprintf(buf, "%i\n", sclp_console_drop);
1057}
1058
1059static DRIVER_ATTR(con_drop, S_IRUSR, sclp_show_con_drop, NULL);
1060
1061static ssize_t sclp_show_console_full(struct device_driver *dev, char *buf)
1062{
1063 return sprintf(buf, "%lu\n", sclp_console_full);
1064}
1065
1066static DRIVER_ATTR(con_full, S_IRUSR, sclp_show_console_full, NULL);
1067
1068static struct attribute *sclp_drv_attrs[] = {
1069 &driver_attr_con_pages.attr,
1070 &driver_attr_con_drop.attr,
1071 &driver_attr_con_full.attr,
1072 NULL,
1073};
1074static struct attribute_group sclp_drv_attr_group = {
1075 .attrs = sclp_drv_attrs,
1076};
1077static const struct attribute_group *sclp_drv_attr_groups[] = {
1078 &sclp_drv_attr_group,
1079 NULL,
1080};
1081
1016static struct platform_driver sclp_pdrv = { 1082static struct platform_driver sclp_pdrv = {
1017 .driver = { 1083 .driver = {
1018 .name = "sclp", 1084 .name = "sclp",
1019 .owner = THIS_MODULE, 1085 .owner = THIS_MODULE,
1020 .pm = &sclp_pm_ops, 1086 .pm = &sclp_pm_ops,
1087 .groups = sclp_drv_attr_groups,
1021 }, 1088 },
1022}; 1089};
1023 1090
@@ -1096,10 +1163,12 @@ static __init int sclp_initcall(void)
1096 rc = platform_driver_register(&sclp_pdrv); 1163 rc = platform_driver_register(&sclp_pdrv);
1097 if (rc) 1164 if (rc)
1098 return rc; 1165 return rc;
1166
1099 sclp_pdev = platform_device_register_simple("sclp", -1, NULL, 0); 1167 sclp_pdev = platform_device_register_simple("sclp", -1, NULL, 0);
1100 rc = IS_ERR(sclp_pdev) ? PTR_ERR(sclp_pdev) : 0; 1168 rc = IS_ERR(sclp_pdev) ? PTR_ERR(sclp_pdev) : 0;
1101 if (rc) 1169 if (rc)
1102 goto fail_platform_driver_unregister; 1170 goto fail_platform_driver_unregister;
1171
1103 rc = atomic_notifier_chain_register(&panic_notifier_list, 1172 rc = atomic_notifier_chain_register(&panic_notifier_list,
1104 &sclp_on_panic_nb); 1173 &sclp_on_panic_nb);
1105 if (rc) 1174 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 @@
15 15
16/* maximum number of pages concerning our own memory management */ 16/* maximum number of pages concerning our own memory management */
17#define MAX_KMEM_PAGES (sizeof(unsigned long) << 3) 17#define MAX_KMEM_PAGES (sizeof(unsigned long) << 3)
18#define MAX_CONSOLE_PAGES 6 18#define SCLP_CONSOLE_PAGES 6
19 19
20#define EVTYP_OPCMD 0x01 20#define EVTYP_OPCMD 0x01
21#define EVTYP_MSG 0x02 21#define EVTYP_MSG 0x02
@@ -175,6 +175,10 @@ int sclp_service_call(sclp_cmdw_t command, void *sccb);
175int sclp_sdias_init(void); 175int sclp_sdias_init(void);
176void sclp_sdias_exit(void); 176void sclp_sdias_exit(void);
177 177
178extern int sclp_console_pages;
179extern int sclp_console_drop;
180extern unsigned long sclp_console_full;
181
178/* useful inlines */ 182/* useful inlines */
179 183
180/* VM uses EBCDIC 037, LPAR+native(SE+HMC) use EBCDIC 500 */ 184/* 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)
130} 130}
131 131
132/* 132/*
133 * Drop oldest console buffer if sclp_con_drop is set
134 */
135static int
136sclp_console_drop_buffer(void)
137{
138 struct list_head *list;
139 struct sclp_buffer *buffer;
140 void *page;
141
142 if (!sclp_console_drop)
143 return 0;
144 list = sclp_con_outqueue.next;
145 if (sclp_con_queue_running)
146 /* The first element is in I/O */
147 list = list->next;
148 if (list == &sclp_con_outqueue)
149 return 0;
150 list_del(list);
151 buffer = list_entry(list, struct sclp_buffer, list);
152 page = sclp_unmake_buffer(buffer);
153 list_add_tail((struct list_head *) page, &sclp_con_pages);
154 return 1;
155}
156
157/*
133 * Writes the given message to S390 system console 158 * Writes the given message to S390 system console
134 */ 159 */
135static void 160static void
@@ -150,9 +175,13 @@ sclp_console_write(struct console *console, const char *message,
150 do { 175 do {
151 /* make sure we have a console output buffer */ 176 /* make sure we have a console output buffer */
152 if (sclp_conbuf == NULL) { 177 if (sclp_conbuf == NULL) {
178 if (list_empty(&sclp_con_pages))
179 sclp_console_full++;
153 while (list_empty(&sclp_con_pages)) { 180 while (list_empty(&sclp_con_pages)) {
154 if (sclp_con_suspended) 181 if (sclp_con_suspended)
155 goto out; 182 goto out;
183 if (sclp_console_drop_buffer())
184 break;
156 spin_unlock_irqrestore(&sclp_con_lock, flags); 185 spin_unlock_irqrestore(&sclp_con_lock, flags);
157 sclp_sync_wait(); 186 sclp_sync_wait();
158 spin_lock_irqsave(&sclp_con_lock, flags); 187 spin_lock_irqsave(&sclp_con_lock, flags);
@@ -297,7 +326,7 @@ sclp_console_init(void)
297 return rc; 326 return rc;
298 /* Allocate pages for output buffering */ 327 /* Allocate pages for output buffering */
299 INIT_LIST_HEAD(&sclp_con_pages); 328 INIT_LIST_HEAD(&sclp_con_pages);
300 for (i = 0; i < MAX_CONSOLE_PAGES; i++) { 329 for (i = 0; i < sclp_console_pages; i++) {
301 page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); 330 page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
302 list_add_tail(page, &sclp_con_pages); 331 list_add_tail(page, &sclp_con_pages);
303 } 332 }
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)
362 362
363#define BUFFER_MAX_DELAY HZ/20 363#define BUFFER_MAX_DELAY HZ/20
364 364
365/*
366 * Drop oldest console buffer if sclp_con_drop is set
367 */
368static int
369sclp_vt220_drop_buffer(void)
370{
371 struct list_head *list;
372 struct sclp_vt220_request *request;
373 void *page;
374
375 if (!sclp_console_drop)
376 return 0;
377 list = sclp_vt220_outqueue.next;
378 if (sclp_vt220_queue_running)
379 /* The first element is in I/O */
380 list = list->next;
381 if (list == &sclp_vt220_outqueue)
382 return 0;
383 list_del(list);
384 request = list_entry(list, struct sclp_vt220_request, list);
385 page = request->sclp_req.sccb;
386 list_add_tail((struct list_head *) page, &sclp_vt220_empty);
387 return 1;
388}
389
365/* 390/*
366 * Internal implementation of the write function. Write COUNT bytes of data 391 * Internal implementation of the write function. Write COUNT bytes of data
367 * from memory at BUF 392 * from memory at BUF
@@ -390,12 +415,16 @@ __sclp_vt220_write(const unsigned char *buf, int count, int do_schedule,
390 do { 415 do {
391 /* Create an sclp output buffer if none exists yet */ 416 /* Create an sclp output buffer if none exists yet */
392 if (sclp_vt220_current_request == NULL) { 417 if (sclp_vt220_current_request == NULL) {
418 if (list_empty(&sclp_vt220_empty))
419 sclp_console_full++;
393 while (list_empty(&sclp_vt220_empty)) { 420 while (list_empty(&sclp_vt220_empty)) {
394 spin_unlock_irqrestore(&sclp_vt220_lock, flags);
395 if (may_fail || sclp_vt220_suspended) 421 if (may_fail || sclp_vt220_suspended)
396 goto out; 422 goto out;
397 else 423 if (sclp_vt220_drop_buffer())
398 sclp_sync_wait(); 424 break;
425 spin_unlock_irqrestore(&sclp_vt220_lock, flags);
426
427 sclp_sync_wait();
399 spin_lock_irqsave(&sclp_vt220_lock, flags); 428 spin_lock_irqsave(&sclp_vt220_lock, flags);
400 } 429 }
401 page = (void *) sclp_vt220_empty.next; 430 page = (void *) sclp_vt220_empty.next;
@@ -428,8 +457,8 @@ __sclp_vt220_write(const unsigned char *buf, int count, int do_schedule,
428 sclp_vt220_timer.expires = jiffies + BUFFER_MAX_DELAY; 457 sclp_vt220_timer.expires = jiffies + BUFFER_MAX_DELAY;
429 add_timer(&sclp_vt220_timer); 458 add_timer(&sclp_vt220_timer);
430 } 459 }
431 spin_unlock_irqrestore(&sclp_vt220_lock, flags);
432out: 460out:
461 spin_unlock_irqrestore(&sclp_vt220_lock, flags);
433 return overall_written; 462 return overall_written;
434} 463}
435 464
@@ -803,7 +832,7 @@ sclp_vt220_con_init(void)
803 832
804 if (!CONSOLE_IS_SCLP) 833 if (!CONSOLE_IS_SCLP)
805 return 0; 834 return 0;
806 rc = __sclp_vt220_init(MAX_CONSOLE_PAGES); 835 rc = __sclp_vt220_init(sclp_console_pages);
807 if (rc) 836 if (rc)
808 return rc; 837 return rc;
809 /* Attach linux console */ 838 /* Attach linux console */