]>
Commit | Line | Data |
---|---|---|
c6aa2418 GKH |
1 | From 0462c56c290a99a7f03e817ae5b843116dfb575c Mon Sep 17 00:00:00 2001 |
2 | From: Herve Codina <herve.codina@bootlin.com> | |
3 | Date: Mon, 25 Mar 2024 16:21:25 +0100 | |
4 | Subject: driver core: Introduce device_link_wait_removal() | |
5 | ||
6 | From: Herve Codina <herve.codina@bootlin.com> | |
7 | ||
8 | commit 0462c56c290a99a7f03e817ae5b843116dfb575c upstream. | |
9 | ||
10 | The commit 80dd33cf72d1 ("drivers: base: Fix device link removal") | |
11 | introduces a workqueue to release the consumer and supplier devices used | |
12 | in the devlink. | |
13 | In the job queued, devices are release and in turn, when all the | |
14 | references to these devices are dropped, the release function of the | |
15 | device itself is called. | |
16 | ||
17 | Nothing is present to provide some synchronisation with this workqueue | |
18 | in order to ensure that all ongoing releasing operations are done and | |
19 | so, some other operations can be started safely. | |
20 | ||
21 | For instance, in the following sequence: | |
22 | 1) of_platform_depopulate() | |
23 | 2) of_overlay_remove() | |
24 | ||
25 | During the step 1, devices are released and related devlinks are removed | |
26 | (jobs pushed in the workqueue). | |
27 | During the step 2, OF nodes are destroyed but, without any | |
28 | synchronisation with devlink removal jobs, of_overlay_remove() can raise | |
29 | warnings related to missing of_node_put(): | |
30 | ERROR: memory leak, expected refcount 1 instead of 2 | |
31 | ||
32 | Indeed, the missing of_node_put() call is going to be done, too late, | |
33 | from the workqueue job execution. | |
34 | ||
35 | Introduce device_link_wait_removal() to offer a way to synchronize | |
36 | operations waiting for the end of devlink removals (i.e. end of | |
37 | workqueue jobs). | |
38 | Also, as a flushing operation is done on the workqueue, the workqueue | |
39 | used is moved from a system-wide workqueue to a local one. | |
40 | ||
41 | Cc: stable@vger.kernel.org | |
42 | Signed-off-by: Herve Codina <herve.codina@bootlin.com> | |
43 | Tested-by: Luca Ceresoli <luca.ceresoli@bootlin.com> | |
44 | Reviewed-by: Nuno Sa <nuno.sa@analog.com> | |
45 | Reviewed-by: Saravana Kannan <saravanak@google.com> | |
46 | Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
47 | Link: https://lore.kernel.org/r/20240325152140.198219-2-herve.codina@bootlin.com | |
48 | Signed-off-by: Rob Herring <robh@kernel.org> | |
49 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
50 | --- | |
51 | drivers/base/core.c | 26 +++++++++++++++++++++++--- | |
52 | include/linux/device.h | 1 + | |
53 | 2 files changed, 24 insertions(+), 3 deletions(-) | |
54 | ||
55 | --- a/drivers/base/core.c | |
56 | +++ b/drivers/base/core.c | |
57 | @@ -44,6 +44,7 @@ static bool fw_devlink_is_permissive(voi | |
58 | static void __fw_devlink_link_to_consumers(struct device *dev); | |
59 | static bool fw_devlink_drv_reg_done; | |
60 | static bool fw_devlink_best_effort; | |
61 | +static struct workqueue_struct *device_link_wq; | |
62 | ||
63 | /** | |
64 | * __fwnode_link_add - Create a link between two fwnode_handles. | |
65 | @@ -531,12 +532,26 @@ static void devlink_dev_release(struct d | |
66 | /* | |
67 | * It may take a while to complete this work because of the SRCU | |
68 | * synchronization in device_link_release_fn() and if the consumer or | |
69 | - * supplier devices get deleted when it runs, so put it into the "long" | |
70 | - * workqueue. | |
71 | + * supplier devices get deleted when it runs, so put it into the | |
72 | + * dedicated workqueue. | |
73 | */ | |
74 | - queue_work(system_long_wq, &link->rm_work); | |
75 | + queue_work(device_link_wq, &link->rm_work); | |
76 | } | |
77 | ||
78 | +/** | |
79 | + * device_link_wait_removal - Wait for ongoing devlink removal jobs to terminate | |
80 | + */ | |
81 | +void device_link_wait_removal(void) | |
82 | +{ | |
83 | + /* | |
84 | + * devlink removal jobs are queued in the dedicated work queue. | |
85 | + * To be sure that all removal jobs are terminated, ensure that any | |
86 | + * scheduled work has run to completion. | |
87 | + */ | |
88 | + flush_workqueue(device_link_wq); | |
89 | +} | |
90 | +EXPORT_SYMBOL_GPL(device_link_wait_removal); | |
91 | + | |
92 | static struct class devlink_class = { | |
93 | .name = "devlink", | |
94 | .dev_groups = devlink_groups, | |
95 | @@ -4090,9 +4105,14 @@ int __init devices_init(void) | |
96 | sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj); | |
97 | if (!sysfs_dev_char_kobj) | |
98 | goto char_kobj_err; | |
99 | + device_link_wq = alloc_workqueue("device_link_wq", 0, 0); | |
100 | + if (!device_link_wq) | |
101 | + goto wq_err; | |
102 | ||
103 | return 0; | |
104 | ||
105 | + wq_err: | |
106 | + kobject_put(sysfs_dev_char_kobj); | |
107 | char_kobj_err: | |
108 | kobject_put(sysfs_dev_block_kobj); | |
109 | block_kobj_err: | |
110 | --- a/include/linux/device.h | |
111 | +++ b/include/linux/device.h | |
112 | @@ -1250,6 +1250,7 @@ void device_link_del(struct device_link | |
113 | void device_link_remove(void *consumer, struct device *supplier); | |
114 | void device_links_supplier_sync_state_pause(void); | |
115 | void device_links_supplier_sync_state_resume(void); | |
116 | +void device_link_wait_removal(void); | |
117 | ||
118 | /* Create alias, so I can be autoloaded. */ | |
119 | #define MODULE_ALIAS_CHARDEV(major,minor) \ |