[PATCH 3/3] Synaptics - fix lockdep warnings

From: Jiri Kosina
Date: Wed Sep 13 2006 - 20:46:26 EST


=============================================
[ INFO: possible recursive locking detected ]
2.6.18-rc6-mm2-dirty #7
---------------------------------------------
swapper/0 is trying to acquire lock:
(&serio->lock){++..}, at: [<c02b7a20>] serio_interrupt+0x20/0x60

but task is already holding lock:
(&serio->lock){++..}, at: [<c02b7a20>] serio_interrupt+0x20/0x60

other info that might help us debug this:
1 lock held by swapper/0:
#0: (&serio->lock){++..}, at: [<c02b7a20>] serio_interrupt+0x20/0x60

stack backtrace:
[<c01039e5>] dump_trace+0x225/0x240
[<c0103af0>] show_trace_log_lvl+0x30/0x50
[<c0103b38>] show_trace+0x28/0x30
[<c0103c72>] dump_stack+0x22/0x30
[<c0135130>] print_deadlock_bug+0xc0/0xd0
[<c01351b2>] check_deadlock+0x72/0x80
[<c0136a4d>] __lock_acquire+0x43d/0x990
[<c0137468>] lock_acquire+0x68/0x80
[<c036947d>] _spin_lock_irqsave+0x4d/0x70
[<c02b7a20>] serio_interrupt+0x20/0x60
[<c02c3f84>] synaptics_pass_pt_packet+0x44/0xd0
[<c02c4a9a>] synaptics_process_byte+0xba/0xd0
[<c02c017a>] psmouse_handle_byte+0x1a/0x110
[<c02c031a>] psmouse_interrupt+0xaa/0x2e0
[<c02b79ca>] __serio_interrupt+0x3a/0x70
[<c02b7a42>] serio_interrupt+0x42/0x60
[<c02b841e>] i8042_interrupt+0x10e/0x240
[<c014cab1>] handle_IRQ_event+0x31/0x80
[<c014dc5e>] handle_level_irq+0x8e/0x120
[<c010570a>] do_IRQ+0x5a/0xb0
[<c01035a2>] common_interrupt+0x2e/0x34
[<c024e964>] acpi_processor_idle+0x1e8/0x31b
[<c0101195>] cpu_idle+0x95/0xa0
[<c0100334>] rest_init+0x44/0x50
[<c04ca8cd>] start_kernel+0x1cd/0x220
=======================

This is caused by synaptics driver, in pass-through port situation,
recursively calling serio_interrupt(), which acquires the device lock
recursively. This is however OK in the synaptics pass-through port case,
as the per-device lock is acquired exactly twice - first for child, then
for parent device, so no deadlock occurs.

The patch splits serio_interrupt() function into nested and non-nested
versions, and changes calls from synaptics pass-through packet handler
accordingly.

This patch depends on the existence of spin_lock_irqsave_nested(), which
is provided by the previous patch.

Please apply, if applicable, to get rid of spurious lockdep warnings. This patch
was generated against 2.6.18-rc6-mm2.

Signed-off-by: Jiri Kosina <jikos@xxxxxxxx>

diff -rup linux-2.6.18-rc6-mm2.orig/drivers/input/mouse/synaptics.c linux-2.6.18-rc6-mm2/drivers/input/mouse/synaptics.c
--- linux-2.6.18-rc6-mm2.orig/drivers/input/mouse/synaptics.c 2006-09-14 01:05:30.000000000 +0200
+++ linux-2.6.18-rc6-mm2/drivers/input/mouse/synaptics.c 2006-09-14 02:03:38.000000000 +0200
@@ -216,13 +216,13 @@ static void synaptics_pass_pt_packet(str
struct psmouse *child = serio_get_drvdata(ptport);

if (child && child->state == PSMOUSE_ACTIVATED) {
- serio_interrupt(ptport, packet[1], 0, NULL);
- serio_interrupt(ptport, packet[4], 0, NULL);
- serio_interrupt(ptport, packet[5], 0, NULL);
+ serio_interrupt_nested(ptport, packet[1], 0, NULL);
+ serio_interrupt_nested(ptport, packet[4], 0, NULL);
+ serio_interrupt_nested(ptport, packet[5], 0, NULL);
if (child->pktsize == 4)
- serio_interrupt(ptport, packet[2], 0, NULL);
+ serio_interrupt_nested(ptport, packet[2], 0, NULL);
} else
- serio_interrupt(ptport, packet[1], 0, NULL);
+ serio_interrupt_nested(ptport, packet[1], 0, NULL);
}

static void synaptics_pt_activate(struct psmouse *psmouse)
diff -rup linux-2.6.18-rc6-mm2.orig/drivers/input/serio/serio.c linux-2.6.18-rc6-mm2/drivers/input/serio/serio.c
--- linux-2.6.18-rc6-mm2.orig/drivers/input/serio/serio.c 2006-09-04 04:19:48.000000000 +0200
+++ linux-2.6.18-rc6-mm2/drivers/input/serio/serio.c 2006-09-14 01:51:00.000000000 +0200
@@ -41,6 +41,7 @@ MODULE_DESCRIPTION("Serio abstraction co
MODULE_LICENSE("GPL");

EXPORT_SYMBOL(serio_interrupt);
+EXPORT_SYMBOL(serio_interrupt_nested);
EXPORT_SYMBOL(__serio_register_port);
EXPORT_SYMBOL(serio_unregister_port);
EXPORT_SYMBOL(serio_unregister_child_port);
@@ -910,14 +911,11 @@ void serio_close(struct serio *serio)
serio_set_drv(serio, NULL);
}

-irqreturn_t serio_interrupt(struct serio *serio,
+irqreturn_t __serio_interrupt(struct serio *serio,
unsigned char data, unsigned int dfl, struct pt_regs *regs)
{
- unsigned long flags;
irqreturn_t ret = IRQ_NONE;

- spin_lock_irqsave(&serio->lock, flags);
-
if (likely(serio->drv)) {
ret = serio->drv->interrupt(serio, data, dfl, regs);
} else if (!dfl && serio->registered) {
@@ -925,8 +923,30 @@ irqreturn_t serio_interrupt(struct serio
ret = IRQ_HANDLED;
}

+ return ret;
+}
+
+irqreturn_t serio_interrupt(struct serio *serio,
+ unsigned char data, unsigned int dfl, struct pt_regs *regs)
+{
+ unsigned long flags;
+ irqreturn_t ret;
+
+ spin_lock_irqsave(&serio->lock, flags);
+ ret = __serio_interrupt(serio, data, dfl, regs);
spin_unlock_irqrestore(&serio->lock, flags);
+ return ret;
+}

+irqreturn_t serio_interrupt_nested(struct serio *serio,
+ unsigned char data, unsigned int dfl, struct pt_regs *regs)
+{
+ unsigned long flags;
+ irqreturn_t ret;
+
+ spin_lock_irqsave_nested(&serio->lock, flags, SINGLE_DEPTH_NESTING);
+ ret = __serio_interrupt(serio, data, dfl, regs);
+ spin_unlock_irqrestore(&serio->lock, flags);
return ret;
}

diff -rup linux-2.6.18-rc6-mm2.orig/include/linux/serio.h linux-2.6.18-rc6-mm2/include/linux/serio.h
--- linux-2.6.18-rc6-mm2.orig/include/linux/serio.h 2006-09-14 00:49:35.000000000 +0200
+++ linux-2.6.18-rc6-mm2/include/linux/serio.h 2006-09-14 01:39:57.000000000 +0200
@@ -76,6 +76,7 @@ void serio_close(struct serio *serio);
void serio_rescan(struct serio *serio);
void serio_reconnect(struct serio *serio);
irqreturn_t serio_interrupt(struct serio *serio, unsigned char data, unsigned int flags, struct pt_regs *regs);
+irqreturn_t serio_interrupt_nested(struct serio *serio, unsigned char data, unsigned int flags, struct pt_regs *regs);

void __serio_register_port(struct serio *serio, struct module *owner);
static inline void serio_register_port(struct serio *serio)
diff -rup linux-2.6.18-rc6-mm2.orig/kernel/spinlock.c linux-2.6.18-rc6-mm2/kernel/spinlock.c
--- linux-2.6.18-rc6-mm2.orig/kernel/spinlock.c 2006-09-14 01:35:00.000000000 +0200
+++ linux-2.6.18-rc6-mm2/kernel/spinlock.c 2006-09-14 01:42:40.000000000 +0200
@@ -310,7 +310,7 @@ unsigned long __lockfunc _spin_lock_irqs

local_irq_save(flags);
preempt_disable();
- spin_acquire(&lock->dep_map, subtype, 0, _RET_IP_);
+ spin_acquire(&lock->dep_map, subclass, 0, _RET_IP_);
/*
* On lockdep we dont want the hand-coded irq-enable of
* _raw_spin_lock_flags() code, because lockdep assumes
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/