Commit f4f54166 authored by Vladyslav Tarasiuk's avatar Vladyslav Tarasiuk Committed by David S. Miller
Browse files

devlink: Implement devlink health reporters on per-port basis



Add devlink-health reporter support on per-port basis.
The main difference existing devlink-health is that port reporters are
stored in per-devlink_port lists. Upon creation of such health reporter the
reference to a port it belongs to is stored in reporter struct.

Fill the port index attribute in devlink-health response to
allow devlink userspace utility to distinguish between device and port
reporters.

Signed-off-by: default avatarVladyslav Tarasiuk <vladyslavt@mellanox.com>
Reviewed-by: default avatarMoshe Shemesh <moshe@mellanox.com>
Reviewed-by: default avatarJiri Pirko <jiri@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent bd821005
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -101,6 +101,8 @@ struct devlink_port {
	u8 attrs_set:1,
	   switch_port:1;
	struct delayed_work type_warn_dw;
	struct list_head reporter_list;
	struct mutex reporters_lock; /* Protects reporter_list */
};

struct devlink_sb_pool_info {
+77 −17
Original line number Diff line number Diff line
@@ -388,17 +388,19 @@ devlink_region_snapshot_get_by_id(struct devlink_region *region, u32 id)

#define DEVLINK_NL_FLAG_NEED_DEVLINK		BIT(0)
#define DEVLINK_NL_FLAG_NEED_PORT		BIT(1)
#define DEVLINK_NL_FLAG_NEED_SB		BIT(2)
#define DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT	BIT(2)
#define DEVLINK_NL_FLAG_NEED_SB			BIT(3)

/* The per devlink instance lock is taken by default in the pre-doit
 * operation, yet several commands do not require this. The global
 * devlink lock is taken and protects from disruption by user-calls.
 */
#define DEVLINK_NL_FLAG_NO_LOCK		BIT(3)
#define DEVLINK_NL_FLAG_NO_LOCK			BIT(4)

static int devlink_nl_pre_doit(const struct genl_ops *ops,
			       struct sk_buff *skb, struct genl_info *info)
{
	struct devlink_port *devlink_port;
	struct devlink *devlink;
	int err;

@@ -413,14 +415,17 @@ static int devlink_nl_pre_doit(const struct genl_ops *ops,
	if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_DEVLINK) {
		info->user_ptr[0] = devlink;
	} else if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_PORT) {
		struct devlink_port *devlink_port;

		devlink_port = devlink_port_get_from_info(devlink, info);
		if (IS_ERR(devlink_port)) {
			err = PTR_ERR(devlink_port);
			goto unlock;
		}
		info->user_ptr[0] = devlink_port;
	} else if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT) {
		info->user_ptr[0] = devlink;
		devlink_port = devlink_port_get_from_info(devlink, info);
		if (!IS_ERR(devlink_port))
			info->user_ptr[1] = devlink_port;
	}
	if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_SB) {
		struct devlink_sb *devlink_sb;
@@ -5287,6 +5292,7 @@ struct devlink_health_reporter {
	void *priv;
	const struct devlink_health_reporter_ops *ops;
	struct devlink *devlink;
	struct devlink_port *devlink_port;
	struct devlink_fmsg *dump_fmsg;
	struct mutex dump_lock; /* lock parallel read/write from dump buffers */
	u64 graceful_period;
@@ -5331,6 +5337,15 @@ devlink_health_reporter_find_by_name(struct devlink *devlink,
						      reporter_name);
}

static struct devlink_health_reporter *
devlink_port_health_reporter_find_by_name(struct devlink_port *devlink_port,
					  const char *reporter_name)
{
	return __devlink_health_reporter_find_by_name(&devlink_port->reporter_list,
						      &devlink_port->reporters_lock,
						      reporter_name);
}

static struct devlink_health_reporter *
__devlink_health_reporter_create(struct devlink *devlink,
				 const struct devlink_health_reporter_ops *ops,
@@ -5443,6 +5458,10 @@ devlink_nl_health_reporter_fill(struct sk_buff *msg,
	if (devlink_nl_put_handle(msg, devlink))
		goto genlmsg_cancel;

	if (reporter->devlink_port) {
		if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, reporter->devlink_port->index))
			goto genlmsg_cancel;
	}
	reporter_attr = nla_nest_start_noflag(msg,
					      DEVLINK_ATTR_HEALTH_REPORTER);
	if (!reporter_attr)
@@ -5650,17 +5669,28 @@ devlink_health_reporter_get_from_attrs(struct devlink *devlink,
				       struct nlattr **attrs)
{
	struct devlink_health_reporter *reporter;
	struct devlink_port *devlink_port;
	char *reporter_name;

	if (!attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME])
		return NULL;

	reporter_name = nla_data(attrs[DEVLINK_ATTR_HEALTH_REPORTER_NAME]);
	devlink_port = devlink_port_get_from_attrs(devlink, attrs);
	if (IS_ERR(devlink_port)) {
		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);
	} else {
		mutex_lock(&devlink_port->reporters_lock);
		reporter = devlink_port_health_reporter_find_by_name(devlink_port, reporter_name);
		if (reporter)
			refcount_inc(&reporter->refcount);
		mutex_unlock(&devlink_port->reporters_lock);
	}

	return reporter;
}

@@ -5748,6 +5778,7 @@ devlink_nl_cmd_health_reporter_get_dumpit(struct sk_buff *msg,
					  struct netlink_callback *cb)
{
	struct devlink_health_reporter *reporter;
	struct devlink_port *port;
	struct devlink *devlink;
	int start = cb->args[0];
	int idx = 0;
@@ -5778,6 +5809,31 @@ devlink_nl_cmd_health_reporter_get_dumpit(struct sk_buff *msg,
		}
		mutex_unlock(&devlink->reporters_lock);
	}

	list_for_each_entry(devlink, &devlink_list, list) {
		if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
			continue;
		list_for_each_entry(port, &devlink->port_list, list) {
			mutex_lock(&port->reporters_lock);
			list_for_each_entry(reporter, &port->reporter_list, list) {
				if (idx < start) {
					idx++;
					continue;
				}
				err = devlink_nl_health_reporter_fill(msg, devlink, reporter,
								      DEVLINK_CMD_HEALTH_REPORTER_GET,
								      NETLINK_CB(cb->skb).portid,
								      cb->nlh->nlmsg_seq,
								      NLM_F_MULTI);
				if (err) {
					mutex_unlock(&port->reporters_lock);
					goto out;
				}
				idx++;
			}
			mutex_unlock(&port->reporters_lock);
		}
	}
out:
	mutex_unlock(&devlink_mutex);

@@ -7157,7 +7213,7 @@ static const struct genl_ops devlink_nl_ops[] = {
		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
		.doit = devlink_nl_cmd_health_reporter_get_doit,
		.dumpit = devlink_nl_cmd_health_reporter_get_dumpit,
		.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
		.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT |
				  DEVLINK_NL_FLAG_NO_LOCK,
		/* can be retrieved by unprivileged users */
	},
@@ -7166,7 +7222,7 @@ static const struct genl_ops devlink_nl_ops[] = {
		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
		.doit = devlink_nl_cmd_health_reporter_set_doit,
		.flags = GENL_ADMIN_PERM,
		.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
		.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT |
				  DEVLINK_NL_FLAG_NO_LOCK,
	},
	{
@@ -7174,7 +7230,7 @@ static const struct genl_ops devlink_nl_ops[] = {
		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
		.doit = devlink_nl_cmd_health_reporter_recover_doit,
		.flags = GENL_ADMIN_PERM,
		.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
		.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT |
				  DEVLINK_NL_FLAG_NO_LOCK,
	},
	{
@@ -7182,7 +7238,7 @@ static const struct genl_ops devlink_nl_ops[] = {
		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
		.doit = devlink_nl_cmd_health_reporter_diagnose_doit,
		.flags = GENL_ADMIN_PERM,
		.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
		.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT |
				  DEVLINK_NL_FLAG_NO_LOCK,
	},
	{
@@ -7191,7 +7247,7 @@ static const struct genl_ops devlink_nl_ops[] = {
			    GENL_DONT_VALIDATE_DUMP_STRICT,
		.dumpit = devlink_nl_cmd_health_reporter_dump_get_dumpit,
		.flags = GENL_ADMIN_PERM,
		.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
		.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT |
				  DEVLINK_NL_FLAG_NO_LOCK,
	},
	{
@@ -7199,7 +7255,7 @@ static const struct genl_ops devlink_nl_ops[] = {
		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
		.doit = devlink_nl_cmd_health_reporter_dump_clear_doit,
		.flags = GENL_ADMIN_PERM,
		.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
		.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT |
				  DEVLINK_NL_FLAG_NO_LOCK,
	},
	{
@@ -7459,6 +7515,8 @@ int devlink_port_register(struct devlink *devlink,
	list_add_tail(&devlink_port->list, &devlink->port_list);
	INIT_LIST_HEAD(&devlink_port->param_list);
	mutex_unlock(&devlink->lock);
	INIT_LIST_HEAD(&devlink_port->reporter_list);
	mutex_init(&devlink_port->reporters_lock);
	INIT_DELAYED_WORK(&devlink_port->type_warn_dw, &devlink_port_type_warn);
	devlink_port_type_warn_schedule(devlink_port);
	devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW);
@@ -7475,6 +7533,8 @@ void devlink_port_unregister(struct devlink_port *devlink_port)
{
	struct devlink *devlink = devlink_port->devlink;

	WARN_ON(!list_empty(&devlink_port->reporter_list));
	mutex_destroy(&devlink_port->reporters_lock);
	devlink_port_type_warn_cancel(devlink_port);
	devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_DEL);
	mutex_lock(&devlink->lock);