patch-2.3.46 linux/drivers/char/console.c
Next file: linux/drivers/char/dsp56k.c
Previous file: linux/drivers/char/Makefile
Back to the patch index
Back to the overall index
- Lines: 311
- Date:
Wed Feb 16 15:42:05 2000
- Orig file:
v2.3.45/linux/drivers/char/console.c
- Orig date:
Fri Jan 28 15:09:07 2000
diff -u --recursive --new-file v2.3.45/linux/drivers/char/console.c linux/drivers/char/console.c
@@ -81,6 +81,7 @@
#include <linux/mm.h>
#include <linux/console.h>
#include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
#include <linux/vt_kern.h>
#include <linux/selection.h>
#include <linux/console_struct.h>
@@ -120,6 +121,10 @@
#define DEFAULT_BELL_PITCH 750
#define DEFAULT_BELL_DURATION (HZ/8)
+extern int tty_register_devfs (struct tty_driver *driver, unsigned int flags,
+ unsigned int minor);
+extern void vcs_make_devfs (unsigned int index, int unregister);
+
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
@@ -177,12 +182,13 @@
* Unfortunately, we need to delay tty echo when we're currently writing to the
* console since the code is (and always was) not re-entrant, so we insert
* all filp requests to con_task_queue instead of tq_timer and run it from
- * the console_bh.
+ * the console_tasklet. The console_tasklet is protected by the IRQ
+ * protected console_lock.
*/
DECLARE_TASK_QUEUE(con_task_queue);
/*
- * For the same reason, we defer scrollback to the console_bh.
+ * For the same reason, we defer scrollback to the console tasklet.
*/
static int scrollback_delta = 0;
@@ -224,7 +230,7 @@
static inline void scrolldelta(int lines)
{
scrollback_delta += lines;
- mark_bh(CONSOLE_BH);
+ tasklet_schedule(&console_tasklet);
}
static void scrup(int currcons, unsigned int t, unsigned int b, int nr)
@@ -546,16 +552,12 @@
{
int redraw = 1;
int currcons, old_console;
- static int lock = 0;
- if (lock)
- return;
if (!vc_cons_allocated(new_console)) {
/* strange ... */
- printk("redraw_screen: tty %d not allocated ??\n", new_console+1);
+ /* printk("redraw_screen: tty %d not allocated ??\n", new_console+1); */
return;
}
- lock = 1;
if (is_switch) {
currcons = fg_console;
@@ -591,7 +593,6 @@
set_leds();
compute_shiftstate();
}
- lock = 0;
}
/*
@@ -1785,6 +1786,19 @@
}
}
+/* This is a temporary buffer used to prepare a tty console write
+ * so that we can easily avoid touching user space while holding the
+ * console spinlock. It is allocated in con_init and is shared by
+ * this code and the vc_screen read/write tty calls.
+ *
+ * We have to allocate this statically in the kernel data section
+ * since console_init (and thus con_init) are called before any
+ * kernel memory allocation is available.
+ */
+char con_buf[PAGE_SIZE];
+#define CON_BUF_SIZE PAGE_SIZE
+DECLARE_MUTEX(con_buf_sem);
+
static int do_con_write(struct tty_struct * tty, int from_user,
const unsigned char *buf, int count)
{
@@ -1814,12 +1828,28 @@
return 0;
}
+ down(&con_buf_sem);
+
if (from_user) {
- /* just to make sure that noone lurks at places he shouldn't see. */
- if (verify_area(VERIFY_READ, buf, count))
- return 0; /* ?? are error codes legal here ?? */
+ if (count > CON_BUF_SIZE)
+ count = CON_BUF_SIZE;
+ if (copy_from_user(con_buf, buf, count)) {
+ n = 0; /* ?? are error codes legal here ?? */
+ goto out;
+ }
+
+ buf = con_buf;
}
+ /* At this point 'buf' is guarenteed to be a kernel buffer
+ * and therefore no access to userspace (and therefore sleeping)
+ * will be needed. The con_buf_sem serializes all tty based
+ * console rendering and vcs write/read operations. We hold
+ * the console spinlock during the entire write.
+ */
+
+ spin_lock_irq(&console_lock);
+
himask = hi_font_mask;
charmask = himask ? 0x1ff : 0xff;
@@ -1827,15 +1857,11 @@
if (IS_FG)
hide_cursor(currcons);
- disable_bh(CONSOLE_BH);
while (!tty->stopped && count) {
- enable_bh(CONSOLE_BH);
- if (from_user)
- __get_user(c, buf);
- else
- c = *buf;
- buf++; n++; count--;
- disable_bh(CONSOLE_BH);
+ c = *buf;
+ buf++;
+ n++;
+ count--;
if (utf) {
/* Combine UTF-8 into Unicode */
@@ -1940,23 +1966,34 @@
do_con_trol(tty, currcons, c);
}
FLUSH
- enable_bh(CONSOLE_BH);
+ spin_unlock_irq(&console_lock);
+
+out:
+ up(&con_buf_sem);
+
return n;
#undef FLUSH
}
/*
- * This is the console switching bottom half handler.
+ * This is the console switching tasklet.
*
- * Doing console switching in a bottom half handler allows
+ * Doing console switching in a tasklet allows
* us to do the switches asynchronously (needed when we want
- * to switch due to a keyboard interrupt), while still giving
- * us the option to easily disable it to avoid races when we
- * need to write to the console.
- */
-static void console_bh(void)
-{
+ * to switch due to a keyboard interrupt). Synchronization
+ * with other console code and prevention of re-entrancy is
+ * ensured with console_lock.
+ */
+static void console_softint(unsigned long ignored)
+{
+ /* Runs the task queue outside of the console lock. These
+ * callbacks can come back into the console code and thus
+ * will perform their own locking.
+ */
run_task_queue(&con_task_queue);
+
+ spin_lock_irq(&console_lock);
+
if (want_console >= 0) {
if (want_console != fg_console && vc_cons_allocated(want_console)) {
hide_cursor(fg_console);
@@ -1978,6 +2015,8 @@
sw->con_scrolldelta(vc_cons[currcons].d, scrollback_delta);
scrollback_delta = 0;
}
+
+ spin_unlock_irq(&console_lock);
}
#ifdef CONFIG_VT_CONSOLE
@@ -1985,10 +2024,7 @@
/*
* Console on virtual terminal
*
- * NOTE NOTE NOTE! This code can do no global locking. In particular,
- * we can't disable interrupts or bottom half handlers globally, because
- * we can be called from contexts that hold critical spinlocks, and
- * trying do get a global lock at this point will lead to deadlocks.
+ * The console_lock must be held when we get here.
*/
void vt_console_print(struct console *co, const char * b, unsigned count)
@@ -2015,7 +2051,7 @@
if (!vc_cons_allocated(currcons)) {
/* impossible */
- printk("vt_console_print: tty %d not allocated ??\n", currcons+1);
+ /* printk("vt_console_print: tty %d not allocated ??\n", currcons+1); */
goto quit;
}
@@ -2263,13 +2299,18 @@
tty->winsize.ws_row = video_num_lines;
tty->winsize.ws_col = video_num_columns;
}
+ if (tty->count == 1)
+ vcs_make_devfs (currcons, 0);
return 0;
}
static void con_close(struct tty_struct *tty, struct file * filp)
{
- if (tty->count == 1)
- tty->driver_data = 0;
+ if (!tty)
+ return;
+ if (tty->count != 1) return;
+ vcs_make_devfs (MINOR (tty->device) - tty->driver.minor_start, 1);
+ tty->driver_data = 0;
}
static void vc_init(unsigned int currcons, unsigned int rows, unsigned int cols, int do_clear)
@@ -2305,6 +2346,8 @@
struct tty_driver console_driver;
static int console_refcount;
+DECLARE_TASKLET_DISABLED(console_tasklet, console_softint, 0);
+
void __init con_init(void)
{
const char *display_desc = NULL;
@@ -2319,7 +2362,7 @@
memset(&console_driver, 0, sizeof(struct tty_driver));
console_driver.magic = TTY_DRIVER_MAGIC;
- console_driver.name = "tty";
+ console_driver.name = "vc/%d";
console_driver.name_base = 1;
console_driver.major = TTY_MAJOR;
console_driver.minor_start = 1;
@@ -2327,6 +2370,11 @@
console_driver.type = TTY_DRIVER_TYPE_CONSOLE;
console_driver.init_termios = tty_std_termios;
console_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS;
+ /* Tell tty_register_driver() to skip consoles because they are
+ * registered before kmalloc() is ready. We'll patch them in later.
+ * See comments at console_init(); see also con_init_devfs().
+ */
+ console_driver.flags |= TTY_DRIVER_NO_DEVFS;
console_driver.refcount = &console_refcount;
console_driver.table = console_table;
console_driver.termios = console_termios;
@@ -2393,7 +2441,8 @@
register_console(&vt_console_driver);
#endif
- init_bh(CONSOLE_BH, console_bh);
+ tasklet_enable(&console_tasklet);
+ tasklet_schedule(&console_tasklet);
}
#ifndef VT_SINGLE_DRIVER
@@ -2486,6 +2535,19 @@
vesa_blank_mode = (mode < 4) ? mode : 0;
}
+/* We can't register the console with devfs during con_init(), because it
+ * is called before kmalloc() works. This function is called later to
+ * do the registration.
+ */
+void __init con_init_devfs (void)
+{
+ int i;
+
+ for (i = 0; i < console_driver.num; i++)
+ tty_register_devfs (&console_driver, 0,
+ console_driver.minor_start + i);
+}
+
static void vesa_powerdown(void)
{
struct vc_data *c = vc_cons[fg_console].d;
@@ -2744,9 +2806,11 @@
}
op->data = temp;
}
- disable_bh(CONSOLE_BH);
+
+ spin_lock_irq(&console_lock);
rc = sw->con_font_op(vc_cons[currcons].d, op);
- enable_bh(CONSOLE_BH);
+ spin_unlock_irq(&console_lock);
+
op->data = old_op.data;
if (!rc && !set) {
int c = (op->width+7)/8 * 32 * op->charcount;
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)