diff options
Diffstat (limited to 'drivers/s390/char/sclp_con.c')
-rw-r--r-- | drivers/s390/char/sclp_con.c | 139 |
1 files changed, 99 insertions, 40 deletions
diff --git a/drivers/s390/char/sclp_con.c b/drivers/s390/char/sclp_con.c index 9a25c4bd1421..336811a77672 100644 --- a/drivers/s390/char/sclp_con.c +++ b/drivers/s390/char/sclp_con.c | |||
@@ -1,11 +1,9 @@ | |||
1 | /* | 1 | /* |
2 | * drivers/s390/char/sclp_con.c | 2 | * SCLP line mode console driver |
3 | * SCLP line mode console driver | ||
4 | * | 3 | * |
5 | * S390 version | 4 | * Copyright IBM Corp. 1999, 2009 |
6 | * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation | 5 | * Author(s): Martin Peschke <mpeschke@de.ibm.com> |
7 | * Author(s): Martin Peschke <mpeschke@de.ibm.com> | 6 | * Martin Schwidefsky <schwidefsky@de.ibm.com> |
8 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | ||
9 | */ | 7 | */ |
10 | 8 | ||
11 | #include <linux/kmod.h> | 9 | #include <linux/kmod.h> |
@@ -32,13 +30,14 @@ static spinlock_t sclp_con_lock; | |||
32 | static struct list_head sclp_con_pages; | 30 | static struct list_head sclp_con_pages; |
33 | /* List of full struct sclp_buffer structures ready for output */ | 31 | /* List of full struct sclp_buffer structures ready for output */ |
34 | static struct list_head sclp_con_outqueue; | 32 | static struct list_head sclp_con_outqueue; |
35 | /* Counter how many buffers are emitted (max 1) and how many */ | ||
36 | /* are on the output queue. */ | ||
37 | static int sclp_con_buffer_count; | ||
38 | /* Pointer to current console buffer */ | 33 | /* Pointer to current console buffer */ |
39 | static struct sclp_buffer *sclp_conbuf; | 34 | static struct sclp_buffer *sclp_conbuf; |
40 | /* Timer for delayed output of console messages */ | 35 | /* Timer for delayed output of console messages */ |
41 | static struct timer_list sclp_con_timer; | 36 | static struct timer_list sclp_con_timer; |
37 | /* Suspend mode flag */ | ||
38 | static int sclp_con_suspended; | ||
39 | /* Flag that output queue is currently running */ | ||
40 | static int sclp_con_queue_running; | ||
42 | 41 | ||
43 | /* Output format for console messages */ | 42 | /* Output format for console messages */ |
44 | static unsigned short sclp_con_columns; | 43 | static unsigned short sclp_con_columns; |
@@ -53,42 +52,71 @@ sclp_conbuf_callback(struct sclp_buffer *buffer, int rc) | |||
53 | do { | 52 | do { |
54 | page = sclp_unmake_buffer(buffer); | 53 | page = sclp_unmake_buffer(buffer); |
55 | spin_lock_irqsave(&sclp_con_lock, flags); | 54 | spin_lock_irqsave(&sclp_con_lock, flags); |
55 | |||
56 | /* Remove buffer from outqueue */ | 56 | /* Remove buffer from outqueue */ |
57 | list_del(&buffer->list); | 57 | list_del(&buffer->list); |
58 | sclp_con_buffer_count--; | ||
59 | list_add_tail((struct list_head *) page, &sclp_con_pages); | 58 | list_add_tail((struct list_head *) page, &sclp_con_pages); |
59 | |||
60 | /* Check if there is a pending buffer on the out queue. */ | 60 | /* Check if there is a pending buffer on the out queue. */ |
61 | buffer = NULL; | 61 | buffer = NULL; |
62 | if (!list_empty(&sclp_con_outqueue)) | 62 | if (!list_empty(&sclp_con_outqueue)) |
63 | buffer = list_entry(sclp_con_outqueue.next, | 63 | buffer = list_first_entry(&sclp_con_outqueue, |
64 | struct sclp_buffer, list); | 64 | struct sclp_buffer, list); |
65 | if (!buffer || sclp_con_suspended) { | ||
66 | sclp_con_queue_running = 0; | ||
67 | spin_unlock_irqrestore(&sclp_con_lock, flags); | ||
68 | break; | ||
69 | } | ||
65 | spin_unlock_irqrestore(&sclp_con_lock, flags); | 70 | spin_unlock_irqrestore(&sclp_con_lock, flags); |
66 | } while (buffer && sclp_emit_buffer(buffer, sclp_conbuf_callback)); | 71 | } while (sclp_emit_buffer(buffer, sclp_conbuf_callback)); |
67 | } | 72 | } |
68 | 73 | ||
69 | static void | 74 | /* |
70 | sclp_conbuf_emit(void) | 75 | * Finalize and emit first pending buffer. |
76 | */ | ||
77 | static void sclp_conbuf_emit(void) | ||
71 | { | 78 | { |
72 | struct sclp_buffer* buffer; | 79 | struct sclp_buffer* buffer; |
73 | unsigned long flags; | 80 | unsigned long flags; |
74 | int count; | ||
75 | int rc; | 81 | int rc; |
76 | 82 | ||
77 | spin_lock_irqsave(&sclp_con_lock, flags); | 83 | spin_lock_irqsave(&sclp_con_lock, flags); |
78 | buffer = sclp_conbuf; | 84 | if (sclp_conbuf) |
85 | list_add_tail(&sclp_conbuf->list, &sclp_con_outqueue); | ||
79 | sclp_conbuf = NULL; | 86 | sclp_conbuf = NULL; |
80 | if (buffer == NULL) { | 87 | if (sclp_con_queue_running || sclp_con_suspended) |
81 | spin_unlock_irqrestore(&sclp_con_lock, flags); | 88 | goto out_unlock; |
82 | return; | 89 | if (list_empty(&sclp_con_outqueue)) |
83 | } | 90 | goto out_unlock; |
84 | list_add_tail(&buffer->list, &sclp_con_outqueue); | 91 | buffer = list_first_entry(&sclp_con_outqueue, struct sclp_buffer, |
85 | count = sclp_con_buffer_count++; | 92 | list); |
93 | sclp_con_queue_running = 1; | ||
86 | spin_unlock_irqrestore(&sclp_con_lock, flags); | 94 | spin_unlock_irqrestore(&sclp_con_lock, flags); |
87 | if (count) | 95 | |
88 | return; | ||
89 | rc = sclp_emit_buffer(buffer, sclp_conbuf_callback); | 96 | rc = sclp_emit_buffer(buffer, sclp_conbuf_callback); |
90 | if (rc) | 97 | if (rc) |
91 | sclp_conbuf_callback(buffer, rc); | 98 | sclp_conbuf_callback(buffer, rc); |
99 | return; | ||
100 | out_unlock: | ||
101 | spin_unlock_irqrestore(&sclp_con_lock, flags); | ||
102 | } | ||
103 | |||
104 | /* | ||
105 | * Wait until out queue is empty | ||
106 | */ | ||
107 | static void sclp_console_sync_queue(void) | ||
108 | { | ||
109 | unsigned long flags; | ||
110 | |||
111 | spin_lock_irqsave(&sclp_con_lock, flags); | ||
112 | if (timer_pending(&sclp_con_timer)) | ||
113 | del_timer_sync(&sclp_con_timer); | ||
114 | while (sclp_con_queue_running) { | ||
115 | spin_unlock_irqrestore(&sclp_con_lock, flags); | ||
116 | sclp_sync_wait(); | ||
117 | spin_lock_irqsave(&sclp_con_lock, flags); | ||
118 | } | ||
119 | spin_unlock_irqrestore(&sclp_con_lock, flags); | ||
92 | } | 120 | } |
93 | 121 | ||
94 | /* | 122 | /* |
@@ -123,6 +151,8 @@ sclp_console_write(struct console *console, const char *message, | |||
123 | /* make sure we have a console output buffer */ | 151 | /* make sure we have a console output buffer */ |
124 | if (sclp_conbuf == NULL) { | 152 | if (sclp_conbuf == NULL) { |
125 | while (list_empty(&sclp_con_pages)) { | 153 | while (list_empty(&sclp_con_pages)) { |
154 | if (sclp_con_suspended) | ||
155 | goto out; | ||
126 | spin_unlock_irqrestore(&sclp_con_lock, flags); | 156 | spin_unlock_irqrestore(&sclp_con_lock, flags); |
127 | sclp_sync_wait(); | 157 | sclp_sync_wait(); |
128 | spin_lock_irqsave(&sclp_con_lock, flags); | 158 | spin_lock_irqsave(&sclp_con_lock, flags); |
@@ -157,6 +187,7 @@ sclp_console_write(struct console *console, const char *message, | |||
157 | sclp_con_timer.expires = jiffies + HZ/10; | 187 | sclp_con_timer.expires = jiffies + HZ/10; |
158 | add_timer(&sclp_con_timer); | 188 | add_timer(&sclp_con_timer); |
159 | } | 189 | } |
190 | out: | ||
160 | spin_unlock_irqrestore(&sclp_con_lock, flags); | 191 | spin_unlock_irqrestore(&sclp_con_lock, flags); |
161 | } | 192 | } |
162 | 193 | ||
@@ -168,30 +199,43 @@ sclp_console_device(struct console *c, int *index) | |||
168 | } | 199 | } |
169 | 200 | ||
170 | /* | 201 | /* |
171 | * This routine is called from panic when the kernel | 202 | * Make sure that all buffers will be flushed to the SCLP. |
172 | * is going to give up. We have to make sure that all buffers | ||
173 | * will be flushed to the SCLP. | ||
174 | */ | 203 | */ |
175 | static void | 204 | static void |
176 | sclp_console_flush(void) | 205 | sclp_console_flush(void) |
177 | { | 206 | { |
207 | sclp_conbuf_emit(); | ||
208 | sclp_console_sync_queue(); | ||
209 | } | ||
210 | |||
211 | /* | ||
212 | * Resume console: If there are cached messages, emit them. | ||
213 | */ | ||
214 | static void sclp_console_resume(void) | ||
215 | { | ||
178 | unsigned long flags; | 216 | unsigned long flags; |
179 | 217 | ||
218 | spin_lock_irqsave(&sclp_con_lock, flags); | ||
219 | sclp_con_suspended = 0; | ||
220 | spin_unlock_irqrestore(&sclp_con_lock, flags); | ||
180 | sclp_conbuf_emit(); | 221 | sclp_conbuf_emit(); |
222 | } | ||
223 | |||
224 | /* | ||
225 | * Suspend console: Set suspend flag and flush console | ||
226 | */ | ||
227 | static void sclp_console_suspend(void) | ||
228 | { | ||
229 | unsigned long flags; | ||
230 | |||
181 | spin_lock_irqsave(&sclp_con_lock, flags); | 231 | spin_lock_irqsave(&sclp_con_lock, flags); |
182 | if (timer_pending(&sclp_con_timer)) | 232 | sclp_con_suspended = 1; |
183 | del_timer(&sclp_con_timer); | ||
184 | while (sclp_con_buffer_count > 0) { | ||
185 | spin_unlock_irqrestore(&sclp_con_lock, flags); | ||
186 | sclp_sync_wait(); | ||
187 | spin_lock_irqsave(&sclp_con_lock, flags); | ||
188 | } | ||
189 | spin_unlock_irqrestore(&sclp_con_lock, flags); | 233 | spin_unlock_irqrestore(&sclp_con_lock, flags); |
234 | sclp_console_flush(); | ||
190 | } | 235 | } |
191 | 236 | ||
192 | static int | 237 | static int sclp_console_notify(struct notifier_block *self, |
193 | sclp_console_notify(struct notifier_block *self, | 238 | unsigned long event, void *data) |
194 | unsigned long event, void *data) | ||
195 | { | 239 | { |
196 | sclp_console_flush(); | 240 | sclp_console_flush(); |
197 | return NOTIFY_OK; | 241 | return NOTIFY_OK; |
@@ -199,7 +243,7 @@ sclp_console_notify(struct notifier_block *self, | |||
199 | 243 | ||
200 | static struct notifier_block on_panic_nb = { | 244 | static struct notifier_block on_panic_nb = { |
201 | .notifier_call = sclp_console_notify, | 245 | .notifier_call = sclp_console_notify, |
202 | .priority = 1, | 246 | .priority = SCLP_PANIC_PRIO_CLIENT, |
203 | }; | 247 | }; |
204 | 248 | ||
205 | static struct notifier_block on_reboot_nb = { | 249 | static struct notifier_block on_reboot_nb = { |
@@ -221,6 +265,22 @@ static struct console sclp_console = | |||
221 | }; | 265 | }; |
222 | 266 | ||
223 | /* | 267 | /* |
268 | * This function is called for SCLP suspend and resume events. | ||
269 | */ | ||
270 | void sclp_console_pm_event(enum sclp_pm_event sclp_pm_event) | ||
271 | { | ||
272 | switch (sclp_pm_event) { | ||
273 | case SCLP_PM_EVENT_FREEZE: | ||
274 | sclp_console_suspend(); | ||
275 | break; | ||
276 | case SCLP_PM_EVENT_RESTORE: | ||
277 | case SCLP_PM_EVENT_THAW: | ||
278 | sclp_console_resume(); | ||
279 | break; | ||
280 | } | ||
281 | } | ||
282 | |||
283 | /* | ||
224 | * called by console_init() in drivers/char/tty_io.c at boot-time. | 284 | * called by console_init() in drivers/char/tty_io.c at boot-time. |
225 | */ | 285 | */ |
226 | static int __init | 286 | static int __init |
@@ -243,7 +303,6 @@ sclp_console_init(void) | |||
243 | } | 303 | } |
244 | INIT_LIST_HEAD(&sclp_con_outqueue); | 304 | INIT_LIST_HEAD(&sclp_con_outqueue); |
245 | spin_lock_init(&sclp_con_lock); | 305 | spin_lock_init(&sclp_con_lock); |
246 | sclp_con_buffer_count = 0; | ||
247 | sclp_conbuf = NULL; | 306 | sclp_conbuf = NULL; |
248 | init_timer(&sclp_con_timer); | 307 | init_timer(&sclp_con_timer); |
249 | 308 | ||