Commit | Line | Data |
---|---|---|
2f90b865 | 1 | /* |
698e1d23 | 2 | * Copyright (c) 2008-2011, Intel Corporation. |
2f90b865 AD |
3 | * |
4 | * This program is free software; you can redistribute it and/or modify it | |
5 | * under the terms and conditions of the GNU General Public License, | |
6 | * version 2, as published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope it will be useful, but WITHOUT | |
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
11 | * more details. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public License along with | |
14 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | |
15 | * Place - Suite 330, Boston, MA 02111-1307 USA. | |
16 | * | |
17 | * Author: Lucy Liu <lucy.liu@intel.com> | |
18 | */ | |
19 | ||
20 | #include <linux/netdevice.h> | |
21 | #include <linux/netlink.h> | |
5a0e3ad6 | 22 | #include <linux/slab.h> |
2f90b865 AD |
23 | #include <net/netlink.h> |
24 | #include <net/rtnetlink.h> | |
25 | #include <linux/dcbnl.h> | |
96b99684 | 26 | #include <net/dcbevent.h> |
2f90b865 | 27 | #include <linux/rtnetlink.h> |
3a9a231d | 28 | #include <linux/module.h> |
2f90b865 AD |
29 | #include <net/sock.h> |
30 | ||
31 | /** | |
32 | * Data Center Bridging (DCB) is a collection of Ethernet enhancements | |
33 | * intended to allow network traffic with differing requirements | |
34 | * (highly reliable, no drops vs. best effort vs. low latency) to operate | |
35 | * and co-exist on Ethernet. Current DCB features are: | |
36 | * | |
37 | * Enhanced Transmission Selection (aka Priority Grouping [PG]) - provides a | |
38 | * framework for assigning bandwidth guarantees to traffic classes. | |
39 | * | |
40 | * Priority-based Flow Control (PFC) - provides a flow control mechanism which | |
41 | * can work independently for each 802.1p priority. | |
42 | * | |
43 | * Congestion Notification - provides a mechanism for end-to-end congestion | |
44 | * control for protocols which do not have built-in congestion management. | |
45 | * | |
46 | * More information about the emerging standards for these Ethernet features | |
47 | * can be found at: http://www.ieee802.org/1/pages/dcbridges.html | |
48 | * | |
49 | * This file implements an rtnetlink interface to allow configuration of DCB | |
50 | * features for capable devices. | |
51 | */ | |
52 | ||
53 | MODULE_AUTHOR("Lucy Liu, <lucy.liu@intel.com>"); | |
7a6b6f51 | 54 | MODULE_DESCRIPTION("Data Center Bridging netlink interface"); |
2f90b865 AD |
55 | MODULE_LICENSE("GPL"); |
56 | ||
57 | /**************** DCB attribute policies *************************************/ | |
58 | ||
59 | /* DCB netlink attributes policy */ | |
b54452b0 | 60 | static const struct nla_policy dcbnl_rtnl_policy[DCB_ATTR_MAX + 1] = { |
859ee3c4 AD |
61 | [DCB_ATTR_IFNAME] = {.type = NLA_NUL_STRING, .len = IFNAMSIZ - 1}, |
62 | [DCB_ATTR_STATE] = {.type = NLA_U8}, | |
63 | [DCB_ATTR_PFC_CFG] = {.type = NLA_NESTED}, | |
64 | [DCB_ATTR_PG_CFG] = {.type = NLA_NESTED}, | |
65 | [DCB_ATTR_SET_ALL] = {.type = NLA_U8}, | |
2f90b865 | 66 | [DCB_ATTR_PERM_HWADDR] = {.type = NLA_FLAG}, |
859ee3c4 AD |
67 | [DCB_ATTR_CAP] = {.type = NLA_NESTED}, |
68 | [DCB_ATTR_PFC_STATE] = {.type = NLA_U8}, | |
69 | [DCB_ATTR_BCN] = {.type = NLA_NESTED}, | |
6fa382af | 70 | [DCB_ATTR_APP] = {.type = NLA_NESTED}, |
3e29027a | 71 | [DCB_ATTR_IEEE] = {.type = NLA_NESTED}, |
6241b625 | 72 | [DCB_ATTR_DCBX] = {.type = NLA_U8}, |
ea45fe4e | 73 | [DCB_ATTR_FEATCFG] = {.type = NLA_NESTED}, |
2f90b865 AD |
74 | }; |
75 | ||
76 | /* DCB priority flow control to User Priority nested attributes */ | |
b54452b0 | 77 | static const struct nla_policy dcbnl_pfc_up_nest[DCB_PFC_UP_ATTR_MAX + 1] = { |
2f90b865 AD |
78 | [DCB_PFC_UP_ATTR_0] = {.type = NLA_U8}, |
79 | [DCB_PFC_UP_ATTR_1] = {.type = NLA_U8}, | |
80 | [DCB_PFC_UP_ATTR_2] = {.type = NLA_U8}, | |
81 | [DCB_PFC_UP_ATTR_3] = {.type = NLA_U8}, | |
82 | [DCB_PFC_UP_ATTR_4] = {.type = NLA_U8}, | |
83 | [DCB_PFC_UP_ATTR_5] = {.type = NLA_U8}, | |
84 | [DCB_PFC_UP_ATTR_6] = {.type = NLA_U8}, | |
85 | [DCB_PFC_UP_ATTR_7] = {.type = NLA_U8}, | |
86 | [DCB_PFC_UP_ATTR_ALL] = {.type = NLA_FLAG}, | |
87 | }; | |
88 | ||
89 | /* DCB priority grouping nested attributes */ | |
b54452b0 | 90 | static const struct nla_policy dcbnl_pg_nest[DCB_PG_ATTR_MAX + 1] = { |
2f90b865 AD |
91 | [DCB_PG_ATTR_TC_0] = {.type = NLA_NESTED}, |
92 | [DCB_PG_ATTR_TC_1] = {.type = NLA_NESTED}, | |
93 | [DCB_PG_ATTR_TC_2] = {.type = NLA_NESTED}, | |
94 | [DCB_PG_ATTR_TC_3] = {.type = NLA_NESTED}, | |
95 | [DCB_PG_ATTR_TC_4] = {.type = NLA_NESTED}, | |
96 | [DCB_PG_ATTR_TC_5] = {.type = NLA_NESTED}, | |
97 | [DCB_PG_ATTR_TC_6] = {.type = NLA_NESTED}, | |
98 | [DCB_PG_ATTR_TC_7] = {.type = NLA_NESTED}, | |
99 | [DCB_PG_ATTR_TC_ALL] = {.type = NLA_NESTED}, | |
100 | [DCB_PG_ATTR_BW_ID_0] = {.type = NLA_U8}, | |
101 | [DCB_PG_ATTR_BW_ID_1] = {.type = NLA_U8}, | |
102 | [DCB_PG_ATTR_BW_ID_2] = {.type = NLA_U8}, | |
103 | [DCB_PG_ATTR_BW_ID_3] = {.type = NLA_U8}, | |
104 | [DCB_PG_ATTR_BW_ID_4] = {.type = NLA_U8}, | |
105 | [DCB_PG_ATTR_BW_ID_5] = {.type = NLA_U8}, | |
106 | [DCB_PG_ATTR_BW_ID_6] = {.type = NLA_U8}, | |
107 | [DCB_PG_ATTR_BW_ID_7] = {.type = NLA_U8}, | |
108 | [DCB_PG_ATTR_BW_ID_ALL] = {.type = NLA_FLAG}, | |
109 | }; | |
110 | ||
111 | /* DCB traffic class nested attributes. */ | |
b54452b0 | 112 | static const struct nla_policy dcbnl_tc_param_nest[DCB_TC_ATTR_PARAM_MAX + 1] = { |
2f90b865 AD |
113 | [DCB_TC_ATTR_PARAM_PGID] = {.type = NLA_U8}, |
114 | [DCB_TC_ATTR_PARAM_UP_MAPPING] = {.type = NLA_U8}, | |
115 | [DCB_TC_ATTR_PARAM_STRICT_PRIO] = {.type = NLA_U8}, | |
116 | [DCB_TC_ATTR_PARAM_BW_PCT] = {.type = NLA_U8}, | |
117 | [DCB_TC_ATTR_PARAM_ALL] = {.type = NLA_FLAG}, | |
118 | }; | |
119 | ||
46132188 | 120 | /* DCB capabilities nested attributes. */ |
b54452b0 | 121 | static const struct nla_policy dcbnl_cap_nest[DCB_CAP_ATTR_MAX + 1] = { |
46132188 AD |
122 | [DCB_CAP_ATTR_ALL] = {.type = NLA_FLAG}, |
123 | [DCB_CAP_ATTR_PG] = {.type = NLA_U8}, | |
124 | [DCB_CAP_ATTR_PFC] = {.type = NLA_U8}, | |
125 | [DCB_CAP_ATTR_UP2TC] = {.type = NLA_U8}, | |
126 | [DCB_CAP_ATTR_PG_TCS] = {.type = NLA_U8}, | |
127 | [DCB_CAP_ATTR_PFC_TCS] = {.type = NLA_U8}, | |
128 | [DCB_CAP_ATTR_GSP] = {.type = NLA_U8}, | |
129 | [DCB_CAP_ATTR_BCN] = {.type = NLA_U8}, | |
6241b625 | 130 | [DCB_CAP_ATTR_DCBX] = {.type = NLA_U8}, |
46132188 | 131 | }; |
2f90b865 | 132 | |
33dbabc4 | 133 | /* DCB capabilities nested attributes. */ |
b54452b0 | 134 | static const struct nla_policy dcbnl_numtcs_nest[DCB_NUMTCS_ATTR_MAX + 1] = { |
33dbabc4 AD |
135 | [DCB_NUMTCS_ATTR_ALL] = {.type = NLA_FLAG}, |
136 | [DCB_NUMTCS_ATTR_PG] = {.type = NLA_U8}, | |
137 | [DCB_NUMTCS_ATTR_PFC] = {.type = NLA_U8}, | |
138 | }; | |
139 | ||
859ee3c4 | 140 | /* DCB BCN nested attributes. */ |
b54452b0 | 141 | static const struct nla_policy dcbnl_bcn_nest[DCB_BCN_ATTR_MAX + 1] = { |
859ee3c4 AD |
142 | [DCB_BCN_ATTR_RP_0] = {.type = NLA_U8}, |
143 | [DCB_BCN_ATTR_RP_1] = {.type = NLA_U8}, | |
144 | [DCB_BCN_ATTR_RP_2] = {.type = NLA_U8}, | |
145 | [DCB_BCN_ATTR_RP_3] = {.type = NLA_U8}, | |
146 | [DCB_BCN_ATTR_RP_4] = {.type = NLA_U8}, | |
147 | [DCB_BCN_ATTR_RP_5] = {.type = NLA_U8}, | |
148 | [DCB_BCN_ATTR_RP_6] = {.type = NLA_U8}, | |
149 | [DCB_BCN_ATTR_RP_7] = {.type = NLA_U8}, | |
150 | [DCB_BCN_ATTR_RP_ALL] = {.type = NLA_FLAG}, | |
f4314e81 DS |
151 | [DCB_BCN_ATTR_BCNA_0] = {.type = NLA_U32}, |
152 | [DCB_BCN_ATTR_BCNA_1] = {.type = NLA_U32}, | |
859ee3c4 AD |
153 | [DCB_BCN_ATTR_ALPHA] = {.type = NLA_U32}, |
154 | [DCB_BCN_ATTR_BETA] = {.type = NLA_U32}, | |
155 | [DCB_BCN_ATTR_GD] = {.type = NLA_U32}, | |
156 | [DCB_BCN_ATTR_GI] = {.type = NLA_U32}, | |
157 | [DCB_BCN_ATTR_TMAX] = {.type = NLA_U32}, | |
158 | [DCB_BCN_ATTR_TD] = {.type = NLA_U32}, | |
159 | [DCB_BCN_ATTR_RMIN] = {.type = NLA_U32}, | |
160 | [DCB_BCN_ATTR_W] = {.type = NLA_U32}, | |
161 | [DCB_BCN_ATTR_RD] = {.type = NLA_U32}, | |
162 | [DCB_BCN_ATTR_RU] = {.type = NLA_U32}, | |
163 | [DCB_BCN_ATTR_WRTT] = {.type = NLA_U32}, | |
164 | [DCB_BCN_ATTR_RI] = {.type = NLA_U32}, | |
165 | [DCB_BCN_ATTR_C] = {.type = NLA_U32}, | |
166 | [DCB_BCN_ATTR_ALL] = {.type = NLA_FLAG}, | |
167 | }; | |
168 | ||
6fa382af | 169 | /* DCB APP nested attributes. */ |
b54452b0 | 170 | static const struct nla_policy dcbnl_app_nest[DCB_APP_ATTR_MAX + 1] = { |
6fa382af YZ |
171 | [DCB_APP_ATTR_IDTYPE] = {.type = NLA_U8}, |
172 | [DCB_APP_ATTR_ID] = {.type = NLA_U16}, | |
173 | [DCB_APP_ATTR_PRIORITY] = {.type = NLA_U8}, | |
174 | }; | |
175 | ||
3e29027a JF |
176 | /* IEEE 802.1Qaz nested attributes. */ |
177 | static const struct nla_policy dcbnl_ieee_policy[DCB_ATTR_IEEE_MAX + 1] = { | |
178 | [DCB_ATTR_IEEE_ETS] = {.len = sizeof(struct ieee_ets)}, | |
179 | [DCB_ATTR_IEEE_PFC] = {.len = sizeof(struct ieee_pfc)}, | |
180 | [DCB_ATTR_IEEE_APP_TABLE] = {.type = NLA_NESTED}, | |
08f10aff | 181 | [DCB_ATTR_IEEE_MAXRATE] = {.len = sizeof(struct ieee_maxrate)}, |
3e29027a JF |
182 | }; |
183 | ||
184 | static const struct nla_policy dcbnl_ieee_app[DCB_ATTR_IEEE_APP_MAX + 1] = { | |
185 | [DCB_ATTR_IEEE_APP] = {.len = sizeof(struct dcb_app)}, | |
186 | }; | |
187 | ||
ea45fe4e SR |
188 | /* DCB number of traffic classes nested attributes. */ |
189 | static const struct nla_policy dcbnl_featcfg_nest[DCB_FEATCFG_ATTR_MAX + 1] = { | |
190 | [DCB_FEATCFG_ATTR_ALL] = {.type = NLA_FLAG}, | |
191 | [DCB_FEATCFG_ATTR_PG] = {.type = NLA_U8}, | |
192 | [DCB_FEATCFG_ATTR_PFC] = {.type = NLA_U8}, | |
193 | [DCB_FEATCFG_ATTR_APP] = {.type = NLA_U8}, | |
194 | }; | |
195 | ||
9ab933ab JF |
196 | static LIST_HEAD(dcb_app_list); |
197 | static DEFINE_SPINLOCK(dcb_lock); | |
198 | ||
2f90b865 AD |
199 | /* standard netlink reply call */ |
200 | static int dcbnl_reply(u8 value, u8 event, u8 cmd, u8 attr, u32 pid, | |
201 | u32 seq, u16 flags) | |
202 | { | |
203 | struct sk_buff *dcbnl_skb; | |
204 | struct dcbmsg *dcb; | |
205 | struct nlmsghdr *nlh; | |
206 | int ret = -EINVAL; | |
207 | ||
208 | dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
209 | if (!dcbnl_skb) | |
210 | return ret; | |
211 | ||
212 | nlh = NLMSG_NEW(dcbnl_skb, pid, seq, event, sizeof(*dcb), flags); | |
213 | ||
214 | dcb = NLMSG_DATA(nlh); | |
215 | dcb->dcb_family = AF_UNSPEC; | |
216 | dcb->cmd = cmd; | |
217 | dcb->dcb_pad = 0; | |
218 | ||
219 | ret = nla_put_u8(dcbnl_skb, attr, value); | |
220 | if (ret) | |
221 | goto err; | |
222 | ||
223 | /* end the message, assign the nlmsg_len. */ | |
224 | nlmsg_end(dcbnl_skb, nlh); | |
225 | ret = rtnl_unicast(dcbnl_skb, &init_net, pid); | |
226 | if (ret) | |
7eaf5077 | 227 | return -EINVAL; |
2f90b865 AD |
228 | |
229 | return 0; | |
230 | nlmsg_failure: | |
231 | err: | |
858eb711 | 232 | kfree_skb(dcbnl_skb); |
2f90b865 AD |
233 | return ret; |
234 | } | |
235 | ||
236 | static int dcbnl_getstate(struct net_device *netdev, struct nlattr **tb, | |
237 | u32 pid, u32 seq, u16 flags) | |
238 | { | |
239 | int ret = -EINVAL; | |
240 | ||
241 | /* if (!tb[DCB_ATTR_STATE] || !netdev->dcbnl_ops->getstate) */ | |
242 | if (!netdev->dcbnl_ops->getstate) | |
243 | return ret; | |
244 | ||
245 | ret = dcbnl_reply(netdev->dcbnl_ops->getstate(netdev), RTM_GETDCB, | |
246 | DCB_CMD_GSTATE, DCB_ATTR_STATE, pid, seq, flags); | |
247 | ||
248 | return ret; | |
249 | } | |
250 | ||
251 | static int dcbnl_getpfccfg(struct net_device *netdev, struct nlattr **tb, | |
252 | u32 pid, u32 seq, u16 flags) | |
253 | { | |
254 | struct sk_buff *dcbnl_skb; | |
255 | struct nlmsghdr *nlh; | |
256 | struct dcbmsg *dcb; | |
257 | struct nlattr *data[DCB_PFC_UP_ATTR_MAX + 1], *nest; | |
258 | u8 value; | |
259 | int ret = -EINVAL; | |
260 | int i; | |
261 | int getall = 0; | |
262 | ||
263 | if (!tb[DCB_ATTR_PFC_CFG] || !netdev->dcbnl_ops->getpfccfg) | |
264 | return ret; | |
265 | ||
266 | ret = nla_parse_nested(data, DCB_PFC_UP_ATTR_MAX, | |
267 | tb[DCB_ATTR_PFC_CFG], | |
268 | dcbnl_pfc_up_nest); | |
269 | if (ret) | |
270 | goto err_out; | |
271 | ||
272 | dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
273 | if (!dcbnl_skb) | |
274 | goto err_out; | |
275 | ||
276 | nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); | |
277 | ||
278 | dcb = NLMSG_DATA(nlh); | |
279 | dcb->dcb_family = AF_UNSPEC; | |
280 | dcb->cmd = DCB_CMD_PFC_GCFG; | |
281 | ||
282 | nest = nla_nest_start(dcbnl_skb, DCB_ATTR_PFC_CFG); | |
283 | if (!nest) | |
284 | goto err; | |
285 | ||
286 | if (data[DCB_PFC_UP_ATTR_ALL]) | |
287 | getall = 1; | |
288 | ||
289 | for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) { | |
290 | if (!getall && !data[i]) | |
291 | continue; | |
292 | ||
293 | netdev->dcbnl_ops->getpfccfg(netdev, i - DCB_PFC_UP_ATTR_0, | |
294 | &value); | |
295 | ret = nla_put_u8(dcbnl_skb, i, value); | |
296 | ||
297 | if (ret) { | |
298 | nla_nest_cancel(dcbnl_skb, nest); | |
299 | goto err; | |
300 | } | |
301 | } | |
302 | nla_nest_end(dcbnl_skb, nest); | |
303 | ||
304 | nlmsg_end(dcbnl_skb, nlh); | |
305 | ||
306 | ret = rtnl_unicast(dcbnl_skb, &init_net, pid); | |
307 | if (ret) | |
7eaf5077 | 308 | goto err_out; |
2f90b865 AD |
309 | |
310 | return 0; | |
311 | nlmsg_failure: | |
312 | err: | |
858eb711 | 313 | kfree_skb(dcbnl_skb); |
2f90b865 AD |
314 | err_out: |
315 | return -EINVAL; | |
316 | } | |
317 | ||
318 | static int dcbnl_getperm_hwaddr(struct net_device *netdev, struct nlattr **tb, | |
319 | u32 pid, u32 seq, u16 flags) | |
320 | { | |
321 | struct sk_buff *dcbnl_skb; | |
322 | struct nlmsghdr *nlh; | |
323 | struct dcbmsg *dcb; | |
324 | u8 perm_addr[MAX_ADDR_LEN]; | |
325 | int ret = -EINVAL; | |
326 | ||
327 | if (!netdev->dcbnl_ops->getpermhwaddr) | |
328 | return ret; | |
329 | ||
330 | dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
331 | if (!dcbnl_skb) | |
332 | goto err_out; | |
333 | ||
334 | nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); | |
335 | ||
336 | dcb = NLMSG_DATA(nlh); | |
337 | dcb->dcb_family = AF_UNSPEC; | |
338 | dcb->cmd = DCB_CMD_GPERM_HWADDR; | |
339 | ||
340 | netdev->dcbnl_ops->getpermhwaddr(netdev, perm_addr); | |
341 | ||
342 | ret = nla_put(dcbnl_skb, DCB_ATTR_PERM_HWADDR, sizeof(perm_addr), | |
343 | perm_addr); | |
344 | ||
345 | nlmsg_end(dcbnl_skb, nlh); | |
346 | ||
347 | ret = rtnl_unicast(dcbnl_skb, &init_net, pid); | |
348 | if (ret) | |
7eaf5077 | 349 | goto err_out; |
2f90b865 AD |
350 | |
351 | return 0; | |
352 | ||
353 | nlmsg_failure: | |
858eb711 | 354 | kfree_skb(dcbnl_skb); |
2f90b865 AD |
355 | err_out: |
356 | return -EINVAL; | |
357 | } | |
358 | ||
46132188 AD |
359 | static int dcbnl_getcap(struct net_device *netdev, struct nlattr **tb, |
360 | u32 pid, u32 seq, u16 flags) | |
361 | { | |
362 | struct sk_buff *dcbnl_skb; | |
363 | struct nlmsghdr *nlh; | |
364 | struct dcbmsg *dcb; | |
365 | struct nlattr *data[DCB_CAP_ATTR_MAX + 1], *nest; | |
366 | u8 value; | |
367 | int ret = -EINVAL; | |
368 | int i; | |
369 | int getall = 0; | |
370 | ||
371 | if (!tb[DCB_ATTR_CAP] || !netdev->dcbnl_ops->getcap) | |
372 | return ret; | |
373 | ||
374 | ret = nla_parse_nested(data, DCB_CAP_ATTR_MAX, tb[DCB_ATTR_CAP], | |
375 | dcbnl_cap_nest); | |
376 | if (ret) | |
377 | goto err_out; | |
378 | ||
379 | dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
380 | if (!dcbnl_skb) | |
381 | goto err_out; | |
382 | ||
383 | nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); | |
384 | ||
385 | dcb = NLMSG_DATA(nlh); | |
386 | dcb->dcb_family = AF_UNSPEC; | |
387 | dcb->cmd = DCB_CMD_GCAP; | |
388 | ||
389 | nest = nla_nest_start(dcbnl_skb, DCB_ATTR_CAP); | |
390 | if (!nest) | |
391 | goto err; | |
392 | ||
393 | if (data[DCB_CAP_ATTR_ALL]) | |
394 | getall = 1; | |
395 | ||
396 | for (i = DCB_CAP_ATTR_ALL+1; i <= DCB_CAP_ATTR_MAX; i++) { | |
397 | if (!getall && !data[i]) | |
398 | continue; | |
399 | ||
400 | if (!netdev->dcbnl_ops->getcap(netdev, i, &value)) { | |
401 | ret = nla_put_u8(dcbnl_skb, i, value); | |
402 | ||
403 | if (ret) { | |
404 | nla_nest_cancel(dcbnl_skb, nest); | |
405 | goto err; | |
406 | } | |
407 | } | |
408 | } | |
409 | nla_nest_end(dcbnl_skb, nest); | |
410 | ||
411 | nlmsg_end(dcbnl_skb, nlh); | |
412 | ||
413 | ret = rtnl_unicast(dcbnl_skb, &init_net, pid); | |
414 | if (ret) | |
7eaf5077 | 415 | goto err_out; |
46132188 AD |
416 | |
417 | return 0; | |
418 | nlmsg_failure: | |
419 | err: | |
858eb711 | 420 | kfree_skb(dcbnl_skb); |
46132188 AD |
421 | err_out: |
422 | return -EINVAL; | |
423 | } | |
424 | ||
33dbabc4 AD |
425 | static int dcbnl_getnumtcs(struct net_device *netdev, struct nlattr **tb, |
426 | u32 pid, u32 seq, u16 flags) | |
427 | { | |
428 | struct sk_buff *dcbnl_skb; | |
429 | struct nlmsghdr *nlh; | |
430 | struct dcbmsg *dcb; | |
431 | struct nlattr *data[DCB_NUMTCS_ATTR_MAX + 1], *nest; | |
432 | u8 value; | |
433 | int ret = -EINVAL; | |
434 | int i; | |
435 | int getall = 0; | |
436 | ||
437 | if (!tb[DCB_ATTR_NUMTCS] || !netdev->dcbnl_ops->getnumtcs) | |
438 | return ret; | |
439 | ||
440 | ret = nla_parse_nested(data, DCB_NUMTCS_ATTR_MAX, tb[DCB_ATTR_NUMTCS], | |
441 | dcbnl_numtcs_nest); | |
442 | if (ret) { | |
443 | ret = -EINVAL; | |
444 | goto err_out; | |
445 | } | |
446 | ||
447 | dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
448 | if (!dcbnl_skb) { | |
449 | ret = -EINVAL; | |
450 | goto err_out; | |
451 | } | |
452 | ||
453 | nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); | |
454 | ||
455 | dcb = NLMSG_DATA(nlh); | |
456 | dcb->dcb_family = AF_UNSPEC; | |
457 | dcb->cmd = DCB_CMD_GNUMTCS; | |
458 | ||
459 | nest = nla_nest_start(dcbnl_skb, DCB_ATTR_NUMTCS); | |
460 | if (!nest) { | |
461 | ret = -EINVAL; | |
462 | goto err; | |
463 | } | |
464 | ||
465 | if (data[DCB_NUMTCS_ATTR_ALL]) | |
466 | getall = 1; | |
467 | ||
468 | for (i = DCB_NUMTCS_ATTR_ALL+1; i <= DCB_NUMTCS_ATTR_MAX; i++) { | |
469 | if (!getall && !data[i]) | |
470 | continue; | |
471 | ||
472 | ret = netdev->dcbnl_ops->getnumtcs(netdev, i, &value); | |
473 | if (!ret) { | |
474 | ret = nla_put_u8(dcbnl_skb, i, value); | |
475 | ||
476 | if (ret) { | |
477 | nla_nest_cancel(dcbnl_skb, nest); | |
478 | ret = -EINVAL; | |
479 | goto err; | |
480 | } | |
481 | } else { | |
482 | goto err; | |
483 | } | |
484 | } | |
485 | nla_nest_end(dcbnl_skb, nest); | |
486 | ||
487 | nlmsg_end(dcbnl_skb, nlh); | |
488 | ||
489 | ret = rtnl_unicast(dcbnl_skb, &init_net, pid); | |
490 | if (ret) { | |
491 | ret = -EINVAL; | |
7eaf5077 | 492 | goto err_out; |
33dbabc4 AD |
493 | } |
494 | ||
495 | return 0; | |
496 | nlmsg_failure: | |
497 | err: | |
858eb711 | 498 | kfree_skb(dcbnl_skb); |
33dbabc4 AD |
499 | err_out: |
500 | return ret; | |
501 | } | |
502 | ||
503 | static int dcbnl_setnumtcs(struct net_device *netdev, struct nlattr **tb, | |
504 | u32 pid, u32 seq, u16 flags) | |
505 | { | |
506 | struct nlattr *data[DCB_NUMTCS_ATTR_MAX + 1]; | |
507 | int ret = -EINVAL; | |
508 | u8 value; | |
509 | int i; | |
510 | ||
8b124a8e | 511 | if (!tb[DCB_ATTR_NUMTCS] || !netdev->dcbnl_ops->setnumtcs) |
33dbabc4 AD |
512 | return ret; |
513 | ||
514 | ret = nla_parse_nested(data, DCB_NUMTCS_ATTR_MAX, tb[DCB_ATTR_NUMTCS], | |
515 | dcbnl_numtcs_nest); | |
516 | ||
517 | if (ret) { | |
518 | ret = -EINVAL; | |
519 | goto err; | |
520 | } | |
521 | ||
522 | for (i = DCB_NUMTCS_ATTR_ALL+1; i <= DCB_NUMTCS_ATTR_MAX; i++) { | |
523 | if (data[i] == NULL) | |
524 | continue; | |
525 | ||
526 | value = nla_get_u8(data[i]); | |
527 | ||
528 | ret = netdev->dcbnl_ops->setnumtcs(netdev, i, value); | |
529 | ||
530 | if (ret) | |
531 | goto operr; | |
532 | } | |
533 | ||
534 | operr: | |
535 | ret = dcbnl_reply(!!ret, RTM_SETDCB, DCB_CMD_SNUMTCS, | |
536 | DCB_ATTR_NUMTCS, pid, seq, flags); | |
537 | ||
538 | err: | |
539 | return ret; | |
540 | } | |
541 | ||
0eb3aa9b AD |
542 | static int dcbnl_getpfcstate(struct net_device *netdev, struct nlattr **tb, |
543 | u32 pid, u32 seq, u16 flags) | |
544 | { | |
545 | int ret = -EINVAL; | |
546 | ||
547 | if (!netdev->dcbnl_ops->getpfcstate) | |
548 | return ret; | |
549 | ||
550 | ret = dcbnl_reply(netdev->dcbnl_ops->getpfcstate(netdev), RTM_GETDCB, | |
551 | DCB_CMD_PFC_GSTATE, DCB_ATTR_PFC_STATE, | |
552 | pid, seq, flags); | |
553 | ||
554 | return ret; | |
555 | } | |
556 | ||
557 | static int dcbnl_setpfcstate(struct net_device *netdev, struct nlattr **tb, | |
558 | u32 pid, u32 seq, u16 flags) | |
559 | { | |
560 | int ret = -EINVAL; | |
561 | u8 value; | |
562 | ||
563 | if (!tb[DCB_ATTR_PFC_STATE] || !netdev->dcbnl_ops->setpfcstate) | |
564 | return ret; | |
565 | ||
566 | value = nla_get_u8(tb[DCB_ATTR_PFC_STATE]); | |
567 | ||
568 | netdev->dcbnl_ops->setpfcstate(netdev, value); | |
569 | ||
570 | ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_PFC_SSTATE, DCB_ATTR_PFC_STATE, | |
571 | pid, seq, flags); | |
572 | ||
573 | return ret; | |
574 | } | |
575 | ||
57949686 YZ |
576 | static int dcbnl_getapp(struct net_device *netdev, struct nlattr **tb, |
577 | u32 pid, u32 seq, u16 flags) | |
578 | { | |
579 | struct sk_buff *dcbnl_skb; | |
580 | struct nlmsghdr *nlh; | |
581 | struct dcbmsg *dcb; | |
582 | struct nlattr *app_nest; | |
583 | struct nlattr *app_tb[DCB_APP_ATTR_MAX + 1]; | |
584 | u16 id; | |
585 | u8 up, idtype; | |
586 | int ret = -EINVAL; | |
587 | ||
3dce38a0 | 588 | if (!tb[DCB_ATTR_APP]) |
57949686 YZ |
589 | goto out; |
590 | ||
591 | ret = nla_parse_nested(app_tb, DCB_APP_ATTR_MAX, tb[DCB_ATTR_APP], | |
592 | dcbnl_app_nest); | |
593 | if (ret) | |
594 | goto out; | |
595 | ||
596 | ret = -EINVAL; | |
597 | /* all must be non-null */ | |
598 | if ((!app_tb[DCB_APP_ATTR_IDTYPE]) || | |
599 | (!app_tb[DCB_APP_ATTR_ID])) | |
600 | goto out; | |
601 | ||
602 | /* either by eth type or by socket number */ | |
603 | idtype = nla_get_u8(app_tb[DCB_APP_ATTR_IDTYPE]); | |
604 | if ((idtype != DCB_APP_IDTYPE_ETHTYPE) && | |
605 | (idtype != DCB_APP_IDTYPE_PORTNUM)) | |
606 | goto out; | |
607 | ||
608 | id = nla_get_u16(app_tb[DCB_APP_ATTR_ID]); | |
3dce38a0 JF |
609 | |
610 | if (netdev->dcbnl_ops->getapp) { | |
611 | up = netdev->dcbnl_ops->getapp(netdev, idtype, id); | |
612 | } else { | |
613 | struct dcb_app app = { | |
614 | .selector = idtype, | |
615 | .protocol = id, | |
616 | }; | |
617 | up = dcb_getapp(netdev, &app); | |
618 | } | |
57949686 YZ |
619 | |
620 | /* send this back */ | |
621 | dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
622 | if (!dcbnl_skb) | |
623 | goto out; | |
624 | ||
625 | nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); | |
626 | dcb = NLMSG_DATA(nlh); | |
627 | dcb->dcb_family = AF_UNSPEC; | |
628 | dcb->cmd = DCB_CMD_GAPP; | |
629 | ||
630 | app_nest = nla_nest_start(dcbnl_skb, DCB_ATTR_APP); | |
d3337de5 JJ |
631 | if (!app_nest) |
632 | goto out_cancel; | |
633 | ||
57949686 YZ |
634 | ret = nla_put_u8(dcbnl_skb, DCB_APP_ATTR_IDTYPE, idtype); |
635 | if (ret) | |
636 | goto out_cancel; | |
637 | ||
638 | ret = nla_put_u16(dcbnl_skb, DCB_APP_ATTR_ID, id); | |
639 | if (ret) | |
640 | goto out_cancel; | |
641 | ||
642 | ret = nla_put_u8(dcbnl_skb, DCB_APP_ATTR_PRIORITY, up); | |
643 | if (ret) | |
644 | goto out_cancel; | |
645 | ||
646 | nla_nest_end(dcbnl_skb, app_nest); | |
647 | nlmsg_end(dcbnl_skb, nlh); | |
648 | ||
649 | ret = rtnl_unicast(dcbnl_skb, &init_net, pid); | |
650 | if (ret) | |
651 | goto nlmsg_failure; | |
652 | ||
653 | goto out; | |
654 | ||
655 | out_cancel: | |
656 | nla_nest_cancel(dcbnl_skb, app_nest); | |
657 | nlmsg_failure: | |
658 | kfree_skb(dcbnl_skb); | |
659 | out: | |
660 | return ret; | |
661 | } | |
662 | ||
663 | static int dcbnl_setapp(struct net_device *netdev, struct nlattr **tb, | |
664 | u32 pid, u32 seq, u16 flags) | |
665 | { | |
9ab933ab | 666 | int err, ret = -EINVAL; |
57949686 YZ |
667 | u16 id; |
668 | u8 up, idtype; | |
669 | struct nlattr *app_tb[DCB_APP_ATTR_MAX + 1]; | |
670 | ||
9ab933ab | 671 | if (!tb[DCB_ATTR_APP]) |
57949686 YZ |
672 | goto out; |
673 | ||
674 | ret = nla_parse_nested(app_tb, DCB_APP_ATTR_MAX, tb[DCB_ATTR_APP], | |
675 | dcbnl_app_nest); | |
676 | if (ret) | |
677 | goto out; | |
678 | ||
679 | ret = -EINVAL; | |
680 | /* all must be non-null */ | |
681 | if ((!app_tb[DCB_APP_ATTR_IDTYPE]) || | |
682 | (!app_tb[DCB_APP_ATTR_ID]) || | |
683 | (!app_tb[DCB_APP_ATTR_PRIORITY])) | |
684 | goto out; | |
685 | ||
686 | /* either by eth type or by socket number */ | |
687 | idtype = nla_get_u8(app_tb[DCB_APP_ATTR_IDTYPE]); | |
688 | if ((idtype != DCB_APP_IDTYPE_ETHTYPE) && | |
689 | (idtype != DCB_APP_IDTYPE_PORTNUM)) | |
690 | goto out; | |
691 | ||
692 | id = nla_get_u16(app_tb[DCB_APP_ATTR_ID]); | |
693 | up = nla_get_u8(app_tb[DCB_APP_ATTR_PRIORITY]); | |
694 | ||
9ab933ab JF |
695 | if (netdev->dcbnl_ops->setapp) { |
696 | err = netdev->dcbnl_ops->setapp(netdev, idtype, id, up); | |
697 | } else { | |
698 | struct dcb_app app; | |
699 | app.selector = idtype; | |
700 | app.protocol = id; | |
701 | app.priority = up; | |
702 | err = dcb_setapp(netdev, &app); | |
703 | } | |
704 | ||
705 | ret = dcbnl_reply(err, RTM_SETDCB, DCB_CMD_SAPP, DCB_ATTR_APP, | |
706 | pid, seq, flags); | |
57949686 YZ |
707 | out: |
708 | return ret; | |
709 | } | |
710 | ||
2f90b865 AD |
711 | static int __dcbnl_pg_getcfg(struct net_device *netdev, struct nlattr **tb, |
712 | u32 pid, u32 seq, u16 flags, int dir) | |
713 | { | |
714 | struct sk_buff *dcbnl_skb; | |
715 | struct nlmsghdr *nlh; | |
716 | struct dcbmsg *dcb; | |
717 | struct nlattr *pg_nest, *param_nest, *data; | |
718 | struct nlattr *pg_tb[DCB_PG_ATTR_MAX + 1]; | |
719 | struct nlattr *param_tb[DCB_TC_ATTR_PARAM_MAX + 1]; | |
720 | u8 prio, pgid, tc_pct, up_map; | |
721 | int ret = -EINVAL; | |
722 | int getall = 0; | |
723 | int i; | |
724 | ||
725 | if (!tb[DCB_ATTR_PG_CFG] || | |
726 | !netdev->dcbnl_ops->getpgtccfgtx || | |
727 | !netdev->dcbnl_ops->getpgtccfgrx || | |
728 | !netdev->dcbnl_ops->getpgbwgcfgtx || | |
729 | !netdev->dcbnl_ops->getpgbwgcfgrx) | |
730 | return ret; | |
731 | ||
732 | ret = nla_parse_nested(pg_tb, DCB_PG_ATTR_MAX, | |
733 | tb[DCB_ATTR_PG_CFG], dcbnl_pg_nest); | |
734 | ||
735 | if (ret) | |
736 | goto err_out; | |
737 | ||
738 | dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
739 | if (!dcbnl_skb) | |
740 | goto err_out; | |
741 | ||
742 | nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); | |
743 | ||
744 | dcb = NLMSG_DATA(nlh); | |
745 | dcb->dcb_family = AF_UNSPEC; | |
746 | dcb->cmd = (dir) ? DCB_CMD_PGRX_GCFG : DCB_CMD_PGTX_GCFG; | |
747 | ||
748 | pg_nest = nla_nest_start(dcbnl_skb, DCB_ATTR_PG_CFG); | |
749 | if (!pg_nest) | |
750 | goto err; | |
751 | ||
752 | if (pg_tb[DCB_PG_ATTR_TC_ALL]) | |
753 | getall = 1; | |
754 | ||
755 | for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) { | |
756 | if (!getall && !pg_tb[i]) | |
757 | continue; | |
758 | ||
759 | if (pg_tb[DCB_PG_ATTR_TC_ALL]) | |
760 | data = pg_tb[DCB_PG_ATTR_TC_ALL]; | |
761 | else | |
762 | data = pg_tb[i]; | |
763 | ret = nla_parse_nested(param_tb, DCB_TC_ATTR_PARAM_MAX, | |
764 | data, dcbnl_tc_param_nest); | |
765 | if (ret) | |
766 | goto err_pg; | |
767 | ||
768 | param_nest = nla_nest_start(dcbnl_skb, i); | |
769 | if (!param_nest) | |
770 | goto err_pg; | |
771 | ||
772 | pgid = DCB_ATTR_VALUE_UNDEFINED; | |
773 | prio = DCB_ATTR_VALUE_UNDEFINED; | |
774 | tc_pct = DCB_ATTR_VALUE_UNDEFINED; | |
775 | up_map = DCB_ATTR_VALUE_UNDEFINED; | |
776 | ||
777 | if (dir) { | |
778 | /* Rx */ | |
779 | netdev->dcbnl_ops->getpgtccfgrx(netdev, | |
780 | i - DCB_PG_ATTR_TC_0, &prio, | |
781 | &pgid, &tc_pct, &up_map); | |
782 | } else { | |
783 | /* Tx */ | |
784 | netdev->dcbnl_ops->getpgtccfgtx(netdev, | |
785 | i - DCB_PG_ATTR_TC_0, &prio, | |
786 | &pgid, &tc_pct, &up_map); | |
787 | } | |
788 | ||
789 | if (param_tb[DCB_TC_ATTR_PARAM_PGID] || | |
790 | param_tb[DCB_TC_ATTR_PARAM_ALL]) { | |
791 | ret = nla_put_u8(dcbnl_skb, | |
792 | DCB_TC_ATTR_PARAM_PGID, pgid); | |
793 | if (ret) | |
794 | goto err_param; | |
795 | } | |
796 | if (param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING] || | |
797 | param_tb[DCB_TC_ATTR_PARAM_ALL]) { | |
798 | ret = nla_put_u8(dcbnl_skb, | |
799 | DCB_TC_ATTR_PARAM_UP_MAPPING, up_map); | |
800 | if (ret) | |
801 | goto err_param; | |
802 | } | |
803 | if (param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO] || | |
804 | param_tb[DCB_TC_ATTR_PARAM_ALL]) { | |
805 | ret = nla_put_u8(dcbnl_skb, | |
806 | DCB_TC_ATTR_PARAM_STRICT_PRIO, prio); | |
807 | if (ret) | |
808 | goto err_param; | |
809 | } | |
810 | if (param_tb[DCB_TC_ATTR_PARAM_BW_PCT] || | |
811 | param_tb[DCB_TC_ATTR_PARAM_ALL]) { | |
812 | ret = nla_put_u8(dcbnl_skb, DCB_TC_ATTR_PARAM_BW_PCT, | |
813 | tc_pct); | |
814 | if (ret) | |
815 | goto err_param; | |
816 | } | |
817 | nla_nest_end(dcbnl_skb, param_nest); | |
818 | } | |
819 | ||
820 | if (pg_tb[DCB_PG_ATTR_BW_ID_ALL]) | |
821 | getall = 1; | |
822 | else | |
823 | getall = 0; | |
824 | ||
825 | for (i = DCB_PG_ATTR_BW_ID_0; i <= DCB_PG_ATTR_BW_ID_7; i++) { | |
826 | if (!getall && !pg_tb[i]) | |
827 | continue; | |
828 | ||
829 | tc_pct = DCB_ATTR_VALUE_UNDEFINED; | |
830 | ||
831 | if (dir) { | |
832 | /* Rx */ | |
833 | netdev->dcbnl_ops->getpgbwgcfgrx(netdev, | |
834 | i - DCB_PG_ATTR_BW_ID_0, &tc_pct); | |
835 | } else { | |
836 | /* Tx */ | |
837 | netdev->dcbnl_ops->getpgbwgcfgtx(netdev, | |
838 | i - DCB_PG_ATTR_BW_ID_0, &tc_pct); | |
839 | } | |
840 | ret = nla_put_u8(dcbnl_skb, i, tc_pct); | |
841 | ||
842 | if (ret) | |
843 | goto err_pg; | |
844 | } | |
845 | ||
846 | nla_nest_end(dcbnl_skb, pg_nest); | |
847 | ||
848 | nlmsg_end(dcbnl_skb, nlh); | |
849 | ||
850 | ret = rtnl_unicast(dcbnl_skb, &init_net, pid); | |
851 | if (ret) | |
7eaf5077 | 852 | goto err_out; |
2f90b865 AD |
853 | |
854 | return 0; | |
855 | ||
856 | err_param: | |
857 | nla_nest_cancel(dcbnl_skb, param_nest); | |
858 | err_pg: | |
859 | nla_nest_cancel(dcbnl_skb, pg_nest); | |
860 | nlmsg_failure: | |
861 | err: | |
858eb711 | 862 | kfree_skb(dcbnl_skb); |
2f90b865 AD |
863 | err_out: |
864 | ret = -EINVAL; | |
865 | return ret; | |
866 | } | |
867 | ||
868 | static int dcbnl_pgtx_getcfg(struct net_device *netdev, struct nlattr **tb, | |
869 | u32 pid, u32 seq, u16 flags) | |
870 | { | |
871 | return __dcbnl_pg_getcfg(netdev, tb, pid, seq, flags, 0); | |
872 | } | |
873 | ||
874 | static int dcbnl_pgrx_getcfg(struct net_device *netdev, struct nlattr **tb, | |
875 | u32 pid, u32 seq, u16 flags) | |
876 | { | |
877 | return __dcbnl_pg_getcfg(netdev, tb, pid, seq, flags, 1); | |
878 | } | |
879 | ||
880 | static int dcbnl_setstate(struct net_device *netdev, struct nlattr **tb, | |
881 | u32 pid, u32 seq, u16 flags) | |
882 | { | |
883 | int ret = -EINVAL; | |
884 | u8 value; | |
885 | ||
886 | if (!tb[DCB_ATTR_STATE] || !netdev->dcbnl_ops->setstate) | |
887 | return ret; | |
888 | ||
889 | value = nla_get_u8(tb[DCB_ATTR_STATE]); | |
890 | ||
1486a61e DS |
891 | ret = dcbnl_reply(netdev->dcbnl_ops->setstate(netdev, value), |
892 | RTM_SETDCB, DCB_CMD_SSTATE, DCB_ATTR_STATE, | |
2f90b865 AD |
893 | pid, seq, flags); |
894 | ||
895 | return ret; | |
896 | } | |
897 | ||
898 | static int dcbnl_setpfccfg(struct net_device *netdev, struct nlattr **tb, | |
899 | u32 pid, u32 seq, u16 flags) | |
900 | { | |
901 | struct nlattr *data[DCB_PFC_UP_ATTR_MAX + 1]; | |
902 | int i; | |
903 | int ret = -EINVAL; | |
904 | u8 value; | |
905 | ||
906 | if (!tb[DCB_ATTR_PFC_CFG] || !netdev->dcbnl_ops->setpfccfg) | |
907 | return ret; | |
908 | ||
909 | ret = nla_parse_nested(data, DCB_PFC_UP_ATTR_MAX, | |
910 | tb[DCB_ATTR_PFC_CFG], | |
911 | dcbnl_pfc_up_nest); | |
912 | if (ret) | |
913 | goto err; | |
914 | ||
915 | for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) { | |
916 | if (data[i] == NULL) | |
917 | continue; | |
918 | value = nla_get_u8(data[i]); | |
919 | netdev->dcbnl_ops->setpfccfg(netdev, | |
920 | data[i]->nla_type - DCB_PFC_UP_ATTR_0, value); | |
921 | } | |
922 | ||
923 | ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_PFC_SCFG, DCB_ATTR_PFC_CFG, | |
924 | pid, seq, flags); | |
925 | err: | |
926 | return ret; | |
927 | } | |
928 | ||
929 | static int dcbnl_setall(struct net_device *netdev, struct nlattr **tb, | |
930 | u32 pid, u32 seq, u16 flags) | |
931 | { | |
932 | int ret = -EINVAL; | |
933 | ||
934 | if (!tb[DCB_ATTR_SET_ALL] || !netdev->dcbnl_ops->setall) | |
935 | return ret; | |
936 | ||
937 | ret = dcbnl_reply(netdev->dcbnl_ops->setall(netdev), RTM_SETDCB, | |
938 | DCB_CMD_SET_ALL, DCB_ATTR_SET_ALL, pid, seq, flags); | |
939 | ||
940 | return ret; | |
941 | } | |
942 | ||
943 | static int __dcbnl_pg_setcfg(struct net_device *netdev, struct nlattr **tb, | |
944 | u32 pid, u32 seq, u16 flags, int dir) | |
945 | { | |
946 | struct nlattr *pg_tb[DCB_PG_ATTR_MAX + 1]; | |
947 | struct nlattr *param_tb[DCB_TC_ATTR_PARAM_MAX + 1]; | |
948 | int ret = -EINVAL; | |
949 | int i; | |
950 | u8 pgid; | |
951 | u8 up_map; | |
952 | u8 prio; | |
953 | u8 tc_pct; | |
954 | ||
955 | if (!tb[DCB_ATTR_PG_CFG] || | |
956 | !netdev->dcbnl_ops->setpgtccfgtx || | |
957 | !netdev->dcbnl_ops->setpgtccfgrx || | |
958 | !netdev->dcbnl_ops->setpgbwgcfgtx || | |
959 | !netdev->dcbnl_ops->setpgbwgcfgrx) | |
960 | return ret; | |
961 | ||
962 | ret = nla_parse_nested(pg_tb, DCB_PG_ATTR_MAX, | |
963 | tb[DCB_ATTR_PG_CFG], dcbnl_pg_nest); | |
964 | if (ret) | |
965 | goto err; | |
966 | ||
967 | for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) { | |
968 | if (!pg_tb[i]) | |
969 | continue; | |
970 | ||
971 | ret = nla_parse_nested(param_tb, DCB_TC_ATTR_PARAM_MAX, | |
972 | pg_tb[i], dcbnl_tc_param_nest); | |
973 | if (ret) | |
974 | goto err; | |
975 | ||
976 | pgid = DCB_ATTR_VALUE_UNDEFINED; | |
977 | prio = DCB_ATTR_VALUE_UNDEFINED; | |
978 | tc_pct = DCB_ATTR_VALUE_UNDEFINED; | |
979 | up_map = DCB_ATTR_VALUE_UNDEFINED; | |
980 | ||
981 | if (param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO]) | |
982 | prio = | |
983 | nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO]); | |
984 | ||
985 | if (param_tb[DCB_TC_ATTR_PARAM_PGID]) | |
986 | pgid = nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_PGID]); | |
987 | ||
988 | if (param_tb[DCB_TC_ATTR_PARAM_BW_PCT]) | |
989 | tc_pct = nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_BW_PCT]); | |
990 | ||
991 | if (param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING]) | |
992 | up_map = | |
993 | nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING]); | |
994 | ||
995 | /* dir: Tx = 0, Rx = 1 */ | |
996 | if (dir) { | |
997 | /* Rx */ | |
998 | netdev->dcbnl_ops->setpgtccfgrx(netdev, | |
999 | i - DCB_PG_ATTR_TC_0, | |
1000 | prio, pgid, tc_pct, up_map); | |
1001 | } else { | |
1002 | /* Tx */ | |
1003 | netdev->dcbnl_ops->setpgtccfgtx(netdev, | |
1004 | i - DCB_PG_ATTR_TC_0, | |
1005 | prio, pgid, tc_pct, up_map); | |
1006 | } | |
1007 | } | |
1008 | ||
1009 | for (i = DCB_PG_ATTR_BW_ID_0; i <= DCB_PG_ATTR_BW_ID_7; i++) { | |
1010 | if (!pg_tb[i]) | |
1011 | continue; | |
1012 | ||
1013 | tc_pct = nla_get_u8(pg_tb[i]); | |
1014 | ||
1015 | /* dir: Tx = 0, Rx = 1 */ | |
1016 | if (dir) { | |
1017 | /* Rx */ | |
1018 | netdev->dcbnl_ops->setpgbwgcfgrx(netdev, | |
1019 | i - DCB_PG_ATTR_BW_ID_0, tc_pct); | |
1020 | } else { | |
1021 | /* Tx */ | |
1022 | netdev->dcbnl_ops->setpgbwgcfgtx(netdev, | |
1023 | i - DCB_PG_ATTR_BW_ID_0, tc_pct); | |
1024 | } | |
1025 | } | |
1026 | ||
1027 | ret = dcbnl_reply(0, RTM_SETDCB, | |
1028 | (dir ? DCB_CMD_PGRX_SCFG : DCB_CMD_PGTX_SCFG), | |
1029 | DCB_ATTR_PG_CFG, pid, seq, flags); | |
1030 | ||
1031 | err: | |
1032 | return ret; | |
1033 | } | |
1034 | ||
1035 | static int dcbnl_pgtx_setcfg(struct net_device *netdev, struct nlattr **tb, | |
1036 | u32 pid, u32 seq, u16 flags) | |
1037 | { | |
1038 | return __dcbnl_pg_setcfg(netdev, tb, pid, seq, flags, 0); | |
1039 | } | |
1040 | ||
1041 | static int dcbnl_pgrx_setcfg(struct net_device *netdev, struct nlattr **tb, | |
1042 | u32 pid, u32 seq, u16 flags) | |
1043 | { | |
1044 | return __dcbnl_pg_setcfg(netdev, tb, pid, seq, flags, 1); | |
1045 | } | |
1046 | ||
859ee3c4 AD |
1047 | static int dcbnl_bcn_getcfg(struct net_device *netdev, struct nlattr **tb, |
1048 | u32 pid, u32 seq, u16 flags) | |
1049 | { | |
1050 | struct sk_buff *dcbnl_skb; | |
1051 | struct nlmsghdr *nlh; | |
1052 | struct dcbmsg *dcb; | |
1053 | struct nlattr *bcn_nest; | |
1054 | struct nlattr *bcn_tb[DCB_BCN_ATTR_MAX + 1]; | |
1055 | u8 value_byte; | |
1056 | u32 value_integer; | |
1057 | int ret = -EINVAL; | |
1058 | bool getall = false; | |
1059 | int i; | |
1060 | ||
1061 | if (!tb[DCB_ATTR_BCN] || !netdev->dcbnl_ops->getbcnrp || | |
1062 | !netdev->dcbnl_ops->getbcncfg) | |
1063 | return ret; | |
1064 | ||
1065 | ret = nla_parse_nested(bcn_tb, DCB_BCN_ATTR_MAX, | |
1066 | tb[DCB_ATTR_BCN], dcbnl_bcn_nest); | |
1067 | ||
1068 | if (ret) | |
1069 | goto err_out; | |
1070 | ||
1071 | dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
1072 | if (!dcbnl_skb) | |
1073 | goto err_out; | |
1074 | ||
1075 | nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); | |
1076 | ||
1077 | dcb = NLMSG_DATA(nlh); | |
1078 | dcb->dcb_family = AF_UNSPEC; | |
1079 | dcb->cmd = DCB_CMD_BCN_GCFG; | |
1080 | ||
1081 | bcn_nest = nla_nest_start(dcbnl_skb, DCB_ATTR_BCN); | |
1082 | if (!bcn_nest) | |
1083 | goto err; | |
1084 | ||
1085 | if (bcn_tb[DCB_BCN_ATTR_ALL]) | |
1086 | getall = true; | |
1087 | ||
1088 | for (i = DCB_BCN_ATTR_RP_0; i <= DCB_BCN_ATTR_RP_7; i++) { | |
1089 | if (!getall && !bcn_tb[i]) | |
1090 | continue; | |
1091 | ||
1092 | netdev->dcbnl_ops->getbcnrp(netdev, i - DCB_BCN_ATTR_RP_0, | |
1093 | &value_byte); | |
1094 | ret = nla_put_u8(dcbnl_skb, i, value_byte); | |
1095 | if (ret) | |
1096 | goto err_bcn; | |
1097 | } | |
1098 | ||
f4314e81 | 1099 | for (i = DCB_BCN_ATTR_BCNA_0; i <= DCB_BCN_ATTR_RI; i++) { |
859ee3c4 AD |
1100 | if (!getall && !bcn_tb[i]) |
1101 | continue; | |
1102 | ||
1103 | netdev->dcbnl_ops->getbcncfg(netdev, i, | |
1104 | &value_integer); | |
1105 | ret = nla_put_u32(dcbnl_skb, i, value_integer); | |
1106 | if (ret) | |
1107 | goto err_bcn; | |
1108 | } | |
1109 | ||
1110 | nla_nest_end(dcbnl_skb, bcn_nest); | |
1111 | ||
1112 | nlmsg_end(dcbnl_skb, nlh); | |
1113 | ||
1114 | ret = rtnl_unicast(dcbnl_skb, &init_net, pid); | |
1115 | if (ret) | |
7eaf5077 | 1116 | goto err_out; |
859ee3c4 AD |
1117 | |
1118 | return 0; | |
1119 | ||
1120 | err_bcn: | |
1121 | nla_nest_cancel(dcbnl_skb, bcn_nest); | |
1122 | nlmsg_failure: | |
1123 | err: | |
858eb711 | 1124 | kfree_skb(dcbnl_skb); |
859ee3c4 AD |
1125 | err_out: |
1126 | ret = -EINVAL; | |
1127 | return ret; | |
1128 | } | |
1129 | ||
1130 | static int dcbnl_bcn_setcfg(struct net_device *netdev, struct nlattr **tb, | |
1131 | u32 pid, u32 seq, u16 flags) | |
1132 | { | |
1133 | struct nlattr *data[DCB_BCN_ATTR_MAX + 1]; | |
1134 | int i; | |
1135 | int ret = -EINVAL; | |
1136 | u8 value_byte; | |
1137 | u32 value_int; | |
1138 | ||
f64f9e71 JP |
1139 | if (!tb[DCB_ATTR_BCN] || !netdev->dcbnl_ops->setbcncfg || |
1140 | !netdev->dcbnl_ops->setbcnrp) | |
859ee3c4 AD |
1141 | return ret; |
1142 | ||
1143 | ret = nla_parse_nested(data, DCB_BCN_ATTR_MAX, | |
1144 | tb[DCB_ATTR_BCN], | |
1145 | dcbnl_pfc_up_nest); | |
1146 | if (ret) | |
1147 | goto err; | |
1148 | ||
1149 | for (i = DCB_BCN_ATTR_RP_0; i <= DCB_BCN_ATTR_RP_7; i++) { | |
1150 | if (data[i] == NULL) | |
1151 | continue; | |
1152 | value_byte = nla_get_u8(data[i]); | |
1153 | netdev->dcbnl_ops->setbcnrp(netdev, | |
1154 | data[i]->nla_type - DCB_BCN_ATTR_RP_0, value_byte); | |
1155 | } | |
1156 | ||
f4314e81 | 1157 | for (i = DCB_BCN_ATTR_BCNA_0; i <= DCB_BCN_ATTR_RI; i++) { |
859ee3c4 AD |
1158 | if (data[i] == NULL) |
1159 | continue; | |
1160 | value_int = nla_get_u32(data[i]); | |
1161 | netdev->dcbnl_ops->setbcncfg(netdev, | |
1162 | i, value_int); | |
1163 | } | |
1164 | ||
1165 | ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_BCN_SCFG, DCB_ATTR_BCN, | |
1166 | pid, seq, flags); | |
1167 | err: | |
1168 | return ret; | |
1169 | } | |
1170 | ||
dc6ed1df SR |
1171 | static int dcbnl_build_peer_app(struct net_device *netdev, struct sk_buff* skb, |
1172 | int app_nested_type, int app_info_type, | |
1173 | int app_entry_type) | |
eed84713 SR |
1174 | { |
1175 | struct dcb_peer_app_info info; | |
1176 | struct dcb_app *table = NULL; | |
1177 | const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops; | |
1178 | u16 app_count; | |
1179 | int err; | |
1180 | ||
1181 | ||
1182 | /** | |
1183 | * retrieve the peer app configuration form the driver. If the driver | |
1184 | * handlers fail exit without doing anything | |
1185 | */ | |
1186 | err = ops->peer_getappinfo(netdev, &info, &app_count); | |
1187 | if (!err && app_count) { | |
1188 | table = kmalloc(sizeof(struct dcb_app) * app_count, GFP_KERNEL); | |
1189 | if (!table) | |
1190 | return -ENOMEM; | |
1191 | ||
1192 | err = ops->peer_getapptable(netdev, table); | |
1193 | } | |
1194 | ||
1195 | if (!err) { | |
1196 | u16 i; | |
1197 | struct nlattr *app; | |
1198 | ||
1199 | /** | |
1200 | * build the message, from here on the only possible failure | |
1201 | * is due to the skb size | |
1202 | */ | |
1203 | err = -EMSGSIZE; | |
1204 | ||
dc6ed1df | 1205 | app = nla_nest_start(skb, app_nested_type); |
eed84713 SR |
1206 | if (!app) |
1207 | goto nla_put_failure; | |
1208 | ||
1eb4c977 DM |
1209 | if (app_info_type && |
1210 | nla_put(skb, app_info_type, sizeof(info), &info)) | |
1211 | goto nla_put_failure; | |
eed84713 | 1212 | |
1eb4c977 DM |
1213 | for (i = 0; i < app_count; i++) { |
1214 | if (nla_put(skb, app_entry_type, sizeof(struct dcb_app), | |
1215 | &table[i])) | |
1216 | goto nla_put_failure; | |
1217 | } | |
eed84713 SR |
1218 | nla_nest_end(skb, app); |
1219 | } | |
1220 | err = 0; | |
1221 | ||
1222 | nla_put_failure: | |
1223 | kfree(table); | |
1224 | return err; | |
1225 | } | |
3e29027a JF |
1226 | |
1227 | /* Handle IEEE 802.1Qaz GET commands. */ | |
314b4778 | 1228 | static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev) |
3e29027a | 1229 | { |
9ab933ab JF |
1230 | struct nlattr *ieee, *app; |
1231 | struct dcb_app_type *itr; | |
3e29027a | 1232 | const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops; |
c7797baf | 1233 | int dcbx; |
314b4778 | 1234 | int err = -EMSGSIZE; |
3e29027a | 1235 | |
1eb4c977 DM |
1236 | if (nla_put_string(skb, DCB_ATTR_IFNAME, netdev->name)) |
1237 | goto nla_put_failure; | |
3e29027a JF |
1238 | ieee = nla_nest_start(skb, DCB_ATTR_IEEE); |
1239 | if (!ieee) | |
1240 | goto nla_put_failure; | |
1241 | ||
1242 | if (ops->ieee_getets) { | |
1243 | struct ieee_ets ets; | |
1244 | err = ops->ieee_getets(netdev, &ets); | |
1eb4c977 DM |
1245 | if (!err && |
1246 | nla_put(skb, DCB_ATTR_IEEE_ETS, sizeof(ets), &ets)) | |
1247 | goto nla_put_failure; | |
3e29027a JF |
1248 | } |
1249 | ||
08f10aff AV |
1250 | if (ops->ieee_getmaxrate) { |
1251 | struct ieee_maxrate maxrate; | |
1252 | err = ops->ieee_getmaxrate(netdev, &maxrate); | |
1253 | if (!err) { | |
1254 | err = nla_put(skb, DCB_ATTR_IEEE_MAXRATE, | |
1255 | sizeof(maxrate), &maxrate); | |
1256 | if (err) | |
1257 | goto nla_put_failure; | |
1258 | } | |
1259 | } | |
1260 | ||
3e29027a JF |
1261 | if (ops->ieee_getpfc) { |
1262 | struct ieee_pfc pfc; | |
1263 | err = ops->ieee_getpfc(netdev, &pfc); | |
1eb4c977 DM |
1264 | if (!err && |
1265 | nla_put(skb, DCB_ATTR_IEEE_PFC, sizeof(pfc), &pfc)) | |
1266 | goto nla_put_failure; | |
3e29027a JF |
1267 | } |
1268 | ||
9ab933ab JF |
1269 | app = nla_nest_start(skb, DCB_ATTR_IEEE_APP_TABLE); |
1270 | if (!app) | |
1271 | goto nla_put_failure; | |
1272 | ||
1273 | spin_lock(&dcb_lock); | |
1274 | list_for_each_entry(itr, &dcb_app_list, list) { | |
e290ed81 | 1275 | if (itr->ifindex == netdev->ifindex) { |
70bfa2d2 DC |
1276 | err = nla_put(skb, DCB_ATTR_IEEE_APP, sizeof(itr->app), |
1277 | &itr->app); | |
1278 | if (err) { | |
1279 | spin_unlock(&dcb_lock); | |
1280 | goto nla_put_failure; | |
1281 | } | |
1282 | } | |
9ab933ab | 1283 | } |
c7797baf JF |
1284 | |
1285 | if (netdev->dcbnl_ops->getdcbx) | |
1286 | dcbx = netdev->dcbnl_ops->getdcbx(netdev); | |
1287 | else | |
1288 | dcbx = -EOPNOTSUPP; | |
1289 | ||
9ab933ab JF |
1290 | spin_unlock(&dcb_lock); |
1291 | nla_nest_end(skb, app); | |
1292 | ||
eed84713 SR |
1293 | /* get peer info if available */ |
1294 | if (ops->ieee_peer_getets) { | |
1295 | struct ieee_ets ets; | |
1296 | err = ops->ieee_peer_getets(netdev, &ets); | |
1eb4c977 DM |
1297 | if (!err && |
1298 | nla_put(skb, DCB_ATTR_IEEE_PEER_ETS, sizeof(ets), &ets)) | |
1299 | goto nla_put_failure; | |
eed84713 SR |
1300 | } |
1301 | ||
1302 | if (ops->ieee_peer_getpfc) { | |
1303 | struct ieee_pfc pfc; | |
1304 | err = ops->ieee_peer_getpfc(netdev, &pfc); | |
1eb4c977 DM |
1305 | if (!err && |
1306 | nla_put(skb, DCB_ATTR_IEEE_PEER_PFC, sizeof(pfc), &pfc)) | |
1307 | goto nla_put_failure; | |
eed84713 SR |
1308 | } |
1309 | ||
1310 | if (ops->peer_getappinfo && ops->peer_getapptable) { | |
dc6ed1df SR |
1311 | err = dcbnl_build_peer_app(netdev, skb, |
1312 | DCB_ATTR_IEEE_PEER_APP, | |
1313 | DCB_ATTR_IEEE_APP_UNSPEC, | |
1314 | DCB_ATTR_IEEE_APP); | |
eed84713 SR |
1315 | if (err) |
1316 | goto nla_put_failure; | |
1317 | } | |
1318 | ||
3e29027a | 1319 | nla_nest_end(skb, ieee); |
c7797baf JF |
1320 | if (dcbx >= 0) { |
1321 | err = nla_put_u8(skb, DCB_ATTR_DCBX, dcbx); | |
1322 | if (err) | |
1323 | goto nla_put_failure; | |
1324 | } | |
3e29027a | 1325 | |
314b4778 JF |
1326 | return 0; |
1327 | ||
3e29027a | 1328 | nla_put_failure: |
314b4778 | 1329 | return err; |
3e29027a JF |
1330 | } |
1331 | ||
5b7f7626 SR |
1332 | static int dcbnl_cee_pg_fill(struct sk_buff *skb, struct net_device *dev, |
1333 | int dir) | |
1334 | { | |
1335 | u8 pgid, up_map, prio, tc_pct; | |
1336 | const struct dcbnl_rtnl_ops *ops = dev->dcbnl_ops; | |
1337 | int i = dir ? DCB_ATTR_CEE_TX_PG : DCB_ATTR_CEE_RX_PG; | |
1338 | struct nlattr *pg = nla_nest_start(skb, i); | |
1339 | ||
1340 | if (!pg) | |
1341 | goto nla_put_failure; | |
1342 | ||
1343 | for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) { | |
1344 | struct nlattr *tc_nest = nla_nest_start(skb, i); | |
1345 | ||
1346 | if (!tc_nest) | |
1347 | goto nla_put_failure; | |
1348 | ||
1349 | pgid = DCB_ATTR_VALUE_UNDEFINED; | |
1350 | prio = DCB_ATTR_VALUE_UNDEFINED; | |
1351 | tc_pct = DCB_ATTR_VALUE_UNDEFINED; | |
1352 | up_map = DCB_ATTR_VALUE_UNDEFINED; | |
1353 | ||
1354 | if (!dir) | |
1355 | ops->getpgtccfgrx(dev, i - DCB_PG_ATTR_TC_0, | |
1356 | &prio, &pgid, &tc_pct, &up_map); | |
1357 | else | |
1358 | ops->getpgtccfgtx(dev, i - DCB_PG_ATTR_TC_0, | |
1359 | &prio, &pgid, &tc_pct, &up_map); | |
1360 | ||
1eb4c977 DM |
1361 | if (nla_put_u8(skb, DCB_TC_ATTR_PARAM_PGID, pgid) || |
1362 | nla_put_u8(skb, DCB_TC_ATTR_PARAM_UP_MAPPING, up_map) || | |
1363 | nla_put_u8(skb, DCB_TC_ATTR_PARAM_STRICT_PRIO, prio) || | |
1364 | nla_put_u8(skb, DCB_TC_ATTR_PARAM_BW_PCT, tc_pct)) | |
1365 | goto nla_put_failure; | |
5b7f7626 SR |
1366 | nla_nest_end(skb, tc_nest); |
1367 | } | |
1368 | ||
1369 | for (i = DCB_PG_ATTR_BW_ID_0; i <= DCB_PG_ATTR_BW_ID_7; i++) { | |
1370 | tc_pct = DCB_ATTR_VALUE_UNDEFINED; | |
1371 | ||
1372 | if (!dir) | |
1373 | ops->getpgbwgcfgrx(dev, i - DCB_PG_ATTR_BW_ID_0, | |
1374 | &tc_pct); | |
1375 | else | |
1376 | ops->getpgbwgcfgtx(dev, i - DCB_PG_ATTR_BW_ID_0, | |
1377 | &tc_pct); | |
1eb4c977 DM |
1378 | if (nla_put_u8(skb, i, tc_pct)) |
1379 | goto nla_put_failure; | |
5b7f7626 SR |
1380 | } |
1381 | nla_nest_end(skb, pg); | |
1382 | return 0; | |
1383 | ||
1384 | nla_put_failure: | |
1385 | return -EMSGSIZE; | |
1386 | } | |
1387 | ||
1388 | static int dcbnl_cee_fill(struct sk_buff *skb, struct net_device *netdev) | |
1389 | { | |
1390 | struct nlattr *cee, *app; | |
1391 | struct dcb_app_type *itr; | |
1392 | const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops; | |
1393 | int dcbx, i, err = -EMSGSIZE; | |
1394 | u8 value; | |
1395 | ||
1eb4c977 DM |
1396 | if (nla_put_string(skb, DCB_ATTR_IFNAME, netdev->name)) |
1397 | goto nla_put_failure; | |
5b7f7626 SR |
1398 | cee = nla_nest_start(skb, DCB_ATTR_CEE); |
1399 | if (!cee) | |
1400 | goto nla_put_failure; | |
1401 | ||
1402 | /* local pg */ | |
1403 | if (ops->getpgtccfgtx && ops->getpgbwgcfgtx) { | |
1404 | err = dcbnl_cee_pg_fill(skb, netdev, 1); | |
1405 | if (err) | |
1406 | goto nla_put_failure; | |
1407 | } | |
1408 | ||
1409 | if (ops->getpgtccfgrx && ops->getpgbwgcfgrx) { | |
1410 | err = dcbnl_cee_pg_fill(skb, netdev, 0); | |
1411 | if (err) | |
1412 | goto nla_put_failure; | |
1413 | } | |
1414 | ||
1415 | /* local pfc */ | |
1416 | if (ops->getpfccfg) { | |
1417 | struct nlattr *pfc_nest = nla_nest_start(skb, DCB_ATTR_CEE_PFC); | |
1418 | ||
1419 | if (!pfc_nest) | |
1420 | goto nla_put_failure; | |
1421 | ||
1422 | for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) { | |
1423 | ops->getpfccfg(netdev, i - DCB_PFC_UP_ATTR_0, &value); | |
1eb4c977 DM |
1424 | if (nla_put_u8(skb, i, value)) |
1425 | goto nla_put_failure; | |
5b7f7626 SR |
1426 | } |
1427 | nla_nest_end(skb, pfc_nest); | |
1428 | } | |
1429 | ||
1430 | /* local app */ | |
1431 | spin_lock(&dcb_lock); | |
1432 | app = nla_nest_start(skb, DCB_ATTR_CEE_APP_TABLE); | |
1433 | if (!app) | |
40f5d72a | 1434 | goto dcb_unlock; |
5b7f7626 SR |
1435 | |
1436 | list_for_each_entry(itr, &dcb_app_list, list) { | |
e290ed81 | 1437 | if (itr->ifindex == netdev->ifindex) { |
5b7f7626 SR |
1438 | struct nlattr *app_nest = nla_nest_start(skb, |
1439 | DCB_ATTR_APP); | |
1440 | if (!app_nest) | |
1441 | goto dcb_unlock; | |
1442 | ||
1443 | err = nla_put_u8(skb, DCB_APP_ATTR_IDTYPE, | |
1444 | itr->app.selector); | |
1445 | if (err) | |
1446 | goto dcb_unlock; | |
1447 | ||
1448 | err = nla_put_u16(skb, DCB_APP_ATTR_ID, | |
1449 | itr->app.protocol); | |
1450 | if (err) | |
1451 | goto dcb_unlock; | |
1452 | ||
1453 | err = nla_put_u8(skb, DCB_APP_ATTR_PRIORITY, | |
1454 | itr->app.priority); | |
1455 | if (err) | |
1456 | goto dcb_unlock; | |
1457 | ||
1458 | nla_nest_end(skb, app_nest); | |
1459 | } | |
1460 | } | |
1461 | nla_nest_end(skb, app); | |
1462 | ||
1463 | if (netdev->dcbnl_ops->getdcbx) | |
1464 | dcbx = netdev->dcbnl_ops->getdcbx(netdev); | |
1465 | else | |
1466 | dcbx = -EOPNOTSUPP; | |
1467 | ||
1468 | spin_unlock(&dcb_lock); | |
1469 | ||
1470 | /* features flags */ | |
1471 | if (ops->getfeatcfg) { | |
1472 | struct nlattr *feat = nla_nest_start(skb, DCB_ATTR_CEE_FEAT); | |
1473 | if (!feat) | |
1474 | goto nla_put_failure; | |
1475 | ||
1476 | for (i = DCB_FEATCFG_ATTR_ALL + 1; i <= DCB_FEATCFG_ATTR_MAX; | |
1477 | i++) | |
1eb4c977 DM |
1478 | if (!ops->getfeatcfg(netdev, i, &value) && |
1479 | nla_put_u8(skb, i, value)) | |
1480 | goto nla_put_failure; | |
5b7f7626 SR |
1481 | |
1482 | nla_nest_end(skb, feat); | |
1483 | } | |
1484 | ||
1485 | /* peer info if available */ | |
1486 | if (ops->cee_peer_getpg) { | |
1487 | struct cee_pg pg; | |
1488 | err = ops->cee_peer_getpg(netdev, &pg); | |
1eb4c977 DM |
1489 | if (!err && |
1490 | nla_put(skb, DCB_ATTR_CEE_PEER_PG, sizeof(pg), &pg)) | |
1491 | goto nla_put_failure; | |
5b7f7626 SR |
1492 | } |
1493 | ||
1494 | if (ops->cee_peer_getpfc) { | |
1495 | struct cee_pfc pfc; | |
1496 | err = ops->cee_peer_getpfc(netdev, &pfc); | |
1eb4c977 DM |
1497 | if (!err && |
1498 | nla_put(skb, DCB_ATTR_CEE_PEER_PFC, sizeof(pfc), &pfc)) | |
1499 | goto nla_put_failure; | |
5b7f7626 SR |
1500 | } |
1501 | ||
1502 | if (ops->peer_getappinfo && ops->peer_getapptable) { | |
1503 | err = dcbnl_build_peer_app(netdev, skb, | |
1504 | DCB_ATTR_CEE_PEER_APP_TABLE, | |
1505 | DCB_ATTR_CEE_PEER_APP_INFO, | |
1506 | DCB_ATTR_CEE_PEER_APP); | |
1507 | if (err) | |
1508 | goto nla_put_failure; | |
1509 | } | |
1510 | nla_nest_end(skb, cee); | |
1511 | ||
1512 | /* DCBX state */ | |
1513 | if (dcbx >= 0) { | |
1514 | err = nla_put_u8(skb, DCB_ATTR_DCBX, dcbx); | |
1515 | if (err) | |
1516 | goto nla_put_failure; | |
1517 | } | |
1518 | return 0; | |
1519 | ||
1520 | dcb_unlock: | |
1521 | spin_unlock(&dcb_lock); | |
1522 | nla_put_failure: | |
1523 | return err; | |
1524 | } | |
1525 | ||
1526 | static int dcbnl_notify(struct net_device *dev, int event, int cmd, | |
1527 | u32 seq, u32 pid, int dcbx_ver) | |
314b4778 JF |
1528 | { |
1529 | struct net *net = dev_net(dev); | |
1530 | struct sk_buff *skb; | |
1531 | struct nlmsghdr *nlh; | |
1532 | struct dcbmsg *dcb; | |
1533 | const struct dcbnl_rtnl_ops *ops = dev->dcbnl_ops; | |
1534 | int err; | |
1535 | ||
1536 | if (!ops) | |
1537 | return -EOPNOTSUPP; | |
1538 | ||
1539 | skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
1540 | if (!skb) | |
1541 | return -ENOBUFS; | |
1542 | ||
1543 | nlh = nlmsg_put(skb, pid, 0, event, sizeof(*dcb), 0); | |
1544 | if (nlh == NULL) { | |
4d054f2f | 1545 | nlmsg_free(skb); |
314b4778 JF |
1546 | return -EMSGSIZE; |
1547 | } | |
1548 | ||
1549 | dcb = NLMSG_DATA(nlh); | |
1550 | dcb->dcb_family = AF_UNSPEC; | |
1551 | dcb->cmd = cmd; | |
1552 | ||
5b7f7626 SR |
1553 | if (dcbx_ver == DCB_CAP_DCBX_VER_IEEE) |
1554 | err = dcbnl_ieee_fill(skb, dev); | |
1555 | else | |
1556 | err = dcbnl_cee_fill(skb, dev); | |
1557 | ||
314b4778 JF |
1558 | if (err < 0) { |
1559 | /* Report error to broadcast listeners */ | |
1560 | nlmsg_cancel(skb, nlh); | |
1561 | kfree_skb(skb); | |
1562 | rtnl_set_sk_err(net, RTNLGRP_DCB, err); | |
1563 | } else { | |
1564 | /* End nlmsg and notify broadcast listeners */ | |
1565 | nlmsg_end(skb, nlh); | |
1566 | rtnl_notify(skb, net, 0, RTNLGRP_DCB, NULL, GFP_KERNEL); | |
1567 | } | |
1568 | ||
1569 | return err; | |
1570 | } | |
5b7f7626 SR |
1571 | |
1572 | int dcbnl_ieee_notify(struct net_device *dev, int event, int cmd, | |
1573 | u32 seq, u32 pid) | |
1574 | { | |
1575 | return dcbnl_notify(dev, event, cmd, seq, pid, DCB_CAP_DCBX_VER_IEEE); | |
1576 | } | |
1577 | EXPORT_SYMBOL(dcbnl_ieee_notify); | |
1578 | ||
1579 | int dcbnl_cee_notify(struct net_device *dev, int event, int cmd, | |
1580 | u32 seq, u32 pid) | |
1581 | { | |
1582 | return dcbnl_notify(dev, event, cmd, seq, pid, DCB_CAP_DCBX_VER_CEE); | |
1583 | } | |
1584 | EXPORT_SYMBOL(dcbnl_cee_notify); | |
314b4778 JF |
1585 | |
1586 | /* Handle IEEE 802.1Qaz SET commands. If any requested operation can not | |
1587 | * be completed the entire msg is aborted and error value is returned. | |
1588 | * No attempt is made to reconcile the case where only part of the | |
1589 | * cmd can be completed. | |
1590 | */ | |
1591 | static int dcbnl_ieee_set(struct net_device *netdev, struct nlattr **tb, | |
1592 | u32 pid, u32 seq, u16 flags) | |
1593 | { | |
1594 | const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops; | |
1595 | struct nlattr *ieee[DCB_ATTR_IEEE_MAX + 1]; | |
1596 | int err = -EOPNOTSUPP; | |
1597 | ||
1598 | if (!ops) | |
1599 | return err; | |
1600 | ||
4003b658 JF |
1601 | if (!tb[DCB_ATTR_IEEE]) |
1602 | return -EINVAL; | |
1603 | ||
314b4778 JF |
1604 | err = nla_parse_nested(ieee, DCB_ATTR_IEEE_MAX, |
1605 | tb[DCB_ATTR_IEEE], dcbnl_ieee_policy); | |
1606 | if (err) | |
1607 | return err; | |
1608 | ||
1609 | if (ieee[DCB_ATTR_IEEE_ETS] && ops->ieee_setets) { | |
1610 | struct ieee_ets *ets = nla_data(ieee[DCB_ATTR_IEEE_ETS]); | |
1611 | err = ops->ieee_setets(netdev, ets); | |
1612 | if (err) | |
1613 | goto err; | |
1614 | } | |
1615 | ||
08f10aff AV |
1616 | if (ieee[DCB_ATTR_IEEE_MAXRATE] && ops->ieee_setmaxrate) { |
1617 | struct ieee_maxrate *maxrate = | |
1618 | nla_data(ieee[DCB_ATTR_IEEE_MAXRATE]); | |
1619 | err = ops->ieee_setmaxrate(netdev, maxrate); | |
1620 | if (err) | |
1621 | goto err; | |
1622 | } | |
1623 | ||
314b4778 JF |
1624 | if (ieee[DCB_ATTR_IEEE_PFC] && ops->ieee_setpfc) { |
1625 | struct ieee_pfc *pfc = nla_data(ieee[DCB_ATTR_IEEE_PFC]); | |
1626 | err = ops->ieee_setpfc(netdev, pfc); | |
1627 | if (err) | |
1628 | goto err; | |
1629 | } | |
1630 | ||
1631 | if (ieee[DCB_ATTR_IEEE_APP_TABLE]) { | |
1632 | struct nlattr *attr; | |
1633 | int rem; | |
1634 | ||
1635 | nla_for_each_nested(attr, ieee[DCB_ATTR_IEEE_APP_TABLE], rem) { | |
1636 | struct dcb_app *app_data; | |
1637 | if (nla_type(attr) != DCB_ATTR_IEEE_APP) | |
1638 | continue; | |
1639 | app_data = nla_data(attr); | |
1640 | if (ops->ieee_setapp) | |
1641 | err = ops->ieee_setapp(netdev, app_data); | |
1642 | else | |
b6db2174 | 1643 | err = dcb_ieee_setapp(netdev, app_data); |
314b4778 JF |
1644 | if (err) |
1645 | goto err; | |
1646 | } | |
1647 | } | |
1648 | ||
1649 | err: | |
1650 | dcbnl_reply(err, RTM_SETDCB, DCB_CMD_IEEE_SET, DCB_ATTR_IEEE, | |
1651 | pid, seq, flags); | |
5b7f7626 | 1652 | dcbnl_ieee_notify(netdev, RTM_SETDCB, DCB_CMD_IEEE_SET, seq, 0); |
314b4778 JF |
1653 | return err; |
1654 | } | |
1655 | ||
1656 | static int dcbnl_ieee_get(struct net_device *netdev, struct nlattr **tb, | |
1657 | u32 pid, u32 seq, u16 flags) | |
1658 | { | |
1659 | struct net *net = dev_net(netdev); | |
1660 | struct sk_buff *skb; | |
1661 | struct nlmsghdr *nlh; | |
1662 | struct dcbmsg *dcb; | |
1663 | const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops; | |
1664 | int err; | |
1665 | ||
1666 | if (!ops) | |
1667 | return -EOPNOTSUPP; | |
1668 | ||
1669 | skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
1670 | if (!skb) | |
1671 | return -ENOBUFS; | |
1672 | ||
1673 | nlh = nlmsg_put(skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); | |
1674 | if (nlh == NULL) { | |
4d054f2f | 1675 | nlmsg_free(skb); |
314b4778 JF |
1676 | return -EMSGSIZE; |
1677 | } | |
1678 | ||
1679 | dcb = NLMSG_DATA(nlh); | |
1680 | dcb->dcb_family = AF_UNSPEC; | |
1681 | dcb->cmd = DCB_CMD_IEEE_GET; | |
1682 | ||
1683 | err = dcbnl_ieee_fill(skb, netdev); | |
1684 | ||
1685 | if (err < 0) { | |
1686 | nlmsg_cancel(skb, nlh); | |
1687 | kfree_skb(skb); | |
1688 | } else { | |
1689 | nlmsg_end(skb, nlh); | |
1690 | err = rtnl_unicast(skb, net, pid); | |
1691 | } | |
1692 | ||
1693 | return err; | |
1694 | } | |
f9ae7e4b JF |
1695 | |
1696 | static int dcbnl_ieee_del(struct net_device *netdev, struct nlattr **tb, | |
1697 | u32 pid, u32 seq, u16 flags) | |
1698 | { | |
1699 | const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops; | |
1700 | struct nlattr *ieee[DCB_ATTR_IEEE_MAX + 1]; | |
1701 | int err = -EOPNOTSUPP; | |
1702 | ||
1703 | if (!ops) | |
1704 | return -EOPNOTSUPP; | |
1705 | ||
1706 | if (!tb[DCB_ATTR_IEEE]) | |
1707 | return -EINVAL; | |
1708 | ||
1709 | err = nla_parse_nested(ieee, DCB_ATTR_IEEE_MAX, | |
1710 | tb[DCB_ATTR_IEEE], dcbnl_ieee_policy); | |
1711 | if (err) | |
1712 | return err; | |
1713 | ||
1714 | if (ieee[DCB_ATTR_IEEE_APP_TABLE]) { | |
1715 | struct nlattr *attr; | |
1716 | int rem; | |
1717 | ||
1718 | nla_for_each_nested(attr, ieee[DCB_ATTR_IEEE_APP_TABLE], rem) { | |
1719 | struct dcb_app *app_data; | |
1720 | ||
1721 | if (nla_type(attr) != DCB_ATTR_IEEE_APP) | |
1722 | continue; | |
1723 | app_data = nla_data(attr); | |
1724 | if (ops->ieee_delapp) | |
1725 | err = ops->ieee_delapp(netdev, app_data); | |
1726 | else | |
1727 | err = dcb_ieee_delapp(netdev, app_data); | |
1728 | if (err) | |
1729 | goto err; | |
1730 | } | |
1731 | } | |
1732 | ||
1733 | err: | |
1734 | dcbnl_reply(err, RTM_SETDCB, DCB_CMD_IEEE_DEL, DCB_ATTR_IEEE, | |
1735 | pid, seq, flags); | |
5b7f7626 | 1736 | dcbnl_ieee_notify(netdev, RTM_SETDCB, DCB_CMD_IEEE_DEL, seq, 0); |
f9ae7e4b JF |
1737 | return err; |
1738 | } | |
1739 | ||
1740 | ||
6241b625 SR |
1741 | /* DCBX configuration */ |
1742 | static int dcbnl_getdcbx(struct net_device *netdev, struct nlattr **tb, | |
1743 | u32 pid, u32 seq, u16 flags) | |
1744 | { | |
7f891cf1 | 1745 | int ret; |
6241b625 SR |
1746 | |
1747 | if (!netdev->dcbnl_ops->getdcbx) | |
7f891cf1 | 1748 | return -EOPNOTSUPP; |
6241b625 SR |
1749 | |
1750 | ret = dcbnl_reply(netdev->dcbnl_ops->getdcbx(netdev), RTM_GETDCB, | |
1751 | DCB_CMD_GDCBX, DCB_ATTR_DCBX, pid, seq, flags); | |
1752 | ||
1753 | return ret; | |
1754 | } | |
1755 | ||
1756 | static int dcbnl_setdcbx(struct net_device *netdev, struct nlattr **tb, | |
1757 | u32 pid, u32 seq, u16 flags) | |
1758 | { | |
7f891cf1 | 1759 | int ret; |
6241b625 SR |
1760 | u8 value; |
1761 | ||
7f891cf1 SR |
1762 | if (!netdev->dcbnl_ops->setdcbx) |
1763 | return -EOPNOTSUPP; | |
1764 | ||
1765 | if (!tb[DCB_ATTR_DCBX]) | |
1766 | return -EINVAL; | |
6241b625 SR |
1767 | |
1768 | value = nla_get_u8(tb[DCB_ATTR_DCBX]); | |
1769 | ||
1770 | ret = dcbnl_reply(netdev->dcbnl_ops->setdcbx(netdev, value), | |
1771 | RTM_SETDCB, DCB_CMD_SDCBX, DCB_ATTR_DCBX, | |
1772 | pid, seq, flags); | |
1773 | ||
1774 | return ret; | |
1775 | } | |
1776 | ||
ea45fe4e SR |
1777 | static int dcbnl_getfeatcfg(struct net_device *netdev, struct nlattr **tb, |
1778 | u32 pid, u32 seq, u16 flags) | |
1779 | { | |
1780 | struct sk_buff *dcbnl_skb; | |
1781 | struct nlmsghdr *nlh; | |
1782 | struct dcbmsg *dcb; | |
1783 | struct nlattr *data[DCB_FEATCFG_ATTR_MAX + 1], *nest; | |
1784 | u8 value; | |
7f891cf1 | 1785 | int ret, i; |
ea45fe4e SR |
1786 | int getall = 0; |
1787 | ||
7f891cf1 SR |
1788 | if (!netdev->dcbnl_ops->getfeatcfg) |
1789 | return -EOPNOTSUPP; | |
1790 | ||
1791 | if (!tb[DCB_ATTR_FEATCFG]) | |
1792 | return -EINVAL; | |
ea45fe4e SR |
1793 | |
1794 | ret = nla_parse_nested(data, DCB_FEATCFG_ATTR_MAX, tb[DCB_ATTR_FEATCFG], | |
1795 | dcbnl_featcfg_nest); | |
7f891cf1 | 1796 | if (ret) |
ea45fe4e | 1797 | goto err_out; |
ea45fe4e SR |
1798 | |
1799 | dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
1800 | if (!dcbnl_skb) { | |
7f891cf1 | 1801 | ret = -ENOBUFS; |
ea45fe4e SR |
1802 | goto err_out; |
1803 | } | |
1804 | ||
1805 | nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); | |
1806 | ||
1807 | dcb = NLMSG_DATA(nlh); | |
1808 | dcb->dcb_family = AF_UNSPEC; | |
1809 | dcb->cmd = DCB_CMD_GFEATCFG; | |
1810 | ||
1811 | nest = nla_nest_start(dcbnl_skb, DCB_ATTR_FEATCFG); | |
1812 | if (!nest) { | |
7f891cf1 SR |
1813 | ret = -EMSGSIZE; |
1814 | goto nla_put_failure; | |
ea45fe4e SR |
1815 | } |
1816 | ||
1817 | if (data[DCB_FEATCFG_ATTR_ALL]) | |
1818 | getall = 1; | |
1819 | ||
1820 | for (i = DCB_FEATCFG_ATTR_ALL+1; i <= DCB_FEATCFG_ATTR_MAX; i++) { | |
1821 | if (!getall && !data[i]) | |
1822 | continue; | |
1823 | ||
1824 | ret = netdev->dcbnl_ops->getfeatcfg(netdev, i, &value); | |
7f891cf1 | 1825 | if (!ret) |
ea45fe4e SR |
1826 | ret = nla_put_u8(dcbnl_skb, i, value); |
1827 | ||
7f891cf1 SR |
1828 | if (ret) { |
1829 | nla_nest_cancel(dcbnl_skb, nest); | |
1830 | goto nla_put_failure; | |
1831 | } | |
ea45fe4e SR |
1832 | } |
1833 | nla_nest_end(dcbnl_skb, nest); | |
1834 | ||
1835 | nlmsg_end(dcbnl_skb, nlh); | |
1836 | ||
7f891cf1 SR |
1837 | return rtnl_unicast(dcbnl_skb, &init_net, pid); |
1838 | nla_put_failure: | |
1839 | nlmsg_cancel(dcbnl_skb, nlh); | |
ea45fe4e | 1840 | nlmsg_failure: |
ea45fe4e SR |
1841 | kfree_skb(dcbnl_skb); |
1842 | err_out: | |
1843 | return ret; | |
1844 | } | |
1845 | ||
1846 | static int dcbnl_setfeatcfg(struct net_device *netdev, struct nlattr **tb, | |
1847 | u32 pid, u32 seq, u16 flags) | |
1848 | { | |
1849 | struct nlattr *data[DCB_FEATCFG_ATTR_MAX + 1]; | |
7f891cf1 | 1850 | int ret, i; |
ea45fe4e | 1851 | u8 value; |
ea45fe4e | 1852 | |
7f891cf1 SR |
1853 | if (!netdev->dcbnl_ops->setfeatcfg) |
1854 | return -ENOTSUPP; | |
1855 | ||
1856 | if (!tb[DCB_ATTR_FEATCFG]) | |
1857 | return -EINVAL; | |
ea45fe4e SR |
1858 | |
1859 | ret = nla_parse_nested(data, DCB_FEATCFG_ATTR_MAX, tb[DCB_ATTR_FEATCFG], | |
1860 | dcbnl_featcfg_nest); | |
1861 | ||
7f891cf1 | 1862 | if (ret) |
ea45fe4e | 1863 | goto err; |
ea45fe4e SR |
1864 | |
1865 | for (i = DCB_FEATCFG_ATTR_ALL+1; i <= DCB_FEATCFG_ATTR_MAX; i++) { | |
1866 | if (data[i] == NULL) | |
1867 | continue; | |
1868 | ||
1869 | value = nla_get_u8(data[i]); | |
1870 | ||
1871 | ret = netdev->dcbnl_ops->setfeatcfg(netdev, i, value); | |
1872 | ||
1873 | if (ret) | |
7f891cf1 | 1874 | goto err; |
ea45fe4e | 1875 | } |
ea45fe4e | 1876 | err: |
7f891cf1 SR |
1877 | dcbnl_reply(ret, RTM_SETDCB, DCB_CMD_SFEATCFG, DCB_ATTR_FEATCFG, |
1878 | pid, seq, flags); | |
1879 | ||
ea45fe4e SR |
1880 | return ret; |
1881 | } | |
1882 | ||
dc6ed1df SR |
1883 | /* Handle CEE DCBX GET commands. */ |
1884 | static int dcbnl_cee_get(struct net_device *netdev, struct nlattr **tb, | |
1885 | u32 pid, u32 seq, u16 flags) | |
1886 | { | |
5b7f7626 | 1887 | struct net *net = dev_net(netdev); |
dc6ed1df SR |
1888 | struct sk_buff *skb; |
1889 | struct nlmsghdr *nlh; | |
1890 | struct dcbmsg *dcb; | |
dc6ed1df | 1891 | const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops; |
5b7f7626 | 1892 | int err; |
dc6ed1df SR |
1893 | |
1894 | if (!ops) | |
1895 | return -EOPNOTSUPP; | |
1896 | ||
1897 | skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
1898 | if (!skb) | |
1899 | return -ENOBUFS; | |
1900 | ||
5b7f7626 SR |
1901 | nlh = nlmsg_put(skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); |
1902 | if (nlh == NULL) { | |
1903 | nlmsg_free(skb); | |
1904 | return -EMSGSIZE; | |
1905 | } | |
dc6ed1df SR |
1906 | |
1907 | dcb = NLMSG_DATA(nlh); | |
1908 | dcb->dcb_family = AF_UNSPEC; | |
1909 | dcb->cmd = DCB_CMD_CEE_GET; | |
1910 | ||
5b7f7626 | 1911 | err = dcbnl_cee_fill(skb, netdev); |
37cf4d1a | 1912 | |
5b7f7626 SR |
1913 | if (err < 0) { |
1914 | nlmsg_cancel(skb, nlh); | |
1915 | nlmsg_free(skb); | |
1916 | } else { | |
1917 | nlmsg_end(skb, nlh); | |
1918 | err = rtnl_unicast(skb, net, pid); | |
37cf4d1a | 1919 | } |
37cf4d1a | 1920 | return err; |
dc6ed1df SR |
1921 | } |
1922 | ||
2f90b865 AD |
1923 | static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) |
1924 | { | |
1925 | struct net *net = sock_net(skb->sk); | |
1926 | struct net_device *netdev; | |
1927 | struct dcbmsg *dcb = (struct dcbmsg *)NLMSG_DATA(nlh); | |
1928 | struct nlattr *tb[DCB_ATTR_MAX + 1]; | |
1929 | u32 pid = skb ? NETLINK_CB(skb).pid : 0; | |
1930 | int ret = -EINVAL; | |
1931 | ||
09ad9bc7 | 1932 | if (!net_eq(net, &init_net)) |
2f90b865 AD |
1933 | return -EINVAL; |
1934 | ||
1935 | ret = nlmsg_parse(nlh, sizeof(*dcb), tb, DCB_ATTR_MAX, | |
1936 | dcbnl_rtnl_policy); | |
1937 | if (ret < 0) | |
1938 | return ret; | |
1939 | ||
1940 | if (!tb[DCB_ATTR_IFNAME]) | |
1941 | return -EINVAL; | |
1942 | ||
1943 | netdev = dev_get_by_name(&init_net, nla_data(tb[DCB_ATTR_IFNAME])); | |
1944 | if (!netdev) | |
1945 | return -EINVAL; | |
1946 | ||
1947 | if (!netdev->dcbnl_ops) | |
1948 | goto errout; | |
1949 | ||
1950 | switch (dcb->cmd) { | |
1951 | case DCB_CMD_GSTATE: | |
1952 | ret = dcbnl_getstate(netdev, tb, pid, nlh->nlmsg_seq, | |
1953 | nlh->nlmsg_flags); | |
1954 | goto out; | |
1955 | case DCB_CMD_PFC_GCFG: | |
1956 | ret = dcbnl_getpfccfg(netdev, tb, pid, nlh->nlmsg_seq, | |
1957 | nlh->nlmsg_flags); | |
1958 | goto out; | |
1959 | case DCB_CMD_GPERM_HWADDR: | |
1960 | ret = dcbnl_getperm_hwaddr(netdev, tb, pid, nlh->nlmsg_seq, | |
1961 | nlh->nlmsg_flags); | |
1962 | goto out; | |
1963 | case DCB_CMD_PGTX_GCFG: | |
1964 | ret = dcbnl_pgtx_getcfg(netdev, tb, pid, nlh->nlmsg_seq, | |
1965 | nlh->nlmsg_flags); | |
1966 | goto out; | |
1967 | case DCB_CMD_PGRX_GCFG: | |
1968 | ret = dcbnl_pgrx_getcfg(netdev, tb, pid, nlh->nlmsg_seq, | |
1969 | nlh->nlmsg_flags); | |
1970 | goto out; | |
859ee3c4 AD |
1971 | case DCB_CMD_BCN_GCFG: |
1972 | ret = dcbnl_bcn_getcfg(netdev, tb, pid, nlh->nlmsg_seq, | |
1973 | nlh->nlmsg_flags); | |
1974 | goto out; | |
2f90b865 AD |
1975 | case DCB_CMD_SSTATE: |
1976 | ret = dcbnl_setstate(netdev, tb, pid, nlh->nlmsg_seq, | |
1977 | nlh->nlmsg_flags); | |
1978 | goto out; | |
1979 | case DCB_CMD_PFC_SCFG: | |
1980 | ret = dcbnl_setpfccfg(netdev, tb, pid, nlh->nlmsg_seq, | |
1981 | nlh->nlmsg_flags); | |
1982 | goto out; | |
1983 | ||
1984 | case DCB_CMD_SET_ALL: | |
1985 | ret = dcbnl_setall(netdev, tb, pid, nlh->nlmsg_seq, | |
1986 | nlh->nlmsg_flags); | |
1987 | goto out; | |
1988 | case DCB_CMD_PGTX_SCFG: | |
1989 | ret = dcbnl_pgtx_setcfg(netdev, tb, pid, nlh->nlmsg_seq, | |
1990 | nlh->nlmsg_flags); | |
1991 | goto out; | |
1992 | case DCB_CMD_PGRX_SCFG: | |
1993 | ret = dcbnl_pgrx_setcfg(netdev, tb, pid, nlh->nlmsg_seq, | |
1994 | nlh->nlmsg_flags); | |
1995 | goto out; | |
46132188 AD |
1996 | case DCB_CMD_GCAP: |
1997 | ret = dcbnl_getcap(netdev, tb, pid, nlh->nlmsg_seq, | |
1998 | nlh->nlmsg_flags); | |
1999 | goto out; | |
33dbabc4 AD |
2000 | case DCB_CMD_GNUMTCS: |
2001 | ret = dcbnl_getnumtcs(netdev, tb, pid, nlh->nlmsg_seq, | |
2002 | nlh->nlmsg_flags); | |
2003 | goto out; | |
2004 | case DCB_CMD_SNUMTCS: | |
2005 | ret = dcbnl_setnumtcs(netdev, tb, pid, nlh->nlmsg_seq, | |
2006 | nlh->nlmsg_flags); | |
2007 | goto out; | |
0eb3aa9b AD |
2008 | case DCB_CMD_PFC_GSTATE: |
2009 | ret = dcbnl_getpfcstate(netdev, tb, pid, nlh->nlmsg_seq, | |
2010 | nlh->nlmsg_flags); | |
2011 | goto out; | |
2012 | case DCB_CMD_PFC_SSTATE: | |
2013 | ret = dcbnl_setpfcstate(netdev, tb, pid, nlh->nlmsg_seq, | |
2014 | nlh->nlmsg_flags); | |
2015 | goto out; | |
859ee3c4 AD |
2016 | case DCB_CMD_BCN_SCFG: |
2017 | ret = dcbnl_bcn_setcfg(netdev, tb, pid, nlh->nlmsg_seq, | |
2018 | nlh->nlmsg_flags); | |
2019 | goto out; | |
57949686 YZ |
2020 | case DCB_CMD_GAPP: |
2021 | ret = dcbnl_getapp(netdev, tb, pid, nlh->nlmsg_seq, | |
2022 | nlh->nlmsg_flags); | |
2023 | goto out; | |
2024 | case DCB_CMD_SAPP: | |
2025 | ret = dcbnl_setapp(netdev, tb, pid, nlh->nlmsg_seq, | |
2026 | nlh->nlmsg_flags); | |
2027 | goto out; | |
3e29027a JF |
2028 | case DCB_CMD_IEEE_SET: |
2029 | ret = dcbnl_ieee_set(netdev, tb, pid, nlh->nlmsg_seq, | |
f9ae7e4b | 2030 | nlh->nlmsg_flags); |
3e29027a JF |
2031 | goto out; |
2032 | case DCB_CMD_IEEE_GET: | |
2033 | ret = dcbnl_ieee_get(netdev, tb, pid, nlh->nlmsg_seq, | |
f9ae7e4b JF |
2034 | nlh->nlmsg_flags); |
2035 | goto out; | |
2036 | case DCB_CMD_IEEE_DEL: | |
2037 | ret = dcbnl_ieee_del(netdev, tb, pid, nlh->nlmsg_seq, | |
2038 | nlh->nlmsg_flags); | |
3e29027a | 2039 | goto out; |
6241b625 SR |
2040 | case DCB_CMD_GDCBX: |
2041 | ret = dcbnl_getdcbx(netdev, tb, pid, nlh->nlmsg_seq, | |
2042 | nlh->nlmsg_flags); | |
2043 | goto out; | |
2044 | case DCB_CMD_SDCBX: | |
2045 | ret = dcbnl_setdcbx(netdev, tb, pid, nlh->nlmsg_seq, | |
2046 | nlh->nlmsg_flags); | |
2047 | goto out; | |
ea45fe4e SR |
2048 | case DCB_CMD_GFEATCFG: |
2049 | ret = dcbnl_getfeatcfg(netdev, tb, pid, nlh->nlmsg_seq, | |
2050 | nlh->nlmsg_flags); | |
2051 | goto out; | |
2052 | case DCB_CMD_SFEATCFG: | |
2053 | ret = dcbnl_setfeatcfg(netdev, tb, pid, nlh->nlmsg_seq, | |
2054 | nlh->nlmsg_flags); | |
2055 | goto out; | |
dc6ed1df SR |
2056 | case DCB_CMD_CEE_GET: |
2057 | ret = dcbnl_cee_get(netdev, tb, pid, nlh->nlmsg_seq, | |
2058 | nlh->nlmsg_flags); | |
2059 | goto out; | |
2f90b865 AD |
2060 | default: |
2061 | goto errout; | |
2062 | } | |
2063 | errout: | |
2064 | ret = -EINVAL; | |
2065 | out: | |
2066 | dev_put(netdev); | |
2067 | return ret; | |
2068 | } | |
2069 | ||
9ab933ab JF |
2070 | /** |
2071 | * dcb_getapp - retrieve the DCBX application user priority | |
2072 | * | |
2073 | * On success returns a non-zero 802.1p user priority bitmap | |
2074 | * otherwise returns 0 as the invalid user priority bitmap to | |
2075 | * indicate an error. | |
2076 | */ | |
2077 | u8 dcb_getapp(struct net_device *dev, struct dcb_app *app) | |
2078 | { | |
2079 | struct dcb_app_type *itr; | |
2080 | u8 prio = 0; | |
2081 | ||
2082 | spin_lock(&dcb_lock); | |
2083 | list_for_each_entry(itr, &dcb_app_list, list) { | |
2084 | if (itr->app.selector == app->selector && | |
2085 | itr->app.protocol == app->protocol && | |
e290ed81 | 2086 | itr->ifindex == dev->ifindex) { |
9ab933ab JF |
2087 | prio = itr->app.priority; |
2088 | break; | |
2089 | } | |
2090 | } | |
2091 | spin_unlock(&dcb_lock); | |
2092 | ||
2093 | return prio; | |
2094 | } | |
2095 | EXPORT_SYMBOL(dcb_getapp); | |
2096 | ||
2097 | /** | |
b6db2174 | 2098 | * dcb_setapp - add CEE dcb application data to app list |
9ab933ab | 2099 | * |
b6db2174 JF |
2100 | * Priority 0 is an invalid priority in CEE spec. This routine |
2101 | * removes applications from the app list if the priority is | |
2102 | * set to zero. | |
9ab933ab | 2103 | */ |
ab6baf98 | 2104 | int dcb_setapp(struct net_device *dev, struct dcb_app *new) |
9ab933ab JF |
2105 | { |
2106 | struct dcb_app_type *itr; | |
7ec79270 JF |
2107 | struct dcb_app_type event; |
2108 | ||
e290ed81 | 2109 | event.ifindex = dev->ifindex; |
7ec79270 | 2110 | memcpy(&event.app, new, sizeof(event.app)); |
6bd0e1cb JF |
2111 | if (dev->dcbnl_ops->getdcbx) |
2112 | event.dcbx = dev->dcbnl_ops->getdcbx(dev); | |
9ab933ab JF |
2113 | |
2114 | spin_lock(&dcb_lock); | |
2115 | /* Search for existing match and replace */ | |
2116 | list_for_each_entry(itr, &dcb_app_list, list) { | |
2117 | if (itr->app.selector == new->selector && | |
2118 | itr->app.protocol == new->protocol && | |
e290ed81 | 2119 | itr->ifindex == dev->ifindex) { |
9ab933ab JF |
2120 | if (new->priority) |
2121 | itr->app.priority = new->priority; | |
2122 | else { | |
2123 | list_del(&itr->list); | |
2124 | kfree(itr); | |
2125 | } | |
2126 | goto out; | |
2127 | } | |
2128 | } | |
2129 | /* App type does not exist add new application type */ | |
2130 | if (new->priority) { | |
2131 | struct dcb_app_type *entry; | |
2132 | entry = kmalloc(sizeof(struct dcb_app_type), GFP_ATOMIC); | |
2133 | if (!entry) { | |
2134 | spin_unlock(&dcb_lock); | |
2135 | return -ENOMEM; | |
2136 | } | |
2137 | ||
2138 | memcpy(&entry->app, new, sizeof(*new)); | |
e290ed81 | 2139 | entry->ifindex = dev->ifindex; |
9ab933ab JF |
2140 | list_add(&entry->list, &dcb_app_list); |
2141 | } | |
2142 | out: | |
2143 | spin_unlock(&dcb_lock); | |
7ec79270 | 2144 | call_dcbevent_notifiers(DCB_APP_EVENT, &event); |
9ab933ab JF |
2145 | return 0; |
2146 | } | |
2147 | EXPORT_SYMBOL(dcb_setapp); | |
2148 | ||
a364c8cf JF |
2149 | /** |
2150 | * dcb_ieee_getapp_mask - retrieve the IEEE DCB application priority | |
2151 | * | |
2152 | * Helper routine which on success returns a non-zero 802.1Qaz user | |
2153 | * priority bitmap otherwise returns 0 to indicate the dcb_app was | |
2154 | * not found in APP list. | |
2155 | */ | |
2156 | u8 dcb_ieee_getapp_mask(struct net_device *dev, struct dcb_app *app) | |
2157 | { | |
2158 | struct dcb_app_type *itr; | |
2159 | u8 prio = 0; | |
2160 | ||
2161 | spin_lock(&dcb_lock); | |
2162 | list_for_each_entry(itr, &dcb_app_list, list) { | |
2163 | if (itr->app.selector == app->selector && | |
2164 | itr->app.protocol == app->protocol && | |
e290ed81 | 2165 | itr->ifindex == dev->ifindex) { |
a364c8cf JF |
2166 | prio |= 1 << itr->app.priority; |
2167 | } | |
2168 | } | |
2169 | spin_unlock(&dcb_lock); | |
2170 | ||
2171 | return prio; | |
2172 | } | |
2173 | EXPORT_SYMBOL(dcb_ieee_getapp_mask); | |
2174 | ||
b6db2174 JF |
2175 | /** |
2176 | * dcb_ieee_setapp - add IEEE dcb application data to app list | |
2177 | * | |
2178 | * This adds Application data to the list. Multiple application | |
2179 | * entries may exists for the same selector and protocol as long | |
2180 | * as the priorities are different. | |
2181 | */ | |
2182 | int dcb_ieee_setapp(struct net_device *dev, struct dcb_app *new) | |
2183 | { | |
2184 | struct dcb_app_type *itr, *entry; | |
2185 | struct dcb_app_type event; | |
2186 | int err = 0; | |
2187 | ||
e290ed81 | 2188 | event.ifindex = dev->ifindex; |
b6db2174 | 2189 | memcpy(&event.app, new, sizeof(event.app)); |
6bd0e1cb JF |
2190 | if (dev->dcbnl_ops->getdcbx) |
2191 | event.dcbx = dev->dcbnl_ops->getdcbx(dev); | |
b6db2174 JF |
2192 | |
2193 | spin_lock(&dcb_lock); | |
2194 | /* Search for existing match and abort if found */ | |
2195 | list_for_each_entry(itr, &dcb_app_list, list) { | |
2196 | if (itr->app.selector == new->selector && | |
2197 | itr->app.protocol == new->protocol && | |
2198 | itr->app.priority == new->priority && | |
e290ed81 | 2199 | itr->ifindex == dev->ifindex) { |
b6db2174 JF |
2200 | err = -EEXIST; |
2201 | goto out; | |
2202 | } | |
2203 | } | |
2204 | ||
2205 | /* App entry does not exist add new entry */ | |
2206 | entry = kmalloc(sizeof(struct dcb_app_type), GFP_ATOMIC); | |
2207 | if (!entry) { | |
2208 | err = -ENOMEM; | |
2209 | goto out; | |
2210 | } | |
2211 | ||
2212 | memcpy(&entry->app, new, sizeof(*new)); | |
e290ed81 | 2213 | entry->ifindex = dev->ifindex; |
b6db2174 JF |
2214 | list_add(&entry->list, &dcb_app_list); |
2215 | out: | |
2216 | spin_unlock(&dcb_lock); | |
2217 | if (!err) | |
2218 | call_dcbevent_notifiers(DCB_APP_EVENT, &event); | |
2219 | return err; | |
2220 | } | |
2221 | EXPORT_SYMBOL(dcb_ieee_setapp); | |
2222 | ||
f9ae7e4b JF |
2223 | /** |
2224 | * dcb_ieee_delapp - delete IEEE dcb application data from list | |
2225 | * | |
2226 | * This removes a matching APP data from the APP list | |
2227 | */ | |
2228 | int dcb_ieee_delapp(struct net_device *dev, struct dcb_app *del) | |
2229 | { | |
2230 | struct dcb_app_type *itr; | |
2231 | struct dcb_app_type event; | |
2232 | int err = -ENOENT; | |
2233 | ||
e290ed81 | 2234 | event.ifindex = dev->ifindex; |
f9ae7e4b | 2235 | memcpy(&event.app, del, sizeof(event.app)); |
6bd0e1cb JF |
2236 | if (dev->dcbnl_ops->getdcbx) |
2237 | event.dcbx = dev->dcbnl_ops->getdcbx(dev); | |
f9ae7e4b JF |
2238 | |
2239 | spin_lock(&dcb_lock); | |
2240 | /* Search for existing match and remove it. */ | |
2241 | list_for_each_entry(itr, &dcb_app_list, list) { | |
2242 | if (itr->app.selector == del->selector && | |
2243 | itr->app.protocol == del->protocol && | |
2244 | itr->app.priority == del->priority && | |
e290ed81 | 2245 | itr->ifindex == dev->ifindex) { |
f9ae7e4b JF |
2246 | list_del(&itr->list); |
2247 | kfree(itr); | |
2248 | err = 0; | |
2249 | goto out; | |
2250 | } | |
2251 | } | |
2252 | ||
2253 | out: | |
2254 | spin_unlock(&dcb_lock); | |
2255 | if (!err) | |
2256 | call_dcbevent_notifiers(DCB_APP_EVENT, &event); | |
2257 | return err; | |
2258 | } | |
2259 | EXPORT_SYMBOL(dcb_ieee_delapp); | |
2260 | ||
7c14c3f1 | 2261 | static void dcb_flushapp(void) |
9ab933ab JF |
2262 | { |
2263 | struct dcb_app_type *app; | |
2a8fe003 | 2264 | struct dcb_app_type *tmp; |
9ab933ab JF |
2265 | |
2266 | spin_lock(&dcb_lock); | |
2a8fe003 | 2267 | list_for_each_entry_safe(app, tmp, &dcb_app_list, list) { |
9ab933ab JF |
2268 | list_del(&app->list); |
2269 | kfree(app); | |
2270 | } | |
2271 | spin_unlock(&dcb_lock); | |
2272 | } | |
2273 | ||
2f90b865 AD |
2274 | static int __init dcbnl_init(void) |
2275 | { | |
9ab933ab JF |
2276 | INIT_LIST_HEAD(&dcb_app_list); |
2277 | ||
c7ac8679 GR |
2278 | rtnl_register(PF_UNSPEC, RTM_GETDCB, dcb_doit, NULL, NULL); |
2279 | rtnl_register(PF_UNSPEC, RTM_SETDCB, dcb_doit, NULL, NULL); | |
2f90b865 AD |
2280 | |
2281 | return 0; | |
2282 | } | |
2283 | module_init(dcbnl_init); | |
2284 | ||
2285 | static void __exit dcbnl_exit(void) | |
2286 | { | |
2287 | rtnl_unregister(PF_UNSPEC, RTM_GETDCB); | |
2288 | rtnl_unregister(PF_UNSPEC, RTM_SETDCB); | |
9ab933ab | 2289 | dcb_flushapp(); |
2f90b865 AD |
2290 | } |
2291 | module_exit(dcbnl_exit); |