Skip to content

Commit

Permalink
ACPI / hotplug / PCI: Use global PCI rescan-remove locking
Browse files Browse the repository at this point in the history
Multiple race conditions are possible between the ACPI-based PCI hotplug
(ACPIPHP) and the generic PCI bus rescan and device removal that can be
triggered via sysfs.

To avoid those race conditions make the ACPIPHP code use global PCI
rescan-remove locking.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
  • Loading branch information
Rafael J. Wysocki authored and Bjorn Helgaas committed Jan 14, 2014
1 parent 7a3bb55 commit 9217a98
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 7 deletions.
5 changes: 4 additions & 1 deletion drivers/pci/hotplug/acpiphp.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ struct acpiphp_bridge {

/* PCI-to-PCI bridge device */
struct pci_dev *pci_dev;

bool is_going_away;
};


Expand Down Expand Up @@ -150,6 +152,7 @@ struct acpiphp_attention_info
/* slot flags */

#define SLOT_ENABLED (0x00000001)
#define SLOT_IS_GOING_AWAY (0x00000002)

/* function flags */

Expand All @@ -169,7 +172,7 @@ void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *slot);
typedef int (*acpiphp_callback)(struct acpiphp_slot *slot, void *data);

int acpiphp_enable_slot(struct acpiphp_slot *slot);
int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot);
int acpiphp_disable_slot(struct acpiphp_slot *slot);
u8 acpiphp_get_power_status(struct acpiphp_slot *slot);
u8 acpiphp_get_attention_status(struct acpiphp_slot *slot);
u8 acpiphp_get_latch_status(struct acpiphp_slot *slot);
Expand Down
2 changes: 1 addition & 1 deletion drivers/pci/hotplug/acpiphp_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ static int disable_slot(struct hotplug_slot *hotplug_slot)
pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));

/* disable the specified slot */
return acpiphp_disable_and_eject_slot(slot->acpi_slot);
return acpiphp_disable_slot(slot->acpi_slot);
}


Expand Down
43 changes: 38 additions & 5 deletions drivers/pci/hotplug/acpiphp_glue.c
Original file line number Diff line number Diff line change
Expand Up @@ -430,13 +430,16 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge)
pr_err("failed to remove notify handler\n");
}
}
slot->flags |= SLOT_IS_GOING_AWAY;
if (slot->slot)
acpiphp_unregister_hotplug_slot(slot);
}

mutex_lock(&bridge_mutex);
list_del(&bridge->list);
mutex_unlock(&bridge_mutex);

bridge->is_going_away = true;
}

/**
Expand Down Expand Up @@ -736,6 +739,10 @@ static void acpiphp_check_bridge(struct acpiphp_bridge *bridge)
{
struct acpiphp_slot *slot;

/* Bail out if the bridge is going away. */
if (bridge->is_going_away)
return;

list_for_each_entry(slot, &bridge->slots, node) {
struct pci_bus *bus = slot->bus;
struct pci_dev *dev, *tmp;
Expand Down Expand Up @@ -805,6 +812,8 @@ void acpiphp_check_host_bridge(acpi_handle handle)
}
}

static int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot);

static void hotplug_event(acpi_handle handle, u32 type, void *data)
{
struct acpiphp_context *context = data;
Expand Down Expand Up @@ -834,6 +843,9 @@ static void hotplug_event(acpi_handle handle, u32 type, void *data)
} else {
struct acpiphp_slot *slot = func->slot;

if (slot->flags & SLOT_IS_GOING_AWAY)
break;

mutex_lock(&slot->crit_sect);
enable_slot(slot);
mutex_unlock(&slot->crit_sect);
Expand All @@ -849,6 +861,9 @@ static void hotplug_event(acpi_handle handle, u32 type, void *data)
struct acpiphp_slot *slot = func->slot;
int ret;

if (slot->flags & SLOT_IS_GOING_AWAY)
break;

/*
* Check if anything has changed in the slot and rescan
* from the parent if that's the case.
Expand Down Expand Up @@ -878,9 +893,11 @@ static void hotplug_event_work(void *data, u32 type)
acpi_handle handle = context->handle;

acpi_scan_lock_acquire();
pci_lock_rescan_remove();

hotplug_event(handle, type, context);

pci_unlock_rescan_remove();
acpi_scan_lock_release();
acpi_evaluate_hotplug_ost(handle, type, ACPI_OST_SC_SUCCESS, NULL);
put_bridge(context->func.parent);
Expand Down Expand Up @@ -1048,23 +1065,32 @@ void acpiphp_remove_slots(struct pci_bus *bus)
*/
int acpiphp_enable_slot(struct acpiphp_slot *slot)
{
pci_lock_rescan_remove();

if (slot->flags & SLOT_IS_GOING_AWAY)
return -ENODEV;

mutex_lock(&slot->crit_sect);
/* configure all functions */
if (!(slot->flags & SLOT_ENABLED))
enable_slot(slot);

mutex_unlock(&slot->crit_sect);

pci_unlock_rescan_remove();
return 0;
}

/**
* acpiphp_disable_and_eject_slot - power off and eject slot
* @slot: ACPI PHP slot
*/
int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot)
static int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot)
{
struct acpiphp_func *func;
int retval = 0;

if (slot->flags & SLOT_IS_GOING_AWAY)
return -ENODEV;

mutex_lock(&slot->crit_sect);

Expand All @@ -1082,9 +1108,18 @@ int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot)
}

mutex_unlock(&slot->crit_sect);
return retval;
return 0;
}

int acpiphp_disable_slot(struct acpiphp_slot *slot)
{
int ret;

pci_lock_rescan_remove();
ret = acpiphp_disable_and_eject_slot(slot);
pci_unlock_rescan_remove();
return ret;
}

/*
* slot enabled: 1
Expand All @@ -1095,7 +1130,6 @@ u8 acpiphp_get_power_status(struct acpiphp_slot *slot)
return (slot->flags & SLOT_ENABLED);
}


/*
* latch open: 1
* latch closed: 0
Expand All @@ -1105,7 +1139,6 @@ u8 acpiphp_get_latch_status(struct acpiphp_slot *slot)
return !(get_slot_status(slot) & ACPI_STA_DEVICE_UI);
}


/*
* adapter presence : 1
* absence : 0
Expand Down

0 comments on commit 9217a98

Please sign in to comment.