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

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)