diff options
author | Jakub Kicinski <kuba@kernel.org> | 2021-06-19 05:47:02 +0300 |
---|---|---|
committer | Jakub Kicinski <kuba@kernel.org> | 2021-06-19 05:47:02 +0300 |
commit | adc2e56ebe6377f5c032d96aee0feac30a640453 (patch) | |
tree | f8937ffc72e1991418b0d54a0672766237855c94 /drivers/base/core.c | |
parent | 4bea7207a80c8bba3b3eb5b84c407b162968475f (diff) | |
parent | 9ed13a17e38e0537e24d9b507645002bf8d0201f (diff) | |
download | linux-adc2e56ebe6377f5c032d96aee0feac30a640453.tar.xz |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
Trivial conflicts in net/can/isotp.c and
tools/testing/selftests/net/mptcp/mptcp_connect.sh
scaled_ppm_to_ppb() was moved from drivers/ptp/ptp_clock.c
to include/linux/ptp_clock_kernel.h in -next so re-apply
the fix there.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'drivers/base/core.c')
-rw-r--r-- | drivers/base/core.c | 68 |
1 files changed, 36 insertions, 32 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c index b6836bfa985c..2a61003ea2c1 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -194,6 +194,17 @@ int device_links_read_lock_held(void) { return srcu_read_lock_held(&device_links_srcu); } + +static void device_link_synchronize_removal(void) +{ + synchronize_srcu(&device_links_srcu); +} + +static void device_link_remove_from_lists(struct device_link *link) +{ + list_del_rcu(&link->s_node); + list_del_rcu(&link->c_node); +} #else /* !CONFIG_SRCU */ static DECLARE_RWSEM(device_links_lock); @@ -224,6 +235,16 @@ int device_links_read_lock_held(void) return lockdep_is_held(&device_links_lock); } #endif + +static inline void device_link_synchronize_removal(void) +{ +} + +static void device_link_remove_from_lists(struct device_link *link) +{ + list_del(&link->s_node); + list_del(&link->c_node); +} #endif /* !CONFIG_SRCU */ static bool device_is_ancestor(struct device *dev, struct device *target) @@ -445,8 +466,13 @@ static struct attribute *devlink_attrs[] = { }; ATTRIBUTE_GROUPS(devlink); -static void device_link_free(struct device_link *link) +static void device_link_release_fn(struct work_struct *work) { + struct device_link *link = container_of(work, struct device_link, rm_work); + + /* Ensure that all references to the link object have been dropped. */ + device_link_synchronize_removal(); + while (refcount_dec_not_one(&link->rpm_active)) pm_runtime_put(link->supplier); @@ -455,24 +481,19 @@ static void device_link_free(struct device_link *link) kfree(link); } -#ifdef CONFIG_SRCU -static void __device_link_free_srcu(struct rcu_head *rhead) -{ - device_link_free(container_of(rhead, struct device_link, rcu_head)); -} - static void devlink_dev_release(struct device *dev) { struct device_link *link = to_devlink(dev); - call_srcu(&device_links_srcu, &link->rcu_head, __device_link_free_srcu); -} -#else -static void devlink_dev_release(struct device *dev) -{ - device_link_free(to_devlink(dev)); + INIT_WORK(&link->rm_work, device_link_release_fn); + /* + * It may take a while to complete this work because of the SRCU + * synchronization in device_link_release_fn() and if the consumer or + * supplier devices get deleted when it runs, so put it into the "long" + * workqueue. + */ + queue_work(system_long_wq, &link->rm_work); } -#endif static struct class devlink_class = { .name = "devlink", @@ -846,7 +867,6 @@ out: } EXPORT_SYMBOL_GPL(device_link_add); -#ifdef CONFIG_SRCU static void __device_link_del(struct kref *kref) { struct device_link *link = container_of(kref, struct device_link, kref); @@ -856,25 +876,9 @@ static void __device_link_del(struct kref *kref) pm_runtime_drop_link(link); - list_del_rcu(&link->s_node); - list_del_rcu(&link->c_node); - device_unregister(&link->link_dev); -} -#else /* !CONFIG_SRCU */ -static void __device_link_del(struct kref *kref) -{ - struct device_link *link = container_of(kref, struct device_link, kref); - - dev_info(link->consumer, "Dropping the link to %s\n", - dev_name(link->supplier)); - - pm_runtime_drop_link(link); - - list_del(&link->s_node); - list_del(&link->c_node); + device_link_remove_from_lists(link); device_unregister(&link->link_dev); } -#endif /* !CONFIG_SRCU */ static void device_link_put_kref(struct device_link *link) { |