Skip to contentSkip to conversation

SMP kernel hangs on C360, resolved

parisc-linux Richard Hirst 1 participant 1 message Aug 3, 2005

lore
Share & tools
lore
#0 lore raw

Turns out this is down to bugs in drivers/input/serio/gscps2.c. Looking at gscps2_probe(), there are two locks involved; one in struct gscps2port and one in struct serio. The probe function initialises the one in struct gscps2port and then calls gscps2_reset() which enables the device such that it can start generating interrupts, then adds the port to the list of ports the int handler looks at, then calls serio_register_port() which initialises the lock in struct serio. gscps2_interrupt() calls serio_interrupt(), so if an interrupt is raised before serio_register_port() is called, the code will hang trying to claim the serio lock which has not yet been initialised. Another issue is that gscps2_reset() resets the device _and_ enables the port. I think enabling the port should only happen once something calls open().

The attached patch addresses these issues. Before applying the patch I could provoke the hang easily after adding an mdelay() to the middle of the probe function and then banging on the keyboard and mouse during boot.

Richard

Index: drivers/input/serio/gscps2.c
===================================================================
RCS file: /var/cvs/linux-2.6/drivers/input/serio/gscps2.c,v retrieving revision 1.16

input/serio/gscps2.c +5 -6
diff -u -r1.16 gscps2.c
@@ -211,9 +211,6 @@
 	writeb(0xff, addr+GSC_RESET);
 	gscps2_flush(ps2port);
 	spin_unlock_irqrestore(&ps2port->lock, flags);
-
-	/* enable it */
-	gscps2_enable(ps2port, ENABLE);
 }
 
 static LIST_HEAD(ps2port_list);
@@ -307,6 +304,9 @@
 
 	gscps2_reset(ps2port);
 
+	/* enable it */
+	gscps2_enable(ps2port, ENABLE);
+
 	gscps2_interrupt(0, NULL, NULL);
 
 	return 0;
@@ -370,8 +370,6 @@
 	serio->port_data	= ps2port;
 	serio->dev.parent	= &dev->dev;
 
-	list_add_tail(&ps2port->node, &ps2port_list);
-
 	ret = -EBUSY;
 	if (request_irq(dev->irq, gscps2_interrupt, SA_SHIRQ, ps2port->port->name, ps2port))
 		goto fail_miserably;
@@ -396,13 +394,14 @@
 
 	serio_register_port(ps2port->port);
 
+	list_add_tail(&ps2port->node, &ps2port_list);
+
 	return 0;
 
 fail:
 	free_irq(dev->irq, ps2port);
 
 fail_miserably:
-	list_del(&ps2port->node);
 	iounmap(ps2port->addr);
 	release_mem_region(dev->hpa, GSC_STATUS + 4);
 

_______________________________________________
parisc-linux mailing list
[email protected]
http://lists.parisc-linux.org/mailman/listinfo/parisc-linux