mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-25 18:30:06 +00:00
devlink: Add health recover notifications on devlink flows
Devlink health recover notifications were added only on driver direct
updates of health_state through devlink_health_reporter_state_update().
Add notifications on updates of health_state by devlink flows of report
and recover.
Moved functions devlink_nl_health_reporter_fill() and
devlink_recover_notify() to avoid forward declaration.
Fixes: 97ff3bd37f
("devlink: add devink notification when reporter update health state")
Signed-off-by: Moshe Shemesh <moshe@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
53c6770095
commit
6ec8b6cd79
1 changed files with 176 additions and 174 deletions
|
@ -4843,180 +4843,6 @@ devlink_health_reporter_destroy(struct devlink_health_reporter *reporter)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(devlink_health_reporter_destroy);
|
EXPORT_SYMBOL_GPL(devlink_health_reporter_destroy);
|
||||||
|
|
||||||
void
|
|
||||||
devlink_health_reporter_recovery_done(struct devlink_health_reporter *reporter)
|
|
||||||
{
|
|
||||||
reporter->recovery_count++;
|
|
||||||
reporter->last_recovery_ts = jiffies;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(devlink_health_reporter_recovery_done);
|
|
||||||
|
|
||||||
static int
|
|
||||||
devlink_health_reporter_recover(struct devlink_health_reporter *reporter,
|
|
||||||
void *priv_ctx, struct netlink_ext_ack *extack)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
|
|
||||||
if (reporter->health_state == DEVLINK_HEALTH_REPORTER_STATE_HEALTHY)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (!reporter->ops->recover)
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
|
|
||||||
err = reporter->ops->recover(reporter, priv_ctx, extack);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
devlink_health_reporter_recovery_done(reporter);
|
|
||||||
reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_HEALTHY;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
devlink_health_dump_clear(struct devlink_health_reporter *reporter)
|
|
||||||
{
|
|
||||||
if (!reporter->dump_fmsg)
|
|
||||||
return;
|
|
||||||
devlink_fmsg_free(reporter->dump_fmsg);
|
|
||||||
reporter->dump_fmsg = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int devlink_health_do_dump(struct devlink_health_reporter *reporter,
|
|
||||||
void *priv_ctx,
|
|
||||||
struct netlink_ext_ack *extack)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
|
|
||||||
if (!reporter->ops->dump)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (reporter->dump_fmsg)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
reporter->dump_fmsg = devlink_fmsg_alloc();
|
|
||||||
if (!reporter->dump_fmsg) {
|
|
||||||
err = -ENOMEM;
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = devlink_fmsg_obj_nest_start(reporter->dump_fmsg);
|
|
||||||
if (err)
|
|
||||||
goto dump_err;
|
|
||||||
|
|
||||||
err = reporter->ops->dump(reporter, reporter->dump_fmsg,
|
|
||||||
priv_ctx, extack);
|
|
||||||
if (err)
|
|
||||||
goto dump_err;
|
|
||||||
|
|
||||||
err = devlink_fmsg_obj_nest_end(reporter->dump_fmsg);
|
|
||||||
if (err)
|
|
||||||
goto dump_err;
|
|
||||||
|
|
||||||
reporter->dump_ts = jiffies;
|
|
||||||
reporter->dump_real_ts = ktime_get_real_ns();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
dump_err:
|
|
||||||
devlink_health_dump_clear(reporter);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
int devlink_health_report(struct devlink_health_reporter *reporter,
|
|
||||||
const char *msg, void *priv_ctx)
|
|
||||||
{
|
|
||||||
enum devlink_health_reporter_state prev_health_state;
|
|
||||||
struct devlink *devlink = reporter->devlink;
|
|
||||||
|
|
||||||
/* write a log message of the current error */
|
|
||||||
WARN_ON(!msg);
|
|
||||||
trace_devlink_health_report(devlink, reporter->ops->name, msg);
|
|
||||||
reporter->error_count++;
|
|
||||||
prev_health_state = reporter->health_state;
|
|
||||||
reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_ERROR;
|
|
||||||
|
|
||||||
/* abort if the previous error wasn't recovered */
|
|
||||||
if (reporter->auto_recover &&
|
|
||||||
(prev_health_state != DEVLINK_HEALTH_REPORTER_STATE_HEALTHY ||
|
|
||||||
jiffies - reporter->last_recovery_ts <
|
|
||||||
msecs_to_jiffies(reporter->graceful_period))) {
|
|
||||||
trace_devlink_health_recover_aborted(devlink,
|
|
||||||
reporter->ops->name,
|
|
||||||
reporter->health_state,
|
|
||||||
jiffies -
|
|
||||||
reporter->last_recovery_ts);
|
|
||||||
return -ECANCELED;
|
|
||||||
}
|
|
||||||
|
|
||||||
reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_ERROR;
|
|
||||||
|
|
||||||
mutex_lock(&reporter->dump_lock);
|
|
||||||
/* store current dump of current error, for later analysis */
|
|
||||||
devlink_health_do_dump(reporter, priv_ctx, NULL);
|
|
||||||
mutex_unlock(&reporter->dump_lock);
|
|
||||||
|
|
||||||
if (reporter->auto_recover)
|
|
||||||
return devlink_health_reporter_recover(reporter,
|
|
||||||
priv_ctx, NULL);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(devlink_health_report);
|
|
||||||
|
|
||||||
static struct devlink_health_reporter *
|
|
||||||
devlink_health_reporter_get_from_attrs(struct devlink *devlink,
|
|
||||||
struct nlattr **attrs)
|
|
||||||
{
|
|
||||||
struct devlink_health_reporter *reporter;
|
|
||||||
char *reporter_name;
|
|
||||||
|
|
||||||
if (!attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME])
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
reporter_name = nla_data(attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME]);
|
|
||||||
mutex_lock(&devlink->reporters_lock);
|
|
||||||
reporter = devlink_health_reporter_find_by_name(devlink, reporter_name);
|
|
||||||
if (reporter)
|
|
||||||
refcount_inc(&reporter->refcount);
|
|
||||||
mutex_unlock(&devlink->reporters_lock);
|
|
||||||
return reporter;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct devlink_health_reporter *
|
|
||||||
devlink_health_reporter_get_from_info(struct devlink *devlink,
|
|
||||||
struct genl_info *info)
|
|
||||||
{
|
|
||||||
return devlink_health_reporter_get_from_attrs(devlink, info->attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct devlink_health_reporter *
|
|
||||||
devlink_health_reporter_get_from_cb(struct netlink_callback *cb)
|
|
||||||
{
|
|
||||||
const struct genl_dumpit_info *info = genl_dumpit_info(cb);
|
|
||||||
struct devlink_health_reporter *reporter;
|
|
||||||
struct nlattr **attrs = info->attrs;
|
|
||||||
struct devlink *devlink;
|
|
||||||
|
|
||||||
mutex_lock(&devlink_mutex);
|
|
||||||
devlink = devlink_get_from_attrs(sock_net(cb->skb->sk), attrs);
|
|
||||||
if (IS_ERR(devlink))
|
|
||||||
goto unlock;
|
|
||||||
|
|
||||||
reporter = devlink_health_reporter_get_from_attrs(devlink, attrs);
|
|
||||||
mutex_unlock(&devlink_mutex);
|
|
||||||
return reporter;
|
|
||||||
unlock:
|
|
||||||
mutex_unlock(&devlink_mutex);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
devlink_health_reporter_put(struct devlink_health_reporter *reporter)
|
|
||||||
{
|
|
||||||
refcount_dec(&reporter->refcount);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
devlink_nl_health_reporter_fill(struct sk_buff *msg,
|
devlink_nl_health_reporter_fill(struct sk_buff *msg,
|
||||||
struct devlink *devlink,
|
struct devlink *devlink,
|
||||||
|
@ -5104,6 +4930,182 @@ static void devlink_recover_notify(struct devlink_health_reporter *reporter,
|
||||||
msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
|
msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
devlink_health_reporter_recovery_done(struct devlink_health_reporter *reporter)
|
||||||
|
{
|
||||||
|
reporter->recovery_count++;
|
||||||
|
reporter->last_recovery_ts = jiffies;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(devlink_health_reporter_recovery_done);
|
||||||
|
|
||||||
|
static int
|
||||||
|
devlink_health_reporter_recover(struct devlink_health_reporter *reporter,
|
||||||
|
void *priv_ctx, struct netlink_ext_ack *extack)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (reporter->health_state == DEVLINK_HEALTH_REPORTER_STATE_HEALTHY)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!reporter->ops->recover)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
err = reporter->ops->recover(reporter, priv_ctx, extack);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
devlink_health_reporter_recovery_done(reporter);
|
||||||
|
reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_HEALTHY;
|
||||||
|
devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
devlink_health_dump_clear(struct devlink_health_reporter *reporter)
|
||||||
|
{
|
||||||
|
if (!reporter->dump_fmsg)
|
||||||
|
return;
|
||||||
|
devlink_fmsg_free(reporter->dump_fmsg);
|
||||||
|
reporter->dump_fmsg = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int devlink_health_do_dump(struct devlink_health_reporter *reporter,
|
||||||
|
void *priv_ctx,
|
||||||
|
struct netlink_ext_ack *extack)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!reporter->ops->dump)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (reporter->dump_fmsg)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
reporter->dump_fmsg = devlink_fmsg_alloc();
|
||||||
|
if (!reporter->dump_fmsg) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = devlink_fmsg_obj_nest_start(reporter->dump_fmsg);
|
||||||
|
if (err)
|
||||||
|
goto dump_err;
|
||||||
|
|
||||||
|
err = reporter->ops->dump(reporter, reporter->dump_fmsg,
|
||||||
|
priv_ctx, extack);
|
||||||
|
if (err)
|
||||||
|
goto dump_err;
|
||||||
|
|
||||||
|
err = devlink_fmsg_obj_nest_end(reporter->dump_fmsg);
|
||||||
|
if (err)
|
||||||
|
goto dump_err;
|
||||||
|
|
||||||
|
reporter->dump_ts = jiffies;
|
||||||
|
reporter->dump_real_ts = ktime_get_real_ns();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
dump_err:
|
||||||
|
devlink_health_dump_clear(reporter);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int devlink_health_report(struct devlink_health_reporter *reporter,
|
||||||
|
const char *msg, void *priv_ctx)
|
||||||
|
{
|
||||||
|
enum devlink_health_reporter_state prev_health_state;
|
||||||
|
struct devlink *devlink = reporter->devlink;
|
||||||
|
|
||||||
|
/* write a log message of the current error */
|
||||||
|
WARN_ON(!msg);
|
||||||
|
trace_devlink_health_report(devlink, reporter->ops->name, msg);
|
||||||
|
reporter->error_count++;
|
||||||
|
prev_health_state = reporter->health_state;
|
||||||
|
reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_ERROR;
|
||||||
|
devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
|
||||||
|
|
||||||
|
/* abort if the previous error wasn't recovered */
|
||||||
|
if (reporter->auto_recover &&
|
||||||
|
(prev_health_state != DEVLINK_HEALTH_REPORTER_STATE_HEALTHY ||
|
||||||
|
jiffies - reporter->last_recovery_ts <
|
||||||
|
msecs_to_jiffies(reporter->graceful_period))) {
|
||||||
|
trace_devlink_health_recover_aborted(devlink,
|
||||||
|
reporter->ops->name,
|
||||||
|
reporter->health_state,
|
||||||
|
jiffies -
|
||||||
|
reporter->last_recovery_ts);
|
||||||
|
return -ECANCELED;
|
||||||
|
}
|
||||||
|
|
||||||
|
reporter->health_state = DEVLINK_HEALTH_REPORTER_STATE_ERROR;
|
||||||
|
|
||||||
|
mutex_lock(&reporter->dump_lock);
|
||||||
|
/* store current dump of current error, for later analysis */
|
||||||
|
devlink_health_do_dump(reporter, priv_ctx, NULL);
|
||||||
|
mutex_unlock(&reporter->dump_lock);
|
||||||
|
|
||||||
|
if (reporter->auto_recover)
|
||||||
|
return devlink_health_reporter_recover(reporter,
|
||||||
|
priv_ctx, NULL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(devlink_health_report);
|
||||||
|
|
||||||
|
static struct devlink_health_reporter *
|
||||||
|
devlink_health_reporter_get_from_attrs(struct devlink *devlink,
|
||||||
|
struct nlattr **attrs)
|
||||||
|
{
|
||||||
|
struct devlink_health_reporter *reporter;
|
||||||
|
char *reporter_name;
|
||||||
|
|
||||||
|
if (!attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME])
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
reporter_name = nla_data(attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME]);
|
||||||
|
mutex_lock(&devlink->reporters_lock);
|
||||||
|
reporter = devlink_health_reporter_find_by_name(devlink, reporter_name);
|
||||||
|
if (reporter)
|
||||||
|
refcount_inc(&reporter->refcount);
|
||||||
|
mutex_unlock(&devlink->reporters_lock);
|
||||||
|
return reporter;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct devlink_health_reporter *
|
||||||
|
devlink_health_reporter_get_from_info(struct devlink *devlink,
|
||||||
|
struct genl_info *info)
|
||||||
|
{
|
||||||
|
return devlink_health_reporter_get_from_attrs(devlink, info->attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct devlink_health_reporter *
|
||||||
|
devlink_health_reporter_get_from_cb(struct netlink_callback *cb)
|
||||||
|
{
|
||||||
|
const struct genl_dumpit_info *info = genl_dumpit_info(cb);
|
||||||
|
struct devlink_health_reporter *reporter;
|
||||||
|
struct nlattr **attrs = info->attrs;
|
||||||
|
struct devlink *devlink;
|
||||||
|
|
||||||
|
mutex_lock(&devlink_mutex);
|
||||||
|
devlink = devlink_get_from_attrs(sock_net(cb->skb->sk), attrs);
|
||||||
|
if (IS_ERR(devlink))
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
reporter = devlink_health_reporter_get_from_attrs(devlink, attrs);
|
||||||
|
mutex_unlock(&devlink_mutex);
|
||||||
|
return reporter;
|
||||||
|
unlock:
|
||||||
|
mutex_unlock(&devlink_mutex);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
devlink_health_reporter_put(struct devlink_health_reporter *reporter)
|
||||||
|
{
|
||||||
|
refcount_dec(&reporter->refcount);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
devlink_health_reporter_state_update(struct devlink_health_reporter *reporter,
|
devlink_health_reporter_state_update(struct devlink_health_reporter *reporter,
|
||||||
enum devlink_health_reporter_state state)
|
enum devlink_health_reporter_state state)
|
||||||
|
|
Loading…
Reference in a new issue