Input: i8042 - break load dependency between atkbd/psmouse and i8042
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Mon, 25 Jul 2016 18:36:54 +0000 (11:36 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Wed, 27 Jul 2016 21:20:09 +0000 (14:20 -0700)
As explained in 1407814240-4275-1-git-send-email-decui@microsoft.com we
have a hard load dependency between i8042 and atkbd which prevents
keyboard from working on Gen2 Hyper-V VMs.

> hyperv_keyboard invokes serio_interrupt(), which needs a valid serio
> driver like atkbd.c.  atkbd.c depends on libps2.c because it invokes
> ps2_command().  libps2.c depends on i8042.c because it invokes
> i8042_check_port_owner().  As a result, hyperv_keyboard actually
> depends on i8042.c.
>
> For a Generation 2 Hyper-V VM (meaning no i8042 device emulated), if a
> Linux VM (like Arch Linux) happens to configure CONFIG_SERIO_I8042=m
> rather than =y, atkbd.ko can't load because i8042.ko can't load(due to
> no i8042 device emulated) and finally hyperv_keyboard can't work and
> the user can't input: https://bugs.archlinux.org/task/39820
> (Ubuntu/RHEL/SUSE aren't affected since they use CONFIG_SERIO_I8042=y)

To break the dependency we move away from using i8042_check_port_owner()
and instead allow serio port owner specify a mutex that clients should use
to serialize PS/2 command stream.

Reported-by: Mark Laws <mdl@60hz.org>
Tested-by: Mark Laws <mdl@60hz.org>
Cc: stable@vger.kernel.org
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
drivers/input/serio/i8042.c
drivers/input/serio/libps2.c
include/linux/i8042.h
include/linux/serio.h

index 454195709a824b3e5a346b3aa9e087672dccdcae..b4d34086e73f5aac0654e607028f13f59ef771be 100644 (file)
@@ -1277,6 +1277,7 @@ static int __init i8042_create_kbd_port(void)
        serio->start            = i8042_start;
        serio->stop             = i8042_stop;
        serio->close            = i8042_port_close;
+       serio->ps2_cmd_mutex    = &i8042_mutex;
        serio->port_data        = port;
        serio->dev.parent       = &i8042_platform_device->dev;
        strlcpy(serio->name, "i8042 KBD port", sizeof(serio->name));
@@ -1373,21 +1374,6 @@ static void i8042_unregister_ports(void)
        }
 }
 
-/*
- * Checks whether port belongs to i8042 controller.
- */
-bool i8042_check_port_owner(const struct serio *port)
-{
-       int i;
-
-       for (i = 0; i < I8042_NUM_PORTS; i++)
-               if (i8042_ports[i].serio == port)
-                       return true;
-
-       return false;
-}
-EXPORT_SYMBOL(i8042_check_port_owner);
-
 static void i8042_free_irqs(void)
 {
        if (i8042_aux_irq_registered)
index 316f2c8971011dae527d506ee18d49ce96f316e0..83e9c663aa6727da129d2fe8b0256f73f4581d53 100644 (file)
@@ -56,19 +56,17 @@ EXPORT_SYMBOL(ps2_sendbyte);
 
 void ps2_begin_command(struct ps2dev *ps2dev)
 {
-       mutex_lock(&ps2dev->cmd_mutex);
+       struct mutex *m = ps2dev->serio->ps2_cmd_mutex ?: &ps2dev->cmd_mutex;
 
-       if (i8042_check_port_owner(ps2dev->serio))
-               i8042_lock_chip();
+       mutex_lock(m);
 }
 EXPORT_SYMBOL(ps2_begin_command);
 
 void ps2_end_command(struct ps2dev *ps2dev)
 {
-       if (i8042_check_port_owner(ps2dev->serio))
-               i8042_unlock_chip();
+       struct mutex *m = ps2dev->serio->ps2_cmd_mutex ?: &ps2dev->cmd_mutex;
 
-       mutex_unlock(&ps2dev->cmd_mutex);
+       mutex_unlock(m);
 }
 EXPORT_SYMBOL(ps2_end_command);
 
index 0f9bafa17a02dde9af7f4866ca8b9f741c83ddf1..d98780ca9604a7cae22d6c103c1b54eea2e22d0d 100644 (file)
@@ -62,7 +62,6 @@ struct serio;
 void i8042_lock_chip(void);
 void i8042_unlock_chip(void);
 int i8042_command(unsigned char *param, int command);
-bool i8042_check_port_owner(const struct serio *);
 int i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str,
                                        struct serio *serio));
 int i8042_remove_filter(bool (*filter)(unsigned char data, unsigned char str,
@@ -83,11 +82,6 @@ static inline int i8042_command(unsigned char *param, int command)
        return -ENODEV;
 }
 
-static inline bool i8042_check_port_owner(const struct serio *serio)
-{
-       return false;
-}
-
 static inline int i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str,
                                        struct serio *serio))
 {
index df4ab5de15862c7ba35fb532174db56866ffff47..c733cff44e18a74f3949032d1413356df0a9e226 100644 (file)
@@ -31,7 +31,8 @@ struct serio {
 
        struct serio_device_id id;
 
-       spinlock_t lock;                /* protects critical sections from port's interrupt handler */
+       /* Protects critical sections from port's interrupt handler */
+       spinlock_t lock;
 
        int (*write)(struct serio *, unsigned char);
        int (*open)(struct serio *);
@@ -40,16 +41,29 @@ struct serio {
        void (*stop)(struct serio *);
 
        struct serio *parent;
-       struct list_head child_node;    /* Entry in parent->children list */
+       /* Entry in parent->children list */
+       struct list_head child_node;
        struct list_head children;
-       unsigned int depth;             /* level of nesting in serio hierarchy */
+       /* Level of nesting in serio hierarchy */
+       unsigned int depth;
 
-       struct serio_driver *drv;       /* accessed from interrupt, must be protected by serio->lock and serio->sem */
-       struct mutex drv_mutex;         /* protects serio->drv so attributes can pin driver */
+       /*
+        * serio->drv is accessed from interrupt handlers; when modifying
+        * caller should acquire serio->drv_mutex and serio->lock.
+        */
+       struct serio_driver *drv;
+       /* Protects serio->drv so attributes can pin current driver */
+       struct mutex drv_mutex;
 
        struct device dev;
 
        struct list_head node;
+
+       /*
+        * For use by PS/2 layer when several ports share hardware and
+        * may get indigestion when exposed to concurrent access (i8042).
+        */
+       struct mutex *ps2_cmd_mutex;
 };
 #define to_serio_port(d)       container_of(d, struct serio, dev)