]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/2.6.24.1/pci-fix-fakephp-deadlock.patch
Fixes for 5.10
[thirdparty/kernel/stable-queue.git] / releases / 2.6.24.1 / pci-fix-fakephp-deadlock.patch
1 From stable-bounces@linux.kernel.org Mon Feb 4 06:11:08 2008
2 From: Ian Abbott <abbotti@mev.co.uk>
3 Date: Mon, 04 Feb 2008 13:43:13 +0000
4 Subject: PCI: Fix fakephp deadlock
5 To: stable@kernel.org
6 Cc: linux-pci@atrey.karlin.mff.cuni.cz, linux-kernel@vger.kernel.org
7 Message-ID: <47A71671.7080306@mev.co.uk>
8
9
10 From: Ian Abbott <abbotti@mev.co.uk>
11
12 This patch works around a problem in the fakephp driver when a process
13 writing "0" to a "power" sysfs file to fake removal of a PCI device ends
14 up deadlocking itself in the sysfs code.
15
16 The patch was recently accepted into Linus' tree after the 2.6.24 release:
17 http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=5c796ae7a7ebe56967ed9b9963d7c16d733635ff
18
19 Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
20 Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
21
22 ---
23 drivers/pci/hotplug/fakephp.c | 39 +++++++++++++++++++++++++++++++++++----
24 1 file changed, 35 insertions(+), 4 deletions(-)
25
26 --- a/drivers/pci/hotplug/fakephp.c
27 +++ b/drivers/pci/hotplug/fakephp.c
28 @@ -39,6 +39,7 @@
29 #include <linux/init.h>
30 #include <linux/string.h>
31 #include <linux/slab.h>
32 +#include <linux/workqueue.h>
33 #include "../pci.h"
34
35 #if !defined(MODULE)
36 @@ -63,10 +64,16 @@ struct dummy_slot {
37 struct list_head node;
38 struct hotplug_slot *slot;
39 struct pci_dev *dev;
40 + struct work_struct remove_work;
41 + unsigned long removed;
42 };
43
44 static int debug;
45 static LIST_HEAD(slot_list);
46 +static struct workqueue_struct *dummyphp_wq;
47 +
48 +static void pci_rescan_worker(struct work_struct *work);
49 +static DECLARE_WORK(pci_rescan_work, pci_rescan_worker);
50
51 static int enable_slot (struct hotplug_slot *slot);
52 static int disable_slot (struct hotplug_slot *slot);
53 @@ -109,7 +116,7 @@ static int add_slot(struct pci_dev *dev)
54 slot->name = &dev->dev.bus_id[0];
55 dbg("slot->name = %s\n", slot->name);
56
57 - dslot = kmalloc(sizeof(struct dummy_slot), GFP_KERNEL);
58 + dslot = kzalloc(sizeof(struct dummy_slot), GFP_KERNEL);
59 if (!dslot)
60 goto error_info;
61
62 @@ -164,6 +171,14 @@ static void remove_slot(struct dummy_slo
63 err("Problem unregistering a slot %s\n", dslot->slot->name);
64 }
65
66 +/* called from the single-threaded workqueue handler to remove a slot */
67 +static void remove_slot_worker(struct work_struct *work)
68 +{
69 + struct dummy_slot *dslot =
70 + container_of(work, struct dummy_slot, remove_work);
71 + remove_slot(dslot);
72 +}
73 +
74 /**
75 * pci_rescan_slot - Rescan slot
76 * @temp: Device template. Should be set: bus and devfn.
77 @@ -267,11 +282,17 @@ static inline void pci_rescan(void) {
78 pci_rescan_buses(&pci_root_buses);
79 }
80
81 +/* called from the single-threaded workqueue handler to rescan all pci buses */
82 +static void pci_rescan_worker(struct work_struct *work)
83 +{
84 + pci_rescan();
85 +}
86
87 static int enable_slot(struct hotplug_slot *hotplug_slot)
88 {
89 /* mis-use enable_slot for rescanning of the pci bus */
90 - pci_rescan();
91 + cancel_work_sync(&pci_rescan_work);
92 + queue_work(dummyphp_wq, &pci_rescan_work);
93 return -ENODEV;
94 }
95
96 @@ -306,6 +327,10 @@ static int disable_slot(struct hotplug_s
97 err("Can't remove PCI devices with other PCI devices behind it yet.\n");
98 return -ENODEV;
99 }
100 + if (test_and_set_bit(0, &dslot->removed)) {
101 + dbg("Slot already scheduled for removal\n");
102 + return -ENODEV;
103 + }
104 /* search for subfunctions and disable them first */
105 if (!(dslot->dev->devfn & 7)) {
106 for (func = 1; func < 8; func++) {
107 @@ -328,8 +353,9 @@ static int disable_slot(struct hotplug_s
108 /* remove the device from the pci core */
109 pci_remove_bus_device(dslot->dev);
110
111 - /* blow away this sysfs entry and other parts. */
112 - remove_slot(dslot);
113 + /* queue work item to blow away this sysfs entry and other parts. */
114 + INIT_WORK(&dslot->remove_work, remove_slot_worker);
115 + queue_work(dummyphp_wq, &dslot->remove_work);
116
117 return 0;
118 }
119 @@ -340,6 +366,7 @@ static void cleanup_slots (void)
120 struct list_head *next;
121 struct dummy_slot *dslot;
122
123 + destroy_workqueue(dummyphp_wq);
124 list_for_each_safe (tmp, next, &slot_list) {
125 dslot = list_entry (tmp, struct dummy_slot, node);
126 remove_slot(dslot);
127 @@ -351,6 +378,10 @@ static int __init dummyphp_init(void)
128 {
129 info(DRIVER_DESC "\n");
130
131 + dummyphp_wq = create_singlethread_workqueue(MY_NAME);
132 + if (!dummyphp_wq)
133 + return -ENOMEM;
134 +
135 return pci_scan_buses();
136 }
137