xref: /openbmc/linux/net/devlink/netlink.c (revision 623cd13b165486afaa2df706d49209392f3764ca)
1*623cd13bSJakub Kicinski // SPDX-License-Identifier: GPL-2.0-or-later
2*623cd13bSJakub Kicinski /*
3*623cd13bSJakub Kicinski  * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
4*623cd13bSJakub Kicinski  * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
5*623cd13bSJakub Kicinski  */
6*623cd13bSJakub Kicinski 
7*623cd13bSJakub Kicinski #include <net/genetlink.h>
8*623cd13bSJakub Kicinski 
9*623cd13bSJakub Kicinski #include "devl_internal.h"
10*623cd13bSJakub Kicinski 
11*623cd13bSJakub Kicinski static const struct genl_multicast_group devlink_nl_mcgrps[] = {
12*623cd13bSJakub Kicinski 	[DEVLINK_MCGRP_CONFIG] = { .name = DEVLINK_GENL_MCGRP_CONFIG_NAME },
13*623cd13bSJakub Kicinski };
14*623cd13bSJakub Kicinski 
15*623cd13bSJakub Kicinski static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
16*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_UNSPEC] = { .strict_start_type =
17*623cd13bSJakub Kicinski 		DEVLINK_ATTR_TRAP_POLICER_ID },
18*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING },
19*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING },
20*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_PORT_INDEX] = { .type = NLA_U32 },
21*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_PORT_TYPE] = NLA_POLICY_RANGE(NLA_U16, DEVLINK_PORT_TYPE_AUTO,
22*623cd13bSJakub Kicinski 						    DEVLINK_PORT_TYPE_IB),
23*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_PORT_SPLIT_COUNT] = { .type = NLA_U32 },
24*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_SB_INDEX] = { .type = NLA_U32 },
25*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_SB_POOL_INDEX] = { .type = NLA_U16 },
26*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_SB_POOL_TYPE] = { .type = NLA_U8 },
27*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_SB_POOL_SIZE] = { .type = NLA_U32 },
28*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE] = { .type = NLA_U8 },
29*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_SB_THRESHOLD] = { .type = NLA_U32 },
30*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_SB_TC_INDEX] = { .type = NLA_U16 },
31*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_ESWITCH_MODE] = NLA_POLICY_RANGE(NLA_U16, DEVLINK_ESWITCH_MODE_LEGACY,
32*623cd13bSJakub Kicinski 						       DEVLINK_ESWITCH_MODE_SWITCHDEV),
33*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_ESWITCH_INLINE_MODE] = { .type = NLA_U8 },
34*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_ESWITCH_ENCAP_MODE] = { .type = NLA_U8 },
35*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_DPIPE_TABLE_NAME] = { .type = NLA_NUL_STRING },
36*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED] = { .type = NLA_U8 },
37*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_RESOURCE_ID] = { .type = NLA_U64},
38*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_RESOURCE_SIZE] = { .type = NLA_U64},
39*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_PARAM_NAME] = { .type = NLA_NUL_STRING },
40*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_PARAM_TYPE] = { .type = NLA_U8 },
41*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_PARAM_VALUE_CMODE] = { .type = NLA_U8 },
42*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_REGION_NAME] = { .type = NLA_NUL_STRING },
43*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_REGION_SNAPSHOT_ID] = { .type = NLA_U32 },
44*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_REGION_CHUNK_ADDR] = { .type = NLA_U64 },
45*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_REGION_CHUNK_LEN] = { .type = NLA_U64 },
46*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_HEALTH_REPORTER_NAME] = { .type = NLA_NUL_STRING },
47*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD] = { .type = NLA_U64 },
48*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER] = { .type = NLA_U8 },
49*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME] = { .type = NLA_NUL_STRING },
50*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT] = { .type = NLA_NUL_STRING },
51*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_FLASH_UPDATE_OVERWRITE_MASK] =
52*623cd13bSJakub Kicinski 		NLA_POLICY_BITFIELD32(DEVLINK_SUPPORTED_FLASH_OVERWRITE_SECTIONS),
53*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_TRAP_NAME] = { .type = NLA_NUL_STRING },
54*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_TRAP_ACTION] = { .type = NLA_U8 },
55*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_TRAP_GROUP_NAME] = { .type = NLA_NUL_STRING },
56*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_NETNS_PID] = { .type = NLA_U32 },
57*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_NETNS_FD] = { .type = NLA_U32 },
58*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_NETNS_ID] = { .type = NLA_U32 },
59*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP] = { .type = NLA_U8 },
60*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_TRAP_POLICER_ID] = { .type = NLA_U32 },
61*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_TRAP_POLICER_RATE] = { .type = NLA_U64 },
62*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_TRAP_POLICER_BURST] = { .type = NLA_U64 },
63*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_PORT_FUNCTION] = { .type = NLA_NESTED },
64*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_RELOAD_ACTION] = NLA_POLICY_RANGE(NLA_U8, DEVLINK_RELOAD_ACTION_DRIVER_REINIT,
65*623cd13bSJakub Kicinski 							DEVLINK_RELOAD_ACTION_MAX),
66*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_RELOAD_LIMITS] = NLA_POLICY_BITFIELD32(DEVLINK_RELOAD_LIMITS_VALID_MASK),
67*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_PORT_FLAVOUR] = { .type = NLA_U16 },
68*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_PORT_PCI_PF_NUMBER] = { .type = NLA_U16 },
69*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_PORT_PCI_SF_NUMBER] = { .type = NLA_U32 },
70*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_PORT_CONTROLLER_NUMBER] = { .type = NLA_U32 },
71*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_RATE_TYPE] = { .type = NLA_U16 },
72*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_RATE_TX_SHARE] = { .type = NLA_U64 },
73*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_RATE_TX_MAX] = { .type = NLA_U64 },
74*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_RATE_NODE_NAME] = { .type = NLA_NUL_STRING },
75*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_RATE_PARENT_NODE_NAME] = { .type = NLA_NUL_STRING },
76*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_LINECARD_INDEX] = { .type = NLA_U32 },
77*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_LINECARD_TYPE] = { .type = NLA_NUL_STRING },
78*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_SELFTESTS] = { .type = NLA_NESTED },
79*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_RATE_TX_PRIORITY] = { .type = NLA_U32 },
80*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_RATE_TX_WEIGHT] = { .type = NLA_U32 },
81*623cd13bSJakub Kicinski 	[DEVLINK_ATTR_REGION_DIRECT] = { .type = NLA_FLAG },
82*623cd13bSJakub Kicinski };
83*623cd13bSJakub Kicinski 
84*623cd13bSJakub Kicinski struct devlink *devlink_get_from_attrs(struct net *net, struct nlattr **attrs)
85*623cd13bSJakub Kicinski {
86*623cd13bSJakub Kicinski 	struct devlink *devlink;
87*623cd13bSJakub Kicinski 	unsigned long index;
88*623cd13bSJakub Kicinski 	char *busname;
89*623cd13bSJakub Kicinski 	char *devname;
90*623cd13bSJakub Kicinski 
91*623cd13bSJakub Kicinski 	if (!attrs[DEVLINK_ATTR_BUS_NAME] || !attrs[DEVLINK_ATTR_DEV_NAME])
92*623cd13bSJakub Kicinski 		return ERR_PTR(-EINVAL);
93*623cd13bSJakub Kicinski 
94*623cd13bSJakub Kicinski 	busname = nla_data(attrs[DEVLINK_ATTR_BUS_NAME]);
95*623cd13bSJakub Kicinski 	devname = nla_data(attrs[DEVLINK_ATTR_DEV_NAME]);
96*623cd13bSJakub Kicinski 
97*623cd13bSJakub Kicinski 	devlinks_xa_for_each_registered_get(net, index, devlink) {
98*623cd13bSJakub Kicinski 		if (strcmp(devlink->dev->bus->name, busname) == 0 &&
99*623cd13bSJakub Kicinski 		    strcmp(dev_name(devlink->dev), devname) == 0)
100*623cd13bSJakub Kicinski 			return devlink;
101*623cd13bSJakub Kicinski 		devlink_put(devlink);
102*623cd13bSJakub Kicinski 	}
103*623cd13bSJakub Kicinski 
104*623cd13bSJakub Kicinski 	return ERR_PTR(-ENODEV);
105*623cd13bSJakub Kicinski }
106*623cd13bSJakub Kicinski 
107*623cd13bSJakub Kicinski static int devlink_nl_pre_doit(const struct genl_split_ops *ops,
108*623cd13bSJakub Kicinski 			       struct sk_buff *skb, struct genl_info *info)
109*623cd13bSJakub Kicinski {
110*623cd13bSJakub Kicinski 	struct devlink_linecard *linecard;
111*623cd13bSJakub Kicinski 	struct devlink_port *devlink_port;
112*623cd13bSJakub Kicinski 	struct devlink *devlink;
113*623cd13bSJakub Kicinski 	int err;
114*623cd13bSJakub Kicinski 
115*623cd13bSJakub Kicinski 	devlink = devlink_get_from_attrs(genl_info_net(info), info->attrs);
116*623cd13bSJakub Kicinski 	if (IS_ERR(devlink))
117*623cd13bSJakub Kicinski 		return PTR_ERR(devlink);
118*623cd13bSJakub Kicinski 	devl_lock(devlink);
119*623cd13bSJakub Kicinski 	info->user_ptr[0] = devlink;
120*623cd13bSJakub Kicinski 	if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_PORT) {
121*623cd13bSJakub Kicinski 		devlink_port = devlink_port_get_from_info(devlink, info);
122*623cd13bSJakub Kicinski 		if (IS_ERR(devlink_port)) {
123*623cd13bSJakub Kicinski 			err = PTR_ERR(devlink_port);
124*623cd13bSJakub Kicinski 			goto unlock;
125*623cd13bSJakub Kicinski 		}
126*623cd13bSJakub Kicinski 		info->user_ptr[1] = devlink_port;
127*623cd13bSJakub Kicinski 	} else if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT) {
128*623cd13bSJakub Kicinski 		devlink_port = devlink_port_get_from_info(devlink, info);
129*623cd13bSJakub Kicinski 		if (!IS_ERR(devlink_port))
130*623cd13bSJakub Kicinski 			info->user_ptr[1] = devlink_port;
131*623cd13bSJakub Kicinski 	} else if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_RATE) {
132*623cd13bSJakub Kicinski 		struct devlink_rate *devlink_rate;
133*623cd13bSJakub Kicinski 
134*623cd13bSJakub Kicinski 		devlink_rate = devlink_rate_get_from_info(devlink, info);
135*623cd13bSJakub Kicinski 		if (IS_ERR(devlink_rate)) {
136*623cd13bSJakub Kicinski 			err = PTR_ERR(devlink_rate);
137*623cd13bSJakub Kicinski 			goto unlock;
138*623cd13bSJakub Kicinski 		}
139*623cd13bSJakub Kicinski 		info->user_ptr[1] = devlink_rate;
140*623cd13bSJakub Kicinski 	} else if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_RATE_NODE) {
141*623cd13bSJakub Kicinski 		struct devlink_rate *rate_node;
142*623cd13bSJakub Kicinski 
143*623cd13bSJakub Kicinski 		rate_node = devlink_rate_node_get_from_info(devlink, info);
144*623cd13bSJakub Kicinski 		if (IS_ERR(rate_node)) {
145*623cd13bSJakub Kicinski 			err = PTR_ERR(rate_node);
146*623cd13bSJakub Kicinski 			goto unlock;
147*623cd13bSJakub Kicinski 		}
148*623cd13bSJakub Kicinski 		info->user_ptr[1] = rate_node;
149*623cd13bSJakub Kicinski 	} else if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_LINECARD) {
150*623cd13bSJakub Kicinski 		linecard = devlink_linecard_get_from_info(devlink, info);
151*623cd13bSJakub Kicinski 		if (IS_ERR(linecard)) {
152*623cd13bSJakub Kicinski 			err = PTR_ERR(linecard);
153*623cd13bSJakub Kicinski 			goto unlock;
154*623cd13bSJakub Kicinski 		}
155*623cd13bSJakub Kicinski 		info->user_ptr[1] = linecard;
156*623cd13bSJakub Kicinski 	}
157*623cd13bSJakub Kicinski 	return 0;
158*623cd13bSJakub Kicinski 
159*623cd13bSJakub Kicinski unlock:
160*623cd13bSJakub Kicinski 	devl_unlock(devlink);
161*623cd13bSJakub Kicinski 	devlink_put(devlink);
162*623cd13bSJakub Kicinski 	return err;
163*623cd13bSJakub Kicinski }
164*623cd13bSJakub Kicinski 
165*623cd13bSJakub Kicinski static void devlink_nl_post_doit(const struct genl_split_ops *ops,
166*623cd13bSJakub Kicinski 				 struct sk_buff *skb, struct genl_info *info)
167*623cd13bSJakub Kicinski {
168*623cd13bSJakub Kicinski 	struct devlink_linecard *linecard;
169*623cd13bSJakub Kicinski 	struct devlink *devlink;
170*623cd13bSJakub Kicinski 
171*623cd13bSJakub Kicinski 	devlink = info->user_ptr[0];
172*623cd13bSJakub Kicinski 	if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_LINECARD) {
173*623cd13bSJakub Kicinski 		linecard = info->user_ptr[1];
174*623cd13bSJakub Kicinski 		devlink_linecard_put(linecard);
175*623cd13bSJakub Kicinski 	}
176*623cd13bSJakub Kicinski 	devl_unlock(devlink);
177*623cd13bSJakub Kicinski 	devlink_put(devlink);
178*623cd13bSJakub Kicinski }
179*623cd13bSJakub Kicinski 
180*623cd13bSJakub Kicinski struct genl_family devlink_nl_family __ro_after_init = {
181*623cd13bSJakub Kicinski 	.name		= DEVLINK_GENL_NAME,
182*623cd13bSJakub Kicinski 	.version	= DEVLINK_GENL_VERSION,
183*623cd13bSJakub Kicinski 	.maxattr	= DEVLINK_ATTR_MAX,
184*623cd13bSJakub Kicinski 	.policy		= devlink_nl_policy,
185*623cd13bSJakub Kicinski 	.netnsok	= true,
186*623cd13bSJakub Kicinski 	.parallel_ops	= true,
187*623cd13bSJakub Kicinski 	.pre_doit	= devlink_nl_pre_doit,
188*623cd13bSJakub Kicinski 	.post_doit	= devlink_nl_post_doit,
189*623cd13bSJakub Kicinski 	.module		= THIS_MODULE,
190*623cd13bSJakub Kicinski 	.small_ops	= devlink_nl_ops,
191*623cd13bSJakub Kicinski 	.n_small_ops	= ARRAY_SIZE(devlink_nl_ops),
192*623cd13bSJakub Kicinski 	.resv_start_op	= DEVLINK_CMD_SELFTESTS_RUN + 1,
193*623cd13bSJakub Kicinski 	.mcgrps		= devlink_nl_mcgrps,
194*623cd13bSJakub Kicinski 	.n_mcgrps	= ARRAY_SIZE(devlink_nl_mcgrps),
195*623cd13bSJakub Kicinski };
196