diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-02-06 11:31:37 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-02-06 11:31:37 -0500 |
commit | 3c2cc7ff9e2522e42468f8e81a7277be386c5ec4 (patch) | |
tree | e8a8405373896d0ff02a7d41a244fa81547c6f73 /drivers/pci | |
parent | e525506fcb67a9bbd94f01eac84af802139004eb (diff) |
ACPI / hotplug / PCI: Consolidate ACPIPHP with ACPI core hotplug
The ACPI-based PCI hotplug (ACPIPHP) code currently attaches its
hotplug context objects directly to ACPI namespace nodes representing
hotplug devices. However, after recent changes causing struct
acpi_device to be created for every namespace node representing a
device (regardless of its status), that is not necessary any more.
Moreover, it's vulnerable to the theoretical issue that the ACPI
handle passed in the context between handle_hotplug_event() and
hotplug_event_work() may become invalid in the meantime (as a
result of a concurrent table unload).
In principle, this issue might be addressed by adding a non-empty
release handler for ACPIPHP hotplug context objects analogous to
acpi_scan_drop_device(), but that would duplicate the code in that
function and in acpi_device_del_work_fn(). For this reason, it's
better to modify ACPIPHP to attach its device hotplug contexts to
struct device objects representing hotplug devices and make it
use acpi_hotplug_notify_cb() as its notify handler. At the same
time, acpi_device_hotplug() can be modified to dispatch the new
.hp.event() callback pointing to acpiphp_hotplug_event() from ACPI
device objects associated with PCI devices or use the generic
ACPI device hotplug code for device objects with matching scan
handlers.
This allows the existing code duplication between ACPIPHP and the
ACPI core to be reduced too and makes further ACPI-based device
hotplug consolidation possible.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/hotplug/acpiphp.h | 9 | ||||
-rw-r--r-- | drivers/pci/hotplug/acpiphp_glue.c | 168 |
2 files changed, 45 insertions, 132 deletions
diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index 373c7aa3b4a6..d7c1fc9712ad 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h | |||
@@ -116,12 +116,17 @@ struct acpiphp_func { | |||
116 | }; | 116 | }; |
117 | 117 | ||
118 | struct acpiphp_context { | 118 | struct acpiphp_context { |
119 | struct acpi_hotplug_context hp; | ||
119 | struct acpiphp_func func; | 120 | struct acpiphp_func func; |
120 | struct acpi_device *adev; | ||
121 | struct acpiphp_bridge *bridge; | 121 | struct acpiphp_bridge *bridge; |
122 | unsigned int refcount; | 122 | unsigned int refcount; |
123 | }; | 123 | }; |
124 | 124 | ||
125 | static inline struct acpiphp_context *to_acpiphp_context(struct acpi_hotplug_context *hp) | ||
126 | { | ||
127 | return container_of(hp, struct acpiphp_context, hp); | ||
128 | } | ||
129 | |||
125 | static inline struct acpiphp_context *func_to_context(struct acpiphp_func *func) | 130 | static inline struct acpiphp_context *func_to_context(struct acpiphp_func *func) |
126 | { | 131 | { |
127 | return container_of(func, struct acpiphp_context, func); | 132 | return container_of(func, struct acpiphp_context, func); |
@@ -129,7 +134,7 @@ static inline struct acpiphp_context *func_to_context(struct acpiphp_func *func) | |||
129 | 134 | ||
130 | static inline struct acpi_device *func_to_acpi_device(struct acpiphp_func *func) | 135 | static inline struct acpi_device *func_to_acpi_device(struct acpiphp_func *func) |
131 | { | 136 | { |
132 | return func_to_context(func)->adev; | 137 | return func_to_context(func)->hp.self; |
133 | } | 138 | } |
134 | 139 | ||
135 | static inline acpi_handle func_to_handle(struct acpiphp_func *func) | 140 | static inline acpi_handle func_to_handle(struct acpiphp_func *func) |
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 8139a4b0d389..7c498d663eb3 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c | |||
@@ -59,17 +59,12 @@ | |||
59 | static LIST_HEAD(bridge_list); | 59 | static LIST_HEAD(bridge_list); |
60 | static DEFINE_MUTEX(bridge_mutex); | 60 | static DEFINE_MUTEX(bridge_mutex); |
61 | 61 | ||
62 | static void handle_hotplug_event(acpi_handle handle, u32 type, void *data); | 62 | static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type); |
63 | static void acpiphp_sanitize_bus(struct pci_bus *bus); | 63 | static void acpiphp_sanitize_bus(struct pci_bus *bus); |
64 | static void acpiphp_set_hpp_values(struct pci_bus *bus); | 64 | static void acpiphp_set_hpp_values(struct pci_bus *bus); |
65 | static void hotplug_event(u32 type, struct acpiphp_context *context); | 65 | static void hotplug_event(u32 type, struct acpiphp_context *context); |
66 | static void free_bridge(struct kref *kref); | 66 | static void free_bridge(struct kref *kref); |
67 | 67 | ||
68 | static void acpiphp_context_handler(acpi_handle handle, void *context) | ||
69 | { | ||
70 | /* Intentionally empty. */ | ||
71 | } | ||
72 | |||
73 | /** | 68 | /** |
74 | * acpiphp_init_context - Create hotplug context and grab a reference to it. | 69 | * acpiphp_init_context - Create hotplug context and grab a reference to it. |
75 | * @adev: ACPI device object to create the context for. | 70 | * @adev: ACPI device object to create the context for. |
@@ -79,39 +74,31 @@ static void acpiphp_context_handler(acpi_handle handle, void *context) | |||
79 | static struct acpiphp_context *acpiphp_init_context(struct acpi_device *adev) | 74 | static struct acpiphp_context *acpiphp_init_context(struct acpi_device *adev) |
80 | { | 75 | { |
81 | struct acpiphp_context *context; | 76 | struct acpiphp_context *context; |
82 | acpi_status status; | ||
83 | 77 | ||
84 | context = kzalloc(sizeof(*context), GFP_KERNEL); | 78 | context = kzalloc(sizeof(*context), GFP_KERNEL); |
85 | if (!context) | 79 | if (!context) |
86 | return NULL; | 80 | return NULL; |
87 | 81 | ||
88 | context->adev = adev; | ||
89 | context->refcount = 1; | 82 | context->refcount = 1; |
90 | status = acpi_attach_data(adev->handle, acpiphp_context_handler, context); | 83 | acpi_set_hp_context(adev, &context->hp, acpiphp_hotplug_event); |
91 | if (ACPI_FAILURE(status)) { | ||
92 | kfree(context); | ||
93 | return NULL; | ||
94 | } | ||
95 | return context; | 84 | return context; |
96 | } | 85 | } |
97 | 86 | ||
98 | /** | 87 | /** |
99 | * acpiphp_get_context - Get hotplug context and grab a reference to it. | 88 | * acpiphp_get_context - Get hotplug context and grab a reference to it. |
100 | * @handle: ACPI object handle to get the context for. | 89 | * @adev: ACPI device object to get the context for. |
101 | * | 90 | * |
102 | * Call under acpi_hp_context_lock. | 91 | * Call under acpi_hp_context_lock. |
103 | */ | 92 | */ |
104 | static struct acpiphp_context *acpiphp_get_context(acpi_handle handle) | 93 | static struct acpiphp_context *acpiphp_get_context(struct acpi_device *adev) |
105 | { | 94 | { |
106 | struct acpiphp_context *context = NULL; | 95 | struct acpiphp_context *context; |
107 | acpi_status status; | ||
108 | void *data; | ||
109 | 96 | ||
110 | status = acpi_get_data(handle, acpiphp_context_handler, &data); | 97 | if (!adev->hp) |
111 | if (ACPI_SUCCESS(status)) { | 98 | return NULL; |
112 | context = data; | 99 | |
113 | context->refcount++; | 100 | context = to_acpiphp_context(adev->hp); |
114 | } | 101 | context->refcount++; |
115 | return context; | 102 | return context; |
116 | } | 103 | } |
117 | 104 | ||
@@ -129,7 +116,7 @@ static void acpiphp_put_context(struct acpiphp_context *context) | |||
129 | return; | 116 | return; |
130 | 117 | ||
131 | WARN_ON(context->bridge); | 118 | WARN_ON(context->bridge); |
132 | acpi_detach_data(context->adev->handle, acpiphp_context_handler); | 119 | context->hp.self->hp = NULL; |
133 | kfree(context); | 120 | kfree(context); |
134 | } | 121 | } |
135 | 122 | ||
@@ -211,22 +198,13 @@ static void post_dock_fixups(acpi_handle not_used, u32 event, void *data) | |||
211 | 198 | ||
212 | static void dock_event(acpi_handle handle, u32 type, void *data) | 199 | static void dock_event(acpi_handle handle, u32 type, void *data) |
213 | { | 200 | { |
214 | struct acpiphp_context *context; | 201 | struct acpi_device *adev; |
215 | 202 | ||
216 | acpi_lock_hp_context(); | 203 | adev = acpi_bus_get_acpi_device(handle); |
217 | context = acpiphp_get_context(handle); | 204 | if (adev) { |
218 | if (!context || WARN_ON(context->adev->handle != handle) | 205 | acpiphp_hotplug_event(adev, type); |
219 | || context->func.parent->is_going_away) { | 206 | acpi_bus_put_acpi_device(adev); |
220 | acpi_unlock_hp_context(); | ||
221 | return; | ||
222 | } | 207 | } |
223 | get_bridge(context->func.parent); | ||
224 | acpiphp_put_context(context); | ||
225 | acpi_unlock_hp_context(); | ||
226 | |||
227 | hotplug_event(type, context); | ||
228 | |||
229 | put_bridge(context->func.parent); | ||
230 | } | 208 | } |
231 | 209 | ||
232 | static const struct acpi_dock_ops acpiphp_dock_ops = { | 210 | static const struct acpi_dock_ops acpiphp_dock_ops = { |
@@ -397,25 +375,23 @@ static acpi_status register_slot(acpi_handle handle, u32 lvl, void *data, | |||
397 | } | 375 | } |
398 | 376 | ||
399 | /* install notify handler */ | 377 | /* install notify handler */ |
400 | if (!(newfunc->flags & FUNC_HAS_DCK)) { | 378 | if (!(newfunc->flags & FUNC_HAS_DCK)) |
401 | status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, | 379 | acpi_install_hotplug_notify_handler(handle, NULL); |
402 | handle_hotplug_event, | ||
403 | context); | ||
404 | if (ACPI_FAILURE(status)) | ||
405 | acpi_handle_err(handle, | ||
406 | "failed to install notify handler\n"); | ||
407 | } | ||
408 | 380 | ||
409 | return AE_OK; | 381 | return AE_OK; |
410 | } | 382 | } |
411 | 383 | ||
412 | static struct acpiphp_bridge *acpiphp_handle_to_bridge(acpi_handle handle) | 384 | static struct acpiphp_bridge *acpiphp_handle_to_bridge(acpi_handle handle) |
413 | { | 385 | { |
386 | struct acpi_device *adev = acpi_bus_get_acpi_device(handle); | ||
414 | struct acpiphp_context *context; | 387 | struct acpiphp_context *context; |
415 | struct acpiphp_bridge *bridge = NULL; | 388 | struct acpiphp_bridge *bridge = NULL; |
416 | 389 | ||
390 | if (!adev) | ||
391 | return NULL; | ||
392 | |||
417 | acpi_lock_hp_context(); | 393 | acpi_lock_hp_context(); |
418 | context = acpiphp_get_context(handle); | 394 | context = acpiphp_get_context(adev); |
419 | if (context) { | 395 | if (context) { |
420 | bridge = context->bridge; | 396 | bridge = context->bridge; |
421 | if (bridge) | 397 | if (bridge) |
@@ -424,6 +400,7 @@ static struct acpiphp_bridge *acpiphp_handle_to_bridge(acpi_handle handle) | |||
424 | acpiphp_put_context(context); | 400 | acpiphp_put_context(context); |
425 | } | 401 | } |
426 | acpi_unlock_hp_context(); | 402 | acpi_unlock_hp_context(); |
403 | acpi_bus_put_acpi_device(adev); | ||
427 | return bridge; | 404 | return bridge; |
428 | } | 405 | } |
429 | 406 | ||
@@ -431,7 +408,6 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) | |||
431 | { | 408 | { |
432 | struct acpiphp_slot *slot; | 409 | struct acpiphp_slot *slot; |
433 | struct acpiphp_func *func; | 410 | struct acpiphp_func *func; |
434 | acpi_status status; | ||
435 | 411 | ||
436 | list_for_each_entry(slot, &bridge->slots, node) { | 412 | list_for_each_entry(slot, &bridge->slots, node) { |
437 | list_for_each_entry(func, &slot->funcs, sibling) { | 413 | list_for_each_entry(func, &slot->funcs, sibling) { |
@@ -440,13 +416,8 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) | |||
440 | if (is_dock_device(handle)) | 416 | if (is_dock_device(handle)) |
441 | unregister_hotplug_dock_device(handle); | 417 | unregister_hotplug_dock_device(handle); |
442 | 418 | ||
443 | if (!(func->flags & FUNC_HAS_DCK)) { | 419 | if (!(func->flags & FUNC_HAS_DCK)) |
444 | status = acpi_remove_notify_handler(handle, | 420 | acpi_remove_hotplug_notify_handler(handle); |
445 | ACPI_SYSTEM_NOTIFY, | ||
446 | handle_hotplug_event); | ||
447 | if (ACPI_FAILURE(status)) | ||
448 | pr_err("failed to remove notify handler\n"); | ||
449 | } | ||
450 | } | 421 | } |
451 | slot->flags |= SLOT_IS_GOING_AWAY; | 422 | slot->flags |= SLOT_IS_GOING_AWAY; |
452 | if (slot->slot) | 423 | if (slot->slot) |
@@ -814,7 +785,7 @@ static int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot); | |||
814 | 785 | ||
815 | static void hotplug_event(u32 type, struct acpiphp_context *context) | 786 | static void hotplug_event(u32 type, struct acpiphp_context *context) |
816 | { | 787 | { |
817 | acpi_handle handle = context->adev->handle; | 788 | acpi_handle handle = context->hp.self->handle; |
818 | struct acpiphp_func *func = &context->func; | 789 | struct acpiphp_func *func = &context->func; |
819 | struct acpiphp_slot *slot = func->slot; | 790 | struct acpiphp_slot *slot = func->slot; |
820 | struct acpiphp_bridge *bridge; | 791 | struct acpiphp_bridge *bridge; |
@@ -866,87 +837,24 @@ static void hotplug_event(u32 type, struct acpiphp_context *context) | |||
866 | put_bridge(bridge); | 837 | put_bridge(bridge); |
867 | } | 838 | } |
868 | 839 | ||
869 | static void hotplug_event_work(void *data, u32 type) | 840 | static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type) |
870 | { | ||
871 | struct acpiphp_context *context = data; | ||
872 | |||
873 | acpi_scan_lock_acquire(); | ||
874 | |||
875 | hotplug_event(type, context); | ||
876 | |||
877 | acpi_scan_lock_release(); | ||
878 | acpi_evaluate_hotplug_ost(context->adev->handle, type, | ||
879 | ACPI_OST_SC_SUCCESS, NULL); | ||
880 | put_bridge(context->func.parent); | ||
881 | } | ||
882 | |||
883 | /** | ||
884 | * handle_hotplug_event - handle ACPI hotplug event | ||
885 | * @handle: Notify()'ed acpi_handle | ||
886 | * @type: Notify code | ||
887 | * @data: pointer to acpiphp_context structure | ||
888 | * | ||
889 | * Handles ACPI event notification on slots. | ||
890 | */ | ||
891 | static void handle_hotplug_event(acpi_handle handle, u32 type, void *data) | ||
892 | { | 841 | { |
893 | struct acpiphp_context *context; | 842 | struct acpiphp_context *context; |
894 | u32 ost_code = ACPI_OST_SC_SUCCESS; | ||
895 | acpi_status status; | ||
896 | |||
897 | switch (type) { | ||
898 | case ACPI_NOTIFY_BUS_CHECK: | ||
899 | case ACPI_NOTIFY_DEVICE_CHECK: | ||
900 | break; | ||
901 | case ACPI_NOTIFY_EJECT_REQUEST: | ||
902 | ost_code = ACPI_OST_SC_EJECT_IN_PROGRESS; | ||
903 | acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL); | ||
904 | break; | ||
905 | |||
906 | case ACPI_NOTIFY_DEVICE_WAKE: | ||
907 | return; | ||
908 | |||
909 | case ACPI_NOTIFY_FREQUENCY_MISMATCH: | ||
910 | acpi_handle_err(handle, "Device cannot be configured due " | ||
911 | "to a frequency mismatch\n"); | ||
912 | goto out; | ||
913 | |||
914 | case ACPI_NOTIFY_BUS_MODE_MISMATCH: | ||
915 | acpi_handle_err(handle, "Device cannot be configured due " | ||
916 | "to a bus mode mismatch\n"); | ||
917 | goto out; | ||
918 | |||
919 | case ACPI_NOTIFY_POWER_FAULT: | ||
920 | acpi_handle_err(handle, "Device has suffered a power fault\n"); | ||
921 | goto out; | ||
922 | |||
923 | default: | ||
924 | acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type); | ||
925 | ost_code = ACPI_OST_SC_UNRECOGNIZED_NOTIFY; | ||
926 | goto out; | ||
927 | } | ||
928 | 843 | ||
929 | acpi_lock_hp_context(); | 844 | acpi_lock_hp_context(); |
930 | context = acpiphp_get_context(handle); | 845 | context = acpiphp_get_context(adev); |
931 | if (!context || WARN_ON(context->adev->handle != handle) | 846 | if (!context || context->func.parent->is_going_away) { |
932 | || context->func.parent->is_going_away) | ||
933 | goto err_out; | ||
934 | |||
935 | get_bridge(context->func.parent); | ||
936 | acpiphp_put_context(context); | ||
937 | status = acpi_hotplug_execute(hotplug_event_work, context, type); | ||
938 | if (ACPI_SUCCESS(status)) { | ||
939 | acpi_unlock_hp_context(); | 847 | acpi_unlock_hp_context(); |
940 | return; | 848 | return -ENODATA; |
941 | } | 849 | } |
942 | put_bridge(context->func.parent); | 850 | get_bridge(context->func.parent); |
943 | 851 | acpiphp_put_context(context); | |
944 | err_out: | ||
945 | acpi_unlock_hp_context(); | 852 | acpi_unlock_hp_context(); |
946 | ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; | ||
947 | 853 | ||
948 | out: | 854 | hotplug_event(type, context); |
949 | acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL); | 855 | |
856 | put_bridge(context->func.parent); | ||
857 | return 0; | ||
950 | } | 858 | } |
951 | 859 | ||
952 | /** | 860 | /** |
@@ -999,7 +907,7 @@ void acpiphp_enumerate_slots(struct pci_bus *bus) | |||
999 | * bridge is not interesting to us either. | 907 | * bridge is not interesting to us either. |
1000 | */ | 908 | */ |
1001 | acpi_lock_hp_context(); | 909 | acpi_lock_hp_context(); |
1002 | context = acpiphp_get_context(handle); | 910 | context = acpiphp_get_context(adev); |
1003 | if (!context) { | 911 | if (!context) { |
1004 | acpi_unlock_hp_context(); | 912 | acpi_unlock_hp_context(); |
1005 | put_device(&bus->dev); | 913 | put_device(&bus->dev); |