Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 40974618 authored by Dmitry Torokhov's avatar Dmitry Torokhov
Browse files

Input: i8042 - break load dependency between atkbd/psmouse and i8042

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: default avatarMark Laws <mdl@60hz.org>
Tested-by: default avatarMark Laws <mdl@60hz.org>
Cc: stable@vger.kernel.org
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent 887ec0bf
Loading
Loading
Loading
Loading
+1 −15
Original line number Diff line number Diff line
@@ -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)
+4 −6
Original line number Diff line number Diff line
@@ -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);

+0 −6
Original line number Diff line number Diff line
@@ -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))
{
+19 −5
Original line number Diff line number Diff line
@@ -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)