xref: /openbmc/linux/net/dcb/dcbnl.c (revision 91b0383f)
19952f691SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
22f90b865SAlexander Duyck /*
3698e1d23SMark Rustad  * Copyright (c) 2008-2011, Intel Corporation.
42f90b865SAlexander Duyck  *
536b9ad80SPaul Gortmaker  * Description: Data Center Bridging netlink interface
62f90b865SAlexander Duyck  * Author: Lucy Liu <lucy.liu@intel.com>
72f90b865SAlexander Duyck  */
82f90b865SAlexander Duyck 
92f90b865SAlexander Duyck #include <linux/netdevice.h>
102f90b865SAlexander Duyck #include <linux/netlink.h>
115a0e3ad6STejun Heo #include <linux/slab.h>
122f90b865SAlexander Duyck #include <net/netlink.h>
132f90b865SAlexander Duyck #include <net/rtnetlink.h>
142f90b865SAlexander Duyck #include <linux/dcbnl.h>
1596b99684SJohn Fastabend #include <net/dcbevent.h>
162f90b865SAlexander Duyck #include <linux/rtnetlink.h>
1736b9ad80SPaul Gortmaker #include <linux/init.h>
182f90b865SAlexander Duyck #include <net/sock.h>
192f90b865SAlexander Duyck 
20ae86b9e3SBen Hutchings /* Data Center Bridging (DCB) is a collection of Ethernet enhancements
212f90b865SAlexander Duyck  * intended to allow network traffic with differing requirements
222f90b865SAlexander Duyck  * (highly reliable, no drops vs. best effort vs. low latency) to operate
232f90b865SAlexander Duyck  * and co-exist on Ethernet.  Current DCB features are:
242f90b865SAlexander Duyck  *
252f90b865SAlexander Duyck  * Enhanced Transmission Selection (aka Priority Grouping [PG]) - provides a
262f90b865SAlexander Duyck  *   framework for assigning bandwidth guarantees to traffic classes.
272f90b865SAlexander Duyck  *
282f90b865SAlexander Duyck  * Priority-based Flow Control (PFC) - provides a flow control mechanism which
292f90b865SAlexander Duyck  *   can work independently for each 802.1p priority.
302f90b865SAlexander Duyck  *
312f90b865SAlexander Duyck  * Congestion Notification - provides a mechanism for end-to-end congestion
322f90b865SAlexander Duyck  *   control for protocols which do not have built-in congestion management.
332f90b865SAlexander Duyck  *
342f90b865SAlexander Duyck  * More information about the emerging standards for these Ethernet features
352f90b865SAlexander Duyck  * can be found at: http://www.ieee802.org/1/pages/dcbridges.html
362f90b865SAlexander Duyck  *
372f90b865SAlexander Duyck  * This file implements an rtnetlink interface to allow configuration of DCB
382f90b865SAlexander Duyck  * features for capable devices.
392f90b865SAlexander Duyck  */
402f90b865SAlexander Duyck 
412f90b865SAlexander Duyck /**************** DCB attribute policies *************************************/
422f90b865SAlexander Duyck 
432f90b865SAlexander Duyck /* DCB netlink attributes policy */
44b54452b0SAlexey Dobriyan static const struct nla_policy dcbnl_rtnl_policy[DCB_ATTR_MAX + 1] = {
45859ee3c4SAlexander Duyck 	[DCB_ATTR_IFNAME]      = {.type = NLA_NUL_STRING, .len = IFNAMSIZ - 1},
462f90b865SAlexander Duyck 	[DCB_ATTR_STATE]       = {.type = NLA_U8},
472f90b865SAlexander Duyck 	[DCB_ATTR_PFC_CFG]     = {.type = NLA_NESTED},
482f90b865SAlexander Duyck 	[DCB_ATTR_PG_CFG]      = {.type = NLA_NESTED},
492f90b865SAlexander Duyck 	[DCB_ATTR_SET_ALL]     = {.type = NLA_U8},
502f90b865SAlexander Duyck 	[DCB_ATTR_PERM_HWADDR] = {.type = NLA_FLAG},
5146132188SAlexander Duyck 	[DCB_ATTR_CAP]         = {.type = NLA_NESTED},
520eb3aa9bSAlexander Duyck 	[DCB_ATTR_PFC_STATE]   = {.type = NLA_U8},
53859ee3c4SAlexander Duyck 	[DCB_ATTR_BCN]         = {.type = NLA_NESTED},
546fa382afSYi Zou 	[DCB_ATTR_APP]         = {.type = NLA_NESTED},
553e29027aSJohn Fastabend 	[DCB_ATTR_IEEE]	       = {.type = NLA_NESTED},
566241b625SShmulik Ravid 	[DCB_ATTR_DCBX]        = {.type = NLA_U8},
57ea45fe4eSShmulik Ravid 	[DCB_ATTR_FEATCFG]     = {.type = NLA_NESTED},
582f90b865SAlexander Duyck };
592f90b865SAlexander Duyck 
602f90b865SAlexander Duyck /* DCB priority flow control to User Priority nested attributes */
61b54452b0SAlexey Dobriyan static const struct nla_policy dcbnl_pfc_up_nest[DCB_PFC_UP_ATTR_MAX + 1] = {
622f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_0]   = {.type = NLA_U8},
632f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_1]   = {.type = NLA_U8},
642f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_2]   = {.type = NLA_U8},
652f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_3]   = {.type = NLA_U8},
662f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_4]   = {.type = NLA_U8},
672f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_5]   = {.type = NLA_U8},
682f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_6]   = {.type = NLA_U8},
692f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_7]   = {.type = NLA_U8},
702f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_ALL] = {.type = NLA_FLAG},
712f90b865SAlexander Duyck };
722f90b865SAlexander Duyck 
732f90b865SAlexander Duyck /* DCB priority grouping nested attributes */
74b54452b0SAlexey Dobriyan static const struct nla_policy dcbnl_pg_nest[DCB_PG_ATTR_MAX + 1] = {
752f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_0]      = {.type = NLA_NESTED},
762f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_1]      = {.type = NLA_NESTED},
772f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_2]      = {.type = NLA_NESTED},
782f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_3]      = {.type = NLA_NESTED},
792f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_4]      = {.type = NLA_NESTED},
802f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_5]      = {.type = NLA_NESTED},
812f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_6]      = {.type = NLA_NESTED},
822f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_7]      = {.type = NLA_NESTED},
832f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_ALL]    = {.type = NLA_NESTED},
842f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_0]   = {.type = NLA_U8},
852f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_1]   = {.type = NLA_U8},
862f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_2]   = {.type = NLA_U8},
872f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_3]   = {.type = NLA_U8},
882f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_4]   = {.type = NLA_U8},
892f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_5]   = {.type = NLA_U8},
902f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_6]   = {.type = NLA_U8},
912f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_7]   = {.type = NLA_U8},
922f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_ALL] = {.type = NLA_FLAG},
932f90b865SAlexander Duyck };
942f90b865SAlexander Duyck 
952f90b865SAlexander Duyck /* DCB traffic class nested attributes. */
96b54452b0SAlexey Dobriyan static const struct nla_policy dcbnl_tc_param_nest[DCB_TC_ATTR_PARAM_MAX + 1] = {
972f90b865SAlexander Duyck 	[DCB_TC_ATTR_PARAM_PGID]            = {.type = NLA_U8},
982f90b865SAlexander Duyck 	[DCB_TC_ATTR_PARAM_UP_MAPPING]      = {.type = NLA_U8},
992f90b865SAlexander Duyck 	[DCB_TC_ATTR_PARAM_STRICT_PRIO]     = {.type = NLA_U8},
1002f90b865SAlexander Duyck 	[DCB_TC_ATTR_PARAM_BW_PCT]          = {.type = NLA_U8},
1012f90b865SAlexander Duyck 	[DCB_TC_ATTR_PARAM_ALL]             = {.type = NLA_FLAG},
1022f90b865SAlexander Duyck };
1032f90b865SAlexander Duyck 
10446132188SAlexander Duyck /* DCB capabilities nested attributes. */
105b54452b0SAlexey Dobriyan static const struct nla_policy dcbnl_cap_nest[DCB_CAP_ATTR_MAX + 1] = {
10646132188SAlexander Duyck 	[DCB_CAP_ATTR_ALL]     = {.type = NLA_FLAG},
10746132188SAlexander Duyck 	[DCB_CAP_ATTR_PG]      = {.type = NLA_U8},
10846132188SAlexander Duyck 	[DCB_CAP_ATTR_PFC]     = {.type = NLA_U8},
10946132188SAlexander Duyck 	[DCB_CAP_ATTR_UP2TC]   = {.type = NLA_U8},
11046132188SAlexander Duyck 	[DCB_CAP_ATTR_PG_TCS]  = {.type = NLA_U8},
11146132188SAlexander Duyck 	[DCB_CAP_ATTR_PFC_TCS] = {.type = NLA_U8},
11246132188SAlexander Duyck 	[DCB_CAP_ATTR_GSP]     = {.type = NLA_U8},
11346132188SAlexander Duyck 	[DCB_CAP_ATTR_BCN]     = {.type = NLA_U8},
1146241b625SShmulik Ravid 	[DCB_CAP_ATTR_DCBX]    = {.type = NLA_U8},
11546132188SAlexander Duyck };
1162f90b865SAlexander Duyck 
11733dbabc4SAlexander Duyck /* DCB capabilities nested attributes. */
118b54452b0SAlexey Dobriyan static const struct nla_policy dcbnl_numtcs_nest[DCB_NUMTCS_ATTR_MAX + 1] = {
11933dbabc4SAlexander Duyck 	[DCB_NUMTCS_ATTR_ALL]     = {.type = NLA_FLAG},
12033dbabc4SAlexander Duyck 	[DCB_NUMTCS_ATTR_PG]      = {.type = NLA_U8},
12133dbabc4SAlexander Duyck 	[DCB_NUMTCS_ATTR_PFC]     = {.type = NLA_U8},
12233dbabc4SAlexander Duyck };
12333dbabc4SAlexander Duyck 
124859ee3c4SAlexander Duyck /* DCB BCN nested attributes. */
125b54452b0SAlexey Dobriyan static const struct nla_policy dcbnl_bcn_nest[DCB_BCN_ATTR_MAX + 1] = {
126859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_0]         = {.type = NLA_U8},
127859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_1]         = {.type = NLA_U8},
128859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_2]         = {.type = NLA_U8},
129859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_3]         = {.type = NLA_U8},
130859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_4]         = {.type = NLA_U8},
131859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_5]         = {.type = NLA_U8},
132859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_6]         = {.type = NLA_U8},
133859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_7]         = {.type = NLA_U8},
134859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_ALL]       = {.type = NLA_FLAG},
135f4314e81SDon Skidmore 	[DCB_BCN_ATTR_BCNA_0]       = {.type = NLA_U32},
136f4314e81SDon Skidmore 	[DCB_BCN_ATTR_BCNA_1]       = {.type = NLA_U32},
137859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_ALPHA]        = {.type = NLA_U32},
138859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_BETA]         = {.type = NLA_U32},
139859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_GD]           = {.type = NLA_U32},
140859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_GI]           = {.type = NLA_U32},
141859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_TMAX]         = {.type = NLA_U32},
142859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_TD]           = {.type = NLA_U32},
143859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RMIN]         = {.type = NLA_U32},
144859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_W]            = {.type = NLA_U32},
145859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RD]           = {.type = NLA_U32},
146859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RU]           = {.type = NLA_U32},
147859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_WRTT]         = {.type = NLA_U32},
148859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RI]           = {.type = NLA_U32},
149859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_C]            = {.type = NLA_U32},
150859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_ALL]          = {.type = NLA_FLAG},
151859ee3c4SAlexander Duyck };
152859ee3c4SAlexander Duyck 
1536fa382afSYi Zou /* DCB APP nested attributes. */
154b54452b0SAlexey Dobriyan static const struct nla_policy dcbnl_app_nest[DCB_APP_ATTR_MAX + 1] = {
1556fa382afSYi Zou 	[DCB_APP_ATTR_IDTYPE]       = {.type = NLA_U8},
1566fa382afSYi Zou 	[DCB_APP_ATTR_ID]           = {.type = NLA_U16},
1576fa382afSYi Zou 	[DCB_APP_ATTR_PRIORITY]     = {.type = NLA_U8},
1586fa382afSYi Zou };
1596fa382afSYi Zou 
1603e29027aSJohn Fastabend /* IEEE 802.1Qaz nested attributes. */
1613e29027aSJohn Fastabend static const struct nla_policy dcbnl_ieee_policy[DCB_ATTR_IEEE_MAX + 1] = {
1623e29027aSJohn Fastabend 	[DCB_ATTR_IEEE_ETS]	    = {.len = sizeof(struct ieee_ets)},
1633e29027aSJohn Fastabend 	[DCB_ATTR_IEEE_PFC]	    = {.len = sizeof(struct ieee_pfc)},
1643e29027aSJohn Fastabend 	[DCB_ATTR_IEEE_APP_TABLE]   = {.type = NLA_NESTED},
16508f10affSAmir Vadai 	[DCB_ATTR_IEEE_MAXRATE]   = {.len = sizeof(struct ieee_maxrate)},
166c9368247SShani Michaeli 	[DCB_ATTR_IEEE_QCN]         = {.len = sizeof(struct ieee_qcn)},
167c9368247SShani Michaeli 	[DCB_ATTR_IEEE_QCN_STATS]   = {.len = sizeof(struct ieee_qcn_stats)},
168e549f6f9SHuy Nguyen 	[DCB_ATTR_DCB_BUFFER]       = {.len = sizeof(struct dcbnl_buffer)},
1693e29027aSJohn Fastabend };
1703e29027aSJohn Fastabend 
171ea45fe4eSShmulik Ravid /* DCB number of traffic classes nested attributes. */
172ea45fe4eSShmulik Ravid static const struct nla_policy dcbnl_featcfg_nest[DCB_FEATCFG_ATTR_MAX + 1] = {
173ea45fe4eSShmulik Ravid 	[DCB_FEATCFG_ATTR_ALL]      = {.type = NLA_FLAG},
174ea45fe4eSShmulik Ravid 	[DCB_FEATCFG_ATTR_PG]       = {.type = NLA_U8},
175ea45fe4eSShmulik Ravid 	[DCB_FEATCFG_ATTR_PFC]      = {.type = NLA_U8},
176ea45fe4eSShmulik Ravid 	[DCB_FEATCFG_ATTR_APP]      = {.type = NLA_U8},
177ea45fe4eSShmulik Ravid };
178ea45fe4eSShmulik Ravid 
1799ab933abSJohn Fastabend static LIST_HEAD(dcb_app_list);
1809ab933abSJohn Fastabend static DEFINE_SPINLOCK(dcb_lock);
1819ab933abSJohn Fastabend 
18233a03aadSThomas Graf static struct sk_buff *dcbnl_newmsg(int type, u8 cmd, u32 port, u32 seq,
18333a03aadSThomas Graf 				    u32 flags, struct nlmsghdr **nlhp)
18433a03aadSThomas Graf {
18533a03aadSThomas Graf 	struct sk_buff *skb;
18633a03aadSThomas Graf 	struct dcbmsg *dcb;
18733a03aadSThomas Graf 	struct nlmsghdr *nlh;
18833a03aadSThomas Graf 
18933a03aadSThomas Graf 	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
19033a03aadSThomas Graf 	if (!skb)
19133a03aadSThomas Graf 		return NULL;
19233a03aadSThomas Graf 
19333a03aadSThomas Graf 	nlh = nlmsg_put(skb, port, seq, type, sizeof(*dcb), flags);
194b3908e22SThomas Graf 	BUG_ON(!nlh);
19533a03aadSThomas Graf 
19633a03aadSThomas Graf 	dcb = nlmsg_data(nlh);
19733a03aadSThomas Graf 	dcb->dcb_family = AF_UNSPEC;
19833a03aadSThomas Graf 	dcb->cmd = cmd;
19933a03aadSThomas Graf 	dcb->dcb_pad = 0;
20033a03aadSThomas Graf 
20133a03aadSThomas Graf 	if (nlhp)
20233a03aadSThomas Graf 		*nlhp = nlh;
20333a03aadSThomas Graf 
20433a03aadSThomas Graf 	return skb;
20533a03aadSThomas Graf }
20633a03aadSThomas Graf 
2077be99413SThomas Graf static int dcbnl_getstate(struct net_device *netdev, struct nlmsghdr *nlh,
2087be99413SThomas Graf 			  u32 seq, struct nlattr **tb, struct sk_buff *skb)
2092f90b865SAlexander Duyck {
2102f90b865SAlexander Duyck 	/* if (!tb[DCB_ATTR_STATE] || !netdev->dcbnl_ops->getstate) */
2112f90b865SAlexander Duyck 	if (!netdev->dcbnl_ops->getstate)
2123d1f4869SThomas Graf 		return -EOPNOTSUPP;
2132f90b865SAlexander Duyck 
2147be99413SThomas Graf 	return nla_put_u8(skb, DCB_ATTR_STATE,
2157be99413SThomas Graf 			  netdev->dcbnl_ops->getstate(netdev));
2162f90b865SAlexander Duyck }
2172f90b865SAlexander Duyck 
2187be99413SThomas Graf static int dcbnl_getpfccfg(struct net_device *netdev, struct nlmsghdr *nlh,
2197be99413SThomas Graf 			   u32 seq, struct nlattr **tb, struct sk_buff *skb)
2202f90b865SAlexander Duyck {
2212f90b865SAlexander Duyck 	struct nlattr *data[DCB_PFC_UP_ATTR_MAX + 1], *nest;
2222f90b865SAlexander Duyck 	u8 value;
2233d1f4869SThomas Graf 	int ret;
2242f90b865SAlexander Duyck 	int i;
2252f90b865SAlexander Duyck 	int getall = 0;
2262f90b865SAlexander Duyck 
2273d1f4869SThomas Graf 	if (!tb[DCB_ATTR_PFC_CFG])
2283d1f4869SThomas Graf 		return -EINVAL;
2293d1f4869SThomas Graf 
2303d1f4869SThomas Graf 	if (!netdev->dcbnl_ops->getpfccfg)
2313d1f4869SThomas Graf 		return -EOPNOTSUPP;
2322f90b865SAlexander Duyck 
2338cb08174SJohannes Berg 	ret = nla_parse_nested_deprecated(data, DCB_PFC_UP_ATTR_MAX,
2348cb08174SJohannes Berg 					  tb[DCB_ATTR_PFC_CFG],
2358cb08174SJohannes Berg 					  dcbnl_pfc_up_nest, NULL);
2362f90b865SAlexander Duyck 	if (ret)
2373d1f4869SThomas Graf 		return ret;
2382f90b865SAlexander Duyck 
239ae0be8deSMichal Kubecek 	nest = nla_nest_start_noflag(skb, DCB_ATTR_PFC_CFG);
2402f90b865SAlexander Duyck 	if (!nest)
2413d1f4869SThomas Graf 		return -EMSGSIZE;
2422f90b865SAlexander Duyck 
2432f90b865SAlexander Duyck 	if (data[DCB_PFC_UP_ATTR_ALL])
2442f90b865SAlexander Duyck 		getall = 1;
2452f90b865SAlexander Duyck 
2462f90b865SAlexander Duyck 	for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) {
2472f90b865SAlexander Duyck 		if (!getall && !data[i])
2482f90b865SAlexander Duyck 			continue;
2492f90b865SAlexander Duyck 
2502f90b865SAlexander Duyck 		netdev->dcbnl_ops->getpfccfg(netdev, i - DCB_PFC_UP_ATTR_0,
2512f90b865SAlexander Duyck 		                             &value);
2527be99413SThomas Graf 		ret = nla_put_u8(skb, i, value);
2532f90b865SAlexander Duyck 		if (ret) {
2547be99413SThomas Graf 			nla_nest_cancel(skb, nest);
2553d1f4869SThomas Graf 			return ret;
2562f90b865SAlexander Duyck 		}
2572f90b865SAlexander Duyck 	}
2587be99413SThomas Graf 	nla_nest_end(skb, nest);
2592f90b865SAlexander Duyck 
2602f90b865SAlexander Duyck 	return 0;
2612f90b865SAlexander Duyck }
2622f90b865SAlexander Duyck 
2637be99413SThomas Graf static int dcbnl_getperm_hwaddr(struct net_device *netdev, struct nlmsghdr *nlh,
2647be99413SThomas Graf 				u32 seq, struct nlattr **tb, struct sk_buff *skb)
2652f90b865SAlexander Duyck {
2662f90b865SAlexander Duyck 	u8 perm_addr[MAX_ADDR_LEN];
2672f90b865SAlexander Duyck 
2682f90b865SAlexander Duyck 	if (!netdev->dcbnl_ops->getpermhwaddr)
2693d1f4869SThomas Graf 		return -EOPNOTSUPP;
2702f90b865SAlexander Duyck 
27129cd8ae0SMathias Krause 	memset(perm_addr, 0, sizeof(perm_addr));
2722f90b865SAlexander Duyck 	netdev->dcbnl_ops->getpermhwaddr(netdev, perm_addr);
2732f90b865SAlexander Duyck 
2747be99413SThomas Graf 	return nla_put(skb, DCB_ATTR_PERM_HWADDR, sizeof(perm_addr), perm_addr);
2752f90b865SAlexander Duyck }
2762f90b865SAlexander Duyck 
2777be99413SThomas Graf static int dcbnl_getcap(struct net_device *netdev, struct nlmsghdr *nlh,
2787be99413SThomas Graf 			u32 seq, struct nlattr **tb, struct sk_buff *skb)
27946132188SAlexander Duyck {
28046132188SAlexander Duyck 	struct nlattr *data[DCB_CAP_ATTR_MAX + 1], *nest;
28146132188SAlexander Duyck 	u8 value;
2823d1f4869SThomas Graf 	int ret;
28346132188SAlexander Duyck 	int i;
28446132188SAlexander Duyck 	int getall = 0;
28546132188SAlexander Duyck 
2863d1f4869SThomas Graf 	if (!tb[DCB_ATTR_CAP])
2873d1f4869SThomas Graf 		return -EINVAL;
2883d1f4869SThomas Graf 
2893d1f4869SThomas Graf 	if (!netdev->dcbnl_ops->getcap)
2903d1f4869SThomas Graf 		return -EOPNOTSUPP;
29146132188SAlexander Duyck 
2928cb08174SJohannes Berg 	ret = nla_parse_nested_deprecated(data, DCB_CAP_ATTR_MAX,
2938cb08174SJohannes Berg 					  tb[DCB_ATTR_CAP], dcbnl_cap_nest,
2948cb08174SJohannes Berg 					  NULL);
29546132188SAlexander Duyck 	if (ret)
2963d1f4869SThomas Graf 		return ret;
29746132188SAlexander Duyck 
298ae0be8deSMichal Kubecek 	nest = nla_nest_start_noflag(skb, DCB_ATTR_CAP);
29946132188SAlexander Duyck 	if (!nest)
3003d1f4869SThomas Graf 		return -EMSGSIZE;
30146132188SAlexander Duyck 
30246132188SAlexander Duyck 	if (data[DCB_CAP_ATTR_ALL])
30346132188SAlexander Duyck 		getall = 1;
30446132188SAlexander Duyck 
30546132188SAlexander Duyck 	for (i = DCB_CAP_ATTR_ALL+1; i <= DCB_CAP_ATTR_MAX; i++) {
30646132188SAlexander Duyck 		if (!getall && !data[i])
30746132188SAlexander Duyck 			continue;
30846132188SAlexander Duyck 
30946132188SAlexander Duyck 		if (!netdev->dcbnl_ops->getcap(netdev, i, &value)) {
3107be99413SThomas Graf 			ret = nla_put_u8(skb, i, value);
31146132188SAlexander Duyck 			if (ret) {
3127be99413SThomas Graf 				nla_nest_cancel(skb, nest);
3133d1f4869SThomas Graf 				return ret;
3147be99413SThomas Graf 			}
3157be99413SThomas Graf 		}
3167be99413SThomas Graf 	}
3177be99413SThomas Graf 	nla_nest_end(skb, nest);
31846132188SAlexander Duyck 
31946132188SAlexander Duyck 	return 0;
32046132188SAlexander Duyck }
32146132188SAlexander Duyck 
3227be99413SThomas Graf static int dcbnl_getnumtcs(struct net_device *netdev, struct nlmsghdr *nlh,
3237be99413SThomas Graf 			   u32 seq, struct nlattr **tb, struct sk_buff *skb)
32433dbabc4SAlexander Duyck {
32533dbabc4SAlexander Duyck 	struct nlattr *data[DCB_NUMTCS_ATTR_MAX + 1], *nest;
32633dbabc4SAlexander Duyck 	u8 value;
3273d1f4869SThomas Graf 	int ret;
32833dbabc4SAlexander Duyck 	int i;
32933dbabc4SAlexander Duyck 	int getall = 0;
33033dbabc4SAlexander Duyck 
3313d1f4869SThomas Graf 	if (!tb[DCB_ATTR_NUMTCS])
3323d1f4869SThomas Graf 		return -EINVAL;
3333d1f4869SThomas Graf 
3343d1f4869SThomas Graf 	if (!netdev->dcbnl_ops->getnumtcs)
3353d1f4869SThomas Graf 		return -EOPNOTSUPP;
33633dbabc4SAlexander Duyck 
3378cb08174SJohannes Berg 	ret = nla_parse_nested_deprecated(data, DCB_NUMTCS_ATTR_MAX,
3388cb08174SJohannes Berg 					  tb[DCB_ATTR_NUMTCS],
339fceb6435SJohannes Berg 					  dcbnl_numtcs_nest, NULL);
3403d1f4869SThomas Graf 	if (ret)
3413d1f4869SThomas Graf 		return ret;
34233dbabc4SAlexander Duyck 
343ae0be8deSMichal Kubecek 	nest = nla_nest_start_noflag(skb, DCB_ATTR_NUMTCS);
3443d1f4869SThomas Graf 	if (!nest)
3453d1f4869SThomas Graf 		return -EMSGSIZE;
34633dbabc4SAlexander Duyck 
34733dbabc4SAlexander Duyck 	if (data[DCB_NUMTCS_ATTR_ALL])
34833dbabc4SAlexander Duyck 		getall = 1;
34933dbabc4SAlexander Duyck 
35033dbabc4SAlexander Duyck 	for (i = DCB_NUMTCS_ATTR_ALL+1; i <= DCB_NUMTCS_ATTR_MAX; i++) {
35133dbabc4SAlexander Duyck 		if (!getall && !data[i])
35233dbabc4SAlexander Duyck 			continue;
35333dbabc4SAlexander Duyck 
35433dbabc4SAlexander Duyck 		ret = netdev->dcbnl_ops->getnumtcs(netdev, i, &value);
35533dbabc4SAlexander Duyck 		if (!ret) {
3567be99413SThomas Graf 			ret = nla_put_u8(skb, i, value);
35733dbabc4SAlexander Duyck 			if (ret) {
3587be99413SThomas Graf 				nla_nest_cancel(skb, nest);
3593d1f4869SThomas Graf 				return ret;
36033dbabc4SAlexander Duyck 			}
3613d1f4869SThomas Graf 		} else
3623d1f4869SThomas Graf 			return -EINVAL;
3637be99413SThomas Graf 	}
3647be99413SThomas Graf 	nla_nest_end(skb, nest);
36533dbabc4SAlexander Duyck 
36633dbabc4SAlexander Duyck 	return 0;
36733dbabc4SAlexander Duyck }
36833dbabc4SAlexander Duyck 
3697be99413SThomas Graf static int dcbnl_setnumtcs(struct net_device *netdev, struct nlmsghdr *nlh,
3707be99413SThomas Graf 			   u32 seq, struct nlattr **tb, struct sk_buff *skb)
37133dbabc4SAlexander Duyck {
37233dbabc4SAlexander Duyck 	struct nlattr *data[DCB_NUMTCS_ATTR_MAX + 1];
3733d1f4869SThomas Graf 	int ret;
37433dbabc4SAlexander Duyck 	u8 value;
37533dbabc4SAlexander Duyck 	int i;
37633dbabc4SAlexander Duyck 
3773d1f4869SThomas Graf 	if (!tb[DCB_ATTR_NUMTCS])
3783d1f4869SThomas Graf 		return -EINVAL;
3793d1f4869SThomas Graf 
3803d1f4869SThomas Graf 	if (!netdev->dcbnl_ops->setnumtcs)
3813d1f4869SThomas Graf 		return -EOPNOTSUPP;
38233dbabc4SAlexander Duyck 
3838cb08174SJohannes Berg 	ret = nla_parse_nested_deprecated(data, DCB_NUMTCS_ATTR_MAX,
3848cb08174SJohannes Berg 					  tb[DCB_ATTR_NUMTCS],
385fceb6435SJohannes Berg 					  dcbnl_numtcs_nest, NULL);
3867be99413SThomas Graf 	if (ret)
3873d1f4869SThomas Graf 		return ret;
38833dbabc4SAlexander Duyck 
38933dbabc4SAlexander Duyck 	for (i = DCB_NUMTCS_ATTR_ALL+1; i <= DCB_NUMTCS_ATTR_MAX; i++) {
39033dbabc4SAlexander Duyck 		if (data[i] == NULL)
39133dbabc4SAlexander Duyck 			continue;
39233dbabc4SAlexander Duyck 
39333dbabc4SAlexander Duyck 		value = nla_get_u8(data[i]);
39433dbabc4SAlexander Duyck 
39533dbabc4SAlexander Duyck 		ret = netdev->dcbnl_ops->setnumtcs(netdev, i, value);
39633dbabc4SAlexander Duyck 		if (ret)
3977be99413SThomas Graf 			break;
39833dbabc4SAlexander Duyck 	}
39933dbabc4SAlexander Duyck 
4007be99413SThomas Graf 	return nla_put_u8(skb, DCB_ATTR_NUMTCS, !!ret);
40133dbabc4SAlexander Duyck }
40233dbabc4SAlexander Duyck 
4037be99413SThomas Graf static int dcbnl_getpfcstate(struct net_device *netdev, struct nlmsghdr *nlh,
4047be99413SThomas Graf 			     u32 seq, struct nlattr **tb, struct sk_buff *skb)
4050eb3aa9bSAlexander Duyck {
4060eb3aa9bSAlexander Duyck 	if (!netdev->dcbnl_ops->getpfcstate)
4073d1f4869SThomas Graf 		return -EOPNOTSUPP;
4080eb3aa9bSAlexander Duyck 
4097be99413SThomas Graf 	return nla_put_u8(skb, DCB_ATTR_PFC_STATE,
4107be99413SThomas Graf 			  netdev->dcbnl_ops->getpfcstate(netdev));
4110eb3aa9bSAlexander Duyck }
4120eb3aa9bSAlexander Duyck 
4137be99413SThomas Graf static int dcbnl_setpfcstate(struct net_device *netdev, struct nlmsghdr *nlh,
4147be99413SThomas Graf 			     u32 seq, struct nlattr **tb, struct sk_buff *skb)
4150eb3aa9bSAlexander Duyck {
4160eb3aa9bSAlexander Duyck 	u8 value;
4170eb3aa9bSAlexander Duyck 
4183d1f4869SThomas Graf 	if (!tb[DCB_ATTR_PFC_STATE])
4197be99413SThomas Graf 		return -EINVAL;
4200eb3aa9bSAlexander Duyck 
4213d1f4869SThomas Graf 	if (!netdev->dcbnl_ops->setpfcstate)
4223d1f4869SThomas Graf 		return -EOPNOTSUPP;
4233d1f4869SThomas Graf 
4240eb3aa9bSAlexander Duyck 	value = nla_get_u8(tb[DCB_ATTR_PFC_STATE]);
4250eb3aa9bSAlexander Duyck 
4260eb3aa9bSAlexander Duyck 	netdev->dcbnl_ops->setpfcstate(netdev, value);
4270eb3aa9bSAlexander Duyck 
4287be99413SThomas Graf 	return nla_put_u8(skb, DCB_ATTR_PFC_STATE, 0);
4290eb3aa9bSAlexander Duyck }
4300eb3aa9bSAlexander Duyck 
4317be99413SThomas Graf static int dcbnl_getapp(struct net_device *netdev, struct nlmsghdr *nlh,
4327be99413SThomas Graf 			u32 seq, struct nlattr **tb, struct sk_buff *skb)
43357949686SYi Zou {
43457949686SYi Zou 	struct nlattr *app_nest;
43557949686SYi Zou 	struct nlattr *app_tb[DCB_APP_ATTR_MAX + 1];
43657949686SYi Zou 	u16 id;
43757949686SYi Zou 	u8 up, idtype;
4383d1f4869SThomas Graf 	int ret;
43957949686SYi Zou 
4403dce38a0SJohn Fastabend 	if (!tb[DCB_ATTR_APP])
4413d1f4869SThomas Graf 		return -EINVAL;
44257949686SYi Zou 
4438cb08174SJohannes Berg 	ret = nla_parse_nested_deprecated(app_tb, DCB_APP_ATTR_MAX,
4448cb08174SJohannes Berg 					  tb[DCB_ATTR_APP], dcbnl_app_nest,
4458cb08174SJohannes Berg 					  NULL);
44657949686SYi Zou 	if (ret)
4473d1f4869SThomas Graf 		return ret;
44857949686SYi Zou 
44957949686SYi Zou 	/* all must be non-null */
45057949686SYi Zou 	if ((!app_tb[DCB_APP_ATTR_IDTYPE]) ||
45157949686SYi Zou 	    (!app_tb[DCB_APP_ATTR_ID]))
4523d1f4869SThomas Graf 		return -EINVAL;
45357949686SYi Zou 
45457949686SYi Zou 	/* either by eth type or by socket number */
45557949686SYi Zou 	idtype = nla_get_u8(app_tb[DCB_APP_ATTR_IDTYPE]);
45657949686SYi Zou 	if ((idtype != DCB_APP_IDTYPE_ETHTYPE) &&
45757949686SYi Zou 	    (idtype != DCB_APP_IDTYPE_PORTNUM))
4583d1f4869SThomas Graf 		return -EINVAL;
45957949686SYi Zou 
46057949686SYi Zou 	id = nla_get_u16(app_tb[DCB_APP_ATTR_ID]);
4613dce38a0SJohn Fastabend 
4623dce38a0SJohn Fastabend 	if (netdev->dcbnl_ops->getapp) {
463c2659479SAnish Bhatt 		ret = netdev->dcbnl_ops->getapp(netdev, idtype, id);
464c2659479SAnish Bhatt 		if (ret < 0)
465c2659479SAnish Bhatt 			return ret;
466c2659479SAnish Bhatt 		else
467c2659479SAnish Bhatt 			up = ret;
4683dce38a0SJohn Fastabend 	} else {
4693dce38a0SJohn Fastabend 		struct dcb_app app = {
4703dce38a0SJohn Fastabend 					.selector = idtype,
4713dce38a0SJohn Fastabend 					.protocol = id,
4723dce38a0SJohn Fastabend 				     };
4733dce38a0SJohn Fastabend 		up = dcb_getapp(netdev, &app);
4743dce38a0SJohn Fastabend 	}
47557949686SYi Zou 
476ae0be8deSMichal Kubecek 	app_nest = nla_nest_start_noflag(skb, DCB_ATTR_APP);
4777be99413SThomas Graf 	if (!app_nest)
4783d1f4869SThomas Graf 		return -EMSGSIZE;
47957949686SYi Zou 
4807be99413SThomas Graf 	ret = nla_put_u8(skb, DCB_APP_ATTR_IDTYPE, idtype);
48157949686SYi Zou 	if (ret)
48257949686SYi Zou 		goto out_cancel;
48357949686SYi Zou 
4847be99413SThomas Graf 	ret = nla_put_u16(skb, DCB_APP_ATTR_ID, id);
48557949686SYi Zou 	if (ret)
48657949686SYi Zou 		goto out_cancel;
48757949686SYi Zou 
4887be99413SThomas Graf 	ret = nla_put_u8(skb, DCB_APP_ATTR_PRIORITY, up);
48957949686SYi Zou 	if (ret)
49057949686SYi Zou 		goto out_cancel;
49157949686SYi Zou 
4927be99413SThomas Graf 	nla_nest_end(skb, app_nest);
49357949686SYi Zou 
4943d1f4869SThomas Graf 	return 0;
49557949686SYi Zou 
49657949686SYi Zou out_cancel:
4977be99413SThomas Graf 	nla_nest_cancel(skb, app_nest);
49857949686SYi Zou 	return ret;
49957949686SYi Zou }
50057949686SYi Zou 
5017be99413SThomas Graf static int dcbnl_setapp(struct net_device *netdev, struct nlmsghdr *nlh,
5027be99413SThomas Graf 			u32 seq, struct nlattr **tb, struct sk_buff *skb)
50357949686SYi Zou {
5043d1f4869SThomas Graf 	int ret;
50557949686SYi Zou 	u16 id;
50657949686SYi Zou 	u8 up, idtype;
50757949686SYi Zou 	struct nlattr *app_tb[DCB_APP_ATTR_MAX + 1];
50857949686SYi Zou 
5099ab933abSJohn Fastabend 	if (!tb[DCB_ATTR_APP])
5103d1f4869SThomas Graf 		return -EINVAL;
51157949686SYi Zou 
5128cb08174SJohannes Berg 	ret = nla_parse_nested_deprecated(app_tb, DCB_APP_ATTR_MAX,
5138cb08174SJohannes Berg 					  tb[DCB_ATTR_APP], dcbnl_app_nest,
5148cb08174SJohannes Berg 					  NULL);
51557949686SYi Zou 	if (ret)
5163d1f4869SThomas Graf 		return ret;
51757949686SYi Zou 
51857949686SYi Zou 	/* all must be non-null */
51957949686SYi Zou 	if ((!app_tb[DCB_APP_ATTR_IDTYPE]) ||
52057949686SYi Zou 	    (!app_tb[DCB_APP_ATTR_ID]) ||
52157949686SYi Zou 	    (!app_tb[DCB_APP_ATTR_PRIORITY]))
5223d1f4869SThomas Graf 		return -EINVAL;
52357949686SYi Zou 
52457949686SYi Zou 	/* either by eth type or by socket number */
52557949686SYi Zou 	idtype = nla_get_u8(app_tb[DCB_APP_ATTR_IDTYPE]);
52657949686SYi Zou 	if ((idtype != DCB_APP_IDTYPE_ETHTYPE) &&
52757949686SYi Zou 	    (idtype != DCB_APP_IDTYPE_PORTNUM))
5283d1f4869SThomas Graf 		return -EINVAL;
52957949686SYi Zou 
53057949686SYi Zou 	id = nla_get_u16(app_tb[DCB_APP_ATTR_ID]);
53157949686SYi Zou 	up = nla_get_u8(app_tb[DCB_APP_ATTR_PRIORITY]);
53257949686SYi Zou 
5339ab933abSJohn Fastabend 	if (netdev->dcbnl_ops->setapp) {
5343d1f4869SThomas Graf 		ret = netdev->dcbnl_ops->setapp(netdev, idtype, id, up);
535c2659479SAnish Bhatt 		if (ret < 0)
536c2659479SAnish Bhatt 			return ret;
5379ab933abSJohn Fastabend 	} else {
5389ab933abSJohn Fastabend 		struct dcb_app app;
5399ab933abSJohn Fastabend 		app.selector = idtype;
5409ab933abSJohn Fastabend 		app.protocol = id;
5419ab933abSJohn Fastabend 		app.priority = up;
5423d1f4869SThomas Graf 		ret = dcb_setapp(netdev, &app);
5439ab933abSJohn Fastabend 	}
5449ab933abSJohn Fastabend 
5457be99413SThomas Graf 	ret = nla_put_u8(skb, DCB_ATTR_APP, ret);
54608157984SJohn Fastabend 	dcbnl_cee_notify(netdev, RTM_SETDCB, DCB_CMD_SAPP, seq, 0);
5473d1f4869SThomas Graf 
54857949686SYi Zou 	return ret;
54957949686SYi Zou }
55057949686SYi Zou 
5517be99413SThomas Graf static int __dcbnl_pg_getcfg(struct net_device *netdev, struct nlmsghdr *nlh,
5527be99413SThomas Graf 			     struct nlattr **tb, struct sk_buff *skb, int dir)
5532f90b865SAlexander Duyck {
5542f90b865SAlexander Duyck 	struct nlattr *pg_nest, *param_nest, *data;
5552f90b865SAlexander Duyck 	struct nlattr *pg_tb[DCB_PG_ATTR_MAX + 1];
5562f90b865SAlexander Duyck 	struct nlattr *param_tb[DCB_TC_ATTR_PARAM_MAX + 1];
5572f90b865SAlexander Duyck 	u8 prio, pgid, tc_pct, up_map;
5583d1f4869SThomas Graf 	int ret;
5592f90b865SAlexander Duyck 	int getall = 0;
5602f90b865SAlexander Duyck 	int i;
5612f90b865SAlexander Duyck 
5623d1f4869SThomas Graf 	if (!tb[DCB_ATTR_PG_CFG])
5633d1f4869SThomas Graf 		return -EINVAL;
5643d1f4869SThomas Graf 
5653d1f4869SThomas Graf 	if (!netdev->dcbnl_ops->getpgtccfgtx ||
5662f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->getpgtccfgrx ||
5672f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->getpgbwgcfgtx ||
5682f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->getpgbwgcfgrx)
5693d1f4869SThomas Graf 		return -EOPNOTSUPP;
5702f90b865SAlexander Duyck 
5718cb08174SJohannes Berg 	ret = nla_parse_nested_deprecated(pg_tb, DCB_PG_ATTR_MAX,
5728cb08174SJohannes Berg 					  tb[DCB_ATTR_PG_CFG], dcbnl_pg_nest,
5738cb08174SJohannes Berg 					  NULL);
5742f90b865SAlexander Duyck 	if (ret)
5753d1f4869SThomas Graf 		return ret;
5762f90b865SAlexander Duyck 
577ae0be8deSMichal Kubecek 	pg_nest = nla_nest_start_noflag(skb, DCB_ATTR_PG_CFG);
5782f90b865SAlexander Duyck 	if (!pg_nest)
5793d1f4869SThomas Graf 		return -EMSGSIZE;
5802f90b865SAlexander Duyck 
5812f90b865SAlexander Duyck 	if (pg_tb[DCB_PG_ATTR_TC_ALL])
5822f90b865SAlexander Duyck 		getall = 1;
5832f90b865SAlexander Duyck 
5842f90b865SAlexander Duyck 	for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) {
5852f90b865SAlexander Duyck 		if (!getall && !pg_tb[i])
5862f90b865SAlexander Duyck 			continue;
5872f90b865SAlexander Duyck 
5882f90b865SAlexander Duyck 		if (pg_tb[DCB_PG_ATTR_TC_ALL])
5892f90b865SAlexander Duyck 			data = pg_tb[DCB_PG_ATTR_TC_ALL];
5902f90b865SAlexander Duyck 		else
5912f90b865SAlexander Duyck 			data = pg_tb[i];
5928cb08174SJohannes Berg 		ret = nla_parse_nested_deprecated(param_tb,
5938cb08174SJohannes Berg 						  DCB_TC_ATTR_PARAM_MAX, data,
594fceb6435SJohannes Berg 						  dcbnl_tc_param_nest, NULL);
5952f90b865SAlexander Duyck 		if (ret)
5962f90b865SAlexander Duyck 			goto err_pg;
5972f90b865SAlexander Duyck 
598ae0be8deSMichal Kubecek 		param_nest = nla_nest_start_noflag(skb, i);
5992f90b865SAlexander Duyck 		if (!param_nest)
6002f90b865SAlexander Duyck 			goto err_pg;
6012f90b865SAlexander Duyck 
6022f90b865SAlexander Duyck 		pgid = DCB_ATTR_VALUE_UNDEFINED;
6032f90b865SAlexander Duyck 		prio = DCB_ATTR_VALUE_UNDEFINED;
6042f90b865SAlexander Duyck 		tc_pct = DCB_ATTR_VALUE_UNDEFINED;
6052f90b865SAlexander Duyck 		up_map = DCB_ATTR_VALUE_UNDEFINED;
6062f90b865SAlexander Duyck 
6072f90b865SAlexander Duyck 		if (dir) {
6082f90b865SAlexander Duyck 			/* Rx */
6092f90b865SAlexander Duyck 			netdev->dcbnl_ops->getpgtccfgrx(netdev,
6102f90b865SAlexander Duyck 						i - DCB_PG_ATTR_TC_0, &prio,
6112f90b865SAlexander Duyck 						&pgid, &tc_pct, &up_map);
6122f90b865SAlexander Duyck 		} else {
6132f90b865SAlexander Duyck 			/* Tx */
6142f90b865SAlexander Duyck 			netdev->dcbnl_ops->getpgtccfgtx(netdev,
6152f90b865SAlexander Duyck 						i - DCB_PG_ATTR_TC_0, &prio,
6162f90b865SAlexander Duyck 						&pgid, &tc_pct, &up_map);
6172f90b865SAlexander Duyck 		}
6182f90b865SAlexander Duyck 
6192f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_PGID] ||
6202f90b865SAlexander Duyck 		    param_tb[DCB_TC_ATTR_PARAM_ALL]) {
6217be99413SThomas Graf 			ret = nla_put_u8(skb,
6222f90b865SAlexander Duyck 			                 DCB_TC_ATTR_PARAM_PGID, pgid);
6232f90b865SAlexander Duyck 			if (ret)
6242f90b865SAlexander Duyck 				goto err_param;
6252f90b865SAlexander Duyck 		}
6262f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING] ||
6272f90b865SAlexander Duyck 		    param_tb[DCB_TC_ATTR_PARAM_ALL]) {
6287be99413SThomas Graf 			ret = nla_put_u8(skb,
6292f90b865SAlexander Duyck 			                 DCB_TC_ATTR_PARAM_UP_MAPPING, up_map);
6302f90b865SAlexander Duyck 			if (ret)
6312f90b865SAlexander Duyck 				goto err_param;
6322f90b865SAlexander Duyck 		}
6332f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO] ||
6342f90b865SAlexander Duyck 		    param_tb[DCB_TC_ATTR_PARAM_ALL]) {
6357be99413SThomas Graf 			ret = nla_put_u8(skb,
6362f90b865SAlexander Duyck 			                 DCB_TC_ATTR_PARAM_STRICT_PRIO, prio);
6372f90b865SAlexander Duyck 			if (ret)
6382f90b865SAlexander Duyck 				goto err_param;
6392f90b865SAlexander Duyck 		}
6402f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_BW_PCT] ||
6412f90b865SAlexander Duyck 		    param_tb[DCB_TC_ATTR_PARAM_ALL]) {
6427be99413SThomas Graf 			ret = nla_put_u8(skb, DCB_TC_ATTR_PARAM_BW_PCT,
6432f90b865SAlexander Duyck 			                 tc_pct);
6442f90b865SAlexander Duyck 			if (ret)
6452f90b865SAlexander Duyck 				goto err_param;
6462f90b865SAlexander Duyck 		}
6477be99413SThomas Graf 		nla_nest_end(skb, param_nest);
6482f90b865SAlexander Duyck 	}
6492f90b865SAlexander Duyck 
6502f90b865SAlexander Duyck 	if (pg_tb[DCB_PG_ATTR_BW_ID_ALL])
6512f90b865SAlexander Duyck 		getall = 1;
6522f90b865SAlexander Duyck 	else
6532f90b865SAlexander Duyck 		getall = 0;
6542f90b865SAlexander Duyck 
6552f90b865SAlexander Duyck 	for (i = DCB_PG_ATTR_BW_ID_0; i <= DCB_PG_ATTR_BW_ID_7; i++) {
6562f90b865SAlexander Duyck 		if (!getall && !pg_tb[i])
6572f90b865SAlexander Duyck 			continue;
6582f90b865SAlexander Duyck 
6592f90b865SAlexander Duyck 		tc_pct = DCB_ATTR_VALUE_UNDEFINED;
6602f90b865SAlexander Duyck 
6612f90b865SAlexander Duyck 		if (dir) {
6622f90b865SAlexander Duyck 			/* Rx */
6632f90b865SAlexander Duyck 			netdev->dcbnl_ops->getpgbwgcfgrx(netdev,
6642f90b865SAlexander Duyck 					i - DCB_PG_ATTR_BW_ID_0, &tc_pct);
6652f90b865SAlexander Duyck 		} else {
6662f90b865SAlexander Duyck 			/* Tx */
6672f90b865SAlexander Duyck 			netdev->dcbnl_ops->getpgbwgcfgtx(netdev,
6682f90b865SAlexander Duyck 					i - DCB_PG_ATTR_BW_ID_0, &tc_pct);
6692f90b865SAlexander Duyck 		}
6707be99413SThomas Graf 		ret = nla_put_u8(skb, i, tc_pct);
6712f90b865SAlexander Duyck 		if (ret)
6722f90b865SAlexander Duyck 			goto err_pg;
6732f90b865SAlexander Duyck 	}
6742f90b865SAlexander Duyck 
6757be99413SThomas Graf 	nla_nest_end(skb, pg_nest);
6762f90b865SAlexander Duyck 
6772f90b865SAlexander Duyck 	return 0;
6782f90b865SAlexander Duyck 
6792f90b865SAlexander Duyck err_param:
6807be99413SThomas Graf 	nla_nest_cancel(skb, param_nest);
6812f90b865SAlexander Duyck err_pg:
6827be99413SThomas Graf 	nla_nest_cancel(skb, pg_nest);
6833d1f4869SThomas Graf 
6843d1f4869SThomas Graf 	return -EMSGSIZE;
6852f90b865SAlexander Duyck }
6862f90b865SAlexander Duyck 
6877be99413SThomas Graf static int dcbnl_pgtx_getcfg(struct net_device *netdev, struct nlmsghdr *nlh,
6887be99413SThomas Graf 			     u32 seq, struct nlattr **tb, struct sk_buff *skb)
6892f90b865SAlexander Duyck {
6907be99413SThomas Graf 	return __dcbnl_pg_getcfg(netdev, nlh, tb, skb, 0);
6912f90b865SAlexander Duyck }
6922f90b865SAlexander Duyck 
6937be99413SThomas Graf static int dcbnl_pgrx_getcfg(struct net_device *netdev, struct nlmsghdr *nlh,
6947be99413SThomas Graf 			     u32 seq, struct nlattr **tb, struct sk_buff *skb)
6952f90b865SAlexander Duyck {
6967be99413SThomas Graf 	return __dcbnl_pg_getcfg(netdev, nlh, tb, skb, 1);
6972f90b865SAlexander Duyck }
6982f90b865SAlexander Duyck 
6997be99413SThomas Graf static int dcbnl_setstate(struct net_device *netdev, struct nlmsghdr *nlh,
7007be99413SThomas Graf 			  u32 seq, struct nlattr **tb, struct sk_buff *skb)
7012f90b865SAlexander Duyck {
7022f90b865SAlexander Duyck 	u8 value;
7032f90b865SAlexander Duyck 
7043d1f4869SThomas Graf 	if (!tb[DCB_ATTR_STATE])
7057be99413SThomas Graf 		return -EINVAL;
7062f90b865SAlexander Duyck 
7073d1f4869SThomas Graf 	if (!netdev->dcbnl_ops->setstate)
7083d1f4869SThomas Graf 		return -EOPNOTSUPP;
7093d1f4869SThomas Graf 
7102f90b865SAlexander Duyck 	value = nla_get_u8(tb[DCB_ATTR_STATE]);
7112f90b865SAlexander Duyck 
7127be99413SThomas Graf 	return nla_put_u8(skb, DCB_ATTR_STATE,
7137be99413SThomas Graf 			  netdev->dcbnl_ops->setstate(netdev, value));
7142f90b865SAlexander Duyck }
7152f90b865SAlexander Duyck 
7167be99413SThomas Graf static int dcbnl_setpfccfg(struct net_device *netdev, struct nlmsghdr *nlh,
7177be99413SThomas Graf 			   u32 seq, struct nlattr **tb, struct sk_buff *skb)
7182f90b865SAlexander Duyck {
7192f90b865SAlexander Duyck 	struct nlattr *data[DCB_PFC_UP_ATTR_MAX + 1];
7202f90b865SAlexander Duyck 	int i;
7213d1f4869SThomas Graf 	int ret;
7222f90b865SAlexander Duyck 	u8 value;
7232f90b865SAlexander Duyck 
7243d1f4869SThomas Graf 	if (!tb[DCB_ATTR_PFC_CFG])
7253d1f4869SThomas Graf 		return -EINVAL;
7263d1f4869SThomas Graf 
7273d1f4869SThomas Graf 	if (!netdev->dcbnl_ops->setpfccfg)
7283d1f4869SThomas Graf 		return -EOPNOTSUPP;
7292f90b865SAlexander Duyck 
7308cb08174SJohannes Berg 	ret = nla_parse_nested_deprecated(data, DCB_PFC_UP_ATTR_MAX,
7318cb08174SJohannes Berg 					  tb[DCB_ATTR_PFC_CFG],
7328cb08174SJohannes Berg 					  dcbnl_pfc_up_nest, NULL);
7332f90b865SAlexander Duyck 	if (ret)
7343d1f4869SThomas Graf 		return ret;
7352f90b865SAlexander Duyck 
7362f90b865SAlexander Duyck 	for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) {
7372f90b865SAlexander Duyck 		if (data[i] == NULL)
7382f90b865SAlexander Duyck 			continue;
7392f90b865SAlexander Duyck 		value = nla_get_u8(data[i]);
7402f90b865SAlexander Duyck 		netdev->dcbnl_ops->setpfccfg(netdev,
7412f90b865SAlexander Duyck 			data[i]->nla_type - DCB_PFC_UP_ATTR_0, value);
7422f90b865SAlexander Duyck 	}
7432f90b865SAlexander Duyck 
7447be99413SThomas Graf 	return nla_put_u8(skb, DCB_ATTR_PFC_CFG, 0);
7452f90b865SAlexander Duyck }
7462f90b865SAlexander Duyck 
7477be99413SThomas Graf static int dcbnl_setall(struct net_device *netdev, struct nlmsghdr *nlh,
7487be99413SThomas Graf 			u32 seq, struct nlattr **tb, struct sk_buff *skb)
7492f90b865SAlexander Duyck {
7503d1f4869SThomas Graf 	int ret;
7512f90b865SAlexander Duyck 
7523d1f4869SThomas Graf 	if (!tb[DCB_ATTR_SET_ALL])
7533d1f4869SThomas Graf 		return -EINVAL;
7543d1f4869SThomas Graf 
7553d1f4869SThomas Graf 	if (!netdev->dcbnl_ops->setall)
7563d1f4869SThomas Graf 		return -EOPNOTSUPP;
7572f90b865SAlexander Duyck 
7587be99413SThomas Graf 	ret = nla_put_u8(skb, DCB_ATTR_SET_ALL,
7597be99413SThomas Graf 			 netdev->dcbnl_ops->setall(netdev));
76008157984SJohn Fastabend 	dcbnl_cee_notify(netdev, RTM_SETDCB, DCB_CMD_SET_ALL, seq, 0);
7612f90b865SAlexander Duyck 
7622f90b865SAlexander Duyck 	return ret;
7632f90b865SAlexander Duyck }
7642f90b865SAlexander Duyck 
7657be99413SThomas Graf static int __dcbnl_pg_setcfg(struct net_device *netdev, struct nlmsghdr *nlh,
7667be99413SThomas Graf 			     u32 seq, struct nlattr **tb, struct sk_buff *skb,
7677be99413SThomas Graf 			     int dir)
7682f90b865SAlexander Duyck {
7692f90b865SAlexander Duyck 	struct nlattr *pg_tb[DCB_PG_ATTR_MAX + 1];
7702f90b865SAlexander Duyck 	struct nlattr *param_tb[DCB_TC_ATTR_PARAM_MAX + 1];
7713d1f4869SThomas Graf 	int ret;
7722f90b865SAlexander Duyck 	int i;
7732f90b865SAlexander Duyck 	u8 pgid;
7742f90b865SAlexander Duyck 	u8 up_map;
7752f90b865SAlexander Duyck 	u8 prio;
7762f90b865SAlexander Duyck 	u8 tc_pct;
7772f90b865SAlexander Duyck 
7783d1f4869SThomas Graf 	if (!tb[DCB_ATTR_PG_CFG])
7793d1f4869SThomas Graf 		return -EINVAL;
7803d1f4869SThomas Graf 
7813d1f4869SThomas Graf 	if (!netdev->dcbnl_ops->setpgtccfgtx ||
7822f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->setpgtccfgrx ||
7832f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->setpgbwgcfgtx ||
7842f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->setpgbwgcfgrx)
7853d1f4869SThomas Graf 		return -EOPNOTSUPP;
7862f90b865SAlexander Duyck 
7878cb08174SJohannes Berg 	ret = nla_parse_nested_deprecated(pg_tb, DCB_PG_ATTR_MAX,
7888cb08174SJohannes Berg 					  tb[DCB_ATTR_PG_CFG], dcbnl_pg_nest,
7898cb08174SJohannes Berg 					  NULL);
7902f90b865SAlexander Duyck 	if (ret)
7913d1f4869SThomas Graf 		return ret;
7922f90b865SAlexander Duyck 
7932f90b865SAlexander Duyck 	for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) {
7942f90b865SAlexander Duyck 		if (!pg_tb[i])
7952f90b865SAlexander Duyck 			continue;
7962f90b865SAlexander Duyck 
7978cb08174SJohannes Berg 		ret = nla_parse_nested_deprecated(param_tb,
7988cb08174SJohannes Berg 						  DCB_TC_ATTR_PARAM_MAX,
7998cb08174SJohannes Berg 						  pg_tb[i],
8008cb08174SJohannes Berg 						  dcbnl_tc_param_nest, NULL);
8012f90b865SAlexander Duyck 		if (ret)
8023d1f4869SThomas Graf 			return ret;
8032f90b865SAlexander Duyck 
8042f90b865SAlexander Duyck 		pgid = DCB_ATTR_VALUE_UNDEFINED;
8052f90b865SAlexander Duyck 		prio = DCB_ATTR_VALUE_UNDEFINED;
8062f90b865SAlexander Duyck 		tc_pct = DCB_ATTR_VALUE_UNDEFINED;
8072f90b865SAlexander Duyck 		up_map = DCB_ATTR_VALUE_UNDEFINED;
8082f90b865SAlexander Duyck 
8092f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO])
8102f90b865SAlexander Duyck 			prio =
8112f90b865SAlexander Duyck 			    nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO]);
8122f90b865SAlexander Duyck 
8132f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_PGID])
8142f90b865SAlexander Duyck 			pgid = nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_PGID]);
8152f90b865SAlexander Duyck 
8162f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_BW_PCT])
8172f90b865SAlexander Duyck 			tc_pct = nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_BW_PCT]);
8182f90b865SAlexander Duyck 
8192f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING])
8202f90b865SAlexander Duyck 			up_map =
8212f90b865SAlexander Duyck 			     nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING]);
8222f90b865SAlexander Duyck 
8232f90b865SAlexander Duyck 		/* dir: Tx = 0, Rx = 1 */
8242f90b865SAlexander Duyck 		if (dir) {
8252f90b865SAlexander Duyck 			/* Rx */
8262f90b865SAlexander Duyck 			netdev->dcbnl_ops->setpgtccfgrx(netdev,
8272f90b865SAlexander Duyck 				i - DCB_PG_ATTR_TC_0,
8282f90b865SAlexander Duyck 				prio, pgid, tc_pct, up_map);
8292f90b865SAlexander Duyck 		} else {
8302f90b865SAlexander Duyck 			/* Tx */
8312f90b865SAlexander Duyck 			netdev->dcbnl_ops->setpgtccfgtx(netdev,
8322f90b865SAlexander Duyck 				i - DCB_PG_ATTR_TC_0,
8332f90b865SAlexander Duyck 				prio, pgid, tc_pct, up_map);
8342f90b865SAlexander Duyck 		}
8352f90b865SAlexander Duyck 	}
8362f90b865SAlexander Duyck 
8372f90b865SAlexander Duyck 	for (i = DCB_PG_ATTR_BW_ID_0; i <= DCB_PG_ATTR_BW_ID_7; i++) {
8382f90b865SAlexander Duyck 		if (!pg_tb[i])
8392f90b865SAlexander Duyck 			continue;
8402f90b865SAlexander Duyck 
8412f90b865SAlexander Duyck 		tc_pct = nla_get_u8(pg_tb[i]);
8422f90b865SAlexander Duyck 
8432f90b865SAlexander Duyck 		/* dir: Tx = 0, Rx = 1 */
8442f90b865SAlexander Duyck 		if (dir) {
8452f90b865SAlexander Duyck 			/* Rx */
8462f90b865SAlexander Duyck 			netdev->dcbnl_ops->setpgbwgcfgrx(netdev,
8472f90b865SAlexander Duyck 					 i - DCB_PG_ATTR_BW_ID_0, tc_pct);
8482f90b865SAlexander Duyck 		} else {
8492f90b865SAlexander Duyck 			/* Tx */
8502f90b865SAlexander Duyck 			netdev->dcbnl_ops->setpgbwgcfgtx(netdev,
8512f90b865SAlexander Duyck 					 i - DCB_PG_ATTR_BW_ID_0, tc_pct);
8522f90b865SAlexander Duyck 		}
8532f90b865SAlexander Duyck 	}
8542f90b865SAlexander Duyck 
855bb1dfefdSJohn Fastabend 	return nla_put_u8(skb, DCB_ATTR_PG_CFG, 0);
8562f90b865SAlexander Duyck }
8572f90b865SAlexander Duyck 
8587be99413SThomas Graf static int dcbnl_pgtx_setcfg(struct net_device *netdev, struct nlmsghdr *nlh,
8597be99413SThomas Graf 			     u32 seq, struct nlattr **tb, struct sk_buff *skb)
8602f90b865SAlexander Duyck {
8617be99413SThomas Graf 	return __dcbnl_pg_setcfg(netdev, nlh, seq, tb, skb, 0);
8622f90b865SAlexander Duyck }
8632f90b865SAlexander Duyck 
8647be99413SThomas Graf static int dcbnl_pgrx_setcfg(struct net_device *netdev, struct nlmsghdr *nlh,
8657be99413SThomas Graf 			     u32 seq, struct nlattr **tb, struct sk_buff *skb)
8662f90b865SAlexander Duyck {
8677be99413SThomas Graf 	return __dcbnl_pg_setcfg(netdev, nlh, seq, tb, skb, 1);
8682f90b865SAlexander Duyck }
8692f90b865SAlexander Duyck 
8707be99413SThomas Graf static int dcbnl_bcn_getcfg(struct net_device *netdev, struct nlmsghdr *nlh,
8717be99413SThomas Graf 			    u32 seq, struct nlattr **tb, struct sk_buff *skb)
872859ee3c4SAlexander Duyck {
873859ee3c4SAlexander Duyck 	struct nlattr *bcn_nest;
874859ee3c4SAlexander Duyck 	struct nlattr *bcn_tb[DCB_BCN_ATTR_MAX + 1];
875859ee3c4SAlexander Duyck 	u8 value_byte;
876859ee3c4SAlexander Duyck 	u32 value_integer;
8773d1f4869SThomas Graf 	int ret;
878859ee3c4SAlexander Duyck 	bool getall = false;
879859ee3c4SAlexander Duyck 	int i;
880859ee3c4SAlexander Duyck 
8813d1f4869SThomas Graf 	if (!tb[DCB_ATTR_BCN])
8823d1f4869SThomas Graf 		return -EINVAL;
8833d1f4869SThomas Graf 
8843d1f4869SThomas Graf 	if (!netdev->dcbnl_ops->getbcnrp ||
885859ee3c4SAlexander Duyck 	    !netdev->dcbnl_ops->getbcncfg)
8863d1f4869SThomas Graf 		return -EOPNOTSUPP;
887859ee3c4SAlexander Duyck 
8888cb08174SJohannes Berg 	ret = nla_parse_nested_deprecated(bcn_tb, DCB_BCN_ATTR_MAX,
8898cb08174SJohannes Berg 					  tb[DCB_ATTR_BCN], dcbnl_bcn_nest,
8908cb08174SJohannes Berg 					  NULL);
891859ee3c4SAlexander Duyck 	if (ret)
8923d1f4869SThomas Graf 		return ret;
893859ee3c4SAlexander Duyck 
894ae0be8deSMichal Kubecek 	bcn_nest = nla_nest_start_noflag(skb, DCB_ATTR_BCN);
895859ee3c4SAlexander Duyck 	if (!bcn_nest)
8963d1f4869SThomas Graf 		return -EMSGSIZE;
897859ee3c4SAlexander Duyck 
898859ee3c4SAlexander Duyck 	if (bcn_tb[DCB_BCN_ATTR_ALL])
899859ee3c4SAlexander Duyck 		getall = true;
900859ee3c4SAlexander Duyck 
901859ee3c4SAlexander Duyck 	for (i = DCB_BCN_ATTR_RP_0; i <= DCB_BCN_ATTR_RP_7; i++) {
902859ee3c4SAlexander Duyck 		if (!getall && !bcn_tb[i])
903859ee3c4SAlexander Duyck 			continue;
904859ee3c4SAlexander Duyck 
905859ee3c4SAlexander Duyck 		netdev->dcbnl_ops->getbcnrp(netdev, i - DCB_BCN_ATTR_RP_0,
906859ee3c4SAlexander Duyck 		                            &value_byte);
9077be99413SThomas Graf 		ret = nla_put_u8(skb, i, value_byte);
908859ee3c4SAlexander Duyck 		if (ret)
909859ee3c4SAlexander Duyck 			goto err_bcn;
910859ee3c4SAlexander Duyck 	}
911859ee3c4SAlexander Duyck 
912f4314e81SDon Skidmore 	for (i = DCB_BCN_ATTR_BCNA_0; i <= DCB_BCN_ATTR_RI; i++) {
913859ee3c4SAlexander Duyck 		if (!getall && !bcn_tb[i])
914859ee3c4SAlexander Duyck 			continue;
915859ee3c4SAlexander Duyck 
916859ee3c4SAlexander Duyck 		netdev->dcbnl_ops->getbcncfg(netdev, i,
917859ee3c4SAlexander Duyck 		                             &value_integer);
9187be99413SThomas Graf 		ret = nla_put_u32(skb, i, value_integer);
919859ee3c4SAlexander Duyck 		if (ret)
920859ee3c4SAlexander Duyck 			goto err_bcn;
921859ee3c4SAlexander Duyck 	}
922859ee3c4SAlexander Duyck 
9237be99413SThomas Graf 	nla_nest_end(skb, bcn_nest);
924859ee3c4SAlexander Duyck 
925859ee3c4SAlexander Duyck 	return 0;
926859ee3c4SAlexander Duyck 
927859ee3c4SAlexander Duyck err_bcn:
9287be99413SThomas Graf 	nla_nest_cancel(skb, bcn_nest);
929859ee3c4SAlexander Duyck 	return ret;
930859ee3c4SAlexander Duyck }
931859ee3c4SAlexander Duyck 
9327be99413SThomas Graf static int dcbnl_bcn_setcfg(struct net_device *netdev, struct nlmsghdr *nlh,
9337be99413SThomas Graf 			    u32 seq, struct nlattr **tb, struct sk_buff *skb)
934859ee3c4SAlexander Duyck {
935859ee3c4SAlexander Duyck 	struct nlattr *data[DCB_BCN_ATTR_MAX + 1];
936859ee3c4SAlexander Duyck 	int i;
9373d1f4869SThomas Graf 	int ret;
938859ee3c4SAlexander Duyck 	u8 value_byte;
939859ee3c4SAlexander Duyck 	u32 value_int;
940859ee3c4SAlexander Duyck 
9413d1f4869SThomas Graf 	if (!tb[DCB_ATTR_BCN])
9423d1f4869SThomas Graf 		return -EINVAL;
9433d1f4869SThomas Graf 
9443d1f4869SThomas Graf 	if (!netdev->dcbnl_ops->setbcncfg ||
945f64f9e71SJoe Perches 	    !netdev->dcbnl_ops->setbcnrp)
9463d1f4869SThomas Graf 		return -EOPNOTSUPP;
947859ee3c4SAlexander Duyck 
9488cb08174SJohannes Berg 	ret = nla_parse_nested_deprecated(data, DCB_BCN_ATTR_MAX,
9498cb08174SJohannes Berg 					  tb[DCB_ATTR_BCN], dcbnl_pfc_up_nest,
9508cb08174SJohannes Berg 					  NULL);
951859ee3c4SAlexander Duyck 	if (ret)
9523d1f4869SThomas Graf 		return ret;
953859ee3c4SAlexander Duyck 
954859ee3c4SAlexander Duyck 	for (i = DCB_BCN_ATTR_RP_0; i <= DCB_BCN_ATTR_RP_7; i++) {
955859ee3c4SAlexander Duyck 		if (data[i] == NULL)
956859ee3c4SAlexander Duyck 			continue;
957859ee3c4SAlexander Duyck 		value_byte = nla_get_u8(data[i]);
958859ee3c4SAlexander Duyck 		netdev->dcbnl_ops->setbcnrp(netdev,
959859ee3c4SAlexander Duyck 			data[i]->nla_type - DCB_BCN_ATTR_RP_0, value_byte);
960859ee3c4SAlexander Duyck 	}
961859ee3c4SAlexander Duyck 
962f4314e81SDon Skidmore 	for (i = DCB_BCN_ATTR_BCNA_0; i <= DCB_BCN_ATTR_RI; i++) {
963859ee3c4SAlexander Duyck 		if (data[i] == NULL)
964859ee3c4SAlexander Duyck 			continue;
965859ee3c4SAlexander Duyck 		value_int = nla_get_u32(data[i]);
966859ee3c4SAlexander Duyck 		netdev->dcbnl_ops->setbcncfg(netdev,
967859ee3c4SAlexander Duyck 	                                     i, value_int);
968859ee3c4SAlexander Duyck 	}
969859ee3c4SAlexander Duyck 
9703d1f4869SThomas Graf 	return nla_put_u8(skb, DCB_ATTR_BCN, 0);
971859ee3c4SAlexander Duyck }
972859ee3c4SAlexander Duyck 
973dc6ed1dfSShmulik Ravid static int dcbnl_build_peer_app(struct net_device *netdev, struct sk_buff* skb,
974dc6ed1dfSShmulik Ravid 				int app_nested_type, int app_info_type,
975dc6ed1dfSShmulik Ravid 				int app_entry_type)
976eed84713SShmulik Ravid {
977eed84713SShmulik Ravid 	struct dcb_peer_app_info info;
978eed84713SShmulik Ravid 	struct dcb_app *table = NULL;
979eed84713SShmulik Ravid 	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
980eed84713SShmulik Ravid 	u16 app_count;
981eed84713SShmulik Ravid 	int err;
982eed84713SShmulik Ravid 
983eed84713SShmulik Ravid 
984eed84713SShmulik Ravid 	/**
985eed84713SShmulik Ravid 	 * retrieve the peer app configuration form the driver. If the driver
986eed84713SShmulik Ravid 	 * handlers fail exit without doing anything
987eed84713SShmulik Ravid 	 */
988eed84713SShmulik Ravid 	err = ops->peer_getappinfo(netdev, &info, &app_count);
989eed84713SShmulik Ravid 	if (!err && app_count) {
9906da2ec56SKees Cook 		table = kmalloc_array(app_count, sizeof(struct dcb_app),
9916da2ec56SKees Cook 				      GFP_KERNEL);
992eed84713SShmulik Ravid 		if (!table)
993eed84713SShmulik Ravid 			return -ENOMEM;
994eed84713SShmulik Ravid 
995eed84713SShmulik Ravid 		err = ops->peer_getapptable(netdev, table);
996eed84713SShmulik Ravid 	}
997eed84713SShmulik Ravid 
998eed84713SShmulik Ravid 	if (!err) {
999eed84713SShmulik Ravid 		u16 i;
1000eed84713SShmulik Ravid 		struct nlattr *app;
1001eed84713SShmulik Ravid 
1002eed84713SShmulik Ravid 		/**
1003eed84713SShmulik Ravid 		 * build the message, from here on the only possible failure
1004eed84713SShmulik Ravid 		 * is due to the skb size
1005eed84713SShmulik Ravid 		 */
1006eed84713SShmulik Ravid 		err = -EMSGSIZE;
1007eed84713SShmulik Ravid 
1008ae0be8deSMichal Kubecek 		app = nla_nest_start_noflag(skb, app_nested_type);
1009eed84713SShmulik Ravid 		if (!app)
1010eed84713SShmulik Ravid 			goto nla_put_failure;
1011eed84713SShmulik Ravid 
10121eb4c977SDavid S. Miller 		if (app_info_type &&
10131eb4c977SDavid S. Miller 		    nla_put(skb, app_info_type, sizeof(info), &info))
10141eb4c977SDavid S. Miller 			goto nla_put_failure;
1015dc6ed1dfSShmulik Ravid 
10161eb4c977SDavid S. Miller 		for (i = 0; i < app_count; i++) {
10171eb4c977SDavid S. Miller 			if (nla_put(skb, app_entry_type, sizeof(struct dcb_app),
10181eb4c977SDavid S. Miller 				    &table[i]))
10191eb4c977SDavid S. Miller 				goto nla_put_failure;
10201eb4c977SDavid S. Miller 		}
1021eed84713SShmulik Ravid 		nla_nest_end(skb, app);
1022eed84713SShmulik Ravid 	}
1023eed84713SShmulik Ravid 	err = 0;
1024eed84713SShmulik Ravid 
1025eed84713SShmulik Ravid nla_put_failure:
1026eed84713SShmulik Ravid 	kfree(table);
1027eed84713SShmulik Ravid 	return err;
1028eed84713SShmulik Ravid }
10293e29027aSJohn Fastabend 
1030c9368247SShani Michaeli /* Handle IEEE 802.1Qaz/802.1Qau/802.1Qbb GET commands. */
1031314b4778SJohn Fastabend static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev)
10323e29027aSJohn Fastabend {
10339ab933abSJohn Fastabend 	struct nlattr *ieee, *app;
10349ab933abSJohn Fastabend 	struct dcb_app_type *itr;
10353e29027aSJohn Fastabend 	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
1036c7797bafSJohn Fastabend 	int dcbx;
10373d1f4869SThomas Graf 	int err;
10383e29027aSJohn Fastabend 
10391eb4c977SDavid S. Miller 	if (nla_put_string(skb, DCB_ATTR_IFNAME, netdev->name))
10403d1f4869SThomas Graf 		return -EMSGSIZE;
10413d1f4869SThomas Graf 
1042ae0be8deSMichal Kubecek 	ieee = nla_nest_start_noflag(skb, DCB_ATTR_IEEE);
10433e29027aSJohn Fastabend 	if (!ieee)
10443d1f4869SThomas Graf 		return -EMSGSIZE;
10453e29027aSJohn Fastabend 
10463e29027aSJohn Fastabend 	if (ops->ieee_getets) {
10473e29027aSJohn Fastabend 		struct ieee_ets ets;
104829cd8ae0SMathias Krause 		memset(&ets, 0, sizeof(ets));
10493e29027aSJohn Fastabend 		err = ops->ieee_getets(netdev, &ets);
10501eb4c977SDavid S. Miller 		if (!err &&
10511eb4c977SDavid S. Miller 		    nla_put(skb, DCB_ATTR_IEEE_ETS, sizeof(ets), &ets))
10523d1f4869SThomas Graf 			return -EMSGSIZE;
10533e29027aSJohn Fastabend 	}
10543e29027aSJohn Fastabend 
105508f10affSAmir Vadai 	if (ops->ieee_getmaxrate) {
105608f10affSAmir Vadai 		struct ieee_maxrate maxrate;
105729cd8ae0SMathias Krause 		memset(&maxrate, 0, sizeof(maxrate));
105808f10affSAmir Vadai 		err = ops->ieee_getmaxrate(netdev, &maxrate);
105908f10affSAmir Vadai 		if (!err) {
106008f10affSAmir Vadai 			err = nla_put(skb, DCB_ATTR_IEEE_MAXRATE,
106108f10affSAmir Vadai 				      sizeof(maxrate), &maxrate);
106208f10affSAmir Vadai 			if (err)
10633d1f4869SThomas Graf 				return -EMSGSIZE;
106408f10affSAmir Vadai 		}
106508f10affSAmir Vadai 	}
106608f10affSAmir Vadai 
1067c9368247SShani Michaeli 	if (ops->ieee_getqcn) {
1068c9368247SShani Michaeli 		struct ieee_qcn qcn;
1069c9368247SShani Michaeli 
1070c9368247SShani Michaeli 		memset(&qcn, 0, sizeof(qcn));
1071c9368247SShani Michaeli 		err = ops->ieee_getqcn(netdev, &qcn);
1072c9368247SShani Michaeli 		if (!err) {
1073c9368247SShani Michaeli 			err = nla_put(skb, DCB_ATTR_IEEE_QCN,
1074c9368247SShani Michaeli 				      sizeof(qcn), &qcn);
1075c9368247SShani Michaeli 			if (err)
1076c9368247SShani Michaeli 				return -EMSGSIZE;
1077c9368247SShani Michaeli 		}
1078c9368247SShani Michaeli 	}
1079c9368247SShani Michaeli 
1080c9368247SShani Michaeli 	if (ops->ieee_getqcnstats) {
1081c9368247SShani Michaeli 		struct ieee_qcn_stats qcn_stats;
1082c9368247SShani Michaeli 
1083c9368247SShani Michaeli 		memset(&qcn_stats, 0, sizeof(qcn_stats));
1084c9368247SShani Michaeli 		err = ops->ieee_getqcnstats(netdev, &qcn_stats);
1085c9368247SShani Michaeli 		if (!err) {
1086c9368247SShani Michaeli 			err = nla_put(skb, DCB_ATTR_IEEE_QCN_STATS,
1087c9368247SShani Michaeli 				      sizeof(qcn_stats), &qcn_stats);
1088c9368247SShani Michaeli 			if (err)
1089c9368247SShani Michaeli 				return -EMSGSIZE;
1090c9368247SShani Michaeli 		}
1091c9368247SShani Michaeli 	}
1092c9368247SShani Michaeli 
10933e29027aSJohn Fastabend 	if (ops->ieee_getpfc) {
10943e29027aSJohn Fastabend 		struct ieee_pfc pfc;
109529cd8ae0SMathias Krause 		memset(&pfc, 0, sizeof(pfc));
10963e29027aSJohn Fastabend 		err = ops->ieee_getpfc(netdev, &pfc);
10971eb4c977SDavid S. Miller 		if (!err &&
10981eb4c977SDavid S. Miller 		    nla_put(skb, DCB_ATTR_IEEE_PFC, sizeof(pfc), &pfc))
10993d1f4869SThomas Graf 			return -EMSGSIZE;
11003e29027aSJohn Fastabend 	}
11013e29027aSJohn Fastabend 
1102e549f6f9SHuy Nguyen 	if (ops->dcbnl_getbuffer) {
1103e549f6f9SHuy Nguyen 		struct dcbnl_buffer buffer;
1104e549f6f9SHuy Nguyen 
1105e549f6f9SHuy Nguyen 		memset(&buffer, 0, sizeof(buffer));
1106e549f6f9SHuy Nguyen 		err = ops->dcbnl_getbuffer(netdev, &buffer);
1107e549f6f9SHuy Nguyen 		if (!err &&
1108e549f6f9SHuy Nguyen 		    nla_put(skb, DCB_ATTR_DCB_BUFFER, sizeof(buffer), &buffer))
1109e549f6f9SHuy Nguyen 			return -EMSGSIZE;
1110e549f6f9SHuy Nguyen 	}
1111e549f6f9SHuy Nguyen 
1112ae0be8deSMichal Kubecek 	app = nla_nest_start_noflag(skb, DCB_ATTR_IEEE_APP_TABLE);
11139ab933abSJohn Fastabend 	if (!app)
11143d1f4869SThomas Graf 		return -EMSGSIZE;
11159ab933abSJohn Fastabend 
111652cff74eSAnish Bhatt 	spin_lock_bh(&dcb_lock);
11179ab933abSJohn Fastabend 	list_for_each_entry(itr, &dcb_app_list, list) {
1118e290ed81SMark Rustad 		if (itr->ifindex == netdev->ifindex) {
111970bfa2d2SDan Carpenter 			err = nla_put(skb, DCB_ATTR_IEEE_APP, sizeof(itr->app),
112070bfa2d2SDan Carpenter 					 &itr->app);
112170bfa2d2SDan Carpenter 			if (err) {
112252cff74eSAnish Bhatt 				spin_unlock_bh(&dcb_lock);
11233d1f4869SThomas Graf 				return -EMSGSIZE;
112470bfa2d2SDan Carpenter 			}
112570bfa2d2SDan Carpenter 		}
11269ab933abSJohn Fastabend 	}
1127c7797bafSJohn Fastabend 
1128c7797bafSJohn Fastabend 	if (netdev->dcbnl_ops->getdcbx)
1129c7797bafSJohn Fastabend 		dcbx = netdev->dcbnl_ops->getdcbx(netdev);
1130c7797bafSJohn Fastabend 	else
1131c7797bafSJohn Fastabend 		dcbx = -EOPNOTSUPP;
1132c7797bafSJohn Fastabend 
113352cff74eSAnish Bhatt 	spin_unlock_bh(&dcb_lock);
11349ab933abSJohn Fastabend 	nla_nest_end(skb, app);
11359ab933abSJohn Fastabend 
1136eed84713SShmulik Ravid 	/* get peer info if available */
1137eed84713SShmulik Ravid 	if (ops->ieee_peer_getets) {
1138eed84713SShmulik Ravid 		struct ieee_ets ets;
113929cd8ae0SMathias Krause 		memset(&ets, 0, sizeof(ets));
1140eed84713SShmulik Ravid 		err = ops->ieee_peer_getets(netdev, &ets);
11411eb4c977SDavid S. Miller 		if (!err &&
11421eb4c977SDavid S. Miller 		    nla_put(skb, DCB_ATTR_IEEE_PEER_ETS, sizeof(ets), &ets))
11433d1f4869SThomas Graf 			return -EMSGSIZE;
1144eed84713SShmulik Ravid 	}
1145eed84713SShmulik Ravid 
1146eed84713SShmulik Ravid 	if (ops->ieee_peer_getpfc) {
1147eed84713SShmulik Ravid 		struct ieee_pfc pfc;
114829cd8ae0SMathias Krause 		memset(&pfc, 0, sizeof(pfc));
1149eed84713SShmulik Ravid 		err = ops->ieee_peer_getpfc(netdev, &pfc);
11501eb4c977SDavid S. Miller 		if (!err &&
11511eb4c977SDavid S. Miller 		    nla_put(skb, DCB_ATTR_IEEE_PEER_PFC, sizeof(pfc), &pfc))
11523d1f4869SThomas Graf 			return -EMSGSIZE;
1153eed84713SShmulik Ravid 	}
1154eed84713SShmulik Ravid 
1155eed84713SShmulik Ravid 	if (ops->peer_getappinfo && ops->peer_getapptable) {
1156dc6ed1dfSShmulik Ravid 		err = dcbnl_build_peer_app(netdev, skb,
1157dc6ed1dfSShmulik Ravid 					   DCB_ATTR_IEEE_PEER_APP,
1158dc6ed1dfSShmulik Ravid 					   DCB_ATTR_IEEE_APP_UNSPEC,
1159dc6ed1dfSShmulik Ravid 					   DCB_ATTR_IEEE_APP);
1160eed84713SShmulik Ravid 		if (err)
11613d1f4869SThomas Graf 			return -EMSGSIZE;
1162eed84713SShmulik Ravid 	}
1163eed84713SShmulik Ravid 
11643e29027aSJohn Fastabend 	nla_nest_end(skb, ieee);
1165c7797bafSJohn Fastabend 	if (dcbx >= 0) {
1166c7797bafSJohn Fastabend 		err = nla_put_u8(skb, DCB_ATTR_DCBX, dcbx);
1167c7797bafSJohn Fastabend 		if (err)
11683d1f4869SThomas Graf 			return -EMSGSIZE;
1169c7797bafSJohn Fastabend 	}
11703e29027aSJohn Fastabend 
1171314b4778SJohn Fastabend 	return 0;
11723e29027aSJohn Fastabend }
11733e29027aSJohn Fastabend 
11745b7f7626SShmulik Ravid static int dcbnl_cee_pg_fill(struct sk_buff *skb, struct net_device *dev,
11755b7f7626SShmulik Ravid 			     int dir)
11765b7f7626SShmulik Ravid {
11775b7f7626SShmulik Ravid 	u8 pgid, up_map, prio, tc_pct;
11785b7f7626SShmulik Ravid 	const struct dcbnl_rtnl_ops *ops = dev->dcbnl_ops;
11795b7f7626SShmulik Ravid 	int i = dir ? DCB_ATTR_CEE_TX_PG : DCB_ATTR_CEE_RX_PG;
1180ae0be8deSMichal Kubecek 	struct nlattr *pg = nla_nest_start_noflag(skb, i);
11815b7f7626SShmulik Ravid 
11825b7f7626SShmulik Ravid 	if (!pg)
11833d1f4869SThomas Graf 		return -EMSGSIZE;
11845b7f7626SShmulik Ravid 
11855b7f7626SShmulik Ravid 	for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) {
1186ae0be8deSMichal Kubecek 		struct nlattr *tc_nest = nla_nest_start_noflag(skb, i);
11875b7f7626SShmulik Ravid 
11885b7f7626SShmulik Ravid 		if (!tc_nest)
11893d1f4869SThomas Graf 			return -EMSGSIZE;
11905b7f7626SShmulik Ravid 
11915b7f7626SShmulik Ravid 		pgid = DCB_ATTR_VALUE_UNDEFINED;
11925b7f7626SShmulik Ravid 		prio = DCB_ATTR_VALUE_UNDEFINED;
11935b7f7626SShmulik Ravid 		tc_pct = DCB_ATTR_VALUE_UNDEFINED;
11945b7f7626SShmulik Ravid 		up_map = DCB_ATTR_VALUE_UNDEFINED;
11955b7f7626SShmulik Ravid 
11965b7f7626SShmulik Ravid 		if (!dir)
11975b7f7626SShmulik Ravid 			ops->getpgtccfgrx(dev, i - DCB_PG_ATTR_TC_0,
11985b7f7626SShmulik Ravid 					  &prio, &pgid, &tc_pct, &up_map);
11995b7f7626SShmulik Ravid 		else
12005b7f7626SShmulik Ravid 			ops->getpgtccfgtx(dev, i - DCB_PG_ATTR_TC_0,
12015b7f7626SShmulik Ravid 					  &prio, &pgid, &tc_pct, &up_map);
12025b7f7626SShmulik Ravid 
12031eb4c977SDavid S. Miller 		if (nla_put_u8(skb, DCB_TC_ATTR_PARAM_PGID, pgid) ||
12041eb4c977SDavid S. Miller 		    nla_put_u8(skb, DCB_TC_ATTR_PARAM_UP_MAPPING, up_map) ||
12051eb4c977SDavid S. Miller 		    nla_put_u8(skb, DCB_TC_ATTR_PARAM_STRICT_PRIO, prio) ||
12061eb4c977SDavid S. Miller 		    nla_put_u8(skb, DCB_TC_ATTR_PARAM_BW_PCT, tc_pct))
12073d1f4869SThomas Graf 			return -EMSGSIZE;
12085b7f7626SShmulik Ravid 		nla_nest_end(skb, tc_nest);
12095b7f7626SShmulik Ravid 	}
12105b7f7626SShmulik Ravid 
12115b7f7626SShmulik Ravid 	for (i = DCB_PG_ATTR_BW_ID_0; i <= DCB_PG_ATTR_BW_ID_7; i++) {
12125b7f7626SShmulik Ravid 		tc_pct = DCB_ATTR_VALUE_UNDEFINED;
12135b7f7626SShmulik Ravid 
12145b7f7626SShmulik Ravid 		if (!dir)
12155b7f7626SShmulik Ravid 			ops->getpgbwgcfgrx(dev, i - DCB_PG_ATTR_BW_ID_0,
12165b7f7626SShmulik Ravid 					   &tc_pct);
12175b7f7626SShmulik Ravid 		else
12185b7f7626SShmulik Ravid 			ops->getpgbwgcfgtx(dev, i - DCB_PG_ATTR_BW_ID_0,
12195b7f7626SShmulik Ravid 					   &tc_pct);
12201eb4c977SDavid S. Miller 		if (nla_put_u8(skb, i, tc_pct))
12213d1f4869SThomas Graf 			return -EMSGSIZE;
12225b7f7626SShmulik Ravid 	}
12235b7f7626SShmulik Ravid 	nla_nest_end(skb, pg);
12245b7f7626SShmulik Ravid 	return 0;
12255b7f7626SShmulik Ravid }
12265b7f7626SShmulik Ravid 
12275b7f7626SShmulik Ravid static int dcbnl_cee_fill(struct sk_buff *skb, struct net_device *netdev)
12285b7f7626SShmulik Ravid {
12295b7f7626SShmulik Ravid 	struct nlattr *cee, *app;
12305b7f7626SShmulik Ravid 	struct dcb_app_type *itr;
12315b7f7626SShmulik Ravid 	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
12325b7f7626SShmulik Ravid 	int dcbx, i, err = -EMSGSIZE;
12335b7f7626SShmulik Ravid 	u8 value;
12345b7f7626SShmulik Ravid 
12351eb4c977SDavid S. Miller 	if (nla_put_string(skb, DCB_ATTR_IFNAME, netdev->name))
12361eb4c977SDavid S. Miller 		goto nla_put_failure;
1237ae0be8deSMichal Kubecek 	cee = nla_nest_start_noflag(skb, DCB_ATTR_CEE);
12385b7f7626SShmulik Ravid 	if (!cee)
12395b7f7626SShmulik Ravid 		goto nla_put_failure;
12405b7f7626SShmulik Ravid 
12415b7f7626SShmulik Ravid 	/* local pg */
12425b7f7626SShmulik Ravid 	if (ops->getpgtccfgtx && ops->getpgbwgcfgtx) {
12435b7f7626SShmulik Ravid 		err = dcbnl_cee_pg_fill(skb, netdev, 1);
12445b7f7626SShmulik Ravid 		if (err)
12455b7f7626SShmulik Ravid 			goto nla_put_failure;
12465b7f7626SShmulik Ravid 	}
12475b7f7626SShmulik Ravid 
12485b7f7626SShmulik Ravid 	if (ops->getpgtccfgrx && ops->getpgbwgcfgrx) {
12495b7f7626SShmulik Ravid 		err = dcbnl_cee_pg_fill(skb, netdev, 0);
12505b7f7626SShmulik Ravid 		if (err)
12515b7f7626SShmulik Ravid 			goto nla_put_failure;
12525b7f7626SShmulik Ravid 	}
12535b7f7626SShmulik Ravid 
12545b7f7626SShmulik Ravid 	/* local pfc */
12555b7f7626SShmulik Ravid 	if (ops->getpfccfg) {
1256ae0be8deSMichal Kubecek 		struct nlattr *pfc_nest = nla_nest_start_noflag(skb,
1257ae0be8deSMichal Kubecek 								DCB_ATTR_CEE_PFC);
12585b7f7626SShmulik Ravid 
12595b7f7626SShmulik Ravid 		if (!pfc_nest)
12605b7f7626SShmulik Ravid 			goto nla_put_failure;
12615b7f7626SShmulik Ravid 
12625b7f7626SShmulik Ravid 		for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) {
12635b7f7626SShmulik Ravid 			ops->getpfccfg(netdev, i - DCB_PFC_UP_ATTR_0, &value);
12641eb4c977SDavid S. Miller 			if (nla_put_u8(skb, i, value))
12651eb4c977SDavid S. Miller 				goto nla_put_failure;
12665b7f7626SShmulik Ravid 		}
12675b7f7626SShmulik Ravid 		nla_nest_end(skb, pfc_nest);
12685b7f7626SShmulik Ravid 	}
12695b7f7626SShmulik Ravid 
12705b7f7626SShmulik Ravid 	/* local app */
127152cff74eSAnish Bhatt 	spin_lock_bh(&dcb_lock);
1272ae0be8deSMichal Kubecek 	app = nla_nest_start_noflag(skb, DCB_ATTR_CEE_APP_TABLE);
12735b7f7626SShmulik Ravid 	if (!app)
127440f5d72aSDan Carpenter 		goto dcb_unlock;
12755b7f7626SShmulik Ravid 
12765b7f7626SShmulik Ravid 	list_for_each_entry(itr, &dcb_app_list, list) {
1277e290ed81SMark Rustad 		if (itr->ifindex == netdev->ifindex) {
1278ae0be8deSMichal Kubecek 			struct nlattr *app_nest = nla_nest_start_noflag(skb,
12795b7f7626SShmulik Ravid 									DCB_ATTR_APP);
12805b7f7626SShmulik Ravid 			if (!app_nest)
12815b7f7626SShmulik Ravid 				goto dcb_unlock;
12825b7f7626SShmulik Ravid 
12835b7f7626SShmulik Ravid 			err = nla_put_u8(skb, DCB_APP_ATTR_IDTYPE,
12845b7f7626SShmulik Ravid 					 itr->app.selector);
12855b7f7626SShmulik Ravid 			if (err)
12865b7f7626SShmulik Ravid 				goto dcb_unlock;
12875b7f7626SShmulik Ravid 
12885b7f7626SShmulik Ravid 			err = nla_put_u16(skb, DCB_APP_ATTR_ID,
12895b7f7626SShmulik Ravid 					  itr->app.protocol);
12905b7f7626SShmulik Ravid 			if (err)
12915b7f7626SShmulik Ravid 				goto dcb_unlock;
12925b7f7626SShmulik Ravid 
12935b7f7626SShmulik Ravid 			err = nla_put_u8(skb, DCB_APP_ATTR_PRIORITY,
12945b7f7626SShmulik Ravid 					 itr->app.priority);
12955b7f7626SShmulik Ravid 			if (err)
12965b7f7626SShmulik Ravid 				goto dcb_unlock;
12975b7f7626SShmulik Ravid 
12985b7f7626SShmulik Ravid 			nla_nest_end(skb, app_nest);
12995b7f7626SShmulik Ravid 		}
13005b7f7626SShmulik Ravid 	}
13015b7f7626SShmulik Ravid 	nla_nest_end(skb, app);
13025b7f7626SShmulik Ravid 
13035b7f7626SShmulik Ravid 	if (netdev->dcbnl_ops->getdcbx)
13045b7f7626SShmulik Ravid 		dcbx = netdev->dcbnl_ops->getdcbx(netdev);
13055b7f7626SShmulik Ravid 	else
13065b7f7626SShmulik Ravid 		dcbx = -EOPNOTSUPP;
13075b7f7626SShmulik Ravid 
130852cff74eSAnish Bhatt 	spin_unlock_bh(&dcb_lock);
13095b7f7626SShmulik Ravid 
13105b7f7626SShmulik Ravid 	/* features flags */
13115b7f7626SShmulik Ravid 	if (ops->getfeatcfg) {
1312ae0be8deSMichal Kubecek 		struct nlattr *feat = nla_nest_start_noflag(skb,
1313ae0be8deSMichal Kubecek 							    DCB_ATTR_CEE_FEAT);
13145b7f7626SShmulik Ravid 		if (!feat)
13155b7f7626SShmulik Ravid 			goto nla_put_failure;
13165b7f7626SShmulik Ravid 
13175b7f7626SShmulik Ravid 		for (i = DCB_FEATCFG_ATTR_ALL + 1; i <= DCB_FEATCFG_ATTR_MAX;
13185b7f7626SShmulik Ravid 		     i++)
13191eb4c977SDavid S. Miller 			if (!ops->getfeatcfg(netdev, i, &value) &&
13201eb4c977SDavid S. Miller 			    nla_put_u8(skb, i, value))
13211eb4c977SDavid S. Miller 				goto nla_put_failure;
13225b7f7626SShmulik Ravid 
13235b7f7626SShmulik Ravid 		nla_nest_end(skb, feat);
13245b7f7626SShmulik Ravid 	}
13255b7f7626SShmulik Ravid 
13265b7f7626SShmulik Ravid 	/* peer info if available */
13275b7f7626SShmulik Ravid 	if (ops->cee_peer_getpg) {
13285b7f7626SShmulik Ravid 		struct cee_pg pg;
132929cd8ae0SMathias Krause 		memset(&pg, 0, sizeof(pg));
13305b7f7626SShmulik Ravid 		err = ops->cee_peer_getpg(netdev, &pg);
13311eb4c977SDavid S. Miller 		if (!err &&
13321eb4c977SDavid S. Miller 		    nla_put(skb, DCB_ATTR_CEE_PEER_PG, sizeof(pg), &pg))
13331eb4c977SDavid S. Miller 			goto nla_put_failure;
13345b7f7626SShmulik Ravid 	}
13355b7f7626SShmulik Ravid 
13365b7f7626SShmulik Ravid 	if (ops->cee_peer_getpfc) {
13375b7f7626SShmulik Ravid 		struct cee_pfc pfc;
133829cd8ae0SMathias Krause 		memset(&pfc, 0, sizeof(pfc));
13395b7f7626SShmulik Ravid 		err = ops->cee_peer_getpfc(netdev, &pfc);
13401eb4c977SDavid S. Miller 		if (!err &&
13411eb4c977SDavid S. Miller 		    nla_put(skb, DCB_ATTR_CEE_PEER_PFC, sizeof(pfc), &pfc))
13421eb4c977SDavid S. Miller 			goto nla_put_failure;
13435b7f7626SShmulik Ravid 	}
13445b7f7626SShmulik Ravid 
13455b7f7626SShmulik Ravid 	if (ops->peer_getappinfo && ops->peer_getapptable) {
13465b7f7626SShmulik Ravid 		err = dcbnl_build_peer_app(netdev, skb,
13475b7f7626SShmulik Ravid 					   DCB_ATTR_CEE_PEER_APP_TABLE,
13485b7f7626SShmulik Ravid 					   DCB_ATTR_CEE_PEER_APP_INFO,
13495b7f7626SShmulik Ravid 					   DCB_ATTR_CEE_PEER_APP);
13505b7f7626SShmulik Ravid 		if (err)
13515b7f7626SShmulik Ravid 			goto nla_put_failure;
13525b7f7626SShmulik Ravid 	}
13535b7f7626SShmulik Ravid 	nla_nest_end(skb, cee);
13545b7f7626SShmulik Ravid 
13555b7f7626SShmulik Ravid 	/* DCBX state */
13565b7f7626SShmulik Ravid 	if (dcbx >= 0) {
13575b7f7626SShmulik Ravid 		err = nla_put_u8(skb, DCB_ATTR_DCBX, dcbx);
13585b7f7626SShmulik Ravid 		if (err)
13595b7f7626SShmulik Ravid 			goto nla_put_failure;
13605b7f7626SShmulik Ravid 	}
13615b7f7626SShmulik Ravid 	return 0;
13625b7f7626SShmulik Ravid 
13635b7f7626SShmulik Ravid dcb_unlock:
136452cff74eSAnish Bhatt 	spin_unlock_bh(&dcb_lock);
13655b7f7626SShmulik Ravid nla_put_failure:
1366c66ebf2dSPan Bian 	err = -EMSGSIZE;
13675b7f7626SShmulik Ravid 	return err;
13685b7f7626SShmulik Ravid }
13695b7f7626SShmulik Ravid 
13705b7f7626SShmulik Ravid static int dcbnl_notify(struct net_device *dev, int event, int cmd,
137115e47304SEric W. Biederman 			u32 seq, u32 portid, int dcbx_ver)
1372314b4778SJohn Fastabend {
1373314b4778SJohn Fastabend 	struct net *net = dev_net(dev);
1374314b4778SJohn Fastabend 	struct sk_buff *skb;
1375314b4778SJohn Fastabend 	struct nlmsghdr *nlh;
1376314b4778SJohn Fastabend 	const struct dcbnl_rtnl_ops *ops = dev->dcbnl_ops;
1377314b4778SJohn Fastabend 	int err;
1378314b4778SJohn Fastabend 
1379314b4778SJohn Fastabend 	if (!ops)
1380314b4778SJohn Fastabend 		return -EOPNOTSUPP;
1381314b4778SJohn Fastabend 
138215e47304SEric W. Biederman 	skb = dcbnl_newmsg(event, cmd, portid, seq, 0, &nlh);
1383314b4778SJohn Fastabend 	if (!skb)
1384b923cda9SZheng Yongjun 		return -ENOMEM;
1385314b4778SJohn Fastabend 
13865b7f7626SShmulik Ravid 	if (dcbx_ver == DCB_CAP_DCBX_VER_IEEE)
1387314b4778SJohn Fastabend 		err = dcbnl_ieee_fill(skb, dev);
13885b7f7626SShmulik Ravid 	else
13895b7f7626SShmulik Ravid 		err = dcbnl_cee_fill(skb, dev);
13905b7f7626SShmulik Ravid 
1391314b4778SJohn Fastabend 	if (err < 0) {
1392314b4778SJohn Fastabend 		/* Report error to broadcast listeners */
1393ab6d4707SThomas Graf 		nlmsg_free(skb);
1394314b4778SJohn Fastabend 		rtnl_set_sk_err(net, RTNLGRP_DCB, err);
1395314b4778SJohn Fastabend 	} else {
1396314b4778SJohn Fastabend 		/* End nlmsg and notify broadcast listeners */
1397314b4778SJohn Fastabend 		nlmsg_end(skb, nlh);
1398314b4778SJohn Fastabend 		rtnl_notify(skb, net, 0, RTNLGRP_DCB, NULL, GFP_KERNEL);
1399314b4778SJohn Fastabend 	}
1400314b4778SJohn Fastabend 
1401314b4778SJohn Fastabend 	return err;
1402314b4778SJohn Fastabend }
14035b7f7626SShmulik Ravid 
14045b7f7626SShmulik Ravid int dcbnl_ieee_notify(struct net_device *dev, int event, int cmd,
140515e47304SEric W. Biederman 		      u32 seq, u32 portid)
14065b7f7626SShmulik Ravid {
140715e47304SEric W. Biederman 	return dcbnl_notify(dev, event, cmd, seq, portid, DCB_CAP_DCBX_VER_IEEE);
14085b7f7626SShmulik Ravid }
14095b7f7626SShmulik Ravid EXPORT_SYMBOL(dcbnl_ieee_notify);
14105b7f7626SShmulik Ravid 
14115b7f7626SShmulik Ravid int dcbnl_cee_notify(struct net_device *dev, int event, int cmd,
141215e47304SEric W. Biederman 		     u32 seq, u32 portid)
14135b7f7626SShmulik Ravid {
141415e47304SEric W. Biederman 	return dcbnl_notify(dev, event, cmd, seq, portid, DCB_CAP_DCBX_VER_CEE);
14155b7f7626SShmulik Ravid }
14165b7f7626SShmulik Ravid EXPORT_SYMBOL(dcbnl_cee_notify);
1417314b4778SJohn Fastabend 
1418c9368247SShani Michaeli /* Handle IEEE 802.1Qaz/802.1Qau/802.1Qbb SET commands.
1419c9368247SShani Michaeli  * If any requested operation can not be completed
1420c9368247SShani Michaeli  * the entire msg is aborted and error value is returned.
1421314b4778SJohn Fastabend  * No attempt is made to reconcile the case where only part of the
1422314b4778SJohn Fastabend  * cmd can be completed.
1423314b4778SJohn Fastabend  */
14247be99413SThomas Graf static int dcbnl_ieee_set(struct net_device *netdev, struct nlmsghdr *nlh,
14257be99413SThomas Graf 			  u32 seq, struct nlattr **tb, struct sk_buff *skb)
1426314b4778SJohn Fastabend {
1427314b4778SJohn Fastabend 	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
1428314b4778SJohn Fastabend 	struct nlattr *ieee[DCB_ATTR_IEEE_MAX + 1];
1429297e77e5SPetr Machata 	int prio;
14303d1f4869SThomas Graf 	int err;
1431314b4778SJohn Fastabend 
1432314b4778SJohn Fastabend 	if (!ops)
14333d1f4869SThomas Graf 		return -EOPNOTSUPP;
1434314b4778SJohn Fastabend 
14354003b658SJohn Fastabend 	if (!tb[DCB_ATTR_IEEE])
14364003b658SJohn Fastabend 		return -EINVAL;
14374003b658SJohn Fastabend 
14388cb08174SJohannes Berg 	err = nla_parse_nested_deprecated(ieee, DCB_ATTR_IEEE_MAX,
14398cb08174SJohannes Berg 					  tb[DCB_ATTR_IEEE],
1440fceb6435SJohannes Berg 					  dcbnl_ieee_policy, NULL);
1441314b4778SJohn Fastabend 	if (err)
1442314b4778SJohn Fastabend 		return err;
1443314b4778SJohn Fastabend 
1444314b4778SJohn Fastabend 	if (ieee[DCB_ATTR_IEEE_ETS] && ops->ieee_setets) {
1445314b4778SJohn Fastabend 		struct ieee_ets *ets = nla_data(ieee[DCB_ATTR_IEEE_ETS]);
1446314b4778SJohn Fastabend 		err = ops->ieee_setets(netdev, ets);
1447314b4778SJohn Fastabend 		if (err)
1448314b4778SJohn Fastabend 			goto err;
1449314b4778SJohn Fastabend 	}
1450314b4778SJohn Fastabend 
145108f10affSAmir Vadai 	if (ieee[DCB_ATTR_IEEE_MAXRATE] && ops->ieee_setmaxrate) {
145208f10affSAmir Vadai 		struct ieee_maxrate *maxrate =
145308f10affSAmir Vadai 			nla_data(ieee[DCB_ATTR_IEEE_MAXRATE]);
145408f10affSAmir Vadai 		err = ops->ieee_setmaxrate(netdev, maxrate);
145508f10affSAmir Vadai 		if (err)
145608f10affSAmir Vadai 			goto err;
145708f10affSAmir Vadai 	}
145808f10affSAmir Vadai 
1459c9368247SShani Michaeli 	if (ieee[DCB_ATTR_IEEE_QCN] && ops->ieee_setqcn) {
1460c9368247SShani Michaeli 		struct ieee_qcn *qcn =
1461c9368247SShani Michaeli 			nla_data(ieee[DCB_ATTR_IEEE_QCN]);
1462c9368247SShani Michaeli 
1463c9368247SShani Michaeli 		err = ops->ieee_setqcn(netdev, qcn);
1464c9368247SShani Michaeli 		if (err)
1465c9368247SShani Michaeli 			goto err;
1466c9368247SShani Michaeli 	}
1467c9368247SShani Michaeli 
1468314b4778SJohn Fastabend 	if (ieee[DCB_ATTR_IEEE_PFC] && ops->ieee_setpfc) {
1469314b4778SJohn Fastabend 		struct ieee_pfc *pfc = nla_data(ieee[DCB_ATTR_IEEE_PFC]);
1470314b4778SJohn Fastabend 		err = ops->ieee_setpfc(netdev, pfc);
1471314b4778SJohn Fastabend 		if (err)
1472314b4778SJohn Fastabend 			goto err;
1473314b4778SJohn Fastabend 	}
1474314b4778SJohn Fastabend 
1475e549f6f9SHuy Nguyen 	if (ieee[DCB_ATTR_DCB_BUFFER] && ops->dcbnl_setbuffer) {
1476e549f6f9SHuy Nguyen 		struct dcbnl_buffer *buffer =
1477e549f6f9SHuy Nguyen 			nla_data(ieee[DCB_ATTR_DCB_BUFFER]);
1478e549f6f9SHuy Nguyen 
1479297e77e5SPetr Machata 		for (prio = 0; prio < ARRAY_SIZE(buffer->prio2buffer); prio++) {
1480297e77e5SPetr Machata 			if (buffer->prio2buffer[prio] >= DCBX_MAX_BUFFERS) {
1481297e77e5SPetr Machata 				err = -EINVAL;
1482297e77e5SPetr Machata 				goto err;
1483297e77e5SPetr Machata 			}
1484297e77e5SPetr Machata 		}
1485297e77e5SPetr Machata 
1486e549f6f9SHuy Nguyen 		err = ops->dcbnl_setbuffer(netdev, buffer);
1487e549f6f9SHuy Nguyen 		if (err)
1488e549f6f9SHuy Nguyen 			goto err;
1489e549f6f9SHuy Nguyen 	}
1490e549f6f9SHuy Nguyen 
1491314b4778SJohn Fastabend 	if (ieee[DCB_ATTR_IEEE_APP_TABLE]) {
1492314b4778SJohn Fastabend 		struct nlattr *attr;
1493314b4778SJohn Fastabend 		int rem;
1494314b4778SJohn Fastabend 
1495314b4778SJohn Fastabend 		nla_for_each_nested(attr, ieee[DCB_ATTR_IEEE_APP_TABLE], rem) {
1496314b4778SJohn Fastabend 			struct dcb_app *app_data;
1497332b4fc8Sstephen hemminger 
1498314b4778SJohn Fastabend 			if (nla_type(attr) != DCB_ATTR_IEEE_APP)
1499314b4778SJohn Fastabend 				continue;
1500332b4fc8Sstephen hemminger 
1501332b4fc8Sstephen hemminger 			if (nla_len(attr) < sizeof(struct dcb_app)) {
1502332b4fc8Sstephen hemminger 				err = -ERANGE;
1503332b4fc8Sstephen hemminger 				goto err;
1504332b4fc8Sstephen hemminger 			}
1505332b4fc8Sstephen hemminger 
1506314b4778SJohn Fastabend 			app_data = nla_data(attr);
1507314b4778SJohn Fastabend 			if (ops->ieee_setapp)
1508314b4778SJohn Fastabend 				err = ops->ieee_setapp(netdev, app_data);
1509314b4778SJohn Fastabend 			else
1510b6db2174SJohn Fastabend 				err = dcb_ieee_setapp(netdev, app_data);
1511314b4778SJohn Fastabend 			if (err)
1512314b4778SJohn Fastabend 				goto err;
1513314b4778SJohn Fastabend 		}
1514314b4778SJohn Fastabend 	}
1515314b4778SJohn Fastabend 
1516314b4778SJohn Fastabend err:
15177be99413SThomas Graf 	err = nla_put_u8(skb, DCB_ATTR_IEEE, err);
15185b7f7626SShmulik Ravid 	dcbnl_ieee_notify(netdev, RTM_SETDCB, DCB_CMD_IEEE_SET, seq, 0);
1519314b4778SJohn Fastabend 	return err;
1520314b4778SJohn Fastabend }
1521314b4778SJohn Fastabend 
15227be99413SThomas Graf static int dcbnl_ieee_get(struct net_device *netdev, struct nlmsghdr *nlh,
15237be99413SThomas Graf 			  u32 seq, struct nlattr **tb, struct sk_buff *skb)
1524314b4778SJohn Fastabend {
1525314b4778SJohn Fastabend 	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
1526314b4778SJohn Fastabend 
1527314b4778SJohn Fastabend 	if (!ops)
1528314b4778SJohn Fastabend 		return -EOPNOTSUPP;
1529314b4778SJohn Fastabend 
15307be99413SThomas Graf 	return dcbnl_ieee_fill(skb, netdev);
1531314b4778SJohn Fastabend }
1532314b4778SJohn Fastabend 
15337be99413SThomas Graf static int dcbnl_ieee_del(struct net_device *netdev, struct nlmsghdr *nlh,
15347be99413SThomas Graf 			  u32 seq, struct nlattr **tb, struct sk_buff *skb)
1535f9ae7e4bSJohn Fastabend {
1536f9ae7e4bSJohn Fastabend 	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
1537f9ae7e4bSJohn Fastabend 	struct nlattr *ieee[DCB_ATTR_IEEE_MAX + 1];
15383d1f4869SThomas Graf 	int err;
1539f9ae7e4bSJohn Fastabend 
1540f9ae7e4bSJohn Fastabend 	if (!ops)
1541f9ae7e4bSJohn Fastabend 		return -EOPNOTSUPP;
1542f9ae7e4bSJohn Fastabend 
1543f9ae7e4bSJohn Fastabend 	if (!tb[DCB_ATTR_IEEE])
1544f9ae7e4bSJohn Fastabend 		return -EINVAL;
1545f9ae7e4bSJohn Fastabend 
15468cb08174SJohannes Berg 	err = nla_parse_nested_deprecated(ieee, DCB_ATTR_IEEE_MAX,
15478cb08174SJohannes Berg 					  tb[DCB_ATTR_IEEE],
1548fceb6435SJohannes Berg 					  dcbnl_ieee_policy, NULL);
1549f9ae7e4bSJohn Fastabend 	if (err)
1550f9ae7e4bSJohn Fastabend 		return err;
1551f9ae7e4bSJohn Fastabend 
1552f9ae7e4bSJohn Fastabend 	if (ieee[DCB_ATTR_IEEE_APP_TABLE]) {
1553f9ae7e4bSJohn Fastabend 		struct nlattr *attr;
1554f9ae7e4bSJohn Fastabend 		int rem;
1555f9ae7e4bSJohn Fastabend 
1556f9ae7e4bSJohn Fastabend 		nla_for_each_nested(attr, ieee[DCB_ATTR_IEEE_APP_TABLE], rem) {
1557f9ae7e4bSJohn Fastabend 			struct dcb_app *app_data;
1558f9ae7e4bSJohn Fastabend 
1559f9ae7e4bSJohn Fastabend 			if (nla_type(attr) != DCB_ATTR_IEEE_APP)
1560f9ae7e4bSJohn Fastabend 				continue;
1561f9ae7e4bSJohn Fastabend 			app_data = nla_data(attr);
1562f9ae7e4bSJohn Fastabend 			if (ops->ieee_delapp)
1563f9ae7e4bSJohn Fastabend 				err = ops->ieee_delapp(netdev, app_data);
1564f9ae7e4bSJohn Fastabend 			else
1565f9ae7e4bSJohn Fastabend 				err = dcb_ieee_delapp(netdev, app_data);
1566f9ae7e4bSJohn Fastabend 			if (err)
1567f9ae7e4bSJohn Fastabend 				goto err;
1568f9ae7e4bSJohn Fastabend 		}
1569f9ae7e4bSJohn Fastabend 	}
1570f9ae7e4bSJohn Fastabend 
1571f9ae7e4bSJohn Fastabend err:
15727be99413SThomas Graf 	err = nla_put_u8(skb, DCB_ATTR_IEEE, err);
15735b7f7626SShmulik Ravid 	dcbnl_ieee_notify(netdev, RTM_SETDCB, DCB_CMD_IEEE_DEL, seq, 0);
1574f9ae7e4bSJohn Fastabend 	return err;
1575f9ae7e4bSJohn Fastabend }
1576f9ae7e4bSJohn Fastabend 
1577f9ae7e4bSJohn Fastabend 
15786241b625SShmulik Ravid /* DCBX configuration */
15797be99413SThomas Graf static int dcbnl_getdcbx(struct net_device *netdev, struct nlmsghdr *nlh,
15807be99413SThomas Graf 			 u32 seq, struct nlattr **tb, struct sk_buff *skb)
15816241b625SShmulik Ravid {
15826241b625SShmulik Ravid 	if (!netdev->dcbnl_ops->getdcbx)
15837f891cf1SShmulik Ravid 		return -EOPNOTSUPP;
15846241b625SShmulik Ravid 
15857be99413SThomas Graf 	return nla_put_u8(skb, DCB_ATTR_DCBX,
15867be99413SThomas Graf 			  netdev->dcbnl_ops->getdcbx(netdev));
15876241b625SShmulik Ravid }
15886241b625SShmulik Ravid 
15897be99413SThomas Graf static int dcbnl_setdcbx(struct net_device *netdev, struct nlmsghdr *nlh,
15907be99413SThomas Graf 			 u32 seq, struct nlattr **tb, struct sk_buff *skb)
15916241b625SShmulik Ravid {
15926241b625SShmulik Ravid 	u8 value;
15936241b625SShmulik Ravid 
15947f891cf1SShmulik Ravid 	if (!netdev->dcbnl_ops->setdcbx)
15957f891cf1SShmulik Ravid 		return -EOPNOTSUPP;
15967f891cf1SShmulik Ravid 
15977f891cf1SShmulik Ravid 	if (!tb[DCB_ATTR_DCBX])
15987f891cf1SShmulik Ravid 		return -EINVAL;
15996241b625SShmulik Ravid 
16006241b625SShmulik Ravid 	value = nla_get_u8(tb[DCB_ATTR_DCBX]);
16016241b625SShmulik Ravid 
16027be99413SThomas Graf 	return nla_put_u8(skb, DCB_ATTR_DCBX,
16037be99413SThomas Graf 			  netdev->dcbnl_ops->setdcbx(netdev, value));
16046241b625SShmulik Ravid }
16056241b625SShmulik Ravid 
16067be99413SThomas Graf static int dcbnl_getfeatcfg(struct net_device *netdev, struct nlmsghdr *nlh,
16077be99413SThomas Graf 			    u32 seq, struct nlattr **tb, struct sk_buff *skb)
1608ea45fe4eSShmulik Ravid {
1609ea45fe4eSShmulik Ravid 	struct nlattr *data[DCB_FEATCFG_ATTR_MAX + 1], *nest;
1610ea45fe4eSShmulik Ravid 	u8 value;
16117f891cf1SShmulik Ravid 	int ret, i;
1612ea45fe4eSShmulik Ravid 	int getall = 0;
1613ea45fe4eSShmulik Ravid 
16147f891cf1SShmulik Ravid 	if (!netdev->dcbnl_ops->getfeatcfg)
16157f891cf1SShmulik Ravid 		return -EOPNOTSUPP;
16167f891cf1SShmulik Ravid 
16177f891cf1SShmulik Ravid 	if (!tb[DCB_ATTR_FEATCFG])
16187f891cf1SShmulik Ravid 		return -EINVAL;
1619ea45fe4eSShmulik Ravid 
16208cb08174SJohannes Berg 	ret = nla_parse_nested_deprecated(data, DCB_FEATCFG_ATTR_MAX,
16218cb08174SJohannes Berg 					  tb[DCB_ATTR_FEATCFG],
16228cb08174SJohannes Berg 					  dcbnl_featcfg_nest, NULL);
16237f891cf1SShmulik Ravid 	if (ret)
16247be99413SThomas Graf 		return ret;
1625ea45fe4eSShmulik Ravid 
1626ae0be8deSMichal Kubecek 	nest = nla_nest_start_noflag(skb, DCB_ATTR_FEATCFG);
16277be99413SThomas Graf 	if (!nest)
16287be99413SThomas Graf 		return -EMSGSIZE;
1629ea45fe4eSShmulik Ravid 
1630ea45fe4eSShmulik Ravid 	if (data[DCB_FEATCFG_ATTR_ALL])
1631ea45fe4eSShmulik Ravid 		getall = 1;
1632ea45fe4eSShmulik Ravid 
1633ea45fe4eSShmulik Ravid 	for (i = DCB_FEATCFG_ATTR_ALL+1; i <= DCB_FEATCFG_ATTR_MAX; i++) {
1634ea45fe4eSShmulik Ravid 		if (!getall && !data[i])
1635ea45fe4eSShmulik Ravid 			continue;
1636ea45fe4eSShmulik Ravid 
1637ea45fe4eSShmulik Ravid 		ret = netdev->dcbnl_ops->getfeatcfg(netdev, i, &value);
16387f891cf1SShmulik Ravid 		if (!ret)
16397be99413SThomas Graf 			ret = nla_put_u8(skb, i, value);
1640ea45fe4eSShmulik Ravid 
1641ea45fe4eSShmulik Ravid 		if (ret) {
16427be99413SThomas Graf 			nla_nest_cancel(skb, nest);
16437f891cf1SShmulik Ravid 			goto nla_put_failure;
1644ea45fe4eSShmulik Ravid 		}
1645ea45fe4eSShmulik Ravid 	}
16467be99413SThomas Graf 	nla_nest_end(skb, nest);
1647ea45fe4eSShmulik Ravid 
16487f891cf1SShmulik Ravid nla_put_failure:
1649ea45fe4eSShmulik Ravid 	return ret;
1650ea45fe4eSShmulik Ravid }
1651ea45fe4eSShmulik Ravid 
16527be99413SThomas Graf static int dcbnl_setfeatcfg(struct net_device *netdev, struct nlmsghdr *nlh,
16537be99413SThomas Graf 			    u32 seq, struct nlattr **tb, struct sk_buff *skb)
1654ea45fe4eSShmulik Ravid {
1655ea45fe4eSShmulik Ravid 	struct nlattr *data[DCB_FEATCFG_ATTR_MAX + 1];
16567f891cf1SShmulik Ravid 	int ret, i;
1657ea45fe4eSShmulik Ravid 	u8 value;
1658ea45fe4eSShmulik Ravid 
16597f891cf1SShmulik Ravid 	if (!netdev->dcbnl_ops->setfeatcfg)
16607f891cf1SShmulik Ravid 		return -ENOTSUPP;
16617f891cf1SShmulik Ravid 
16627f891cf1SShmulik Ravid 	if (!tb[DCB_ATTR_FEATCFG])
16637f891cf1SShmulik Ravid 		return -EINVAL;
1664ea45fe4eSShmulik Ravid 
16658cb08174SJohannes Berg 	ret = nla_parse_nested_deprecated(data, DCB_FEATCFG_ATTR_MAX,
16668cb08174SJohannes Berg 					  tb[DCB_ATTR_FEATCFG],
16678cb08174SJohannes Berg 					  dcbnl_featcfg_nest, NULL);
1668ea45fe4eSShmulik Ravid 
16697f891cf1SShmulik Ravid 	if (ret)
1670ea45fe4eSShmulik Ravid 		goto err;
1671ea45fe4eSShmulik Ravid 
1672ea45fe4eSShmulik Ravid 	for (i = DCB_FEATCFG_ATTR_ALL+1; i <= DCB_FEATCFG_ATTR_MAX; i++) {
1673ea45fe4eSShmulik Ravid 		if (data[i] == NULL)
1674ea45fe4eSShmulik Ravid 			continue;
1675ea45fe4eSShmulik Ravid 
1676ea45fe4eSShmulik Ravid 		value = nla_get_u8(data[i]);
1677ea45fe4eSShmulik Ravid 
1678ea45fe4eSShmulik Ravid 		ret = netdev->dcbnl_ops->setfeatcfg(netdev, i, value);
1679ea45fe4eSShmulik Ravid 
1680ea45fe4eSShmulik Ravid 		if (ret)
16817f891cf1SShmulik Ravid 			goto err;
1682ea45fe4eSShmulik Ravid 	}
1683ea45fe4eSShmulik Ravid err:
16847be99413SThomas Graf 	ret = nla_put_u8(skb, DCB_ATTR_FEATCFG, ret);
16857f891cf1SShmulik Ravid 
1686ea45fe4eSShmulik Ravid 	return ret;
1687ea45fe4eSShmulik Ravid }
1688ea45fe4eSShmulik Ravid 
1689dc6ed1dfSShmulik Ravid /* Handle CEE DCBX GET commands. */
16907be99413SThomas Graf static int dcbnl_cee_get(struct net_device *netdev, struct nlmsghdr *nlh,
16917be99413SThomas Graf 			 u32 seq, struct nlattr **tb, struct sk_buff *skb)
1692dc6ed1dfSShmulik Ravid {
1693dc6ed1dfSShmulik Ravid 	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
1694dc6ed1dfSShmulik Ravid 
1695dc6ed1dfSShmulik Ravid 	if (!ops)
1696dc6ed1dfSShmulik Ravid 		return -EOPNOTSUPP;
1697dc6ed1dfSShmulik Ravid 
16987be99413SThomas Graf 	return dcbnl_cee_fill(skb, netdev);
1699dc6ed1dfSShmulik Ravid }
1700dc6ed1dfSShmulik Ravid 
170133a03aadSThomas Graf struct reply_func {
170233a03aadSThomas Graf 	/* reply netlink message type */
170333a03aadSThomas Graf 	int	type;
170433a03aadSThomas Graf 
170533a03aadSThomas Graf 	/* function to fill message contents */
170633a03aadSThomas Graf 	int   (*cb)(struct net_device *, struct nlmsghdr *, u32,
170733a03aadSThomas Graf 		    struct nlattr **, struct sk_buff *);
170833a03aadSThomas Graf };
170933a03aadSThomas Graf 
171033a03aadSThomas Graf static const struct reply_func reply_funcs[DCB_CMD_MAX+1] = {
17117be99413SThomas Graf 	[DCB_CMD_GSTATE]	= { RTM_GETDCB, dcbnl_getstate },
17127be99413SThomas Graf 	[DCB_CMD_SSTATE]	= { RTM_SETDCB, dcbnl_setstate },
17137be99413SThomas Graf 	[DCB_CMD_PFC_GCFG]	= { RTM_GETDCB, dcbnl_getpfccfg },
17147be99413SThomas Graf 	[DCB_CMD_PFC_SCFG]	= { RTM_SETDCB, dcbnl_setpfccfg },
17157be99413SThomas Graf 	[DCB_CMD_GPERM_HWADDR]	= { RTM_GETDCB, dcbnl_getperm_hwaddr },
17167be99413SThomas Graf 	[DCB_CMD_GCAP]		= { RTM_GETDCB, dcbnl_getcap },
17177be99413SThomas Graf 	[DCB_CMD_GNUMTCS]	= { RTM_GETDCB, dcbnl_getnumtcs },
17187be99413SThomas Graf 	[DCB_CMD_SNUMTCS]	= { RTM_SETDCB, dcbnl_setnumtcs },
17197be99413SThomas Graf 	[DCB_CMD_PFC_GSTATE]	= { RTM_GETDCB, dcbnl_getpfcstate },
17207be99413SThomas Graf 	[DCB_CMD_PFC_SSTATE]	= { RTM_SETDCB, dcbnl_setpfcstate },
17217be99413SThomas Graf 	[DCB_CMD_GAPP]		= { RTM_GETDCB, dcbnl_getapp },
17227be99413SThomas Graf 	[DCB_CMD_SAPP]		= { RTM_SETDCB, dcbnl_setapp },
17237be99413SThomas Graf 	[DCB_CMD_PGTX_GCFG]	= { RTM_GETDCB, dcbnl_pgtx_getcfg },
17247be99413SThomas Graf 	[DCB_CMD_PGTX_SCFG]	= { RTM_SETDCB, dcbnl_pgtx_setcfg },
17257be99413SThomas Graf 	[DCB_CMD_PGRX_GCFG]	= { RTM_GETDCB, dcbnl_pgrx_getcfg },
17267be99413SThomas Graf 	[DCB_CMD_PGRX_SCFG]	= { RTM_SETDCB, dcbnl_pgrx_setcfg },
17277be99413SThomas Graf 	[DCB_CMD_SET_ALL]	= { RTM_SETDCB, dcbnl_setall },
17287be99413SThomas Graf 	[DCB_CMD_BCN_GCFG]	= { RTM_GETDCB, dcbnl_bcn_getcfg },
17297be99413SThomas Graf 	[DCB_CMD_BCN_SCFG]	= { RTM_SETDCB, dcbnl_bcn_setcfg },
17307be99413SThomas Graf 	[DCB_CMD_IEEE_GET]	= { RTM_GETDCB, dcbnl_ieee_get },
17317be99413SThomas Graf 	[DCB_CMD_IEEE_SET]	= { RTM_SETDCB, dcbnl_ieee_set },
17327be99413SThomas Graf 	[DCB_CMD_IEEE_DEL]	= { RTM_SETDCB, dcbnl_ieee_del },
17337be99413SThomas Graf 	[DCB_CMD_GDCBX]		= { RTM_GETDCB, dcbnl_getdcbx },
17347be99413SThomas Graf 	[DCB_CMD_SDCBX]		= { RTM_SETDCB, dcbnl_setdcbx },
17357be99413SThomas Graf 	[DCB_CMD_GFEATCFG]	= { RTM_GETDCB, dcbnl_getfeatcfg },
17367be99413SThomas Graf 	[DCB_CMD_SFEATCFG]	= { RTM_SETDCB, dcbnl_setfeatcfg },
17377be99413SThomas Graf 	[DCB_CMD_CEE_GET]	= { RTM_GETDCB, dcbnl_cee_get },
173833a03aadSThomas Graf };
173933a03aadSThomas Graf 
1740c21ef3e3SDavid Ahern static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
1741c21ef3e3SDavid Ahern 		    struct netlink_ext_ack *extack)
17422f90b865SAlexander Duyck {
17432f90b865SAlexander Duyck 	struct net *net = sock_net(skb->sk);
17442f90b865SAlexander Duyck 	struct net_device *netdev;
17457a282bc3SThomas Graf 	struct dcbmsg *dcb = nlmsg_data(nlh);
17462f90b865SAlexander Duyck 	struct nlattr *tb[DCB_ATTR_MAX + 1];
17470cc55e69SGaurav Singh 	u32 portid = NETLINK_CB(skb).portid;
17482f90b865SAlexander Duyck 	int ret = -EINVAL;
174933a03aadSThomas Graf 	struct sk_buff *reply_skb;
175039912f9cSThomas Graf 	struct nlmsghdr *reply_nlh = NULL;
175133a03aadSThomas Graf 	const struct reply_func *fn;
17522f90b865SAlexander Duyck 
175390f62cf3SEric W. Biederman 	if ((nlh->nlmsg_type == RTM_SETDCB) && !netlink_capable(skb, CAP_NET_ADMIN))
1754dfc47ef8SEric W. Biederman 		return -EPERM;
1755dfc47ef8SEric W. Biederman 
17568cb08174SJohannes Berg 	ret = nlmsg_parse_deprecated(nlh, sizeof(*dcb), tb, DCB_ATTR_MAX,
1757c21ef3e3SDavid Ahern 				     dcbnl_rtnl_policy, extack);
17582f90b865SAlexander Duyck 	if (ret < 0)
17592f90b865SAlexander Duyck 		return ret;
17602f90b865SAlexander Duyck 
176133a03aadSThomas Graf 	if (dcb->cmd > DCB_CMD_MAX)
176233a03aadSThomas Graf 		return -EINVAL;
176333a03aadSThomas Graf 
176433a03aadSThomas Graf 	/* check if a reply function has been defined for the command */
176533a03aadSThomas Graf 	fn = &reply_funcs[dcb->cmd];
176633a03aadSThomas Graf 	if (!fn->cb)
176733a03aadSThomas Graf 		return -EOPNOTSUPP;
1768df85bc14SPetr Machata 	if (fn->type == RTM_SETDCB && !netlink_capable(skb, CAP_NET_ADMIN))
1769826f328eSPetr Machata 		return -EPERM;
177033a03aadSThomas Graf 
17712f90b865SAlexander Duyck 	if (!tb[DCB_ATTR_IFNAME])
17722f90b865SAlexander Duyck 		return -EINVAL;
17732f90b865SAlexander Duyck 
1774d9ac62beSYing Xue 	netdev = __dev_get_by_name(net, nla_data(tb[DCB_ATTR_IFNAME]));
17752f90b865SAlexander Duyck 	if (!netdev)
17763d1f4869SThomas Graf 		return -ENODEV;
17772f90b865SAlexander Duyck 
1778d9ac62beSYing Xue 	if (!netdev->dcbnl_ops)
1779d9ac62beSYing Xue 		return -EOPNOTSUPP;
17802f90b865SAlexander Duyck 
178115e47304SEric W. Biederman 	reply_skb = dcbnl_newmsg(fn->type, dcb->cmd, portid, nlh->nlmsg_seq,
178233a03aadSThomas Graf 				 nlh->nlmsg_flags, &reply_nlh);
1783d9ac62beSYing Xue 	if (!reply_skb)
1784b923cda9SZheng Yongjun 		return -ENOMEM;
178533a03aadSThomas Graf 
178633a03aadSThomas Graf 	ret = fn->cb(netdev, nlh, nlh->nlmsg_seq, tb, reply_skb);
178733a03aadSThomas Graf 	if (ret < 0) {
178833a03aadSThomas Graf 		nlmsg_free(reply_skb);
178933a03aadSThomas Graf 		goto out;
179033a03aadSThomas Graf 	}
179133a03aadSThomas Graf 
179233a03aadSThomas Graf 	nlmsg_end(reply_skb, reply_nlh);
179333a03aadSThomas Graf 
17947c77ab24SJohn Fastabend 	ret = rtnl_unicast(reply_skb, net, portid);
17952f90b865SAlexander Duyck out:
17962f90b865SAlexander Duyck 	return ret;
17972f90b865SAlexander Duyck }
17982f90b865SAlexander Duyck 
1799716b31abSThomas Graf static struct dcb_app_type *dcb_app_lookup(const struct dcb_app *app,
1800716b31abSThomas Graf 					   int ifindex, int prio)
1801716b31abSThomas Graf {
1802716b31abSThomas Graf 	struct dcb_app_type *itr;
1803716b31abSThomas Graf 
1804716b31abSThomas Graf 	list_for_each_entry(itr, &dcb_app_list, list) {
1805716b31abSThomas Graf 		if (itr->app.selector == app->selector &&
1806716b31abSThomas Graf 		    itr->app.protocol == app->protocol &&
1807716b31abSThomas Graf 		    itr->ifindex == ifindex &&
180808193d1aSPetr Machata 		    ((prio == -1) || itr->app.priority == prio))
1809716b31abSThomas Graf 			return itr;
1810716b31abSThomas Graf 	}
1811716b31abSThomas Graf 
1812716b31abSThomas Graf 	return NULL;
1813716b31abSThomas Graf }
1814716b31abSThomas Graf 
18154e4f2f69SThomas Graf static int dcb_app_add(const struct dcb_app *app, int ifindex)
18164e4f2f69SThomas Graf {
18174e4f2f69SThomas Graf 	struct dcb_app_type *entry;
18184e4f2f69SThomas Graf 
18194e4f2f69SThomas Graf 	entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
18204e4f2f69SThomas Graf 	if (!entry)
18214e4f2f69SThomas Graf 		return -ENOMEM;
18224e4f2f69SThomas Graf 
18234e4f2f69SThomas Graf 	memcpy(&entry->app, app, sizeof(*app));
18244e4f2f69SThomas Graf 	entry->ifindex = ifindex;
18254e4f2f69SThomas Graf 	list_add(&entry->list, &dcb_app_list);
18264e4f2f69SThomas Graf 
18274e4f2f69SThomas Graf 	return 0;
18284e4f2f69SThomas Graf }
18294e4f2f69SThomas Graf 
18309ab933abSJohn Fastabend /**
18319ab933abSJohn Fastabend  * dcb_getapp - retrieve the DCBX application user priority
1832a89a501cSAndrew Lunn  * @dev: network interface
1833a89a501cSAndrew Lunn  * @app: application to get user priority of
18349ab933abSJohn Fastabend  *
18359ab933abSJohn Fastabend  * On success returns a non-zero 802.1p user priority bitmap
18369ab933abSJohn Fastabend  * otherwise returns 0 as the invalid user priority bitmap to
18379ab933abSJohn Fastabend  * indicate an error.
18389ab933abSJohn Fastabend  */
18399ab933abSJohn Fastabend u8 dcb_getapp(struct net_device *dev, struct dcb_app *app)
18409ab933abSJohn Fastabend {
18419ab933abSJohn Fastabend 	struct dcb_app_type *itr;
18429ab933abSJohn Fastabend 	u8 prio = 0;
18439ab933abSJohn Fastabend 
184452cff74eSAnish Bhatt 	spin_lock_bh(&dcb_lock);
184508193d1aSPetr Machata 	itr = dcb_app_lookup(app, dev->ifindex, -1);
184608193d1aSPetr Machata 	if (itr)
18479ab933abSJohn Fastabend 		prio = itr->app.priority;
184852cff74eSAnish Bhatt 	spin_unlock_bh(&dcb_lock);
18499ab933abSJohn Fastabend 
18509ab933abSJohn Fastabend 	return prio;
18519ab933abSJohn Fastabend }
18529ab933abSJohn Fastabend EXPORT_SYMBOL(dcb_getapp);
18539ab933abSJohn Fastabend 
18549ab933abSJohn Fastabend /**
1855b6db2174SJohn Fastabend  * dcb_setapp - add CEE dcb application data to app list
1856a89a501cSAndrew Lunn  * @dev: network interface
1857a89a501cSAndrew Lunn  * @new: application data to add
18589ab933abSJohn Fastabend  *
1859b6db2174SJohn Fastabend  * Priority 0 is an invalid priority in CEE spec. This routine
1860b6db2174SJohn Fastabend  * removes applications from the app list if the priority is
186116eecd9bSAnish Bhatt  * set to zero. Priority is expected to be 8-bit 802.1p user priority bitmap
18629ab933abSJohn Fastabend  */
1863ab6baf98SJohn Fastabend int dcb_setapp(struct net_device *dev, struct dcb_app *new)
18649ab933abSJohn Fastabend {
18659ab933abSJohn Fastabend 	struct dcb_app_type *itr;
18667ec79270SJohn Fastabend 	struct dcb_app_type event;
18674e4f2f69SThomas Graf 	int err = 0;
18687ec79270SJohn Fastabend 
1869e290ed81SMark Rustad 	event.ifindex = dev->ifindex;
18707ec79270SJohn Fastabend 	memcpy(&event.app, new, sizeof(event.app));
18716bd0e1cbSJohn Fastabend 	if (dev->dcbnl_ops->getdcbx)
18726bd0e1cbSJohn Fastabend 		event.dcbx = dev->dcbnl_ops->getdcbx(dev);
18739ab933abSJohn Fastabend 
187452cff74eSAnish Bhatt 	spin_lock_bh(&dcb_lock);
18759ab933abSJohn Fastabend 	/* Search for existing match and replace */
187608193d1aSPetr Machata 	itr = dcb_app_lookup(new, dev->ifindex, -1);
187708193d1aSPetr Machata 	if (itr) {
18789ab933abSJohn Fastabend 		if (new->priority)
18799ab933abSJohn Fastabend 			itr->app.priority = new->priority;
18809ab933abSJohn Fastabend 		else {
18819ab933abSJohn Fastabend 			list_del(&itr->list);
18829ab933abSJohn Fastabend 			kfree(itr);
18839ab933abSJohn Fastabend 		}
18849ab933abSJohn Fastabend 		goto out;
18859ab933abSJohn Fastabend 	}
18869ab933abSJohn Fastabend 	/* App type does not exist add new application type */
18874e4f2f69SThomas Graf 	if (new->priority)
18884e4f2f69SThomas Graf 		err = dcb_app_add(new, dev->ifindex);
18899ab933abSJohn Fastabend out:
189052cff74eSAnish Bhatt 	spin_unlock_bh(&dcb_lock);
18914e4f2f69SThomas Graf 	if (!err)
18927ec79270SJohn Fastabend 		call_dcbevent_notifiers(DCB_APP_EVENT, &event);
18934e4f2f69SThomas Graf 	return err;
18949ab933abSJohn Fastabend }
18959ab933abSJohn Fastabend EXPORT_SYMBOL(dcb_setapp);
18969ab933abSJohn Fastabend 
1897b6db2174SJohn Fastabend /**
1898a364c8cfSJohn Fastabend  * dcb_ieee_getapp_mask - retrieve the IEEE DCB application priority
1899a89a501cSAndrew Lunn  * @dev: network interface
1900a89a501cSAndrew Lunn  * @app: where to store the retrieve application data
1901a364c8cfSJohn Fastabend  *
1902a364c8cfSJohn Fastabend  * Helper routine which on success returns a non-zero 802.1Qaz user
1903a364c8cfSJohn Fastabend  * priority bitmap otherwise returns 0 to indicate the dcb_app was
1904a364c8cfSJohn Fastabend  * not found in APP list.
1905a364c8cfSJohn Fastabend  */
1906a364c8cfSJohn Fastabend u8 dcb_ieee_getapp_mask(struct net_device *dev, struct dcb_app *app)
1907a364c8cfSJohn Fastabend {
1908a364c8cfSJohn Fastabend 	struct dcb_app_type *itr;
1909a364c8cfSJohn Fastabend 	u8 prio = 0;
1910a364c8cfSJohn Fastabend 
191152cff74eSAnish Bhatt 	spin_lock_bh(&dcb_lock);
191208193d1aSPetr Machata 	itr = dcb_app_lookup(app, dev->ifindex, -1);
191308193d1aSPetr Machata 	if (itr)
1914a364c8cfSJohn Fastabend 		prio |= 1 << itr->app.priority;
191552cff74eSAnish Bhatt 	spin_unlock_bh(&dcb_lock);
1916a364c8cfSJohn Fastabend 
1917a364c8cfSJohn Fastabend 	return prio;
1918a364c8cfSJohn Fastabend }
1919a364c8cfSJohn Fastabend EXPORT_SYMBOL(dcb_ieee_getapp_mask);
1920a364c8cfSJohn Fastabend 
1921a364c8cfSJohn Fastabend /**
1922b6db2174SJohn Fastabend  * dcb_ieee_setapp - add IEEE dcb application data to app list
1923a89a501cSAndrew Lunn  * @dev: network interface
1924a89a501cSAndrew Lunn  * @new: application data to add
1925b6db2174SJohn Fastabend  *
1926b6db2174SJohn Fastabend  * This adds Application data to the list. Multiple application
1927b6db2174SJohn Fastabend  * entries may exists for the same selector and protocol as long
192816eecd9bSAnish Bhatt  * as the priorities are different. Priority is expected to be a
192916eecd9bSAnish Bhatt  * 3-bit unsigned integer
1930b6db2174SJohn Fastabend  */
1931b6db2174SJohn Fastabend int dcb_ieee_setapp(struct net_device *dev, struct dcb_app *new)
1932b6db2174SJohn Fastabend {
1933b6db2174SJohn Fastabend 	struct dcb_app_type event;
1934b6db2174SJohn Fastabend 	int err = 0;
1935b6db2174SJohn Fastabend 
1936e290ed81SMark Rustad 	event.ifindex = dev->ifindex;
1937b6db2174SJohn Fastabend 	memcpy(&event.app, new, sizeof(event.app));
19386bd0e1cbSJohn Fastabend 	if (dev->dcbnl_ops->getdcbx)
19396bd0e1cbSJohn Fastabend 		event.dcbx = dev->dcbnl_ops->getdcbx(dev);
1940b6db2174SJohn Fastabend 
194152cff74eSAnish Bhatt 	spin_lock_bh(&dcb_lock);
1942b6db2174SJohn Fastabend 	/* Search for existing match and abort if found */
1943716b31abSThomas Graf 	if (dcb_app_lookup(new, dev->ifindex, new->priority)) {
1944b6db2174SJohn Fastabend 		err = -EEXIST;
1945b6db2174SJohn Fastabend 		goto out;
1946b6db2174SJohn Fastabend 	}
1947b6db2174SJohn Fastabend 
19484e4f2f69SThomas Graf 	err = dcb_app_add(new, dev->ifindex);
1949b6db2174SJohn Fastabend out:
195052cff74eSAnish Bhatt 	spin_unlock_bh(&dcb_lock);
1951b6db2174SJohn Fastabend 	if (!err)
1952b6db2174SJohn Fastabend 		call_dcbevent_notifiers(DCB_APP_EVENT, &event);
1953b6db2174SJohn Fastabend 	return err;
1954b6db2174SJohn Fastabend }
1955b6db2174SJohn Fastabend EXPORT_SYMBOL(dcb_ieee_setapp);
1956b6db2174SJohn Fastabend 
1957f9ae7e4bSJohn Fastabend /**
1958f9ae7e4bSJohn Fastabend  * dcb_ieee_delapp - delete IEEE dcb application data from list
1959a89a501cSAndrew Lunn  * @dev: network interface
1960a89a501cSAndrew Lunn  * @del: application data to delete
1961f9ae7e4bSJohn Fastabend  *
1962f9ae7e4bSJohn Fastabend  * This removes a matching APP data from the APP list
1963f9ae7e4bSJohn Fastabend  */
1964f9ae7e4bSJohn Fastabend int dcb_ieee_delapp(struct net_device *dev, struct dcb_app *del)
1965f9ae7e4bSJohn Fastabend {
1966f9ae7e4bSJohn Fastabend 	struct dcb_app_type *itr;
1967f9ae7e4bSJohn Fastabend 	struct dcb_app_type event;
1968f9ae7e4bSJohn Fastabend 	int err = -ENOENT;
1969f9ae7e4bSJohn Fastabend 
1970e290ed81SMark Rustad 	event.ifindex = dev->ifindex;
1971f9ae7e4bSJohn Fastabend 	memcpy(&event.app, del, sizeof(event.app));
19726bd0e1cbSJohn Fastabend 	if (dev->dcbnl_ops->getdcbx)
19736bd0e1cbSJohn Fastabend 		event.dcbx = dev->dcbnl_ops->getdcbx(dev);
1974f9ae7e4bSJohn Fastabend 
197552cff74eSAnish Bhatt 	spin_lock_bh(&dcb_lock);
1976f9ae7e4bSJohn Fastabend 	/* Search for existing match and remove it. */
1977716b31abSThomas Graf 	if ((itr = dcb_app_lookup(del, dev->ifindex, del->priority))) {
1978f9ae7e4bSJohn Fastabend 		list_del(&itr->list);
1979f9ae7e4bSJohn Fastabend 		kfree(itr);
1980f9ae7e4bSJohn Fastabend 		err = 0;
1981f9ae7e4bSJohn Fastabend 	}
1982f9ae7e4bSJohn Fastabend 
198352cff74eSAnish Bhatt 	spin_unlock_bh(&dcb_lock);
1984f9ae7e4bSJohn Fastabend 	if (!err)
1985f9ae7e4bSJohn Fastabend 		call_dcbevent_notifiers(DCB_APP_EVENT, &event);
1986f9ae7e4bSJohn Fastabend 	return err;
1987f9ae7e4bSJohn Fastabend }
1988f9ae7e4bSJohn Fastabend EXPORT_SYMBOL(dcb_ieee_delapp);
1989f9ae7e4bSJohn Fastabend 
1990a89a501cSAndrew Lunn /*
1991b67c540bSPetr Machata  * dcb_ieee_getapp_prio_dscp_mask_map - For a given device, find mapping from
1992b67c540bSPetr Machata  * priorities to the DSCP values assigned to that priority. Initialize p_map
1993b67c540bSPetr Machata  * such that each map element holds a bit mask of DSCP values configured for
1994b67c540bSPetr Machata  * that priority by APP entries.
1995b67c540bSPetr Machata  */
1996b67c540bSPetr Machata void dcb_ieee_getapp_prio_dscp_mask_map(const struct net_device *dev,
1997b67c540bSPetr Machata 					struct dcb_ieee_app_prio_map *p_map)
1998b67c540bSPetr Machata {
1999b67c540bSPetr Machata 	int ifindex = dev->ifindex;
2000b67c540bSPetr Machata 	struct dcb_app_type *itr;
2001b67c540bSPetr Machata 	u8 prio;
2002b67c540bSPetr Machata 
2003b67c540bSPetr Machata 	memset(p_map->map, 0, sizeof(p_map->map));
2004b67c540bSPetr Machata 
2005b67c540bSPetr Machata 	spin_lock_bh(&dcb_lock);
2006b67c540bSPetr Machata 	list_for_each_entry(itr, &dcb_app_list, list) {
2007b67c540bSPetr Machata 		if (itr->ifindex == ifindex &&
2008b67c540bSPetr Machata 		    itr->app.selector == IEEE_8021QAZ_APP_SEL_DSCP &&
2009b67c540bSPetr Machata 		    itr->app.protocol < 64 &&
2010b67c540bSPetr Machata 		    itr->app.priority < IEEE_8021QAZ_MAX_TCS) {
2011b67c540bSPetr Machata 			prio = itr->app.priority;
2012b67c540bSPetr Machata 			p_map->map[prio] |= 1ULL << itr->app.protocol;
2013b67c540bSPetr Machata 		}
2014b67c540bSPetr Machata 	}
2015b67c540bSPetr Machata 	spin_unlock_bh(&dcb_lock);
2016b67c540bSPetr Machata }
2017b67c540bSPetr Machata EXPORT_SYMBOL(dcb_ieee_getapp_prio_dscp_mask_map);
2018b67c540bSPetr Machata 
2019a89a501cSAndrew Lunn /*
2020b67c540bSPetr Machata  * dcb_ieee_getapp_dscp_prio_mask_map - For a given device, find mapping from
2021b67c540bSPetr Machata  * DSCP values to the priorities assigned to that DSCP value. Initialize p_map
2022b67c540bSPetr Machata  * such that each map element holds a bit mask of priorities configured for a
2023b67c540bSPetr Machata  * given DSCP value by APP entries.
2024b67c540bSPetr Machata  */
2025b67c540bSPetr Machata void
2026b67c540bSPetr Machata dcb_ieee_getapp_dscp_prio_mask_map(const struct net_device *dev,
2027b67c540bSPetr Machata 				   struct dcb_ieee_app_dscp_map *p_map)
2028b67c540bSPetr Machata {
2029b67c540bSPetr Machata 	int ifindex = dev->ifindex;
2030b67c540bSPetr Machata 	struct dcb_app_type *itr;
2031b67c540bSPetr Machata 
2032b67c540bSPetr Machata 	memset(p_map->map, 0, sizeof(p_map->map));
2033b67c540bSPetr Machata 
2034b67c540bSPetr Machata 	spin_lock_bh(&dcb_lock);
2035b67c540bSPetr Machata 	list_for_each_entry(itr, &dcb_app_list, list) {
2036b67c540bSPetr Machata 		if (itr->ifindex == ifindex &&
2037b67c540bSPetr Machata 		    itr->app.selector == IEEE_8021QAZ_APP_SEL_DSCP &&
2038b67c540bSPetr Machata 		    itr->app.protocol < 64 &&
2039b67c540bSPetr Machata 		    itr->app.priority < IEEE_8021QAZ_MAX_TCS)
2040b67c540bSPetr Machata 			p_map->map[itr->app.protocol] |= 1 << itr->app.priority;
2041b67c540bSPetr Machata 	}
2042b67c540bSPetr Machata 	spin_unlock_bh(&dcb_lock);
2043b67c540bSPetr Machata }
2044b67c540bSPetr Machata EXPORT_SYMBOL(dcb_ieee_getapp_dscp_prio_mask_map);
2045b67c540bSPetr Machata 
2046a89a501cSAndrew Lunn /*
2047b67c540bSPetr Machata  * Per 802.1Q-2014, the selector value of 1 is used for matching on Ethernet
2048b67c540bSPetr Machata  * type, with valid PID values >= 1536. A special meaning is then assigned to
2049b67c540bSPetr Machata  * protocol value of 0: "default priority. For use when priority is not
2050b67c540bSPetr Machata  * otherwise specified".
2051b67c540bSPetr Machata  *
2052b67c540bSPetr Machata  * dcb_ieee_getapp_default_prio_mask - For a given device, find all APP entries
2053b67c540bSPetr Machata  * of the form {$PRIO, ETHERTYPE, 0} and construct a bit mask of all default
2054b67c540bSPetr Machata  * priorities set by these entries.
2055b67c540bSPetr Machata  */
2056b67c540bSPetr Machata u8 dcb_ieee_getapp_default_prio_mask(const struct net_device *dev)
2057b67c540bSPetr Machata {
2058b67c540bSPetr Machata 	int ifindex = dev->ifindex;
2059b67c540bSPetr Machata 	struct dcb_app_type *itr;
2060b67c540bSPetr Machata 	u8 mask = 0;
2061b67c540bSPetr Machata 
2062b67c540bSPetr Machata 	spin_lock_bh(&dcb_lock);
2063b67c540bSPetr Machata 	list_for_each_entry(itr, &dcb_app_list, list) {
2064b67c540bSPetr Machata 		if (itr->ifindex == ifindex &&
2065b67c540bSPetr Machata 		    itr->app.selector == IEEE_8021QAZ_APP_SEL_ETHERTYPE &&
2066b67c540bSPetr Machata 		    itr->app.protocol == 0 &&
2067b67c540bSPetr Machata 		    itr->app.priority < IEEE_8021QAZ_MAX_TCS)
2068b67c540bSPetr Machata 			mask |= 1 << itr->app.priority;
2069b67c540bSPetr Machata 	}
2070b67c540bSPetr Machata 	spin_unlock_bh(&dcb_lock);
2071b67c540bSPetr Machata 
2072b67c540bSPetr Machata 	return mask;
2073b67c540bSPetr Machata }
2074b67c540bSPetr Machata EXPORT_SYMBOL(dcb_ieee_getapp_default_prio_mask);
2075b67c540bSPetr Machata 
2076*91b0383fSVladimir Oltean static void dcbnl_flush_dev(struct net_device *dev)
2077*91b0383fSVladimir Oltean {
2078*91b0383fSVladimir Oltean 	struct dcb_app_type *itr, *tmp;
2079*91b0383fSVladimir Oltean 
2080*91b0383fSVladimir Oltean 	spin_lock(&dcb_lock);
2081*91b0383fSVladimir Oltean 
2082*91b0383fSVladimir Oltean 	list_for_each_entry_safe(itr, tmp, &dcb_app_list, list) {
2083*91b0383fSVladimir Oltean 		if (itr->ifindex == dev->ifindex) {
2084*91b0383fSVladimir Oltean 			list_del(&itr->list);
2085*91b0383fSVladimir Oltean 			kfree(itr);
2086*91b0383fSVladimir Oltean 		}
2087*91b0383fSVladimir Oltean 	}
2088*91b0383fSVladimir Oltean 
2089*91b0383fSVladimir Oltean 	spin_unlock(&dcb_lock);
2090*91b0383fSVladimir Oltean }
2091*91b0383fSVladimir Oltean 
2092*91b0383fSVladimir Oltean static int dcbnl_netdevice_event(struct notifier_block *nb,
2093*91b0383fSVladimir Oltean 				 unsigned long event, void *ptr)
2094*91b0383fSVladimir Oltean {
2095*91b0383fSVladimir Oltean 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
2096*91b0383fSVladimir Oltean 
2097*91b0383fSVladimir Oltean 	switch (event) {
2098*91b0383fSVladimir Oltean 	case NETDEV_UNREGISTER:
2099*91b0383fSVladimir Oltean 		if (!dev->dcbnl_ops)
2100*91b0383fSVladimir Oltean 			return NOTIFY_DONE;
2101*91b0383fSVladimir Oltean 
2102*91b0383fSVladimir Oltean 		dcbnl_flush_dev(dev);
2103*91b0383fSVladimir Oltean 
2104*91b0383fSVladimir Oltean 		return NOTIFY_OK;
2105*91b0383fSVladimir Oltean 	default:
2106*91b0383fSVladimir Oltean 		return NOTIFY_DONE;
2107*91b0383fSVladimir Oltean 	}
2108*91b0383fSVladimir Oltean }
2109*91b0383fSVladimir Oltean 
2110*91b0383fSVladimir Oltean static struct notifier_block dcbnl_nb __read_mostly = {
2111*91b0383fSVladimir Oltean 	.notifier_call  = dcbnl_netdevice_event,
2112*91b0383fSVladimir Oltean };
2113*91b0383fSVladimir Oltean 
21142f90b865SAlexander Duyck static int __init dcbnl_init(void)
21152f90b865SAlexander Duyck {
2116*91b0383fSVladimir Oltean 	int err;
2117*91b0383fSVladimir Oltean 
2118*91b0383fSVladimir Oltean 	err = register_netdevice_notifier(&dcbnl_nb);
2119*91b0383fSVladimir Oltean 	if (err)
2120*91b0383fSVladimir Oltean 		return err;
2121*91b0383fSVladimir Oltean 
2122b97bac64SFlorian Westphal 	rtnl_register(PF_UNSPEC, RTM_GETDCB, dcb_doit, NULL, 0);
2123b97bac64SFlorian Westphal 	rtnl_register(PF_UNSPEC, RTM_SETDCB, dcb_doit, NULL, 0);
21242f90b865SAlexander Duyck 
21252f90b865SAlexander Duyck 	return 0;
21262f90b865SAlexander Duyck }
212736b9ad80SPaul Gortmaker device_initcall(dcbnl_init);
2128