xref: /openbmc/linux/net/dcb/dcbnl.c (revision ab6baf98)
12f90b865SAlexander Duyck /*
2698e1d23SMark Rustad  * Copyright (c) 2008-2011, Intel Corporation.
32f90b865SAlexander Duyck  *
42f90b865SAlexander Duyck  * This program is free software; you can redistribute it and/or modify it
52f90b865SAlexander Duyck  * under the terms and conditions of the GNU General Public License,
62f90b865SAlexander Duyck  * version 2, as published by the Free Software Foundation.
72f90b865SAlexander Duyck  *
82f90b865SAlexander Duyck  * This program is distributed in the hope it will be useful, but WITHOUT
92f90b865SAlexander Duyck  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
102f90b865SAlexander Duyck  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
112f90b865SAlexander Duyck  * more details.
122f90b865SAlexander Duyck  *
132f90b865SAlexander Duyck  * You should have received a copy of the GNU General Public License along with
142f90b865SAlexander Duyck  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
152f90b865SAlexander Duyck  * Place - Suite 330, Boston, MA 02111-1307 USA.
162f90b865SAlexander Duyck  *
172f90b865SAlexander Duyck  * Author: Lucy Liu <lucy.liu@intel.com>
182f90b865SAlexander Duyck  */
192f90b865SAlexander Duyck 
202f90b865SAlexander Duyck #include <linux/netdevice.h>
212f90b865SAlexander Duyck #include <linux/netlink.h>
225a0e3ad6STejun Heo #include <linux/slab.h>
232f90b865SAlexander Duyck #include <net/netlink.h>
242f90b865SAlexander Duyck #include <net/rtnetlink.h>
252f90b865SAlexander Duyck #include <linux/dcbnl.h>
2696b99684SJohn Fastabend #include <net/dcbevent.h>
272f90b865SAlexander Duyck #include <linux/rtnetlink.h>
282f90b865SAlexander Duyck #include <net/sock.h>
292f90b865SAlexander Duyck 
302f90b865SAlexander Duyck /**
312f90b865SAlexander Duyck  * Data Center Bridging (DCB) is a collection of Ethernet enhancements
322f90b865SAlexander Duyck  * intended to allow network traffic with differing requirements
332f90b865SAlexander Duyck  * (highly reliable, no drops vs. best effort vs. low latency) to operate
342f90b865SAlexander Duyck  * and co-exist on Ethernet.  Current DCB features are:
352f90b865SAlexander Duyck  *
362f90b865SAlexander Duyck  * Enhanced Transmission Selection (aka Priority Grouping [PG]) - provides a
372f90b865SAlexander Duyck  *   framework for assigning bandwidth guarantees to traffic classes.
382f90b865SAlexander Duyck  *
392f90b865SAlexander Duyck  * Priority-based Flow Control (PFC) - provides a flow control mechanism which
402f90b865SAlexander Duyck  *   can work independently for each 802.1p priority.
412f90b865SAlexander Duyck  *
422f90b865SAlexander Duyck  * Congestion Notification - provides a mechanism for end-to-end congestion
432f90b865SAlexander Duyck  *   control for protocols which do not have built-in congestion management.
442f90b865SAlexander Duyck  *
452f90b865SAlexander Duyck  * More information about the emerging standards for these Ethernet features
462f90b865SAlexander Duyck  * can be found at: http://www.ieee802.org/1/pages/dcbridges.html
472f90b865SAlexander Duyck  *
482f90b865SAlexander Duyck  * This file implements an rtnetlink interface to allow configuration of DCB
492f90b865SAlexander Duyck  * features for capable devices.
502f90b865SAlexander Duyck  */
512f90b865SAlexander Duyck 
522f90b865SAlexander Duyck MODULE_AUTHOR("Lucy Liu, <lucy.liu@intel.com>");
537a6b6f51SJeff Kirsher MODULE_DESCRIPTION("Data Center Bridging netlink interface");
542f90b865SAlexander Duyck MODULE_LICENSE("GPL");
552f90b865SAlexander Duyck 
562f90b865SAlexander Duyck /**************** DCB attribute policies *************************************/
572f90b865SAlexander Duyck 
582f90b865SAlexander Duyck /* DCB netlink attributes policy */
59b54452b0SAlexey Dobriyan static const struct nla_policy dcbnl_rtnl_policy[DCB_ATTR_MAX + 1] = {
60859ee3c4SAlexander Duyck 	[DCB_ATTR_IFNAME]      = {.type = NLA_NUL_STRING, .len = IFNAMSIZ - 1},
612f90b865SAlexander Duyck 	[DCB_ATTR_STATE]       = {.type = NLA_U8},
622f90b865SAlexander Duyck 	[DCB_ATTR_PFC_CFG]     = {.type = NLA_NESTED},
632f90b865SAlexander Duyck 	[DCB_ATTR_PG_CFG]      = {.type = NLA_NESTED},
642f90b865SAlexander Duyck 	[DCB_ATTR_SET_ALL]     = {.type = NLA_U8},
652f90b865SAlexander Duyck 	[DCB_ATTR_PERM_HWADDR] = {.type = NLA_FLAG},
6646132188SAlexander Duyck 	[DCB_ATTR_CAP]         = {.type = NLA_NESTED},
670eb3aa9bSAlexander Duyck 	[DCB_ATTR_PFC_STATE]   = {.type = NLA_U8},
68859ee3c4SAlexander Duyck 	[DCB_ATTR_BCN]         = {.type = NLA_NESTED},
696fa382afSYi Zou 	[DCB_ATTR_APP]         = {.type = NLA_NESTED},
703e29027aSJohn Fastabend 	[DCB_ATTR_IEEE]	       = {.type = NLA_NESTED},
716241b625SShmulik Ravid 	[DCB_ATTR_DCBX]        = {.type = NLA_U8},
72ea45fe4eSShmulik Ravid 	[DCB_ATTR_FEATCFG]     = {.type = NLA_NESTED},
732f90b865SAlexander Duyck };
742f90b865SAlexander Duyck 
752f90b865SAlexander Duyck /* DCB priority flow control to User Priority nested attributes */
76b54452b0SAlexey Dobriyan static const struct nla_policy dcbnl_pfc_up_nest[DCB_PFC_UP_ATTR_MAX + 1] = {
772f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_0]   = {.type = NLA_U8},
782f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_1]   = {.type = NLA_U8},
792f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_2]   = {.type = NLA_U8},
802f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_3]   = {.type = NLA_U8},
812f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_4]   = {.type = NLA_U8},
822f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_5]   = {.type = NLA_U8},
832f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_6]   = {.type = NLA_U8},
842f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_7]   = {.type = NLA_U8},
852f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_ALL] = {.type = NLA_FLAG},
862f90b865SAlexander Duyck };
872f90b865SAlexander Duyck 
882f90b865SAlexander Duyck /* DCB priority grouping nested attributes */
89b54452b0SAlexey Dobriyan static const struct nla_policy dcbnl_pg_nest[DCB_PG_ATTR_MAX + 1] = {
902f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_0]      = {.type = NLA_NESTED},
912f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_1]      = {.type = NLA_NESTED},
922f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_2]      = {.type = NLA_NESTED},
932f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_3]      = {.type = NLA_NESTED},
942f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_4]      = {.type = NLA_NESTED},
952f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_5]      = {.type = NLA_NESTED},
962f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_6]      = {.type = NLA_NESTED},
972f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_7]      = {.type = NLA_NESTED},
982f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_ALL]    = {.type = NLA_NESTED},
992f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_0]   = {.type = NLA_U8},
1002f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_1]   = {.type = NLA_U8},
1012f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_2]   = {.type = NLA_U8},
1022f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_3]   = {.type = NLA_U8},
1032f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_4]   = {.type = NLA_U8},
1042f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_5]   = {.type = NLA_U8},
1052f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_6]   = {.type = NLA_U8},
1062f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_7]   = {.type = NLA_U8},
1072f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_ALL] = {.type = NLA_FLAG},
1082f90b865SAlexander Duyck };
1092f90b865SAlexander Duyck 
1102f90b865SAlexander Duyck /* DCB traffic class nested attributes. */
111b54452b0SAlexey Dobriyan static const struct nla_policy dcbnl_tc_param_nest[DCB_TC_ATTR_PARAM_MAX + 1] = {
1122f90b865SAlexander Duyck 	[DCB_TC_ATTR_PARAM_PGID]            = {.type = NLA_U8},
1132f90b865SAlexander Duyck 	[DCB_TC_ATTR_PARAM_UP_MAPPING]      = {.type = NLA_U8},
1142f90b865SAlexander Duyck 	[DCB_TC_ATTR_PARAM_STRICT_PRIO]     = {.type = NLA_U8},
1152f90b865SAlexander Duyck 	[DCB_TC_ATTR_PARAM_BW_PCT]          = {.type = NLA_U8},
1162f90b865SAlexander Duyck 	[DCB_TC_ATTR_PARAM_ALL]             = {.type = NLA_FLAG},
1172f90b865SAlexander Duyck };
1182f90b865SAlexander Duyck 
11946132188SAlexander Duyck /* DCB capabilities nested attributes. */
120b54452b0SAlexey Dobriyan static const struct nla_policy dcbnl_cap_nest[DCB_CAP_ATTR_MAX + 1] = {
12146132188SAlexander Duyck 	[DCB_CAP_ATTR_ALL]     = {.type = NLA_FLAG},
12246132188SAlexander Duyck 	[DCB_CAP_ATTR_PG]      = {.type = NLA_U8},
12346132188SAlexander Duyck 	[DCB_CAP_ATTR_PFC]     = {.type = NLA_U8},
12446132188SAlexander Duyck 	[DCB_CAP_ATTR_UP2TC]   = {.type = NLA_U8},
12546132188SAlexander Duyck 	[DCB_CAP_ATTR_PG_TCS]  = {.type = NLA_U8},
12646132188SAlexander Duyck 	[DCB_CAP_ATTR_PFC_TCS] = {.type = NLA_U8},
12746132188SAlexander Duyck 	[DCB_CAP_ATTR_GSP]     = {.type = NLA_U8},
12846132188SAlexander Duyck 	[DCB_CAP_ATTR_BCN]     = {.type = NLA_U8},
1296241b625SShmulik Ravid 	[DCB_CAP_ATTR_DCBX]    = {.type = NLA_U8},
13046132188SAlexander Duyck };
1312f90b865SAlexander Duyck 
13233dbabc4SAlexander Duyck /* DCB capabilities nested attributes. */
133b54452b0SAlexey Dobriyan static const struct nla_policy dcbnl_numtcs_nest[DCB_NUMTCS_ATTR_MAX + 1] = {
13433dbabc4SAlexander Duyck 	[DCB_NUMTCS_ATTR_ALL]     = {.type = NLA_FLAG},
13533dbabc4SAlexander Duyck 	[DCB_NUMTCS_ATTR_PG]      = {.type = NLA_U8},
13633dbabc4SAlexander Duyck 	[DCB_NUMTCS_ATTR_PFC]     = {.type = NLA_U8},
13733dbabc4SAlexander Duyck };
13833dbabc4SAlexander Duyck 
139859ee3c4SAlexander Duyck /* DCB BCN nested attributes. */
140b54452b0SAlexey Dobriyan static const struct nla_policy dcbnl_bcn_nest[DCB_BCN_ATTR_MAX + 1] = {
141859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_0]         = {.type = NLA_U8},
142859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_1]         = {.type = NLA_U8},
143859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_2]         = {.type = NLA_U8},
144859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_3]         = {.type = NLA_U8},
145859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_4]         = {.type = NLA_U8},
146859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_5]         = {.type = NLA_U8},
147859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_6]         = {.type = NLA_U8},
148859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_7]         = {.type = NLA_U8},
149859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_ALL]       = {.type = NLA_FLAG},
150f4314e81SDon Skidmore 	[DCB_BCN_ATTR_BCNA_0]       = {.type = NLA_U32},
151f4314e81SDon Skidmore 	[DCB_BCN_ATTR_BCNA_1]       = {.type = NLA_U32},
152859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_ALPHA]        = {.type = NLA_U32},
153859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_BETA]         = {.type = NLA_U32},
154859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_GD]           = {.type = NLA_U32},
155859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_GI]           = {.type = NLA_U32},
156859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_TMAX]         = {.type = NLA_U32},
157859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_TD]           = {.type = NLA_U32},
158859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RMIN]         = {.type = NLA_U32},
159859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_W]            = {.type = NLA_U32},
160859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RD]           = {.type = NLA_U32},
161859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RU]           = {.type = NLA_U32},
162859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_WRTT]         = {.type = NLA_U32},
163859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RI]           = {.type = NLA_U32},
164859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_C]            = {.type = NLA_U32},
165859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_ALL]          = {.type = NLA_FLAG},
166859ee3c4SAlexander Duyck };
167859ee3c4SAlexander Duyck 
1686fa382afSYi Zou /* DCB APP nested attributes. */
169b54452b0SAlexey Dobriyan static const struct nla_policy dcbnl_app_nest[DCB_APP_ATTR_MAX + 1] = {
1706fa382afSYi Zou 	[DCB_APP_ATTR_IDTYPE]       = {.type = NLA_U8},
1716fa382afSYi Zou 	[DCB_APP_ATTR_ID]           = {.type = NLA_U16},
1726fa382afSYi Zou 	[DCB_APP_ATTR_PRIORITY]     = {.type = NLA_U8},
1736fa382afSYi Zou };
1746fa382afSYi Zou 
1753e29027aSJohn Fastabend /* IEEE 802.1Qaz nested attributes. */
1763e29027aSJohn Fastabend static const struct nla_policy dcbnl_ieee_policy[DCB_ATTR_IEEE_MAX + 1] = {
1773e29027aSJohn Fastabend 	[DCB_ATTR_IEEE_ETS]	    = {.len = sizeof(struct ieee_ets)},
1783e29027aSJohn Fastabend 	[DCB_ATTR_IEEE_PFC]	    = {.len = sizeof(struct ieee_pfc)},
1793e29027aSJohn Fastabend 	[DCB_ATTR_IEEE_APP_TABLE]   = {.type = NLA_NESTED},
1803e29027aSJohn Fastabend };
1813e29027aSJohn Fastabend 
1823e29027aSJohn Fastabend static const struct nla_policy dcbnl_ieee_app[DCB_ATTR_IEEE_APP_MAX + 1] = {
1833e29027aSJohn Fastabend 	[DCB_ATTR_IEEE_APP]	    = {.len = sizeof(struct dcb_app)},
1843e29027aSJohn Fastabend };
1853e29027aSJohn Fastabend 
186ea45fe4eSShmulik Ravid /* DCB number of traffic classes nested attributes. */
187ea45fe4eSShmulik Ravid static const struct nla_policy dcbnl_featcfg_nest[DCB_FEATCFG_ATTR_MAX + 1] = {
188ea45fe4eSShmulik Ravid 	[DCB_FEATCFG_ATTR_ALL]      = {.type = NLA_FLAG},
189ea45fe4eSShmulik Ravid 	[DCB_FEATCFG_ATTR_PG]       = {.type = NLA_U8},
190ea45fe4eSShmulik Ravid 	[DCB_FEATCFG_ATTR_PFC]      = {.type = NLA_U8},
191ea45fe4eSShmulik Ravid 	[DCB_FEATCFG_ATTR_APP]      = {.type = NLA_U8},
192ea45fe4eSShmulik Ravid };
193ea45fe4eSShmulik Ravid 
1949ab933abSJohn Fastabend static LIST_HEAD(dcb_app_list);
1959ab933abSJohn Fastabend static DEFINE_SPINLOCK(dcb_lock);
1969ab933abSJohn Fastabend 
1972f90b865SAlexander Duyck /* standard netlink reply call */
1982f90b865SAlexander Duyck static int dcbnl_reply(u8 value, u8 event, u8 cmd, u8 attr, u32 pid,
1992f90b865SAlexander Duyck                        u32 seq, u16 flags)
2002f90b865SAlexander Duyck {
2012f90b865SAlexander Duyck 	struct sk_buff *dcbnl_skb;
2022f90b865SAlexander Duyck 	struct dcbmsg *dcb;
2032f90b865SAlexander Duyck 	struct nlmsghdr *nlh;
2042f90b865SAlexander Duyck 	int ret = -EINVAL;
2052f90b865SAlexander Duyck 
2062f90b865SAlexander Duyck 	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
2072f90b865SAlexander Duyck 	if (!dcbnl_skb)
2082f90b865SAlexander Duyck 		return ret;
2092f90b865SAlexander Duyck 
2102f90b865SAlexander Duyck 	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, event, sizeof(*dcb), flags);
2112f90b865SAlexander Duyck 
2122f90b865SAlexander Duyck 	dcb = NLMSG_DATA(nlh);
2132f90b865SAlexander Duyck 	dcb->dcb_family = AF_UNSPEC;
2142f90b865SAlexander Duyck 	dcb->cmd = cmd;
2152f90b865SAlexander Duyck 	dcb->dcb_pad = 0;
2162f90b865SAlexander Duyck 
2172f90b865SAlexander Duyck 	ret = nla_put_u8(dcbnl_skb, attr, value);
2182f90b865SAlexander Duyck 	if (ret)
2192f90b865SAlexander Duyck 		goto err;
2202f90b865SAlexander Duyck 
2212f90b865SAlexander Duyck 	/* end the message, assign the nlmsg_len. */
2222f90b865SAlexander Duyck 	nlmsg_end(dcbnl_skb, nlh);
2232f90b865SAlexander Duyck 	ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
2242f90b865SAlexander Duyck 	if (ret)
2257eaf5077SJohn Fastabend 		return -EINVAL;
2262f90b865SAlexander Duyck 
2272f90b865SAlexander Duyck 	return 0;
2282f90b865SAlexander Duyck nlmsg_failure:
2292f90b865SAlexander Duyck err:
230858eb711SRoel Kluin 	kfree_skb(dcbnl_skb);
2312f90b865SAlexander Duyck 	return ret;
2322f90b865SAlexander Duyck }
2332f90b865SAlexander Duyck 
2342f90b865SAlexander Duyck static int dcbnl_getstate(struct net_device *netdev, struct nlattr **tb,
2352f90b865SAlexander Duyck                           u32 pid, u32 seq, u16 flags)
2362f90b865SAlexander Duyck {
2372f90b865SAlexander Duyck 	int ret = -EINVAL;
2382f90b865SAlexander Duyck 
2392f90b865SAlexander Duyck 	/* if (!tb[DCB_ATTR_STATE] || !netdev->dcbnl_ops->getstate) */
2402f90b865SAlexander Duyck 	if (!netdev->dcbnl_ops->getstate)
2412f90b865SAlexander Duyck 		return ret;
2422f90b865SAlexander Duyck 
2432f90b865SAlexander Duyck 	ret = dcbnl_reply(netdev->dcbnl_ops->getstate(netdev), RTM_GETDCB,
2442f90b865SAlexander Duyck 	                  DCB_CMD_GSTATE, DCB_ATTR_STATE, pid, seq, flags);
2452f90b865SAlexander Duyck 
2462f90b865SAlexander Duyck 	return ret;
2472f90b865SAlexander Duyck }
2482f90b865SAlexander Duyck 
2492f90b865SAlexander Duyck static int dcbnl_getpfccfg(struct net_device *netdev, struct nlattr **tb,
2502f90b865SAlexander Duyck                            u32 pid, u32 seq, u16 flags)
2512f90b865SAlexander Duyck {
2522f90b865SAlexander Duyck 	struct sk_buff *dcbnl_skb;
2532f90b865SAlexander Duyck 	struct nlmsghdr *nlh;
2542f90b865SAlexander Duyck 	struct dcbmsg *dcb;
2552f90b865SAlexander Duyck 	struct nlattr *data[DCB_PFC_UP_ATTR_MAX + 1], *nest;
2562f90b865SAlexander Duyck 	u8 value;
2572f90b865SAlexander Duyck 	int ret = -EINVAL;
2582f90b865SAlexander Duyck 	int i;
2592f90b865SAlexander Duyck 	int getall = 0;
2602f90b865SAlexander Duyck 
2612f90b865SAlexander Duyck 	if (!tb[DCB_ATTR_PFC_CFG] || !netdev->dcbnl_ops->getpfccfg)
2622f90b865SAlexander Duyck 		return ret;
2632f90b865SAlexander Duyck 
2642f90b865SAlexander Duyck 	ret = nla_parse_nested(data, DCB_PFC_UP_ATTR_MAX,
2652f90b865SAlexander Duyck 	                       tb[DCB_ATTR_PFC_CFG],
2662f90b865SAlexander Duyck 	                       dcbnl_pfc_up_nest);
2672f90b865SAlexander Duyck 	if (ret)
2682f90b865SAlexander Duyck 		goto err_out;
2692f90b865SAlexander Duyck 
2702f90b865SAlexander Duyck 	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
2712f90b865SAlexander Duyck 	if (!dcbnl_skb)
2722f90b865SAlexander Duyck 		goto err_out;
2732f90b865SAlexander Duyck 
2742f90b865SAlexander Duyck 	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
2752f90b865SAlexander Duyck 
2762f90b865SAlexander Duyck 	dcb = NLMSG_DATA(nlh);
2772f90b865SAlexander Duyck 	dcb->dcb_family = AF_UNSPEC;
2782f90b865SAlexander Duyck 	dcb->cmd = DCB_CMD_PFC_GCFG;
2792f90b865SAlexander Duyck 
2802f90b865SAlexander Duyck 	nest = nla_nest_start(dcbnl_skb, DCB_ATTR_PFC_CFG);
2812f90b865SAlexander Duyck 	if (!nest)
2822f90b865SAlexander Duyck 		goto err;
2832f90b865SAlexander Duyck 
2842f90b865SAlexander Duyck 	if (data[DCB_PFC_UP_ATTR_ALL])
2852f90b865SAlexander Duyck 		getall = 1;
2862f90b865SAlexander Duyck 
2872f90b865SAlexander Duyck 	for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) {
2882f90b865SAlexander Duyck 		if (!getall && !data[i])
2892f90b865SAlexander Duyck 			continue;
2902f90b865SAlexander Duyck 
2912f90b865SAlexander Duyck 		netdev->dcbnl_ops->getpfccfg(netdev, i - DCB_PFC_UP_ATTR_0,
2922f90b865SAlexander Duyck 		                             &value);
2932f90b865SAlexander Duyck 		ret = nla_put_u8(dcbnl_skb, i, value);
2942f90b865SAlexander Duyck 
2952f90b865SAlexander Duyck 		if (ret) {
2962f90b865SAlexander Duyck 			nla_nest_cancel(dcbnl_skb, nest);
2972f90b865SAlexander Duyck 			goto err;
2982f90b865SAlexander Duyck 		}
2992f90b865SAlexander Duyck 	}
3002f90b865SAlexander Duyck 	nla_nest_end(dcbnl_skb, nest);
3012f90b865SAlexander Duyck 
3022f90b865SAlexander Duyck 	nlmsg_end(dcbnl_skb, nlh);
3032f90b865SAlexander Duyck 
3042f90b865SAlexander Duyck 	ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
3052f90b865SAlexander Duyck 	if (ret)
3067eaf5077SJohn Fastabend 		goto err_out;
3072f90b865SAlexander Duyck 
3082f90b865SAlexander Duyck 	return 0;
3092f90b865SAlexander Duyck nlmsg_failure:
3102f90b865SAlexander Duyck err:
311858eb711SRoel Kluin 	kfree_skb(dcbnl_skb);
3122f90b865SAlexander Duyck err_out:
3132f90b865SAlexander Duyck 	return -EINVAL;
3142f90b865SAlexander Duyck }
3152f90b865SAlexander Duyck 
3162f90b865SAlexander Duyck static int dcbnl_getperm_hwaddr(struct net_device *netdev, struct nlattr **tb,
3172f90b865SAlexander Duyck                                 u32 pid, u32 seq, u16 flags)
3182f90b865SAlexander Duyck {
3192f90b865SAlexander Duyck 	struct sk_buff *dcbnl_skb;
3202f90b865SAlexander Duyck 	struct nlmsghdr *nlh;
3212f90b865SAlexander Duyck 	struct dcbmsg *dcb;
3222f90b865SAlexander Duyck 	u8 perm_addr[MAX_ADDR_LEN];
3232f90b865SAlexander Duyck 	int ret = -EINVAL;
3242f90b865SAlexander Duyck 
3252f90b865SAlexander Duyck 	if (!netdev->dcbnl_ops->getpermhwaddr)
3262f90b865SAlexander Duyck 		return ret;
3272f90b865SAlexander Duyck 
3282f90b865SAlexander Duyck 	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
3292f90b865SAlexander Duyck 	if (!dcbnl_skb)
3302f90b865SAlexander Duyck 		goto err_out;
3312f90b865SAlexander Duyck 
3322f90b865SAlexander Duyck 	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
3332f90b865SAlexander Duyck 
3342f90b865SAlexander Duyck 	dcb = NLMSG_DATA(nlh);
3352f90b865SAlexander Duyck 	dcb->dcb_family = AF_UNSPEC;
3362f90b865SAlexander Duyck 	dcb->cmd = DCB_CMD_GPERM_HWADDR;
3372f90b865SAlexander Duyck 
3382f90b865SAlexander Duyck 	netdev->dcbnl_ops->getpermhwaddr(netdev, perm_addr);
3392f90b865SAlexander Duyck 
3402f90b865SAlexander Duyck 	ret = nla_put(dcbnl_skb, DCB_ATTR_PERM_HWADDR, sizeof(perm_addr),
3412f90b865SAlexander Duyck 	              perm_addr);
3422f90b865SAlexander Duyck 
3432f90b865SAlexander Duyck 	nlmsg_end(dcbnl_skb, nlh);
3442f90b865SAlexander Duyck 
3452f90b865SAlexander Duyck 	ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
3462f90b865SAlexander Duyck 	if (ret)
3477eaf5077SJohn Fastabend 		goto err_out;
3482f90b865SAlexander Duyck 
3492f90b865SAlexander Duyck 	return 0;
3502f90b865SAlexander Duyck 
3512f90b865SAlexander Duyck nlmsg_failure:
352858eb711SRoel Kluin 	kfree_skb(dcbnl_skb);
3532f90b865SAlexander Duyck err_out:
3542f90b865SAlexander Duyck 	return -EINVAL;
3552f90b865SAlexander Duyck }
3562f90b865SAlexander Duyck 
35746132188SAlexander Duyck static int dcbnl_getcap(struct net_device *netdev, struct nlattr **tb,
35846132188SAlexander Duyck                         u32 pid, u32 seq, u16 flags)
35946132188SAlexander Duyck {
36046132188SAlexander Duyck 	struct sk_buff *dcbnl_skb;
36146132188SAlexander Duyck 	struct nlmsghdr *nlh;
36246132188SAlexander Duyck 	struct dcbmsg *dcb;
36346132188SAlexander Duyck 	struct nlattr *data[DCB_CAP_ATTR_MAX + 1], *nest;
36446132188SAlexander Duyck 	u8 value;
36546132188SAlexander Duyck 	int ret = -EINVAL;
36646132188SAlexander Duyck 	int i;
36746132188SAlexander Duyck 	int getall = 0;
36846132188SAlexander Duyck 
36946132188SAlexander Duyck 	if (!tb[DCB_ATTR_CAP] || !netdev->dcbnl_ops->getcap)
37046132188SAlexander Duyck 		return ret;
37146132188SAlexander Duyck 
37246132188SAlexander Duyck 	ret = nla_parse_nested(data, DCB_CAP_ATTR_MAX, tb[DCB_ATTR_CAP],
37346132188SAlexander Duyck 	                       dcbnl_cap_nest);
37446132188SAlexander Duyck 	if (ret)
37546132188SAlexander Duyck 		goto err_out;
37646132188SAlexander Duyck 
37746132188SAlexander Duyck 	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
37846132188SAlexander Duyck 	if (!dcbnl_skb)
37946132188SAlexander Duyck 		goto err_out;
38046132188SAlexander Duyck 
38146132188SAlexander Duyck 	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
38246132188SAlexander Duyck 
38346132188SAlexander Duyck 	dcb = NLMSG_DATA(nlh);
38446132188SAlexander Duyck 	dcb->dcb_family = AF_UNSPEC;
38546132188SAlexander Duyck 	dcb->cmd = DCB_CMD_GCAP;
38646132188SAlexander Duyck 
38746132188SAlexander Duyck 	nest = nla_nest_start(dcbnl_skb, DCB_ATTR_CAP);
38846132188SAlexander Duyck 	if (!nest)
38946132188SAlexander Duyck 		goto err;
39046132188SAlexander Duyck 
39146132188SAlexander Duyck 	if (data[DCB_CAP_ATTR_ALL])
39246132188SAlexander Duyck 		getall = 1;
39346132188SAlexander Duyck 
39446132188SAlexander Duyck 	for (i = DCB_CAP_ATTR_ALL+1; i <= DCB_CAP_ATTR_MAX; i++) {
39546132188SAlexander Duyck 		if (!getall && !data[i])
39646132188SAlexander Duyck 			continue;
39746132188SAlexander Duyck 
39846132188SAlexander Duyck 		if (!netdev->dcbnl_ops->getcap(netdev, i, &value)) {
39946132188SAlexander Duyck 			ret = nla_put_u8(dcbnl_skb, i, value);
40046132188SAlexander Duyck 
40146132188SAlexander Duyck 			if (ret) {
40246132188SAlexander Duyck 				nla_nest_cancel(dcbnl_skb, nest);
40346132188SAlexander Duyck 				goto err;
40446132188SAlexander Duyck 			}
40546132188SAlexander Duyck 		}
40646132188SAlexander Duyck 	}
40746132188SAlexander Duyck 	nla_nest_end(dcbnl_skb, nest);
40846132188SAlexander Duyck 
40946132188SAlexander Duyck 	nlmsg_end(dcbnl_skb, nlh);
41046132188SAlexander Duyck 
41146132188SAlexander Duyck 	ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
41246132188SAlexander Duyck 	if (ret)
4137eaf5077SJohn Fastabend 		goto err_out;
41446132188SAlexander Duyck 
41546132188SAlexander Duyck 	return 0;
41646132188SAlexander Duyck nlmsg_failure:
41746132188SAlexander Duyck err:
418858eb711SRoel Kluin 	kfree_skb(dcbnl_skb);
41946132188SAlexander Duyck err_out:
42046132188SAlexander Duyck 	return -EINVAL;
42146132188SAlexander Duyck }
42246132188SAlexander Duyck 
42333dbabc4SAlexander Duyck static int dcbnl_getnumtcs(struct net_device *netdev, struct nlattr **tb,
42433dbabc4SAlexander Duyck                            u32 pid, u32 seq, u16 flags)
42533dbabc4SAlexander Duyck {
42633dbabc4SAlexander Duyck 	struct sk_buff *dcbnl_skb;
42733dbabc4SAlexander Duyck 	struct nlmsghdr *nlh;
42833dbabc4SAlexander Duyck 	struct dcbmsg *dcb;
42933dbabc4SAlexander Duyck 	struct nlattr *data[DCB_NUMTCS_ATTR_MAX + 1], *nest;
43033dbabc4SAlexander Duyck 	u8 value;
43133dbabc4SAlexander Duyck 	int ret = -EINVAL;
43233dbabc4SAlexander Duyck 	int i;
43333dbabc4SAlexander Duyck 	int getall = 0;
43433dbabc4SAlexander Duyck 
43533dbabc4SAlexander Duyck 	if (!tb[DCB_ATTR_NUMTCS] || !netdev->dcbnl_ops->getnumtcs)
43633dbabc4SAlexander Duyck 		return ret;
43733dbabc4SAlexander Duyck 
43833dbabc4SAlexander Duyck 	ret = nla_parse_nested(data, DCB_NUMTCS_ATTR_MAX, tb[DCB_ATTR_NUMTCS],
43933dbabc4SAlexander Duyck 	                       dcbnl_numtcs_nest);
44033dbabc4SAlexander Duyck 	if (ret) {
44133dbabc4SAlexander Duyck 		ret = -EINVAL;
44233dbabc4SAlexander Duyck 		goto err_out;
44333dbabc4SAlexander Duyck 	}
44433dbabc4SAlexander Duyck 
44533dbabc4SAlexander Duyck 	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
44633dbabc4SAlexander Duyck 	if (!dcbnl_skb) {
44733dbabc4SAlexander Duyck 		ret = -EINVAL;
44833dbabc4SAlexander Duyck 		goto err_out;
44933dbabc4SAlexander Duyck 	}
45033dbabc4SAlexander Duyck 
45133dbabc4SAlexander Duyck 	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
45233dbabc4SAlexander Duyck 
45333dbabc4SAlexander Duyck 	dcb = NLMSG_DATA(nlh);
45433dbabc4SAlexander Duyck 	dcb->dcb_family = AF_UNSPEC;
45533dbabc4SAlexander Duyck 	dcb->cmd = DCB_CMD_GNUMTCS;
45633dbabc4SAlexander Duyck 
45733dbabc4SAlexander Duyck 	nest = nla_nest_start(dcbnl_skb, DCB_ATTR_NUMTCS);
45833dbabc4SAlexander Duyck 	if (!nest) {
45933dbabc4SAlexander Duyck 		ret = -EINVAL;
46033dbabc4SAlexander Duyck 		goto err;
46133dbabc4SAlexander Duyck 	}
46233dbabc4SAlexander Duyck 
46333dbabc4SAlexander Duyck 	if (data[DCB_NUMTCS_ATTR_ALL])
46433dbabc4SAlexander Duyck 		getall = 1;
46533dbabc4SAlexander Duyck 
46633dbabc4SAlexander Duyck 	for (i = DCB_NUMTCS_ATTR_ALL+1; i <= DCB_NUMTCS_ATTR_MAX; i++) {
46733dbabc4SAlexander Duyck 		if (!getall && !data[i])
46833dbabc4SAlexander Duyck 			continue;
46933dbabc4SAlexander Duyck 
47033dbabc4SAlexander Duyck 		ret = netdev->dcbnl_ops->getnumtcs(netdev, i, &value);
47133dbabc4SAlexander Duyck 		if (!ret) {
47233dbabc4SAlexander Duyck 			ret = nla_put_u8(dcbnl_skb, i, value);
47333dbabc4SAlexander Duyck 
47433dbabc4SAlexander Duyck 			if (ret) {
47533dbabc4SAlexander Duyck 				nla_nest_cancel(dcbnl_skb, nest);
47633dbabc4SAlexander Duyck 				ret = -EINVAL;
47733dbabc4SAlexander Duyck 				goto err;
47833dbabc4SAlexander Duyck 			}
47933dbabc4SAlexander Duyck 		} else {
48033dbabc4SAlexander Duyck 			goto err;
48133dbabc4SAlexander Duyck 		}
48233dbabc4SAlexander Duyck 	}
48333dbabc4SAlexander Duyck 	nla_nest_end(dcbnl_skb, nest);
48433dbabc4SAlexander Duyck 
48533dbabc4SAlexander Duyck 	nlmsg_end(dcbnl_skb, nlh);
48633dbabc4SAlexander Duyck 
48733dbabc4SAlexander Duyck 	ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
48833dbabc4SAlexander Duyck 	if (ret) {
48933dbabc4SAlexander Duyck 		ret = -EINVAL;
4907eaf5077SJohn Fastabend 		goto err_out;
49133dbabc4SAlexander Duyck 	}
49233dbabc4SAlexander Duyck 
49333dbabc4SAlexander Duyck 	return 0;
49433dbabc4SAlexander Duyck nlmsg_failure:
49533dbabc4SAlexander Duyck err:
496858eb711SRoel Kluin 	kfree_skb(dcbnl_skb);
49733dbabc4SAlexander Duyck err_out:
49833dbabc4SAlexander Duyck 	return ret;
49933dbabc4SAlexander Duyck }
50033dbabc4SAlexander Duyck 
50133dbabc4SAlexander Duyck static int dcbnl_setnumtcs(struct net_device *netdev, struct nlattr **tb,
50233dbabc4SAlexander Duyck                            u32 pid, u32 seq, u16 flags)
50333dbabc4SAlexander Duyck {
50433dbabc4SAlexander Duyck 	struct nlattr *data[DCB_NUMTCS_ATTR_MAX + 1];
50533dbabc4SAlexander Duyck 	int ret = -EINVAL;
50633dbabc4SAlexander Duyck 	u8 value;
50733dbabc4SAlexander Duyck 	int i;
50833dbabc4SAlexander Duyck 
5098b124a8eSDon Skidmore 	if (!tb[DCB_ATTR_NUMTCS] || !netdev->dcbnl_ops->setnumtcs)
51033dbabc4SAlexander Duyck 		return ret;
51133dbabc4SAlexander Duyck 
51233dbabc4SAlexander Duyck 	ret = nla_parse_nested(data, DCB_NUMTCS_ATTR_MAX, tb[DCB_ATTR_NUMTCS],
51333dbabc4SAlexander Duyck 	                       dcbnl_numtcs_nest);
51433dbabc4SAlexander Duyck 
51533dbabc4SAlexander Duyck 	if (ret) {
51633dbabc4SAlexander Duyck 		ret = -EINVAL;
51733dbabc4SAlexander Duyck 		goto err;
51833dbabc4SAlexander Duyck 	}
51933dbabc4SAlexander Duyck 
52033dbabc4SAlexander Duyck 	for (i = DCB_NUMTCS_ATTR_ALL+1; i <= DCB_NUMTCS_ATTR_MAX; i++) {
52133dbabc4SAlexander Duyck 		if (data[i] == NULL)
52233dbabc4SAlexander Duyck 			continue;
52333dbabc4SAlexander Duyck 
52433dbabc4SAlexander Duyck 		value = nla_get_u8(data[i]);
52533dbabc4SAlexander Duyck 
52633dbabc4SAlexander Duyck 		ret = netdev->dcbnl_ops->setnumtcs(netdev, i, value);
52733dbabc4SAlexander Duyck 
52833dbabc4SAlexander Duyck 		if (ret)
52933dbabc4SAlexander Duyck 			goto operr;
53033dbabc4SAlexander Duyck 	}
53133dbabc4SAlexander Duyck 
53233dbabc4SAlexander Duyck operr:
53333dbabc4SAlexander Duyck 	ret = dcbnl_reply(!!ret, RTM_SETDCB, DCB_CMD_SNUMTCS,
53433dbabc4SAlexander Duyck 	                  DCB_ATTR_NUMTCS, pid, seq, flags);
53533dbabc4SAlexander Duyck 
53633dbabc4SAlexander Duyck err:
53733dbabc4SAlexander Duyck 	return ret;
53833dbabc4SAlexander Duyck }
53933dbabc4SAlexander Duyck 
5400eb3aa9bSAlexander Duyck static int dcbnl_getpfcstate(struct net_device *netdev, struct nlattr **tb,
5410eb3aa9bSAlexander Duyck                              u32 pid, u32 seq, u16 flags)
5420eb3aa9bSAlexander Duyck {
5430eb3aa9bSAlexander Duyck 	int ret = -EINVAL;
5440eb3aa9bSAlexander Duyck 
5450eb3aa9bSAlexander Duyck 	if (!netdev->dcbnl_ops->getpfcstate)
5460eb3aa9bSAlexander Duyck 		return ret;
5470eb3aa9bSAlexander Duyck 
5480eb3aa9bSAlexander Duyck 	ret = dcbnl_reply(netdev->dcbnl_ops->getpfcstate(netdev), RTM_GETDCB,
5490eb3aa9bSAlexander Duyck 	                  DCB_CMD_PFC_GSTATE, DCB_ATTR_PFC_STATE,
5500eb3aa9bSAlexander Duyck 	                  pid, seq, flags);
5510eb3aa9bSAlexander Duyck 
5520eb3aa9bSAlexander Duyck 	return ret;
5530eb3aa9bSAlexander Duyck }
5540eb3aa9bSAlexander Duyck 
5550eb3aa9bSAlexander Duyck static int dcbnl_setpfcstate(struct net_device *netdev, struct nlattr **tb,
5560eb3aa9bSAlexander Duyck                              u32 pid, u32 seq, u16 flags)
5570eb3aa9bSAlexander Duyck {
5580eb3aa9bSAlexander Duyck 	int ret = -EINVAL;
5590eb3aa9bSAlexander Duyck 	u8 value;
5600eb3aa9bSAlexander Duyck 
5610eb3aa9bSAlexander Duyck 	if (!tb[DCB_ATTR_PFC_STATE] || !netdev->dcbnl_ops->setpfcstate)
5620eb3aa9bSAlexander Duyck 		return ret;
5630eb3aa9bSAlexander Duyck 
5640eb3aa9bSAlexander Duyck 	value = nla_get_u8(tb[DCB_ATTR_PFC_STATE]);
5650eb3aa9bSAlexander Duyck 
5660eb3aa9bSAlexander Duyck 	netdev->dcbnl_ops->setpfcstate(netdev, value);
5670eb3aa9bSAlexander Duyck 
5680eb3aa9bSAlexander Duyck 	ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_PFC_SSTATE, DCB_ATTR_PFC_STATE,
5690eb3aa9bSAlexander Duyck 	                  pid, seq, flags);
5700eb3aa9bSAlexander Duyck 
5710eb3aa9bSAlexander Duyck 	return ret;
5720eb3aa9bSAlexander Duyck }
5730eb3aa9bSAlexander Duyck 
57457949686SYi Zou static int dcbnl_getapp(struct net_device *netdev, struct nlattr **tb,
57557949686SYi Zou                         u32 pid, u32 seq, u16 flags)
57657949686SYi Zou {
57757949686SYi Zou 	struct sk_buff *dcbnl_skb;
57857949686SYi Zou 	struct nlmsghdr *nlh;
57957949686SYi Zou 	struct dcbmsg *dcb;
58057949686SYi Zou 	struct nlattr *app_nest;
58157949686SYi Zou 	struct nlattr *app_tb[DCB_APP_ATTR_MAX + 1];
58257949686SYi Zou 	u16 id;
58357949686SYi Zou 	u8 up, idtype;
58457949686SYi Zou 	int ret = -EINVAL;
58557949686SYi Zou 
5863dce38a0SJohn Fastabend 	if (!tb[DCB_ATTR_APP])
58757949686SYi Zou 		goto out;
58857949686SYi Zou 
58957949686SYi Zou 	ret = nla_parse_nested(app_tb, DCB_APP_ATTR_MAX, tb[DCB_ATTR_APP],
59057949686SYi Zou 	                       dcbnl_app_nest);
59157949686SYi Zou 	if (ret)
59257949686SYi Zou 		goto out;
59357949686SYi Zou 
59457949686SYi Zou 	ret = -EINVAL;
59557949686SYi Zou 	/* all must be non-null */
59657949686SYi Zou 	if ((!app_tb[DCB_APP_ATTR_IDTYPE]) ||
59757949686SYi Zou 	    (!app_tb[DCB_APP_ATTR_ID]))
59857949686SYi Zou 		goto out;
59957949686SYi Zou 
60057949686SYi Zou 	/* either by eth type or by socket number */
60157949686SYi Zou 	idtype = nla_get_u8(app_tb[DCB_APP_ATTR_IDTYPE]);
60257949686SYi Zou 	if ((idtype != DCB_APP_IDTYPE_ETHTYPE) &&
60357949686SYi Zou 	    (idtype != DCB_APP_IDTYPE_PORTNUM))
60457949686SYi Zou 		goto out;
60557949686SYi Zou 
60657949686SYi Zou 	id = nla_get_u16(app_tb[DCB_APP_ATTR_ID]);
6073dce38a0SJohn Fastabend 
6083dce38a0SJohn Fastabend 	if (netdev->dcbnl_ops->getapp) {
60957949686SYi Zou 		up = netdev->dcbnl_ops->getapp(netdev, idtype, id);
6103dce38a0SJohn Fastabend 	} else {
6113dce38a0SJohn Fastabend 		struct dcb_app app = {
6123dce38a0SJohn Fastabend 					.selector = idtype,
6133dce38a0SJohn Fastabend 					.protocol = id,
6143dce38a0SJohn Fastabend 				     };
6153dce38a0SJohn Fastabend 		up = dcb_getapp(netdev, &app);
6163dce38a0SJohn Fastabend 	}
61757949686SYi Zou 
61857949686SYi Zou 	/* send this back */
61957949686SYi Zou 	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
62057949686SYi Zou 	if (!dcbnl_skb)
62157949686SYi Zou 		goto out;
62257949686SYi Zou 
62357949686SYi Zou 	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
62457949686SYi Zou 	dcb = NLMSG_DATA(nlh);
62557949686SYi Zou 	dcb->dcb_family = AF_UNSPEC;
62657949686SYi Zou 	dcb->cmd = DCB_CMD_GAPP;
62757949686SYi Zou 
62857949686SYi Zou 	app_nest = nla_nest_start(dcbnl_skb, DCB_ATTR_APP);
629d3337de5SJesper Juhl 	if (!app_nest)
630d3337de5SJesper Juhl 		goto out_cancel;
631d3337de5SJesper Juhl 
63257949686SYi Zou 	ret = nla_put_u8(dcbnl_skb, DCB_APP_ATTR_IDTYPE, idtype);
63357949686SYi Zou 	if (ret)
63457949686SYi Zou 		goto out_cancel;
63557949686SYi Zou 
63657949686SYi Zou 	ret = nla_put_u16(dcbnl_skb, DCB_APP_ATTR_ID, id);
63757949686SYi Zou 	if (ret)
63857949686SYi Zou 		goto out_cancel;
63957949686SYi Zou 
64057949686SYi Zou 	ret = nla_put_u8(dcbnl_skb, DCB_APP_ATTR_PRIORITY, up);
64157949686SYi Zou 	if (ret)
64257949686SYi Zou 		goto out_cancel;
64357949686SYi Zou 
64457949686SYi Zou 	nla_nest_end(dcbnl_skb, app_nest);
64557949686SYi Zou 	nlmsg_end(dcbnl_skb, nlh);
64657949686SYi Zou 
64757949686SYi Zou 	ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
64857949686SYi Zou 	if (ret)
64957949686SYi Zou 		goto nlmsg_failure;
65057949686SYi Zou 
65157949686SYi Zou 	goto out;
65257949686SYi Zou 
65357949686SYi Zou out_cancel:
65457949686SYi Zou 	nla_nest_cancel(dcbnl_skb, app_nest);
65557949686SYi Zou nlmsg_failure:
65657949686SYi Zou 	kfree_skb(dcbnl_skb);
65757949686SYi Zou out:
65857949686SYi Zou 	return ret;
65957949686SYi Zou }
66057949686SYi Zou 
66157949686SYi Zou static int dcbnl_setapp(struct net_device *netdev, struct nlattr **tb,
66257949686SYi Zou                         u32 pid, u32 seq, u16 flags)
66357949686SYi Zou {
6649ab933abSJohn Fastabend 	int err, ret = -EINVAL;
66557949686SYi Zou 	u16 id;
66657949686SYi Zou 	u8 up, idtype;
66757949686SYi Zou 	struct nlattr *app_tb[DCB_APP_ATTR_MAX + 1];
66857949686SYi Zou 
6699ab933abSJohn Fastabend 	if (!tb[DCB_ATTR_APP])
67057949686SYi Zou 		goto out;
67157949686SYi Zou 
67257949686SYi Zou 	ret = nla_parse_nested(app_tb, DCB_APP_ATTR_MAX, tb[DCB_ATTR_APP],
67357949686SYi Zou 	                       dcbnl_app_nest);
67457949686SYi Zou 	if (ret)
67557949686SYi Zou 		goto out;
67657949686SYi Zou 
67757949686SYi Zou 	ret = -EINVAL;
67857949686SYi Zou 	/* all must be non-null */
67957949686SYi Zou 	if ((!app_tb[DCB_APP_ATTR_IDTYPE]) ||
68057949686SYi Zou 	    (!app_tb[DCB_APP_ATTR_ID]) ||
68157949686SYi Zou 	    (!app_tb[DCB_APP_ATTR_PRIORITY]))
68257949686SYi Zou 		goto out;
68357949686SYi Zou 
68457949686SYi Zou 	/* either by eth type or by socket number */
68557949686SYi Zou 	idtype = nla_get_u8(app_tb[DCB_APP_ATTR_IDTYPE]);
68657949686SYi Zou 	if ((idtype != DCB_APP_IDTYPE_ETHTYPE) &&
68757949686SYi Zou 	    (idtype != DCB_APP_IDTYPE_PORTNUM))
68857949686SYi Zou 		goto out;
68957949686SYi Zou 
69057949686SYi Zou 	id = nla_get_u16(app_tb[DCB_APP_ATTR_ID]);
69157949686SYi Zou 	up = nla_get_u8(app_tb[DCB_APP_ATTR_PRIORITY]);
69257949686SYi Zou 
6939ab933abSJohn Fastabend 	if (netdev->dcbnl_ops->setapp) {
6949ab933abSJohn Fastabend 		err = netdev->dcbnl_ops->setapp(netdev, idtype, id, up);
6959ab933abSJohn Fastabend 	} else {
6969ab933abSJohn Fastabend 		struct dcb_app app;
6979ab933abSJohn Fastabend 		app.selector = idtype;
6989ab933abSJohn Fastabend 		app.protocol = id;
6999ab933abSJohn Fastabend 		app.priority = up;
7009ab933abSJohn Fastabend 		err = dcb_setapp(netdev, &app);
7019ab933abSJohn Fastabend 	}
7029ab933abSJohn Fastabend 
7039ab933abSJohn Fastabend 	ret = dcbnl_reply(err, RTM_SETDCB, DCB_CMD_SAPP, DCB_ATTR_APP,
70457949686SYi Zou 			  pid, seq, flags);
70557949686SYi Zou out:
70657949686SYi Zou 	return ret;
70757949686SYi Zou }
70857949686SYi Zou 
7092f90b865SAlexander Duyck static int __dcbnl_pg_getcfg(struct net_device *netdev, struct nlattr **tb,
7102f90b865SAlexander Duyck                              u32 pid, u32 seq, u16 flags, int dir)
7112f90b865SAlexander Duyck {
7122f90b865SAlexander Duyck 	struct sk_buff *dcbnl_skb;
7132f90b865SAlexander Duyck 	struct nlmsghdr *nlh;
7142f90b865SAlexander Duyck 	struct dcbmsg *dcb;
7152f90b865SAlexander Duyck 	struct nlattr *pg_nest, *param_nest, *data;
7162f90b865SAlexander Duyck 	struct nlattr *pg_tb[DCB_PG_ATTR_MAX + 1];
7172f90b865SAlexander Duyck 	struct nlattr *param_tb[DCB_TC_ATTR_PARAM_MAX + 1];
7182f90b865SAlexander Duyck 	u8 prio, pgid, tc_pct, up_map;
7192f90b865SAlexander Duyck 	int ret  = -EINVAL;
7202f90b865SAlexander Duyck 	int getall = 0;
7212f90b865SAlexander Duyck 	int i;
7222f90b865SAlexander Duyck 
7232f90b865SAlexander Duyck 	if (!tb[DCB_ATTR_PG_CFG] ||
7242f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->getpgtccfgtx ||
7252f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->getpgtccfgrx ||
7262f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->getpgbwgcfgtx ||
7272f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->getpgbwgcfgrx)
7282f90b865SAlexander Duyck 		return ret;
7292f90b865SAlexander Duyck 
7302f90b865SAlexander Duyck 	ret = nla_parse_nested(pg_tb, DCB_PG_ATTR_MAX,
7312f90b865SAlexander Duyck 	                       tb[DCB_ATTR_PG_CFG], dcbnl_pg_nest);
7322f90b865SAlexander Duyck 
7332f90b865SAlexander Duyck 	if (ret)
7342f90b865SAlexander Duyck 		goto err_out;
7352f90b865SAlexander Duyck 
7362f90b865SAlexander Duyck 	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
7372f90b865SAlexander Duyck 	if (!dcbnl_skb)
7382f90b865SAlexander Duyck 		goto err_out;
7392f90b865SAlexander Duyck 
7402f90b865SAlexander Duyck 	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
7412f90b865SAlexander Duyck 
7422f90b865SAlexander Duyck 	dcb = NLMSG_DATA(nlh);
7432f90b865SAlexander Duyck 	dcb->dcb_family = AF_UNSPEC;
7442f90b865SAlexander Duyck 	dcb->cmd = (dir) ? DCB_CMD_PGRX_GCFG : DCB_CMD_PGTX_GCFG;
7452f90b865SAlexander Duyck 
7462f90b865SAlexander Duyck 	pg_nest = nla_nest_start(dcbnl_skb, DCB_ATTR_PG_CFG);
7472f90b865SAlexander Duyck 	if (!pg_nest)
7482f90b865SAlexander Duyck 		goto err;
7492f90b865SAlexander Duyck 
7502f90b865SAlexander Duyck 	if (pg_tb[DCB_PG_ATTR_TC_ALL])
7512f90b865SAlexander Duyck 		getall = 1;
7522f90b865SAlexander Duyck 
7532f90b865SAlexander Duyck 	for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) {
7542f90b865SAlexander Duyck 		if (!getall && !pg_tb[i])
7552f90b865SAlexander Duyck 			continue;
7562f90b865SAlexander Duyck 
7572f90b865SAlexander Duyck 		if (pg_tb[DCB_PG_ATTR_TC_ALL])
7582f90b865SAlexander Duyck 			data = pg_tb[DCB_PG_ATTR_TC_ALL];
7592f90b865SAlexander Duyck 		else
7602f90b865SAlexander Duyck 			data = pg_tb[i];
7612f90b865SAlexander Duyck 		ret = nla_parse_nested(param_tb, DCB_TC_ATTR_PARAM_MAX,
7622f90b865SAlexander Duyck 				       data, dcbnl_tc_param_nest);
7632f90b865SAlexander Duyck 		if (ret)
7642f90b865SAlexander Duyck 			goto err_pg;
7652f90b865SAlexander Duyck 
7662f90b865SAlexander Duyck 		param_nest = nla_nest_start(dcbnl_skb, i);
7672f90b865SAlexander Duyck 		if (!param_nest)
7682f90b865SAlexander Duyck 			goto err_pg;
7692f90b865SAlexander Duyck 
7702f90b865SAlexander Duyck 		pgid = DCB_ATTR_VALUE_UNDEFINED;
7712f90b865SAlexander Duyck 		prio = DCB_ATTR_VALUE_UNDEFINED;
7722f90b865SAlexander Duyck 		tc_pct = DCB_ATTR_VALUE_UNDEFINED;
7732f90b865SAlexander Duyck 		up_map = DCB_ATTR_VALUE_UNDEFINED;
7742f90b865SAlexander Duyck 
7752f90b865SAlexander Duyck 		if (dir) {
7762f90b865SAlexander Duyck 			/* Rx */
7772f90b865SAlexander Duyck 			netdev->dcbnl_ops->getpgtccfgrx(netdev,
7782f90b865SAlexander Duyck 						i - DCB_PG_ATTR_TC_0, &prio,
7792f90b865SAlexander Duyck 						&pgid, &tc_pct, &up_map);
7802f90b865SAlexander Duyck 		} else {
7812f90b865SAlexander Duyck 			/* Tx */
7822f90b865SAlexander Duyck 			netdev->dcbnl_ops->getpgtccfgtx(netdev,
7832f90b865SAlexander Duyck 						i - DCB_PG_ATTR_TC_0, &prio,
7842f90b865SAlexander Duyck 						&pgid, &tc_pct, &up_map);
7852f90b865SAlexander Duyck 		}
7862f90b865SAlexander Duyck 
7872f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_PGID] ||
7882f90b865SAlexander Duyck 		    param_tb[DCB_TC_ATTR_PARAM_ALL]) {
7892f90b865SAlexander Duyck 			ret = nla_put_u8(dcbnl_skb,
7902f90b865SAlexander Duyck 			                 DCB_TC_ATTR_PARAM_PGID, pgid);
7912f90b865SAlexander Duyck 			if (ret)
7922f90b865SAlexander Duyck 				goto err_param;
7932f90b865SAlexander Duyck 		}
7942f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING] ||
7952f90b865SAlexander Duyck 		    param_tb[DCB_TC_ATTR_PARAM_ALL]) {
7962f90b865SAlexander Duyck 			ret = nla_put_u8(dcbnl_skb,
7972f90b865SAlexander Duyck 			                 DCB_TC_ATTR_PARAM_UP_MAPPING, up_map);
7982f90b865SAlexander Duyck 			if (ret)
7992f90b865SAlexander Duyck 				goto err_param;
8002f90b865SAlexander Duyck 		}
8012f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO] ||
8022f90b865SAlexander Duyck 		    param_tb[DCB_TC_ATTR_PARAM_ALL]) {
8032f90b865SAlexander Duyck 			ret = nla_put_u8(dcbnl_skb,
8042f90b865SAlexander Duyck 			                 DCB_TC_ATTR_PARAM_STRICT_PRIO, prio);
8052f90b865SAlexander Duyck 			if (ret)
8062f90b865SAlexander Duyck 				goto err_param;
8072f90b865SAlexander Duyck 		}
8082f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_BW_PCT] ||
8092f90b865SAlexander Duyck 		    param_tb[DCB_TC_ATTR_PARAM_ALL]) {
8102f90b865SAlexander Duyck 			ret = nla_put_u8(dcbnl_skb, DCB_TC_ATTR_PARAM_BW_PCT,
8112f90b865SAlexander Duyck 			                 tc_pct);
8122f90b865SAlexander Duyck 			if (ret)
8132f90b865SAlexander Duyck 				goto err_param;
8142f90b865SAlexander Duyck 		}
8152f90b865SAlexander Duyck 		nla_nest_end(dcbnl_skb, param_nest);
8162f90b865SAlexander Duyck 	}
8172f90b865SAlexander Duyck 
8182f90b865SAlexander Duyck 	if (pg_tb[DCB_PG_ATTR_BW_ID_ALL])
8192f90b865SAlexander Duyck 		getall = 1;
8202f90b865SAlexander Duyck 	else
8212f90b865SAlexander Duyck 		getall = 0;
8222f90b865SAlexander Duyck 
8232f90b865SAlexander Duyck 	for (i = DCB_PG_ATTR_BW_ID_0; i <= DCB_PG_ATTR_BW_ID_7; i++) {
8242f90b865SAlexander Duyck 		if (!getall && !pg_tb[i])
8252f90b865SAlexander Duyck 			continue;
8262f90b865SAlexander Duyck 
8272f90b865SAlexander Duyck 		tc_pct = DCB_ATTR_VALUE_UNDEFINED;
8282f90b865SAlexander Duyck 
8292f90b865SAlexander Duyck 		if (dir) {
8302f90b865SAlexander Duyck 			/* Rx */
8312f90b865SAlexander Duyck 			netdev->dcbnl_ops->getpgbwgcfgrx(netdev,
8322f90b865SAlexander Duyck 					i - DCB_PG_ATTR_BW_ID_0, &tc_pct);
8332f90b865SAlexander Duyck 		} else {
8342f90b865SAlexander Duyck 			/* Tx */
8352f90b865SAlexander Duyck 			netdev->dcbnl_ops->getpgbwgcfgtx(netdev,
8362f90b865SAlexander Duyck 					i - DCB_PG_ATTR_BW_ID_0, &tc_pct);
8372f90b865SAlexander Duyck 		}
8382f90b865SAlexander Duyck 		ret = nla_put_u8(dcbnl_skb, i, tc_pct);
8392f90b865SAlexander Duyck 
8402f90b865SAlexander Duyck 		if (ret)
8412f90b865SAlexander Duyck 			goto err_pg;
8422f90b865SAlexander Duyck 	}
8432f90b865SAlexander Duyck 
8442f90b865SAlexander Duyck 	nla_nest_end(dcbnl_skb, pg_nest);
8452f90b865SAlexander Duyck 
8462f90b865SAlexander Duyck 	nlmsg_end(dcbnl_skb, nlh);
8472f90b865SAlexander Duyck 
8482f90b865SAlexander Duyck 	ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
8492f90b865SAlexander Duyck 	if (ret)
8507eaf5077SJohn Fastabend 		goto err_out;
8512f90b865SAlexander Duyck 
8522f90b865SAlexander Duyck 	return 0;
8532f90b865SAlexander Duyck 
8542f90b865SAlexander Duyck err_param:
8552f90b865SAlexander Duyck 	nla_nest_cancel(dcbnl_skb, param_nest);
8562f90b865SAlexander Duyck err_pg:
8572f90b865SAlexander Duyck 	nla_nest_cancel(dcbnl_skb, pg_nest);
8582f90b865SAlexander Duyck nlmsg_failure:
8592f90b865SAlexander Duyck err:
860858eb711SRoel Kluin 	kfree_skb(dcbnl_skb);
8612f90b865SAlexander Duyck err_out:
8622f90b865SAlexander Duyck 	ret  = -EINVAL;
8632f90b865SAlexander Duyck 	return ret;
8642f90b865SAlexander Duyck }
8652f90b865SAlexander Duyck 
8662f90b865SAlexander Duyck static int dcbnl_pgtx_getcfg(struct net_device *netdev, struct nlattr **tb,
8672f90b865SAlexander Duyck                              u32 pid, u32 seq, u16 flags)
8682f90b865SAlexander Duyck {
8692f90b865SAlexander Duyck 	return __dcbnl_pg_getcfg(netdev, tb, pid, seq, flags, 0);
8702f90b865SAlexander Duyck }
8712f90b865SAlexander Duyck 
8722f90b865SAlexander Duyck static int dcbnl_pgrx_getcfg(struct net_device *netdev, struct nlattr **tb,
8732f90b865SAlexander Duyck                              u32 pid, u32 seq, u16 flags)
8742f90b865SAlexander Duyck {
8752f90b865SAlexander Duyck 	return __dcbnl_pg_getcfg(netdev, tb, pid, seq, flags, 1);
8762f90b865SAlexander Duyck }
8772f90b865SAlexander Duyck 
8782f90b865SAlexander Duyck static int dcbnl_setstate(struct net_device *netdev, struct nlattr **tb,
8792f90b865SAlexander Duyck                           u32 pid, u32 seq, u16 flags)
8802f90b865SAlexander Duyck {
8812f90b865SAlexander Duyck 	int ret = -EINVAL;
8822f90b865SAlexander Duyck 	u8 value;
8832f90b865SAlexander Duyck 
8842f90b865SAlexander Duyck 	if (!tb[DCB_ATTR_STATE] || !netdev->dcbnl_ops->setstate)
8852f90b865SAlexander Duyck 		return ret;
8862f90b865SAlexander Duyck 
8872f90b865SAlexander Duyck 	value = nla_get_u8(tb[DCB_ATTR_STATE]);
8882f90b865SAlexander Duyck 
8891486a61eSDon Skidmore 	ret = dcbnl_reply(netdev->dcbnl_ops->setstate(netdev, value),
8901486a61eSDon Skidmore 	                  RTM_SETDCB, DCB_CMD_SSTATE, DCB_ATTR_STATE,
8912f90b865SAlexander Duyck 	                  pid, seq, flags);
8922f90b865SAlexander Duyck 
8932f90b865SAlexander Duyck 	return ret;
8942f90b865SAlexander Duyck }
8952f90b865SAlexander Duyck 
8962f90b865SAlexander Duyck static int dcbnl_setpfccfg(struct net_device *netdev, struct nlattr **tb,
8972f90b865SAlexander Duyck                            u32 pid, u32 seq, u16 flags)
8982f90b865SAlexander Duyck {
8992f90b865SAlexander Duyck 	struct nlattr *data[DCB_PFC_UP_ATTR_MAX + 1];
9002f90b865SAlexander Duyck 	int i;
9012f90b865SAlexander Duyck 	int ret = -EINVAL;
9022f90b865SAlexander Duyck 	u8 value;
9032f90b865SAlexander Duyck 
9042f90b865SAlexander Duyck 	if (!tb[DCB_ATTR_PFC_CFG] || !netdev->dcbnl_ops->setpfccfg)
9052f90b865SAlexander Duyck 		return ret;
9062f90b865SAlexander Duyck 
9072f90b865SAlexander Duyck 	ret = nla_parse_nested(data, DCB_PFC_UP_ATTR_MAX,
9082f90b865SAlexander Duyck 	                       tb[DCB_ATTR_PFC_CFG],
9092f90b865SAlexander Duyck 	                       dcbnl_pfc_up_nest);
9102f90b865SAlexander Duyck 	if (ret)
9112f90b865SAlexander Duyck 		goto err;
9122f90b865SAlexander Duyck 
9132f90b865SAlexander Duyck 	for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) {
9142f90b865SAlexander Duyck 		if (data[i] == NULL)
9152f90b865SAlexander Duyck 			continue;
9162f90b865SAlexander Duyck 		value = nla_get_u8(data[i]);
9172f90b865SAlexander Duyck 		netdev->dcbnl_ops->setpfccfg(netdev,
9182f90b865SAlexander Duyck 			data[i]->nla_type - DCB_PFC_UP_ATTR_0, value);
9192f90b865SAlexander Duyck 	}
9202f90b865SAlexander Duyck 
9212f90b865SAlexander Duyck 	ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_PFC_SCFG, DCB_ATTR_PFC_CFG,
9222f90b865SAlexander Duyck 	                  pid, seq, flags);
9232f90b865SAlexander Duyck err:
9242f90b865SAlexander Duyck 	return ret;
9252f90b865SAlexander Duyck }
9262f90b865SAlexander Duyck 
9272f90b865SAlexander Duyck static int dcbnl_setall(struct net_device *netdev, struct nlattr **tb,
9282f90b865SAlexander Duyck                         u32 pid, u32 seq, u16 flags)
9292f90b865SAlexander Duyck {
9302f90b865SAlexander Duyck 	int ret = -EINVAL;
9312f90b865SAlexander Duyck 
9322f90b865SAlexander Duyck 	if (!tb[DCB_ATTR_SET_ALL] || !netdev->dcbnl_ops->setall)
9332f90b865SAlexander Duyck 		return ret;
9342f90b865SAlexander Duyck 
9352f90b865SAlexander Duyck 	ret = dcbnl_reply(netdev->dcbnl_ops->setall(netdev), RTM_SETDCB,
9362f90b865SAlexander Duyck 	                  DCB_CMD_SET_ALL, DCB_ATTR_SET_ALL, pid, seq, flags);
9372f90b865SAlexander Duyck 
9382f90b865SAlexander Duyck 	return ret;
9392f90b865SAlexander Duyck }
9402f90b865SAlexander Duyck 
9412f90b865SAlexander Duyck static int __dcbnl_pg_setcfg(struct net_device *netdev, struct nlattr **tb,
9422f90b865SAlexander Duyck                              u32 pid, u32 seq, u16 flags, int dir)
9432f90b865SAlexander Duyck {
9442f90b865SAlexander Duyck 	struct nlattr *pg_tb[DCB_PG_ATTR_MAX + 1];
9452f90b865SAlexander Duyck 	struct nlattr *param_tb[DCB_TC_ATTR_PARAM_MAX + 1];
9462f90b865SAlexander Duyck 	int ret = -EINVAL;
9472f90b865SAlexander Duyck 	int i;
9482f90b865SAlexander Duyck 	u8 pgid;
9492f90b865SAlexander Duyck 	u8 up_map;
9502f90b865SAlexander Duyck 	u8 prio;
9512f90b865SAlexander Duyck 	u8 tc_pct;
9522f90b865SAlexander Duyck 
9532f90b865SAlexander Duyck 	if (!tb[DCB_ATTR_PG_CFG] ||
9542f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->setpgtccfgtx ||
9552f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->setpgtccfgrx ||
9562f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->setpgbwgcfgtx ||
9572f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->setpgbwgcfgrx)
9582f90b865SAlexander Duyck 		return ret;
9592f90b865SAlexander Duyck 
9602f90b865SAlexander Duyck 	ret = nla_parse_nested(pg_tb, DCB_PG_ATTR_MAX,
9612f90b865SAlexander Duyck 	                       tb[DCB_ATTR_PG_CFG], dcbnl_pg_nest);
9622f90b865SAlexander Duyck 	if (ret)
9632f90b865SAlexander Duyck 		goto err;
9642f90b865SAlexander Duyck 
9652f90b865SAlexander Duyck 	for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) {
9662f90b865SAlexander Duyck 		if (!pg_tb[i])
9672f90b865SAlexander Duyck 			continue;
9682f90b865SAlexander Duyck 
9692f90b865SAlexander Duyck 		ret = nla_parse_nested(param_tb, DCB_TC_ATTR_PARAM_MAX,
9702f90b865SAlexander Duyck 		                       pg_tb[i], dcbnl_tc_param_nest);
9712f90b865SAlexander Duyck 		if (ret)
9722f90b865SAlexander Duyck 			goto err;
9732f90b865SAlexander Duyck 
9742f90b865SAlexander Duyck 		pgid = DCB_ATTR_VALUE_UNDEFINED;
9752f90b865SAlexander Duyck 		prio = DCB_ATTR_VALUE_UNDEFINED;
9762f90b865SAlexander Duyck 		tc_pct = DCB_ATTR_VALUE_UNDEFINED;
9772f90b865SAlexander Duyck 		up_map = DCB_ATTR_VALUE_UNDEFINED;
9782f90b865SAlexander Duyck 
9792f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO])
9802f90b865SAlexander Duyck 			prio =
9812f90b865SAlexander Duyck 			    nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO]);
9822f90b865SAlexander Duyck 
9832f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_PGID])
9842f90b865SAlexander Duyck 			pgid = nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_PGID]);
9852f90b865SAlexander Duyck 
9862f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_BW_PCT])
9872f90b865SAlexander Duyck 			tc_pct = nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_BW_PCT]);
9882f90b865SAlexander Duyck 
9892f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING])
9902f90b865SAlexander Duyck 			up_map =
9912f90b865SAlexander Duyck 			     nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING]);
9922f90b865SAlexander Duyck 
9932f90b865SAlexander Duyck 		/* dir: Tx = 0, Rx = 1 */
9942f90b865SAlexander Duyck 		if (dir) {
9952f90b865SAlexander Duyck 			/* Rx */
9962f90b865SAlexander Duyck 			netdev->dcbnl_ops->setpgtccfgrx(netdev,
9972f90b865SAlexander Duyck 				i - DCB_PG_ATTR_TC_0,
9982f90b865SAlexander Duyck 				prio, pgid, tc_pct, up_map);
9992f90b865SAlexander Duyck 		} else {
10002f90b865SAlexander Duyck 			/* Tx */
10012f90b865SAlexander Duyck 			netdev->dcbnl_ops->setpgtccfgtx(netdev,
10022f90b865SAlexander Duyck 				i - DCB_PG_ATTR_TC_0,
10032f90b865SAlexander Duyck 				prio, pgid, tc_pct, up_map);
10042f90b865SAlexander Duyck 		}
10052f90b865SAlexander Duyck 	}
10062f90b865SAlexander Duyck 
10072f90b865SAlexander Duyck 	for (i = DCB_PG_ATTR_BW_ID_0; i <= DCB_PG_ATTR_BW_ID_7; i++) {
10082f90b865SAlexander Duyck 		if (!pg_tb[i])
10092f90b865SAlexander Duyck 			continue;
10102f90b865SAlexander Duyck 
10112f90b865SAlexander Duyck 		tc_pct = nla_get_u8(pg_tb[i]);
10122f90b865SAlexander Duyck 
10132f90b865SAlexander Duyck 		/* dir: Tx = 0, Rx = 1 */
10142f90b865SAlexander Duyck 		if (dir) {
10152f90b865SAlexander Duyck 			/* Rx */
10162f90b865SAlexander Duyck 			netdev->dcbnl_ops->setpgbwgcfgrx(netdev,
10172f90b865SAlexander Duyck 					 i - DCB_PG_ATTR_BW_ID_0, tc_pct);
10182f90b865SAlexander Duyck 		} else {
10192f90b865SAlexander Duyck 			/* Tx */
10202f90b865SAlexander Duyck 			netdev->dcbnl_ops->setpgbwgcfgtx(netdev,
10212f90b865SAlexander Duyck 					 i - DCB_PG_ATTR_BW_ID_0, tc_pct);
10222f90b865SAlexander Duyck 		}
10232f90b865SAlexander Duyck 	}
10242f90b865SAlexander Duyck 
10252f90b865SAlexander Duyck 	ret = dcbnl_reply(0, RTM_SETDCB,
10262f90b865SAlexander Duyck 			  (dir ? DCB_CMD_PGRX_SCFG : DCB_CMD_PGTX_SCFG),
10272f90b865SAlexander Duyck 			  DCB_ATTR_PG_CFG, pid, seq, flags);
10282f90b865SAlexander Duyck 
10292f90b865SAlexander Duyck err:
10302f90b865SAlexander Duyck 	return ret;
10312f90b865SAlexander Duyck }
10322f90b865SAlexander Duyck 
10332f90b865SAlexander Duyck static int dcbnl_pgtx_setcfg(struct net_device *netdev, struct nlattr **tb,
10342f90b865SAlexander Duyck                              u32 pid, u32 seq, u16 flags)
10352f90b865SAlexander Duyck {
10362f90b865SAlexander Duyck 	return __dcbnl_pg_setcfg(netdev, tb, pid, seq, flags, 0);
10372f90b865SAlexander Duyck }
10382f90b865SAlexander Duyck 
10392f90b865SAlexander Duyck static int dcbnl_pgrx_setcfg(struct net_device *netdev, struct nlattr **tb,
10402f90b865SAlexander Duyck                              u32 pid, u32 seq, u16 flags)
10412f90b865SAlexander Duyck {
10422f90b865SAlexander Duyck 	return __dcbnl_pg_setcfg(netdev, tb, pid, seq, flags, 1);
10432f90b865SAlexander Duyck }
10442f90b865SAlexander Duyck 
1045859ee3c4SAlexander Duyck static int dcbnl_bcn_getcfg(struct net_device *netdev, struct nlattr **tb,
1046859ee3c4SAlexander Duyck                             u32 pid, u32 seq, u16 flags)
1047859ee3c4SAlexander Duyck {
1048859ee3c4SAlexander Duyck 	struct sk_buff *dcbnl_skb;
1049859ee3c4SAlexander Duyck 	struct nlmsghdr *nlh;
1050859ee3c4SAlexander Duyck 	struct dcbmsg *dcb;
1051859ee3c4SAlexander Duyck 	struct nlattr *bcn_nest;
1052859ee3c4SAlexander Duyck 	struct nlattr *bcn_tb[DCB_BCN_ATTR_MAX + 1];
1053859ee3c4SAlexander Duyck 	u8 value_byte;
1054859ee3c4SAlexander Duyck 	u32 value_integer;
1055859ee3c4SAlexander Duyck 	int ret  = -EINVAL;
1056859ee3c4SAlexander Duyck 	bool getall = false;
1057859ee3c4SAlexander Duyck 	int i;
1058859ee3c4SAlexander Duyck 
1059859ee3c4SAlexander Duyck 	if (!tb[DCB_ATTR_BCN] || !netdev->dcbnl_ops->getbcnrp ||
1060859ee3c4SAlexander Duyck 	    !netdev->dcbnl_ops->getbcncfg)
1061859ee3c4SAlexander Duyck 		return ret;
1062859ee3c4SAlexander Duyck 
1063859ee3c4SAlexander Duyck 	ret = nla_parse_nested(bcn_tb, DCB_BCN_ATTR_MAX,
1064859ee3c4SAlexander Duyck 	                       tb[DCB_ATTR_BCN], dcbnl_bcn_nest);
1065859ee3c4SAlexander Duyck 
1066859ee3c4SAlexander Duyck 	if (ret)
1067859ee3c4SAlexander Duyck 		goto err_out;
1068859ee3c4SAlexander Duyck 
1069859ee3c4SAlexander Duyck 	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
1070859ee3c4SAlexander Duyck 	if (!dcbnl_skb)
1071859ee3c4SAlexander Duyck 		goto err_out;
1072859ee3c4SAlexander Duyck 
1073859ee3c4SAlexander Duyck 	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
1074859ee3c4SAlexander Duyck 
1075859ee3c4SAlexander Duyck 	dcb = NLMSG_DATA(nlh);
1076859ee3c4SAlexander Duyck 	dcb->dcb_family = AF_UNSPEC;
1077859ee3c4SAlexander Duyck 	dcb->cmd = DCB_CMD_BCN_GCFG;
1078859ee3c4SAlexander Duyck 
1079859ee3c4SAlexander Duyck 	bcn_nest = nla_nest_start(dcbnl_skb, DCB_ATTR_BCN);
1080859ee3c4SAlexander Duyck 	if (!bcn_nest)
1081859ee3c4SAlexander Duyck 		goto err;
1082859ee3c4SAlexander Duyck 
1083859ee3c4SAlexander Duyck 	if (bcn_tb[DCB_BCN_ATTR_ALL])
1084859ee3c4SAlexander Duyck 		getall = true;
1085859ee3c4SAlexander Duyck 
1086859ee3c4SAlexander Duyck 	for (i = DCB_BCN_ATTR_RP_0; i <= DCB_BCN_ATTR_RP_7; i++) {
1087859ee3c4SAlexander Duyck 		if (!getall && !bcn_tb[i])
1088859ee3c4SAlexander Duyck 			continue;
1089859ee3c4SAlexander Duyck 
1090859ee3c4SAlexander Duyck 		netdev->dcbnl_ops->getbcnrp(netdev, i - DCB_BCN_ATTR_RP_0,
1091859ee3c4SAlexander Duyck 		                            &value_byte);
1092859ee3c4SAlexander Duyck 		ret = nla_put_u8(dcbnl_skb, i, value_byte);
1093859ee3c4SAlexander Duyck 		if (ret)
1094859ee3c4SAlexander Duyck 			goto err_bcn;
1095859ee3c4SAlexander Duyck 	}
1096859ee3c4SAlexander Duyck 
1097f4314e81SDon Skidmore 	for (i = DCB_BCN_ATTR_BCNA_0; i <= DCB_BCN_ATTR_RI; i++) {
1098859ee3c4SAlexander Duyck 		if (!getall && !bcn_tb[i])
1099859ee3c4SAlexander Duyck 			continue;
1100859ee3c4SAlexander Duyck 
1101859ee3c4SAlexander Duyck 		netdev->dcbnl_ops->getbcncfg(netdev, i,
1102859ee3c4SAlexander Duyck 		                             &value_integer);
1103859ee3c4SAlexander Duyck 		ret = nla_put_u32(dcbnl_skb, i, value_integer);
1104859ee3c4SAlexander Duyck 		if (ret)
1105859ee3c4SAlexander Duyck 			goto err_bcn;
1106859ee3c4SAlexander Duyck 	}
1107859ee3c4SAlexander Duyck 
1108859ee3c4SAlexander Duyck 	nla_nest_end(dcbnl_skb, bcn_nest);
1109859ee3c4SAlexander Duyck 
1110859ee3c4SAlexander Duyck 	nlmsg_end(dcbnl_skb, nlh);
1111859ee3c4SAlexander Duyck 
1112859ee3c4SAlexander Duyck 	ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
1113859ee3c4SAlexander Duyck 	if (ret)
11147eaf5077SJohn Fastabend 		goto err_out;
1115859ee3c4SAlexander Duyck 
1116859ee3c4SAlexander Duyck 	return 0;
1117859ee3c4SAlexander Duyck 
1118859ee3c4SAlexander Duyck err_bcn:
1119859ee3c4SAlexander Duyck 	nla_nest_cancel(dcbnl_skb, bcn_nest);
1120859ee3c4SAlexander Duyck nlmsg_failure:
1121859ee3c4SAlexander Duyck err:
1122858eb711SRoel Kluin 	kfree_skb(dcbnl_skb);
1123859ee3c4SAlexander Duyck err_out:
1124859ee3c4SAlexander Duyck 	ret  = -EINVAL;
1125859ee3c4SAlexander Duyck 	return ret;
1126859ee3c4SAlexander Duyck }
1127859ee3c4SAlexander Duyck 
1128859ee3c4SAlexander Duyck static int dcbnl_bcn_setcfg(struct net_device *netdev, struct nlattr **tb,
1129859ee3c4SAlexander Duyck                             u32 pid, u32 seq, u16 flags)
1130859ee3c4SAlexander Duyck {
1131859ee3c4SAlexander Duyck 	struct nlattr *data[DCB_BCN_ATTR_MAX + 1];
1132859ee3c4SAlexander Duyck 	int i;
1133859ee3c4SAlexander Duyck 	int ret = -EINVAL;
1134859ee3c4SAlexander Duyck 	u8 value_byte;
1135859ee3c4SAlexander Duyck 	u32 value_int;
1136859ee3c4SAlexander Duyck 
1137f64f9e71SJoe Perches 	if (!tb[DCB_ATTR_BCN] || !netdev->dcbnl_ops->setbcncfg ||
1138f64f9e71SJoe Perches 	    !netdev->dcbnl_ops->setbcnrp)
1139859ee3c4SAlexander Duyck 		return ret;
1140859ee3c4SAlexander Duyck 
1141859ee3c4SAlexander Duyck 	ret = nla_parse_nested(data, DCB_BCN_ATTR_MAX,
1142859ee3c4SAlexander Duyck 	                       tb[DCB_ATTR_BCN],
1143859ee3c4SAlexander Duyck 	                       dcbnl_pfc_up_nest);
1144859ee3c4SAlexander Duyck 	if (ret)
1145859ee3c4SAlexander Duyck 		goto err;
1146859ee3c4SAlexander Duyck 
1147859ee3c4SAlexander Duyck 	for (i = DCB_BCN_ATTR_RP_0; i <= DCB_BCN_ATTR_RP_7; i++) {
1148859ee3c4SAlexander Duyck 		if (data[i] == NULL)
1149859ee3c4SAlexander Duyck 			continue;
1150859ee3c4SAlexander Duyck 		value_byte = nla_get_u8(data[i]);
1151859ee3c4SAlexander Duyck 		netdev->dcbnl_ops->setbcnrp(netdev,
1152859ee3c4SAlexander Duyck 			data[i]->nla_type - DCB_BCN_ATTR_RP_0, value_byte);
1153859ee3c4SAlexander Duyck 	}
1154859ee3c4SAlexander Duyck 
1155f4314e81SDon Skidmore 	for (i = DCB_BCN_ATTR_BCNA_0; i <= DCB_BCN_ATTR_RI; i++) {
1156859ee3c4SAlexander Duyck 		if (data[i] == NULL)
1157859ee3c4SAlexander Duyck 			continue;
1158859ee3c4SAlexander Duyck 		value_int = nla_get_u32(data[i]);
1159859ee3c4SAlexander Duyck 		netdev->dcbnl_ops->setbcncfg(netdev,
1160859ee3c4SAlexander Duyck 	                                     i, value_int);
1161859ee3c4SAlexander Duyck 	}
1162859ee3c4SAlexander Duyck 
1163859ee3c4SAlexander Duyck 	ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_BCN_SCFG, DCB_ATTR_BCN,
1164859ee3c4SAlexander Duyck 	                  pid, seq, flags);
1165859ee3c4SAlexander Duyck err:
1166859ee3c4SAlexander Duyck 	return ret;
1167859ee3c4SAlexander Duyck }
1168859ee3c4SAlexander Duyck 
1169dc6ed1dfSShmulik Ravid static int dcbnl_build_peer_app(struct net_device *netdev, struct sk_buff* skb,
1170dc6ed1dfSShmulik Ravid 				int app_nested_type, int app_info_type,
1171dc6ed1dfSShmulik Ravid 				int app_entry_type)
1172eed84713SShmulik Ravid {
1173eed84713SShmulik Ravid 	struct dcb_peer_app_info info;
1174eed84713SShmulik Ravid 	struct dcb_app *table = NULL;
1175eed84713SShmulik Ravid 	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
1176eed84713SShmulik Ravid 	u16 app_count;
1177eed84713SShmulik Ravid 	int err;
1178eed84713SShmulik Ravid 
1179eed84713SShmulik Ravid 
1180eed84713SShmulik Ravid 	/**
1181eed84713SShmulik Ravid 	 * retrieve the peer app configuration form the driver. If the driver
1182eed84713SShmulik Ravid 	 * handlers fail exit without doing anything
1183eed84713SShmulik Ravid 	 */
1184eed84713SShmulik Ravid 	err = ops->peer_getappinfo(netdev, &info, &app_count);
1185eed84713SShmulik Ravid 	if (!err && app_count) {
1186eed84713SShmulik Ravid 		table = kmalloc(sizeof(struct dcb_app) * app_count, GFP_KERNEL);
1187eed84713SShmulik Ravid 		if (!table)
1188eed84713SShmulik Ravid 			return -ENOMEM;
1189eed84713SShmulik Ravid 
1190eed84713SShmulik Ravid 		err = ops->peer_getapptable(netdev, table);
1191eed84713SShmulik Ravid 	}
1192eed84713SShmulik Ravid 
1193eed84713SShmulik Ravid 	if (!err) {
1194eed84713SShmulik Ravid 		u16 i;
1195eed84713SShmulik Ravid 		struct nlattr *app;
1196eed84713SShmulik Ravid 
1197eed84713SShmulik Ravid 		/**
1198eed84713SShmulik Ravid 		 * build the message, from here on the only possible failure
1199eed84713SShmulik Ravid 		 * is due to the skb size
1200eed84713SShmulik Ravid 		 */
1201eed84713SShmulik Ravid 		err = -EMSGSIZE;
1202eed84713SShmulik Ravid 
1203dc6ed1dfSShmulik Ravid 		app = nla_nest_start(skb, app_nested_type);
1204eed84713SShmulik Ravid 		if (!app)
1205eed84713SShmulik Ravid 			goto nla_put_failure;
1206eed84713SShmulik Ravid 
1207dc6ed1dfSShmulik Ravid 		if (app_info_type)
1208dc6ed1dfSShmulik Ravid 			NLA_PUT(skb, app_info_type, sizeof(info), &info);
1209dc6ed1dfSShmulik Ravid 
1210eed84713SShmulik Ravid 		for (i = 0; i < app_count; i++)
1211dc6ed1dfSShmulik Ravid 			NLA_PUT(skb, app_entry_type, sizeof(struct dcb_app),
1212eed84713SShmulik Ravid 				&table[i]);
1213eed84713SShmulik Ravid 
1214eed84713SShmulik Ravid 		nla_nest_end(skb, app);
1215eed84713SShmulik Ravid 	}
1216eed84713SShmulik Ravid 	err = 0;
1217eed84713SShmulik Ravid 
1218eed84713SShmulik Ravid nla_put_failure:
1219eed84713SShmulik Ravid 	kfree(table);
1220eed84713SShmulik Ravid 	return err;
1221eed84713SShmulik Ravid }
12223e29027aSJohn Fastabend 
12233e29027aSJohn Fastabend /* Handle IEEE 802.1Qaz GET commands. */
1224314b4778SJohn Fastabend static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev)
12253e29027aSJohn Fastabend {
12269ab933abSJohn Fastabend 	struct nlattr *ieee, *app;
12279ab933abSJohn Fastabend 	struct dcb_app_type *itr;
12283e29027aSJohn Fastabend 	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
1229c7797bafSJohn Fastabend 	int dcbx;
1230314b4778SJohn Fastabend 	int err = -EMSGSIZE;
12313e29027aSJohn Fastabend 
12323e29027aSJohn Fastabend 	NLA_PUT_STRING(skb, DCB_ATTR_IFNAME, netdev->name);
12333e29027aSJohn Fastabend 
12343e29027aSJohn Fastabend 	ieee = nla_nest_start(skb, DCB_ATTR_IEEE);
12353e29027aSJohn Fastabend 	if (!ieee)
12363e29027aSJohn Fastabend 		goto nla_put_failure;
12373e29027aSJohn Fastabend 
12383e29027aSJohn Fastabend 	if (ops->ieee_getets) {
12393e29027aSJohn Fastabend 		struct ieee_ets ets;
12403e29027aSJohn Fastabend 		err = ops->ieee_getets(netdev, &ets);
12413e29027aSJohn Fastabend 		if (!err)
12423e29027aSJohn Fastabend 			NLA_PUT(skb, DCB_ATTR_IEEE_ETS, sizeof(ets), &ets);
12433e29027aSJohn Fastabend 	}
12443e29027aSJohn Fastabend 
12453e29027aSJohn Fastabend 	if (ops->ieee_getpfc) {
12463e29027aSJohn Fastabend 		struct ieee_pfc pfc;
12473e29027aSJohn Fastabend 		err = ops->ieee_getpfc(netdev, &pfc);
12483e29027aSJohn Fastabend 		if (!err)
12493e29027aSJohn Fastabend 			NLA_PUT(skb, DCB_ATTR_IEEE_PFC, sizeof(pfc), &pfc);
12503e29027aSJohn Fastabend 	}
12513e29027aSJohn Fastabend 
12529ab933abSJohn Fastabend 	app = nla_nest_start(skb, DCB_ATTR_IEEE_APP_TABLE);
12539ab933abSJohn Fastabend 	if (!app)
12549ab933abSJohn Fastabend 		goto nla_put_failure;
12559ab933abSJohn Fastabend 
12569ab933abSJohn Fastabend 	spin_lock(&dcb_lock);
12579ab933abSJohn Fastabend 	list_for_each_entry(itr, &dcb_app_list, list) {
125870bfa2d2SDan Carpenter 		if (strncmp(itr->name, netdev->name, IFNAMSIZ) == 0) {
125970bfa2d2SDan Carpenter 			err = nla_put(skb, DCB_ATTR_IEEE_APP, sizeof(itr->app),
126070bfa2d2SDan Carpenter 					 &itr->app);
126170bfa2d2SDan Carpenter 			if (err) {
126270bfa2d2SDan Carpenter 				spin_unlock(&dcb_lock);
126370bfa2d2SDan Carpenter 				goto nla_put_failure;
126470bfa2d2SDan Carpenter 			}
126570bfa2d2SDan Carpenter 		}
12669ab933abSJohn Fastabend 	}
1267c7797bafSJohn Fastabend 
1268c7797bafSJohn Fastabend 	if (netdev->dcbnl_ops->getdcbx)
1269c7797bafSJohn Fastabend 		dcbx = netdev->dcbnl_ops->getdcbx(netdev);
1270c7797bafSJohn Fastabend 	else
1271c7797bafSJohn Fastabend 		dcbx = -EOPNOTSUPP;
1272c7797bafSJohn Fastabend 
12739ab933abSJohn Fastabend 	spin_unlock(&dcb_lock);
12749ab933abSJohn Fastabend 	nla_nest_end(skb, app);
12759ab933abSJohn Fastabend 
1276eed84713SShmulik Ravid 	/* get peer info if available */
1277eed84713SShmulik Ravid 	if (ops->ieee_peer_getets) {
1278eed84713SShmulik Ravid 		struct ieee_ets ets;
1279eed84713SShmulik Ravid 		err = ops->ieee_peer_getets(netdev, &ets);
1280eed84713SShmulik Ravid 		if (!err)
1281eed84713SShmulik Ravid 			NLA_PUT(skb, DCB_ATTR_IEEE_PEER_ETS, sizeof(ets), &ets);
1282eed84713SShmulik Ravid 	}
1283eed84713SShmulik Ravid 
1284eed84713SShmulik Ravid 	if (ops->ieee_peer_getpfc) {
1285eed84713SShmulik Ravid 		struct ieee_pfc pfc;
1286eed84713SShmulik Ravid 		err = ops->ieee_peer_getpfc(netdev, &pfc);
1287eed84713SShmulik Ravid 		if (!err)
1288eed84713SShmulik Ravid 			NLA_PUT(skb, DCB_ATTR_IEEE_PEER_PFC, sizeof(pfc), &pfc);
1289eed84713SShmulik Ravid 	}
1290eed84713SShmulik Ravid 
1291eed84713SShmulik Ravid 	if (ops->peer_getappinfo && ops->peer_getapptable) {
1292dc6ed1dfSShmulik Ravid 		err = dcbnl_build_peer_app(netdev, skb,
1293dc6ed1dfSShmulik Ravid 					   DCB_ATTR_IEEE_PEER_APP,
1294dc6ed1dfSShmulik Ravid 					   DCB_ATTR_IEEE_APP_UNSPEC,
1295dc6ed1dfSShmulik Ravid 					   DCB_ATTR_IEEE_APP);
1296eed84713SShmulik Ravid 		if (err)
1297eed84713SShmulik Ravid 			goto nla_put_failure;
1298eed84713SShmulik Ravid 	}
1299eed84713SShmulik Ravid 
13003e29027aSJohn Fastabend 	nla_nest_end(skb, ieee);
1301c7797bafSJohn Fastabend 	if (dcbx >= 0) {
1302c7797bafSJohn Fastabend 		err = nla_put_u8(skb, DCB_ATTR_DCBX, dcbx);
1303c7797bafSJohn Fastabend 		if (err)
1304c7797bafSJohn Fastabend 			goto nla_put_failure;
1305c7797bafSJohn Fastabend 	}
13063e29027aSJohn Fastabend 
1307314b4778SJohn Fastabend 	return 0;
1308314b4778SJohn Fastabend 
13093e29027aSJohn Fastabend nla_put_failure:
1310314b4778SJohn Fastabend 	return err;
13113e29027aSJohn Fastabend }
13123e29027aSJohn Fastabend 
1313314b4778SJohn Fastabend int dcbnl_notify(struct net_device *dev, int event, int cmd,
1314314b4778SJohn Fastabend 			u32 seq, u32 pid)
1315314b4778SJohn Fastabend {
1316314b4778SJohn Fastabend 	struct net *net = dev_net(dev);
1317314b4778SJohn Fastabend 	struct sk_buff *skb;
1318314b4778SJohn Fastabend 	struct nlmsghdr *nlh;
1319314b4778SJohn Fastabend 	struct dcbmsg *dcb;
1320314b4778SJohn Fastabend 	const struct dcbnl_rtnl_ops *ops = dev->dcbnl_ops;
1321314b4778SJohn Fastabend 	int err;
1322314b4778SJohn Fastabend 
1323314b4778SJohn Fastabend 	if (!ops)
1324314b4778SJohn Fastabend 		return -EOPNOTSUPP;
1325314b4778SJohn Fastabend 
1326314b4778SJohn Fastabend 	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
1327314b4778SJohn Fastabend 	if (!skb)
1328314b4778SJohn Fastabend 		return -ENOBUFS;
1329314b4778SJohn Fastabend 
1330314b4778SJohn Fastabend 	nlh = nlmsg_put(skb, pid, 0, event, sizeof(*dcb), 0);
1331314b4778SJohn Fastabend 	if (nlh == NULL) {
1332314b4778SJohn Fastabend 		kfree(skb);
1333314b4778SJohn Fastabend 		return -EMSGSIZE;
1334314b4778SJohn Fastabend 	}
1335314b4778SJohn Fastabend 
1336314b4778SJohn Fastabend 	dcb = NLMSG_DATA(nlh);
1337314b4778SJohn Fastabend 	dcb->dcb_family = AF_UNSPEC;
1338314b4778SJohn Fastabend 	dcb->cmd = cmd;
1339314b4778SJohn Fastabend 
1340314b4778SJohn Fastabend 	err = dcbnl_ieee_fill(skb, dev);
1341314b4778SJohn Fastabend 	if (err < 0) {
1342314b4778SJohn Fastabend 		/* Report error to broadcast listeners */
1343314b4778SJohn Fastabend 		nlmsg_cancel(skb, nlh);
1344314b4778SJohn Fastabend 		kfree_skb(skb);
1345314b4778SJohn Fastabend 		rtnl_set_sk_err(net, RTNLGRP_DCB, err);
1346314b4778SJohn Fastabend 	} else {
1347314b4778SJohn Fastabend 		/* End nlmsg and notify broadcast listeners */
1348314b4778SJohn Fastabend 		nlmsg_end(skb, nlh);
1349314b4778SJohn Fastabend 		rtnl_notify(skb, net, 0, RTNLGRP_DCB, NULL, GFP_KERNEL);
1350314b4778SJohn Fastabend 	}
1351314b4778SJohn Fastabend 
1352314b4778SJohn Fastabend 	return err;
1353314b4778SJohn Fastabend }
1354314b4778SJohn Fastabend EXPORT_SYMBOL(dcbnl_notify);
1355314b4778SJohn Fastabend 
1356314b4778SJohn Fastabend /* Handle IEEE 802.1Qaz SET commands. If any requested operation can not
1357314b4778SJohn Fastabend  * be completed the entire msg is aborted and error value is returned.
1358314b4778SJohn Fastabend  * No attempt is made to reconcile the case where only part of the
1359314b4778SJohn Fastabend  * cmd can be completed.
1360314b4778SJohn Fastabend  */
1361314b4778SJohn Fastabend static int dcbnl_ieee_set(struct net_device *netdev, struct nlattr **tb,
1362314b4778SJohn Fastabend 			  u32 pid, u32 seq, u16 flags)
1363314b4778SJohn Fastabend {
1364314b4778SJohn Fastabend 	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
1365314b4778SJohn Fastabend 	struct nlattr *ieee[DCB_ATTR_IEEE_MAX + 1];
1366314b4778SJohn Fastabend 	int err = -EOPNOTSUPP;
1367314b4778SJohn Fastabend 
1368314b4778SJohn Fastabend 	if (!ops)
1369314b4778SJohn Fastabend 		return err;
1370314b4778SJohn Fastabend 
1371314b4778SJohn Fastabend 	err = nla_parse_nested(ieee, DCB_ATTR_IEEE_MAX,
1372314b4778SJohn Fastabend 			       tb[DCB_ATTR_IEEE], dcbnl_ieee_policy);
1373314b4778SJohn Fastabend 	if (err)
1374314b4778SJohn Fastabend 		return err;
1375314b4778SJohn Fastabend 
1376314b4778SJohn Fastabend 	if (ieee[DCB_ATTR_IEEE_ETS] && ops->ieee_setets) {
1377314b4778SJohn Fastabend 		struct ieee_ets *ets = nla_data(ieee[DCB_ATTR_IEEE_ETS]);
1378314b4778SJohn Fastabend 		err = ops->ieee_setets(netdev, ets);
1379314b4778SJohn Fastabend 		if (err)
1380314b4778SJohn Fastabend 			goto err;
1381314b4778SJohn Fastabend 	}
1382314b4778SJohn Fastabend 
1383314b4778SJohn Fastabend 	if (ieee[DCB_ATTR_IEEE_PFC] && ops->ieee_setpfc) {
1384314b4778SJohn Fastabend 		struct ieee_pfc *pfc = nla_data(ieee[DCB_ATTR_IEEE_PFC]);
1385314b4778SJohn Fastabend 		err = ops->ieee_setpfc(netdev, pfc);
1386314b4778SJohn Fastabend 		if (err)
1387314b4778SJohn Fastabend 			goto err;
1388314b4778SJohn Fastabend 	}
1389314b4778SJohn Fastabend 
1390314b4778SJohn Fastabend 	if (ieee[DCB_ATTR_IEEE_APP_TABLE]) {
1391314b4778SJohn Fastabend 		struct nlattr *attr;
1392314b4778SJohn Fastabend 		int rem;
1393314b4778SJohn Fastabend 
1394314b4778SJohn Fastabend 		nla_for_each_nested(attr, ieee[DCB_ATTR_IEEE_APP_TABLE], rem) {
1395314b4778SJohn Fastabend 			struct dcb_app *app_data;
1396314b4778SJohn Fastabend 			if (nla_type(attr) != DCB_ATTR_IEEE_APP)
1397314b4778SJohn Fastabend 				continue;
1398314b4778SJohn Fastabend 			app_data = nla_data(attr);
1399314b4778SJohn Fastabend 			if (ops->ieee_setapp)
1400314b4778SJohn Fastabend 				err = ops->ieee_setapp(netdev, app_data);
1401314b4778SJohn Fastabend 			else
1402b6db2174SJohn Fastabend 				err = dcb_ieee_setapp(netdev, app_data);
1403314b4778SJohn Fastabend 			if (err)
1404314b4778SJohn Fastabend 				goto err;
1405314b4778SJohn Fastabend 		}
1406314b4778SJohn Fastabend 	}
1407314b4778SJohn Fastabend 
1408314b4778SJohn Fastabend err:
1409314b4778SJohn Fastabend 	dcbnl_reply(err, RTM_SETDCB, DCB_CMD_IEEE_SET, DCB_ATTR_IEEE,
1410314b4778SJohn Fastabend 		    pid, seq, flags);
1411314b4778SJohn Fastabend 	dcbnl_notify(netdev, RTM_SETDCB, DCB_CMD_IEEE_SET, seq, 0);
1412314b4778SJohn Fastabend 	return err;
1413314b4778SJohn Fastabend }
1414314b4778SJohn Fastabend 
1415314b4778SJohn Fastabend static int dcbnl_ieee_get(struct net_device *netdev, struct nlattr **tb,
1416314b4778SJohn Fastabend 			  u32 pid, u32 seq, u16 flags)
1417314b4778SJohn Fastabend {
1418314b4778SJohn Fastabend 	struct net *net = dev_net(netdev);
1419314b4778SJohn Fastabend 	struct sk_buff *skb;
1420314b4778SJohn Fastabend 	struct nlmsghdr *nlh;
1421314b4778SJohn Fastabend 	struct dcbmsg *dcb;
1422314b4778SJohn Fastabend 	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
1423314b4778SJohn Fastabend 	int err;
1424314b4778SJohn Fastabend 
1425314b4778SJohn Fastabend 	if (!ops)
1426314b4778SJohn Fastabend 		return -EOPNOTSUPP;
1427314b4778SJohn Fastabend 
1428314b4778SJohn Fastabend 	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
1429314b4778SJohn Fastabend 	if (!skb)
1430314b4778SJohn Fastabend 		return -ENOBUFS;
1431314b4778SJohn Fastabend 
1432314b4778SJohn Fastabend 	nlh = nlmsg_put(skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
1433314b4778SJohn Fastabend 	if (nlh == NULL) {
1434314b4778SJohn Fastabend 		kfree(skb);
1435314b4778SJohn Fastabend 		return -EMSGSIZE;
1436314b4778SJohn Fastabend 	}
1437314b4778SJohn Fastabend 
1438314b4778SJohn Fastabend 	dcb = NLMSG_DATA(nlh);
1439314b4778SJohn Fastabend 	dcb->dcb_family = AF_UNSPEC;
1440314b4778SJohn Fastabend 	dcb->cmd = DCB_CMD_IEEE_GET;
1441314b4778SJohn Fastabend 
1442314b4778SJohn Fastabend 	err = dcbnl_ieee_fill(skb, netdev);
1443314b4778SJohn Fastabend 
1444314b4778SJohn Fastabend 	if (err < 0) {
1445314b4778SJohn Fastabend 		nlmsg_cancel(skb, nlh);
1446314b4778SJohn Fastabend 		kfree_skb(skb);
1447314b4778SJohn Fastabend 	} else {
1448314b4778SJohn Fastabend 		nlmsg_end(skb, nlh);
1449314b4778SJohn Fastabend 		err = rtnl_unicast(skb, net, pid);
1450314b4778SJohn Fastabend 	}
1451314b4778SJohn Fastabend 
1452314b4778SJohn Fastabend 	return err;
1453314b4778SJohn Fastabend }
1454f9ae7e4bSJohn Fastabend 
1455f9ae7e4bSJohn Fastabend static int dcbnl_ieee_del(struct net_device *netdev, struct nlattr **tb,
1456f9ae7e4bSJohn Fastabend 			  u32 pid, u32 seq, u16 flags)
1457f9ae7e4bSJohn Fastabend {
1458f9ae7e4bSJohn Fastabend 	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
1459f9ae7e4bSJohn Fastabend 	struct nlattr *ieee[DCB_ATTR_IEEE_MAX + 1];
1460f9ae7e4bSJohn Fastabend 	int err = -EOPNOTSUPP;
1461f9ae7e4bSJohn Fastabend 
1462f9ae7e4bSJohn Fastabend 	if (!ops)
1463f9ae7e4bSJohn Fastabend 		return -EOPNOTSUPP;
1464f9ae7e4bSJohn Fastabend 
1465f9ae7e4bSJohn Fastabend 	if (!tb[DCB_ATTR_IEEE])
1466f9ae7e4bSJohn Fastabend 		return -EINVAL;
1467f9ae7e4bSJohn Fastabend 
1468f9ae7e4bSJohn Fastabend 	err = nla_parse_nested(ieee, DCB_ATTR_IEEE_MAX,
1469f9ae7e4bSJohn Fastabend 			       tb[DCB_ATTR_IEEE], dcbnl_ieee_policy);
1470f9ae7e4bSJohn Fastabend 	if (err)
1471f9ae7e4bSJohn Fastabend 		return err;
1472f9ae7e4bSJohn Fastabend 
1473f9ae7e4bSJohn Fastabend 	if (ieee[DCB_ATTR_IEEE_APP_TABLE]) {
1474f9ae7e4bSJohn Fastabend 		struct nlattr *attr;
1475f9ae7e4bSJohn Fastabend 		int rem;
1476f9ae7e4bSJohn Fastabend 
1477f9ae7e4bSJohn Fastabend 		nla_for_each_nested(attr, ieee[DCB_ATTR_IEEE_APP_TABLE], rem) {
1478f9ae7e4bSJohn Fastabend 			struct dcb_app *app_data;
1479f9ae7e4bSJohn Fastabend 
1480f9ae7e4bSJohn Fastabend 			if (nla_type(attr) != DCB_ATTR_IEEE_APP)
1481f9ae7e4bSJohn Fastabend 				continue;
1482f9ae7e4bSJohn Fastabend 			app_data = nla_data(attr);
1483f9ae7e4bSJohn Fastabend 			if (ops->ieee_delapp)
1484f9ae7e4bSJohn Fastabend 				err = ops->ieee_delapp(netdev, app_data);
1485f9ae7e4bSJohn Fastabend 			else
1486f9ae7e4bSJohn Fastabend 				err = dcb_ieee_delapp(netdev, app_data);
1487f9ae7e4bSJohn Fastabend 			if (err)
1488f9ae7e4bSJohn Fastabend 				goto err;
1489f9ae7e4bSJohn Fastabend 		}
1490f9ae7e4bSJohn Fastabend 	}
1491f9ae7e4bSJohn Fastabend 
1492f9ae7e4bSJohn Fastabend err:
1493f9ae7e4bSJohn Fastabend 	dcbnl_reply(err, RTM_SETDCB, DCB_CMD_IEEE_DEL, DCB_ATTR_IEEE,
1494f9ae7e4bSJohn Fastabend 		    pid, seq, flags);
1495f9ae7e4bSJohn Fastabend 	dcbnl_notify(netdev, RTM_SETDCB, DCB_CMD_IEEE_DEL, seq, 0);
1496f9ae7e4bSJohn Fastabend 	return err;
1497f9ae7e4bSJohn Fastabend }
1498f9ae7e4bSJohn Fastabend 
1499f9ae7e4bSJohn Fastabend 
15006241b625SShmulik Ravid /* DCBX configuration */
15016241b625SShmulik Ravid static int dcbnl_getdcbx(struct net_device *netdev, struct nlattr **tb,
15026241b625SShmulik Ravid 			 u32 pid, u32 seq, u16 flags)
15036241b625SShmulik Ravid {
15047f891cf1SShmulik Ravid 	int ret;
15056241b625SShmulik Ravid 
15066241b625SShmulik Ravid 	if (!netdev->dcbnl_ops->getdcbx)
15077f891cf1SShmulik Ravid 		return -EOPNOTSUPP;
15086241b625SShmulik Ravid 
15096241b625SShmulik Ravid 	ret = dcbnl_reply(netdev->dcbnl_ops->getdcbx(netdev), RTM_GETDCB,
15106241b625SShmulik Ravid 			  DCB_CMD_GDCBX, DCB_ATTR_DCBX, pid, seq, flags);
15116241b625SShmulik Ravid 
15126241b625SShmulik Ravid 	return ret;
15136241b625SShmulik Ravid }
15146241b625SShmulik Ravid 
15156241b625SShmulik Ravid static int dcbnl_setdcbx(struct net_device *netdev, struct nlattr **tb,
15166241b625SShmulik Ravid 			 u32 pid, u32 seq, u16 flags)
15176241b625SShmulik Ravid {
15187f891cf1SShmulik Ravid 	int ret;
15196241b625SShmulik Ravid 	u8 value;
15206241b625SShmulik Ravid 
15217f891cf1SShmulik Ravid 	if (!netdev->dcbnl_ops->setdcbx)
15227f891cf1SShmulik Ravid 		return -EOPNOTSUPP;
15237f891cf1SShmulik Ravid 
15247f891cf1SShmulik Ravid 	if (!tb[DCB_ATTR_DCBX])
15257f891cf1SShmulik Ravid 		return -EINVAL;
15266241b625SShmulik Ravid 
15276241b625SShmulik Ravid 	value = nla_get_u8(tb[DCB_ATTR_DCBX]);
15286241b625SShmulik Ravid 
15296241b625SShmulik Ravid 	ret = dcbnl_reply(netdev->dcbnl_ops->setdcbx(netdev, value),
15306241b625SShmulik Ravid 			  RTM_SETDCB, DCB_CMD_SDCBX, DCB_ATTR_DCBX,
15316241b625SShmulik Ravid 			  pid, seq, flags);
15326241b625SShmulik Ravid 
15336241b625SShmulik Ravid 	return ret;
15346241b625SShmulik Ravid }
15356241b625SShmulik Ravid 
1536ea45fe4eSShmulik Ravid static int dcbnl_getfeatcfg(struct net_device *netdev, struct nlattr **tb,
1537ea45fe4eSShmulik Ravid 			    u32 pid, u32 seq, u16 flags)
1538ea45fe4eSShmulik Ravid {
1539ea45fe4eSShmulik Ravid 	struct sk_buff *dcbnl_skb;
1540ea45fe4eSShmulik Ravid 	struct nlmsghdr *nlh;
1541ea45fe4eSShmulik Ravid 	struct dcbmsg *dcb;
1542ea45fe4eSShmulik Ravid 	struct nlattr *data[DCB_FEATCFG_ATTR_MAX + 1], *nest;
1543ea45fe4eSShmulik Ravid 	u8 value;
15447f891cf1SShmulik Ravid 	int ret, i;
1545ea45fe4eSShmulik Ravid 	int getall = 0;
1546ea45fe4eSShmulik Ravid 
15477f891cf1SShmulik Ravid 	if (!netdev->dcbnl_ops->getfeatcfg)
15487f891cf1SShmulik Ravid 		return -EOPNOTSUPP;
15497f891cf1SShmulik Ravid 
15507f891cf1SShmulik Ravid 	if (!tb[DCB_ATTR_FEATCFG])
15517f891cf1SShmulik Ravid 		return -EINVAL;
1552ea45fe4eSShmulik Ravid 
1553ea45fe4eSShmulik Ravid 	ret = nla_parse_nested(data, DCB_FEATCFG_ATTR_MAX, tb[DCB_ATTR_FEATCFG],
1554ea45fe4eSShmulik Ravid 			       dcbnl_featcfg_nest);
15557f891cf1SShmulik Ravid 	if (ret)
1556ea45fe4eSShmulik Ravid 		goto err_out;
1557ea45fe4eSShmulik Ravid 
1558ea45fe4eSShmulik Ravid 	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
1559ea45fe4eSShmulik Ravid 	if (!dcbnl_skb) {
15607f891cf1SShmulik Ravid 		ret = -ENOBUFS;
1561ea45fe4eSShmulik Ravid 		goto err_out;
1562ea45fe4eSShmulik Ravid 	}
1563ea45fe4eSShmulik Ravid 
1564ea45fe4eSShmulik Ravid 	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
1565ea45fe4eSShmulik Ravid 
1566ea45fe4eSShmulik Ravid 	dcb = NLMSG_DATA(nlh);
1567ea45fe4eSShmulik Ravid 	dcb->dcb_family = AF_UNSPEC;
1568ea45fe4eSShmulik Ravid 	dcb->cmd = DCB_CMD_GFEATCFG;
1569ea45fe4eSShmulik Ravid 
1570ea45fe4eSShmulik Ravid 	nest = nla_nest_start(dcbnl_skb, DCB_ATTR_FEATCFG);
1571ea45fe4eSShmulik Ravid 	if (!nest) {
15727f891cf1SShmulik Ravid 		ret = -EMSGSIZE;
15737f891cf1SShmulik Ravid 		goto nla_put_failure;
1574ea45fe4eSShmulik Ravid 	}
1575ea45fe4eSShmulik Ravid 
1576ea45fe4eSShmulik Ravid 	if (data[DCB_FEATCFG_ATTR_ALL])
1577ea45fe4eSShmulik Ravid 		getall = 1;
1578ea45fe4eSShmulik Ravid 
1579ea45fe4eSShmulik Ravid 	for (i = DCB_FEATCFG_ATTR_ALL+1; i <= DCB_FEATCFG_ATTR_MAX; i++) {
1580ea45fe4eSShmulik Ravid 		if (!getall && !data[i])
1581ea45fe4eSShmulik Ravid 			continue;
1582ea45fe4eSShmulik Ravid 
1583ea45fe4eSShmulik Ravid 		ret = netdev->dcbnl_ops->getfeatcfg(netdev, i, &value);
15847f891cf1SShmulik Ravid 		if (!ret)
1585ea45fe4eSShmulik Ravid 			ret = nla_put_u8(dcbnl_skb, i, value);
1586ea45fe4eSShmulik Ravid 
1587ea45fe4eSShmulik Ravid 		if (ret) {
1588ea45fe4eSShmulik Ravid 			nla_nest_cancel(dcbnl_skb, nest);
15897f891cf1SShmulik Ravid 			goto nla_put_failure;
1590ea45fe4eSShmulik Ravid 		}
1591ea45fe4eSShmulik Ravid 	}
1592ea45fe4eSShmulik Ravid 	nla_nest_end(dcbnl_skb, nest);
1593ea45fe4eSShmulik Ravid 
1594ea45fe4eSShmulik Ravid 	nlmsg_end(dcbnl_skb, nlh);
1595ea45fe4eSShmulik Ravid 
15967f891cf1SShmulik Ravid 	return rtnl_unicast(dcbnl_skb, &init_net, pid);
15977f891cf1SShmulik Ravid nla_put_failure:
15987f891cf1SShmulik Ravid 	nlmsg_cancel(dcbnl_skb, nlh);
1599ea45fe4eSShmulik Ravid nlmsg_failure:
1600ea45fe4eSShmulik Ravid 	kfree_skb(dcbnl_skb);
1601ea45fe4eSShmulik Ravid err_out:
1602ea45fe4eSShmulik Ravid 	return ret;
1603ea45fe4eSShmulik Ravid }
1604ea45fe4eSShmulik Ravid 
1605ea45fe4eSShmulik Ravid static int dcbnl_setfeatcfg(struct net_device *netdev, struct nlattr **tb,
1606ea45fe4eSShmulik Ravid 			    u32 pid, u32 seq, u16 flags)
1607ea45fe4eSShmulik Ravid {
1608ea45fe4eSShmulik Ravid 	struct nlattr *data[DCB_FEATCFG_ATTR_MAX + 1];
16097f891cf1SShmulik Ravid 	int ret, i;
1610ea45fe4eSShmulik Ravid 	u8 value;
1611ea45fe4eSShmulik Ravid 
16127f891cf1SShmulik Ravid 	if (!netdev->dcbnl_ops->setfeatcfg)
16137f891cf1SShmulik Ravid 		return -ENOTSUPP;
16147f891cf1SShmulik Ravid 
16157f891cf1SShmulik Ravid 	if (!tb[DCB_ATTR_FEATCFG])
16167f891cf1SShmulik Ravid 		return -EINVAL;
1617ea45fe4eSShmulik Ravid 
1618ea45fe4eSShmulik Ravid 	ret = nla_parse_nested(data, DCB_FEATCFG_ATTR_MAX, tb[DCB_ATTR_FEATCFG],
1619ea45fe4eSShmulik Ravid 			       dcbnl_featcfg_nest);
1620ea45fe4eSShmulik Ravid 
16217f891cf1SShmulik Ravid 	if (ret)
1622ea45fe4eSShmulik Ravid 		goto err;
1623ea45fe4eSShmulik Ravid 
1624ea45fe4eSShmulik Ravid 	for (i = DCB_FEATCFG_ATTR_ALL+1; i <= DCB_FEATCFG_ATTR_MAX; i++) {
1625ea45fe4eSShmulik Ravid 		if (data[i] == NULL)
1626ea45fe4eSShmulik Ravid 			continue;
1627ea45fe4eSShmulik Ravid 
1628ea45fe4eSShmulik Ravid 		value = nla_get_u8(data[i]);
1629ea45fe4eSShmulik Ravid 
1630ea45fe4eSShmulik Ravid 		ret = netdev->dcbnl_ops->setfeatcfg(netdev, i, value);
1631ea45fe4eSShmulik Ravid 
1632ea45fe4eSShmulik Ravid 		if (ret)
16337f891cf1SShmulik Ravid 			goto err;
1634ea45fe4eSShmulik Ravid 	}
1635ea45fe4eSShmulik Ravid err:
16367f891cf1SShmulik Ravid 	dcbnl_reply(ret, RTM_SETDCB, DCB_CMD_SFEATCFG, DCB_ATTR_FEATCFG,
16377f891cf1SShmulik Ravid 		    pid, seq, flags);
16387f891cf1SShmulik Ravid 
1639ea45fe4eSShmulik Ravid 	return ret;
1640ea45fe4eSShmulik Ravid }
1641ea45fe4eSShmulik Ravid 
1642dc6ed1dfSShmulik Ravid /* Handle CEE DCBX GET commands. */
1643dc6ed1dfSShmulik Ravid static int dcbnl_cee_get(struct net_device *netdev, struct nlattr **tb,
1644dc6ed1dfSShmulik Ravid 			 u32 pid, u32 seq, u16 flags)
1645dc6ed1dfSShmulik Ravid {
1646dc6ed1dfSShmulik Ravid 	struct sk_buff *skb;
1647dc6ed1dfSShmulik Ravid 	struct nlmsghdr *nlh;
1648dc6ed1dfSShmulik Ravid 	struct dcbmsg *dcb;
1649dc6ed1dfSShmulik Ravid 	struct nlattr *cee;
1650dc6ed1dfSShmulik Ravid 	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
1651dc6ed1dfSShmulik Ravid 	int err;
1652dc6ed1dfSShmulik Ravid 
1653dc6ed1dfSShmulik Ravid 	if (!ops)
1654dc6ed1dfSShmulik Ravid 		return -EOPNOTSUPP;
1655dc6ed1dfSShmulik Ravid 
1656dc6ed1dfSShmulik Ravid 	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
1657dc6ed1dfSShmulik Ravid 	if (!skb)
1658dc6ed1dfSShmulik Ravid 		return -ENOBUFS;
1659dc6ed1dfSShmulik Ravid 
1660dc6ed1dfSShmulik Ravid 	nlh = NLMSG_NEW(skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
1661dc6ed1dfSShmulik Ravid 
1662dc6ed1dfSShmulik Ravid 	dcb = NLMSG_DATA(nlh);
1663dc6ed1dfSShmulik Ravid 	dcb->dcb_family = AF_UNSPEC;
1664dc6ed1dfSShmulik Ravid 	dcb->cmd = DCB_CMD_CEE_GET;
1665dc6ed1dfSShmulik Ravid 
1666dc6ed1dfSShmulik Ravid 	NLA_PUT_STRING(skb, DCB_ATTR_IFNAME, netdev->name);
1667dc6ed1dfSShmulik Ravid 
1668dc6ed1dfSShmulik Ravid 	cee = nla_nest_start(skb, DCB_ATTR_CEE);
1669dc6ed1dfSShmulik Ravid 	if (!cee)
1670dc6ed1dfSShmulik Ravid 		goto nla_put_failure;
1671dc6ed1dfSShmulik Ravid 
1672dc6ed1dfSShmulik Ravid 	/* get peer info if available */
1673dc6ed1dfSShmulik Ravid 	if (ops->cee_peer_getpg) {
1674dc6ed1dfSShmulik Ravid 		struct cee_pg pg;
1675dc6ed1dfSShmulik Ravid 		err = ops->cee_peer_getpg(netdev, &pg);
1676dc6ed1dfSShmulik Ravid 		if (!err)
1677dc6ed1dfSShmulik Ravid 			NLA_PUT(skb, DCB_ATTR_CEE_PEER_PG, sizeof(pg), &pg);
1678dc6ed1dfSShmulik Ravid 	}
1679dc6ed1dfSShmulik Ravid 
1680dc6ed1dfSShmulik Ravid 	if (ops->cee_peer_getpfc) {
1681dc6ed1dfSShmulik Ravid 		struct cee_pfc pfc;
1682dc6ed1dfSShmulik Ravid 		err = ops->cee_peer_getpfc(netdev, &pfc);
1683dc6ed1dfSShmulik Ravid 		if (!err)
1684dc6ed1dfSShmulik Ravid 			NLA_PUT(skb, DCB_ATTR_CEE_PEER_PFC, sizeof(pfc), &pfc);
1685dc6ed1dfSShmulik Ravid 	}
1686dc6ed1dfSShmulik Ravid 
1687dc6ed1dfSShmulik Ravid 	if (ops->peer_getappinfo && ops->peer_getapptable) {
1688dc6ed1dfSShmulik Ravid 		err = dcbnl_build_peer_app(netdev, skb,
1689dc6ed1dfSShmulik Ravid 					   DCB_ATTR_CEE_PEER_APP_TABLE,
1690dc6ed1dfSShmulik Ravid 					   DCB_ATTR_CEE_PEER_APP_INFO,
1691dc6ed1dfSShmulik Ravid 					   DCB_ATTR_CEE_PEER_APP);
1692dc6ed1dfSShmulik Ravid 		if (err)
1693dc6ed1dfSShmulik Ravid 			goto nla_put_failure;
1694dc6ed1dfSShmulik Ravid 	}
1695dc6ed1dfSShmulik Ravid 
1696dc6ed1dfSShmulik Ravid 	nla_nest_end(skb, cee);
1697dc6ed1dfSShmulik Ravid 	nlmsg_end(skb, nlh);
1698dc6ed1dfSShmulik Ravid 
1699dc6ed1dfSShmulik Ravid 	return rtnl_unicast(skb, &init_net, pid);
1700dc6ed1dfSShmulik Ravid nla_put_failure:
1701dc6ed1dfSShmulik Ravid 	nlmsg_cancel(skb, nlh);
1702dc6ed1dfSShmulik Ravid nlmsg_failure:
1703dc6ed1dfSShmulik Ravid 	kfree_skb(skb);
1704dc6ed1dfSShmulik Ravid 	return -1;
1705dc6ed1dfSShmulik Ravid }
1706dc6ed1dfSShmulik Ravid 
17072f90b865SAlexander Duyck static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
17082f90b865SAlexander Duyck {
17092f90b865SAlexander Duyck 	struct net *net = sock_net(skb->sk);
17102f90b865SAlexander Duyck 	struct net_device *netdev;
17112f90b865SAlexander Duyck 	struct dcbmsg  *dcb = (struct dcbmsg *)NLMSG_DATA(nlh);
17122f90b865SAlexander Duyck 	struct nlattr *tb[DCB_ATTR_MAX + 1];
17132f90b865SAlexander Duyck 	u32 pid = skb ? NETLINK_CB(skb).pid : 0;
17142f90b865SAlexander Duyck 	int ret = -EINVAL;
17152f90b865SAlexander Duyck 
171609ad9bc7SOctavian Purdila 	if (!net_eq(net, &init_net))
17172f90b865SAlexander Duyck 		return -EINVAL;
17182f90b865SAlexander Duyck 
17192f90b865SAlexander Duyck 	ret = nlmsg_parse(nlh, sizeof(*dcb), tb, DCB_ATTR_MAX,
17202f90b865SAlexander Duyck 			  dcbnl_rtnl_policy);
17212f90b865SAlexander Duyck 	if (ret < 0)
17222f90b865SAlexander Duyck 		return ret;
17232f90b865SAlexander Duyck 
17242f90b865SAlexander Duyck 	if (!tb[DCB_ATTR_IFNAME])
17252f90b865SAlexander Duyck 		return -EINVAL;
17262f90b865SAlexander Duyck 
17272f90b865SAlexander Duyck 	netdev = dev_get_by_name(&init_net, nla_data(tb[DCB_ATTR_IFNAME]));
17282f90b865SAlexander Duyck 	if (!netdev)
17292f90b865SAlexander Duyck 		return -EINVAL;
17302f90b865SAlexander Duyck 
17312f90b865SAlexander Duyck 	if (!netdev->dcbnl_ops)
17322f90b865SAlexander Duyck 		goto errout;
17332f90b865SAlexander Duyck 
17342f90b865SAlexander Duyck 	switch (dcb->cmd) {
17352f90b865SAlexander Duyck 	case DCB_CMD_GSTATE:
17362f90b865SAlexander Duyck 		ret = dcbnl_getstate(netdev, tb, pid, nlh->nlmsg_seq,
17372f90b865SAlexander Duyck 		                     nlh->nlmsg_flags);
17382f90b865SAlexander Duyck 		goto out;
17392f90b865SAlexander Duyck 	case DCB_CMD_PFC_GCFG:
17402f90b865SAlexander Duyck 		ret = dcbnl_getpfccfg(netdev, tb, pid, nlh->nlmsg_seq,
17412f90b865SAlexander Duyck 		                      nlh->nlmsg_flags);
17422f90b865SAlexander Duyck 		goto out;
17432f90b865SAlexander Duyck 	case DCB_CMD_GPERM_HWADDR:
17442f90b865SAlexander Duyck 		ret = dcbnl_getperm_hwaddr(netdev, tb, pid, nlh->nlmsg_seq,
17452f90b865SAlexander Duyck 		                           nlh->nlmsg_flags);
17462f90b865SAlexander Duyck 		goto out;
17472f90b865SAlexander Duyck 	case DCB_CMD_PGTX_GCFG:
17482f90b865SAlexander Duyck 		ret = dcbnl_pgtx_getcfg(netdev, tb, pid, nlh->nlmsg_seq,
17492f90b865SAlexander Duyck 		                        nlh->nlmsg_flags);
17502f90b865SAlexander Duyck 		goto out;
17512f90b865SAlexander Duyck 	case DCB_CMD_PGRX_GCFG:
17522f90b865SAlexander Duyck 		ret = dcbnl_pgrx_getcfg(netdev, tb, pid, nlh->nlmsg_seq,
17532f90b865SAlexander Duyck 		                        nlh->nlmsg_flags);
17542f90b865SAlexander Duyck 		goto out;
1755859ee3c4SAlexander Duyck 	case DCB_CMD_BCN_GCFG:
1756859ee3c4SAlexander Duyck 		ret = dcbnl_bcn_getcfg(netdev, tb, pid, nlh->nlmsg_seq,
1757859ee3c4SAlexander Duyck 		                       nlh->nlmsg_flags);
1758859ee3c4SAlexander Duyck 		goto out;
17592f90b865SAlexander Duyck 	case DCB_CMD_SSTATE:
17602f90b865SAlexander Duyck 		ret = dcbnl_setstate(netdev, tb, pid, nlh->nlmsg_seq,
17612f90b865SAlexander Duyck 		                     nlh->nlmsg_flags);
17622f90b865SAlexander Duyck 		goto out;
17632f90b865SAlexander Duyck 	case DCB_CMD_PFC_SCFG:
17642f90b865SAlexander Duyck 		ret = dcbnl_setpfccfg(netdev, tb, pid, nlh->nlmsg_seq,
17652f90b865SAlexander Duyck 		                      nlh->nlmsg_flags);
17662f90b865SAlexander Duyck 		goto out;
17672f90b865SAlexander Duyck 
17682f90b865SAlexander Duyck 	case DCB_CMD_SET_ALL:
17692f90b865SAlexander Duyck 		ret = dcbnl_setall(netdev, tb, pid, nlh->nlmsg_seq,
17702f90b865SAlexander Duyck 		                   nlh->nlmsg_flags);
17712f90b865SAlexander Duyck 		goto out;
17722f90b865SAlexander Duyck 	case DCB_CMD_PGTX_SCFG:
17732f90b865SAlexander Duyck 		ret = dcbnl_pgtx_setcfg(netdev, tb, pid, nlh->nlmsg_seq,
17742f90b865SAlexander Duyck 		                        nlh->nlmsg_flags);
17752f90b865SAlexander Duyck 		goto out;
17762f90b865SAlexander Duyck 	case DCB_CMD_PGRX_SCFG:
17772f90b865SAlexander Duyck 		ret = dcbnl_pgrx_setcfg(netdev, tb, pid, nlh->nlmsg_seq,
17782f90b865SAlexander Duyck 		                        nlh->nlmsg_flags);
17792f90b865SAlexander Duyck 		goto out;
178046132188SAlexander Duyck 	case DCB_CMD_GCAP:
178146132188SAlexander Duyck 		ret = dcbnl_getcap(netdev, tb, pid, nlh->nlmsg_seq,
178246132188SAlexander Duyck 		                   nlh->nlmsg_flags);
178346132188SAlexander Duyck 		goto out;
178433dbabc4SAlexander Duyck 	case DCB_CMD_GNUMTCS:
178533dbabc4SAlexander Duyck 		ret = dcbnl_getnumtcs(netdev, tb, pid, nlh->nlmsg_seq,
178633dbabc4SAlexander Duyck 		                      nlh->nlmsg_flags);
178733dbabc4SAlexander Duyck 		goto out;
178833dbabc4SAlexander Duyck 	case DCB_CMD_SNUMTCS:
178933dbabc4SAlexander Duyck 		ret = dcbnl_setnumtcs(netdev, tb, pid, nlh->nlmsg_seq,
179033dbabc4SAlexander Duyck 		                      nlh->nlmsg_flags);
179133dbabc4SAlexander Duyck 		goto out;
17920eb3aa9bSAlexander Duyck 	case DCB_CMD_PFC_GSTATE:
17930eb3aa9bSAlexander Duyck 		ret = dcbnl_getpfcstate(netdev, tb, pid, nlh->nlmsg_seq,
17940eb3aa9bSAlexander Duyck 		                        nlh->nlmsg_flags);
17950eb3aa9bSAlexander Duyck 		goto out;
17960eb3aa9bSAlexander Duyck 	case DCB_CMD_PFC_SSTATE:
17970eb3aa9bSAlexander Duyck 		ret = dcbnl_setpfcstate(netdev, tb, pid, nlh->nlmsg_seq,
17980eb3aa9bSAlexander Duyck 		                        nlh->nlmsg_flags);
17990eb3aa9bSAlexander Duyck 		goto out;
1800859ee3c4SAlexander Duyck 	case DCB_CMD_BCN_SCFG:
1801859ee3c4SAlexander Duyck 		ret = dcbnl_bcn_setcfg(netdev, tb, pid, nlh->nlmsg_seq,
1802859ee3c4SAlexander Duyck 		                       nlh->nlmsg_flags);
1803859ee3c4SAlexander Duyck 		goto out;
180457949686SYi Zou 	case DCB_CMD_GAPP:
180557949686SYi Zou 		ret = dcbnl_getapp(netdev, tb, pid, nlh->nlmsg_seq,
180657949686SYi Zou 		                   nlh->nlmsg_flags);
180757949686SYi Zou 		goto out;
180857949686SYi Zou 	case DCB_CMD_SAPP:
180957949686SYi Zou 		ret = dcbnl_setapp(netdev, tb, pid, nlh->nlmsg_seq,
181057949686SYi Zou 		                   nlh->nlmsg_flags);
181157949686SYi Zou 		goto out;
18123e29027aSJohn Fastabend 	case DCB_CMD_IEEE_SET:
18133e29027aSJohn Fastabend 		ret = dcbnl_ieee_set(netdev, tb, pid, nlh->nlmsg_seq,
18143e29027aSJohn Fastabend 				     nlh->nlmsg_flags);
18153e29027aSJohn Fastabend 		goto out;
18163e29027aSJohn Fastabend 	case DCB_CMD_IEEE_GET:
18173e29027aSJohn Fastabend 		ret = dcbnl_ieee_get(netdev, tb, pid, nlh->nlmsg_seq,
18183e29027aSJohn Fastabend 				     nlh->nlmsg_flags);
18193e29027aSJohn Fastabend 		goto out;
1820f9ae7e4bSJohn Fastabend 	case DCB_CMD_IEEE_DEL:
1821f9ae7e4bSJohn Fastabend 		ret = dcbnl_ieee_del(netdev, tb, pid, nlh->nlmsg_seq,
1822f9ae7e4bSJohn Fastabend 				     nlh->nlmsg_flags);
1823f9ae7e4bSJohn Fastabend 		goto out;
18246241b625SShmulik Ravid 	case DCB_CMD_GDCBX:
18256241b625SShmulik Ravid 		ret = dcbnl_getdcbx(netdev, tb, pid, nlh->nlmsg_seq,
18266241b625SShmulik Ravid 				    nlh->nlmsg_flags);
18276241b625SShmulik Ravid 		goto out;
18286241b625SShmulik Ravid 	case DCB_CMD_SDCBX:
18296241b625SShmulik Ravid 		ret = dcbnl_setdcbx(netdev, tb, pid, nlh->nlmsg_seq,
18306241b625SShmulik Ravid 				    nlh->nlmsg_flags);
18316241b625SShmulik Ravid 		goto out;
1832ea45fe4eSShmulik Ravid 	case DCB_CMD_GFEATCFG:
1833ea45fe4eSShmulik Ravid 		ret = dcbnl_getfeatcfg(netdev, tb, pid, nlh->nlmsg_seq,
1834ea45fe4eSShmulik Ravid 				       nlh->nlmsg_flags);
1835ea45fe4eSShmulik Ravid 		goto out;
1836ea45fe4eSShmulik Ravid 	case DCB_CMD_SFEATCFG:
1837ea45fe4eSShmulik Ravid 		ret = dcbnl_setfeatcfg(netdev, tb, pid, nlh->nlmsg_seq,
1838ea45fe4eSShmulik Ravid 				       nlh->nlmsg_flags);
1839ea45fe4eSShmulik Ravid 		goto out;
1840dc6ed1dfSShmulik Ravid 	case DCB_CMD_CEE_GET:
1841dc6ed1dfSShmulik Ravid 		ret = dcbnl_cee_get(netdev, tb, pid, nlh->nlmsg_seq,
1842dc6ed1dfSShmulik Ravid 				    nlh->nlmsg_flags);
1843dc6ed1dfSShmulik Ravid 		goto out;
18442f90b865SAlexander Duyck 	default:
18452f90b865SAlexander Duyck 		goto errout;
18462f90b865SAlexander Duyck 	}
18472f90b865SAlexander Duyck errout:
18482f90b865SAlexander Duyck 	ret = -EINVAL;
18492f90b865SAlexander Duyck out:
18502f90b865SAlexander Duyck 	dev_put(netdev);
18512f90b865SAlexander Duyck 	return ret;
18522f90b865SAlexander Duyck }
18532f90b865SAlexander Duyck 
18549ab933abSJohn Fastabend /**
18559ab933abSJohn Fastabend  * dcb_getapp - retrieve the DCBX application user priority
18569ab933abSJohn Fastabend  *
18579ab933abSJohn Fastabend  * On success returns a non-zero 802.1p user priority bitmap
18589ab933abSJohn Fastabend  * otherwise returns 0 as the invalid user priority bitmap to
18599ab933abSJohn Fastabend  * indicate an error.
18609ab933abSJohn Fastabend  */
18619ab933abSJohn Fastabend u8 dcb_getapp(struct net_device *dev, struct dcb_app *app)
18629ab933abSJohn Fastabend {
18639ab933abSJohn Fastabend 	struct dcb_app_type *itr;
18649ab933abSJohn Fastabend 	u8 prio = 0;
18659ab933abSJohn Fastabend 
18669ab933abSJohn Fastabend 	spin_lock(&dcb_lock);
18679ab933abSJohn Fastabend 	list_for_each_entry(itr, &dcb_app_list, list) {
18689ab933abSJohn Fastabend 		if (itr->app.selector == app->selector &&
18699ab933abSJohn Fastabend 		    itr->app.protocol == app->protocol &&
18709ab933abSJohn Fastabend 		    (strncmp(itr->name, dev->name, IFNAMSIZ) == 0)) {
18719ab933abSJohn Fastabend 			prio = itr->app.priority;
18729ab933abSJohn Fastabend 			break;
18739ab933abSJohn Fastabend 		}
18749ab933abSJohn Fastabend 	}
18759ab933abSJohn Fastabend 	spin_unlock(&dcb_lock);
18769ab933abSJohn Fastabend 
18779ab933abSJohn Fastabend 	return prio;
18789ab933abSJohn Fastabend }
18799ab933abSJohn Fastabend EXPORT_SYMBOL(dcb_getapp);
18809ab933abSJohn Fastabend 
18819ab933abSJohn Fastabend /**
1882b6db2174SJohn Fastabend  * dcb_setapp - add CEE dcb application data to app list
18839ab933abSJohn Fastabend  *
1884b6db2174SJohn Fastabend  * Priority 0 is an invalid priority in CEE spec. This routine
1885b6db2174SJohn Fastabend  * removes applications from the app list if the priority is
1886b6db2174SJohn Fastabend  * set to zero.
18879ab933abSJohn Fastabend  */
1888ab6baf98SJohn Fastabend int dcb_setapp(struct net_device *dev, struct dcb_app *new)
18899ab933abSJohn Fastabend {
18909ab933abSJohn Fastabend 	struct dcb_app_type *itr;
18917ec79270SJohn Fastabend 	struct dcb_app_type event;
18927ec79270SJohn Fastabend 
18937ec79270SJohn Fastabend 	memcpy(&event.name, dev->name, sizeof(event.name));
18947ec79270SJohn Fastabend 	memcpy(&event.app, new, sizeof(event.app));
18959ab933abSJohn Fastabend 
18969ab933abSJohn Fastabend 	spin_lock(&dcb_lock);
18979ab933abSJohn Fastabend 	/* Search for existing match and replace */
18989ab933abSJohn Fastabend 	list_for_each_entry(itr, &dcb_app_list, list) {
18999ab933abSJohn Fastabend 		if (itr->app.selector == new->selector &&
19009ab933abSJohn Fastabend 		    itr->app.protocol == new->protocol &&
19019ab933abSJohn Fastabend 		    (strncmp(itr->name, dev->name, IFNAMSIZ) == 0)) {
19029ab933abSJohn Fastabend 			if (new->priority)
19039ab933abSJohn Fastabend 				itr->app.priority = new->priority;
19049ab933abSJohn Fastabend 			else {
19059ab933abSJohn Fastabend 				list_del(&itr->list);
19069ab933abSJohn Fastabend 				kfree(itr);
19079ab933abSJohn Fastabend 			}
19089ab933abSJohn Fastabend 			goto out;
19099ab933abSJohn Fastabend 		}
19109ab933abSJohn Fastabend 	}
19119ab933abSJohn Fastabend 	/* App type does not exist add new application type */
19129ab933abSJohn Fastabend 	if (new->priority) {
19139ab933abSJohn Fastabend 		struct dcb_app_type *entry;
19149ab933abSJohn Fastabend 		entry = kmalloc(sizeof(struct dcb_app_type), GFP_ATOMIC);
19159ab933abSJohn Fastabend 		if (!entry) {
19169ab933abSJohn Fastabend 			spin_unlock(&dcb_lock);
19179ab933abSJohn Fastabend 			return -ENOMEM;
19189ab933abSJohn Fastabend 		}
19199ab933abSJohn Fastabend 
19209ab933abSJohn Fastabend 		memcpy(&entry->app, new, sizeof(*new));
19219ab933abSJohn Fastabend 		strncpy(entry->name, dev->name, IFNAMSIZ);
19229ab933abSJohn Fastabend 		list_add(&entry->list, &dcb_app_list);
19239ab933abSJohn Fastabend 	}
19249ab933abSJohn Fastabend out:
19259ab933abSJohn Fastabend 	spin_unlock(&dcb_lock);
19267ec79270SJohn Fastabend 	call_dcbevent_notifiers(DCB_APP_EVENT, &event);
19279ab933abSJohn Fastabend 	return 0;
19289ab933abSJohn Fastabend }
19299ab933abSJohn Fastabend EXPORT_SYMBOL(dcb_setapp);
19309ab933abSJohn Fastabend 
1931b6db2174SJohn Fastabend /**
1932a364c8cfSJohn Fastabend  * dcb_ieee_getapp_mask - retrieve the IEEE DCB application priority
1933a364c8cfSJohn Fastabend  *
1934a364c8cfSJohn Fastabend  * Helper routine which on success returns a non-zero 802.1Qaz user
1935a364c8cfSJohn Fastabend  * priority bitmap otherwise returns 0 to indicate the dcb_app was
1936a364c8cfSJohn Fastabend  * not found in APP list.
1937a364c8cfSJohn Fastabend  */
1938a364c8cfSJohn Fastabend u8 dcb_ieee_getapp_mask(struct net_device *dev, struct dcb_app *app)
1939a364c8cfSJohn Fastabend {
1940a364c8cfSJohn Fastabend 	struct dcb_app_type *itr;
1941a364c8cfSJohn Fastabend 	u8 prio = 0;
1942a364c8cfSJohn Fastabend 
1943a364c8cfSJohn Fastabend 	spin_lock(&dcb_lock);
1944a364c8cfSJohn Fastabend 	list_for_each_entry(itr, &dcb_app_list, list) {
1945a364c8cfSJohn Fastabend 		if (itr->app.selector == app->selector &&
1946a364c8cfSJohn Fastabend 		    itr->app.protocol == app->protocol &&
1947a364c8cfSJohn Fastabend 		    (strncmp(itr->name, dev->name, IFNAMSIZ) == 0)) {
1948a364c8cfSJohn Fastabend 			prio |= 1 << itr->app.priority;
1949a364c8cfSJohn Fastabend 		}
1950a364c8cfSJohn Fastabend 	}
1951a364c8cfSJohn Fastabend 	spin_unlock(&dcb_lock);
1952a364c8cfSJohn Fastabend 
1953a364c8cfSJohn Fastabend 	return prio;
1954a364c8cfSJohn Fastabend }
1955a364c8cfSJohn Fastabend EXPORT_SYMBOL(dcb_ieee_getapp_mask);
1956a364c8cfSJohn Fastabend 
1957a364c8cfSJohn Fastabend /**
1958b6db2174SJohn Fastabend  * dcb_ieee_setapp - add IEEE dcb application data to app list
1959b6db2174SJohn Fastabend  *
1960b6db2174SJohn Fastabend  * This adds Application data to the list. Multiple application
1961b6db2174SJohn Fastabend  * entries may exists for the same selector and protocol as long
1962b6db2174SJohn Fastabend  * as the priorities are different.
1963b6db2174SJohn Fastabend  */
1964b6db2174SJohn Fastabend int dcb_ieee_setapp(struct net_device *dev, struct dcb_app *new)
1965b6db2174SJohn Fastabend {
1966b6db2174SJohn Fastabend 	struct dcb_app_type *itr, *entry;
1967b6db2174SJohn Fastabend 	struct dcb_app_type event;
1968b6db2174SJohn Fastabend 	int err = 0;
1969b6db2174SJohn Fastabend 
1970b6db2174SJohn Fastabend 	memcpy(&event.name, dev->name, sizeof(event.name));
1971b6db2174SJohn Fastabend 	memcpy(&event.app, new, sizeof(event.app));
1972b6db2174SJohn Fastabend 
1973b6db2174SJohn Fastabend 	spin_lock(&dcb_lock);
1974b6db2174SJohn Fastabend 	/* Search for existing match and abort if found */
1975b6db2174SJohn Fastabend 	list_for_each_entry(itr, &dcb_app_list, list) {
1976b6db2174SJohn Fastabend 		if (itr->app.selector == new->selector &&
1977b6db2174SJohn Fastabend 		    itr->app.protocol == new->protocol &&
1978b6db2174SJohn Fastabend 		    itr->app.priority == new->priority &&
1979b6db2174SJohn Fastabend 		    (strncmp(itr->name, dev->name, IFNAMSIZ) == 0)) {
1980b6db2174SJohn Fastabend 			err = -EEXIST;
1981b6db2174SJohn Fastabend 			goto out;
1982b6db2174SJohn Fastabend 		}
1983b6db2174SJohn Fastabend 	}
1984b6db2174SJohn Fastabend 
1985b6db2174SJohn Fastabend 	/* App entry does not exist add new entry */
1986b6db2174SJohn Fastabend 	entry = kmalloc(sizeof(struct dcb_app_type), GFP_ATOMIC);
1987b6db2174SJohn Fastabend 	if (!entry) {
1988b6db2174SJohn Fastabend 		err = -ENOMEM;
1989b6db2174SJohn Fastabend 		goto out;
1990b6db2174SJohn Fastabend 	}
1991b6db2174SJohn Fastabend 
1992b6db2174SJohn Fastabend 	memcpy(&entry->app, new, sizeof(*new));
1993b6db2174SJohn Fastabend 	strncpy(entry->name, dev->name, IFNAMSIZ);
1994b6db2174SJohn Fastabend 	list_add(&entry->list, &dcb_app_list);
1995b6db2174SJohn Fastabend out:
1996b6db2174SJohn Fastabend 	spin_unlock(&dcb_lock);
1997b6db2174SJohn Fastabend 	if (!err)
1998b6db2174SJohn Fastabend 		call_dcbevent_notifiers(DCB_APP_EVENT, &event);
1999b6db2174SJohn Fastabend 	return err;
2000b6db2174SJohn Fastabend }
2001b6db2174SJohn Fastabend EXPORT_SYMBOL(dcb_ieee_setapp);
2002b6db2174SJohn Fastabend 
2003f9ae7e4bSJohn Fastabend /**
2004f9ae7e4bSJohn Fastabend  * dcb_ieee_delapp - delete IEEE dcb application data from list
2005f9ae7e4bSJohn Fastabend  *
2006f9ae7e4bSJohn Fastabend  * This removes a matching APP data from the APP list
2007f9ae7e4bSJohn Fastabend  */
2008f9ae7e4bSJohn Fastabend int dcb_ieee_delapp(struct net_device *dev, struct dcb_app *del)
2009f9ae7e4bSJohn Fastabend {
2010f9ae7e4bSJohn Fastabend 	struct dcb_app_type *itr;
2011f9ae7e4bSJohn Fastabend 	struct dcb_app_type event;
2012f9ae7e4bSJohn Fastabend 	int err = -ENOENT;
2013f9ae7e4bSJohn Fastabend 
2014f9ae7e4bSJohn Fastabend 	memcpy(&event.name, dev->name, sizeof(event.name));
2015f9ae7e4bSJohn Fastabend 	memcpy(&event.app, del, sizeof(event.app));
2016f9ae7e4bSJohn Fastabend 
2017f9ae7e4bSJohn Fastabend 	spin_lock(&dcb_lock);
2018f9ae7e4bSJohn Fastabend 	/* Search for existing match and remove it. */
2019f9ae7e4bSJohn Fastabend 	list_for_each_entry(itr, &dcb_app_list, list) {
2020f9ae7e4bSJohn Fastabend 		if (itr->app.selector == del->selector &&
2021f9ae7e4bSJohn Fastabend 		    itr->app.protocol == del->protocol &&
2022f9ae7e4bSJohn Fastabend 		    itr->app.priority == del->priority &&
2023f9ae7e4bSJohn Fastabend 		    (strncmp(itr->name, dev->name, IFNAMSIZ) == 0)) {
2024f9ae7e4bSJohn Fastabend 			list_del(&itr->list);
2025f9ae7e4bSJohn Fastabend 			kfree(itr);
2026f9ae7e4bSJohn Fastabend 			err = 0;
2027f9ae7e4bSJohn Fastabend 			goto out;
2028f9ae7e4bSJohn Fastabend 		}
2029f9ae7e4bSJohn Fastabend 	}
2030f9ae7e4bSJohn Fastabend 
2031f9ae7e4bSJohn Fastabend out:
2032f9ae7e4bSJohn Fastabend 	spin_unlock(&dcb_lock);
2033f9ae7e4bSJohn Fastabend 	if (!err)
2034f9ae7e4bSJohn Fastabend 		call_dcbevent_notifiers(DCB_APP_EVENT, &event);
2035f9ae7e4bSJohn Fastabend 	return err;
2036f9ae7e4bSJohn Fastabend }
2037f9ae7e4bSJohn Fastabend EXPORT_SYMBOL(dcb_ieee_delapp);
2038f9ae7e4bSJohn Fastabend 
20397c14c3f1SShmulik Ravid static void dcb_flushapp(void)
20409ab933abSJohn Fastabend {
20419ab933abSJohn Fastabend 	struct dcb_app_type *app;
20422a8fe003SDan Carpenter 	struct dcb_app_type *tmp;
20439ab933abSJohn Fastabend 
20449ab933abSJohn Fastabend 	spin_lock(&dcb_lock);
20452a8fe003SDan Carpenter 	list_for_each_entry_safe(app, tmp, &dcb_app_list, list) {
20469ab933abSJohn Fastabend 		list_del(&app->list);
20479ab933abSJohn Fastabend 		kfree(app);
20489ab933abSJohn Fastabend 	}
20499ab933abSJohn Fastabend 	spin_unlock(&dcb_lock);
20509ab933abSJohn Fastabend }
20519ab933abSJohn Fastabend 
20522f90b865SAlexander Duyck static int __init dcbnl_init(void)
20532f90b865SAlexander Duyck {
20549ab933abSJohn Fastabend 	INIT_LIST_HEAD(&dcb_app_list);
20559ab933abSJohn Fastabend 
2056c7ac8679SGreg Rose 	rtnl_register(PF_UNSPEC, RTM_GETDCB, dcb_doit, NULL, NULL);
2057c7ac8679SGreg Rose 	rtnl_register(PF_UNSPEC, RTM_SETDCB, dcb_doit, NULL, NULL);
20582f90b865SAlexander Duyck 
20592f90b865SAlexander Duyck 	return 0;
20602f90b865SAlexander Duyck }
20612f90b865SAlexander Duyck module_init(dcbnl_init);
20622f90b865SAlexander Duyck 
20632f90b865SAlexander Duyck static void __exit dcbnl_exit(void)
20642f90b865SAlexander Duyck {
20652f90b865SAlexander Duyck 	rtnl_unregister(PF_UNSPEC, RTM_GETDCB);
20662f90b865SAlexander Duyck 	rtnl_unregister(PF_UNSPEC, RTM_SETDCB);
20679ab933abSJohn Fastabend 	dcb_flushapp();
20682f90b865SAlexander Duyck }
20692f90b865SAlexander Duyck module_exit(dcbnl_exit);
2070