162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
462306a36Sopenharmony_ci * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <net/genetlink.h>
862306a36Sopenharmony_ci#include <net/sock.h>
962306a36Sopenharmony_ci#include "devl_internal.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistruct devlink_info_req {
1262306a36Sopenharmony_ci	struct sk_buff *msg;
1362306a36Sopenharmony_ci	void (*version_cb)(const char *version_name,
1462306a36Sopenharmony_ci			   enum devlink_info_version_type version_type,
1562306a36Sopenharmony_ci			   void *version_cb_priv);
1662306a36Sopenharmony_ci	void *version_cb_priv;
1762306a36Sopenharmony_ci};
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistruct devlink_reload_combination {
2062306a36Sopenharmony_ci	enum devlink_reload_action action;
2162306a36Sopenharmony_ci	enum devlink_reload_limit limit;
2262306a36Sopenharmony_ci};
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic const struct devlink_reload_combination devlink_reload_invalid_combinations[] = {
2562306a36Sopenharmony_ci	{
2662306a36Sopenharmony_ci		/* can't reinitialize driver with no down time */
2762306a36Sopenharmony_ci		.action = DEVLINK_RELOAD_ACTION_DRIVER_REINIT,
2862306a36Sopenharmony_ci		.limit = DEVLINK_RELOAD_LIMIT_NO_RESET,
2962306a36Sopenharmony_ci	},
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic bool
3362306a36Sopenharmony_cidevlink_reload_combination_is_invalid(enum devlink_reload_action action,
3462306a36Sopenharmony_ci				      enum devlink_reload_limit limit)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	int i;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(devlink_reload_invalid_combinations); i++)
3962306a36Sopenharmony_ci		if (devlink_reload_invalid_combinations[i].action == action &&
4062306a36Sopenharmony_ci		    devlink_reload_invalid_combinations[i].limit == limit)
4162306a36Sopenharmony_ci			return true;
4262306a36Sopenharmony_ci	return false;
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic bool
4662306a36Sopenharmony_cidevlink_reload_action_is_supported(struct devlink *devlink, enum devlink_reload_action action)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	return test_bit(action, &devlink->ops->reload_actions);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic bool
5262306a36Sopenharmony_cidevlink_reload_limit_is_supported(struct devlink *devlink, enum devlink_reload_limit limit)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	return test_bit(limit, &devlink->ops->reload_limits);
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic int devlink_reload_stat_put(struct sk_buff *msg,
5862306a36Sopenharmony_ci				   enum devlink_reload_limit limit, u32 value)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct nlattr *reload_stats_entry;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	reload_stats_entry = nla_nest_start(msg, DEVLINK_ATTR_RELOAD_STATS_ENTRY);
6362306a36Sopenharmony_ci	if (!reload_stats_entry)
6462306a36Sopenharmony_ci		return -EMSGSIZE;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	if (nla_put_u8(msg, DEVLINK_ATTR_RELOAD_STATS_LIMIT, limit) ||
6762306a36Sopenharmony_ci	    nla_put_u32(msg, DEVLINK_ATTR_RELOAD_STATS_VALUE, value))
6862306a36Sopenharmony_ci		goto nla_put_failure;
6962306a36Sopenharmony_ci	nla_nest_end(msg, reload_stats_entry);
7062306a36Sopenharmony_ci	return 0;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cinla_put_failure:
7362306a36Sopenharmony_ci	nla_nest_cancel(msg, reload_stats_entry);
7462306a36Sopenharmony_ci	return -EMSGSIZE;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic int
7862306a36Sopenharmony_cidevlink_reload_stats_put(struct sk_buff *msg, struct devlink *devlink, bool is_remote)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct nlattr *reload_stats_attr, *act_info, *act_stats;
8162306a36Sopenharmony_ci	int i, j, stat_idx;
8262306a36Sopenharmony_ci	u32 value;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	if (!is_remote)
8562306a36Sopenharmony_ci		reload_stats_attr = nla_nest_start(msg, DEVLINK_ATTR_RELOAD_STATS);
8662306a36Sopenharmony_ci	else
8762306a36Sopenharmony_ci		reload_stats_attr = nla_nest_start(msg, DEVLINK_ATTR_REMOTE_RELOAD_STATS);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if (!reload_stats_attr)
9062306a36Sopenharmony_ci		return -EMSGSIZE;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	for (i = 0; i <= DEVLINK_RELOAD_ACTION_MAX; i++) {
9362306a36Sopenharmony_ci		if ((!is_remote &&
9462306a36Sopenharmony_ci		     !devlink_reload_action_is_supported(devlink, i)) ||
9562306a36Sopenharmony_ci		    i == DEVLINK_RELOAD_ACTION_UNSPEC)
9662306a36Sopenharmony_ci			continue;
9762306a36Sopenharmony_ci		act_info = nla_nest_start(msg, DEVLINK_ATTR_RELOAD_ACTION_INFO);
9862306a36Sopenharmony_ci		if (!act_info)
9962306a36Sopenharmony_ci			goto nla_put_failure;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci		if (nla_put_u8(msg, DEVLINK_ATTR_RELOAD_ACTION, i))
10262306a36Sopenharmony_ci			goto action_info_nest_cancel;
10362306a36Sopenharmony_ci		act_stats = nla_nest_start(msg, DEVLINK_ATTR_RELOAD_ACTION_STATS);
10462306a36Sopenharmony_ci		if (!act_stats)
10562306a36Sopenharmony_ci			goto action_info_nest_cancel;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci		for (j = 0; j <= DEVLINK_RELOAD_LIMIT_MAX; j++) {
10862306a36Sopenharmony_ci			/* Remote stats are shown even if not locally supported.
10962306a36Sopenharmony_ci			 * Stats of actions with unspecified limit are shown
11062306a36Sopenharmony_ci			 * though drivers don't need to register unspecified
11162306a36Sopenharmony_ci			 * limit.
11262306a36Sopenharmony_ci			 */
11362306a36Sopenharmony_ci			if ((!is_remote && j != DEVLINK_RELOAD_LIMIT_UNSPEC &&
11462306a36Sopenharmony_ci			     !devlink_reload_limit_is_supported(devlink, j)) ||
11562306a36Sopenharmony_ci			    devlink_reload_combination_is_invalid(i, j))
11662306a36Sopenharmony_ci				continue;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci			stat_idx = j * __DEVLINK_RELOAD_ACTION_MAX + i;
11962306a36Sopenharmony_ci			if (!is_remote)
12062306a36Sopenharmony_ci				value = devlink->stats.reload_stats[stat_idx];
12162306a36Sopenharmony_ci			else
12262306a36Sopenharmony_ci				value = devlink->stats.remote_reload_stats[stat_idx];
12362306a36Sopenharmony_ci			if (devlink_reload_stat_put(msg, j, value))
12462306a36Sopenharmony_ci				goto action_stats_nest_cancel;
12562306a36Sopenharmony_ci		}
12662306a36Sopenharmony_ci		nla_nest_end(msg, act_stats);
12762306a36Sopenharmony_ci		nla_nest_end(msg, act_info);
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci	nla_nest_end(msg, reload_stats_attr);
13062306a36Sopenharmony_ci	return 0;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ciaction_stats_nest_cancel:
13362306a36Sopenharmony_ci	nla_nest_cancel(msg, act_stats);
13462306a36Sopenharmony_ciaction_info_nest_cancel:
13562306a36Sopenharmony_ci	nla_nest_cancel(msg, act_info);
13662306a36Sopenharmony_cinla_put_failure:
13762306a36Sopenharmony_ci	nla_nest_cancel(msg, reload_stats_attr);
13862306a36Sopenharmony_ci	return -EMSGSIZE;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic int devlink_nl_fill(struct sk_buff *msg, struct devlink *devlink,
14262306a36Sopenharmony_ci			   enum devlink_command cmd, u32 portid,
14362306a36Sopenharmony_ci			   u32 seq, int flags)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	struct nlattr *dev_stats;
14662306a36Sopenharmony_ci	void *hdr;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
14962306a36Sopenharmony_ci	if (!hdr)
15062306a36Sopenharmony_ci		return -EMSGSIZE;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (devlink_nl_put_handle(msg, devlink))
15362306a36Sopenharmony_ci		goto nla_put_failure;
15462306a36Sopenharmony_ci	if (nla_put_u8(msg, DEVLINK_ATTR_RELOAD_FAILED, devlink->reload_failed))
15562306a36Sopenharmony_ci		goto nla_put_failure;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	dev_stats = nla_nest_start(msg, DEVLINK_ATTR_DEV_STATS);
15862306a36Sopenharmony_ci	if (!dev_stats)
15962306a36Sopenharmony_ci		goto nla_put_failure;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (devlink_reload_stats_put(msg, devlink, false))
16262306a36Sopenharmony_ci		goto dev_stats_nest_cancel;
16362306a36Sopenharmony_ci	if (devlink_reload_stats_put(msg, devlink, true))
16462306a36Sopenharmony_ci		goto dev_stats_nest_cancel;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	nla_nest_end(msg, dev_stats);
16762306a36Sopenharmony_ci	genlmsg_end(msg, hdr);
16862306a36Sopenharmony_ci	return 0;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cidev_stats_nest_cancel:
17162306a36Sopenharmony_ci	nla_nest_cancel(msg, dev_stats);
17262306a36Sopenharmony_cinla_put_failure:
17362306a36Sopenharmony_ci	genlmsg_cancel(msg, hdr);
17462306a36Sopenharmony_ci	return -EMSGSIZE;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic void devlink_notify(struct devlink *devlink, enum devlink_command cmd)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	struct sk_buff *msg;
18062306a36Sopenharmony_ci	int err;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	WARN_ON(cmd != DEVLINK_CMD_NEW && cmd != DEVLINK_CMD_DEL);
18362306a36Sopenharmony_ci	WARN_ON(!xa_get_mark(&devlinks, devlink->index, DEVLINK_REGISTERED));
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
18662306a36Sopenharmony_ci	if (!msg)
18762306a36Sopenharmony_ci		return;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	err = devlink_nl_fill(msg, devlink, cmd, 0, 0, 0);
19062306a36Sopenharmony_ci	if (err) {
19162306a36Sopenharmony_ci		nlmsg_free(msg);
19262306a36Sopenharmony_ci		return;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),
19662306a36Sopenharmony_ci				msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ciint devlink_nl_get_doit(struct sk_buff *skb, struct genl_info *info)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	struct devlink *devlink = info->user_ptr[0];
20262306a36Sopenharmony_ci	struct sk_buff *msg;
20362306a36Sopenharmony_ci	int err;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
20662306a36Sopenharmony_ci	if (!msg)
20762306a36Sopenharmony_ci		return -ENOMEM;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	err = devlink_nl_fill(msg, devlink, DEVLINK_CMD_NEW,
21062306a36Sopenharmony_ci			      info->snd_portid, info->snd_seq, 0);
21162306a36Sopenharmony_ci	if (err) {
21262306a36Sopenharmony_ci		nlmsg_free(msg);
21362306a36Sopenharmony_ci		return err;
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	return genlmsg_reply(msg, info);
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic int
22062306a36Sopenharmony_cidevlink_nl_get_dump_one(struct sk_buff *msg, struct devlink *devlink,
22162306a36Sopenharmony_ci			struct netlink_callback *cb, int flags)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	return devlink_nl_fill(msg, devlink, DEVLINK_CMD_NEW,
22462306a36Sopenharmony_ci			       NETLINK_CB(cb->skb).portid,
22562306a36Sopenharmony_ci			       cb->nlh->nlmsg_seq, flags);
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ciint devlink_nl_get_dumpit(struct sk_buff *msg, struct netlink_callback *cb)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	return devlink_nl_dumpit(msg, cb, devlink_nl_get_dump_one);
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_civoid devlink_notify_register(struct devlink *devlink)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	devlink_notify(devlink, DEVLINK_CMD_NEW);
23662306a36Sopenharmony_ci	devlink_linecards_notify_register(devlink);
23762306a36Sopenharmony_ci	devlink_ports_notify_register(devlink);
23862306a36Sopenharmony_ci	devlink_trap_policers_notify_register(devlink);
23962306a36Sopenharmony_ci	devlink_trap_groups_notify_register(devlink);
24062306a36Sopenharmony_ci	devlink_traps_notify_register(devlink);
24162306a36Sopenharmony_ci	devlink_rates_notify_register(devlink);
24262306a36Sopenharmony_ci	devlink_regions_notify_register(devlink);
24362306a36Sopenharmony_ci	devlink_params_notify_register(devlink);
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_civoid devlink_notify_unregister(struct devlink *devlink)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	devlink_params_notify_unregister(devlink);
24962306a36Sopenharmony_ci	devlink_regions_notify_unregister(devlink);
25062306a36Sopenharmony_ci	devlink_rates_notify_unregister(devlink);
25162306a36Sopenharmony_ci	devlink_traps_notify_unregister(devlink);
25262306a36Sopenharmony_ci	devlink_trap_groups_notify_unregister(devlink);
25362306a36Sopenharmony_ci	devlink_trap_policers_notify_unregister(devlink);
25462306a36Sopenharmony_ci	devlink_ports_notify_unregister(devlink);
25562306a36Sopenharmony_ci	devlink_linecards_notify_unregister(devlink);
25662306a36Sopenharmony_ci	devlink_notify(devlink, DEVLINK_CMD_DEL);
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic void devlink_reload_failed_set(struct devlink *devlink,
26062306a36Sopenharmony_ci				      bool reload_failed)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	if (devlink->reload_failed == reload_failed)
26362306a36Sopenharmony_ci		return;
26462306a36Sopenharmony_ci	devlink->reload_failed = reload_failed;
26562306a36Sopenharmony_ci	devlink_notify(devlink, DEVLINK_CMD_NEW);
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cibool devlink_is_reload_failed(const struct devlink *devlink)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	return devlink->reload_failed;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_is_reload_failed);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic void
27562306a36Sopenharmony_ci__devlink_reload_stats_update(struct devlink *devlink, u32 *reload_stats,
27662306a36Sopenharmony_ci			      enum devlink_reload_limit limit, u32 actions_performed)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	unsigned long actions = actions_performed;
27962306a36Sopenharmony_ci	int stat_idx;
28062306a36Sopenharmony_ci	int action;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	for_each_set_bit(action, &actions, __DEVLINK_RELOAD_ACTION_MAX) {
28362306a36Sopenharmony_ci		stat_idx = limit * __DEVLINK_RELOAD_ACTION_MAX + action;
28462306a36Sopenharmony_ci		reload_stats[stat_idx]++;
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci	devlink_notify(devlink, DEVLINK_CMD_NEW);
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic void
29062306a36Sopenharmony_cidevlink_reload_stats_update(struct devlink *devlink, enum devlink_reload_limit limit,
29162306a36Sopenharmony_ci			    u32 actions_performed)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	__devlink_reload_stats_update(devlink, devlink->stats.reload_stats, limit,
29462306a36Sopenharmony_ci				      actions_performed);
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci/**
29862306a36Sopenharmony_ci *	devlink_remote_reload_actions_performed - Update devlink on reload actions
29962306a36Sopenharmony_ci *	  performed which are not a direct result of devlink reload call.
30062306a36Sopenharmony_ci *
30162306a36Sopenharmony_ci *	This should be called by a driver after performing reload actions in case it was not
30262306a36Sopenharmony_ci *	a result of devlink reload call. For example fw_activate was performed as a result
30362306a36Sopenharmony_ci *	of devlink reload triggered fw_activate on another host.
30462306a36Sopenharmony_ci *	The motivation for this function is to keep data on reload actions performed on this
30562306a36Sopenharmony_ci *	function whether it was done due to direct devlink reload call or not.
30662306a36Sopenharmony_ci *
30762306a36Sopenharmony_ci *	@devlink: devlink
30862306a36Sopenharmony_ci *	@limit: reload limit
30962306a36Sopenharmony_ci *	@actions_performed: bitmask of actions performed
31062306a36Sopenharmony_ci */
31162306a36Sopenharmony_civoid devlink_remote_reload_actions_performed(struct devlink *devlink,
31262306a36Sopenharmony_ci					     enum devlink_reload_limit limit,
31362306a36Sopenharmony_ci					     u32 actions_performed)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	if (WARN_ON(!actions_performed ||
31662306a36Sopenharmony_ci		    actions_performed & BIT(DEVLINK_RELOAD_ACTION_UNSPEC) ||
31762306a36Sopenharmony_ci		    actions_performed >= BIT(__DEVLINK_RELOAD_ACTION_MAX) ||
31862306a36Sopenharmony_ci		    limit > DEVLINK_RELOAD_LIMIT_MAX))
31962306a36Sopenharmony_ci		return;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	__devlink_reload_stats_update(devlink, devlink->stats.remote_reload_stats, limit,
32262306a36Sopenharmony_ci				      actions_performed);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_remote_reload_actions_performed);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic struct net *devlink_netns_get(struct sk_buff *skb,
32762306a36Sopenharmony_ci				     struct genl_info *info)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	struct nlattr *netns_pid_attr = info->attrs[DEVLINK_ATTR_NETNS_PID];
33062306a36Sopenharmony_ci	struct nlattr *netns_fd_attr = info->attrs[DEVLINK_ATTR_NETNS_FD];
33162306a36Sopenharmony_ci	struct nlattr *netns_id_attr = info->attrs[DEVLINK_ATTR_NETNS_ID];
33262306a36Sopenharmony_ci	struct net *net;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	if (!!netns_pid_attr + !!netns_fd_attr + !!netns_id_attr > 1) {
33562306a36Sopenharmony_ci		NL_SET_ERR_MSG(info->extack, "multiple netns identifying attributes specified");
33662306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	if (netns_pid_attr) {
34062306a36Sopenharmony_ci		net = get_net_ns_by_pid(nla_get_u32(netns_pid_attr));
34162306a36Sopenharmony_ci	} else if (netns_fd_attr) {
34262306a36Sopenharmony_ci		net = get_net_ns_by_fd(nla_get_u32(netns_fd_attr));
34362306a36Sopenharmony_ci	} else if (netns_id_attr) {
34462306a36Sopenharmony_ci		net = get_net_ns_by_id(sock_net(skb->sk),
34562306a36Sopenharmony_ci				       nla_get_u32(netns_id_attr));
34662306a36Sopenharmony_ci		if (!net)
34762306a36Sopenharmony_ci			net = ERR_PTR(-EINVAL);
34862306a36Sopenharmony_ci	} else {
34962306a36Sopenharmony_ci		WARN_ON(1);
35062306a36Sopenharmony_ci		net = ERR_PTR(-EINVAL);
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci	if (IS_ERR(net)) {
35362306a36Sopenharmony_ci		NL_SET_ERR_MSG(info->extack, "Unknown network namespace");
35462306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci	if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) {
35762306a36Sopenharmony_ci		put_net(net);
35862306a36Sopenharmony_ci		return ERR_PTR(-EPERM);
35962306a36Sopenharmony_ci	}
36062306a36Sopenharmony_ci	return net;
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic void devlink_reload_netns_change(struct devlink *devlink,
36462306a36Sopenharmony_ci					struct net *curr_net,
36562306a36Sopenharmony_ci					struct net *dest_net)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	/* Userspace needs to be notified about devlink objects
36862306a36Sopenharmony_ci	 * removed from original and entering new network namespace.
36962306a36Sopenharmony_ci	 * The rest of the devlink objects are re-created during
37062306a36Sopenharmony_ci	 * reload process so the notifications are generated separatelly.
37162306a36Sopenharmony_ci	 */
37262306a36Sopenharmony_ci	devlink_notify_unregister(devlink);
37362306a36Sopenharmony_ci	write_pnet(&devlink->_net, dest_net);
37462306a36Sopenharmony_ci	devlink_notify_register(devlink);
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ciint devlink_reload(struct devlink *devlink, struct net *dest_net,
37862306a36Sopenharmony_ci		   enum devlink_reload_action action,
37962306a36Sopenharmony_ci		   enum devlink_reload_limit limit,
38062306a36Sopenharmony_ci		   u32 *actions_performed, struct netlink_ext_ack *extack)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	u32 remote_reload_stats[DEVLINK_RELOAD_STATS_ARRAY_SIZE];
38362306a36Sopenharmony_ci	struct net *curr_net;
38462306a36Sopenharmony_ci	int err;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	memcpy(remote_reload_stats, devlink->stats.remote_reload_stats,
38762306a36Sopenharmony_ci	       sizeof(remote_reload_stats));
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	err = devlink->ops->reload_down(devlink, !!dest_net, action, limit, extack);
39062306a36Sopenharmony_ci	if (err)
39162306a36Sopenharmony_ci		return err;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	curr_net = devlink_net(devlink);
39462306a36Sopenharmony_ci	if (dest_net && !net_eq(dest_net, curr_net))
39562306a36Sopenharmony_ci		devlink_reload_netns_change(devlink, curr_net, dest_net);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	if (action == DEVLINK_RELOAD_ACTION_DRIVER_REINIT)
39862306a36Sopenharmony_ci		devlink_params_driverinit_load_new(devlink);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	err = devlink->ops->reload_up(devlink, action, limit, actions_performed, extack);
40162306a36Sopenharmony_ci	devlink_reload_failed_set(devlink, !!err);
40262306a36Sopenharmony_ci	if (err)
40362306a36Sopenharmony_ci		return err;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	WARN_ON(!(*actions_performed & BIT(action)));
40662306a36Sopenharmony_ci	/* Catch driver on updating the remote action within devlink reload */
40762306a36Sopenharmony_ci	WARN_ON(memcmp(remote_reload_stats, devlink->stats.remote_reload_stats,
40862306a36Sopenharmony_ci		       sizeof(remote_reload_stats)));
40962306a36Sopenharmony_ci	devlink_reload_stats_update(devlink, limit, *actions_performed);
41062306a36Sopenharmony_ci	return 0;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic int
41462306a36Sopenharmony_cidevlink_nl_reload_actions_performed_snd(struct devlink *devlink, u32 actions_performed,
41562306a36Sopenharmony_ci					enum devlink_command cmd, struct genl_info *info)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	struct sk_buff *msg;
41862306a36Sopenharmony_ci	void *hdr;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
42162306a36Sopenharmony_ci	if (!msg)
42262306a36Sopenharmony_ci		return -ENOMEM;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, &devlink_nl_family, 0, cmd);
42562306a36Sopenharmony_ci	if (!hdr)
42662306a36Sopenharmony_ci		goto free_msg;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	if (devlink_nl_put_handle(msg, devlink))
42962306a36Sopenharmony_ci		goto nla_put_failure;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	if (nla_put_bitfield32(msg, DEVLINK_ATTR_RELOAD_ACTIONS_PERFORMED, actions_performed,
43262306a36Sopenharmony_ci			       actions_performed))
43362306a36Sopenharmony_ci		goto nla_put_failure;
43462306a36Sopenharmony_ci	genlmsg_end(msg, hdr);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	return genlmsg_reply(msg, info);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_cinla_put_failure:
43962306a36Sopenharmony_ci	genlmsg_cancel(msg, hdr);
44062306a36Sopenharmony_cifree_msg:
44162306a36Sopenharmony_ci	nlmsg_free(msg);
44262306a36Sopenharmony_ci	return -EMSGSIZE;
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ciint devlink_nl_cmd_reload(struct sk_buff *skb, struct genl_info *info)
44662306a36Sopenharmony_ci{
44762306a36Sopenharmony_ci	struct devlink *devlink = info->user_ptr[0];
44862306a36Sopenharmony_ci	enum devlink_reload_action action;
44962306a36Sopenharmony_ci	enum devlink_reload_limit limit;
45062306a36Sopenharmony_ci	struct net *dest_net = NULL;
45162306a36Sopenharmony_ci	u32 actions_performed;
45262306a36Sopenharmony_ci	int err;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	err = devlink_resources_validate(devlink, NULL, info);
45562306a36Sopenharmony_ci	if (err) {
45662306a36Sopenharmony_ci		NL_SET_ERR_MSG(info->extack, "resources size validation failed");
45762306a36Sopenharmony_ci		return err;
45862306a36Sopenharmony_ci	}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	if (info->attrs[DEVLINK_ATTR_RELOAD_ACTION])
46162306a36Sopenharmony_ci		action = nla_get_u8(info->attrs[DEVLINK_ATTR_RELOAD_ACTION]);
46262306a36Sopenharmony_ci	else
46362306a36Sopenharmony_ci		action = DEVLINK_RELOAD_ACTION_DRIVER_REINIT;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	if (!devlink_reload_action_is_supported(devlink, action)) {
46662306a36Sopenharmony_ci		NL_SET_ERR_MSG(info->extack, "Requested reload action is not supported by the driver");
46762306a36Sopenharmony_ci		return -EOPNOTSUPP;
46862306a36Sopenharmony_ci	}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	limit = DEVLINK_RELOAD_LIMIT_UNSPEC;
47162306a36Sopenharmony_ci	if (info->attrs[DEVLINK_ATTR_RELOAD_LIMITS]) {
47262306a36Sopenharmony_ci		struct nla_bitfield32 limits;
47362306a36Sopenharmony_ci		u32 limits_selected;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci		limits = nla_get_bitfield32(info->attrs[DEVLINK_ATTR_RELOAD_LIMITS]);
47662306a36Sopenharmony_ci		limits_selected = limits.value & limits.selector;
47762306a36Sopenharmony_ci		if (!limits_selected) {
47862306a36Sopenharmony_ci			NL_SET_ERR_MSG(info->extack, "Invalid limit selected");
47962306a36Sopenharmony_ci			return -EINVAL;
48062306a36Sopenharmony_ci		}
48162306a36Sopenharmony_ci		for (limit = 0 ; limit <= DEVLINK_RELOAD_LIMIT_MAX ; limit++)
48262306a36Sopenharmony_ci			if (limits_selected & BIT(limit))
48362306a36Sopenharmony_ci				break;
48462306a36Sopenharmony_ci		/* UAPI enables multiselection, but currently it is not used */
48562306a36Sopenharmony_ci		if (limits_selected != BIT(limit)) {
48662306a36Sopenharmony_ci			NL_SET_ERR_MSG(info->extack, "Multiselection of limit is not supported");
48762306a36Sopenharmony_ci			return -EOPNOTSUPP;
48862306a36Sopenharmony_ci		}
48962306a36Sopenharmony_ci		if (!devlink_reload_limit_is_supported(devlink, limit)) {
49062306a36Sopenharmony_ci			NL_SET_ERR_MSG(info->extack, "Requested limit is not supported by the driver");
49162306a36Sopenharmony_ci			return -EOPNOTSUPP;
49262306a36Sopenharmony_ci		}
49362306a36Sopenharmony_ci		if (devlink_reload_combination_is_invalid(action, limit)) {
49462306a36Sopenharmony_ci			NL_SET_ERR_MSG(info->extack, "Requested limit is invalid for this action");
49562306a36Sopenharmony_ci			return -EINVAL;
49662306a36Sopenharmony_ci		}
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci	if (info->attrs[DEVLINK_ATTR_NETNS_PID] ||
49962306a36Sopenharmony_ci	    info->attrs[DEVLINK_ATTR_NETNS_FD] ||
50062306a36Sopenharmony_ci	    info->attrs[DEVLINK_ATTR_NETNS_ID]) {
50162306a36Sopenharmony_ci		dest_net = devlink_netns_get(skb, info);
50262306a36Sopenharmony_ci		if (IS_ERR(dest_net))
50362306a36Sopenharmony_ci			return PTR_ERR(dest_net);
50462306a36Sopenharmony_ci		if (!net_eq(dest_net, devlink_net(devlink)) &&
50562306a36Sopenharmony_ci		    action != DEVLINK_RELOAD_ACTION_DRIVER_REINIT) {
50662306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(info->extack,
50762306a36Sopenharmony_ci					   "Changing namespace is only supported for reinit action");
50862306a36Sopenharmony_ci			return -EOPNOTSUPP;
50962306a36Sopenharmony_ci		}
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	err = devlink_reload(devlink, dest_net, action, limit, &actions_performed, info->extack);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	if (dest_net)
51562306a36Sopenharmony_ci		put_net(dest_net);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	if (err)
51862306a36Sopenharmony_ci		return err;
51962306a36Sopenharmony_ci	/* For backward compatibility generate reply only if attributes used by user */
52062306a36Sopenharmony_ci	if (!info->attrs[DEVLINK_ATTR_RELOAD_ACTION] && !info->attrs[DEVLINK_ATTR_RELOAD_LIMITS])
52162306a36Sopenharmony_ci		return 0;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	return devlink_nl_reload_actions_performed_snd(devlink, actions_performed,
52462306a36Sopenharmony_ci						       DEVLINK_CMD_RELOAD, info);
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cibool devlink_reload_actions_valid(const struct devlink_ops *ops)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	const struct devlink_reload_combination *comb;
53062306a36Sopenharmony_ci	int i;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	if (!devlink_reload_supported(ops)) {
53362306a36Sopenharmony_ci		if (WARN_ON(ops->reload_actions))
53462306a36Sopenharmony_ci			return false;
53562306a36Sopenharmony_ci		return true;
53662306a36Sopenharmony_ci	}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	if (WARN_ON(!ops->reload_actions ||
53962306a36Sopenharmony_ci		    ops->reload_actions & BIT(DEVLINK_RELOAD_ACTION_UNSPEC) ||
54062306a36Sopenharmony_ci		    ops->reload_actions >= BIT(__DEVLINK_RELOAD_ACTION_MAX)))
54162306a36Sopenharmony_ci		return false;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (WARN_ON(ops->reload_limits & BIT(DEVLINK_RELOAD_LIMIT_UNSPEC) ||
54462306a36Sopenharmony_ci		    ops->reload_limits >= BIT(__DEVLINK_RELOAD_LIMIT_MAX)))
54562306a36Sopenharmony_ci		return false;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(devlink_reload_invalid_combinations); i++)  {
54862306a36Sopenharmony_ci		comb = &devlink_reload_invalid_combinations[i];
54962306a36Sopenharmony_ci		if (ops->reload_actions == BIT(comb->action) &&
55062306a36Sopenharmony_ci		    ops->reload_limits == BIT(comb->limit))
55162306a36Sopenharmony_ci			return false;
55262306a36Sopenharmony_ci	}
55362306a36Sopenharmony_ci	return true;
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_cistatic int devlink_nl_eswitch_fill(struct sk_buff *msg, struct devlink *devlink,
55762306a36Sopenharmony_ci				   enum devlink_command cmd, u32 portid,
55862306a36Sopenharmony_ci				   u32 seq, int flags)
55962306a36Sopenharmony_ci{
56062306a36Sopenharmony_ci	const struct devlink_ops *ops = devlink->ops;
56162306a36Sopenharmony_ci	enum devlink_eswitch_encap_mode encap_mode;
56262306a36Sopenharmony_ci	u8 inline_mode;
56362306a36Sopenharmony_ci	void *hdr;
56462306a36Sopenharmony_ci	int err = 0;
56562306a36Sopenharmony_ci	u16 mode;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
56862306a36Sopenharmony_ci	if (!hdr)
56962306a36Sopenharmony_ci		return -EMSGSIZE;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	err = devlink_nl_put_handle(msg, devlink);
57262306a36Sopenharmony_ci	if (err)
57362306a36Sopenharmony_ci		goto nla_put_failure;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	if (ops->eswitch_mode_get) {
57662306a36Sopenharmony_ci		err = ops->eswitch_mode_get(devlink, &mode);
57762306a36Sopenharmony_ci		if (err)
57862306a36Sopenharmony_ci			goto nla_put_failure;
57962306a36Sopenharmony_ci		err = nla_put_u16(msg, DEVLINK_ATTR_ESWITCH_MODE, mode);
58062306a36Sopenharmony_ci		if (err)
58162306a36Sopenharmony_ci			goto nla_put_failure;
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	if (ops->eswitch_inline_mode_get) {
58562306a36Sopenharmony_ci		err = ops->eswitch_inline_mode_get(devlink, &inline_mode);
58662306a36Sopenharmony_ci		if (err)
58762306a36Sopenharmony_ci			goto nla_put_failure;
58862306a36Sopenharmony_ci		err = nla_put_u8(msg, DEVLINK_ATTR_ESWITCH_INLINE_MODE,
58962306a36Sopenharmony_ci				 inline_mode);
59062306a36Sopenharmony_ci		if (err)
59162306a36Sopenharmony_ci			goto nla_put_failure;
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	if (ops->eswitch_encap_mode_get) {
59562306a36Sopenharmony_ci		err = ops->eswitch_encap_mode_get(devlink, &encap_mode);
59662306a36Sopenharmony_ci		if (err)
59762306a36Sopenharmony_ci			goto nla_put_failure;
59862306a36Sopenharmony_ci		err = nla_put_u8(msg, DEVLINK_ATTR_ESWITCH_ENCAP_MODE, encap_mode);
59962306a36Sopenharmony_ci		if (err)
60062306a36Sopenharmony_ci			goto nla_put_failure;
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	genlmsg_end(msg, hdr);
60462306a36Sopenharmony_ci	return 0;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_cinla_put_failure:
60762306a36Sopenharmony_ci	genlmsg_cancel(msg, hdr);
60862306a36Sopenharmony_ci	return err;
60962306a36Sopenharmony_ci}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ciint devlink_nl_cmd_eswitch_get_doit(struct sk_buff *skb, struct genl_info *info)
61262306a36Sopenharmony_ci{
61362306a36Sopenharmony_ci	struct devlink *devlink = info->user_ptr[0];
61462306a36Sopenharmony_ci	struct sk_buff *msg;
61562306a36Sopenharmony_ci	int err;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
61862306a36Sopenharmony_ci	if (!msg)
61962306a36Sopenharmony_ci		return -ENOMEM;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	err = devlink_nl_eswitch_fill(msg, devlink, DEVLINK_CMD_ESWITCH_GET,
62262306a36Sopenharmony_ci				      info->snd_portid, info->snd_seq, 0);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	if (err) {
62562306a36Sopenharmony_ci		nlmsg_free(msg);
62662306a36Sopenharmony_ci		return err;
62762306a36Sopenharmony_ci	}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	return genlmsg_reply(msg, info);
63062306a36Sopenharmony_ci}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ciint devlink_nl_cmd_eswitch_set_doit(struct sk_buff *skb, struct genl_info *info)
63362306a36Sopenharmony_ci{
63462306a36Sopenharmony_ci	struct devlink *devlink = info->user_ptr[0];
63562306a36Sopenharmony_ci	const struct devlink_ops *ops = devlink->ops;
63662306a36Sopenharmony_ci	enum devlink_eswitch_encap_mode encap_mode;
63762306a36Sopenharmony_ci	u8 inline_mode;
63862306a36Sopenharmony_ci	int err = 0;
63962306a36Sopenharmony_ci	u16 mode;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	if (info->attrs[DEVLINK_ATTR_ESWITCH_MODE]) {
64262306a36Sopenharmony_ci		if (!ops->eswitch_mode_set)
64362306a36Sopenharmony_ci			return -EOPNOTSUPP;
64462306a36Sopenharmony_ci		mode = nla_get_u16(info->attrs[DEVLINK_ATTR_ESWITCH_MODE]);
64562306a36Sopenharmony_ci		err = devlink_rate_nodes_check(devlink, mode, info->extack);
64662306a36Sopenharmony_ci		if (err)
64762306a36Sopenharmony_ci			return err;
64862306a36Sopenharmony_ci		err = ops->eswitch_mode_set(devlink, mode, info->extack);
64962306a36Sopenharmony_ci		if (err)
65062306a36Sopenharmony_ci			return err;
65162306a36Sopenharmony_ci	}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	if (info->attrs[DEVLINK_ATTR_ESWITCH_INLINE_MODE]) {
65462306a36Sopenharmony_ci		if (!ops->eswitch_inline_mode_set)
65562306a36Sopenharmony_ci			return -EOPNOTSUPP;
65662306a36Sopenharmony_ci		inline_mode = nla_get_u8(info->attrs[DEVLINK_ATTR_ESWITCH_INLINE_MODE]);
65762306a36Sopenharmony_ci		err = ops->eswitch_inline_mode_set(devlink, inline_mode,
65862306a36Sopenharmony_ci						   info->extack);
65962306a36Sopenharmony_ci		if (err)
66062306a36Sopenharmony_ci			return err;
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	if (info->attrs[DEVLINK_ATTR_ESWITCH_ENCAP_MODE]) {
66462306a36Sopenharmony_ci		if (!ops->eswitch_encap_mode_set)
66562306a36Sopenharmony_ci			return -EOPNOTSUPP;
66662306a36Sopenharmony_ci		encap_mode = nla_get_u8(info->attrs[DEVLINK_ATTR_ESWITCH_ENCAP_MODE]);
66762306a36Sopenharmony_ci		err = ops->eswitch_encap_mode_set(devlink, encap_mode,
66862306a36Sopenharmony_ci						  info->extack);
66962306a36Sopenharmony_ci		if (err)
67062306a36Sopenharmony_ci			return err;
67162306a36Sopenharmony_ci	}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	return 0;
67462306a36Sopenharmony_ci}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ciint devlink_info_serial_number_put(struct devlink_info_req *req, const char *sn)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	if (!req->msg)
67962306a36Sopenharmony_ci		return 0;
68062306a36Sopenharmony_ci	return nla_put_string(req->msg, DEVLINK_ATTR_INFO_SERIAL_NUMBER, sn);
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_info_serial_number_put);
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ciint devlink_info_board_serial_number_put(struct devlink_info_req *req,
68562306a36Sopenharmony_ci					 const char *bsn)
68662306a36Sopenharmony_ci{
68762306a36Sopenharmony_ci	if (!req->msg)
68862306a36Sopenharmony_ci		return 0;
68962306a36Sopenharmony_ci	return nla_put_string(req->msg, DEVLINK_ATTR_INFO_BOARD_SERIAL_NUMBER,
69062306a36Sopenharmony_ci			      bsn);
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_info_board_serial_number_put);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_cistatic int devlink_info_version_put(struct devlink_info_req *req, int attr,
69562306a36Sopenharmony_ci				    const char *version_name,
69662306a36Sopenharmony_ci				    const char *version_value,
69762306a36Sopenharmony_ci				    enum devlink_info_version_type version_type)
69862306a36Sopenharmony_ci{
69962306a36Sopenharmony_ci	struct nlattr *nest;
70062306a36Sopenharmony_ci	int err;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	if (req->version_cb)
70362306a36Sopenharmony_ci		req->version_cb(version_name, version_type,
70462306a36Sopenharmony_ci				req->version_cb_priv);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	if (!req->msg)
70762306a36Sopenharmony_ci		return 0;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	nest = nla_nest_start_noflag(req->msg, attr);
71062306a36Sopenharmony_ci	if (!nest)
71162306a36Sopenharmony_ci		return -EMSGSIZE;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	err = nla_put_string(req->msg, DEVLINK_ATTR_INFO_VERSION_NAME,
71462306a36Sopenharmony_ci			     version_name);
71562306a36Sopenharmony_ci	if (err)
71662306a36Sopenharmony_ci		goto nla_put_failure;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	err = nla_put_string(req->msg, DEVLINK_ATTR_INFO_VERSION_VALUE,
71962306a36Sopenharmony_ci			     version_value);
72062306a36Sopenharmony_ci	if (err)
72162306a36Sopenharmony_ci		goto nla_put_failure;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	nla_nest_end(req->msg, nest);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	return 0;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_cinla_put_failure:
72862306a36Sopenharmony_ci	nla_nest_cancel(req->msg, nest);
72962306a36Sopenharmony_ci	return err;
73062306a36Sopenharmony_ci}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ciint devlink_info_version_fixed_put(struct devlink_info_req *req,
73362306a36Sopenharmony_ci				   const char *version_name,
73462306a36Sopenharmony_ci				   const char *version_value)
73562306a36Sopenharmony_ci{
73662306a36Sopenharmony_ci	return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_FIXED,
73762306a36Sopenharmony_ci					version_name, version_value,
73862306a36Sopenharmony_ci					DEVLINK_INFO_VERSION_TYPE_NONE);
73962306a36Sopenharmony_ci}
74062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_info_version_fixed_put);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ciint devlink_info_version_stored_put(struct devlink_info_req *req,
74362306a36Sopenharmony_ci				    const char *version_name,
74462306a36Sopenharmony_ci				    const char *version_value)
74562306a36Sopenharmony_ci{
74662306a36Sopenharmony_ci	return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_STORED,
74762306a36Sopenharmony_ci					version_name, version_value,
74862306a36Sopenharmony_ci					DEVLINK_INFO_VERSION_TYPE_NONE);
74962306a36Sopenharmony_ci}
75062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_info_version_stored_put);
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ciint devlink_info_version_stored_put_ext(struct devlink_info_req *req,
75362306a36Sopenharmony_ci					const char *version_name,
75462306a36Sopenharmony_ci					const char *version_value,
75562306a36Sopenharmony_ci					enum devlink_info_version_type version_type)
75662306a36Sopenharmony_ci{
75762306a36Sopenharmony_ci	return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_STORED,
75862306a36Sopenharmony_ci					version_name, version_value,
75962306a36Sopenharmony_ci					version_type);
76062306a36Sopenharmony_ci}
76162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_info_version_stored_put_ext);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ciint devlink_info_version_running_put(struct devlink_info_req *req,
76462306a36Sopenharmony_ci				     const char *version_name,
76562306a36Sopenharmony_ci				     const char *version_value)
76662306a36Sopenharmony_ci{
76762306a36Sopenharmony_ci	return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_RUNNING,
76862306a36Sopenharmony_ci					version_name, version_value,
76962306a36Sopenharmony_ci					DEVLINK_INFO_VERSION_TYPE_NONE);
77062306a36Sopenharmony_ci}
77162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_info_version_running_put);
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ciint devlink_info_version_running_put_ext(struct devlink_info_req *req,
77462306a36Sopenharmony_ci					 const char *version_name,
77562306a36Sopenharmony_ci					 const char *version_value,
77662306a36Sopenharmony_ci					 enum devlink_info_version_type version_type)
77762306a36Sopenharmony_ci{
77862306a36Sopenharmony_ci	return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_RUNNING,
77962306a36Sopenharmony_ci					version_name, version_value,
78062306a36Sopenharmony_ci					version_type);
78162306a36Sopenharmony_ci}
78262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_info_version_running_put_ext);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_cistatic int devlink_nl_driver_info_get(struct device_driver *drv,
78562306a36Sopenharmony_ci				      struct devlink_info_req *req)
78662306a36Sopenharmony_ci{
78762306a36Sopenharmony_ci	if (!drv)
78862306a36Sopenharmony_ci		return 0;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	if (drv->name[0])
79162306a36Sopenharmony_ci		return nla_put_string(req->msg, DEVLINK_ATTR_INFO_DRIVER_NAME,
79262306a36Sopenharmony_ci				      drv->name);
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	return 0;
79562306a36Sopenharmony_ci}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_cistatic int
79862306a36Sopenharmony_cidevlink_nl_info_fill(struct sk_buff *msg, struct devlink *devlink,
79962306a36Sopenharmony_ci		     enum devlink_command cmd, u32 portid,
80062306a36Sopenharmony_ci		     u32 seq, int flags, struct netlink_ext_ack *extack)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	struct device *dev = devlink_to_dev(devlink);
80362306a36Sopenharmony_ci	struct devlink_info_req req = {};
80462306a36Sopenharmony_ci	void *hdr;
80562306a36Sopenharmony_ci	int err;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
80862306a36Sopenharmony_ci	if (!hdr)
80962306a36Sopenharmony_ci		return -EMSGSIZE;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	err = -EMSGSIZE;
81262306a36Sopenharmony_ci	if (devlink_nl_put_handle(msg, devlink))
81362306a36Sopenharmony_ci		goto err_cancel_msg;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	req.msg = msg;
81662306a36Sopenharmony_ci	if (devlink->ops->info_get) {
81762306a36Sopenharmony_ci		err = devlink->ops->info_get(devlink, &req, extack);
81862306a36Sopenharmony_ci		if (err)
81962306a36Sopenharmony_ci			goto err_cancel_msg;
82062306a36Sopenharmony_ci	}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	err = devlink_nl_driver_info_get(dev->driver, &req);
82362306a36Sopenharmony_ci	if (err)
82462306a36Sopenharmony_ci		goto err_cancel_msg;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	genlmsg_end(msg, hdr);
82762306a36Sopenharmony_ci	return 0;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_cierr_cancel_msg:
83062306a36Sopenharmony_ci	genlmsg_cancel(msg, hdr);
83162306a36Sopenharmony_ci	return err;
83262306a36Sopenharmony_ci}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ciint devlink_nl_info_get_doit(struct sk_buff *skb, struct genl_info *info)
83562306a36Sopenharmony_ci{
83662306a36Sopenharmony_ci	struct devlink *devlink = info->user_ptr[0];
83762306a36Sopenharmony_ci	struct sk_buff *msg;
83862306a36Sopenharmony_ci	int err;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
84162306a36Sopenharmony_ci	if (!msg)
84262306a36Sopenharmony_ci		return -ENOMEM;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	err = devlink_nl_info_fill(msg, devlink, DEVLINK_CMD_INFO_GET,
84562306a36Sopenharmony_ci				   info->snd_portid, info->snd_seq, 0,
84662306a36Sopenharmony_ci				   info->extack);
84762306a36Sopenharmony_ci	if (err) {
84862306a36Sopenharmony_ci		nlmsg_free(msg);
84962306a36Sopenharmony_ci		return err;
85062306a36Sopenharmony_ci	}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	return genlmsg_reply(msg, info);
85362306a36Sopenharmony_ci}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_cistatic int
85662306a36Sopenharmony_cidevlink_nl_info_get_dump_one(struct sk_buff *msg, struct devlink *devlink,
85762306a36Sopenharmony_ci			     struct netlink_callback *cb, int flags)
85862306a36Sopenharmony_ci{
85962306a36Sopenharmony_ci	int err;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	err = devlink_nl_info_fill(msg, devlink, DEVLINK_CMD_INFO_GET,
86262306a36Sopenharmony_ci				   NETLINK_CB(cb->skb).portid,
86362306a36Sopenharmony_ci				   cb->nlh->nlmsg_seq, flags,
86462306a36Sopenharmony_ci				   cb->extack);
86562306a36Sopenharmony_ci	if (err == -EOPNOTSUPP)
86662306a36Sopenharmony_ci		err = 0;
86762306a36Sopenharmony_ci	return err;
86862306a36Sopenharmony_ci}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ciint devlink_nl_info_get_dumpit(struct sk_buff *msg, struct netlink_callback *cb)
87162306a36Sopenharmony_ci{
87262306a36Sopenharmony_ci	return devlink_nl_dumpit(msg, cb, devlink_nl_info_get_dump_one);
87362306a36Sopenharmony_ci}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_cistatic int devlink_nl_flash_update_fill(struct sk_buff *msg,
87662306a36Sopenharmony_ci					struct devlink *devlink,
87762306a36Sopenharmony_ci					enum devlink_command cmd,
87862306a36Sopenharmony_ci					struct devlink_flash_notify *params)
87962306a36Sopenharmony_ci{
88062306a36Sopenharmony_ci	void *hdr;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	hdr = genlmsg_put(msg, 0, 0, &devlink_nl_family, 0, cmd);
88362306a36Sopenharmony_ci	if (!hdr)
88462306a36Sopenharmony_ci		return -EMSGSIZE;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	if (devlink_nl_put_handle(msg, devlink))
88762306a36Sopenharmony_ci		goto nla_put_failure;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	if (cmd != DEVLINK_CMD_FLASH_UPDATE_STATUS)
89062306a36Sopenharmony_ci		goto out;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	if (params->status_msg &&
89362306a36Sopenharmony_ci	    nla_put_string(msg, DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG,
89462306a36Sopenharmony_ci			   params->status_msg))
89562306a36Sopenharmony_ci		goto nla_put_failure;
89662306a36Sopenharmony_ci	if (params->component &&
89762306a36Sopenharmony_ci	    nla_put_string(msg, DEVLINK_ATTR_FLASH_UPDATE_COMPONENT,
89862306a36Sopenharmony_ci			   params->component))
89962306a36Sopenharmony_ci		goto nla_put_failure;
90062306a36Sopenharmony_ci	if (nla_put_u64_64bit(msg, DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE,
90162306a36Sopenharmony_ci			      params->done, DEVLINK_ATTR_PAD))
90262306a36Sopenharmony_ci		goto nla_put_failure;
90362306a36Sopenharmony_ci	if (nla_put_u64_64bit(msg, DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL,
90462306a36Sopenharmony_ci			      params->total, DEVLINK_ATTR_PAD))
90562306a36Sopenharmony_ci		goto nla_put_failure;
90662306a36Sopenharmony_ci	if (nla_put_u64_64bit(msg, DEVLINK_ATTR_FLASH_UPDATE_STATUS_TIMEOUT,
90762306a36Sopenharmony_ci			      params->timeout, DEVLINK_ATTR_PAD))
90862306a36Sopenharmony_ci		goto nla_put_failure;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ciout:
91162306a36Sopenharmony_ci	genlmsg_end(msg, hdr);
91262306a36Sopenharmony_ci	return 0;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_cinla_put_failure:
91562306a36Sopenharmony_ci	genlmsg_cancel(msg, hdr);
91662306a36Sopenharmony_ci	return -EMSGSIZE;
91762306a36Sopenharmony_ci}
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_cistatic void __devlink_flash_update_notify(struct devlink *devlink,
92062306a36Sopenharmony_ci					  enum devlink_command cmd,
92162306a36Sopenharmony_ci					  struct devlink_flash_notify *params)
92262306a36Sopenharmony_ci{
92362306a36Sopenharmony_ci	struct sk_buff *msg;
92462306a36Sopenharmony_ci	int err;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	WARN_ON(cmd != DEVLINK_CMD_FLASH_UPDATE &&
92762306a36Sopenharmony_ci		cmd != DEVLINK_CMD_FLASH_UPDATE_END &&
92862306a36Sopenharmony_ci		cmd != DEVLINK_CMD_FLASH_UPDATE_STATUS);
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	if (!xa_get_mark(&devlinks, devlink->index, DEVLINK_REGISTERED))
93162306a36Sopenharmony_ci		return;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
93462306a36Sopenharmony_ci	if (!msg)
93562306a36Sopenharmony_ci		return;
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	err = devlink_nl_flash_update_fill(msg, devlink, cmd, params);
93862306a36Sopenharmony_ci	if (err)
93962306a36Sopenharmony_ci		goto out_free_msg;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),
94262306a36Sopenharmony_ci				msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
94362306a36Sopenharmony_ci	return;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ciout_free_msg:
94662306a36Sopenharmony_ci	nlmsg_free(msg);
94762306a36Sopenharmony_ci}
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_cistatic void devlink_flash_update_begin_notify(struct devlink *devlink)
95062306a36Sopenharmony_ci{
95162306a36Sopenharmony_ci	struct devlink_flash_notify params = {};
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	__devlink_flash_update_notify(devlink,
95462306a36Sopenharmony_ci				      DEVLINK_CMD_FLASH_UPDATE,
95562306a36Sopenharmony_ci				      &params);
95662306a36Sopenharmony_ci}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_cistatic void devlink_flash_update_end_notify(struct devlink *devlink)
95962306a36Sopenharmony_ci{
96062306a36Sopenharmony_ci	struct devlink_flash_notify params = {};
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	__devlink_flash_update_notify(devlink,
96362306a36Sopenharmony_ci				      DEVLINK_CMD_FLASH_UPDATE_END,
96462306a36Sopenharmony_ci				      &params);
96562306a36Sopenharmony_ci}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_civoid devlink_flash_update_status_notify(struct devlink *devlink,
96862306a36Sopenharmony_ci					const char *status_msg,
96962306a36Sopenharmony_ci					const char *component,
97062306a36Sopenharmony_ci					unsigned long done,
97162306a36Sopenharmony_ci					unsigned long total)
97262306a36Sopenharmony_ci{
97362306a36Sopenharmony_ci	struct devlink_flash_notify params = {
97462306a36Sopenharmony_ci		.status_msg = status_msg,
97562306a36Sopenharmony_ci		.component = component,
97662306a36Sopenharmony_ci		.done = done,
97762306a36Sopenharmony_ci		.total = total,
97862306a36Sopenharmony_ci	};
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	__devlink_flash_update_notify(devlink,
98162306a36Sopenharmony_ci				      DEVLINK_CMD_FLASH_UPDATE_STATUS,
98262306a36Sopenharmony_ci				      &params);
98362306a36Sopenharmony_ci}
98462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_flash_update_status_notify);
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_civoid devlink_flash_update_timeout_notify(struct devlink *devlink,
98762306a36Sopenharmony_ci					 const char *status_msg,
98862306a36Sopenharmony_ci					 const char *component,
98962306a36Sopenharmony_ci					 unsigned long timeout)
99062306a36Sopenharmony_ci{
99162306a36Sopenharmony_ci	struct devlink_flash_notify params = {
99262306a36Sopenharmony_ci		.status_msg = status_msg,
99362306a36Sopenharmony_ci		.component = component,
99462306a36Sopenharmony_ci		.timeout = timeout,
99562306a36Sopenharmony_ci	};
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	__devlink_flash_update_notify(devlink,
99862306a36Sopenharmony_ci				      DEVLINK_CMD_FLASH_UPDATE_STATUS,
99962306a36Sopenharmony_ci				      &params);
100062306a36Sopenharmony_ci}
100162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devlink_flash_update_timeout_notify);
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_cistruct devlink_flash_component_lookup_ctx {
100462306a36Sopenharmony_ci	const char *lookup_name;
100562306a36Sopenharmony_ci	bool lookup_name_found;
100662306a36Sopenharmony_ci};
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_cistatic void
100962306a36Sopenharmony_cidevlink_flash_component_lookup_cb(const char *version_name,
101062306a36Sopenharmony_ci				  enum devlink_info_version_type version_type,
101162306a36Sopenharmony_ci				  void *version_cb_priv)
101262306a36Sopenharmony_ci{
101362306a36Sopenharmony_ci	struct devlink_flash_component_lookup_ctx *lookup_ctx = version_cb_priv;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	if (version_type != DEVLINK_INFO_VERSION_TYPE_COMPONENT ||
101662306a36Sopenharmony_ci	    lookup_ctx->lookup_name_found)
101762306a36Sopenharmony_ci		return;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	lookup_ctx->lookup_name_found =
102062306a36Sopenharmony_ci		!strcmp(lookup_ctx->lookup_name, version_name);
102162306a36Sopenharmony_ci}
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_cistatic int devlink_flash_component_get(struct devlink *devlink,
102462306a36Sopenharmony_ci				       struct nlattr *nla_component,
102562306a36Sopenharmony_ci				       const char **p_component,
102662306a36Sopenharmony_ci				       struct netlink_ext_ack *extack)
102762306a36Sopenharmony_ci{
102862306a36Sopenharmony_ci	struct devlink_flash_component_lookup_ctx lookup_ctx = {};
102962306a36Sopenharmony_ci	struct devlink_info_req req = {};
103062306a36Sopenharmony_ci	const char *component;
103162306a36Sopenharmony_ci	int ret;
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	if (!nla_component)
103462306a36Sopenharmony_ci		return 0;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	component = nla_data(nla_component);
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	if (!devlink->ops->info_get) {
103962306a36Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, nla_component,
104062306a36Sopenharmony_ci				    "component update is not supported by this device");
104162306a36Sopenharmony_ci		return -EOPNOTSUPP;
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	lookup_ctx.lookup_name = component;
104562306a36Sopenharmony_ci	req.version_cb = devlink_flash_component_lookup_cb;
104662306a36Sopenharmony_ci	req.version_cb_priv = &lookup_ctx;
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	ret = devlink->ops->info_get(devlink, &req, NULL);
104962306a36Sopenharmony_ci	if (ret)
105062306a36Sopenharmony_ci		return ret;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	if (!lookup_ctx.lookup_name_found) {
105362306a36Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, nla_component,
105462306a36Sopenharmony_ci				    "selected component is not supported by this device");
105562306a36Sopenharmony_ci		return -EINVAL;
105662306a36Sopenharmony_ci	}
105762306a36Sopenharmony_ci	*p_component = component;
105862306a36Sopenharmony_ci	return 0;
105962306a36Sopenharmony_ci}
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ciint devlink_nl_cmd_flash_update(struct sk_buff *skb, struct genl_info *info)
106262306a36Sopenharmony_ci{
106362306a36Sopenharmony_ci	struct nlattr *nla_overwrite_mask, *nla_file_name;
106462306a36Sopenharmony_ci	struct devlink_flash_update_params params = {};
106562306a36Sopenharmony_ci	struct devlink *devlink = info->user_ptr[0];
106662306a36Sopenharmony_ci	const char *file_name;
106762306a36Sopenharmony_ci	u32 supported_params;
106862306a36Sopenharmony_ci	int ret;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	if (!devlink->ops->flash_update)
107162306a36Sopenharmony_ci		return -EOPNOTSUPP;
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME))
107462306a36Sopenharmony_ci		return -EINVAL;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	ret = devlink_flash_component_get(devlink,
107762306a36Sopenharmony_ci					  info->attrs[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT],
107862306a36Sopenharmony_ci					  &params.component, info->extack);
107962306a36Sopenharmony_ci	if (ret)
108062306a36Sopenharmony_ci		return ret;
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	supported_params = devlink->ops->supported_flash_update_params;
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	nla_overwrite_mask = info->attrs[DEVLINK_ATTR_FLASH_UPDATE_OVERWRITE_MASK];
108562306a36Sopenharmony_ci	if (nla_overwrite_mask) {
108662306a36Sopenharmony_ci		struct nla_bitfield32 sections;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci		if (!(supported_params & DEVLINK_SUPPORT_FLASH_UPDATE_OVERWRITE_MASK)) {
108962306a36Sopenharmony_ci			NL_SET_ERR_MSG_ATTR(info->extack, nla_overwrite_mask,
109062306a36Sopenharmony_ci					    "overwrite settings are not supported by this device");
109162306a36Sopenharmony_ci			return -EOPNOTSUPP;
109262306a36Sopenharmony_ci		}
109362306a36Sopenharmony_ci		sections = nla_get_bitfield32(nla_overwrite_mask);
109462306a36Sopenharmony_ci		params.overwrite_mask = sections.value & sections.selector;
109562306a36Sopenharmony_ci	}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	nla_file_name = info->attrs[DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME];
109862306a36Sopenharmony_ci	file_name = nla_data(nla_file_name);
109962306a36Sopenharmony_ci	ret = request_firmware(&params.fw, file_name, devlink->dev);
110062306a36Sopenharmony_ci	if (ret) {
110162306a36Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(info->extack, nla_file_name,
110262306a36Sopenharmony_ci				    "failed to locate the requested firmware file");
110362306a36Sopenharmony_ci		return ret;
110462306a36Sopenharmony_ci	}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	devlink_flash_update_begin_notify(devlink);
110762306a36Sopenharmony_ci	ret = devlink->ops->flash_update(devlink, &params, info->extack);
110862306a36Sopenharmony_ci	devlink_flash_update_end_notify(devlink);
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	release_firmware(params.fw);
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	return ret;
111362306a36Sopenharmony_ci}
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_cistatic void __devlink_compat_running_version(struct devlink *devlink,
111662306a36Sopenharmony_ci					     char *buf, size_t len)
111762306a36Sopenharmony_ci{
111862306a36Sopenharmony_ci	struct devlink_info_req req = {};
111962306a36Sopenharmony_ci	const struct nlattr *nlattr;
112062306a36Sopenharmony_ci	struct sk_buff *msg;
112162306a36Sopenharmony_ci	int rem, err;
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
112462306a36Sopenharmony_ci	if (!msg)
112562306a36Sopenharmony_ci		return;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	req.msg = msg;
112862306a36Sopenharmony_ci	err = devlink->ops->info_get(devlink, &req, NULL);
112962306a36Sopenharmony_ci	if (err)
113062306a36Sopenharmony_ci		goto free_msg;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	nla_for_each_attr(nlattr, (void *)msg->data, msg->len, rem) {
113362306a36Sopenharmony_ci		const struct nlattr *kv;
113462306a36Sopenharmony_ci		int rem_kv;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci		if (nla_type(nlattr) != DEVLINK_ATTR_INFO_VERSION_RUNNING)
113762306a36Sopenharmony_ci			continue;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci		nla_for_each_nested(kv, nlattr, rem_kv) {
114062306a36Sopenharmony_ci			if (nla_type(kv) != DEVLINK_ATTR_INFO_VERSION_VALUE)
114162306a36Sopenharmony_ci				continue;
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci			strlcat(buf, nla_data(kv), len);
114462306a36Sopenharmony_ci			strlcat(buf, " ", len);
114562306a36Sopenharmony_ci		}
114662306a36Sopenharmony_ci	}
114762306a36Sopenharmony_cifree_msg:
114862306a36Sopenharmony_ci	nlmsg_free(msg);
114962306a36Sopenharmony_ci}
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_civoid devlink_compat_running_version(struct devlink *devlink,
115262306a36Sopenharmony_ci				    char *buf, size_t len)
115362306a36Sopenharmony_ci{
115462306a36Sopenharmony_ci	if (!devlink->ops->info_get)
115562306a36Sopenharmony_ci		return;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	devl_lock(devlink);
115862306a36Sopenharmony_ci	if (devl_is_registered(devlink))
115962306a36Sopenharmony_ci		__devlink_compat_running_version(devlink, buf, len);
116062306a36Sopenharmony_ci	devl_unlock(devlink);
116162306a36Sopenharmony_ci}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ciint devlink_compat_flash_update(struct devlink *devlink, const char *file_name)
116462306a36Sopenharmony_ci{
116562306a36Sopenharmony_ci	struct devlink_flash_update_params params = {};
116662306a36Sopenharmony_ci	int ret;
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	devl_lock(devlink);
116962306a36Sopenharmony_ci	if (!devl_is_registered(devlink)) {
117062306a36Sopenharmony_ci		ret = -ENODEV;
117162306a36Sopenharmony_ci		goto out_unlock;
117262306a36Sopenharmony_ci	}
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	if (!devlink->ops->flash_update) {
117562306a36Sopenharmony_ci		ret = -EOPNOTSUPP;
117662306a36Sopenharmony_ci		goto out_unlock;
117762306a36Sopenharmony_ci	}
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	ret = request_firmware(&params.fw, file_name, devlink->dev);
118062306a36Sopenharmony_ci	if (ret)
118162306a36Sopenharmony_ci		goto out_unlock;
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	devlink_flash_update_begin_notify(devlink);
118462306a36Sopenharmony_ci	ret = devlink->ops->flash_update(devlink, &params, NULL);
118562306a36Sopenharmony_ci	devlink_flash_update_end_notify(devlink);
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	release_firmware(params.fw);
118862306a36Sopenharmony_ciout_unlock:
118962306a36Sopenharmony_ci	devl_unlock(devlink);
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	return ret;
119262306a36Sopenharmony_ci}
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_cistatic int
119562306a36Sopenharmony_cidevlink_nl_selftests_fill(struct sk_buff *msg, struct devlink *devlink,
119662306a36Sopenharmony_ci			  u32 portid, u32 seq, int flags,
119762306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
119862306a36Sopenharmony_ci{
119962306a36Sopenharmony_ci	struct nlattr *selftests;
120062306a36Sopenharmony_ci	void *hdr;
120162306a36Sopenharmony_ci	int err;
120262306a36Sopenharmony_ci	int i;
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags,
120562306a36Sopenharmony_ci			  DEVLINK_CMD_SELFTESTS_GET);
120662306a36Sopenharmony_ci	if (!hdr)
120762306a36Sopenharmony_ci		return -EMSGSIZE;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	err = -EMSGSIZE;
121062306a36Sopenharmony_ci	if (devlink_nl_put_handle(msg, devlink))
121162306a36Sopenharmony_ci		goto err_cancel_msg;
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	selftests = nla_nest_start(msg, DEVLINK_ATTR_SELFTESTS);
121462306a36Sopenharmony_ci	if (!selftests)
121562306a36Sopenharmony_ci		goto err_cancel_msg;
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	for (i = DEVLINK_ATTR_SELFTEST_ID_UNSPEC + 1;
121862306a36Sopenharmony_ci	     i <= DEVLINK_ATTR_SELFTEST_ID_MAX; i++) {
121962306a36Sopenharmony_ci		if (devlink->ops->selftest_check(devlink, i, extack)) {
122062306a36Sopenharmony_ci			err = nla_put_flag(msg, i);
122162306a36Sopenharmony_ci			if (err)
122262306a36Sopenharmony_ci				goto err_cancel_msg;
122362306a36Sopenharmony_ci		}
122462306a36Sopenharmony_ci	}
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	nla_nest_end(msg, selftests);
122762306a36Sopenharmony_ci	genlmsg_end(msg, hdr);
122862306a36Sopenharmony_ci	return 0;
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_cierr_cancel_msg:
123162306a36Sopenharmony_ci	genlmsg_cancel(msg, hdr);
123262306a36Sopenharmony_ci	return err;
123362306a36Sopenharmony_ci}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ciint devlink_nl_selftests_get_doit(struct sk_buff *skb, struct genl_info *info)
123662306a36Sopenharmony_ci{
123762306a36Sopenharmony_ci	struct devlink *devlink = info->user_ptr[0];
123862306a36Sopenharmony_ci	struct sk_buff *msg;
123962306a36Sopenharmony_ci	int err;
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	if (!devlink->ops->selftest_check)
124262306a36Sopenharmony_ci		return -EOPNOTSUPP;
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
124562306a36Sopenharmony_ci	if (!msg)
124662306a36Sopenharmony_ci		return -ENOMEM;
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	err = devlink_nl_selftests_fill(msg, devlink, info->snd_portid,
124962306a36Sopenharmony_ci					info->snd_seq, 0, info->extack);
125062306a36Sopenharmony_ci	if (err) {
125162306a36Sopenharmony_ci		nlmsg_free(msg);
125262306a36Sopenharmony_ci		return err;
125362306a36Sopenharmony_ci	}
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	return genlmsg_reply(msg, info);
125662306a36Sopenharmony_ci}
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_cistatic int devlink_nl_selftests_get_dump_one(struct sk_buff *msg,
125962306a36Sopenharmony_ci					     struct devlink *devlink,
126062306a36Sopenharmony_ci					     struct netlink_callback *cb,
126162306a36Sopenharmony_ci					     int flags)
126262306a36Sopenharmony_ci{
126362306a36Sopenharmony_ci	if (!devlink->ops->selftest_check)
126462306a36Sopenharmony_ci		return 0;
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	return devlink_nl_selftests_fill(msg, devlink,
126762306a36Sopenharmony_ci					 NETLINK_CB(cb->skb).portid,
126862306a36Sopenharmony_ci					 cb->nlh->nlmsg_seq, flags,
126962306a36Sopenharmony_ci					 cb->extack);
127062306a36Sopenharmony_ci}
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ciint devlink_nl_selftests_get_dumpit(struct sk_buff *skb,
127362306a36Sopenharmony_ci				    struct netlink_callback *cb)
127462306a36Sopenharmony_ci{
127562306a36Sopenharmony_ci	return devlink_nl_dumpit(skb, cb, devlink_nl_selftests_get_dump_one);
127662306a36Sopenharmony_ci}
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_cistatic int devlink_selftest_result_put(struct sk_buff *skb, unsigned int id,
127962306a36Sopenharmony_ci				       enum devlink_selftest_status test_status)
128062306a36Sopenharmony_ci{
128162306a36Sopenharmony_ci	struct nlattr *result_attr;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	result_attr = nla_nest_start(skb, DEVLINK_ATTR_SELFTEST_RESULT);
128462306a36Sopenharmony_ci	if (!result_attr)
128562306a36Sopenharmony_ci		return -EMSGSIZE;
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	if (nla_put_u32(skb, DEVLINK_ATTR_SELFTEST_RESULT_ID, id) ||
128862306a36Sopenharmony_ci	    nla_put_u8(skb, DEVLINK_ATTR_SELFTEST_RESULT_STATUS,
128962306a36Sopenharmony_ci		       test_status))
129062306a36Sopenharmony_ci		goto nla_put_failure;
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	nla_nest_end(skb, result_attr);
129362306a36Sopenharmony_ci	return 0;
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_cinla_put_failure:
129662306a36Sopenharmony_ci	nla_nest_cancel(skb, result_attr);
129762306a36Sopenharmony_ci	return -EMSGSIZE;
129862306a36Sopenharmony_ci}
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_cistatic const struct nla_policy devlink_selftest_nl_policy[DEVLINK_ATTR_SELFTEST_ID_MAX + 1] = {
130162306a36Sopenharmony_ci	[DEVLINK_ATTR_SELFTEST_ID_FLASH] = { .type = NLA_FLAG },
130262306a36Sopenharmony_ci};
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ciint devlink_nl_cmd_selftests_run(struct sk_buff *skb, struct genl_info *info)
130562306a36Sopenharmony_ci{
130662306a36Sopenharmony_ci	struct nlattr *tb[DEVLINK_ATTR_SELFTEST_ID_MAX + 1];
130762306a36Sopenharmony_ci	struct devlink *devlink = info->user_ptr[0];
130862306a36Sopenharmony_ci	struct nlattr *attrs, *selftests;
130962306a36Sopenharmony_ci	struct sk_buff *msg;
131062306a36Sopenharmony_ci	void *hdr;
131162306a36Sopenharmony_ci	int err;
131262306a36Sopenharmony_ci	int i;
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	if (!devlink->ops->selftest_run || !devlink->ops->selftest_check)
131562306a36Sopenharmony_ci		return -EOPNOTSUPP;
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_SELFTESTS))
131862306a36Sopenharmony_ci		return -EINVAL;
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	attrs = info->attrs[DEVLINK_ATTR_SELFTESTS];
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	err = nla_parse_nested(tb, DEVLINK_ATTR_SELFTEST_ID_MAX, attrs,
132362306a36Sopenharmony_ci			       devlink_selftest_nl_policy, info->extack);
132462306a36Sopenharmony_ci	if (err < 0)
132562306a36Sopenharmony_ci		return err;
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
132862306a36Sopenharmony_ci	if (!msg)
132962306a36Sopenharmony_ci		return -ENOMEM;
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	err = -EMSGSIZE;
133262306a36Sopenharmony_ci	hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
133362306a36Sopenharmony_ci			  &devlink_nl_family, 0, DEVLINK_CMD_SELFTESTS_RUN);
133462306a36Sopenharmony_ci	if (!hdr)
133562306a36Sopenharmony_ci		goto free_msg;
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	if (devlink_nl_put_handle(msg, devlink))
133862306a36Sopenharmony_ci		goto genlmsg_cancel;
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	selftests = nla_nest_start(msg, DEVLINK_ATTR_SELFTESTS);
134162306a36Sopenharmony_ci	if (!selftests)
134262306a36Sopenharmony_ci		goto genlmsg_cancel;
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	for (i = DEVLINK_ATTR_SELFTEST_ID_UNSPEC + 1;
134562306a36Sopenharmony_ci	     i <= DEVLINK_ATTR_SELFTEST_ID_MAX; i++) {
134662306a36Sopenharmony_ci		enum devlink_selftest_status test_status;
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci		if (nla_get_flag(tb[i])) {
134962306a36Sopenharmony_ci			if (!devlink->ops->selftest_check(devlink, i,
135062306a36Sopenharmony_ci							  info->extack)) {
135162306a36Sopenharmony_ci				if (devlink_selftest_result_put(msg, i,
135262306a36Sopenharmony_ci								DEVLINK_SELFTEST_STATUS_SKIP))
135362306a36Sopenharmony_ci					goto selftests_nest_cancel;
135462306a36Sopenharmony_ci				continue;
135562306a36Sopenharmony_ci			}
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci			test_status = devlink->ops->selftest_run(devlink, i,
135862306a36Sopenharmony_ci								 info->extack);
135962306a36Sopenharmony_ci			if (devlink_selftest_result_put(msg, i, test_status))
136062306a36Sopenharmony_ci				goto selftests_nest_cancel;
136162306a36Sopenharmony_ci		}
136262306a36Sopenharmony_ci	}
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	nla_nest_end(msg, selftests);
136562306a36Sopenharmony_ci	genlmsg_end(msg, hdr);
136662306a36Sopenharmony_ci	return genlmsg_reply(msg, info);
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ciselftests_nest_cancel:
136962306a36Sopenharmony_ci	nla_nest_cancel(msg, selftests);
137062306a36Sopenharmony_cigenlmsg_cancel:
137162306a36Sopenharmony_ci	genlmsg_cancel(msg, hdr);
137262306a36Sopenharmony_cifree_msg:
137362306a36Sopenharmony_ci	nlmsg_free(msg);
137462306a36Sopenharmony_ci	return err;
137562306a36Sopenharmony_ci}
1376