These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / tty / vt / vt.c
index 4a24eb2..4462d16 100644 (file)
 #define CON_DRIVER_FLAG_MODULE 1
 #define CON_DRIVER_FLAG_INIT   2
 #define CON_DRIVER_FLAG_ATTR   4
+#define CON_DRIVER_FLAG_ZOMBIE 8
 
 struct con_driver {
        const struct consw *con;
@@ -135,6 +136,7 @@ const struct consw *conswitchp;
  */
 #define DEFAULT_BELL_PITCH     750
 #define DEFAULT_BELL_DURATION  (HZ/8)
+#define DEFAULT_CURSOR_BLINK_MS        200
 
 struct vc vc_cons [MAX_NR_CONSOLES];
 
@@ -153,6 +155,7 @@ static int set_vesa_blanking(char __user *p);
 static void set_cursor(struct vc_data *vc);
 static void hide_cursor(struct vc_data *vc);
 static void console_callback(struct work_struct *ignored);
+static void con_driver_unregister_callback(struct work_struct *ignored);
 static void blank_screen_t(unsigned long dummy);
 static void set_palette(struct vc_data *vc);
 
@@ -182,6 +185,7 @@ static int blankinterval = 10*60;
 core_param(consoleblank, blankinterval, int, 0444);
 
 static DECLARE_WORK(console_work, console_callback);
+static DECLARE_WORK(con_driver_unregister_work, con_driver_unregister_callback);
 
 /*
  * fg_console is the current virtual console,
@@ -738,6 +742,8 @@ static void visual_init(struct vc_data *vc, int num, int init)
        __module_get(vc->vc_sw->owner);
        vc->vc_num = num;
        vc->vc_display_fg = &master_display_fg;
+       if (vc->vc_uni_pagedir_loc)
+               con_free_unimap(vc);
        vc->vc_uni_pagedir_loc = &vc->vc_uni_pagedir;
        vc->vc_uni_pagedir = NULL;
        vc->vc_hi_font_mask = 0;
@@ -1590,6 +1596,13 @@ static void setterm_command(struct vc_data *vc)
                case 15: /* activate the previous console */
                        set_console(last_console);
                        break;
+               case 16: /* set cursor blink duration in msec */
+                       if (vc->vc_npar >= 1 && vc->vc_par[1] >= 50 &&
+                                       vc->vc_par[1] <= USHRT_MAX)
+                               vc->vc_cur_blink_ms = vc->vc_par[1];
+                       else
+                               vc->vc_cur_blink_ms = DEFAULT_CURSOR_BLINK_MS;
+                       break;
        }
 }
 
@@ -1717,6 +1730,7 @@ static void reset_terminal(struct vc_data *vc, int do_clear)
 
        vc->vc_bell_pitch = DEFAULT_BELL_PITCH;
        vc->vc_bell_duration = DEFAULT_BELL_DURATION;
+       vc->vc_cur_blink_ms = DEFAULT_CURSOR_BLINK_MS;
 
        gotoxy(vc, 0, 0);
        save_cur(vc);
@@ -3192,22 +3206,6 @@ err:
 
 
 #ifdef CONFIG_VT_HW_CONSOLE_BINDING
-static int con_is_graphics(const struct consw *csw, int first, int last)
-{
-       int i, retval = 0;
-
-       for (i = first; i <= last; i++) {
-               struct vc_data *vc = vc_cons[i].d;
-
-               if (vc && vc->vc_mode == KD_GRAPHICS) {
-                       retval = 1;
-                       break;
-               }
-       }
-
-       return retval;
-}
-
 /* unlocked version of unbind_con_driver() */
 int do_unbind_con_driver(const struct consw *csw, int first, int last, int deflt)
 {
@@ -3293,8 +3291,7 @@ static int vt_bind(struct con_driver *con)
        const struct consw *defcsw = NULL, *csw = NULL;
        int i, more = 1, first = -1, last = -1, deflt = 0;
 
-       if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE) ||
-           con_is_graphics(con->con, con->first, con->last))
+       if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE))
                goto err;
 
        csw = con->con;
@@ -3345,8 +3342,7 @@ static int vt_unbind(struct con_driver *con)
        int i, more = 1, first = -1, last = -1, deflt = 0;
        int ret;
 
-       if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE) ||
-           con_is_graphics(con->con, con->first, con->last))
+       if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE))
                goto err;
 
        csw = con->con;
@@ -3596,7 +3592,8 @@ static int do_register_con_driver(const struct consw *csw, int first, int last)
        for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
                con_driver = &registered_con_driver[i];
 
-               if (con_driver->con == NULL) {
+               if (con_driver->con == NULL &&
+                   !(con_driver->flag & CON_DRIVER_FLAG_ZOMBIE)) {
                        con_driver->con = csw;
                        con_driver->desc = desc;
                        con_driver->node = i;
@@ -3658,16 +3655,20 @@ int do_unregister_con_driver(const struct consw *csw)
                struct con_driver *con_driver = &registered_con_driver[i];
 
                if (con_driver->con == csw) {
-                       vtconsole_deinit_device(con_driver);
-                       device_destroy(vtconsole_class,
-                                      MKDEV(0, con_driver->node));
+                       /*
+                        * Defer the removal of the sysfs entries since that
+                        * will acquire the kernfs s_active lock and we can't
+                        * acquire this lock while holding the console lock:
+                        * the unbind sysfs entry imposes already the opposite
+                        * order. Reset con already here to prevent any later
+                        * lookup to succeed and mark this slot as zombie, so
+                        * it won't get reused until we complete the removal
+                        * in the deferred work.
+                        */
                        con_driver->con = NULL;
-                       con_driver->desc = NULL;
-                       con_driver->dev = NULL;
-                       con_driver->node = 0;
-                       con_driver->flag = 0;
-                       con_driver->first = 0;
-                       con_driver->last = 0;
+                       con_driver->flag = CON_DRIVER_FLAG_ZOMBIE;
+                       schedule_work(&con_driver_unregister_work);
+
                        return 0;
                }
        }
@@ -3676,6 +3677,39 @@ int do_unregister_con_driver(const struct consw *csw)
 }
 EXPORT_SYMBOL_GPL(do_unregister_con_driver);
 
+static void con_driver_unregister_callback(struct work_struct *ignored)
+{
+       int i;
+
+       console_lock();
+
+       for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+               struct con_driver *con_driver = &registered_con_driver[i];
+
+               if (!(con_driver->flag & CON_DRIVER_FLAG_ZOMBIE))
+                       continue;
+
+               console_unlock();
+
+               vtconsole_deinit_device(con_driver);
+               device_destroy(vtconsole_class, MKDEV(0, con_driver->node));
+
+               console_lock();
+
+               if (WARN_ON_ONCE(con_driver->con))
+                       con_driver->con = NULL;
+               con_driver->desc = NULL;
+               con_driver->dev = NULL;
+               con_driver->node = 0;
+               WARN_ON_ONCE(con_driver->flag != CON_DRIVER_FLAG_ZOMBIE);
+               con_driver->flag = 0;
+               con_driver->first = 0;
+               con_driver->last = 0;
+       }
+
+       console_unlock();
+}
+
 /*
  *     If we support more console drivers, this function is used
  *     when a driver wants to take over some existing consoles