xref: /openbmc/linux/net/dcb/dcbnl.c (revision 858eb711)
12f90b865SAlexander Duyck /*
22f90b865SAlexander Duyck  * Copyright (c) 2008, 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>
222f90b865SAlexander Duyck #include <net/netlink.h>
232f90b865SAlexander Duyck #include <net/rtnetlink.h>
242f90b865SAlexander Duyck #include <linux/dcbnl.h>
252f90b865SAlexander Duyck #include <linux/rtnetlink.h>
262f90b865SAlexander Duyck #include <net/sock.h>
272f90b865SAlexander Duyck 
282f90b865SAlexander Duyck /**
292f90b865SAlexander Duyck  * Data Center Bridging (DCB) is a collection of Ethernet enhancements
302f90b865SAlexander Duyck  * intended to allow network traffic with differing requirements
312f90b865SAlexander Duyck  * (highly reliable, no drops vs. best effort vs. low latency) to operate
322f90b865SAlexander Duyck  * and co-exist on Ethernet.  Current DCB features are:
332f90b865SAlexander Duyck  *
342f90b865SAlexander Duyck  * Enhanced Transmission Selection (aka Priority Grouping [PG]) - provides a
352f90b865SAlexander Duyck  *   framework for assigning bandwidth guarantees to traffic classes.
362f90b865SAlexander Duyck  *
372f90b865SAlexander Duyck  * Priority-based Flow Control (PFC) - provides a flow control mechanism which
382f90b865SAlexander Duyck  *   can work independently for each 802.1p priority.
392f90b865SAlexander Duyck  *
402f90b865SAlexander Duyck  * Congestion Notification - provides a mechanism for end-to-end congestion
412f90b865SAlexander Duyck  *   control for protocols which do not have built-in congestion management.
422f90b865SAlexander Duyck  *
432f90b865SAlexander Duyck  * More information about the emerging standards for these Ethernet features
442f90b865SAlexander Duyck  * can be found at: http://www.ieee802.org/1/pages/dcbridges.html
452f90b865SAlexander Duyck  *
462f90b865SAlexander Duyck  * This file implements an rtnetlink interface to allow configuration of DCB
472f90b865SAlexander Duyck  * features for capable devices.
482f90b865SAlexander Duyck  */
492f90b865SAlexander Duyck 
502f90b865SAlexander Duyck MODULE_AUTHOR("Lucy Liu, <lucy.liu@intel.com>");
517a6b6f51SJeff Kirsher MODULE_DESCRIPTION("Data Center Bridging netlink interface");
522f90b865SAlexander Duyck MODULE_LICENSE("GPL");
532f90b865SAlexander Duyck 
542f90b865SAlexander Duyck /**************** DCB attribute policies *************************************/
552f90b865SAlexander Duyck 
562f90b865SAlexander Duyck /* DCB netlink attributes policy */
572f90b865SAlexander Duyck static struct nla_policy dcbnl_rtnl_policy[DCB_ATTR_MAX + 1] = {
58859ee3c4SAlexander Duyck 	[DCB_ATTR_IFNAME]      = {.type = NLA_NUL_STRING, .len = IFNAMSIZ - 1},
592f90b865SAlexander Duyck 	[DCB_ATTR_STATE]       = {.type = NLA_U8},
602f90b865SAlexander Duyck 	[DCB_ATTR_PFC_CFG]     = {.type = NLA_NESTED},
612f90b865SAlexander Duyck 	[DCB_ATTR_PG_CFG]      = {.type = NLA_NESTED},
622f90b865SAlexander Duyck 	[DCB_ATTR_SET_ALL]     = {.type = NLA_U8},
632f90b865SAlexander Duyck 	[DCB_ATTR_PERM_HWADDR] = {.type = NLA_FLAG},
6446132188SAlexander Duyck 	[DCB_ATTR_CAP]         = {.type = NLA_NESTED},
650eb3aa9bSAlexander Duyck 	[DCB_ATTR_PFC_STATE]   = {.type = NLA_U8},
66859ee3c4SAlexander Duyck 	[DCB_ATTR_BCN]         = {.type = NLA_NESTED},
672f90b865SAlexander Duyck };
682f90b865SAlexander Duyck 
692f90b865SAlexander Duyck /* DCB priority flow control to User Priority nested attributes */
702f90b865SAlexander Duyck static struct nla_policy dcbnl_pfc_up_nest[DCB_PFC_UP_ATTR_MAX + 1] = {
712f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_0]   = {.type = NLA_U8},
722f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_1]   = {.type = NLA_U8},
732f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_2]   = {.type = NLA_U8},
742f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_3]   = {.type = NLA_U8},
752f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_4]   = {.type = NLA_U8},
762f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_5]   = {.type = NLA_U8},
772f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_6]   = {.type = NLA_U8},
782f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_7]   = {.type = NLA_U8},
792f90b865SAlexander Duyck 	[DCB_PFC_UP_ATTR_ALL] = {.type = NLA_FLAG},
802f90b865SAlexander Duyck };
812f90b865SAlexander Duyck 
822f90b865SAlexander Duyck /* DCB priority grouping nested attributes */
832f90b865SAlexander Duyck static struct nla_policy dcbnl_pg_nest[DCB_PG_ATTR_MAX + 1] = {
842f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_0]      = {.type = NLA_NESTED},
852f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_1]      = {.type = NLA_NESTED},
862f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_2]      = {.type = NLA_NESTED},
872f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_3]      = {.type = NLA_NESTED},
882f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_4]      = {.type = NLA_NESTED},
892f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_5]      = {.type = NLA_NESTED},
902f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_6]      = {.type = NLA_NESTED},
912f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_7]      = {.type = NLA_NESTED},
922f90b865SAlexander Duyck 	[DCB_PG_ATTR_TC_ALL]    = {.type = NLA_NESTED},
932f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_0]   = {.type = NLA_U8},
942f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_1]   = {.type = NLA_U8},
952f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_2]   = {.type = NLA_U8},
962f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_3]   = {.type = NLA_U8},
972f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_4]   = {.type = NLA_U8},
982f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_5]   = {.type = NLA_U8},
992f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_6]   = {.type = NLA_U8},
1002f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_7]   = {.type = NLA_U8},
1012f90b865SAlexander Duyck 	[DCB_PG_ATTR_BW_ID_ALL] = {.type = NLA_FLAG},
1022f90b865SAlexander Duyck };
1032f90b865SAlexander Duyck 
1042f90b865SAlexander Duyck /* DCB traffic class nested attributes. */
1052f90b865SAlexander Duyck static struct nla_policy dcbnl_tc_param_nest[DCB_TC_ATTR_PARAM_MAX + 1] = {
1062f90b865SAlexander Duyck 	[DCB_TC_ATTR_PARAM_PGID]            = {.type = NLA_U8},
1072f90b865SAlexander Duyck 	[DCB_TC_ATTR_PARAM_UP_MAPPING]      = {.type = NLA_U8},
1082f90b865SAlexander Duyck 	[DCB_TC_ATTR_PARAM_STRICT_PRIO]     = {.type = NLA_U8},
1092f90b865SAlexander Duyck 	[DCB_TC_ATTR_PARAM_BW_PCT]          = {.type = NLA_U8},
1102f90b865SAlexander Duyck 	[DCB_TC_ATTR_PARAM_ALL]             = {.type = NLA_FLAG},
1112f90b865SAlexander Duyck };
1122f90b865SAlexander Duyck 
11346132188SAlexander Duyck /* DCB capabilities nested attributes. */
11446132188SAlexander Duyck static struct nla_policy dcbnl_cap_nest[DCB_CAP_ATTR_MAX + 1] = {
11546132188SAlexander Duyck 	[DCB_CAP_ATTR_ALL]     = {.type = NLA_FLAG},
11646132188SAlexander Duyck 	[DCB_CAP_ATTR_PG]      = {.type = NLA_U8},
11746132188SAlexander Duyck 	[DCB_CAP_ATTR_PFC]     = {.type = NLA_U8},
11846132188SAlexander Duyck 	[DCB_CAP_ATTR_UP2TC]   = {.type = NLA_U8},
11946132188SAlexander Duyck 	[DCB_CAP_ATTR_PG_TCS]  = {.type = NLA_U8},
12046132188SAlexander Duyck 	[DCB_CAP_ATTR_PFC_TCS] = {.type = NLA_U8},
12146132188SAlexander Duyck 	[DCB_CAP_ATTR_GSP]     = {.type = NLA_U8},
12246132188SAlexander Duyck 	[DCB_CAP_ATTR_BCN]     = {.type = NLA_U8},
12346132188SAlexander Duyck };
1242f90b865SAlexander Duyck 
12533dbabc4SAlexander Duyck /* DCB capabilities nested attributes. */
12633dbabc4SAlexander Duyck static struct nla_policy dcbnl_numtcs_nest[DCB_NUMTCS_ATTR_MAX + 1] = {
12733dbabc4SAlexander Duyck 	[DCB_NUMTCS_ATTR_ALL]     = {.type = NLA_FLAG},
12833dbabc4SAlexander Duyck 	[DCB_NUMTCS_ATTR_PG]      = {.type = NLA_U8},
12933dbabc4SAlexander Duyck 	[DCB_NUMTCS_ATTR_PFC]     = {.type = NLA_U8},
13033dbabc4SAlexander Duyck };
13133dbabc4SAlexander Duyck 
132859ee3c4SAlexander Duyck /* DCB BCN nested attributes. */
133859ee3c4SAlexander Duyck static struct nla_policy dcbnl_bcn_nest[DCB_BCN_ATTR_MAX + 1] = {
134859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_0]         = {.type = NLA_U8},
135859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_1]         = {.type = NLA_U8},
136859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_2]         = {.type = NLA_U8},
137859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_3]         = {.type = NLA_U8},
138859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_4]         = {.type = NLA_U8},
139859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_5]         = {.type = NLA_U8},
140859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_6]         = {.type = NLA_U8},
141859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_7]         = {.type = NLA_U8},
142859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RP_ALL]       = {.type = NLA_FLAG},
143f4314e81SDon Skidmore 	[DCB_BCN_ATTR_BCNA_0]       = {.type = NLA_U32},
144f4314e81SDon Skidmore 	[DCB_BCN_ATTR_BCNA_1]       = {.type = NLA_U32},
145859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_ALPHA]        = {.type = NLA_U32},
146859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_BETA]         = {.type = NLA_U32},
147859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_GD]           = {.type = NLA_U32},
148859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_GI]           = {.type = NLA_U32},
149859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_TMAX]         = {.type = NLA_U32},
150859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_TD]           = {.type = NLA_U32},
151859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RMIN]         = {.type = NLA_U32},
152859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_W]            = {.type = NLA_U32},
153859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RD]           = {.type = NLA_U32},
154859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RU]           = {.type = NLA_U32},
155859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_WRTT]         = {.type = NLA_U32},
156859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RI]           = {.type = NLA_U32},
157859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_C]            = {.type = NLA_U32},
158859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_ALL]          = {.type = NLA_FLAG},
159859ee3c4SAlexander Duyck };
160859ee3c4SAlexander Duyck 
1612f90b865SAlexander Duyck /* standard netlink reply call */
1622f90b865SAlexander Duyck static int dcbnl_reply(u8 value, u8 event, u8 cmd, u8 attr, u32 pid,
1632f90b865SAlexander Duyck                        u32 seq, u16 flags)
1642f90b865SAlexander Duyck {
1652f90b865SAlexander Duyck 	struct sk_buff *dcbnl_skb;
1662f90b865SAlexander Duyck 	struct dcbmsg *dcb;
1672f90b865SAlexander Duyck 	struct nlmsghdr *nlh;
1682f90b865SAlexander Duyck 	int ret = -EINVAL;
1692f90b865SAlexander Duyck 
1702f90b865SAlexander Duyck 	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
1712f90b865SAlexander Duyck 	if (!dcbnl_skb)
1722f90b865SAlexander Duyck 		return ret;
1732f90b865SAlexander Duyck 
1742f90b865SAlexander Duyck 	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, event, sizeof(*dcb), flags);
1752f90b865SAlexander Duyck 
1762f90b865SAlexander Duyck 	dcb = NLMSG_DATA(nlh);
1772f90b865SAlexander Duyck 	dcb->dcb_family = AF_UNSPEC;
1782f90b865SAlexander Duyck 	dcb->cmd = cmd;
1792f90b865SAlexander Duyck 	dcb->dcb_pad = 0;
1802f90b865SAlexander Duyck 
1812f90b865SAlexander Duyck 	ret = nla_put_u8(dcbnl_skb, attr, value);
1822f90b865SAlexander Duyck 	if (ret)
1832f90b865SAlexander Duyck 		goto err;
1842f90b865SAlexander Duyck 
1852f90b865SAlexander Duyck 	/* end the message, assign the nlmsg_len. */
1862f90b865SAlexander Duyck 	nlmsg_end(dcbnl_skb, nlh);
1872f90b865SAlexander Duyck 	ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
1882f90b865SAlexander Duyck 	if (ret)
1892f90b865SAlexander Duyck 		goto err;
1902f90b865SAlexander Duyck 
1912f90b865SAlexander Duyck 	return 0;
1922f90b865SAlexander Duyck nlmsg_failure:
1932f90b865SAlexander Duyck err:
194858eb711SRoel Kluin 	kfree_skb(dcbnl_skb);
1952f90b865SAlexander Duyck 	return ret;
1962f90b865SAlexander Duyck }
1972f90b865SAlexander Duyck 
1982f90b865SAlexander Duyck static int dcbnl_getstate(struct net_device *netdev, struct nlattr **tb,
1992f90b865SAlexander Duyck                           u32 pid, u32 seq, u16 flags)
2002f90b865SAlexander Duyck {
2012f90b865SAlexander Duyck 	int ret = -EINVAL;
2022f90b865SAlexander Duyck 
2032f90b865SAlexander Duyck 	/* if (!tb[DCB_ATTR_STATE] || !netdev->dcbnl_ops->getstate) */
2042f90b865SAlexander Duyck 	if (!netdev->dcbnl_ops->getstate)
2052f90b865SAlexander Duyck 		return ret;
2062f90b865SAlexander Duyck 
2072f90b865SAlexander Duyck 	ret = dcbnl_reply(netdev->dcbnl_ops->getstate(netdev), RTM_GETDCB,
2082f90b865SAlexander Duyck 	                  DCB_CMD_GSTATE, DCB_ATTR_STATE, pid, seq, flags);
2092f90b865SAlexander Duyck 
2102f90b865SAlexander Duyck 	return ret;
2112f90b865SAlexander Duyck }
2122f90b865SAlexander Duyck 
2132f90b865SAlexander Duyck static int dcbnl_getpfccfg(struct net_device *netdev, struct nlattr **tb,
2142f90b865SAlexander Duyck                            u32 pid, u32 seq, u16 flags)
2152f90b865SAlexander Duyck {
2162f90b865SAlexander Duyck 	struct sk_buff *dcbnl_skb;
2172f90b865SAlexander Duyck 	struct nlmsghdr *nlh;
2182f90b865SAlexander Duyck 	struct dcbmsg *dcb;
2192f90b865SAlexander Duyck 	struct nlattr *data[DCB_PFC_UP_ATTR_MAX + 1], *nest;
2202f90b865SAlexander Duyck 	u8 value;
2212f90b865SAlexander Duyck 	int ret = -EINVAL;
2222f90b865SAlexander Duyck 	int i;
2232f90b865SAlexander Duyck 	int getall = 0;
2242f90b865SAlexander Duyck 
2252f90b865SAlexander Duyck 	if (!tb[DCB_ATTR_PFC_CFG] || !netdev->dcbnl_ops->getpfccfg)
2262f90b865SAlexander Duyck 		return ret;
2272f90b865SAlexander Duyck 
2282f90b865SAlexander Duyck 	ret = nla_parse_nested(data, DCB_PFC_UP_ATTR_MAX,
2292f90b865SAlexander Duyck 	                       tb[DCB_ATTR_PFC_CFG],
2302f90b865SAlexander Duyck 	                       dcbnl_pfc_up_nest);
2312f90b865SAlexander Duyck 	if (ret)
2322f90b865SAlexander Duyck 		goto err_out;
2332f90b865SAlexander Duyck 
2342f90b865SAlexander Duyck 	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
2352f90b865SAlexander Duyck 	if (!dcbnl_skb)
2362f90b865SAlexander Duyck 		goto err_out;
2372f90b865SAlexander Duyck 
2382f90b865SAlexander Duyck 	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
2392f90b865SAlexander Duyck 
2402f90b865SAlexander Duyck 	dcb = NLMSG_DATA(nlh);
2412f90b865SAlexander Duyck 	dcb->dcb_family = AF_UNSPEC;
2422f90b865SAlexander Duyck 	dcb->cmd = DCB_CMD_PFC_GCFG;
2432f90b865SAlexander Duyck 
2442f90b865SAlexander Duyck 	nest = nla_nest_start(dcbnl_skb, DCB_ATTR_PFC_CFG);
2452f90b865SAlexander Duyck 	if (!nest)
2462f90b865SAlexander Duyck 		goto err;
2472f90b865SAlexander Duyck 
2482f90b865SAlexander Duyck 	if (data[DCB_PFC_UP_ATTR_ALL])
2492f90b865SAlexander Duyck 		getall = 1;
2502f90b865SAlexander Duyck 
2512f90b865SAlexander Duyck 	for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) {
2522f90b865SAlexander Duyck 		if (!getall && !data[i])
2532f90b865SAlexander Duyck 			continue;
2542f90b865SAlexander Duyck 
2552f90b865SAlexander Duyck 		netdev->dcbnl_ops->getpfccfg(netdev, i - DCB_PFC_UP_ATTR_0,
2562f90b865SAlexander Duyck 		                             &value);
2572f90b865SAlexander Duyck 		ret = nla_put_u8(dcbnl_skb, i, value);
2582f90b865SAlexander Duyck 
2592f90b865SAlexander Duyck 		if (ret) {
2602f90b865SAlexander Duyck 			nla_nest_cancel(dcbnl_skb, nest);
2612f90b865SAlexander Duyck 			goto err;
2622f90b865SAlexander Duyck 		}
2632f90b865SAlexander Duyck 	}
2642f90b865SAlexander Duyck 	nla_nest_end(dcbnl_skb, nest);
2652f90b865SAlexander Duyck 
2662f90b865SAlexander Duyck 	nlmsg_end(dcbnl_skb, nlh);
2672f90b865SAlexander Duyck 
2682f90b865SAlexander Duyck 	ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
2692f90b865SAlexander Duyck 	if (ret)
2702f90b865SAlexander Duyck 		goto err;
2712f90b865SAlexander Duyck 
2722f90b865SAlexander Duyck 	return 0;
2732f90b865SAlexander Duyck nlmsg_failure:
2742f90b865SAlexander Duyck err:
275858eb711SRoel Kluin 	kfree_skb(dcbnl_skb);
2762f90b865SAlexander Duyck err_out:
2772f90b865SAlexander Duyck 	return -EINVAL;
2782f90b865SAlexander Duyck }
2792f90b865SAlexander Duyck 
2802f90b865SAlexander Duyck static int dcbnl_getperm_hwaddr(struct net_device *netdev, struct nlattr **tb,
2812f90b865SAlexander Duyck                                 u32 pid, u32 seq, u16 flags)
2822f90b865SAlexander Duyck {
2832f90b865SAlexander Duyck 	struct sk_buff *dcbnl_skb;
2842f90b865SAlexander Duyck 	struct nlmsghdr *nlh;
2852f90b865SAlexander Duyck 	struct dcbmsg *dcb;
2862f90b865SAlexander Duyck 	u8 perm_addr[MAX_ADDR_LEN];
2872f90b865SAlexander Duyck 	int ret = -EINVAL;
2882f90b865SAlexander Duyck 
2892f90b865SAlexander Duyck 	if (!netdev->dcbnl_ops->getpermhwaddr)
2902f90b865SAlexander Duyck 		return ret;
2912f90b865SAlexander Duyck 
2922f90b865SAlexander Duyck 	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
2932f90b865SAlexander Duyck 	if (!dcbnl_skb)
2942f90b865SAlexander Duyck 		goto err_out;
2952f90b865SAlexander Duyck 
2962f90b865SAlexander Duyck 	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
2972f90b865SAlexander Duyck 
2982f90b865SAlexander Duyck 	dcb = NLMSG_DATA(nlh);
2992f90b865SAlexander Duyck 	dcb->dcb_family = AF_UNSPEC;
3002f90b865SAlexander Duyck 	dcb->cmd = DCB_CMD_GPERM_HWADDR;
3012f90b865SAlexander Duyck 
3022f90b865SAlexander Duyck 	netdev->dcbnl_ops->getpermhwaddr(netdev, perm_addr);
3032f90b865SAlexander Duyck 
3042f90b865SAlexander Duyck 	ret = nla_put(dcbnl_skb, DCB_ATTR_PERM_HWADDR, sizeof(perm_addr),
3052f90b865SAlexander Duyck 	              perm_addr);
3062f90b865SAlexander Duyck 
3072f90b865SAlexander Duyck 	nlmsg_end(dcbnl_skb, nlh);
3082f90b865SAlexander Duyck 
3092f90b865SAlexander Duyck 	ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
3102f90b865SAlexander Duyck 	if (ret)
3112f90b865SAlexander Duyck 		goto err;
3122f90b865SAlexander Duyck 
3132f90b865SAlexander Duyck 	return 0;
3142f90b865SAlexander Duyck 
3152f90b865SAlexander Duyck nlmsg_failure:
3162f90b865SAlexander Duyck err:
317858eb711SRoel Kluin 	kfree_skb(dcbnl_skb);
3182f90b865SAlexander Duyck err_out:
3192f90b865SAlexander Duyck 	return -EINVAL;
3202f90b865SAlexander Duyck }
3212f90b865SAlexander Duyck 
32246132188SAlexander Duyck static int dcbnl_getcap(struct net_device *netdev, struct nlattr **tb,
32346132188SAlexander Duyck                         u32 pid, u32 seq, u16 flags)
32446132188SAlexander Duyck {
32546132188SAlexander Duyck 	struct sk_buff *dcbnl_skb;
32646132188SAlexander Duyck 	struct nlmsghdr *nlh;
32746132188SAlexander Duyck 	struct dcbmsg *dcb;
32846132188SAlexander Duyck 	struct nlattr *data[DCB_CAP_ATTR_MAX + 1], *nest;
32946132188SAlexander Duyck 	u8 value;
33046132188SAlexander Duyck 	int ret = -EINVAL;
33146132188SAlexander Duyck 	int i;
33246132188SAlexander Duyck 	int getall = 0;
33346132188SAlexander Duyck 
33446132188SAlexander Duyck 	if (!tb[DCB_ATTR_CAP] || !netdev->dcbnl_ops->getcap)
33546132188SAlexander Duyck 		return ret;
33646132188SAlexander Duyck 
33746132188SAlexander Duyck 	ret = nla_parse_nested(data, DCB_CAP_ATTR_MAX, tb[DCB_ATTR_CAP],
33846132188SAlexander Duyck 	                       dcbnl_cap_nest);
33946132188SAlexander Duyck 	if (ret)
34046132188SAlexander Duyck 		goto err_out;
34146132188SAlexander Duyck 
34246132188SAlexander Duyck 	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
34346132188SAlexander Duyck 	if (!dcbnl_skb)
34446132188SAlexander Duyck 		goto err_out;
34546132188SAlexander Duyck 
34646132188SAlexander Duyck 	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
34746132188SAlexander Duyck 
34846132188SAlexander Duyck 	dcb = NLMSG_DATA(nlh);
34946132188SAlexander Duyck 	dcb->dcb_family = AF_UNSPEC;
35046132188SAlexander Duyck 	dcb->cmd = DCB_CMD_GCAP;
35146132188SAlexander Duyck 
35246132188SAlexander Duyck 	nest = nla_nest_start(dcbnl_skb, DCB_ATTR_CAP);
35346132188SAlexander Duyck 	if (!nest)
35446132188SAlexander Duyck 		goto err;
35546132188SAlexander Duyck 
35646132188SAlexander Duyck 	if (data[DCB_CAP_ATTR_ALL])
35746132188SAlexander Duyck 		getall = 1;
35846132188SAlexander Duyck 
35946132188SAlexander Duyck 	for (i = DCB_CAP_ATTR_ALL+1; i <= DCB_CAP_ATTR_MAX; i++) {
36046132188SAlexander Duyck 		if (!getall && !data[i])
36146132188SAlexander Duyck 			continue;
36246132188SAlexander Duyck 
36346132188SAlexander Duyck 		if (!netdev->dcbnl_ops->getcap(netdev, i, &value)) {
36446132188SAlexander Duyck 			ret = nla_put_u8(dcbnl_skb, i, value);
36546132188SAlexander Duyck 
36646132188SAlexander Duyck 			if (ret) {
36746132188SAlexander Duyck 				nla_nest_cancel(dcbnl_skb, nest);
36846132188SAlexander Duyck 				goto err;
36946132188SAlexander Duyck 			}
37046132188SAlexander Duyck 		}
37146132188SAlexander Duyck 	}
37246132188SAlexander Duyck 	nla_nest_end(dcbnl_skb, nest);
37346132188SAlexander Duyck 
37446132188SAlexander Duyck 	nlmsg_end(dcbnl_skb, nlh);
37546132188SAlexander Duyck 
37646132188SAlexander Duyck 	ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
37746132188SAlexander Duyck 	if (ret)
37846132188SAlexander Duyck 		goto err;
37946132188SAlexander Duyck 
38046132188SAlexander Duyck 	return 0;
38146132188SAlexander Duyck nlmsg_failure:
38246132188SAlexander Duyck err:
383858eb711SRoel Kluin 	kfree_skb(dcbnl_skb);
38446132188SAlexander Duyck err_out:
38546132188SAlexander Duyck 	return -EINVAL;
38646132188SAlexander Duyck }
38746132188SAlexander Duyck 
38833dbabc4SAlexander Duyck static int dcbnl_getnumtcs(struct net_device *netdev, struct nlattr **tb,
38933dbabc4SAlexander Duyck                            u32 pid, u32 seq, u16 flags)
39033dbabc4SAlexander Duyck {
39133dbabc4SAlexander Duyck 	struct sk_buff *dcbnl_skb;
39233dbabc4SAlexander Duyck 	struct nlmsghdr *nlh;
39333dbabc4SAlexander Duyck 	struct dcbmsg *dcb;
39433dbabc4SAlexander Duyck 	struct nlattr *data[DCB_NUMTCS_ATTR_MAX + 1], *nest;
39533dbabc4SAlexander Duyck 	u8 value;
39633dbabc4SAlexander Duyck 	int ret = -EINVAL;
39733dbabc4SAlexander Duyck 	int i;
39833dbabc4SAlexander Duyck 	int getall = 0;
39933dbabc4SAlexander Duyck 
40033dbabc4SAlexander Duyck 	if (!tb[DCB_ATTR_NUMTCS] || !netdev->dcbnl_ops->getnumtcs)
40133dbabc4SAlexander Duyck 		return ret;
40233dbabc4SAlexander Duyck 
40333dbabc4SAlexander Duyck 	ret = nla_parse_nested(data, DCB_NUMTCS_ATTR_MAX, tb[DCB_ATTR_NUMTCS],
40433dbabc4SAlexander Duyck 	                       dcbnl_numtcs_nest);
40533dbabc4SAlexander Duyck 	if (ret) {
40633dbabc4SAlexander Duyck 		ret = -EINVAL;
40733dbabc4SAlexander Duyck 		goto err_out;
40833dbabc4SAlexander Duyck 	}
40933dbabc4SAlexander Duyck 
41033dbabc4SAlexander Duyck 	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
41133dbabc4SAlexander Duyck 	if (!dcbnl_skb) {
41233dbabc4SAlexander Duyck 		ret = -EINVAL;
41333dbabc4SAlexander Duyck 		goto err_out;
41433dbabc4SAlexander Duyck 	}
41533dbabc4SAlexander Duyck 
41633dbabc4SAlexander Duyck 	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
41733dbabc4SAlexander Duyck 
41833dbabc4SAlexander Duyck 	dcb = NLMSG_DATA(nlh);
41933dbabc4SAlexander Duyck 	dcb->dcb_family = AF_UNSPEC;
42033dbabc4SAlexander Duyck 	dcb->cmd = DCB_CMD_GNUMTCS;
42133dbabc4SAlexander Duyck 
42233dbabc4SAlexander Duyck 	nest = nla_nest_start(dcbnl_skb, DCB_ATTR_NUMTCS);
42333dbabc4SAlexander Duyck 	if (!nest) {
42433dbabc4SAlexander Duyck 		ret = -EINVAL;
42533dbabc4SAlexander Duyck 		goto err;
42633dbabc4SAlexander Duyck 	}
42733dbabc4SAlexander Duyck 
42833dbabc4SAlexander Duyck 	if (data[DCB_NUMTCS_ATTR_ALL])
42933dbabc4SAlexander Duyck 		getall = 1;
43033dbabc4SAlexander Duyck 
43133dbabc4SAlexander Duyck 	for (i = DCB_NUMTCS_ATTR_ALL+1; i <= DCB_NUMTCS_ATTR_MAX; i++) {
43233dbabc4SAlexander Duyck 		if (!getall && !data[i])
43333dbabc4SAlexander Duyck 			continue;
43433dbabc4SAlexander Duyck 
43533dbabc4SAlexander Duyck 		ret = netdev->dcbnl_ops->getnumtcs(netdev, i, &value);
43633dbabc4SAlexander Duyck 		if (!ret) {
43733dbabc4SAlexander Duyck 			ret = nla_put_u8(dcbnl_skb, i, value);
43833dbabc4SAlexander Duyck 
43933dbabc4SAlexander Duyck 			if (ret) {
44033dbabc4SAlexander Duyck 				nla_nest_cancel(dcbnl_skb, nest);
44133dbabc4SAlexander Duyck 				ret = -EINVAL;
44233dbabc4SAlexander Duyck 				goto err;
44333dbabc4SAlexander Duyck 			}
44433dbabc4SAlexander Duyck 		} else {
44533dbabc4SAlexander Duyck 			goto err;
44633dbabc4SAlexander Duyck 		}
44733dbabc4SAlexander Duyck 	}
44833dbabc4SAlexander Duyck 	nla_nest_end(dcbnl_skb, nest);
44933dbabc4SAlexander Duyck 
45033dbabc4SAlexander Duyck 	nlmsg_end(dcbnl_skb, nlh);
45133dbabc4SAlexander Duyck 
45233dbabc4SAlexander Duyck 	ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
45333dbabc4SAlexander Duyck 	if (ret) {
45433dbabc4SAlexander Duyck 		ret = -EINVAL;
45533dbabc4SAlexander Duyck 		goto err;
45633dbabc4SAlexander Duyck 	}
45733dbabc4SAlexander Duyck 
45833dbabc4SAlexander Duyck 	return 0;
45933dbabc4SAlexander Duyck nlmsg_failure:
46033dbabc4SAlexander Duyck err:
461858eb711SRoel Kluin 	kfree_skb(dcbnl_skb);
46233dbabc4SAlexander Duyck err_out:
46333dbabc4SAlexander Duyck 	return ret;
46433dbabc4SAlexander Duyck }
46533dbabc4SAlexander Duyck 
46633dbabc4SAlexander Duyck static int dcbnl_setnumtcs(struct net_device *netdev, struct nlattr **tb,
46733dbabc4SAlexander Duyck                            u32 pid, u32 seq, u16 flags)
46833dbabc4SAlexander Duyck {
46933dbabc4SAlexander Duyck 	struct nlattr *data[DCB_NUMTCS_ATTR_MAX + 1];
47033dbabc4SAlexander Duyck 	int ret = -EINVAL;
47133dbabc4SAlexander Duyck 	u8 value;
47233dbabc4SAlexander Duyck 	int i;
47333dbabc4SAlexander Duyck 
4748b124a8eSDon Skidmore 	if (!tb[DCB_ATTR_NUMTCS] || !netdev->dcbnl_ops->setnumtcs)
47533dbabc4SAlexander Duyck 		return ret;
47633dbabc4SAlexander Duyck 
47733dbabc4SAlexander Duyck 	ret = nla_parse_nested(data, DCB_NUMTCS_ATTR_MAX, tb[DCB_ATTR_NUMTCS],
47833dbabc4SAlexander Duyck 	                       dcbnl_numtcs_nest);
47933dbabc4SAlexander Duyck 
48033dbabc4SAlexander Duyck 	if (ret) {
48133dbabc4SAlexander Duyck 		ret = -EINVAL;
48233dbabc4SAlexander Duyck 		goto err;
48333dbabc4SAlexander Duyck 	}
48433dbabc4SAlexander Duyck 
48533dbabc4SAlexander Duyck 	for (i = DCB_NUMTCS_ATTR_ALL+1; i <= DCB_NUMTCS_ATTR_MAX; i++) {
48633dbabc4SAlexander Duyck 		if (data[i] == NULL)
48733dbabc4SAlexander Duyck 			continue;
48833dbabc4SAlexander Duyck 
48933dbabc4SAlexander Duyck 		value = nla_get_u8(data[i]);
49033dbabc4SAlexander Duyck 
49133dbabc4SAlexander Duyck 		ret = netdev->dcbnl_ops->setnumtcs(netdev, i, value);
49233dbabc4SAlexander Duyck 
49333dbabc4SAlexander Duyck 		if (ret)
49433dbabc4SAlexander Duyck 			goto operr;
49533dbabc4SAlexander Duyck 	}
49633dbabc4SAlexander Duyck 
49733dbabc4SAlexander Duyck operr:
49833dbabc4SAlexander Duyck 	ret = dcbnl_reply(!!ret, RTM_SETDCB, DCB_CMD_SNUMTCS,
49933dbabc4SAlexander Duyck 	                  DCB_ATTR_NUMTCS, pid, seq, flags);
50033dbabc4SAlexander Duyck 
50133dbabc4SAlexander Duyck err:
50233dbabc4SAlexander Duyck 	return ret;
50333dbabc4SAlexander Duyck }
50433dbabc4SAlexander Duyck 
5050eb3aa9bSAlexander Duyck static int dcbnl_getpfcstate(struct net_device *netdev, struct nlattr **tb,
5060eb3aa9bSAlexander Duyck                              u32 pid, u32 seq, u16 flags)
5070eb3aa9bSAlexander Duyck {
5080eb3aa9bSAlexander Duyck 	int ret = -EINVAL;
5090eb3aa9bSAlexander Duyck 
5100eb3aa9bSAlexander Duyck 	if (!netdev->dcbnl_ops->getpfcstate)
5110eb3aa9bSAlexander Duyck 		return ret;
5120eb3aa9bSAlexander Duyck 
5130eb3aa9bSAlexander Duyck 	ret = dcbnl_reply(netdev->dcbnl_ops->getpfcstate(netdev), RTM_GETDCB,
5140eb3aa9bSAlexander Duyck 	                  DCB_CMD_PFC_GSTATE, DCB_ATTR_PFC_STATE,
5150eb3aa9bSAlexander Duyck 	                  pid, seq, flags);
5160eb3aa9bSAlexander Duyck 
5170eb3aa9bSAlexander Duyck 	return ret;
5180eb3aa9bSAlexander Duyck }
5190eb3aa9bSAlexander Duyck 
5200eb3aa9bSAlexander Duyck static int dcbnl_setpfcstate(struct net_device *netdev, struct nlattr **tb,
5210eb3aa9bSAlexander Duyck                              u32 pid, u32 seq, u16 flags)
5220eb3aa9bSAlexander Duyck {
5230eb3aa9bSAlexander Duyck 	int ret = -EINVAL;
5240eb3aa9bSAlexander Duyck 	u8 value;
5250eb3aa9bSAlexander Duyck 
5260eb3aa9bSAlexander Duyck 	if (!tb[DCB_ATTR_PFC_STATE] || !netdev->dcbnl_ops->setpfcstate)
5270eb3aa9bSAlexander Duyck 		return ret;
5280eb3aa9bSAlexander Duyck 
5290eb3aa9bSAlexander Duyck 	value = nla_get_u8(tb[DCB_ATTR_PFC_STATE]);
5300eb3aa9bSAlexander Duyck 
5310eb3aa9bSAlexander Duyck 	netdev->dcbnl_ops->setpfcstate(netdev, value);
5320eb3aa9bSAlexander Duyck 
5330eb3aa9bSAlexander Duyck 	ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_PFC_SSTATE, DCB_ATTR_PFC_STATE,
5340eb3aa9bSAlexander Duyck 	                  pid, seq, flags);
5350eb3aa9bSAlexander Duyck 
5360eb3aa9bSAlexander Duyck 	return ret;
5370eb3aa9bSAlexander Duyck }
5380eb3aa9bSAlexander Duyck 
5392f90b865SAlexander Duyck static int __dcbnl_pg_getcfg(struct net_device *netdev, struct nlattr **tb,
5402f90b865SAlexander Duyck                              u32 pid, u32 seq, u16 flags, int dir)
5412f90b865SAlexander Duyck {
5422f90b865SAlexander Duyck 	struct sk_buff *dcbnl_skb;
5432f90b865SAlexander Duyck 	struct nlmsghdr *nlh;
5442f90b865SAlexander Duyck 	struct dcbmsg *dcb;
5452f90b865SAlexander Duyck 	struct nlattr *pg_nest, *param_nest, *data;
5462f90b865SAlexander Duyck 	struct nlattr *pg_tb[DCB_PG_ATTR_MAX + 1];
5472f90b865SAlexander Duyck 	struct nlattr *param_tb[DCB_TC_ATTR_PARAM_MAX + 1];
5482f90b865SAlexander Duyck 	u8 prio, pgid, tc_pct, up_map;
5492f90b865SAlexander Duyck 	int ret  = -EINVAL;
5502f90b865SAlexander Duyck 	int getall = 0;
5512f90b865SAlexander Duyck 	int i;
5522f90b865SAlexander Duyck 
5532f90b865SAlexander Duyck 	if (!tb[DCB_ATTR_PG_CFG] ||
5542f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->getpgtccfgtx ||
5552f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->getpgtccfgrx ||
5562f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->getpgbwgcfgtx ||
5572f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->getpgbwgcfgrx)
5582f90b865SAlexander Duyck 		return ret;
5592f90b865SAlexander Duyck 
5602f90b865SAlexander Duyck 	ret = nla_parse_nested(pg_tb, DCB_PG_ATTR_MAX,
5612f90b865SAlexander Duyck 	                       tb[DCB_ATTR_PG_CFG], dcbnl_pg_nest);
5622f90b865SAlexander Duyck 
5632f90b865SAlexander Duyck 	if (ret)
5642f90b865SAlexander Duyck 		goto err_out;
5652f90b865SAlexander Duyck 
5662f90b865SAlexander Duyck 	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
5672f90b865SAlexander Duyck 	if (!dcbnl_skb)
5682f90b865SAlexander Duyck 		goto err_out;
5692f90b865SAlexander Duyck 
5702f90b865SAlexander Duyck 	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
5712f90b865SAlexander Duyck 
5722f90b865SAlexander Duyck 	dcb = NLMSG_DATA(nlh);
5732f90b865SAlexander Duyck 	dcb->dcb_family = AF_UNSPEC;
5742f90b865SAlexander Duyck 	dcb->cmd = (dir) ? DCB_CMD_PGRX_GCFG : DCB_CMD_PGTX_GCFG;
5752f90b865SAlexander Duyck 
5762f90b865SAlexander Duyck 	pg_nest = nla_nest_start(dcbnl_skb, DCB_ATTR_PG_CFG);
5772f90b865SAlexander Duyck 	if (!pg_nest)
5782f90b865SAlexander Duyck 		goto err;
5792f90b865SAlexander Duyck 
5802f90b865SAlexander Duyck 	if (pg_tb[DCB_PG_ATTR_TC_ALL])
5812f90b865SAlexander Duyck 		getall = 1;
5822f90b865SAlexander Duyck 
5832f90b865SAlexander Duyck 	for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) {
5842f90b865SAlexander Duyck 		if (!getall && !pg_tb[i])
5852f90b865SAlexander Duyck 			continue;
5862f90b865SAlexander Duyck 
5872f90b865SAlexander Duyck 		if (pg_tb[DCB_PG_ATTR_TC_ALL])
5882f90b865SAlexander Duyck 			data = pg_tb[DCB_PG_ATTR_TC_ALL];
5892f90b865SAlexander Duyck 		else
5902f90b865SAlexander Duyck 			data = pg_tb[i];
5912f90b865SAlexander Duyck 		ret = nla_parse_nested(param_tb, DCB_TC_ATTR_PARAM_MAX,
5922f90b865SAlexander Duyck 				       data, dcbnl_tc_param_nest);
5932f90b865SAlexander Duyck 		if (ret)
5942f90b865SAlexander Duyck 			goto err_pg;
5952f90b865SAlexander Duyck 
5962f90b865SAlexander Duyck 		param_nest = nla_nest_start(dcbnl_skb, i);
5972f90b865SAlexander Duyck 		if (!param_nest)
5982f90b865SAlexander Duyck 			goto err_pg;
5992f90b865SAlexander Duyck 
6002f90b865SAlexander Duyck 		pgid = DCB_ATTR_VALUE_UNDEFINED;
6012f90b865SAlexander Duyck 		prio = DCB_ATTR_VALUE_UNDEFINED;
6022f90b865SAlexander Duyck 		tc_pct = DCB_ATTR_VALUE_UNDEFINED;
6032f90b865SAlexander Duyck 		up_map = DCB_ATTR_VALUE_UNDEFINED;
6042f90b865SAlexander Duyck 
6052f90b865SAlexander Duyck 		if (dir) {
6062f90b865SAlexander Duyck 			/* Rx */
6072f90b865SAlexander Duyck 			netdev->dcbnl_ops->getpgtccfgrx(netdev,
6082f90b865SAlexander Duyck 						i - DCB_PG_ATTR_TC_0, &prio,
6092f90b865SAlexander Duyck 						&pgid, &tc_pct, &up_map);
6102f90b865SAlexander Duyck 		} else {
6112f90b865SAlexander Duyck 			/* Tx */
6122f90b865SAlexander Duyck 			netdev->dcbnl_ops->getpgtccfgtx(netdev,
6132f90b865SAlexander Duyck 						i - DCB_PG_ATTR_TC_0, &prio,
6142f90b865SAlexander Duyck 						&pgid, &tc_pct, &up_map);
6152f90b865SAlexander Duyck 		}
6162f90b865SAlexander Duyck 
6172f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_PGID] ||
6182f90b865SAlexander Duyck 		    param_tb[DCB_TC_ATTR_PARAM_ALL]) {
6192f90b865SAlexander Duyck 			ret = nla_put_u8(dcbnl_skb,
6202f90b865SAlexander Duyck 			                 DCB_TC_ATTR_PARAM_PGID, pgid);
6212f90b865SAlexander Duyck 			if (ret)
6222f90b865SAlexander Duyck 				goto err_param;
6232f90b865SAlexander Duyck 		}
6242f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING] ||
6252f90b865SAlexander Duyck 		    param_tb[DCB_TC_ATTR_PARAM_ALL]) {
6262f90b865SAlexander Duyck 			ret = nla_put_u8(dcbnl_skb,
6272f90b865SAlexander Duyck 			                 DCB_TC_ATTR_PARAM_UP_MAPPING, up_map);
6282f90b865SAlexander Duyck 			if (ret)
6292f90b865SAlexander Duyck 				goto err_param;
6302f90b865SAlexander Duyck 		}
6312f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO] ||
6322f90b865SAlexander Duyck 		    param_tb[DCB_TC_ATTR_PARAM_ALL]) {
6332f90b865SAlexander Duyck 			ret = nla_put_u8(dcbnl_skb,
6342f90b865SAlexander Duyck 			                 DCB_TC_ATTR_PARAM_STRICT_PRIO, prio);
6352f90b865SAlexander Duyck 			if (ret)
6362f90b865SAlexander Duyck 				goto err_param;
6372f90b865SAlexander Duyck 		}
6382f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_BW_PCT] ||
6392f90b865SAlexander Duyck 		    param_tb[DCB_TC_ATTR_PARAM_ALL]) {
6402f90b865SAlexander Duyck 			ret = nla_put_u8(dcbnl_skb, DCB_TC_ATTR_PARAM_BW_PCT,
6412f90b865SAlexander Duyck 			                 tc_pct);
6422f90b865SAlexander Duyck 			if (ret)
6432f90b865SAlexander Duyck 				goto err_param;
6442f90b865SAlexander Duyck 		}
6452f90b865SAlexander Duyck 		nla_nest_end(dcbnl_skb, param_nest);
6462f90b865SAlexander Duyck 	}
6472f90b865SAlexander Duyck 
6482f90b865SAlexander Duyck 	if (pg_tb[DCB_PG_ATTR_BW_ID_ALL])
6492f90b865SAlexander Duyck 		getall = 1;
6502f90b865SAlexander Duyck 	else
6512f90b865SAlexander Duyck 		getall = 0;
6522f90b865SAlexander Duyck 
6532f90b865SAlexander Duyck 	for (i = DCB_PG_ATTR_BW_ID_0; i <= DCB_PG_ATTR_BW_ID_7; i++) {
6542f90b865SAlexander Duyck 		if (!getall && !pg_tb[i])
6552f90b865SAlexander Duyck 			continue;
6562f90b865SAlexander Duyck 
6572f90b865SAlexander Duyck 		tc_pct = DCB_ATTR_VALUE_UNDEFINED;
6582f90b865SAlexander Duyck 
6592f90b865SAlexander Duyck 		if (dir) {
6602f90b865SAlexander Duyck 			/* Rx */
6612f90b865SAlexander Duyck 			netdev->dcbnl_ops->getpgbwgcfgrx(netdev,
6622f90b865SAlexander Duyck 					i - DCB_PG_ATTR_BW_ID_0, &tc_pct);
6632f90b865SAlexander Duyck 		} else {
6642f90b865SAlexander Duyck 			/* Tx */
6652f90b865SAlexander Duyck 			netdev->dcbnl_ops->getpgbwgcfgtx(netdev,
6662f90b865SAlexander Duyck 					i - DCB_PG_ATTR_BW_ID_0, &tc_pct);
6672f90b865SAlexander Duyck 		}
6682f90b865SAlexander Duyck 		ret = nla_put_u8(dcbnl_skb, i, tc_pct);
6692f90b865SAlexander Duyck 
6702f90b865SAlexander Duyck 		if (ret)
6712f90b865SAlexander Duyck 			goto err_pg;
6722f90b865SAlexander Duyck 	}
6732f90b865SAlexander Duyck 
6742f90b865SAlexander Duyck 	nla_nest_end(dcbnl_skb, pg_nest);
6752f90b865SAlexander Duyck 
6762f90b865SAlexander Duyck 	nlmsg_end(dcbnl_skb, nlh);
6772f90b865SAlexander Duyck 
6782f90b865SAlexander Duyck 	ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
6792f90b865SAlexander Duyck 	if (ret)
6802f90b865SAlexander Duyck 		goto err;
6812f90b865SAlexander Duyck 
6822f90b865SAlexander Duyck 	return 0;
6832f90b865SAlexander Duyck 
6842f90b865SAlexander Duyck err_param:
6852f90b865SAlexander Duyck 	nla_nest_cancel(dcbnl_skb, param_nest);
6862f90b865SAlexander Duyck err_pg:
6872f90b865SAlexander Duyck 	nla_nest_cancel(dcbnl_skb, pg_nest);
6882f90b865SAlexander Duyck nlmsg_failure:
6892f90b865SAlexander Duyck err:
690858eb711SRoel Kluin 	kfree_skb(dcbnl_skb);
6912f90b865SAlexander Duyck err_out:
6922f90b865SAlexander Duyck 	ret  = -EINVAL;
6932f90b865SAlexander Duyck 	return ret;
6942f90b865SAlexander Duyck }
6952f90b865SAlexander Duyck 
6962f90b865SAlexander Duyck static int dcbnl_pgtx_getcfg(struct net_device *netdev, struct nlattr **tb,
6972f90b865SAlexander Duyck                              u32 pid, u32 seq, u16 flags)
6982f90b865SAlexander Duyck {
6992f90b865SAlexander Duyck 	return __dcbnl_pg_getcfg(netdev, tb, pid, seq, flags, 0);
7002f90b865SAlexander Duyck }
7012f90b865SAlexander Duyck 
7022f90b865SAlexander Duyck static int dcbnl_pgrx_getcfg(struct net_device *netdev, struct nlattr **tb,
7032f90b865SAlexander Duyck                              u32 pid, u32 seq, u16 flags)
7042f90b865SAlexander Duyck {
7052f90b865SAlexander Duyck 	return __dcbnl_pg_getcfg(netdev, tb, pid, seq, flags, 1);
7062f90b865SAlexander Duyck }
7072f90b865SAlexander Duyck 
7082f90b865SAlexander Duyck static int dcbnl_setstate(struct net_device *netdev, struct nlattr **tb,
7092f90b865SAlexander Duyck                           u32 pid, u32 seq, u16 flags)
7102f90b865SAlexander Duyck {
7112f90b865SAlexander Duyck 	int ret = -EINVAL;
7122f90b865SAlexander Duyck 	u8 value;
7132f90b865SAlexander Duyck 
7142f90b865SAlexander Duyck 	if (!tb[DCB_ATTR_STATE] || !netdev->dcbnl_ops->setstate)
7152f90b865SAlexander Duyck 		return ret;
7162f90b865SAlexander Duyck 
7172f90b865SAlexander Duyck 	value = nla_get_u8(tb[DCB_ATTR_STATE]);
7182f90b865SAlexander Duyck 
7191486a61eSDon Skidmore 	ret = dcbnl_reply(netdev->dcbnl_ops->setstate(netdev, value),
7201486a61eSDon Skidmore 	                  RTM_SETDCB, DCB_CMD_SSTATE, DCB_ATTR_STATE,
7212f90b865SAlexander Duyck 	                  pid, seq, flags);
7222f90b865SAlexander Duyck 
7232f90b865SAlexander Duyck 	return ret;
7242f90b865SAlexander Duyck }
7252f90b865SAlexander Duyck 
7262f90b865SAlexander Duyck static int dcbnl_setpfccfg(struct net_device *netdev, struct nlattr **tb,
7272f90b865SAlexander Duyck                            u32 pid, u32 seq, u16 flags)
7282f90b865SAlexander Duyck {
7292f90b865SAlexander Duyck 	struct nlattr *data[DCB_PFC_UP_ATTR_MAX + 1];
7302f90b865SAlexander Duyck 	int i;
7312f90b865SAlexander Duyck 	int ret = -EINVAL;
7322f90b865SAlexander Duyck 	u8 value;
7332f90b865SAlexander Duyck 
7342f90b865SAlexander Duyck 	if (!tb[DCB_ATTR_PFC_CFG] || !netdev->dcbnl_ops->setpfccfg)
7352f90b865SAlexander Duyck 		return ret;
7362f90b865SAlexander Duyck 
7372f90b865SAlexander Duyck 	ret = nla_parse_nested(data, DCB_PFC_UP_ATTR_MAX,
7382f90b865SAlexander Duyck 	                       tb[DCB_ATTR_PFC_CFG],
7392f90b865SAlexander Duyck 	                       dcbnl_pfc_up_nest);
7402f90b865SAlexander Duyck 	if (ret)
7412f90b865SAlexander Duyck 		goto err;
7422f90b865SAlexander Duyck 
7432f90b865SAlexander Duyck 	for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) {
7442f90b865SAlexander Duyck 		if (data[i] == NULL)
7452f90b865SAlexander Duyck 			continue;
7462f90b865SAlexander Duyck 		value = nla_get_u8(data[i]);
7472f90b865SAlexander Duyck 		netdev->dcbnl_ops->setpfccfg(netdev,
7482f90b865SAlexander Duyck 			data[i]->nla_type - DCB_PFC_UP_ATTR_0, value);
7492f90b865SAlexander Duyck 	}
7502f90b865SAlexander Duyck 
7512f90b865SAlexander Duyck 	ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_PFC_SCFG, DCB_ATTR_PFC_CFG,
7522f90b865SAlexander Duyck 	                  pid, seq, flags);
7532f90b865SAlexander Duyck err:
7542f90b865SAlexander Duyck 	return ret;
7552f90b865SAlexander Duyck }
7562f90b865SAlexander Duyck 
7572f90b865SAlexander Duyck static int dcbnl_setall(struct net_device *netdev, struct nlattr **tb,
7582f90b865SAlexander Duyck                         u32 pid, u32 seq, u16 flags)
7592f90b865SAlexander Duyck {
7602f90b865SAlexander Duyck 	int ret = -EINVAL;
7612f90b865SAlexander Duyck 
7622f90b865SAlexander Duyck 	if (!tb[DCB_ATTR_SET_ALL] || !netdev->dcbnl_ops->setall)
7632f90b865SAlexander Duyck 		return ret;
7642f90b865SAlexander Duyck 
7652f90b865SAlexander Duyck 	ret = dcbnl_reply(netdev->dcbnl_ops->setall(netdev), RTM_SETDCB,
7662f90b865SAlexander Duyck 	                  DCB_CMD_SET_ALL, DCB_ATTR_SET_ALL, pid, seq, flags);
7672f90b865SAlexander Duyck 
7682f90b865SAlexander Duyck 	return ret;
7692f90b865SAlexander Duyck }
7702f90b865SAlexander Duyck 
7712f90b865SAlexander Duyck static int __dcbnl_pg_setcfg(struct net_device *netdev, struct nlattr **tb,
7722f90b865SAlexander Duyck                              u32 pid, u32 seq, u16 flags, int dir)
7732f90b865SAlexander Duyck {
7742f90b865SAlexander Duyck 	struct nlattr *pg_tb[DCB_PG_ATTR_MAX + 1];
7752f90b865SAlexander Duyck 	struct nlattr *param_tb[DCB_TC_ATTR_PARAM_MAX + 1];
7762f90b865SAlexander Duyck 	int ret = -EINVAL;
7772f90b865SAlexander Duyck 	int i;
7782f90b865SAlexander Duyck 	u8 pgid;
7792f90b865SAlexander Duyck 	u8 up_map;
7802f90b865SAlexander Duyck 	u8 prio;
7812f90b865SAlexander Duyck 	u8 tc_pct;
7822f90b865SAlexander Duyck 
7832f90b865SAlexander Duyck 	if (!tb[DCB_ATTR_PG_CFG] ||
7842f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->setpgtccfgtx ||
7852f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->setpgtccfgrx ||
7862f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->setpgbwgcfgtx ||
7872f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->setpgbwgcfgrx)
7882f90b865SAlexander Duyck 		return ret;
7892f90b865SAlexander Duyck 
7902f90b865SAlexander Duyck 	ret = nla_parse_nested(pg_tb, DCB_PG_ATTR_MAX,
7912f90b865SAlexander Duyck 	                       tb[DCB_ATTR_PG_CFG], dcbnl_pg_nest);
7922f90b865SAlexander Duyck 	if (ret)
7932f90b865SAlexander Duyck 		goto err;
7942f90b865SAlexander Duyck 
7952f90b865SAlexander Duyck 	for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) {
7962f90b865SAlexander Duyck 		if (!pg_tb[i])
7972f90b865SAlexander Duyck 			continue;
7982f90b865SAlexander Duyck 
7992f90b865SAlexander Duyck 		ret = nla_parse_nested(param_tb, DCB_TC_ATTR_PARAM_MAX,
8002f90b865SAlexander Duyck 		                       pg_tb[i], dcbnl_tc_param_nest);
8012f90b865SAlexander Duyck 		if (ret)
8022f90b865SAlexander Duyck 			goto err;
8032f90b865SAlexander Duyck 
8042f90b865SAlexander Duyck 		pgid = DCB_ATTR_VALUE_UNDEFINED;
8052f90b865SAlexander Duyck 		prio = DCB_ATTR_VALUE_UNDEFINED;
8062f90b865SAlexander Duyck 		tc_pct = DCB_ATTR_VALUE_UNDEFINED;
8072f90b865SAlexander Duyck 		up_map = DCB_ATTR_VALUE_UNDEFINED;
8082f90b865SAlexander Duyck 
8092f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO])
8102f90b865SAlexander Duyck 			prio =
8112f90b865SAlexander Duyck 			    nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO]);
8122f90b865SAlexander Duyck 
8132f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_PGID])
8142f90b865SAlexander Duyck 			pgid = nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_PGID]);
8152f90b865SAlexander Duyck 
8162f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_BW_PCT])
8172f90b865SAlexander Duyck 			tc_pct = nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_BW_PCT]);
8182f90b865SAlexander Duyck 
8192f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING])
8202f90b865SAlexander Duyck 			up_map =
8212f90b865SAlexander Duyck 			     nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING]);
8222f90b865SAlexander Duyck 
8232f90b865SAlexander Duyck 		/* dir: Tx = 0, Rx = 1 */
8242f90b865SAlexander Duyck 		if (dir) {
8252f90b865SAlexander Duyck 			/* Rx */
8262f90b865SAlexander Duyck 			netdev->dcbnl_ops->setpgtccfgrx(netdev,
8272f90b865SAlexander Duyck 				i - DCB_PG_ATTR_TC_0,
8282f90b865SAlexander Duyck 				prio, pgid, tc_pct, up_map);
8292f90b865SAlexander Duyck 		} else {
8302f90b865SAlexander Duyck 			/* Tx */
8312f90b865SAlexander Duyck 			netdev->dcbnl_ops->setpgtccfgtx(netdev,
8322f90b865SAlexander Duyck 				i - DCB_PG_ATTR_TC_0,
8332f90b865SAlexander Duyck 				prio, pgid, tc_pct, up_map);
8342f90b865SAlexander Duyck 		}
8352f90b865SAlexander Duyck 	}
8362f90b865SAlexander Duyck 
8372f90b865SAlexander Duyck 	for (i = DCB_PG_ATTR_BW_ID_0; i <= DCB_PG_ATTR_BW_ID_7; i++) {
8382f90b865SAlexander Duyck 		if (!pg_tb[i])
8392f90b865SAlexander Duyck 			continue;
8402f90b865SAlexander Duyck 
8412f90b865SAlexander Duyck 		tc_pct = nla_get_u8(pg_tb[i]);
8422f90b865SAlexander Duyck 
8432f90b865SAlexander Duyck 		/* dir: Tx = 0, Rx = 1 */
8442f90b865SAlexander Duyck 		if (dir) {
8452f90b865SAlexander Duyck 			/* Rx */
8462f90b865SAlexander Duyck 			netdev->dcbnl_ops->setpgbwgcfgrx(netdev,
8472f90b865SAlexander Duyck 					 i - DCB_PG_ATTR_BW_ID_0, tc_pct);
8482f90b865SAlexander Duyck 		} else {
8492f90b865SAlexander Duyck 			/* Tx */
8502f90b865SAlexander Duyck 			netdev->dcbnl_ops->setpgbwgcfgtx(netdev,
8512f90b865SAlexander Duyck 					 i - DCB_PG_ATTR_BW_ID_0, tc_pct);
8522f90b865SAlexander Duyck 		}
8532f90b865SAlexander Duyck 	}
8542f90b865SAlexander Duyck 
8552f90b865SAlexander Duyck 	ret = dcbnl_reply(0, RTM_SETDCB,
8562f90b865SAlexander Duyck 			  (dir ? DCB_CMD_PGRX_SCFG : DCB_CMD_PGTX_SCFG),
8572f90b865SAlexander Duyck 			  DCB_ATTR_PG_CFG, pid, seq, flags);
8582f90b865SAlexander Duyck 
8592f90b865SAlexander Duyck err:
8602f90b865SAlexander Duyck 	return ret;
8612f90b865SAlexander Duyck }
8622f90b865SAlexander Duyck 
8632f90b865SAlexander Duyck static int dcbnl_pgtx_setcfg(struct net_device *netdev, struct nlattr **tb,
8642f90b865SAlexander Duyck                              u32 pid, u32 seq, u16 flags)
8652f90b865SAlexander Duyck {
8662f90b865SAlexander Duyck 	return __dcbnl_pg_setcfg(netdev, tb, pid, seq, flags, 0);
8672f90b865SAlexander Duyck }
8682f90b865SAlexander Duyck 
8692f90b865SAlexander Duyck static int dcbnl_pgrx_setcfg(struct net_device *netdev, struct nlattr **tb,
8702f90b865SAlexander Duyck                              u32 pid, u32 seq, u16 flags)
8712f90b865SAlexander Duyck {
8722f90b865SAlexander Duyck 	return __dcbnl_pg_setcfg(netdev, tb, pid, seq, flags, 1);
8732f90b865SAlexander Duyck }
8742f90b865SAlexander Duyck 
875859ee3c4SAlexander Duyck static int dcbnl_bcn_getcfg(struct net_device *netdev, struct nlattr **tb,
876859ee3c4SAlexander Duyck                             u32 pid, u32 seq, u16 flags)
877859ee3c4SAlexander Duyck {
878859ee3c4SAlexander Duyck 	struct sk_buff *dcbnl_skb;
879859ee3c4SAlexander Duyck 	struct nlmsghdr *nlh;
880859ee3c4SAlexander Duyck 	struct dcbmsg *dcb;
881859ee3c4SAlexander Duyck 	struct nlattr *bcn_nest;
882859ee3c4SAlexander Duyck 	struct nlattr *bcn_tb[DCB_BCN_ATTR_MAX + 1];
883859ee3c4SAlexander Duyck 	u8 value_byte;
884859ee3c4SAlexander Duyck 	u32 value_integer;
885859ee3c4SAlexander Duyck 	int ret  = -EINVAL;
886859ee3c4SAlexander Duyck 	bool getall = false;
887859ee3c4SAlexander Duyck 	int i;
888859ee3c4SAlexander Duyck 
889859ee3c4SAlexander Duyck 	if (!tb[DCB_ATTR_BCN] || !netdev->dcbnl_ops->getbcnrp ||
890859ee3c4SAlexander Duyck 	    !netdev->dcbnl_ops->getbcncfg)
891859ee3c4SAlexander Duyck 		return ret;
892859ee3c4SAlexander Duyck 
893859ee3c4SAlexander Duyck 	ret = nla_parse_nested(bcn_tb, DCB_BCN_ATTR_MAX,
894859ee3c4SAlexander Duyck 	                       tb[DCB_ATTR_BCN], dcbnl_bcn_nest);
895859ee3c4SAlexander Duyck 
896859ee3c4SAlexander Duyck 	if (ret)
897859ee3c4SAlexander Duyck 		goto err_out;
898859ee3c4SAlexander Duyck 
899859ee3c4SAlexander Duyck 	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
900859ee3c4SAlexander Duyck 	if (!dcbnl_skb)
901859ee3c4SAlexander Duyck 		goto err_out;
902859ee3c4SAlexander Duyck 
903859ee3c4SAlexander Duyck 	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
904859ee3c4SAlexander Duyck 
905859ee3c4SAlexander Duyck 	dcb = NLMSG_DATA(nlh);
906859ee3c4SAlexander Duyck 	dcb->dcb_family = AF_UNSPEC;
907859ee3c4SAlexander Duyck 	dcb->cmd = DCB_CMD_BCN_GCFG;
908859ee3c4SAlexander Duyck 
909859ee3c4SAlexander Duyck 	bcn_nest = nla_nest_start(dcbnl_skb, DCB_ATTR_BCN);
910859ee3c4SAlexander Duyck 	if (!bcn_nest)
911859ee3c4SAlexander Duyck 		goto err;
912859ee3c4SAlexander Duyck 
913859ee3c4SAlexander Duyck 	if (bcn_tb[DCB_BCN_ATTR_ALL])
914859ee3c4SAlexander Duyck 		getall = true;
915859ee3c4SAlexander Duyck 
916859ee3c4SAlexander Duyck 	for (i = DCB_BCN_ATTR_RP_0; i <= DCB_BCN_ATTR_RP_7; i++) {
917859ee3c4SAlexander Duyck 		if (!getall && !bcn_tb[i])
918859ee3c4SAlexander Duyck 			continue;
919859ee3c4SAlexander Duyck 
920859ee3c4SAlexander Duyck 		netdev->dcbnl_ops->getbcnrp(netdev, i - DCB_BCN_ATTR_RP_0,
921859ee3c4SAlexander Duyck 		                            &value_byte);
922859ee3c4SAlexander Duyck 		ret = nla_put_u8(dcbnl_skb, i, value_byte);
923859ee3c4SAlexander Duyck 		if (ret)
924859ee3c4SAlexander Duyck 			goto err_bcn;
925859ee3c4SAlexander Duyck 	}
926859ee3c4SAlexander Duyck 
927f4314e81SDon Skidmore 	for (i = DCB_BCN_ATTR_BCNA_0; i <= DCB_BCN_ATTR_RI; i++) {
928859ee3c4SAlexander Duyck 		if (!getall && !bcn_tb[i])
929859ee3c4SAlexander Duyck 			continue;
930859ee3c4SAlexander Duyck 
931859ee3c4SAlexander Duyck 		netdev->dcbnl_ops->getbcncfg(netdev, i,
932859ee3c4SAlexander Duyck 		                             &value_integer);
933859ee3c4SAlexander Duyck 		ret = nla_put_u32(dcbnl_skb, i, value_integer);
934859ee3c4SAlexander Duyck 		if (ret)
935859ee3c4SAlexander Duyck 			goto err_bcn;
936859ee3c4SAlexander Duyck 	}
937859ee3c4SAlexander Duyck 
938859ee3c4SAlexander Duyck 	nla_nest_end(dcbnl_skb, bcn_nest);
939859ee3c4SAlexander Duyck 
940859ee3c4SAlexander Duyck 	nlmsg_end(dcbnl_skb, nlh);
941859ee3c4SAlexander Duyck 
942859ee3c4SAlexander Duyck 	ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
943859ee3c4SAlexander Duyck 	if (ret)
944859ee3c4SAlexander Duyck 		goto err;
945859ee3c4SAlexander Duyck 
946859ee3c4SAlexander Duyck 	return 0;
947859ee3c4SAlexander Duyck 
948859ee3c4SAlexander Duyck err_bcn:
949859ee3c4SAlexander Duyck 	nla_nest_cancel(dcbnl_skb, bcn_nest);
950859ee3c4SAlexander Duyck nlmsg_failure:
951859ee3c4SAlexander Duyck err:
952858eb711SRoel Kluin 	kfree_skb(dcbnl_skb);
953859ee3c4SAlexander Duyck err_out:
954859ee3c4SAlexander Duyck 	ret  = -EINVAL;
955859ee3c4SAlexander Duyck 	return ret;
956859ee3c4SAlexander Duyck }
957859ee3c4SAlexander Duyck 
958859ee3c4SAlexander Duyck static int dcbnl_bcn_setcfg(struct net_device *netdev, struct nlattr **tb,
959859ee3c4SAlexander Duyck                             u32 pid, u32 seq, u16 flags)
960859ee3c4SAlexander Duyck {
961859ee3c4SAlexander Duyck 	struct nlattr *data[DCB_BCN_ATTR_MAX + 1];
962859ee3c4SAlexander Duyck 	int i;
963859ee3c4SAlexander Duyck 	int ret = -EINVAL;
964859ee3c4SAlexander Duyck 	u8 value_byte;
965859ee3c4SAlexander Duyck 	u32 value_int;
966859ee3c4SAlexander Duyck 
967859ee3c4SAlexander Duyck 	if (!tb[DCB_ATTR_BCN] || !netdev->dcbnl_ops->setbcncfg
968859ee3c4SAlexander Duyck 	    || !netdev->dcbnl_ops->setbcnrp)
969859ee3c4SAlexander Duyck 		return ret;
970859ee3c4SAlexander Duyck 
971859ee3c4SAlexander Duyck 	ret = nla_parse_nested(data, DCB_BCN_ATTR_MAX,
972859ee3c4SAlexander Duyck 	                       tb[DCB_ATTR_BCN],
973859ee3c4SAlexander Duyck 	                       dcbnl_pfc_up_nest);
974859ee3c4SAlexander Duyck 	if (ret)
975859ee3c4SAlexander Duyck 		goto err;
976859ee3c4SAlexander Duyck 
977859ee3c4SAlexander Duyck 	for (i = DCB_BCN_ATTR_RP_0; i <= DCB_BCN_ATTR_RP_7; i++) {
978859ee3c4SAlexander Duyck 		if (data[i] == NULL)
979859ee3c4SAlexander Duyck 			continue;
980859ee3c4SAlexander Duyck 		value_byte = nla_get_u8(data[i]);
981859ee3c4SAlexander Duyck 		netdev->dcbnl_ops->setbcnrp(netdev,
982859ee3c4SAlexander Duyck 			data[i]->nla_type - DCB_BCN_ATTR_RP_0, value_byte);
983859ee3c4SAlexander Duyck 	}
984859ee3c4SAlexander Duyck 
985f4314e81SDon Skidmore 	for (i = DCB_BCN_ATTR_BCNA_0; i <= DCB_BCN_ATTR_RI; i++) {
986859ee3c4SAlexander Duyck 		if (data[i] == NULL)
987859ee3c4SAlexander Duyck 			continue;
988859ee3c4SAlexander Duyck 		value_int = nla_get_u32(data[i]);
989859ee3c4SAlexander Duyck 		netdev->dcbnl_ops->setbcncfg(netdev,
990859ee3c4SAlexander Duyck 	                                     i, value_int);
991859ee3c4SAlexander Duyck 	}
992859ee3c4SAlexander Duyck 
993859ee3c4SAlexander Duyck 	ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_BCN_SCFG, DCB_ATTR_BCN,
994859ee3c4SAlexander Duyck 	                  pid, seq, flags);
995859ee3c4SAlexander Duyck err:
996859ee3c4SAlexander Duyck 	return ret;
997859ee3c4SAlexander Duyck }
998859ee3c4SAlexander Duyck 
9992f90b865SAlexander Duyck static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
10002f90b865SAlexander Duyck {
10012f90b865SAlexander Duyck 	struct net *net = sock_net(skb->sk);
10022f90b865SAlexander Duyck 	struct net_device *netdev;
10032f90b865SAlexander Duyck 	struct dcbmsg  *dcb = (struct dcbmsg *)NLMSG_DATA(nlh);
10042f90b865SAlexander Duyck 	struct nlattr *tb[DCB_ATTR_MAX + 1];
10052f90b865SAlexander Duyck 	u32 pid = skb ? NETLINK_CB(skb).pid : 0;
10062f90b865SAlexander Duyck 	int ret = -EINVAL;
10072f90b865SAlexander Duyck 
10082f90b865SAlexander Duyck 	if (net != &init_net)
10092f90b865SAlexander Duyck 		return -EINVAL;
10102f90b865SAlexander Duyck 
10112f90b865SAlexander Duyck 	ret = nlmsg_parse(nlh, sizeof(*dcb), tb, DCB_ATTR_MAX,
10122f90b865SAlexander Duyck 			  dcbnl_rtnl_policy);
10132f90b865SAlexander Duyck 	if (ret < 0)
10142f90b865SAlexander Duyck 		return ret;
10152f90b865SAlexander Duyck 
10162f90b865SAlexander Duyck 	if (!tb[DCB_ATTR_IFNAME])
10172f90b865SAlexander Duyck 		return -EINVAL;
10182f90b865SAlexander Duyck 
10192f90b865SAlexander Duyck 	netdev = dev_get_by_name(&init_net, nla_data(tb[DCB_ATTR_IFNAME]));
10202f90b865SAlexander Duyck 	if (!netdev)
10212f90b865SAlexander Duyck 		return -EINVAL;
10222f90b865SAlexander Duyck 
10232f90b865SAlexander Duyck 	if (!netdev->dcbnl_ops)
10242f90b865SAlexander Duyck 		goto errout;
10252f90b865SAlexander Duyck 
10262f90b865SAlexander Duyck 	switch (dcb->cmd) {
10272f90b865SAlexander Duyck 	case DCB_CMD_GSTATE:
10282f90b865SAlexander Duyck 		ret = dcbnl_getstate(netdev, tb, pid, nlh->nlmsg_seq,
10292f90b865SAlexander Duyck 		                     nlh->nlmsg_flags);
10302f90b865SAlexander Duyck 		goto out;
10312f90b865SAlexander Duyck 	case DCB_CMD_PFC_GCFG:
10322f90b865SAlexander Duyck 		ret = dcbnl_getpfccfg(netdev, tb, pid, nlh->nlmsg_seq,
10332f90b865SAlexander Duyck 		                      nlh->nlmsg_flags);
10342f90b865SAlexander Duyck 		goto out;
10352f90b865SAlexander Duyck 	case DCB_CMD_GPERM_HWADDR:
10362f90b865SAlexander Duyck 		ret = dcbnl_getperm_hwaddr(netdev, tb, pid, nlh->nlmsg_seq,
10372f90b865SAlexander Duyck 		                           nlh->nlmsg_flags);
10382f90b865SAlexander Duyck 		goto out;
10392f90b865SAlexander Duyck 	case DCB_CMD_PGTX_GCFG:
10402f90b865SAlexander Duyck 		ret = dcbnl_pgtx_getcfg(netdev, tb, pid, nlh->nlmsg_seq,
10412f90b865SAlexander Duyck 		                        nlh->nlmsg_flags);
10422f90b865SAlexander Duyck 		goto out;
10432f90b865SAlexander Duyck 	case DCB_CMD_PGRX_GCFG:
10442f90b865SAlexander Duyck 		ret = dcbnl_pgrx_getcfg(netdev, tb, pid, nlh->nlmsg_seq,
10452f90b865SAlexander Duyck 		                        nlh->nlmsg_flags);
10462f90b865SAlexander Duyck 		goto out;
1047859ee3c4SAlexander Duyck 	case DCB_CMD_BCN_GCFG:
1048859ee3c4SAlexander Duyck 		ret = dcbnl_bcn_getcfg(netdev, tb, pid, nlh->nlmsg_seq,
1049859ee3c4SAlexander Duyck 		                       nlh->nlmsg_flags);
1050859ee3c4SAlexander Duyck 		goto out;
10512f90b865SAlexander Duyck 	case DCB_CMD_SSTATE:
10522f90b865SAlexander Duyck 		ret = dcbnl_setstate(netdev, tb, pid, nlh->nlmsg_seq,
10532f90b865SAlexander Duyck 		                     nlh->nlmsg_flags);
10542f90b865SAlexander Duyck 		goto out;
10552f90b865SAlexander Duyck 	case DCB_CMD_PFC_SCFG:
10562f90b865SAlexander Duyck 		ret = dcbnl_setpfccfg(netdev, tb, pid, nlh->nlmsg_seq,
10572f90b865SAlexander Duyck 		                      nlh->nlmsg_flags);
10582f90b865SAlexander Duyck 		goto out;
10592f90b865SAlexander Duyck 
10602f90b865SAlexander Duyck 	case DCB_CMD_SET_ALL:
10612f90b865SAlexander Duyck 		ret = dcbnl_setall(netdev, tb, pid, nlh->nlmsg_seq,
10622f90b865SAlexander Duyck 		                   nlh->nlmsg_flags);
10632f90b865SAlexander Duyck 		goto out;
10642f90b865SAlexander Duyck 	case DCB_CMD_PGTX_SCFG:
10652f90b865SAlexander Duyck 		ret = dcbnl_pgtx_setcfg(netdev, tb, pid, nlh->nlmsg_seq,
10662f90b865SAlexander Duyck 		                        nlh->nlmsg_flags);
10672f90b865SAlexander Duyck 		goto out;
10682f90b865SAlexander Duyck 	case DCB_CMD_PGRX_SCFG:
10692f90b865SAlexander Duyck 		ret = dcbnl_pgrx_setcfg(netdev, tb, pid, nlh->nlmsg_seq,
10702f90b865SAlexander Duyck 		                        nlh->nlmsg_flags);
10712f90b865SAlexander Duyck 		goto out;
107246132188SAlexander Duyck 	case DCB_CMD_GCAP:
107346132188SAlexander Duyck 		ret = dcbnl_getcap(netdev, tb, pid, nlh->nlmsg_seq,
107446132188SAlexander Duyck 		                   nlh->nlmsg_flags);
107546132188SAlexander Duyck 		goto out;
107633dbabc4SAlexander Duyck 	case DCB_CMD_GNUMTCS:
107733dbabc4SAlexander Duyck 		ret = dcbnl_getnumtcs(netdev, tb, pid, nlh->nlmsg_seq,
107833dbabc4SAlexander Duyck 		                      nlh->nlmsg_flags);
107933dbabc4SAlexander Duyck 		goto out;
108033dbabc4SAlexander Duyck 	case DCB_CMD_SNUMTCS:
108133dbabc4SAlexander Duyck 		ret = dcbnl_setnumtcs(netdev, tb, pid, nlh->nlmsg_seq,
108233dbabc4SAlexander Duyck 		                      nlh->nlmsg_flags);
108333dbabc4SAlexander Duyck 		goto out;
10840eb3aa9bSAlexander Duyck 	case DCB_CMD_PFC_GSTATE:
10850eb3aa9bSAlexander Duyck 		ret = dcbnl_getpfcstate(netdev, tb, pid, nlh->nlmsg_seq,
10860eb3aa9bSAlexander Duyck 		                        nlh->nlmsg_flags);
10870eb3aa9bSAlexander Duyck 		goto out;
10880eb3aa9bSAlexander Duyck 	case DCB_CMD_PFC_SSTATE:
10890eb3aa9bSAlexander Duyck 		ret = dcbnl_setpfcstate(netdev, tb, pid, nlh->nlmsg_seq,
10900eb3aa9bSAlexander Duyck 		                        nlh->nlmsg_flags);
10910eb3aa9bSAlexander Duyck 		goto out;
1092859ee3c4SAlexander Duyck 	case DCB_CMD_BCN_SCFG:
1093859ee3c4SAlexander Duyck 		ret = dcbnl_bcn_setcfg(netdev, tb, pid, nlh->nlmsg_seq,
1094859ee3c4SAlexander Duyck 		                       nlh->nlmsg_flags);
1095859ee3c4SAlexander Duyck 		goto out;
10962f90b865SAlexander Duyck 	default:
10972f90b865SAlexander Duyck 		goto errout;
10982f90b865SAlexander Duyck 	}
10992f90b865SAlexander Duyck errout:
11002f90b865SAlexander Duyck 	ret = -EINVAL;
11012f90b865SAlexander Duyck out:
11022f90b865SAlexander Duyck 	dev_put(netdev);
11032f90b865SAlexander Duyck 	return ret;
11042f90b865SAlexander Duyck }
11052f90b865SAlexander Duyck 
11062f90b865SAlexander Duyck static int __init dcbnl_init(void)
11072f90b865SAlexander Duyck {
11082f90b865SAlexander Duyck 	rtnl_register(PF_UNSPEC, RTM_GETDCB, dcb_doit, NULL);
11092f90b865SAlexander Duyck 	rtnl_register(PF_UNSPEC, RTM_SETDCB, dcb_doit, NULL);
11102f90b865SAlexander Duyck 
11112f90b865SAlexander Duyck 	return 0;
11122f90b865SAlexander Duyck }
11132f90b865SAlexander Duyck module_init(dcbnl_init);
11142f90b865SAlexander Duyck 
11152f90b865SAlexander Duyck static void __exit dcbnl_exit(void)
11162f90b865SAlexander Duyck {
11172f90b865SAlexander Duyck 	rtnl_unregister(PF_UNSPEC, RTM_GETDCB);
11182f90b865SAlexander Duyck 	rtnl_unregister(PF_UNSPEC, RTM_SETDCB);
11192f90b865SAlexander Duyck }
11202f90b865SAlexander Duyck module_exit(dcbnl_exit);
11212f90b865SAlexander Duyck 
11222f90b865SAlexander Duyck 
1123