sclp_con.c (7652B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * SCLP line mode console driver 4 * 5 * Copyright IBM Corp. 1999, 2009 6 * Author(s): Martin Peschke <mpeschke@de.ibm.com> 7 * Martin Schwidefsky <schwidefsky@de.ibm.com> 8 */ 9 10#include <linux/kmod.h> 11#include <linux/console.h> 12#include <linux/init.h> 13#include <linux/panic_notifier.h> 14#include <linux/timer.h> 15#include <linux/jiffies.h> 16#include <linux/termios.h> 17#include <linux/err.h> 18#include <linux/reboot.h> 19#include <linux/gfp.h> 20 21#include "sclp.h" 22#include "sclp_rw.h" 23#include "sclp_tty.h" 24 25#define sclp_console_major 4 /* TTYAUX_MAJOR */ 26#define sclp_console_minor 64 27#define sclp_console_name "ttyS" 28 29/* Lock to guard over changes to global variables */ 30static DEFINE_SPINLOCK(sclp_con_lock); 31/* List of free pages that can be used for console output buffering */ 32static LIST_HEAD(sclp_con_pages); 33/* List of full struct sclp_buffer structures ready for output */ 34static LIST_HEAD(sclp_con_outqueue); 35/* Pointer to current console buffer */ 36static struct sclp_buffer *sclp_conbuf; 37/* Timer for delayed output of console messages */ 38static struct timer_list sclp_con_timer; 39/* Flag that output queue is currently running */ 40static int sclp_con_queue_running; 41 42/* Output format for console messages */ 43#define SCLP_CON_COLUMNS 320 44#define SPACES_PER_TAB 8 45 46static void 47sclp_conbuf_callback(struct sclp_buffer *buffer, int rc) 48{ 49 unsigned long flags; 50 void *page; 51 52 do { 53 page = sclp_unmake_buffer(buffer); 54 spin_lock_irqsave(&sclp_con_lock, flags); 55 56 /* Remove buffer from outqueue */ 57 list_del(&buffer->list); 58 list_add_tail((struct list_head *) page, &sclp_con_pages); 59 60 /* Check if there is a pending buffer on the out queue. */ 61 buffer = NULL; 62 if (!list_empty(&sclp_con_outqueue)) 63 buffer = list_first_entry(&sclp_con_outqueue, 64 struct sclp_buffer, list); 65 if (!buffer) { 66 sclp_con_queue_running = 0; 67 spin_unlock_irqrestore(&sclp_con_lock, flags); 68 break; 69 } 70 spin_unlock_irqrestore(&sclp_con_lock, flags); 71 } while (sclp_emit_buffer(buffer, sclp_conbuf_callback)); 72} 73 74/* 75 * Finalize and emit first pending buffer. 76 */ 77static void sclp_conbuf_emit(void) 78{ 79 struct sclp_buffer* buffer; 80 unsigned long flags; 81 int rc; 82 83 spin_lock_irqsave(&sclp_con_lock, flags); 84 if (sclp_conbuf) 85 list_add_tail(&sclp_conbuf->list, &sclp_con_outqueue); 86 sclp_conbuf = NULL; 87 if (sclp_con_queue_running) 88 goto out_unlock; 89 if (list_empty(&sclp_con_outqueue)) 90 goto out_unlock; 91 buffer = list_first_entry(&sclp_con_outqueue, struct sclp_buffer, 92 list); 93 sclp_con_queue_running = 1; 94 spin_unlock_irqrestore(&sclp_con_lock, flags); 95 96 rc = sclp_emit_buffer(buffer, sclp_conbuf_callback); 97 if (rc) 98 sclp_conbuf_callback(buffer, rc); 99 return; 100out_unlock: 101 spin_unlock_irqrestore(&sclp_con_lock, flags); 102} 103 104/* 105 * Wait until out queue is empty 106 */ 107static void sclp_console_sync_queue(void) 108{ 109 unsigned long flags; 110 111 spin_lock_irqsave(&sclp_con_lock, flags); 112 del_timer(&sclp_con_timer); 113 while (sclp_con_queue_running) { 114 spin_unlock_irqrestore(&sclp_con_lock, flags); 115 sclp_sync_wait(); 116 spin_lock_irqsave(&sclp_con_lock, flags); 117 } 118 spin_unlock_irqrestore(&sclp_con_lock, flags); 119} 120 121/* 122 * When this routine is called from the timer then we flush the 123 * temporary write buffer without further waiting on a final new line. 124 */ 125static void 126sclp_console_timeout(struct timer_list *unused) 127{ 128 sclp_conbuf_emit(); 129} 130 131/* 132 * Drop oldest console buffer if sclp_con_drop is set 133 */ 134static int 135sclp_console_drop_buffer(void) 136{ 137 struct list_head *list; 138 struct sclp_buffer *buffer; 139 void *page; 140 141 if (!sclp_console_drop) 142 return 0; 143 list = sclp_con_outqueue.next; 144 if (sclp_con_queue_running) 145 /* The first element is in I/O */ 146 list = list->next; 147 if (list == &sclp_con_outqueue) 148 return 0; 149 list_del(list); 150 buffer = list_entry(list, struct sclp_buffer, list); 151 page = sclp_unmake_buffer(buffer); 152 list_add_tail((struct list_head *) page, &sclp_con_pages); 153 return 1; 154} 155 156/* 157 * Writes the given message to S390 system console 158 */ 159static void 160sclp_console_write(struct console *console, const char *message, 161 unsigned int count) 162{ 163 unsigned long flags; 164 void *page; 165 int written; 166 167 if (count == 0) 168 return; 169 spin_lock_irqsave(&sclp_con_lock, flags); 170 /* 171 * process escape characters, write message into buffer, 172 * send buffer to SCLP 173 */ 174 do { 175 /* make sure we have a console output buffer */ 176 if (sclp_conbuf == NULL) { 177 if (list_empty(&sclp_con_pages)) 178 sclp_console_full++; 179 while (list_empty(&sclp_con_pages)) { 180 if (sclp_console_drop_buffer()) 181 break; 182 spin_unlock_irqrestore(&sclp_con_lock, flags); 183 sclp_sync_wait(); 184 spin_lock_irqsave(&sclp_con_lock, flags); 185 } 186 page = sclp_con_pages.next; 187 list_del((struct list_head *) page); 188 sclp_conbuf = sclp_make_buffer(page, SCLP_CON_COLUMNS, 189 SPACES_PER_TAB); 190 } 191 /* try to write the string to the current output buffer */ 192 written = sclp_write(sclp_conbuf, (const unsigned char *) 193 message, count); 194 if (written == count) 195 break; 196 /* 197 * Not all characters could be written to the current 198 * output buffer. Emit the buffer, create a new buffer 199 * and then output the rest of the string. 200 */ 201 spin_unlock_irqrestore(&sclp_con_lock, flags); 202 sclp_conbuf_emit(); 203 spin_lock_irqsave(&sclp_con_lock, flags); 204 message += written; 205 count -= written; 206 } while (count > 0); 207 /* Setup timer to output current console buffer after 1/10 second */ 208 if (sclp_conbuf != NULL && sclp_chars_in_buffer(sclp_conbuf) != 0 && 209 !timer_pending(&sclp_con_timer)) { 210 mod_timer(&sclp_con_timer, jiffies + HZ / 10); 211 } 212 spin_unlock_irqrestore(&sclp_con_lock, flags); 213} 214 215static struct tty_driver * 216sclp_console_device(struct console *c, int *index) 217{ 218 *index = c->index; 219 return sclp_tty_driver; 220} 221 222/* 223 * This panic/reboot notifier makes sure that all buffers 224 * will be flushed to the SCLP. 225 */ 226static int sclp_console_notify(struct notifier_block *self, 227 unsigned long event, void *data) 228{ 229 /* 230 * Perform the lock check before effectively getting the 231 * lock on sclp_conbuf_emit() / sclp_console_sync_queue() 232 * to prevent potential lockups in atomic context. 233 */ 234 if (spin_is_locked(&sclp_con_lock)) 235 return NOTIFY_DONE; 236 237 sclp_conbuf_emit(); 238 sclp_console_sync_queue(); 239 240 return NOTIFY_DONE; 241} 242 243static struct notifier_block on_panic_nb = { 244 .notifier_call = sclp_console_notify, 245 .priority = INT_MIN + 1, /* run the callback late */ 246}; 247 248static struct notifier_block on_reboot_nb = { 249 .notifier_call = sclp_console_notify, 250 .priority = INT_MIN + 1, /* run the callback late */ 251}; 252 253/* 254 * used to register the SCLP console to the kernel and to 255 * give printk necessary information 256 */ 257static struct console sclp_console = 258{ 259 .name = sclp_console_name, 260 .write = sclp_console_write, 261 .device = sclp_console_device, 262 .flags = CON_PRINTBUFFER, 263 .index = 0 /* ttyS0 */ 264}; 265 266/* 267 * called by console_init() in drivers/char/tty_io.c at boot-time. 268 */ 269static int __init 270sclp_console_init(void) 271{ 272 void *page; 273 int i; 274 int rc; 275 276 /* SCLP consoles are handled together */ 277 if (!(CONSOLE_IS_SCLP || CONSOLE_IS_VT220)) 278 return 0; 279 rc = sclp_rw_init(); 280 if (rc) 281 return rc; 282 /* Allocate pages for output buffering */ 283 for (i = 0; i < sclp_console_pages; i++) { 284 page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); 285 list_add_tail(page, &sclp_con_pages); 286 } 287 sclp_conbuf = NULL; 288 timer_setup(&sclp_con_timer, sclp_console_timeout, 0); 289 290 /* enable printk-access to this driver */ 291 atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb); 292 register_reboot_notifier(&on_reboot_nb); 293 register_console(&sclp_console); 294 return 0; 295} 296 297console_initcall(sclp_console_init);