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 | ||
