DCB: Add support for DCB BCN
authorAlexander Duyck <alexander.h.duyck@intel.com>
Fri, 21 Nov 2008 05:10:23 +0000 (21:10 -0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 21 Nov 2008 05:10:23 +0000 (21:10 -0800)
Adds an interface to configure the Backward Congestion Notification
(BCN) feature.  In a BCN capabale network, congestion notifications
from congested points out in the network can cause the end station
limit the rate of a given traffic flow.

Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: Peter P Waskiewicz Jr <peter.p.waskiewicz.jr@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ixgbe/ixgbe_dcb.h
drivers/net/ixgbe/ixgbe_dcb_nl.c
include/linux/dcbnl.h
include/net/dcbnl.h
net/dcb/dcbnl.c

index 62dfd243bedce33540ba3874713a4d03766e8d02..75f6efe1e36920a9fba4c1f4859ec701f0a2549a 100644 (file)
@@ -108,7 +108,34 @@ enum dcb_rx_pba_cfg {
        pba_80_48      /* PBA[0-3] each use 80KB, PBA[4-7] each use 48KB */
 };
 
+/*
+ * This structure contains many values encoded as fixed-point
+ * numbers, meaning that some of bits are dedicated to the
+ * magnitude and others to the fraction part. In the comments
+ * this is shown as f=n, where n is the number of fraction bits.
+ * These fraction bits are always the low-order bits. The size
+ * of the magnitude is not specified.
+ */
+struct bcn_config {
+       u32 rp_admin_mode[MAX_TRAFFIC_CLASS]; /* BCN enabled, per TC */
+       u32 bcna_option[2]; /* BCNA Port + MAC Addr */
+       u32 rp_w;        /* Derivative Weight, f=3 */
+       u32 rp_gi;       /* Increase Gain, f=12 */
+       u32 rp_gd;       /* Decrease Gain, f=12 */
+       u32 rp_ru;       /* Rate Unit */
+       u32 rp_alpha;    /* Max Decrease Factor, f=12 */
+       u32 rp_beta;     /* Max Increase Factor, f=12 */
+       u32 rp_ri;       /* Initial Rate */
+       u32 rp_td;       /* Drift Interval Timer */
+       u32 rp_rd;       /* Drift Increase */
+       u32 rp_tmax;     /* Severe Congestion Backoff Timer Range */
+       u32 rp_rmin;     /* Severe Congestion Restart Rate */
+       u32 rp_wrtt;     /* RTT Moving Average Weight */
+};
+
 struct ixgbe_dcb_config {
+       struct bcn_config bcn;
+
        struct tc_configuration tc_config[MAX_TRAFFIC_CLASS];
        u8     bw_percentage[2][MAX_BW_GROUP]; /* One each for Tx/Rx */
 
index dd940a8f935722923bb452c7f4b09ec5879448e1..615c2803202a3d829c6797f50999cf9deb685715 100644 (file)
@@ -34,6 +34,7 @@
 #define BIT_PFC                0x02
 #define BIT_PG_RX      0x04
 #define BIT_PG_TX      0x08
+#define BIT_BCN         0x10
 
 int ixgbe_copy_dcb_cfg(struct ixgbe_dcb_config *src_dcb_cfg,
                        struct ixgbe_dcb_config *dst_dcb_cfg, int tc_max)
@@ -88,6 +89,23 @@ int ixgbe_copy_dcb_cfg(struct ixgbe_dcb_config *src_dcb_cfg,
                        src_dcb_cfg->tc_config[i - DCB_PFC_UP_ATTR_0].dcb_pfc;
        }
 
+       for (i = DCB_BCN_ATTR_RP_0; i < DCB_BCN_ATTR_RP_ALL; i++) {
+               dst_dcb_cfg->bcn.rp_admin_mode[i - DCB_BCN_ATTR_RP_0] =
+                       src_dcb_cfg->bcn.rp_admin_mode[i - DCB_BCN_ATTR_RP_0];
+       }
+       dst_dcb_cfg->bcn.rp_alpha = src_dcb_cfg->bcn.rp_alpha;
+       dst_dcb_cfg->bcn.rp_beta = src_dcb_cfg->bcn.rp_beta;
+       dst_dcb_cfg->bcn.rp_gd = src_dcb_cfg->bcn.rp_gd;
+       dst_dcb_cfg->bcn.rp_gi = src_dcb_cfg->bcn.rp_gi;
+       dst_dcb_cfg->bcn.rp_tmax = src_dcb_cfg->bcn.rp_tmax;
+       dst_dcb_cfg->bcn.rp_td = src_dcb_cfg->bcn.rp_td;
+       dst_dcb_cfg->bcn.rp_rmin = src_dcb_cfg->bcn.rp_rmin;
+       dst_dcb_cfg->bcn.rp_w = src_dcb_cfg->bcn.rp_w;
+       dst_dcb_cfg->bcn.rp_rd = src_dcb_cfg->bcn.rp_rd;
+       dst_dcb_cfg->bcn.rp_ru = src_dcb_cfg->bcn.rp_ru;
+       dst_dcb_cfg->bcn.rp_wrtt = src_dcb_cfg->bcn.rp_wrtt;
+       dst_dcb_cfg->bcn.rp_ri = src_dcb_cfg->bcn.rp_ri;
+
        return 0;
 }
 
@@ -313,6 +331,7 @@ static u8 ixgbe_dcbnl_set_all(struct net_device *netdev)
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        int ret;
 
+       adapter->dcb_set_bitmap &= ~BIT_BCN;    /* no set for BCN */
        if (!adapter->dcb_set_bitmap)
                return 1;
 
@@ -417,6 +436,157 @@ static void ixgbe_dcbnl_setpfcstate(struct net_device *netdev, u8 state)
        return;
 }
 
+static void ixgbe_dcbnl_getbcnrp(struct net_device *netdev, int priority,
+                                 u8 *setting)
+{
+       struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+       *setting = adapter->dcb_cfg.bcn.rp_admin_mode[priority];
+}
+
+
+static void ixgbe_dcbnl_getbcncfg(struct net_device *netdev, int enum_index,
+                                 u32 *setting)
+{
+       struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+       switch (enum_index) {
+       case DCB_BCN_ATTR_ALPHA:
+               *setting = adapter->dcb_cfg.bcn.rp_alpha;
+               break;
+       case DCB_BCN_ATTR_BETA:
+               *setting = adapter->dcb_cfg.bcn.rp_beta;
+               break;
+       case DCB_BCN_ATTR_GD:
+               *setting = adapter->dcb_cfg.bcn.rp_gd;
+               break;
+       case DCB_BCN_ATTR_GI:
+               *setting = adapter->dcb_cfg.bcn.rp_gi;
+               break;
+       case DCB_BCN_ATTR_TMAX:
+               *setting = adapter->dcb_cfg.bcn.rp_tmax;
+               break;
+       case DCB_BCN_ATTR_TD:
+               *setting = adapter->dcb_cfg.bcn.rp_td;
+               break;
+       case DCB_BCN_ATTR_RMIN:
+               *setting = adapter->dcb_cfg.bcn.rp_rmin;
+               break;
+       case DCB_BCN_ATTR_W:
+               *setting = adapter->dcb_cfg.bcn.rp_w;
+               break;
+       case DCB_BCN_ATTR_RD:
+               *setting = adapter->dcb_cfg.bcn.rp_rd;
+               break;
+       case DCB_BCN_ATTR_RU:
+               *setting = adapter->dcb_cfg.bcn.rp_ru;
+               break;
+       case DCB_BCN_ATTR_WRTT:
+               *setting = adapter->dcb_cfg.bcn.rp_wrtt;
+               break;
+       case DCB_BCN_ATTR_RI:
+               *setting = adapter->dcb_cfg.bcn.rp_ri;
+               break;
+       default:
+               *setting = -1;
+       }
+}
+
+static void ixgbe_dcbnl_setbcnrp(struct net_device *netdev, int priority,
+                                u8 setting)
+{
+       struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+       adapter->temp_dcb_cfg.bcn.rp_admin_mode[priority] = setting;
+
+       if (adapter->temp_dcb_cfg.bcn.rp_admin_mode[priority] !=
+           adapter->dcb_cfg.bcn.rp_admin_mode[priority])
+               adapter->dcb_set_bitmap |= BIT_BCN;
+}
+
+static void ixgbe_dcbnl_setbcncfg(struct net_device *netdev, int enum_index,
+                                u32 setting)
+{
+       struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+       switch (enum_index) {
+       case DCB_BCN_ATTR_ALPHA:
+               adapter->temp_dcb_cfg.bcn.rp_alpha = setting;
+               if (adapter->temp_dcb_cfg.bcn.rp_alpha !=
+                   adapter->dcb_cfg.bcn.rp_alpha)
+                       adapter->dcb_set_bitmap |= BIT_BCN;
+               break;
+       case DCB_BCN_ATTR_BETA:
+               adapter->temp_dcb_cfg.bcn.rp_beta = setting;
+               if (adapter->temp_dcb_cfg.bcn.rp_beta !=
+                   adapter->dcb_cfg.bcn.rp_beta)
+                       adapter->dcb_set_bitmap |= BIT_BCN;
+               break;
+       case DCB_BCN_ATTR_GD:
+               adapter->temp_dcb_cfg.bcn.rp_gd = setting;
+               if (adapter->temp_dcb_cfg.bcn.rp_gd !=
+                   adapter->dcb_cfg.bcn.rp_gd)
+                       adapter->dcb_set_bitmap |= BIT_BCN;
+               break;
+       case DCB_BCN_ATTR_GI:
+               adapter->temp_dcb_cfg.bcn.rp_gi = setting;
+               if (adapter->temp_dcb_cfg.bcn.rp_gi !=
+                   adapter->dcb_cfg.bcn.rp_gi)
+                       adapter->dcb_set_bitmap |= BIT_BCN;
+               break;
+       case DCB_BCN_ATTR_TMAX:
+               adapter->temp_dcb_cfg.bcn.rp_tmax = setting;
+               if (adapter->temp_dcb_cfg.bcn.rp_tmax !=
+                   adapter->dcb_cfg.bcn.rp_tmax)
+                       adapter->dcb_set_bitmap |= BIT_BCN;
+               break;
+       case DCB_BCN_ATTR_TD:
+               adapter->temp_dcb_cfg.bcn.rp_td = setting;
+               if (adapter->temp_dcb_cfg.bcn.rp_td !=
+                   adapter->dcb_cfg.bcn.rp_td)
+                       adapter->dcb_set_bitmap |= BIT_BCN;
+               break;
+       case DCB_BCN_ATTR_RMIN:
+               adapter->temp_dcb_cfg.bcn.rp_rmin = setting;
+               if (adapter->temp_dcb_cfg.bcn.rp_rmin !=
+                   adapter->dcb_cfg.bcn.rp_rmin)
+                       adapter->dcb_set_bitmap |= BIT_BCN;
+               break;
+       case DCB_BCN_ATTR_W:
+               adapter->temp_dcb_cfg.bcn.rp_w = setting;
+               if (adapter->temp_dcb_cfg.bcn.rp_w !=
+                   adapter->dcb_cfg.bcn.rp_w)
+                       adapter->dcb_set_bitmap |= BIT_BCN;
+               break;
+       case DCB_BCN_ATTR_RD:
+               adapter->temp_dcb_cfg.bcn.rp_rd = setting;
+               if (adapter->temp_dcb_cfg.bcn.rp_rd !=
+                   adapter->dcb_cfg.bcn.rp_rd)
+                       adapter->dcb_set_bitmap |= BIT_BCN;
+               break;
+       case DCB_BCN_ATTR_RU:
+               adapter->temp_dcb_cfg.bcn.rp_ru = setting;
+               if (adapter->temp_dcb_cfg.bcn.rp_ru !=
+                   adapter->dcb_cfg.bcn.rp_ru)
+                       adapter->dcb_set_bitmap |= BIT_BCN;
+               break;
+       case DCB_BCN_ATTR_WRTT:
+               adapter->temp_dcb_cfg.bcn.rp_wrtt = setting;
+               if (adapter->temp_dcb_cfg.bcn.rp_wrtt !=
+                   adapter->dcb_cfg.bcn.rp_wrtt)
+                       adapter->dcb_set_bitmap |= BIT_BCN;
+               break;
+       case DCB_BCN_ATTR_RI:
+               adapter->temp_dcb_cfg.bcn.rp_ri = setting;
+               if (adapter->temp_dcb_cfg.bcn.rp_ri !=
+                   adapter->dcb_cfg.bcn.rp_ri)
+                       adapter->dcb_set_bitmap |= BIT_BCN;
+               break;
+       default:
+               break;
+       }
+}
+
 struct dcbnl_rtnl_ops dcbnl_ops = {
        .getstate       = ixgbe_dcbnl_get_state,
        .setstate       = ixgbe_dcbnl_set_state,
@@ -436,6 +606,10 @@ struct dcbnl_rtnl_ops dcbnl_ops = {
        .getnumtcs      = ixgbe_dcbnl_getnumtcs,
        .setnumtcs      = ixgbe_dcbnl_setnumtcs,
        .getpfcstate    = ixgbe_dcbnl_getpfcstate,
-       .setpfcstate    = ixgbe_dcbnl_setpfcstate
+       .setpfcstate    = ixgbe_dcbnl_setpfcstate,
+       .getbcncfg      = ixgbe_dcbnl_getbcncfg,
+       .getbcnrp       = ixgbe_dcbnl_getbcnrp,
+       .setbcncfg      = ixgbe_dcbnl_setbcncfg,
+       .setbcnrp       = ixgbe_dcbnl_setbcnrp
 };
 
index 6cc4560bc376ce4e70be76e609118f05d9afcd1d..e73a61449ad6dacabc99a2e2372252b8732396fe 100644 (file)
@@ -46,6 +46,8 @@ struct dcbmsg {
  * @DCB_CMD_GCAP: request the DCB capabilities of the device
  * @DCB_CMD_GNUMTCS: get the number of traffic classes currently supported
  * @DCB_CMD_SNUMTCS: set the number of traffic classes
+ * @DCB_CMD_GBCN: set backward congestion notification configuration
+ * @DCB_CMD_SBCN: get backward congestion notification configration.
  */
 enum dcbnl_commands {
        DCB_CMD_UNDEFINED,
@@ -62,18 +64,24 @@ enum dcbnl_commands {
        DCB_CMD_PFC_SCFG,
 
        DCB_CMD_SET_ALL,
+
        DCB_CMD_GPERM_HWADDR,
+
        DCB_CMD_GCAP,
+
        DCB_CMD_GNUMTCS,
        DCB_CMD_SNUMTCS,
+
        DCB_CMD_PFC_GSTATE,
        DCB_CMD_PFC_SSTATE,
 
+       DCB_CMD_BCN_GCFG,
+       DCB_CMD_BCN_SCFG,
+
        __DCB_CMD_ENUM_MAX,
        DCB_CMD_MAX = __DCB_CMD_ENUM_MAX - 1,
 };
 
-
 /**
  * enum dcbnl_attrs - DCB top-level netlink attributes
  *
@@ -88,6 +96,7 @@ enum dcbnl_commands {
  * @DCB_ATTR_PERM_HWADDR: MAC address of the physical device (NLA_NESTED)
  * @DCB_ATTR_CAP: DCB capabilities of the device (NLA_NESTED)
  * @DCB_ATTR_NUMTCS: number of traffic classes supported (NLA_NESTED)
+ * @DCB_ATTR_BCN: backward congestion notification configuration (NLA_NESTED)
  */
 enum dcbnl_attrs {
        DCB_ATTR_UNDEFINED,
@@ -102,6 +111,7 @@ enum dcbnl_attrs {
        DCB_ATTR_PERM_HWADDR,
        DCB_ATTR_CAP,
        DCB_ATTR_NUMTCS,
+       DCB_ATTR_BCN,
 
        __DCB_ATTR_ENUM_MAX,
        DCB_ATTR_MAX = __DCB_ATTR_ENUM_MAX - 1,
@@ -282,6 +292,38 @@ enum dcbnl_numtcs_attrs {
        DCB_NUMTCS_ATTR_MAX = __DCB_NUMTCS_ATTR_ENUM_MAX - 1,
 };
 
+enum dcbnl_bcn_attrs{
+       DCB_BCN_ATTR_UNDEFINED = 0,
+
+       DCB_BCN_ATTR_RP_0,
+       DCB_BCN_ATTR_RP_1,
+       DCB_BCN_ATTR_RP_2,
+       DCB_BCN_ATTR_RP_3,
+       DCB_BCN_ATTR_RP_4,
+       DCB_BCN_ATTR_RP_5,
+       DCB_BCN_ATTR_RP_6,
+       DCB_BCN_ATTR_RP_7,
+       DCB_BCN_ATTR_RP_ALL,
+
+       DCB_BCN_ATTR_ALPHA,
+       DCB_BCN_ATTR_BETA,
+       DCB_BCN_ATTR_GD,
+       DCB_BCN_ATTR_GI,
+       DCB_BCN_ATTR_TMAX,
+       DCB_BCN_ATTR_TD,
+       DCB_BCN_ATTR_RMIN,
+       DCB_BCN_ATTR_W,
+       DCB_BCN_ATTR_RD,
+       DCB_BCN_ATTR_RU,
+       DCB_BCN_ATTR_WRTT,
+       DCB_BCN_ATTR_RI,
+       DCB_BCN_ATTR_C,
+       DCB_BCN_ATTR_ALL,
+
+       __DCB_BCN_ATTR_ENUM_MAX,
+       DCB_BCN_ATTR_MAX = __DCB_BCN_ATTR_ENUM_MAX - 1,
+};
+
 /**
  * enum dcb_general_attr_values - general DCB attribute values
  *
index c7d87caf3f99a8b3341f017af7434ea82791e594..91e0a3d7faf236c1c0c3e54e0336dee366e74acb 100644 (file)
@@ -44,6 +44,10 @@ struct dcbnl_rtnl_ops {
        u8   (*setnumtcs)(struct net_device *, int, u8);
        u8   (*getpfcstate)(struct net_device *);
        void (*setpfcstate)(struct net_device *, u8);
+       void (*getbcncfg)(struct net_device *, int, u32 *);
+       void (*setbcncfg)(struct net_device *, int, u32);
+       void (*getbcnrp)(struct net_device *, int, u8 *);
+       void (*setbcnrp)(struct net_device *, int, u8);
 };
 
 #endif /* __NET_DCBNL_H__ */
index 758419c6f59b892a18983428a5e97ea1099f4748..b2bda3f610df7b460e5fa21ae8eabb87f34221db 100644 (file)
@@ -55,14 +55,15 @@ MODULE_LICENSE("GPL");
 
 /* 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_PFC_STATE] = {.type = NLA_U8},
+       [DCB_ATTR_CAP]         = {.type = NLA_NESTED},
+       [DCB_ATTR_PFC_STATE]   = {.type = NLA_U8},
+       [DCB_ATTR_BCN]         = {.type = NLA_NESTED},
 };
 
 /* DCB priority flow control to User Priority nested attributes */
@@ -128,6 +129,33 @@ static struct nla_policy dcbnl_numtcs_nest[DCB_NUMTCS_ATTR_MAX + 1] = {
        [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_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},
+};
+
 /* standard netlink reply call */
 static int dcbnl_reply(u8 value, u8 event, u8 cmd, u8 attr, u32 pid,
                        u32 seq, u16 flags)
@@ -843,6 +871,130 @@ static int dcbnl_pgrx_setcfg(struct net_device *netdev, struct nlattr **tb,
        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_ALPHA; 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;
+
+       return 0;
+
+err_bcn:
+       nla_nest_cancel(dcbnl_skb, bcn_nest);
+nlmsg_failure:
+err:
+       kfree(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_ALPHA; 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);
@@ -891,6 +1043,10 @@ static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
                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);
@@ -932,6 +1088,10 @@ static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
                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;
        default:
                goto errout;
        }