devlink: Hold devlink lock on health reporter dump get

Devlink health dump get callback should take devlink lock as any other
devlink callback. Otherwise, since devlink_mutex was removed, this
callback is not protected from a race of the reporter being destroyed
while handling the callback.

Add devlink lock to the callback and to any call for
devlink_health_do_dump(). This should be safe as non of the drivers dump
callback implementation takes devlink lock.

As devlink lock is added to any callback of dump, the reporter dump_lock
is now redundant and can be removed.

Fixes: d3efc2a6a6 ("net: devlink: remove devlink_mutex")
Signed-off-by: Moshe Shemesh <moshe@nvidia.com>
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
Link: https://lore.kernel.org/r/1696510216-189379-1-git-send-email-moshe@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Moshe Shemesh 2023-10-05 15:50:16 +03:00 committed by Jakub Kicinski
parent c4d49196ce
commit aba0e909dc
1 changed files with 16 additions and 14 deletions

View File

@ -58,7 +58,6 @@ struct devlink_health_reporter {
struct devlink *devlink; struct devlink *devlink;
struct devlink_port *devlink_port; struct devlink_port *devlink_port;
struct devlink_fmsg *dump_fmsg; struct devlink_fmsg *dump_fmsg;
struct mutex dump_lock; /* lock parallel read/write from dump buffers */
u64 graceful_period; u64 graceful_period;
bool auto_recover; bool auto_recover;
bool auto_dump; bool auto_dump;
@ -125,7 +124,6 @@ __devlink_health_reporter_create(struct devlink *devlink,
reporter->graceful_period = graceful_period; reporter->graceful_period = graceful_period;
reporter->auto_recover = !!ops->recover; reporter->auto_recover = !!ops->recover;
reporter->auto_dump = !!ops->dump; reporter->auto_dump = !!ops->dump;
mutex_init(&reporter->dump_lock);
return reporter; return reporter;
} }
@ -226,7 +224,6 @@ EXPORT_SYMBOL_GPL(devlink_health_reporter_create);
static void static void
devlink_health_reporter_free(struct devlink_health_reporter *reporter) devlink_health_reporter_free(struct devlink_health_reporter *reporter)
{ {
mutex_destroy(&reporter->dump_lock);
if (reporter->dump_fmsg) if (reporter->dump_fmsg)
devlink_fmsg_free(reporter->dump_fmsg); devlink_fmsg_free(reporter->dump_fmsg);
kfree(reporter); kfree(reporter);
@ -625,10 +622,10 @@ int devlink_health_report(struct devlink_health_reporter *reporter,
} }
if (reporter->auto_dump) { if (reporter->auto_dump) {
mutex_lock(&reporter->dump_lock); devl_lock(devlink);
/* store current dump of current error, for later analysis */ /* store current dump of current error, for later analysis */
devlink_health_do_dump(reporter, priv_ctx, NULL); devlink_health_do_dump(reporter, priv_ctx, NULL);
mutex_unlock(&reporter->dump_lock); devl_unlock(devlink);
} }
if (!reporter->auto_recover) if (!reporter->auto_recover)
@ -1262,7 +1259,7 @@ out:
} }
static struct devlink_health_reporter * static struct devlink_health_reporter *
devlink_health_reporter_get_from_cb(struct netlink_callback *cb) devlink_health_reporter_get_from_cb_lock(struct netlink_callback *cb)
{ {
const struct genl_info *info = genl_info_dump(cb); const struct genl_info *info = genl_info_dump(cb);
struct devlink_health_reporter *reporter; struct devlink_health_reporter *reporter;
@ -1272,10 +1269,12 @@ devlink_health_reporter_get_from_cb(struct netlink_callback *cb)
devlink = devlink_get_from_attrs_lock(sock_net(cb->skb->sk), attrs); devlink = devlink_get_from_attrs_lock(sock_net(cb->skb->sk), attrs);
if (IS_ERR(devlink)) if (IS_ERR(devlink))
return NULL; return NULL;
devl_unlock(devlink);
reporter = devlink_health_reporter_get_from_attrs(devlink, attrs); reporter = devlink_health_reporter_get_from_attrs(devlink, attrs);
devlink_put(devlink); if (!reporter) {
devl_unlock(devlink);
devlink_put(devlink);
}
return reporter; return reporter;
} }
@ -1284,16 +1283,20 @@ int devlink_nl_cmd_health_reporter_dump_get_dumpit(struct sk_buff *skb,
{ {
struct devlink_nl_dump_state *state = devlink_dump_state(cb); struct devlink_nl_dump_state *state = devlink_dump_state(cb);
struct devlink_health_reporter *reporter; struct devlink_health_reporter *reporter;
struct devlink *devlink;
int err; int err;
reporter = devlink_health_reporter_get_from_cb(cb); reporter = devlink_health_reporter_get_from_cb_lock(cb);
if (!reporter) if (!reporter)
return -EINVAL; return -EINVAL;
if (!reporter->ops->dump) devlink = reporter->devlink;
if (!reporter->ops->dump) {
devl_unlock(devlink);
devlink_put(devlink);
return -EOPNOTSUPP; return -EOPNOTSUPP;
}
mutex_lock(&reporter->dump_lock);
if (!state->idx) { if (!state->idx) {
err = devlink_health_do_dump(reporter, NULL, cb->extack); err = devlink_health_do_dump(reporter, NULL, cb->extack);
if (err) if (err)
@ -1309,7 +1312,8 @@ int devlink_nl_cmd_health_reporter_dump_get_dumpit(struct sk_buff *skb,
err = devlink_fmsg_dumpit(reporter->dump_fmsg, skb, cb, err = devlink_fmsg_dumpit(reporter->dump_fmsg, skb, cb,
DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET); DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET);
unlock: unlock:
mutex_unlock(&reporter->dump_lock); devl_unlock(devlink);
devlink_put(devlink);
return err; return err;
} }
@ -1326,9 +1330,7 @@ int devlink_nl_cmd_health_reporter_dump_clear_doit(struct sk_buff *skb,
if (!reporter->ops->dump) if (!reporter->ops->dump)
return -EOPNOTSUPP; return -EOPNOTSUPP;
mutex_lock(&reporter->dump_lock);
devlink_health_dump_clear(reporter); devlink_health_dump_clear(reporter);
mutex_unlock(&reporter->dump_lock);
return 0; return 0;
} }