patch-2.4.10 linux/drivers/char/console.c
Next file: linux/drivers/char/cyclades.c
Previous file: linux/drivers/char/busmouse.c
Back to the patch index
Back to the overall index
- Lines: 520
- Date:
Mon Sep 17 22:52:35 2001
- Orig file:
v2.4.9/linux/drivers/char/console.c
- Orig date:
Mon Aug 27 12:41:40 2001
diff -u --recursive --new-file v2.4.9/linux/drivers/char/console.c linux/drivers/char/console.c
@@ -69,6 +69,9 @@
*
* Removed old-style timers, introduced console_timer, made timer
* deletion SMP-safe. 17Jun00, Andrew Morton <andrewm@uow.edu.au>
+ *
+ * Removed console_lock, enabled interrupts across all console operations
+ * 13 March 2001, Andrew Morton
*/
#include <linux/module.h>
@@ -149,6 +152,7 @@
static void set_cursor(int currcons);
static void hide_cursor(int currcons);
static void unblank_screen_t(unsigned long dummy);
+static void console_callback(void *ignored);
static int printable; /* Is console ready for printing? */
@@ -159,6 +163,10 @@
static int blankinterval = 10*60*HZ;
static int vesa_off_interval;
+static struct tq_struct console_callback_tq = {
+ routine: console_callback,
+};
+
/*
* fg_console is the current virtual console,
* last_console is the last used one,
@@ -180,15 +188,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_tasklet. The console_tasklet is protected by the IRQ
- * protected console_lock.
+ * console since the code is (and always was) not re-entrant, so we schedule
+ * all flip requests to process context with schedule-task() and run it from
+ * console_callback().
*/
-DECLARE_TASK_QUEUE(con_task_queue);
/*
- * For the same reason, we defer scrollback to the console tasklet.
+ * For the same reason, we defer scrollback to the console callback.
*/
static int scrollback_delta;
@@ -232,7 +238,12 @@
static inline void scrolldelta(int lines)
{
scrollback_delta += lines;
- tasklet_schedule(&console_tasklet);
+ schedule_console_callback();
+}
+
+void schedule_console_callback(void)
+{
+ schedule_task(&console_callback_tq);
}
static void scrup(int currcons, unsigned int t, unsigned int b, int nr)
@@ -650,6 +661,7 @@
p = (long) kmalloc(structsize, GFP_KERNEL);
if (!p)
return -ENOMEM;
+ memset((void *)p, 0, structsize);
vc_cons[currcons].d = (struct vc_data *)p;
vt_cons[currcons] = (struct vt_struct *)(p+sizeof(struct vc_data));
visual_init(currcons, 1);
@@ -780,6 +792,7 @@
void vc_disallocate(unsigned int currcons)
{
+ acquire_console_sem();
if (vc_cons_allocated(currcons)) {
sw->con_deinit(vc_cons[currcons].d);
if (kmalloced)
@@ -788,6 +801,7 @@
kfree(vc_cons[currcons].d);
vc_cons[currcons].d = NULL;
}
+ release_console_sem();
}
/*
@@ -1026,6 +1040,7 @@
color = def_color;
}
+/* console_sem is held */
static void csi_m(int currcons)
{
int i;
@@ -1165,6 +1180,7 @@
return report_mouse;
}
+/* console_sem is held */
static void set_mode(int currcons, int on_off)
{
int i;
@@ -1230,6 +1246,7 @@
}
}
+/* console_sem is held */
static void setterm_command(int currcons)
{
switch(par[0]) {
@@ -1284,19 +1301,7 @@
}
}
-static void insert_line(int currcons, unsigned int nr)
-{
- scrdown(currcons,y,bottom,nr);
- need_wrap = 0;
-}
-
-
-static void delete_line(int currcons, unsigned int nr)
-{
- scrup(currcons,y,bottom,nr);
- need_wrap = 0;
-}
-
+/* console_sem is held */
static void csi_at(int currcons, unsigned int nr)
{
if (nr > video_num_columns - x)
@@ -1306,15 +1311,18 @@
insert_char(currcons, nr);
}
+/* console_sem is held */
static void csi_L(int currcons, unsigned int nr)
{
if (nr > video_num_lines - y)
nr = video_num_lines - y;
else if (!nr)
nr = 1;
- insert_line(currcons, nr);
+ scrdown(currcons,y,bottom,nr);
+ need_wrap = 0;
}
+/* console_sem is held */
static void csi_P(int currcons, unsigned int nr)
{
if (nr > video_num_columns - x)
@@ -1324,15 +1332,18 @@
delete_char(currcons, nr);
}
+/* console_sem is held */
static void csi_M(int currcons, unsigned int nr)
{
if (nr > video_num_lines - y)
nr = video_num_lines - y;
else if (!nr)
nr=1;
- delete_line(currcons, nr);
+ scrup(currcons,y,bottom,nr);
+ need_wrap = 0;
}
+/* console_sem is held (except via vc_init->reset_terminal */
static void save_cur(int currcons)
{
saved_x = x;
@@ -1347,6 +1358,7 @@
saved_G1 = G1_charset;
}
+/* console_sem is held */
static void restore_cur(int currcons)
{
gotoxy(currcons,saved_x,saved_y);
@@ -1367,6 +1379,7 @@
EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd,
ESpalette };
+/* console_sem is held (except via vc_init()) */
static void reset_terminal(int currcons, int do_clear)
{
top = 0;
@@ -1422,6 +1435,7 @@
csi_J(currcons,2);
}
+/* console_sem is held */
static void do_con_trol(struct tty_struct *tty, unsigned int currcons, int c)
{
/*
@@ -1802,6 +1816,7 @@
#define CON_BUF_SIZE PAGE_SIZE
DECLARE_MUTEX(con_buf_sem);
+/* acquires console_sem */
static int do_con_write(struct tty_struct * tty, int from_user,
const unsigned char *buf, int count)
{
@@ -1822,6 +1837,9 @@
const unsigned char *orig_buf = NULL;
int orig_count;
+ if (in_interrupt())
+ return count;
+
currcons = vt->vc_num;
if (!vc_cons_allocated(currcons)) {
/* could this happen? */
@@ -1842,6 +1860,7 @@
again:
if (count > CON_BUF_SIZE)
count = CON_BUF_SIZE;
+ console_conditional_schedule();
if (copy_from_user(con_buf, buf, count)) {
n = 0; /* ?? are error codes legal here ?? */
goto out;
@@ -1857,7 +1876,7 @@
* the console spinlock during the entire write.
*/
- spin_lock_irq(&console_lock);
+ acquire_console_sem();
himask = hi_font_mask;
charmask = himask ? 0x1ff : 0xff;
@@ -1975,7 +1994,8 @@
do_con_trol(tty, currcons, c);
}
FLUSH
- spin_unlock_irq(&console_lock);
+ console_conditional_schedule();
+ release_console_sem();
out:
if (from_user) {
@@ -1999,23 +2019,17 @@
}
/*
- * This is the console switching tasklet.
+ * This is the console switching callback.
*
- * Doing console switching in a tasklet allows
+ * Doing console switching in a process context allows
* us to do the switches asynchronously (needed when we want
* to switch due to a keyboard interrupt). Synchronization
* with other console code and prevention of re-entrancy is
- * ensured with console_lock.
+ * ensured with console_sem.
*/
-static void console_softint(unsigned long ignored)
+static void console_callback(void *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);
+ acquire_console_sem();
if (want_console >= 0) {
if (want_console != fg_console && vc_cons_allocated(want_console)) {
@@ -2039,7 +2053,13 @@
scrollback_delta = 0;
}
- spin_unlock_irq(&console_lock);
+ release_console_sem();
+}
+
+void set_console(int nr)
+{
+ want_console = nr;
+ schedule_console_callback();
}
#ifdef CONFIG_VT_CONSOLE
@@ -2047,7 +2067,7 @@
/*
* Console on virtual terminal
*
- * The console_lock must be held when we get here.
+ * The console must be locked when we get here.
*/
void vt_console_print(struct console *co, const char * b, unsigned count)
@@ -2134,6 +2154,9 @@
}
set_cursor(currcons);
+ if (!oops_in_progress)
+ poke_blanked_console();
+
quit:
clear_bit(0, &printing);
}
@@ -2158,27 +2181,45 @@
* Handling of Linux-specific VC ioctls
*/
+/*
+ * Generally a bit racy with respect to console_sem().
+ *
+ * There are some functions which don't need it.
+ *
+ * There are some functions which can sleep for arbitrary periods (paste_selection)
+ * but we don't need the lock there anyway.
+ *
+ * set_selection has locking, and definitely needs it
+ */
+
int tioclinux(struct tty_struct *tty, unsigned long arg)
{
char type, data;
+ int ret;
if (tty->driver.type != TTY_DRIVER_TYPE_CONSOLE)
return -EINVAL;
- if (current->tty != tty && !suser())
+ if (current->tty != tty && !capable(CAP_SYS_ADMIN))
return -EPERM;
if (get_user(type, (char *)arg))
return -EFAULT;
+ ret = 0;
switch (type)
{
case 2:
- return set_selection(arg, tty, 1);
+ acquire_console_sem();
+ ret = set_selection(arg, tty, 1);
+ release_console_sem();
+ break;
case 3:
- return paste_selection(tty);
+ ret = paste_selection(tty);
+ break;
case 4:
unblank_screen();
- return 0;
+ break;
case 5:
- return sel_loadlut(arg);
+ ret = sel_loadlut(arg);
+ break;
case 6:
/*
@@ -2188,24 +2229,33 @@
* related to the kernel should not use this.
*/
data = shift_state;
- return __put_user(data, (char *) arg);
+ ret = __put_user(data, (char *) arg);
+ break;
case 7:
data = mouse_reporting();
- return __put_user(data, (char *) arg);
+ ret = __put_user(data, (char *) arg);
+ break;
case 10:
set_vesa_blanking(arg);
- return 0;
+ break;;
case 11: /* set kmsg redirect */
- if (!suser())
- return -EPERM;
- if (get_user(data, (char *)arg+1))
- return -EFAULT;
- kmsg_redirect = data;
- return 0;
+ if (!capable(CAP_SYS_ADMIN)) {
+ ret = -EPERM;
+ } else {
+ if (get_user(data, (char *)arg+1))
+ ret = -EFAULT;
+ else
+ kmsg_redirect = data;
+ }
+ break;
case 12: /* get fg_console */
- return fg_console;
+ ret = fg_console;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
}
- return -EINVAL;
+ return ret;
}
/*
@@ -2226,6 +2276,8 @@
static void con_put_char(struct tty_struct *tty, unsigned char ch)
{
+ if (in_interrupt())
+ return; /* n_r3964 calls put_char() from interrupt context */
pm_access(pm_con);
do_con_write(tty, 0, &ch, 1);
}
@@ -2290,13 +2342,15 @@
static void con_flush_chars(struct tty_struct *tty)
{
- unsigned long flags;
struct vt_struct *vt = (struct vt_struct *)tty->driver_data;
+ if (in_interrupt()) /* from flush_to_ldisc */
+ return;
+
pm_access(pm_con);
- spin_lock_irqsave(&console_lock, flags);
+ acquire_console_sem();
set_cursor(vt->vc_num);
- spin_unlock_irqrestore(&console_lock, flags);
+ release_console_sem();
}
/*
@@ -2367,8 +2421,6 @@
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;
@@ -2453,9 +2505,6 @@
#ifdef CONFIG_VT_CONSOLE
register_console(&vt_console_driver);
#endif
-
- tasklet_enable(&console_tasklet);
- tasklet_schedule(&console_tasklet);
}
#ifndef VT_SINGLE_DRIVER
@@ -2561,6 +2610,9 @@
console_driver.minor_start + i);
}
+/*
+ * This is called by a timer handler
+ */
static void vesa_powerdown(void)
{
struct vc_data *c = vc_cons[fg_console].d;
@@ -2581,9 +2633,12 @@
}
}
+/*
+ * This is a timer handler
+ */
static void vesa_powerdown_screen(unsigned long dummy)
{
- console_timer.function = unblank_screen_t; /* I don't have a clue why this is necessary */
+ console_timer.function = unblank_screen_t;
vesa_powerdown();
}
@@ -2642,11 +2697,17 @@
timer_do_blank_screen(entering_gfx, 0);
}
+/*
+ * This is a timer handler
+ */
static void unblank_screen_t(unsigned long dummy)
{
unblank_screen();
}
+/*
+ * Called by timer as well as from vt_console_driver
+ */
void unblank_screen(void)
{
int currcons;
@@ -2677,6 +2738,9 @@
set_cursor(fg_console);
}
+/*
+ * This is both a user-level callable and a timer handler
+ */
static void blank_screen(unsigned long dummy)
{
timer_do_blank_screen(0, 1);
@@ -2684,7 +2748,7 @@
void poke_blanked_console(void)
{
- del_timer(&console_timer); /* Can't use _sync here: called from tasklet */
+ del_timer(&console_timer);
if (!vt_cons[fg_console] || vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
return;
if (console_blanked) {
@@ -2832,9 +2896,9 @@
op->data = temp;
}
- spin_lock_irq(&console_lock);
+ acquire_console_sem();
rc = sw->con_font_op(vc_cons[currcons].d, op);
- spin_unlock_irq(&console_lock);
+ release_console_sem();
op->data = old_op.data;
if (!rc && !set) {
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)