Skip to content

Commit

Permalink
Merge branch 'devlink-unregister'
Browse files Browse the repository at this point in the history
Jakub Kicinski says:

====================
devlink: remove the wait-for-references on unregister

Move the registration and unregistration of the devlink instances
under their instance locks. Don't perform the netdev-style wait
for all references when unregistering the instance.

Instead the devlink instance refcount will only ensure that
the memory of the instance is not freed. All places which acquire
access to devlink instances via a reference must check that the
instance is still registered under the instance lock.

This fixes the problem of the netdev code accessing devlink
instances before they are registered.

RFC: https://lore.kernel.org/all/20221217011953.152487-1-kuba@kernel.org/
 - rewrite the cover letter
 - rewrite the commit message for patch 1
 - un-export and rename devl_is_alive
 - squash the netdevsim patches
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Jan 6, 2023
2 parents 6b754d7 + 82a3aef commit 6bd4755
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 112 deletions.
15 changes: 10 additions & 5 deletions drivers/net/netdevsim/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -1556,14 +1556,18 @@ int nsim_drv_probe(struct nsim_bus_dev *nsim_bus_dev)
goto err_devlink_unlock;
}

err = nsim_dev_resources_register(devlink);
err = devl_register(devlink);
if (err)
goto err_vfc_free;

err = nsim_dev_resources_register(devlink);
if (err)
goto err_dl_unregister;

err = devlink_params_register(devlink, nsim_devlink_params,
ARRAY_SIZE(nsim_devlink_params));
if (err)
goto err_dl_unregister;
goto err_resource_unregister;
nsim_devlink_set_params_init_values(nsim_dev, devlink);

err = nsim_dev_dummy_region_init(nsim_dev, devlink);
Expand Down Expand Up @@ -1607,7 +1611,6 @@ int nsim_drv_probe(struct nsim_bus_dev *nsim_bus_dev)
nsim_dev->esw_mode = DEVLINK_ESWITCH_MODE_LEGACY;
devlink_set_features(devlink, DEVLINK_F_RELOAD);
devl_unlock(devlink);
devlink_register(devlink);
return 0;

err_hwstats_exit:
Expand All @@ -1629,8 +1632,10 @@ int nsim_drv_probe(struct nsim_bus_dev *nsim_bus_dev)
err_params_unregister:
devlink_params_unregister(devlink, nsim_devlink_params,
ARRAY_SIZE(nsim_devlink_params));
err_dl_unregister:
err_resource_unregister:
devl_resources_unregister(devlink);
err_dl_unregister:
devl_unregister(devlink);
err_vfc_free:
kfree(nsim_dev->vfconfigs);
err_devlink_unlock:
Expand Down Expand Up @@ -1668,7 +1673,6 @@ void nsim_drv_remove(struct nsim_bus_dev *nsim_bus_dev)
struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
struct devlink *devlink = priv_to_devlink(nsim_dev);

devlink_unregister(devlink);
devl_lock(devlink);
nsim_dev_reload_destroy(nsim_dev);

Expand All @@ -1677,6 +1681,7 @@ void nsim_drv_remove(struct nsim_bus_dev *nsim_bus_dev)
devlink_params_unregister(devlink, nsim_devlink_params,
ARRAY_SIZE(nsim_devlink_params));
devl_resources_unregister(devlink);
devl_unregister(devlink);
kfree(nsim_dev->vfconfigs);
kfree(nsim_dev->fa_cookie);
devl_unlock(devlink);
Expand Down
2 changes: 2 additions & 0 deletions include/net/devlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -1647,6 +1647,8 @@ static inline struct devlink *devlink_alloc(const struct devlink_ops *ops,
return devlink_alloc_ns(ops, priv_size, &init_net, dev);
}
void devlink_set_features(struct devlink *devlink, u64 features);
int devl_register(struct devlink *devlink);
void devl_unregister(struct devlink *devlink);
void devlink_register(struct devlink *devlink);
void devlink_unregister(struct devlink *devlink);
void devlink_free(struct devlink *devlink);
Expand Down
121 changes: 55 additions & 66 deletions net/devlink/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,73 +67,51 @@ void devl_unlock(struct devlink *devlink)
}
EXPORT_SYMBOL_GPL(devl_unlock);

/**
* devlink_try_get() - try to obtain a reference on a devlink instance
* @devlink: instance to reference
*
* Obtain a reference on a devlink instance. A reference on a devlink instance
* only implies that it's safe to take the instance lock. It does not imply
* that the instance is registered, use devl_is_registered() after taking
* the instance lock to check registration status.
*/
struct devlink *__must_check devlink_try_get(struct devlink *devlink)
{
if (refcount_inc_not_zero(&devlink->refcount))
return devlink;
return NULL;
}

static void __devlink_put_rcu(struct rcu_head *head)
{
struct devlink *devlink = container_of(head, struct devlink, rcu);

complete(&devlink->comp);
}

void devlink_put(struct devlink *devlink)
{
if (refcount_dec_and_test(&devlink->refcount))
/* Make sure unregister operation that may await the completion
* is unblocked only after all users are after the end of
* RCU grace period.
*/
call_rcu(&devlink->rcu, __devlink_put_rcu);
kfree_rcu(devlink, rcu);
}

struct devlink *
devlinks_xa_find_get(struct net *net, unsigned long *indexp,
void * (*xa_find_fn)(struct xarray *, unsigned long *,
unsigned long, xa_mark_t))
struct devlink *devlinks_xa_find_get(struct net *net, unsigned long *indexp)
{
struct devlink *devlink;
struct devlink *devlink = NULL;

rcu_read_lock();
retry:
devlink = xa_find_fn(&devlinks, indexp, ULONG_MAX, DEVLINK_REGISTERED);
devlink = xa_find(&devlinks, indexp, ULONG_MAX, DEVLINK_REGISTERED);
if (!devlink)
goto unlock;

/* In case devlink_unregister() was already called and "unregistering"
* mark was set, do not allow to get a devlink reference here.
* This prevents live-lock of devlink_unregister() wait for completion.
*/
if (xa_get_mark(&devlinks, *indexp, DEVLINK_UNREGISTERING))
goto retry;

/* For a possible retry, the xa_find_after() should be always used */
xa_find_fn = xa_find_after;
if (!devlink_try_get(devlink))
goto retry;
goto next;
if (!net_eq(devlink_net(devlink), net)) {
devlink_put(devlink);
goto retry;
goto next;
}
unlock:
rcu_read_unlock();
return devlink;
}

struct devlink *
devlinks_xa_find_get_first(struct net *net, unsigned long *indexp)
{
return devlinks_xa_find_get(net, indexp, xa_find);
}

struct devlink *
devlinks_xa_find_get_next(struct net *net, unsigned long *indexp)
{
return devlinks_xa_find_get(net, indexp, xa_find_after);
next:
(*indexp)++;
goto retry;
}

/**
Expand All @@ -147,46 +125,55 @@ devlinks_xa_find_get_next(struct net *net, unsigned long *indexp)
*/
void devlink_set_features(struct devlink *devlink, u64 features)
{
ASSERT_DEVLINK_NOT_REGISTERED(devlink);

WARN_ON(features & DEVLINK_F_RELOAD &&
!devlink_reload_supported(devlink->ops));
devlink->features = features;
}
EXPORT_SYMBOL_GPL(devlink_set_features);

/**
* devlink_register - Register devlink instance
*
* @devlink: devlink
* devl_register - Register devlink instance
* @devlink: devlink
*/
void devlink_register(struct devlink *devlink)
int devl_register(struct devlink *devlink)
{
ASSERT_DEVLINK_NOT_REGISTERED(devlink);
/* Make sure that we are in .probe() routine */
devl_assert_locked(devlink);

xa_set_mark(&devlinks, devlink->index, DEVLINK_REGISTERED);
devlink_notify_register(devlink);

return 0;
}
EXPORT_SYMBOL_GPL(devl_register);

void devlink_register(struct devlink *devlink)
{
devl_lock(devlink);
devl_register(devlink);
devl_unlock(devlink);
}
EXPORT_SYMBOL_GPL(devlink_register);

/**
* devlink_unregister - Unregister devlink instance
*
* @devlink: devlink
* devl_unregister - Unregister devlink instance
* @devlink: devlink
*/
void devlink_unregister(struct devlink *devlink)
void devl_unregister(struct devlink *devlink)
{
ASSERT_DEVLINK_REGISTERED(devlink);
/* Make sure that we are in .remove() routine */

xa_set_mark(&devlinks, devlink->index, DEVLINK_UNREGISTERING);
devlink_put(devlink);
wait_for_completion(&devlink->comp);
devl_assert_locked(devlink);

devlink_notify_unregister(devlink);
xa_clear_mark(&devlinks, devlink->index, DEVLINK_REGISTERED);
xa_clear_mark(&devlinks, devlink->index, DEVLINK_UNREGISTERING);
}
EXPORT_SYMBOL_GPL(devl_unregister);

void devlink_unregister(struct devlink *devlink)
{
devl_lock(devlink);
devl_unregister(devlink);
devl_unlock(devlink);
}
EXPORT_SYMBOL_GPL(devlink_unregister);

Expand Down Expand Up @@ -250,7 +237,6 @@ struct devlink *devlink_alloc_ns(const struct devlink_ops *ops,
mutex_init(&devlink->reporters_lock);
mutex_init(&devlink->linecards_lock);
refcount_set(&devlink->refcount, 1);
init_completion(&devlink->comp);

return devlink;

Expand Down Expand Up @@ -296,7 +282,7 @@ void devlink_free(struct devlink *devlink)

xa_erase(&devlinks, devlink->index);

kfree(devlink);
devlink_put(devlink);
}
EXPORT_SYMBOL_GPL(devlink_free);

Expand All @@ -312,15 +298,18 @@ static void __net_exit devlink_pernet_pre_exit(struct net *net)
*/
devlinks_xa_for_each_registered_get(net, index, devlink) {
WARN_ON(!(devlink->features & DEVLINK_F_RELOAD));
mutex_lock(&devlink->lock);
err = devlink_reload(devlink, &init_net,
DEVLINK_RELOAD_ACTION_DRIVER_REINIT,
DEVLINK_RELOAD_LIMIT_UNSPEC,
&actions_performed, NULL);
mutex_unlock(&devlink->lock);
devl_lock(devlink);
err = 0;
if (devl_is_registered(devlink))
err = devlink_reload(devlink, &init_net,
DEVLINK_RELOAD_ACTION_DRIVER_REINIT,
DEVLINK_RELOAD_LIMIT_UNSPEC,
&actions_performed, NULL);
devl_unlock(devlink);
devlink_put(devlink);

if (err && err != -EOPNOTSUPP)
pr_warn("Failed to reload devlink instance into init_net\n");
devlink_put(devlink);
}
}

Expand Down
28 changes: 13 additions & 15 deletions net/devlink/devl_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
#include <net/net_namespace.h>

#define DEVLINK_REGISTERED XA_MARK_1
#define DEVLINK_UNREGISTERING XA_MARK_2

#define DEVLINK_RELOAD_STATS_ARRAY_SIZE \
(__DEVLINK_RELOAD_LIMIT_MAX * __DEVLINK_RELOAD_ACTION_MAX)
Expand Down Expand Up @@ -52,7 +51,6 @@ struct devlink {
struct lock_class_key lock_key;
u8 reload_failed:1;
refcount_t refcount;
struct completion comp;
struct rcu_head rcu;
struct notifier_block netdevice_nb;
char priv[] __aligned(NETDEV_ALIGN);
Expand Down Expand Up @@ -82,18 +80,17 @@ extern struct genl_family devlink_nl_family;
* in loop body in order to release the reference.
*/
#define devlinks_xa_for_each_registered_get(net, index, devlink) \
for (index = 0, \
devlink = devlinks_xa_find_get_first(net, &index); \
devlink; devlink = devlinks_xa_find_get_next(net, &index))
for (index = 0; (devlink = devlinks_xa_find_get(net, &index)); index++)

struct devlink *
devlinks_xa_find_get(struct net *net, unsigned long *indexp,
void * (*xa_find_fn)(struct xarray *, unsigned long *,
unsigned long, xa_mark_t));
struct devlink *
devlinks_xa_find_get_first(struct net *net, unsigned long *indexp);
struct devlink *
devlinks_xa_find_get_next(struct net *net, unsigned long *indexp);
struct devlink *devlinks_xa_find_get(struct net *net, unsigned long *indexp);

static inline bool devl_is_registered(struct devlink *devlink)
{
/* To prevent races the caller must hold the instance lock
* or another lock taken during unregistration.
*/
return xa_get_mark(&devlinks, devlink->index, DEVLINK_REGISTERED);
}

/* Netlink */
#define DEVLINK_NL_FLAG_NEED_PORT BIT(0)
Expand Down Expand Up @@ -135,12 +132,13 @@ struct devlink_gen_cmd {
*/
#define devlink_dump_for_each_instance_get(msg, state, devlink) \
for (; (devlink = devlinks_xa_find_get(sock_net(msg->sk), \
&state->instance, xa_find)); \
&state->instance)); \
state->instance++, state->idx = 0)

extern const struct genl_small_ops devlink_nl_ops[56];

struct devlink *devlink_get_from_attrs(struct net *net, struct nlattr **attrs);
struct devlink *
devlink_get_from_attrs_lock(struct net *net, struct nlattr **attrs);

void devlink_notify_unregister(struct devlink *devlink);
void devlink_notify_register(struct devlink *devlink);
Expand Down
Loading

0 comments on commit 6bd4755

Please sign in to comment.