xref: /openbmc/linux/net/dcb/dcbnl.c (revision 7a6b6f51)
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},
143859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_ALPHA]        = {.type = NLA_U32},
144859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_BETA]         = {.type = NLA_U32},
145859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_GD]           = {.type = NLA_U32},
146859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_GI]           = {.type = NLA_U32},
147859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_TMAX]         = {.type = NLA_U32},
148859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_TD]           = {.type = NLA_U32},
149859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RMIN]         = {.type = NLA_U32},
150859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_W]            = {.type = NLA_U32},
151859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RD]           = {.type = NLA_U32},
152859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RU]           = {.type = NLA_U32},
153859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_WRTT]         = {.type = NLA_U32},
154859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_RI]           = {.type = NLA_U32},
155859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_C]            = {.type = NLA_U32},
156859ee3c4SAlexander Duyck 	[DCB_BCN_ATTR_ALL]          = {.type = NLA_FLAG},
157859ee3c4SAlexander Duyck };
158859ee3c4SAlexander Duyck 
1592f90b865SAlexander Duyck /* standard netlink reply call */
1602f90b865SAlexander Duyck static int dcbnl_reply(u8 value, u8 event, u8 cmd, u8 attr, u32 pid,
1612f90b865SAlexander Duyck                        u32 seq, u16 flags)
1622f90b865SAlexander Duyck {
1632f90b865SAlexander Duyck 	struct sk_buff *dcbnl_skb;
1642f90b865SAlexander Duyck 	struct dcbmsg *dcb;
1652f90b865SAlexander Duyck 	struct nlmsghdr *nlh;
1662f90b865SAlexander Duyck 	int ret = -EINVAL;
1672f90b865SAlexander Duyck 
1682f90b865SAlexander Duyck 	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
1692f90b865SAlexander Duyck 	if (!dcbnl_skb)
1702f90b865SAlexander Duyck 		return ret;
1712f90b865SAlexander Duyck 
1722f90b865SAlexander Duyck 	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, event, sizeof(*dcb), flags);
1732f90b865SAlexander Duyck 
1742f90b865SAlexander Duyck 	dcb = NLMSG_DATA(nlh);
1752f90b865SAlexander Duyck 	dcb->dcb_family = AF_UNSPEC;
1762f90b865SAlexander Duyck 	dcb->cmd = cmd;
1772f90b865SAlexander Duyck 	dcb->dcb_pad = 0;
1782f90b865SAlexander Duyck 
1792f90b865SAlexander Duyck 	ret = nla_put_u8(dcbnl_skb, attr, value);
1802f90b865SAlexander Duyck 	if (ret)
1812f90b865SAlexander Duyck 		goto err;
1822f90b865SAlexander Duyck 
1832f90b865SAlexander Duyck 	/* end the message, assign the nlmsg_len. */
1842f90b865SAlexander Duyck 	nlmsg_end(dcbnl_skb, nlh);
1852f90b865SAlexander Duyck 	ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
1862f90b865SAlexander Duyck 	if (ret)
1872f90b865SAlexander Duyck 		goto err;
1882f90b865SAlexander Duyck 
1892f90b865SAlexander Duyck 	return 0;
1902f90b865SAlexander Duyck nlmsg_failure:
1912f90b865SAlexander Duyck err:
1922f90b865SAlexander Duyck 	kfree(dcbnl_skb);
1932f90b865SAlexander Duyck 	return ret;
1942f90b865SAlexander Duyck }
1952f90b865SAlexander Duyck 
1962f90b865SAlexander Duyck static int dcbnl_getstate(struct net_device *netdev, struct nlattr **tb,
1972f90b865SAlexander Duyck                           u32 pid, u32 seq, u16 flags)
1982f90b865SAlexander Duyck {
1992f90b865SAlexander Duyck 	int ret = -EINVAL;
2002f90b865SAlexander Duyck 
2012f90b865SAlexander Duyck 	/* if (!tb[DCB_ATTR_STATE] || !netdev->dcbnl_ops->getstate) */
2022f90b865SAlexander Duyck 	if (!netdev->dcbnl_ops->getstate)
2032f90b865SAlexander Duyck 		return ret;
2042f90b865SAlexander Duyck 
2052f90b865SAlexander Duyck 	ret = dcbnl_reply(netdev->dcbnl_ops->getstate(netdev), RTM_GETDCB,
2062f90b865SAlexander Duyck 	                  DCB_CMD_GSTATE, DCB_ATTR_STATE, pid, seq, flags);
2072f90b865SAlexander Duyck 
2082f90b865SAlexander Duyck 	return ret;
2092f90b865SAlexander Duyck }
2102f90b865SAlexander Duyck 
2112f90b865SAlexander Duyck static int dcbnl_getpfccfg(struct net_device *netdev, struct nlattr **tb,
2122f90b865SAlexander Duyck                            u32 pid, u32 seq, u16 flags)
2132f90b865SAlexander Duyck {
2142f90b865SAlexander Duyck 	struct sk_buff *dcbnl_skb;
2152f90b865SAlexander Duyck 	struct nlmsghdr *nlh;
2162f90b865SAlexander Duyck 	struct dcbmsg *dcb;
2172f90b865SAlexander Duyck 	struct nlattr *data[DCB_PFC_UP_ATTR_MAX + 1], *nest;
2182f90b865SAlexander Duyck 	u8 value;
2192f90b865SAlexander Duyck 	int ret = -EINVAL;
2202f90b865SAlexander Duyck 	int i;
2212f90b865SAlexander Duyck 	int getall = 0;
2222f90b865SAlexander Duyck 
2232f90b865SAlexander Duyck 	if (!tb[DCB_ATTR_PFC_CFG] || !netdev->dcbnl_ops->getpfccfg)
2242f90b865SAlexander Duyck 		return ret;
2252f90b865SAlexander Duyck 
2262f90b865SAlexander Duyck 	ret = nla_parse_nested(data, DCB_PFC_UP_ATTR_MAX,
2272f90b865SAlexander Duyck 	                       tb[DCB_ATTR_PFC_CFG],
2282f90b865SAlexander Duyck 	                       dcbnl_pfc_up_nest);
2292f90b865SAlexander Duyck 	if (ret)
2302f90b865SAlexander Duyck 		goto err_out;
2312f90b865SAlexander Duyck 
2322f90b865SAlexander Duyck 	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
2332f90b865SAlexander Duyck 	if (!dcbnl_skb)
2342f90b865SAlexander Duyck 		goto err_out;
2352f90b865SAlexander Duyck 
2362f90b865SAlexander Duyck 	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
2372f90b865SAlexander Duyck 
2382f90b865SAlexander Duyck 	dcb = NLMSG_DATA(nlh);
2392f90b865SAlexander Duyck 	dcb->dcb_family = AF_UNSPEC;
2402f90b865SAlexander Duyck 	dcb->cmd = DCB_CMD_PFC_GCFG;
2412f90b865SAlexander Duyck 
2422f90b865SAlexander Duyck 	nest = nla_nest_start(dcbnl_skb, DCB_ATTR_PFC_CFG);
2432f90b865SAlexander Duyck 	if (!nest)
2442f90b865SAlexander Duyck 		goto err;
2452f90b865SAlexander Duyck 
2462f90b865SAlexander Duyck 	if (data[DCB_PFC_UP_ATTR_ALL])
2472f90b865SAlexander Duyck 		getall = 1;
2482f90b865SAlexander Duyck 
2492f90b865SAlexander Duyck 	for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) {
2502f90b865SAlexander Duyck 		if (!getall && !data[i])
2512f90b865SAlexander Duyck 			continue;
2522f90b865SAlexander Duyck 
2532f90b865SAlexander Duyck 		netdev->dcbnl_ops->getpfccfg(netdev, i - DCB_PFC_UP_ATTR_0,
2542f90b865SAlexander Duyck 		                             &value);
2552f90b865SAlexander Duyck 		ret = nla_put_u8(dcbnl_skb, i, value);
2562f90b865SAlexander Duyck 
2572f90b865SAlexander Duyck 		if (ret) {
2582f90b865SAlexander Duyck 			nla_nest_cancel(dcbnl_skb, nest);
2592f90b865SAlexander Duyck 			goto err;
2602f90b865SAlexander Duyck 		}
2612f90b865SAlexander Duyck 	}
2622f90b865SAlexander Duyck 	nla_nest_end(dcbnl_skb, nest);
2632f90b865SAlexander Duyck 
2642f90b865SAlexander Duyck 	nlmsg_end(dcbnl_skb, nlh);
2652f90b865SAlexander Duyck 
2662f90b865SAlexander Duyck 	ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
2672f90b865SAlexander Duyck 	if (ret)
2682f90b865SAlexander Duyck 		goto err;
2692f90b865SAlexander Duyck 
2702f90b865SAlexander Duyck 	return 0;
2712f90b865SAlexander Duyck nlmsg_failure:
2722f90b865SAlexander Duyck err:
2732f90b865SAlexander Duyck 	kfree(dcbnl_skb);
2742f90b865SAlexander Duyck err_out:
2752f90b865SAlexander Duyck 	return -EINVAL;
2762f90b865SAlexander Duyck }
2772f90b865SAlexander Duyck 
2782f90b865SAlexander Duyck static int dcbnl_getperm_hwaddr(struct net_device *netdev, struct nlattr **tb,
2792f90b865SAlexander Duyck                                 u32 pid, u32 seq, u16 flags)
2802f90b865SAlexander Duyck {
2812f90b865SAlexander Duyck 	struct sk_buff *dcbnl_skb;
2822f90b865SAlexander Duyck 	struct nlmsghdr *nlh;
2832f90b865SAlexander Duyck 	struct dcbmsg *dcb;
2842f90b865SAlexander Duyck 	u8 perm_addr[MAX_ADDR_LEN];
2852f90b865SAlexander Duyck 	int ret = -EINVAL;
2862f90b865SAlexander Duyck 
2872f90b865SAlexander Duyck 	if (!netdev->dcbnl_ops->getpermhwaddr)
2882f90b865SAlexander Duyck 		return ret;
2892f90b865SAlexander Duyck 
2902f90b865SAlexander Duyck 	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
2912f90b865SAlexander Duyck 	if (!dcbnl_skb)
2922f90b865SAlexander Duyck 		goto err_out;
2932f90b865SAlexander Duyck 
2942f90b865SAlexander Duyck 	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
2952f90b865SAlexander Duyck 
2962f90b865SAlexander Duyck 	dcb = NLMSG_DATA(nlh);
2972f90b865SAlexander Duyck 	dcb->dcb_family = AF_UNSPEC;
2982f90b865SAlexander Duyck 	dcb->cmd = DCB_CMD_GPERM_HWADDR;
2992f90b865SAlexander Duyck 
3002f90b865SAlexander Duyck 	netdev->dcbnl_ops->getpermhwaddr(netdev, perm_addr);
3012f90b865SAlexander Duyck 
3022f90b865SAlexander Duyck 	ret = nla_put(dcbnl_skb, DCB_ATTR_PERM_HWADDR, sizeof(perm_addr),
3032f90b865SAlexander Duyck 	              perm_addr);
3042f90b865SAlexander Duyck 
3052f90b865SAlexander Duyck 	nlmsg_end(dcbnl_skb, nlh);
3062f90b865SAlexander Duyck 
3072f90b865SAlexander Duyck 	ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
3082f90b865SAlexander Duyck 	if (ret)
3092f90b865SAlexander Duyck 		goto err;
3102f90b865SAlexander Duyck 
3112f90b865SAlexander Duyck 	return 0;
3122f90b865SAlexander Duyck 
3132f90b865SAlexander Duyck nlmsg_failure:
3142f90b865SAlexander Duyck err:
3152f90b865SAlexander Duyck 	kfree(dcbnl_skb);
3162f90b865SAlexander Duyck err_out:
3172f90b865SAlexander Duyck 	return -EINVAL;
3182f90b865SAlexander Duyck }
3192f90b865SAlexander Duyck 
32046132188SAlexander Duyck static int dcbnl_getcap(struct net_device *netdev, struct nlattr **tb,
32146132188SAlexander Duyck                         u32 pid, u32 seq, u16 flags)
32246132188SAlexander Duyck {
32346132188SAlexander Duyck 	struct sk_buff *dcbnl_skb;
32446132188SAlexander Duyck 	struct nlmsghdr *nlh;
32546132188SAlexander Duyck 	struct dcbmsg *dcb;
32646132188SAlexander Duyck 	struct nlattr *data[DCB_CAP_ATTR_MAX + 1], *nest;
32746132188SAlexander Duyck 	u8 value;
32846132188SAlexander Duyck 	int ret = -EINVAL;
32946132188SAlexander Duyck 	int i;
33046132188SAlexander Duyck 	int getall = 0;
33146132188SAlexander Duyck 
33246132188SAlexander Duyck 	if (!tb[DCB_ATTR_CAP] || !netdev->dcbnl_ops->getcap)
33346132188SAlexander Duyck 		return ret;
33446132188SAlexander Duyck 
33546132188SAlexander Duyck 	ret = nla_parse_nested(data, DCB_CAP_ATTR_MAX, tb[DCB_ATTR_CAP],
33646132188SAlexander Duyck 	                       dcbnl_cap_nest);
33746132188SAlexander Duyck 	if (ret)
33846132188SAlexander Duyck 		goto err_out;
33946132188SAlexander Duyck 
34046132188SAlexander Duyck 	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
34146132188SAlexander Duyck 	if (!dcbnl_skb)
34246132188SAlexander Duyck 		goto err_out;
34346132188SAlexander Duyck 
34446132188SAlexander Duyck 	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
34546132188SAlexander Duyck 
34646132188SAlexander Duyck 	dcb = NLMSG_DATA(nlh);
34746132188SAlexander Duyck 	dcb->dcb_family = AF_UNSPEC;
34846132188SAlexander Duyck 	dcb->cmd = DCB_CMD_GCAP;
34946132188SAlexander Duyck 
35046132188SAlexander Duyck 	nest = nla_nest_start(dcbnl_skb, DCB_ATTR_CAP);
35146132188SAlexander Duyck 	if (!nest)
35246132188SAlexander Duyck 		goto err;
35346132188SAlexander Duyck 
35446132188SAlexander Duyck 	if (data[DCB_CAP_ATTR_ALL])
35546132188SAlexander Duyck 		getall = 1;
35646132188SAlexander Duyck 
35746132188SAlexander Duyck 	for (i = DCB_CAP_ATTR_ALL+1; i <= DCB_CAP_ATTR_MAX; i++) {
35846132188SAlexander Duyck 		if (!getall && !data[i])
35946132188SAlexander Duyck 			continue;
36046132188SAlexander Duyck 
36146132188SAlexander Duyck 		if (!netdev->dcbnl_ops->getcap(netdev, i, &value)) {
36246132188SAlexander Duyck 			ret = nla_put_u8(dcbnl_skb, i, value);
36346132188SAlexander Duyck 
36446132188SAlexander Duyck 			if (ret) {
36546132188SAlexander Duyck 				nla_nest_cancel(dcbnl_skb, nest);
36646132188SAlexander Duyck 				goto err;
36746132188SAlexander Duyck 			}
36846132188SAlexander Duyck 		}
36946132188SAlexander Duyck 	}
37046132188SAlexander Duyck 	nla_nest_end(dcbnl_skb, nest);
37146132188SAlexander Duyck 
37246132188SAlexander Duyck 	nlmsg_end(dcbnl_skb, nlh);
37346132188SAlexander Duyck 
37446132188SAlexander Duyck 	ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
37546132188SAlexander Duyck 	if (ret)
37646132188SAlexander Duyck 		goto err;
37746132188SAlexander Duyck 
37846132188SAlexander Duyck 	return 0;
37946132188SAlexander Duyck nlmsg_failure:
38046132188SAlexander Duyck err:
38146132188SAlexander Duyck 	kfree(dcbnl_skb);
38246132188SAlexander Duyck err_out:
38346132188SAlexander Duyck 	return -EINVAL;
38446132188SAlexander Duyck }
38546132188SAlexander Duyck 
38633dbabc4SAlexander Duyck static int dcbnl_getnumtcs(struct net_device *netdev, struct nlattr **tb,
38733dbabc4SAlexander Duyck                            u32 pid, u32 seq, u16 flags)
38833dbabc4SAlexander Duyck {
38933dbabc4SAlexander Duyck 	struct sk_buff *dcbnl_skb;
39033dbabc4SAlexander Duyck 	struct nlmsghdr *nlh;
39133dbabc4SAlexander Duyck 	struct dcbmsg *dcb;
39233dbabc4SAlexander Duyck 	struct nlattr *data[DCB_NUMTCS_ATTR_MAX + 1], *nest;
39333dbabc4SAlexander Duyck 	u8 value;
39433dbabc4SAlexander Duyck 	int ret = -EINVAL;
39533dbabc4SAlexander Duyck 	int i;
39633dbabc4SAlexander Duyck 	int getall = 0;
39733dbabc4SAlexander Duyck 
39833dbabc4SAlexander Duyck 	if (!tb[DCB_ATTR_NUMTCS] || !netdev->dcbnl_ops->getnumtcs)
39933dbabc4SAlexander Duyck 		return ret;
40033dbabc4SAlexander Duyck 
40133dbabc4SAlexander Duyck 	ret = nla_parse_nested(data, DCB_NUMTCS_ATTR_MAX, tb[DCB_ATTR_NUMTCS],
40233dbabc4SAlexander Duyck 	                       dcbnl_numtcs_nest);
40333dbabc4SAlexander Duyck 	if (ret) {
40433dbabc4SAlexander Duyck 		ret = -EINVAL;
40533dbabc4SAlexander Duyck 		goto err_out;
40633dbabc4SAlexander Duyck 	}
40733dbabc4SAlexander Duyck 
40833dbabc4SAlexander Duyck 	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
40933dbabc4SAlexander Duyck 	if (!dcbnl_skb) {
41033dbabc4SAlexander Duyck 		ret = -EINVAL;
41133dbabc4SAlexander Duyck 		goto err_out;
41233dbabc4SAlexander Duyck 	}
41333dbabc4SAlexander Duyck 
41433dbabc4SAlexander Duyck 	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
41533dbabc4SAlexander Duyck 
41633dbabc4SAlexander Duyck 	dcb = NLMSG_DATA(nlh);
41733dbabc4SAlexander Duyck 	dcb->dcb_family = AF_UNSPEC;
41833dbabc4SAlexander Duyck 	dcb->cmd = DCB_CMD_GNUMTCS;
41933dbabc4SAlexander Duyck 
42033dbabc4SAlexander Duyck 	nest = nla_nest_start(dcbnl_skb, DCB_ATTR_NUMTCS);
42133dbabc4SAlexander Duyck 	if (!nest) {
42233dbabc4SAlexander Duyck 		ret = -EINVAL;
42333dbabc4SAlexander Duyck 		goto err;
42433dbabc4SAlexander Duyck 	}
42533dbabc4SAlexander Duyck 
42633dbabc4SAlexander Duyck 	if (data[DCB_NUMTCS_ATTR_ALL])
42733dbabc4SAlexander Duyck 		getall = 1;
42833dbabc4SAlexander Duyck 
42933dbabc4SAlexander Duyck 	for (i = DCB_NUMTCS_ATTR_ALL+1; i <= DCB_NUMTCS_ATTR_MAX; i++) {
43033dbabc4SAlexander Duyck 		if (!getall && !data[i])
43133dbabc4SAlexander Duyck 			continue;
43233dbabc4SAlexander Duyck 
43333dbabc4SAlexander Duyck 		ret = netdev->dcbnl_ops->getnumtcs(netdev, i, &value);
43433dbabc4SAlexander Duyck 		if (!ret) {
43533dbabc4SAlexander Duyck 			ret = nla_put_u8(dcbnl_skb, i, value);
43633dbabc4SAlexander Duyck 
43733dbabc4SAlexander Duyck 			if (ret) {
43833dbabc4SAlexander Duyck 				nla_nest_cancel(dcbnl_skb, nest);
43933dbabc4SAlexander Duyck 				ret = -EINVAL;
44033dbabc4SAlexander Duyck 				goto err;
44133dbabc4SAlexander Duyck 			}
44233dbabc4SAlexander Duyck 		} else {
44333dbabc4SAlexander Duyck 			goto err;
44433dbabc4SAlexander Duyck 		}
44533dbabc4SAlexander Duyck 	}
44633dbabc4SAlexander Duyck 	nla_nest_end(dcbnl_skb, nest);
44733dbabc4SAlexander Duyck 
44833dbabc4SAlexander Duyck 	nlmsg_end(dcbnl_skb, nlh);
44933dbabc4SAlexander Duyck 
45033dbabc4SAlexander Duyck 	ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
45133dbabc4SAlexander Duyck 	if (ret) {
45233dbabc4SAlexander Duyck 		ret = -EINVAL;
45333dbabc4SAlexander Duyck 		goto err;
45433dbabc4SAlexander Duyck 	}
45533dbabc4SAlexander Duyck 
45633dbabc4SAlexander Duyck 	return 0;
45733dbabc4SAlexander Duyck nlmsg_failure:
45833dbabc4SAlexander Duyck err:
45933dbabc4SAlexander Duyck 	kfree(dcbnl_skb);
46033dbabc4SAlexander Duyck err_out:
46133dbabc4SAlexander Duyck 	return ret;
46233dbabc4SAlexander Duyck }
46333dbabc4SAlexander Duyck 
46433dbabc4SAlexander Duyck static int dcbnl_setnumtcs(struct net_device *netdev, struct nlattr **tb,
46533dbabc4SAlexander Duyck                            u32 pid, u32 seq, u16 flags)
46633dbabc4SAlexander Duyck {
46733dbabc4SAlexander Duyck 	struct nlattr *data[DCB_NUMTCS_ATTR_MAX + 1];
46833dbabc4SAlexander Duyck 	int ret = -EINVAL;
46933dbabc4SAlexander Duyck 	u8 value;
47033dbabc4SAlexander Duyck 	int i;
47133dbabc4SAlexander Duyck 
47233dbabc4SAlexander Duyck 	if (!tb[DCB_ATTR_NUMTCS] || !netdev->dcbnl_ops->setstate)
47333dbabc4SAlexander Duyck 		return ret;
47433dbabc4SAlexander Duyck 
47533dbabc4SAlexander Duyck 	ret = nla_parse_nested(data, DCB_NUMTCS_ATTR_MAX, tb[DCB_ATTR_NUMTCS],
47633dbabc4SAlexander Duyck 	                       dcbnl_numtcs_nest);
47733dbabc4SAlexander Duyck 
47833dbabc4SAlexander Duyck 	if (ret) {
47933dbabc4SAlexander Duyck 		ret = -EINVAL;
48033dbabc4SAlexander Duyck 		goto err;
48133dbabc4SAlexander Duyck 	}
48233dbabc4SAlexander Duyck 
48333dbabc4SAlexander Duyck 	for (i = DCB_NUMTCS_ATTR_ALL+1; i <= DCB_NUMTCS_ATTR_MAX; i++) {
48433dbabc4SAlexander Duyck 		if (data[i] == NULL)
48533dbabc4SAlexander Duyck 			continue;
48633dbabc4SAlexander Duyck 
48733dbabc4SAlexander Duyck 		value = nla_get_u8(data[i]);
48833dbabc4SAlexander Duyck 
48933dbabc4SAlexander Duyck 		ret = netdev->dcbnl_ops->setnumtcs(netdev, i, value);
49033dbabc4SAlexander Duyck 
49133dbabc4SAlexander Duyck 		if (ret)
49233dbabc4SAlexander Duyck 			goto operr;
49333dbabc4SAlexander Duyck 	}
49433dbabc4SAlexander Duyck 
49533dbabc4SAlexander Duyck operr:
49633dbabc4SAlexander Duyck 	ret = dcbnl_reply(!!ret, RTM_SETDCB, DCB_CMD_SNUMTCS,
49733dbabc4SAlexander Duyck 	                  DCB_ATTR_NUMTCS, pid, seq, flags);
49833dbabc4SAlexander Duyck 
49933dbabc4SAlexander Duyck err:
50033dbabc4SAlexander Duyck 	return ret;
50133dbabc4SAlexander Duyck }
50233dbabc4SAlexander Duyck 
5030eb3aa9bSAlexander Duyck static int dcbnl_getpfcstate(struct net_device *netdev, struct nlattr **tb,
5040eb3aa9bSAlexander Duyck                              u32 pid, u32 seq, u16 flags)
5050eb3aa9bSAlexander Duyck {
5060eb3aa9bSAlexander Duyck 	int ret = -EINVAL;
5070eb3aa9bSAlexander Duyck 
5080eb3aa9bSAlexander Duyck 	if (!netdev->dcbnl_ops->getpfcstate)
5090eb3aa9bSAlexander Duyck 		return ret;
5100eb3aa9bSAlexander Duyck 
5110eb3aa9bSAlexander Duyck 	ret = dcbnl_reply(netdev->dcbnl_ops->getpfcstate(netdev), RTM_GETDCB,
5120eb3aa9bSAlexander Duyck 	                  DCB_CMD_PFC_GSTATE, DCB_ATTR_PFC_STATE,
5130eb3aa9bSAlexander Duyck 	                  pid, seq, flags);
5140eb3aa9bSAlexander Duyck 
5150eb3aa9bSAlexander Duyck 	return ret;
5160eb3aa9bSAlexander Duyck }
5170eb3aa9bSAlexander Duyck 
5180eb3aa9bSAlexander Duyck static int dcbnl_setpfcstate(struct net_device *netdev, struct nlattr **tb,
5190eb3aa9bSAlexander Duyck                              u32 pid, u32 seq, u16 flags)
5200eb3aa9bSAlexander Duyck {
5210eb3aa9bSAlexander Duyck 	int ret = -EINVAL;
5220eb3aa9bSAlexander Duyck 	u8 value;
5230eb3aa9bSAlexander Duyck 
5240eb3aa9bSAlexander Duyck 	if (!tb[DCB_ATTR_PFC_STATE] || !netdev->dcbnl_ops->setpfcstate)
5250eb3aa9bSAlexander Duyck 		return ret;
5260eb3aa9bSAlexander Duyck 
5270eb3aa9bSAlexander Duyck 	value = nla_get_u8(tb[DCB_ATTR_PFC_STATE]);
5280eb3aa9bSAlexander Duyck 
5290eb3aa9bSAlexander Duyck 	netdev->dcbnl_ops->setpfcstate(netdev, value);
5300eb3aa9bSAlexander Duyck 
5310eb3aa9bSAlexander Duyck 	ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_PFC_SSTATE, DCB_ATTR_PFC_STATE,
5320eb3aa9bSAlexander Duyck 	                  pid, seq, flags);
5330eb3aa9bSAlexander Duyck 
5340eb3aa9bSAlexander Duyck 	return ret;
5350eb3aa9bSAlexander Duyck }
5360eb3aa9bSAlexander Duyck 
5372f90b865SAlexander Duyck static int __dcbnl_pg_getcfg(struct net_device *netdev, struct nlattr **tb,
5382f90b865SAlexander Duyck                              u32 pid, u32 seq, u16 flags, int dir)
5392f90b865SAlexander Duyck {
5402f90b865SAlexander Duyck 	struct sk_buff *dcbnl_skb;
5412f90b865SAlexander Duyck 	struct nlmsghdr *nlh;
5422f90b865SAlexander Duyck 	struct dcbmsg *dcb;
5432f90b865SAlexander Duyck 	struct nlattr *pg_nest, *param_nest, *data;
5442f90b865SAlexander Duyck 	struct nlattr *pg_tb[DCB_PG_ATTR_MAX + 1];
5452f90b865SAlexander Duyck 	struct nlattr *param_tb[DCB_TC_ATTR_PARAM_MAX + 1];
5462f90b865SAlexander Duyck 	u8 prio, pgid, tc_pct, up_map;
5472f90b865SAlexander Duyck 	int ret  = -EINVAL;
5482f90b865SAlexander Duyck 	int getall = 0;
5492f90b865SAlexander Duyck 	int i;
5502f90b865SAlexander Duyck 
5512f90b865SAlexander Duyck 	if (!tb[DCB_ATTR_PG_CFG] ||
5522f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->getpgtccfgtx ||
5532f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->getpgtccfgrx ||
5542f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->getpgbwgcfgtx ||
5552f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->getpgbwgcfgrx)
5562f90b865SAlexander Duyck 		return ret;
5572f90b865SAlexander Duyck 
5582f90b865SAlexander Duyck 	ret = nla_parse_nested(pg_tb, DCB_PG_ATTR_MAX,
5592f90b865SAlexander Duyck 	                       tb[DCB_ATTR_PG_CFG], dcbnl_pg_nest);
5602f90b865SAlexander Duyck 
5612f90b865SAlexander Duyck 	if (ret)
5622f90b865SAlexander Duyck 		goto err_out;
5632f90b865SAlexander Duyck 
5642f90b865SAlexander Duyck 	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
5652f90b865SAlexander Duyck 	if (!dcbnl_skb)
5662f90b865SAlexander Duyck 		goto err_out;
5672f90b865SAlexander Duyck 
5682f90b865SAlexander Duyck 	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
5692f90b865SAlexander Duyck 
5702f90b865SAlexander Duyck 	dcb = NLMSG_DATA(nlh);
5712f90b865SAlexander Duyck 	dcb->dcb_family = AF_UNSPEC;
5722f90b865SAlexander Duyck 	dcb->cmd = (dir) ? DCB_CMD_PGRX_GCFG : DCB_CMD_PGTX_GCFG;
5732f90b865SAlexander Duyck 
5742f90b865SAlexander Duyck 	pg_nest = nla_nest_start(dcbnl_skb, DCB_ATTR_PG_CFG);
5752f90b865SAlexander Duyck 	if (!pg_nest)
5762f90b865SAlexander Duyck 		goto err;
5772f90b865SAlexander Duyck 
5782f90b865SAlexander Duyck 	if (pg_tb[DCB_PG_ATTR_TC_ALL])
5792f90b865SAlexander Duyck 		getall = 1;
5802f90b865SAlexander Duyck 
5812f90b865SAlexander Duyck 	for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) {
5822f90b865SAlexander Duyck 		if (!getall && !pg_tb[i])
5832f90b865SAlexander Duyck 			continue;
5842f90b865SAlexander Duyck 
5852f90b865SAlexander Duyck 		if (pg_tb[DCB_PG_ATTR_TC_ALL])
5862f90b865SAlexander Duyck 			data = pg_tb[DCB_PG_ATTR_TC_ALL];
5872f90b865SAlexander Duyck 		else
5882f90b865SAlexander Duyck 			data = pg_tb[i];
5892f90b865SAlexander Duyck 		ret = nla_parse_nested(param_tb, DCB_TC_ATTR_PARAM_MAX,
5902f90b865SAlexander Duyck 				       data, dcbnl_tc_param_nest);
5912f90b865SAlexander Duyck 		if (ret)
5922f90b865SAlexander Duyck 			goto err_pg;
5932f90b865SAlexander Duyck 
5942f90b865SAlexander Duyck 		param_nest = nla_nest_start(dcbnl_skb, i);
5952f90b865SAlexander Duyck 		if (!param_nest)
5962f90b865SAlexander Duyck 			goto err_pg;
5972f90b865SAlexander Duyck 
5982f90b865SAlexander Duyck 		pgid = DCB_ATTR_VALUE_UNDEFINED;
5992f90b865SAlexander Duyck 		prio = DCB_ATTR_VALUE_UNDEFINED;
6002f90b865SAlexander Duyck 		tc_pct = DCB_ATTR_VALUE_UNDEFINED;
6012f90b865SAlexander Duyck 		up_map = DCB_ATTR_VALUE_UNDEFINED;
6022f90b865SAlexander Duyck 
6032f90b865SAlexander Duyck 		if (dir) {
6042f90b865SAlexander Duyck 			/* Rx */
6052f90b865SAlexander Duyck 			netdev->dcbnl_ops->getpgtccfgrx(netdev,
6062f90b865SAlexander Duyck 						i - DCB_PG_ATTR_TC_0, &prio,
6072f90b865SAlexander Duyck 						&pgid, &tc_pct, &up_map);
6082f90b865SAlexander Duyck 		} else {
6092f90b865SAlexander Duyck 			/* Tx */
6102f90b865SAlexander Duyck 			netdev->dcbnl_ops->getpgtccfgtx(netdev,
6112f90b865SAlexander Duyck 						i - DCB_PG_ATTR_TC_0, &prio,
6122f90b865SAlexander Duyck 						&pgid, &tc_pct, &up_map);
6132f90b865SAlexander Duyck 		}
6142f90b865SAlexander Duyck 
6152f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_PGID] ||
6162f90b865SAlexander Duyck 		    param_tb[DCB_TC_ATTR_PARAM_ALL]) {
6172f90b865SAlexander Duyck 			ret = nla_put_u8(dcbnl_skb,
6182f90b865SAlexander Duyck 			                 DCB_TC_ATTR_PARAM_PGID, pgid);
6192f90b865SAlexander Duyck 			if (ret)
6202f90b865SAlexander Duyck 				goto err_param;
6212f90b865SAlexander Duyck 		}
6222f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING] ||
6232f90b865SAlexander Duyck 		    param_tb[DCB_TC_ATTR_PARAM_ALL]) {
6242f90b865SAlexander Duyck 			ret = nla_put_u8(dcbnl_skb,
6252f90b865SAlexander Duyck 			                 DCB_TC_ATTR_PARAM_UP_MAPPING, up_map);
6262f90b865SAlexander Duyck 			if (ret)
6272f90b865SAlexander Duyck 				goto err_param;
6282f90b865SAlexander Duyck 		}
6292f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO] ||
6302f90b865SAlexander Duyck 		    param_tb[DCB_TC_ATTR_PARAM_ALL]) {
6312f90b865SAlexander Duyck 			ret = nla_put_u8(dcbnl_skb,
6322f90b865SAlexander Duyck 			                 DCB_TC_ATTR_PARAM_STRICT_PRIO, prio);
6332f90b865SAlexander Duyck 			if (ret)
6342f90b865SAlexander Duyck 				goto err_param;
6352f90b865SAlexander Duyck 		}
6362f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_BW_PCT] ||
6372f90b865SAlexander Duyck 		    param_tb[DCB_TC_ATTR_PARAM_ALL]) {
6382f90b865SAlexander Duyck 			ret = nla_put_u8(dcbnl_skb, DCB_TC_ATTR_PARAM_BW_PCT,
6392f90b865SAlexander Duyck 			                 tc_pct);
6402f90b865SAlexander Duyck 			if (ret)
6412f90b865SAlexander Duyck 				goto err_param;
6422f90b865SAlexander Duyck 		}
6432f90b865SAlexander Duyck 		nla_nest_end(dcbnl_skb, param_nest);
6442f90b865SAlexander Duyck 	}
6452f90b865SAlexander Duyck 
6462f90b865SAlexander Duyck 	if (pg_tb[DCB_PG_ATTR_BW_ID_ALL])
6472f90b865SAlexander Duyck 		getall = 1;
6482f90b865SAlexander Duyck 	else
6492f90b865SAlexander Duyck 		getall = 0;
6502f90b865SAlexander Duyck 
6512f90b865SAlexander Duyck 	for (i = DCB_PG_ATTR_BW_ID_0; i <= DCB_PG_ATTR_BW_ID_7; i++) {
6522f90b865SAlexander Duyck 		if (!getall && !pg_tb[i])
6532f90b865SAlexander Duyck 			continue;
6542f90b865SAlexander Duyck 
6552f90b865SAlexander Duyck 		tc_pct = DCB_ATTR_VALUE_UNDEFINED;
6562f90b865SAlexander Duyck 
6572f90b865SAlexander Duyck 		if (dir) {
6582f90b865SAlexander Duyck 			/* Rx */
6592f90b865SAlexander Duyck 			netdev->dcbnl_ops->getpgbwgcfgrx(netdev,
6602f90b865SAlexander Duyck 					i - DCB_PG_ATTR_BW_ID_0, &tc_pct);
6612f90b865SAlexander Duyck 		} else {
6622f90b865SAlexander Duyck 			/* Tx */
6632f90b865SAlexander Duyck 			netdev->dcbnl_ops->getpgbwgcfgtx(netdev,
6642f90b865SAlexander Duyck 					i - DCB_PG_ATTR_BW_ID_0, &tc_pct);
6652f90b865SAlexander Duyck 		}
6662f90b865SAlexander Duyck 		ret = nla_put_u8(dcbnl_skb, i, tc_pct);
6672f90b865SAlexander Duyck 
6682f90b865SAlexander Duyck 		if (ret)
6692f90b865SAlexander Duyck 			goto err_pg;
6702f90b865SAlexander Duyck 	}
6712f90b865SAlexander Duyck 
6722f90b865SAlexander Duyck 	nla_nest_end(dcbnl_skb, pg_nest);
6732f90b865SAlexander Duyck 
6742f90b865SAlexander Duyck 	nlmsg_end(dcbnl_skb, nlh);
6752f90b865SAlexander Duyck 
6762f90b865SAlexander Duyck 	ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
6772f90b865SAlexander Duyck 	if (ret)
6782f90b865SAlexander Duyck 		goto err;
6792f90b865SAlexander Duyck 
6802f90b865SAlexander Duyck 	return 0;
6812f90b865SAlexander Duyck 
6822f90b865SAlexander Duyck err_param:
6832f90b865SAlexander Duyck 	nla_nest_cancel(dcbnl_skb, param_nest);
6842f90b865SAlexander Duyck err_pg:
6852f90b865SAlexander Duyck 	nla_nest_cancel(dcbnl_skb, pg_nest);
6862f90b865SAlexander Duyck nlmsg_failure:
6872f90b865SAlexander Duyck err:
6882f90b865SAlexander Duyck 	kfree(dcbnl_skb);
6892f90b865SAlexander Duyck err_out:
6902f90b865SAlexander Duyck 	ret  = -EINVAL;
6912f90b865SAlexander Duyck 	return ret;
6922f90b865SAlexander Duyck }
6932f90b865SAlexander Duyck 
6942f90b865SAlexander Duyck static int dcbnl_pgtx_getcfg(struct net_device *netdev, struct nlattr **tb,
6952f90b865SAlexander Duyck                              u32 pid, u32 seq, u16 flags)
6962f90b865SAlexander Duyck {
6972f90b865SAlexander Duyck 	return __dcbnl_pg_getcfg(netdev, tb, pid, seq, flags, 0);
6982f90b865SAlexander Duyck }
6992f90b865SAlexander Duyck 
7002f90b865SAlexander Duyck static int dcbnl_pgrx_getcfg(struct net_device *netdev, struct nlattr **tb,
7012f90b865SAlexander Duyck                              u32 pid, u32 seq, u16 flags)
7022f90b865SAlexander Duyck {
7032f90b865SAlexander Duyck 	return __dcbnl_pg_getcfg(netdev, tb, pid, seq, flags, 1);
7042f90b865SAlexander Duyck }
7052f90b865SAlexander Duyck 
7062f90b865SAlexander Duyck static int dcbnl_setstate(struct net_device *netdev, struct nlattr **tb,
7072f90b865SAlexander Duyck                           u32 pid, u32 seq, u16 flags)
7082f90b865SAlexander Duyck {
7092f90b865SAlexander Duyck 	int ret = -EINVAL;
7102f90b865SAlexander Duyck 	u8 value;
7112f90b865SAlexander Duyck 
7122f90b865SAlexander Duyck 	if (!tb[DCB_ATTR_STATE] || !netdev->dcbnl_ops->setstate)
7132f90b865SAlexander Duyck 		return ret;
7142f90b865SAlexander Duyck 
7152f90b865SAlexander Duyck 	value = nla_get_u8(tb[DCB_ATTR_STATE]);
7162f90b865SAlexander Duyck 
7172f90b865SAlexander Duyck 	netdev->dcbnl_ops->setstate(netdev, value);
7182f90b865SAlexander Duyck 
7192f90b865SAlexander Duyck 	ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_SSTATE, DCB_ATTR_STATE,
7202f90b865SAlexander Duyck 	                  pid, seq, flags);
7212f90b865SAlexander Duyck 
7222f90b865SAlexander Duyck 	return ret;
7232f90b865SAlexander Duyck }
7242f90b865SAlexander Duyck 
7252f90b865SAlexander Duyck static int dcbnl_setpfccfg(struct net_device *netdev, struct nlattr **tb,
7262f90b865SAlexander Duyck                            u32 pid, u32 seq, u16 flags)
7272f90b865SAlexander Duyck {
7282f90b865SAlexander Duyck 	struct nlattr *data[DCB_PFC_UP_ATTR_MAX + 1];
7292f90b865SAlexander Duyck 	int i;
7302f90b865SAlexander Duyck 	int ret = -EINVAL;
7312f90b865SAlexander Duyck 	u8 value;
7322f90b865SAlexander Duyck 
7332f90b865SAlexander Duyck 	if (!tb[DCB_ATTR_PFC_CFG] || !netdev->dcbnl_ops->setpfccfg)
7342f90b865SAlexander Duyck 		return ret;
7352f90b865SAlexander Duyck 
7362f90b865SAlexander Duyck 	ret = nla_parse_nested(data, DCB_PFC_UP_ATTR_MAX,
7372f90b865SAlexander Duyck 	                       tb[DCB_ATTR_PFC_CFG],
7382f90b865SAlexander Duyck 	                       dcbnl_pfc_up_nest);
7392f90b865SAlexander Duyck 	if (ret)
7402f90b865SAlexander Duyck 		goto err;
7412f90b865SAlexander Duyck 
7422f90b865SAlexander Duyck 	for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) {
7432f90b865SAlexander Duyck 		if (data[i] == NULL)
7442f90b865SAlexander Duyck 			continue;
7452f90b865SAlexander Duyck 		value = nla_get_u8(data[i]);
7462f90b865SAlexander Duyck 		netdev->dcbnl_ops->setpfccfg(netdev,
7472f90b865SAlexander Duyck 			data[i]->nla_type - DCB_PFC_UP_ATTR_0, value);
7482f90b865SAlexander Duyck 	}
7492f90b865SAlexander Duyck 
7502f90b865SAlexander Duyck 	ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_PFC_SCFG, DCB_ATTR_PFC_CFG,
7512f90b865SAlexander Duyck 	                  pid, seq, flags);
7522f90b865SAlexander Duyck err:
7532f90b865SAlexander Duyck 	return ret;
7542f90b865SAlexander Duyck }
7552f90b865SAlexander Duyck 
7562f90b865SAlexander Duyck static int dcbnl_setall(struct net_device *netdev, struct nlattr **tb,
7572f90b865SAlexander Duyck                         u32 pid, u32 seq, u16 flags)
7582f90b865SAlexander Duyck {
7592f90b865SAlexander Duyck 	int ret = -EINVAL;
7602f90b865SAlexander Duyck 
7612f90b865SAlexander Duyck 	if (!tb[DCB_ATTR_SET_ALL] || !netdev->dcbnl_ops->setall)
7622f90b865SAlexander Duyck 		return ret;
7632f90b865SAlexander Duyck 
7642f90b865SAlexander Duyck 	ret = dcbnl_reply(netdev->dcbnl_ops->setall(netdev), RTM_SETDCB,
7652f90b865SAlexander Duyck 	                  DCB_CMD_SET_ALL, DCB_ATTR_SET_ALL, pid, seq, flags);
7662f90b865SAlexander Duyck 
7672f90b865SAlexander Duyck 	return ret;
7682f90b865SAlexander Duyck }
7692f90b865SAlexander Duyck 
7702f90b865SAlexander Duyck static int __dcbnl_pg_setcfg(struct net_device *netdev, struct nlattr **tb,
7712f90b865SAlexander Duyck                              u32 pid, u32 seq, u16 flags, int dir)
7722f90b865SAlexander Duyck {
7732f90b865SAlexander Duyck 	struct nlattr *pg_tb[DCB_PG_ATTR_MAX + 1];
7742f90b865SAlexander Duyck 	struct nlattr *param_tb[DCB_TC_ATTR_PARAM_MAX + 1];
7752f90b865SAlexander Duyck 	int ret = -EINVAL;
7762f90b865SAlexander Duyck 	int i;
7772f90b865SAlexander Duyck 	u8 pgid;
7782f90b865SAlexander Duyck 	u8 up_map;
7792f90b865SAlexander Duyck 	u8 prio;
7802f90b865SAlexander Duyck 	u8 tc_pct;
7812f90b865SAlexander Duyck 
7822f90b865SAlexander Duyck 	if (!tb[DCB_ATTR_PG_CFG] ||
7832f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->setpgtccfgtx ||
7842f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->setpgtccfgrx ||
7852f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->setpgbwgcfgtx ||
7862f90b865SAlexander Duyck 	    !netdev->dcbnl_ops->setpgbwgcfgrx)
7872f90b865SAlexander Duyck 		return ret;
7882f90b865SAlexander Duyck 
7892f90b865SAlexander Duyck 	ret = nla_parse_nested(pg_tb, DCB_PG_ATTR_MAX,
7902f90b865SAlexander Duyck 	                       tb[DCB_ATTR_PG_CFG], dcbnl_pg_nest);
7912f90b865SAlexander Duyck 	if (ret)
7922f90b865SAlexander Duyck 		goto err;
7932f90b865SAlexander Duyck 
7942f90b865SAlexander Duyck 	for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) {
7952f90b865SAlexander Duyck 		if (!pg_tb[i])
7962f90b865SAlexander Duyck 			continue;
7972f90b865SAlexander Duyck 
7982f90b865SAlexander Duyck 		ret = nla_parse_nested(param_tb, DCB_TC_ATTR_PARAM_MAX,
7992f90b865SAlexander Duyck 		                       pg_tb[i], dcbnl_tc_param_nest);
8002f90b865SAlexander Duyck 		if (ret)
8012f90b865SAlexander Duyck 			goto err;
8022f90b865SAlexander Duyck 
8032f90b865SAlexander Duyck 		pgid = DCB_ATTR_VALUE_UNDEFINED;
8042f90b865SAlexander Duyck 		prio = DCB_ATTR_VALUE_UNDEFINED;
8052f90b865SAlexander Duyck 		tc_pct = DCB_ATTR_VALUE_UNDEFINED;
8062f90b865SAlexander Duyck 		up_map = DCB_ATTR_VALUE_UNDEFINED;
8072f90b865SAlexander Duyck 
8082f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO])
8092f90b865SAlexander Duyck 			prio =
8102f90b865SAlexander Duyck 			    nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO]);
8112f90b865SAlexander Duyck 
8122f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_PGID])
8132f90b865SAlexander Duyck 			pgid = nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_PGID]);
8142f90b865SAlexander Duyck 
8152f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_BW_PCT])
8162f90b865SAlexander Duyck 			tc_pct = nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_BW_PCT]);
8172f90b865SAlexander Duyck 
8182f90b865SAlexander Duyck 		if (param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING])
8192f90b865SAlexander Duyck 			up_map =
8202f90b865SAlexander Duyck 			     nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING]);
8212f90b865SAlexander Duyck 
8222f90b865SAlexander Duyck 		/* dir: Tx = 0, Rx = 1 */
8232f90b865SAlexander Duyck 		if (dir) {
8242f90b865SAlexander Duyck 			/* Rx */
8252f90b865SAlexander Duyck 			netdev->dcbnl_ops->setpgtccfgrx(netdev,
8262f90b865SAlexander Duyck 				i - DCB_PG_ATTR_TC_0,
8272f90b865SAlexander Duyck 				prio, pgid, tc_pct, up_map);
8282f90b865SAlexander Duyck 		} else {
8292f90b865SAlexander Duyck 			/* Tx */
8302f90b865SAlexander Duyck 			netdev->dcbnl_ops->setpgtccfgtx(netdev,
8312f90b865SAlexander Duyck 				i - DCB_PG_ATTR_TC_0,
8322f90b865SAlexander Duyck 				prio, pgid, tc_pct, up_map);
8332f90b865SAlexander Duyck 		}
8342f90b865SAlexander Duyck 	}
8352f90b865SAlexander Duyck 
8362f90b865SAlexander Duyck 	for (i = DCB_PG_ATTR_BW_ID_0; i <= DCB_PG_ATTR_BW_ID_7; i++) {
8372f90b865SAlexander Duyck 		if (!pg_tb[i])
8382f90b865SAlexander Duyck 			continue;
8392f90b865SAlexander Duyck 
8402f90b865SAlexander Duyck 		tc_pct = nla_get_u8(pg_tb[i]);
8412f90b865SAlexander Duyck 
8422f90b865SAlexander Duyck 		/* dir: Tx = 0, Rx = 1 */
8432f90b865SAlexander Duyck 		if (dir) {
8442f90b865SAlexander Duyck 			/* Rx */
8452f90b865SAlexander Duyck 			netdev->dcbnl_ops->setpgbwgcfgrx(netdev,
8462f90b865SAlexander Duyck 					 i - DCB_PG_ATTR_BW_ID_0, tc_pct);
8472f90b865SAlexander Duyck 		} else {
8482f90b865SAlexander Duyck 			/* Tx */
8492f90b865SAlexander Duyck 			netdev->dcbnl_ops->setpgbwgcfgtx(netdev,
8502f90b865SAlexander Duyck 					 i - DCB_PG_ATTR_BW_ID_0, tc_pct);
8512f90b865SAlexander Duyck 		}
8522f90b865SAlexander Duyck 	}
8532f90b865SAlexander Duyck 
8542f90b865SAlexander Duyck 	ret = dcbnl_reply(0, RTM_SETDCB,
8552f90b865SAlexander Duyck 			  (dir ? DCB_CMD_PGRX_SCFG : DCB_CMD_PGTX_SCFG),
8562f90b865SAlexander Duyck 			  DCB_ATTR_PG_CFG, pid, seq, flags);
8572f90b865SAlexander Duyck 
8582f90b865SAlexander Duyck err:
8592f90b865SAlexander Duyck 	return ret;
8602f90b865SAlexander Duyck }
8612f90b865SAlexander Duyck 
8622f90b865SAlexander Duyck static int dcbnl_pgtx_setcfg(struct net_device *netdev, struct nlattr **tb,
8632f90b865SAlexander Duyck                              u32 pid, u32 seq, u16 flags)
8642f90b865SAlexander Duyck {
8652f90b865SAlexander Duyck 	return __dcbnl_pg_setcfg(netdev, tb, pid, seq, flags, 0);
8662f90b865SAlexander Duyck }
8672f90b865SAlexander Duyck 
8682f90b865SAlexander Duyck static int dcbnl_pgrx_setcfg(struct net_device *netdev, struct nlattr **tb,
8692f90b865SAlexander Duyck                              u32 pid, u32 seq, u16 flags)
8702f90b865SAlexander Duyck {
8712f90b865SAlexander Duyck 	return __dcbnl_pg_setcfg(netdev, tb, pid, seq, flags, 1);
8722f90b865SAlexander Duyck }
8732f90b865SAlexander Duyck 
874859ee3c4SAlexander Duyck static int dcbnl_bcn_getcfg(struct net_device *netdev, struct nlattr **tb,
875859ee3c4SAlexander Duyck                             u32 pid, u32 seq, u16 flags)
876859ee3c4SAlexander Duyck {
877859ee3c4SAlexander Duyck 	struct sk_buff *dcbnl_skb;
878859ee3c4SAlexander Duyck 	struct nlmsghdr *nlh;
879859ee3c4SAlexander Duyck 	struct dcbmsg *dcb;
880859ee3c4SAlexander Duyck 	struct nlattr *bcn_nest;
881859ee3c4SAlexander Duyck 	struct nlattr *bcn_tb[DCB_BCN_ATTR_MAX + 1];
882859ee3c4SAlexander Duyck 	u8 value_byte;
883859ee3c4SAlexander Duyck 	u32 value_integer;
884859ee3c4SAlexander Duyck 	int ret  = -EINVAL;
885859ee3c4SAlexander Duyck 	bool getall = false;
886859ee3c4SAlexander Duyck 	int i;
887859ee3c4SAlexander Duyck 
888859ee3c4SAlexander Duyck 	if (!tb[DCB_ATTR_BCN] || !netdev->dcbnl_ops->getbcnrp ||
889859ee3c4SAlexander Duyck 	    !netdev->dcbnl_ops->getbcncfg)
890859ee3c4SAlexander Duyck 		return ret;
891859ee3c4SAlexander Duyck 
892859ee3c4SAlexander Duyck 	ret = nla_parse_nested(bcn_tb, DCB_BCN_ATTR_MAX,
893859ee3c4SAlexander Duyck 	                       tb[DCB_ATTR_BCN], dcbnl_bcn_nest);
894859ee3c4SAlexander Duyck 
895859ee3c4SAlexander Duyck 	if (ret)
896859ee3c4SAlexander Duyck 		goto err_out;
897859ee3c4SAlexander Duyck 
898859ee3c4SAlexander Duyck 	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
899859ee3c4SAlexander Duyck 	if (!dcbnl_skb)
900859ee3c4SAlexander Duyck 		goto err_out;
901859ee3c4SAlexander Duyck 
902859ee3c4SAlexander Duyck 	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
903859ee3c4SAlexander Duyck 
904859ee3c4SAlexander Duyck 	dcb = NLMSG_DATA(nlh);
905859ee3c4SAlexander Duyck 	dcb->dcb_family = AF_UNSPEC;
906859ee3c4SAlexander Duyck 	dcb->cmd = DCB_CMD_BCN_GCFG;
907859ee3c4SAlexander Duyck 
908859ee3c4SAlexander Duyck 	bcn_nest = nla_nest_start(dcbnl_skb, DCB_ATTR_BCN);
909859ee3c4SAlexander Duyck 	if (!bcn_nest)
910859ee3c4SAlexander Duyck 		goto err;
911859ee3c4SAlexander Duyck 
912859ee3c4SAlexander Duyck 	if (bcn_tb[DCB_BCN_ATTR_ALL])
913859ee3c4SAlexander Duyck 		getall = true;
914859ee3c4SAlexander Duyck 
915859ee3c4SAlexander Duyck 	for (i = DCB_BCN_ATTR_RP_0; i <= DCB_BCN_ATTR_RP_7; i++) {
916859ee3c4SAlexander Duyck 		if (!getall && !bcn_tb[i])
917859ee3c4SAlexander Duyck 			continue;
918859ee3c4SAlexander Duyck 
919859ee3c4SAlexander Duyck 		netdev->dcbnl_ops->getbcnrp(netdev, i - DCB_BCN_ATTR_RP_0,
920859ee3c4SAlexander Duyck 		                            &value_byte);
921859ee3c4SAlexander Duyck 		ret = nla_put_u8(dcbnl_skb, i, value_byte);
922859ee3c4SAlexander Duyck 		if (ret)
923859ee3c4SAlexander Duyck 			goto err_bcn;
924859ee3c4SAlexander Duyck 	}
925859ee3c4SAlexander Duyck 
926859ee3c4SAlexander Duyck 	for (i = DCB_BCN_ATTR_ALPHA; i <= DCB_BCN_ATTR_RI; i++) {
927859ee3c4SAlexander Duyck 		if (!getall && !bcn_tb[i])
928859ee3c4SAlexander Duyck 			continue;
929859ee3c4SAlexander Duyck 
930859ee3c4SAlexander Duyck 		netdev->dcbnl_ops->getbcncfg(netdev, i,
931859ee3c4SAlexander Duyck 		                             &value_integer);
932859ee3c4SAlexander Duyck 		ret = nla_put_u32(dcbnl_skb, i, value_integer);
933859ee3c4SAlexander Duyck 		if (ret)
934859ee3c4SAlexander Duyck 			goto err_bcn;
935859ee3c4SAlexander Duyck 	}
936859ee3c4SAlexander Duyck 
937859ee3c4SAlexander Duyck 	nla_nest_end(dcbnl_skb, bcn_nest);
938859ee3c4SAlexander Duyck 
939859ee3c4SAlexander Duyck 	nlmsg_end(dcbnl_skb, nlh);
940859ee3c4SAlexander Duyck 
941859ee3c4SAlexander Duyck 	ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
942859ee3c4SAlexander Duyck 	if (ret)
943859ee3c4SAlexander Duyck 		goto err;
944859ee3c4SAlexander Duyck 
945859ee3c4SAlexander Duyck 	return 0;
946859ee3c4SAlexander Duyck 
947859ee3c4SAlexander Duyck err_bcn:
948859ee3c4SAlexander Duyck 	nla_nest_cancel(dcbnl_skb, bcn_nest);
949859ee3c4SAlexander Duyck nlmsg_failure:
950859ee3c4SAlexander Duyck err:
951859ee3c4SAlexander Duyck 	kfree(dcbnl_skb);
952859ee3c4SAlexander Duyck err_out:
953859ee3c4SAlexander Duyck 	ret  = -EINVAL;
954859ee3c4SAlexander Duyck 	return ret;
955859ee3c4SAlexander Duyck }
956859ee3c4SAlexander Duyck 
957859ee3c4SAlexander Duyck static int dcbnl_bcn_setcfg(struct net_device *netdev, struct nlattr **tb,
958859ee3c4SAlexander Duyck                             u32 pid, u32 seq, u16 flags)
959859ee3c4SAlexander Duyck {
960859ee3c4SAlexander Duyck 	struct nlattr *data[DCB_BCN_ATTR_MAX + 1];
961859ee3c4SAlexander Duyck 	int i;
962859ee3c4SAlexander Duyck 	int ret = -EINVAL;
963859ee3c4SAlexander Duyck 	u8 value_byte;
964859ee3c4SAlexander Duyck 	u32 value_int;
965859ee3c4SAlexander Duyck 
966859ee3c4SAlexander Duyck 	if (!tb[DCB_ATTR_BCN] || !netdev->dcbnl_ops->setbcncfg
967859ee3c4SAlexander Duyck 	    || !netdev->dcbnl_ops->setbcnrp)
968859ee3c4SAlexander Duyck 		return ret;
969859ee3c4SAlexander Duyck 
970859ee3c4SAlexander Duyck 	ret = nla_parse_nested(data, DCB_BCN_ATTR_MAX,
971859ee3c4SAlexander Duyck 	                       tb[DCB_ATTR_BCN],
972859ee3c4SAlexander Duyck 	                       dcbnl_pfc_up_nest);
973859ee3c4SAlexander Duyck 	if (ret)
974859ee3c4SAlexander Duyck 		goto err;
975859ee3c4SAlexander Duyck 
976859ee3c4SAlexander Duyck 	for (i = DCB_BCN_ATTR_RP_0; i <= DCB_BCN_ATTR_RP_7; i++) {
977859ee3c4SAlexander Duyck 		if (data[i] == NULL)
978859ee3c4SAlexander Duyck 			continue;
979859ee3c4SAlexander Duyck 		value_byte = nla_get_u8(data[i]);
980859ee3c4SAlexander Duyck 		netdev->dcbnl_ops->setbcnrp(netdev,
981859ee3c4SAlexander Duyck 			data[i]->nla_type - DCB_BCN_ATTR_RP_0, value_byte);
982859ee3c4SAlexander Duyck 	}
983859ee3c4SAlexander Duyck 
984859ee3c4SAlexander Duyck 	for (i = DCB_BCN_ATTR_ALPHA; i <= DCB_BCN_ATTR_RI; i++) {
985859ee3c4SAlexander Duyck 		if (data[i] == NULL)
986859ee3c4SAlexander Duyck 			continue;
987859ee3c4SAlexander Duyck 		value_int = nla_get_u32(data[i]);
988859ee3c4SAlexander Duyck 		netdev->dcbnl_ops->setbcncfg(netdev,
989859ee3c4SAlexander Duyck 	                                     i, value_int);
990859ee3c4SAlexander Duyck 	}
991859ee3c4SAlexander Duyck 
992859ee3c4SAlexander Duyck 	ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_BCN_SCFG, DCB_ATTR_BCN,
993859ee3c4SAlexander Duyck 	                  pid, seq, flags);
994859ee3c4SAlexander Duyck err:
995859ee3c4SAlexander Duyck 	return ret;
996859ee3c4SAlexander Duyck }
997859ee3c4SAlexander Duyck 
9982f90b865SAlexander Duyck static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
9992f90b865SAlexander Duyck {
10002f90b865SAlexander Duyck 	struct net *net = sock_net(skb->sk);
10012f90b865SAlexander Duyck 	struct net_device *netdev;
10022f90b865SAlexander Duyck 	struct dcbmsg  *dcb = (struct dcbmsg *)NLMSG_DATA(nlh);
10032f90b865SAlexander Duyck 	struct nlattr *tb[DCB_ATTR_MAX + 1];
10042f90b865SAlexander Duyck 	u32 pid = skb ? NETLINK_CB(skb).pid : 0;
10052f90b865SAlexander Duyck 	int ret = -EINVAL;
10062f90b865SAlexander Duyck 
10072f90b865SAlexander Duyck 	if (net != &init_net)
10082f90b865SAlexander Duyck 		return -EINVAL;
10092f90b865SAlexander Duyck 
10102f90b865SAlexander Duyck 	ret = nlmsg_parse(nlh, sizeof(*dcb), tb, DCB_ATTR_MAX,
10112f90b865SAlexander Duyck 			  dcbnl_rtnl_policy);
10122f90b865SAlexander Duyck 	if (ret < 0)
10132f90b865SAlexander Duyck 		return ret;
10142f90b865SAlexander Duyck 
10152f90b865SAlexander Duyck 	if (!tb[DCB_ATTR_IFNAME])
10162f90b865SAlexander Duyck 		return -EINVAL;
10172f90b865SAlexander Duyck 
10182f90b865SAlexander Duyck 	netdev = dev_get_by_name(&init_net, nla_data(tb[DCB_ATTR_IFNAME]));
10192f90b865SAlexander Duyck 	if (!netdev)
10202f90b865SAlexander Duyck 		return -EINVAL;
10212f90b865SAlexander Duyck 
10222f90b865SAlexander Duyck 	if (!netdev->dcbnl_ops)
10232f90b865SAlexander Duyck 		goto errout;
10242f90b865SAlexander Duyck 
10252f90b865SAlexander Duyck 	switch (dcb->cmd) {
10262f90b865SAlexander Duyck 	case DCB_CMD_GSTATE:
10272f90b865SAlexander Duyck 		ret = dcbnl_getstate(netdev, tb, pid, nlh->nlmsg_seq,
10282f90b865SAlexander Duyck 		                     nlh->nlmsg_flags);
10292f90b865SAlexander Duyck 		goto out;
10302f90b865SAlexander Duyck 	case DCB_CMD_PFC_GCFG:
10312f90b865SAlexander Duyck 		ret = dcbnl_getpfccfg(netdev, tb, pid, nlh->nlmsg_seq,
10322f90b865SAlexander Duyck 		                      nlh->nlmsg_flags);
10332f90b865SAlexander Duyck 		goto out;
10342f90b865SAlexander Duyck 	case DCB_CMD_GPERM_HWADDR:
10352f90b865SAlexander Duyck 		ret = dcbnl_getperm_hwaddr(netdev, tb, pid, nlh->nlmsg_seq,
10362f90b865SAlexander Duyck 		                           nlh->nlmsg_flags);
10372f90b865SAlexander Duyck 		goto out;
10382f90b865SAlexander Duyck 	case DCB_CMD_PGTX_GCFG:
10392f90b865SAlexander Duyck 		ret = dcbnl_pgtx_getcfg(netdev, tb, pid, nlh->nlmsg_seq,
10402f90b865SAlexander Duyck 		                        nlh->nlmsg_flags);
10412f90b865SAlexander Duyck 		goto out;
10422f90b865SAlexander Duyck 	case DCB_CMD_PGRX_GCFG:
10432f90b865SAlexander Duyck 		ret = dcbnl_pgrx_getcfg(netdev, tb, pid, nlh->nlmsg_seq,
10442f90b865SAlexander Duyck 		                        nlh->nlmsg_flags);
10452f90b865SAlexander Duyck 		goto out;
1046859ee3c4SAlexander Duyck 	case DCB_CMD_BCN_GCFG:
1047859ee3c4SAlexander Duyck 		ret = dcbnl_bcn_getcfg(netdev, tb, pid, nlh->nlmsg_seq,
1048859ee3c4SAlexander Duyck 		                       nlh->nlmsg_flags);
1049859ee3c4SAlexander Duyck 		goto out;
10502f90b865SAlexander Duyck 	case DCB_CMD_SSTATE:
10512f90b865SAlexander Duyck 		ret = dcbnl_setstate(netdev, tb, pid, nlh->nlmsg_seq,
10522f90b865SAlexander Duyck 		                     nlh->nlmsg_flags);
10532f90b865SAlexander Duyck 		goto out;
10542f90b865SAlexander Duyck 	case DCB_CMD_PFC_SCFG:
10552f90b865SAlexander Duyck 		ret = dcbnl_setpfccfg(netdev, tb, pid, nlh->nlmsg_seq,
10562f90b865SAlexander Duyck 		                      nlh->nlmsg_flags);
10572f90b865SAlexander Duyck 		goto out;
10582f90b865SAlexander Duyck 
10592f90b865SAlexander Duyck 	case DCB_CMD_SET_ALL:
10602f90b865SAlexander Duyck 		ret = dcbnl_setall(netdev, tb, pid, nlh->nlmsg_seq,
10612f90b865SAlexander Duyck 		                   nlh->nlmsg_flags);
10622f90b865SAlexander Duyck 		goto out;
10632f90b865SAlexander Duyck 	case DCB_CMD_PGTX_SCFG:
10642f90b865SAlexander Duyck 		ret = dcbnl_pgtx_setcfg(netdev, tb, pid, nlh->nlmsg_seq,
10652f90b865SAlexander Duyck 		                        nlh->nlmsg_flags);
10662f90b865SAlexander Duyck 		goto out;
10672f90b865SAlexander Duyck 	case DCB_CMD_PGRX_SCFG:
10682f90b865SAlexander Duyck 		ret = dcbnl_pgrx_setcfg(netdev, tb, pid, nlh->nlmsg_seq,
10692f90b865SAlexander Duyck 		                        nlh->nlmsg_flags);
10702f90b865SAlexander Duyck 		goto out;
107146132188SAlexander Duyck 	case DCB_CMD_GCAP:
107246132188SAlexander Duyck 		ret = dcbnl_getcap(netdev, tb, pid, nlh->nlmsg_seq,
107346132188SAlexander Duyck 		                   nlh->nlmsg_flags);
107446132188SAlexander Duyck 		goto out;
107533dbabc4SAlexander Duyck 	case DCB_CMD_GNUMTCS:
107633dbabc4SAlexander Duyck 		ret = dcbnl_getnumtcs(netdev, tb, pid, nlh->nlmsg_seq,
107733dbabc4SAlexander Duyck 		                      nlh->nlmsg_flags);
107833dbabc4SAlexander Duyck 		goto out;
107933dbabc4SAlexander Duyck 	case DCB_CMD_SNUMTCS:
108033dbabc4SAlexander Duyck 		ret = dcbnl_setnumtcs(netdev, tb, pid, nlh->nlmsg_seq,
108133dbabc4SAlexander Duyck 		                      nlh->nlmsg_flags);
108233dbabc4SAlexander Duyck 		goto out;
10830eb3aa9bSAlexander Duyck 	case DCB_CMD_PFC_GSTATE:
10840eb3aa9bSAlexander Duyck 		ret = dcbnl_getpfcstate(netdev, tb, pid, nlh->nlmsg_seq,
10850eb3aa9bSAlexander Duyck 		                        nlh->nlmsg_flags);
10860eb3aa9bSAlexander Duyck 		goto out;
10870eb3aa9bSAlexander Duyck 	case DCB_CMD_PFC_SSTATE:
10880eb3aa9bSAlexander Duyck 		ret = dcbnl_setpfcstate(netdev, tb, pid, nlh->nlmsg_seq,
10890eb3aa9bSAlexander Duyck 		                        nlh->nlmsg_flags);
10900eb3aa9bSAlexander Duyck 		goto out;
1091859ee3c4SAlexander Duyck 	case DCB_CMD_BCN_SCFG:
1092859ee3c4SAlexander Duyck 		ret = dcbnl_bcn_setcfg(netdev, tb, pid, nlh->nlmsg_seq,
1093859ee3c4SAlexander Duyck 		                       nlh->nlmsg_flags);
1094859ee3c4SAlexander Duyck 		goto out;
10952f90b865SAlexander Duyck 	default:
10962f90b865SAlexander Duyck 		goto errout;
10972f90b865SAlexander Duyck 	}
10982f90b865SAlexander Duyck errout:
10992f90b865SAlexander Duyck 	ret = -EINVAL;
11002f90b865SAlexander Duyck out:
11012f90b865SAlexander Duyck 	dev_put(netdev);
11022f90b865SAlexander Duyck 	return ret;
11032f90b865SAlexander Duyck }
11042f90b865SAlexander Duyck 
11052f90b865SAlexander Duyck static int __init dcbnl_init(void)
11062f90b865SAlexander Duyck {
11072f90b865SAlexander Duyck 	rtnl_register(PF_UNSPEC, RTM_GETDCB, dcb_doit, NULL);
11082f90b865SAlexander Duyck 	rtnl_register(PF_UNSPEC, RTM_SETDCB, dcb_doit, NULL);
11092f90b865SAlexander Duyck 
11102f90b865SAlexander Duyck 	return 0;
11112f90b865SAlexander Duyck }
11122f90b865SAlexander Duyck module_init(dcbnl_init);
11132f90b865SAlexander Duyck 
11142f90b865SAlexander Duyck static void __exit dcbnl_exit(void)
11152f90b865SAlexander Duyck {
11162f90b865SAlexander Duyck 	rtnl_unregister(PF_UNSPEC, RTM_GETDCB);
11172f90b865SAlexander Duyck 	rtnl_unregister(PF_UNSPEC, RTM_SETDCB);
11182f90b865SAlexander Duyck }
11192f90b865SAlexander Duyck module_exit(dcbnl_exit);
11202f90b865SAlexander Duyck 
11212f90b865SAlexander Duyck 
1122