vt: push the tty_lock down into the map handling
authorAlan Cox <alan@linux.intel.com>
Tue, 24 Apr 2012 10:06:06 +0000 (11:06 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 24 Apr 2012 23:14:14 +0000 (16:14 -0700)
When we do this it becomes clear the lock we should be holding is the vc
lock, and in fact many of our other helpers are properly invoked this way.

We don't at this point guarantee not to race the keyboard code but the results
of that appear harmless and that was true before we started as well.

We now have no users of tty_lock in the console driver...

Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/vt/consolemap.c
drivers/tty/vt/vt_ioctl.c
include/linux/vt_kern.h

index 8308fc7cdc260657c87e1a42cd14dee10e12db56..2aaa0c22840953504dc1920dcb518457cca8c708 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/init.h>
 #include <linux/tty.h>
 #include <asm/uaccess.h>
+#include <linux/console.h>
 #include <linux/consolemap.h>
 #include <linux/vt_kern.h>
 
@@ -312,6 +313,7 @@ int con_set_trans_old(unsigned char __user * arg)
        if (!access_ok(VERIFY_READ, arg, E_TABSZ))
                return -EFAULT;
 
+       console_lock();
        for (i=0; i<E_TABSZ ; i++) {
                unsigned char uc;
                __get_user(uc, arg+i);
@@ -319,6 +321,7 @@ int con_set_trans_old(unsigned char __user * arg)
        }
 
        update_user_maps();
+       console_unlock();
        return 0;
 }
 
@@ -330,11 +333,13 @@ int con_get_trans_old(unsigned char __user * arg)
        if (!access_ok(VERIFY_WRITE, arg, E_TABSZ))
                return -EFAULT;
 
+       console_lock();
        for (i=0; i<E_TABSZ ; i++)
-         {
-           ch = conv_uni_to_pc(vc_cons[fg_console].d, p[i]);
-           __put_user((ch & ~0xff) ? 0 : ch, arg+i);
-         }
+       {
+               ch = conv_uni_to_pc(vc_cons[fg_console].d, p[i]);
+               __put_user((ch & ~0xff) ? 0 : ch, arg+i);
+       }
+       console_unlock();
        return 0;
 }
 
@@ -346,6 +351,7 @@ int con_set_trans_new(ushort __user * arg)
        if (!access_ok(VERIFY_READ, arg, E_TABSZ*sizeof(unsigned short)))
                return -EFAULT;
 
+       console_lock();
        for (i=0; i<E_TABSZ ; i++) {
                unsigned short us;
                __get_user(us, arg+i);
@@ -353,6 +359,7 @@ int con_set_trans_new(ushort __user * arg)
        }
 
        update_user_maps();
+       console_unlock();
        return 0;
 }
 
@@ -364,8 +371,10 @@ int con_get_trans_new(ushort __user * arg)
        if (!access_ok(VERIFY_WRITE, arg, E_TABSZ*sizeof(unsigned short)))
                return -EFAULT;
 
+       console_lock();
        for (i=0; i<E_TABSZ ; i++)
          __put_user(p[i], arg+i);
+       console_unlock();
        
        return 0;
 }
@@ -407,6 +416,7 @@ static void con_release_unimap(struct uni_pagedir *p)
        }
 }
 
+/* Caller must hold the console lock */
 void con_free_unimap(struct vc_data *vc)
 {
        struct uni_pagedir *p;
@@ -487,17 +497,21 @@ con_insert_unipair(struct uni_pagedir *p, u_short unicode, u_short fontpos)
        return 0;
 }
 
-/* ui is a leftover from using a hashtable, but might be used again */
-int con_clear_unimap(struct vc_data *vc, struct unimapinit *ui)
+/* ui is a leftover from using a hashtable, but might be used again
+   Caller must hold the lock */
+static int con_do_clear_unimap(struct vc_data *vc, struct unimapinit *ui)
 {
        struct uni_pagedir *p, *q;
-  
+
        p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc;
-       if (p && p->readonly) return -EIO;
+       if (p && p->readonly)
+               return -EIO;
+
        if (!p || --p->refcount) {
                q = kzalloc(sizeof(*p), GFP_KERNEL);
                if (!q) {
-                       if (p) p->refcount++;
+                       if (p)
+                               p->refcount++;
                        return -ENOMEM;
                }
                q->refcount=1;
@@ -511,23 +525,43 @@ int con_clear_unimap(struct vc_data *vc, struct unimapinit *ui)
        return 0;
 }
 
+int con_clear_unimap(struct vc_data *vc, struct unimapinit *ui)
+{
+       int ret;
+       console_lock();
+       ret = con_do_clear_unimap(vc, ui);
+       console_unlock();
+       return ret;
+}
+       
 int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list)
 {
        int err = 0, err1, i;
        struct uni_pagedir *p, *q;
 
+       console_lock();
+
        /* Save original vc_unipagdir_loc in case we allocate a new one */
        p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc;
-       if (p->readonly) return -EIO;
+       if (p->readonly) {
+               console_unlock();
+               return -EIO;
+       }
        
-       if (!ct) return 0;
+       if (!ct) {
+               console_unlock();
+               return 0;
+       }
        
        if (p->refcount > 1) {
                int j, k;
                u16 **p1, *p2, l;
                
-               err1 = con_clear_unimap(vc, NULL);
-               if (err1) return err1;
+               err1 = con_do_clear_unimap(vc, NULL);
+               if (err1) {
+                       console_unlock();
+                       return err1;
+               }
                
                /*
                 * Since refcount was > 1, con_clear_unimap() allocated a
@@ -558,7 +592,8 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list)
                                                *vc->vc_uni_pagedir_loc = (unsigned long)p;
                                                con_release_unimap(q);
                                                kfree(q);
-                                               return err1;
+                                               console_unlock();
+                                               return err1; 
                                        }
                                }
                        } else {
@@ -592,21 +627,30 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list)
        /*
         * Merge with fontmaps of any other virtual consoles.
         */
-       if (con_unify_unimap(vc, p))
+       if (con_unify_unimap(vc, p)) {
+               console_unlock();
                return err;
+       }
 
        for (i = 0; i <= 3; i++)
                set_inverse_transl(vc, p, i); /* Update inverse translations */
        set_inverse_trans_unicode(vc, p);
-  
+
+       console_unlock();
        return err;
 }
 
-/* Loads the unimap for the hardware font, as defined in uni_hash.tbl.
-   The representation used was the most compact I could come up
-   with.  This routine is executed at sys_setup time, and when the
-   PIO_FONTRESET ioctl is called. */
-
+/**
+ *     con_set_default_unimap  -       set default unicode map
+ *     @vc: the console we are updating
+ *
+ *     Loads the unimap for the hardware font, as defined in uni_hash.tbl.
+ *     The representation used was the most compact I could come up
+ *     with.  This routine is executed at video setup, and when the
+ *     PIO_FONTRESET ioctl is called. 
+ *
+ *     The caller must hold the console lock
+ */
 int con_set_default_unimap(struct vc_data *vc)
 {
        int i, j, err = 0, err1;
@@ -617,6 +661,7 @@ int con_set_default_unimap(struct vc_data *vc)
                p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc;
                if (p == dflt)
                        return 0;
+
                dflt->refcount++;
                *vc->vc_uni_pagedir_loc = (unsigned long)dflt;
                if (p && !--p->refcount) {
@@ -628,8 +673,9 @@ int con_set_default_unimap(struct vc_data *vc)
        
        /* The default font is always 256 characters */
 
-       err = con_clear_unimap(vc, NULL);
-       if (err) return err;
+       err = con_do_clear_unimap(vc, NULL);
+       if (err)
+               return err;
     
        p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc;
        q = dfont_unitable;
@@ -654,6 +700,13 @@ int con_set_default_unimap(struct vc_data *vc)
 }
 EXPORT_SYMBOL(con_set_default_unimap);
 
+/**
+ *     con_copy_unimap         -       copy unimap between two vts
+ *     @dst_vc: target
+ *     @src_vt: source
+ *
+ *     The caller must hold the console lock when invoking this method
+ */
 int con_copy_unimap(struct vc_data *dst_vc, struct vc_data *src_vc)
 {
        struct uni_pagedir *q;
@@ -668,13 +721,23 @@ int con_copy_unimap(struct vc_data *dst_vc, struct vc_data *src_vc)
        *dst_vc->vc_uni_pagedir_loc = (long)q;
        return 0;
 }
+EXPORT_SYMBOL(con_copy_unimap);
 
+/**
+ *     con_get_unimap          -       get the unicode map
+ *     @vc: the console to read from
+ *
+ *     Read the console unicode data for this console. Called from the ioctl
+ *     handlers.
+ */
 int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct unipair __user *list)
 {
        int i, j, k, ect;
        u16 **p1, *p2;
        struct uni_pagedir *p;
 
+       console_lock();
+
        ect = 0;
        if (*vc->vc_uni_pagedir_loc) {
                p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc;
@@ -694,22 +757,19 @@ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct uni
                                }
        }
        __put_user(ect, uct);
+       console_unlock();
        return ((ect <= ct) ? 0 : -ENOMEM);
 }
 
-void con_protect_unimap(struct vc_data *vc, int rdonly)
-{
-       struct uni_pagedir *p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc;
-       
-       if (p)
-               p->readonly = rdonly;
-}
-
 /*
  * Always use USER_MAP. These functions are used by the keyboard,
  * which shouldn't be affected by G0/G1 switching, etc.
  * If the user map still contains default values, i.e. the
  * direct-to-font mapping, then assume user is using Latin1.
+ *
+ * FIXME: at some point we need to decide if we want to lock the table
+ * update element itself via the keyboard_event_lock for consistency with the
+ * keyboard driver as well as the consoles
  */
 /* may be called during an interrupt */
 u32 conv_8bit_to_uni(unsigned char c)
@@ -777,4 +837,3 @@ console_map_init(void)
                        con_set_default_unimap(vc_cons[i].d);
 }
 
-EXPORT_SYMBOL(con_copy_unimap);
index ede2ef18d2fb4a763df3380186de14f27ad562c1..64618547be11d3e2170f80242d0937d6741046fe 100644 (file)
@@ -910,7 +910,9 @@ int vt_ioctl(struct tty_struct *tty,
                ret = con_font_op(vc_cons[fg_console].d, &op);
                if (ret)
                        break;
+               console_lock();
                con_set_default_unimap(vc_cons[fg_console].d);
+               console_unlock();
                break;
                }
 #endif
@@ -934,33 +936,23 @@ int vt_ioctl(struct tty_struct *tty,
        case PIO_SCRNMAP:
                if (!perm)
                        ret = -EPERM;
-               else {
-                       tty_lock();
+               else
                        ret = con_set_trans_old(up);
-                       tty_unlock();
-               }
                break;
 
        case GIO_SCRNMAP:
-               tty_lock();
                ret = con_get_trans_old(up);
-               tty_unlock();
                break;
 
        case PIO_UNISCRNMAP:
                if (!perm)
                        ret = -EPERM;
-               else {
-                       tty_lock();
+               else
                        ret = con_set_trans_new(up);
-                       tty_unlock();
-               }
                break;
 
        case GIO_UNISCRNMAP:
-               tty_lock();
                ret = con_get_trans_new(up);
-               tty_unlock();
                break;
 
        case PIO_UNIMAPCLR:
@@ -970,19 +962,14 @@ int vt_ioctl(struct tty_struct *tty,
                ret = copy_from_user(&ui, up, sizeof(struct unimapinit));
                if (ret)
                        ret = -EFAULT;
-               else {
-                       tty_lock();
+               else
                        con_clear_unimap(vc, &ui);
-                       tty_unlock();
-               }
                break;
              }
 
        case PIO_UNIMAP:
        case GIO_UNIMAP:
-               tty_lock();
                ret = do_unimap_ioctl(cmd, up, perm, vc);
-               tty_unlock();
                break;
 
        case VT_LOCKSWITCH:
@@ -1196,9 +1183,7 @@ long vt_compat_ioctl(struct tty_struct *tty,
 
        case PIO_UNIMAP:
        case GIO_UNIMAP:
-               tty_lock();
                ret = compat_unimap_ioctl(cmd, up, perm, vc);
-               tty_unlock();
                break;
 
        /*
index e33d77f15bdaea4151590225d65ba9e70a63b4bd..50ae7d0c279ed2ddbdb626aa67dcc9abad619781 100644 (file)
@@ -70,7 +70,6 @@ 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);
-void con_protect_unimap(struct vc_data *vc, int rdonly);
 int con_copy_unimap(struct vc_data *dst_vc, struct vc_data *src_vc);
 
 #define vc_translate(vc, c) ((vc)->vc_translate[(c) |                  \