xref: /openbmc/linux/net/dcb/dcbnl.c (revision d9ac62be)
12f90b865SAlexander Duyck /*
2698e1d23SMark Rustad  * Copyright (c) 2008-2011, Intel Corporation.
32f90b865SAlexander Duyck  *
42f90b865SAlexander Duyck  * This program is free software; you can redistribute it and/or modify it
52f90b865SAlexander Duyck  * under the terms and conditions of the GNU General Public License,
62f90b865SAlexander Duyck  * version 2, as published by the Free Software Foundation.
72f90b865SAlexander Duyck  *
82f90b865SAlexander Duyck  * This program is distributed in the hope it will be useful, but WITHOUT
92f90b865SAlexander Duyck  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
102f90b865SAlexander Duyck  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
112f90b865SAlexander Duyck  * more details.
122f90b865SAlexander Duyck  *
132f90b865SAlexander Duyck  * You should have received a copy of the GNU General Public License along with
14c057b190SJeff Kirsher  * this program; if not, see <http://www.gnu.org/licenses/>.
152f90b865SAlexander Duyck  *
162f90b865SAlexander Duyck  * Author: Lucy Liu <lucy.liu@intel.com>
172f90b865SAlexander Duyck  */
182f90b865SAlexander Duyck 
192f90b865SAlexander Duyck #include <linux/netdevice.h>
202f90b865SAlexander Duyck #include <linux/netlink.h>
215a0e3ad6STejun Heo #include <linux/slab.h>
222f90b865SAlexander Duyck #include <net/netlink.h>
232f90b865SAlexander Duyck #include <net/rtnetlink.h>
242f90b865SAlexander Duyck #include <linux/dcbnl.h>
2596b99684SJohn Fastabend #include <net/dcbevent.h>
262f90b865SAlexander Duyck #include <linux/rtnetlink.h>
273a9a231dSPaul Gortmaker #include <linux/module.h>
282f90b865SAlexander Duyck #include <net/sock.h>
292f90b865SAlexander Duyck 
30ae86b9e3SBen Hutchings /* Data Center Bridging (DCB) is a collection of Ethernet enhancements
312f90b865SAlexander Duyck  * intended to allow network traffic with differing requirements
322f90b865SAlexander Duyck  * (highly reliable, no drops vs. best effort vs. low latency) to operate
332f90b865SAlexander Duyck  * and co-exist on Ethernet.  Current DCB features are:
342f90b865SAlexander Duyck  *
352f90b865SAlexander Duyck  * Enhanced Transmission Selection (aka Priority Grouping [PG]) - provides a
362f90b865SAlexander Duyck  *   framework for assigning bandwidth guarantees to traffic classes.
372f90b865SAlexander Duyck  *
382f90b865SAlexander Duyck  * Priority-based Flow Control (PFC) - provides a flow control mechanism which
392f90b865SAlexander Duyck  *   can work independently for each 802.1p priority.
402f90b865SAlexander Duyck  *
412f90b865SAlexander Duyck  * Congestion Notification - provides a mechanism for end-to-end congestion
422f90b865SAlexander Duyck  *   control for protocols which do not have built-in congestion management.
432f90b865SAlexander Duyck  *
442f90b865SAlexander Duyck  * More information about the emerging standards for these Ethernet features
452f90b865SAlexander Duyck  * can be found at: http://www.ieee802.org/1/pages/dcbridges.html
462f90b865SAlexander Duyck  *
472f90b865SAlexander Duyck  * This file implements an rtnetlink interface to allow configuration of DCB
482f90b865SAlexander Duyck  * features for capable devices.
492f90b865SAlexander Duyck  */
502f90b865SAlexander Duyck 
512f90b865SAlexander Duyck MODULE_AUTHOR("Lucy Liu, <lucy.liu@intel.com>");
527a6b6f51SJeff Kirsher MODULE_DESCRIPTION("Data Center Bridging netlink interface");
532f90b865SAlexander Duyck MODULE_LICENSE("GPL");
542f90b865SAlexander Duyck 
552f90b865SAlexander Duyck /**************** DCB attribute policies *************************************/
562f90b865SAlexander Duyck 
572f90b865SAlexander Duyck /* DCB netlink attributes policy */
58b54452b0SAlexey Dobriyan static const struct nla_policy dcbnl_rtnl_policy[DCB_ATTR_MAX + 1] = {
59859ee3c4SAlexander Duyck 	[DCB_ATTR_IFNAME]      = {.type = NLA_NUL_STRING, .len = IFNAMSIZ - 1},
602f90b865SAlexander Duyck 	[DCB_ATTR_STATE]       = {.type = NLA_U8},
612f90b865SAlexander Duyck 	[DCB_ATTR_PFC_CFG]     = {.type = NLA_NESTED},
622f90b865SAlexander Duyck 	[DCB_ATTR_PG_CFG]      = {.type = NLA_NESTED},
632f90b865SAlexander Duyck 	[DCB_ATTR_SET_ALL]     = {.type = NLA_U8},
642f90b865SAlexander Duyck 	[DCB_ATTR_PERM_HWADDR] = {.type = NLA_FLAG},
6546132188SAlexander Duyck 	[DCB_ATTR_CAP]         = {.type = NLA_NESTED},
660eb3aa9bSAlexander Duyck 	[DCB_ATTR_PFC_STATE]   = {.type = NLA_U8},
67859ee3c4SAlexander Duyck 	[DCB_ATTR_BCN]         = {.type = NLA_NESTED},
686fa382afSYi Zou 	[DCB_ATTR_APP]         = {.type = NLA_NESTED},
693e29027aSJohn Fastabend 	[DCB_ATTR_IEEE]	       = {.type = NLA_NESTED},
706241b625SShmulik Ravid 	[DCB_ATTR_DCBX]        = {.type = NLA_U8},
71ea45fe4eSShmulik Ravid 	[DCB_ATTR_FEATCFG]     = {.type = NLA_NESTED},
722f90b865SAlexander Duyck };
732f90b865SAlexander Duyck 
742f90b865SAlexander Duyck /* DCB priority flow control to User Priority nested attributes */
75b54452b0SAlexey Dobriyan static const struct nla_policy dcbnl_pfc_up_nest[DCB_PFC_UP_ATTR_MAX + 1] = {
762f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_0]   = {.type = NLA_U8},
772f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_1]   = {.type = NLA_U8},
782f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_2]   = {.type = NLA_U8},
792f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_3]   = {.type = NLA_U8},
802f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_4]   = {.type = NLA_U8},
812f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_5]   = {.type = NLA_U8},
822f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_6]   = {.type = NLA_U8},
832f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_7]   = {.type = NLA_U8},
842f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_ALL] = {.type = NLA_FLAG},
852f90b865SAlexander Duyck };
862f90b865SAlexander Duyck 
872f90b865SAlexander Duyck /* DCB priority grouping nested attributes */
88b54452b0SAlexey Dobriyan static const struct nla_policy dcbnl_pg_nest[DCB_PG_ATTR_MAX + 1] = {
892f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_0]      = {.type = NLA_NESTED},
902f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_1]      = {.type = NLA_NESTED},
912f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_2]      = {.type = NLA_NESTED},
922f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_3]      = {.type = NLA_NESTED},
932f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_4]      = {.type = NLA_NESTED},
942f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_5]      = {.type = NLA_NESTED},
952f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_6]      = {.type = NLA_NESTED},
962f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_7]      = {.type = NLA_NESTED},
972f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_ALL]    = {.type = NLA_NESTED},
982f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_0]   = {.type = NLA_U8},
992f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_1]   = {.type = NLA_U8},
1002f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_2]   = {.type = NLA_U8},
1012f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_3]   = {.type = NLA_U8},
1022f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_4]   = {.type = NLA_U8},
1032f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_5]   = {.type = NLA_U8},
1042f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_6]   = {.type = NLA_U8},
1052f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_7]   = {.type = NLA_U8},
1062f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_ALL] = {.type = NLA_FLAG},
1072f90b865SAlexander Duyck };
1082f90b865SAlexander Duyck 
1092f90b865SAlexander Duyck /* DCB traffic class nested attributes. */
110b54452b0SAlexey Dobriyan static const struct nla_policy dcbnl_tc_param_nest[DCB_TC_ATTR_PARAM_MAX + 1] = {
1112f90b865SAlexander Duyck 	[DCB_TC_ATTR_PARAM_PGID]            = {.type = NLA_U8},
1122f90b865SAlexander Duyck 	[DCB_TC_ATTR_PARAM_UP_MAPPING]      = {.type = NLA_U8},
1132f90b865SAlexander Duyck 	[DCB_TC_ATTR_PARAM_STRICT_PRIO]     = {.type = NLA_U8},
1142f90b865SAlexander Duyck 	[DCB_TC_ATTR_PARAM_BW_PCT]          = {.type = NLA_U8},
1152f90b865SAlexander Duyck 	[DCB_TC_ATTR_PARAM_ALL]             = {.type = NLA_FLAG},
1162f90b865SAlexander Duyck };
1172f90b865SAlexander Duyck 
11846132188SAlexander Duyck /* DCB capabilities nested attributes. */
119b54452b0SAlexey Dobriyan static const struct nla_policy dcbnl_cap_nest[DCB_CAP_ATTR_MAX + 1] = {
12046132188SAlexander Duyck 	[DCB_CAP_ATTR_ALL]     = {.type = NLA_FLAG},
12146132188SAlexander Duyck 	[DCB_CAP_ATTR_PG]      = {.type = NLA_U8},
12246132188SAlexander Duyck 	[DCB_CAP_ATTR_PFC]     = {.type = NLA_U8},
12346132188SAlexander Duyck 	[DCB_CAP_ATTR_UP2TC]   = {.type = NLA_U8},
12446132188SAlexander Duyck 	[DCB_CAP_ATTR_PG_TCS]  = {.type = NLA_U8},
12546132188SAlexander Duyck 	[DCB_CAP_ATTR_PFC_TCS] = {.type = NLA_U8},
12646132188SAlexander Duyck 	[DCB_CAP_ATTR_GSP]     = {.type = NLA_U8},
12746132188SAlexander Duyck 	[DCB_CAP_ATTR_BCN]     = {.type = NLA_U8},
1286241b625SShmulik Ravid 	[DCB_CAP_ATTR_DCBX]    = {.type = NLA_U8},
12946132188SAlexander Duyck };
1302f90b865SAlexander Duyck 
13133dbabc4SAlexander Duyck /* DCB capabilities nested attributes. */
132b54452b0SAlexey Dobriyan static const struct nla_policy dcbnl_numtcs_nest[DCB_NUMTCS_ATTR_MAX + 1] = {
13333dbabc4SAlexander Duyck 	[DCB_NUMTCS_ATTR_ALL]     = {.type = NLA_FLAG},
13433dbabc4SAlexander Duyck 	[DCB_NUMTCS_ATTR_PG]      = {.type = NLA_U8},
13533dbabc4SAlexander Duyck 	[DCB_NUMTCS_ATTR_PFC]     = {.type = NLA_U8},
13633dbabc4SAlexander Duyck };
13733dbabc4SAlexander Duyck 
138859ee3c4SAlexander Duyck /* DCB BCN nested attributes. */
139b54452b0SAlexey Dobriyan static const struct nla_policy dcbnl_bcn_nest[DCB_BCN_ATTR_MAX + 1] = {
140859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_0]         = {.type = NLA_U8},
141859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_1]         = {.type = NLA_U8},
142859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_2]         = {.type = NLA_U8},
143859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_3]         = {.type = NLA_U8},
144859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_4]         = {.type = NLA_U8},
145859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_5]         = {.type = NLA_U8},
146859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_6]         = {.type = NLA_U8},
147859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_7]         = {.type = NLA_U8},
148859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_ALL]       = {.type = NLA_FLAG},
149f4314e81SDon Skidmore 	[DCB_BCN_ATTR_BCNA_0]       = {.type = NLA_U32},
150f4314e81SDon Skidmore 	[DCB_BCN_ATTR_BCNA_1]       = {.type = NLA_U32},
151859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_ALPHA]        = {.type = NLA_U32},
152859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_BETA]         = {.type = NLA_U32},
153859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_GD]           = {.type = NLA_U32},
154859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_GI]           = {.type = NLA_U32},
155859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_TMAX]         = {.type = NLA_U32},
156859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_TD]           = {.type = NLA_U32},
157859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RMIN]         = {.type = NLA_U32},
158859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_W]            = {.type = NLA_U32},
159859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RD]           = {.type = NLA_U32},
160859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RU]           = {.type = NLA_U32},
161859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_WRTT]         = {.type = NLA_U32},
162859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RI]           = {.type = NLA_U32},
163859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_C]            = {.type = NLA_U32},
164859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_ALL]          = {.type = NLA_FLAG},
165859ee3c4SAlexander Duyck };
166859ee3c4SAlexander Duyck 
1676fa382afSYi Zou /* DCB APP nested attributes. */
168b54452b0SAlexey Dobriyan static const struct nla_policy dcbnl_app_nest[DCB_APP_ATTR_MAX + 1] = {
1696fa382afSYi Zou 	[DCB_APP_ATTR_IDTYPE]       = {.type = NLA_U8},
1706fa382afSYi Zou 	[DCB_APP_ATTR_ID]           = {.type = NLA_U16},
1716fa382afSYi Zou 	[DCB_APP_ATTR_PRIORITY]     = {.type = NLA_U8},
1726fa382afSYi Zou };
1736fa382afSYi Zou 
1743e29027aSJohn Fastabend /* IEEE 802.1Qaz nested attributes. */
1753e29027aSJohn Fastabend static const struct nla_policy dcbnl_ieee_policy[DCB_ATTR_IEEE_MAX + 1] = {
1763e29027aSJohn Fastabend 	[DCB_ATTR_IEEE_ETS]	    = {.len = sizeof(struct ieee_ets)},
1773e29027aSJohn Fastabend 	[DCB_ATTR_IEEE_PFC]	    = {.len = sizeof(struct ieee_pfc)},
1783e29027aSJohn Fastabend 	[DCB_ATTR_IEEE_APP_TABLE]   = {.type = NLA_NESTED},
17908f10affSAmir Vadai 	[DCB_ATTR_IEEE_MAXRATE]   = {.len = sizeof(struct ieee_maxrate)},
1803e29027aSJohn Fastabend };
1813e29027aSJohn Fastabend 
1823e29027aSJohn Fastabend static const struct nla_policy dcbnl_ieee_app[DCB_ATTR_IEEE_APP_MAX + 1] = {
1833e29027aSJohn Fastabend 	[DCB_ATTR_IEEE_APP]	    = {.len = sizeof(struct dcb_app)},
1843e29027aSJohn Fastabend };
1853e29027aSJohn Fastabend 
186ea45fe4eSShmulik Ravid /* DCB number of traffic classes nested attributes. */
187ea45fe4eSShmulik Ravid static const struct nla_policy dcbnl_featcfg_nest[DCB_FEATCFG_ATTR_MAX + 1] = {
188ea45fe4eSShmulik Ravid 	[DCB_FEATCFG_ATTR_ALL]      = {.type = NLA_FLAG},
189ea45fe4eSShmulik Ravid 	[DCB_FEATCFG_ATTR_PG]       = {.type = NLA_U8},
190ea45fe4eSShmulik Ravid 	[DCB_FEATCFG_ATTR_PFC]      = {.type = NLA_U8},
191ea45fe4eSShmulik Ravid 	[DCB_FEATCFG_ATTR_APP]      = {.type = NLA_U8},
192ea45fe4eSShmulik Ravid };
193ea45fe4eSShmulik Ravid 
1949ab933abSJohn Fastabend static LIST_HEAD(dcb_app_list);
1959ab933abSJohn Fastabend static DEFINE_SPINLOCK(dcb_lock);
1969ab933abSJohn Fastabend 
19733a03aadSThomas Graf static struct sk_buff *dcbnl_newmsg(int type, u8 cmd, u32 port, u32 seq,
19833a03aadSThomas Graf 				    u32 flags, struct nlmsghdr **nlhp)
19933a03aadSThomas Graf {
20033a03aadSThomas Graf 	struct sk_buff *skb;
20133a03aadSThomas Graf 	struct dcbmsg *dcb;
20233a03aadSThomas Graf 	struct nlmsghdr *nlh;
20333a03aadSThomas Graf 
20433a03aadSThomas Graf 	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
20533a03aadSThomas Graf 	if (!skb)
20633a03aadSThomas Graf 		return NULL;
20733a03aadSThomas Graf 
20833a03aadSThomas Graf 	nlh = nlmsg_put(skb, port, seq, type, sizeof(*dcb), flags);
209b3908e22SThomas Graf 	BUG_ON(!nlh);
21033a03aadSThomas Graf 
21133a03aadSThomas Graf 	dcb = nlmsg_data(nlh);
21233a03aadSThomas Graf 	dcb->dcb_family = AF_UNSPEC;
21333a03aadSThomas Graf 	dcb->cmd = cmd;
21433a03aadSThomas Graf 	dcb->dcb_pad = 0;
21533a03aadSThomas Graf 
21633a03aadSThomas Graf 	if (nlhp)
21733a03aadSThomas Graf 		*nlhp = nlh;
21833a03aadSThomas Graf 
21933a03aadSThomas Graf 	return skb;
22033a03aadSThomas Graf }
22133a03aadSThomas Graf 
2227be99413SThomas Graf static int dcbnl_getstate(struct net_device *netdev, struct nlmsghdr *nlh,
2237be99413SThomas Graf 			  u32 seq, struct nlattr **tb, struct sk_buff *skb)
2242f90b865SAlexander Duyck {
2252f90b865SAlexander Duyck 	/* if (!tb[DCB_ATTR_STATE] || !netdev->dcbnl_ops->getstate) */
2262f90b865SAlexander Duyck 	if (!netdev->dcbnl_ops->getstate)
2273d1f4869SThomas Graf 		return -EOPNOTSUPP;
2282f90b865SAlexander Duyck 
2297be99413SThomas Graf 	return nla_put_u8(skb, DCB_ATTR_STATE,
2307be99413SThomas Graf 			  netdev->dcbnl_ops->getstate(netdev));
2312f90b865SAlexander Duyck }
2322f90b865SAlexander Duyck 
2337be99413SThomas Graf static int dcbnl_getpfccfg(struct net_device *netdev, struct nlmsghdr *nlh,
2347be99413SThomas Graf 			   u32 seq, struct nlattr **tb, struct sk_buff *skb)
2352f90b865SAlexander Duyck {
2362f90b865SAlexander Duyck 	struct nlattr *data[DCB_PFC_UP_ATTR_MAX + 1], *nest;
2372f90b865SAlexander Duyck 	u8 value;
2383d1f4869SThomas Graf 	int ret;
2392f90b865SAlexander Duyck 	int i;
2402f90b865SAlexander Duyck 	int getall = 0;
2412f90b865SAlexander Duyck 
2423d1f4869SThomas Graf 	if (!tb[DCB_ATTR_PFC_CFG])
2433d1f4869SThomas Graf 		return -EINVAL;
2443d1f4869SThomas Graf 
2453d1f4869SThomas Graf 	if (!netdev->dcbnl_ops->getpfccfg)
2463d1f4869SThomas Graf 		return -EOPNOTSUPP;
2472f90b865SAlexander Duyck 
2482f90b865SAlexander Duyck 	ret = nla_parse_nested(data, DCB_PFC_UP_ATTR_MAX,
2492f90b865SAlexander Duyck 	                       tb[DCB_ATTR_PFC_CFG],
2502f90b865SAlexander Duyck 	                       dcbnl_pfc_up_nest);
2512f90b865SAlexander Duyck 	if (ret)
2523d1f4869SThomas Graf 		return ret;
2532f90b865SAlexander Duyck 
2547be99413SThomas Graf 	nest = nla_nest_start(skb, DCB_ATTR_PFC_CFG);
2552f90b865SAlexander Duyck 	if (!nest)
2563d1f4869SThomas Graf 		return -EMSGSIZE;
2572f90b865SAlexander Duyck 
2582f90b865SAlexander Duyck 	if (data[DCB_PFC_UP_ATTR_ALL])
2592f90b865SAlexander Duyck 		getall = 1;
2602f90b865SAlexander Duyck 
2612f90b865SAlexander Duyck 	for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) {
2622f90b865SAlexander Duyck 		if (!getall && !data[i])
2632f90b865SAlexander Duyck 			continue;
2642f90b865SAlexander Duyck 
2652f90b865SAlexander Duyck 		netdev->dcbnl_ops->getpfccfg(netdev, i - DCB_PFC_UP_ATTR_0,
2662f90b865SAlexander Duyck 		                             &value);
2677be99413SThomas Graf 		ret = nla_put_u8(skb, i, value);
2682f90b865SAlexander Duyck 		if (ret) {
2697be99413SThomas Graf 			nla_nest_cancel(skb, nest);
2703d1f4869SThomas Graf 			return ret;
2712f90b865SAlexander Duyck 		}
2722f90b865SAlexander Duyck 	}
2737be99413SThomas Graf 	nla_nest_end(skb, nest);
2742f90b865SAlexander Duyck 
2752f90b865SAlexander Duyck 	return 0;
2762f90b865SAlexander Duyck }
2772f90b865SAlexander Duyck 
2787be99413SThomas Graf static int dcbnl_getperm_hwaddr(struct net_device *netdev, struct nlmsghdr *nlh,
2797be99413SThomas Graf 				u32 seq, struct nlattr **tb, struct sk_buff *skb)
2802f90b865SAlexander Duyck {
2812f90b865SAlexander Duyck 	u8 perm_addr[MAX_ADDR_LEN];
2822f90b865SAlexander Duyck 
2832f90b865SAlexander Duyck 	if (!netdev->dcbnl_ops->getpermhwaddr)
2843d1f4869SThomas Graf 		return -EOPNOTSUPP;
2852f90b865SAlexander Duyck 
28629cd8ae0SMathias Krause 	memset(perm_addr, 0, sizeof(perm_addr));
2872f90b865SAlexander Duyck 	netdev->dcbnl_ops->getpermhwaddr(netdev, perm_addr);
2882f90b865SAlexander Duyck 
2897be99413SThomas Graf 	return nla_put(skb, DCB_ATTR_PERM_HWADDR, sizeof(perm_addr), perm_addr);
2902f90b865SAlexander Duyck }
2912f90b865SAlexander Duyck 
2927be99413SThomas Graf static int dcbnl_getcap(struct net_device *netdev, struct nlmsghdr *nlh,
2937be99413SThomas Graf 			u32 seq, struct nlattr **tb, struct sk_buff *skb)
29446132188SAlexander Duyck {
29546132188SAlexander Duyck 	struct nlattr *data[DCB_CAP_ATTR_MAX + 1], *nest;
29646132188SAlexander Duyck 	u8 value;
2973d1f4869SThomas Graf 	int ret;
29846132188SAlexander Duyck 	int i;
29946132188SAlexander Duyck 	int getall = 0;
30046132188SAlexander Duyck 
3013d1f4869SThomas Graf 	if (!tb[DCB_ATTR_CAP])
3023d1f4869SThomas Graf 		return -EINVAL;
3033d1f4869SThomas Graf 
3043d1f4869SThomas Graf 	if (!netdev->dcbnl_ops->getcap)
3053d1f4869SThomas Graf 		return -EOPNOTSUPP;
30646132188SAlexander Duyck 
30746132188SAlexander Duyck 	ret = nla_parse_nested(data, DCB_CAP_ATTR_MAX, tb[DCB_ATTR_CAP],
30846132188SAlexander Duyck 	                       dcbnl_cap_nest);
30946132188SAlexander Duyck 	if (ret)
3103d1f4869SThomas Graf 		return ret;
31146132188SAlexander Duyck 
3127be99413SThomas Graf 	nest = nla_nest_start(skb, DCB_ATTR_CAP);
31346132188SAlexander Duyck 	if (!nest)
3143d1f4869SThomas Graf 		return -EMSGSIZE;
31546132188SAlexander Duyck 
31646132188SAlexander Duyck 	if (data[DCB_CAP_ATTR_ALL])
31746132188SAlexander Duyck 		getall = 1;
31846132188SAlexander Duyck 
31946132188SAlexander Duyck 	for (i = DCB_CAP_ATTR_ALL+1; i <= DCB_CAP_ATTR_MAX; i++) {
32046132188SAlexander Duyck 		if (!getall && !data[i])
32146132188SAlexander Duyck 			continue;
32246132188SAlexander Duyck 
32346132188SAlexander Duyck 		if (!netdev->dcbnl_ops->getcap(netdev, i, &value)) {
3247be99413SThomas Graf 			ret = nla_put_u8(skb, i, value);
32546132188SAlexander Duyck 			if (ret) {
3267be99413SThomas Graf 				nla_nest_cancel(skb, nest);
3273d1f4869SThomas Graf 				return ret;
3287be99413SThomas Graf 			}
3297be99413SThomas Graf 		}
3307be99413SThomas Graf 	}
3317be99413SThomas Graf 	nla_nest_end(skb, nest);
33246132188SAlexander Duyck 
33346132188SAlexander Duyck 	return 0;
33446132188SAlexander Duyck }
33546132188SAlexander Duyck 
3367be99413SThomas Graf static int dcbnl_getnumtcs(struct net_device *netdev, struct nlmsghdr *nlh,
3377be99413SThomas Graf 			   u32 seq, struct nlattr **tb, struct sk_buff *skb)
33833dbabc4SAlexander Duyck {
33933dbabc4SAlexander Duyck 	struct nlattr *data[DCB_NUMTCS_ATTR_MAX + 1], *nest;
34033dbabc4SAlexander Duyck 	u8 value;
3413d1f4869SThomas Graf 	int ret;
34233dbabc4SAlexander Duyck 	int i;
34333dbabc4SAlexander Duyck 	int getall = 0;
34433dbabc4SAlexander Duyck 
3453d1f4869SThomas Graf 	if (!tb[DCB_ATTR_NUMTCS])
3463d1f4869SThomas Graf 		return -EINVAL;
3473d1f4869SThomas Graf 
3483d1f4869SThomas Graf 	if (!netdev->dcbnl_ops->getnumtcs)
3493d1f4869SThomas Graf 		return -EOPNOTSUPP;
35033dbabc4SAlexander Duyck 
35133dbabc4SAlexander Duyck 	ret = nla_parse_nested(data, DCB_NUMTCS_ATTR_MAX, tb[DCB_ATTR_NUMTCS],
35233dbabc4SAlexander Duyck 	                       dcbnl_numtcs_nest);
3533d1f4869SThomas Graf 	if (ret)
3543d1f4869SThomas Graf 		return ret;
35533dbabc4SAlexander Duyck 
3567be99413SThomas Graf 	nest = nla_nest_start(skb, DCB_ATTR_NUMTCS);
3573d1f4869SThomas Graf 	if (!nest)
3583d1f4869SThomas Graf 		return -EMSGSIZE;
35933dbabc4SAlexander Duyck 
36033dbabc4SAlexander Duyck 	if (data[DCB_NUMTCS_ATTR_ALL])
36133dbabc4SAlexander Duyck 		getall = 1;
36233dbabc4SAlexander Duyck 
36333dbabc4SAlexander Duyck 	for (i = DCB_NUMTCS_ATTR_ALL+1; i <= DCB_NUMTCS_ATTR_MAX; i++) {
36433dbabc4SAlexander Duyck 		if (!getall && !data[i])
36533dbabc4SAlexander Duyck 			continue;
36633dbabc4SAlexander Duyck 
36733dbabc4SAlexander Duyck 		ret = netdev->dcbnl_ops->getnumtcs(netdev, i, &value);
36833dbabc4SAlexander Duyck 		if (!ret) {
3697be99413SThomas Graf 			ret = nla_put_u8(skb, i, value);
37033dbabc4SAlexander Duyck 			if (ret) {
3717be99413SThomas Graf 				nla_nest_cancel(skb, nest);
3723d1f4869SThomas Graf 				return ret;
37333dbabc4SAlexander Duyck 			}
3743d1f4869SThomas Graf 		} else
3753d1f4869SThomas Graf 			return -EINVAL;
3767be99413SThomas Graf 	}
3777be99413SThomas Graf 	nla_nest_end(skb, nest);
37833dbabc4SAlexander Duyck 
37933dbabc4SAlexander Duyck 	return 0;
38033dbabc4SAlexander Duyck }
38133dbabc4SAlexander Duyck 
3827be99413SThomas Graf static int dcbnl_setnumtcs(struct net_device *netdev, struct nlmsghdr *nlh,
3837be99413SThomas Graf 			   u32 seq, struct nlattr **tb, struct sk_buff *skb)
38433dbabc4SAlexander Duyck {
38533dbabc4SAlexander Duyck 	struct nlattr *data[DCB_NUMTCS_ATTR_MAX + 1];
3863d1f4869SThomas Graf 	int ret;
38733dbabc4SAlexander Duyck 	u8 value;
38833dbabc4SAlexander Duyck 	int i;
38933dbabc4SAlexander Duyck 
3903d1f4869SThomas Graf 	if (!tb[DCB_ATTR_NUMTCS])
3913d1f4869SThomas Graf 		return -EINVAL;
3923d1f4869SThomas Graf 
3933d1f4869SThomas Graf 	if (!netdev->dcbnl_ops->setnumtcs)
3943d1f4869SThomas Graf 		return -EOPNOTSUPP;
39533dbabc4SAlexander Duyck 
39633dbabc4SAlexander Duyck 	ret = nla_parse_nested(data, DCB_NUMTCS_ATTR_MAX, tb[DCB_ATTR_NUMTCS],
39733dbabc4SAlexander Duyck 	                       dcbnl_numtcs_nest);
3987be99413SThomas Graf 	if (ret)
3993d1f4869SThomas Graf 		return ret;
40033dbabc4SAlexander Duyck 
40133dbabc4SAlexander Duyck 	for (i = DCB_NUMTCS_ATTR_ALL+1; i <= DCB_NUMTCS_ATTR_MAX; i++) {
40233dbabc4SAlexander Duyck 		if (data[i] == NULL)
40333dbabc4SAlexander Duyck 			continue;
40433dbabc4SAlexander Duyck 
40533dbabc4SAlexander Duyck 		value = nla_get_u8(data[i]);
40633dbabc4SAlexander Duyck 
40733dbabc4SAlexander Duyck 		ret = netdev->dcbnl_ops->setnumtcs(netdev, i, value);
40833dbabc4SAlexander Duyck 		if (ret)
4097be99413SThomas Graf 			break;
41033dbabc4SAlexander Duyck 	}
41133dbabc4SAlexander Duyck 
4127be99413SThomas Graf 	return nla_put_u8(skb, DCB_ATTR_NUMTCS, !!ret);
41333dbabc4SAlexander Duyck }
41433dbabc4SAlexander Duyck 
4157be99413SThomas Graf static int dcbnl_getpfcstate(struct net_device *netdev, struct nlmsghdr *nlh,
4167be99413SThomas Graf 			     u32 seq, struct nlattr **tb, struct sk_buff *skb)
4170eb3aa9bSAlexander Duyck {
4180eb3aa9bSAlexander Duyck 	if (!netdev->dcbnl_ops->getpfcstate)
4193d1f4869SThomas Graf 		return -EOPNOTSUPP;
4200eb3aa9bSAlexander Duyck 
4217be99413SThomas Graf 	return nla_put_u8(skb, DCB_ATTR_PFC_STATE,
4227be99413SThomas Graf 			  netdev->dcbnl_ops->getpfcstate(netdev));
4230eb3aa9bSAlexander Duyck }
4240eb3aa9bSAlexander Duyck 
4257be99413SThomas Graf static int dcbnl_setpfcstate(struct net_device *netdev, struct nlmsghdr *nlh,
4267be99413SThomas Graf 			     u32 seq, struct nlattr **tb, struct sk_buff *skb)
4270eb3aa9bSAlexander Duyck {
4280eb3aa9bSAlexander Duyck 	u8 value;
4290eb3aa9bSAlexander Duyck 
4303d1f4869SThomas Graf 	if (!tb[DCB_ATTR_PFC_STATE])
4317be99413SThomas Graf 		return -EINVAL;
4320eb3aa9bSAlexander Duyck 
4333d1f4869SThomas Graf 	if (!netdev->dcbnl_ops->setpfcstate)
4343d1f4869SThomas Graf 		return -EOPNOTSUPP;
4353d1f4869SThomas Graf 
4360eb3aa9bSAlexander Duyck 	value = nla_get_u8(tb[DCB_ATTR_PFC_STATE]);
4370eb3aa9bSAlexander Duyck 
4380eb3aa9bSAlexander Duyck 	netdev->dcbnl_ops->setpfcstate(netdev, value);
4390eb3aa9bSAlexander Duyck 
4407be99413SThomas Graf 	return nla_put_u8(skb, DCB_ATTR_PFC_STATE, 0);
4410eb3aa9bSAlexander Duyck }
4420eb3aa9bSAlexander Duyck 
4437be99413SThomas Graf static int dcbnl_getapp(struct net_device *netdev, struct nlmsghdr *nlh,
4447be99413SThomas Graf 			u32 seq, struct nlattr **tb, struct sk_buff *skb)
44557949686SYi Zou {
44657949686SYi Zou 	struct nlattr *app_nest;
44757949686SYi Zou 	struct nlattr *app_tb[DCB_APP_ATTR_MAX + 1];
44857949686SYi Zou 	u16 id;
44957949686SYi Zou 	u8 up, idtype;
4503d1f4869SThomas Graf 	int ret;
45157949686SYi Zou 
4523dce38a0SJohn Fastabend 	if (!tb[DCB_ATTR_APP])
4533d1f4869SThomas Graf 		return -EINVAL;
45457949686SYi Zou 
45557949686SYi Zou 	ret = nla_parse_nested(app_tb, DCB_APP_ATTR_MAX, tb[DCB_ATTR_APP],
45657949686SYi Zou 	                       dcbnl_app_nest);
45757949686SYi Zou 	if (ret)
4583d1f4869SThomas Graf 		return ret;
45957949686SYi Zou 
46057949686SYi Zou 	/* all must be non-null */
46157949686SYi Zou 	if ((!app_tb[DCB_APP_ATTR_IDTYPE]) ||
46257949686SYi Zou 	    (!app_tb[DCB_APP_ATTR_ID]))
4633d1f4869SThomas Graf 		return -EINVAL;
46457949686SYi Zou 
46557949686SYi Zou 	/* either by eth type or by socket number */
46657949686SYi Zou 	idtype = nla_get_u8(app_tb[DCB_APP_ATTR_IDTYPE]);
46757949686SYi Zou 	if ((idtype != DCB_APP_IDTYPE_ETHTYPE) &&
46857949686SYi Zou 	    (idtype != DCB_APP_IDTYPE_PORTNUM))
4693d1f4869SThomas Graf 		return -EINVAL;
47057949686SYi Zou 
47157949686SYi Zou 	id = nla_get_u16(app_tb[DCB_APP_ATTR_ID]);
4723dce38a0SJohn Fastabend 
4733dce38a0SJohn Fastabend 	if (netdev->dcbnl_ops->getapp) {
47457949686SYi Zou 		up = netdev->dcbnl_ops->getapp(netdev, idtype, id);
4753dce38a0SJohn Fastabend 	} else {
4763dce38a0SJohn Fastabend 		struct dcb_app app = {
4773dce38a0SJohn Fastabend 					.selector = idtype,
4783dce38a0SJohn Fastabend 					.protocol = id,
4793dce38a0SJohn Fastabend 				     };
4803dce38a0SJohn Fastabend 		up = dcb_getapp(netdev, &app);
4813dce38a0SJohn Fastabend 	}
48257949686SYi Zou 
4837be99413SThomas Graf 	app_nest = nla_nest_start(skb, DCB_ATTR_APP);
4847be99413SThomas Graf 	if (!app_nest)
4853d1f4869SThomas Graf 		return -EMSGSIZE;
48657949686SYi Zou 
4877be99413SThomas Graf 	ret = nla_put_u8(skb, DCB_APP_ATTR_IDTYPE, idtype);
48857949686SYi Zou 	if (ret)
48957949686SYi Zou 		goto out_cancel;
49057949686SYi Zou 
4917be99413SThomas Graf 	ret = nla_put_u16(skb, DCB_APP_ATTR_ID, id);
49257949686SYi Zou 	if (ret)
49357949686SYi Zou 		goto out_cancel;
49457949686SYi Zou 
4957be99413SThomas Graf 	ret = nla_put_u8(skb, DCB_APP_ATTR_PRIORITY, up);
49657949686SYi Zou 	if (ret)
49757949686SYi Zou 		goto out_cancel;
49857949686SYi Zou 
4997be99413SThomas Graf 	nla_nest_end(skb, app_nest);
50057949686SYi Zou 
5013d1f4869SThomas Graf 	return 0;
50257949686SYi Zou 
50357949686SYi Zou out_cancel:
5047be99413SThomas Graf 	nla_nest_cancel(skb, app_nest);
50557949686SYi Zou 	return ret;
50657949686SYi Zou }
50757949686SYi Zou 
5087be99413SThomas Graf static int dcbnl_setapp(struct net_device *netdev, struct nlmsghdr *nlh,
5097be99413SThomas Graf 			u32 seq, struct nlattr **tb, struct sk_buff *skb)
51057949686SYi Zou {
5113d1f4869SThomas Graf 	int ret;
51257949686SYi Zou 	u16 id;
51357949686SYi Zou 	u8 up, idtype;
51457949686SYi Zou 	struct nlattr *app_tb[DCB_APP_ATTR_MAX + 1];
51557949686SYi Zou 
5169ab933abSJohn Fastabend 	if (!tb[DCB_ATTR_APP])
5173d1f4869SThomas Graf 		return -EINVAL;
51857949686SYi Zou 
51957949686SYi Zou 	ret = nla_parse_nested(app_tb, DCB_APP_ATTR_MAX, tb[DCB_ATTR_APP],
52057949686SYi Zou 	                       dcbnl_app_nest);
52157949686SYi Zou 	if (ret)
5223d1f4869SThomas Graf 		return ret;
52357949686SYi Zou 
52457949686SYi Zou 	/* all must be non-null */
52557949686SYi Zou 	if ((!app_tb[DCB_APP_ATTR_IDTYPE]) ||
52657949686SYi Zou 	    (!app_tb[DCB_APP_ATTR_ID]) ||
52757949686SYi Zou 	    (!app_tb[DCB_APP_ATTR_PRIORITY]))
5283d1f4869SThomas Graf 		return -EINVAL;
52957949686SYi Zou 
53057949686SYi Zou 	/* either by eth type or by socket number */
53157949686SYi Zou 	idtype = nla_get_u8(app_tb[DCB_APP_ATTR_IDTYPE]);
53257949686SYi Zou 	if ((idtype != DCB_APP_IDTYPE_ETHTYPE) &&
53357949686SYi Zou 	    (idtype != DCB_APP_IDTYPE_PORTNUM))
5343d1f4869SThomas Graf 		return -EINVAL;
53557949686SYi Zou 
53657949686SYi Zou 	id = nla_get_u16(app_tb[DCB_APP_ATTR_ID]);
53757949686SYi Zou 	up = nla_get_u8(app_tb[DCB_APP_ATTR_PRIORITY]);
53857949686SYi Zou 
5399ab933abSJohn Fastabend 	if (netdev->dcbnl_ops->setapp) {
5403d1f4869SThomas Graf 		ret = netdev->dcbnl_ops->setapp(netdev, idtype, id, up);
5419ab933abSJohn Fastabend 	} else {
5429ab933abSJohn Fastabend 		struct dcb_app app;
5439ab933abSJohn Fastabend 		app.selector = idtype;
5449ab933abSJohn Fastabend 		app.protocol = id;
5459ab933abSJohn Fastabend 		app.priority = up;
5463d1f4869SThomas Graf 		ret = dcb_setapp(netdev, &app);
5479ab933abSJohn Fastabend 	}
5489ab933abSJohn Fastabend 
5497be99413SThomas Graf 	ret = nla_put_u8(skb, DCB_ATTR_APP, ret);
55008157984SJohn Fastabend 	dcbnl_cee_notify(netdev, RTM_SETDCB, DCB_CMD_SAPP, seq, 0);
5513d1f4869SThomas Graf 
55257949686SYi Zou 	return ret;
55357949686SYi Zou }
55457949686SYi Zou 
5557be99413SThomas Graf static int __dcbnl_pg_getcfg(struct net_device *netdev, struct nlmsghdr *nlh,
5567be99413SThomas Graf 			     struct nlattr **tb, struct sk_buff *skb, int dir)
5572f90b865SAlexander Duyck {
5582f90b865SAlexander Duyck 	struct nlattr *pg_nest, *param_nest, *data;
5592f90b865SAlexander Duyck 	struct nlattr *pg_tb[DCB_PG_ATTR_MAX + 1];
5602f90b865SAlexander Duyck 	struct nlattr *param_tb[DCB_TC_ATTR_PARAM_MAX + 1];
5612f90b865SAlexander Duyck 	u8 prio, pgid, tc_pct, up_map;
5623d1f4869SThomas Graf 	int ret;
5632f90b865SAlexander Duyck 	int getall = 0;
5642f90b865SAlexander Duyck 	int i;
5652f90b865SAlexander Duyck 
5663d1f4869SThomas Graf 	if (!tb[DCB_ATTR_PG_CFG])
5673d1f4869SThomas Graf 		return -EINVAL;
5683d1f4869SThomas Graf 
5693d1f4869SThomas Graf 	if (!netdev->dcbnl_ops->getpgtccfgtx ||
5702f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->getpgtccfgrx ||
5712f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->getpgbwgcfgtx ||
5722f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->getpgbwgcfgrx)
5733d1f4869SThomas Graf 		return -EOPNOTSUPP;
5742f90b865SAlexander Duyck 
5752f90b865SAlexander Duyck 	ret = nla_parse_nested(pg_tb, DCB_PG_ATTR_MAX,
5762f90b865SAlexander Duyck 	                       tb[DCB_ATTR_PG_CFG], dcbnl_pg_nest);
5772f90b865SAlexander Duyck 	if (ret)
5783d1f4869SThomas Graf 		return ret;
5792f90b865SAlexander Duyck 
5807be99413SThomas Graf 	pg_nest = nla_nest_start(skb, DCB_ATTR_PG_CFG);
5812f90b865SAlexander Duyck 	if (!pg_nest)
5823d1f4869SThomas Graf 		return -EMSGSIZE;
5832f90b865SAlexander Duyck 
5842f90b865SAlexander Duyck 	if (pg_tb[DCB_PG_ATTR_TC_ALL])
5852f90b865SAlexander Duyck 		getall = 1;
5862f90b865SAlexander Duyck 
5872f90b865SAlexander Duyck 	for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) {
5882f90b865SAlexander Duyck 		if (!getall && !pg_tb[i])
5892f90b865SAlexander Duyck 			continue;
5902f90b865SAlexander Duyck 
5912f90b865SAlexander Duyck 		if (pg_tb[DCB_PG_ATTR_TC_ALL])
5922f90b865SAlexander Duyck 			data = pg_tb[DCB_PG_ATTR_TC_ALL];
5932f90b865SAlexander Duyck 		else
5942f90b865SAlexander Duyck 			data = pg_tb[i];
5952f90b865SAlexander Duyck 		ret = nla_parse_nested(param_tb, DCB_TC_ATTR_PARAM_MAX,
5962f90b865SAlexander Duyck 				       data, dcbnl_tc_param_nest);
5972f90b865SAlexander Duyck 		if (ret)
5982f90b865SAlexander Duyck 			goto err_pg;
5992f90b865SAlexander Duyck 
6007be99413SThomas Graf 		param_nest = nla_nest_start(skb, i);
6012f90b865SAlexander Duyck 		if (!param_nest)
6022f90b865SAlexander Duyck 			goto err_pg;
6032f90b865SAlexander Duyck 
6042f90b865SAlexander Duyck 		pgid = DCB_ATTR_VALUE_UNDEFINED;
6052f90b865SAlexander Duyck 		prio = DCB_ATTR_VALUE_UNDEFINED;
6062f90b865SAlexander Duyck 		tc_pct = DCB_ATTR_VALUE_UNDEFINED;
6072f90b865SAlexander Duyck 		up_map = DCB_ATTR_VALUE_UNDEFINED;
6082f90b865SAlexander Duyck 
6092f90b865SAlexander Duyck 		if (dir) {
6102f90b865SAlexander Duyck 			/* Rx */
6112f90b865SAlexander Duyck 			netdev->dcbnl_ops->getpgtccfgrx(netdev,
6122f90b865SAlexander Duyck 						i - DCB_PG_ATTR_TC_0, &prio,
6132f90b865SAlexander Duyck 						&pgid, &tc_pct, &up_map);
6142f90b865SAlexander Duyck 		} else {
6152f90b865SAlexander Duyck 			/* Tx */
6162f90b865SAlexander Duyck 			netdev->dcbnl_ops->getpgtccfgtx(netdev,
6172f90b865SAlexander Duyck 						i - DCB_PG_ATTR_TC_0, &prio,
6182f90b865SAlexander Duyck 						&pgid, &tc_pct, &up_map);
6192f90b865SAlexander Duyck 		}
6202f90b865SAlexander Duyck 
6212f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_PGID] ||
6222f90b865SAlexander Duyck 		    param_tb[DCB_TC_ATTR_PARAM_ALL]) {
6237be99413SThomas Graf 			ret = nla_put_u8(skb,
6242f90b865SAlexander Duyck 			                 DCB_TC_ATTR_PARAM_PGID, pgid);
6252f90b865SAlexander Duyck 			if (ret)
6262f90b865SAlexander Duyck 				goto err_param;
6272f90b865SAlexander Duyck 		}
6282f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING] ||
6292f90b865SAlexander Duyck 		    param_tb[DCB_TC_ATTR_PARAM_ALL]) {
6307be99413SThomas Graf 			ret = nla_put_u8(skb,
6312f90b865SAlexander Duyck 			                 DCB_TC_ATTR_PARAM_UP_MAPPING, up_map);
6322f90b865SAlexander Duyck 			if (ret)
6332f90b865SAlexander Duyck 				goto err_param;
6342f90b865SAlexander Duyck 		}
6352f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO] ||
6362f90b865SAlexander Duyck 		    param_tb[DCB_TC_ATTR_PARAM_ALL]) {
6377be99413SThomas Graf 			ret = nla_put_u8(skb,
6382f90b865SAlexander Duyck 			                 DCB_TC_ATTR_PARAM_STRICT_PRIO, prio);
6392f90b865SAlexander Duyck 			if (ret)
6402f90b865SAlexander Duyck 				goto err_param;
6412f90b865SAlexander Duyck 		}
6422f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_BW_PCT] ||
6432f90b865SAlexander Duyck 		    param_tb[DCB_TC_ATTR_PARAM_ALL]) {
6447be99413SThomas Graf 			ret = nla_put_u8(skb, DCB_TC_ATTR_PARAM_BW_PCT,
6452f90b865SAlexander Duyck 			                 tc_pct);
6462f90b865SAlexander Duyck 			if (ret)
6472f90b865SAlexander Duyck 				goto err_param;
6482f90b865SAlexander Duyck 		}
6497be99413SThomas Graf 		nla_nest_end(skb, param_nest);
6502f90b865SAlexander Duyck 	}
6512f90b865SAlexander Duyck 
6522f90b865SAlexander Duyck 	if (pg_tb[DCB_PG_ATTR_BW_ID_ALL])
6532f90b865SAlexander Duyck 		getall = 1;
6542f90b865SAlexander Duyck 	else
6552f90b865SAlexander Duyck 		getall = 0;
6562f90b865SAlexander Duyck 
6572f90b865SAlexander Duyck 	for (i = DCB_PG_ATTR_BW_ID_0; i <= DCB_PG_ATTR_BW_ID_7; i++) {
6582f90b865SAlexander Duyck 		if (!getall && !pg_tb[i])
6592f90b865SAlexander Duyck 			continue;
6602f90b865SAlexander Duyck 
6612f90b865SAlexander Duyck 		tc_pct = DCB_ATTR_VALUE_UNDEFINED;
6622f90b865SAlexander Duyck 
6632f90b865SAlexander Duyck 		if (dir) {
6642f90b865SAlexander Duyck 			/* Rx */
6652f90b865SAlexander Duyck 			netdev->dcbnl_ops->getpgbwgcfgrx(netdev,
6662f90b865SAlexander Duyck 					i - DCB_PG_ATTR_BW_ID_0, &tc_pct);
6672f90b865SAlexander Duyck 		} else {
6682f90b865SAlexander Duyck 			/* Tx */
6692f90b865SAlexander Duyck 			netdev->dcbnl_ops->getpgbwgcfgtx(netdev,
6702f90b865SAlexander Duyck 					i - DCB_PG_ATTR_BW_ID_0, &tc_pct);
6712f90b865SAlexander Duyck 		}
6727be99413SThomas Graf 		ret = nla_put_u8(skb, i, tc_pct);
6732f90b865SAlexander Duyck 		if (ret)
6742f90b865SAlexander Duyck 			goto err_pg;
6752f90b865SAlexander Duyck 	}
6762f90b865SAlexander Duyck 
6777be99413SThomas Graf 	nla_nest_end(skb, pg_nest);
6782f90b865SAlexander Duyck 
6792f90b865SAlexander Duyck 	return 0;
6802f90b865SAlexander Duyck 
6812f90b865SAlexander Duyck err_param:
6827be99413SThomas Graf 	nla_nest_cancel(skb, param_nest);
6832f90b865SAlexander Duyck err_pg:
6847be99413SThomas Graf 	nla_nest_cancel(skb, pg_nest);
6853d1f4869SThomas Graf 
6863d1f4869SThomas Graf 	return -EMSGSIZE;
6872f90b865SAlexander Duyck }
6882f90b865SAlexander Duyck 
6897be99413SThomas Graf static int dcbnl_pgtx_getcfg(struct net_device *netdev, struct nlmsghdr *nlh,
6907be99413SThomas Graf 			     u32 seq, struct nlattr **tb, struct sk_buff *skb)
6912f90b865SAlexander Duyck {
6927be99413SThomas Graf 	return __dcbnl_pg_getcfg(netdev, nlh, tb, skb, 0);
6932f90b865SAlexander Duyck }
6942f90b865SAlexander Duyck 
6957be99413SThomas Graf static int dcbnl_pgrx_getcfg(struct net_device *netdev, struct nlmsghdr *nlh,
6967be99413SThomas Graf 			     u32 seq, struct nlattr **tb, struct sk_buff *skb)
6972f90b865SAlexander Duyck {
6987be99413SThomas Graf 	return __dcbnl_pg_getcfg(netdev, nlh, tb, skb, 1);
6992f90b865SAlexander Duyck }
7002f90b865SAlexander Duyck 
7017be99413SThomas Graf static int dcbnl_setstate(struct net_device *netdev, struct nlmsghdr *nlh,
7027be99413SThomas Graf 			  u32 seq, struct nlattr **tb, struct sk_buff *skb)
7032f90b865SAlexander Duyck {
7042f90b865SAlexander Duyck 	u8 value;
7052f90b865SAlexander Duyck 
7063d1f4869SThomas Graf 	if (!tb[DCB_ATTR_STATE])
7077be99413SThomas Graf 		return -EINVAL;
7082f90b865SAlexander Duyck 
7093d1f4869SThomas Graf 	if (!netdev->dcbnl_ops->setstate)
7103d1f4869SThomas Graf 		return -EOPNOTSUPP;
7113d1f4869SThomas Graf 
7122f90b865SAlexander Duyck 	value = nla_get_u8(tb[DCB_ATTR_STATE]);
7132f90b865SAlexander Duyck 
7147be99413SThomas Graf 	return nla_put_u8(skb, DCB_ATTR_STATE,
7157be99413SThomas Graf 			  netdev->dcbnl_ops->setstate(netdev, value));
7162f90b865SAlexander Duyck }
7172f90b865SAlexander Duyck 
7187be99413SThomas Graf static int dcbnl_setpfccfg(struct net_device *netdev, struct nlmsghdr *nlh,
7197be99413SThomas Graf 			   u32 seq, struct nlattr **tb, struct sk_buff *skb)
7202f90b865SAlexander Duyck {
7212f90b865SAlexander Duyck 	struct nlattr *data[DCB_PFC_UP_ATTR_MAX + 1];
7222f90b865SAlexander Duyck 	int i;
7233d1f4869SThomas Graf 	int ret;
7242f90b865SAlexander Duyck 	u8 value;
7252f90b865SAlexander Duyck 
7263d1f4869SThomas Graf 	if (!tb[DCB_ATTR_PFC_CFG])
7273d1f4869SThomas Graf 		return -EINVAL;
7283d1f4869SThomas Graf 
7293d1f4869SThomas Graf 	if (!netdev->dcbnl_ops->setpfccfg)
7303d1f4869SThomas Graf 		return -EOPNOTSUPP;
7312f90b865SAlexander Duyck 
7322f90b865SAlexander Duyck 	ret = nla_parse_nested(data, DCB_PFC_UP_ATTR_MAX,
7332f90b865SAlexander Duyck 	                       tb[DCB_ATTR_PFC_CFG],
7342f90b865SAlexander Duyck 	                       dcbnl_pfc_up_nest);
7352f90b865SAlexander Duyck 	if (ret)
7363d1f4869SThomas Graf 		return ret;
7372f90b865SAlexander Duyck 
7382f90b865SAlexander Duyck 	for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) {
7392f90b865SAlexander Duyck 		if (data[i] == NULL)
7402f90b865SAlexander Duyck 			continue;
7412f90b865SAlexander Duyck 		value = nla_get_u8(data[i]);
7422f90b865SAlexander Duyck 		netdev->dcbnl_ops->setpfccfg(netdev,
7432f90b865SAlexander Duyck 			data[i]->nla_type - DCB_PFC_UP_ATTR_0, value);
7442f90b865SAlexander Duyck 	}
7452f90b865SAlexander Duyck 
7467be99413SThomas Graf 	return nla_put_u8(skb, DCB_ATTR_PFC_CFG, 0);
7472f90b865SAlexander Duyck }
7482f90b865SAlexander Duyck 
7497be99413SThomas Graf static int dcbnl_setall(struct net_device *netdev, struct nlmsghdr *nlh,
7507be99413SThomas Graf 			u32 seq, struct nlattr **tb, struct sk_buff *skb)
7512f90b865SAlexander Duyck {
7523d1f4869SThomas Graf 	int ret;
7532f90b865SAlexander Duyck 
7543d1f4869SThomas Graf 	if (!tb[DCB_ATTR_SET_ALL])
7553d1f4869SThomas Graf 		return -EINVAL;
7563d1f4869SThomas Graf 
7573d1f4869SThomas Graf 	if (!netdev->dcbnl_ops->setall)
7583d1f4869SThomas Graf 		return -EOPNOTSUPP;
7592f90b865SAlexander Duyck 
7607be99413SThomas Graf 	ret = nla_put_u8(skb, DCB_ATTR_SET_ALL,
7617be99413SThomas Graf 			 netdev->dcbnl_ops->setall(netdev));
76208157984SJohn Fastabend 	dcbnl_cee_notify(netdev, RTM_SETDCB, DCB_CMD_SET_ALL, seq, 0);
7632f90b865SAlexander Duyck 
7642f90b865SAlexander Duyck 	return ret;
7652f90b865SAlexander Duyck }
7662f90b865SAlexander Duyck 
7677be99413SThomas Graf static int __dcbnl_pg_setcfg(struct net_device *netdev, struct nlmsghdr *nlh,
7687be99413SThomas Graf 			     u32 seq, struct nlattr **tb, struct sk_buff *skb,
7697be99413SThomas Graf 			     int dir)
7702f90b865SAlexander Duyck {
7712f90b865SAlexander Duyck 	struct nlattr *pg_tb[DCB_PG_ATTR_MAX + 1];
7722f90b865SAlexander Duyck 	struct nlattr *param_tb[DCB_TC_ATTR_PARAM_MAX + 1];
7733d1f4869SThomas Graf 	int ret;
7742f90b865SAlexander Duyck 	int i;
7752f90b865SAlexander Duyck 	u8 pgid;
7762f90b865SAlexander Duyck 	u8 up_map;
7772f90b865SAlexander Duyck 	u8 prio;
7782f90b865SAlexander Duyck 	u8 tc_pct;
7792f90b865SAlexander Duyck 
7803d1f4869SThomas Graf 	if (!tb[DCB_ATTR_PG_CFG])
7813d1f4869SThomas Graf 		return -EINVAL;
7823d1f4869SThomas Graf 
7833d1f4869SThomas Graf 	if (!netdev->dcbnl_ops->setpgtccfgtx ||
7842f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->setpgtccfgrx ||
7852f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->setpgbwgcfgtx ||
7862f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->setpgbwgcfgrx)
7873d1f4869SThomas Graf 		return -EOPNOTSUPP;
7882f90b865SAlexander Duyck 
7892f90b865SAlexander Duyck 	ret = nla_parse_nested(pg_tb, DCB_PG_ATTR_MAX,
7902f90b865SAlexander Duyck 	                       tb[DCB_ATTR_PG_CFG], dcbnl_pg_nest);
7912f90b865SAlexander Duyck 	if (ret)
7923d1f4869SThomas Graf 		return ret;
7932f90b865SAlexander Duyck 
7942f90b865SAlexander Duyck 	for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) {
7952f90b865SAlexander Duyck 		if (!pg_tb[i])
7962f90b865SAlexander Duyck 			continue;
7972f90b865SAlexander Duyck 
7982f90b865SAlexander Duyck 		ret = nla_parse_nested(param_tb, DCB_TC_ATTR_PARAM_MAX,
7992f90b865SAlexander Duyck 		                       pg_tb[i], dcbnl_tc_param_nest);
8002f90b865SAlexander Duyck 		if (ret)
8013d1f4869SThomas Graf 			return ret;
8022f90b865SAlexander Duyck 
8032f90b865SAlexander Duyck 		pgid = DCB_ATTR_VALUE_UNDEFINED;
8042f90b865SAlexander Duyck 		prio = DCB_ATTR_VALUE_UNDEFINED;
8052f90b865SAlexander Duyck 		tc_pct = DCB_ATTR_VALUE_UNDEFINED;
8062f90b865SAlexander Duyck 		up_map = DCB_ATTR_VALUE_UNDEFINED;
8072f90b865SAlexander Duyck 
8082f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO])
8092f90b865SAlexander Duyck 			prio =
8102f90b865SAlexander Duyck 			    nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO]);
8112f90b865SAlexander Duyck 
8122f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_PGID])
8132f90b865SAlexander Duyck 			pgid = nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_PGID]);
8142f90b865SAlexander Duyck 
8152f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_BW_PCT])
8162f90b865SAlexander Duyck 			tc_pct = nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_BW_PCT]);
8172f90b865SAlexander Duyck 
8182f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING])
8192f90b865SAlexander Duyck 			up_map =
8202f90b865SAlexander Duyck 			     nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING]);
8212f90b865SAlexander Duyck 
8222f90b865SAlexander Duyck 		/* dir: Tx = 0, Rx = 1 */
8232f90b865SAlexander Duyck 		if (dir) {
8242f90b865SAlexander Duyck 			/* Rx */
8252f90b865SAlexander Duyck 			netdev->dcbnl_ops->setpgtccfgrx(netdev,
8262f90b865SAlexander Duyck 				i - DCB_PG_ATTR_TC_0,
8272f90b865SAlexander Duyck 				prio, pgid, tc_pct, up_map);
8282f90b865SAlexander Duyck 		} else {
8292f90b865SAlexander Duyck 			/* Tx */
8302f90b865SAlexander Duyck 			netdev->dcbnl_ops->setpgtccfgtx(netdev,
8312f90b865SAlexander Duyck 				i - DCB_PG_ATTR_TC_0,
8322f90b865SAlexander Duyck 				prio, pgid, tc_pct, up_map);
8332f90b865SAlexander Duyck 		}
8342f90b865SAlexander Duyck 	}
8352f90b865SAlexander Duyck 
8362f90b865SAlexander Duyck 	for (i = DCB_PG_ATTR_BW_ID_0; i <= DCB_PG_ATTR_BW_ID_7; i++) {
8372f90b865SAlexander Duyck 		if (!pg_tb[i])
8382f90b865SAlexander Duyck 			continue;
8392f90b865SAlexander Duyck 
8402f90b865SAlexander Duyck 		tc_pct = nla_get_u8(pg_tb[i]);
8412f90b865SAlexander Duyck 
8422f90b865SAlexander Duyck 		/* dir: Tx = 0, Rx = 1 */
8432f90b865SAlexander Duyck 		if (dir) {
8442f90b865SAlexander Duyck 			/* Rx */
8452f90b865SAlexander Duyck 			netdev->dcbnl_ops->setpgbwgcfgrx(netdev,
8462f90b865SAlexander Duyck 					 i - DCB_PG_ATTR_BW_ID_0, tc_pct);
8472f90b865SAlexander Duyck 		} else {
8482f90b865SAlexander Duyck 			/* Tx */
8492f90b865SAlexander Duyck 			netdev->dcbnl_ops->setpgbwgcfgtx(netdev,
8502f90b865SAlexander Duyck 					 i - DCB_PG_ATTR_BW_ID_0, tc_pct);
8512f90b865SAlexander Duyck 		}
8522f90b865SAlexander Duyck 	}
8532f90b865SAlexander Duyck 
854bb1dfefdSJohn Fastabend 	return nla_put_u8(skb, DCB_ATTR_PG_CFG, 0);
8552f90b865SAlexander Duyck }
8562f90b865SAlexander Duyck 
8577be99413SThomas Graf static int dcbnl_pgtx_setcfg(struct net_device *netdev, struct nlmsghdr *nlh,
8587be99413SThomas Graf 			     u32 seq, struct nlattr **tb, struct sk_buff *skb)
8592f90b865SAlexander Duyck {
8607be99413SThomas Graf 	return __dcbnl_pg_setcfg(netdev, nlh, seq, tb, skb, 0);
8612f90b865SAlexander Duyck }
8622f90b865SAlexander Duyck 
8637be99413SThomas Graf static int dcbnl_pgrx_setcfg(struct net_device *netdev, struct nlmsghdr *nlh,
8647be99413SThomas Graf 			     u32 seq, struct nlattr **tb, struct sk_buff *skb)
8652f90b865SAlexander Duyck {
8667be99413SThomas Graf 	return __dcbnl_pg_setcfg(netdev, nlh, seq, tb, skb, 1);
8672f90b865SAlexander Duyck }
8682f90b865SAlexander Duyck 
8697be99413SThomas Graf static int dcbnl_bcn_getcfg(struct net_device *netdev, struct nlmsghdr *nlh,
8707be99413SThomas Graf 			    u32 seq, struct nlattr **tb, struct sk_buff *skb)
871859ee3c4SAlexander Duyck {
872859ee3c4SAlexander Duyck 	struct nlattr *bcn_nest;
873859ee3c4SAlexander Duyck 	struct nlattr *bcn_tb[DCB_BCN_ATTR_MAX + 1];
874859ee3c4SAlexander Duyck 	u8 value_byte;
875859ee3c4SAlexander Duyck 	u32 value_integer;
8763d1f4869SThomas Graf 	int ret;
877859ee3c4SAlexander Duyck 	bool getall = false;
878859ee3c4SAlexander Duyck 	int i;
879859ee3c4SAlexander Duyck 
8803d1f4869SThomas Graf 	if (!tb[DCB_ATTR_BCN])
8813d1f4869SThomas Graf 		return -EINVAL;
8823d1f4869SThomas Graf 
8833d1f4869SThomas Graf 	if (!netdev->dcbnl_ops->getbcnrp ||
884859ee3c4SAlexander Duyck 	    !netdev->dcbnl_ops->getbcncfg)
8853d1f4869SThomas Graf 		return -EOPNOTSUPP;
886859ee3c4SAlexander Duyck 
887859ee3c4SAlexander Duyck 	ret = nla_parse_nested(bcn_tb, DCB_BCN_ATTR_MAX,
888859ee3c4SAlexander Duyck 	                       tb[DCB_ATTR_BCN], dcbnl_bcn_nest);
889859ee3c4SAlexander Duyck 	if (ret)
8903d1f4869SThomas Graf 		return ret;
891859ee3c4SAlexander Duyck 
8927be99413SThomas Graf 	bcn_nest = nla_nest_start(skb, DCB_ATTR_BCN);
893859ee3c4SAlexander Duyck 	if (!bcn_nest)
8943d1f4869SThomas Graf 		return -EMSGSIZE;
895859ee3c4SAlexander Duyck 
896859ee3c4SAlexander Duyck 	if (bcn_tb[DCB_BCN_ATTR_ALL])
897859ee3c4SAlexander Duyck 		getall = true;
898859ee3c4SAlexander Duyck 
899859ee3c4SAlexander Duyck 	for (i = DCB_BCN_ATTR_RP_0; i <= DCB_BCN_ATTR_RP_7; i++) {
900859ee3c4SAlexander Duyck 		if (!getall && !bcn_tb[i])
901859ee3c4SAlexander Duyck 			continue;
902859ee3c4SAlexander Duyck 
903859ee3c4SAlexander Duyck 		netdev->dcbnl_ops->getbcnrp(netdev, i - DCB_BCN_ATTR_RP_0,
904859ee3c4SAlexander Duyck 		                            &value_byte);
9057be99413SThomas Graf 		ret = nla_put_u8(skb, i, value_byte);
906859ee3c4SAlexander Duyck 		if (ret)
907859ee3c4SAlexander Duyck 			goto err_bcn;
908859ee3c4SAlexander Duyck 	}
909859ee3c4SAlexander Duyck 
910f4314e81SDon Skidmore 	for (i = DCB_BCN_ATTR_BCNA_0; i <= DCB_BCN_ATTR_RI; i++) {
911859ee3c4SAlexander Duyck 		if (!getall && !bcn_tb[i])
912859ee3c4SAlexander Duyck 			continue;
913859ee3c4SAlexander Duyck 
914859ee3c4SAlexander Duyck 		netdev->dcbnl_ops->getbcncfg(netdev, i,
915859ee3c4SAlexander Duyck 		                             &value_integer);
9167be99413SThomas Graf 		ret = nla_put_u32(skb, i, value_integer);
917859ee3c4SAlexander Duyck 		if (ret)
918859ee3c4SAlexander Duyck 			goto err_bcn;
919859ee3c4SAlexander Duyck 	}
920859ee3c4SAlexander Duyck 
9217be99413SThomas Graf 	nla_nest_end(skb, bcn_nest);
922859ee3c4SAlexander Duyck 
923859ee3c4SAlexander Duyck 	return 0;
924859ee3c4SAlexander Duyck 
925859ee3c4SAlexander Duyck err_bcn:
9267be99413SThomas Graf 	nla_nest_cancel(skb, bcn_nest);
927859ee3c4SAlexander Duyck 	return ret;
928859ee3c4SAlexander Duyck }
929859ee3c4SAlexander Duyck 
9307be99413SThomas Graf static int dcbnl_bcn_setcfg(struct net_device *netdev, struct nlmsghdr *nlh,
9317be99413SThomas Graf 			    u32 seq, struct nlattr **tb, struct sk_buff *skb)
932859ee3c4SAlexander Duyck {
933859ee3c4SAlexander Duyck 	struct nlattr *data[DCB_BCN_ATTR_MAX + 1];
934859ee3c4SAlexander Duyck 	int i;
9353d1f4869SThomas Graf 	int ret;
936859ee3c4SAlexander Duyck 	u8 value_byte;
937859ee3c4SAlexander Duyck 	u32 value_int;
938859ee3c4SAlexander Duyck 
9393d1f4869SThomas Graf 	if (!tb[DCB_ATTR_BCN])
9403d1f4869SThomas Graf 		return -EINVAL;
9413d1f4869SThomas Graf 
9423d1f4869SThomas Graf 	if (!netdev->dcbnl_ops->setbcncfg ||
943f64f9e71SJoe Perches 	    !netdev->dcbnl_ops->setbcnrp)
9443d1f4869SThomas Graf 		return -EOPNOTSUPP;
945859ee3c4SAlexander Duyck 
946859ee3c4SAlexander Duyck 	ret = nla_parse_nested(data, DCB_BCN_ATTR_MAX,
947859ee3c4SAlexander Duyck 	                       tb[DCB_ATTR_BCN],
948859ee3c4SAlexander Duyck 	                       dcbnl_pfc_up_nest);
949859ee3c4SAlexander Duyck 	if (ret)
9503d1f4869SThomas Graf 		return ret;
951859ee3c4SAlexander Duyck 
952859ee3c4SAlexander Duyck 	for (i = DCB_BCN_ATTR_RP_0; i <= DCB_BCN_ATTR_RP_7; i++) {
953859ee3c4SAlexander Duyck 		if (data[i] == NULL)
954859ee3c4SAlexander Duyck 			continue;
955859ee3c4SAlexander Duyck 		value_byte = nla_get_u8(data[i]);
956859ee3c4SAlexander Duyck 		netdev->dcbnl_ops->setbcnrp(netdev,
957859ee3c4SAlexander Duyck 			data[i]->nla_type - DCB_BCN_ATTR_RP_0, value_byte);
958859ee3c4SAlexander Duyck 	}
959859ee3c4SAlexander Duyck 
960f4314e81SDon Skidmore 	for (i = DCB_BCN_ATTR_BCNA_0; i <= DCB_BCN_ATTR_RI; i++) {
961859ee3c4SAlexander Duyck 		if (data[i] == NULL)
962859ee3c4SAlexander Duyck 			continue;
963859ee3c4SAlexander Duyck 		value_int = nla_get_u32(data[i]);
964859ee3c4SAlexander Duyck 		netdev->dcbnl_ops->setbcncfg(netdev,
965859ee3c4SAlexander Duyck 	                                     i, value_int);
966859ee3c4SAlexander Duyck 	}
967859ee3c4SAlexander Duyck 
9683d1f4869SThomas Graf 	return nla_put_u8(skb, DCB_ATTR_BCN, 0);
969859ee3c4SAlexander Duyck }
970859ee3c4SAlexander Duyck 
971dc6ed1dfSShmulik Ravid static int dcbnl_build_peer_app(struct net_device *netdev, struct sk_buff* skb,
972dc6ed1dfSShmulik Ravid 				int app_nested_type, int app_info_type,
973dc6ed1dfSShmulik Ravid 				int app_entry_type)
974eed84713SShmulik Ravid {
975eed84713SShmulik Ravid 	struct dcb_peer_app_info info;
976eed84713SShmulik Ravid 	struct dcb_app *table = NULL;
977eed84713SShmulik Ravid 	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
978eed84713SShmulik Ravid 	u16 app_count;
979eed84713SShmulik Ravid 	int err;
980eed84713SShmulik Ravid 
981eed84713SShmulik Ravid 
982eed84713SShmulik Ravid 	/**
983eed84713SShmulik Ravid 	 * retrieve the peer app configuration form the driver. If the driver
984eed84713SShmulik Ravid 	 * handlers fail exit without doing anything
985eed84713SShmulik Ravid 	 */
986eed84713SShmulik Ravid 	err = ops->peer_getappinfo(netdev, &info, &app_count);
987eed84713SShmulik Ravid 	if (!err && app_count) {
988eed84713SShmulik Ravid 		table = kmalloc(sizeof(struct dcb_app) * app_count, GFP_KERNEL);
989eed84713SShmulik Ravid 		if (!table)
990eed84713SShmulik Ravid 			return -ENOMEM;
991eed84713SShmulik Ravid 
992eed84713SShmulik Ravid 		err = ops->peer_getapptable(netdev, table);
993eed84713SShmulik Ravid 	}
994eed84713SShmulik Ravid 
995eed84713SShmulik Ravid 	if (!err) {
996eed84713SShmulik Ravid 		u16 i;
997eed84713SShmulik Ravid 		struct nlattr *app;
998eed84713SShmulik Ravid 
999eed84713SShmulik Ravid 		/**
1000eed84713SShmulik Ravid 		 * build the message, from here on the only possible failure
1001eed84713SShmulik Ravid 		 * is due to the skb size
1002eed84713SShmulik Ravid 		 */
1003eed84713SShmulik Ravid 		err = -EMSGSIZE;
1004eed84713SShmulik Ravid 
1005dc6ed1dfSShmulik Ravid 		app = nla_nest_start(skb, app_nested_type);
1006eed84713SShmulik Ravid 		if (!app)
1007eed84713SShmulik Ravid 			goto nla_put_failure;
1008eed84713SShmulik Ravid 
10091eb4c977SDavid S. Miller 		if (app_info_type &&
10101eb4c977SDavid S. Miller 		    nla_put(skb, app_info_type, sizeof(info), &info))
10111eb4c977SDavid S. Miller 			goto nla_put_failure;
1012dc6ed1dfSShmulik Ravid 
10131eb4c977SDavid S. Miller 		for (i = 0; i < app_count; i++) {
10141eb4c977SDavid S. Miller 			if (nla_put(skb, app_entry_type, sizeof(struct dcb_app),
10151eb4c977SDavid S. Miller 				    &table[i]))
10161eb4c977SDavid S. Miller 				goto nla_put_failure;
10171eb4c977SDavid S. Miller 		}
1018eed84713SShmulik Ravid 		nla_nest_end(skb, app);
1019eed84713SShmulik Ravid 	}
1020eed84713SShmulik Ravid 	err = 0;
1021eed84713SShmulik Ravid 
1022eed84713SShmulik Ravid nla_put_failure:
1023eed84713SShmulik Ravid 	kfree(table);
1024eed84713SShmulik Ravid 	return err;
1025eed84713SShmulik Ravid }
10263e29027aSJohn Fastabend 
10273e29027aSJohn Fastabend /* Handle IEEE 802.1Qaz GET commands. */
1028314b4778SJohn Fastabend static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev)
10293e29027aSJohn Fastabend {
10309ab933abSJohn Fastabend 	struct nlattr *ieee, *app;
10319ab933abSJohn Fastabend 	struct dcb_app_type *itr;
10323e29027aSJohn Fastabend 	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
1033c7797bafSJohn Fastabend 	int dcbx;
10343d1f4869SThomas Graf 	int err;
10353e29027aSJohn Fastabend 
10361eb4c977SDavid S. Miller 	if (nla_put_string(skb, DCB_ATTR_IFNAME, netdev->name))
10373d1f4869SThomas Graf 		return -EMSGSIZE;
10383d1f4869SThomas Graf 
10393e29027aSJohn Fastabend 	ieee = nla_nest_start(skb, DCB_ATTR_IEEE);
10403e29027aSJohn Fastabend 	if (!ieee)
10413d1f4869SThomas Graf 		return -EMSGSIZE;
10423e29027aSJohn Fastabend 
10433e29027aSJohn Fastabend 	if (ops->ieee_getets) {
10443e29027aSJohn Fastabend 		struct ieee_ets ets;
104529cd8ae0SMathias Krause 		memset(&ets, 0, sizeof(ets));
10463e29027aSJohn Fastabend 		err = ops->ieee_getets(netdev, &ets);
10471eb4c977SDavid S. Miller 		if (!err &&
10481eb4c977SDavid S. Miller 		    nla_put(skb, DCB_ATTR_IEEE_ETS, sizeof(ets), &ets))
10493d1f4869SThomas Graf 			return -EMSGSIZE;
10503e29027aSJohn Fastabend 	}
10513e29027aSJohn Fastabend 
105208f10affSAmir Vadai 	if (ops->ieee_getmaxrate) {
105308f10affSAmir Vadai 		struct ieee_maxrate maxrate;
105429cd8ae0SMathias Krause 		memset(&maxrate, 0, sizeof(maxrate));
105508f10affSAmir Vadai 		err = ops->ieee_getmaxrate(netdev, &maxrate);
105608f10affSAmir Vadai 		if (!err) {
105708f10affSAmir Vadai 			err = nla_put(skb, DCB_ATTR_IEEE_MAXRATE,
105808f10affSAmir Vadai 				      sizeof(maxrate), &maxrate);
105908f10affSAmir Vadai 			if (err)
10603d1f4869SThomas Graf 				return -EMSGSIZE;
106108f10affSAmir Vadai 		}
106208f10affSAmir Vadai 	}
106308f10affSAmir Vadai 
10643e29027aSJohn Fastabend 	if (ops->ieee_getpfc) {
10653e29027aSJohn Fastabend 		struct ieee_pfc pfc;
106629cd8ae0SMathias Krause 		memset(&pfc, 0, sizeof(pfc));
10673e29027aSJohn Fastabend 		err = ops->ieee_getpfc(netdev, &pfc);
10681eb4c977SDavid S. Miller 		if (!err &&
10691eb4c977SDavid S. Miller 		    nla_put(skb, DCB_ATTR_IEEE_PFC, sizeof(pfc), &pfc))
10703d1f4869SThomas Graf 			return -EMSGSIZE;
10713e29027aSJohn Fastabend 	}
10723e29027aSJohn Fastabend 
10739ab933abSJohn Fastabend 	app = nla_nest_start(skb, DCB_ATTR_IEEE_APP_TABLE);
10749ab933abSJohn Fastabend 	if (!app)
10753d1f4869SThomas Graf 		return -EMSGSIZE;
10769ab933abSJohn Fastabend 
10779ab933abSJohn Fastabend 	spin_lock(&dcb_lock);
10789ab933abSJohn Fastabend 	list_for_each_entry(itr, &dcb_app_list, list) {
1079e290ed81SMark Rustad 		if (itr->ifindex == netdev->ifindex) {
108070bfa2d2SDan Carpenter 			err = nla_put(skb, DCB_ATTR_IEEE_APP, sizeof(itr->app),
108170bfa2d2SDan Carpenter 					 &itr->app);
108270bfa2d2SDan Carpenter 			if (err) {
108370bfa2d2SDan Carpenter 				spin_unlock(&dcb_lock);
10843d1f4869SThomas Graf 				return -EMSGSIZE;
108570bfa2d2SDan Carpenter 			}
108670bfa2d2SDan Carpenter 		}
10879ab933abSJohn Fastabend 	}
1088c7797bafSJohn Fastabend 
1089c7797bafSJohn Fastabend 	if (netdev->dcbnl_ops->getdcbx)
1090c7797bafSJohn Fastabend 		dcbx = netdev->dcbnl_ops->getdcbx(netdev);
1091c7797bafSJohn Fastabend 	else
1092c7797bafSJohn Fastabend 		dcbx = -EOPNOTSUPP;
1093c7797bafSJohn Fastabend 
10949ab933abSJohn Fastabend 	spin_unlock(&dcb_lock);
10959ab933abSJohn Fastabend 	nla_nest_end(skb, app);
10969ab933abSJohn Fastabend 
1097eed84713SShmulik Ravid 	/* get peer info if available */
1098eed84713SShmulik Ravid 	if (ops->ieee_peer_getets) {
1099eed84713SShmulik Ravid 		struct ieee_ets ets;
110029cd8ae0SMathias Krause 		memset(&ets, 0, sizeof(ets));
1101eed84713SShmulik Ravid 		err = ops->ieee_peer_getets(netdev, &ets);
11021eb4c977SDavid S. Miller 		if (!err &&
11031eb4c977SDavid S. Miller 		    nla_put(skb, DCB_ATTR_IEEE_PEER_ETS, sizeof(ets), &ets))
11043d1f4869SThomas Graf 			return -EMSGSIZE;
1105eed84713SShmulik Ravid 	}
1106eed84713SShmulik Ravid 
1107eed84713SShmulik Ravid 	if (ops->ieee_peer_getpfc) {
1108eed84713SShmulik Ravid 		struct ieee_pfc pfc;
110929cd8ae0SMathias Krause 		memset(&pfc, 0, sizeof(pfc));
1110eed84713SShmulik Ravid 		err = ops->ieee_peer_getpfc(netdev, &pfc);
11111eb4c977SDavid S. Miller 		if (!err &&
11121eb4c977SDavid S. Miller 		    nla_put(skb, DCB_ATTR_IEEE_PEER_PFC, sizeof(pfc), &pfc))
11133d1f4869SThomas Graf 			return -EMSGSIZE;
1114eed84713SShmulik Ravid 	}
1115eed84713SShmulik Ravid 
1116eed84713SShmulik Ravid 	if (ops->peer_getappinfo && ops->peer_getapptable) {
1117dc6ed1dfSShmulik Ravid 		err = dcbnl_build_peer_app(netdev, skb,
1118dc6ed1dfSShmulik Ravid 					   DCB_ATTR_IEEE_PEER_APP,
1119dc6ed1dfSShmulik Ravid 					   DCB_ATTR_IEEE_APP_UNSPEC,
1120dc6ed1dfSShmulik Ravid 					   DCB_ATTR_IEEE_APP);
1121eed84713SShmulik Ravid 		if (err)
11223d1f4869SThomas Graf 			return -EMSGSIZE;
1123eed84713SShmulik Ravid 	}
1124eed84713SShmulik Ravid 
11253e29027aSJohn Fastabend 	nla_nest_end(skb, ieee);
1126c7797bafSJohn Fastabend 	if (dcbx >= 0) {
1127c7797bafSJohn Fastabend 		err = nla_put_u8(skb, DCB_ATTR_DCBX, dcbx);
1128c7797bafSJohn Fastabend 		if (err)
11293d1f4869SThomas Graf 			return -EMSGSIZE;
1130c7797bafSJohn Fastabend 	}
11313e29027aSJohn Fastabend 
1132314b4778SJohn Fastabend 	return 0;
11333e29027aSJohn Fastabend }
11343e29027aSJohn Fastabend 
11355b7f7626SShmulik Ravid static int dcbnl_cee_pg_fill(struct sk_buff *skb, struct net_device *dev,
11365b7f7626SShmulik Ravid 			     int dir)
11375b7f7626SShmulik Ravid {
11385b7f7626SShmulik Ravid 	u8 pgid, up_map, prio, tc_pct;
11395b7f7626SShmulik Ravid 	const struct dcbnl_rtnl_ops *ops = dev->dcbnl_ops;
11405b7f7626SShmulik Ravid 	int i = dir ? DCB_ATTR_CEE_TX_PG : DCB_ATTR_CEE_RX_PG;
11415b7f7626SShmulik Ravid 	struct nlattr *pg = nla_nest_start(skb, i);
11425b7f7626SShmulik Ravid 
11435b7f7626SShmulik Ravid 	if (!pg)
11443d1f4869SThomas Graf 		return -EMSGSIZE;
11455b7f7626SShmulik Ravid 
11465b7f7626SShmulik Ravid 	for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) {
11475b7f7626SShmulik Ravid 		struct nlattr *tc_nest = nla_nest_start(skb, i);
11485b7f7626SShmulik Ravid 
11495b7f7626SShmulik Ravid 		if (!tc_nest)
11503d1f4869SThomas Graf 			return -EMSGSIZE;
11515b7f7626SShmulik Ravid 
11525b7f7626SShmulik Ravid 		pgid = DCB_ATTR_VALUE_UNDEFINED;
11535b7f7626SShmulik Ravid 		prio = DCB_ATTR_VALUE_UNDEFINED;
11545b7f7626SShmulik Ravid 		tc_pct = DCB_ATTR_VALUE_UNDEFINED;
11555b7f7626SShmulik Ravid 		up_map = DCB_ATTR_VALUE_UNDEFINED;
11565b7f7626SShmulik Ravid 
11575b7f7626SShmulik Ravid 		if (!dir)
11585b7f7626SShmulik Ravid 			ops->getpgtccfgrx(dev, i - DCB_PG_ATTR_TC_0,
11595b7f7626SShmulik Ravid 					  &prio, &pgid, &tc_pct, &up_map);
11605b7f7626SShmulik Ravid 		else
11615b7f7626SShmulik Ravid 			ops->getpgtccfgtx(dev, i - DCB_PG_ATTR_TC_0,
11625b7f7626SShmulik Ravid 					  &prio, &pgid, &tc_pct, &up_map);
11635b7f7626SShmulik Ravid 
11641eb4c977SDavid S. Miller 		if (nla_put_u8(skb, DCB_TC_ATTR_PARAM_PGID, pgid) ||
11651eb4c977SDavid S. Miller 		    nla_put_u8(skb, DCB_TC_ATTR_PARAM_UP_MAPPING, up_map) ||
11661eb4c977SDavid S. Miller 		    nla_put_u8(skb, DCB_TC_ATTR_PARAM_STRICT_PRIO, prio) ||
11671eb4c977SDavid S. Miller 		    nla_put_u8(skb, DCB_TC_ATTR_PARAM_BW_PCT, tc_pct))
11683d1f4869SThomas Graf 			return -EMSGSIZE;
11695b7f7626SShmulik Ravid 		nla_nest_end(skb, tc_nest);
11705b7f7626SShmulik Ravid 	}
11715b7f7626SShmulik Ravid 
11725b7f7626SShmulik Ravid 	for (i = DCB_PG_ATTR_BW_ID_0; i <= DCB_PG_ATTR_BW_ID_7; i++) {
11735b7f7626SShmulik Ravid 		tc_pct = DCB_ATTR_VALUE_UNDEFINED;
11745b7f7626SShmulik Ravid 
11755b7f7626SShmulik Ravid 		if (!dir)
11765b7f7626SShmulik Ravid 			ops->getpgbwgcfgrx(dev, i - DCB_PG_ATTR_BW_ID_0,
11775b7f7626SShmulik Ravid 					   &tc_pct);
11785b7f7626SShmulik Ravid 		else
11795b7f7626SShmulik Ravid 			ops->getpgbwgcfgtx(dev, i - DCB_PG_ATTR_BW_ID_0,
11805b7f7626SShmulik Ravid 					   &tc_pct);
11811eb4c977SDavid S. Miller 		if (nla_put_u8(skb, i, tc_pct))
11823d1f4869SThomas Graf 			return -EMSGSIZE;
11835b7f7626SShmulik Ravid 	}
11845b7f7626SShmulik Ravid 	nla_nest_end(skb, pg);
11855b7f7626SShmulik Ravid 	return 0;
11865b7f7626SShmulik Ravid }
11875b7f7626SShmulik Ravid 
11885b7f7626SShmulik Ravid static int dcbnl_cee_fill(struct sk_buff *skb, struct net_device *netdev)
11895b7f7626SShmulik Ravid {
11905b7f7626SShmulik Ravid 	struct nlattr *cee, *app;
11915b7f7626SShmulik Ravid 	struct dcb_app_type *itr;
11925b7f7626SShmulik Ravid 	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
11935b7f7626SShmulik Ravid 	int dcbx, i, err = -EMSGSIZE;
11945b7f7626SShmulik Ravid 	u8 value;
11955b7f7626SShmulik Ravid 
11961eb4c977SDavid S. Miller 	if (nla_put_string(skb, DCB_ATTR_IFNAME, netdev->name))
11971eb4c977SDavid S. Miller 		goto nla_put_failure;
11985b7f7626SShmulik Ravid 	cee = nla_nest_start(skb, DCB_ATTR_CEE);
11995b7f7626SShmulik Ravid 	if (!cee)
12005b7f7626SShmulik Ravid 		goto nla_put_failure;
12015b7f7626SShmulik Ravid 
12025b7f7626SShmulik Ravid 	/* local pg */
12035b7f7626SShmulik Ravid 	if (ops->getpgtccfgtx && ops->getpgbwgcfgtx) {
12045b7f7626SShmulik Ravid 		err = dcbnl_cee_pg_fill(skb, netdev, 1);
12055b7f7626SShmulik Ravid 		if (err)
12065b7f7626SShmulik Ravid 			goto nla_put_failure;
12075b7f7626SShmulik Ravid 	}
12085b7f7626SShmulik Ravid 
12095b7f7626SShmulik Ravid 	if (ops->getpgtccfgrx && ops->getpgbwgcfgrx) {
12105b7f7626SShmulik Ravid 		err = dcbnl_cee_pg_fill(skb, netdev, 0);
12115b7f7626SShmulik Ravid 		if (err)
12125b7f7626SShmulik Ravid 			goto nla_put_failure;
12135b7f7626SShmulik Ravid 	}
12145b7f7626SShmulik Ravid 
12155b7f7626SShmulik Ravid 	/* local pfc */
12165b7f7626SShmulik Ravid 	if (ops->getpfccfg) {
12175b7f7626SShmulik Ravid 		struct nlattr *pfc_nest = nla_nest_start(skb, DCB_ATTR_CEE_PFC);
12185b7f7626SShmulik Ravid 
12195b7f7626SShmulik Ravid 		if (!pfc_nest)
12205b7f7626SShmulik Ravid 			goto nla_put_failure;
12215b7f7626SShmulik Ravid 
12225b7f7626SShmulik Ravid 		for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) {
12235b7f7626SShmulik Ravid 			ops->getpfccfg(netdev, i - DCB_PFC_UP_ATTR_0, &value);
12241eb4c977SDavid S. Miller 			if (nla_put_u8(skb, i, value))
12251eb4c977SDavid S. Miller 				goto nla_put_failure;
12265b7f7626SShmulik Ravid 		}
12275b7f7626SShmulik Ravid 		nla_nest_end(skb, pfc_nest);
12285b7f7626SShmulik Ravid 	}
12295b7f7626SShmulik Ravid 
12305b7f7626SShmulik Ravid 	/* local app */
12315b7f7626SShmulik Ravid 	spin_lock(&dcb_lock);
12325b7f7626SShmulik Ravid 	app = nla_nest_start(skb, DCB_ATTR_CEE_APP_TABLE);
12335b7f7626SShmulik Ravid 	if (!app)
123440f5d72aSDan Carpenter 		goto dcb_unlock;
12355b7f7626SShmulik Ravid 
12365b7f7626SShmulik Ravid 	list_for_each_entry(itr, &dcb_app_list, list) {
1237e290ed81SMark Rustad 		if (itr->ifindex == netdev->ifindex) {
12385b7f7626SShmulik Ravid 			struct nlattr *app_nest = nla_nest_start(skb,
12395b7f7626SShmulik Ravid 								 DCB_ATTR_APP);
12405b7f7626SShmulik Ravid 			if (!app_nest)
12415b7f7626SShmulik Ravid 				goto dcb_unlock;
12425b7f7626SShmulik Ravid 
12435b7f7626SShmulik Ravid 			err = nla_put_u8(skb, DCB_APP_ATTR_IDTYPE,
12445b7f7626SShmulik Ravid 					 itr->app.selector);
12455b7f7626SShmulik Ravid 			if (err)
12465b7f7626SShmulik Ravid 				goto dcb_unlock;
12475b7f7626SShmulik Ravid 
12485b7f7626SShmulik Ravid 			err = nla_put_u16(skb, DCB_APP_ATTR_ID,
12495b7f7626SShmulik Ravid 					  itr->app.protocol);
12505b7f7626SShmulik Ravid 			if (err)
12515b7f7626SShmulik Ravid 				goto dcb_unlock;
12525b7f7626SShmulik Ravid 
12535b7f7626SShmulik Ravid 			err = nla_put_u8(skb, DCB_APP_ATTR_PRIORITY,
12545b7f7626SShmulik Ravid 					 itr->app.priority);
12555b7f7626SShmulik Ravid 			if (err)
12565b7f7626SShmulik Ravid 				goto dcb_unlock;
12575b7f7626SShmulik Ravid 
12585b7f7626SShmulik Ravid 			nla_nest_end(skb, app_nest);
12595b7f7626SShmulik Ravid 		}
12605b7f7626SShmulik Ravid 	}
12615b7f7626SShmulik Ravid 	nla_nest_end(skb, app);
12625b7f7626SShmulik Ravid 
12635b7f7626SShmulik Ravid 	if (netdev->dcbnl_ops->getdcbx)
12645b7f7626SShmulik Ravid 		dcbx = netdev->dcbnl_ops->getdcbx(netdev);
12655b7f7626SShmulik Ravid 	else
12665b7f7626SShmulik Ravid 		dcbx = -EOPNOTSUPP;
12675b7f7626SShmulik Ravid 
12685b7f7626SShmulik Ravid 	spin_unlock(&dcb_lock);
12695b7f7626SShmulik Ravid 
12705b7f7626SShmulik Ravid 	/* features flags */
12715b7f7626SShmulik Ravid 	if (ops->getfeatcfg) {
12725b7f7626SShmulik Ravid 		struct nlattr *feat = nla_nest_start(skb, DCB_ATTR_CEE_FEAT);
12735b7f7626SShmulik Ravid 		if (!feat)
12745b7f7626SShmulik Ravid 			goto nla_put_failure;
12755b7f7626SShmulik Ravid 
12765b7f7626SShmulik Ravid 		for (i = DCB_FEATCFG_ATTR_ALL + 1; i <= DCB_FEATCFG_ATTR_MAX;
12775b7f7626SShmulik Ravid 		     i++)
12781eb4c977SDavid S. Miller 			if (!ops->getfeatcfg(netdev, i, &value) &&
12791eb4c977SDavid S. Miller 			    nla_put_u8(skb, i, value))
12801eb4c977SDavid S. Miller 				goto nla_put_failure;
12815b7f7626SShmulik Ravid 
12825b7f7626SShmulik Ravid 		nla_nest_end(skb, feat);
12835b7f7626SShmulik Ravid 	}
12845b7f7626SShmulik Ravid 
12855b7f7626SShmulik Ravid 	/* peer info if available */
12865b7f7626SShmulik Ravid 	if (ops->cee_peer_getpg) {
12875b7f7626SShmulik Ravid 		struct cee_pg pg;
128829cd8ae0SMathias Krause 		memset(&pg, 0, sizeof(pg));
12895b7f7626SShmulik Ravid 		err = ops->cee_peer_getpg(netdev, &pg);
12901eb4c977SDavid S. Miller 		if (!err &&
12911eb4c977SDavid S. Miller 		    nla_put(skb, DCB_ATTR_CEE_PEER_PG, sizeof(pg), &pg))
12921eb4c977SDavid S. Miller 			goto nla_put_failure;
12935b7f7626SShmulik Ravid 	}
12945b7f7626SShmulik Ravid 
12955b7f7626SShmulik Ravid 	if (ops->cee_peer_getpfc) {
12965b7f7626SShmulik Ravid 		struct cee_pfc pfc;
129729cd8ae0SMathias Krause 		memset(&pfc, 0, sizeof(pfc));
12985b7f7626SShmulik Ravid 		err = ops->cee_peer_getpfc(netdev, &pfc);
12991eb4c977SDavid S. Miller 		if (!err &&
13001eb4c977SDavid S. Miller 		    nla_put(skb, DCB_ATTR_CEE_PEER_PFC, sizeof(pfc), &pfc))
13011eb4c977SDavid S. Miller 			goto nla_put_failure;
13025b7f7626SShmulik Ravid 	}
13035b7f7626SShmulik Ravid 
13045b7f7626SShmulik Ravid 	if (ops->peer_getappinfo && ops->peer_getapptable) {
13055b7f7626SShmulik Ravid 		err = dcbnl_build_peer_app(netdev, skb,
13065b7f7626SShmulik Ravid 					   DCB_ATTR_CEE_PEER_APP_TABLE,
13075b7f7626SShmulik Ravid 					   DCB_ATTR_CEE_PEER_APP_INFO,
13085b7f7626SShmulik Ravid 					   DCB_ATTR_CEE_PEER_APP);
13095b7f7626SShmulik Ravid 		if (err)
13105b7f7626SShmulik Ravid 			goto nla_put_failure;
13115b7f7626SShmulik Ravid 	}
13125b7f7626SShmulik Ravid 	nla_nest_end(skb, cee);
13135b7f7626SShmulik Ravid 
13145b7f7626SShmulik Ravid 	/* DCBX state */
13155b7f7626SShmulik Ravid 	if (dcbx >= 0) {
13165b7f7626SShmulik Ravid 		err = nla_put_u8(skb, DCB_ATTR_DCBX, dcbx);
13175b7f7626SShmulik Ravid 		if (err)
13185b7f7626SShmulik Ravid 			goto nla_put_failure;
13195b7f7626SShmulik Ravid 	}
13205b7f7626SShmulik Ravid 	return 0;
13215b7f7626SShmulik Ravid 
13225b7f7626SShmulik Ravid dcb_unlock:
13235b7f7626SShmulik Ravid 	spin_unlock(&dcb_lock);
13245b7f7626SShmulik Ravid nla_put_failure:
13255b7f7626SShmulik Ravid 	return err;
13265b7f7626SShmulik Ravid }
13275b7f7626SShmulik Ravid 
13285b7f7626SShmulik Ravid static int dcbnl_notify(struct net_device *dev, int event, int cmd,
132915e47304SEric W. Biederman 			u32 seq, u32 portid, int dcbx_ver)
1330314b4778SJohn Fastabend {
1331314b4778SJohn Fastabend 	struct net *net = dev_net(dev);
1332314b4778SJohn Fastabend 	struct sk_buff *skb;
1333314b4778SJohn Fastabend 	struct nlmsghdr *nlh;
1334314b4778SJohn Fastabend 	const struct dcbnl_rtnl_ops *ops = dev->dcbnl_ops;
1335314b4778SJohn Fastabend 	int err;
1336314b4778SJohn Fastabend 
1337314b4778SJohn Fastabend 	if (!ops)
1338314b4778SJohn Fastabend 		return -EOPNOTSUPP;
1339314b4778SJohn Fastabend 
134015e47304SEric W. Biederman 	skb = dcbnl_newmsg(event, cmd, portid, seq, 0, &nlh);
1341314b4778SJohn Fastabend 	if (!skb)
1342314b4778SJohn Fastabend 		return -ENOBUFS;
1343314b4778SJohn Fastabend 
13445b7f7626SShmulik Ravid 	if (dcbx_ver == DCB_CAP_DCBX_VER_IEEE)
1345314b4778SJohn Fastabend 		err = dcbnl_ieee_fill(skb, dev);
13465b7f7626SShmulik Ravid 	else
13475b7f7626SShmulik Ravid 		err = dcbnl_cee_fill(skb, dev);
13485b7f7626SShmulik Ravid 
1349314b4778SJohn Fastabend 	if (err < 0) {
1350314b4778SJohn Fastabend 		/* Report error to broadcast listeners */
1351ab6d4707SThomas Graf 		nlmsg_free(skb);
1352314b4778SJohn Fastabend 		rtnl_set_sk_err(net, RTNLGRP_DCB, err);
1353314b4778SJohn Fastabend 	} else {
1354314b4778SJohn Fastabend 		/* End nlmsg and notify broadcast listeners */
1355314b4778SJohn Fastabend 		nlmsg_end(skb, nlh);
1356314b4778SJohn Fastabend 		rtnl_notify(skb, net, 0, RTNLGRP_DCB, NULL, GFP_KERNEL);
1357314b4778SJohn Fastabend 	}
1358314b4778SJohn Fastabend 
1359314b4778SJohn Fastabend 	return err;
1360314b4778SJohn Fastabend }
13615b7f7626SShmulik Ravid 
13625b7f7626SShmulik Ravid int dcbnl_ieee_notify(struct net_device *dev, int event, int cmd,
136315e47304SEric W. Biederman 		      u32 seq, u32 portid)
13645b7f7626SShmulik Ravid {
136515e47304SEric W. Biederman 	return dcbnl_notify(dev, event, cmd, seq, portid, DCB_CAP_DCBX_VER_IEEE);
13665b7f7626SShmulik Ravid }
13675b7f7626SShmulik Ravid EXPORT_SYMBOL(dcbnl_ieee_notify);
13685b7f7626SShmulik Ravid 
13695b7f7626SShmulik Ravid int dcbnl_cee_notify(struct net_device *dev, int event, int cmd,
137015e47304SEric W. Biederman 		     u32 seq, u32 portid)
13715b7f7626SShmulik Ravid {
137215e47304SEric W. Biederman 	return dcbnl_notify(dev, event, cmd, seq, portid, DCB_CAP_DCBX_VER_CEE);
13735b7f7626SShmulik Ravid }
13745b7f7626SShmulik Ravid EXPORT_SYMBOL(dcbnl_cee_notify);
1375314b4778SJohn Fastabend 
1376314b4778SJohn Fastabend /* Handle IEEE 802.1Qaz SET commands. If any requested operation can not
1377314b4778SJohn Fastabend  * be completed the entire msg is aborted and error value is returned.
1378314b4778SJohn Fastabend  * No attempt is made to reconcile the case where only part of the
1379314b4778SJohn Fastabend  * cmd can be completed.
1380314b4778SJohn Fastabend  */
13817be99413SThomas Graf static int dcbnl_ieee_set(struct net_device *netdev, struct nlmsghdr *nlh,
13827be99413SThomas Graf 			  u32 seq, struct nlattr **tb, struct sk_buff *skb)
1383314b4778SJohn Fastabend {
1384314b4778SJohn Fastabend 	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
1385314b4778SJohn Fastabend 	struct nlattr *ieee[DCB_ATTR_IEEE_MAX + 1];
13863d1f4869SThomas Graf 	int err;
1387314b4778SJohn Fastabend 
1388314b4778SJohn Fastabend 	if (!ops)
13893d1f4869SThomas Graf 		return -EOPNOTSUPP;
1390314b4778SJohn Fastabend 
13914003b658SJohn Fastabend 	if (!tb[DCB_ATTR_IEEE])
13924003b658SJohn Fastabend 		return -EINVAL;
13934003b658SJohn Fastabend 
1394314b4778SJohn Fastabend 	err = nla_parse_nested(ieee, DCB_ATTR_IEEE_MAX,
1395314b4778SJohn Fastabend 			       tb[DCB_ATTR_IEEE], dcbnl_ieee_policy);
1396314b4778SJohn Fastabend 	if (err)
1397314b4778SJohn Fastabend 		return err;
1398314b4778SJohn Fastabend 
1399314b4778SJohn Fastabend 	if (ieee[DCB_ATTR_IEEE_ETS] && ops->ieee_setets) {
1400314b4778SJohn Fastabend 		struct ieee_ets *ets = nla_data(ieee[DCB_ATTR_IEEE_ETS]);
1401314b4778SJohn Fastabend 		err = ops->ieee_setets(netdev, ets);
1402314b4778SJohn Fastabend 		if (err)
1403314b4778SJohn Fastabend 			goto err;
1404314b4778SJohn Fastabend 	}
1405314b4778SJohn Fastabend 
140608f10affSAmir Vadai 	if (ieee[DCB_ATTR_IEEE_MAXRATE] && ops->ieee_setmaxrate) {
140708f10affSAmir Vadai 		struct ieee_maxrate *maxrate =
140808f10affSAmir Vadai 			nla_data(ieee[DCB_ATTR_IEEE_MAXRATE]);
140908f10affSAmir Vadai 		err = ops->ieee_setmaxrate(netdev, maxrate);
141008f10affSAmir Vadai 		if (err)
141108f10affSAmir Vadai 			goto err;
141208f10affSAmir Vadai 	}
141308f10affSAmir Vadai 
1414314b4778SJohn Fastabend 	if (ieee[DCB_ATTR_IEEE_PFC] && ops->ieee_setpfc) {
1415314b4778SJohn Fastabend 		struct ieee_pfc *pfc = nla_data(ieee[DCB_ATTR_IEEE_PFC]);
1416314b4778SJohn Fastabend 		err = ops->ieee_setpfc(netdev, pfc);
1417314b4778SJohn Fastabend 		if (err)
1418314b4778SJohn Fastabend 			goto err;
1419314b4778SJohn Fastabend 	}
1420314b4778SJohn Fastabend 
1421314b4778SJohn Fastabend 	if (ieee[DCB_ATTR_IEEE_APP_TABLE]) {
1422314b4778SJohn Fastabend 		struct nlattr *attr;
1423314b4778SJohn Fastabend 		int rem;
1424314b4778SJohn Fastabend 
1425314b4778SJohn Fastabend 		nla_for_each_nested(attr, ieee[DCB_ATTR_IEEE_APP_TABLE], rem) {
1426314b4778SJohn Fastabend 			struct dcb_app *app_data;
1427314b4778SJohn Fastabend 			if (nla_type(attr) != DCB_ATTR_IEEE_APP)
1428314b4778SJohn Fastabend 				continue;
1429314b4778SJohn Fastabend 			app_data = nla_data(attr);
1430314b4778SJohn Fastabend 			if (ops->ieee_setapp)
1431314b4778SJohn Fastabend 				err = ops->ieee_setapp(netdev, app_data);
1432314b4778SJohn Fastabend 			else
1433b6db2174SJohn Fastabend 				err = dcb_ieee_setapp(netdev, app_data);
1434314b4778SJohn Fastabend 			if (err)
1435314b4778SJohn Fastabend 				goto err;
1436314b4778SJohn Fastabend 		}
1437314b4778SJohn Fastabend 	}
1438314b4778SJohn Fastabend 
1439314b4778SJohn Fastabend err:
14407be99413SThomas Graf 	err = nla_put_u8(skb, DCB_ATTR_IEEE, err);
14415b7f7626SShmulik Ravid 	dcbnl_ieee_notify(netdev, RTM_SETDCB, DCB_CMD_IEEE_SET, seq, 0);
1442314b4778SJohn Fastabend 	return err;
1443314b4778SJohn Fastabend }
1444314b4778SJohn Fastabend 
14457be99413SThomas Graf static int dcbnl_ieee_get(struct net_device *netdev, struct nlmsghdr *nlh,
14467be99413SThomas Graf 			  u32 seq, struct nlattr **tb, struct sk_buff *skb)
1447314b4778SJohn Fastabend {
1448314b4778SJohn Fastabend 	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
1449314b4778SJohn Fastabend 
1450314b4778SJohn Fastabend 	if (!ops)
1451314b4778SJohn Fastabend 		return -EOPNOTSUPP;
1452314b4778SJohn Fastabend 
14537be99413SThomas Graf 	return dcbnl_ieee_fill(skb, netdev);
1454314b4778SJohn Fastabend }
1455314b4778SJohn Fastabend 
14567be99413SThomas Graf static int dcbnl_ieee_del(struct net_device *netdev, struct nlmsghdr *nlh,
14577be99413SThomas Graf 			  u32 seq, struct nlattr **tb, struct sk_buff *skb)
1458f9ae7e4bSJohn Fastabend {
1459f9ae7e4bSJohn Fastabend 	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
1460f9ae7e4bSJohn Fastabend 	struct nlattr *ieee[DCB_ATTR_IEEE_MAX + 1];
14613d1f4869SThomas Graf 	int err;
1462f9ae7e4bSJohn Fastabend 
1463f9ae7e4bSJohn Fastabend 	if (!ops)
1464f9ae7e4bSJohn Fastabend 		return -EOPNOTSUPP;
1465f9ae7e4bSJohn Fastabend 
1466f9ae7e4bSJohn Fastabend 	if (!tb[DCB_ATTR_IEEE])
1467f9ae7e4bSJohn Fastabend 		return -EINVAL;
1468f9ae7e4bSJohn Fastabend 
1469f9ae7e4bSJohn Fastabend 	err = nla_parse_nested(ieee, DCB_ATTR_IEEE_MAX,
1470f9ae7e4bSJohn Fastabend 			       tb[DCB_ATTR_IEEE], dcbnl_ieee_policy);
1471f9ae7e4bSJohn Fastabend 	if (err)
1472f9ae7e4bSJohn Fastabend 		return err;
1473f9ae7e4bSJohn Fastabend 
1474f9ae7e4bSJohn Fastabend 	if (ieee[DCB_ATTR_IEEE_APP_TABLE]) {
1475f9ae7e4bSJohn Fastabend 		struct nlattr *attr;
1476f9ae7e4bSJohn Fastabend 		int rem;
1477f9ae7e4bSJohn Fastabend 
1478f9ae7e4bSJohn Fastabend 		nla_for_each_nested(attr, ieee[DCB_ATTR_IEEE_APP_TABLE], rem) {
1479f9ae7e4bSJohn Fastabend 			struct dcb_app *app_data;
1480f9ae7e4bSJohn Fastabend 
1481f9ae7e4bSJohn Fastabend 			if (nla_type(attr) != DCB_ATTR_IEEE_APP)
1482f9ae7e4bSJohn Fastabend 				continue;
1483f9ae7e4bSJohn Fastabend 			app_data = nla_data(attr);
1484f9ae7e4bSJohn Fastabend 			if (ops->ieee_delapp)
1485f9ae7e4bSJohn Fastabend 				err = ops->ieee_delapp(netdev, app_data);
1486f9ae7e4bSJohn Fastabend 			else
1487f9ae7e4bSJohn Fastabend 				err = dcb_ieee_delapp(netdev, app_data);
1488f9ae7e4bSJohn Fastabend 			if (err)
1489f9ae7e4bSJohn Fastabend 				goto err;
1490f9ae7e4bSJohn Fastabend 		}
1491f9ae7e4bSJohn Fastabend 	}
1492f9ae7e4bSJohn Fastabend 
1493f9ae7e4bSJohn Fastabend err:
14947be99413SThomas Graf 	err = nla_put_u8(skb, DCB_ATTR_IEEE, err);
14955b7f7626SShmulik Ravid 	dcbnl_ieee_notify(netdev, RTM_SETDCB, DCB_CMD_IEEE_DEL, seq, 0);
1496f9ae7e4bSJohn Fastabend 	return err;
1497f9ae7e4bSJohn Fastabend }
1498f9ae7e4bSJohn Fastabend 
1499f9ae7e4bSJohn Fastabend 
15006241b625SShmulik Ravid /* DCBX configuration */
15017be99413SThomas Graf static int dcbnl_getdcbx(struct net_device *netdev, struct nlmsghdr *nlh,
15027be99413SThomas Graf 			 u32 seq, struct nlattr **tb, struct sk_buff *skb)
15036241b625SShmulik Ravid {
15046241b625SShmulik Ravid 	if (!netdev->dcbnl_ops->getdcbx)
15057f891cf1SShmulik Ravid 		return -EOPNOTSUPP;
15066241b625SShmulik Ravid 
15077be99413SThomas Graf 	return nla_put_u8(skb, DCB_ATTR_DCBX,
15087be99413SThomas Graf 			  netdev->dcbnl_ops->getdcbx(netdev));
15096241b625SShmulik Ravid }
15106241b625SShmulik Ravid 
15117be99413SThomas Graf static int dcbnl_setdcbx(struct net_device *netdev, struct nlmsghdr *nlh,
15127be99413SThomas Graf 			 u32 seq, struct nlattr **tb, struct sk_buff *skb)
15136241b625SShmulik Ravid {
15146241b625SShmulik Ravid 	u8 value;
15156241b625SShmulik Ravid 
15167f891cf1SShmulik Ravid 	if (!netdev->dcbnl_ops->setdcbx)
15177f891cf1SShmulik Ravid 		return -EOPNOTSUPP;
15187f891cf1SShmulik Ravid 
15197f891cf1SShmulik Ravid 	if (!tb[DCB_ATTR_DCBX])
15207f891cf1SShmulik Ravid 		return -EINVAL;
15216241b625SShmulik Ravid 
15226241b625SShmulik Ravid 	value = nla_get_u8(tb[DCB_ATTR_DCBX]);
15236241b625SShmulik Ravid 
15247be99413SThomas Graf 	return nla_put_u8(skb, DCB_ATTR_DCBX,
15257be99413SThomas Graf 			  netdev->dcbnl_ops->setdcbx(netdev, value));
15266241b625SShmulik Ravid }
15276241b625SShmulik Ravid 
15287be99413SThomas Graf static int dcbnl_getfeatcfg(struct net_device *netdev, struct nlmsghdr *nlh,
15297be99413SThomas Graf 			    u32 seq, struct nlattr **tb, struct sk_buff *skb)
1530ea45fe4eSShmulik Ravid {
1531ea45fe4eSShmulik Ravid 	struct nlattr *data[DCB_FEATCFG_ATTR_MAX + 1], *nest;
1532ea45fe4eSShmulik Ravid 	u8 value;
15337f891cf1SShmulik Ravid 	int ret, i;
1534ea45fe4eSShmulik Ravid 	int getall = 0;
1535ea45fe4eSShmulik Ravid 
15367f891cf1SShmulik Ravid 	if (!netdev->dcbnl_ops->getfeatcfg)
15377f891cf1SShmulik Ravid 		return -EOPNOTSUPP;
15387f891cf1SShmulik Ravid 
15397f891cf1SShmulik Ravid 	if (!tb[DCB_ATTR_FEATCFG])
15407f891cf1SShmulik Ravid 		return -EINVAL;
1541ea45fe4eSShmulik Ravid 
1542ea45fe4eSShmulik Ravid 	ret = nla_parse_nested(data, DCB_FEATCFG_ATTR_MAX, tb[DCB_ATTR_FEATCFG],
1543ea45fe4eSShmulik Ravid 			       dcbnl_featcfg_nest);
15447f891cf1SShmulik Ravid 	if (ret)
15457be99413SThomas Graf 		return ret;
1546ea45fe4eSShmulik Ravid 
15477be99413SThomas Graf 	nest = nla_nest_start(skb, DCB_ATTR_FEATCFG);
15487be99413SThomas Graf 	if (!nest)
15497be99413SThomas Graf 		return -EMSGSIZE;
1550ea45fe4eSShmulik Ravid 
1551ea45fe4eSShmulik Ravid 	if (data[DCB_FEATCFG_ATTR_ALL])
1552ea45fe4eSShmulik Ravid 		getall = 1;
1553ea45fe4eSShmulik Ravid 
1554ea45fe4eSShmulik Ravid 	for (i = DCB_FEATCFG_ATTR_ALL+1; i <= DCB_FEATCFG_ATTR_MAX; i++) {
1555ea45fe4eSShmulik Ravid 		if (!getall && !data[i])
1556ea45fe4eSShmulik Ravid 			continue;
1557ea45fe4eSShmulik Ravid 
1558ea45fe4eSShmulik Ravid 		ret = netdev->dcbnl_ops->getfeatcfg(netdev, i, &value);
15597f891cf1SShmulik Ravid 		if (!ret)
15607be99413SThomas Graf 			ret = nla_put_u8(skb, i, value);
1561ea45fe4eSShmulik Ravid 
1562ea45fe4eSShmulik Ravid 		if (ret) {
15637be99413SThomas Graf 			nla_nest_cancel(skb, nest);
15647f891cf1SShmulik Ravid 			goto nla_put_failure;
1565ea45fe4eSShmulik Ravid 		}
1566ea45fe4eSShmulik Ravid 	}
15677be99413SThomas Graf 	nla_nest_end(skb, nest);
1568ea45fe4eSShmulik Ravid 
15697f891cf1SShmulik Ravid nla_put_failure:
1570ea45fe4eSShmulik Ravid 	return ret;
1571ea45fe4eSShmulik Ravid }
1572ea45fe4eSShmulik Ravid 
15737be99413SThomas Graf static int dcbnl_setfeatcfg(struct net_device *netdev, struct nlmsghdr *nlh,
15747be99413SThomas Graf 			    u32 seq, struct nlattr **tb, struct sk_buff *skb)
1575ea45fe4eSShmulik Ravid {
1576ea45fe4eSShmulik Ravid 	struct nlattr *data[DCB_FEATCFG_ATTR_MAX + 1];
15777f891cf1SShmulik Ravid 	int ret, i;
1578ea45fe4eSShmulik Ravid 	u8 value;
1579ea45fe4eSShmulik Ravid 
15807f891cf1SShmulik Ravid 	if (!netdev->dcbnl_ops->setfeatcfg)
15817f891cf1SShmulik Ravid 		return -ENOTSUPP;
15827f891cf1SShmulik Ravid 
15837f891cf1SShmulik Ravid 	if (!tb[DCB_ATTR_FEATCFG])
15847f891cf1SShmulik Ravid 		return -EINVAL;
1585ea45fe4eSShmulik Ravid 
1586ea45fe4eSShmulik Ravid 	ret = nla_parse_nested(data, DCB_FEATCFG_ATTR_MAX, tb[DCB_ATTR_FEATCFG],
1587ea45fe4eSShmulik Ravid 			       dcbnl_featcfg_nest);
1588ea45fe4eSShmulik Ravid 
15897f891cf1SShmulik Ravid 	if (ret)
1590ea45fe4eSShmulik Ravid 		goto err;
1591ea45fe4eSShmulik Ravid 
1592ea45fe4eSShmulik Ravid 	for (i = DCB_FEATCFG_ATTR_ALL+1; i <= DCB_FEATCFG_ATTR_MAX; i++) {
1593ea45fe4eSShmulik Ravid 		if (data[i] == NULL)
1594ea45fe4eSShmulik Ravid 			continue;
1595ea45fe4eSShmulik Ravid 
1596ea45fe4eSShmulik Ravid 		value = nla_get_u8(data[i]);
1597ea45fe4eSShmulik Ravid 
1598ea45fe4eSShmulik Ravid 		ret = netdev->dcbnl_ops->setfeatcfg(netdev, i, value);
1599ea45fe4eSShmulik Ravid 
1600ea45fe4eSShmulik Ravid 		if (ret)
16017f891cf1SShmulik Ravid 			goto err;
1602ea45fe4eSShmulik Ravid 	}
1603ea45fe4eSShmulik Ravid err:
16047be99413SThomas Graf 	ret = nla_put_u8(skb, DCB_ATTR_FEATCFG, ret);
16057f891cf1SShmulik Ravid 
1606ea45fe4eSShmulik Ravid 	return ret;
1607ea45fe4eSShmulik Ravid }
1608ea45fe4eSShmulik Ravid 
1609dc6ed1dfSShmulik Ravid /* Handle CEE DCBX GET commands. */
16107be99413SThomas Graf static int dcbnl_cee_get(struct net_device *netdev, struct nlmsghdr *nlh,
16117be99413SThomas Graf 			 u32 seq, struct nlattr **tb, struct sk_buff *skb)
1612dc6ed1dfSShmulik Ravid {
1613dc6ed1dfSShmulik Ravid 	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
1614dc6ed1dfSShmulik Ravid 
1615dc6ed1dfSShmulik Ravid 	if (!ops)
1616dc6ed1dfSShmulik Ravid 		return -EOPNOTSUPP;
1617dc6ed1dfSShmulik Ravid 
16187be99413SThomas Graf 	return dcbnl_cee_fill(skb, netdev);
1619dc6ed1dfSShmulik Ravid }
1620dc6ed1dfSShmulik Ravid 
162133a03aadSThomas Graf struct reply_func {
162233a03aadSThomas Graf 	/* reply netlink message type */
162333a03aadSThomas Graf 	int	type;
162433a03aadSThomas Graf 
162533a03aadSThomas Graf 	/* function to fill message contents */
162633a03aadSThomas Graf 	int   (*cb)(struct net_device *, struct nlmsghdr *, u32,
162733a03aadSThomas Graf 		    struct nlattr **, struct sk_buff *);
162833a03aadSThomas Graf };
162933a03aadSThomas Graf 
163033a03aadSThomas Graf static const struct reply_func reply_funcs[DCB_CMD_MAX+1] = {
16317be99413SThomas Graf 	[DCB_CMD_GSTATE]	= { RTM_GETDCB, dcbnl_getstate },
16327be99413SThomas Graf 	[DCB_CMD_SSTATE]	= { RTM_SETDCB, dcbnl_setstate },
16337be99413SThomas Graf 	[DCB_CMD_PFC_GCFG]	= { RTM_GETDCB, dcbnl_getpfccfg },
16347be99413SThomas Graf 	[DCB_CMD_PFC_SCFG]	= { RTM_SETDCB, dcbnl_setpfccfg },
16357be99413SThomas Graf 	[DCB_CMD_GPERM_HWADDR]	= { RTM_GETDCB, dcbnl_getperm_hwaddr },
16367be99413SThomas Graf 	[DCB_CMD_GCAP]		= { RTM_GETDCB, dcbnl_getcap },
16377be99413SThomas Graf 	[DCB_CMD_GNUMTCS]	= { RTM_GETDCB, dcbnl_getnumtcs },
16387be99413SThomas Graf 	[DCB_CMD_SNUMTCS]	= { RTM_SETDCB, dcbnl_setnumtcs },
16397be99413SThomas Graf 	[DCB_CMD_PFC_GSTATE]	= { RTM_GETDCB, dcbnl_getpfcstate },
16407be99413SThomas Graf 	[DCB_CMD_PFC_SSTATE]	= { RTM_SETDCB, dcbnl_setpfcstate },
16417be99413SThomas Graf 	[DCB_CMD_GAPP]		= { RTM_GETDCB, dcbnl_getapp },
16427be99413SThomas Graf 	[DCB_CMD_SAPP]		= { RTM_SETDCB, dcbnl_setapp },
16437be99413SThomas Graf 	[DCB_CMD_PGTX_GCFG]	= { RTM_GETDCB, dcbnl_pgtx_getcfg },
16447be99413SThomas Graf 	[DCB_CMD_PGTX_SCFG]	= { RTM_SETDCB, dcbnl_pgtx_setcfg },
16457be99413SThomas Graf 	[DCB_CMD_PGRX_GCFG]	= { RTM_GETDCB, dcbnl_pgrx_getcfg },
16467be99413SThomas Graf 	[DCB_CMD_PGRX_SCFG]	= { RTM_SETDCB, dcbnl_pgrx_setcfg },
16477be99413SThomas Graf 	[DCB_CMD_SET_ALL]	= { RTM_SETDCB, dcbnl_setall },
16487be99413SThomas Graf 	[DCB_CMD_BCN_GCFG]	= { RTM_GETDCB, dcbnl_bcn_getcfg },
16497be99413SThomas Graf 	[DCB_CMD_BCN_SCFG]	= { RTM_SETDCB, dcbnl_bcn_setcfg },
16507be99413SThomas Graf 	[DCB_CMD_IEEE_GET]	= { RTM_GETDCB, dcbnl_ieee_get },
16517be99413SThomas Graf 	[DCB_CMD_IEEE_SET]	= { RTM_SETDCB, dcbnl_ieee_set },
16527be99413SThomas Graf 	[DCB_CMD_IEEE_DEL]	= { RTM_SETDCB, dcbnl_ieee_del },
16537be99413SThomas Graf 	[DCB_CMD_GDCBX]		= { RTM_GETDCB, dcbnl_getdcbx },
16547be99413SThomas Graf 	[DCB_CMD_SDCBX]		= { RTM_SETDCB, dcbnl_setdcbx },
16557be99413SThomas Graf 	[DCB_CMD_GFEATCFG]	= { RTM_GETDCB, dcbnl_getfeatcfg },
16567be99413SThomas Graf 	[DCB_CMD_SFEATCFG]	= { RTM_SETDCB, dcbnl_setfeatcfg },
16577be99413SThomas Graf 	[DCB_CMD_CEE_GET]	= { RTM_GETDCB, dcbnl_cee_get },
165833a03aadSThomas Graf };
165933a03aadSThomas Graf 
1660661d2967SThomas Graf static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh)
16612f90b865SAlexander Duyck {
16622f90b865SAlexander Duyck 	struct net *net = sock_net(skb->sk);
16632f90b865SAlexander Duyck 	struct net_device *netdev;
16647a282bc3SThomas Graf 	struct dcbmsg *dcb = nlmsg_data(nlh);
16652f90b865SAlexander Duyck 	struct nlattr *tb[DCB_ATTR_MAX + 1];
166615e47304SEric W. Biederman 	u32 portid = skb ? NETLINK_CB(skb).portid : 0;
16672f90b865SAlexander Duyck 	int ret = -EINVAL;
166833a03aadSThomas Graf 	struct sk_buff *reply_skb;
166939912f9cSThomas Graf 	struct nlmsghdr *reply_nlh = NULL;
167033a03aadSThomas Graf 	const struct reply_func *fn;
16712f90b865SAlexander Duyck 
1672dfc47ef8SEric W. Biederman 	if ((nlh->nlmsg_type == RTM_SETDCB) && !capable(CAP_NET_ADMIN))
1673dfc47ef8SEric W. Biederman 		return -EPERM;
1674dfc47ef8SEric W. Biederman 
16752f90b865SAlexander Duyck 	ret = nlmsg_parse(nlh, sizeof(*dcb), tb, DCB_ATTR_MAX,
16762f90b865SAlexander Duyck 			  dcbnl_rtnl_policy);
16772f90b865SAlexander Duyck 	if (ret < 0)
16782f90b865SAlexander Duyck 		return ret;
16792f90b865SAlexander Duyck 
168033a03aadSThomas Graf 	if (dcb->cmd > DCB_CMD_MAX)
168133a03aadSThomas Graf 		return -EINVAL;
168233a03aadSThomas Graf 
168333a03aadSThomas Graf 	/* check if a reply function has been defined for the command */
168433a03aadSThomas Graf 	fn = &reply_funcs[dcb->cmd];
168533a03aadSThomas Graf 	if (!fn->cb)
168633a03aadSThomas Graf 		return -EOPNOTSUPP;
168733a03aadSThomas Graf 
16882f90b865SAlexander Duyck 	if (!tb[DCB_ATTR_IFNAME])
16892f90b865SAlexander Duyck 		return -EINVAL;
16902f90b865SAlexander Duyck 
1691d9ac62beSYing Xue 	netdev = __dev_get_by_name(net, nla_data(tb[DCB_ATTR_IFNAME]));
16922f90b865SAlexander Duyck 	if (!netdev)
16933d1f4869SThomas Graf 		return -ENODEV;
16942f90b865SAlexander Duyck 
1695d9ac62beSYing Xue 	if (!netdev->dcbnl_ops)
1696d9ac62beSYing Xue 		return -EOPNOTSUPP;
16972f90b865SAlexander Duyck 
169815e47304SEric W. Biederman 	reply_skb = dcbnl_newmsg(fn->type, dcb->cmd, portid, nlh->nlmsg_seq,
169933a03aadSThomas Graf 				 nlh->nlmsg_flags, &reply_nlh);
1700d9ac62beSYing Xue 	if (!reply_skb)
1701d9ac62beSYing Xue 		return -ENOBUFS;
170233a03aadSThomas Graf 
170333a03aadSThomas Graf 	ret = fn->cb(netdev, nlh, nlh->nlmsg_seq, tb, reply_skb);
170433a03aadSThomas Graf 	if (ret < 0) {
170533a03aadSThomas Graf 		nlmsg_free(reply_skb);
170633a03aadSThomas Graf 		goto out;
170733a03aadSThomas Graf 	}
170833a03aadSThomas Graf 
170933a03aadSThomas Graf 	nlmsg_end(reply_skb, reply_nlh);
171033a03aadSThomas Graf 
17117c77ab24SJohn Fastabend 	ret = rtnl_unicast(reply_skb, net, portid);
17122f90b865SAlexander Duyck out:
17132f90b865SAlexander Duyck 	return ret;
17142f90b865SAlexander Duyck }
17152f90b865SAlexander Duyck 
1716716b31abSThomas Graf static struct dcb_app_type *dcb_app_lookup(const struct dcb_app *app,
1717716b31abSThomas Graf 					   int ifindex, int prio)
1718716b31abSThomas Graf {
1719716b31abSThomas Graf 	struct dcb_app_type *itr;
1720716b31abSThomas Graf 
1721716b31abSThomas Graf 	list_for_each_entry(itr, &dcb_app_list, list) {
1722716b31abSThomas Graf 		if (itr->app.selector == app->selector &&
1723716b31abSThomas Graf 		    itr->app.protocol == app->protocol &&
1724716b31abSThomas Graf 		    itr->ifindex == ifindex &&
1725716b31abSThomas Graf 		    (!prio || itr->app.priority == prio))
1726716b31abSThomas Graf 			return itr;
1727716b31abSThomas Graf 	}
1728716b31abSThomas Graf 
1729716b31abSThomas Graf 	return NULL;
1730716b31abSThomas Graf }
1731716b31abSThomas Graf 
17324e4f2f69SThomas Graf static int dcb_app_add(const struct dcb_app *app, int ifindex)
17334e4f2f69SThomas Graf {
17344e4f2f69SThomas Graf 	struct dcb_app_type *entry;
17354e4f2f69SThomas Graf 
17364e4f2f69SThomas Graf 	entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
17374e4f2f69SThomas Graf 	if (!entry)
17384e4f2f69SThomas Graf 		return -ENOMEM;
17394e4f2f69SThomas Graf 
17404e4f2f69SThomas Graf 	memcpy(&entry->app, app, sizeof(*app));
17414e4f2f69SThomas Graf 	entry->ifindex = ifindex;
17424e4f2f69SThomas Graf 	list_add(&entry->list, &dcb_app_list);
17434e4f2f69SThomas Graf 
17444e4f2f69SThomas Graf 	return 0;
17454e4f2f69SThomas Graf }
17464e4f2f69SThomas Graf 
17479ab933abSJohn Fastabend /**
17489ab933abSJohn Fastabend  * dcb_getapp - retrieve the DCBX application user priority
17499ab933abSJohn Fastabend  *
17509ab933abSJohn Fastabend  * On success returns a non-zero 802.1p user priority bitmap
17519ab933abSJohn Fastabend  * otherwise returns 0 as the invalid user priority bitmap to
17529ab933abSJohn Fastabend  * indicate an error.
17539ab933abSJohn Fastabend  */
17549ab933abSJohn Fastabend u8 dcb_getapp(struct net_device *dev, struct dcb_app *app)
17559ab933abSJohn Fastabend {
17569ab933abSJohn Fastabend 	struct dcb_app_type *itr;
17579ab933abSJohn Fastabend 	u8 prio = 0;
17589ab933abSJohn Fastabend 
17599ab933abSJohn Fastabend 	spin_lock(&dcb_lock);
1760716b31abSThomas Graf 	if ((itr = dcb_app_lookup(app, dev->ifindex, 0)))
17619ab933abSJohn Fastabend 		prio = itr->app.priority;
17629ab933abSJohn Fastabend 	spin_unlock(&dcb_lock);
17639ab933abSJohn Fastabend 
17649ab933abSJohn Fastabend 	return prio;
17659ab933abSJohn Fastabend }
17669ab933abSJohn Fastabend EXPORT_SYMBOL(dcb_getapp);
17679ab933abSJohn Fastabend 
17689ab933abSJohn Fastabend /**
1769b6db2174SJohn Fastabend  * dcb_setapp - add CEE dcb application data to app list
17709ab933abSJohn Fastabend  *
1771b6db2174SJohn Fastabend  * Priority 0 is an invalid priority in CEE spec. This routine
1772b6db2174SJohn Fastabend  * removes applications from the app list if the priority is
1773b6db2174SJohn Fastabend  * set to zero.
17749ab933abSJohn Fastabend  */
1775ab6baf98SJohn Fastabend int dcb_setapp(struct net_device *dev, struct dcb_app *new)
17769ab933abSJohn Fastabend {
17779ab933abSJohn Fastabend 	struct dcb_app_type *itr;
17787ec79270SJohn Fastabend 	struct dcb_app_type event;
17794e4f2f69SThomas Graf 	int err = 0;
17807ec79270SJohn Fastabend 
1781e290ed81SMark Rustad 	event.ifindex = dev->ifindex;
17827ec79270SJohn Fastabend 	memcpy(&event.app, new, sizeof(event.app));
17836bd0e1cbSJohn Fastabend 	if (dev->dcbnl_ops->getdcbx)
17846bd0e1cbSJohn Fastabend 		event.dcbx = dev->dcbnl_ops->getdcbx(dev);
17859ab933abSJohn Fastabend 
17869ab933abSJohn Fastabend 	spin_lock(&dcb_lock);
17879ab933abSJohn Fastabend 	/* Search for existing match and replace */
1788716b31abSThomas Graf 	if ((itr = dcb_app_lookup(new, dev->ifindex, 0))) {
17899ab933abSJohn Fastabend 		if (new->priority)
17909ab933abSJohn Fastabend 			itr->app.priority = new->priority;
17919ab933abSJohn Fastabend 		else {
17929ab933abSJohn Fastabend 			list_del(&itr->list);
17939ab933abSJohn Fastabend 			kfree(itr);
17949ab933abSJohn Fastabend 		}
17959ab933abSJohn Fastabend 		goto out;
17969ab933abSJohn Fastabend 	}
17979ab933abSJohn Fastabend 	/* App type does not exist add new application type */
17984e4f2f69SThomas Graf 	if (new->priority)
17994e4f2f69SThomas Graf 		err = dcb_app_add(new, dev->ifindex);
18009ab933abSJohn Fastabend out:
18019ab933abSJohn Fastabend 	spin_unlock(&dcb_lock);
18024e4f2f69SThomas Graf 	if (!err)
18037ec79270SJohn Fastabend 		call_dcbevent_notifiers(DCB_APP_EVENT, &event);
18044e4f2f69SThomas Graf 	return err;
18059ab933abSJohn Fastabend }
18069ab933abSJohn Fastabend EXPORT_SYMBOL(dcb_setapp);
18079ab933abSJohn Fastabend 
1808b6db2174SJohn Fastabend /**
1809a364c8cfSJohn Fastabend  * dcb_ieee_getapp_mask - retrieve the IEEE DCB application priority
1810a364c8cfSJohn Fastabend  *
1811a364c8cfSJohn Fastabend  * Helper routine which on success returns a non-zero 802.1Qaz user
1812a364c8cfSJohn Fastabend  * priority bitmap otherwise returns 0 to indicate the dcb_app was
1813a364c8cfSJohn Fastabend  * not found in APP list.
1814a364c8cfSJohn Fastabend  */
1815a364c8cfSJohn Fastabend u8 dcb_ieee_getapp_mask(struct net_device *dev, struct dcb_app *app)
1816a364c8cfSJohn Fastabend {
1817a364c8cfSJohn Fastabend 	struct dcb_app_type *itr;
1818a364c8cfSJohn Fastabend 	u8 prio = 0;
1819a364c8cfSJohn Fastabend 
1820a364c8cfSJohn Fastabend 	spin_lock(&dcb_lock);
1821716b31abSThomas Graf 	if ((itr = dcb_app_lookup(app, dev->ifindex, 0)))
1822a364c8cfSJohn Fastabend 		prio |= 1 << itr->app.priority;
1823a364c8cfSJohn Fastabend 	spin_unlock(&dcb_lock);
1824a364c8cfSJohn Fastabend 
1825a364c8cfSJohn Fastabend 	return prio;
1826a364c8cfSJohn Fastabend }
1827a364c8cfSJohn Fastabend EXPORT_SYMBOL(dcb_ieee_getapp_mask);
1828a364c8cfSJohn Fastabend 
1829a364c8cfSJohn Fastabend /**
1830b6db2174SJohn Fastabend  * dcb_ieee_setapp - add IEEE dcb application data to app list
1831b6db2174SJohn Fastabend  *
1832b6db2174SJohn Fastabend  * This adds Application data to the list. Multiple application
1833b6db2174SJohn Fastabend  * entries may exists for the same selector and protocol as long
1834b6db2174SJohn Fastabend  * as the priorities are different.
1835b6db2174SJohn Fastabend  */
1836b6db2174SJohn Fastabend int dcb_ieee_setapp(struct net_device *dev, struct dcb_app *new)
1837b6db2174SJohn Fastabend {
1838b6db2174SJohn Fastabend 	struct dcb_app_type event;
1839b6db2174SJohn Fastabend 	int err = 0;
1840b6db2174SJohn Fastabend 
1841e290ed81SMark Rustad 	event.ifindex = dev->ifindex;
1842b6db2174SJohn Fastabend 	memcpy(&event.app, new, sizeof(event.app));
18436bd0e1cbSJohn Fastabend 	if (dev->dcbnl_ops->getdcbx)
18446bd0e1cbSJohn Fastabend 		event.dcbx = dev->dcbnl_ops->getdcbx(dev);
1845b6db2174SJohn Fastabend 
1846b6db2174SJohn Fastabend 	spin_lock(&dcb_lock);
1847b6db2174SJohn Fastabend 	/* Search for existing match and abort if found */
1848716b31abSThomas Graf 	if (dcb_app_lookup(new, dev->ifindex, new->priority)) {
1849b6db2174SJohn Fastabend 		err = -EEXIST;
1850b6db2174SJohn Fastabend 		goto out;
1851b6db2174SJohn Fastabend 	}
1852b6db2174SJohn Fastabend 
18534e4f2f69SThomas Graf 	err = dcb_app_add(new, dev->ifindex);
1854b6db2174SJohn Fastabend out:
1855b6db2174SJohn Fastabend 	spin_unlock(&dcb_lock);
1856b6db2174SJohn Fastabend 	if (!err)
1857b6db2174SJohn Fastabend 		call_dcbevent_notifiers(DCB_APP_EVENT, &event);
1858b6db2174SJohn Fastabend 	return err;
1859b6db2174SJohn Fastabend }
1860b6db2174SJohn Fastabend EXPORT_SYMBOL(dcb_ieee_setapp);
1861b6db2174SJohn Fastabend 
1862f9ae7e4bSJohn Fastabend /**
1863f9ae7e4bSJohn Fastabend  * dcb_ieee_delapp - delete IEEE dcb application data from list
1864f9ae7e4bSJohn Fastabend  *
1865f9ae7e4bSJohn Fastabend  * This removes a matching APP data from the APP list
1866f9ae7e4bSJohn Fastabend  */
1867f9ae7e4bSJohn Fastabend int dcb_ieee_delapp(struct net_device *dev, struct dcb_app *del)
1868f9ae7e4bSJohn Fastabend {
1869f9ae7e4bSJohn Fastabend 	struct dcb_app_type *itr;
1870f9ae7e4bSJohn Fastabend 	struct dcb_app_type event;
1871f9ae7e4bSJohn Fastabend 	int err = -ENOENT;
1872f9ae7e4bSJohn Fastabend 
1873e290ed81SMark Rustad 	event.ifindex = dev->ifindex;
1874f9ae7e4bSJohn Fastabend 	memcpy(&event.app, del, sizeof(event.app));
18756bd0e1cbSJohn Fastabend 	if (dev->dcbnl_ops->getdcbx)
18766bd0e1cbSJohn Fastabend 		event.dcbx = dev->dcbnl_ops->getdcbx(dev);
1877f9ae7e4bSJohn Fastabend 
1878f9ae7e4bSJohn Fastabend 	spin_lock(&dcb_lock);
1879f9ae7e4bSJohn Fastabend 	/* Search for existing match and remove it. */
1880716b31abSThomas Graf 	if ((itr = dcb_app_lookup(del, dev->ifindex, del->priority))) {
1881f9ae7e4bSJohn Fastabend 		list_del(&itr->list);
1882f9ae7e4bSJohn Fastabend 		kfree(itr);
1883f9ae7e4bSJohn Fastabend 		err = 0;
1884f9ae7e4bSJohn Fastabend 	}
1885f9ae7e4bSJohn Fastabend 
1886f9ae7e4bSJohn Fastabend 	spin_unlock(&dcb_lock);
1887f9ae7e4bSJohn Fastabend 	if (!err)
1888f9ae7e4bSJohn Fastabend 		call_dcbevent_notifiers(DCB_APP_EVENT, &event);
1889f9ae7e4bSJohn Fastabend 	return err;
1890f9ae7e4bSJohn Fastabend }
1891f9ae7e4bSJohn Fastabend EXPORT_SYMBOL(dcb_ieee_delapp);
1892f9ae7e4bSJohn Fastabend 
18937c14c3f1SShmulik Ravid static void dcb_flushapp(void)
18949ab933abSJohn Fastabend {
18959ab933abSJohn Fastabend 	struct dcb_app_type *app;
18962a8fe003SDan Carpenter 	struct dcb_app_type *tmp;
18979ab933abSJohn Fastabend 
18989ab933abSJohn Fastabend 	spin_lock(&dcb_lock);
18992a8fe003SDan Carpenter 	list_for_each_entry_safe(app, tmp, &dcb_app_list, list) {
19009ab933abSJohn Fastabend 		list_del(&app->list);
19019ab933abSJohn Fastabend 		kfree(app);
19029ab933abSJohn Fastabend 	}
19039ab933abSJohn Fastabend 	spin_unlock(&dcb_lock);
19049ab933abSJohn Fastabend }
19059ab933abSJohn Fastabend 
19062f90b865SAlexander Duyck static int __init dcbnl_init(void)
19072f90b865SAlexander Duyck {
19089ab933abSJohn Fastabend 	INIT_LIST_HEAD(&dcb_app_list);
19099ab933abSJohn Fastabend 
1910c7ac8679SGreg Rose 	rtnl_register(PF_UNSPEC, RTM_GETDCB, dcb_doit, NULL, NULL);
1911c7ac8679SGreg Rose 	rtnl_register(PF_UNSPEC, RTM_SETDCB, dcb_doit, NULL, NULL);
19122f90b865SAlexander Duyck 
19132f90b865SAlexander Duyck 	return 0;
19142f90b865SAlexander Duyck }
19152f90b865SAlexander Duyck module_init(dcbnl_init);
19162f90b865SAlexander Duyck 
19172f90b865SAlexander Duyck static void __exit dcbnl_exit(void)
19182f90b865SAlexander Duyck {
19192f90b865SAlexander Duyck 	rtnl_unregister(PF_UNSPEC, RTM_GETDCB);
19202f90b865SAlexander Duyck 	rtnl_unregister(PF_UNSPEC, RTM_SETDCB);
19219ab933abSJohn Fastabend 	dcb_flushapp();
19222f90b865SAlexander Duyck }
19232f90b865SAlexander Duyck module_exit(dcbnl_exit);
1924