|
@@ -39,6 +39,7 @@
|
|
#include <linux/init.h>
|
|
#include <linux/init.h>
|
|
#include <linux/string.h>
|
|
#include <linux/string.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/slab.h>
|
|
|
|
+#include <linux/workqueue.h>
|
|
#include "../pci.h"
|
|
#include "../pci.h"
|
|
|
|
|
|
#if !defined(MODULE)
|
|
#if !defined(MODULE)
|
|
@@ -63,10 +64,16 @@ struct dummy_slot {
|
|
struct list_head node;
|
|
struct list_head node;
|
|
struct hotplug_slot *slot;
|
|
struct hotplug_slot *slot;
|
|
struct pci_dev *dev;
|
|
struct pci_dev *dev;
|
|
|
|
+ struct work_struct remove_work;
|
|
|
|
+ unsigned long removed;
|
|
};
|
|
};
|
|
|
|
|
|
static int debug;
|
|
static int debug;
|
|
static LIST_HEAD(slot_list);
|
|
static LIST_HEAD(slot_list);
|
|
|
|
+static struct workqueue_struct *dummyphp_wq;
|
|
|
|
+
|
|
|
|
+static void pci_rescan_worker(struct work_struct *work);
|
|
|
|
+static DECLARE_WORK(pci_rescan_work, pci_rescan_worker);
|
|
|
|
|
|
static int enable_slot (struct hotplug_slot *slot);
|
|
static int enable_slot (struct hotplug_slot *slot);
|
|
static int disable_slot (struct hotplug_slot *slot);
|
|
static int disable_slot (struct hotplug_slot *slot);
|
|
@@ -109,7 +116,7 @@ static int add_slot(struct pci_dev *dev)
|
|
slot->name = &dev->dev.bus_id[0];
|
|
slot->name = &dev->dev.bus_id[0];
|
|
dbg("slot->name = %s\n", slot->name);
|
|
dbg("slot->name = %s\n", slot->name);
|
|
|
|
|
|
- dslot = kmalloc(sizeof(struct dummy_slot), GFP_KERNEL);
|
|
|
|
|
|
+ dslot = kzalloc(sizeof(struct dummy_slot), GFP_KERNEL);
|
|
if (!dslot)
|
|
if (!dslot)
|
|
goto error_info;
|
|
goto error_info;
|
|
|
|
|
|
@@ -164,6 +171,14 @@ static void remove_slot(struct dummy_slot *dslot)
|
|
err("Problem unregistering a slot %s\n", dslot->slot->name);
|
|
err("Problem unregistering a slot %s\n", dslot->slot->name);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/* called from the single-threaded workqueue handler to remove a slot */
|
|
|
|
+static void remove_slot_worker(struct work_struct *work)
|
|
|
|
+{
|
|
|
|
+ struct dummy_slot *dslot =
|
|
|
|
+ container_of(work, struct dummy_slot, remove_work);
|
|
|
|
+ remove_slot(dslot);
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* pci_rescan_slot - Rescan slot
|
|
* pci_rescan_slot - Rescan slot
|
|
* @temp: Device template. Should be set: bus and devfn.
|
|
* @temp: Device template. Should be set: bus and devfn.
|
|
@@ -267,11 +282,17 @@ static inline void pci_rescan(void) {
|
|
pci_rescan_buses(&pci_root_buses);
|
|
pci_rescan_buses(&pci_root_buses);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/* called from the single-threaded workqueue handler to rescan all pci buses */
|
|
|
|
+static void pci_rescan_worker(struct work_struct *work)
|
|
|
|
+{
|
|
|
|
+ pci_rescan();
|
|
|
|
+}
|
|
|
|
|
|
static int enable_slot(struct hotplug_slot *hotplug_slot)
|
|
static int enable_slot(struct hotplug_slot *hotplug_slot)
|
|
{
|
|
{
|
|
/* mis-use enable_slot for rescanning of the pci bus */
|
|
/* mis-use enable_slot for rescanning of the pci bus */
|
|
- pci_rescan();
|
|
|
|
|
|
+ cancel_work_sync(&pci_rescan_work);
|
|
|
|
+ queue_work(dummyphp_wq, &pci_rescan_work);
|
|
return -ENODEV;
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -306,6 +327,10 @@ static int disable_slot(struct hotplug_slot *slot)
|
|
err("Can't remove PCI devices with other PCI devices behind it yet.\n");
|
|
err("Can't remove PCI devices with other PCI devices behind it yet.\n");
|
|
return -ENODEV;
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
|
|
+ if (test_and_set_bit(0, &dslot->removed)) {
|
|
|
|
+ dbg("Slot already scheduled for removal\n");
|
|
|
|
+ return -ENODEV;
|
|
|
|
+ }
|
|
/* search for subfunctions and disable them first */
|
|
/* search for subfunctions and disable them first */
|
|
if (!(dslot->dev->devfn & 7)) {
|
|
if (!(dslot->dev->devfn & 7)) {
|
|
for (func = 1; func < 8; func++) {
|
|
for (func = 1; func < 8; func++) {
|
|
@@ -328,8 +353,9 @@ static int disable_slot(struct hotplug_slot *slot)
|
|
/* remove the device from the pci core */
|
|
/* remove the device from the pci core */
|
|
pci_remove_bus_device(dslot->dev);
|
|
pci_remove_bus_device(dslot->dev);
|
|
|
|
|
|
- /* blow away this sysfs entry and other parts. */
|
|
|
|
- remove_slot(dslot);
|
|
|
|
|
|
+ /* queue work item to blow away this sysfs entry and other parts. */
|
|
|
|
+ INIT_WORK(&dslot->remove_work, remove_slot_worker);
|
|
|
|
+ queue_work(dummyphp_wq, &dslot->remove_work);
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -340,6 +366,7 @@ static void cleanup_slots (void)
|
|
struct list_head *next;
|
|
struct list_head *next;
|
|
struct dummy_slot *dslot;
|
|
struct dummy_slot *dslot;
|
|
|
|
|
|
|
|
+ destroy_workqueue(dummyphp_wq);
|
|
list_for_each_safe (tmp, next, &slot_list) {
|
|
list_for_each_safe (tmp, next, &slot_list) {
|
|
dslot = list_entry (tmp, struct dummy_slot, node);
|
|
dslot = list_entry (tmp, struct dummy_slot, node);
|
|
remove_slot(dslot);
|
|
remove_slot(dslot);
|
|
@@ -351,6 +378,10 @@ static int __init dummyphp_init(void)
|
|
{
|
|
{
|
|
info(DRIVER_DESC "\n");
|
|
info(DRIVER_DESC "\n");
|
|
|
|
|
|
|
|
+ dummyphp_wq = create_singlethread_workqueue(MY_NAME);
|
|
|
|
+ if (!dummyphp_wq)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
return pci_scan_buses();
|
|
return pci_scan_buses();
|
|
}
|
|
}
|
|
|
|
|