linux-hotplug.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] PCI device list locking
@ 2002-03-04 23:54 john stultz
  2002-03-05  0:17 ` Greg KH
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: john stultz @ 2002-03-04 23:54 UTC (permalink / raw)
  To: linux-hotplug


[-- Attachment #1.1: Type: text/plain, Size: 864 bytes --]

At Greg K-H's request, I've been looking into locking the pci_devices
list to protect it from being altered by hotplug while being traversed.
So far I've limited my work to just the core pci code, only adding the
pcidevlist_lock spinlock and using it to wrap all uses of
pci_for_each_dev() or pci_devices. I'm planning to go on and lock all
uses of pci_for_each_dev() in the driver and arch specific files, as
well as the bus device lists (and later the bus lists), but I wanted to
send this out for comments first. I don't really have any test cases for
sanity checking, but it boots on an 8 way and running 50 "cat
/proc/pci"s in parallel doesn't choke, so let me know if I've mistakenly
broken anything. 

Thanks 
-john 

(please CC me as I'm not sub'ed to the lists)

I apologize if this is a dup, evo crashed on send and I'm not sure if it
made it out first.

[-- Attachment #1.2: Type: text/html, Size: 1137 bytes --]

[-- Attachment #2: pcidevlist_lock.patch --]
[-- Type: text/plain, Size: 8373 bytes --]

Index: linux24/drivers/pci/pci.c
diff -u linux24/drivers/pci/pci.c:1.1.1.1 linux24/drivers/pci/pci.c:1.1.1.1.28.16
--- linux24/drivers/pci/pci.c:1.1.1.1	Tue Dec 18 15:49:16 2001
+++ linux24/drivers/pci/pci.c	Mon Mar  4 15:38:39 2002
@@ -37,6 +37,7 @@
 
 LIST_HEAD(pci_root_buses);
 LIST_HEAD(pci_devices);
+spinlock_t pcidevlist_lock = SPIN_LOCK_UNLOCKED;
 
 /**
  * pci_find_slot - locate PCI device from a given PCI slot
@@ -54,11 +55,14 @@
 pci_find_slot(unsigned int bus, unsigned int devfn)
 {
 	struct pci_dev *dev;
-
+	spin_lock(&pcidevlist_lock);
 	pci_for_each_dev(dev) {
-		if (dev->bus->number == bus && dev->devfn == devfn)
+		if (dev->bus->number == bus && dev->devfn == devfn){
+			spin_unlock(&pcidevlist_lock);
 			return dev;
+		}
 	}
+	spin_unlock(&pcidevlist_lock);
 	return NULL;
 }
 
@@ -81,17 +85,23 @@
 		unsigned int ss_vendor, unsigned int ss_device,
 		const struct pci_dev *from)
 {
-	struct list_head *n = from ? from->global_list.next : pci_devices.next;
+	struct list_head *n;
+	
+	spin_lock(&pcidevlist_lock);
+	n = from ? from->global_list.next : pci_devices.next;
 
 	while (n != &pci_devices) {
 		struct pci_dev *dev = pci_dev_g(n);
 		if ((vendor == PCI_ANY_ID || dev->vendor == vendor) &&
 		    (device == PCI_ANY_ID || dev->device == device) &&
 		    (ss_vendor == PCI_ANY_ID || dev->subsystem_vendor == ss_vendor) &&
-		    (ss_device == PCI_ANY_ID || dev->subsystem_device == ss_device))
+		    (ss_device == PCI_ANY_ID || dev->subsystem_device == ss_device)){
+			spin_unlock(&pcidevlist_lock);
 			return dev;
+		}
 		n = n->next;
 	}
+	spin_unlock(&pcidevlist_lock);
 	return NULL;
 }
 
@@ -130,14 +140,19 @@
 struct pci_dev *
 pci_find_class(unsigned int class, const struct pci_dev *from)
 {
-	struct list_head *n = from ? from->global_list.next : pci_devices.next;
+	struct list_head *n;
 
+	spin_lock(&pcidevlist_lock);
+	n = from ? from->global_list.next : pci_devices.next;
 	while (n != &pci_devices) {
 		struct pci_dev *dev = pci_dev_g(n);
-		if (dev->class == class)
+		if (dev->class == class){
+			spin_unlock(&pcidevlist_lock);
 			return dev;
+		}
 		n = n->next;
 	}
+	spin_unlock(&pcidevlist_lock);
 	return NULL;
 }
 
@@ -605,10 +620,12 @@
 	int count = 0;
 
 	list_add_tail(&drv->node, &pci_drivers);
+	spin_lock(&pcidevlist_lock);
 	pci_for_each_dev(dev) {
 		if (!pci_dev_driver(dev))
 			count += pci_announce_device(drv, dev);
 	}
+	spin_unlock(&pcidevlist_lock);
 	return count;
 }
 
@@ -628,6 +645,7 @@
 	struct pci_dev *dev;
 
 	list_del(&drv->node);
+	spin_lock(&pcidevlist_lock);
 	pci_for_each_dev(dev) {
 		if (dev->driver == drv) {
 			if (drv->remove)
@@ -635,6 +653,7 @@
 			dev->driver = NULL;
 		}
 	}
+	spin_unlock(&pcidevlist_lock);
 }
 
 #ifdef CONFIG_HOTPLUG
@@ -700,7 +719,7 @@
 		if (drv->remove && pci_announce_device(drv, dev))
 			break;
 	}
-
+	
 	/* notify userspace of new hotplug device */
 	run_sbin_hotplug(dev, TRUE);
 }
@@ -716,7 +735,9 @@
 pci_insert_device(struct pci_dev *dev, struct pci_bus *bus)
 {
 	list_add_tail(&dev->bus_list, &bus->devices);
+	spin_lock(&pcidevlist_lock);
 	list_add_tail(&dev->global_list, &pci_devices);
+	spin_unlock(&pcidevlist_lock);
 #ifdef CONFIG_PROC_FS
 	pci_proc_attach_device(dev);
 #endif
@@ -751,7 +772,9 @@
 		dev->driver = NULL;
 	}
 	list_del(&dev->bus_list);
+	spin_lock(&pcidevlist_lock); 
 	list_del(&dev->global_list);
+	spin_unlock(&pcidevlist_lock);
 	pci_free_resources(dev);
 #ifdef CONFIG_PROC_FS
 	pci_proc_detach_device(dev);
@@ -1327,7 +1350,9 @@
 		 * Link the device to both the global PCI device chain and
 		 * the per-bus list of devices.
 		 */
+		spin_lock(&pcidevlist_lock);
 		list_add_tail(&dev->global_list, &pci_devices);
+		spin_unlock(&pcidevlist_lock);
 		list_add_tail(&dev->bus_list, &bus->devices);
 
 		/* Fix up broken headers */
@@ -1368,7 +1393,6 @@
 			if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
 				max = pci_scan_bridge(bus, dev, max, pass);
 		}
-
 	/*
 	 * We've scanned the bus and so we know all about what's on
 	 * the other side of any bridges that may be on this bus plus
@@ -1935,9 +1959,11 @@
 
 	pcibios_init();
 
+	spin_lock(&pcidevlist_lock);
 	pci_for_each_dev(dev) {
 		pci_fixup_device(PCI_FIXUP_FINAL, dev);
 	}
+	spin_unlock(&pcidevlist_lock);
 
 #ifdef CONFIG_PM
 	pm_register(PM_PCI_DEV, 0, pci_pm_callback);
Index: linux24/drivers/pci/proc.c
diff -u linux24/drivers/pci/proc.c:1.1.1.1 linux24/drivers/pci/proc.c:1.1.1.1.28.2
--- linux24/drivers/pci/proc.c:1.1.1.1	Tue Dec 18 15:49:16 2001
+++ linux24/drivers/pci/proc.c	Fri Mar  1 12:51:09 2002
@@ -306,17 +306,26 @@
 /* iterator */
 static void *pci_seq_start(struct seq_file *m, loff_t *pos)
 {
-	struct list_head *p = &pci_devices;
-	loff_t n = *pos;
-
-	/* XXX: surely we need some locking for traversing the list? */
+	struct list_head *p;
+	loff_t n;
+	spin_lock(&pcidevlist_lock);
+	p = &pci_devices;
+	n = *pos;
 	while (n--) {
 		p = p->next;
-		if (p == &pci_devices)
+		if (p == &pci_devices){
+			spin_unlock(&pcidevlist_lock);
 			return NULL;
+		}
 	}
+	spin_unlock(&pcidevlist_lock);
 	return p;
 }
+
+/*
+XXX: pci_seq_next() doesn't use pcidevlist_lock, make sure 
+you hold it before calling. (Currently unused?)
+*/
 static void *pci_seq_next(struct seq_file *m, void *v, loff_t *pos)
 {
 	struct list_head *p = v;
@@ -559,9 +568,11 @@
 		entry = create_proc_entry("devices", 0, proc_bus_pci_dir);
 		if (entry)
 			entry->proc_fops = &proc_bus_pci_dev_operations;
+		spin_lock(&pcidevlist_lock);
 		pci_for_each_dev(dev) {
 			pci_proc_attach_device(dev);
 		}
+		spin_unlock(&pcidevlist_lock);
 		entry = create_proc_entry("pci", 0, NULL);
 		if (entry)
 			entry->proc_fops = &proc_pci_operations;
Index: linux24/drivers/pci/setup-bus.c
diff -u linux24/drivers/pci/setup-bus.c:1.1.1.1 linux24/drivers/pci/setup-bus.c:1.1.1.1.28.3
--- linux24/drivers/pci/setup-bus.c:1.1.1.1	Tue Dec 18 15:49:16 2001
+++ linux24/drivers/pci/setup-bus.c	Mon Mar  4 15:33:30 2002
@@ -222,9 +222,11 @@
 		ranges.found_vga = 0;
 		pbus_assign_resources(b, &ranges);
 	}
+	spin_lock(&pcidevlist_lock);
 	pci_for_each_dev(dev) {
 		pdev_enable_device(dev);
 	}
+	spin_unlock(&pcidevlist_lock);
 }
 
 /* Check whether the bridge supports I/O forwarding.
Index: linux24/drivers/pci/setup-irq.c
diff -u linux24/drivers/pci/setup-irq.c:1.1.1.1 linux24/drivers/pci/setup-irq.c:1.1.1.1.28.1
--- linux24/drivers/pci/setup-irq.c:1.1.1.1	Tue Dec 18 15:49:16 2001
+++ linux24/drivers/pci/setup-irq.c	Fri Mar  1 14:27:55 2002
@@ -65,7 +65,9 @@
 	       int (*map_irq)(struct pci_dev *, u8, u8))
 {
 	struct pci_dev *dev;
+	spin_lock(&pcidevlist_lock);
 	pci_for_each_dev(dev) {
 		pdev_fixup_irq(dev, swizzle, map_irq);
 	}
+	spin_unlock(&pcidevlist_lock);
 }
Index: linux24/drivers/pnp/isapnp.c
diff -u linux24/drivers/pnp/isapnp.c:1.1.1.2 linux24/drivers/pnp/isapnp.c:1.1.1.2.2.3
--- linux24/drivers/pnp/isapnp.c:1.1.1.2	Wed Feb 13 14:53:31 2002
+++ linux24/drivers/pnp/isapnp.c	Mon Mar  4 15:33:30 2002
@@ -1685,10 +1685,14 @@
 	}
 #ifdef CONFIG_PCI
 	if (!isapnp_skip_pci_scan) {
+		spin_lock(&pcidevlist_lock);
 		pci_for_each_dev(dev) {
-			if (dev->irq == irq)
+			if (dev->irq == irq){
+				spin_unlock(&pcidevlist_lock);
 				return 1;
+			}
 		}
+		spin_unlock(&pcidevlist_lock);
 	}
 #endif
 	if (request_irq(irq, isapnp_test_handler, SA_INTERRUPT, "isapnp", NULL))
Index: linux24/include/linux/pci.h
diff -u linux24/include/linux/pci.h:1.1.1.1 linux24/include/linux/pci.h:1.1.1.1.28.7
--- linux24/include/linux/pci.h:1.1.1.1	Tue Dec 18 15:49:00 2001
+++ linux24/include/linux/pci.h	Wed Feb 27 15:11:05 2002
@@ -16,7 +16,7 @@
 
 #ifndef LINUX_PCI_H
 #define LINUX_PCI_H
-
+#include <linux/spinlock.h>
 /*
  * Under PCI, each device has 256 bytes of configuration address space,
  * of which the first 64 bytes are standardized as follows:
@@ -435,6 +435,7 @@
 
 extern struct list_head pci_root_buses;	/* list of all known PCI buses */
 extern struct list_head pci_devices;	/* list of all devices */
+extern spinlock_t pcidevlist_lock;
 
 /*
  * Error values that may be returned by PCI functions.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] PCI device list locking
  2002-03-04 23:54 [PATCH] PCI device list locking john stultz
@ 2002-03-05  0:17 ` Greg KH
  2002-03-05  1:05 ` john stultz
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Greg KH @ 2002-03-05  0:17 UTC (permalink / raw)
  To: linux-hotplug

On Mon, Mar 04, 2002 at 03:54:11PM -0800, john stultz wrote:
> @@ -605,10 +620,12 @@
>  	int count = 0;
>  
>  	list_add_tail(&drv->node, &pci_drivers);
> +	spin_lock(&pcidevlist_lock);
>  	pci_for_each_dev(dev) {
>  		if (!pci_dev_driver(dev))
>  			count += pci_announce_device(drv, dev);
>  	}
> +	spin_unlock(&pcidevlist_lock);
>  	return count;
>  }
>  

pci_announce_device() calls the pci driver's probe() function, which can
easily sleep.  Not a good thing :(

> @@ -1935,9 +1959,11 @@
>  
>  	pcibios_init();
>  
> +	spin_lock(&pcidevlist_lock);
>  	pci_for_each_dev(dev) {
>  		pci_fixup_device(PCI_FIXUP_FINAL, dev);
>  	}
> +	spin_unlock(&pcidevlist_lock);
>  
>  #ifdef CONFIG_PM
>  	pm_register(PM_PCI_DEV, 0, pci_pm_callback);

pci_fixup_device() might also be able to sleep, but I didn't go through
all of the different fixup calls to make sure.  Did you look at these?

> diff -u linux24/drivers/pci/setup-bus.c:1.1.1.1 linux24/drivers/pci/setup-bus.c:1.1.1.1.28.3
> --- linux24/drivers/pci/setup-bus.c:1.1.1.1	Tue Dec 18 15:49:16 2001
> +++ linux24/drivers/pci/setup-bus.c	Mon Mar  4 15:33:30 2002
> @@ -222,9 +222,11 @@
>  		ranges.found_vga = 0;
>  		pbus_assign_resources(b, &ranges);
>  	}
> +	spin_lock(&pcidevlist_lock);
>  	pci_for_each_dev(dev) {
>  		pdev_enable_device(dev);
>  	}
> +	spin_unlock(&pcidevlist_lock);
>  }
>  
>  /* Check whether the bridge supports I/O forwarding.

Can all implementations of pci_write_config_* be safely called with a
spinlock held?

In short, I don't think you can use a spinlock, but rather a semaphore
is probably necessary.

thanks,

greg k-h

_______________________________________________
Linux-hotplug-devel mailing list  http://linux-hotplug.sourceforge.net
Linux-hotplug-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-hotplug-devel

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] PCI device list locking
  2002-03-04 23:54 [PATCH] PCI device list locking john stultz
  2002-03-05  0:17 ` Greg KH
@ 2002-03-05  1:05 ` john stultz
  2002-03-05  1:12 ` Craig Christophel
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: john stultz @ 2002-03-05  1:05 UTC (permalink / raw)
  To: linux-hotplug

[-- Attachment #1: Type: text/plain, Size: 487 bytes --]

On Mon, 2002-03-04 at 16:17, Greg KH wrote:
> In short, I don't think you can use a spinlock, but rather a semaphore
> is probably necessary.

Sage advice indeed!

Take 2: with semaphores

Again, this is just fixes to the core pci code. Various drivers and arch
specific code still traverse the pci_devices list without the
pcidevlist_sem. If there are no complaints with this code (probably just
wishful thinking) I'll go on and clean up those unprotected traversals. 

thanks,
-john



[-- Attachment #2: pcidevlist_lock.patch --]
[-- Type: text/plain, Size: 7537 bytes --]

Index: linux24/drivers/pci/pci.c
diff -u linux24/drivers/pci/pci.c:1.1.1.1 linux24/drivers/pci/pci.c:1.1.1.1.28.20
--- linux24/drivers/pci/pci.c:1.1.1.1	Tue Dec 18 15:49:16 2001
+++ linux24/drivers/pci/pci.c	Mon Mar  4 17:03:27 2002
@@ -37,6 +37,7 @@
 
 LIST_HEAD(pci_root_buses);
 LIST_HEAD(pci_devices);
+DECLARE_MUTEX(pcidevlist_sem);
 
 /**
  * pci_find_slot - locate PCI device from a given PCI slot
@@ -55,10 +56,14 @@
 {
 	struct pci_dev *dev;
 
+	down(&pcidevlist_sem);
 	pci_for_each_dev(dev) {
-		if (dev->bus->number == bus && dev->devfn == devfn)
+		if (dev->bus->number == bus && dev->devfn == devfn){
+			up(&pcidevlist_sem);
 			return dev;
+		}
 	}
+	up(&pcidevlist_sem);
 	return NULL;
 }
 
@@ -81,17 +86,23 @@
 		unsigned int ss_vendor, unsigned int ss_device,
 		const struct pci_dev *from)
 {
-	struct list_head *n = from ? from->global_list.next : pci_devices.next;
+	struct list_head *n;
+	
+	down(&pcidevlist_sem);
+	n = from ? from->global_list.next : pci_devices.next;
 
 	while (n != &pci_devices) {
 		struct pci_dev *dev = pci_dev_g(n);
 		if ((vendor == PCI_ANY_ID || dev->vendor == vendor) &&
 		    (device == PCI_ANY_ID || dev->device == device) &&
 		    (ss_vendor == PCI_ANY_ID || dev->subsystem_vendor == ss_vendor) &&
-		    (ss_device == PCI_ANY_ID || dev->subsystem_device == ss_device))
+		    (ss_device == PCI_ANY_ID || dev->subsystem_device == ss_device)){
+			up(&pcidevlist_sem);
 			return dev;
+		}
 		n = n->next;
 	}
+	up(&pcidevlist_sem);
 	return NULL;
 }
 
@@ -130,14 +141,19 @@
 struct pci_dev *
 pci_find_class(unsigned int class, const struct pci_dev *from)
 {
-	struct list_head *n = from ? from->global_list.next : pci_devices.next;
+	struct list_head *n;
 
+	down(&pcidevlist_sem);
+	n = from ? from->global_list.next : pci_devices.next;
 	while (n != &pci_devices) {
 		struct pci_dev *dev = pci_dev_g(n);
-		if (dev->class == class)
+		if (dev->class == class){
+			up(&pcidevlist_sem);
 			return dev;
+		}
 		n = n->next;
 	}
+	up(&pcidevlist_sem);
 	return NULL;
 }
 
@@ -605,10 +621,12 @@
 	int count = 0;
 
 	list_add_tail(&drv->node, &pci_drivers);
+	down(&pcidevlist_sem);
 	pci_for_each_dev(dev) {
 		if (!pci_dev_driver(dev))
 			count += pci_announce_device(drv, dev);
 	}
+	up(&pcidevlist_sem);
 	return count;
 }
 
@@ -628,6 +646,7 @@
 	struct pci_dev *dev;
 
 	list_del(&drv->node);
+	down(&pcidevlist_sem);
 	pci_for_each_dev(dev) {
 		if (dev->driver == drv) {
 			if (drv->remove)
@@ -635,6 +654,7 @@
 			dev->driver = NULL;
 		}
 	}
+	up(&pcidevlist_sem);
 }
 
 #ifdef CONFIG_HOTPLUG
@@ -716,7 +736,9 @@
 pci_insert_device(struct pci_dev *dev, struct pci_bus *bus)
 {
 	list_add_tail(&dev->bus_list, &bus->devices);
+	down(&pcidevlist_sem);
 	list_add_tail(&dev->global_list, &pci_devices);
+	up(&pcidevlist_sem);
 #ifdef CONFIG_PROC_FS
 	pci_proc_attach_device(dev);
 #endif
@@ -751,7 +773,9 @@
 		dev->driver = NULL;
 	}
 	list_del(&dev->bus_list);
+	down(&pcidevlist_sem); 
 	list_del(&dev->global_list);
+	up(&pcidevlist_sem);
 	pci_free_resources(dev);
 #ifdef CONFIG_PROC_FS
 	pci_proc_detach_device(dev);
@@ -1327,7 +1351,9 @@
 		 * Link the device to both the global PCI device chain and
 		 * the per-bus list of devices.
 		 */
+		down(&pcidevlist_sem);
 		list_add_tail(&dev->global_list, &pci_devices);
+		up(&pcidevlist_sem);
 		list_add_tail(&dev->bus_list, &bus->devices);
 
 		/* Fix up broken headers */
@@ -1935,9 +1961,11 @@
 
 	pcibios_init();
 
+	down(&pcidevlist_sem);
 	pci_for_each_dev(dev) {
 		pci_fixup_device(PCI_FIXUP_FINAL, dev);
 	}
+	up(&pcidevlist_sem);
 
 #ifdef CONFIG_PM
 	pm_register(PM_PCI_DEV, 0, pci_pm_callback);
Index: linux24/drivers/pci/proc.c
diff -u linux24/drivers/pci/proc.c:1.1.1.1 linux24/drivers/pci/proc.c:1.1.1.1.28.3
--- linux24/drivers/pci/proc.c:1.1.1.1	Tue Dec 18 15:49:16 2001
+++ linux24/drivers/pci/proc.c	Mon Mar  4 16:55:22 2002
@@ -306,17 +306,26 @@
 /* iterator */
 static void *pci_seq_start(struct seq_file *m, loff_t *pos)
 {
-	struct list_head *p = &pci_devices;
-	loff_t n = *pos;
-
-	/* XXX: surely we need some locking for traversing the list? */
+	struct list_head *p;
+	loff_t n;
+	down(&pcidevlist_sem);
+	p = &pci_devices;
+	n = *pos;
 	while (n--) {
 		p = p->next;
-		if (p == &pci_devices)
+		if (p == &pci_devices){
+			up(&pcidevlist_sem);
 			return NULL;
+		}
 	}
+	up(&pcidevlist_sem);
 	return p;
 }
+
+/*
+XXX: pci_seq_next() doesn't use pcidevlist_sem, make sure 
+you down it before calling. (Currently unused?)
+*/
 static void *pci_seq_next(struct seq_file *m, void *v, loff_t *pos)
 {
 	struct list_head *p = v;
@@ -559,9 +568,11 @@
 		entry = create_proc_entry("devices", 0, proc_bus_pci_dir);
 		if (entry)
 			entry->proc_fops = &proc_bus_pci_dev_operations;
+		down(&pcidevlist_sem);
 		pci_for_each_dev(dev) {
 			pci_proc_attach_device(dev);
 		}
+		up(&pcidevlist_sem);
 		entry = create_proc_entry("pci", 0, NULL);
 		if (entry)
 			entry->proc_fops = &proc_pci_operations;
Index: linux24/drivers/pci/setup-bus.c
diff -u linux24/drivers/pci/setup-bus.c:1.1.1.1 linux24/drivers/pci/setup-bus.c:1.1.1.1.28.4
--- linux24/drivers/pci/setup-bus.c:1.1.1.1	Tue Dec 18 15:49:16 2001
+++ linux24/drivers/pci/setup-bus.c	Mon Mar  4 16:55:22 2002
@@ -222,9 +222,11 @@
 		ranges.found_vga = 0;
 		pbus_assign_resources(b, &ranges);
 	}
+	down(&pcidevlist_sem);
 	pci_for_each_dev(dev) {
 		pdev_enable_device(dev);
 	}
+	up(&pcidevlist_sem);
 }
 
 /* Check whether the bridge supports I/O forwarding.
Index: linux24/drivers/pci/setup-irq.c
diff -u linux24/drivers/pci/setup-irq.c:1.1.1.1 linux24/drivers/pci/setup-irq.c:1.1.1.1.28.2
--- linux24/drivers/pci/setup-irq.c:1.1.1.1	Tue Dec 18 15:49:16 2001
+++ linux24/drivers/pci/setup-irq.c	Mon Mar  4 16:55:22 2002
@@ -65,7 +65,9 @@
 	       int (*map_irq)(struct pci_dev *, u8, u8))
 {
 	struct pci_dev *dev;
+	down(&pcidevlist_sem);
 	pci_for_each_dev(dev) {
 		pdev_fixup_irq(dev, swizzle, map_irq);
 	}
+	up(&pcidevlist_sem);
 }
Index: linux24/drivers/pnp/isapnp.c
diff -u linux24/drivers/pnp/isapnp.c:1.1.1.2 linux24/drivers/pnp/isapnp.c:1.1.1.2.2.4
--- linux24/drivers/pnp/isapnp.c:1.1.1.2	Wed Feb 13 14:53:31 2002
+++ linux24/drivers/pnp/isapnp.c	Mon Mar  4 16:55:22 2002
@@ -1685,10 +1685,14 @@
 	}
 #ifdef CONFIG_PCI
 	if (!isapnp_skip_pci_scan) {
+		down(&pcidevlist_sem);
 		pci_for_each_dev(dev) {
-			if (dev->irq == irq)
+			if (dev->irq == irq){
+				up(&pcidevlist_sem);
 				return 1;
+			}
 		}
+		up(&pcidevlist_sem);
 	}
 #endif
 	if (request_irq(irq, isapnp_test_handler, SA_INTERRUPT, "isapnp", NULL))
Index: linux24/include/linux/pci.h
diff -u linux24/include/linux/pci.h:1.1.1.1 linux24/include/linux/pci.h:1.1.1.1.28.9
--- linux24/include/linux/pci.h:1.1.1.1	Tue Dec 18 15:49:00 2001
+++ linux24/include/linux/pci.h	Mon Mar  4 16:57:00 2002
@@ -16,7 +16,7 @@
 
 #ifndef LINUX_PCI_H
 #define LINUX_PCI_H
-
+#include <asm/semaphore.h>
 /*
  * Under PCI, each device has 256 bytes of configuration address space,
  * of which the first 64 bytes are standardized as follows:
@@ -435,6 +435,7 @@
 
 extern struct list_head pci_root_buses;	/* list of all known PCI buses */
 extern struct list_head pci_devices;	/* list of all devices */
+extern struct semaphore pcidevlist_sem;
 
 /*
  * Error values that may be returned by PCI functions.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] PCI device list locking
  2002-03-04 23:54 [PATCH] PCI device list locking john stultz
  2002-03-05  0:17 ` Greg KH
  2002-03-05  1:05 ` john stultz
@ 2002-03-05  1:12 ` Craig Christophel
  2002-03-05  1:47 ` john stultz
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Craig Christophel @ 2002-03-05  1:12 UTC (permalink / raw)
  To: linux-hotplug

Also if there is only infrequent useage of the list to change the list, 
perhaps you should use a rwlock_t instead.   It's just as light as a spinlock 
but has the advantage of allowing multiple readers.



Craig.


On Monday 04 March 2002 19:17, Greg KH wrote:
> On Mon, Mar 04, 2002 at 03:54:11PM -0800, john stultz wrote:
> > @@ -605,10 +620,12 @@
> >  	int count = 0;
> >
> >  	list_add_tail(&drv->node, &pci_drivers);
> > +	spin_lock(&pcidevlist_lock);
> >  	pci_for_each_dev(dev) {
> >  		if (!pci_dev_driver(dev))
> >  			count += pci_announce_device(drv, dev);
> >  	}
> > +	spin_unlock(&pcidevlist_lock);
> >  	return count;
> >  }
>
> pci_announce_device() calls the pci driver's probe() function, which can
> easily sleep.  Not a good thing :(
>
> > @@ -1935,9 +1959,11 @@
> >
> >  	pcibios_init();
> >
> > +	spin_lock(&pcidevlist_lock);
> >  	pci_for_each_dev(dev) {
> >  		pci_fixup_device(PCI_FIXUP_FINAL, dev);
> >  	}
> > +	spin_unlock(&pcidevlist_lock);
> >
> >  #ifdef CONFIG_PM
> >  	pm_register(PM_PCI_DEV, 0, pci_pm_callback);
>
> pci_fixup_device() might also be able to sleep, but I didn't go through
> all of the different fixup calls to make sure.  Did you look at these?
>
> > diff -u linux24/drivers/pci/setup-bus.c:1.1.1.1
> > linux24/drivers/pci/setup-bus.c:1.1.1.1.28.3 ---
> > linux24/drivers/pci/setup-bus.c:1.1.1.1	Tue Dec 18 15:49:16 2001 +++
> > linux24/drivers/pci/setup-bus.c	Mon Mar  4 15:33:30 2002
> > @@ -222,9 +222,11 @@
> >  		ranges.found_vga = 0;
> >  		pbus_assign_resources(b, &ranges);
> >  	}
> > +	spin_lock(&pcidevlist_lock);
> >  	pci_for_each_dev(dev) {
> >  		pdev_enable_device(dev);
> >  	}
> > +	spin_unlock(&pcidevlist_lock);
> >  }
> >
> >  /* Check whether the bridge supports I/O forwarding.
>
> Can all implementations of pci_write_config_* be safely called with a
> spinlock held?
>
> In short, I don't think you can use a spinlock, but rather a semaphore
> is probably necessary.
>
> thanks,
>
> greg k-h
>
> _______________________________________________
> Kernel-janitor-discuss mailing list
> Kernel-janitor-discuss@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/kernel-janitor-discuss

_______________________________________________
Linux-hotplug-devel mailing list  http://linux-hotplug.sourceforge.net
Linux-hotplug-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-hotplug-devel

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] PCI device list locking
  2002-03-04 23:54 [PATCH] PCI device list locking john stultz
                   ` (2 preceding siblings ...)
  2002-03-05  1:12 ` Craig Christophel
@ 2002-03-05  1:47 ` john stultz
  2002-03-06  1:31 ` Martin Diehl
  2002-03-10 12:07 ` Martin Mares
  5 siblings, 0 replies; 7+ messages in thread
From: john stultz @ 2002-03-05  1:47 UTC (permalink / raw)
  To: linux-hotplug

[-- Attachment #1: Type: text/plain, Size: 635 bytes --]

On Mon, 2002-03-04 at 17:12, Craig Christophel wrote:
> Also if there is only infrequent useage of the list to change the list, 
> perhaps you should use a rwlock_t instead.   It's just as light as a spinlock 
> but has the advantage of allowing multiple readers.

How about rwsem? See attached.

I think I did this properly, but there may be a few boo-boos. 

particularly:
o pci_proc_attach_device()
o pdev_enable_device()
o pdev_fixup_irq()

look like reader safe to me, but I could be wrong. 

and 
o pci_fixup_device()
o pci_announce_device

are writers, as I'm not sure what drv->probe or fixup->hook are doing.


thanks,
-john


[-- Attachment #2: pcidevlist_lock.patch --]
[-- Type: text/plain, Size: 7804 bytes --]

Index: linux24/drivers/pci/pci.c
diff -u linux24/drivers/pci/pci.c:1.1.1.1 linux24/drivers/pci/pci.c:1.1.1.1.28.23
--- linux24/drivers/pci/pci.c:1.1.1.1	Tue Dec 18 15:49:16 2001
+++ linux24/drivers/pci/pci.c	Mon Mar  4 17:46:15 2002
@@ -37,6 +37,7 @@
 
 LIST_HEAD(pci_root_buses);
 LIST_HEAD(pci_devices);
+DECLARE_RWSEM(pcidevlist_sem);
 
 /**
  * pci_find_slot - locate PCI device from a given PCI slot
@@ -55,10 +56,14 @@
 {
 	struct pci_dev *dev;
 
+	down_read(&pcidevlist_sem);
 	pci_for_each_dev(dev) {
-		if (dev->bus->number == bus && dev->devfn == devfn)
+		if (dev->bus->number == bus && dev->devfn == devfn){
+			up_read(&pcidevlist_sem);
 			return dev;
+		}
 	}
+	up_read(&pcidevlist_sem);
 	return NULL;
 }
 
@@ -81,17 +86,23 @@
 		unsigned int ss_vendor, unsigned int ss_device,
 		const struct pci_dev *from)
 {
-	struct list_head *n = from ? from->global_list.next : pci_devices.next;
+	struct list_head *n;
+	
+	down_read(&pcidevlist_sem);
+	n = from ? from->global_list.next : pci_devices.next;
 
 	while (n != &pci_devices) {
 		struct pci_dev *dev = pci_dev_g(n);
 		if ((vendor == PCI_ANY_ID || dev->vendor == vendor) &&
 		    (device == PCI_ANY_ID || dev->device == device) &&
 		    (ss_vendor == PCI_ANY_ID || dev->subsystem_vendor == ss_vendor) &&
-		    (ss_device == PCI_ANY_ID || dev->subsystem_device == ss_device))
+		    (ss_device == PCI_ANY_ID || dev->subsystem_device == ss_device)){
+			up_read(&pcidevlist_sem);
 			return dev;
+		}
 		n = n->next;
 	}
+	up_read(&pcidevlist_sem);
 	return NULL;
 }
 
@@ -130,14 +141,19 @@
 struct pci_dev *
 pci_find_class(unsigned int class, const struct pci_dev *from)
 {
-	struct list_head *n = from ? from->global_list.next : pci_devices.next;
+	struct list_head *n;
 
+	down_read(&pcidevlist_sem);
+	n = from ? from->global_list.next : pci_devices.next;
 	while (n != &pci_devices) {
 		struct pci_dev *dev = pci_dev_g(n);
-		if (dev->class == class)
+		if (dev->class == class){
+			up_read(&pcidevlist_sem);
 			return dev;
+		}
 		n = n->next;
 	}
+	up_read(&pcidevlist_sem);
 	return NULL;
 }
 
@@ -605,10 +621,12 @@
 	int count = 0;
 
 	list_add_tail(&drv->node, &pci_drivers);
+	down_write(&pcidevlist_sem); /* XXX: unsure of what dev->probe will do*/
 	pci_for_each_dev(dev) {
 		if (!pci_dev_driver(dev))
 			count += pci_announce_device(drv, dev);
 	}
+	up_write(&pcidevlist_sem);
 	return count;
 }
 
@@ -628,6 +646,7 @@
 	struct pci_dev *dev;
 
 	list_del(&drv->node);
+	down_write(&pcidevlist_sem);
 	pci_for_each_dev(dev) {
 		if (dev->driver == drv) {
 			if (drv->remove)
@@ -635,6 +654,7 @@
 			dev->driver = NULL;
 		}
 	}
+	up_write(&pcidevlist_sem);
 }
 
 #ifdef CONFIG_HOTPLUG
@@ -716,7 +736,9 @@
 pci_insert_device(struct pci_dev *dev, struct pci_bus *bus)
 {
 	list_add_tail(&dev->bus_list, &bus->devices);
+	down_write(&pcidevlist_sem);
 	list_add_tail(&dev->global_list, &pci_devices);
+	up_write(&pcidevlist_sem);
 #ifdef CONFIG_PROC_FS
 	pci_proc_attach_device(dev);
 #endif
@@ -751,7 +773,9 @@
 		dev->driver = NULL;
 	}
 	list_del(&dev->bus_list);
+	down_write(&pcidevlist_sem); 
 	list_del(&dev->global_list);
+	up_write(&pcidevlist_sem);
 	pci_free_resources(dev);
 #ifdef CONFIG_PROC_FS
 	pci_proc_detach_device(dev);
@@ -1327,7 +1351,9 @@
 		 * Link the device to both the global PCI device chain and
 		 * the per-bus list of devices.
 		 */
+		down_write(&pcidevlist_sem);
 		list_add_tail(&dev->global_list, &pci_devices);
+		up_write(&pcidevlist_sem);
 		list_add_tail(&dev->bus_list, &bus->devices);
 
 		/* Fix up broken headers */
@@ -1935,9 +1961,11 @@
 
 	pcibios_init();
 
+	down_write(&pcidevlist_sem); /*XXX: unsure what fixup->hook will do*/
 	pci_for_each_dev(dev) {
 		pci_fixup_device(PCI_FIXUP_FINAL, dev);
 	}
+	up_write(&pcidevlist_sem);
 
 #ifdef CONFIG_PM
 	pm_register(PM_PCI_DEV, 0, pci_pm_callback);
Index: linux24/drivers/pci/proc.c
diff -u linux24/drivers/pci/proc.c:1.1.1.1 linux24/drivers/pci/proc.c:1.1.1.1.28.5
--- linux24/drivers/pci/proc.c:1.1.1.1	Tue Dec 18 15:49:16 2001
+++ linux24/drivers/pci/proc.c	Mon Mar  4 17:43:22 2002
@@ -306,17 +306,26 @@
 /* iterator */
 static void *pci_seq_start(struct seq_file *m, loff_t *pos)
 {
-	struct list_head *p = &pci_devices;
-	loff_t n = *pos;
-
-	/* XXX: surely we need some locking for traversing the list? */
+	struct list_head *p;
+	loff_t n;
+	down_read(&pcidevlist_sem);
+	p = &pci_devices;
+	n = *pos;
 	while (n--) {
 		p = p->next;
-		if (p == &pci_devices)
+		if (p == &pci_devices){
+			up_read(&pcidevlist_sem);
 			return NULL;
+		}
 	}
+	up_read(&pcidevlist_sem);
 	return p;
 }
+
+/*
+XXX: pci_seq_next() doesn't use pcidevlist_sem, make sure 
+you down it before calling. (Currently unused?)
+*/
 static void *pci_seq_next(struct seq_file *m, void *v, loff_t *pos)
 {
 	struct list_head *p = v;
@@ -559,9 +568,11 @@
 		entry = create_proc_entry("devices", 0, proc_bus_pci_dir);
 		if (entry)
 			entry->proc_fops = &proc_bus_pci_dev_operations;
+		down_read(&pcidevlist_sem);
 		pci_for_each_dev(dev) {
 			pci_proc_attach_device(dev);
 		}
+		up_read(&pcidevlist_sem); 
 		entry = create_proc_entry("pci", 0, NULL);
 		if (entry)
 			entry->proc_fops = &proc_pci_operations;
Index: linux24/drivers/pci/setup-bus.c
diff -u linux24/drivers/pci/setup-bus.c:1.1.1.1 linux24/drivers/pci/setup-bus.c:1.1.1.1.28.6
--- linux24/drivers/pci/setup-bus.c:1.1.1.1	Tue Dec 18 15:49:16 2001
+++ linux24/drivers/pci/setup-bus.c	Mon Mar  4 17:43:22 2002
@@ -222,9 +222,11 @@
 		ranges.found_vga = 0;
 		pbus_assign_resources(b, &ranges);
 	}
+	down_read(&pcidevlist_sem); 
 	pci_for_each_dev(dev) {
 		pdev_enable_device(dev);
 	}
+	up_read(&pcidevlist_sem); 
 }
 
 /* Check whether the bridge supports I/O forwarding.
Index: linux24/drivers/pci/setup-irq.c
diff -u linux24/drivers/pci/setup-irq.c:1.1.1.1 linux24/drivers/pci/setup-irq.c:1.1.1.1.28.4
--- linux24/drivers/pci/setup-irq.c:1.1.1.1	Tue Dec 18 15:49:16 2001
+++ linux24/drivers/pci/setup-irq.c	Mon Mar  4 17:45:13 2002
@@ -65,7 +65,9 @@
 	       int (*map_irq)(struct pci_dev *, u8, u8))
 {
 	struct pci_dev *dev;
+	down_read(&pcidevlist_sem);
 	pci_for_each_dev(dev) {
 		pdev_fixup_irq(dev, swizzle, map_irq);
 	}
+	up_read(&pcidevlist_sem);
 }
Index: linux24/drivers/pnp/isapnp.c
diff -u linux24/drivers/pnp/isapnp.c:1.1.1.2 linux24/drivers/pnp/isapnp.c:1.1.1.2.2.5
--- linux24/drivers/pnp/isapnp.c:1.1.1.2	Wed Feb 13 14:53:31 2002
+++ linux24/drivers/pnp/isapnp.c	Mon Mar  4 17:31:15 2002
@@ -1685,10 +1685,14 @@
 	}
 #ifdef CONFIG_PCI
 	if (!isapnp_skip_pci_scan) {
+		down_read(&pcidevlist_sem);
 		pci_for_each_dev(dev) {
-			if (dev->irq == irq)
+			if (dev->irq == irq){
+				up_read(&pcidevlist_sem);
 				return 1;
+			}
 		}
+		up_read(&pcidevlist_sem);
 	}
 #endif
 	if (request_irq(irq, isapnp_test_handler, SA_INTERRUPT, "isapnp", NULL))
Index: linux24/include/linux/pci.h
diff -u linux24/include/linux/pci.h:1.1.1.1 linux24/include/linux/pci.h:1.1.1.1.28.10
--- linux24/include/linux/pci.h:1.1.1.1	Tue Dec 18 15:49:00 2001
+++ linux24/include/linux/pci.h	Mon Mar  4 17:31:15 2002
@@ -16,7 +16,7 @@
 
 #ifndef LINUX_PCI_H
 #define LINUX_PCI_H
-
+#include <linux/rwsem.h>
 /*
  * Under PCI, each device has 256 bytes of configuration address space,
  * of which the first 64 bytes are standardized as follows:
@@ -435,6 +435,7 @@
 
 extern struct list_head pci_root_buses;	/* list of all known PCI buses */
 extern struct list_head pci_devices;	/* list of all devices */
+extern struct rw_semaphore pcidevlist_sem;
 
 /*
  * Error values that may be returned by PCI functions.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] PCI device list locking
  2002-03-04 23:54 [PATCH] PCI device list locking john stultz
                   ` (3 preceding siblings ...)
  2002-03-05  1:47 ` john stultz
@ 2002-03-06  1:31 ` Martin Diehl
  2002-03-10 12:07 ` Martin Mares
  5 siblings, 0 replies; 7+ messages in thread
From: Martin Diehl @ 2002-03-06  1:31 UTC (permalink / raw)
  To: linux-hotplug

On Mon, 4 Mar 2002, Greg KH wrote:

> > +	spin_lock(&pcidevlist_lock);
> >  	pci_for_each_dev(dev) {
> >  		if (!pci_dev_driver(dev))
> >  			count += pci_announce_device(drv, dev);
> >  	}
> > +	spin_unlock(&pcidevlist_lock);
> 
> pci_announce_device() calls the pci driver's probe() function, which can
> easily sleep.  Not a good thing :(

Unless I missed something even a semaphore might deadlock then:

think of a pci driver for bridge-type device which has devices already 
attached when probed - for example cardbus (or hotplug) controller with
device on the downside. If the devices are found during probe(), the 
driver might want to just add them in one go.

Martin


_______________________________________________
Linux-hotplug-devel mailing list  http://linux-hotplug.sourceforge.net
Linux-hotplug-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-hotplug-devel

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] PCI device list locking
  2002-03-04 23:54 [PATCH] PCI device list locking john stultz
                   ` (4 preceding siblings ...)
  2002-03-06  1:31 ` Martin Diehl
@ 2002-03-10 12:07 ` Martin Mares
  5 siblings, 0 replies; 7+ messages in thread
From: Martin Mares @ 2002-03-10 12:07 UTC (permalink / raw)
  To: linux-hotplug

Hello!

When I was actively maintaining the PCI subsystem, I was becoming increasingly
unhappy about the lack of locking, but I was unable to find any simple
strategy which wouldn't require rewriting of many drivers.

Lots of drivers still use the old probing interface (pci_find_*). Possible
solutions: either add locking around probing loop in all drivers or (maybe
better) convert all drivers to the new interface and drop the old one
completely.

> Unless I missed something even a semaphore might deadlock then:
> 
> think of a pci driver for bridge-type device which has devices already 
> attached when probed - for example cardbus (or hotplug) controller with
> device on the downside. If the devices are found during probe(), the 
> driver might want to just add them in one go.

Device addition is not a hard problem, it just suffices to define the locking
rules a bit better to avoid deadlock. Removal is much worse -- you need to
ensure not only nobody is working with the list, but also that nobody is
using the pci_dev being removed.

Maybe we could do it this way:  (assuming all drivers use the new interface)

  o  Introduce a global semaphore guarding all PCI lists (the global
     device list and all per-bus linkages). Add pci_{un,}lock_lists().
     [Using any r/w-thing is probably pointless as both reads and writes
     to the list are very rare.]

  o  Make pci_for_each_dev call pci_{un,}lock_lists().

  o  Define that for calling pci_{insert,remove}_device() the lists
     must be locked by the caller.

  o  Define that if you want to access any pci_dev, you must either have
     the lists locked or be the owner of the device (pci_dev->driver).

  o  Define that when you are walking the list by pci_for_each_dev(),
     you can remove any device except the current one.

Unless I've missed anything, these rules seem to cover all the cases
needed including probe/remove functions adding/removing devices on the
downside of a bridge.

				Have a nice fortnight
-- 
Martin `MJ' Mares   <mj@ucw.cz>   http://atrey.karlin.mff.cuni.cz/~mj/
Faculty of Math and Physics, Charles University, Prague, Czech Rep., Earth
For every complex problem, there's a solution that is simple, neat and wrong.

_______________________________________________
Linux-hotplug-devel mailing list  http://linux-hotplug.sourceforge.net
Linux-hotplug-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-hotplug-devel

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2002-03-10 12:07 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2002-03-04 23:54 [PATCH] PCI device list locking john stultz
2002-03-05  0:17 ` Greg KH
2002-03-05  1:05 ` john stultz
2002-03-05  1:12 ` Craig Christophel
2002-03-05  1:47 ` john stultz
2002-03-06  1:31 ` Martin Diehl
2002-03-10 12:07 ` Martin Mares

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).