mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-05 10:29:37 +00:00
![Peter Hurley](/assets/img/avatar_default.png)
Now that the tty port owns the flip buffers and i/o is allowed from the driver even when no tty is attached, the destruction of the tty port (and the flip buffers) must ensure that no outstanding work is pending. Unfortunately, this creates a lock order problem with the console_lock (see attached lockdep report [1] below). For single console deallocation, drop the console_lock prior to port destruction. When multiple console deallocation, defer port destruction until the consoles have been deallocated. tty_port_destroy() is not required if the port has not been used; remove from vc_allocate() failure path. [1] lockdep report from Dave Jones <davej@redhat.com> ====================================================== [ INFO: possible circular locking dependency detected ] 3.9.0+ #16 Not tainted ------------------------------------------------------- (agetty)/26163 is trying to acquire lock: blocked: ((&buf->work)){+.+...}, instance: ffff88011c8b0020, at: [<ffffffff81062065>] flush_work+0x5/0x2e0 but task is already holding lock: blocked: (console_lock){+.+.+.}, instance: ffffffff81c2fde0, at: [<ffffffff813bc201>] vt_ioctl+0xb61/0x1230 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #1 (console_lock){+.+.+.}: [<ffffffff810b3f74>] lock_acquire+0xa4/0x210 [<ffffffff810416c7>] console_lock+0x77/0x80 [<ffffffff813c3dcd>] con_flush_chars+0x2d/0x50 [<ffffffff813b32b2>] n_tty_receive_buf+0x122/0x14d0 [<ffffffff813b7709>] flush_to_ldisc+0x119/0x170 [<ffffffff81064381>] process_one_work+0x211/0x700 [<ffffffff8106498b>] worker_thread+0x11b/0x3a0 [<ffffffff8106ce5d>] kthread+0xed/0x100 [<ffffffff81601cac>] ret_from_fork+0x7c/0xb0 -> #0 ((&buf->work)){+.+...}: [<ffffffff810b349a>] __lock_acquire+0x193a/0x1c00 [<ffffffff810b3f74>] lock_acquire+0xa4/0x210 [<ffffffff810620ae>] flush_work+0x4e/0x2e0 [<ffffffff81065305>] __cancel_work_timer+0x95/0x130 [<ffffffff810653b0>] cancel_work_sync+0x10/0x20 [<ffffffff813b8212>] tty_port_destroy+0x12/0x20 [<ffffffff813c65e8>] vc_deallocate+0xf8/0x110 [<ffffffff813bc20c>] vt_ioctl+0xb6c/0x1230 [<ffffffff813b01a5>] tty_ioctl+0x285/0xd50 [<ffffffff811ba825>] do_vfs_ioctl+0x305/0x530 [<ffffffff811baad1>] sys_ioctl+0x81/0xa0 [<ffffffff81601d59>] system_call_fastpath+0x16/0x1b other info that might help us debug this: [ 6760.076175] Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(console_lock); lock((&buf->work)); lock(console_lock); lock((&buf->work)); *** DEADLOCK *** 1 lock on stack by (agetty)/26163: #0: blocked: (console_lock){+.+.+.}, instance: ffffffff81c2fde0, at: [<ffffffff813bc201>] vt_ioctl+0xb61/0x1230 stack backtrace: Pid: 26163, comm: (agetty) Not tainted 3.9.0+ #16 Call Trace: [<ffffffff815edb14>] print_circular_bug+0x200/0x20e [<ffffffff810b349a>] __lock_acquire+0x193a/0x1c00 [<ffffffff8100a269>] ? sched_clock+0x9/0x10 [<ffffffff8100a269>] ? sched_clock+0x9/0x10 [<ffffffff8100a200>] ? native_sched_clock+0x20/0x80 [<ffffffff810b3f74>] lock_acquire+0xa4/0x210 [<ffffffff81062065>] ? flush_work+0x5/0x2e0 [<ffffffff810620ae>] flush_work+0x4e/0x2e0 [<ffffffff81062065>] ? flush_work+0x5/0x2e0 [<ffffffff810b15db>] ? mark_held_locks+0xbb/0x140 [<ffffffff8113c8a3>] ? __free_pages_ok.part.57+0x93/0xc0 [<ffffffff810b15db>] ? mark_held_locks+0xbb/0x140 [<ffffffff810652f2>] ? __cancel_work_timer+0x82/0x130 [<ffffffff81065305>] __cancel_work_timer+0x95/0x130 [<ffffffff810653b0>] cancel_work_sync+0x10/0x20 [<ffffffff813b8212>] tty_port_destroy+0x12/0x20 [<ffffffff813c65e8>] vc_deallocate+0xf8/0x110 [<ffffffff813bc20c>] vt_ioctl+0xb6c/0x1230 [<ffffffff810aec41>] ? lock_release_holdtime.part.30+0xa1/0x170 [<ffffffff813b01a5>] tty_ioctl+0x285/0xd50 [<ffffffff812b00f6>] ? inode_has_perm.isra.46.constprop.61+0x56/0x80 [<ffffffff811ba825>] do_vfs_ioctl+0x305/0x530 [<ffffffff812b04db>] ? selinux_file_ioctl+0x5b/0x110 [<ffffffff811baad1>] sys_ioctl+0x81/0xa0 [<ffffffff81601d59>] system_call_fastpath+0x16/0x1b Cc: Dave Jones <davej@redhat.com> Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
198 lines
6.2 KiB
C
198 lines
6.2 KiB
C
#ifndef _VT_KERN_H
|
|
#define _VT_KERN_H
|
|
|
|
/*
|
|
* this really is an extension of the vc_cons structure in console.c, but
|
|
* with information needed by the vt package
|
|
*/
|
|
|
|
#include <linux/vt.h>
|
|
#include <linux/kd.h>
|
|
#include <linux/tty.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/console_struct.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/consolemap.h>
|
|
#include <linux/notifier.h>
|
|
|
|
/*
|
|
* Presently, a lot of graphics programs do not restore the contents of
|
|
* the higher font pages. Defining this flag will avoid use of them, but
|
|
* will lose support for PIO_FONTRESET. Note that many font operations are
|
|
* not likely to work with these programs anyway; they need to be
|
|
* fixed. The linux/Documentation directory includes a code snippet
|
|
* to save and restore the text font.
|
|
*/
|
|
#ifdef CONFIG_VGA_CONSOLE
|
|
#define BROKEN_GRAPHICS_PROGRAMS 1
|
|
#endif
|
|
|
|
extern void kd_mksound(unsigned int hz, unsigned int ticks);
|
|
extern int kbd_rate(struct kbd_repeat *rep);
|
|
extern int fg_console, last_console, want_console;
|
|
|
|
/* console.c */
|
|
|
|
int vc_allocate(unsigned int console);
|
|
int vc_cons_allocated(unsigned int console);
|
|
int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines);
|
|
struct vc_data *vc_deallocate(unsigned int console);
|
|
void reset_palette(struct vc_data *vc);
|
|
void do_blank_screen(int entering_gfx);
|
|
void do_unblank_screen(int leaving_gfx);
|
|
void unblank_screen(void);
|
|
void poke_blanked_console(void);
|
|
int con_font_op(struct vc_data *vc, struct console_font_op *op);
|
|
int con_set_cmap(unsigned char __user *cmap);
|
|
int con_get_cmap(unsigned char __user *cmap);
|
|
void scrollback(struct vc_data *vc, int lines);
|
|
void scrollfront(struct vc_data *vc, int lines);
|
|
void clear_buffer_attributes(struct vc_data *vc);
|
|
void update_region(struct vc_data *vc, unsigned long start, int count);
|
|
void redraw_screen(struct vc_data *vc, int is_switch);
|
|
#define update_screen(x) redraw_screen(x, 0)
|
|
#define switch_screen(x) redraw_screen(x, 1)
|
|
|
|
struct tty_struct;
|
|
int tioclinux(struct tty_struct *tty, unsigned long arg);
|
|
|
|
#ifdef CONFIG_CONSOLE_TRANSLATIONS
|
|
/* consolemap.c */
|
|
|
|
struct unimapinit;
|
|
struct unipair;
|
|
|
|
int con_set_trans_old(unsigned char __user * table);
|
|
int con_get_trans_old(unsigned char __user * table);
|
|
int con_set_trans_new(unsigned short __user * table);
|
|
int con_get_trans_new(unsigned short __user * table);
|
|
int con_clear_unimap(struct vc_data *vc, struct unimapinit *ui);
|
|
int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list);
|
|
int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct unipair __user *list);
|
|
int con_set_default_unimap(struct vc_data *vc);
|
|
void con_free_unimap(struct vc_data *vc);
|
|
int con_copy_unimap(struct vc_data *dst_vc, struct vc_data *src_vc);
|
|
|
|
#define vc_translate(vc, c) ((vc)->vc_translate[(c) | \
|
|
((vc)->vc_toggle_meta ? 0x80 : 0)])
|
|
#else
|
|
static inline int con_set_trans_old(unsigned char __user *table)
|
|
{
|
|
return 0;
|
|
}
|
|
static inline int con_get_trans_old(unsigned char __user *table)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
static inline int con_set_trans_new(unsigned short __user *table)
|
|
{
|
|
return 0;
|
|
}
|
|
static inline int con_get_trans_new(unsigned short __user *table)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
static inline int con_clear_unimap(struct vc_data *vc, struct unimapinit *ui)
|
|
{
|
|
return 0;
|
|
}
|
|
static inline
|
|
int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list)
|
|
{
|
|
return 0;
|
|
}
|
|
static inline
|
|
int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct,
|
|
struct unipair __user *list)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
static inline int con_set_default_unimap(struct vc_data *vc)
|
|
{
|
|
return 0;
|
|
}
|
|
static inline void con_free_unimap(struct vc_data *vc)
|
|
{
|
|
}
|
|
static inline void con_protect_unimap(struct vc_data *vc, int rdonly)
|
|
{
|
|
}
|
|
static inline
|
|
int con_copy_unimap(struct vc_data *dst_vc, struct vc_data *src_vc)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#define vc_translate(vc, c) (c)
|
|
#endif
|
|
|
|
/* vt.c */
|
|
void vt_event_post(unsigned int event, unsigned int old, unsigned int new);
|
|
int vt_waitactive(int n);
|
|
void change_console(struct vc_data *new_vc);
|
|
void reset_vc(struct vc_data *vc);
|
|
extern int do_unbind_con_driver(const struct consw *csw, int first, int last,
|
|
int deflt);
|
|
extern int unbind_con_driver(const struct consw *csw, int first, int last,
|
|
int deflt);
|
|
int vty_init(const struct file_operations *console_fops);
|
|
|
|
static inline bool vt_force_oops_output(struct vc_data *vc)
|
|
{
|
|
if (oops_in_progress && vc->vc_panic_force_write && panic_timeout >= 0)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
extern char vt_dont_switch;
|
|
extern int default_utf8;
|
|
extern int global_cursor_default;
|
|
|
|
struct vt_spawn_console {
|
|
spinlock_t lock;
|
|
struct pid *pid;
|
|
int sig;
|
|
};
|
|
extern struct vt_spawn_console vt_spawn_con;
|
|
|
|
extern int vt_move_to_console(unsigned int vt, int alloc);
|
|
|
|
/* Interfaces for VC notification of character events (for accessibility etc) */
|
|
|
|
struct vt_notifier_param {
|
|
struct vc_data *vc; /* VC on which the update happened */
|
|
unsigned int c; /* Printed char */
|
|
};
|
|
|
|
extern int register_vt_notifier(struct notifier_block *nb);
|
|
extern int unregister_vt_notifier(struct notifier_block *nb);
|
|
|
|
extern void hide_boot_cursor(bool hide);
|
|
|
|
/* keyboard provided interfaces */
|
|
extern int vt_do_diacrit(unsigned int cmd, void __user *up, int eperm);
|
|
extern int vt_do_kdskbmode(int console, unsigned int arg);
|
|
extern int vt_do_kdskbmeta(int console, unsigned int arg);
|
|
extern int vt_do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc,
|
|
int perm);
|
|
extern int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe,
|
|
int perm, int console);
|
|
extern int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb,
|
|
int perm);
|
|
extern int vt_do_kdskled(int console, int cmd, unsigned long arg, int perm);
|
|
extern int vt_do_kdgkbmode(int console);
|
|
extern int vt_do_kdgkbmeta(int console);
|
|
extern void vt_reset_unicode(int console);
|
|
extern int vt_get_shift_state(void);
|
|
extern void vt_reset_keyboard(int console);
|
|
extern int vt_get_leds(int console, int flag);
|
|
extern int vt_get_kbd_mode_bit(int console, int bit);
|
|
extern void vt_set_kbd_mode_bit(int console, int bit);
|
|
extern void vt_clr_kbd_mode_bit(int console, int bit);
|
|
extern void vt_set_led_state(int console, int leds);
|
|
extern void vt_set_led_state(int console, int leds);
|
|
extern void vt_kbd_con_start(int console);
|
|
extern void vt_kbd_con_stop(int console);
|
|
|
|
|
|
#endif /* _VT_KERN_H */
|