From 026699961337ea21e05f08e1043c4a8411a7f92b Mon Sep 17 00:00:00 2001 From: Josef 'Jeff' Sipek Date: Tue, 15 Nov 2011 16:53:10 -0500 Subject: [PATCH] cp: replace crusty console line buffering mechanism with a spoolfile The spool file layer needs to exist anyways so let's use it. It does mean that a console I/O from a guest will end up being copied 3 times (guest buffer -> spooldev_cons buffer -> spoolfile -> I/O buffer). The is no way to avoid at least one copy. At this point, it's pointless to try to optimize that and so we'll live this behavior for now. Signed-off-by: Josef 'Jeff' Sipek --- cp/drivers/console.c | 256 ++++++++++++++------------------------------------- cp/include/console.h | 34 ++----- 2 files changed, 74 insertions(+), 216 deletions(-) diff --git a/cp/drivers/console.c b/cp/drivers/console.c index bd7d685..4a71eab 100644 --- a/cp/drivers/console.c +++ b/cp/drivers/console.c @@ -1,5 +1,5 @@ /* - * (C) Copyright 2007-2010 Josef 'Jeff' Sipek + * (C) Copyright 2007-2011 Josef 'Jeff' Sipek * * This file is released under the GPLv2. See the COPYING file for more * details. @@ -10,6 +10,8 @@ #include #include #include +#include +#include /* * List of all consoles on the system @@ -17,67 +19,17 @@ static LIST_HEAD(consoles); static spinlock_t consoles_lock = SPIN_LOCK_UNLOCKED; -static int read_io_int_handler(struct device *dev, struct io_op *ioop, struct irb *irb) -{ - struct console_line *cline; - struct ccw *ccw; - void *ptr; - - /* Device End is set, we're done */ - if (!(irb->scsw.dev_status & 0x04)) { - ioop->err = -EAGAIN; - return 0; - } - - ccw = (struct ccw*) (u64) ioop->orb.addr; - ptr = (void*) (u64) ccw->addr; - cline = container_of(ptr, struct console_line, buf); - - cline->len = strnlen((char*) cline->buf, CON_MAX_LINE_LEN-1); - cline->state = CON_STATE_IO; - - ioop->err = 0; - return 0; -} - static void do_issue_read(struct console *con, struct io_op *ioop, struct ccw *ccws) { - struct console_line *cline; - int found; - - found = 0; + int ret; atomic_dec(&con->dev->attention); - spin_lock(&con->lock); - list_for_each_entry(cline, &con->read_lines, lines) { - if (cline->state != CON_STATE_FREE) - continue; - - cline->state = CON_STATE_PENDING; - found = 1; - break; - } - - if (!found) { - /* - * No unused console lines, time to allocate a new one - */ - cline = malloc(CON_LINE_ALLOC_SIZE, ZONE_NORMAL); - BUG_ON(!cline); - - cline->state = CON_STATE_PENDING; - list_add_tail(&cline->lines, &con->read_lines); - } - spin_unlock(&con->lock); - /* clear the buffer to allow strlen on the result */ - memset(cline->buf, 0, CON_MAX_LINE_LEN); + memset(con->bigbuf, 0, CONSOLE_LINE_LEN+1); - memset(ccws, 0, sizeof(struct ccw)); - - ccws[0].addr = ADDR31(cline->buf); - ccws[0].count = CON_MAX_LINE_LEN - 1; + ccws[0].addr = ADDR31(con->bigbuf); + ccws[0].count = CONSOLE_LINE_LEN; ccws[0].cmd = 0x0a; ccws[0].flags = CCW_FLAG_SLI; @@ -86,10 +38,14 @@ static void do_issue_read(struct console *con, struct io_op *ioop, struct ccw *c ioop->orb.addr = ADDR31(ccws); ioop->orb.f = 1; - ioop->handler = read_io_int_handler; + ioop->handler = NULL; ioop->dtor = NULL; submit_io(con->dev, ioop, CAN_SLEEP); + + ret = spool_append_rec(con->rlines, con->bigbuf, + strnlen(con->bigbuf, CONSOLE_LINE_LEN)); + BUG_ON(ret); } /** @@ -98,77 +54,43 @@ static void do_issue_read(struct console *con, struct io_op *ioop, struct ccw *c static int console_flusher(void *data) { struct console *con = data; - struct console_line *cline; - int free_count; - int ccw_count; + u8 *buf; + u16 len; + u16 left; /* needed for the IO */ struct io_op ioop; - struct ccw ccws[CON_MAX_FLUSH_LINES]; + struct ccw ccws[PAGE_SIZE/CONSOLE_LINE_LEN]; + int i; for(;;) { + /* FIXME: this should be a sub-unless-zero */ if (atomic_read(&con->dev->attention)) do_issue_read(con, &ioop, ccws); - spin_lock(&con->lock); - - /* - * free all the lines we just finished the IO for - */ - free_count = 0; - list_for_each_entry(cline, &con->write_lines, lines) { - if (cline->state != CON_STATE_IO) - continue; - - cline->state = CON_STATE_FREE; - free_count++; - - if (free_count > CON_MAX_FREE_LINES) { - list_del(&cline->lines); - free(cline); - } - } + buf = con->bigbuf; + left = PAGE_SIZE; - /* - * find at most CON_MAX_FLUSH_LINES of CON_STATE_PENDING - * lines and shove necessary information into the right - * CCW - */ - ccw_count = 0; - list_for_each_entry(cline, &con->write_lines, lines) { - if (ccw_count >= CON_MAX_FLUSH_LINES) + for(i=0; left >= CONSOLE_LINE_LEN; i++) { + len = CONSOLE_LINE_LEN; + if (spool_grab_rec(con->wlines, buf, &len)) break; - if (cline->state != CON_STATE_PENDING) - continue; - - cline->state = CON_STATE_IO; + ccws[i].addr = ADDR31(buf); + ccws[i].count = len; + ccws[i].cmd = 0x01; /* write */ + ccws[i].flags = CCW_FLAG_CC | CCW_FLAG_SLI; - ccws[ccw_count].addr = ADDR31(cline->buf); - ccws[ccw_count].count = cline->len; - ccws[ccw_count].cmd = 0x01; /* write */ - ccws[ccw_count].flags = CCW_FLAG_CC | CCW_FLAG_SLI; - - ccw_count++; + left -= len; + buf += len; } - /* - * We don't need the lock anymore - */ - spin_unlock(&con->lock); - - /* - * Anything to do? - */ - if (!ccw_count) { + if (!i) { schedule(); continue; } - /* - * Clear Command-Chaining on the last CCW - */ - ccws[ccw_count-1].flags &= ~CCW_FLAG_CC; + ccws[i-1].flags &= ~CCW_FLAG_CC; /* * Now, set up the ORB and CCW @@ -197,7 +119,8 @@ static int console_flusher(void *data) static int register_console(struct device *dev) { struct console *con; - struct console_line *cline; + struct page *page; + int ret = 0; dev_get(dev); @@ -206,23 +129,39 @@ static int register_console(struct device *dev) con->sys = NULL; con->dev = dev; - con->lock = SPIN_LOCK_UNLOCKED; - INIT_LIST_HEAD(&con->write_lines); - INIT_LIST_HEAD(&con->read_lines); - /* - * alloc one read-line - */ - cline = malloc(CON_LINE_ALLOC_SIZE, ZONE_NORMAL); - BUG_ON(!cline); - cline->state = CON_STATE_FREE; - list_add(&cline->lines, &con->read_lines); + con->wlines = alloc_spool(); + if (IS_ERR(con->wlines)) { + ret = PTR_ERR(con->wlines); + goto out; + } + + con->rlines = alloc_spool(); + if (IS_ERR(con->rlines)) { + ret = PTR_ERR(con->rlines); + goto out_free; + } + + page = alloc_pages(0, ZONE_NORMAL); + if (!page) { + ret = -ENOMEM; + goto out_free2; + } + + con->bigbuf = page_to_addr(page); spin_lock(&consoles_lock); list_add_tail(&con->consoles, &consoles); spin_unlock(&consoles_lock); - return 0; +out: + return ret; + +out_free2: + free_spool(con->rlines); +out_free: + free_spool(con->wlines); + goto out; } static void print_splash(struct console *con) @@ -289,86 +228,25 @@ void* console_enable(struct device *dev) int con_read_pending(struct console *con) { - struct console_line *cline; - int ret = 0; - - spin_lock(&con->lock); - - list_for_each_entry(cline, &con->read_lines, lines) { - if (cline->state == CON_STATE_IO) { - ret = 1; - break; - } - } - - spin_unlock(&con->lock); - - return ret; + return spool_nrecs(con->rlines); } int con_read(struct console *con, u8 *buf, int size) { - struct console_line *cline; - int len; - - spin_lock(&con->lock); - - list_for_each_entry(cline, &con->read_lines, lines) { - if (cline->state == CON_STATE_IO) - goto found; - } - - spin_unlock(&con->lock); + u16 len = size; - return -1; - -found: - len = (size-1 < cline->len) ? size-1 : cline->len; - - memcpy(buf, cline->buf, len); - buf[len] = '\0'; - - cline->state = CON_STATE_FREE; - - spin_unlock(&con->lock); + if (spool_grab_rec(con->rlines, buf, &len)) + return -1; return len; } int con_write(struct console *con, u8 *buf, int len) { - int bytes = 0; - struct console_line *cline; - - spin_lock(&con->lock); - - list_for_each_entry(cline, &con->write_lines, lines) { - if (cline->state == CON_STATE_FREE) - goto found; - } - - /* None found, can we allocate a new one? */ - cline = malloc(CON_LINE_ALLOC_SIZE, ZONE_NORMAL); - if (!cline) - goto abort; - - list_add_tail(&cline->lines, &con->write_lines); - -found: - cline->state = CON_STATE_PENDING; - - cline->len = (len < CON_MAX_LINE_LEN) ? len : CON_MAX_LINE_LEN; - memcpy(cline->buf, buf, cline->len); - - /* - * All done here. The async thread will pick up the line of text, - * and issue the IO. - */ - -abort: - spin_unlock(&con->lock); + if (spool_append_rec(con->wlines, buf, len)) + return 0; - return bytes; + return len; } void for_each_console(void (*f)(struct console *con)) diff --git a/cp/include/console.h b/cp/include/console.h index 7c876c9..d2facb2 100644 --- a/cp/include/console.h +++ b/cp/include/console.h @@ -1,5 +1,5 @@ /* - * (C) Copyright 2007-2010 Josef 'Jeff' Sipek + * (C) Copyright 2007-2011 Josef 'Jeff' Sipek * * This file is released under the GPLv2. See the COPYING file for more * details. @@ -12,37 +12,17 @@ #include #include -/* line allocation size */ -#define CON_LINE_ALLOC_SIZE 256 - -/* maximum line length */ -#define CON_MAX_LINE_LEN (CON_LINE_ALLOC_SIZE - sizeof(struct console_line)) - -/* maximum number of free lines to keep around */ -#define CON_MAX_FREE_LINES 32 - -/* number of lines of console text to flush at a time */ -#define CON_MAX_FLUSH_LINES 32 - -/* line state */ -#define CON_STATE_FREE 0 -#define CON_STATE_PENDING 1 -#define CON_STATE_IO 2 - -struct console_line { - struct list_head lines; - u16 len; - u16 state; - u8 buf[0]; -}; +#define CONSOLE_LINE_LEN 160 struct console { struct list_head consoles; struct virt_sys *sys; struct device *dev; - spinlock_t lock; - struct list_head write_lines; - struct list_head read_lines; + + struct spool_file *wlines; + struct spool_file *rlines; + + u8 *bigbuf; }; extern int console_interrupt(struct device *dev, struct irb *irb); -- 2.11.4.GIT