]>
Commit | Line | Data |
---|---|---|
2f90b865 AD |
1 | /* |
2 | * Copyright (c) 2008, Intel Corporation. | |
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 <[email protected]> | |
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> | |
26 | #include <linux/rtnetlink.h> | |
27 | #include <net/sock.h> | |
28 | ||
29 | /** | |
30 | * Data Center Bridging (DCB) is a collection of Ethernet enhancements | |
31 | * intended to allow network traffic with differing requirements | |
32 | * (highly reliable, no drops vs. best effort vs. low latency) to operate | |
33 | * and co-exist on Ethernet. Current DCB features are: | |
34 | * | |
35 | * Enhanced Transmission Selection (aka Priority Grouping [PG]) - provides a | |
36 | * framework for assigning bandwidth guarantees to traffic classes. | |
37 | * | |
38 | * Priority-based Flow Control (PFC) - provides a flow control mechanism which | |
39 | * can work independently for each 802.1p priority. | |
40 | * | |
41 | * Congestion Notification - provides a mechanism for end-to-end congestion | |
42 | * control for protocols which do not have built-in congestion management. | |
43 | * | |
44 | * More information about the emerging standards for these Ethernet features | |
45 | * can be found at: http://www.ieee802.org/1/pages/dcbridges.html | |
46 | * | |
47 | * This file implements an rtnetlink interface to allow configuration of DCB | |
48 | * features for capable devices. | |
49 | */ | |
50 | ||
51 | MODULE_AUTHOR("Lucy Liu, <[email protected]>"); | |
7a6b6f51 | 52 | MODULE_DESCRIPTION("Data Center Bridging netlink interface"); |
2f90b865 AD |
53 | MODULE_LICENSE("GPL"); |
54 | ||
55 | /**************** DCB attribute policies *************************************/ | |
56 | ||
57 | /* DCB netlink attributes policy */ | |
b54452b0 | 58 | static const struct nla_policy dcbnl_rtnl_policy[DCB_ATTR_MAX + 1] = { |
859ee3c4 AD |
59 | [DCB_ATTR_IFNAME] = {.type = NLA_NUL_STRING, .len = IFNAMSIZ - 1}, |
60 | [DCB_ATTR_STATE] = {.type = NLA_U8}, | |
61 | [DCB_ATTR_PFC_CFG] = {.type = NLA_NESTED}, | |
62 | [DCB_ATTR_PG_CFG] = {.type = NLA_NESTED}, | |
63 | [DCB_ATTR_SET_ALL] = {.type = NLA_U8}, | |
2f90b865 | 64 | [DCB_ATTR_PERM_HWADDR] = {.type = NLA_FLAG}, |
859ee3c4 AD |
65 | [DCB_ATTR_CAP] = {.type = NLA_NESTED}, |
66 | [DCB_ATTR_PFC_STATE] = {.type = NLA_U8}, | |
67 | [DCB_ATTR_BCN] = {.type = NLA_NESTED}, | |
6fa382af | 68 | [DCB_ATTR_APP] = {.type = NLA_NESTED}, |
2f90b865 AD |
69 | }; |
70 | ||
71 | /* DCB priority flow control to User Priority nested attributes */ | |
b54452b0 | 72 | static const struct nla_policy dcbnl_pfc_up_nest[DCB_PFC_UP_ATTR_MAX + 1] = { |
2f90b865 AD |
73 | [DCB_PFC_UP_ATTR_0] = {.type = NLA_U8}, |
74 | [DCB_PFC_UP_ATTR_1] = {.type = NLA_U8}, | |
75 | [DCB_PFC_UP_ATTR_2] = {.type = NLA_U8}, | |
76 | [DCB_PFC_UP_ATTR_3] = {.type = NLA_U8}, | |
77 | [DCB_PFC_UP_ATTR_4] = {.type = NLA_U8}, | |
78 | [DCB_PFC_UP_ATTR_5] = {.type = NLA_U8}, | |
79 | [DCB_PFC_UP_ATTR_6] = {.type = NLA_U8}, | |
80 | [DCB_PFC_UP_ATTR_7] = {.type = NLA_U8}, | |
81 | [DCB_PFC_UP_ATTR_ALL] = {.type = NLA_FLAG}, | |
82 | }; | |
83 | ||
84 | /* DCB priority grouping nested attributes */ | |
b54452b0 | 85 | static const struct nla_policy dcbnl_pg_nest[DCB_PG_ATTR_MAX + 1] = { |
2f90b865 AD |
86 | [DCB_PG_ATTR_TC_0] = {.type = NLA_NESTED}, |
87 | [DCB_PG_ATTR_TC_1] = {.type = NLA_NESTED}, | |
88 | [DCB_PG_ATTR_TC_2] = {.type = NLA_NESTED}, | |
89 | [DCB_PG_ATTR_TC_3] = {.type = NLA_NESTED}, | |
90 | [DCB_PG_ATTR_TC_4] = {.type = NLA_NESTED}, | |
91 | [DCB_PG_ATTR_TC_5] = {.type = NLA_NESTED}, | |
92 | [DCB_PG_ATTR_TC_6] = {.type = NLA_NESTED}, | |
93 | [DCB_PG_ATTR_TC_7] = {.type = NLA_NESTED}, | |
94 | [DCB_PG_ATTR_TC_ALL] = {.type = NLA_NESTED}, | |
95 | [DCB_PG_ATTR_BW_ID_0] = {.type = NLA_U8}, | |
96 | [DCB_PG_ATTR_BW_ID_1] = {.type = NLA_U8}, | |
97 | [DCB_PG_ATTR_BW_ID_2] = {.type = NLA_U8}, | |
98 | [DCB_PG_ATTR_BW_ID_3] = {.type = NLA_U8}, | |
99 | [DCB_PG_ATTR_BW_ID_4] = {.type = NLA_U8}, | |
100 | [DCB_PG_ATTR_BW_ID_5] = {.type = NLA_U8}, | |
101 | [DCB_PG_ATTR_BW_ID_6] = {.type = NLA_U8}, | |
102 | [DCB_PG_ATTR_BW_ID_7] = {.type = NLA_U8}, | |
103 | [DCB_PG_ATTR_BW_ID_ALL] = {.type = NLA_FLAG}, | |
104 | }; | |
105 | ||
106 | /* DCB traffic class nested attributes. */ | |
b54452b0 | 107 | static const struct nla_policy dcbnl_tc_param_nest[DCB_TC_ATTR_PARAM_MAX + 1] = { |
2f90b865 AD |
108 | [DCB_TC_ATTR_PARAM_PGID] = {.type = NLA_U8}, |
109 | [DCB_TC_ATTR_PARAM_UP_MAPPING] = {.type = NLA_U8}, | |
110 | [DCB_TC_ATTR_PARAM_STRICT_PRIO] = {.type = NLA_U8}, | |
111 | [DCB_TC_ATTR_PARAM_BW_PCT] = {.type = NLA_U8}, | |
112 | [DCB_TC_ATTR_PARAM_ALL] = {.type = NLA_FLAG}, | |
113 | }; | |
114 | ||
46132188 | 115 | /* DCB capabilities nested attributes. */ |
b54452b0 | 116 | static const struct nla_policy dcbnl_cap_nest[DCB_CAP_ATTR_MAX + 1] = { |
46132188 AD |
117 | [DCB_CAP_ATTR_ALL] = {.type = NLA_FLAG}, |
118 | [DCB_CAP_ATTR_PG] = {.type = NLA_U8}, | |
119 | [DCB_CAP_ATTR_PFC] = {.type = NLA_U8}, | |
120 | [DCB_CAP_ATTR_UP2TC] = {.type = NLA_U8}, | |
121 | [DCB_CAP_ATTR_PG_TCS] = {.type = NLA_U8}, | |
122 | [DCB_CAP_ATTR_PFC_TCS] = {.type = NLA_U8}, | |
123 | [DCB_CAP_ATTR_GSP] = {.type = NLA_U8}, | |
124 | [DCB_CAP_ATTR_BCN] = {.type = NLA_U8}, | |
125 | }; | |
2f90b865 | 126 | |
33dbabc4 | 127 | /* DCB capabilities nested attributes. */ |
b54452b0 | 128 | static const struct nla_policy dcbnl_numtcs_nest[DCB_NUMTCS_ATTR_MAX + 1] = { |
33dbabc4 AD |
129 | [DCB_NUMTCS_ATTR_ALL] = {.type = NLA_FLAG}, |
130 | [DCB_NUMTCS_ATTR_PG] = {.type = NLA_U8}, | |
131 | [DCB_NUMTCS_ATTR_PFC] = {.type = NLA_U8}, | |
132 | }; | |
133 | ||
859ee3c4 | 134 | /* DCB BCN nested attributes. */ |
b54452b0 | 135 | static const struct nla_policy dcbnl_bcn_nest[DCB_BCN_ATTR_MAX + 1] = { |
859ee3c4 AD |
136 | [DCB_BCN_ATTR_RP_0] = {.type = NLA_U8}, |
137 | [DCB_BCN_ATTR_RP_1] = {.type = NLA_U8}, | |
138 | [DCB_BCN_ATTR_RP_2] = {.type = NLA_U8}, | |
139 | [DCB_BCN_ATTR_RP_3] = {.type = NLA_U8}, | |
140 | [DCB_BCN_ATTR_RP_4] = {.type = NLA_U8}, | |
141 | [DCB_BCN_ATTR_RP_5] = {.type = NLA_U8}, | |
142 | [DCB_BCN_ATTR_RP_6] = {.type = NLA_U8}, | |
143 | [DCB_BCN_ATTR_RP_7] = {.type = NLA_U8}, | |
144 | [DCB_BCN_ATTR_RP_ALL] = {.type = NLA_FLAG}, | |
f4314e81 DS |
145 | [DCB_BCN_ATTR_BCNA_0] = {.type = NLA_U32}, |
146 | [DCB_BCN_ATTR_BCNA_1] = {.type = NLA_U32}, | |
859ee3c4 AD |
147 | [DCB_BCN_ATTR_ALPHA] = {.type = NLA_U32}, |
148 | [DCB_BCN_ATTR_BETA] = {.type = NLA_U32}, | |
149 | [DCB_BCN_ATTR_GD] = {.type = NLA_U32}, | |
150 | [DCB_BCN_ATTR_GI] = {.type = NLA_U32}, | |
151 | [DCB_BCN_ATTR_TMAX] = {.type = NLA_U32}, | |
152 | [DCB_BCN_ATTR_TD] = {.type = NLA_U32}, | |
153 | [DCB_BCN_ATTR_RMIN] = {.type = NLA_U32}, | |
154 | [DCB_BCN_ATTR_W] = {.type = NLA_U32}, | |
155 | [DCB_BCN_ATTR_RD] = {.type = NLA_U32}, | |
156 | [DCB_BCN_ATTR_RU] = {.type = NLA_U32}, | |
157 | [DCB_BCN_ATTR_WRTT] = {.type = NLA_U32}, | |
158 | [DCB_BCN_ATTR_RI] = {.type = NLA_U32}, | |
159 | [DCB_BCN_ATTR_C] = {.type = NLA_U32}, | |
160 | [DCB_BCN_ATTR_ALL] = {.type = NLA_FLAG}, | |
161 | }; | |
162 | ||
6fa382af | 163 | /* DCB APP nested attributes. */ |
b54452b0 | 164 | static const struct nla_policy dcbnl_app_nest[DCB_APP_ATTR_MAX + 1] = { |
6fa382af YZ |
165 | [DCB_APP_ATTR_IDTYPE] = {.type = NLA_U8}, |
166 | [DCB_APP_ATTR_ID] = {.type = NLA_U16}, | |
167 | [DCB_APP_ATTR_PRIORITY] = {.type = NLA_U8}, | |
168 | }; | |
169 | ||
2f90b865 AD |
170 | /* standard netlink reply call */ |
171 | static int dcbnl_reply(u8 value, u8 event, u8 cmd, u8 attr, u32 pid, | |
172 | u32 seq, u16 flags) | |
173 | { | |
174 | struct sk_buff *dcbnl_skb; | |
175 | struct dcbmsg *dcb; | |
176 | struct nlmsghdr *nlh; | |
177 | int ret = -EINVAL; | |
178 | ||
179 | dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
180 | if (!dcbnl_skb) | |
181 | return ret; | |
182 | ||
183 | nlh = NLMSG_NEW(dcbnl_skb, pid, seq, event, sizeof(*dcb), flags); | |
184 | ||
185 | dcb = NLMSG_DATA(nlh); | |
186 | dcb->dcb_family = AF_UNSPEC; | |
187 | dcb->cmd = cmd; | |
188 | dcb->dcb_pad = 0; | |
189 | ||
190 | ret = nla_put_u8(dcbnl_skb, attr, value); | |
191 | if (ret) | |
192 | goto err; | |
193 | ||
194 | /* end the message, assign the nlmsg_len. */ | |
195 | nlmsg_end(dcbnl_skb, nlh); | |
196 | ret = rtnl_unicast(dcbnl_skb, &init_net, pid); | |
197 | if (ret) | |
7eaf5077 | 198 | return -EINVAL; |
2f90b865 AD |
199 | |
200 | return 0; | |
201 | nlmsg_failure: | |
202 | err: | |
858eb711 | 203 | kfree_skb(dcbnl_skb); |
2f90b865 AD |
204 | return ret; |
205 | } | |
206 | ||
207 | static int dcbnl_getstate(struct net_device *netdev, struct nlattr **tb, | |
208 | u32 pid, u32 seq, u16 flags) | |
209 | { | |
210 | int ret = -EINVAL; | |
211 | ||
212 | /* if (!tb[DCB_ATTR_STATE] || !netdev->dcbnl_ops->getstate) */ | |
213 | if (!netdev->dcbnl_ops->getstate) | |
214 | return ret; | |
215 | ||
216 | ret = dcbnl_reply(netdev->dcbnl_ops->getstate(netdev), RTM_GETDCB, | |
217 | DCB_CMD_GSTATE, DCB_ATTR_STATE, pid, seq, flags); | |
218 | ||
219 | return ret; | |
220 | } | |
221 | ||
222 | static int dcbnl_getpfccfg(struct net_device *netdev, struct nlattr **tb, | |
223 | u32 pid, u32 seq, u16 flags) | |
224 | { | |
225 | struct sk_buff *dcbnl_skb; | |
226 | struct nlmsghdr *nlh; | |
227 | struct dcbmsg *dcb; | |
228 | struct nlattr *data[DCB_PFC_UP_ATTR_MAX + 1], *nest; | |
229 | u8 value; | |
230 | int ret = -EINVAL; | |
231 | int i; | |
232 | int getall = 0; | |
233 | ||
234 | if (!tb[DCB_ATTR_PFC_CFG] || !netdev->dcbnl_ops->getpfccfg) | |
235 | return ret; | |
236 | ||
237 | ret = nla_parse_nested(data, DCB_PFC_UP_ATTR_MAX, | |
238 | tb[DCB_ATTR_PFC_CFG], | |
239 | dcbnl_pfc_up_nest); | |
240 | if (ret) | |
241 | goto err_out; | |
242 | ||
243 | dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
244 | if (!dcbnl_skb) | |
245 | goto err_out; | |
246 | ||
247 | nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); | |
248 | ||
249 | dcb = NLMSG_DATA(nlh); | |
250 | dcb->dcb_family = AF_UNSPEC; | |
251 | dcb->cmd = DCB_CMD_PFC_GCFG; | |
252 | ||
253 | nest = nla_nest_start(dcbnl_skb, DCB_ATTR_PFC_CFG); | |
254 | if (!nest) | |
255 | goto err; | |
256 | ||
257 | if (data[DCB_PFC_UP_ATTR_ALL]) | |
258 | getall = 1; | |
259 | ||
260 | for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) { | |
261 | if (!getall && !data[i]) | |
262 | continue; | |
263 | ||
264 | netdev->dcbnl_ops->getpfccfg(netdev, i - DCB_PFC_UP_ATTR_0, | |
265 | &value); | |
266 | ret = nla_put_u8(dcbnl_skb, i, value); | |
267 | ||
268 | if (ret) { | |
269 | nla_nest_cancel(dcbnl_skb, nest); | |
270 | goto err; | |
271 | } | |
272 | } | |
273 | nla_nest_end(dcbnl_skb, nest); | |
274 | ||
275 | nlmsg_end(dcbnl_skb, nlh); | |
276 | ||
277 | ret = rtnl_unicast(dcbnl_skb, &init_net, pid); | |
278 | if (ret) | |
7eaf5077 | 279 | goto err_out; |
2f90b865 AD |
280 | |
281 | return 0; | |
282 | nlmsg_failure: | |
283 | err: | |
858eb711 | 284 | kfree_skb(dcbnl_skb); |
2f90b865 AD |
285 | err_out: |
286 | return -EINVAL; | |
287 | } | |
288 | ||
289 | static int dcbnl_getperm_hwaddr(struct net_device *netdev, struct nlattr **tb, | |
290 | u32 pid, u32 seq, u16 flags) | |
291 | { | |
292 | struct sk_buff *dcbnl_skb; | |
293 | struct nlmsghdr *nlh; | |
294 | struct dcbmsg *dcb; | |
295 | u8 perm_addr[MAX_ADDR_LEN]; | |
296 | int ret = -EINVAL; | |
297 | ||
298 | if (!netdev->dcbnl_ops->getpermhwaddr) | |
299 | return ret; | |
300 | ||
301 | dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
302 | if (!dcbnl_skb) | |
303 | goto err_out; | |
304 | ||
305 | nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); | |
306 | ||
307 | dcb = NLMSG_DATA(nlh); | |
308 | dcb->dcb_family = AF_UNSPEC; | |
309 | dcb->cmd = DCB_CMD_GPERM_HWADDR; | |
310 | ||
311 | netdev->dcbnl_ops->getpermhwaddr(netdev, perm_addr); | |
312 | ||
313 | ret = nla_put(dcbnl_skb, DCB_ATTR_PERM_HWADDR, sizeof(perm_addr), | |
314 | perm_addr); | |
315 | ||
316 | nlmsg_end(dcbnl_skb, nlh); | |
317 | ||
318 | ret = rtnl_unicast(dcbnl_skb, &init_net, pid); | |
319 | if (ret) | |
7eaf5077 | 320 | goto err_out; |
2f90b865 AD |
321 | |
322 | return 0; | |
323 | ||
324 | nlmsg_failure: | |
858eb711 | 325 | kfree_skb(dcbnl_skb); |
2f90b865 AD |
326 | err_out: |
327 | return -EINVAL; | |
328 | } | |
329 | ||
46132188 AD |
330 | static int dcbnl_getcap(struct net_device *netdev, struct nlattr **tb, |
331 | u32 pid, u32 seq, u16 flags) | |
332 | { | |
333 | struct sk_buff *dcbnl_skb; | |
334 | struct nlmsghdr *nlh; | |
335 | struct dcbmsg *dcb; | |
336 | struct nlattr *data[DCB_CAP_ATTR_MAX + 1], *nest; | |
337 | u8 value; | |
338 | int ret = -EINVAL; | |
339 | int i; | |
340 | int getall = 0; | |
341 | ||
342 | if (!tb[DCB_ATTR_CAP] || !netdev->dcbnl_ops->getcap) | |
343 | return ret; | |
344 | ||
345 | ret = nla_parse_nested(data, DCB_CAP_ATTR_MAX, tb[DCB_ATTR_CAP], | |
346 | dcbnl_cap_nest); | |
347 | if (ret) | |
348 | goto err_out; | |
349 | ||
350 | dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
351 | if (!dcbnl_skb) | |
352 | goto err_out; | |
353 | ||
354 | nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); | |
355 | ||
356 | dcb = NLMSG_DATA(nlh); | |
357 | dcb->dcb_family = AF_UNSPEC; | |
358 | dcb->cmd = DCB_CMD_GCAP; | |
359 | ||
360 | nest = nla_nest_start(dcbnl_skb, DCB_ATTR_CAP); | |
361 | if (!nest) | |
362 | goto err; | |
363 | ||
364 | if (data[DCB_CAP_ATTR_ALL]) | |
365 | getall = 1; | |
366 | ||
367 | for (i = DCB_CAP_ATTR_ALL+1; i <= DCB_CAP_ATTR_MAX; i++) { | |
368 | if (!getall && !data[i]) | |
369 | continue; | |
370 | ||
371 | if (!netdev->dcbnl_ops->getcap(netdev, i, &value)) { | |
372 | ret = nla_put_u8(dcbnl_skb, i, value); | |
373 | ||
374 | if (ret) { | |
375 | nla_nest_cancel(dcbnl_skb, nest); | |
376 | goto err; | |
377 | } | |
378 | } | |
379 | } | |
380 | nla_nest_end(dcbnl_skb, nest); | |
381 | ||
382 | nlmsg_end(dcbnl_skb, nlh); | |
383 | ||
384 | ret = rtnl_unicast(dcbnl_skb, &init_net, pid); | |
385 | if (ret) | |
7eaf5077 | 386 | goto err_out; |
46132188 AD |
387 | |
388 | return 0; | |
389 | nlmsg_failure: | |
390 | err: | |
858eb711 | 391 | kfree_skb(dcbnl_skb); |
46132188 AD |
392 | err_out: |
393 | return -EINVAL; | |
394 | } | |
395 | ||
33dbabc4 AD |
396 | static int dcbnl_getnumtcs(struct net_device *netdev, struct nlattr **tb, |
397 | u32 pid, u32 seq, u16 flags) | |
398 | { | |
399 | struct sk_buff *dcbnl_skb; | |
400 | struct nlmsghdr *nlh; | |
401 | struct dcbmsg *dcb; | |
402 | struct nlattr *data[DCB_NUMTCS_ATTR_MAX + 1], *nest; | |
403 | u8 value; | |
404 | int ret = -EINVAL; | |
405 | int i; | |
406 | int getall = 0; | |
407 | ||
408 | if (!tb[DCB_ATTR_NUMTCS] || !netdev->dcbnl_ops->getnumtcs) | |
409 | return ret; | |
410 | ||
411 | ret = nla_parse_nested(data, DCB_NUMTCS_ATTR_MAX, tb[DCB_ATTR_NUMTCS], | |
412 | dcbnl_numtcs_nest); | |
413 | if (ret) { | |
414 | ret = -EINVAL; | |
415 | goto err_out; | |
416 | } | |
417 | ||
418 | dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
419 | if (!dcbnl_skb) { | |
420 | ret = -EINVAL; | |
421 | goto err_out; | |
422 | } | |
423 | ||
424 | nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); | |
425 | ||
426 | dcb = NLMSG_DATA(nlh); | |
427 | dcb->dcb_family = AF_UNSPEC; | |
428 | dcb->cmd = DCB_CMD_GNUMTCS; | |
429 | ||
430 | nest = nla_nest_start(dcbnl_skb, DCB_ATTR_NUMTCS); | |
431 | if (!nest) { | |
432 | ret = -EINVAL; | |
433 | goto err; | |
434 | } | |
435 | ||
436 | if (data[DCB_NUMTCS_ATTR_ALL]) | |
437 | getall = 1; | |
438 | ||
439 | for (i = DCB_NUMTCS_ATTR_ALL+1; i <= DCB_NUMTCS_ATTR_MAX; i++) { | |
440 | if (!getall && !data[i]) | |
441 | continue; | |
442 | ||
443 | ret = netdev->dcbnl_ops->getnumtcs(netdev, i, &value); | |
444 | if (!ret) { | |
445 | ret = nla_put_u8(dcbnl_skb, i, value); | |
446 | ||
447 | if (ret) { | |
448 | nla_nest_cancel(dcbnl_skb, nest); | |
449 | ret = -EINVAL; | |
450 | goto err; | |
451 | } | |
452 | } else { | |
453 | goto err; | |
454 | } | |
455 | } | |
456 | nla_nest_end(dcbnl_skb, nest); | |
457 | ||
458 | nlmsg_end(dcbnl_skb, nlh); | |
459 | ||
460 | ret = rtnl_unicast(dcbnl_skb, &init_net, pid); | |
461 | if (ret) { | |
462 | ret = -EINVAL; | |
7eaf5077 | 463 | goto err_out; |
33dbabc4 AD |
464 | } |
465 | ||
466 | return 0; | |
467 | nlmsg_failure: | |
468 | err: | |
858eb711 | 469 | kfree_skb(dcbnl_skb); |
33dbabc4 AD |
470 | err_out: |
471 | return ret; | |
472 | } | |
473 | ||
474 | static int dcbnl_setnumtcs(struct net_device *netdev, struct nlattr **tb, | |
475 | u32 pid, u32 seq, u16 flags) | |
476 | { | |
477 | struct nlattr *data[DCB_NUMTCS_ATTR_MAX + 1]; | |
478 | int ret = -EINVAL; | |
479 | u8 value; | |
480 | int i; | |
481 | ||
8b124a8e | 482 | if (!tb[DCB_ATTR_NUMTCS] || !netdev->dcbnl_ops->setnumtcs) |
33dbabc4 AD |
483 | return ret; |
484 | ||
485 | ret = nla_parse_nested(data, DCB_NUMTCS_ATTR_MAX, tb[DCB_ATTR_NUMTCS], | |
486 | dcbnl_numtcs_nest); | |
487 | ||
488 | if (ret) { | |
489 | ret = -EINVAL; | |
490 | goto err; | |
491 | } | |
492 | ||
493 | for (i = DCB_NUMTCS_ATTR_ALL+1; i <= DCB_NUMTCS_ATTR_MAX; i++) { | |
494 | if (data[i] == NULL) | |
495 | continue; | |
496 | ||
497 | value = nla_get_u8(data[i]); | |
498 | ||
499 | ret = netdev->dcbnl_ops->setnumtcs(netdev, i, value); | |
500 | ||
501 | if (ret) | |
502 | goto operr; | |
503 | } | |
504 | ||
505 | operr: | |
506 | ret = dcbnl_reply(!!ret, RTM_SETDCB, DCB_CMD_SNUMTCS, | |
507 | DCB_ATTR_NUMTCS, pid, seq, flags); | |
508 | ||
509 | err: | |
510 | return ret; | |
511 | } | |
512 | ||
0eb3aa9b AD |
513 | static int dcbnl_getpfcstate(struct net_device *netdev, struct nlattr **tb, |
514 | u32 pid, u32 seq, u16 flags) | |
515 | { | |
516 | int ret = -EINVAL; | |
517 | ||
518 | if (!netdev->dcbnl_ops->getpfcstate) | |
519 | return ret; | |
520 | ||
521 | ret = dcbnl_reply(netdev->dcbnl_ops->getpfcstate(netdev), RTM_GETDCB, | |
522 | DCB_CMD_PFC_GSTATE, DCB_ATTR_PFC_STATE, | |
523 | pid, seq, flags); | |
524 | ||
525 | return ret; | |
526 | } | |
527 | ||
528 | static int dcbnl_setpfcstate(struct net_device *netdev, struct nlattr **tb, | |
529 | u32 pid, u32 seq, u16 flags) | |
530 | { | |
531 | int ret = -EINVAL; | |
532 | u8 value; | |
533 | ||
534 | if (!tb[DCB_ATTR_PFC_STATE] || !netdev->dcbnl_ops->setpfcstate) | |
535 | return ret; | |
536 | ||
537 | value = nla_get_u8(tb[DCB_ATTR_PFC_STATE]); | |
538 | ||
539 | netdev->dcbnl_ops->setpfcstate(netdev, value); | |
540 | ||
541 | ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_PFC_SSTATE, DCB_ATTR_PFC_STATE, | |
542 | pid, seq, flags); | |
543 | ||
544 | return ret; | |
545 | } | |
546 | ||
57949686 YZ |
547 | static int dcbnl_getapp(struct net_device *netdev, struct nlattr **tb, |
548 | u32 pid, u32 seq, u16 flags) | |
549 | { | |
550 | struct sk_buff *dcbnl_skb; | |
551 | struct nlmsghdr *nlh; | |
552 | struct dcbmsg *dcb; | |
553 | struct nlattr *app_nest; | |
554 | struct nlattr *app_tb[DCB_APP_ATTR_MAX + 1]; | |
555 | u16 id; | |
556 | u8 up, idtype; | |
557 | int ret = -EINVAL; | |
558 | ||
559 | if (!tb[DCB_ATTR_APP] || !netdev->dcbnl_ops->getapp) | |
560 | goto out; | |
561 | ||
562 | ret = nla_parse_nested(app_tb, DCB_APP_ATTR_MAX, tb[DCB_ATTR_APP], | |
563 | dcbnl_app_nest); | |
564 | if (ret) | |
565 | goto out; | |
566 | ||
567 | ret = -EINVAL; | |
568 | /* all must be non-null */ | |
569 | if ((!app_tb[DCB_APP_ATTR_IDTYPE]) || | |
570 | (!app_tb[DCB_APP_ATTR_ID])) | |
571 | goto out; | |
572 | ||
573 | /* either by eth type or by socket number */ | |
574 | idtype = nla_get_u8(app_tb[DCB_APP_ATTR_IDTYPE]); | |
575 | if ((idtype != DCB_APP_IDTYPE_ETHTYPE) && | |
576 | (idtype != DCB_APP_IDTYPE_PORTNUM)) | |
577 | goto out; | |
578 | ||
579 | id = nla_get_u16(app_tb[DCB_APP_ATTR_ID]); | |
580 | up = netdev->dcbnl_ops->getapp(netdev, idtype, id); | |
581 | ||
582 | /* send this back */ | |
583 | dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
584 | if (!dcbnl_skb) | |
585 | goto out; | |
586 | ||
587 | nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); | |
588 | dcb = NLMSG_DATA(nlh); | |
589 | dcb->dcb_family = AF_UNSPEC; | |
590 | dcb->cmd = DCB_CMD_GAPP; | |
591 | ||
592 | app_nest = nla_nest_start(dcbnl_skb, DCB_ATTR_APP); | |
593 | ret = nla_put_u8(dcbnl_skb, DCB_APP_ATTR_IDTYPE, idtype); | |
594 | if (ret) | |
595 | goto out_cancel; | |
596 | ||
597 | ret = nla_put_u16(dcbnl_skb, DCB_APP_ATTR_ID, id); | |
598 | if (ret) | |
599 | goto out_cancel; | |
600 | ||
601 | ret = nla_put_u8(dcbnl_skb, DCB_APP_ATTR_PRIORITY, up); | |
602 | if (ret) | |
603 | goto out_cancel; | |
604 | ||
605 | nla_nest_end(dcbnl_skb, app_nest); | |
606 | nlmsg_end(dcbnl_skb, nlh); | |
607 | ||
608 | ret = rtnl_unicast(dcbnl_skb, &init_net, pid); | |
609 | if (ret) | |
610 | goto nlmsg_failure; | |
611 | ||
612 | goto out; | |
613 | ||
614 | out_cancel: | |
615 | nla_nest_cancel(dcbnl_skb, app_nest); | |
616 | nlmsg_failure: | |
617 | kfree_skb(dcbnl_skb); | |
618 | out: | |
619 | return ret; | |
620 | } | |
621 | ||
622 | static int dcbnl_setapp(struct net_device *netdev, struct nlattr **tb, | |
623 | u32 pid, u32 seq, u16 flags) | |
624 | { | |
625 | int ret = -EINVAL; | |
626 | u16 id; | |
627 | u8 up, idtype; | |
628 | struct nlattr *app_tb[DCB_APP_ATTR_MAX + 1]; | |
629 | ||
630 | if (!tb[DCB_ATTR_APP] || !netdev->dcbnl_ops->setapp) | |
631 | goto out; | |
632 | ||
633 | ret = nla_parse_nested(app_tb, DCB_APP_ATTR_MAX, tb[DCB_ATTR_APP], | |
634 | dcbnl_app_nest); | |
635 | if (ret) | |
636 | goto out; | |
637 | ||
638 | ret = -EINVAL; | |
639 | /* all must be non-null */ | |
640 | if ((!app_tb[DCB_APP_ATTR_IDTYPE]) || | |
641 | (!app_tb[DCB_APP_ATTR_ID]) || | |
642 | (!app_tb[DCB_APP_ATTR_PRIORITY])) | |
643 | goto out; | |
644 | ||
645 | /* either by eth type or by socket number */ | |
646 | idtype = nla_get_u8(app_tb[DCB_APP_ATTR_IDTYPE]); | |
647 | if ((idtype != DCB_APP_IDTYPE_ETHTYPE) && | |
648 | (idtype != DCB_APP_IDTYPE_PORTNUM)) | |
649 | goto out; | |
650 | ||
651 | id = nla_get_u16(app_tb[DCB_APP_ATTR_ID]); | |
652 | up = nla_get_u8(app_tb[DCB_APP_ATTR_PRIORITY]); | |
653 | ||
654 | ret = dcbnl_reply(netdev->dcbnl_ops->setapp(netdev, idtype, id, up), | |
655 | RTM_SETDCB, DCB_CMD_SAPP, DCB_ATTR_APP, | |
656 | pid, seq, flags); | |
657 | out: | |
658 | return ret; | |
659 | } | |
660 | ||
2f90b865 AD |
661 | static int __dcbnl_pg_getcfg(struct net_device *netdev, struct nlattr **tb, |
662 | u32 pid, u32 seq, u16 flags, int dir) | |
663 | { | |
664 | struct sk_buff *dcbnl_skb; | |
665 | struct nlmsghdr *nlh; | |
666 | struct dcbmsg *dcb; | |
667 | struct nlattr *pg_nest, *param_nest, *data; | |
668 | struct nlattr *pg_tb[DCB_PG_ATTR_MAX + 1]; | |
669 | struct nlattr *param_tb[DCB_TC_ATTR_PARAM_MAX + 1]; | |
670 | u8 prio, pgid, tc_pct, up_map; | |
671 | int ret = -EINVAL; | |
672 | int getall = 0; | |
673 | int i; | |
674 | ||
675 | if (!tb[DCB_ATTR_PG_CFG] || | |
676 | !netdev->dcbnl_ops->getpgtccfgtx || | |
677 | !netdev->dcbnl_ops->getpgtccfgrx || | |
678 | !netdev->dcbnl_ops->getpgbwgcfgtx || | |
679 | !netdev->dcbnl_ops->getpgbwgcfgrx) | |
680 | return ret; | |
681 | ||
682 | ret = nla_parse_nested(pg_tb, DCB_PG_ATTR_MAX, | |
683 | tb[DCB_ATTR_PG_CFG], dcbnl_pg_nest); | |
684 | ||
685 | if (ret) | |
686 | goto err_out; | |
687 | ||
688 | dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
689 | if (!dcbnl_skb) | |
690 | goto err_out; | |
691 | ||
692 | nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); | |
693 | ||
694 | dcb = NLMSG_DATA(nlh); | |
695 | dcb->dcb_family = AF_UNSPEC; | |
696 | dcb->cmd = (dir) ? DCB_CMD_PGRX_GCFG : DCB_CMD_PGTX_GCFG; | |
697 | ||
698 | pg_nest = nla_nest_start(dcbnl_skb, DCB_ATTR_PG_CFG); | |
699 | if (!pg_nest) | |
700 | goto err; | |
701 | ||
702 | if (pg_tb[DCB_PG_ATTR_TC_ALL]) | |
703 | getall = 1; | |
704 | ||
705 | for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) { | |
706 | if (!getall && !pg_tb[i]) | |
707 | continue; | |
708 | ||
709 | if (pg_tb[DCB_PG_ATTR_TC_ALL]) | |
710 | data = pg_tb[DCB_PG_ATTR_TC_ALL]; | |
711 | else | |
712 | data = pg_tb[i]; | |
713 | ret = nla_parse_nested(param_tb, DCB_TC_ATTR_PARAM_MAX, | |
714 | data, dcbnl_tc_param_nest); | |
715 | if (ret) | |
716 | goto err_pg; | |
717 | ||
718 | param_nest = nla_nest_start(dcbnl_skb, i); | |
719 | if (!param_nest) | |
720 | goto err_pg; | |
721 | ||
722 | pgid = DCB_ATTR_VALUE_UNDEFINED; | |
723 | prio = DCB_ATTR_VALUE_UNDEFINED; | |
724 | tc_pct = DCB_ATTR_VALUE_UNDEFINED; | |
725 | up_map = DCB_ATTR_VALUE_UNDEFINED; | |
726 | ||
727 | if (dir) { | |
728 | /* Rx */ | |
729 | netdev->dcbnl_ops->getpgtccfgrx(netdev, | |
730 | i - DCB_PG_ATTR_TC_0, &prio, | |
731 | &pgid, &tc_pct, &up_map); | |
732 | } else { | |
733 | /* Tx */ | |
734 | netdev->dcbnl_ops->getpgtccfgtx(netdev, | |
735 | i - DCB_PG_ATTR_TC_0, &prio, | |
736 | &pgid, &tc_pct, &up_map); | |
737 | } | |
738 | ||
739 | if (param_tb[DCB_TC_ATTR_PARAM_PGID] || | |
740 | param_tb[DCB_TC_ATTR_PARAM_ALL]) { | |
741 | ret = nla_put_u8(dcbnl_skb, | |
742 | DCB_TC_ATTR_PARAM_PGID, pgid); | |
743 | if (ret) | |
744 | goto err_param; | |
745 | } | |
746 | if (param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING] || | |
747 | param_tb[DCB_TC_ATTR_PARAM_ALL]) { | |
748 | ret = nla_put_u8(dcbnl_skb, | |
749 | DCB_TC_ATTR_PARAM_UP_MAPPING, up_map); | |
750 | if (ret) | |
751 | goto err_param; | |
752 | } | |
753 | if (param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO] || | |
754 | param_tb[DCB_TC_ATTR_PARAM_ALL]) { | |
755 | ret = nla_put_u8(dcbnl_skb, | |
756 | DCB_TC_ATTR_PARAM_STRICT_PRIO, prio); | |
757 | if (ret) | |
758 | goto err_param; | |
759 | } | |
760 | if (param_tb[DCB_TC_ATTR_PARAM_BW_PCT] || | |
761 | param_tb[DCB_TC_ATTR_PARAM_ALL]) { | |
762 | ret = nla_put_u8(dcbnl_skb, DCB_TC_ATTR_PARAM_BW_PCT, | |
763 | tc_pct); | |
764 | if (ret) | |
765 | goto err_param; | |
766 | } | |
767 | nla_nest_end(dcbnl_skb, param_nest); | |
768 | } | |
769 | ||
770 | if (pg_tb[DCB_PG_ATTR_BW_ID_ALL]) | |
771 | getall = 1; | |
772 | else | |
773 | getall = 0; | |
774 | ||
775 | for (i = DCB_PG_ATTR_BW_ID_0; i <= DCB_PG_ATTR_BW_ID_7; i++) { | |
776 | if (!getall && !pg_tb[i]) | |
777 | continue; | |
778 | ||
779 | tc_pct = DCB_ATTR_VALUE_UNDEFINED; | |
780 | ||
781 | if (dir) { | |
782 | /* Rx */ | |
783 | netdev->dcbnl_ops->getpgbwgcfgrx(netdev, | |
784 | i - DCB_PG_ATTR_BW_ID_0, &tc_pct); | |
785 | } else { | |
786 | /* Tx */ | |
787 | netdev->dcbnl_ops->getpgbwgcfgtx(netdev, | |
788 | i - DCB_PG_ATTR_BW_ID_0, &tc_pct); | |
789 | } | |
790 | ret = nla_put_u8(dcbnl_skb, i, tc_pct); | |
791 | ||
792 | if (ret) | |
793 | goto err_pg; | |
794 | } | |
795 | ||
796 | nla_nest_end(dcbnl_skb, pg_nest); | |
797 | ||
798 | nlmsg_end(dcbnl_skb, nlh); | |
799 | ||
800 | ret = rtnl_unicast(dcbnl_skb, &init_net, pid); | |
801 | if (ret) | |
7eaf5077 | 802 | goto err_out; |
2f90b865 AD |
803 | |
804 | return 0; | |
805 | ||
806 | err_param: | |
807 | nla_nest_cancel(dcbnl_skb, param_nest); | |
808 | err_pg: | |
809 | nla_nest_cancel(dcbnl_skb, pg_nest); | |
810 | nlmsg_failure: | |
811 | err: | |
858eb711 | 812 | kfree_skb(dcbnl_skb); |
2f90b865 AD |
813 | err_out: |
814 | ret = -EINVAL; | |
815 | return ret; | |
816 | } | |
817 | ||
818 | static int dcbnl_pgtx_getcfg(struct net_device *netdev, struct nlattr **tb, | |
819 | u32 pid, u32 seq, u16 flags) | |
820 | { | |
821 | return __dcbnl_pg_getcfg(netdev, tb, pid, seq, flags, 0); | |
822 | } | |
823 | ||
824 | static int dcbnl_pgrx_getcfg(struct net_device *netdev, struct nlattr **tb, | |
825 | u32 pid, u32 seq, u16 flags) | |
826 | { | |
827 | return __dcbnl_pg_getcfg(netdev, tb, pid, seq, flags, 1); | |
828 | } | |
829 | ||
830 | static int dcbnl_setstate(struct net_device *netdev, struct nlattr **tb, | |
831 | u32 pid, u32 seq, u16 flags) | |
832 | { | |
833 | int ret = -EINVAL; | |
834 | u8 value; | |
835 | ||
836 | if (!tb[DCB_ATTR_STATE] || !netdev->dcbnl_ops->setstate) | |
837 | return ret; | |
838 | ||
839 | value = nla_get_u8(tb[DCB_ATTR_STATE]); | |
840 | ||
1486a61e DS |
841 | ret = dcbnl_reply(netdev->dcbnl_ops->setstate(netdev, value), |
842 | RTM_SETDCB, DCB_CMD_SSTATE, DCB_ATTR_STATE, | |
2f90b865 AD |
843 | pid, seq, flags); |
844 | ||
845 | return ret; | |
846 | } | |
847 | ||
848 | static int dcbnl_setpfccfg(struct net_device *netdev, struct nlattr **tb, | |
849 | u32 pid, u32 seq, u16 flags) | |
850 | { | |
851 | struct nlattr *data[DCB_PFC_UP_ATTR_MAX + 1]; | |
852 | int i; | |
853 | int ret = -EINVAL; | |
854 | u8 value; | |
855 | ||
856 | if (!tb[DCB_ATTR_PFC_CFG] || !netdev->dcbnl_ops->setpfccfg) | |
857 | return ret; | |
858 | ||
859 | ret = nla_parse_nested(data, DCB_PFC_UP_ATTR_MAX, | |
860 | tb[DCB_ATTR_PFC_CFG], | |
861 | dcbnl_pfc_up_nest); | |
862 | if (ret) | |
863 | goto err; | |
864 | ||
865 | for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) { | |
866 | if (data[i] == NULL) | |
867 | continue; | |
868 | value = nla_get_u8(data[i]); | |
869 | netdev->dcbnl_ops->setpfccfg(netdev, | |
870 | data[i]->nla_type - DCB_PFC_UP_ATTR_0, value); | |
871 | } | |
872 | ||
873 | ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_PFC_SCFG, DCB_ATTR_PFC_CFG, | |
874 | pid, seq, flags); | |
875 | err: | |
876 | return ret; | |
877 | } | |
878 | ||
879 | static int dcbnl_setall(struct net_device *netdev, struct nlattr **tb, | |
880 | u32 pid, u32 seq, u16 flags) | |
881 | { | |
882 | int ret = -EINVAL; | |
883 | ||
884 | if (!tb[DCB_ATTR_SET_ALL] || !netdev->dcbnl_ops->setall) | |
885 | return ret; | |
886 | ||
887 | ret = dcbnl_reply(netdev->dcbnl_ops->setall(netdev), RTM_SETDCB, | |
888 | DCB_CMD_SET_ALL, DCB_ATTR_SET_ALL, pid, seq, flags); | |
889 | ||
890 | return ret; | |
891 | } | |
892 | ||
893 | static int __dcbnl_pg_setcfg(struct net_device *netdev, struct nlattr **tb, | |
894 | u32 pid, u32 seq, u16 flags, int dir) | |
895 | { | |
896 | struct nlattr *pg_tb[DCB_PG_ATTR_MAX + 1]; | |
897 | struct nlattr *param_tb[DCB_TC_ATTR_PARAM_MAX + 1]; | |
898 | int ret = -EINVAL; | |
899 | int i; | |
900 | u8 pgid; | |
901 | u8 up_map; | |
902 | u8 prio; | |
903 | u8 tc_pct; | |
904 | ||
905 | if (!tb[DCB_ATTR_PG_CFG] || | |
906 | !netdev->dcbnl_ops->setpgtccfgtx || | |
907 | !netdev->dcbnl_ops->setpgtccfgrx || | |
908 | !netdev->dcbnl_ops->setpgbwgcfgtx || | |
909 | !netdev->dcbnl_ops->setpgbwgcfgrx) | |
910 | return ret; | |
911 | ||
912 | ret = nla_parse_nested(pg_tb, DCB_PG_ATTR_MAX, | |
913 | tb[DCB_ATTR_PG_CFG], dcbnl_pg_nest); | |
914 | if (ret) | |
915 | goto err; | |
916 | ||
917 | for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) { | |
918 | if (!pg_tb[i]) | |
919 | continue; | |
920 | ||
921 | ret = nla_parse_nested(param_tb, DCB_TC_ATTR_PARAM_MAX, | |
922 | pg_tb[i], dcbnl_tc_param_nest); | |
923 | if (ret) | |
924 | goto err; | |
925 | ||
926 | pgid = DCB_ATTR_VALUE_UNDEFINED; | |
927 | prio = DCB_ATTR_VALUE_UNDEFINED; | |
928 | tc_pct = DCB_ATTR_VALUE_UNDEFINED; | |
929 | up_map = DCB_ATTR_VALUE_UNDEFINED; | |
930 | ||
931 | if (param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO]) | |
932 | prio = | |
933 | nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO]); | |
934 | ||
935 | if (param_tb[DCB_TC_ATTR_PARAM_PGID]) | |
936 | pgid = nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_PGID]); | |
937 | ||
938 | if (param_tb[DCB_TC_ATTR_PARAM_BW_PCT]) | |
939 | tc_pct = nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_BW_PCT]); | |
940 | ||
941 | if (param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING]) | |
942 | up_map = | |
943 | nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING]); | |
944 | ||
945 | /* dir: Tx = 0, Rx = 1 */ | |
946 | if (dir) { | |
947 | /* Rx */ | |
948 | netdev->dcbnl_ops->setpgtccfgrx(netdev, | |
949 | i - DCB_PG_ATTR_TC_0, | |
950 | prio, pgid, tc_pct, up_map); | |
951 | } else { | |
952 | /* Tx */ | |
953 | netdev->dcbnl_ops->setpgtccfgtx(netdev, | |
954 | i - DCB_PG_ATTR_TC_0, | |
955 | prio, pgid, tc_pct, up_map); | |
956 | } | |
957 | } | |
958 | ||
959 | for (i = DCB_PG_ATTR_BW_ID_0; i <= DCB_PG_ATTR_BW_ID_7; i++) { | |
960 | if (!pg_tb[i]) | |
961 | continue; | |
962 | ||
963 | tc_pct = nla_get_u8(pg_tb[i]); | |
964 | ||
965 | /* dir: Tx = 0, Rx = 1 */ | |
966 | if (dir) { | |
967 | /* Rx */ | |
968 | netdev->dcbnl_ops->setpgbwgcfgrx(netdev, | |
969 | i - DCB_PG_ATTR_BW_ID_0, tc_pct); | |
970 | } else { | |
971 | /* Tx */ | |
972 | netdev->dcbnl_ops->setpgbwgcfgtx(netdev, | |
973 | i - DCB_PG_ATTR_BW_ID_0, tc_pct); | |
974 | } | |
975 | } | |
976 | ||
977 | ret = dcbnl_reply(0, RTM_SETDCB, | |
978 | (dir ? DCB_CMD_PGRX_SCFG : DCB_CMD_PGTX_SCFG), | |
979 | DCB_ATTR_PG_CFG, pid, seq, flags); | |
980 | ||
981 | err: | |
982 | return ret; | |
983 | } | |
984 | ||
985 | static int dcbnl_pgtx_setcfg(struct net_device *netdev, struct nlattr **tb, | |
986 | u32 pid, u32 seq, u16 flags) | |
987 | { | |
988 | return __dcbnl_pg_setcfg(netdev, tb, pid, seq, flags, 0); | |
989 | } | |
990 | ||
991 | static int dcbnl_pgrx_setcfg(struct net_device *netdev, struct nlattr **tb, | |
992 | u32 pid, u32 seq, u16 flags) | |
993 | { | |
994 | return __dcbnl_pg_setcfg(netdev, tb, pid, seq, flags, 1); | |
995 | } | |
996 | ||
859ee3c4 AD |
997 | static int dcbnl_bcn_getcfg(struct net_device *netdev, struct nlattr **tb, |
998 | u32 pid, u32 seq, u16 flags) | |
999 | { | |
1000 | struct sk_buff *dcbnl_skb; | |
1001 | struct nlmsghdr *nlh; | |
1002 | struct dcbmsg *dcb; | |
1003 | struct nlattr *bcn_nest; | |
1004 | struct nlattr *bcn_tb[DCB_BCN_ATTR_MAX + 1]; | |
1005 | u8 value_byte; | |
1006 | u32 value_integer; | |
1007 | int ret = -EINVAL; | |
1008 | bool getall = false; | |
1009 | int i; | |
1010 | ||
1011 | if (!tb[DCB_ATTR_BCN] || !netdev->dcbnl_ops->getbcnrp || | |
1012 | !netdev->dcbnl_ops->getbcncfg) | |
1013 | return ret; | |
1014 | ||
1015 | ret = nla_parse_nested(bcn_tb, DCB_BCN_ATTR_MAX, | |
1016 | tb[DCB_ATTR_BCN], dcbnl_bcn_nest); | |
1017 | ||
1018 | if (ret) | |
1019 | goto err_out; | |
1020 | ||
1021 | dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
1022 | if (!dcbnl_skb) | |
1023 | goto err_out; | |
1024 | ||
1025 | nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); | |
1026 | ||
1027 | dcb = NLMSG_DATA(nlh); | |
1028 | dcb->dcb_family = AF_UNSPEC; | |
1029 | dcb->cmd = DCB_CMD_BCN_GCFG; | |
1030 | ||
1031 | bcn_nest = nla_nest_start(dcbnl_skb, DCB_ATTR_BCN); | |
1032 | if (!bcn_nest) | |
1033 | goto err; | |
1034 | ||
1035 | if (bcn_tb[DCB_BCN_ATTR_ALL]) | |
1036 | getall = true; | |
1037 | ||
1038 | for (i = DCB_BCN_ATTR_RP_0; i <= DCB_BCN_ATTR_RP_7; i++) { | |
1039 | if (!getall && !bcn_tb[i]) | |
1040 | continue; | |
1041 | ||
1042 | netdev->dcbnl_ops->getbcnrp(netdev, i - DCB_BCN_ATTR_RP_0, | |
1043 | &value_byte); | |
1044 | ret = nla_put_u8(dcbnl_skb, i, value_byte); | |
1045 | if (ret) | |
1046 | goto err_bcn; | |
1047 | } | |
1048 | ||
f4314e81 | 1049 | for (i = DCB_BCN_ATTR_BCNA_0; i <= DCB_BCN_ATTR_RI; i++) { |
859ee3c4 AD |
1050 | if (!getall && !bcn_tb[i]) |
1051 | continue; | |
1052 | ||
1053 | netdev->dcbnl_ops->getbcncfg(netdev, i, | |
1054 | &value_integer); | |
1055 | ret = nla_put_u32(dcbnl_skb, i, value_integer); | |
1056 | if (ret) | |
1057 | goto err_bcn; | |
1058 | } | |
1059 | ||
1060 | nla_nest_end(dcbnl_skb, bcn_nest); | |
1061 | ||
1062 | nlmsg_end(dcbnl_skb, nlh); | |
1063 | ||
1064 | ret = rtnl_unicast(dcbnl_skb, &init_net, pid); | |
1065 | if (ret) | |
7eaf5077 | 1066 | goto err_out; |
859ee3c4 AD |
1067 | |
1068 | return 0; | |
1069 | ||
1070 | err_bcn: | |
1071 | nla_nest_cancel(dcbnl_skb, bcn_nest); | |
1072 | nlmsg_failure: | |
1073 | err: | |
858eb711 | 1074 | kfree_skb(dcbnl_skb); |
859ee3c4 AD |
1075 | err_out: |
1076 | ret = -EINVAL; | |
1077 | return ret; | |
1078 | } | |
1079 | ||
1080 | static int dcbnl_bcn_setcfg(struct net_device *netdev, struct nlattr **tb, | |
1081 | u32 pid, u32 seq, u16 flags) | |
1082 | { | |
1083 | struct nlattr *data[DCB_BCN_ATTR_MAX + 1]; | |
1084 | int i; | |
1085 | int ret = -EINVAL; | |
1086 | u8 value_byte; | |
1087 | u32 value_int; | |
1088 | ||
f64f9e71 JP |
1089 | if (!tb[DCB_ATTR_BCN] || !netdev->dcbnl_ops->setbcncfg || |
1090 | !netdev->dcbnl_ops->setbcnrp) | |
859ee3c4 AD |
1091 | return ret; |
1092 | ||
1093 | ret = nla_parse_nested(data, DCB_BCN_ATTR_MAX, | |
1094 | tb[DCB_ATTR_BCN], | |
1095 | dcbnl_pfc_up_nest); | |
1096 | if (ret) | |
1097 | goto err; | |
1098 | ||
1099 | for (i = DCB_BCN_ATTR_RP_0; i <= DCB_BCN_ATTR_RP_7; i++) { | |
1100 | if (data[i] == NULL) | |
1101 | continue; | |
1102 | value_byte = nla_get_u8(data[i]); | |
1103 | netdev->dcbnl_ops->setbcnrp(netdev, | |
1104 | data[i]->nla_type - DCB_BCN_ATTR_RP_0, value_byte); | |
1105 | } | |
1106 | ||
f4314e81 | 1107 | for (i = DCB_BCN_ATTR_BCNA_0; i <= DCB_BCN_ATTR_RI; i++) { |
859ee3c4 AD |
1108 | if (data[i] == NULL) |
1109 | continue; | |
1110 | value_int = nla_get_u32(data[i]); | |
1111 | netdev->dcbnl_ops->setbcncfg(netdev, | |
1112 | i, value_int); | |
1113 | } | |
1114 | ||
1115 | ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_BCN_SCFG, DCB_ATTR_BCN, | |
1116 | pid, seq, flags); | |
1117 | err: | |
1118 | return ret; | |
1119 | } | |
1120 | ||
2f90b865 AD |
1121 | static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) |
1122 | { | |
1123 | struct net *net = sock_net(skb->sk); | |
1124 | struct net_device *netdev; | |
1125 | struct dcbmsg *dcb = (struct dcbmsg *)NLMSG_DATA(nlh); | |
1126 | struct nlattr *tb[DCB_ATTR_MAX + 1]; | |
1127 | u32 pid = skb ? NETLINK_CB(skb).pid : 0; | |
1128 | int ret = -EINVAL; | |
1129 | ||
09ad9bc7 | 1130 | if (!net_eq(net, &init_net)) |
2f90b865 AD |
1131 | return -EINVAL; |
1132 | ||
1133 | ret = nlmsg_parse(nlh, sizeof(*dcb), tb, DCB_ATTR_MAX, | |
1134 | dcbnl_rtnl_policy); | |
1135 | if (ret < 0) | |
1136 | return ret; | |
1137 | ||
1138 | if (!tb[DCB_ATTR_IFNAME]) | |
1139 | return -EINVAL; | |
1140 | ||
1141 | netdev = dev_get_by_name(&init_net, nla_data(tb[DCB_ATTR_IFNAME])); | |
1142 | if (!netdev) | |
1143 | return -EINVAL; | |
1144 | ||
1145 | if (!netdev->dcbnl_ops) | |
1146 | goto errout; | |
1147 | ||
1148 | switch (dcb->cmd) { | |
1149 | case DCB_CMD_GSTATE: | |
1150 | ret = dcbnl_getstate(netdev, tb, pid, nlh->nlmsg_seq, | |
1151 | nlh->nlmsg_flags); | |
1152 | goto out; | |
1153 | case DCB_CMD_PFC_GCFG: | |
1154 | ret = dcbnl_getpfccfg(netdev, tb, pid, nlh->nlmsg_seq, | |
1155 | nlh->nlmsg_flags); | |
1156 | goto out; | |
1157 | case DCB_CMD_GPERM_HWADDR: | |
1158 | ret = dcbnl_getperm_hwaddr(netdev, tb, pid, nlh->nlmsg_seq, | |
1159 | nlh->nlmsg_flags); | |
1160 | goto out; | |
1161 | case DCB_CMD_PGTX_GCFG: | |
1162 | ret = dcbnl_pgtx_getcfg(netdev, tb, pid, nlh->nlmsg_seq, | |
1163 | nlh->nlmsg_flags); | |
1164 | goto out; | |
1165 | case DCB_CMD_PGRX_GCFG: | |
1166 | ret = dcbnl_pgrx_getcfg(netdev, tb, pid, nlh->nlmsg_seq, | |
1167 | nlh->nlmsg_flags); | |
1168 | goto out; | |
859ee3c4 AD |
1169 | case DCB_CMD_BCN_GCFG: |
1170 | ret = dcbnl_bcn_getcfg(netdev, tb, pid, nlh->nlmsg_seq, | |
1171 | nlh->nlmsg_flags); | |
1172 | goto out; | |
2f90b865 AD |
1173 | case DCB_CMD_SSTATE: |
1174 | ret = dcbnl_setstate(netdev, tb, pid, nlh->nlmsg_seq, | |
1175 | nlh->nlmsg_flags); | |
1176 | goto out; | |
1177 | case DCB_CMD_PFC_SCFG: | |
1178 | ret = dcbnl_setpfccfg(netdev, tb, pid, nlh->nlmsg_seq, | |
1179 | nlh->nlmsg_flags); | |
1180 | goto out; | |
1181 | ||
1182 | case DCB_CMD_SET_ALL: | |
1183 | ret = dcbnl_setall(netdev, tb, pid, nlh->nlmsg_seq, | |
1184 | nlh->nlmsg_flags); | |
1185 | goto out; | |
1186 | case DCB_CMD_PGTX_SCFG: | |
1187 | ret = dcbnl_pgtx_setcfg(netdev, tb, pid, nlh->nlmsg_seq, | |
1188 | nlh->nlmsg_flags); | |
1189 | goto out; | |
1190 | case DCB_CMD_PGRX_SCFG: | |
1191 | ret = dcbnl_pgrx_setcfg(netdev, tb, pid, nlh->nlmsg_seq, | |
1192 | nlh->nlmsg_flags); | |
1193 | goto out; | |
46132188 AD |
1194 | case DCB_CMD_GCAP: |
1195 | ret = dcbnl_getcap(netdev, tb, pid, nlh->nlmsg_seq, | |
1196 | nlh->nlmsg_flags); | |
1197 | goto out; | |
33dbabc4 AD |
1198 | case DCB_CMD_GNUMTCS: |
1199 | ret = dcbnl_getnumtcs(netdev, tb, pid, nlh->nlmsg_seq, | |
1200 | nlh->nlmsg_flags); | |
1201 | goto out; | |
1202 | case DCB_CMD_SNUMTCS: | |
1203 | ret = dcbnl_setnumtcs(netdev, tb, pid, nlh->nlmsg_seq, | |
1204 | nlh->nlmsg_flags); | |
1205 | goto out; | |
0eb3aa9b AD |
1206 | case DCB_CMD_PFC_GSTATE: |
1207 | ret = dcbnl_getpfcstate(netdev, tb, pid, nlh->nlmsg_seq, | |
1208 | nlh->nlmsg_flags); | |
1209 | goto out; | |
1210 | case DCB_CMD_PFC_SSTATE: | |
1211 | ret = dcbnl_setpfcstate(netdev, tb, pid, nlh->nlmsg_seq, | |
1212 | nlh->nlmsg_flags); | |
1213 | goto out; | |
859ee3c4 AD |
1214 | case DCB_CMD_BCN_SCFG: |
1215 | ret = dcbnl_bcn_setcfg(netdev, tb, pid, nlh->nlmsg_seq, | |
1216 | nlh->nlmsg_flags); | |
1217 | goto out; | |
57949686 YZ |
1218 | case DCB_CMD_GAPP: |
1219 | ret = dcbnl_getapp(netdev, tb, pid, nlh->nlmsg_seq, | |
1220 | nlh->nlmsg_flags); | |
1221 | goto out; | |
1222 | case DCB_CMD_SAPP: | |
1223 | ret = dcbnl_setapp(netdev, tb, pid, nlh->nlmsg_seq, | |
1224 | nlh->nlmsg_flags); | |
1225 | goto out; | |
2f90b865 AD |
1226 | default: |
1227 | goto errout; | |
1228 | } | |
1229 | errout: | |
1230 | ret = -EINVAL; | |
1231 | out: | |
1232 | dev_put(netdev); | |
1233 | return ret; | |
1234 | } | |
1235 | ||
1236 | static int __init dcbnl_init(void) | |
1237 | { | |
1238 | rtnl_register(PF_UNSPEC, RTM_GETDCB, dcb_doit, NULL); | |
1239 | rtnl_register(PF_UNSPEC, RTM_SETDCB, dcb_doit, NULL); | |
1240 | ||
1241 | return 0; | |
1242 | } | |
1243 | module_init(dcbnl_init); | |
1244 | ||
1245 | static void __exit dcbnl_exit(void) | |
1246 | { | |
1247 | rtnl_unregister(PF_UNSPEC, RTM_GETDCB); | |
1248 | rtnl_unregister(PF_UNSPEC, RTM_SETDCB); | |
1249 | } | |
1250 | module_exit(dcbnl_exit); | |
1251 | ||
1252 |