*/
MODULE_AUTHOR("Lucy Liu, <lucy.liu@intel.com>");
-MODULE_DESCRIPTION("Data Center Bridging generic netlink interface");
+MODULE_DESCRIPTION("Data Center Bridging netlink interface");
MODULE_LICENSE("GPL");
/**************** DCB attribute policies *************************************/
/* DCB netlink attributes policy */
static struct nla_policy dcbnl_rtnl_policy[DCB_ATTR_MAX + 1] = {
- [DCB_ATTR_IFNAME] = {.type = NLA_STRING, .len = IFNAMSIZ - 1},
- [DCB_ATTR_STATE] = {.type = NLA_U8},
- [DCB_ATTR_PFC_CFG] = {.type = NLA_NESTED},
- [DCB_ATTR_PG_CFG] = {.type = NLA_NESTED},
- [DCB_ATTR_SET_ALL] = {.type = NLA_U8},
+ [DCB_ATTR_IFNAME] = {.type = NLA_NUL_STRING, .len = IFNAMSIZ - 1},
+ [DCB_ATTR_STATE] = {.type = NLA_U8},
+ [DCB_ATTR_PFC_CFG] = {.type = NLA_NESTED},
+ [DCB_ATTR_PG_CFG] = {.type = NLA_NESTED},
+ [DCB_ATTR_SET_ALL] = {.type = NLA_U8},
[DCB_ATTR_PERM_HWADDR] = {.type = NLA_FLAG},
- [DCB_ATTR_CAP] = {.type = NLA_NESTED},
+ [DCB_ATTR_CAP] = {.type = NLA_NESTED},
+ [DCB_ATTR_PFC_STATE] = {.type = NLA_U8},
+ [DCB_ATTR_BCN] = {.type = NLA_NESTED},
+ [DCB_ATTR_APP] = {.type = NLA_NESTED},
};
/* DCB priority flow control to User Priority nested attributes */
[DCB_NUMTCS_ATTR_PFC] = {.type = NLA_U8},
};
+/* DCB BCN nested attributes. */
+static struct nla_policy dcbnl_bcn_nest[DCB_BCN_ATTR_MAX + 1] = {
+ [DCB_BCN_ATTR_RP_0] = {.type = NLA_U8},
+ [DCB_BCN_ATTR_RP_1] = {.type = NLA_U8},
+ [DCB_BCN_ATTR_RP_2] = {.type = NLA_U8},
+ [DCB_BCN_ATTR_RP_3] = {.type = NLA_U8},
+ [DCB_BCN_ATTR_RP_4] = {.type = NLA_U8},
+ [DCB_BCN_ATTR_RP_5] = {.type = NLA_U8},
+ [DCB_BCN_ATTR_RP_6] = {.type = NLA_U8},
+ [DCB_BCN_ATTR_RP_7] = {.type = NLA_U8},
+ [DCB_BCN_ATTR_RP_ALL] = {.type = NLA_FLAG},
+ [DCB_BCN_ATTR_BCNA_0] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_BCNA_1] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_ALPHA] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_BETA] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_GD] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_GI] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_TMAX] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_TD] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_RMIN] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_W] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_RD] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_RU] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_WRTT] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_RI] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_C] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_ALL] = {.type = NLA_FLAG},
+};
+
+/* DCB APP nested attributes. */
+static struct nla_policy dcbnl_app_nest[DCB_APP_ATTR_MAX + 1] = {
+ [DCB_APP_ATTR_IDTYPE] = {.type = NLA_U8},
+ [DCB_APP_ATTR_ID] = {.type = NLA_U16},
+ [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)
nlmsg_end(dcbnl_skb, nlh);
ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
if (ret)
- goto err;
+ return -EINVAL;
return 0;
nlmsg_failure:
err:
- kfree(dcbnl_skb);
+ kfree_skb(dcbnl_skb);
return ret;
}
ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
if (ret)
- goto err;
+ goto err_out;
return 0;
nlmsg_failure:
err:
- kfree(dcbnl_skb);
+ kfree_skb(dcbnl_skb);
err_out:
return -EINVAL;
}
ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
if (ret)
- goto err;
+ goto err_out;
return 0;
nlmsg_failure:
-err:
- kfree(dcbnl_skb);
+ kfree_skb(dcbnl_skb);
err_out:
return -EINVAL;
}
ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
if (ret)
- goto err;
+ goto err_out;
return 0;
nlmsg_failure:
err:
- kfree(dcbnl_skb);
+ kfree_skb(dcbnl_skb);
err_out:
return -EINVAL;
}
ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
if (ret) {
ret = -EINVAL;
- goto err;
+ goto err_out;
}
return 0;
nlmsg_failure:
err:
- kfree(dcbnl_skb);
+ kfree_skb(dcbnl_skb);
err_out:
return ret;
}
u8 value;
int i;
- if (!tb[DCB_ATTR_NUMTCS] || !netdev->dcbnl_ops->setstate)
+ if (!tb[DCB_ATTR_NUMTCS] || !netdev->dcbnl_ops->setnumtcs)
return ret;
ret = nla_parse_nested(data, DCB_NUMTCS_ATTR_MAX, tb[DCB_ATTR_NUMTCS],
return ret;
}
+static int dcbnl_getpfcstate(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags)
+{
+ 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 ret;
+}
+
+static int dcbnl_setpfcstate(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags)
+{
+ int ret = -EINVAL;
+ u8 value;
+
+ if (!tb[DCB_ATTR_PFC_STATE] || !netdev->dcbnl_ops->setpfcstate)
+ return ret;
+
+ 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;
+}
+
+static int dcbnl_getapp(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags)
+{
+ 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;
+
+ if (!tb[DCB_ATTR_APP] || !netdev->dcbnl_ops->getapp)
+ goto out;
+
+ ret = nla_parse_nested(app_tb, DCB_APP_ATTR_MAX, tb[DCB_ATTR_APP],
+ dcbnl_app_nest);
+ if (ret)
+ goto out;
+
+ ret = -EINVAL;
+ /* all must be non-null */
+ if ((!app_tb[DCB_APP_ATTR_IDTYPE]) ||
+ (!app_tb[DCB_APP_ATTR_ID]))
+ goto out;
+
+ /* 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;
+
+ 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;
+
+ 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(dcbnl_skb, DCB_ATTR_APP);
+ ret = nla_put_u8(dcbnl_skb, DCB_APP_ATTR_IDTYPE, idtype);
+ if (ret)
+ goto out_cancel;
+
+ ret = nla_put_u16(dcbnl_skb, DCB_APP_ATTR_ID, id);
+ if (ret)
+ goto out_cancel;
+
+ ret = nla_put_u8(dcbnl_skb, DCB_APP_ATTR_PRIORITY, up);
+ if (ret)
+ goto out_cancel;
+
+ nla_nest_end(dcbnl_skb, app_nest);
+ nlmsg_end(dcbnl_skb, nlh);
+
+ ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
+ if (ret)
+ goto nlmsg_failure;
+
+ goto out;
+
+out_cancel:
+ nla_nest_cancel(dcbnl_skb, app_nest);
+nlmsg_failure:
+ kfree_skb(dcbnl_skb);
+out:
+ return ret;
+}
+
+static int dcbnl_setapp(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags)
+{
+ int ret = -EINVAL;
+ 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;
+
+ ret = nla_parse_nested(app_tb, DCB_APP_ATTR_MAX, tb[DCB_ATTR_APP],
+ dcbnl_app_nest);
+ if (ret)
+ goto out;
+
+ 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;
+
+ /* 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;
+
+ 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:
+ return ret;
+}
+
static int __dcbnl_pg_getcfg(struct net_device *netdev, struct nlattr **tb,
u32 pid, u32 seq, u16 flags, int dir)
{
ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
if (ret)
- goto err;
+ goto err_out;
return 0;
nla_nest_cancel(dcbnl_skb, pg_nest);
nlmsg_failure:
err:
- kfree(dcbnl_skb);
+ kfree_skb(dcbnl_skb);
err_out:
ret = -EINVAL;
return ret;
value = nla_get_u8(tb[DCB_ATTR_STATE]);
- netdev->dcbnl_ops->setstate(netdev, value);
-
- ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_SSTATE, DCB_ATTR_STATE,
+ ret = dcbnl_reply(netdev->dcbnl_ops->setstate(netdev, value),
+ RTM_SETDCB, DCB_CMD_SSTATE, DCB_ATTR_STATE,
pid, seq, flags);
return ret;
return __dcbnl_pg_setcfg(netdev, tb, pid, seq, flags, 1);
}
+static int dcbnl_bcn_getcfg(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags)
+{
+ 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;
+ bool getall = false;
+ int i;
+
+ if (!tb[DCB_ATTR_BCN] || !netdev->dcbnl_ops->getbcnrp ||
+ !netdev->dcbnl_ops->getbcncfg)
+ return ret;
+
+ 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;
+
+ bcn_nest = nla_nest_start(dcbnl_skb, DCB_ATTR_BCN);
+ if (!bcn_nest)
+ goto err;
+
+ if (bcn_tb[DCB_BCN_ATTR_ALL])
+ getall = true;
+
+ for (i = DCB_BCN_ATTR_RP_0; i <= DCB_BCN_ATTR_RP_7; i++) {
+ if (!getall && !bcn_tb[i])
+ continue;
+
+ netdev->dcbnl_ops->getbcnrp(netdev, i - DCB_BCN_ATTR_RP_0,
+ &value_byte);
+ ret = nla_put_u8(dcbnl_skb, i, value_byte);
+ if (ret)
+ goto err_bcn;
+ }
+
+ for (i = DCB_BCN_ATTR_BCNA_0; i <= DCB_BCN_ATTR_RI; i++) {
+ if (!getall && !bcn_tb[i])
+ continue;
+
+ netdev->dcbnl_ops->getbcncfg(netdev, i,
+ &value_integer);
+ ret = nla_put_u32(dcbnl_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;
+
+ return 0;
+
+err_bcn:
+ nla_nest_cancel(dcbnl_skb, bcn_nest);
+nlmsg_failure:
+err:
+ kfree_skb(dcbnl_skb);
+err_out:
+ ret = -EINVAL;
+ return ret;
+}
+
+static int dcbnl_bcn_setcfg(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags)
+{
+ struct nlattr *data[DCB_BCN_ATTR_MAX + 1];
+ int i;
+ int ret = -EINVAL;
+ u8 value_byte;
+ u32 value_int;
+
+ if (!tb[DCB_ATTR_BCN] || !netdev->dcbnl_ops->setbcncfg ||
+ !netdev->dcbnl_ops->setbcnrp)
+ return ret;
+
+ ret = nla_parse_nested(data, DCB_BCN_ATTR_MAX,
+ tb[DCB_ATTR_BCN],
+ dcbnl_pfc_up_nest);
+ if (ret)
+ goto err;
+
+ for (i = DCB_BCN_ATTR_RP_0; i <= DCB_BCN_ATTR_RP_7; i++) {
+ if (data[i] == NULL)
+ continue;
+ value_byte = nla_get_u8(data[i]);
+ netdev->dcbnl_ops->setbcnrp(netdev,
+ data[i]->nla_type - DCB_BCN_ATTR_RP_0, value_byte);
+ }
+
+ for (i = DCB_BCN_ATTR_BCNA_0; i <= DCB_BCN_ATTR_RI; i++) {
+ if (data[i] == NULL)
+ continue;
+ value_int = nla_get_u32(data[i]);
+ netdev->dcbnl_ops->setbcncfg(netdev,
+ i, value_int);
+ }
+
+ ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_BCN_SCFG, DCB_ATTR_BCN,
+ pid, seq, flags);
+err:
+ return ret;
+}
+
static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
struct net *net = sock_net(skb->sk);
u32 pid = skb ? NETLINK_CB(skb).pid : 0;
int ret = -EINVAL;
- if (net != &init_net)
+ if (!net_eq(net, &init_net))
return -EINVAL;
ret = nlmsg_parse(nlh, sizeof(*dcb), tb, DCB_ATTR_MAX,
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);
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);
+ goto out;
+ case DCB_CMD_GAPP:
+ ret = dcbnl_getapp(netdev, tb, pid, nlh->nlmsg_seq,
+ nlh->nlmsg_flags);
+ goto out;
+ case DCB_CMD_SAPP:
+ ret = dcbnl_setapp(netdev, tb, pid, nlh->nlmsg_seq,
+ nlh->nlmsg_flags);
+ goto out;
default:
goto errout;
}