Skip to content

Commit

Permalink
pciehp: fix interrupt initialization
Browse files Browse the repository at this point in the history
Current pciehp driver's intialization sequence is as follows:

(1) initialize controller data structure
(2) install interrupt handler
(3) enable software notification
(4) initialize controller specific slot data structure
(5) initialize generic slot data structure and register it to pci hotplug core

The interrupt handler of pciehp assumes that controller specific slot
data structure is already initialized. However, it is installed at (2)
before initializing controller specific slot data structure at
(4). Because of this, pciehp driver cannot handle the following cases
properly.

- If devices that shares IRQ with pciehp raise interrupts between (2) and (4).
- If hotplug events (e.g. MRL open) happen between (3) and (4).

We already have a workaround for this problem ("pciehp: fix NULL
dereference in interrupt handler: dbd79ae).
But we still need fundamental fix.

This patch fix the problem by changing the initilization sequence as follows:

(1) initialize controller data structure
(2) initialize controller specific slot data structure
(3) install interrupt handler
(4) enable software notification
(5) initialize generic slot data structure and register it to pci hotplug core

Signed-off-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Acked-by: Alex Chiang <achiang@hp.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
  • Loading branch information
Kenji Kaneshige authored and Jesse Barnes committed Jun 27, 2008
1 parent e4ec7a0 commit c4635eb
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 136 deletions.
5 changes: 3 additions & 2 deletions drivers/pci/hotplug/pciehp.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ extern int pciehp_poll_mode;
extern int pciehp_poll_time;
extern int pciehp_debug;
extern int pciehp_force;
extern int pciehp_slot_with_bus;
extern struct workqueue_struct *pciehp_wq;

#define dbg(format, arg...) \
Expand Down Expand Up @@ -156,10 +157,10 @@ extern u8 pciehp_handle_power_fault(struct slot *p_slot);
extern int pciehp_configure_device(struct slot *p_slot);
extern int pciehp_unconfigure_device(struct slot *p_slot);
extern void pciehp_queue_pushbutton_work(struct work_struct *work);
int pcie_init(struct controller *ctrl, struct pcie_device *dev);
struct controller *pcie_init(struct pcie_device *dev);
int pciehp_enable_slot(struct slot *p_slot);
int pciehp_disable_slot(struct slot *p_slot);
int pcie_init_hardware_part2(struct controller *ctrl, struct pcie_device *dev);
int pcie_enable_notification(struct controller *ctrl);

static inline struct slot *pciehp_find_slot(struct controller *ctrl, u8 device)
{
Expand Down
80 changes: 12 additions & 68 deletions drivers/pci/hotplug/pciehp_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ int pciehp_debug;
int pciehp_poll_mode;
int pciehp_poll_time;
int pciehp_force;
static int pciehp_slot_with_bus;
int pciehp_slot_with_bus;
struct workqueue_struct *pciehp_wq;

#define DRIVER_VERSION "0.4"
Expand Down Expand Up @@ -183,23 +183,10 @@ static struct hotplug_slot_attribute hotplug_slot_attr_lock = {
*/
static void release_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;

dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);

kfree(slot->hotplug_slot->info);
kfree(slot->hotplug_slot);
kfree(slot);
}

static void make_slot_name(struct slot *slot)
{
if (pciehp_slot_with_bus)
snprintf(slot->hotplug_slot->name, SLOT_NAME_SIZE, "%04d_%04d",
slot->bus, slot->number);
else
snprintf(slot->hotplug_slot->name, SLOT_NAME_SIZE, "%d",
slot->number);
kfree(hotplug_slot->info);
kfree(hotplug_slot);
}

static int init_slots(struct controller *ctrl)
Expand All @@ -208,44 +195,27 @@ static int init_slots(struct controller *ctrl)
struct hotplug_slot *hotplug_slot;
struct hotplug_slot_info *info;
int retval = -ENOMEM;
int i;

for (i = 0; i < ctrl->num_slots; i++) {
slot = kzalloc(sizeof(*slot), GFP_KERNEL);
if (!slot)
goto error;

list_for_each_entry(slot, &ctrl->slot_list, slot_list) {
hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
if (!hotplug_slot)
goto error_slot;
slot->hotplug_slot = hotplug_slot;
goto error;

info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info)
goto error_hpslot;
hotplug_slot->info = info;

hotplug_slot->name = slot->name;

slot->hp_slot = i;
slot->ctrl = ctrl;
slot->bus = ctrl->pci_dev->subordinate->number;
slot->device = ctrl->slot_device_offset + i;
slot->hpc_ops = ctrl->hpc_ops;
slot->number = ctrl->first_slot;
mutex_init(&slot->lock);
INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work);

/* register this slot with the hotplug pci core */
hotplug_slot->info = info;
hotplug_slot->name = slot->name;
hotplug_slot->private = slot;
hotplug_slot->release = &release_slot;
make_slot_name(slot);
hotplug_slot->ops = &pciehp_hotplug_slot_ops;

get_power_status(hotplug_slot, &info->power_status);
get_attention_status(hotplug_slot, &info->attention_status);
get_latch_status(hotplug_slot, &info->latch_status);
get_adapter_status(hotplug_slot, &info->adapter_status);
slot->hotplug_slot = hotplug_slot;

dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x "
"slot_device_offset=%x\n", slot->bus, slot->device,
Expand All @@ -271,36 +241,25 @@ static int init_slots(struct controller *ctrl)
goto error_info;
}
}

list_add(&slot->slot_list, &ctrl->slot_list);
}

return 0;
error_info:
kfree(info);
error_hpslot:
kfree(hotplug_slot);
error_slot:
kfree(slot);
error:
return retval;
}

static void cleanup_slots(struct controller *ctrl)
{
struct list_head *tmp;
struct list_head *next;
struct slot *slot;

list_for_each_safe(tmp, next, &ctrl->slot_list) {
slot = list_entry(tmp, struct slot, slot_list);
list_del(&slot->slot_list);
list_for_each_entry(slot, &ctrl->slot_list, slot_list) {
if (EMI(ctrl))
sysfs_remove_file(&slot->hotplug_slot->pci_slot->kobj,
&hotplug_slot_attr_lock.attr);
cancel_delayed_work(&slot->work);
flush_scheduled_work();
flush_workqueue(pciehp_wq);
pci_hp_deregister(slot->hotplug_slot);
}
}
Expand Down Expand Up @@ -441,25 +400,13 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_
else if (pciehp_get_hp_hw_control_from_firmware(pdev))
goto err_out_none;

ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
ctrl = pcie_init(dev);
if (!ctrl) {
err("%s : out of memory\n", __func__);
goto err_out_none;
}
INIT_LIST_HEAD(&ctrl->slot_list);

rc = pcie_init(ctrl, dev);
if (rc) {
dbg("%s: controller initialization failed\n", PCIE_MODULE_NAME);
goto err_out_free_ctrl;
goto err_out_none;
}

pci_set_drvdata(pdev, ctrl);

dbg("%s: ctrl bus=0x%x, device=%x, function=%x, irq=%x\n",
__func__, pdev->bus->number, PCI_SLOT(pdev->devfn),
PCI_FUNC(pdev->devfn), pdev->irq);

/* Setup the slot information structures */
rc = init_slots(ctrl);
if (rc) {
Expand Down Expand Up @@ -492,8 +439,6 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_
cleanup_slots(ctrl);
err_out_release_ctlr:
ctrl->hpc_ops->release_ctlr(ctrl);
err_out_free_ctrl:
kfree(ctrl);
err_out_none:
return -ENODEV;
}
Expand All @@ -505,7 +450,6 @@ static void pciehp_remove (struct pcie_device *dev)

cleanup_slots(ctrl);
ctrl->hpc_ops->release_ctlr(ctrl);
kfree(ctrl);
}

#ifdef CONFIG_PM
Expand All @@ -525,7 +469,7 @@ static int pciehp_resume (struct pcie_device *dev)
u8 status;

/* reinitialize the chipset's event detection logic */
pcie_init_hardware_part2(ctrl, dev);
pcie_enable_notification(ctrl);

t_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset);

Expand Down
Loading

0 comments on commit c4635eb

Please sign in to comment.