diff options
Diffstat (limited to 'net/dcb/dcbnl.c')
| -rw-r--r-- | net/dcb/dcbnl.c | 1705 | 
1 files changed, 1188 insertions, 517 deletions
diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index 19ac2b98548..f8b98d89c28 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -1,5 +1,5 @@  /* - * Copyright (c) 2008, Intel Corporation. + * Copyright (c) 2008-2011, Intel Corporation.   *   * This program is free software; you can redistribute it and/or modify it   * under the terms and conditions of the GNU General Public License, @@ -11,8 +11,7 @@   * more details.   *   * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. + * this program; if not, see <http://www.gnu.org/licenses/>.   *   * Author: Lucy Liu <lucy.liu@intel.com>   */ @@ -23,11 +22,12 @@  #include <net/netlink.h>  #include <net/rtnetlink.h>  #include <linux/dcbnl.h> +#include <net/dcbevent.h>  #include <linux/rtnetlink.h> +#include <linux/module.h>  #include <net/sock.h> -/** - * Data Center Bridging (DCB) is a collection of Ethernet enhancements +/* Data Center Bridging (DCB) is a collection of Ethernet enhancements   * intended to allow network traffic with differing requirements   * (highly reliable, no drops vs. best effort vs. low latency) to operate   * and co-exist on Ethernet.  Current DCB features are: @@ -66,6 +66,9 @@ static const struct nla_policy dcbnl_rtnl_policy[DCB_ATTR_MAX + 1] = {  	[DCB_ATTR_PFC_STATE]   = {.type = NLA_U8},  	[DCB_ATTR_BCN]         = {.type = NLA_NESTED},  	[DCB_ATTR_APP]         = {.type = NLA_NESTED}, +	[DCB_ATTR_IEEE]	       = {.type = NLA_NESTED}, +	[DCB_ATTR_DCBX]        = {.type = NLA_U8}, +	[DCB_ATTR_FEATCFG]     = {.type = NLA_NESTED},  };  /* DCB priority flow control to User Priority nested attributes */ @@ -122,6 +125,7 @@ static const struct nla_policy dcbnl_cap_nest[DCB_CAP_ATTR_MAX + 1] = {  	[DCB_CAP_ATTR_PFC_TCS] = {.type = NLA_U8},  	[DCB_CAP_ATTR_GSP]     = {.type = NLA_U8},  	[DCB_CAP_ATTR_BCN]     = {.type = NLA_U8}, +	[DCB_CAP_ATTR_DCBX]    = {.type = NLA_U8},  };  /* DCB capabilities nested attributes. */ @@ -167,92 +171,89 @@ static const struct nla_policy dcbnl_app_nest[DCB_APP_ATTR_MAX + 1] = {  	[DCB_APP_ATTR_PRIORITY]     = {.type = NLA_U8},  }; -/* standard netlink reply call */ -static int dcbnl_reply(u8 value, u8 event, u8 cmd, u8 attr, u32 pid, -                       u32 seq, u16 flags) +/* IEEE 802.1Qaz nested attributes. */ +static const struct nla_policy dcbnl_ieee_policy[DCB_ATTR_IEEE_MAX + 1] = { +	[DCB_ATTR_IEEE_ETS]	    = {.len = sizeof(struct ieee_ets)}, +	[DCB_ATTR_IEEE_PFC]	    = {.len = sizeof(struct ieee_pfc)}, +	[DCB_ATTR_IEEE_APP_TABLE]   = {.type = NLA_NESTED}, +	[DCB_ATTR_IEEE_MAXRATE]   = {.len = sizeof(struct ieee_maxrate)}, +}; + +static const struct nla_policy dcbnl_ieee_app[DCB_ATTR_IEEE_APP_MAX + 1] = { +	[DCB_ATTR_IEEE_APP]	    = {.len = sizeof(struct dcb_app)}, +}; + +/* DCB number of traffic classes nested attributes. */ +static const struct nla_policy dcbnl_featcfg_nest[DCB_FEATCFG_ATTR_MAX + 1] = { +	[DCB_FEATCFG_ATTR_ALL]      = {.type = NLA_FLAG}, +	[DCB_FEATCFG_ATTR_PG]       = {.type = NLA_U8}, +	[DCB_FEATCFG_ATTR_PFC]      = {.type = NLA_U8}, +	[DCB_FEATCFG_ATTR_APP]      = {.type = NLA_U8}, +}; + +static LIST_HEAD(dcb_app_list); +static DEFINE_SPINLOCK(dcb_lock); + +static struct sk_buff *dcbnl_newmsg(int type, u8 cmd, u32 port, u32 seq, +				    u32 flags, struct nlmsghdr **nlhp)  { -	struct sk_buff *dcbnl_skb; +	struct sk_buff *skb;  	struct dcbmsg *dcb;  	struct nlmsghdr *nlh; -	int ret = -EINVAL; -	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); -	if (!dcbnl_skb) -		return ret; +	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); +	if (!skb) +		return NULL; -	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, event, sizeof(*dcb), flags); +	nlh = nlmsg_put(skb, port, seq, type, sizeof(*dcb), flags); +	BUG_ON(!nlh); -	dcb = NLMSG_DATA(nlh); +	dcb = nlmsg_data(nlh);  	dcb->dcb_family = AF_UNSPEC;  	dcb->cmd = cmd;  	dcb->dcb_pad = 0; -	ret = nla_put_u8(dcbnl_skb, attr, value); -	if (ret) -		goto err; - -	/* end the message, assign the nlmsg_len. */ -	nlmsg_end(dcbnl_skb, nlh); -	ret = rtnl_unicast(dcbnl_skb, &init_net, pid); -	if (ret) -		return -EINVAL; +	if (nlhp) +		*nlhp = nlh; -	return 0; -nlmsg_failure: -err: -	kfree_skb(dcbnl_skb); -	return ret; +	return skb;  } -static int dcbnl_getstate(struct net_device *netdev, struct nlattr **tb, -                          u32 pid, u32 seq, u16 flags) +static int dcbnl_getstate(struct net_device *netdev, struct nlmsghdr *nlh, +			  u32 seq, struct nlattr **tb, struct sk_buff *skb)  { -	int ret = -EINVAL; -  	/* if (!tb[DCB_ATTR_STATE] || !netdev->dcbnl_ops->getstate) */  	if (!netdev->dcbnl_ops->getstate) -		return ret; - -	ret = dcbnl_reply(netdev->dcbnl_ops->getstate(netdev), RTM_GETDCB, -	                  DCB_CMD_GSTATE, DCB_ATTR_STATE, pid, seq, flags); +		return -EOPNOTSUPP; -	return ret; +	return nla_put_u8(skb, DCB_ATTR_STATE, +			  netdev->dcbnl_ops->getstate(netdev));  } -static int dcbnl_getpfccfg(struct net_device *netdev, struct nlattr **tb, -                           u32 pid, u32 seq, u16 flags) +static int dcbnl_getpfccfg(struct net_device *netdev, struct nlmsghdr *nlh, +			   u32 seq, struct nlattr **tb, struct sk_buff *skb)  { -	struct sk_buff *dcbnl_skb; -	struct nlmsghdr *nlh; -	struct dcbmsg *dcb;  	struct nlattr *data[DCB_PFC_UP_ATTR_MAX + 1], *nest;  	u8 value; -	int ret = -EINVAL; +	int ret;  	int i;  	int getall = 0; -	if (!tb[DCB_ATTR_PFC_CFG] || !netdev->dcbnl_ops->getpfccfg) -		return ret; +	if (!tb[DCB_ATTR_PFC_CFG]) +		return -EINVAL; + +	if (!netdev->dcbnl_ops->getpfccfg) +		return -EOPNOTSUPP;  	ret = nla_parse_nested(data, DCB_PFC_UP_ATTR_MAX,  	                       tb[DCB_ATTR_PFC_CFG],  	                       dcbnl_pfc_up_nest);  	if (ret) -		goto err_out; - -	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); -	if (!dcbnl_skb) -		goto err_out; - -	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); - -	dcb = NLMSG_DATA(nlh); -	dcb->dcb_family = AF_UNSPEC; -	dcb->cmd = DCB_CMD_PFC_GCFG; +		return ret; -	nest = nla_nest_start(dcbnl_skb, DCB_ATTR_PFC_CFG); +	nest = nla_nest_start(skb, DCB_ATTR_PFC_CFG);  	if (!nest) -		goto err; +		return -EMSGSIZE;  	if (data[DCB_PFC_UP_ATTR_ALL])  		getall = 1; @@ -263,103 +264,54 @@ static int dcbnl_getpfccfg(struct net_device *netdev, struct nlattr **tb,  		netdev->dcbnl_ops->getpfccfg(netdev, i - DCB_PFC_UP_ATTR_0,  		                             &value); -		ret = nla_put_u8(dcbnl_skb, i, value); - +		ret = nla_put_u8(skb, i, value);  		if (ret) { -			nla_nest_cancel(dcbnl_skb, nest); -			goto err; +			nla_nest_cancel(skb, nest); +			return ret;  		}  	} -	nla_nest_end(dcbnl_skb, nest); - -	nlmsg_end(dcbnl_skb, nlh); - -	ret = rtnl_unicast(dcbnl_skb, &init_net, pid); -	if (ret) -		goto err_out; +	nla_nest_end(skb, nest);  	return 0; -nlmsg_failure: -err: -	kfree_skb(dcbnl_skb); -err_out: -	return -EINVAL;  } -static int dcbnl_getperm_hwaddr(struct net_device *netdev, struct nlattr **tb, -                                u32 pid, u32 seq, u16 flags) +static int dcbnl_getperm_hwaddr(struct net_device *netdev, struct nlmsghdr *nlh, +				u32 seq, struct nlattr **tb, struct sk_buff *skb)  { -	struct sk_buff *dcbnl_skb; -	struct nlmsghdr *nlh; -	struct dcbmsg *dcb;  	u8 perm_addr[MAX_ADDR_LEN]; -	int ret = -EINVAL;  	if (!netdev->dcbnl_ops->getpermhwaddr) -		return ret; - -	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); -	if (!dcbnl_skb) -		goto err_out; - -	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); - -	dcb = NLMSG_DATA(nlh); -	dcb->dcb_family = AF_UNSPEC; -	dcb->cmd = DCB_CMD_GPERM_HWADDR; +		return -EOPNOTSUPP; +	memset(perm_addr, 0, sizeof(perm_addr));  	netdev->dcbnl_ops->getpermhwaddr(netdev, perm_addr); -	ret = nla_put(dcbnl_skb, DCB_ATTR_PERM_HWADDR, sizeof(perm_addr), -	              perm_addr); - -	nlmsg_end(dcbnl_skb, nlh); - -	ret = rtnl_unicast(dcbnl_skb, &init_net, pid); -	if (ret) -		goto err_out; - -	return 0; - -nlmsg_failure: -	kfree_skb(dcbnl_skb); -err_out: -	return -EINVAL; +	return nla_put(skb, DCB_ATTR_PERM_HWADDR, sizeof(perm_addr), perm_addr);  } -static int dcbnl_getcap(struct net_device *netdev, struct nlattr **tb, -                        u32 pid, u32 seq, u16 flags) +static int dcbnl_getcap(struct net_device *netdev, struct nlmsghdr *nlh, +			u32 seq, struct nlattr **tb, struct sk_buff *skb)  { -	struct sk_buff *dcbnl_skb; -	struct nlmsghdr *nlh; -	struct dcbmsg *dcb;  	struct nlattr *data[DCB_CAP_ATTR_MAX + 1], *nest;  	u8 value; -	int ret = -EINVAL; +	int ret;  	int i;  	int getall = 0; -	if (!tb[DCB_ATTR_CAP] || !netdev->dcbnl_ops->getcap) -		return ret; +	if (!tb[DCB_ATTR_CAP]) +		return -EINVAL; + +	if (!netdev->dcbnl_ops->getcap) +		return -EOPNOTSUPP;  	ret = nla_parse_nested(data, DCB_CAP_ATTR_MAX, tb[DCB_ATTR_CAP],  	                       dcbnl_cap_nest);  	if (ret) -		goto err_out; - -	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); -	if (!dcbnl_skb) -		goto err_out; - -	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); - -	dcb = NLMSG_DATA(nlh); -	dcb->dcb_family = AF_UNSPEC; -	dcb->cmd = DCB_CMD_GCAP; +		return ret; -	nest = nla_nest_start(dcbnl_skb, DCB_ATTR_CAP); +	nest = nla_nest_start(skb, DCB_ATTR_CAP);  	if (!nest) -		goto err; +		return -EMSGSIZE;  	if (data[DCB_CAP_ATTR_ALL])  		getall = 1; @@ -369,69 +321,41 @@ static int dcbnl_getcap(struct net_device *netdev, struct nlattr **tb,  			continue;  		if (!netdev->dcbnl_ops->getcap(netdev, i, &value)) { -			ret = nla_put_u8(dcbnl_skb, i, value); - +			ret = nla_put_u8(skb, i, value);  			if (ret) { -				nla_nest_cancel(dcbnl_skb, nest); -				goto err; +				nla_nest_cancel(skb, nest); +				return ret;  			}  		}  	} -	nla_nest_end(dcbnl_skb, nest); - -	nlmsg_end(dcbnl_skb, nlh); - -	ret = rtnl_unicast(dcbnl_skb, &init_net, pid); -	if (ret) -		goto err_out; +	nla_nest_end(skb, nest);  	return 0; -nlmsg_failure: -err: -	kfree_skb(dcbnl_skb); -err_out: -	return -EINVAL;  } -static int dcbnl_getnumtcs(struct net_device *netdev, struct nlattr **tb, -                           u32 pid, u32 seq, u16 flags) +static int dcbnl_getnumtcs(struct net_device *netdev, struct nlmsghdr *nlh, +			   u32 seq, struct nlattr **tb, struct sk_buff *skb)  { -	struct sk_buff *dcbnl_skb; -	struct nlmsghdr *nlh; -	struct dcbmsg *dcb;  	struct nlattr *data[DCB_NUMTCS_ATTR_MAX + 1], *nest;  	u8 value; -	int ret = -EINVAL; +	int ret;  	int i;  	int getall = 0; -	if (!tb[DCB_ATTR_NUMTCS] || !netdev->dcbnl_ops->getnumtcs) -		return ret; +	if (!tb[DCB_ATTR_NUMTCS]) +		return -EINVAL; + +	if (!netdev->dcbnl_ops->getnumtcs) +		return -EOPNOTSUPP;  	ret = nla_parse_nested(data, DCB_NUMTCS_ATTR_MAX, tb[DCB_ATTR_NUMTCS],  	                       dcbnl_numtcs_nest); -	if (ret) { -		ret = -EINVAL; -		goto err_out; -	} - -	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); -	if (!dcbnl_skb) { -		ret = -EINVAL; -		goto err_out; -	} - -	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); - -	dcb = NLMSG_DATA(nlh); -	dcb->dcb_family = AF_UNSPEC; -	dcb->cmd = DCB_CMD_GNUMTCS; +	if (ret) +		return ret; -	nest = nla_nest_start(dcbnl_skb, DCB_ATTR_NUMTCS); -	if (!nest) { -		ret = -EINVAL; -		goto err; -	} +	nest = nla_nest_start(skb, DCB_ATTR_NUMTCS); +	if (!nest) +		return -EMSGSIZE;  	if (data[DCB_NUMTCS_ATTR_ALL])  		getall = 1; @@ -442,53 +366,37 @@ static int dcbnl_getnumtcs(struct net_device *netdev, struct nlattr **tb,  		ret = netdev->dcbnl_ops->getnumtcs(netdev, i, &value);  		if (!ret) { -			ret = nla_put_u8(dcbnl_skb, i, value); - +			ret = nla_put_u8(skb, i, value);  			if (ret) { -				nla_nest_cancel(dcbnl_skb, nest); -				ret = -EINVAL; -				goto err; +				nla_nest_cancel(skb, nest); +				return ret;  			} -		} else { -			goto err; -		} -	} -	nla_nest_end(dcbnl_skb, nest); - -	nlmsg_end(dcbnl_skb, nlh); - -	ret = rtnl_unicast(dcbnl_skb, &init_net, pid); -	if (ret) { -		ret = -EINVAL; -		goto err_out; +		} else +			return -EINVAL;  	} +	nla_nest_end(skb, nest);  	return 0; -nlmsg_failure: -err: -	kfree_skb(dcbnl_skb); -err_out: -	return ret;  } -static int dcbnl_setnumtcs(struct net_device *netdev, struct nlattr **tb, -                           u32 pid, u32 seq, u16 flags) +static int dcbnl_setnumtcs(struct net_device *netdev, struct nlmsghdr *nlh, +			   u32 seq, struct nlattr **tb, struct sk_buff *skb)  {  	struct nlattr *data[DCB_NUMTCS_ATTR_MAX + 1]; -	int ret = -EINVAL; +	int ret;  	u8 value;  	int i; -	if (!tb[DCB_ATTR_NUMTCS] || !netdev->dcbnl_ops->setnumtcs) -		return ret; +	if (!tb[DCB_ATTR_NUMTCS]) +		return -EINVAL; + +	if (!netdev->dcbnl_ops->setnumtcs) +		return -EOPNOTSUPP;  	ret = nla_parse_nested(data, DCB_NUMTCS_ATTR_MAX, tb[DCB_ATTR_NUMTCS],  	                       dcbnl_numtcs_nest); - -	if (ret) { -		ret = -EINVAL; -		goto err; -	} +	if (ret) +		return ret;  	for (i = DCB_NUMTCS_ATTR_ALL+1; i <= DCB_NUMTCS_ATTR_MAX; i++) {  		if (data[i] == NULL) @@ -497,207 +405,181 @@ static int dcbnl_setnumtcs(struct net_device *netdev, struct nlattr **tb,  		value = nla_get_u8(data[i]);  		ret = netdev->dcbnl_ops->setnumtcs(netdev, i, value); -  		if (ret) -			goto operr; +			break;  	} -operr: -	ret = dcbnl_reply(!!ret, RTM_SETDCB, DCB_CMD_SNUMTCS, -	                  DCB_ATTR_NUMTCS, pid, seq, flags); - -err: -	return ret; +	return nla_put_u8(skb, DCB_ATTR_NUMTCS, !!ret);  } -static int dcbnl_getpfcstate(struct net_device *netdev, struct nlattr **tb, -                             u32 pid, u32 seq, u16 flags) +static int dcbnl_getpfcstate(struct net_device *netdev, struct nlmsghdr *nlh, +			     u32 seq, struct nlattr **tb, struct sk_buff *skb)  { -	int ret = -EINVAL; -  	if (!netdev->dcbnl_ops->getpfcstate) -		return ret; - -	ret = dcbnl_reply(netdev->dcbnl_ops->getpfcstate(netdev), RTM_GETDCB, -	                  DCB_CMD_PFC_GSTATE, DCB_ATTR_PFC_STATE, -	                  pid, seq, flags); +		return -EOPNOTSUPP; -	return ret; +	return nla_put_u8(skb, DCB_ATTR_PFC_STATE, +			  netdev->dcbnl_ops->getpfcstate(netdev));  } -static int dcbnl_setpfcstate(struct net_device *netdev, struct nlattr **tb, -                             u32 pid, u32 seq, u16 flags) +static int dcbnl_setpfcstate(struct net_device *netdev, struct nlmsghdr *nlh, +			     u32 seq, struct nlattr **tb, struct sk_buff *skb)  { -	int ret = -EINVAL;  	u8 value; -	if (!tb[DCB_ATTR_PFC_STATE] || !netdev->dcbnl_ops->setpfcstate) -		return ret; +	if (!tb[DCB_ATTR_PFC_STATE]) +		return -EINVAL; + +	if (!netdev->dcbnl_ops->setpfcstate) +		return -EOPNOTSUPP;  	value = nla_get_u8(tb[DCB_ATTR_PFC_STATE]);  	netdev->dcbnl_ops->setpfcstate(netdev, value); -	ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_PFC_SSTATE, DCB_ATTR_PFC_STATE, -	                  pid, seq, flags); - -	return ret; +	return nla_put_u8(skb, DCB_ATTR_PFC_STATE, 0);  } -static int dcbnl_getapp(struct net_device *netdev, struct nlattr **tb, -                        u32 pid, u32 seq, u16 flags) +static int dcbnl_getapp(struct net_device *netdev, struct nlmsghdr *nlh, +			u32 seq, struct nlattr **tb, struct sk_buff *skb)  { -	struct sk_buff *dcbnl_skb; -	struct nlmsghdr *nlh; -	struct dcbmsg *dcb;  	struct nlattr *app_nest;  	struct nlattr *app_tb[DCB_APP_ATTR_MAX + 1];  	u16 id;  	u8 up, idtype; -	int ret = -EINVAL; +	int ret; -	if (!tb[DCB_ATTR_APP] || !netdev->dcbnl_ops->getapp) -		goto out; +	if (!tb[DCB_ATTR_APP]) +		return -EINVAL;  	ret = nla_parse_nested(app_tb, DCB_APP_ATTR_MAX, tb[DCB_ATTR_APP],  	                       dcbnl_app_nest);  	if (ret) -		goto out; +		return ret; -	ret = -EINVAL;  	/* all must be non-null */  	if ((!app_tb[DCB_APP_ATTR_IDTYPE]) ||  	    (!app_tb[DCB_APP_ATTR_ID])) -		goto out; +		return -EINVAL;  	/* either by eth type or by socket number */  	idtype = nla_get_u8(app_tb[DCB_APP_ATTR_IDTYPE]);  	if ((idtype != DCB_APP_IDTYPE_ETHTYPE) &&  	    (idtype != DCB_APP_IDTYPE_PORTNUM)) -		goto out; +		return -EINVAL;  	id = nla_get_u16(app_tb[DCB_APP_ATTR_ID]); -	up = netdev->dcbnl_ops->getapp(netdev, idtype, id); -	/* send this back */ -	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); -	if (!dcbnl_skb) -		goto out; +	if (netdev->dcbnl_ops->getapp) { +		up = netdev->dcbnl_ops->getapp(netdev, idtype, id); +	} else { +		struct dcb_app app = { +					.selector = idtype, +					.protocol = id, +				     }; +		up = dcb_getapp(netdev, &app); +	} -	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); -	dcb = NLMSG_DATA(nlh); -	dcb->dcb_family = AF_UNSPEC; -	dcb->cmd = DCB_CMD_GAPP; +	app_nest = nla_nest_start(skb, DCB_ATTR_APP); +	if (!app_nest) +		return -EMSGSIZE; -	app_nest = nla_nest_start(dcbnl_skb, DCB_ATTR_APP); -	ret = nla_put_u8(dcbnl_skb, DCB_APP_ATTR_IDTYPE, idtype); +	ret = nla_put_u8(skb, DCB_APP_ATTR_IDTYPE, idtype);  	if (ret)  		goto out_cancel; -	ret = nla_put_u16(dcbnl_skb, DCB_APP_ATTR_ID, id); +	ret = nla_put_u16(skb, DCB_APP_ATTR_ID, id);  	if (ret)  		goto out_cancel; -	ret = nla_put_u8(dcbnl_skb, DCB_APP_ATTR_PRIORITY, up); +	ret = nla_put_u8(skb, DCB_APP_ATTR_PRIORITY, up);  	if (ret)  		goto out_cancel; -	nla_nest_end(dcbnl_skb, app_nest); -	nlmsg_end(dcbnl_skb, nlh); +	nla_nest_end(skb, app_nest); -	ret = rtnl_unicast(dcbnl_skb, &init_net, pid); -	if (ret) -		goto nlmsg_failure; - -	goto out; +	return 0;  out_cancel: -	nla_nest_cancel(dcbnl_skb, app_nest); -nlmsg_failure: -	kfree_skb(dcbnl_skb); -out: +	nla_nest_cancel(skb, app_nest);  	return ret;  } -static int dcbnl_setapp(struct net_device *netdev, struct nlattr **tb, -                        u32 pid, u32 seq, u16 flags) +static int dcbnl_setapp(struct net_device *netdev, struct nlmsghdr *nlh, +			u32 seq, struct nlattr **tb, struct sk_buff *skb)  { -	int ret = -EINVAL; +	int ret;  	u16 id;  	u8 up, idtype;  	struct nlattr *app_tb[DCB_APP_ATTR_MAX + 1]; -	if (!tb[DCB_ATTR_APP] || !netdev->dcbnl_ops->setapp) -		goto out; +	if (!tb[DCB_ATTR_APP]) +		return -EINVAL;  	ret = nla_parse_nested(app_tb, DCB_APP_ATTR_MAX, tb[DCB_ATTR_APP],  	                       dcbnl_app_nest);  	if (ret) -		goto out; +		return ret; -	ret = -EINVAL;  	/* all must be non-null */  	if ((!app_tb[DCB_APP_ATTR_IDTYPE]) ||  	    (!app_tb[DCB_APP_ATTR_ID]) ||  	    (!app_tb[DCB_APP_ATTR_PRIORITY])) -		goto out; +		return -EINVAL;  	/* either by eth type or by socket number */  	idtype = nla_get_u8(app_tb[DCB_APP_ATTR_IDTYPE]);  	if ((idtype != DCB_APP_IDTYPE_ETHTYPE) &&  	    (idtype != DCB_APP_IDTYPE_PORTNUM)) -		goto out; +		return -EINVAL;  	id = nla_get_u16(app_tb[DCB_APP_ATTR_ID]);  	up = nla_get_u8(app_tb[DCB_APP_ATTR_PRIORITY]); -	ret = dcbnl_reply(netdev->dcbnl_ops->setapp(netdev, idtype, id, up), -	                  RTM_SETDCB, DCB_CMD_SAPP, DCB_ATTR_APP, -	                  pid, seq, flags); -out: +	if (netdev->dcbnl_ops->setapp) { +		ret = netdev->dcbnl_ops->setapp(netdev, idtype, id, up); +	} else { +		struct dcb_app app; +		app.selector = idtype; +		app.protocol = id; +		app.priority = up; +		ret = dcb_setapp(netdev, &app); +	} + +	ret = nla_put_u8(skb, DCB_ATTR_APP, ret); +	dcbnl_cee_notify(netdev, RTM_SETDCB, DCB_CMD_SAPP, seq, 0); +  	return ret;  } -static int __dcbnl_pg_getcfg(struct net_device *netdev, struct nlattr **tb, -                             u32 pid, u32 seq, u16 flags, int dir) +static int __dcbnl_pg_getcfg(struct net_device *netdev, struct nlmsghdr *nlh, +			     struct nlattr **tb, struct sk_buff *skb, int dir)  { -	struct sk_buff *dcbnl_skb; -	struct nlmsghdr *nlh; -	struct dcbmsg *dcb;  	struct nlattr *pg_nest, *param_nest, *data;  	struct nlattr *pg_tb[DCB_PG_ATTR_MAX + 1];  	struct nlattr *param_tb[DCB_TC_ATTR_PARAM_MAX + 1];  	u8 prio, pgid, tc_pct, up_map; -	int ret  = -EINVAL; +	int ret;  	int getall = 0;  	int i; -	if (!tb[DCB_ATTR_PG_CFG] || -	    !netdev->dcbnl_ops->getpgtccfgtx || +	if (!tb[DCB_ATTR_PG_CFG]) +		return -EINVAL; + +	if (!netdev->dcbnl_ops->getpgtccfgtx ||  	    !netdev->dcbnl_ops->getpgtccfgrx ||  	    !netdev->dcbnl_ops->getpgbwgcfgtx ||  	    !netdev->dcbnl_ops->getpgbwgcfgrx) -		return ret; +		return -EOPNOTSUPP;  	ret = nla_parse_nested(pg_tb, DCB_PG_ATTR_MAX,  	                       tb[DCB_ATTR_PG_CFG], dcbnl_pg_nest); -  	if (ret) -		goto err_out; - -	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); -	if (!dcbnl_skb) -		goto err_out; - -	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); - -	dcb = NLMSG_DATA(nlh); -	dcb->dcb_family = AF_UNSPEC; -	dcb->cmd = (dir) ? DCB_CMD_PGRX_GCFG : DCB_CMD_PGTX_GCFG; +		return ret; -	pg_nest = nla_nest_start(dcbnl_skb, DCB_ATTR_PG_CFG); +	pg_nest = nla_nest_start(skb, DCB_ATTR_PG_CFG);  	if (!pg_nest) -		goto err; +		return -EMSGSIZE;  	if (pg_tb[DCB_PG_ATTR_TC_ALL])  		getall = 1; @@ -715,7 +597,7 @@ static int __dcbnl_pg_getcfg(struct net_device *netdev, struct nlattr **tb,  		if (ret)  			goto err_pg; -		param_nest = nla_nest_start(dcbnl_skb, i); +		param_nest = nla_nest_start(skb, i);  		if (!param_nest)  			goto err_pg; @@ -738,33 +620,33 @@ static int __dcbnl_pg_getcfg(struct net_device *netdev, struct nlattr **tb,  		if (param_tb[DCB_TC_ATTR_PARAM_PGID] ||  		    param_tb[DCB_TC_ATTR_PARAM_ALL]) { -			ret = nla_put_u8(dcbnl_skb, +			ret = nla_put_u8(skb,  			                 DCB_TC_ATTR_PARAM_PGID, pgid);  			if (ret)  				goto err_param;  		}  		if (param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING] ||  		    param_tb[DCB_TC_ATTR_PARAM_ALL]) { -			ret = nla_put_u8(dcbnl_skb, +			ret = nla_put_u8(skb,  			                 DCB_TC_ATTR_PARAM_UP_MAPPING, up_map);  			if (ret)  				goto err_param;  		}  		if (param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO] ||  		    param_tb[DCB_TC_ATTR_PARAM_ALL]) { -			ret = nla_put_u8(dcbnl_skb, +			ret = nla_put_u8(skb,  			                 DCB_TC_ATTR_PARAM_STRICT_PRIO, prio);  			if (ret)  				goto err_param;  		}  		if (param_tb[DCB_TC_ATTR_PARAM_BW_PCT] ||  		    param_tb[DCB_TC_ATTR_PARAM_ALL]) { -			ret = nla_put_u8(dcbnl_skb, DCB_TC_ATTR_PARAM_BW_PCT, +			ret = nla_put_u8(skb, DCB_TC_ATTR_PARAM_BW_PCT,  			                 tc_pct);  			if (ret)  				goto err_param;  		} -		nla_nest_end(dcbnl_skb, param_nest); +		nla_nest_end(skb, param_nest);  	}  	if (pg_tb[DCB_PG_ATTR_BW_ID_ALL]) @@ -787,80 +669,71 @@ static int __dcbnl_pg_getcfg(struct net_device *netdev, struct nlattr **tb,  			netdev->dcbnl_ops->getpgbwgcfgtx(netdev,  					i - DCB_PG_ATTR_BW_ID_0, &tc_pct);  		} -		ret = nla_put_u8(dcbnl_skb, i, tc_pct); - +		ret = nla_put_u8(skb, i, tc_pct);  		if (ret)  			goto err_pg;  	} -	nla_nest_end(dcbnl_skb, pg_nest); - -	nlmsg_end(dcbnl_skb, nlh); - -	ret = rtnl_unicast(dcbnl_skb, &init_net, pid); -	if (ret) -		goto err_out; +	nla_nest_end(skb, pg_nest);  	return 0;  err_param: -	nla_nest_cancel(dcbnl_skb, param_nest); +	nla_nest_cancel(skb, param_nest);  err_pg: -	nla_nest_cancel(dcbnl_skb, pg_nest); -nlmsg_failure: -err: -	kfree_skb(dcbnl_skb); -err_out: -	ret  = -EINVAL; -	return ret; +	nla_nest_cancel(skb, pg_nest); + +	return -EMSGSIZE;  } -static int dcbnl_pgtx_getcfg(struct net_device *netdev, struct nlattr **tb, -                             u32 pid, u32 seq, u16 flags) +static int dcbnl_pgtx_getcfg(struct net_device *netdev, struct nlmsghdr *nlh, +			     u32 seq, struct nlattr **tb, struct sk_buff *skb)  { -	return __dcbnl_pg_getcfg(netdev, tb, pid, seq, flags, 0); +	return __dcbnl_pg_getcfg(netdev, nlh, tb, skb, 0);  } -static int dcbnl_pgrx_getcfg(struct net_device *netdev, struct nlattr **tb, -                             u32 pid, u32 seq, u16 flags) +static int dcbnl_pgrx_getcfg(struct net_device *netdev, struct nlmsghdr *nlh, +			     u32 seq, struct nlattr **tb, struct sk_buff *skb)  { -	return __dcbnl_pg_getcfg(netdev, tb, pid, seq, flags, 1); +	return __dcbnl_pg_getcfg(netdev, nlh, tb, skb, 1);  } -static int dcbnl_setstate(struct net_device *netdev, struct nlattr **tb, -                          u32 pid, u32 seq, u16 flags) +static int dcbnl_setstate(struct net_device *netdev, struct nlmsghdr *nlh, +			  u32 seq, struct nlattr **tb, struct sk_buff *skb)  { -	int ret = -EINVAL;  	u8 value; -	if (!tb[DCB_ATTR_STATE] || !netdev->dcbnl_ops->setstate) -		return ret; +	if (!tb[DCB_ATTR_STATE]) +		return -EINVAL; -	value = nla_get_u8(tb[DCB_ATTR_STATE]); +	if (!netdev->dcbnl_ops->setstate) +		return -EOPNOTSUPP; -	ret = dcbnl_reply(netdev->dcbnl_ops->setstate(netdev, value), -	                  RTM_SETDCB, DCB_CMD_SSTATE, DCB_ATTR_STATE, -	                  pid, seq, flags); +	value = nla_get_u8(tb[DCB_ATTR_STATE]); -	return ret; +	return nla_put_u8(skb, DCB_ATTR_STATE, +			  netdev->dcbnl_ops->setstate(netdev, value));  } -static int dcbnl_setpfccfg(struct net_device *netdev, struct nlattr **tb, -                           u32 pid, u32 seq, u16 flags) +static int dcbnl_setpfccfg(struct net_device *netdev, struct nlmsghdr *nlh, +			   u32 seq, struct nlattr **tb, struct sk_buff *skb)  {  	struct nlattr *data[DCB_PFC_UP_ATTR_MAX + 1];  	int i; -	int ret = -EINVAL; +	int ret;  	u8 value; -	if (!tb[DCB_ATTR_PFC_CFG] || !netdev->dcbnl_ops->setpfccfg) -		return ret; +	if (!tb[DCB_ATTR_PFC_CFG]) +		return -EINVAL; + +	if (!netdev->dcbnl_ops->setpfccfg) +		return -EOPNOTSUPP;  	ret = nla_parse_nested(data, DCB_PFC_UP_ATTR_MAX,  	                       tb[DCB_ATTR_PFC_CFG],  	                       dcbnl_pfc_up_nest);  	if (ret) -		goto err; +		return ret;  	for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) {  		if (data[i] == NULL) @@ -870,49 +743,53 @@ static int dcbnl_setpfccfg(struct net_device *netdev, struct nlattr **tb,  			data[i]->nla_type - DCB_PFC_UP_ATTR_0, value);  	} -	ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_PFC_SCFG, DCB_ATTR_PFC_CFG, -	                  pid, seq, flags); -err: -	return ret; +	return nla_put_u8(skb, DCB_ATTR_PFC_CFG, 0);  } -static int dcbnl_setall(struct net_device *netdev, struct nlattr **tb, -                        u32 pid, u32 seq, u16 flags) +static int dcbnl_setall(struct net_device *netdev, struct nlmsghdr *nlh, +			u32 seq, struct nlattr **tb, struct sk_buff *skb)  { -	int ret = -EINVAL; +	int ret; -	if (!tb[DCB_ATTR_SET_ALL] || !netdev->dcbnl_ops->setall) -		return ret; +	if (!tb[DCB_ATTR_SET_ALL]) +		return -EINVAL; -	ret = dcbnl_reply(netdev->dcbnl_ops->setall(netdev), RTM_SETDCB, -	                  DCB_CMD_SET_ALL, DCB_ATTR_SET_ALL, pid, seq, flags); +	if (!netdev->dcbnl_ops->setall) +		return -EOPNOTSUPP; + +	ret = nla_put_u8(skb, DCB_ATTR_SET_ALL, +			 netdev->dcbnl_ops->setall(netdev)); +	dcbnl_cee_notify(netdev, RTM_SETDCB, DCB_CMD_SET_ALL, seq, 0);  	return ret;  } -static int __dcbnl_pg_setcfg(struct net_device *netdev, struct nlattr **tb, -                             u32 pid, u32 seq, u16 flags, int dir) +static int __dcbnl_pg_setcfg(struct net_device *netdev, struct nlmsghdr *nlh, +			     u32 seq, struct nlattr **tb, struct sk_buff *skb, +			     int dir)  {  	struct nlattr *pg_tb[DCB_PG_ATTR_MAX + 1];  	struct nlattr *param_tb[DCB_TC_ATTR_PARAM_MAX + 1]; -	int ret = -EINVAL; +	int ret;  	int i;  	u8 pgid;  	u8 up_map;  	u8 prio;  	u8 tc_pct; -	if (!tb[DCB_ATTR_PG_CFG] || -	    !netdev->dcbnl_ops->setpgtccfgtx || +	if (!tb[DCB_ATTR_PG_CFG]) +		return -EINVAL; + +	if (!netdev->dcbnl_ops->setpgtccfgtx ||  	    !netdev->dcbnl_ops->setpgtccfgrx ||  	    !netdev->dcbnl_ops->setpgbwgcfgtx ||  	    !netdev->dcbnl_ops->setpgbwgcfgrx) -		return ret; +		return -EOPNOTSUPP;  	ret = nla_parse_nested(pg_tb, DCB_PG_ATTR_MAX,  	                       tb[DCB_ATTR_PG_CFG], dcbnl_pg_nest);  	if (ret) -		goto err; +		return ret;  	for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) {  		if (!pg_tb[i]) @@ -921,7 +798,7 @@ static int __dcbnl_pg_setcfg(struct net_device *netdev, struct nlattr **tb,  		ret = nla_parse_nested(param_tb, DCB_TC_ATTR_PARAM_MAX,  		                       pg_tb[i], dcbnl_tc_param_nest);  		if (ret) -			goto err; +			return ret;  		pgid = DCB_ATTR_VALUE_UNDEFINED;  		prio = DCB_ATTR_VALUE_UNDEFINED; @@ -974,63 +851,47 @@ static int __dcbnl_pg_setcfg(struct net_device *netdev, struct nlattr **tb,  		}  	} -	ret = dcbnl_reply(0, RTM_SETDCB, -			  (dir ? DCB_CMD_PGRX_SCFG : DCB_CMD_PGTX_SCFG), -			  DCB_ATTR_PG_CFG, pid, seq, flags); - -err: -	return ret; +	return nla_put_u8(skb, DCB_ATTR_PG_CFG, 0);  } -static int dcbnl_pgtx_setcfg(struct net_device *netdev, struct nlattr **tb, -                             u32 pid, u32 seq, u16 flags) +static int dcbnl_pgtx_setcfg(struct net_device *netdev, struct nlmsghdr *nlh, +			     u32 seq, struct nlattr **tb, struct sk_buff *skb)  { -	return __dcbnl_pg_setcfg(netdev, tb, pid, seq, flags, 0); +	return __dcbnl_pg_setcfg(netdev, nlh, seq, tb, skb, 0);  } -static int dcbnl_pgrx_setcfg(struct net_device *netdev, struct nlattr **tb, -                             u32 pid, u32 seq, u16 flags) +static int dcbnl_pgrx_setcfg(struct net_device *netdev, struct nlmsghdr *nlh, +			     u32 seq, struct nlattr **tb, struct sk_buff *skb)  { -	return __dcbnl_pg_setcfg(netdev, tb, pid, seq, flags, 1); +	return __dcbnl_pg_setcfg(netdev, nlh, seq, tb, skb, 1);  } -static int dcbnl_bcn_getcfg(struct net_device *netdev, struct nlattr **tb, -                            u32 pid, u32 seq, u16 flags) +static int dcbnl_bcn_getcfg(struct net_device *netdev, struct nlmsghdr *nlh, +			    u32 seq, struct nlattr **tb, struct sk_buff *skb)  { -	struct sk_buff *dcbnl_skb; -	struct nlmsghdr *nlh; -	struct dcbmsg *dcb;  	struct nlattr *bcn_nest;  	struct nlattr *bcn_tb[DCB_BCN_ATTR_MAX + 1];  	u8 value_byte;  	u32 value_integer; -	int ret  = -EINVAL; +	int ret;  	bool getall = false;  	int i; -	if (!tb[DCB_ATTR_BCN] || !netdev->dcbnl_ops->getbcnrp || +	if (!tb[DCB_ATTR_BCN]) +		return -EINVAL; + +	if (!netdev->dcbnl_ops->getbcnrp ||  	    !netdev->dcbnl_ops->getbcncfg) -		return ret; +		return -EOPNOTSUPP;  	ret = nla_parse_nested(bcn_tb, DCB_BCN_ATTR_MAX,  	                       tb[DCB_ATTR_BCN], dcbnl_bcn_nest); -  	if (ret) -		goto err_out; - -	dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); -	if (!dcbnl_skb) -		goto err_out; - -	nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); - -	dcb = NLMSG_DATA(nlh); -	dcb->dcb_family = AF_UNSPEC; -	dcb->cmd = DCB_CMD_BCN_GCFG; +		return ret; -	bcn_nest = nla_nest_start(dcbnl_skb, DCB_ATTR_BCN); +	bcn_nest = nla_nest_start(skb, DCB_ATTR_BCN);  	if (!bcn_nest) -		goto err; +		return -EMSGSIZE;  	if (bcn_tb[DCB_BCN_ATTR_ALL])  		getall = true; @@ -1041,7 +902,7 @@ static int dcbnl_bcn_getcfg(struct net_device *netdev, struct nlattr **tb,  		netdev->dcbnl_ops->getbcnrp(netdev, i - DCB_BCN_ATTR_RP_0,  		                            &value_byte); -		ret = nla_put_u8(dcbnl_skb, i, value_byte); +		ret = nla_put_u8(skb, i, value_byte);  		if (ret)  			goto err_bcn;  	} @@ -1052,49 +913,41 @@ static int dcbnl_bcn_getcfg(struct net_device *netdev, struct nlattr **tb,  		netdev->dcbnl_ops->getbcncfg(netdev, i,  		                             &value_integer); -		ret = nla_put_u32(dcbnl_skb, i, value_integer); +		ret = nla_put_u32(skb, i, value_integer);  		if (ret)  			goto err_bcn;  	} -	nla_nest_end(dcbnl_skb, bcn_nest); - -	nlmsg_end(dcbnl_skb, nlh); - -	ret = rtnl_unicast(dcbnl_skb, &init_net, pid); -	if (ret) -		goto err_out; +	nla_nest_end(skb, bcn_nest);  	return 0;  err_bcn: -	nla_nest_cancel(dcbnl_skb, bcn_nest); -nlmsg_failure: -err: -	kfree_skb(dcbnl_skb); -err_out: -	ret  = -EINVAL; +	nla_nest_cancel(skb, bcn_nest);  	return ret;  } -static int dcbnl_bcn_setcfg(struct net_device *netdev, struct nlattr **tb, -                            u32 pid, u32 seq, u16 flags) +static int dcbnl_bcn_setcfg(struct net_device *netdev, struct nlmsghdr *nlh, +			    u32 seq, struct nlattr **tb, struct sk_buff *skb)  {  	struct nlattr *data[DCB_BCN_ATTR_MAX + 1];  	int i; -	int ret = -EINVAL; +	int ret;  	u8 value_byte;  	u32 value_int; -	if (!tb[DCB_ATTR_BCN] || !netdev->dcbnl_ops->setbcncfg || +	if (!tb[DCB_ATTR_BCN]) +		return -EINVAL; + +	if (!netdev->dcbnl_ops->setbcncfg ||  	    !netdev->dcbnl_ops->setbcnrp) -		return ret; +		return -EOPNOTSUPP;  	ret = nla_parse_nested(data, DCB_BCN_ATTR_MAX,  	                       tb[DCB_ATTR_BCN],  	                       dcbnl_pfc_up_nest);  	if (ret) -		goto err; +		return ret;  	for (i = DCB_BCN_ATTR_RP_0; i <= DCB_BCN_ATTR_RP_7; i++) {  		if (data[i] == NULL) @@ -1112,131 +965,950 @@ static int dcbnl_bcn_setcfg(struct net_device *netdev, struct nlattr **tb,  	                                     i, value_int);  	} -	ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_BCN_SCFG, DCB_ATTR_BCN, -	                  pid, seq, flags); +	return nla_put_u8(skb, DCB_ATTR_BCN, 0); +} + +static int dcbnl_build_peer_app(struct net_device *netdev, struct sk_buff* skb, +				int app_nested_type, int app_info_type, +				int app_entry_type) +{ +	struct dcb_peer_app_info info; +	struct dcb_app *table = NULL; +	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops; +	u16 app_count; +	int err; + + +	/** +	 * retrieve the peer app configuration form the driver. If the driver +	 * handlers fail exit without doing anything +	 */ +	err = ops->peer_getappinfo(netdev, &info, &app_count); +	if (!err && app_count) { +		table = kmalloc(sizeof(struct dcb_app) * app_count, GFP_KERNEL); +		if (!table) +			return -ENOMEM; + +		err = ops->peer_getapptable(netdev, table); +	} + +	if (!err) { +		u16 i; +		struct nlattr *app; + +		/** +		 * build the message, from here on the only possible failure +		 * is due to the skb size +		 */ +		err = -EMSGSIZE; + +		app = nla_nest_start(skb, app_nested_type); +		if (!app) +			goto nla_put_failure; + +		if (app_info_type && +		    nla_put(skb, app_info_type, sizeof(info), &info)) +			goto nla_put_failure; + +		for (i = 0; i < app_count; i++) { +			if (nla_put(skb, app_entry_type, sizeof(struct dcb_app), +				    &table[i])) +				goto nla_put_failure; +		} +		nla_nest_end(skb, app); +	} +	err = 0; + +nla_put_failure: +	kfree(table); +	return err; +} + +/* Handle IEEE 802.1Qaz GET commands. */ +static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev) +{ +	struct nlattr *ieee, *app; +	struct dcb_app_type *itr; +	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops; +	int dcbx; +	int err; + +	if (nla_put_string(skb, DCB_ATTR_IFNAME, netdev->name)) +		return -EMSGSIZE; + +	ieee = nla_nest_start(skb, DCB_ATTR_IEEE); +	if (!ieee) +		return -EMSGSIZE; + +	if (ops->ieee_getets) { +		struct ieee_ets ets; +		memset(&ets, 0, sizeof(ets)); +		err = ops->ieee_getets(netdev, &ets); +		if (!err && +		    nla_put(skb, DCB_ATTR_IEEE_ETS, sizeof(ets), &ets)) +			return -EMSGSIZE; +	} + +	if (ops->ieee_getmaxrate) { +		struct ieee_maxrate maxrate; +		memset(&maxrate, 0, sizeof(maxrate)); +		err = ops->ieee_getmaxrate(netdev, &maxrate); +		if (!err) { +			err = nla_put(skb, DCB_ATTR_IEEE_MAXRATE, +				      sizeof(maxrate), &maxrate); +			if (err) +				return -EMSGSIZE; +		} +	} + +	if (ops->ieee_getpfc) { +		struct ieee_pfc pfc; +		memset(&pfc, 0, sizeof(pfc)); +		err = ops->ieee_getpfc(netdev, &pfc); +		if (!err && +		    nla_put(skb, DCB_ATTR_IEEE_PFC, sizeof(pfc), &pfc)) +			return -EMSGSIZE; +	} + +	app = nla_nest_start(skb, DCB_ATTR_IEEE_APP_TABLE); +	if (!app) +		return -EMSGSIZE; + +	spin_lock(&dcb_lock); +	list_for_each_entry(itr, &dcb_app_list, list) { +		if (itr->ifindex == netdev->ifindex) { +			err = nla_put(skb, DCB_ATTR_IEEE_APP, sizeof(itr->app), +					 &itr->app); +			if (err) { +				spin_unlock(&dcb_lock); +				return -EMSGSIZE; +			} +		} +	} + +	if (netdev->dcbnl_ops->getdcbx) +		dcbx = netdev->dcbnl_ops->getdcbx(netdev); +	else +		dcbx = -EOPNOTSUPP; + +	spin_unlock(&dcb_lock); +	nla_nest_end(skb, app); + +	/* get peer info if available */ +	if (ops->ieee_peer_getets) { +		struct ieee_ets ets; +		memset(&ets, 0, sizeof(ets)); +		err = ops->ieee_peer_getets(netdev, &ets); +		if (!err && +		    nla_put(skb, DCB_ATTR_IEEE_PEER_ETS, sizeof(ets), &ets)) +			return -EMSGSIZE; +	} + +	if (ops->ieee_peer_getpfc) { +		struct ieee_pfc pfc; +		memset(&pfc, 0, sizeof(pfc)); +		err = ops->ieee_peer_getpfc(netdev, &pfc); +		if (!err && +		    nla_put(skb, DCB_ATTR_IEEE_PEER_PFC, sizeof(pfc), &pfc)) +			return -EMSGSIZE; +	} + +	if (ops->peer_getappinfo && ops->peer_getapptable) { +		err = dcbnl_build_peer_app(netdev, skb, +					   DCB_ATTR_IEEE_PEER_APP, +					   DCB_ATTR_IEEE_APP_UNSPEC, +					   DCB_ATTR_IEEE_APP); +		if (err) +			return -EMSGSIZE; +	} + +	nla_nest_end(skb, ieee); +	if (dcbx >= 0) { +		err = nla_put_u8(skb, DCB_ATTR_DCBX, dcbx); +		if (err) +			return -EMSGSIZE; +	} + +	return 0; +} + +static int dcbnl_cee_pg_fill(struct sk_buff *skb, struct net_device *dev, +			     int dir) +{ +	u8 pgid, up_map, prio, tc_pct; +	const struct dcbnl_rtnl_ops *ops = dev->dcbnl_ops; +	int i = dir ? DCB_ATTR_CEE_TX_PG : DCB_ATTR_CEE_RX_PG; +	struct nlattr *pg = nla_nest_start(skb, i); + +	if (!pg) +		return -EMSGSIZE; + +	for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) { +		struct nlattr *tc_nest = nla_nest_start(skb, i); + +		if (!tc_nest) +			return -EMSGSIZE; + +		pgid = DCB_ATTR_VALUE_UNDEFINED; +		prio = DCB_ATTR_VALUE_UNDEFINED; +		tc_pct = DCB_ATTR_VALUE_UNDEFINED; +		up_map = DCB_ATTR_VALUE_UNDEFINED; + +		if (!dir) +			ops->getpgtccfgrx(dev, i - DCB_PG_ATTR_TC_0, +					  &prio, &pgid, &tc_pct, &up_map); +		else +			ops->getpgtccfgtx(dev, i - DCB_PG_ATTR_TC_0, +					  &prio, &pgid, &tc_pct, &up_map); + +		if (nla_put_u8(skb, DCB_TC_ATTR_PARAM_PGID, pgid) || +		    nla_put_u8(skb, DCB_TC_ATTR_PARAM_UP_MAPPING, up_map) || +		    nla_put_u8(skb, DCB_TC_ATTR_PARAM_STRICT_PRIO, prio) || +		    nla_put_u8(skb, DCB_TC_ATTR_PARAM_BW_PCT, tc_pct)) +			return -EMSGSIZE; +		nla_nest_end(skb, tc_nest); +	} + +	for (i = DCB_PG_ATTR_BW_ID_0; i <= DCB_PG_ATTR_BW_ID_7; i++) { +		tc_pct = DCB_ATTR_VALUE_UNDEFINED; + +		if (!dir) +			ops->getpgbwgcfgrx(dev, i - DCB_PG_ATTR_BW_ID_0, +					   &tc_pct); +		else +			ops->getpgbwgcfgtx(dev, i - DCB_PG_ATTR_BW_ID_0, +					   &tc_pct); +		if (nla_put_u8(skb, i, tc_pct)) +			return -EMSGSIZE; +	} +	nla_nest_end(skb, pg); +	return 0; +} + +static int dcbnl_cee_fill(struct sk_buff *skb, struct net_device *netdev) +{ +	struct nlattr *cee, *app; +	struct dcb_app_type *itr; +	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops; +	int dcbx, i, err = -EMSGSIZE; +	u8 value; + +	if (nla_put_string(skb, DCB_ATTR_IFNAME, netdev->name)) +		goto nla_put_failure; +	cee = nla_nest_start(skb, DCB_ATTR_CEE); +	if (!cee) +		goto nla_put_failure; + +	/* local pg */ +	if (ops->getpgtccfgtx && ops->getpgbwgcfgtx) { +		err = dcbnl_cee_pg_fill(skb, netdev, 1); +		if (err) +			goto nla_put_failure; +	} + +	if (ops->getpgtccfgrx && ops->getpgbwgcfgrx) { +		err = dcbnl_cee_pg_fill(skb, netdev, 0); +		if (err) +			goto nla_put_failure; +	} + +	/* local pfc */ +	if (ops->getpfccfg) { +		struct nlattr *pfc_nest = nla_nest_start(skb, DCB_ATTR_CEE_PFC); + +		if (!pfc_nest) +			goto nla_put_failure; + +		for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) { +			ops->getpfccfg(netdev, i - DCB_PFC_UP_ATTR_0, &value); +			if (nla_put_u8(skb, i, value)) +				goto nla_put_failure; +		} +		nla_nest_end(skb, pfc_nest); +	} + +	/* local app */ +	spin_lock(&dcb_lock); +	app = nla_nest_start(skb, DCB_ATTR_CEE_APP_TABLE); +	if (!app) +		goto dcb_unlock; + +	list_for_each_entry(itr, &dcb_app_list, list) { +		if (itr->ifindex == netdev->ifindex) { +			struct nlattr *app_nest = nla_nest_start(skb, +								 DCB_ATTR_APP); +			if (!app_nest) +				goto dcb_unlock; + +			err = nla_put_u8(skb, DCB_APP_ATTR_IDTYPE, +					 itr->app.selector); +			if (err) +				goto dcb_unlock; + +			err = nla_put_u16(skb, DCB_APP_ATTR_ID, +					  itr->app.protocol); +			if (err) +				goto dcb_unlock; + +			err = nla_put_u8(skb, DCB_APP_ATTR_PRIORITY, +					 itr->app.priority); +			if (err) +				goto dcb_unlock; + +			nla_nest_end(skb, app_nest); +		} +	} +	nla_nest_end(skb, app); + +	if (netdev->dcbnl_ops->getdcbx) +		dcbx = netdev->dcbnl_ops->getdcbx(netdev); +	else +		dcbx = -EOPNOTSUPP; + +	spin_unlock(&dcb_lock); + +	/* features flags */ +	if (ops->getfeatcfg) { +		struct nlattr *feat = nla_nest_start(skb, DCB_ATTR_CEE_FEAT); +		if (!feat) +			goto nla_put_failure; + +		for (i = DCB_FEATCFG_ATTR_ALL + 1; i <= DCB_FEATCFG_ATTR_MAX; +		     i++) +			if (!ops->getfeatcfg(netdev, i, &value) && +			    nla_put_u8(skb, i, value)) +				goto nla_put_failure; + +		nla_nest_end(skb, feat); +	} + +	/* peer info if available */ +	if (ops->cee_peer_getpg) { +		struct cee_pg pg; +		memset(&pg, 0, sizeof(pg)); +		err = ops->cee_peer_getpg(netdev, &pg); +		if (!err && +		    nla_put(skb, DCB_ATTR_CEE_PEER_PG, sizeof(pg), &pg)) +			goto nla_put_failure; +	} + +	if (ops->cee_peer_getpfc) { +		struct cee_pfc pfc; +		memset(&pfc, 0, sizeof(pfc)); +		err = ops->cee_peer_getpfc(netdev, &pfc); +		if (!err && +		    nla_put(skb, DCB_ATTR_CEE_PEER_PFC, sizeof(pfc), &pfc)) +			goto nla_put_failure; +	} + +	if (ops->peer_getappinfo && ops->peer_getapptable) { +		err = dcbnl_build_peer_app(netdev, skb, +					   DCB_ATTR_CEE_PEER_APP_TABLE, +					   DCB_ATTR_CEE_PEER_APP_INFO, +					   DCB_ATTR_CEE_PEER_APP); +		if (err) +			goto nla_put_failure; +	} +	nla_nest_end(skb, cee); + +	/* DCBX state */ +	if (dcbx >= 0) { +		err = nla_put_u8(skb, DCB_ATTR_DCBX, dcbx); +		if (err) +			goto nla_put_failure; +	} +	return 0; + +dcb_unlock: +	spin_unlock(&dcb_lock); +nla_put_failure: +	return err; +} + +static int dcbnl_notify(struct net_device *dev, int event, int cmd, +			u32 seq, u32 portid, int dcbx_ver) +{ +	struct net *net = dev_net(dev); +	struct sk_buff *skb; +	struct nlmsghdr *nlh; +	const struct dcbnl_rtnl_ops *ops = dev->dcbnl_ops; +	int err; + +	if (!ops) +		return -EOPNOTSUPP; + +	skb = dcbnl_newmsg(event, cmd, portid, seq, 0, &nlh); +	if (!skb) +		return -ENOBUFS; + +	if (dcbx_ver == DCB_CAP_DCBX_VER_IEEE) +		err = dcbnl_ieee_fill(skb, dev); +	else +		err = dcbnl_cee_fill(skb, dev); + +	if (err < 0) { +		/* Report error to broadcast listeners */ +		nlmsg_free(skb); +		rtnl_set_sk_err(net, RTNLGRP_DCB, err); +	} else { +		/* End nlmsg and notify broadcast listeners */ +		nlmsg_end(skb, nlh); +		rtnl_notify(skb, net, 0, RTNLGRP_DCB, NULL, GFP_KERNEL); +	} + +	return err; +} + +int dcbnl_ieee_notify(struct net_device *dev, int event, int cmd, +		      u32 seq, u32 portid) +{ +	return dcbnl_notify(dev, event, cmd, seq, portid, DCB_CAP_DCBX_VER_IEEE); +} +EXPORT_SYMBOL(dcbnl_ieee_notify); + +int dcbnl_cee_notify(struct net_device *dev, int event, int cmd, +		     u32 seq, u32 portid) +{ +	return dcbnl_notify(dev, event, cmd, seq, portid, DCB_CAP_DCBX_VER_CEE); +} +EXPORT_SYMBOL(dcbnl_cee_notify); + +/* Handle IEEE 802.1Qaz SET commands. If any requested operation can not + * be completed the entire msg is aborted and error value is returned. + * No attempt is made to reconcile the case where only part of the + * cmd can be completed. + */ +static int dcbnl_ieee_set(struct net_device *netdev, struct nlmsghdr *nlh, +			  u32 seq, struct nlattr **tb, struct sk_buff *skb) +{ +	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops; +	struct nlattr *ieee[DCB_ATTR_IEEE_MAX + 1]; +	int err; + +	if (!ops) +		return -EOPNOTSUPP; + +	if (!tb[DCB_ATTR_IEEE]) +		return -EINVAL; + +	err = nla_parse_nested(ieee, DCB_ATTR_IEEE_MAX, +			       tb[DCB_ATTR_IEEE], dcbnl_ieee_policy); +	if (err) +		return err; + +	if (ieee[DCB_ATTR_IEEE_ETS] && ops->ieee_setets) { +		struct ieee_ets *ets = nla_data(ieee[DCB_ATTR_IEEE_ETS]); +		err = ops->ieee_setets(netdev, ets); +		if (err) +			goto err; +	} + +	if (ieee[DCB_ATTR_IEEE_MAXRATE] && ops->ieee_setmaxrate) { +		struct ieee_maxrate *maxrate = +			nla_data(ieee[DCB_ATTR_IEEE_MAXRATE]); +		err = ops->ieee_setmaxrate(netdev, maxrate); +		if (err) +			goto err; +	} + +	if (ieee[DCB_ATTR_IEEE_PFC] && ops->ieee_setpfc) { +		struct ieee_pfc *pfc = nla_data(ieee[DCB_ATTR_IEEE_PFC]); +		err = ops->ieee_setpfc(netdev, pfc); +		if (err) +			goto err; +	} + +	if (ieee[DCB_ATTR_IEEE_APP_TABLE]) { +		struct nlattr *attr; +		int rem; + +		nla_for_each_nested(attr, ieee[DCB_ATTR_IEEE_APP_TABLE], rem) { +			struct dcb_app *app_data; +			if (nla_type(attr) != DCB_ATTR_IEEE_APP) +				continue; +			app_data = nla_data(attr); +			if (ops->ieee_setapp) +				err = ops->ieee_setapp(netdev, app_data); +			else +				err = dcb_ieee_setapp(netdev, app_data); +			if (err) +				goto err; +		} +	} + +err: +	err = nla_put_u8(skb, DCB_ATTR_IEEE, err); +	dcbnl_ieee_notify(netdev, RTM_SETDCB, DCB_CMD_IEEE_SET, seq, 0); +	return err; +} + +static int dcbnl_ieee_get(struct net_device *netdev, struct nlmsghdr *nlh, +			  u32 seq, struct nlattr **tb, struct sk_buff *skb) +{ +	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops; + +	if (!ops) +		return -EOPNOTSUPP; + +	return dcbnl_ieee_fill(skb, netdev); +} + +static int dcbnl_ieee_del(struct net_device *netdev, struct nlmsghdr *nlh, +			  u32 seq, struct nlattr **tb, struct sk_buff *skb) +{ +	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops; +	struct nlattr *ieee[DCB_ATTR_IEEE_MAX + 1]; +	int err; + +	if (!ops) +		return -EOPNOTSUPP; + +	if (!tb[DCB_ATTR_IEEE]) +		return -EINVAL; + +	err = nla_parse_nested(ieee, DCB_ATTR_IEEE_MAX, +			       tb[DCB_ATTR_IEEE], dcbnl_ieee_policy); +	if (err) +		return err; + +	if (ieee[DCB_ATTR_IEEE_APP_TABLE]) { +		struct nlattr *attr; +		int rem; + +		nla_for_each_nested(attr, ieee[DCB_ATTR_IEEE_APP_TABLE], rem) { +			struct dcb_app *app_data; + +			if (nla_type(attr) != DCB_ATTR_IEEE_APP) +				continue; +			app_data = nla_data(attr); +			if (ops->ieee_delapp) +				err = ops->ieee_delapp(netdev, app_data); +			else +				err = dcb_ieee_delapp(netdev, app_data); +			if (err) +				goto err; +		} +	} + +err: +	err = nla_put_u8(skb, DCB_ATTR_IEEE, err); +	dcbnl_ieee_notify(netdev, RTM_SETDCB, DCB_CMD_IEEE_DEL, seq, 0); +	return err; +} + + +/* DCBX configuration */ +static int dcbnl_getdcbx(struct net_device *netdev, struct nlmsghdr *nlh, +			 u32 seq, struct nlattr **tb, struct sk_buff *skb) +{ +	if (!netdev->dcbnl_ops->getdcbx) +		return -EOPNOTSUPP; + +	return nla_put_u8(skb, DCB_ATTR_DCBX, +			  netdev->dcbnl_ops->getdcbx(netdev)); +} + +static int dcbnl_setdcbx(struct net_device *netdev, struct nlmsghdr *nlh, +			 u32 seq, struct nlattr **tb, struct sk_buff *skb) +{ +	u8 value; + +	if (!netdev->dcbnl_ops->setdcbx) +		return -EOPNOTSUPP; + +	if (!tb[DCB_ATTR_DCBX]) +		return -EINVAL; + +	value = nla_get_u8(tb[DCB_ATTR_DCBX]); + +	return nla_put_u8(skb, DCB_ATTR_DCBX, +			  netdev->dcbnl_ops->setdcbx(netdev, value)); +} + +static int dcbnl_getfeatcfg(struct net_device *netdev, struct nlmsghdr *nlh, +			    u32 seq, struct nlattr **tb, struct sk_buff *skb) +{ +	struct nlattr *data[DCB_FEATCFG_ATTR_MAX + 1], *nest; +	u8 value; +	int ret, i; +	int getall = 0; + +	if (!netdev->dcbnl_ops->getfeatcfg) +		return -EOPNOTSUPP; + +	if (!tb[DCB_ATTR_FEATCFG]) +		return -EINVAL; + +	ret = nla_parse_nested(data, DCB_FEATCFG_ATTR_MAX, tb[DCB_ATTR_FEATCFG], +			       dcbnl_featcfg_nest); +	if (ret) +		return ret; + +	nest = nla_nest_start(skb, DCB_ATTR_FEATCFG); +	if (!nest) +		return -EMSGSIZE; + +	if (data[DCB_FEATCFG_ATTR_ALL]) +		getall = 1; + +	for (i = DCB_FEATCFG_ATTR_ALL+1; i <= DCB_FEATCFG_ATTR_MAX; i++) { +		if (!getall && !data[i]) +			continue; + +		ret = netdev->dcbnl_ops->getfeatcfg(netdev, i, &value); +		if (!ret) +			ret = nla_put_u8(skb, i, value); + +		if (ret) { +			nla_nest_cancel(skb, nest); +			goto nla_put_failure; +		} +	} +	nla_nest_end(skb, nest); + +nla_put_failure: +	return ret; +} + +static int dcbnl_setfeatcfg(struct net_device *netdev, struct nlmsghdr *nlh, +			    u32 seq, struct nlattr **tb, struct sk_buff *skb) +{ +	struct nlattr *data[DCB_FEATCFG_ATTR_MAX + 1]; +	int ret, i; +	u8 value; + +	if (!netdev->dcbnl_ops->setfeatcfg) +		return -ENOTSUPP; + +	if (!tb[DCB_ATTR_FEATCFG]) +		return -EINVAL; + +	ret = nla_parse_nested(data, DCB_FEATCFG_ATTR_MAX, tb[DCB_ATTR_FEATCFG], +			       dcbnl_featcfg_nest); + +	if (ret) +		goto err; + +	for (i = DCB_FEATCFG_ATTR_ALL+1; i <= DCB_FEATCFG_ATTR_MAX; i++) { +		if (data[i] == NULL) +			continue; + +		value = nla_get_u8(data[i]); + +		ret = netdev->dcbnl_ops->setfeatcfg(netdev, i, value); + +		if (ret) +			goto err; +	}  err: +	ret = nla_put_u8(skb, DCB_ATTR_FEATCFG, ret); +  	return ret;  } -static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) +/* Handle CEE DCBX GET commands. */ +static int dcbnl_cee_get(struct net_device *netdev, struct nlmsghdr *nlh, +			 u32 seq, struct nlattr **tb, struct sk_buff *skb) +{ +	const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops; + +	if (!ops) +		return -EOPNOTSUPP; + +	return dcbnl_cee_fill(skb, netdev); +} + +struct reply_func { +	/* reply netlink message type */ +	int	type; + +	/* function to fill message contents */ +	int   (*cb)(struct net_device *, struct nlmsghdr *, u32, +		    struct nlattr **, struct sk_buff *); +}; + +static const struct reply_func reply_funcs[DCB_CMD_MAX+1] = { +	[DCB_CMD_GSTATE]	= { RTM_GETDCB, dcbnl_getstate }, +	[DCB_CMD_SSTATE]	= { RTM_SETDCB, dcbnl_setstate }, +	[DCB_CMD_PFC_GCFG]	= { RTM_GETDCB, dcbnl_getpfccfg }, +	[DCB_CMD_PFC_SCFG]	= { RTM_SETDCB, dcbnl_setpfccfg }, +	[DCB_CMD_GPERM_HWADDR]	= { RTM_GETDCB, dcbnl_getperm_hwaddr }, +	[DCB_CMD_GCAP]		= { RTM_GETDCB, dcbnl_getcap }, +	[DCB_CMD_GNUMTCS]	= { RTM_GETDCB, dcbnl_getnumtcs }, +	[DCB_CMD_SNUMTCS]	= { RTM_SETDCB, dcbnl_setnumtcs }, +	[DCB_CMD_PFC_GSTATE]	= { RTM_GETDCB, dcbnl_getpfcstate }, +	[DCB_CMD_PFC_SSTATE]	= { RTM_SETDCB, dcbnl_setpfcstate }, +	[DCB_CMD_GAPP]		= { RTM_GETDCB, dcbnl_getapp }, +	[DCB_CMD_SAPP]		= { RTM_SETDCB, dcbnl_setapp }, +	[DCB_CMD_PGTX_GCFG]	= { RTM_GETDCB, dcbnl_pgtx_getcfg }, +	[DCB_CMD_PGTX_SCFG]	= { RTM_SETDCB, dcbnl_pgtx_setcfg }, +	[DCB_CMD_PGRX_GCFG]	= { RTM_GETDCB, dcbnl_pgrx_getcfg }, +	[DCB_CMD_PGRX_SCFG]	= { RTM_SETDCB, dcbnl_pgrx_setcfg }, +	[DCB_CMD_SET_ALL]	= { RTM_SETDCB, dcbnl_setall }, +	[DCB_CMD_BCN_GCFG]	= { RTM_GETDCB, dcbnl_bcn_getcfg }, +	[DCB_CMD_BCN_SCFG]	= { RTM_SETDCB, dcbnl_bcn_setcfg }, +	[DCB_CMD_IEEE_GET]	= { RTM_GETDCB, dcbnl_ieee_get }, +	[DCB_CMD_IEEE_SET]	= { RTM_SETDCB, dcbnl_ieee_set }, +	[DCB_CMD_IEEE_DEL]	= { RTM_SETDCB, dcbnl_ieee_del }, +	[DCB_CMD_GDCBX]		= { RTM_GETDCB, dcbnl_getdcbx }, +	[DCB_CMD_SDCBX]		= { RTM_SETDCB, dcbnl_setdcbx }, +	[DCB_CMD_GFEATCFG]	= { RTM_GETDCB, dcbnl_getfeatcfg }, +	[DCB_CMD_SFEATCFG]	= { RTM_SETDCB, dcbnl_setfeatcfg }, +	[DCB_CMD_CEE_GET]	= { RTM_GETDCB, dcbnl_cee_get }, +}; + +static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh)  {  	struct net *net = sock_net(skb->sk);  	struct net_device *netdev; -	struct dcbmsg  *dcb = (struct dcbmsg *)NLMSG_DATA(nlh); +	struct dcbmsg *dcb = nlmsg_data(nlh);  	struct nlattr *tb[DCB_ATTR_MAX + 1]; -	u32 pid = skb ? NETLINK_CB(skb).pid : 0; +	u32 portid = skb ? NETLINK_CB(skb).portid : 0;  	int ret = -EINVAL; +	struct sk_buff *reply_skb; +	struct nlmsghdr *reply_nlh = NULL; +	const struct reply_func *fn; -	if (!net_eq(net, &init_net)) -		return -EINVAL; +	if ((nlh->nlmsg_type == RTM_SETDCB) && !netlink_capable(skb, CAP_NET_ADMIN)) +		return -EPERM;  	ret = nlmsg_parse(nlh, sizeof(*dcb), tb, DCB_ATTR_MAX,  			  dcbnl_rtnl_policy);  	if (ret < 0)  		return ret; +	if (dcb->cmd > DCB_CMD_MAX) +		return -EINVAL; + +	/* check if a reply function has been defined for the command */ +	fn = &reply_funcs[dcb->cmd]; +	if (!fn->cb) +		return -EOPNOTSUPP; +  	if (!tb[DCB_ATTR_IFNAME])  		return -EINVAL; -	netdev = dev_get_by_name(&init_net, nla_data(tb[DCB_ATTR_IFNAME])); +	netdev = __dev_get_by_name(net, nla_data(tb[DCB_ATTR_IFNAME]));  	if (!netdev) -		return -EINVAL; +		return -ENODEV;  	if (!netdev->dcbnl_ops) -		goto errout; +		return -EOPNOTSUPP; -	switch (dcb->cmd) { -	case DCB_CMD_GSTATE: -		ret = dcbnl_getstate(netdev, tb, pid, nlh->nlmsg_seq, -		                     nlh->nlmsg_flags); -		goto out; -	case DCB_CMD_PFC_GCFG: -		ret = dcbnl_getpfccfg(netdev, tb, pid, nlh->nlmsg_seq, -		                      nlh->nlmsg_flags); -		goto out; -	case DCB_CMD_GPERM_HWADDR: -		ret = dcbnl_getperm_hwaddr(netdev, tb, pid, nlh->nlmsg_seq, -		                           nlh->nlmsg_flags); -		goto out; -	case DCB_CMD_PGTX_GCFG: -		ret = dcbnl_pgtx_getcfg(netdev, tb, pid, nlh->nlmsg_seq, -		                        nlh->nlmsg_flags); -		goto out; -	case DCB_CMD_PGRX_GCFG: -		ret = dcbnl_pgrx_getcfg(netdev, tb, pid, nlh->nlmsg_seq, -		                        nlh->nlmsg_flags); -		goto out; -	case DCB_CMD_BCN_GCFG: -		ret = dcbnl_bcn_getcfg(netdev, tb, pid, nlh->nlmsg_seq, -		                       nlh->nlmsg_flags); -		goto out; -	case DCB_CMD_SSTATE: -		ret = dcbnl_setstate(netdev, tb, pid, nlh->nlmsg_seq, -		                     nlh->nlmsg_flags); -		goto out; -	case DCB_CMD_PFC_SCFG: -		ret = dcbnl_setpfccfg(netdev, tb, pid, nlh->nlmsg_seq, -		                      nlh->nlmsg_flags); -		goto out; +	reply_skb = dcbnl_newmsg(fn->type, dcb->cmd, portid, nlh->nlmsg_seq, +				 nlh->nlmsg_flags, &reply_nlh); +	if (!reply_skb) +		return -ENOBUFS; -	case DCB_CMD_SET_ALL: -		ret = dcbnl_setall(netdev, tb, pid, nlh->nlmsg_seq, -		                   nlh->nlmsg_flags); -		goto out; -	case DCB_CMD_PGTX_SCFG: -		ret = dcbnl_pgtx_setcfg(netdev, tb, pid, nlh->nlmsg_seq, -		                        nlh->nlmsg_flags); -		goto out; -	case DCB_CMD_PGRX_SCFG: -		ret = dcbnl_pgrx_setcfg(netdev, tb, pid, nlh->nlmsg_seq, -		                        nlh->nlmsg_flags); -		goto out; -	case DCB_CMD_GCAP: -		ret = dcbnl_getcap(netdev, tb, pid, nlh->nlmsg_seq, -		                   nlh->nlmsg_flags); -		goto out; -	case DCB_CMD_GNUMTCS: -		ret = dcbnl_getnumtcs(netdev, tb, pid, nlh->nlmsg_seq, -		                      nlh->nlmsg_flags); -		goto out; -	case DCB_CMD_SNUMTCS: -		ret = dcbnl_setnumtcs(netdev, tb, pid, nlh->nlmsg_seq, -		                      nlh->nlmsg_flags); -		goto out; -	case DCB_CMD_PFC_GSTATE: -		ret = dcbnl_getpfcstate(netdev, tb, pid, nlh->nlmsg_seq, -		                        nlh->nlmsg_flags); -		goto out; -	case DCB_CMD_PFC_SSTATE: -		ret = dcbnl_setpfcstate(netdev, tb, pid, nlh->nlmsg_seq, -		                        nlh->nlmsg_flags); -		goto out; -	case DCB_CMD_BCN_SCFG: -		ret = dcbnl_bcn_setcfg(netdev, tb, pid, nlh->nlmsg_seq, -		                       nlh->nlmsg_flags); +	ret = fn->cb(netdev, nlh, nlh->nlmsg_seq, tb, reply_skb); +	if (ret < 0) { +		nlmsg_free(reply_skb);  		goto out; -	case DCB_CMD_GAPP: -		ret = dcbnl_getapp(netdev, tb, pid, nlh->nlmsg_seq, -		                   nlh->nlmsg_flags); +	} + +	nlmsg_end(reply_skb, reply_nlh); + +	ret = rtnl_unicast(reply_skb, net, portid); +out: +	return ret; +} + +static struct dcb_app_type *dcb_app_lookup(const struct dcb_app *app, +					   int ifindex, int prio) +{ +	struct dcb_app_type *itr; + +	list_for_each_entry(itr, &dcb_app_list, list) { +		if (itr->app.selector == app->selector && +		    itr->app.protocol == app->protocol && +		    itr->ifindex == ifindex && +		    (!prio || itr->app.priority == prio)) +			return itr; +	} + +	return NULL; +} + +static int dcb_app_add(const struct dcb_app *app, int ifindex) +{ +	struct dcb_app_type *entry; + +	entry = kmalloc(sizeof(*entry), GFP_ATOMIC); +	if (!entry) +		return -ENOMEM; + +	memcpy(&entry->app, app, sizeof(*app)); +	entry->ifindex = ifindex; +	list_add(&entry->list, &dcb_app_list); + +	return 0; +} + +/** + * dcb_getapp - retrieve the DCBX application user priority + * + * On success returns a non-zero 802.1p user priority bitmap + * otherwise returns 0 as the invalid user priority bitmap to + * indicate an error. + */ +u8 dcb_getapp(struct net_device *dev, struct dcb_app *app) +{ +	struct dcb_app_type *itr; +	u8 prio = 0; + +	spin_lock(&dcb_lock); +	if ((itr = dcb_app_lookup(app, dev->ifindex, 0))) +		prio = itr->app.priority; +	spin_unlock(&dcb_lock); + +	return prio; +} +EXPORT_SYMBOL(dcb_getapp); + +/** + * dcb_setapp - add CEE dcb application data to app list + * + * Priority 0 is an invalid priority in CEE spec. This routine + * removes applications from the app list if the priority is + * set to zero. + */ +int dcb_setapp(struct net_device *dev, struct dcb_app *new) +{ +	struct dcb_app_type *itr; +	struct dcb_app_type event; +	int err = 0; + +	event.ifindex = dev->ifindex; +	memcpy(&event.app, new, sizeof(event.app)); +	if (dev->dcbnl_ops->getdcbx) +		event.dcbx = dev->dcbnl_ops->getdcbx(dev); + +	spin_lock(&dcb_lock); +	/* Search for existing match and replace */ +	if ((itr = dcb_app_lookup(new, dev->ifindex, 0))) { +		if (new->priority) +			itr->app.priority = new->priority; +		else { +			list_del(&itr->list); +			kfree(itr); +		}  		goto out; -	case DCB_CMD_SAPP: -		ret = dcbnl_setapp(netdev, tb, pid, nlh->nlmsg_seq, -		                   nlh->nlmsg_flags); +	} +	/* App type does not exist add new application type */ +	if (new->priority) +		err = dcb_app_add(new, dev->ifindex); +out: +	spin_unlock(&dcb_lock); +	if (!err) +		call_dcbevent_notifiers(DCB_APP_EVENT, &event); +	return err; +} +EXPORT_SYMBOL(dcb_setapp); + +/** + * dcb_ieee_getapp_mask - retrieve the IEEE DCB application priority + * + * Helper routine which on success returns a non-zero 802.1Qaz user + * priority bitmap otherwise returns 0 to indicate the dcb_app was + * not found in APP list. + */ +u8 dcb_ieee_getapp_mask(struct net_device *dev, struct dcb_app *app) +{ +	struct dcb_app_type *itr; +	u8 prio = 0; + +	spin_lock(&dcb_lock); +	if ((itr = dcb_app_lookup(app, dev->ifindex, 0))) +		prio |= 1 << itr->app.priority; +	spin_unlock(&dcb_lock); + +	return prio; +} +EXPORT_SYMBOL(dcb_ieee_getapp_mask); + +/** + * dcb_ieee_setapp - add IEEE dcb application data to app list + * + * This adds Application data to the list. Multiple application + * entries may exists for the same selector and protocol as long + * as the priorities are different. + */ +int dcb_ieee_setapp(struct net_device *dev, struct dcb_app *new) +{ +	struct dcb_app_type event; +	int err = 0; + +	event.ifindex = dev->ifindex; +	memcpy(&event.app, new, sizeof(event.app)); +	if (dev->dcbnl_ops->getdcbx) +		event.dcbx = dev->dcbnl_ops->getdcbx(dev); + +	spin_lock(&dcb_lock); +	/* Search for existing match and abort if found */ +	if (dcb_app_lookup(new, dev->ifindex, new->priority)) { +		err = -EEXIST;  		goto out; -	default: -		goto errout;  	} -errout: -	ret = -EINVAL; + +	err = dcb_app_add(new, dev->ifindex);  out: -	dev_put(netdev); -	return ret; +	spin_unlock(&dcb_lock); +	if (!err) +		call_dcbevent_notifiers(DCB_APP_EVENT, &event); +	return err; +} +EXPORT_SYMBOL(dcb_ieee_setapp); + +/** + * dcb_ieee_delapp - delete IEEE dcb application data from list + * + * This removes a matching APP data from the APP list + */ +int dcb_ieee_delapp(struct net_device *dev, struct dcb_app *del) +{ +	struct dcb_app_type *itr; +	struct dcb_app_type event; +	int err = -ENOENT; + +	event.ifindex = dev->ifindex; +	memcpy(&event.app, del, sizeof(event.app)); +	if (dev->dcbnl_ops->getdcbx) +		event.dcbx = dev->dcbnl_ops->getdcbx(dev); + +	spin_lock(&dcb_lock); +	/* Search for existing match and remove it. */ +	if ((itr = dcb_app_lookup(del, dev->ifindex, del->priority))) { +		list_del(&itr->list); +		kfree(itr); +		err = 0; +	} + +	spin_unlock(&dcb_lock); +	if (!err) +		call_dcbevent_notifiers(DCB_APP_EVENT, &event); +	return err; +} +EXPORT_SYMBOL(dcb_ieee_delapp); + +static void dcb_flushapp(void) +{ +	struct dcb_app_type *app; +	struct dcb_app_type *tmp; + +	spin_lock(&dcb_lock); +	list_for_each_entry_safe(app, tmp, &dcb_app_list, list) { +		list_del(&app->list); +		kfree(app); +	} +	spin_unlock(&dcb_lock);  }  static int __init dcbnl_init(void)  { -	rtnl_register(PF_UNSPEC, RTM_GETDCB, dcb_doit, NULL); -	rtnl_register(PF_UNSPEC, RTM_SETDCB, dcb_doit, NULL); +	INIT_LIST_HEAD(&dcb_app_list); + +	rtnl_register(PF_UNSPEC, RTM_GETDCB, dcb_doit, NULL, NULL); +	rtnl_register(PF_UNSPEC, RTM_SETDCB, dcb_doit, NULL, NULL);  	return 0;  } @@ -1246,7 +1918,6 @@ static void __exit dcbnl_exit(void)  {  	rtnl_unregister(PF_UNSPEC, RTM_GETDCB);  	rtnl_unregister(PF_UNSPEC, RTM_SETDCB); +	dcb_flushapp();  }  module_exit(dcbnl_exit); - -  | 
