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