aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2014-01-10 09:24:41 -0500
committerBjorn Helgaas <bhelgaas@google.com>2014-01-14 14:14:25 -0500
commit9217a984671e8a7e38f1822eba754898066e2ae1 (patch)
treeb176cec1b70bbf1cbb07b361d86ff5c4ed312017 /drivers/pci
parent7a3bb55ebd1cb04f08dd68cf60af24b434f8cc2a (diff)
ACPI / hotplug / PCI: Use global PCI rescan-remove locking
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>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/hotplug/acpiphp.h5
-rw-r--r--drivers/pci/hotplug/acpiphp_core.c2
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c43
3 files changed, 43 insertions, 7 deletions
diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h
index 1592dbe4f904..b6162be4df40 100644
--- a/drivers/pci/hotplug/acpiphp.h
+++ b/drivers/pci/hotplug/acpiphp.h
@@ -77,6 +77,8 @@ struct acpiphp_bridge {
77 77
78 /* PCI-to-PCI bridge device */ 78 /* PCI-to-PCI bridge device */
79 struct pci_dev *pci_dev; 79 struct pci_dev *pci_dev;
80
81 bool is_going_away;
80}; 82};
81 83
82 84
@@ -150,6 +152,7 @@ struct acpiphp_attention_info
150/* slot flags */ 152/* slot flags */
151 153
152#define SLOT_ENABLED (0x00000001) 154#define SLOT_ENABLED (0x00000001)
155#define SLOT_IS_GOING_AWAY (0x00000002)
153 156
154/* function flags */ 157/* function flags */
155 158
@@ -169,7 +172,7 @@ void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *slot);
169typedef int (*acpiphp_callback)(struct acpiphp_slot *slot, void *data); 172typedef int (*acpiphp_callback)(struct acpiphp_slot *slot, void *data);
170 173
171int acpiphp_enable_slot(struct acpiphp_slot *slot); 174int acpiphp_enable_slot(struct acpiphp_slot *slot);
172int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot); 175int acpiphp_disable_slot(struct acpiphp_slot *slot);
173u8 acpiphp_get_power_status(struct acpiphp_slot *slot); 176u8 acpiphp_get_power_status(struct acpiphp_slot *slot);
174u8 acpiphp_get_attention_status(struct acpiphp_slot *slot); 177u8 acpiphp_get_attention_status(struct acpiphp_slot *slot);
175u8 acpiphp_get_latch_status(struct acpiphp_slot *slot); 178u8 acpiphp_get_latch_status(struct acpiphp_slot *slot);
diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c
index dca66bc44578..728c31f4c2c5 100644
--- a/drivers/pci/hotplug/acpiphp_core.c
+++ b/drivers/pci/hotplug/acpiphp_core.c
@@ -156,7 +156,7 @@ static int disable_slot(struct hotplug_slot *hotplug_slot)
156 pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot)); 156 pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
157 157
158 /* disable the specified slot */ 158 /* disable the specified slot */
159 return acpiphp_disable_and_eject_slot(slot->acpi_slot); 159 return acpiphp_disable_slot(slot->acpi_slot);
160} 160}
161 161
162 162
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index 1cf605f67673..641ba6761bd7 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -430,6 +430,7 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge)
430 pr_err("failed to remove notify handler\n"); 430 pr_err("failed to remove notify handler\n");
431 } 431 }
432 } 432 }
433 slot->flags |= SLOT_IS_GOING_AWAY;
433 if (slot->slot) 434 if (slot->slot)
434 acpiphp_unregister_hotplug_slot(slot); 435 acpiphp_unregister_hotplug_slot(slot);
435 } 436 }
@@ -437,6 +438,8 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge)
437 mutex_lock(&bridge_mutex); 438 mutex_lock(&bridge_mutex);
438 list_del(&bridge->list); 439 list_del(&bridge->list);
439 mutex_unlock(&bridge_mutex); 440 mutex_unlock(&bridge_mutex);
441
442 bridge->is_going_away = true;
440} 443}
441 444
442/** 445/**
@@ -736,6 +739,10 @@ static void acpiphp_check_bridge(struct acpiphp_bridge *bridge)
736{ 739{
737 struct acpiphp_slot *slot; 740 struct acpiphp_slot *slot;
738 741
742 /* Bail out if the bridge is going away. */
743 if (bridge->is_going_away)
744 return;
745
739 list_for_each_entry(slot, &bridge->slots, node) { 746 list_for_each_entry(slot, &bridge->slots, node) {
740 struct pci_bus *bus = slot->bus; 747 struct pci_bus *bus = slot->bus;
741 struct pci_dev *dev, *tmp; 748 struct pci_dev *dev, *tmp;
@@ -805,6 +812,8 @@ void acpiphp_check_host_bridge(acpi_handle handle)
805 } 812 }
806} 813}
807 814
815static int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot);
816
808static void hotplug_event(acpi_handle handle, u32 type, void *data) 817static void hotplug_event(acpi_handle handle, u32 type, void *data)
809{ 818{
810 struct acpiphp_context *context = data; 819 struct acpiphp_context *context = data;
@@ -834,6 +843,9 @@ static void hotplug_event(acpi_handle handle, u32 type, void *data)
834 } else { 843 } else {
835 struct acpiphp_slot *slot = func->slot; 844 struct acpiphp_slot *slot = func->slot;
836 845
846 if (slot->flags & SLOT_IS_GOING_AWAY)
847 break;
848
837 mutex_lock(&slot->crit_sect); 849 mutex_lock(&slot->crit_sect);
838 enable_slot(slot); 850 enable_slot(slot);
839 mutex_unlock(&slot->crit_sect); 851 mutex_unlock(&slot->crit_sect);
@@ -849,6 +861,9 @@ static void hotplug_event(acpi_handle handle, u32 type, void *data)
849 struct acpiphp_slot *slot = func->slot; 861 struct acpiphp_slot *slot = func->slot;
850 int ret; 862 int ret;
851 863
864 if (slot->flags & SLOT_IS_GOING_AWAY)
865 break;
866
852 /* 867 /*
853 * Check if anything has changed in the slot and rescan 868 * Check if anything has changed in the slot and rescan
854 * from the parent if that's the case. 869 * from the parent if that's the case.
@@ -878,9 +893,11 @@ static void hotplug_event_work(void *data, u32 type)
878 acpi_handle handle = context->handle; 893 acpi_handle handle = context->handle;
879 894
880 acpi_scan_lock_acquire(); 895 acpi_scan_lock_acquire();
896 pci_lock_rescan_remove();
881 897
882 hotplug_event(handle, type, context); 898 hotplug_event(handle, type, context);
883 899
900 pci_unlock_rescan_remove();
884 acpi_scan_lock_release(); 901 acpi_scan_lock_release();
885 acpi_evaluate_hotplug_ost(handle, type, ACPI_OST_SC_SUCCESS, NULL); 902 acpi_evaluate_hotplug_ost(handle, type, ACPI_OST_SC_SUCCESS, NULL);
886 put_bridge(context->func.parent); 903 put_bridge(context->func.parent);
@@ -1048,12 +1065,19 @@ void acpiphp_remove_slots(struct pci_bus *bus)
1048 */ 1065 */
1049int acpiphp_enable_slot(struct acpiphp_slot *slot) 1066int acpiphp_enable_slot(struct acpiphp_slot *slot)
1050{ 1067{
1068 pci_lock_rescan_remove();
1069
1070 if (slot->flags & SLOT_IS_GOING_AWAY)
1071 return -ENODEV;
1072
1051 mutex_lock(&slot->crit_sect); 1073 mutex_lock(&slot->crit_sect);
1052 /* configure all functions */ 1074 /* configure all functions */
1053 if (!(slot->flags & SLOT_ENABLED)) 1075 if (!(slot->flags & SLOT_ENABLED))
1054 enable_slot(slot); 1076 enable_slot(slot);
1055 1077
1056 mutex_unlock(&slot->crit_sect); 1078 mutex_unlock(&slot->crit_sect);
1079
1080 pci_unlock_rescan_remove();
1057 return 0; 1081 return 0;
1058} 1082}
1059 1083
@@ -1061,10 +1085,12 @@ int acpiphp_enable_slot(struct acpiphp_slot *slot)
1061 * acpiphp_disable_and_eject_slot - power off and eject slot 1085 * acpiphp_disable_and_eject_slot - power off and eject slot
1062 * @slot: ACPI PHP slot 1086 * @slot: ACPI PHP slot
1063 */ 1087 */
1064int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot) 1088static int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot)
1065{ 1089{
1066 struct acpiphp_func *func; 1090 struct acpiphp_func *func;
1067 int retval = 0; 1091
1092 if (slot->flags & SLOT_IS_GOING_AWAY)
1093 return -ENODEV;
1068 1094
1069 mutex_lock(&slot->crit_sect); 1095 mutex_lock(&slot->crit_sect);
1070 1096
@@ -1082,9 +1108,18 @@ int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot)
1082 } 1108 }
1083 1109
1084 mutex_unlock(&slot->crit_sect); 1110 mutex_unlock(&slot->crit_sect);
1085 return retval; 1111 return 0;
1086} 1112}
1087 1113
1114int acpiphp_disable_slot(struct acpiphp_slot *slot)
1115{
1116 int ret;
1117
1118 pci_lock_rescan_remove();
1119 ret = acpiphp_disable_and_eject_slot(slot);
1120 pci_unlock_rescan_remove();
1121 return ret;
1122}
1088 1123
1089/* 1124/*
1090 * slot enabled: 1 1125 * slot enabled: 1
@@ -1095,7 +1130,6 @@ u8 acpiphp_get_power_status(struct acpiphp_slot *slot)
1095 return (slot->flags & SLOT_ENABLED); 1130 return (slot->flags & SLOT_ENABLED);
1096} 1131}
1097 1132
1098
1099/* 1133/*
1100 * latch open: 1 1134 * latch open: 1
1101 * latch closed: 0 1135 * latch closed: 0
@@ -1105,7 +1139,6 @@ u8 acpiphp_get_latch_status(struct acpiphp_slot *slot)
1105 return !(get_slot_status(slot) & ACPI_STA_DEVICE_UI); 1139 return !(get_slot_status(slot) & ACPI_STA_DEVICE_UI);
1106} 1140}
1107 1141
1108
1109/* 1142/*
1110 * adapter presence : 1 1143 * adapter presence : 1
1111 * absence : 0 1144 * absence : 0