DCB: Add interface to query for the DCB capabilities of an device.
[safe/jmp/linux-2.6] / net / dcb / dcbnl.c
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 <lucy.liu@intel.com>
18  */
19
20 #include <linux/netdevice.h>
21 #include <linux/netlink.h>
22 #include <net/netlink.h>
23 #include <net/rtnetlink.h>
24 #include <linux/dcbnl.h>
25 #include <linux/rtnetlink.h>
26 #include <net/sock.h>
27
28 /**
29  * Data Center Bridging (DCB) is a collection of Ethernet enhancements
30  * intended to allow network traffic with differing requirements
31  * (highly reliable, no drops vs. best effort vs. low latency) to operate
32  * and co-exist on Ethernet.  Current DCB features are:
33  *
34  * Enhanced Transmission Selection (aka Priority Grouping [PG]) - provides a
35  *   framework for assigning bandwidth guarantees to traffic classes.
36  *
37  * Priority-based Flow Control (PFC) - provides a flow control mechanism which
38  *   can work independently for each 802.1p priority.
39  *
40  * Congestion Notification - provides a mechanism for end-to-end congestion
41  *   control for protocols which do not have built-in congestion management.
42  *
43  * More information about the emerging standards for these Ethernet features
44  * can be found at: http://www.ieee802.org/1/pages/dcbridges.html
45  *
46  * This file implements an rtnetlink interface to allow configuration of DCB
47  * features for capable devices.
48  */
49
50 MODULE_AUTHOR("Lucy Liu, <lucy.liu@intel.com>");
51 MODULE_DESCRIPTION("Data Center Bridging generic netlink interface");
52 MODULE_LICENSE("GPL");
53
54 /**************** DCB attribute policies *************************************/
55
56 /* DCB netlink attributes policy */
57 static struct nla_policy dcbnl_rtnl_policy[DCB_ATTR_MAX + 1] = {
58         [DCB_ATTR_IFNAME]    = {.type = NLA_STRING, .len = IFNAMSIZ - 1},
59         [DCB_ATTR_STATE]     = {.type = NLA_U8},
60         [DCB_ATTR_PFC_CFG]   = {.type = NLA_NESTED},
61         [DCB_ATTR_PG_CFG]    = {.type = NLA_NESTED},
62         [DCB_ATTR_SET_ALL]   = {.type = NLA_U8},
63         [DCB_ATTR_PERM_HWADDR] = {.type = NLA_FLAG},
64         [DCB_ATTR_CAP]       = {.type = NLA_NESTED},
65 };
66
67 /* DCB priority flow control to User Priority nested attributes */
68 static struct nla_policy dcbnl_pfc_up_nest[DCB_PFC_UP_ATTR_MAX + 1] = {
69         [DCB_PFC_UP_ATTR_0]   = {.type = NLA_U8},
70         [DCB_PFC_UP_ATTR_1]   = {.type = NLA_U8},
71         [DCB_PFC_UP_ATTR_2]   = {.type = NLA_U8},
72         [DCB_PFC_UP_ATTR_3]   = {.type = NLA_U8},
73         [DCB_PFC_UP_ATTR_4]   = {.type = NLA_U8},
74         [DCB_PFC_UP_ATTR_5]   = {.type = NLA_U8},
75         [DCB_PFC_UP_ATTR_6]   = {.type = NLA_U8},
76         [DCB_PFC_UP_ATTR_7]   = {.type = NLA_U8},
77         [DCB_PFC_UP_ATTR_ALL] = {.type = NLA_FLAG},
78 };
79
80 /* DCB priority grouping nested attributes */
81 static struct nla_policy dcbnl_pg_nest[DCB_PG_ATTR_MAX + 1] = {
82         [DCB_PG_ATTR_TC_0]      = {.type = NLA_NESTED},
83         [DCB_PG_ATTR_TC_1]      = {.type = NLA_NESTED},
84         [DCB_PG_ATTR_TC_2]      = {.type = NLA_NESTED},
85         [DCB_PG_ATTR_TC_3]      = {.type = NLA_NESTED},
86         [DCB_PG_ATTR_TC_4]      = {.type = NLA_NESTED},
87         [DCB_PG_ATTR_TC_5]      = {.type = NLA_NESTED},
88         [DCB_PG_ATTR_TC_6]      = {.type = NLA_NESTED},
89         [DCB_PG_ATTR_TC_7]      = {.type = NLA_NESTED},
90         [DCB_PG_ATTR_TC_ALL]    = {.type = NLA_NESTED},
91         [DCB_PG_ATTR_BW_ID_0]   = {.type = NLA_U8},
92         [DCB_PG_ATTR_BW_ID_1]   = {.type = NLA_U8},
93         [DCB_PG_ATTR_BW_ID_2]   = {.type = NLA_U8},
94         [DCB_PG_ATTR_BW_ID_3]   = {.type = NLA_U8},
95         [DCB_PG_ATTR_BW_ID_4]   = {.type = NLA_U8},
96         [DCB_PG_ATTR_BW_ID_5]   = {.type = NLA_U8},
97         [DCB_PG_ATTR_BW_ID_6]   = {.type = NLA_U8},
98         [DCB_PG_ATTR_BW_ID_7]   = {.type = NLA_U8},
99         [DCB_PG_ATTR_BW_ID_ALL] = {.type = NLA_FLAG},
100 };
101
102 /* DCB traffic class nested attributes. */
103 static struct nla_policy dcbnl_tc_param_nest[DCB_TC_ATTR_PARAM_MAX + 1] = {
104         [DCB_TC_ATTR_PARAM_PGID]            = {.type = NLA_U8},
105         [DCB_TC_ATTR_PARAM_UP_MAPPING]      = {.type = NLA_U8},
106         [DCB_TC_ATTR_PARAM_STRICT_PRIO]     = {.type = NLA_U8},
107         [DCB_TC_ATTR_PARAM_BW_PCT]          = {.type = NLA_U8},
108         [DCB_TC_ATTR_PARAM_ALL]             = {.type = NLA_FLAG},
109 };
110
111 /* DCB capabilities nested attributes. */
112 static struct nla_policy dcbnl_cap_nest[DCB_CAP_ATTR_MAX + 1] = {
113         [DCB_CAP_ATTR_ALL]     = {.type = NLA_FLAG},
114         [DCB_CAP_ATTR_PG]      = {.type = NLA_U8},
115         [DCB_CAP_ATTR_PFC]     = {.type = NLA_U8},
116         [DCB_CAP_ATTR_UP2TC]   = {.type = NLA_U8},
117         [DCB_CAP_ATTR_PG_TCS]  = {.type = NLA_U8},
118         [DCB_CAP_ATTR_PFC_TCS] = {.type = NLA_U8},
119         [DCB_CAP_ATTR_GSP]     = {.type = NLA_U8},
120         [DCB_CAP_ATTR_BCN]     = {.type = NLA_U8},
121 };
122
123 /* standard netlink reply call */
124 static int dcbnl_reply(u8 value, u8 event, u8 cmd, u8 attr, u32 pid,
125                        u32 seq, u16 flags)
126 {
127         struct sk_buff *dcbnl_skb;
128         struct dcbmsg *dcb;
129         struct nlmsghdr *nlh;
130         int ret = -EINVAL;
131
132         dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
133         if (!dcbnl_skb)
134                 return ret;
135
136         nlh = NLMSG_NEW(dcbnl_skb, pid, seq, event, sizeof(*dcb), flags);
137
138         dcb = NLMSG_DATA(nlh);
139         dcb->dcb_family = AF_UNSPEC;
140         dcb->cmd = cmd;
141         dcb->dcb_pad = 0;
142
143         ret = nla_put_u8(dcbnl_skb, attr, value);
144         if (ret)
145                 goto err;
146
147         /* end the message, assign the nlmsg_len. */
148         nlmsg_end(dcbnl_skb, nlh);
149         ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
150         if (ret)
151                 goto err;
152
153         return 0;
154 nlmsg_failure:
155 err:
156         kfree(dcbnl_skb);
157         return ret;
158 }
159
160 static int dcbnl_getstate(struct net_device *netdev, struct nlattr **tb,
161                           u32 pid, u32 seq, u16 flags)
162 {
163         int ret = -EINVAL;
164
165         /* if (!tb[DCB_ATTR_STATE] || !netdev->dcbnl_ops->getstate) */
166         if (!netdev->dcbnl_ops->getstate)
167                 return ret;
168
169         ret = dcbnl_reply(netdev->dcbnl_ops->getstate(netdev), RTM_GETDCB,
170                           DCB_CMD_GSTATE, DCB_ATTR_STATE, pid, seq, flags);
171
172         return ret;
173 }
174
175 static int dcbnl_getpfccfg(struct net_device *netdev, struct nlattr **tb,
176                            u32 pid, u32 seq, u16 flags)
177 {
178         struct sk_buff *dcbnl_skb;
179         struct nlmsghdr *nlh;
180         struct dcbmsg *dcb;
181         struct nlattr *data[DCB_PFC_UP_ATTR_MAX + 1], *nest;
182         u8 value;
183         int ret = -EINVAL;
184         int i;
185         int getall = 0;
186
187         if (!tb[DCB_ATTR_PFC_CFG] || !netdev->dcbnl_ops->getpfccfg)
188                 return ret;
189
190         ret = nla_parse_nested(data, DCB_PFC_UP_ATTR_MAX,
191                                tb[DCB_ATTR_PFC_CFG],
192                                dcbnl_pfc_up_nest);
193         if (ret)
194                 goto err_out;
195
196         dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
197         if (!dcbnl_skb)
198                 goto err_out;
199
200         nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
201
202         dcb = NLMSG_DATA(nlh);
203         dcb->dcb_family = AF_UNSPEC;
204         dcb->cmd = DCB_CMD_PFC_GCFG;
205
206         nest = nla_nest_start(dcbnl_skb, DCB_ATTR_PFC_CFG);
207         if (!nest)
208                 goto err;
209
210         if (data[DCB_PFC_UP_ATTR_ALL])
211                 getall = 1;
212
213         for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) {
214                 if (!getall && !data[i])
215                         continue;
216
217                 netdev->dcbnl_ops->getpfccfg(netdev, i - DCB_PFC_UP_ATTR_0,
218                                              &value);
219                 ret = nla_put_u8(dcbnl_skb, i, value);
220
221                 if (ret) {
222                         nla_nest_cancel(dcbnl_skb, nest);
223                         goto err;
224                 }
225         }
226         nla_nest_end(dcbnl_skb, nest);
227
228         nlmsg_end(dcbnl_skb, nlh);
229
230         ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
231         if (ret)
232                 goto err;
233
234         return 0;
235 nlmsg_failure:
236 err:
237         kfree(dcbnl_skb);
238 err_out:
239         return -EINVAL;
240 }
241
242 static int dcbnl_getperm_hwaddr(struct net_device *netdev, struct nlattr **tb,
243                                 u32 pid, u32 seq, u16 flags)
244 {
245         struct sk_buff *dcbnl_skb;
246         struct nlmsghdr *nlh;
247         struct dcbmsg *dcb;
248         u8 perm_addr[MAX_ADDR_LEN];
249         int ret = -EINVAL;
250
251         if (!netdev->dcbnl_ops->getpermhwaddr)
252                 return ret;
253
254         dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
255         if (!dcbnl_skb)
256                 goto err_out;
257
258         nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
259
260         dcb = NLMSG_DATA(nlh);
261         dcb->dcb_family = AF_UNSPEC;
262         dcb->cmd = DCB_CMD_GPERM_HWADDR;
263
264         netdev->dcbnl_ops->getpermhwaddr(netdev, perm_addr);
265
266         ret = nla_put(dcbnl_skb, DCB_ATTR_PERM_HWADDR, sizeof(perm_addr),
267                       perm_addr);
268
269         nlmsg_end(dcbnl_skb, nlh);
270
271         ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
272         if (ret)
273                 goto err;
274
275         return 0;
276
277 nlmsg_failure:
278 err:
279         kfree(dcbnl_skb);
280 err_out:
281         return -EINVAL;
282 }
283
284 static int dcbnl_getcap(struct net_device *netdev, struct nlattr **tb,
285                         u32 pid, u32 seq, u16 flags)
286 {
287         struct sk_buff *dcbnl_skb;
288         struct nlmsghdr *nlh;
289         struct dcbmsg *dcb;
290         struct nlattr *data[DCB_CAP_ATTR_MAX + 1], *nest;
291         u8 value;
292         int ret = -EINVAL;
293         int i;
294         int getall = 0;
295
296         if (!tb[DCB_ATTR_CAP] || !netdev->dcbnl_ops->getcap)
297                 return ret;
298
299         ret = nla_parse_nested(data, DCB_CAP_ATTR_MAX, tb[DCB_ATTR_CAP],
300                                dcbnl_cap_nest);
301         if (ret)
302                 goto err_out;
303
304         dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
305         if (!dcbnl_skb)
306                 goto err_out;
307
308         nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
309
310         dcb = NLMSG_DATA(nlh);
311         dcb->dcb_family = AF_UNSPEC;
312         dcb->cmd = DCB_CMD_GCAP;
313
314         nest = nla_nest_start(dcbnl_skb, DCB_ATTR_CAP);
315         if (!nest)
316                 goto err;
317
318         if (data[DCB_CAP_ATTR_ALL])
319                 getall = 1;
320
321         for (i = DCB_CAP_ATTR_ALL+1; i <= DCB_CAP_ATTR_MAX; i++) {
322                 if (!getall && !data[i])
323                         continue;
324
325                 if (!netdev->dcbnl_ops->getcap(netdev, i, &value)) {
326                         ret = nla_put_u8(dcbnl_skb, i, value);
327
328                         if (ret) {
329                                 nla_nest_cancel(dcbnl_skb, nest);
330                                 goto err;
331                         }
332                 }
333         }
334         nla_nest_end(dcbnl_skb, nest);
335
336         nlmsg_end(dcbnl_skb, nlh);
337
338         ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
339         if (ret)
340                 goto err;
341
342         return 0;
343 nlmsg_failure:
344 err:
345         kfree(dcbnl_skb);
346 err_out:
347         return -EINVAL;
348 }
349
350 static int __dcbnl_pg_getcfg(struct net_device *netdev, struct nlattr **tb,
351                              u32 pid, u32 seq, u16 flags, int dir)
352 {
353         struct sk_buff *dcbnl_skb;
354         struct nlmsghdr *nlh;
355         struct dcbmsg *dcb;
356         struct nlattr *pg_nest, *param_nest, *data;
357         struct nlattr *pg_tb[DCB_PG_ATTR_MAX + 1];
358         struct nlattr *param_tb[DCB_TC_ATTR_PARAM_MAX + 1];
359         u8 prio, pgid, tc_pct, up_map;
360         int ret  = -EINVAL;
361         int getall = 0;
362         int i;
363
364         if (!tb[DCB_ATTR_PG_CFG] ||
365             !netdev->dcbnl_ops->getpgtccfgtx ||
366             !netdev->dcbnl_ops->getpgtccfgrx ||
367             !netdev->dcbnl_ops->getpgbwgcfgtx ||
368             !netdev->dcbnl_ops->getpgbwgcfgrx)
369                 return ret;
370
371         ret = nla_parse_nested(pg_tb, DCB_PG_ATTR_MAX,
372                                tb[DCB_ATTR_PG_CFG], dcbnl_pg_nest);
373
374         if (ret)
375                 goto err_out;
376
377         dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
378         if (!dcbnl_skb)
379                 goto err_out;
380
381         nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
382
383         dcb = NLMSG_DATA(nlh);
384         dcb->dcb_family = AF_UNSPEC;
385         dcb->cmd = (dir) ? DCB_CMD_PGRX_GCFG : DCB_CMD_PGTX_GCFG;
386
387         pg_nest = nla_nest_start(dcbnl_skb, DCB_ATTR_PG_CFG);
388         if (!pg_nest)
389                 goto err;
390
391         if (pg_tb[DCB_PG_ATTR_TC_ALL])
392                 getall = 1;
393
394         for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) {
395                 if (!getall && !pg_tb[i])
396                         continue;
397
398                 if (pg_tb[DCB_PG_ATTR_TC_ALL])
399                         data = pg_tb[DCB_PG_ATTR_TC_ALL];
400                 else
401                         data = pg_tb[i];
402                 ret = nla_parse_nested(param_tb, DCB_TC_ATTR_PARAM_MAX,
403                                        data, dcbnl_tc_param_nest);
404                 if (ret)
405                         goto err_pg;
406
407                 param_nest = nla_nest_start(dcbnl_skb, i);
408                 if (!param_nest)
409                         goto err_pg;
410
411                 pgid = DCB_ATTR_VALUE_UNDEFINED;
412                 prio = DCB_ATTR_VALUE_UNDEFINED;
413                 tc_pct = DCB_ATTR_VALUE_UNDEFINED;
414                 up_map = DCB_ATTR_VALUE_UNDEFINED;
415
416                 if (dir) {
417                         /* Rx */
418                         netdev->dcbnl_ops->getpgtccfgrx(netdev,
419                                                 i - DCB_PG_ATTR_TC_0, &prio,
420                                                 &pgid, &tc_pct, &up_map);
421                 } else {
422                         /* Tx */
423                         netdev->dcbnl_ops->getpgtccfgtx(netdev,
424                                                 i - DCB_PG_ATTR_TC_0, &prio,
425                                                 &pgid, &tc_pct, &up_map);
426                 }
427
428                 if (param_tb[DCB_TC_ATTR_PARAM_PGID] ||
429                     param_tb[DCB_TC_ATTR_PARAM_ALL]) {
430                         ret = nla_put_u8(dcbnl_skb,
431                                          DCB_TC_ATTR_PARAM_PGID, pgid);
432                         if (ret)
433                                 goto err_param;
434                 }
435                 if (param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING] ||
436                     param_tb[DCB_TC_ATTR_PARAM_ALL]) {
437                         ret = nla_put_u8(dcbnl_skb,
438                                          DCB_TC_ATTR_PARAM_UP_MAPPING, up_map);
439                         if (ret)
440                                 goto err_param;
441                 }
442                 if (param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO] ||
443                     param_tb[DCB_TC_ATTR_PARAM_ALL]) {
444                         ret = nla_put_u8(dcbnl_skb,
445                                          DCB_TC_ATTR_PARAM_STRICT_PRIO, prio);
446                         if (ret)
447                                 goto err_param;
448                 }
449                 if (param_tb[DCB_TC_ATTR_PARAM_BW_PCT] ||
450                     param_tb[DCB_TC_ATTR_PARAM_ALL]) {
451                         ret = nla_put_u8(dcbnl_skb, DCB_TC_ATTR_PARAM_BW_PCT,
452                                          tc_pct);
453                         if (ret)
454                                 goto err_param;
455                 }
456                 nla_nest_end(dcbnl_skb, param_nest);
457         }
458
459         if (pg_tb[DCB_PG_ATTR_BW_ID_ALL])
460                 getall = 1;
461         else
462                 getall = 0;
463
464         for (i = DCB_PG_ATTR_BW_ID_0; i <= DCB_PG_ATTR_BW_ID_7; i++) {
465                 if (!getall && !pg_tb[i])
466                         continue;
467
468                 tc_pct = DCB_ATTR_VALUE_UNDEFINED;
469
470                 if (dir) {
471                         /* Rx */
472                         netdev->dcbnl_ops->getpgbwgcfgrx(netdev,
473                                         i - DCB_PG_ATTR_BW_ID_0, &tc_pct);
474                 } else {
475                         /* Tx */
476                         netdev->dcbnl_ops->getpgbwgcfgtx(netdev,
477                                         i - DCB_PG_ATTR_BW_ID_0, &tc_pct);
478                 }
479                 ret = nla_put_u8(dcbnl_skb, i, tc_pct);
480
481                 if (ret)
482                         goto err_pg;
483         }
484
485         nla_nest_end(dcbnl_skb, pg_nest);
486
487         nlmsg_end(dcbnl_skb, nlh);
488
489         ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
490         if (ret)
491                 goto err;
492
493         return 0;
494
495 err_param:
496         nla_nest_cancel(dcbnl_skb, param_nest);
497 err_pg:
498         nla_nest_cancel(dcbnl_skb, pg_nest);
499 nlmsg_failure:
500 err:
501         kfree(dcbnl_skb);
502 err_out:
503         ret  = -EINVAL;
504         return ret;
505 }
506
507 static int dcbnl_pgtx_getcfg(struct net_device *netdev, struct nlattr **tb,
508                              u32 pid, u32 seq, u16 flags)
509 {
510         return __dcbnl_pg_getcfg(netdev, tb, pid, seq, flags, 0);
511 }
512
513 static int dcbnl_pgrx_getcfg(struct net_device *netdev, struct nlattr **tb,
514                              u32 pid, u32 seq, u16 flags)
515 {
516         return __dcbnl_pg_getcfg(netdev, tb, pid, seq, flags, 1);
517 }
518
519 static int dcbnl_setstate(struct net_device *netdev, struct nlattr **tb,
520                           u32 pid, u32 seq, u16 flags)
521 {
522         int ret = -EINVAL;
523         u8 value;
524
525         if (!tb[DCB_ATTR_STATE] || !netdev->dcbnl_ops->setstate)
526                 return ret;
527
528         value = nla_get_u8(tb[DCB_ATTR_STATE]);
529
530         netdev->dcbnl_ops->setstate(netdev, value);
531
532         ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_SSTATE, DCB_ATTR_STATE,
533                           pid, seq, flags);
534
535         return ret;
536 }
537
538 static int dcbnl_setpfccfg(struct net_device *netdev, struct nlattr **tb,
539                            u32 pid, u32 seq, u16 flags)
540 {
541         struct nlattr *data[DCB_PFC_UP_ATTR_MAX + 1];
542         int i;
543         int ret = -EINVAL;
544         u8 value;
545
546         if (!tb[DCB_ATTR_PFC_CFG] || !netdev->dcbnl_ops->setpfccfg)
547                 return ret;
548
549         ret = nla_parse_nested(data, DCB_PFC_UP_ATTR_MAX,
550                                tb[DCB_ATTR_PFC_CFG],
551                                dcbnl_pfc_up_nest);
552         if (ret)
553                 goto err;
554
555         for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) {
556                 if (data[i] == NULL)
557                         continue;
558                 value = nla_get_u8(data[i]);
559                 netdev->dcbnl_ops->setpfccfg(netdev,
560                         data[i]->nla_type - DCB_PFC_UP_ATTR_0, value);
561         }
562
563         ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_PFC_SCFG, DCB_ATTR_PFC_CFG,
564                           pid, seq, flags);
565 err:
566         return ret;
567 }
568
569 static int dcbnl_setall(struct net_device *netdev, struct nlattr **tb,
570                         u32 pid, u32 seq, u16 flags)
571 {
572         int ret = -EINVAL;
573
574         if (!tb[DCB_ATTR_SET_ALL] || !netdev->dcbnl_ops->setall)
575                 return ret;
576
577         ret = dcbnl_reply(netdev->dcbnl_ops->setall(netdev), RTM_SETDCB,
578                           DCB_CMD_SET_ALL, DCB_ATTR_SET_ALL, pid, seq, flags);
579
580         return ret;
581 }
582
583 static int __dcbnl_pg_setcfg(struct net_device *netdev, struct nlattr **tb,
584                              u32 pid, u32 seq, u16 flags, int dir)
585 {
586         struct nlattr *pg_tb[DCB_PG_ATTR_MAX + 1];
587         struct nlattr *param_tb[DCB_TC_ATTR_PARAM_MAX + 1];
588         int ret = -EINVAL;
589         int i;
590         u8 pgid;
591         u8 up_map;
592         u8 prio;
593         u8 tc_pct;
594
595         if (!tb[DCB_ATTR_PG_CFG] ||
596             !netdev->dcbnl_ops->setpgtccfgtx ||
597             !netdev->dcbnl_ops->setpgtccfgrx ||
598             !netdev->dcbnl_ops->setpgbwgcfgtx ||
599             !netdev->dcbnl_ops->setpgbwgcfgrx)
600                 return ret;
601
602         ret = nla_parse_nested(pg_tb, DCB_PG_ATTR_MAX,
603                                tb[DCB_ATTR_PG_CFG], dcbnl_pg_nest);
604         if (ret)
605                 goto err;
606
607         for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) {
608                 if (!pg_tb[i])
609                         continue;
610
611                 ret = nla_parse_nested(param_tb, DCB_TC_ATTR_PARAM_MAX,
612                                        pg_tb[i], dcbnl_tc_param_nest);
613                 if (ret)
614                         goto err;
615
616                 pgid = DCB_ATTR_VALUE_UNDEFINED;
617                 prio = DCB_ATTR_VALUE_UNDEFINED;
618                 tc_pct = DCB_ATTR_VALUE_UNDEFINED;
619                 up_map = DCB_ATTR_VALUE_UNDEFINED;
620
621                 if (param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO])
622                         prio =
623                             nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO]);
624
625                 if (param_tb[DCB_TC_ATTR_PARAM_PGID])
626                         pgid = nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_PGID]);
627
628                 if (param_tb[DCB_TC_ATTR_PARAM_BW_PCT])
629                         tc_pct = nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_BW_PCT]);
630
631                 if (param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING])
632                         up_map =
633                              nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING]);
634
635                 /* dir: Tx = 0, Rx = 1 */
636                 if (dir) {
637                         /* Rx */
638                         netdev->dcbnl_ops->setpgtccfgrx(netdev,
639                                 i - DCB_PG_ATTR_TC_0,
640                                 prio, pgid, tc_pct, up_map);
641                 } else {
642                         /* Tx */
643                         netdev->dcbnl_ops->setpgtccfgtx(netdev,
644                                 i - DCB_PG_ATTR_TC_0,
645                                 prio, pgid, tc_pct, up_map);
646                 }
647         }
648
649         for (i = DCB_PG_ATTR_BW_ID_0; i <= DCB_PG_ATTR_BW_ID_7; i++) {
650                 if (!pg_tb[i])
651                         continue;
652
653                 tc_pct = nla_get_u8(pg_tb[i]);
654
655                 /* dir: Tx = 0, Rx = 1 */
656                 if (dir) {
657                         /* Rx */
658                         netdev->dcbnl_ops->setpgbwgcfgrx(netdev,
659                                          i - DCB_PG_ATTR_BW_ID_0, tc_pct);
660                 } else {
661                         /* Tx */
662                         netdev->dcbnl_ops->setpgbwgcfgtx(netdev,
663                                          i - DCB_PG_ATTR_BW_ID_0, tc_pct);
664                 }
665         }
666
667         ret = dcbnl_reply(0, RTM_SETDCB,
668                           (dir ? DCB_CMD_PGRX_SCFG : DCB_CMD_PGTX_SCFG),
669                           DCB_ATTR_PG_CFG, pid, seq, flags);
670
671 err:
672         return ret;
673 }
674
675 static int dcbnl_pgtx_setcfg(struct net_device *netdev, struct nlattr **tb,
676                              u32 pid, u32 seq, u16 flags)
677 {
678         return __dcbnl_pg_setcfg(netdev, tb, pid, seq, flags, 0);
679 }
680
681 static int dcbnl_pgrx_setcfg(struct net_device *netdev, struct nlattr **tb,
682                              u32 pid, u32 seq, u16 flags)
683 {
684         return __dcbnl_pg_setcfg(netdev, tb, pid, seq, flags, 1);
685 }
686
687 static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
688 {
689         struct net *net = sock_net(skb->sk);
690         struct net_device *netdev;
691         struct dcbmsg  *dcb = (struct dcbmsg *)NLMSG_DATA(nlh);
692         struct nlattr *tb[DCB_ATTR_MAX + 1];
693         u32 pid = skb ? NETLINK_CB(skb).pid : 0;
694         int ret = -EINVAL;
695
696         if (net != &init_net)
697                 return -EINVAL;
698
699         ret = nlmsg_parse(nlh, sizeof(*dcb), tb, DCB_ATTR_MAX,
700                           dcbnl_rtnl_policy);
701         if (ret < 0)
702                 return ret;
703
704         if (!tb[DCB_ATTR_IFNAME])
705                 return -EINVAL;
706
707         netdev = dev_get_by_name(&init_net, nla_data(tb[DCB_ATTR_IFNAME]));
708         if (!netdev)
709                 return -EINVAL;
710
711         if (!netdev->dcbnl_ops)
712                 goto errout;
713
714         switch (dcb->cmd) {
715         case DCB_CMD_GSTATE:
716                 ret = dcbnl_getstate(netdev, tb, pid, nlh->nlmsg_seq,
717                                      nlh->nlmsg_flags);
718                 goto out;
719         case DCB_CMD_PFC_GCFG:
720                 ret = dcbnl_getpfccfg(netdev, tb, pid, nlh->nlmsg_seq,
721                                       nlh->nlmsg_flags);
722                 goto out;
723         case DCB_CMD_GPERM_HWADDR:
724                 ret = dcbnl_getperm_hwaddr(netdev, tb, pid, nlh->nlmsg_seq,
725                                            nlh->nlmsg_flags);
726                 goto out;
727         case DCB_CMD_PGTX_GCFG:
728                 ret = dcbnl_pgtx_getcfg(netdev, tb, pid, nlh->nlmsg_seq,
729                                         nlh->nlmsg_flags);
730                 goto out;
731         case DCB_CMD_PGRX_GCFG:
732                 ret = dcbnl_pgrx_getcfg(netdev, tb, pid, nlh->nlmsg_seq,
733                                         nlh->nlmsg_flags);
734                 goto out;
735         case DCB_CMD_SSTATE:
736                 ret = dcbnl_setstate(netdev, tb, pid, nlh->nlmsg_seq,
737                                      nlh->nlmsg_flags);
738                 goto out;
739         case DCB_CMD_PFC_SCFG:
740                 ret = dcbnl_setpfccfg(netdev, tb, pid, nlh->nlmsg_seq,
741                                       nlh->nlmsg_flags);
742                 goto out;
743
744         case DCB_CMD_SET_ALL:
745                 ret = dcbnl_setall(netdev, tb, pid, nlh->nlmsg_seq,
746                                    nlh->nlmsg_flags);
747                 goto out;
748         case DCB_CMD_PGTX_SCFG:
749                 ret = dcbnl_pgtx_setcfg(netdev, tb, pid, nlh->nlmsg_seq,
750                                         nlh->nlmsg_flags);
751                 goto out;
752         case DCB_CMD_PGRX_SCFG:
753                 ret = dcbnl_pgrx_setcfg(netdev, tb, pid, nlh->nlmsg_seq,
754                                         nlh->nlmsg_flags);
755                 goto out;
756         case DCB_CMD_GCAP:
757                 ret = dcbnl_getcap(netdev, tb, pid, nlh->nlmsg_seq,
758                                    nlh->nlmsg_flags);
759                 goto out;
760         default:
761                 goto errout;
762         }
763 errout:
764         ret = -EINVAL;
765 out:
766         dev_put(netdev);
767         return ret;
768 }
769
770 static int __init dcbnl_init(void)
771 {
772         rtnl_register(PF_UNSPEC, RTM_GETDCB, dcb_doit, NULL);
773         rtnl_register(PF_UNSPEC, RTM_SETDCB, dcb_doit, NULL);
774
775         return 0;
776 }
777 module_init(dcbnl_init);
778
779 static void __exit dcbnl_exit(void)
780 {
781         rtnl_unregister(PF_UNSPEC, RTM_GETDCB);
782         rtnl_unregister(PF_UNSPEC, RTM_SETDCB);
783 }
784 module_exit(dcbnl_exit);
785
786