[PATCH 2/47] Fix locking in i8042.c and serio.c

Vojtech Pavlik
2004-07-29T14:55:43Z

ChangeSet@1.1612.24.2, 2004-05-28 18:24:08+02:00, vojtech@suse.cz
input: An attempt at fixing locking in i8042.c and serio.c

drivers/input/serio/i8042.c | 4 ++
drivers/input/serio/serio.c | 64 +++++++++++++++++++++++++++++++++++++-------
include/linux/serio.h | 2 -
3 files changed, 59 insertions(+), 11 deletions(-)

===================================================================

diff -Nru a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
--- a/drivers/input/serio/i8042.c Thu Jul 29 14:42:23 2004
+++ b/drivers/input/serio/i8042.c Thu Jul 29 14:42:23 2004
@@ -95,6 +95,7 @@
/*
* The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to
* be ready for reading values from it / writing values to it.
+ * Called always with i8042_lock held.
*/

static int i8042_wait_read(void)
@@ -677,6 +678,7 @@

static int i8042_controller_init(void)
{
+ unsigned long flags;

/*
* Test the i8042. We need to know if it thinks it's working correctly
@@ -723,12 +725,14 @@
* Handle keylock.
*/

+ spin_lock_irqsave(&i8042_lock, flags);
if (~i8042_read_status() & I8042_STR_KEYLOCK) {
if (i8042_unlock)
i8042_ctr |= I8042_CTR_IGNKEYLOCK;
else
printk(KERN_WARNING "i8042.c: Warning: Keylock active.\n");
}
+ spin_unlock_irqrestore(&i8042_lock, flags);

/*
* If the chip is configured into nontranslated mode by the BIOS, don't
diff -Nru a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c
--- a/drivers/input/serio/serio.c Thu Jul 29 14:42:23 2004
+++ b/drivers/input/serio/serio.c Thu Jul 29 14:42:23 2004
@@ -67,12 +67,18 @@
struct list_head node;
};

-static DECLARE_MUTEX(serio_sem);
+
+spinlock_t serio_lock = SPIN_LOCK_UNLOCKED; /* protects serio_event_list and serio->dev */
+static DECLARE_MUTEX(serio_sem); /* protects serio_list and serio_dev_list */
static LIST_HEAD(serio_list);
static LIST_HEAD(serio_dev_list);
static LIST_HEAD(serio_event_list);
static int serio_pid;

+/*
+ * serio_find_dev() must be called with serio_sem down.
+ */
+
static void serio_find_dev(struct serio *serio)
{
struct serio_dev *dev;
@@ -96,22 +102,42 @@
static void serio_invalidate_pending_events(struct serio *serio)
{
struct serio_event *event;
+ unsigned long flags;
+
+ spin_lock_irqsave(&serio_lock, flags);

list_for_each_entry(event, &serio_event_list, node)
if (event->serio == serio)
event->serio = NULL;
+
+ spin_unlock_irqrestore(&serio_lock, flags);
}

void serio_handle_events(void)
{
- struct list_head *node, *next;
+ struct list_head *node;
struct serio_event *event;
+ unsigned long flags;
+
+
+ while (1) {
+
+ spin_lock_irqsave(&serio_lock, flags);
+
+ if (list_empty(&serio_event_list)) {
+ spin_unlock_irqrestore(&serio_lock, flags);
+ break;
+ }

- list_for_each_safe(node, next, &serio_event_list) {
+ node = serio_event_list.next;
event = container_of(node, struct serio_event, node);
+ list_del_init(node);
+
+ spin_unlock_irqrestore(&serio_lock, flags);

down(&serio_sem);
- if (event->serio == NULL)
+
+ if (event->serio == NULL) /*!!!*/
goto event_done;

switch (event->type) {
@@ -139,7 +165,6 @@
}
event_done:
up(&serio_sem);
- list_del_init(node);
kfree(event);
}
}
@@ -178,12 +203,18 @@

void serio_rescan(struct serio *serio)
{
+ unsigned long flags;
+ spin_lock_irqsave(&serio_lock, flags);
serio_queue_event(serio, SERIO_RESCAN);
+ spin_unlock_irqrestore(&serio_lock, flags);
}

void serio_reconnect(struct serio *serio)
{
+ unsigned long flags;
+ spin_lock_irqsave(&serio_lock, flags);
serio_queue_event(serio, SERIO_RECONNECT);
+ spin_unlock_irqrestore(&serio_lock, flags);
}

irqreturn_t serio_interrupt(struct serio *serio,
@@ -191,17 +222,22 @@
{
irqreturn_t ret = IRQ_NONE;

+ spin_lock_irq(&serio_lock);
+
if (serio->dev && serio->dev->interrupt) {
ret = serio->dev->interrupt(serio, data, flags, regs);
} else {
if (!flags) {
- if ((serio->type == SERIO_8042 ||
- serio->type == SERIO_8042_XL) && (data != 0xaa))
- return ret;
- serio_rescan(serio);
- ret = IRQ_HANDLED;
+ if ((serio->type != SERIO_8042 &&
+ serio->type != SERIO_8042_XL) || (data == 0xaa)) {
+ serio_queue_event(serio, SERIO_RESCAN);
+ ret = IRQ_HANDLED;
+ }
}
}
+
+ spin_unlock_irq(&serio_lock);
+
return ret;
}

@@ -292,7 +328,11 @@
/* called from serio_dev->connect/disconnect methods under serio_sem */
int serio_open(struct serio *serio, struct serio_dev *dev)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&serio_lock, flags);
serio->dev = dev;
+ spin_unlock_irqrestore(&serio_lock, flags);
if (serio->open && serio->open(serio)) {
serio->dev = NULL;
return -1;
@@ -303,9 +343,13 @@
/* called from serio_dev->connect/disconnect methods under serio_sem */
void serio_close(struct serio *serio)
{
+ unsigned long flags;
+
if (serio->close)
serio->close(serio);
+ spin_lock_irqsave(&serio_lock, flags);
serio->dev = NULL;
+ spin_unlock_irqrestore(&serio_lock, flags);
}

static int __init serio_init(void)
diff -Nru a/include/linux/serio.h b/include/linux/serio.h
--- a/include/linux/serio.h Thu Jul 29 14:42:23 2004
+++ b/include/linux/serio.h Thu Jul 29 14:42:23 2004
@@ -36,7 +36,7 @@
int (*open)(struct serio *);
void (*close)(struct serio *);

- struct serio_dev *dev;
+ struct serio_dev *dev; /* Accessed from interrupt, writes must be protected by serio_lock */

struct list_head node;
};