blob: 5e392b85d48dfc4e7c39ed2e02c4f7d5cde7f107 [file] [log] [blame]
Alexander Duyck2f90b862008-11-20 20:52:10 -08001/*
Mark Rustad698e1d22011-03-14 09:01:02 +00002 * Copyright (c) 2008-2011, Intel Corporation.
Alexander Duyck2f90b862008-11-20 20:52:10 -08003 *
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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090022#include <linux/slab.h>
Alexander Duyck2f90b862008-11-20 20:52:10 -080023#include <net/netlink.h>
24#include <net/rtnetlink.h>
25#include <linux/dcbnl.h>
John Fastabend96b99682010-12-30 09:26:37 +000026#include <net/dcbevent.h>
Alexander Duyck2f90b862008-11-20 20:52:10 -080027#include <linux/rtnetlink.h>
Paul Gortmaker3a9a2312011-05-27 09:12:25 -040028#include <linux/module.h>
Alexander Duyck2f90b862008-11-20 20:52:10 -080029#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
53MODULE_AUTHOR("Lucy Liu, <lucy.liu@intel.com>");
Jeff Kirsher7a6b6f52008-11-25 01:02:08 -080054MODULE_DESCRIPTION("Data Center Bridging netlink interface");
Alexander Duyck2f90b862008-11-20 20:52:10 -080055MODULE_LICENSE("GPL");
56
57/**************** DCB attribute policies *************************************/
58
59/* DCB netlink attributes policy */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +000060static const struct nla_policy dcbnl_rtnl_policy[DCB_ATTR_MAX + 1] = {
Alexander Duyck859ee3c2008-11-20 21:10:23 -080061 [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},
Alexander Duyck2f90b862008-11-20 20:52:10 -080066 [DCB_ATTR_PERM_HWADDR] = {.type = NLA_FLAG},
Alexander Duyck859ee3c2008-11-20 21:10:23 -080067 [DCB_ATTR_CAP] = {.type = NLA_NESTED},
68 [DCB_ATTR_PFC_STATE] = {.type = NLA_U8},
69 [DCB_ATTR_BCN] = {.type = NLA_NESTED},
Yi Zou6fa382a2009-08-31 12:33:20 +000070 [DCB_ATTR_APP] = {.type = NLA_NESTED},
John Fastabend3e290272010-12-30 09:25:46 +000071 [DCB_ATTR_IEEE] = {.type = NLA_NESTED},
Shmulik Ravid6241b622010-12-30 06:26:48 +000072 [DCB_ATTR_DCBX] = {.type = NLA_U8},
Shmulik Ravidea45fe42010-12-30 06:26:55 +000073 [DCB_ATTR_FEATCFG] = {.type = NLA_NESTED},
Alexander Duyck2f90b862008-11-20 20:52:10 -080074};
75
76/* DCB priority flow control to User Priority nested attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +000077static const struct nla_policy dcbnl_pfc_up_nest[DCB_PFC_UP_ATTR_MAX + 1] = {
Alexander Duyck2f90b862008-11-20 20:52:10 -080078 [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 */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +000090static const struct nla_policy dcbnl_pg_nest[DCB_PG_ATTR_MAX + 1] = {
Alexander Duyck2f90b862008-11-20 20:52:10 -080091 [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. */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +0000112static const struct nla_policy dcbnl_tc_param_nest[DCB_TC_ATTR_PARAM_MAX + 1] = {
Alexander Duyck2f90b862008-11-20 20:52:10 -0800113 [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
Alexander Duyck46132182008-11-20 21:05:08 -0800120/* DCB capabilities nested attributes. */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +0000121static const struct nla_policy dcbnl_cap_nest[DCB_CAP_ATTR_MAX + 1] = {
Alexander Duyck46132182008-11-20 21:05:08 -0800122 [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},
Shmulik Ravid6241b622010-12-30 06:26:48 +0000130 [DCB_CAP_ATTR_DCBX] = {.type = NLA_U8},
Alexander Duyck46132182008-11-20 21:05:08 -0800131};
Alexander Duyck2f90b862008-11-20 20:52:10 -0800132
Alexander Duyck33dbabc2008-11-20 21:08:19 -0800133/* DCB capabilities nested attributes. */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +0000134static const struct nla_policy dcbnl_numtcs_nest[DCB_NUMTCS_ATTR_MAX + 1] = {
Alexander Duyck33dbabc2008-11-20 21:08:19 -0800135 [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
Alexander Duyck859ee3c2008-11-20 21:10:23 -0800140/* DCB BCN nested attributes. */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +0000141static const struct nla_policy dcbnl_bcn_nest[DCB_BCN_ATTR_MAX + 1] = {
Alexander Duyck859ee3c2008-11-20 21:10:23 -0800142 [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},
Don Skidmoref4314e82008-12-21 20:10:29 -0800151 [DCB_BCN_ATTR_BCNA_0] = {.type = NLA_U32},
152 [DCB_BCN_ATTR_BCNA_1] = {.type = NLA_U32},
Alexander Duyck859ee3c2008-11-20 21:10:23 -0800153 [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
Yi Zou6fa382a2009-08-31 12:33:20 +0000169/* DCB APP nested attributes. */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +0000170static const struct nla_policy dcbnl_app_nest[DCB_APP_ATTR_MAX + 1] = {
Yi Zou6fa382a2009-08-31 12:33:20 +0000171 [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
John Fastabend3e290272010-12-30 09:25:46 +0000176/* IEEE 802.1Qaz nested attributes. */
177static 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},
Amir Vadai08f10af2012-04-04 21:33:30 +0000181 [DCB_ATTR_IEEE_MAXRATE] = {.len = sizeof(struct ieee_maxrate)},
John Fastabend3e290272010-12-30 09:25:46 +0000182};
183
184static 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
Shmulik Ravidea45fe42010-12-30 06:26:55 +0000188/* DCB number of traffic classes nested attributes. */
189static 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
John Fastabend9ab933a2010-12-30 09:26:31 +0000196static LIST_HEAD(dcb_app_list);
197static DEFINE_SPINLOCK(dcb_lock);
198
Thomas Graf33a03aa2012-06-13 02:54:54 +0000199static struct sk_buff *dcbnl_newmsg(int type, u8 cmd, u32 port, u32 seq,
200 u32 flags, struct nlmsghdr **nlhp)
201{
202 struct sk_buff *skb;
203 struct dcbmsg *dcb;
204 struct nlmsghdr *nlh;
205
206 skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
207 if (!skb)
208 return NULL;
209
210 nlh = nlmsg_put(skb, port, seq, type, sizeof(*dcb), flags);
211 if (!nlh) {
212 /* header should always fit, allocation must be buggy */
213 BUG();
214 }
215
216 dcb = nlmsg_data(nlh);
217 dcb->dcb_family = AF_UNSPEC;
218 dcb->cmd = cmd;
219 dcb->dcb_pad = 0;
220
221 if (nlhp)
222 *nlhp = nlh;
223
224 return skb;
225}
226
Alexander Duyck2f90b862008-11-20 20:52:10 -0800227/* standard netlink reply call */
228static int dcbnl_reply(u8 value, u8 event, u8 cmd, u8 attr, u32 pid,
229 u32 seq, u16 flags)
230{
231 struct sk_buff *dcbnl_skb;
232 struct dcbmsg *dcb;
233 struct nlmsghdr *nlh;
234 int ret = -EINVAL;
235
236 dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
237 if (!dcbnl_skb)
238 return ret;
239
240 nlh = NLMSG_NEW(dcbnl_skb, pid, seq, event, sizeof(*dcb), flags);
241
242 dcb = NLMSG_DATA(nlh);
243 dcb->dcb_family = AF_UNSPEC;
244 dcb->cmd = cmd;
245 dcb->dcb_pad = 0;
246
247 ret = nla_put_u8(dcbnl_skb, attr, value);
248 if (ret)
249 goto err;
250
251 /* end the message, assign the nlmsg_len. */
252 nlmsg_end(dcbnl_skb, nlh);
253 ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
254 if (ret)
John Fastabend7eaf5072009-09-25 13:12:03 +0000255 return -EINVAL;
Alexander Duyck2f90b862008-11-20 20:52:10 -0800256
257 return 0;
258nlmsg_failure:
259err:
Roel Kluin858eb712009-01-04 17:29:21 -0800260 kfree_skb(dcbnl_skb);
Alexander Duyck2f90b862008-11-20 20:52:10 -0800261 return ret;
262}
263
Thomas Graf7be99412012-06-13 02:54:55 +0000264static int dcbnl_getstate(struct net_device *netdev, struct nlmsghdr *nlh,
265 u32 seq, struct nlattr **tb, struct sk_buff *skb)
Alexander Duyck2f90b862008-11-20 20:52:10 -0800266{
Alexander Duyck2f90b862008-11-20 20:52:10 -0800267 /* if (!tb[DCB_ATTR_STATE] || !netdev->dcbnl_ops->getstate) */
268 if (!netdev->dcbnl_ops->getstate)
Thomas Graf7be99412012-06-13 02:54:55 +0000269 return -EINVAL;
Alexander Duyck2f90b862008-11-20 20:52:10 -0800270
Thomas Graf7be99412012-06-13 02:54:55 +0000271 return nla_put_u8(skb, DCB_ATTR_STATE,
272 netdev->dcbnl_ops->getstate(netdev));
Alexander Duyck2f90b862008-11-20 20:52:10 -0800273}
274
Thomas Graf7be99412012-06-13 02:54:55 +0000275static int dcbnl_getpfccfg(struct net_device *netdev, struct nlmsghdr *nlh,
276 u32 seq, struct nlattr **tb, struct sk_buff *skb)
Alexander Duyck2f90b862008-11-20 20:52:10 -0800277{
Alexander Duyck2f90b862008-11-20 20:52:10 -0800278 struct nlattr *data[DCB_PFC_UP_ATTR_MAX + 1], *nest;
279 u8 value;
280 int ret = -EINVAL;
281 int i;
282 int getall = 0;
283
284 if (!tb[DCB_ATTR_PFC_CFG] || !netdev->dcbnl_ops->getpfccfg)
285 return ret;
286
287 ret = nla_parse_nested(data, DCB_PFC_UP_ATTR_MAX,
288 tb[DCB_ATTR_PFC_CFG],
289 dcbnl_pfc_up_nest);
290 if (ret)
Thomas Graf7be99412012-06-13 02:54:55 +0000291 goto err;
Alexander Duyck2f90b862008-11-20 20:52:10 -0800292
Thomas Graf7be99412012-06-13 02:54:55 +0000293 nest = nla_nest_start(skb, DCB_ATTR_PFC_CFG);
Alexander Duyck2f90b862008-11-20 20:52:10 -0800294 if (!nest)
295 goto err;
296
297 if (data[DCB_PFC_UP_ATTR_ALL])
298 getall = 1;
299
300 for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) {
301 if (!getall && !data[i])
302 continue;
303
304 netdev->dcbnl_ops->getpfccfg(netdev, i - DCB_PFC_UP_ATTR_0,
305 &value);
Thomas Graf7be99412012-06-13 02:54:55 +0000306 ret = nla_put_u8(skb, i, value);
Alexander Duyck2f90b862008-11-20 20:52:10 -0800307 if (ret) {
Thomas Graf7be99412012-06-13 02:54:55 +0000308 nla_nest_cancel(skb, nest);
Alexander Duyck2f90b862008-11-20 20:52:10 -0800309 goto err;
310 }
311 }
Thomas Graf7be99412012-06-13 02:54:55 +0000312 nla_nest_end(skb, nest);
Alexander Duyck2f90b862008-11-20 20:52:10 -0800313
314 return 0;
Alexander Duyck2f90b862008-11-20 20:52:10 -0800315err:
Alexander Duyck2f90b862008-11-20 20:52:10 -0800316 return -EINVAL;
317}
318
Thomas Graf7be99412012-06-13 02:54:55 +0000319static int dcbnl_getperm_hwaddr(struct net_device *netdev, struct nlmsghdr *nlh,
320 u32 seq, struct nlattr **tb, struct sk_buff *skb)
Alexander Duyck2f90b862008-11-20 20:52:10 -0800321{
Alexander Duyck2f90b862008-11-20 20:52:10 -0800322 u8 perm_addr[MAX_ADDR_LEN];
Alexander Duyck2f90b862008-11-20 20:52:10 -0800323
324 if (!netdev->dcbnl_ops->getpermhwaddr)
Thomas Graf7be99412012-06-13 02:54:55 +0000325 return -EINVAL;
Alexander Duyck2f90b862008-11-20 20:52:10 -0800326
327 netdev->dcbnl_ops->getpermhwaddr(netdev, perm_addr);
328
Thomas Graf7be99412012-06-13 02:54:55 +0000329 return nla_put(skb, DCB_ATTR_PERM_HWADDR, sizeof(perm_addr), perm_addr);
Alexander Duyck2f90b862008-11-20 20:52:10 -0800330}
331
Thomas Graf7be99412012-06-13 02:54:55 +0000332static int dcbnl_getcap(struct net_device *netdev, struct nlmsghdr *nlh,
333 u32 seq, struct nlattr **tb, struct sk_buff *skb)
Alexander Duyck46132182008-11-20 21:05:08 -0800334{
Alexander Duyck46132182008-11-20 21:05:08 -0800335 struct nlattr *data[DCB_CAP_ATTR_MAX + 1], *nest;
336 u8 value;
337 int ret = -EINVAL;
338 int i;
339 int getall = 0;
340
341 if (!tb[DCB_ATTR_CAP] || !netdev->dcbnl_ops->getcap)
342 return ret;
343
344 ret = nla_parse_nested(data, DCB_CAP_ATTR_MAX, tb[DCB_ATTR_CAP],
345 dcbnl_cap_nest);
346 if (ret)
347 goto err_out;
348
Thomas Graf7be99412012-06-13 02:54:55 +0000349 nest = nla_nest_start(skb, DCB_ATTR_CAP);
Alexander Duyck46132182008-11-20 21:05:08 -0800350 if (!nest)
Thomas Graf7be99412012-06-13 02:54:55 +0000351 goto err_out;
Alexander Duyck46132182008-11-20 21:05:08 -0800352
353 if (data[DCB_CAP_ATTR_ALL])
354 getall = 1;
355
356 for (i = DCB_CAP_ATTR_ALL+1; i <= DCB_CAP_ATTR_MAX; i++) {
357 if (!getall && !data[i])
358 continue;
359
360 if (!netdev->dcbnl_ops->getcap(netdev, i, &value)) {
Thomas Graf7be99412012-06-13 02:54:55 +0000361 ret = nla_put_u8(skb, i, value);
Alexander Duyck46132182008-11-20 21:05:08 -0800362 if (ret) {
Thomas Graf7be99412012-06-13 02:54:55 +0000363 nla_nest_cancel(skb, nest);
364 goto err_out;
Alexander Duyck46132182008-11-20 21:05:08 -0800365 }
366 }
367 }
Thomas Graf7be99412012-06-13 02:54:55 +0000368 nla_nest_end(skb, nest);
Alexander Duyck46132182008-11-20 21:05:08 -0800369
370 return 0;
Alexander Duyck46132182008-11-20 21:05:08 -0800371err_out:
372 return -EINVAL;
373}
374
Thomas Graf7be99412012-06-13 02:54:55 +0000375static int dcbnl_getnumtcs(struct net_device *netdev, struct nlmsghdr *nlh,
376 u32 seq, struct nlattr **tb, struct sk_buff *skb)
Alexander Duyck33dbabc2008-11-20 21:08:19 -0800377{
Alexander Duyck33dbabc2008-11-20 21:08:19 -0800378 struct nlattr *data[DCB_NUMTCS_ATTR_MAX + 1], *nest;
379 u8 value;
380 int ret = -EINVAL;
381 int i;
382 int getall = 0;
383
384 if (!tb[DCB_ATTR_NUMTCS] || !netdev->dcbnl_ops->getnumtcs)
385 return ret;
386
387 ret = nla_parse_nested(data, DCB_NUMTCS_ATTR_MAX, tb[DCB_ATTR_NUMTCS],
388 dcbnl_numtcs_nest);
389 if (ret) {
390 ret = -EINVAL;
391 goto err_out;
392 }
393
Thomas Graf7be99412012-06-13 02:54:55 +0000394 nest = nla_nest_start(skb, DCB_ATTR_NUMTCS);
Alexander Duyck33dbabc2008-11-20 21:08:19 -0800395 if (!nest) {
396 ret = -EINVAL;
Thomas Graf7be99412012-06-13 02:54:55 +0000397 goto err_out;
Alexander Duyck33dbabc2008-11-20 21:08:19 -0800398 }
399
400 if (data[DCB_NUMTCS_ATTR_ALL])
401 getall = 1;
402
403 for (i = DCB_NUMTCS_ATTR_ALL+1; i <= DCB_NUMTCS_ATTR_MAX; i++) {
404 if (!getall && !data[i])
405 continue;
406
407 ret = netdev->dcbnl_ops->getnumtcs(netdev, i, &value);
408 if (!ret) {
Thomas Graf7be99412012-06-13 02:54:55 +0000409 ret = nla_put_u8(skb, i, value);
Alexander Duyck33dbabc2008-11-20 21:08:19 -0800410 if (ret) {
Thomas Graf7be99412012-06-13 02:54:55 +0000411 nla_nest_cancel(skb, nest);
Alexander Duyck33dbabc2008-11-20 21:08:19 -0800412 ret = -EINVAL;
Thomas Graf7be99412012-06-13 02:54:55 +0000413 goto err_out;
Alexander Duyck33dbabc2008-11-20 21:08:19 -0800414 }
415 } else {
Thomas Graf7be99412012-06-13 02:54:55 +0000416 goto err_out;
Alexander Duyck33dbabc2008-11-20 21:08:19 -0800417 }
418 }
Thomas Graf7be99412012-06-13 02:54:55 +0000419 nla_nest_end(skb, nest);
Alexander Duyck33dbabc2008-11-20 21:08:19 -0800420
421 return 0;
Alexander Duyck33dbabc2008-11-20 21:08:19 -0800422err_out:
423 return ret;
424}
425
Thomas Graf7be99412012-06-13 02:54:55 +0000426static int dcbnl_setnumtcs(struct net_device *netdev, struct nlmsghdr *nlh,
427 u32 seq, struct nlattr **tb, struct sk_buff *skb)
Alexander Duyck33dbabc2008-11-20 21:08:19 -0800428{
429 struct nlattr *data[DCB_NUMTCS_ATTR_MAX + 1];
430 int ret = -EINVAL;
431 u8 value;
432 int i;
433
Don Skidmore8b124a82008-12-15 01:06:23 -0800434 if (!tb[DCB_ATTR_NUMTCS] || !netdev->dcbnl_ops->setnumtcs)
Alexander Duyck33dbabc2008-11-20 21:08:19 -0800435 return ret;
436
437 ret = nla_parse_nested(data, DCB_NUMTCS_ATTR_MAX, tb[DCB_ATTR_NUMTCS],
438 dcbnl_numtcs_nest);
439
Thomas Graf7be99412012-06-13 02:54:55 +0000440 if (ret)
441 return -EINVAL;
Alexander Duyck33dbabc2008-11-20 21:08:19 -0800442
443 for (i = DCB_NUMTCS_ATTR_ALL+1; i <= DCB_NUMTCS_ATTR_MAX; i++) {
444 if (data[i] == NULL)
445 continue;
446
447 value = nla_get_u8(data[i]);
448
449 ret = netdev->dcbnl_ops->setnumtcs(netdev, i, value);
Alexander Duyck33dbabc2008-11-20 21:08:19 -0800450 if (ret)
Thomas Graf7be99412012-06-13 02:54:55 +0000451 break;
Alexander Duyck33dbabc2008-11-20 21:08:19 -0800452 }
453
Thomas Graf7be99412012-06-13 02:54:55 +0000454 return nla_put_u8(skb, DCB_ATTR_NUMTCS, !!ret);
Alexander Duyck33dbabc2008-11-20 21:08:19 -0800455}
456
Thomas Graf7be99412012-06-13 02:54:55 +0000457static int dcbnl_getpfcstate(struct net_device *netdev, struct nlmsghdr *nlh,
458 u32 seq, struct nlattr **tb, struct sk_buff *skb)
Alexander Duyck0eb3aa92008-11-20 21:09:23 -0800459{
Alexander Duyck0eb3aa92008-11-20 21:09:23 -0800460 if (!netdev->dcbnl_ops->getpfcstate)
Thomas Graf7be99412012-06-13 02:54:55 +0000461 return -EINVAL;
Alexander Duyck0eb3aa92008-11-20 21:09:23 -0800462
Thomas Graf7be99412012-06-13 02:54:55 +0000463 return nla_put_u8(skb, DCB_ATTR_PFC_STATE,
464 netdev->dcbnl_ops->getpfcstate(netdev));
Alexander Duyck0eb3aa92008-11-20 21:09:23 -0800465}
466
Thomas Graf7be99412012-06-13 02:54:55 +0000467static int dcbnl_setpfcstate(struct net_device *netdev, struct nlmsghdr *nlh,
468 u32 seq, struct nlattr **tb, struct sk_buff *skb)
Alexander Duyck0eb3aa92008-11-20 21:09:23 -0800469{
Alexander Duyck0eb3aa92008-11-20 21:09:23 -0800470 u8 value;
471
472 if (!tb[DCB_ATTR_PFC_STATE] || !netdev->dcbnl_ops->setpfcstate)
Thomas Graf7be99412012-06-13 02:54:55 +0000473 return -EINVAL;
Alexander Duyck0eb3aa92008-11-20 21:09:23 -0800474
475 value = nla_get_u8(tb[DCB_ATTR_PFC_STATE]);
476
477 netdev->dcbnl_ops->setpfcstate(netdev, value);
478
Thomas Graf7be99412012-06-13 02:54:55 +0000479 return nla_put_u8(skb, DCB_ATTR_PFC_STATE, 0);
Alexander Duyck0eb3aa92008-11-20 21:09:23 -0800480}
481
Thomas Graf7be99412012-06-13 02:54:55 +0000482static int dcbnl_getapp(struct net_device *netdev, struct nlmsghdr *nlh,
483 u32 seq, struct nlattr **tb, struct sk_buff *skb)
Yi Zou57949682009-08-31 12:33:40 +0000484{
Yi Zou57949682009-08-31 12:33:40 +0000485 struct nlattr *app_nest;
486 struct nlattr *app_tb[DCB_APP_ATTR_MAX + 1];
487 u16 id;
488 u8 up, idtype;
489 int ret = -EINVAL;
490
John Fastabend3dce38a2011-01-21 16:35:18 +0000491 if (!tb[DCB_ATTR_APP])
Yi Zou57949682009-08-31 12:33:40 +0000492 goto out;
493
494 ret = nla_parse_nested(app_tb, DCB_APP_ATTR_MAX, tb[DCB_ATTR_APP],
495 dcbnl_app_nest);
496 if (ret)
497 goto out;
498
499 ret = -EINVAL;
500 /* all must be non-null */
501 if ((!app_tb[DCB_APP_ATTR_IDTYPE]) ||
502 (!app_tb[DCB_APP_ATTR_ID]))
503 goto out;
504
505 /* either by eth type or by socket number */
506 idtype = nla_get_u8(app_tb[DCB_APP_ATTR_IDTYPE]);
507 if ((idtype != DCB_APP_IDTYPE_ETHTYPE) &&
508 (idtype != DCB_APP_IDTYPE_PORTNUM))
509 goto out;
510
511 id = nla_get_u16(app_tb[DCB_APP_ATTR_ID]);
John Fastabend3dce38a2011-01-21 16:35:18 +0000512
513 if (netdev->dcbnl_ops->getapp) {
514 up = netdev->dcbnl_ops->getapp(netdev, idtype, id);
515 } else {
516 struct dcb_app app = {
517 .selector = idtype,
518 .protocol = id,
519 };
520 up = dcb_getapp(netdev, &app);
521 }
Yi Zou57949682009-08-31 12:33:40 +0000522
Thomas Graf7be99412012-06-13 02:54:55 +0000523 app_nest = nla_nest_start(skb, DCB_ATTR_APP);
524 if (!app_nest)
Yi Zou57949682009-08-31 12:33:40 +0000525 goto out;
526
Thomas Graf7be99412012-06-13 02:54:55 +0000527 ret = nla_put_u8(skb, DCB_APP_ATTR_IDTYPE, idtype);
Yi Zou57949682009-08-31 12:33:40 +0000528 if (ret)
529 goto out_cancel;
530
Thomas Graf7be99412012-06-13 02:54:55 +0000531 ret = nla_put_u16(skb, DCB_APP_ATTR_ID, id);
Yi Zou57949682009-08-31 12:33:40 +0000532 if (ret)
533 goto out_cancel;
534
Thomas Graf7be99412012-06-13 02:54:55 +0000535 ret = nla_put_u8(skb, DCB_APP_ATTR_PRIORITY, up);
Yi Zou57949682009-08-31 12:33:40 +0000536 if (ret)
537 goto out_cancel;
538
Thomas Graf7be99412012-06-13 02:54:55 +0000539 nla_nest_end(skb, app_nest);
Yi Zou57949682009-08-31 12:33:40 +0000540
541 goto out;
542
543out_cancel:
Thomas Graf7be99412012-06-13 02:54:55 +0000544 nla_nest_cancel(skb, app_nest);
Yi Zou57949682009-08-31 12:33:40 +0000545out:
546 return ret;
547}
548
Thomas Graf7be99412012-06-13 02:54:55 +0000549static int dcbnl_setapp(struct net_device *netdev, struct nlmsghdr *nlh,
550 u32 seq, struct nlattr **tb, struct sk_buff *skb)
Yi Zou57949682009-08-31 12:33:40 +0000551{
John Fastabend9ab933a2010-12-30 09:26:31 +0000552 int err, ret = -EINVAL;
Yi Zou57949682009-08-31 12:33:40 +0000553 u16 id;
554 u8 up, idtype;
555 struct nlattr *app_tb[DCB_APP_ATTR_MAX + 1];
556
John Fastabend9ab933a2010-12-30 09:26:31 +0000557 if (!tb[DCB_ATTR_APP])
Yi Zou57949682009-08-31 12:33:40 +0000558 goto out;
559
560 ret = nla_parse_nested(app_tb, DCB_APP_ATTR_MAX, tb[DCB_ATTR_APP],
561 dcbnl_app_nest);
562 if (ret)
563 goto out;
564
565 ret = -EINVAL;
566 /* all must be non-null */
567 if ((!app_tb[DCB_APP_ATTR_IDTYPE]) ||
568 (!app_tb[DCB_APP_ATTR_ID]) ||
569 (!app_tb[DCB_APP_ATTR_PRIORITY]))
570 goto out;
571
572 /* either by eth type or by socket number */
573 idtype = nla_get_u8(app_tb[DCB_APP_ATTR_IDTYPE]);
574 if ((idtype != DCB_APP_IDTYPE_ETHTYPE) &&
575 (idtype != DCB_APP_IDTYPE_PORTNUM))
576 goto out;
577
578 id = nla_get_u16(app_tb[DCB_APP_ATTR_ID]);
579 up = nla_get_u8(app_tb[DCB_APP_ATTR_PRIORITY]);
580
John Fastabend9ab933a2010-12-30 09:26:31 +0000581 if (netdev->dcbnl_ops->setapp) {
582 err = netdev->dcbnl_ops->setapp(netdev, idtype, id, up);
583 } else {
584 struct dcb_app app;
585 app.selector = idtype;
586 app.protocol = id;
587 app.priority = up;
588 err = dcb_setapp(netdev, &app);
589 }
590
Thomas Graf7be99412012-06-13 02:54:55 +0000591 ret = nla_put_u8(skb, DCB_ATTR_APP, ret);
John Fastabend08157982012-04-20 09:49:23 +0000592 dcbnl_cee_notify(netdev, RTM_SETDCB, DCB_CMD_SAPP, seq, 0);
Yi Zou57949682009-08-31 12:33:40 +0000593out:
594 return ret;
595}
596
Thomas Graf7be99412012-06-13 02:54:55 +0000597static int __dcbnl_pg_getcfg(struct net_device *netdev, struct nlmsghdr *nlh,
598 struct nlattr **tb, struct sk_buff *skb, int dir)
Alexander Duyck2f90b862008-11-20 20:52:10 -0800599{
Alexander Duyck2f90b862008-11-20 20:52:10 -0800600 struct nlattr *pg_nest, *param_nest, *data;
601 struct nlattr *pg_tb[DCB_PG_ATTR_MAX + 1];
602 struct nlattr *param_tb[DCB_TC_ATTR_PARAM_MAX + 1];
603 u8 prio, pgid, tc_pct, up_map;
604 int ret = -EINVAL;
605 int getall = 0;
606 int i;
607
608 if (!tb[DCB_ATTR_PG_CFG] ||
609 !netdev->dcbnl_ops->getpgtccfgtx ||
610 !netdev->dcbnl_ops->getpgtccfgrx ||
611 !netdev->dcbnl_ops->getpgbwgcfgtx ||
612 !netdev->dcbnl_ops->getpgbwgcfgrx)
613 return ret;
614
615 ret = nla_parse_nested(pg_tb, DCB_PG_ATTR_MAX,
616 tb[DCB_ATTR_PG_CFG], dcbnl_pg_nest);
617
618 if (ret)
619 goto err_out;
620
Thomas Graf7be99412012-06-13 02:54:55 +0000621 pg_nest = nla_nest_start(skb, DCB_ATTR_PG_CFG);
Alexander Duyck2f90b862008-11-20 20:52:10 -0800622 if (!pg_nest)
Thomas Graf7be99412012-06-13 02:54:55 +0000623 goto err_out;
Alexander Duyck2f90b862008-11-20 20:52:10 -0800624
625 if (pg_tb[DCB_PG_ATTR_TC_ALL])
626 getall = 1;
627
628 for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) {
629 if (!getall && !pg_tb[i])
630 continue;
631
632 if (pg_tb[DCB_PG_ATTR_TC_ALL])
633 data = pg_tb[DCB_PG_ATTR_TC_ALL];
634 else
635 data = pg_tb[i];
636 ret = nla_parse_nested(param_tb, DCB_TC_ATTR_PARAM_MAX,
637 data, dcbnl_tc_param_nest);
638 if (ret)
639 goto err_pg;
640
Thomas Graf7be99412012-06-13 02:54:55 +0000641 param_nest = nla_nest_start(skb, i);
Alexander Duyck2f90b862008-11-20 20:52:10 -0800642 if (!param_nest)
643 goto err_pg;
644
645 pgid = DCB_ATTR_VALUE_UNDEFINED;
646 prio = DCB_ATTR_VALUE_UNDEFINED;
647 tc_pct = DCB_ATTR_VALUE_UNDEFINED;
648 up_map = DCB_ATTR_VALUE_UNDEFINED;
649
650 if (dir) {
651 /* Rx */
652 netdev->dcbnl_ops->getpgtccfgrx(netdev,
653 i - DCB_PG_ATTR_TC_0, &prio,
654 &pgid, &tc_pct, &up_map);
655 } else {
656 /* Tx */
657 netdev->dcbnl_ops->getpgtccfgtx(netdev,
658 i - DCB_PG_ATTR_TC_0, &prio,
659 &pgid, &tc_pct, &up_map);
660 }
661
662 if (param_tb[DCB_TC_ATTR_PARAM_PGID] ||
663 param_tb[DCB_TC_ATTR_PARAM_ALL]) {
Thomas Graf7be99412012-06-13 02:54:55 +0000664 ret = nla_put_u8(skb,
Alexander Duyck2f90b862008-11-20 20:52:10 -0800665 DCB_TC_ATTR_PARAM_PGID, pgid);
666 if (ret)
667 goto err_param;
668 }
669 if (param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING] ||
670 param_tb[DCB_TC_ATTR_PARAM_ALL]) {
Thomas Graf7be99412012-06-13 02:54:55 +0000671 ret = nla_put_u8(skb,
Alexander Duyck2f90b862008-11-20 20:52:10 -0800672 DCB_TC_ATTR_PARAM_UP_MAPPING, up_map);
673 if (ret)
674 goto err_param;
675 }
676 if (param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO] ||
677 param_tb[DCB_TC_ATTR_PARAM_ALL]) {
Thomas Graf7be99412012-06-13 02:54:55 +0000678 ret = nla_put_u8(skb,
Alexander Duyck2f90b862008-11-20 20:52:10 -0800679 DCB_TC_ATTR_PARAM_STRICT_PRIO, prio);
680 if (ret)
681 goto err_param;
682 }
683 if (param_tb[DCB_TC_ATTR_PARAM_BW_PCT] ||
684 param_tb[DCB_TC_ATTR_PARAM_ALL]) {
Thomas Graf7be99412012-06-13 02:54:55 +0000685 ret = nla_put_u8(skb, DCB_TC_ATTR_PARAM_BW_PCT,
Alexander Duyck2f90b862008-11-20 20:52:10 -0800686 tc_pct);
687 if (ret)
688 goto err_param;
689 }
Thomas Graf7be99412012-06-13 02:54:55 +0000690 nla_nest_end(skb, param_nest);
Alexander Duyck2f90b862008-11-20 20:52:10 -0800691 }
692
693 if (pg_tb[DCB_PG_ATTR_BW_ID_ALL])
694 getall = 1;
695 else
696 getall = 0;
697
698 for (i = DCB_PG_ATTR_BW_ID_0; i <= DCB_PG_ATTR_BW_ID_7; i++) {
699 if (!getall && !pg_tb[i])
700 continue;
701
702 tc_pct = DCB_ATTR_VALUE_UNDEFINED;
703
704 if (dir) {
705 /* Rx */
706 netdev->dcbnl_ops->getpgbwgcfgrx(netdev,
707 i - DCB_PG_ATTR_BW_ID_0, &tc_pct);
708 } else {
709 /* Tx */
710 netdev->dcbnl_ops->getpgbwgcfgtx(netdev,
711 i - DCB_PG_ATTR_BW_ID_0, &tc_pct);
712 }
Thomas Graf7be99412012-06-13 02:54:55 +0000713 ret = nla_put_u8(skb, i, tc_pct);
Alexander Duyck2f90b862008-11-20 20:52:10 -0800714
715 if (ret)
716 goto err_pg;
717 }
718
Thomas Graf7be99412012-06-13 02:54:55 +0000719 nla_nest_end(skb, pg_nest);
Alexander Duyck2f90b862008-11-20 20:52:10 -0800720
721 return 0;
722
723err_param:
Thomas Graf7be99412012-06-13 02:54:55 +0000724 nla_nest_cancel(skb, param_nest);
Alexander Duyck2f90b862008-11-20 20:52:10 -0800725err_pg:
Thomas Graf7be99412012-06-13 02:54:55 +0000726 nla_nest_cancel(skb, pg_nest);
Alexander Duyck2f90b862008-11-20 20:52:10 -0800727err_out:
728 ret = -EINVAL;
729 return ret;
730}
731
Thomas Graf7be99412012-06-13 02:54:55 +0000732static int dcbnl_pgtx_getcfg(struct net_device *netdev, struct nlmsghdr *nlh,
733 u32 seq, struct nlattr **tb, struct sk_buff *skb)
Alexander Duyck2f90b862008-11-20 20:52:10 -0800734{
Thomas Graf7be99412012-06-13 02:54:55 +0000735 return __dcbnl_pg_getcfg(netdev, nlh, tb, skb, 0);
Alexander Duyck2f90b862008-11-20 20:52:10 -0800736}
737
Thomas Graf7be99412012-06-13 02:54:55 +0000738static int dcbnl_pgrx_getcfg(struct net_device *netdev, struct nlmsghdr *nlh,
739 u32 seq, struct nlattr **tb, struct sk_buff *skb)
Alexander Duyck2f90b862008-11-20 20:52:10 -0800740{
Thomas Graf7be99412012-06-13 02:54:55 +0000741 return __dcbnl_pg_getcfg(netdev, nlh, tb, skb, 1);
Alexander Duyck2f90b862008-11-20 20:52:10 -0800742}
743
Thomas Graf7be99412012-06-13 02:54:55 +0000744static int dcbnl_setstate(struct net_device *netdev, struct nlmsghdr *nlh,
745 u32 seq, struct nlattr **tb, struct sk_buff *skb)
Alexander Duyck2f90b862008-11-20 20:52:10 -0800746{
Alexander Duyck2f90b862008-11-20 20:52:10 -0800747 u8 value;
748
749 if (!tb[DCB_ATTR_STATE] || !netdev->dcbnl_ops->setstate)
Thomas Graf7be99412012-06-13 02:54:55 +0000750 return -EINVAL;
Alexander Duyck2f90b862008-11-20 20:52:10 -0800751
752 value = nla_get_u8(tb[DCB_ATTR_STATE]);
753
Thomas Graf7be99412012-06-13 02:54:55 +0000754 return nla_put_u8(skb, DCB_ATTR_STATE,
755 netdev->dcbnl_ops->setstate(netdev, value));
Alexander Duyck2f90b862008-11-20 20:52:10 -0800756}
757
Thomas Graf7be99412012-06-13 02:54:55 +0000758static int dcbnl_setpfccfg(struct net_device *netdev, struct nlmsghdr *nlh,
759 u32 seq, struct nlattr **tb, struct sk_buff *skb)
Alexander Duyck2f90b862008-11-20 20:52:10 -0800760{
761 struct nlattr *data[DCB_PFC_UP_ATTR_MAX + 1];
762 int i;
763 int ret = -EINVAL;
764 u8 value;
765
766 if (!tb[DCB_ATTR_PFC_CFG] || !netdev->dcbnl_ops->setpfccfg)
767 return ret;
768
769 ret = nla_parse_nested(data, DCB_PFC_UP_ATTR_MAX,
770 tb[DCB_ATTR_PFC_CFG],
771 dcbnl_pfc_up_nest);
772 if (ret)
773 goto err;
774
775 for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) {
776 if (data[i] == NULL)
777 continue;
778 value = nla_get_u8(data[i]);
779 netdev->dcbnl_ops->setpfccfg(netdev,
780 data[i]->nla_type - DCB_PFC_UP_ATTR_0, value);
781 }
782
Thomas Graf7be99412012-06-13 02:54:55 +0000783 return nla_put_u8(skb, DCB_ATTR_PFC_CFG, 0);
Alexander Duyck2f90b862008-11-20 20:52:10 -0800784err:
785 return ret;
786}
787
Thomas Graf7be99412012-06-13 02:54:55 +0000788static int dcbnl_setall(struct net_device *netdev, struct nlmsghdr *nlh,
789 u32 seq, struct nlattr **tb, struct sk_buff *skb)
Alexander Duyck2f90b862008-11-20 20:52:10 -0800790{
791 int ret = -EINVAL;
792
793 if (!tb[DCB_ATTR_SET_ALL] || !netdev->dcbnl_ops->setall)
794 return ret;
795
Thomas Graf7be99412012-06-13 02:54:55 +0000796 ret = nla_put_u8(skb, DCB_ATTR_SET_ALL,
797 netdev->dcbnl_ops->setall(netdev));
John Fastabend08157982012-04-20 09:49:23 +0000798 dcbnl_cee_notify(netdev, RTM_SETDCB, DCB_CMD_SET_ALL, seq, 0);
Alexander Duyck2f90b862008-11-20 20:52:10 -0800799
800 return ret;
801}
802
Thomas Graf7be99412012-06-13 02:54:55 +0000803static int __dcbnl_pg_setcfg(struct net_device *netdev, struct nlmsghdr *nlh,
804 u32 seq, struct nlattr **tb, struct sk_buff *skb,
805 int dir)
Alexander Duyck2f90b862008-11-20 20:52:10 -0800806{
807 struct nlattr *pg_tb[DCB_PG_ATTR_MAX + 1];
808 struct nlattr *param_tb[DCB_TC_ATTR_PARAM_MAX + 1];
809 int ret = -EINVAL;
810 int i;
811 u8 pgid;
812 u8 up_map;
813 u8 prio;
814 u8 tc_pct;
815
816 if (!tb[DCB_ATTR_PG_CFG] ||
817 !netdev->dcbnl_ops->setpgtccfgtx ||
818 !netdev->dcbnl_ops->setpgtccfgrx ||
819 !netdev->dcbnl_ops->setpgbwgcfgtx ||
820 !netdev->dcbnl_ops->setpgbwgcfgrx)
821 return ret;
822
823 ret = nla_parse_nested(pg_tb, DCB_PG_ATTR_MAX,
824 tb[DCB_ATTR_PG_CFG], dcbnl_pg_nest);
825 if (ret)
826 goto err;
827
828 for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) {
829 if (!pg_tb[i])
830 continue;
831
832 ret = nla_parse_nested(param_tb, DCB_TC_ATTR_PARAM_MAX,
833 pg_tb[i], dcbnl_tc_param_nest);
834 if (ret)
835 goto err;
836
837 pgid = DCB_ATTR_VALUE_UNDEFINED;
838 prio = DCB_ATTR_VALUE_UNDEFINED;
839 tc_pct = DCB_ATTR_VALUE_UNDEFINED;
840 up_map = DCB_ATTR_VALUE_UNDEFINED;
841
842 if (param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO])
843 prio =
844 nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO]);
845
846 if (param_tb[DCB_TC_ATTR_PARAM_PGID])
847 pgid = nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_PGID]);
848
849 if (param_tb[DCB_TC_ATTR_PARAM_BW_PCT])
850 tc_pct = nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_BW_PCT]);
851
852 if (param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING])
853 up_map =
854 nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING]);
855
856 /* dir: Tx = 0, Rx = 1 */
857 if (dir) {
858 /* Rx */
859 netdev->dcbnl_ops->setpgtccfgrx(netdev,
860 i - DCB_PG_ATTR_TC_0,
861 prio, pgid, tc_pct, up_map);
862 } else {
863 /* Tx */
864 netdev->dcbnl_ops->setpgtccfgtx(netdev,
865 i - DCB_PG_ATTR_TC_0,
866 prio, pgid, tc_pct, up_map);
867 }
868 }
869
870 for (i = DCB_PG_ATTR_BW_ID_0; i <= DCB_PG_ATTR_BW_ID_7; i++) {
871 if (!pg_tb[i])
872 continue;
873
874 tc_pct = nla_get_u8(pg_tb[i]);
875
876 /* dir: Tx = 0, Rx = 1 */
877 if (dir) {
878 /* Rx */
879 netdev->dcbnl_ops->setpgbwgcfgrx(netdev,
880 i - DCB_PG_ATTR_BW_ID_0, tc_pct);
881 } else {
882 /* Tx */
883 netdev->dcbnl_ops->setpgbwgcfgtx(netdev,
884 i - DCB_PG_ATTR_BW_ID_0, tc_pct);
885 }
886 }
887
Thomas Graf7be99412012-06-13 02:54:55 +0000888 ret = nla_put_u8(skb, (dir ? DCB_CMD_PGRX_SCFG : DCB_CMD_PGTX_SCFG), 0);
Alexander Duyck2f90b862008-11-20 20:52:10 -0800889
890err:
891 return ret;
892}
893
Thomas Graf7be99412012-06-13 02:54:55 +0000894static int dcbnl_pgtx_setcfg(struct net_device *netdev, struct nlmsghdr *nlh,
895 u32 seq, struct nlattr **tb, struct sk_buff *skb)
Alexander Duyck2f90b862008-11-20 20:52:10 -0800896{
Thomas Graf7be99412012-06-13 02:54:55 +0000897 return __dcbnl_pg_setcfg(netdev, nlh, seq, tb, skb, 0);
Alexander Duyck2f90b862008-11-20 20:52:10 -0800898}
899
Thomas Graf7be99412012-06-13 02:54:55 +0000900static int dcbnl_pgrx_setcfg(struct net_device *netdev, struct nlmsghdr *nlh,
901 u32 seq, struct nlattr **tb, struct sk_buff *skb)
Alexander Duyck2f90b862008-11-20 20:52:10 -0800902{
Thomas Graf7be99412012-06-13 02:54:55 +0000903 return __dcbnl_pg_setcfg(netdev, nlh, seq, tb, skb, 1);
Alexander Duyck2f90b862008-11-20 20:52:10 -0800904}
905
Thomas Graf7be99412012-06-13 02:54:55 +0000906static int dcbnl_bcn_getcfg(struct net_device *netdev, struct nlmsghdr *nlh,
907 u32 seq, struct nlattr **tb, struct sk_buff *skb)
Alexander Duyck859ee3c2008-11-20 21:10:23 -0800908{
Alexander Duyck859ee3c2008-11-20 21:10:23 -0800909 struct nlattr *bcn_nest;
910 struct nlattr *bcn_tb[DCB_BCN_ATTR_MAX + 1];
911 u8 value_byte;
912 u32 value_integer;
913 int ret = -EINVAL;
914 bool getall = false;
915 int i;
916
917 if (!tb[DCB_ATTR_BCN] || !netdev->dcbnl_ops->getbcnrp ||
918 !netdev->dcbnl_ops->getbcncfg)
919 return ret;
920
921 ret = nla_parse_nested(bcn_tb, DCB_BCN_ATTR_MAX,
922 tb[DCB_ATTR_BCN], dcbnl_bcn_nest);
923
924 if (ret)
925 goto err_out;
926
Thomas Graf7be99412012-06-13 02:54:55 +0000927 bcn_nest = nla_nest_start(skb, DCB_ATTR_BCN);
Alexander Duyck859ee3c2008-11-20 21:10:23 -0800928 if (!bcn_nest)
Thomas Graf7be99412012-06-13 02:54:55 +0000929 goto err_out;
Alexander Duyck859ee3c2008-11-20 21:10:23 -0800930
931 if (bcn_tb[DCB_BCN_ATTR_ALL])
932 getall = true;
933
934 for (i = DCB_BCN_ATTR_RP_0; i <= DCB_BCN_ATTR_RP_7; i++) {
935 if (!getall && !bcn_tb[i])
936 continue;
937
938 netdev->dcbnl_ops->getbcnrp(netdev, i - DCB_BCN_ATTR_RP_0,
939 &value_byte);
Thomas Graf7be99412012-06-13 02:54:55 +0000940 ret = nla_put_u8(skb, i, value_byte);
Alexander Duyck859ee3c2008-11-20 21:10:23 -0800941 if (ret)
942 goto err_bcn;
943 }
944
Don Skidmoref4314e82008-12-21 20:10:29 -0800945 for (i = DCB_BCN_ATTR_BCNA_0; i <= DCB_BCN_ATTR_RI; i++) {
Alexander Duyck859ee3c2008-11-20 21:10:23 -0800946 if (!getall && !bcn_tb[i])
947 continue;
948
949 netdev->dcbnl_ops->getbcncfg(netdev, i,
950 &value_integer);
Thomas Graf7be99412012-06-13 02:54:55 +0000951 ret = nla_put_u32(skb, i, value_integer);
Alexander Duyck859ee3c2008-11-20 21:10:23 -0800952 if (ret)
953 goto err_bcn;
954 }
955
Thomas Graf7be99412012-06-13 02:54:55 +0000956 nla_nest_end(skb, bcn_nest);
Alexander Duyck859ee3c2008-11-20 21:10:23 -0800957
958 return 0;
959
960err_bcn:
Thomas Graf7be99412012-06-13 02:54:55 +0000961 nla_nest_cancel(skb, bcn_nest);
Alexander Duyck859ee3c2008-11-20 21:10:23 -0800962err_out:
963 ret = -EINVAL;
964 return ret;
965}
966
Thomas Graf7be99412012-06-13 02:54:55 +0000967static int dcbnl_bcn_setcfg(struct net_device *netdev, struct nlmsghdr *nlh,
968 u32 seq, struct nlattr **tb, struct sk_buff *skb)
Alexander Duyck859ee3c2008-11-20 21:10:23 -0800969{
970 struct nlattr *data[DCB_BCN_ATTR_MAX + 1];
971 int i;
972 int ret = -EINVAL;
973 u8 value_byte;
974 u32 value_int;
975
Joe Perchesf64f9e72009-11-29 16:55:45 -0800976 if (!tb[DCB_ATTR_BCN] || !netdev->dcbnl_ops->setbcncfg ||
977 !netdev->dcbnl_ops->setbcnrp)
Alexander Duyck859ee3c2008-11-20 21:10:23 -0800978 return ret;
979
980 ret = nla_parse_nested(data, DCB_BCN_ATTR_MAX,
981 tb[DCB_ATTR_BCN],
982 dcbnl_pfc_up_nest);
983 if (ret)
984 goto err;
985
986 for (i = DCB_BCN_ATTR_RP_0; i <= DCB_BCN_ATTR_RP_7; i++) {
987 if (data[i] == NULL)
988 continue;
989 value_byte = nla_get_u8(data[i]);
990 netdev->dcbnl_ops->setbcnrp(netdev,
991 data[i]->nla_type - DCB_BCN_ATTR_RP_0, value_byte);
992 }
993
Don Skidmoref4314e82008-12-21 20:10:29 -0800994 for (i = DCB_BCN_ATTR_BCNA_0; i <= DCB_BCN_ATTR_RI; i++) {
Alexander Duyck859ee3c2008-11-20 21:10:23 -0800995 if (data[i] == NULL)
996 continue;
997 value_int = nla_get_u32(data[i]);
998 netdev->dcbnl_ops->setbcncfg(netdev,
999 i, value_int);
1000 }
1001
Thomas Graf7be99412012-06-13 02:54:55 +00001002 ret = nla_put_u8(skb, DCB_ATTR_BCN, 0);
Alexander Duyck859ee3c2008-11-20 21:10:23 -08001003err:
1004 return ret;
1005}
1006
Shmulik Raviddc6ed1d2011-02-27 05:04:38 +00001007static int dcbnl_build_peer_app(struct net_device *netdev, struct sk_buff* skb,
1008 int app_nested_type, int app_info_type,
1009 int app_entry_type)
Shmulik Ravideed84712011-02-27 05:04:31 +00001010{
1011 struct dcb_peer_app_info info;
1012 struct dcb_app *table = NULL;
1013 const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
1014 u16 app_count;
1015 int err;
1016
1017
1018 /**
1019 * retrieve the peer app configuration form the driver. If the driver
1020 * handlers fail exit without doing anything
1021 */
1022 err = ops->peer_getappinfo(netdev, &info, &app_count);
1023 if (!err && app_count) {
1024 table = kmalloc(sizeof(struct dcb_app) * app_count, GFP_KERNEL);
1025 if (!table)
1026 return -ENOMEM;
1027
1028 err = ops->peer_getapptable(netdev, table);
1029 }
1030
1031 if (!err) {
1032 u16 i;
1033 struct nlattr *app;
1034
1035 /**
1036 * build the message, from here on the only possible failure
1037 * is due to the skb size
1038 */
1039 err = -EMSGSIZE;
1040
Shmulik Raviddc6ed1d2011-02-27 05:04:38 +00001041 app = nla_nest_start(skb, app_nested_type);
Shmulik Ravideed84712011-02-27 05:04:31 +00001042 if (!app)
1043 goto nla_put_failure;
1044
David S. Miller1eb4c972012-04-01 20:03:01 -04001045 if (app_info_type &&
1046 nla_put(skb, app_info_type, sizeof(info), &info))
1047 goto nla_put_failure;
Shmulik Raviddc6ed1d2011-02-27 05:04:38 +00001048
David S. Miller1eb4c972012-04-01 20:03:01 -04001049 for (i = 0; i < app_count; i++) {
1050 if (nla_put(skb, app_entry_type, sizeof(struct dcb_app),
1051 &table[i]))
1052 goto nla_put_failure;
1053 }
Shmulik Ravideed84712011-02-27 05:04:31 +00001054 nla_nest_end(skb, app);
1055 }
1056 err = 0;
1057
1058nla_put_failure:
1059 kfree(table);
1060 return err;
1061}
John Fastabend3e290272010-12-30 09:25:46 +00001062
1063/* Handle IEEE 802.1Qaz GET commands. */
John Fastabend314b4772011-06-21 07:34:37 +00001064static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev)
John Fastabend3e290272010-12-30 09:25:46 +00001065{
John Fastabend9ab933a2010-12-30 09:26:31 +00001066 struct nlattr *ieee, *app;
1067 struct dcb_app_type *itr;
John Fastabend3e290272010-12-30 09:25:46 +00001068 const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
John Fastabendc7797ba2011-06-21 07:34:31 +00001069 int dcbx;
John Fastabend314b4772011-06-21 07:34:37 +00001070 int err = -EMSGSIZE;
John Fastabend3e290272010-12-30 09:25:46 +00001071
David S. Miller1eb4c972012-04-01 20:03:01 -04001072 if (nla_put_string(skb, DCB_ATTR_IFNAME, netdev->name))
1073 goto nla_put_failure;
John Fastabend3e290272010-12-30 09:25:46 +00001074 ieee = nla_nest_start(skb, DCB_ATTR_IEEE);
1075 if (!ieee)
1076 goto nla_put_failure;
1077
1078 if (ops->ieee_getets) {
1079 struct ieee_ets ets;
1080 err = ops->ieee_getets(netdev, &ets);
David S. Miller1eb4c972012-04-01 20:03:01 -04001081 if (!err &&
1082 nla_put(skb, DCB_ATTR_IEEE_ETS, sizeof(ets), &ets))
1083 goto nla_put_failure;
John Fastabend3e290272010-12-30 09:25:46 +00001084 }
1085
Amir Vadai08f10af2012-04-04 21:33:30 +00001086 if (ops->ieee_getmaxrate) {
1087 struct ieee_maxrate maxrate;
1088 err = ops->ieee_getmaxrate(netdev, &maxrate);
1089 if (!err) {
1090 err = nla_put(skb, DCB_ATTR_IEEE_MAXRATE,
1091 sizeof(maxrate), &maxrate);
1092 if (err)
1093 goto nla_put_failure;
1094 }
1095 }
1096
John Fastabend3e290272010-12-30 09:25:46 +00001097 if (ops->ieee_getpfc) {
1098 struct ieee_pfc pfc;
1099 err = ops->ieee_getpfc(netdev, &pfc);
David S. Miller1eb4c972012-04-01 20:03:01 -04001100 if (!err &&
1101 nla_put(skb, DCB_ATTR_IEEE_PFC, sizeof(pfc), &pfc))
1102 goto nla_put_failure;
John Fastabend3e290272010-12-30 09:25:46 +00001103 }
1104
John Fastabend9ab933a2010-12-30 09:26:31 +00001105 app = nla_nest_start(skb, DCB_ATTR_IEEE_APP_TABLE);
1106 if (!app)
1107 goto nla_put_failure;
1108
1109 spin_lock(&dcb_lock);
1110 list_for_each_entry(itr, &dcb_app_list, list) {
Mark Rustade290ed82011-10-06 08:52:33 +00001111 if (itr->ifindex == netdev->ifindex) {
Dan Carpenter70bfa2d2011-01-04 21:03:12 +00001112 err = nla_put(skb, DCB_ATTR_IEEE_APP, sizeof(itr->app),
1113 &itr->app);
1114 if (err) {
1115 spin_unlock(&dcb_lock);
1116 goto nla_put_failure;
1117 }
1118 }
John Fastabend9ab933a2010-12-30 09:26:31 +00001119 }
John Fastabendc7797ba2011-06-21 07:34:31 +00001120
1121 if (netdev->dcbnl_ops->getdcbx)
1122 dcbx = netdev->dcbnl_ops->getdcbx(netdev);
1123 else
1124 dcbx = -EOPNOTSUPP;
1125
John Fastabend9ab933a2010-12-30 09:26:31 +00001126 spin_unlock(&dcb_lock);
1127 nla_nest_end(skb, app);
1128
Shmulik Ravideed84712011-02-27 05:04:31 +00001129 /* get peer info if available */
1130 if (ops->ieee_peer_getets) {
1131 struct ieee_ets ets;
1132 err = ops->ieee_peer_getets(netdev, &ets);
David S. Miller1eb4c972012-04-01 20:03:01 -04001133 if (!err &&
1134 nla_put(skb, DCB_ATTR_IEEE_PEER_ETS, sizeof(ets), &ets))
1135 goto nla_put_failure;
Shmulik Ravideed84712011-02-27 05:04:31 +00001136 }
1137
1138 if (ops->ieee_peer_getpfc) {
1139 struct ieee_pfc pfc;
1140 err = ops->ieee_peer_getpfc(netdev, &pfc);
David S. Miller1eb4c972012-04-01 20:03:01 -04001141 if (!err &&
1142 nla_put(skb, DCB_ATTR_IEEE_PEER_PFC, sizeof(pfc), &pfc))
1143 goto nla_put_failure;
Shmulik Ravideed84712011-02-27 05:04:31 +00001144 }
1145
1146 if (ops->peer_getappinfo && ops->peer_getapptable) {
Shmulik Raviddc6ed1d2011-02-27 05:04:38 +00001147 err = dcbnl_build_peer_app(netdev, skb,
1148 DCB_ATTR_IEEE_PEER_APP,
1149 DCB_ATTR_IEEE_APP_UNSPEC,
1150 DCB_ATTR_IEEE_APP);
Shmulik Ravideed84712011-02-27 05:04:31 +00001151 if (err)
1152 goto nla_put_failure;
1153 }
1154
John Fastabend3e290272010-12-30 09:25:46 +00001155 nla_nest_end(skb, ieee);
John Fastabendc7797ba2011-06-21 07:34:31 +00001156 if (dcbx >= 0) {
1157 err = nla_put_u8(skb, DCB_ATTR_DCBX, dcbx);
1158 if (err)
1159 goto nla_put_failure;
1160 }
John Fastabend3e290272010-12-30 09:25:46 +00001161
John Fastabend314b4772011-06-21 07:34:37 +00001162 return 0;
1163
John Fastabend3e290272010-12-30 09:25:46 +00001164nla_put_failure:
John Fastabend314b4772011-06-21 07:34:37 +00001165 return err;
John Fastabend3e290272010-12-30 09:25:46 +00001166}
1167
Shmulik Ravid5b7f7622011-07-05 06:16:25 +00001168static int dcbnl_cee_pg_fill(struct sk_buff *skb, struct net_device *dev,
1169 int dir)
1170{
1171 u8 pgid, up_map, prio, tc_pct;
1172 const struct dcbnl_rtnl_ops *ops = dev->dcbnl_ops;
1173 int i = dir ? DCB_ATTR_CEE_TX_PG : DCB_ATTR_CEE_RX_PG;
1174 struct nlattr *pg = nla_nest_start(skb, i);
1175
1176 if (!pg)
1177 goto nla_put_failure;
1178
1179 for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) {
1180 struct nlattr *tc_nest = nla_nest_start(skb, i);
1181
1182 if (!tc_nest)
1183 goto nla_put_failure;
1184
1185 pgid = DCB_ATTR_VALUE_UNDEFINED;
1186 prio = DCB_ATTR_VALUE_UNDEFINED;
1187 tc_pct = DCB_ATTR_VALUE_UNDEFINED;
1188 up_map = DCB_ATTR_VALUE_UNDEFINED;
1189
1190 if (!dir)
1191 ops->getpgtccfgrx(dev, i - DCB_PG_ATTR_TC_0,
1192 &prio, &pgid, &tc_pct, &up_map);
1193 else
1194 ops->getpgtccfgtx(dev, i - DCB_PG_ATTR_TC_0,
1195 &prio, &pgid, &tc_pct, &up_map);
1196
David S. Miller1eb4c972012-04-01 20:03:01 -04001197 if (nla_put_u8(skb, DCB_TC_ATTR_PARAM_PGID, pgid) ||
1198 nla_put_u8(skb, DCB_TC_ATTR_PARAM_UP_MAPPING, up_map) ||
1199 nla_put_u8(skb, DCB_TC_ATTR_PARAM_STRICT_PRIO, prio) ||
1200 nla_put_u8(skb, DCB_TC_ATTR_PARAM_BW_PCT, tc_pct))
1201 goto nla_put_failure;
Shmulik Ravid5b7f7622011-07-05 06:16:25 +00001202 nla_nest_end(skb, tc_nest);
1203 }
1204
1205 for (i = DCB_PG_ATTR_BW_ID_0; i <= DCB_PG_ATTR_BW_ID_7; i++) {
1206 tc_pct = DCB_ATTR_VALUE_UNDEFINED;
1207
1208 if (!dir)
1209 ops->getpgbwgcfgrx(dev, i - DCB_PG_ATTR_BW_ID_0,
1210 &tc_pct);
1211 else
1212 ops->getpgbwgcfgtx(dev, i - DCB_PG_ATTR_BW_ID_0,
1213 &tc_pct);
David S. Miller1eb4c972012-04-01 20:03:01 -04001214 if (nla_put_u8(skb, i, tc_pct))
1215 goto nla_put_failure;
Shmulik Ravid5b7f7622011-07-05 06:16:25 +00001216 }
1217 nla_nest_end(skb, pg);
1218 return 0;
1219
1220nla_put_failure:
1221 return -EMSGSIZE;
1222}
1223
1224static int dcbnl_cee_fill(struct sk_buff *skb, struct net_device *netdev)
1225{
1226 struct nlattr *cee, *app;
1227 struct dcb_app_type *itr;
1228 const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
1229 int dcbx, i, err = -EMSGSIZE;
1230 u8 value;
1231
David S. Miller1eb4c972012-04-01 20:03:01 -04001232 if (nla_put_string(skb, DCB_ATTR_IFNAME, netdev->name))
1233 goto nla_put_failure;
Shmulik Ravid5b7f7622011-07-05 06:16:25 +00001234 cee = nla_nest_start(skb, DCB_ATTR_CEE);
1235 if (!cee)
1236 goto nla_put_failure;
1237
1238 /* local pg */
1239 if (ops->getpgtccfgtx && ops->getpgbwgcfgtx) {
1240 err = dcbnl_cee_pg_fill(skb, netdev, 1);
1241 if (err)
1242 goto nla_put_failure;
1243 }
1244
1245 if (ops->getpgtccfgrx && ops->getpgbwgcfgrx) {
1246 err = dcbnl_cee_pg_fill(skb, netdev, 0);
1247 if (err)
1248 goto nla_put_failure;
1249 }
1250
1251 /* local pfc */
1252 if (ops->getpfccfg) {
1253 struct nlattr *pfc_nest = nla_nest_start(skb, DCB_ATTR_CEE_PFC);
1254
1255 if (!pfc_nest)
1256 goto nla_put_failure;
1257
1258 for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) {
1259 ops->getpfccfg(netdev, i - DCB_PFC_UP_ATTR_0, &value);
David S. Miller1eb4c972012-04-01 20:03:01 -04001260 if (nla_put_u8(skb, i, value))
1261 goto nla_put_failure;
Shmulik Ravid5b7f7622011-07-05 06:16:25 +00001262 }
1263 nla_nest_end(skb, pfc_nest);
1264 }
1265
1266 /* local app */
1267 spin_lock(&dcb_lock);
1268 app = nla_nest_start(skb, DCB_ATTR_CEE_APP_TABLE);
1269 if (!app)
Dan Carpenter40f5d722011-07-07 21:27:24 +00001270 goto dcb_unlock;
Shmulik Ravid5b7f7622011-07-05 06:16:25 +00001271
1272 list_for_each_entry(itr, &dcb_app_list, list) {
Mark Rustade290ed82011-10-06 08:52:33 +00001273 if (itr->ifindex == netdev->ifindex) {
Shmulik Ravid5b7f7622011-07-05 06:16:25 +00001274 struct nlattr *app_nest = nla_nest_start(skb,
1275 DCB_ATTR_APP);
1276 if (!app_nest)
1277 goto dcb_unlock;
1278
1279 err = nla_put_u8(skb, DCB_APP_ATTR_IDTYPE,
1280 itr->app.selector);
1281 if (err)
1282 goto dcb_unlock;
1283
1284 err = nla_put_u16(skb, DCB_APP_ATTR_ID,
1285 itr->app.protocol);
1286 if (err)
1287 goto dcb_unlock;
1288
1289 err = nla_put_u8(skb, DCB_APP_ATTR_PRIORITY,
1290 itr->app.priority);
1291 if (err)
1292 goto dcb_unlock;
1293
1294 nla_nest_end(skb, app_nest);
1295 }
1296 }
1297 nla_nest_end(skb, app);
1298
1299 if (netdev->dcbnl_ops->getdcbx)
1300 dcbx = netdev->dcbnl_ops->getdcbx(netdev);
1301 else
1302 dcbx = -EOPNOTSUPP;
1303
1304 spin_unlock(&dcb_lock);
1305
1306 /* features flags */
1307 if (ops->getfeatcfg) {
1308 struct nlattr *feat = nla_nest_start(skb, DCB_ATTR_CEE_FEAT);
1309 if (!feat)
1310 goto nla_put_failure;
1311
1312 for (i = DCB_FEATCFG_ATTR_ALL + 1; i <= DCB_FEATCFG_ATTR_MAX;
1313 i++)
David S. Miller1eb4c972012-04-01 20:03:01 -04001314 if (!ops->getfeatcfg(netdev, i, &value) &&
1315 nla_put_u8(skb, i, value))
1316 goto nla_put_failure;
Shmulik Ravid5b7f7622011-07-05 06:16:25 +00001317
1318 nla_nest_end(skb, feat);
1319 }
1320
1321 /* peer info if available */
1322 if (ops->cee_peer_getpg) {
1323 struct cee_pg pg;
1324 err = ops->cee_peer_getpg(netdev, &pg);
David S. Miller1eb4c972012-04-01 20:03:01 -04001325 if (!err &&
1326 nla_put(skb, DCB_ATTR_CEE_PEER_PG, sizeof(pg), &pg))
1327 goto nla_put_failure;
Shmulik Ravid5b7f7622011-07-05 06:16:25 +00001328 }
1329
1330 if (ops->cee_peer_getpfc) {
1331 struct cee_pfc pfc;
1332 err = ops->cee_peer_getpfc(netdev, &pfc);
David S. Miller1eb4c972012-04-01 20:03:01 -04001333 if (!err &&
1334 nla_put(skb, DCB_ATTR_CEE_PEER_PFC, sizeof(pfc), &pfc))
1335 goto nla_put_failure;
Shmulik Ravid5b7f7622011-07-05 06:16:25 +00001336 }
1337
1338 if (ops->peer_getappinfo && ops->peer_getapptable) {
1339 err = dcbnl_build_peer_app(netdev, skb,
1340 DCB_ATTR_CEE_PEER_APP_TABLE,
1341 DCB_ATTR_CEE_PEER_APP_INFO,
1342 DCB_ATTR_CEE_PEER_APP);
1343 if (err)
1344 goto nla_put_failure;
1345 }
1346 nla_nest_end(skb, cee);
1347
1348 /* DCBX state */
1349 if (dcbx >= 0) {
1350 err = nla_put_u8(skb, DCB_ATTR_DCBX, dcbx);
1351 if (err)
1352 goto nla_put_failure;
1353 }
1354 return 0;
1355
1356dcb_unlock:
1357 spin_unlock(&dcb_lock);
1358nla_put_failure:
1359 return err;
1360}
1361
1362static int dcbnl_notify(struct net_device *dev, int event, int cmd,
1363 u32 seq, u32 pid, int dcbx_ver)
John Fastabend314b4772011-06-21 07:34:37 +00001364{
1365 struct net *net = dev_net(dev);
1366 struct sk_buff *skb;
1367 struct nlmsghdr *nlh;
1368 struct dcbmsg *dcb;
1369 const struct dcbnl_rtnl_ops *ops = dev->dcbnl_ops;
1370 int err;
1371
1372 if (!ops)
1373 return -EOPNOTSUPP;
1374
1375 skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
1376 if (!skb)
1377 return -ENOBUFS;
1378
1379 nlh = nlmsg_put(skb, pid, 0, event, sizeof(*dcb), 0);
1380 if (nlh == NULL) {
Dan Carpenter4d054f22011-06-23 03:14:42 -07001381 nlmsg_free(skb);
John Fastabend314b4772011-06-21 07:34:37 +00001382 return -EMSGSIZE;
1383 }
1384
1385 dcb = NLMSG_DATA(nlh);
1386 dcb->dcb_family = AF_UNSPEC;
1387 dcb->cmd = cmd;
1388
Shmulik Ravid5b7f7622011-07-05 06:16:25 +00001389 if (dcbx_ver == DCB_CAP_DCBX_VER_IEEE)
1390 err = dcbnl_ieee_fill(skb, dev);
1391 else
1392 err = dcbnl_cee_fill(skb, dev);
1393
John Fastabend314b4772011-06-21 07:34:37 +00001394 if (err < 0) {
1395 /* Report error to broadcast listeners */
1396 nlmsg_cancel(skb, nlh);
1397 kfree_skb(skb);
1398 rtnl_set_sk_err(net, RTNLGRP_DCB, err);
1399 } else {
1400 /* End nlmsg and notify broadcast listeners */
1401 nlmsg_end(skb, nlh);
1402 rtnl_notify(skb, net, 0, RTNLGRP_DCB, NULL, GFP_KERNEL);
1403 }
1404
1405 return err;
1406}
Shmulik Ravid5b7f7622011-07-05 06:16:25 +00001407
1408int dcbnl_ieee_notify(struct net_device *dev, int event, int cmd,
1409 u32 seq, u32 pid)
1410{
1411 return dcbnl_notify(dev, event, cmd, seq, pid, DCB_CAP_DCBX_VER_IEEE);
1412}
1413EXPORT_SYMBOL(dcbnl_ieee_notify);
1414
1415int dcbnl_cee_notify(struct net_device *dev, int event, int cmd,
1416 u32 seq, u32 pid)
1417{
1418 return dcbnl_notify(dev, event, cmd, seq, pid, DCB_CAP_DCBX_VER_CEE);
1419}
1420EXPORT_SYMBOL(dcbnl_cee_notify);
John Fastabend314b4772011-06-21 07:34:37 +00001421
1422/* Handle IEEE 802.1Qaz SET commands. If any requested operation can not
1423 * be completed the entire msg is aborted and error value is returned.
1424 * No attempt is made to reconcile the case where only part of the
1425 * cmd can be completed.
1426 */
Thomas Graf7be99412012-06-13 02:54:55 +00001427static int dcbnl_ieee_set(struct net_device *netdev, struct nlmsghdr *nlh,
1428 u32 seq, struct nlattr **tb, struct sk_buff *skb)
John Fastabend314b4772011-06-21 07:34:37 +00001429{
1430 const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
1431 struct nlattr *ieee[DCB_ATTR_IEEE_MAX + 1];
1432 int err = -EOPNOTSUPP;
1433
1434 if (!ops)
1435 return err;
1436
John Fastabend4003b652011-06-21 07:35:04 +00001437 if (!tb[DCB_ATTR_IEEE])
1438 return -EINVAL;
1439
John Fastabend314b4772011-06-21 07:34:37 +00001440 err = nla_parse_nested(ieee, DCB_ATTR_IEEE_MAX,
1441 tb[DCB_ATTR_IEEE], dcbnl_ieee_policy);
1442 if (err)
1443 return err;
1444
1445 if (ieee[DCB_ATTR_IEEE_ETS] && ops->ieee_setets) {
1446 struct ieee_ets *ets = nla_data(ieee[DCB_ATTR_IEEE_ETS]);
1447 err = ops->ieee_setets(netdev, ets);
1448 if (err)
1449 goto err;
1450 }
1451
Amir Vadai08f10af2012-04-04 21:33:30 +00001452 if (ieee[DCB_ATTR_IEEE_MAXRATE] && ops->ieee_setmaxrate) {
1453 struct ieee_maxrate *maxrate =
1454 nla_data(ieee[DCB_ATTR_IEEE_MAXRATE]);
1455 err = ops->ieee_setmaxrate(netdev, maxrate);
1456 if (err)
1457 goto err;
1458 }
1459
John Fastabend314b4772011-06-21 07:34:37 +00001460 if (ieee[DCB_ATTR_IEEE_PFC] && ops->ieee_setpfc) {
1461 struct ieee_pfc *pfc = nla_data(ieee[DCB_ATTR_IEEE_PFC]);
1462 err = ops->ieee_setpfc(netdev, pfc);
1463 if (err)
1464 goto err;
1465 }
1466
1467 if (ieee[DCB_ATTR_IEEE_APP_TABLE]) {
1468 struct nlattr *attr;
1469 int rem;
1470
1471 nla_for_each_nested(attr, ieee[DCB_ATTR_IEEE_APP_TABLE], rem) {
1472 struct dcb_app *app_data;
1473 if (nla_type(attr) != DCB_ATTR_IEEE_APP)
1474 continue;
1475 app_data = nla_data(attr);
1476 if (ops->ieee_setapp)
1477 err = ops->ieee_setapp(netdev, app_data);
1478 else
John Fastabendb6db2172011-06-21 07:34:42 +00001479 err = dcb_ieee_setapp(netdev, app_data);
John Fastabend314b4772011-06-21 07:34:37 +00001480 if (err)
1481 goto err;
1482 }
1483 }
1484
1485err:
Thomas Graf7be99412012-06-13 02:54:55 +00001486 err = nla_put_u8(skb, DCB_ATTR_IEEE, err);
Shmulik Ravid5b7f7622011-07-05 06:16:25 +00001487 dcbnl_ieee_notify(netdev, RTM_SETDCB, DCB_CMD_IEEE_SET, seq, 0);
John Fastabend314b4772011-06-21 07:34:37 +00001488 return err;
1489}
1490
Thomas Graf7be99412012-06-13 02:54:55 +00001491static int dcbnl_ieee_get(struct net_device *netdev, struct nlmsghdr *nlh,
1492 u32 seq, struct nlattr **tb, struct sk_buff *skb)
John Fastabend314b4772011-06-21 07:34:37 +00001493{
John Fastabend314b4772011-06-21 07:34:37 +00001494 const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
John Fastabend314b4772011-06-21 07:34:37 +00001495
1496 if (!ops)
1497 return -EOPNOTSUPP;
1498
Thomas Graf7be99412012-06-13 02:54:55 +00001499 return dcbnl_ieee_fill(skb, netdev);
John Fastabend314b4772011-06-21 07:34:37 +00001500}
John Fastabendf9ae7e42011-06-21 07:34:48 +00001501
Thomas Graf7be99412012-06-13 02:54:55 +00001502static int dcbnl_ieee_del(struct net_device *netdev, struct nlmsghdr *nlh,
1503 u32 seq, struct nlattr **tb, struct sk_buff *skb)
John Fastabendf9ae7e42011-06-21 07:34:48 +00001504{
1505 const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
1506 struct nlattr *ieee[DCB_ATTR_IEEE_MAX + 1];
1507 int err = -EOPNOTSUPP;
1508
1509 if (!ops)
1510 return -EOPNOTSUPP;
1511
1512 if (!tb[DCB_ATTR_IEEE])
1513 return -EINVAL;
1514
1515 err = nla_parse_nested(ieee, DCB_ATTR_IEEE_MAX,
1516 tb[DCB_ATTR_IEEE], dcbnl_ieee_policy);
1517 if (err)
1518 return err;
1519
1520 if (ieee[DCB_ATTR_IEEE_APP_TABLE]) {
1521 struct nlattr *attr;
1522 int rem;
1523
1524 nla_for_each_nested(attr, ieee[DCB_ATTR_IEEE_APP_TABLE], rem) {
1525 struct dcb_app *app_data;
1526
1527 if (nla_type(attr) != DCB_ATTR_IEEE_APP)
1528 continue;
1529 app_data = nla_data(attr);
1530 if (ops->ieee_delapp)
1531 err = ops->ieee_delapp(netdev, app_data);
1532 else
1533 err = dcb_ieee_delapp(netdev, app_data);
1534 if (err)
1535 goto err;
1536 }
1537 }
1538
1539err:
Thomas Graf7be99412012-06-13 02:54:55 +00001540 err = nla_put_u8(skb, DCB_ATTR_IEEE, err);
Shmulik Ravid5b7f7622011-07-05 06:16:25 +00001541 dcbnl_ieee_notify(netdev, RTM_SETDCB, DCB_CMD_IEEE_DEL, seq, 0);
John Fastabendf9ae7e42011-06-21 07:34:48 +00001542 return err;
1543}
1544
1545
Shmulik Ravid6241b622010-12-30 06:26:48 +00001546/* DCBX configuration */
Thomas Graf7be99412012-06-13 02:54:55 +00001547static int dcbnl_getdcbx(struct net_device *netdev, struct nlmsghdr *nlh,
1548 u32 seq, struct nlattr **tb, struct sk_buff *skb)
Shmulik Ravid6241b622010-12-30 06:26:48 +00001549{
Shmulik Ravid6241b622010-12-30 06:26:48 +00001550 if (!netdev->dcbnl_ops->getdcbx)
Shmulik Ravid7f891cf2011-01-03 08:04:59 +00001551 return -EOPNOTSUPP;
Shmulik Ravid6241b622010-12-30 06:26:48 +00001552
Thomas Graf7be99412012-06-13 02:54:55 +00001553 return nla_put_u8(skb, DCB_ATTR_DCBX,
1554 netdev->dcbnl_ops->getdcbx(netdev));
Shmulik Ravid6241b622010-12-30 06:26:48 +00001555}
1556
Thomas Graf7be99412012-06-13 02:54:55 +00001557static int dcbnl_setdcbx(struct net_device *netdev, struct nlmsghdr *nlh,
1558 u32 seq, struct nlattr **tb, struct sk_buff *skb)
Shmulik Ravid6241b622010-12-30 06:26:48 +00001559{
Shmulik Ravid6241b622010-12-30 06:26:48 +00001560 u8 value;
1561
Shmulik Ravid7f891cf2011-01-03 08:04:59 +00001562 if (!netdev->dcbnl_ops->setdcbx)
1563 return -EOPNOTSUPP;
1564
1565 if (!tb[DCB_ATTR_DCBX])
1566 return -EINVAL;
Shmulik Ravid6241b622010-12-30 06:26:48 +00001567
1568 value = nla_get_u8(tb[DCB_ATTR_DCBX]);
1569
Thomas Graf7be99412012-06-13 02:54:55 +00001570 return nla_put_u8(skb, DCB_ATTR_DCBX,
1571 netdev->dcbnl_ops->setdcbx(netdev, value));
Shmulik Ravid6241b622010-12-30 06:26:48 +00001572}
1573
Thomas Graf7be99412012-06-13 02:54:55 +00001574static int dcbnl_getfeatcfg(struct net_device *netdev, struct nlmsghdr *nlh,
1575 u32 seq, struct nlattr **tb, struct sk_buff *skb)
Shmulik Ravidea45fe42010-12-30 06:26:55 +00001576{
Shmulik Ravidea45fe42010-12-30 06:26:55 +00001577 struct nlattr *data[DCB_FEATCFG_ATTR_MAX + 1], *nest;
1578 u8 value;
Shmulik Ravid7f891cf2011-01-03 08:04:59 +00001579 int ret, i;
Shmulik Ravidea45fe42010-12-30 06:26:55 +00001580 int getall = 0;
1581
Shmulik Ravid7f891cf2011-01-03 08:04:59 +00001582 if (!netdev->dcbnl_ops->getfeatcfg)
1583 return -EOPNOTSUPP;
1584
1585 if (!tb[DCB_ATTR_FEATCFG])
1586 return -EINVAL;
Shmulik Ravidea45fe42010-12-30 06:26:55 +00001587
1588 ret = nla_parse_nested(data, DCB_FEATCFG_ATTR_MAX, tb[DCB_ATTR_FEATCFG],
1589 dcbnl_featcfg_nest);
Shmulik Ravid7f891cf2011-01-03 08:04:59 +00001590 if (ret)
Thomas Graf7be99412012-06-13 02:54:55 +00001591 return ret;
Shmulik Ravidea45fe42010-12-30 06:26:55 +00001592
Thomas Graf7be99412012-06-13 02:54:55 +00001593 nest = nla_nest_start(skb, DCB_ATTR_FEATCFG);
1594 if (!nest)
1595 return -EMSGSIZE;
Shmulik Ravidea45fe42010-12-30 06:26:55 +00001596
1597 if (data[DCB_FEATCFG_ATTR_ALL])
1598 getall = 1;
1599
1600 for (i = DCB_FEATCFG_ATTR_ALL+1; i <= DCB_FEATCFG_ATTR_MAX; i++) {
1601 if (!getall && !data[i])
1602 continue;
1603
1604 ret = netdev->dcbnl_ops->getfeatcfg(netdev, i, &value);
Shmulik Ravid7f891cf2011-01-03 08:04:59 +00001605 if (!ret)
Thomas Graf7be99412012-06-13 02:54:55 +00001606 ret = nla_put_u8(skb, i, value);
Shmulik Ravidea45fe42010-12-30 06:26:55 +00001607
Shmulik Ravid7f891cf2011-01-03 08:04:59 +00001608 if (ret) {
Thomas Graf7be99412012-06-13 02:54:55 +00001609 nla_nest_cancel(skb, nest);
Shmulik Ravid7f891cf2011-01-03 08:04:59 +00001610 goto nla_put_failure;
1611 }
Shmulik Ravidea45fe42010-12-30 06:26:55 +00001612 }
Thomas Graf7be99412012-06-13 02:54:55 +00001613 nla_nest_end(skb, nest);
Shmulik Ravidea45fe42010-12-30 06:26:55 +00001614
Shmulik Ravid7f891cf2011-01-03 08:04:59 +00001615nla_put_failure:
Shmulik Ravidea45fe42010-12-30 06:26:55 +00001616 return ret;
1617}
1618
Thomas Graf7be99412012-06-13 02:54:55 +00001619static int dcbnl_setfeatcfg(struct net_device *netdev, struct nlmsghdr *nlh,
1620 u32 seq, struct nlattr **tb, struct sk_buff *skb)
Shmulik Ravidea45fe42010-12-30 06:26:55 +00001621{
1622 struct nlattr *data[DCB_FEATCFG_ATTR_MAX + 1];
Shmulik Ravid7f891cf2011-01-03 08:04:59 +00001623 int ret, i;
Shmulik Ravidea45fe42010-12-30 06:26:55 +00001624 u8 value;
Shmulik Ravidea45fe42010-12-30 06:26:55 +00001625
Shmulik Ravid7f891cf2011-01-03 08:04:59 +00001626 if (!netdev->dcbnl_ops->setfeatcfg)
1627 return -ENOTSUPP;
1628
1629 if (!tb[DCB_ATTR_FEATCFG])
1630 return -EINVAL;
Shmulik Ravidea45fe42010-12-30 06:26:55 +00001631
1632 ret = nla_parse_nested(data, DCB_FEATCFG_ATTR_MAX, tb[DCB_ATTR_FEATCFG],
1633 dcbnl_featcfg_nest);
1634
Shmulik Ravid7f891cf2011-01-03 08:04:59 +00001635 if (ret)
Shmulik Ravidea45fe42010-12-30 06:26:55 +00001636 goto err;
Shmulik Ravidea45fe42010-12-30 06:26:55 +00001637
1638 for (i = DCB_FEATCFG_ATTR_ALL+1; i <= DCB_FEATCFG_ATTR_MAX; i++) {
1639 if (data[i] == NULL)
1640 continue;
1641
1642 value = nla_get_u8(data[i]);
1643
1644 ret = netdev->dcbnl_ops->setfeatcfg(netdev, i, value);
1645
1646 if (ret)
Shmulik Ravid7f891cf2011-01-03 08:04:59 +00001647 goto err;
Shmulik Ravidea45fe42010-12-30 06:26:55 +00001648 }
Shmulik Ravidea45fe42010-12-30 06:26:55 +00001649err:
Thomas Graf7be99412012-06-13 02:54:55 +00001650 ret = nla_put_u8(skb, DCB_ATTR_FEATCFG, ret);
Shmulik Ravid7f891cf2011-01-03 08:04:59 +00001651
Shmulik Ravidea45fe42010-12-30 06:26:55 +00001652 return ret;
1653}
1654
Shmulik Raviddc6ed1d2011-02-27 05:04:38 +00001655/* Handle CEE DCBX GET commands. */
Thomas Graf7be99412012-06-13 02:54:55 +00001656static int dcbnl_cee_get(struct net_device *netdev, struct nlmsghdr *nlh,
1657 u32 seq, struct nlattr **tb, struct sk_buff *skb)
Shmulik Raviddc6ed1d2011-02-27 05:04:38 +00001658{
Shmulik Raviddc6ed1d2011-02-27 05:04:38 +00001659 const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
Shmulik Raviddc6ed1d2011-02-27 05:04:38 +00001660
1661 if (!ops)
1662 return -EOPNOTSUPP;
1663
Thomas Graf7be99412012-06-13 02:54:55 +00001664 return dcbnl_cee_fill(skb, netdev);
Shmulik Raviddc6ed1d2011-02-27 05:04:38 +00001665}
1666
Thomas Graf33a03aa2012-06-13 02:54:54 +00001667struct reply_func {
1668 /* reply netlink message type */
1669 int type;
1670
1671 /* function to fill message contents */
1672 int (*cb)(struct net_device *, struct nlmsghdr *, u32,
1673 struct nlattr **, struct sk_buff *);
1674};
1675
1676static const struct reply_func reply_funcs[DCB_CMD_MAX+1] = {
Thomas Graf7be99412012-06-13 02:54:55 +00001677 [DCB_CMD_GSTATE] = { RTM_GETDCB, dcbnl_getstate },
1678 [DCB_CMD_SSTATE] = { RTM_SETDCB, dcbnl_setstate },
1679 [DCB_CMD_PFC_GCFG] = { RTM_GETDCB, dcbnl_getpfccfg },
1680 [DCB_CMD_PFC_SCFG] = { RTM_SETDCB, dcbnl_setpfccfg },
1681 [DCB_CMD_GPERM_HWADDR] = { RTM_GETDCB, dcbnl_getperm_hwaddr },
1682 [DCB_CMD_GCAP] = { RTM_GETDCB, dcbnl_getcap },
1683 [DCB_CMD_GNUMTCS] = { RTM_GETDCB, dcbnl_getnumtcs },
1684 [DCB_CMD_SNUMTCS] = { RTM_SETDCB, dcbnl_setnumtcs },
1685 [DCB_CMD_PFC_GSTATE] = { RTM_GETDCB, dcbnl_getpfcstate },
1686 [DCB_CMD_PFC_SSTATE] = { RTM_SETDCB, dcbnl_setpfcstate },
1687 [DCB_CMD_GAPP] = { RTM_GETDCB, dcbnl_getapp },
1688 [DCB_CMD_SAPP] = { RTM_SETDCB, dcbnl_setapp },
1689 [DCB_CMD_PGTX_GCFG] = { RTM_GETDCB, dcbnl_pgtx_getcfg },
1690 [DCB_CMD_PGTX_SCFG] = { RTM_SETDCB, dcbnl_pgtx_setcfg },
1691 [DCB_CMD_PGRX_GCFG] = { RTM_GETDCB, dcbnl_pgrx_getcfg },
1692 [DCB_CMD_PGRX_SCFG] = { RTM_SETDCB, dcbnl_pgrx_setcfg },
1693 [DCB_CMD_SET_ALL] = { RTM_SETDCB, dcbnl_setall },
1694 [DCB_CMD_BCN_GCFG] = { RTM_GETDCB, dcbnl_bcn_getcfg },
1695 [DCB_CMD_BCN_SCFG] = { RTM_SETDCB, dcbnl_bcn_setcfg },
1696 [DCB_CMD_IEEE_GET] = { RTM_GETDCB, dcbnl_ieee_get },
1697 [DCB_CMD_IEEE_SET] = { RTM_SETDCB, dcbnl_ieee_set },
1698 [DCB_CMD_IEEE_DEL] = { RTM_SETDCB, dcbnl_ieee_del },
1699 [DCB_CMD_GDCBX] = { RTM_GETDCB, dcbnl_getdcbx },
1700 [DCB_CMD_SDCBX] = { RTM_SETDCB, dcbnl_setdcbx },
1701 [DCB_CMD_GFEATCFG] = { RTM_GETDCB, dcbnl_getfeatcfg },
1702 [DCB_CMD_SFEATCFG] = { RTM_SETDCB, dcbnl_setfeatcfg },
1703 [DCB_CMD_CEE_GET] = { RTM_GETDCB, dcbnl_cee_get },
Thomas Graf33a03aa2012-06-13 02:54:54 +00001704};
1705
Alexander Duyck2f90b862008-11-20 20:52:10 -08001706static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
1707{
1708 struct net *net = sock_net(skb->sk);
1709 struct net_device *netdev;
1710 struct dcbmsg *dcb = (struct dcbmsg *)NLMSG_DATA(nlh);
1711 struct nlattr *tb[DCB_ATTR_MAX + 1];
1712 u32 pid = skb ? NETLINK_CB(skb).pid : 0;
1713 int ret = -EINVAL;
Thomas Graf33a03aa2012-06-13 02:54:54 +00001714 struct sk_buff *reply_skb;
1715 struct nlmsghdr *reply_nlh;
1716 const struct reply_func *fn;
Alexander Duyck2f90b862008-11-20 20:52:10 -08001717
Octavian Purdila09ad9bc2009-11-25 15:14:13 -08001718 if (!net_eq(net, &init_net))
Alexander Duyck2f90b862008-11-20 20:52:10 -08001719 return -EINVAL;
1720
1721 ret = nlmsg_parse(nlh, sizeof(*dcb), tb, DCB_ATTR_MAX,
1722 dcbnl_rtnl_policy);
1723 if (ret < 0)
1724 return ret;
1725
Thomas Graf33a03aa2012-06-13 02:54:54 +00001726 if (dcb->cmd > DCB_CMD_MAX)
1727 return -EINVAL;
1728
1729 /* check if a reply function has been defined for the command */
1730 fn = &reply_funcs[dcb->cmd];
1731 if (!fn->cb)
1732 return -EOPNOTSUPP;
1733
Alexander Duyck2f90b862008-11-20 20:52:10 -08001734 if (!tb[DCB_ATTR_IFNAME])
1735 return -EINVAL;
1736
1737 netdev = dev_get_by_name(&init_net, nla_data(tb[DCB_ATTR_IFNAME]));
1738 if (!netdev)
1739 return -EINVAL;
1740
1741 if (!netdev->dcbnl_ops)
1742 goto errout;
1743
Thomas Graf33a03aa2012-06-13 02:54:54 +00001744 reply_skb = dcbnl_newmsg(fn->type, dcb->cmd, pid, nlh->nlmsg_seq,
1745 nlh->nlmsg_flags, &reply_nlh);
1746 if (!reply_skb) {
1747 ret = -ENOBUFS;
1748 goto out;
1749 }
1750
1751 ret = fn->cb(netdev, nlh, nlh->nlmsg_seq, tb, reply_skb);
1752 if (ret < 0) {
1753 nlmsg_free(reply_skb);
1754 goto out;
1755 }
1756
1757 nlmsg_end(reply_skb, reply_nlh);
1758
1759 ret = rtnl_unicast(reply_skb, &init_net, pid);
Alexander Duyck2f90b862008-11-20 20:52:10 -08001760out:
1761 dev_put(netdev);
1762 return ret;
1763}
1764
John Fastabend9ab933a2010-12-30 09:26:31 +00001765/**
1766 * dcb_getapp - retrieve the DCBX application user priority
1767 *
1768 * On success returns a non-zero 802.1p user priority bitmap
1769 * otherwise returns 0 as the invalid user priority bitmap to
1770 * indicate an error.
1771 */
1772u8 dcb_getapp(struct net_device *dev, struct dcb_app *app)
1773{
1774 struct dcb_app_type *itr;
1775 u8 prio = 0;
1776
1777 spin_lock(&dcb_lock);
1778 list_for_each_entry(itr, &dcb_app_list, list) {
1779 if (itr->app.selector == app->selector &&
1780 itr->app.protocol == app->protocol &&
Mark Rustade290ed82011-10-06 08:52:33 +00001781 itr->ifindex == dev->ifindex) {
John Fastabend9ab933a2010-12-30 09:26:31 +00001782 prio = itr->app.priority;
1783 break;
1784 }
1785 }
1786 spin_unlock(&dcb_lock);
1787
1788 return prio;
1789}
1790EXPORT_SYMBOL(dcb_getapp);
1791
1792/**
John Fastabendb6db2172011-06-21 07:34:42 +00001793 * dcb_setapp - add CEE dcb application data to app list
John Fastabend9ab933a2010-12-30 09:26:31 +00001794 *
John Fastabendb6db2172011-06-21 07:34:42 +00001795 * Priority 0 is an invalid priority in CEE spec. This routine
1796 * removes applications from the app list if the priority is
1797 * set to zero.
John Fastabend9ab933a2010-12-30 09:26:31 +00001798 */
John Fastabendab6baf92011-06-21 07:34:58 +00001799int dcb_setapp(struct net_device *dev, struct dcb_app *new)
John Fastabend9ab933a2010-12-30 09:26:31 +00001800{
1801 struct dcb_app_type *itr;
John Fastabend7ec79272011-01-31 12:00:59 +00001802 struct dcb_app_type event;
1803
Mark Rustade290ed82011-10-06 08:52:33 +00001804 event.ifindex = dev->ifindex;
John Fastabend7ec79272011-01-31 12:00:59 +00001805 memcpy(&event.app, new, sizeof(event.app));
John Fastabend6bd0e1c2011-10-06 08:52:38 +00001806 if (dev->dcbnl_ops->getdcbx)
1807 event.dcbx = dev->dcbnl_ops->getdcbx(dev);
John Fastabend9ab933a2010-12-30 09:26:31 +00001808
1809 spin_lock(&dcb_lock);
1810 /* Search for existing match and replace */
1811 list_for_each_entry(itr, &dcb_app_list, list) {
1812 if (itr->app.selector == new->selector &&
1813 itr->app.protocol == new->protocol &&
Mark Rustade290ed82011-10-06 08:52:33 +00001814 itr->ifindex == dev->ifindex) {
John Fastabend9ab933a2010-12-30 09:26:31 +00001815 if (new->priority)
1816 itr->app.priority = new->priority;
1817 else {
1818 list_del(&itr->list);
1819 kfree(itr);
1820 }
1821 goto out;
1822 }
1823 }
1824 /* App type does not exist add new application type */
1825 if (new->priority) {
1826 struct dcb_app_type *entry;
1827 entry = kmalloc(sizeof(struct dcb_app_type), GFP_ATOMIC);
1828 if (!entry) {
1829 spin_unlock(&dcb_lock);
1830 return -ENOMEM;
1831 }
1832
1833 memcpy(&entry->app, new, sizeof(*new));
Mark Rustade290ed82011-10-06 08:52:33 +00001834 entry->ifindex = dev->ifindex;
John Fastabend9ab933a2010-12-30 09:26:31 +00001835 list_add(&entry->list, &dcb_app_list);
1836 }
1837out:
1838 spin_unlock(&dcb_lock);
John Fastabend7ec79272011-01-31 12:00:59 +00001839 call_dcbevent_notifiers(DCB_APP_EVENT, &event);
John Fastabend9ab933a2010-12-30 09:26:31 +00001840 return 0;
1841}
1842EXPORT_SYMBOL(dcb_setapp);
1843
John Fastabendb6db2172011-06-21 07:34:42 +00001844/**
John Fastabenda364c8c2011-06-21 07:34:53 +00001845 * dcb_ieee_getapp_mask - retrieve the IEEE DCB application priority
1846 *
1847 * Helper routine which on success returns a non-zero 802.1Qaz user
1848 * priority bitmap otherwise returns 0 to indicate the dcb_app was
1849 * not found in APP list.
1850 */
1851u8 dcb_ieee_getapp_mask(struct net_device *dev, struct dcb_app *app)
1852{
1853 struct dcb_app_type *itr;
1854 u8 prio = 0;
1855
1856 spin_lock(&dcb_lock);
1857 list_for_each_entry(itr, &dcb_app_list, list) {
1858 if (itr->app.selector == app->selector &&
1859 itr->app.protocol == app->protocol &&
Mark Rustade290ed82011-10-06 08:52:33 +00001860 itr->ifindex == dev->ifindex) {
John Fastabenda364c8c2011-06-21 07:34:53 +00001861 prio |= 1 << itr->app.priority;
1862 }
1863 }
1864 spin_unlock(&dcb_lock);
1865
1866 return prio;
1867}
1868EXPORT_SYMBOL(dcb_ieee_getapp_mask);
1869
1870/**
John Fastabendb6db2172011-06-21 07:34:42 +00001871 * dcb_ieee_setapp - add IEEE dcb application data to app list
1872 *
1873 * This adds Application data to the list. Multiple application
1874 * entries may exists for the same selector and protocol as long
1875 * as the priorities are different.
1876 */
1877int dcb_ieee_setapp(struct net_device *dev, struct dcb_app *new)
1878{
1879 struct dcb_app_type *itr, *entry;
1880 struct dcb_app_type event;
1881 int err = 0;
1882
Mark Rustade290ed82011-10-06 08:52:33 +00001883 event.ifindex = dev->ifindex;
John Fastabendb6db2172011-06-21 07:34:42 +00001884 memcpy(&event.app, new, sizeof(event.app));
John Fastabend6bd0e1c2011-10-06 08:52:38 +00001885 if (dev->dcbnl_ops->getdcbx)
1886 event.dcbx = dev->dcbnl_ops->getdcbx(dev);
John Fastabendb6db2172011-06-21 07:34:42 +00001887
1888 spin_lock(&dcb_lock);
1889 /* Search for existing match and abort if found */
1890 list_for_each_entry(itr, &dcb_app_list, list) {
1891 if (itr->app.selector == new->selector &&
1892 itr->app.protocol == new->protocol &&
1893 itr->app.priority == new->priority &&
Mark Rustade290ed82011-10-06 08:52:33 +00001894 itr->ifindex == dev->ifindex) {
John Fastabendb6db2172011-06-21 07:34:42 +00001895 err = -EEXIST;
1896 goto out;
1897 }
1898 }
1899
1900 /* App entry does not exist add new entry */
1901 entry = kmalloc(sizeof(struct dcb_app_type), GFP_ATOMIC);
1902 if (!entry) {
1903 err = -ENOMEM;
1904 goto out;
1905 }
1906
1907 memcpy(&entry->app, new, sizeof(*new));
Mark Rustade290ed82011-10-06 08:52:33 +00001908 entry->ifindex = dev->ifindex;
John Fastabendb6db2172011-06-21 07:34:42 +00001909 list_add(&entry->list, &dcb_app_list);
1910out:
1911 spin_unlock(&dcb_lock);
1912 if (!err)
1913 call_dcbevent_notifiers(DCB_APP_EVENT, &event);
1914 return err;
1915}
1916EXPORT_SYMBOL(dcb_ieee_setapp);
1917
John Fastabendf9ae7e42011-06-21 07:34:48 +00001918/**
1919 * dcb_ieee_delapp - delete IEEE dcb application data from list
1920 *
1921 * This removes a matching APP data from the APP list
1922 */
1923int dcb_ieee_delapp(struct net_device *dev, struct dcb_app *del)
1924{
1925 struct dcb_app_type *itr;
1926 struct dcb_app_type event;
1927 int err = -ENOENT;
1928
Mark Rustade290ed82011-10-06 08:52:33 +00001929 event.ifindex = dev->ifindex;
John Fastabendf9ae7e42011-06-21 07:34:48 +00001930 memcpy(&event.app, del, sizeof(event.app));
John Fastabend6bd0e1c2011-10-06 08:52:38 +00001931 if (dev->dcbnl_ops->getdcbx)
1932 event.dcbx = dev->dcbnl_ops->getdcbx(dev);
John Fastabendf9ae7e42011-06-21 07:34:48 +00001933
1934 spin_lock(&dcb_lock);
1935 /* Search for existing match and remove it. */
1936 list_for_each_entry(itr, &dcb_app_list, list) {
1937 if (itr->app.selector == del->selector &&
1938 itr->app.protocol == del->protocol &&
1939 itr->app.priority == del->priority &&
Mark Rustade290ed82011-10-06 08:52:33 +00001940 itr->ifindex == dev->ifindex) {
John Fastabendf9ae7e42011-06-21 07:34:48 +00001941 list_del(&itr->list);
1942 kfree(itr);
1943 err = 0;
1944 goto out;
1945 }
1946 }
1947
1948out:
1949 spin_unlock(&dcb_lock);
1950 if (!err)
1951 call_dcbevent_notifiers(DCB_APP_EVENT, &event);
1952 return err;
1953}
1954EXPORT_SYMBOL(dcb_ieee_delapp);
1955
Shmulik Ravid7c14c3f2010-12-30 06:27:10 +00001956static void dcb_flushapp(void)
John Fastabend9ab933a2010-12-30 09:26:31 +00001957{
1958 struct dcb_app_type *app;
Dan Carpenter2a8fe002011-01-04 21:03:44 +00001959 struct dcb_app_type *tmp;
John Fastabend9ab933a2010-12-30 09:26:31 +00001960
1961 spin_lock(&dcb_lock);
Dan Carpenter2a8fe002011-01-04 21:03:44 +00001962 list_for_each_entry_safe(app, tmp, &dcb_app_list, list) {
John Fastabend9ab933a2010-12-30 09:26:31 +00001963 list_del(&app->list);
1964 kfree(app);
1965 }
1966 spin_unlock(&dcb_lock);
1967}
1968
Alexander Duyck2f90b862008-11-20 20:52:10 -08001969static int __init dcbnl_init(void)
1970{
John Fastabend9ab933a2010-12-30 09:26:31 +00001971 INIT_LIST_HEAD(&dcb_app_list);
1972
Greg Rosec7ac8672011-06-10 01:27:09 +00001973 rtnl_register(PF_UNSPEC, RTM_GETDCB, dcb_doit, NULL, NULL);
1974 rtnl_register(PF_UNSPEC, RTM_SETDCB, dcb_doit, NULL, NULL);
Alexander Duyck2f90b862008-11-20 20:52:10 -08001975
1976 return 0;
1977}
1978module_init(dcbnl_init);
1979
1980static void __exit dcbnl_exit(void)
1981{
1982 rtnl_unregister(PF_UNSPEC, RTM_GETDCB);
1983 rtnl_unregister(PF_UNSPEC, RTM_SETDCB);
John Fastabend9ab933a2010-12-30 09:26:31 +00001984 dcb_flushapp();
Alexander Duyck2f90b862008-11-20 20:52:10 -08001985}
1986module_exit(dcbnl_exit);