diff options
author | Jiri Pirko <jiri@mellanox.com> | 2016-02-26 11:32:23 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-03-01 16:07:29 -0500 |
commit | bfcd3a46617209454cfc0947ab093e37fd1e84ef (patch) | |
tree | 95eeb37695a1b78d4b99f4c4c4a616deed26825a | |
parent | bd070e212688c0d95c68dfe7d54a5aa2a60a8f11 (diff) |
Introduce devlink infrastructure
Introduce devlink infrastructure for drivers to register and expose to
userspace via generic Netlink interface.
There are two basic objects defined:
devlink - one instance for every "parent device", for example switch ASIC
devlink port - one instance for every physical port of the device.
This initial portion implements basic get/dump of objects to userspace.
Also, port splitter and port type setting is implemented.
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | MAINTAINERS | 8 | ||||
-rw-r--r-- | include/net/devlink.h | 140 | ||||
-rw-r--r-- | include/uapi/linux/devlink.h | 72 | ||||
-rw-r--r-- | net/Kconfig | 7 | ||||
-rw-r--r-- | net/core/Makefile | 1 | ||||
-rw-r--r-- | net/core/devlink.c | 738 |
6 files changed, 966 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 12b764f4c93c..e45682745263 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -3499,6 +3499,14 @@ F: include/linux/device-mapper.h | |||
3499 | F: include/linux/dm-*.h | 3499 | F: include/linux/dm-*.h |
3500 | F: include/uapi/linux/dm-*.h | 3500 | F: include/uapi/linux/dm-*.h |
3501 | 3501 | ||
3502 | DEVLINK | ||
3503 | M: Jiri Pirko <jiri@mellanox.com> | ||
3504 | L: netdev@vger.kernel.org | ||
3505 | S: Supported | ||
3506 | F: net/core/devlink.c | ||
3507 | F: include/net/devlink.h | ||
3508 | F: include/uapi/linux/devlink.h | ||
3509 | |||
3502 | DIALOG SEMICONDUCTOR DRIVERS | 3510 | DIALOG SEMICONDUCTOR DRIVERS |
3503 | M: Support Opensource <support.opensource@diasemi.com> | 3511 | M: Support Opensource <support.opensource@diasemi.com> |
3504 | W: http://www.dialog-semiconductor.com/products | 3512 | W: http://www.dialog-semiconductor.com/products |
diff --git a/include/net/devlink.h b/include/net/devlink.h new file mode 100644 index 000000000000..c37d257891d6 --- /dev/null +++ b/include/net/devlink.h | |||
@@ -0,0 +1,140 @@ | |||
1 | /* | ||
2 | * include/net/devlink.h - Network physical device Netlink interface | ||
3 | * Copyright (c) 2016 Mellanox Technologies. All rights reserved. | ||
4 | * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | */ | ||
11 | #ifndef _NET_DEVLINK_H_ | ||
12 | #define _NET_DEVLINK_H_ | ||
13 | |||
14 | #include <linux/device.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/gfp.h> | ||
17 | #include <linux/list.h> | ||
18 | #include <linux/netdevice.h> | ||
19 | #include <net/net_namespace.h> | ||
20 | #include <uapi/linux/devlink.h> | ||
21 | |||
22 | struct devlink_ops; | ||
23 | |||
24 | struct devlink { | ||
25 | struct list_head list; | ||
26 | struct list_head port_list; | ||
27 | const struct devlink_ops *ops; | ||
28 | struct device *dev; | ||
29 | possible_net_t _net; | ||
30 | char priv[0] __aligned(NETDEV_ALIGN); | ||
31 | }; | ||
32 | |||
33 | struct devlink_port { | ||
34 | struct list_head list; | ||
35 | struct devlink *devlink; | ||
36 | unsigned index; | ||
37 | bool registered; | ||
38 | enum devlink_port_type type; | ||
39 | enum devlink_port_type desired_type; | ||
40 | void *type_dev; | ||
41 | bool split; | ||
42 | u32 split_group; | ||
43 | }; | ||
44 | |||
45 | struct devlink_ops { | ||
46 | size_t priv_size; | ||
47 | int (*port_type_set)(struct devlink_port *devlink_port, | ||
48 | enum devlink_port_type port_type); | ||
49 | int (*port_split)(struct devlink *devlink, unsigned int port_index, | ||
50 | unsigned int count); | ||
51 | int (*port_unsplit)(struct devlink *devlink, unsigned int port_index); | ||
52 | }; | ||
53 | |||
54 | static inline void *devlink_priv(struct devlink *devlink) | ||
55 | { | ||
56 | BUG_ON(!devlink); | ||
57 | return &devlink->priv; | ||
58 | } | ||
59 | |||
60 | static inline struct devlink *priv_to_devlink(void *priv) | ||
61 | { | ||
62 | BUG_ON(!priv); | ||
63 | return container_of(priv, struct devlink, priv); | ||
64 | } | ||
65 | |||
66 | struct ib_device; | ||
67 | |||
68 | #if IS_ENABLED(CONFIG_NET_DEVLINK) | ||
69 | |||
70 | struct devlink *devlink_alloc(const struct devlink_ops *ops, size_t priv_size); | ||
71 | int devlink_register(struct devlink *devlink, struct device *dev); | ||
72 | void devlink_unregister(struct devlink *devlink); | ||
73 | void devlink_free(struct devlink *devlink); | ||
74 | int devlink_port_register(struct devlink *devlink, | ||
75 | struct devlink_port *devlink_port, | ||
76 | unsigned int port_index); | ||
77 | void devlink_port_unregister(struct devlink_port *devlink_port); | ||
78 | void devlink_port_type_eth_set(struct devlink_port *devlink_port, | ||
79 | struct net_device *netdev); | ||
80 | void devlink_port_type_ib_set(struct devlink_port *devlink_port, | ||
81 | struct ib_device *ibdev); | ||
82 | void devlink_port_type_clear(struct devlink_port *devlink_port); | ||
83 | void devlink_port_split_set(struct devlink_port *devlink_port, | ||
84 | u32 split_group); | ||
85 | |||
86 | #else | ||
87 | |||
88 | static inline struct devlink *devlink_alloc(const struct devlink_ops *ops, | ||
89 | size_t priv_size) | ||
90 | { | ||
91 | return kzalloc(sizeof(struct devlink) + priv_size, GFP_KERNEL); | ||
92 | } | ||
93 | |||
94 | static inline int devlink_register(struct devlink *devlink, struct device *dev) | ||
95 | { | ||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | static inline void devlink_unregister(struct devlink *devlink) | ||
100 | { | ||
101 | } | ||
102 | |||
103 | static inline void devlink_free(struct devlink *devlink) | ||
104 | { | ||
105 | kfree(devlink); | ||
106 | } | ||
107 | |||
108 | static inline int devlink_port_register(struct devlink *devlink, | ||
109 | struct devlink_port *devlink_port, | ||
110 | unsigned int port_index) | ||
111 | { | ||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | static inline void devlink_port_unregister(struct devlink_port *devlink_port) | ||
116 | { | ||
117 | } | ||
118 | |||
119 | static inline void devlink_port_type_eth_set(struct devlink_port *devlink_port, | ||
120 | struct net_device *netdev) | ||
121 | { | ||
122 | } | ||
123 | |||
124 | static inline void devlink_port_type_ib_set(struct devlink_port *devlink_port, | ||
125 | struct ib_device *ibdev) | ||
126 | { | ||
127 | } | ||
128 | |||
129 | static inline void devlink_port_type_clear(struct devlink_port *devlink_port) | ||
130 | { | ||
131 | } | ||
132 | |||
133 | static inline void devlink_port_split_set(struct devlink_port *devlink_port, | ||
134 | u32 split_group) | ||
135 | { | ||
136 | } | ||
137 | |||
138 | #endif | ||
139 | |||
140 | #endif /* _NET_DEVLINK_H_ */ | ||
diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h new file mode 100644 index 000000000000..c9fee5781eb1 --- /dev/null +++ b/include/uapi/linux/devlink.h | |||
@@ -0,0 +1,72 @@ | |||
1 | /* | ||
2 | * include/uapi/linux/devlink.h - Network physical device Netlink interface | ||
3 | * Copyright (c) 2016 Mellanox Technologies. All rights reserved. | ||
4 | * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #ifndef _UAPI_LINUX_DEVLINK_H_ | ||
13 | #define _UAPI_LINUX_DEVLINK_H_ | ||
14 | |||
15 | #define DEVLINK_GENL_NAME "devlink" | ||
16 | #define DEVLINK_GENL_VERSION 0x1 | ||
17 | #define DEVLINK_GENL_MCGRP_CONFIG_NAME "config" | ||
18 | |||
19 | enum devlink_command { | ||
20 | /* don't change the order or add anything between, this is ABI! */ | ||
21 | DEVLINK_CMD_UNSPEC, | ||
22 | |||
23 | DEVLINK_CMD_GET, /* can dump */ | ||
24 | DEVLINK_CMD_SET, | ||
25 | DEVLINK_CMD_NEW, | ||
26 | DEVLINK_CMD_DEL, | ||
27 | |||
28 | DEVLINK_CMD_PORT_GET, /* can dump */ | ||
29 | DEVLINK_CMD_PORT_SET, | ||
30 | DEVLINK_CMD_PORT_NEW, | ||
31 | DEVLINK_CMD_PORT_DEL, | ||
32 | |||
33 | DEVLINK_CMD_PORT_SPLIT, | ||
34 | DEVLINK_CMD_PORT_UNSPLIT, | ||
35 | |||
36 | /* add new commands above here */ | ||
37 | |||
38 | __DEVLINK_CMD_MAX, | ||
39 | DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1 | ||
40 | }; | ||
41 | |||
42 | enum devlink_port_type { | ||
43 | DEVLINK_PORT_TYPE_NOTSET, | ||
44 | DEVLINK_PORT_TYPE_AUTO, | ||
45 | DEVLINK_PORT_TYPE_ETH, | ||
46 | DEVLINK_PORT_TYPE_IB, | ||
47 | }; | ||
48 | |||
49 | enum devlink_attr { | ||
50 | /* don't change the order or add anything between, this is ABI! */ | ||
51 | DEVLINK_ATTR_UNSPEC, | ||
52 | |||
53 | /* bus name + dev name together are a handle for devlink entity */ | ||
54 | DEVLINK_ATTR_BUS_NAME, /* string */ | ||
55 | DEVLINK_ATTR_DEV_NAME, /* string */ | ||
56 | |||
57 | DEVLINK_ATTR_PORT_INDEX, /* u32 */ | ||
58 | DEVLINK_ATTR_PORT_TYPE, /* u16 */ | ||
59 | DEVLINK_ATTR_PORT_DESIRED_TYPE, /* u16 */ | ||
60 | DEVLINK_ATTR_PORT_NETDEV_IFINDEX, /* u32 */ | ||
61 | DEVLINK_ATTR_PORT_NETDEV_NAME, /* string */ | ||
62 | DEVLINK_ATTR_PORT_IBDEV_NAME, /* string */ | ||
63 | DEVLINK_ATTR_PORT_SPLIT_COUNT, /* u32 */ | ||
64 | DEVLINK_ATTR_PORT_SPLIT_GROUP, /* u32 */ | ||
65 | |||
66 | /* add new attributes above here, update the policy in devlink.c */ | ||
67 | |||
68 | __DEVLINK_ATTR_MAX, | ||
69 | DEVLINK_ATTR_MAX = __DEVLINK_ATTR_MAX - 1 | ||
70 | }; | ||
71 | |||
72 | #endif /* _UAPI_LINUX_DEVLINK_H_ */ | ||
diff --git a/net/Kconfig b/net/Kconfig index b80efecfc1a0..6c9cfb0d7639 100644 --- a/net/Kconfig +++ b/net/Kconfig | |||
@@ -396,6 +396,13 @@ config DST_CACHE | |||
396 | bool "dst cache" | 396 | bool "dst cache" |
397 | default n | 397 | default n |
398 | 398 | ||
399 | config NET_DEVLINK | ||
400 | tristate "Network physical/parent device Netlink interface" | ||
401 | help | ||
402 | Network physical/parent device Netlink interface provides | ||
403 | infrastructure to support access to physical chip-wide config and | ||
404 | monitoring. | ||
405 | |||
399 | endif # if NET | 406 | endif # if NET |
400 | 407 | ||
401 | # Used by archs to tell that they support BPF_JIT | 408 | # Used by archs to tell that they support BPF_JIT |
diff --git a/net/core/Makefile b/net/core/Makefile index 7a8fb8aef992..014422e2561f 100644 --- a/net/core/Makefile +++ b/net/core/Makefile | |||
@@ -25,3 +25,4 @@ obj-$(CONFIG_CGROUP_NET_PRIO) += netprio_cgroup.o | |||
25 | obj-$(CONFIG_CGROUP_NET_CLASSID) += netclassid_cgroup.o | 25 | obj-$(CONFIG_CGROUP_NET_CLASSID) += netclassid_cgroup.o |
26 | obj-$(CONFIG_LWTUNNEL) += lwtunnel.o | 26 | obj-$(CONFIG_LWTUNNEL) += lwtunnel.o |
27 | obj-$(CONFIG_DST_CACHE) += dst_cache.o | 27 | obj-$(CONFIG_DST_CACHE) += dst_cache.o |
28 | obj-$(CONFIG_NET_DEVLINK) += devlink.o | ||
diff --git a/net/core/devlink.c b/net/core/devlink.c new file mode 100644 index 000000000000..590fa561cb7f --- /dev/null +++ b/net/core/devlink.c | |||
@@ -0,0 +1,738 @@ | |||
1 | /* | ||
2 | * net/core/devlink.c - Network physical/parent device Netlink interface | ||
3 | * | ||
4 | * Heavily inspired by net/wireless/ | ||
5 | * Copyright (c) 2016 Mellanox Technologies. All rights reserved. | ||
6 | * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/types.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/gfp.h> | ||
19 | #include <linux/device.h> | ||
20 | #include <linux/list.h> | ||
21 | #include <linux/netdevice.h> | ||
22 | #include <rdma/ib_verbs.h> | ||
23 | #include <net/netlink.h> | ||
24 | #include <net/genetlink.h> | ||
25 | #include <net/rtnetlink.h> | ||
26 | #include <net/net_namespace.h> | ||
27 | #include <net/sock.h> | ||
28 | #include <net/devlink.h> | ||
29 | |||
30 | static LIST_HEAD(devlink_list); | ||
31 | |||
32 | /* devlink_mutex | ||
33 | * | ||
34 | * An overall lock guarding every operation coming from userspace. | ||
35 | * It also guards devlink devices list and it is taken when | ||
36 | * driver registers/unregisters it. | ||
37 | */ | ||
38 | static DEFINE_MUTEX(devlink_mutex); | ||
39 | |||
40 | /* devlink_port_mutex | ||
41 | * | ||
42 | * Shared lock to guard lists of ports in all devlink devices. | ||
43 | */ | ||
44 | static DEFINE_MUTEX(devlink_port_mutex); | ||
45 | |||
46 | static struct net *devlink_net(const struct devlink *devlink) | ||
47 | { | ||
48 | return read_pnet(&devlink->_net); | ||
49 | } | ||
50 | |||
51 | static void devlink_net_set(struct devlink *devlink, struct net *net) | ||
52 | { | ||
53 | write_pnet(&devlink->_net, net); | ||
54 | } | ||
55 | |||
56 | static struct devlink *devlink_get_from_attrs(struct net *net, | ||
57 | struct nlattr **attrs) | ||
58 | { | ||
59 | struct devlink *devlink; | ||
60 | char *busname; | ||
61 | char *devname; | ||
62 | |||
63 | if (!attrs[DEVLINK_ATTR_BUS_NAME] || !attrs[DEVLINK_ATTR_DEV_NAME]) | ||
64 | return ERR_PTR(-EINVAL); | ||
65 | |||
66 | busname = nla_data(attrs[DEVLINK_ATTR_BUS_NAME]); | ||
67 | devname = nla_data(attrs[DEVLINK_ATTR_DEV_NAME]); | ||
68 | |||
69 | list_for_each_entry(devlink, &devlink_list, list) { | ||
70 | if (strcmp(devlink->dev->bus->name, busname) == 0 && | ||
71 | strcmp(dev_name(devlink->dev), devname) == 0 && | ||
72 | net_eq(devlink_net(devlink), net)) | ||
73 | return devlink; | ||
74 | } | ||
75 | |||
76 | return ERR_PTR(-ENODEV); | ||
77 | } | ||
78 | |||
79 | static struct devlink *devlink_get_from_info(struct genl_info *info) | ||
80 | { | ||
81 | return devlink_get_from_attrs(genl_info_net(info), info->attrs); | ||
82 | } | ||
83 | |||
84 | static struct devlink_port *devlink_port_get_by_index(struct devlink *devlink, | ||
85 | int port_index) | ||
86 | { | ||
87 | struct devlink_port *devlink_port; | ||
88 | |||
89 | list_for_each_entry(devlink_port, &devlink->port_list, list) { | ||
90 | if (devlink_port->index == port_index) | ||
91 | return devlink_port; | ||
92 | } | ||
93 | return NULL; | ||
94 | } | ||
95 | |||
96 | static bool devlink_port_index_exists(struct devlink *devlink, int port_index) | ||
97 | { | ||
98 | return devlink_port_get_by_index(devlink, port_index); | ||
99 | } | ||
100 | |||
101 | static struct devlink_port *devlink_port_get_from_attrs(struct devlink *devlink, | ||
102 | struct nlattr **attrs) | ||
103 | { | ||
104 | if (attrs[DEVLINK_ATTR_PORT_INDEX]) { | ||
105 | u32 port_index = nla_get_u32(attrs[DEVLINK_ATTR_PORT_INDEX]); | ||
106 | struct devlink_port *devlink_port; | ||
107 | |||
108 | devlink_port = devlink_port_get_by_index(devlink, port_index); | ||
109 | if (!devlink_port) | ||
110 | return ERR_PTR(-ENODEV); | ||
111 | return devlink_port; | ||
112 | } | ||
113 | return ERR_PTR(-EINVAL); | ||
114 | } | ||
115 | |||
116 | static struct devlink_port *devlink_port_get_from_info(struct devlink *devlink, | ||
117 | struct genl_info *info) | ||
118 | { | ||
119 | return devlink_port_get_from_attrs(devlink, info->attrs); | ||
120 | } | ||
121 | |||
122 | #define DEVLINK_NL_FLAG_NEED_PORT BIT(0) | ||
123 | |||
124 | static int devlink_nl_pre_doit(const struct genl_ops *ops, | ||
125 | struct sk_buff *skb, struct genl_info *info) | ||
126 | { | ||
127 | struct devlink *devlink; | ||
128 | |||
129 | mutex_lock(&devlink_mutex); | ||
130 | devlink = devlink_get_from_info(info); | ||
131 | if (IS_ERR(devlink)) { | ||
132 | mutex_unlock(&devlink_mutex); | ||
133 | return PTR_ERR(devlink); | ||
134 | } | ||
135 | info->user_ptr[0] = devlink; | ||
136 | if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_PORT) { | ||
137 | struct devlink_port *devlink_port; | ||
138 | |||
139 | mutex_lock(&devlink_port_mutex); | ||
140 | devlink_port = devlink_port_get_from_info(devlink, info); | ||
141 | if (IS_ERR(devlink_port)) { | ||
142 | mutex_unlock(&devlink_port_mutex); | ||
143 | mutex_unlock(&devlink_mutex); | ||
144 | return PTR_ERR(devlink_port); | ||
145 | } | ||
146 | info->user_ptr[1] = devlink_port; | ||
147 | } | ||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | static void devlink_nl_post_doit(const struct genl_ops *ops, | ||
152 | struct sk_buff *skb, struct genl_info *info) | ||
153 | { | ||
154 | if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_PORT) | ||
155 | mutex_unlock(&devlink_port_mutex); | ||
156 | mutex_unlock(&devlink_mutex); | ||
157 | } | ||
158 | |||
159 | static struct genl_family devlink_nl_family = { | ||
160 | .id = GENL_ID_GENERATE, | ||
161 | .name = DEVLINK_GENL_NAME, | ||
162 | .version = DEVLINK_GENL_VERSION, | ||
163 | .maxattr = DEVLINK_ATTR_MAX, | ||
164 | .netnsok = true, | ||
165 | .pre_doit = devlink_nl_pre_doit, | ||
166 | .post_doit = devlink_nl_post_doit, | ||
167 | }; | ||
168 | |||
169 | enum devlink_multicast_groups { | ||
170 | DEVLINK_MCGRP_CONFIG, | ||
171 | }; | ||
172 | |||
173 | static const struct genl_multicast_group devlink_nl_mcgrps[] = { | ||
174 | [DEVLINK_MCGRP_CONFIG] = { .name = DEVLINK_GENL_MCGRP_CONFIG_NAME }, | ||
175 | }; | ||
176 | |||
177 | static int devlink_nl_put_handle(struct sk_buff *msg, struct devlink *devlink) | ||
178 | { | ||
179 | if (nla_put_string(msg, DEVLINK_ATTR_BUS_NAME, devlink->dev->bus->name)) | ||
180 | return -EMSGSIZE; | ||
181 | if (nla_put_string(msg, DEVLINK_ATTR_DEV_NAME, dev_name(devlink->dev))) | ||
182 | return -EMSGSIZE; | ||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | static int devlink_nl_fill(struct sk_buff *msg, struct devlink *devlink, | ||
187 | enum devlink_command cmd, u32 portid, | ||
188 | u32 seq, int flags) | ||
189 | { | ||
190 | void *hdr; | ||
191 | |||
192 | hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd); | ||
193 | if (!hdr) | ||
194 | return -EMSGSIZE; | ||
195 | |||
196 | if (devlink_nl_put_handle(msg, devlink)) | ||
197 | goto nla_put_failure; | ||
198 | |||
199 | genlmsg_end(msg, hdr); | ||
200 | return 0; | ||
201 | |||
202 | nla_put_failure: | ||
203 | genlmsg_cancel(msg, hdr); | ||
204 | return -EMSGSIZE; | ||
205 | } | ||
206 | |||
207 | static void devlink_notify(struct devlink *devlink, enum devlink_command cmd) | ||
208 | { | ||
209 | struct sk_buff *msg; | ||
210 | int err; | ||
211 | |||
212 | WARN_ON(cmd != DEVLINK_CMD_NEW && cmd != DEVLINK_CMD_DEL); | ||
213 | |||
214 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
215 | if (!msg) | ||
216 | return; | ||
217 | |||
218 | err = devlink_nl_fill(msg, devlink, cmd, 0, 0, 0); | ||
219 | if (err) { | ||
220 | nlmsg_free(msg); | ||
221 | return; | ||
222 | } | ||
223 | |||
224 | genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink), | ||
225 | msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL); | ||
226 | } | ||
227 | |||
228 | static int devlink_nl_port_fill(struct sk_buff *msg, struct devlink *devlink, | ||
229 | struct devlink_port *devlink_port, | ||
230 | enum devlink_command cmd, u32 portid, | ||
231 | u32 seq, int flags) | ||
232 | { | ||
233 | void *hdr; | ||
234 | |||
235 | hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd); | ||
236 | if (!hdr) | ||
237 | return -EMSGSIZE; | ||
238 | |||
239 | if (devlink_nl_put_handle(msg, devlink)) | ||
240 | goto nla_put_failure; | ||
241 | if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, devlink_port->index)) | ||
242 | goto nla_put_failure; | ||
243 | if (nla_put_u16(msg, DEVLINK_ATTR_PORT_TYPE, devlink_port->type)) | ||
244 | goto nla_put_failure; | ||
245 | if (devlink_port->desired_type != DEVLINK_PORT_TYPE_NOTSET && | ||
246 | nla_put_u16(msg, DEVLINK_ATTR_PORT_DESIRED_TYPE, | ||
247 | devlink_port->desired_type)) | ||
248 | goto nla_put_failure; | ||
249 | if (devlink_port->type == DEVLINK_PORT_TYPE_ETH) { | ||
250 | struct net_device *netdev = devlink_port->type_dev; | ||
251 | |||
252 | if (netdev && | ||
253 | (nla_put_u32(msg, DEVLINK_ATTR_PORT_NETDEV_IFINDEX, | ||
254 | netdev->ifindex) || | ||
255 | nla_put_string(msg, DEVLINK_ATTR_PORT_NETDEV_NAME, | ||
256 | netdev->name))) | ||
257 | goto nla_put_failure; | ||
258 | } | ||
259 | if (devlink_port->type == DEVLINK_PORT_TYPE_IB) { | ||
260 | struct ib_device *ibdev = devlink_port->type_dev; | ||
261 | |||
262 | if (ibdev && | ||
263 | nla_put_string(msg, DEVLINK_ATTR_PORT_IBDEV_NAME, | ||
264 | ibdev->name)) | ||
265 | goto nla_put_failure; | ||
266 | } | ||
267 | if (devlink_port->split && | ||
268 | nla_put_u32(msg, DEVLINK_ATTR_PORT_SPLIT_GROUP, | ||
269 | devlink_port->split_group)) | ||
270 | goto nla_put_failure; | ||
271 | |||
272 | genlmsg_end(msg, hdr); | ||
273 | return 0; | ||
274 | |||
275 | nla_put_failure: | ||
276 | genlmsg_cancel(msg, hdr); | ||
277 | return -EMSGSIZE; | ||
278 | } | ||
279 | |||
280 | static void devlink_port_notify(struct devlink_port *devlink_port, | ||
281 | enum devlink_command cmd) | ||
282 | { | ||
283 | struct devlink *devlink = devlink_port->devlink; | ||
284 | struct sk_buff *msg; | ||
285 | int err; | ||
286 | |||
287 | if (!devlink_port->registered) | ||
288 | return; | ||
289 | |||
290 | WARN_ON(cmd != DEVLINK_CMD_PORT_NEW && cmd != DEVLINK_CMD_PORT_DEL); | ||
291 | |||
292 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
293 | if (!msg) | ||
294 | return; | ||
295 | |||
296 | err = devlink_nl_port_fill(msg, devlink, devlink_port, cmd, 0, 0, 0); | ||
297 | if (err) { | ||
298 | nlmsg_free(msg); | ||
299 | return; | ||
300 | } | ||
301 | |||
302 | genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink), | ||
303 | msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL); | ||
304 | } | ||
305 | |||
306 | static int devlink_nl_cmd_get_doit(struct sk_buff *skb, struct genl_info *info) | ||
307 | { | ||
308 | struct devlink *devlink = info->user_ptr[0]; | ||
309 | struct sk_buff *msg; | ||
310 | int err; | ||
311 | |||
312 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
313 | if (!msg) | ||
314 | return -ENOMEM; | ||
315 | |||
316 | err = devlink_nl_fill(msg, devlink, DEVLINK_CMD_NEW, | ||
317 | info->snd_portid, info->snd_seq, 0); | ||
318 | if (err) { | ||
319 | nlmsg_free(msg); | ||
320 | return err; | ||
321 | } | ||
322 | |||
323 | return genlmsg_reply(msg, info); | ||
324 | } | ||
325 | |||
326 | static int devlink_nl_cmd_get_dumpit(struct sk_buff *msg, | ||
327 | struct netlink_callback *cb) | ||
328 | { | ||
329 | struct devlink *devlink; | ||
330 | int start = cb->args[0]; | ||
331 | int idx = 0; | ||
332 | int err; | ||
333 | |||
334 | mutex_lock(&devlink_mutex); | ||
335 | list_for_each_entry(devlink, &devlink_list, list) { | ||
336 | if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) | ||
337 | continue; | ||
338 | if (idx < start) { | ||
339 | idx++; | ||
340 | continue; | ||
341 | } | ||
342 | err = devlink_nl_fill(msg, devlink, DEVLINK_CMD_NEW, | ||
343 | NETLINK_CB(cb->skb).portid, | ||
344 | cb->nlh->nlmsg_seq, NLM_F_MULTI); | ||
345 | if (err) | ||
346 | goto out; | ||
347 | idx++; | ||
348 | } | ||
349 | out: | ||
350 | mutex_unlock(&devlink_mutex); | ||
351 | |||
352 | cb->args[0] = idx; | ||
353 | return msg->len; | ||
354 | } | ||
355 | |||
356 | static int devlink_nl_cmd_port_get_doit(struct sk_buff *skb, | ||
357 | struct genl_info *info) | ||
358 | { | ||
359 | struct devlink *devlink = info->user_ptr[0]; | ||
360 | struct devlink_port *devlink_port = info->user_ptr[1]; | ||
361 | struct sk_buff *msg; | ||
362 | int err; | ||
363 | |||
364 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
365 | if (!msg) | ||
366 | return -ENOMEM; | ||
367 | |||
368 | err = devlink_nl_port_fill(msg, devlink, devlink_port, | ||
369 | DEVLINK_CMD_PORT_NEW, | ||
370 | info->snd_portid, info->snd_seq, 0); | ||
371 | if (err) { | ||
372 | nlmsg_free(msg); | ||
373 | return err; | ||
374 | } | ||
375 | |||
376 | return genlmsg_reply(msg, info); | ||
377 | } | ||
378 | |||
379 | static int devlink_nl_cmd_port_get_dumpit(struct sk_buff *msg, | ||
380 | struct netlink_callback *cb) | ||
381 | { | ||
382 | struct devlink *devlink; | ||
383 | struct devlink_port *devlink_port; | ||
384 | int start = cb->args[0]; | ||
385 | int idx = 0; | ||
386 | int err; | ||
387 | |||
388 | mutex_lock(&devlink_mutex); | ||
389 | mutex_lock(&devlink_port_mutex); | ||
390 | list_for_each_entry(devlink, &devlink_list, list) { | ||
391 | if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) | ||
392 | continue; | ||
393 | list_for_each_entry(devlink_port, &devlink->port_list, list) { | ||
394 | if (idx < start) { | ||
395 | idx++; | ||
396 | continue; | ||
397 | } | ||
398 | err = devlink_nl_port_fill(msg, devlink, devlink_port, | ||
399 | DEVLINK_CMD_NEW, | ||
400 | NETLINK_CB(cb->skb).portid, | ||
401 | cb->nlh->nlmsg_seq, | ||
402 | NLM_F_MULTI); | ||
403 | if (err) | ||
404 | goto out; | ||
405 | idx++; | ||
406 | } | ||
407 | } | ||
408 | out: | ||
409 | mutex_unlock(&devlink_port_mutex); | ||
410 | mutex_unlock(&devlink_mutex); | ||
411 | |||
412 | cb->args[0] = idx; | ||
413 | return msg->len; | ||
414 | } | ||
415 | |||
416 | static int devlink_port_type_set(struct devlink *devlink, | ||
417 | struct devlink_port *devlink_port, | ||
418 | enum devlink_port_type port_type) | ||
419 | |||
420 | { | ||
421 | int err; | ||
422 | |||
423 | if (devlink->ops && devlink->ops->port_type_set) { | ||
424 | if (port_type == DEVLINK_PORT_TYPE_NOTSET) | ||
425 | return -EINVAL; | ||
426 | err = devlink->ops->port_type_set(devlink_port, port_type); | ||
427 | if (err) | ||
428 | return err; | ||
429 | devlink_port->desired_type = port_type; | ||
430 | devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW); | ||
431 | return 0; | ||
432 | } | ||
433 | return -EOPNOTSUPP; | ||
434 | } | ||
435 | |||
436 | static int devlink_nl_cmd_port_set_doit(struct sk_buff *skb, | ||
437 | struct genl_info *info) | ||
438 | { | ||
439 | struct devlink *devlink = info->user_ptr[0]; | ||
440 | struct devlink_port *devlink_port = info->user_ptr[1]; | ||
441 | int err; | ||
442 | |||
443 | if (info->attrs[DEVLINK_ATTR_PORT_TYPE]) { | ||
444 | enum devlink_port_type port_type; | ||
445 | |||
446 | port_type = nla_get_u16(info->attrs[DEVLINK_ATTR_PORT_TYPE]); | ||
447 | err = devlink_port_type_set(devlink, devlink_port, port_type); | ||
448 | if (err) | ||
449 | return err; | ||
450 | } | ||
451 | return 0; | ||
452 | } | ||
453 | |||
454 | static int devlink_port_split(struct devlink *devlink, | ||
455 | u32 port_index, u32 count) | ||
456 | |||
457 | { | ||
458 | if (devlink->ops && devlink->ops->port_split) | ||
459 | return devlink->ops->port_split(devlink, port_index, count); | ||
460 | return -EOPNOTSUPP; | ||
461 | } | ||
462 | |||
463 | static int devlink_nl_cmd_port_split_doit(struct sk_buff *skb, | ||
464 | struct genl_info *info) | ||
465 | { | ||
466 | struct devlink *devlink = info->user_ptr[0]; | ||
467 | u32 port_index; | ||
468 | u32 count; | ||
469 | |||
470 | if (!info->attrs[DEVLINK_ATTR_PORT_INDEX] || | ||
471 | !info->attrs[DEVLINK_ATTR_PORT_SPLIT_COUNT]) | ||
472 | return -EINVAL; | ||
473 | |||
474 | port_index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]); | ||
475 | count = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_SPLIT_COUNT]); | ||
476 | return devlink_port_split(devlink, port_index, count); | ||
477 | } | ||
478 | |||
479 | static int devlink_port_unsplit(struct devlink *devlink, u32 port_index) | ||
480 | |||
481 | { | ||
482 | if (devlink->ops && devlink->ops->port_unsplit) | ||
483 | return devlink->ops->port_unsplit(devlink, port_index); | ||
484 | return -EOPNOTSUPP; | ||
485 | } | ||
486 | |||
487 | static int devlink_nl_cmd_port_unsplit_doit(struct sk_buff *skb, | ||
488 | struct genl_info *info) | ||
489 | { | ||
490 | struct devlink *devlink = info->user_ptr[0]; | ||
491 | u32 port_index; | ||
492 | |||
493 | if (!info->attrs[DEVLINK_ATTR_PORT_INDEX]) | ||
494 | return -EINVAL; | ||
495 | |||
496 | port_index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]); | ||
497 | return devlink_port_unsplit(devlink, port_index); | ||
498 | } | ||
499 | |||
500 | static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = { | ||
501 | [DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING }, | ||
502 | [DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING }, | ||
503 | [DEVLINK_ATTR_PORT_INDEX] = { .type = NLA_U32 }, | ||
504 | [DEVLINK_ATTR_PORT_TYPE] = { .type = NLA_U16 }, | ||
505 | [DEVLINK_ATTR_PORT_SPLIT_COUNT] = { .type = NLA_U32 }, | ||
506 | }; | ||
507 | |||
508 | static const struct genl_ops devlink_nl_ops[] = { | ||
509 | { | ||
510 | .cmd = DEVLINK_CMD_GET, | ||
511 | .doit = devlink_nl_cmd_get_doit, | ||
512 | .dumpit = devlink_nl_cmd_get_dumpit, | ||
513 | .policy = devlink_nl_policy, | ||
514 | /* can be retrieved by unprivileged users */ | ||
515 | }, | ||
516 | { | ||
517 | .cmd = DEVLINK_CMD_PORT_GET, | ||
518 | .doit = devlink_nl_cmd_port_get_doit, | ||
519 | .dumpit = devlink_nl_cmd_port_get_dumpit, | ||
520 | .policy = devlink_nl_policy, | ||
521 | .internal_flags = DEVLINK_NL_FLAG_NEED_PORT, | ||
522 | /* can be retrieved by unprivileged users */ | ||
523 | }, | ||
524 | { | ||
525 | .cmd = DEVLINK_CMD_PORT_SET, | ||
526 | .doit = devlink_nl_cmd_port_set_doit, | ||
527 | .policy = devlink_nl_policy, | ||
528 | .flags = GENL_ADMIN_PERM, | ||
529 | .internal_flags = DEVLINK_NL_FLAG_NEED_PORT, | ||
530 | }, | ||
531 | { | ||
532 | .cmd = DEVLINK_CMD_PORT_SPLIT, | ||
533 | .doit = devlink_nl_cmd_port_split_doit, | ||
534 | .policy = devlink_nl_policy, | ||
535 | .flags = GENL_ADMIN_PERM, | ||
536 | }, | ||
537 | { | ||
538 | .cmd = DEVLINK_CMD_PORT_UNSPLIT, | ||
539 | .doit = devlink_nl_cmd_port_unsplit_doit, | ||
540 | .policy = devlink_nl_policy, | ||
541 | .flags = GENL_ADMIN_PERM, | ||
542 | }, | ||
543 | }; | ||
544 | |||
545 | /** | ||
546 | * devlink_alloc - Allocate new devlink instance resources | ||
547 | * | ||
548 | * @ops: ops | ||
549 | * @priv_size: size of user private data | ||
550 | * | ||
551 | * Allocate new devlink instance resources, including devlink index | ||
552 | * and name. | ||
553 | */ | ||
554 | struct devlink *devlink_alloc(const struct devlink_ops *ops, size_t priv_size) | ||
555 | { | ||
556 | struct devlink *devlink; | ||
557 | |||
558 | devlink = kzalloc(sizeof(*devlink) + priv_size, GFP_KERNEL); | ||
559 | if (!devlink) | ||
560 | return NULL; | ||
561 | devlink->ops = ops; | ||
562 | devlink_net_set(devlink, &init_net); | ||
563 | INIT_LIST_HEAD(&devlink->port_list); | ||
564 | return devlink; | ||
565 | } | ||
566 | EXPORT_SYMBOL_GPL(devlink_alloc); | ||
567 | |||
568 | /** | ||
569 | * devlink_register - Register devlink instance | ||
570 | * | ||
571 | * @devlink: devlink | ||
572 | */ | ||
573 | int devlink_register(struct devlink *devlink, struct device *dev) | ||
574 | { | ||
575 | mutex_lock(&devlink_mutex); | ||
576 | devlink->dev = dev; | ||
577 | list_add_tail(&devlink->list, &devlink_list); | ||
578 | devlink_notify(devlink, DEVLINK_CMD_NEW); | ||
579 | mutex_unlock(&devlink_mutex); | ||
580 | return 0; | ||
581 | } | ||
582 | EXPORT_SYMBOL_GPL(devlink_register); | ||
583 | |||
584 | /** | ||
585 | * devlink_unregister - Unregister devlink instance | ||
586 | * | ||
587 | * @devlink: devlink | ||
588 | */ | ||
589 | void devlink_unregister(struct devlink *devlink) | ||
590 | { | ||
591 | mutex_lock(&devlink_mutex); | ||
592 | devlink_notify(devlink, DEVLINK_CMD_DEL); | ||
593 | list_del(&devlink->list); | ||
594 | mutex_unlock(&devlink_mutex); | ||
595 | } | ||
596 | EXPORT_SYMBOL_GPL(devlink_unregister); | ||
597 | |||
598 | /** | ||
599 | * devlink_free - Free devlink instance resources | ||
600 | * | ||
601 | * @devlink: devlink | ||
602 | */ | ||
603 | void devlink_free(struct devlink *devlink) | ||
604 | { | ||
605 | kfree(devlink); | ||
606 | } | ||
607 | EXPORT_SYMBOL_GPL(devlink_free); | ||
608 | |||
609 | /** | ||
610 | * devlink_port_register - Register devlink port | ||
611 | * | ||
612 | * @devlink: devlink | ||
613 | * @devlink_port: devlink port | ||
614 | * @port_index | ||
615 | * | ||
616 | * Register devlink port with provided port index. User can use | ||
617 | * any indexing, even hw-related one. devlink_port structure | ||
618 | * is convenient to be embedded inside user driver private structure. | ||
619 | * Note that the caller should take care of zeroing the devlink_port | ||
620 | * structure. | ||
621 | */ | ||
622 | int devlink_port_register(struct devlink *devlink, | ||
623 | struct devlink_port *devlink_port, | ||
624 | unsigned int port_index) | ||
625 | { | ||
626 | mutex_lock(&devlink_port_mutex); | ||
627 | if (devlink_port_index_exists(devlink, port_index)) { | ||
628 | mutex_unlock(&devlink_port_mutex); | ||
629 | return -EEXIST; | ||
630 | } | ||
631 | devlink_port->devlink = devlink; | ||
632 | devlink_port->index = port_index; | ||
633 | devlink_port->type = DEVLINK_PORT_TYPE_NOTSET; | ||
634 | devlink_port->registered = true; | ||
635 | list_add_tail(&devlink_port->list, &devlink->port_list); | ||
636 | mutex_unlock(&devlink_port_mutex); | ||
637 | devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW); | ||
638 | return 0; | ||
639 | } | ||
640 | EXPORT_SYMBOL_GPL(devlink_port_register); | ||
641 | |||
642 | /** | ||
643 | * devlink_port_unregister - Unregister devlink port | ||
644 | * | ||
645 | * @devlink_port: devlink port | ||
646 | */ | ||
647 | void devlink_port_unregister(struct devlink_port *devlink_port) | ||
648 | { | ||
649 | devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_DEL); | ||
650 | mutex_lock(&devlink_port_mutex); | ||
651 | list_del(&devlink_port->list); | ||
652 | mutex_unlock(&devlink_port_mutex); | ||
653 | } | ||
654 | EXPORT_SYMBOL_GPL(devlink_port_unregister); | ||
655 | |||
656 | static void __devlink_port_type_set(struct devlink_port *devlink_port, | ||
657 | enum devlink_port_type type, | ||
658 | void *type_dev) | ||
659 | { | ||
660 | devlink_port->type = type; | ||
661 | devlink_port->type_dev = type_dev; | ||
662 | devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW); | ||
663 | } | ||
664 | |||
665 | /** | ||
666 | * devlink_port_type_eth_set - Set port type to Ethernet | ||
667 | * | ||
668 | * @devlink_port: devlink port | ||
669 | * @netdev: related netdevice | ||
670 | */ | ||
671 | void devlink_port_type_eth_set(struct devlink_port *devlink_port, | ||
672 | struct net_device *netdev) | ||
673 | { | ||
674 | return __devlink_port_type_set(devlink_port, | ||
675 | DEVLINK_PORT_TYPE_ETH, netdev); | ||
676 | } | ||
677 | EXPORT_SYMBOL_GPL(devlink_port_type_eth_set); | ||
678 | |||
679 | /** | ||
680 | * devlink_port_type_ib_set - Set port type to InfiniBand | ||
681 | * | ||
682 | * @devlink_port: devlink port | ||
683 | * @ibdev: related IB device | ||
684 | */ | ||
685 | void devlink_port_type_ib_set(struct devlink_port *devlink_port, | ||
686 | struct ib_device *ibdev) | ||
687 | { | ||
688 | return __devlink_port_type_set(devlink_port, | ||
689 | DEVLINK_PORT_TYPE_IB, ibdev); | ||
690 | } | ||
691 | EXPORT_SYMBOL_GPL(devlink_port_type_ib_set); | ||
692 | |||
693 | /** | ||
694 | * devlink_port_type_clear - Clear port type | ||
695 | * | ||
696 | * @devlink_port: devlink port | ||
697 | */ | ||
698 | void devlink_port_type_clear(struct devlink_port *devlink_port) | ||
699 | { | ||
700 | return __devlink_port_type_set(devlink_port, | ||
701 | DEVLINK_PORT_TYPE_NOTSET, NULL); | ||
702 | } | ||
703 | EXPORT_SYMBOL_GPL(devlink_port_type_clear); | ||
704 | |||
705 | /** | ||
706 | * devlink_port_split_set - Set port is split | ||
707 | * | ||
708 | * @devlink_port: devlink port | ||
709 | * @split_group: split group - identifies group split port is part of | ||
710 | */ | ||
711 | void devlink_port_split_set(struct devlink_port *devlink_port, | ||
712 | u32 split_group) | ||
713 | { | ||
714 | devlink_port->split = true; | ||
715 | devlink_port->split_group = split_group; | ||
716 | devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW); | ||
717 | } | ||
718 | EXPORT_SYMBOL_GPL(devlink_port_split_set); | ||
719 | |||
720 | static int __init devlink_module_init(void) | ||
721 | { | ||
722 | return genl_register_family_with_ops_groups(&devlink_nl_family, | ||
723 | devlink_nl_ops, | ||
724 | devlink_nl_mcgrps); | ||
725 | } | ||
726 | |||
727 | static void __exit devlink_module_exit(void) | ||
728 | { | ||
729 | genl_unregister_family(&devlink_nl_family); | ||
730 | } | ||
731 | |||
732 | module_init(devlink_module_init); | ||
733 | module_exit(devlink_module_exit); | ||
734 | |||
735 | MODULE_LICENSE("GPL v2"); | ||
736 | MODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>"); | ||
737 | MODULE_DESCRIPTION("Network physical device Netlink interface"); | ||
738 | MODULE_ALIAS_GENL_FAMILY(DEVLINK_GENL_NAME); | ||