Commit 93a2d85f authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] VT locking fixes

From: Benjamin Herrenschmidt <benh@kernel.crashing.org>

- Make sure that all console operations are approriately protected under
  console_sem.

- Adds checks to make sure that people are taking console_sem when it is
  expected to be held.
parent 9f078ca2
......@@ -24,6 +24,7 @@
#include <linux/consolemap.h>
#include <linux/selection.h>
#include <linux/tiocl.h>
#include <linux/console.h>
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
......@@ -290,7 +291,10 @@ int paste_selection(struct tty_struct *tty)
int pasted = 0, count;
DECLARE_WAITQUEUE(wait, current);
acquire_console_sem();
poke_blanked_console();
release_console_sem();
add_wait_queue(&vt->paste_wait, &wait);
while (sel_buffer && sel_buffer_lth > pasted) {
set_current_state(TASK_INTERRUPTIBLE);
......
......@@ -1484,7 +1484,12 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
#ifdef CONFIG_VT
if (tty->driver->type == TTY_DRIVER_TYPE_CONSOLE) {
unsigned int currcons = tty->index;
if (vc_resize(currcons, tmp_ws.ws_col, tmp_ws.ws_row))
int rc;
acquire_console_sem();
rc = vc_resize(currcons, tmp_ws.ws_col, tmp_ws.ws_row);
release_console_sem();
if (rc)
return -ENXIO;
}
#endif
......
This diff is collapsed.
......@@ -470,6 +470,9 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
* currently, setting the mode from KD_TEXT to KD_GRAPHICS
* doesn't do a whole lot. i'm not sure if it should do any
* restoration of modes or what...
*
* XXX It should at least call into the driver, fbdev's definitely
* need to restore their engine state. --BenH
*/
if (!perm)
return -EPERM;
......@@ -492,10 +495,12 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
/*
* explicitly blank/unblank the screen if switching modes
*/
acquire_console_sem();
if (arg == KD_TEXT)
unblank_screen();
else
do_blank_screen(1);
release_console_sem();
return 0;
case KDGETMODE:
......@@ -665,18 +670,29 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
return -EFAULT;
if (tmp.mode != VT_AUTO && tmp.mode != VT_PROCESS)
return -EINVAL;
acquire_console_sem();
vt_cons[console]->vt_mode = tmp;
/* the frsig is ignored, so we set it to 0 */
vt_cons[console]->vt_mode.frsig = 0;
vt_cons[console]->vt_pid = current->pid;
/* no switch is required -- saw@shade.msu.ru */
vt_cons[console]->vt_newvt = -1;
release_console_sem();
return 0;
}
case VT_GETMODE:
return copy_to_user((void*)arg, &(vt_cons[console]->vt_mode),
sizeof(struct vt_mode)) ? -EFAULT : 0;
{
struct vt_mode tmp;
int rc;
acquire_console_sem();
memcpy(&tmp, &vt_cons[console]->vt_mode, sizeof(struct vt_mode));
release_console_sem();
rc = copy_to_user((void*)arg, &tmp, sizeof(struct vt_mode));
return rc ? -EFAULT : 0;
}
/*
* Returns global vt state. Note that VT 0 is always open, since
......@@ -718,7 +734,9 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
if (arg == 0 || arg > MAX_NR_CONSOLES)
return -ENXIO;
arg--;
acquire_console_sem();
i = vc_allocate(arg);
release_console_sem();
if (i)
return i;
set_console(arg);
......@@ -768,17 +786,20 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
* The current vt has been released, so
* complete the switch.
*/
int newvt = vt_cons[console]->vt_newvt;
int newvt;
acquire_console_sem();
newvt = vt_cons[console]->vt_newvt;
vt_cons[console]->vt_newvt = -1;
i = vc_allocate(newvt);
if (i)
if (i) {
release_console_sem();
return i;
}
/*
* When we actually do the console switch,
* make sure we are atomic with respect to
* other console switches..
*/
acquire_console_sem();
complete_change_console(newvt);
release_console_sem();
}
......@@ -806,16 +827,21 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
return -ENXIO;
if (arg == 0) {
/* disallocate all unused consoles, but leave 0 */
for (i=1; i<MAX_NR_CONSOLES; i++)
if (! VT_BUSY(i))
vc_disallocate(i);
acquire_console_sem();
for (i=1; i<MAX_NR_CONSOLES; i++)
if (! VT_BUSY(i))
vc_disallocate(i);
release_console_sem();
} else {
/* disallocate a single console, if possible */
arg--;
if (VT_BUSY(arg))
return -EBUSY;
if (arg) /* leave 0 */
vc_disallocate(arg);
/* disallocate a single console, if possible */
arg--;
if (VT_BUSY(arg))
return -EBUSY;
if (arg) { /* leave 0 */
acquire_console_sem();
vc_disallocate(arg);
release_console_sem();
}
}
return 0;
......@@ -828,8 +854,11 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
if (get_user(ll, &vtsizes->v_rows) ||
get_user(cc, &vtsizes->v_cols))
return -EFAULT;
for (i = 0; i < MAX_NR_CONSOLES; i++)
for (i = 0; i < MAX_NR_CONSOLES; i++) {
acquire_console_sem();
vc_resize(i, cc, ll);
release_console_sem();
}
return 0;
}
......@@ -870,11 +899,13 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
for (i = 0; i < MAX_NR_CONSOLES; i++) {
if (!vc_cons[i].d)
continue;
acquire_console_sem();
if (vlin)
vc_cons[i].d->vc_scan_lines = vlin;
if (clin)
vc_cons[i].d->vc_font.height = clin;
vc_resize(i, cc, ll);
release_console_sem();
}
return 0;
}
......
......@@ -102,6 +102,14 @@ extern void acquire_console_sem(void);
extern void release_console_sem(void);
extern void console_conditional_schedule(void);
extern void console_unblank(void);
extern int is_console_locked(void);
/* Some debug stub to catch some of the obvious races in the VT code */
#if 1
#define WARN_CONSOLE_UNLOCKED() WARN_ON(!is_console_locked() && !oops_in_progress)
#else
#define WARN_CONSOLE_UNLOCKED()
#endif
/* VESA Blanking Levels */
#define VESA_NO_BLANKING 0
......
......@@ -6,6 +6,7 @@
#include <linux/vt_kern.h>
#include <linux/kbd_kern.h>
#include <linux/console.h>
#include "power.h"
static int new_loglevel = 10;
......@@ -18,14 +19,20 @@ int pm_prepare_console(void)
console_loglevel = new_loglevel;
#ifdef SUSPEND_CONSOLE
acquire_console_sem();
orig_fgconsole = fg_console;
if (vc_allocate(SUSPEND_CONSOLE))
if (vc_allocate(SUSPEND_CONSOLE)) {
/* we can't have a free VC for now. Too bad,
* we don't want to mess the screen for now. */
release_console_sem();
return 1;
}
set_console(SUSPEND_CONSOLE);
release_console_sem();
if (vt_waitactive(SUSPEND_CONSOLE)) {
pr_debug("Suspend: Can't switch VCs.");
return 1;
......@@ -40,12 +47,9 @@ void pm_restore_console(void)
{
console_loglevel = orig_loglevel;
#ifdef SUSPEND_CONSOLE
acquire_console_sem();
set_console(orig_fgconsole);
/* FIXME:
* This following part is left over from swsusp. Is it really needed?
*/
update_screen(fg_console);
release_console_sem();
#endif
return;
}
......@@ -62,6 +62,15 @@ int oops_in_progress;
*/
static DECLARE_MUTEX(console_sem);
struct console *console_drivers;
/*
* This is used for debugging the mess that is the VT code by
* keeping track if we have the console semaphore held. It's
* definitely not the perfect debug tool (we don't know if _WE_
* hold it are racing, but it helps tracking those weird code
* path in the console code where we end up in places I want
* locked without the console sempahore held
*/
static int console_locked;
/*
* logbuf_lock protects log_buf, log_start, log_end, con_start and logged_chars
......@@ -524,6 +533,7 @@ asmlinkage int printk(const char *fmt, ...)
goto out;
}
if (!down_trylock(&console_sem)) {
console_locked = 1;
/*
* We own the drivers. We can drop the spinlock and let
* release_console_sem() print the text
......@@ -557,10 +567,17 @@ void acquire_console_sem(void)
if (in_interrupt())
BUG();
down(&console_sem);
console_locked = 1;
console_may_schedule = 1;
}
EXPORT_SYMBOL(acquire_console_sem);
int is_console_locked(void)
{
return console_locked;
}
EXPORT_SYMBOL(is_console_locked);
/**
* release_console_sem - unlock the console system
*
......@@ -592,12 +609,14 @@ void release_console_sem(void)
spin_unlock_irqrestore(&logbuf_lock, flags);
call_console_drivers(_con_start, _log_end);
}
console_locked = 0;
console_may_schedule = 0;
up(&console_sem);
spin_unlock_irqrestore(&logbuf_lock, flags);
if (wake_klogd && !oops_in_progress && waitqueue_active(&log_wait))
wake_up_interruptible(&log_wait);
}
EXPORT_SYMBOL(release_console_sem);
/** console_conditional_schedule - yield the CPU if required
*
......@@ -633,6 +652,7 @@ void console_unblank(void)
*/
if (down_trylock(&console_sem) != 0)
return;
console_locked = 1;
console_may_schedule = 0;
for (c = console_drivers; c != NULL; c = c->next)
if ((c->flags & CON_ENABLED) && c->unblank)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment