ACPI: New eject flow to remove devices cautiously
Subsystems (3)
v2 cover ACPI: New eject flow to remove devices cautiously
Currently there are two ways to handle ACPI device ejection. When an eject event happens on a container, the kernel just sends KOBJ_CHANGE to userland and userland should handle offline operation. For other device types, acpi_scan_try_to_offline() is called and it tries to put target device(s) offline and then removes all nodes once they are all offline.
However we found that sometimes applications could intensively access resources on ejectable devices therefore they could have risk if ejection suddenly happens and removes devices without any notification. In stead of executing the offline callbakcs directly, we want to introduce a new approach, which sends change events to notify all target nodes beforehand and hands over offline handling to userland so that userland can have a chance to schedule an offline task based on current workload. The online function to recover from failure is also changed, it follows the same approach to send change events rather than putting devices online directly , which means userland will also need to take care of online handling.
To ensure that eject function can work properly since normal users might not have their own offline/online handling, we will submit a generic udev rule to systemd upstream as default in order to deal with change events and take [offline/online] action accordingly. But the Hot-Removing part still remains so the hotplug function can run to it once target nodes are all offline.
To easily monitor eject status and start over an eject process, there's a status trace mechanism in this eject flow, which helps to count current online devices under the ejectable target, and it can reschedule an eject event when all nodes within the device tree have been put offline.
v2:
- device_sysfs: Add descriptions in /Document/ABI/testing/sysfs-bus-acpi
- device_sysfs: Replace the declartion with DEVICE_ATTR_RW and add cancel option in eject_store.
- scan: Add a retry mechanism when userspace fail to put device offline.
- scan: Add ready-to-remove state.
Chester Lin (3):
ACPI / hotplug: Send change events for offline/online requests when
eject is triggered
ACPI / hotplug: Eject status trace and auto-remove approach ACPI / device_sysfs: Add eject_show and add a cancel option in
eject_store
Documentation/ABI/testing/sysfs-bus-acpi | 9 +- drivers/acpi/container.c | 2 +- drivers/acpi/device_sysfs.c | 94 ++++++- drivers/acpi/glue.c | 146 +++++++++++ drivers/acpi/internal.h | 34 ++- drivers/acpi/scan.c | 318 +++++++++++++++++------ drivers/base/core.c | 4 + include/acpi/acpi_bus.h | 3 +- include/linux/acpi.h | 6 + 9 files changed, 523 insertions(+), 93 deletions(-)
Signature · 1 line
2.20.1
RFC PATCH v2 1/3 ACPI / hotplug: Send change events for offline/online requests when eject is triggered
Here we change offline/online handling in device hotplug by sending change events to userland as notification so that userland can have control and determine when will be a good time to put them offline/online based on current workload. In this approach the real offline/online opertions are handed over to userland so that userland can have more time to prepare before any device change actually happens.
All child devices under the ejection target are traversed and notified hierarchically based on ACPI namespace in ascending order when an eject event happens.
--- drivers/acpi/container.c | 2 +- drivers/acpi/internal.h | 2 +- drivers/acpi/scan.c | 140 +++++++++++++++++---------------------- include/acpi/acpi_bus.h | 2 +- 4 files changed, 65 insertions(+), 81 deletions(-)
drivers/acpi/container.c +1 -1
@@ -33,7 +33,7 @@ static int acpi_container_offline(struct container_dev *cdev)/* Check all of the dependent devices' physical companions. */ list_for_each_entry (child, &adev->children, node) -if (!acpi_scan_is_offline (child, false)) +if (!acpi_scan_is_offline (child))return -EBUSY ;return 0 ;
drivers/acpi/internal.h +1 -1
@@ -83,7 +83,7 @@ void acpi_apd_init(void);acpi_status acpi_hotplug_schedule (struct acpi_device *adev,u32 src);bool acpi_queue_hotplug_work (struct work_struct *work);void acpi_device_hotplug (struct acpi_device *adev,u32 src); -bool acpi_scan_is_offline (struct acpi_device *adev,bool uevent); +bool acpi_scan_is_offline (struct acpi_device *adev);acpi_status acpi_sysfs_table_handler (u32 event,void *table,void *context);void acpi_scan_table_handler (u32 event,void *table,void *context);
drivers/acpi/scan.c +62 -78
210 lines not on this page · raw message
include/acpi/acpi_bus.h +1 -1
@@ -330,7 +330,7 @@ struct acpi_device_physical_node {unsigned int node_id;struct list_head node;struct device *dev; - bool put_online:1; + bool changed_offline:1; };struct acpi_device_properties {
Signature · 1 line
2.20.1
Re: [RFC PATCH v2 1/3] ACPI / hotplug: Send change events for offline/online requests when eject is triggered
Quoted 7 lines · CET Chester Lin
Here we change offline/online handling in device hotplug by sending change events to userland as notification so that userland can have control and determine when will be a good time to put them offline/online based on current workload. In this approach the real offline/online opertions are handed over to userland so that userland can have more time to prepare before any device change actually happens.
Quoted 5 lines
All child devices under the ejection target are traversed and notified hierarchically based on ACPI namespace in ascending order when an eject event happens.
Signed-off-by Chester Lin <[email protected]>
So you replace the old flow with the new one and make the new one mandatory AFAICS.
Thus if anyone has relied on the old flow, they now need to switch over.
This is unfriendly and generally unwelcome, so please avoid making changes like that.
Instead, I would consider adding a device attribute to allow user space to opt in for getting offline notifications for specific individual devices (by setting that attribute user space would tell the kernel that it wants to get offline notifications for the device in question and it would take care of offlining it as needed).
Re: [RFC PATCH v2 1/3] ACPI / hotplug: Send change events for offline/online requests when eject is triggered
Hi Rafael,
Quoted 12 lines · Rafael J. Wysocki
Quoted 11 lines · CET Chester Lin
Here we change offline/online handling in device hotplug by sending change events to userland as notification so that userland can have control and determine when will be a good time to put them offline/online based on current workload. In this approach the real offline/online opertions are handed over to userland so that userland can have more time to prepare before any device change actually happens.
All child devices under the ejection target are traversed and notified hierarchically based on ACPI namespace in ascending order when an eject event happens.
Quoted 9 lines
Quoted 1 line
Signed-off-by Chester Lin <[email protected]>So you replace the old flow with the new one and make the new one mandatory AFAICS.
Thus if anyone has relied on the old flow, they now need to switch over.
This is unfriendly and generally unwelcome, so please avoid making changes like that.
Thank you for the reminder.
Quoted 6 lines
Instead, I would consider adding a device attribute to allow user space to opt in for getting offline notifications for specific individual devices (by setting that attribute user space would tell the kernel that it wants to get offline notifications for the device in question and it would take care of offlining it as needed).
Thanks for your advice. If no one is working on this device attribute [please free feel to correct me if I'm wrong], I am willing to implement it and will send a new RFC patch for code review.
Regards,
Chester
RFC PATCH v2 2/3 ACPI / hotplug: Eject status trace and auto-remove approach
In reply to Chester LinThis is an eject-status trace mechanism which helps to count current online devices under the ejection target, and it can automatically reschedules an eject event when all related devices are offline. The number of online nodes can be updated when any node has been put offline successfully. Any online event or offline failure within the whole device tree during ejection will stop the whole process and devices who have been put offline will need be online again as recovery.
--- drivers/acpi/glue.c | 146 ++++++++++++++++++++++++++++++++++++++ drivers/acpi/internal.h | 30 ++++++++ drivers/acpi/scan.c | 152 +++++++++++++++++++++++++++++++++++++++- drivers/base/core.c | 4 ++ include/acpi/acpi_bus.h | 1 + include/linux/acpi.h | 6 ++ 6 files changed, 336 insertions(+), 3 deletions(-)
drivers/acpi/glue.c +146 -0
@@ -360,6 +360,82 @@ static int acpi_device_notify_remove(struct device *dev)return 0 ; } +static void acpi_device_eject_tracer (struct device *dev, +enum kobject_action action) +{ +struct acpi_device *adev =ACPI_COMPANION (dev); +struct acpi_device *adev_root =NULL ; +struct eject_data *eject_node =NULL ; +struct eject_data *eject_root =NULL ; + +if (!adev) +return ; + +acpi_scan_lock_acquire (); + + eject_node = (struct eject_data *)adev->eject_stat; + +if (!eject_node) + goto tracer_end; + +if (eject_node->base.root_handle) + adev_root = +acpi_bus_get_acpi_device (eject_node->base.root_handle); + +if (adev_root) + eject_root = (struct eject_data *)adev_root->eject_stat; + +if (action ==KOBJ_OFFLINE ) { + eject_node->online_nodes--; + +if (eject_node->online_nodes ==0 ) + eject_node->status =ACPI_EJECT_STATUS_READY_REMOVE ; + +/* +* If the offline device is child, update its eject_root's +* number of online nodes. +*/ +if (eject_root) { + eject_root->online_nodes--; + + goto tracer_end; + } +/* +* Adjust number of online nodes when any physical node under +* eject_root is offline. Trigger acpi_eject_try() once all +* nodes are offline. +*/ +if (eject_node->online_nodes ==0 ) +acpi_eject_retry (adev); + + }else if (action ==KOBJ_ONLINE ) { +/* !eject_root means now eject_node is root. */ +if (!eject_root) + eject_root = eject_node; + +if (eject_root->status ==ACPI_EJECT_STATUS_GOING_OFFLINE || + eject_root->status ==ACPI_EJECT_STATUS_READY_REMOVE ) { + eject_root->status =ACPI_EJECT_STATUS_FAIL ; +acpi_eject_retry (adev_root); + } + } +tracer_end: +if (adev_root) +acpi_bus_put_acpi_device (adev_root); + +acpi_scan_lock_release (); +} + +static inline void acpi_device_check_eject_status (struct device *dev) +{ +acpi_device_eject_tracer (dev,KOBJ_OFFLINE ); +} + +static inline void acpi_device_check_eject_recover (struct device *dev) +{ +acpi_device_eject_tracer (dev,KOBJ_ONLINE ); +} +int acpi_platform_notify (struct device *dev,enum kobject_action action) {switch (action) {
@@ -369,8 +445,78 @@ int acpi_platform_notify(struct device *dev, enum kobject_action action)case KOBJ_REMOVE :acpi_device_notify_remove (dev);break ; +case KOBJ_OFFLINE : +acpi_device_check_eject_status (dev); +break ; +case KOBJ_ONLINE : +acpi_device_check_eject_recover (dev); +break ;default :break ; }return 0 ; } + +static void acpi_device_check_offline_fail (struct device *dev) +{ +struct acpi_device *adev =ACPI_COMPANION (dev); +struct acpi_device *adev_root =NULL ; +struct acpi_device *adev_child =NULL ; +struct eject_data *eject_node =NULL ; +bool has_online_child = false; +char *envp[] = {"EVENT=offline" ,NULL }; + +if (!adev) +return ; + +acpi_scan_lock_acquire (); + + eject_node = (struct eject_data *)adev->eject_stat; + +if (!eject_node || + eject_node->status !=ACPI_EJECT_STATUS_GOING_OFFLINE ) + goto check_end; + +if (!eject_node->retry) { +/* Check if the device contains any online child. */ +list_for_each_entry (adev_child, &adev->children, node) +if (!acpi_scan_is_offline (adev_child)) { + has_online_child = true; +break ; + } + +if (!has_online_child) + eject_node->retry = true; + +/* Sent change event again. */ +kobject_uevent_env (&dev->kobj,KOBJ_CHANGE , envp); + + }else { +/* Treat as failure if it still fails after retry. */ +if (eject_node->base.root_handle) { + adev_root =acpi_bus_get_acpi_device ( + eject_node->base.root_handle); + +if (!adev_root || !adev_root->eject_stat) + goto check_end; + + adev = adev_root; + eject_node = (struct eject_data *)adev_root->eject_stat; + } + + eject_node->status =ACPI_EJECT_STATUS_FAIL ; +acpi_eject_retry (adev); + } + +check_end: +if (adev_root) +acpi_bus_put_acpi_device (adev_root); + +acpi_scan_lock_release (); +} + +void acpi_action_fail_notify (struct device *dev,enum kobject_action action) +{ +if (action ==KOBJ_OFFLINE ) +acpi_device_check_offline_fail (dev); +}
drivers/acpi/internal.h +30 -0
@@ -257,4 +257,34 @@ void acpi_init_lpit(void);static inline void acpi_init_lpit (void ) { }#endif +/* -------------------------------------------------------------------------- +* Eject Status +* -------------------------------------------------------------------------- +*/ +enum acpi_eject_status { +ACPI_EJECT_STATUS_NONE =0 , +ACPI_EJECT_STATUS_GOING_OFFLINE , +ACPI_EJECT_STATUS_READY_REMOVE , +ACPI_EJECT_STATUS_FAIL +}; + +enum acpi_eject_node_type { +ACPI_EJECT_CHILD_NODE =0 , +ACPI_EJECT_ROOT_NODE +}; + +struct eject_data_base { +enum acpi_eject_node_type type; +acpi_handle root_handle; +}; + +struct eject_data { +struct eject_data_base base; +enum acpi_eject_status status; +u32 online_nodes; +bool retry; +}; + +void acpi_eject_retry (struct acpi_device *adev); +#endif /* _ACPI_INTERNAL_H_ */
drivers/acpi/scan.c +149 -3
212 lines not on this page · raw message
drivers/base/core.c +4 -0
@@ -2570,6 +2570,9 @@ int device_offline(struct device *dev)if (!ret) {kobject_uevent (&dev->kobj,KOBJ_OFFLINE ); dev->offline = true; +device_platform_notify (dev,KOBJ_OFFLINE ); + }else { +acpi_action_fail_notify (dev,KOBJ_OFFLINE ); } } }
@@ -2599,6 +2602,7 @@ int device_online(struct device *dev)if (!ret) {kobject_uevent (&dev->kobj,KOBJ_ONLINE ); dev->offline = false; +device_platform_notify (dev,KOBJ_ONLINE ); } }else { ret =1 ;
include/acpi/acpi_bus.h +1 -0
@@ -378,6 +378,7 @@ struct acpi_device {struct list_head physical_node_list;struct mutex physical_node_lock;void (*remove)(struct acpi_device *); +void *eject_stat; };/* Non-device subnode */
include/linux/acpi.h +6 -0
@@ -1321,12 +1321,18 @@ static inline int find_acpi_cpu_cache_topology(unsigned int cpu, int level)#ifdef CONFIG_ACPI extern int acpi_platform_notify (struct device *dev,enum kobject_action action); +extern void acpi_action_fail_notify (struct device *dev, +enum kobject_action action);#else static inline int acpi_platform_notify (struct device *dev,enum kobject_action action) {return 0 ; } +static inline void +void acpi_action_fail_notify(struct device *dev, enum kobject_action action) +{ +}#endif #endif /*_LINUX_ACPI_H*/
Signature · 1 line
2.20.1
[email protected]
maintainerRe: [RFC PATCH v2 2/3] ACPI / hotplug: Eject status trace and auto-remove approach
Review
Quoted 1 line · Chester Lin
(quoted diff) +1
-0+ eject_node = (struct eject_data *)adev->eject_stat;
No need for this cast.
You do that all over this patch, it can be removed everywhere.
thanks,
greg k-h
kbuild test robot
build botRe: [RFC PATCH v2 2/3] ACPI / hotplug: Eject status trace and auto-remove approach
In reply to Chester Lin[if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please…
Hi Chester,
[FYI, it's a private test report for your RFC patch.]
[auto build test ERROR on pm/linux-next]
[also build test ERROR on driver-core/driver-core-testing v5.5-rc4 next-20191219] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Chester-Lin/ACPI-New-eject-flow-to-remove-devices-cautiously/20200104-110159 base: https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next config: i386-tinyconfig (attached as .config) compiler: gcc-7 (Debian 7.5.0-3) 7.5.0 reproduce: # save the attached .config to linux build tree make ARCH=i386
If you fix the issue, kindly add following tag
All errors (new ones prefixed by >>):
In file included from init/main.c:30:0:
>> include/linux/acpi.h:1326:1: error: two or more data types in declaration specifiers
void acpi_action_fail_notify(struct device *dev, enum kobject_action action) ^~~~
vim +1326 include/linux/acpi.h
1314
1315 #ifdef CONFIG_ACPI
1316 extern int acpi_platform_notify(struct device *dev, enum kobject_action action);
1317 extern void acpi_action_fail_notify(struct device *dev,
1318 enum kobject_action action);
1319 #else
1320 static inline int
1321 acpi_platform_notify(struct device *dev, enum kobject_action action)
1322 {
1323 return 0;
1324 }
1325 static inline void
> 1326 void acpi_action_fail_notify(struct device *dev, enum kobject_action action)
1327 {
1328 }
1329 #endif
1330
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org Intel Corporation
kbuild test robot
build botRe: [RFC PATCH v2 2/3] ACPI / hotplug: Eject status trace and auto-remove approach
In reply to Chester Lin[if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please…
Hi Chester,
[FYI, it's a private test report for your RFC patch.]
[auto build test WARNING on pm/linux-next]
[also build test WARNING on driver-core/driver-core-testing v5.5-rc4 next-20191220] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Chester-Lin/ACPI-New-eject-flow-to-remove-devices-cautiously/20200104-110159 base: https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next reproduce: # apt-get install sparse # sparse version: v0.6.1-129-g341daf20-dirty make ARCH=x86_64 allmodconfig make C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__'
If you fix the issue, kindly add following tag
sparse warnings: (new ones prefixed by >>)
>> drivers/acpi/scan.c:191:48: sparse: sparse: Using plain integer as NULL pointer
vim +191 drivers/acpi/scan.c
36
37 static LIST_HEAD(acpi_dep_list);
38 static DEFINE_MUTEX(acpi_dep_list_lock);
39 LIST_HEAD(acpi_bus_id_list);
40 static DEFINE_MUTEX(acpi_scan_lock);
41 static LIST_HEAD(acpi_scan_handlers_list);
> 42 DEFINE_MUTEX(acpi_device_lock);
43 LIST_HEAD(acpi_wakeup_device_list);
44 static DEFINE_MUTEX(acpi_hp_context_lock);
45
46 /*
47 * The UART device described by the SPCR table is the only object which needs
48 * special-casing. Everything else is covered by ACPI namespace paths in STAO
49 * table.
50 */
51 static u64 spcr_uart_addr;
52
53 struct acpi_dep_data {
54 struct list_head node;
55 acpi_handle master;
56 acpi_handle slave;
57 };
58
59 void acpi_scan_lock_acquire(void)
60 {
61 mutex_lock(&acpi_scan_lock);
62 }
63 EXPORT_SYMBOL_GPL(acpi_scan_lock_acquire);
64
65 void acpi_scan_lock_release(void)
66 {
67 mutex_unlock(&acpi_scan_lock);
68 }
69 EXPORT_SYMBOL_GPL(acpi_scan_lock_release);
70
71 void acpi_lock_hp_context(void)
72 {
73 mutex_lock(&acpi_hp_context_lock);
74 }
75
76 void acpi_unlock_hp_context(void)
77 {
78 mutex_unlock(&acpi_hp_context_lock);
79 }
80
81 void acpi_initialize_hp_context(struct acpi_device *adev,
82 struct acpi_hotplug_context *hp,
83 int (*notify)(struct acpi_device *, u32),
84 void (*uevent)(struct acpi_device *, u32))
85 {
86 acpi_lock_hp_context();
87 hp->notify = notify;
88 hp->uevent = uevent;
89 acpi_set_hp_context(adev, hp);
90 acpi_unlock_hp_context();
91 }
92 EXPORT_SYMBOL_GPL(acpi_initialize_hp_context);
93
94 int acpi_scan_add_handler(struct acpi_scan_handler *handler)
95 {
96 if (!handler)
97 return -EINVAL;
98
99 list_add_tail(&handler->list_node, &acpi_scan_handlers_list);
100 return 0;
101 }
102
103 int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler,
104 const char *hotplug_profile_name)
105 {
106 int error;
107
108 error = acpi_scan_add_handler(handler);
109 if (error)
110 return error;
111
112 acpi_sysfs_add_hotplug_profile(&handler->hotplug, hotplug_profile_name);
113 return 0;
114 }
115
116 bool acpi_scan_is_offline(struct acpi_device *adev)
117 {
118 struct acpi_device_physical_node *pn;
119 bool offline = true;
120
121 /*
122 * acpi_container_offline() calls this for all of the container's
123 * children under the container's physical_node_lock lock.
124 */
125 mutex_lock_nested(&adev->physical_node_lock, SINGLE_DEPTH_NESTING);
126
127 list_for_each_entry(pn, &adev->physical_node_list, node)
128 if (device_supports_offline(pn->dev) && !pn->dev->offline) {
129 offline = false;
130 break;
131 }
132
133 mutex_unlock(&adev->physical_node_lock);
134 return offline;
135 }
136
137 void acpi_eject_retry(struct acpi_device *adev)
138 {
139 acpi_status status;
140
141 get_device(&adev->dev);
142
143 status = acpi_hotplug_schedule(adev, ACPI_OST_EC_OSPM_EJECT);
144 if (ACPI_FAILURE(status)) {
145 pr_debug("Failed to reschedule an eject event\n");
146 put_device(&adev->dev);
147 acpi_evaluate_ost(adev->handle, ACPI_OST_EC_OSPM_EJECT,
148 ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL);
149 return;
150 }
151 pr_debug("Rescheduled an eject event\n");
152 }
153
154 static acpi_status acpi_init_eject_stat(struct acpi_device *adev,
155 enum acpi_eject_node_type type)
156 {
157 struct eject_data *eject_node = (struct eject_data *) adev->eject_stat;
158
159 if (!eject_node) {
160 eject_node = kzalloc(sizeof(*eject_node), GFP_KERNEL);
161 if (!eject_node)
162 return AE_NO_MEMORY;
163 adev->eject_stat = eject_node;
164 }
165
166 eject_node->base.type = type;
167 eject_node->online_nodes = 0;
168
169 return AE_OK;
170 }
171
172 static void acpi_enable_eject_stat(struct acpi_device *adev,
173 struct acpi_device *root_adev, u32 online_nodes)
174 {
175 struct eject_data *eject_node = NULL;
176 struct eject_data *eject_root = NULL;
177
178 if (adev)
179 eject_node = (struct eject_data *)adev->eject_stat;
180 if (root_adev)
181 eject_root = (struct eject_data *)root_adev->eject_stat;
182
183 if (!eject_node || !eject_root) {
184 pr_debug("No eject data\n");
185 return;
186 }
187
188 if (eject_node != eject_root)
189 eject_node->base.root_handle = root_adev->handle;
190 else
> 191 eject_node->base.root_handle = 0; 192
193 /* Update node status and online_nodes of root device */
194 if (online_nodes) {
195 eject_root->online_nodes += online_nodes;
196
197 if (eject_node->base.root_handle)
198 eject_node->online_nodes = online_nodes;
199
200 pr_debug("Online nodes:%u, Total:%u\n",
201 eject_node->online_nodes, eject_root->online_nodes);
202
203 eject_node->status = ACPI_EJECT_STATUS_GOING_OFFLINE;
204
205 } else {
206 eject_node->status = ACPI_EJECT_STATUS_READY_REMOVE;
207 }
208 }
209
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org Intel Corporation
RFC PATCH v2 3/3 ACPI / device_sysfs: Add eject_show and add a cancel option in eject_store
In reply to Chester LinAdd an eject_show attribute for users to monitor current status because sometimes it could take time to finish an ejection so we need to know whether it is still in progress or not. For userspace who might need to cancel an onging ejection, we also offer an option in eject_store.
--- Documentation/ABI/testing/sysfs-bus-acpi | 9 ++- drivers/acpi/device_sysfs.c | 94 +++++++++++++++++++++--- drivers/acpi/internal.h | 4 +- drivers/acpi/scan.c | 38 +++++++++- 4 files changed, 129 insertions(+), 16 deletions(-)
Documentation/ABI/testing/sysfs-bus-acpi +6 -3
@@ -53,9 +53,12 @@ What: /sys/bus/acpi/devices/.../eject Date: December 2006 Contact: Rafael J. Wysocki <[email protected]> Description: - Writing 1 to this attribute will trigger hot removal of - this device object. This file exists for every device - object that has _EJ0 method. + (R) Allows users to read eject status of the device object. + (W) Writing 1 to this attribute will trigger hot removal of + this device object. Writing 2 to this attribute will cancel hot + removal work if it's still in offline process and the original + state of this device object will be recovered. This file exists + for every device object that has _EJ0 method. What: /sys/bus/acpi/devices/.../status Date: Jan, 2014
drivers/acpi/device_sysfs.c +84 -10
119 lines not on this page · raw message
drivers/acpi/internal.h +3 -1
@@ -265,7 +265,8 @@ enum acpi_eject_status {ACPI_EJECT_STATUS_NONE =0 ,ACPI_EJECT_STATUS_GOING_OFFLINE ,ACPI_EJECT_STATUS_READY_REMOVE , -ACPI_EJECT_STATUS_FAIL +ACPI_EJECT_STATUS_FAIL , +ACPI_EJECT_STATUS_CANCEL }; enum acpi_eject_node_type {
@@ -286,5 +287,6 @@ struct eject_data { };void acpi_eject_retry (struct acpi_device *adev); +char *acpi_eject_status_string (struct acpi_device *adev);#endif /* _ACPI_INTERNAL_H_ */
drivers/acpi/scan.c +36 -2
@@ -366,8 +366,9 @@ static int acpi_scan_offline_check(struct acpi_device *device)return -EBUSY ; } -if (eject_obj->status ==ACPI_EJECT_STATUS_FAIL ) { -dev_warn (&device->dev,"Eject failed. Recover all.\n" ); +if (eject_obj->status ==ACPI_EJECT_STATUS_FAIL || + eject_obj->status ==ACPI_EJECT_STATUS_CANCEL ) { +dev_warn (&device->dev,"Eject stopped. Recover all.\n" );acpi_scan_notify_online (device);return -EAGAIN ; }
@@ -383,6 +384,39 @@ static int acpi_scan_offline_check(struct acpi_device *device)return ret; } +char *acpi_eject_status_string (struct acpi_device *adev) +{ +struct eject_data *eject_obj; +char *status_string ="none" ; + +mutex_lock (&acpi_scan_lock); + eject_obj = (struct eject_data *) adev->eject_stat; + +if (eject_obj) { +switch (eject_obj->status) { +case ACPI_EJECT_STATUS_NONE : +break ; +case ACPI_EJECT_STATUS_GOING_OFFLINE : + status_string ="going offline" ; +break ; +case ACPI_EJECT_STATUS_READY_REMOVE : + status_string ="ready to remove" ; +break ; +case ACPI_EJECT_STATUS_FAIL : + status_string ="failure" ; +break ; +case ACPI_EJECT_STATUS_CANCEL : + status_string ="cancel" ; +break ; +default : + status_string ="(unknown)" ; + } + } + +mutex_unlock (&acpi_scan_lock); +return status_string; +} +static int acpi_scan_hot_remove (struct acpi_device *device) {acpi_handle handle = device->handle;
Signature · 1 line
2.20.1
[email protected]
maintainerRe: [RFC PATCH v2 3/3] ACPI / device_sysfs: Add eject_show and add a cancel option in eject_store
Quoted 20 lines · Chester Lin
Add an
eject_showattribute for users to monitor current status because sometimes it could take time to finish an ejection so we need to know whether it is still in progress or not. For userspace who might need to cancel an onging ejection, we also offer an option ineject_store.Signed-off-by Chester Lin <[email protected]>--- Documentation/ABI/testing/sysfs-bus-acpi | 9 ++- drivers/acpi/device_sysfs.c | 94 +++++++++++++++++++++--- drivers/acpi/internal.h | 4 +- drivers/acpi/scan.c | 38 +++++++++- 4 files changed, 129 insertions(+), 16 deletions(-)Documentation/ABI/testing/sysfs-bus-acpi +0
-0@@ -53,9 +53,12 @@ What: /sys/bus/acpi/devices/.../eject Date: December 2006 Contact: Rafael J. Wysocki <[email protected]>
Documentation/ABI/testing/sysfs-bus-acpi What: /sys/bus/acpi/devices/.../eject · line 61
Quoted 10 lines
Documentation/ABI/testing/sysfs-bus-acpi +6
-3Description: - Writing 1 to this attribute will trigger hot removal of - this device object. This file exists for every device - object that has _EJ0 method. + (R) Allows users to read eject status of the device object. + (W) Writing 1 to this attribute will trigger hot removal of + this device object. Writing 2 to this attribute will cancel hot + removal work if it's still in offline process and the original + state of this device object will be recovered. This file exists + for every device object that has _EJ0 method.
Oh that's going to cause problems :)
Why not just have a new file, "cancel_eject" that you can write to, to cancel the eject happening?
That way you don't have an odd "state" that this file needs to keep track of.
And what happens if you write a '2' here when eject is not happening? It didn't look like your code handled that state well.
Quoted 166 lines
Documentation/ABI/testing/sysfs-bus-acpi +0
-0What: /sys/bus/acpi/devices/.../status Date: Jan, 2014drivers/acpi/device_sysfs.c +84
-10@@ -365,17 +365,13 @@ static ssize_t power_state_show(struct device *dev,static DEVICE_ATTR_RO (power_state); -static ssize_t -acpi_eject_store (struct device *d,struct device_attribute *attr, -const char *buf,size_t count) +static ssize_t eject_show (struct device *d, +struct device_attribute *attr,char *buf) {struct acpi_device *acpi_device =to_acpi_device (d);acpi_object_type not_used;acpi_status status; -if (!count || buf[0 ] !='1' ) -return -EINVAL ; -if ((!acpi_device->handler || !acpi_device->handler->hotplug.enabled) && !acpi_device->driver)return -ENODEV ;@@ -384,18 +380,96 @@ acpi_eject_store(struct device *d, struct device_attribute *attr,if (ACPI_FAILURE (status) || !acpi_device->flags.ejectable)return -ENODEV ; +return sprintf (buf,"%s\n" ,acpi_eject_status_string (acpi_device)); +} + +static ssize_t +eject_store (struct device *d,struct device_attribute *attr, +const char *buf,size_t count) +{ +struct acpi_device *acpi_device =to_acpi_device (d); +struct eject_data *eject_node =NULL ; +acpi_object_type not_used; +acpi_status status; +bool cancel_eject = false; +ssize_t ret; + +if (!count) +return -EINVAL ; + +switch (buf[0 ]) { +case '1' : +break ; +case '2' : +acpi_scan_lock_acquire (); + eject_node = (struct eject_data *)acpi_device->eject_stat; + +if (!eject_node) { +acpi_scan_lock_release (); + ret = -EINVAL ; + goto eject_end; + } + +/* +* Find a root to start cancellation from the top +*/ +if (eject_node->base.root_handle) { + acpi_device =acpi_bus_get_acpi_device ( + eject_node->base.root_handle); + +if (acpi_device) + eject_node = + (struct eject_data *)acpi_device->eject_stat; +else + eject_node =NULL ; + + } + +if (eject_node && + (eject_node->status ==ACPI_EJECT_STATUS_GOING_OFFLINE || + eject_node->status ==ACPI_EJECT_STATUS_READY_REMOVE )) { + eject_node->status =ACPI_EJECT_STATUS_CANCEL ; + cancel_eject = true; + } + +acpi_scan_lock_release (); +if (cancel_eject) +break ; +default : + ret = -EINVAL ; + goto eject_end; + }; + +if ((!acpi_device->handler || !acpi_device->handler->hotplug.enabled) + && !acpi_device->driver) { + ret = -ENODEV ; + goto eject_end; + } + + status =acpi_get_type (acpi_device->handle, ¬_used); +if (ACPI_FAILURE (status) || !acpi_device->flags.ejectable) { + ret = -ENODEV ; + goto eject_end; + } +get_device (&acpi_device->dev); status =acpi_hotplug_schedule (acpi_device,ACPI_OST_EC_OSPM_EJECT ); -if (ACPI_SUCCESS (status)) -return count; +if (ACPI_SUCCESS (status)) { + ret = count; + goto eject_end; + }put_device (&acpi_device->dev);acpi_evaluate_ost (acpi_device->handle,ACPI_OST_EC_OSPM_EJECT ,ACPI_OST_SC_NON_SPECIFIC_FAILURE ,NULL ); -return status ==AE_NO_MEMORY ? -ENOMEM : -EAGAIN ; + ret = (status ==AE_NO_MEMORY ) ? -ENOMEM : -EAGAIN ; + +eject_end: +return ret; + } -static DEVICE_ATTR(eject, 0200, NULL, acpi_eject_store); +static DEVICE_ATTR_RW (eject);static ssize_t acpi_device_hid_show (struct device *dev,struct device_attribute *attr,char *buf)drivers/acpi/internal.h +3
-1@@ -265,7 +265,8 @@ enum acpi_eject_status {ACPI_EJECT_STATUS_NONE =0 ,ACPI_EJECT_STATUS_GOING_OFFLINE ,ACPI_EJECT_STATUS_READY_REMOVE , -ACPI_EJECT_STATUS_FAIL +ACPI_EJECT_STATUS_FAIL , +ACPI_EJECT_STATUS_CANCEL }; enum acpi_eject_node_type {@@ -286,5 +287,6 @@ struct eject_data { };void acpi_eject_retry (struct acpi_device *adev); +char *acpi_eject_status_string (struct acpi_device *adev);#endif /* _ACPI_INTERNAL_H_ */ drivers/acpi/scan.c +3
-2@@ -366,8 +366,9 @@ static int acpi_scan_offline_check(struct acpi_device *device)return -EBUSY ; } -if (eject_obj->status ==ACPI_EJECT_STATUS_FAIL ) { -dev_warn (&device->dev,"Eject failed. Recover all.\n" ); +if (eject_obj->status ==ACPI_EJECT_STATUS_FAIL || + eject_obj->status ==ACPI_EJECT_STATUS_CANCEL ) { +dev_warn (&device->dev,"Eject stopped. Recover all.\n" );acpi_scan_notify_online (device);return -EAGAIN ; }
drivers/acpi/scan.c static int acpi_scan_offline_check(struct acpi_device *device) · line 393
Quoted 10 lines
drivers/acpi/scan.c +7
-0@@ -383,6 +384,39 @@ static int acpi_scan_offline_check(struct acpi_device *device)return ret; } +char *acpi_eject_status_string (struct acpi_device *adev) +{ +struct eject_data *eject_obj; +char *status_string ="none" ; + +mutex_lock (&acpi_scan_lock); + eject_obj = (struct eject_data *) adev->eject_stat;
Always use checkpatch.pl so maintainers don't have to ask you to use checkpatch.pl :)
Quoted 14 lines
drivers/acpi/scan.c +14
-0+ +if (eject_obj) { +switch (eject_obj->status) { +case ACPI_EJECT_STATUS_NONE : +break ; +case ACPI_EJECT_STATUS_GOING_OFFLINE : + status_string ="going offline" ; +break ; +case ACPI_EJECT_STATUS_READY_REMOVE : + status_string ="ready to remove" ; +break ; +case ACPI_EJECT_STATUS_FAIL : + status_string ="failure" ; +break ;
drivers/acpi/scan.c static int acpi_scan_offline_check(struct acpi_device *device) · line 417
Quoted 10 lines
drivers/acpi/scan.c +10
-0+case ACPI_EJECT_STATUS_CANCEL : + status_string ="cancel" ; +break ; +default : + status_string ="(unknown)" ; + } + } + +mutex_unlock (&acpi_scan_lock); +return status_string;
So the state can change right after checking it and reporting it to userspace?
If so, what good is the lock here at all?
thanks,
greg k-h
Re: [RFC PATCH v2 3/3] ACPI / device_sysfs: Add eject_show and add a cancel option in eject_store
Hi Greg,
Quoted 34 lines · On Fri, Jan 03, 2020 at 09:37:28AM +0100, [email protected] wrote:
Quoted 20 lines · Chester Lin
Add an
eject_showattribute for users to monitor current status because sometimes it could take time to finish an ejection so we need to know whether it is still in progress or not. For userspace who might need to cancel an onging ejection, we also offer an option ineject_store.Signed-off-by Chester Lin <[email protected]>--- Documentation/ABI/testing/sysfs-bus-acpi | 9 ++- drivers/acpi/device_sysfs.c | 94 +++++++++++++++++++++--- drivers/acpi/internal.h | 4 +- drivers/acpi/scan.c | 38 +++++++++- 4 files changed, 129 insertions(+), 16 deletions(-)Documentation/ABI/testing/sysfs-bus-acpi +0
-0@@ -53,9 +53,12 @@ What: /sys/bus/acpi/devices/.../eject Date: December 2006 Contact: Rafael J. Wysocki <[email protected]>Documentation/ABI/testing/sysfs-bus-acpi What: /sys/bus/acpi/devices/.../eject · line 61
Quoted 10 lines
Documentation/ABI/testing/sysfs-bus-acpi +6
-3Description: - Writing 1 to this attribute will trigger hot removal of - this device object. This file exists for every device - object that has _EJ0 method. + (R) Allows users to read eject status of the device object. + (W) Writing 1 to this attribute will trigger hot removal of + this device object. Writing 2 to this attribute will cancel hot + removal work if it's still in offline process and the original + state of this device object will be recovered. This file exists + for every device object that has _EJ0 method.Oh that's going to cause problems :)
Quoted 6 lines
Why not just have a new file, "
cancel_eject" that you can write to, to cancel the eject happening?That way you don't have an odd "state" that this file needs to keep track of.
Thank you for the advice and I will add it.
Quoted 3 lines
And what happens if you write a '2' here when eject is not happening? It didn't look like your code handled that state well.
When eject is not happening, the eject_stat is null so the procedure will go to eject_end without changing anything.
Quoted 169 lines
Quoted 169 lines
Documentation/ABI/testing/sysfs-bus-acpi +0
-0What: /sys/bus/acpi/devices/.../status Date: Jan, 2014drivers/acpi/device_sysfs.c +84
-10@@ -365,17 +365,13 @@ static ssize_t power_state_show(struct device *dev,static DEVICE_ATTR_RO (power_state); -static ssize_t -acpi_eject_store (struct device *d,struct device_attribute *attr, -const char *buf,size_t count) +static ssize_t eject_show (struct device *d, +struct device_attribute *attr,char *buf) {struct acpi_device *acpi_device =to_acpi_device (d);acpi_object_type not_used;acpi_status status; -if (!count || buf[0 ] !='1' ) -return -EINVAL ; -if ((!acpi_device->handler || !acpi_device->handler->hotplug.enabled) && !acpi_device->driver)return -ENODEV ;@@ -384,18 +380,96 @@ acpi_eject_store(struct device *d, struct device_attribute *attr,if (ACPI_FAILURE (status) || !acpi_device->flags.ejectable)return -ENODEV ; +return sprintf (buf,"%s\n" ,acpi_eject_status_string (acpi_device)); +} + +static ssize_t +eject_store (struct device *d,struct device_attribute *attr, +const char *buf,size_t count) +{ +struct acpi_device *acpi_device =to_acpi_device (d); +struct eject_data *eject_node =NULL ; +acpi_object_type not_used; +acpi_status status; +bool cancel_eject = false; +ssize_t ret; + +if (!count) +return -EINVAL ; + +switch (buf[0 ]) { +case '1' : +break ; +case '2' : +acpi_scan_lock_acquire (); + eject_node = (struct eject_data *)acpi_device->eject_stat; + +if (!eject_node) { +acpi_scan_lock_release (); + ret = -EINVAL ; + goto eject_end; + } + +/* +* Find a root to start cancellation from the top +*/ +if (eject_node->base.root_handle) { + acpi_device =acpi_bus_get_acpi_device ( + eject_node->base.root_handle); + +if (acpi_device) + eject_node = + (struct eject_data *)acpi_device->eject_stat; +else + eject_node =NULL ; + + } + +if (eject_node && + (eject_node->status ==ACPI_EJECT_STATUS_GOING_OFFLINE || + eject_node->status ==ACPI_EJECT_STATUS_READY_REMOVE )) { + eject_node->status =ACPI_EJECT_STATUS_CANCEL ; + cancel_eject = true; + } + +acpi_scan_lock_release (); +if (cancel_eject) +break ; +default : + ret = -EINVAL ; + goto eject_end; + }; + +if ((!acpi_device->handler || !acpi_device->handler->hotplug.enabled) + && !acpi_device->driver) { + ret = -ENODEV ; + goto eject_end; + } + + status =acpi_get_type (acpi_device->handle, ¬_used); +if (ACPI_FAILURE (status) || !acpi_device->flags.ejectable) { + ret = -ENODEV ; + goto eject_end; + } +get_device (&acpi_device->dev); status =acpi_hotplug_schedule (acpi_device,ACPI_OST_EC_OSPM_EJECT ); -if (ACPI_SUCCESS (status)) -return count; +if (ACPI_SUCCESS (status)) { + ret = count; + goto eject_end; + }put_device (&acpi_device->dev);acpi_evaluate_ost (acpi_device->handle,ACPI_OST_EC_OSPM_EJECT ,ACPI_OST_SC_NON_SPECIFIC_FAILURE ,NULL ); -return status ==AE_NO_MEMORY ? -ENOMEM : -EAGAIN ; + ret = (status ==AE_NO_MEMORY ) ? -ENOMEM : -EAGAIN ; + +eject_end: +return ret; + } -static DEVICE_ATTR(eject, 0200, NULL, acpi_eject_store); +static DEVICE_ATTR_RW (eject);static ssize_t acpi_device_hid_show (struct device *dev,struct device_attribute *attr,char *buf)drivers/acpi/internal.h +3
-1@@ -265,7 +265,8 @@ enum acpi_eject_status {ACPI_EJECT_STATUS_NONE =0 ,ACPI_EJECT_STATUS_GOING_OFFLINE ,ACPI_EJECT_STATUS_READY_REMOVE , -ACPI_EJECT_STATUS_FAIL +ACPI_EJECT_STATUS_FAIL , +ACPI_EJECT_STATUS_CANCEL }; enum acpi_eject_node_type {@@ -286,5 +287,6 @@ struct eject_data { };void acpi_eject_retry (struct acpi_device *adev); +char *acpi_eject_status_string (struct acpi_device *adev);#endif /* _ACPI_INTERNAL_H_ */ drivers/acpi/scan.c +3
-2@@ -366,8 +366,9 @@ static int acpi_scan_offline_check(struct acpi_device *device)return -EBUSY ; } -if (eject_obj->status ==ACPI_EJECT_STATUS_FAIL ) { -dev_warn (&device->dev,"Eject failed. Recover all.\n" ); +if (eject_obj->status ==ACPI_EJECT_STATUS_FAIL || + eject_obj->status ==ACPI_EJECT_STATUS_CANCEL ) { +dev_warn (&device->dev,"Eject stopped. Recover all.\n" );acpi_scan_notify_online (device);return -EAGAIN ; }@@ -383,6 +384,39 @@ static int acpi_scan_offline_check(struct acpi_device *device)return ret; }
Quoted 11 lines
drivers/acpi/scan.c static int acpi_scan_offline_check(struct acpi_device *device) · line 393
Quoted 7 lines
drivers/acpi/scan.c +7
-0+char *acpi_eject_status_string (struct acpi_device *adev) +{ +struct eject_data *eject_obj; +char *status_string ="none" ; + +mutex_lock (&acpi_scan_lock); + eject_obj = (struct eject_data *) adev->eject_stat;Always use checkpatch.pl so maintainers don't have to ask you to use checkpatch.pl :)
Acutally I have used checkpatch.pl but haven't found a warning on this line:
total: 0 errors, 0 warnings, 199 lines checked
The last commit of checkpatch.pl in my code base is 184b8f7f91ca. Anyway, thank you for the reminder I will remove the cast from next version :)
Quoted 28 lines
Quoted 14 lines
drivers/acpi/scan.c +14
-0+ +if (eject_obj) { +switch (eject_obj->status) { +case ACPI_EJECT_STATUS_NONE : +break ; +case ACPI_EJECT_STATUS_GOING_OFFLINE : + status_string ="going offline" ; +break ; +case ACPI_EJECT_STATUS_READY_REMOVE : + status_string ="ready to remove" ; +break ; +case ACPI_EJECT_STATUS_FAIL : + status_string ="failure" ; +break ;drivers/acpi/scan.c static int acpi_scan_offline_check(struct acpi_device *device) · line 417
Quoted 10 lines
drivers/acpi/scan.c +10
-0+case ACPI_EJECT_STATUS_CANCEL : + status_string ="cancel" ; +break ; +default : + status_string ="(unknown)" ; + } + } + +mutex_unlock (&acpi_scan_lock); +return status_string;So the state can change right after checking it and reporting it to userspace?
Quoted 2 lines
If so, what good is the lock here at all?
I use this lock to prevent the eject_state from being freed during a query since any status change might still happen. For example, the userland process could be preempted before accessing eject_state and then the eject_state could be quickly freed due to eject failure or eject completion. [The free opertion is implemented in acpi_free_eject_stat()]
Quoted 5 lines
thanks,
greg k-h
kbuild test robot
build botRe: [RFC PATCH v2 3/3] ACPI / device_sysfs: Add eject_show and add a cancel option in eject_store
In reply to Chester Lin[if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please…
Hi Chester,
[FYI, it's a private test report for your RFC patch.]
[auto build test WARNING on pm/linux-next]
[also build test WARNING on driver-core/driver-core-testing v5.5-rc4 next-20191220] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Chester-Lin/ACPI-New-eject-flow-to-remove-devices-cautiously/20200104-110159 base: https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next
If you fix the issue, kindly add following tag
coccinelle warnings: (new ones prefixed by >>)
>> drivers/acpi/device_sysfs.c:441:2-3: Unneeded semicolon
Please review and possibly fold the followup patch.
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org Intel Corporation
kbuild test robot
build bot[PATCH] ACPI / device_sysfs: fix semicolon.cocci warnings
In reply to Chester Lindrivers/acpi/device_sysfs.c:441:2-3: Unneeded semicolon
From: kbuild test robot <[email protected]>
drivers/acpi/device_sysfs.c:441:2-3: Unneeded semicolon
Remove unneeded semicolon.
Generated by: scripts/coccinelle/misc/semicolon.cocci
---
url: https://github.com/0day-ci/linux/commits/Chester-Lin/ACPI-New-eject-flow-to-remove-devices-cautiously/20200104-110159 base: https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next
device_sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/acpi/device_sysfs.c +1 -1
@@ -438,7 +438,7 @@ eject_store(struct device *d, struct devdefault : ret = -EINVAL ; goto eject_end; - }; + }if ((!acpi_device->handler || !acpi_device->handler->hotplug.enabled) && !acpi_device->driver) {