diff options
-rw-r--r-- | drivers/pci/hotplug/fakephp.c | 39 |
1 files changed, 35 insertions, 4 deletions
diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c index d7a293e3faf5..94b640146d44 100644 --- a/drivers/pci/hotplug/fakephp.c +++ b/drivers/pci/hotplug/fakephp.c | |||
@@ -39,6 +39,7 @@ | |||
39 | #include <linux/init.h> | 39 | #include <linux/init.h> |
40 | #include <linux/string.h> | 40 | #include <linux/string.h> |
41 | #include <linux/slab.h> | 41 | #include <linux/slab.h> |
42 | #include <linux/workqueue.h> | ||
42 | #include "../pci.h" | 43 | #include "../pci.h" |
43 | 44 | ||
44 | #if !defined(MODULE) | 45 | #if !defined(MODULE) |
@@ -63,10 +64,16 @@ struct dummy_slot { | |||
63 | struct list_head node; | 64 | struct list_head node; |
64 | struct hotplug_slot *slot; | 65 | struct hotplug_slot *slot; |
65 | struct pci_dev *dev; | 66 | struct pci_dev *dev; |
67 | struct work_struct remove_work; | ||
68 | unsigned long removed; | ||
66 | }; | 69 | }; |
67 | 70 | ||
68 | static int debug; | 71 | static int debug; |
69 | static LIST_HEAD(slot_list); | 72 | static LIST_HEAD(slot_list); |
73 | static struct workqueue_struct *dummyphp_wq; | ||
74 | |||
75 | static void pci_rescan_worker(struct work_struct *work); | ||
76 | static DECLARE_WORK(pci_rescan_work, pci_rescan_worker); | ||
70 | 77 | ||
71 | static int enable_slot (struct hotplug_slot *slot); | 78 | static int enable_slot (struct hotplug_slot *slot); |
72 | static int disable_slot (struct hotplug_slot *slot); | 79 | static int disable_slot (struct hotplug_slot *slot); |
@@ -109,7 +116,7 @@ static int add_slot(struct pci_dev *dev) | |||
109 | slot->name = &dev->dev.bus_id[0]; | 116 | slot->name = &dev->dev.bus_id[0]; |
110 | dbg("slot->name = %s\n", slot->name); | 117 | dbg("slot->name = %s\n", slot->name); |
111 | 118 | ||
112 | dslot = kmalloc(sizeof(struct dummy_slot), GFP_KERNEL); | 119 | dslot = kzalloc(sizeof(struct dummy_slot), GFP_KERNEL); |
113 | if (!dslot) | 120 | if (!dslot) |
114 | goto error_info; | 121 | goto error_info; |
115 | 122 | ||
@@ -164,6 +171,14 @@ static void remove_slot(struct dummy_slot *dslot) | |||
164 | err("Problem unregistering a slot %s\n", dslot->slot->name); | 171 | err("Problem unregistering a slot %s\n", dslot->slot->name); |
165 | } | 172 | } |
166 | 173 | ||
174 | /* called from the single-threaded workqueue handler to remove a slot */ | ||
175 | static void remove_slot_worker(struct work_struct *work) | ||
176 | { | ||
177 | struct dummy_slot *dslot = | ||
178 | container_of(work, struct dummy_slot, remove_work); | ||
179 | remove_slot(dslot); | ||
180 | } | ||
181 | |||
167 | /** | 182 | /** |
168 | * pci_rescan_slot - Rescan slot | 183 | * pci_rescan_slot - Rescan slot |
169 | * @temp: Device template. Should be set: bus and devfn. | 184 | * @temp: Device template. Should be set: bus and devfn. |
@@ -267,11 +282,17 @@ static inline void pci_rescan(void) { | |||
267 | pci_rescan_buses(&pci_root_buses); | 282 | pci_rescan_buses(&pci_root_buses); |
268 | } | 283 | } |
269 | 284 | ||
285 | /* called from the single-threaded workqueue handler to rescan all pci buses */ | ||
286 | static void pci_rescan_worker(struct work_struct *work) | ||
287 | { | ||
288 | pci_rescan(); | ||
289 | } | ||
270 | 290 | ||
271 | static int enable_slot(struct hotplug_slot *hotplug_slot) | 291 | static int enable_slot(struct hotplug_slot *hotplug_slot) |
272 | { | 292 | { |
273 | /* mis-use enable_slot for rescanning of the pci bus */ | 293 | /* mis-use enable_slot for rescanning of the pci bus */ |
274 | pci_rescan(); | 294 | cancel_work_sync(&pci_rescan_work); |
295 | queue_work(dummyphp_wq, &pci_rescan_work); | ||
275 | return -ENODEV; | 296 | return -ENODEV; |
276 | } | 297 | } |
277 | 298 | ||
@@ -306,6 +327,10 @@ static int disable_slot(struct hotplug_slot *slot) | |||
306 | err("Can't remove PCI devices with other PCI devices behind it yet.\n"); | 327 | err("Can't remove PCI devices with other PCI devices behind it yet.\n"); |
307 | return -ENODEV; | 328 | return -ENODEV; |
308 | } | 329 | } |
330 | if (test_and_set_bit(0, &dslot->removed)) { | ||
331 | dbg("Slot already scheduled for removal\n"); | ||
332 | return -ENODEV; | ||
333 | } | ||
309 | /* search for subfunctions and disable them first */ | 334 | /* search for subfunctions and disable them first */ |
310 | if (!(dslot->dev->devfn & 7)) { | 335 | if (!(dslot->dev->devfn & 7)) { |
311 | for (func = 1; func < 8; func++) { | 336 | for (func = 1; func < 8; func++) { |
@@ -328,8 +353,9 @@ static int disable_slot(struct hotplug_slot *slot) | |||
328 | /* remove the device from the pci core */ | 353 | /* remove the device from the pci core */ |
329 | pci_remove_bus_device(dslot->dev); | 354 | pci_remove_bus_device(dslot->dev); |
330 | 355 | ||
331 | /* blow away this sysfs entry and other parts. */ | 356 | /* queue work item to blow away this sysfs entry and other parts. */ |
332 | remove_slot(dslot); | 357 | INIT_WORK(&dslot->remove_work, remove_slot_worker); |
358 | queue_work(dummyphp_wq, &dslot->remove_work); | ||
333 | 359 | ||
334 | return 0; | 360 | return 0; |
335 | } | 361 | } |
@@ -340,6 +366,7 @@ static void cleanup_slots (void) | |||
340 | struct list_head *next; | 366 | struct list_head *next; |
341 | struct dummy_slot *dslot; | 367 | struct dummy_slot *dslot; |
342 | 368 | ||
369 | destroy_workqueue(dummyphp_wq); | ||
343 | list_for_each_safe (tmp, next, &slot_list) { | 370 | list_for_each_safe (tmp, next, &slot_list) { |
344 | dslot = list_entry (tmp, struct dummy_slot, node); | 371 | dslot = list_entry (tmp, struct dummy_slot, node); |
345 | remove_slot(dslot); | 372 | remove_slot(dslot); |
@@ -351,6 +378,10 @@ static int __init dummyphp_init(void) | |||
351 | { | 378 | { |
352 | info(DRIVER_DESC "\n"); | 379 | info(DRIVER_DESC "\n"); |
353 | 380 | ||
381 | dummyphp_wq = create_singlethread_workqueue(MY_NAME); | ||
382 | if (!dummyphp_wq) | ||
383 | return -ENOMEM; | ||
384 | |||
354 | return pci_scan_buses(); | 385 | return pci_scan_buses(); |
355 | } | 386 | } |
356 | 387 | ||