Skip to content

Commit

Permalink
i40e: Add support for getlink, setlink ndo ops
Browse files Browse the repository at this point in the history
Add support for bridge offload ndo_ops getlink and setlink to
enable bridge hardware mode as per the mode set via IFLA_BRIDGE_MODE.
The support is only enabled in case of a PF VSI and not available for
any other VSI type.

By default the i40e driver inserts a bridge as part of the bring-up
when a FDIR type VSI and/or a FCoE VSI is created. This bridge is
created in VEB mode by default i.e. after creating the bridge using
"Add VEB" AQ command the loopback for the PF's default VSI is enabled.

The patch adds capability where all the VSIs created as downlink to
the bridge inherits the loopback property and enables loopback only
if the uplink bridge is operating in VEB mode.
Hence, there is no need to explicitly enable loopback as part of
allocating resources for SR-IOV VFs and call to do that has been
removed.

In case a user-request is made either via "bridge" utility or using
the bridge netlink interface that requires to change the hardware
bridge mode then that would require a PF reset and rebuild of the
switch hierarchy.

Also update the copyright year.

Change-ID: I4d78fc1c83158efda29ba7be92239b74f75d6d25
Signed-off-by: Neerav Parikh <neerav.parikh@intel.com>
Tested-By: Jim Young <james.m.young@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
  • Loading branch information
Neerav Parikh authored and Jeff Kirsher committed Feb 25, 2015
1 parent 9666448 commit 5161601
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 22 deletions.
3 changes: 3 additions & 0 deletions drivers/net/ethernet/intel/i40e/i40e.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
#include <net/ip6_checksum.h>
#include <linux/ethtool.h>
#include <linux/if_vlan.h>
#include <linux/if_bridge.h>
#include <linux/clocksource.h>
#include <linux/net_tstamp.h>
#include <linux/ptp_clock_kernel.h>
Expand Down Expand Up @@ -410,6 +411,7 @@ struct i40e_veb {
u16 uplink_seid;
u16 stats_idx; /* index of VEB parent */
u8 enabled_tc;
u16 bridge_mode; /* Bridge Mode (VEB/VEPA) */
u16 flags;
u16 bw_limit;
u8 bw_max_quanta;
Expand Down Expand Up @@ -735,6 +737,7 @@ int i40e_ptp_set_ts_config(struct i40e_pf *pf, struct ifreq *ifr);
int i40e_ptp_get_ts_config(struct i40e_pf *pf, struct ifreq *ifr);
void i40e_ptp_init(struct i40e_pf *pf);
void i40e_ptp_stop(struct i40e_pf *pf);
int i40e_is_vsi_uplink_mode_veb(struct i40e_vsi *vsi);
#if IS_ENABLED(CONFIG_CONFIGFS_FS)
int i40e_configfs_init(void);
void i40e_configfs_exit(void);
Expand Down
5 changes: 3 additions & 2 deletions drivers/net/ethernet/intel/i40e/i40e_debugfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -921,9 +921,10 @@ static void i40e_dbg_dump_veb_seid(struct i40e_pf *pf, int seid)
return;
}
dev_info(&pf->pdev->dev,
"veb idx=%d,%d stats_ic=%d seid=%d uplink=%d\n",
"veb idx=%d,%d stats_ic=%d seid=%d uplink=%d mode=%s\n",
veb->idx, veb->veb_idx, veb->stats_idx, veb->seid,
veb->uplink_seid);
veb->uplink_seid,
veb->bridge_mode == BRIDGE_MODE_VEPA ? "VEPA" : "VEB");
i40e_dbg_dump_eth_stats(pf, &veb->stats);
}

Expand Down
12 changes: 8 additions & 4 deletions drivers/net/ethernet/intel/i40e/i40e_fcoe.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
* Copyright(c) 2013 - 2014 Intel Corporation.
* Copyright(c) 2013 - 2015 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,
Expand Down Expand Up @@ -385,8 +385,7 @@ int i40e_fcoe_vsi_init(struct i40e_vsi *vsi, struct i40e_vsi_context *ctxt)
ctxt->flags = I40E_AQ_VSI_TYPE_PF;

/* FCoE VSI would need the following sections */
info->valid_sections |= cpu_to_le16(I40E_AQ_VSI_PROP_SWITCH_VALID |
I40E_AQ_VSI_PROP_QUEUE_OPT_VALID);
info->valid_sections |= cpu_to_le16(I40E_AQ_VSI_PROP_QUEUE_OPT_VALID);

/* FCoE VSI does not need these sections */
info->valid_sections &= cpu_to_le16(~(I40E_AQ_VSI_PROP_SECURITY_VALID |
Expand All @@ -395,7 +394,12 @@ int i40e_fcoe_vsi_init(struct i40e_vsi *vsi, struct i40e_vsi_context *ctxt)
I40E_AQ_VSI_PROP_INGRESS_UP_VALID |
I40E_AQ_VSI_PROP_EGRESS_UP_VALID));

info->switch_id = cpu_to_le16(I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB);
if (i40e_is_vsi_uplink_mode_veb(vsi)) {
info->valid_sections |=
cpu_to_le16(I40E_AQ_VSI_PROP_SWITCH_VALID);
info->switch_id =
cpu_to_le16(I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB);
}
enabled_tc = i40e_get_fcoe_tc_map(pf);
i40e_vsi_setup_queue_map(vsi, ctxt, enabled_tc, true);

Expand Down
190 changes: 178 additions & 12 deletions drivers/net/ethernet/intel/i40e/i40e_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -5871,6 +5871,26 @@ static void i40e_verify_eeprom(struct i40e_pf *pf)
}
}

/**
* i40e_config_bridge_mode - Configure the HW bridge mode
* @veb: pointer to the bridge instance
*
* Configure the loop back mode for the LAN VSI that is downlink to the
* specified HW bridge instance. It is expected this function is called
* when a new HW bridge is instantiated.
**/
static void i40e_config_bridge_mode(struct i40e_veb *veb)
{
struct i40e_pf *pf = veb->pf;

dev_info(&pf->pdev->dev, "enabling bridge mode: %s\n",
veb->bridge_mode == BRIDGE_MODE_VEPA ? "VEPA" : "VEB");
if (veb->bridge_mode & BRIDGE_MODE_VEPA)
i40e_disable_pf_switch_lb(pf);
else
i40e_enable_pf_switch_lb(pf);
}

/**
* i40e_reconstitute_veb - rebuild the VEB and anything connected to it
* @veb: pointer to the VEB instance
Expand Down Expand Up @@ -5917,8 +5937,7 @@ static int i40e_reconstitute_veb(struct i40e_veb *veb)
if (ret)
goto end_reconstitute;

/* Enable LB mode for the main VSI now that it is on a VEB */
i40e_enable_pf_switch_lb(pf);
i40e_config_bridge_mode(veb);

/* create the remaining VSIs attached to this VEB */
for (v = 0; v < pf->num_alloc_vsi; v++) {
Expand Down Expand Up @@ -7737,6 +7756,118 @@ static int i40e_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
return err;
}

#ifdef HAVE_BRIDGE_ATTRIBS
/**
* i40e_ndo_bridge_setlink - Set the hardware bridge mode
* @dev: the netdev being configured
* @nlh: RTNL message
*
* Inserts a new hardware bridge if not already created and
* enables the bridging mode requested (VEB or VEPA). If the
* hardware bridge has already been inserted and the request
* is to change the mode then that requires a PF reset to
* allow rebuild of the components with required hardware
* bridge mode enabled.
**/
static int i40e_ndo_bridge_setlink(struct net_device *dev,
struct nlmsghdr *nlh)
{
struct i40e_netdev_priv *np = netdev_priv(dev);
struct i40e_vsi *vsi = np->vsi;
struct i40e_pf *pf = vsi->back;
struct i40e_veb *veb = NULL;
struct nlattr *attr, *br_spec;
int i, rem;

/* Only for PF VSI for now */
if (vsi->seid != pf->vsi[pf->lan_vsi]->seid)
return -EOPNOTSUPP;

/* Find the HW bridge for PF VSI */
for (i = 0; i < I40E_MAX_VEB && !veb; i++) {
if (pf->veb[i] && pf->veb[i]->seid == vsi->uplink_seid)
veb = pf->veb[i];
}

br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);

nla_for_each_nested(attr, br_spec, rem) {
__u16 mode;

if (nla_type(attr) != IFLA_BRIDGE_MODE)
continue;

mode = nla_get_u16(attr);
if ((mode != BRIDGE_MODE_VEPA) &&
(mode != BRIDGE_MODE_VEB))
return -EINVAL;

/* Insert a new HW bridge */
if (!veb) {
veb = i40e_veb_setup(pf, 0, vsi->uplink_seid, vsi->seid,
vsi->tc_config.enabled_tc);
if (veb) {
veb->bridge_mode = mode;
i40e_config_bridge_mode(veb);
} else {
/* No Bridge HW offload available */
return -ENOENT;
}
break;
} else if (mode != veb->bridge_mode) {
/* Existing HW bridge but different mode needs reset */
veb->bridge_mode = mode;
i40e_do_reset(pf, (1 << __I40E_PF_RESET_REQUESTED));
break;
}
}

return 0;
}

/**
* i40e_ndo_bridge_getlink - Get the hardware bridge mode
* @skb: skb buff
* @pid: process id
* @seq: RTNL message seq #
* @dev: the netdev being configured
* @filter_mask: unused
*
* Return the mode in which the hardware bridge is operating in
* i.e VEB or VEPA.
**/
#ifdef HAVE_BRIDGE_FILTER
static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
struct net_device *dev,
u32 __always_unused filter_mask)
#else
static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
struct net_device *dev)
#endif /* HAVE_BRIDGE_FILTER */
{
struct i40e_netdev_priv *np = netdev_priv(dev);
struct i40e_vsi *vsi = np->vsi;
struct i40e_pf *pf = vsi->back;
struct i40e_veb *veb = NULL;
int i;

/* Only for PF VSI for now */
if (vsi->seid != pf->vsi[pf->lan_vsi]->seid)
return -EOPNOTSUPP;

/* Find the HW bridge for the PF VSI */
for (i = 0; i < I40E_MAX_VEB && !veb; i++) {
if (pf->veb[i] && pf->veb[i]->seid == vsi->uplink_seid)
veb = pf->veb[i];
}

if (!veb)
return 0;

return ndo_dflt_bridge_getlink(skb, pid, seq, dev, veb->bridge_mode);
}
#endif /* HAVE_BRIDGE_ATTRIBS */

const struct net_device_ops i40e_netdev_ops = {
.ndo_open = i40e_open,
.ndo_stop = i40e_close,
Expand Down Expand Up @@ -7771,6 +7902,10 @@ const struct net_device_ops i40e_netdev_ops = {
#endif
.ndo_get_phys_port_id = i40e_get_phys_port_id,
.ndo_fdb_add = i40e_ndo_fdb_add,
#ifdef HAVE_BRIDGE_ATTRIBS
.ndo_bridge_getlink = i40e_ndo_bridge_getlink,
.ndo_bridge_setlink = i40e_ndo_bridge_setlink,
#endif /* HAVE_BRIDGE_ATTRIBS */
};

/**
Expand Down Expand Up @@ -7882,6 +8017,30 @@ static void i40e_vsi_delete(struct i40e_vsi *vsi)
i40e_aq_delete_element(&vsi->back->hw, vsi->seid, NULL);
}

/**
* i40e_is_vsi_uplink_mode_veb - Check if the VSI's uplink bridge mode is VEB
* @vsi: the VSI being queried
*
* Returns 1 if HW bridge mode is VEB and return 0 in case of VEPA mode
**/
int i40e_is_vsi_uplink_mode_veb(struct i40e_vsi *vsi)
{
struct i40e_veb *veb;
struct i40e_pf *pf = vsi->back;

/* Uplink is not a bridge so default to VEB */
if (vsi->veb_idx == I40E_NO_VEB)
return 1;

veb = pf->veb[vsi->veb_idx];
/* Uplink is a bridge in VEPA mode */
if (veb && (veb->bridge_mode & BRIDGE_MODE_VEPA))
return 0;

/* Uplink is a bridge in VEB mode */
return 1;
}

/**
* i40e_add_vsi - Add a VSI to the switch
* @vsi: the VSI being configured
Expand Down Expand Up @@ -7969,10 +8128,12 @@ static int i40e_add_vsi(struct i40e_vsi *vsi)
ctxt.uplink_seid = vsi->uplink_seid;
ctxt.connection_type = I40E_AQ_VSI_CONN_TYPE_NORMAL;
ctxt.flags = I40E_AQ_VSI_TYPE_PF;
ctxt.info.valid_sections |=
if (i40e_is_vsi_uplink_mode_veb(vsi)) {
ctxt.info.valid_sections |=
cpu_to_le16(I40E_AQ_VSI_PROP_SWITCH_VALID);
ctxt.info.switch_id =
ctxt.info.switch_id =
cpu_to_le16(I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB);
}
i40e_vsi_setup_queue_map(vsi, &ctxt, enabled_tc, true);
break;

Expand All @@ -7983,13 +8144,15 @@ static int i40e_add_vsi(struct i40e_vsi *vsi)
ctxt.connection_type = I40E_AQ_VSI_CONN_TYPE_NORMAL;
ctxt.flags = I40E_AQ_VSI_TYPE_VMDQ2;

ctxt.info.valid_sections |= cpu_to_le16(I40E_AQ_VSI_PROP_SWITCH_VALID);

/* This VSI is connected to VEB so the switch_id
* should be set to zero by default.
*/
ctxt.info.switch_id = 0;
ctxt.info.switch_id |= cpu_to_le16(I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB);
if (i40e_is_vsi_uplink_mode_veb(vsi)) {
ctxt.info.valid_sections |=
cpu_to_le16(I40E_AQ_VSI_PROP_SWITCH_VALID);
ctxt.info.switch_id =
cpu_to_le16(I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB);
}

/* Setup the VSI tx/rx queue map for TC0 only for now */
i40e_vsi_setup_queue_map(vsi, &ctxt, enabled_tc, true);
Expand All @@ -8002,12 +8165,15 @@ static int i40e_add_vsi(struct i40e_vsi *vsi)
ctxt.connection_type = I40E_AQ_VSI_CONN_TYPE_NORMAL;
ctxt.flags = I40E_AQ_VSI_TYPE_VF;

ctxt.info.valid_sections |= cpu_to_le16(I40E_AQ_VSI_PROP_SWITCH_VALID);

/* This VSI is connected to VEB so the switch_id
* should be set to zero by default.
*/
ctxt.info.switch_id = cpu_to_le16(I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB);
if (i40e_is_vsi_uplink_mode_veb(vsi)) {
ctxt.info.valid_sections |=
cpu_to_le16(I40E_AQ_VSI_PROP_SWITCH_VALID);
ctxt.info.switch_id =
cpu_to_le16(I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB);
}

ctxt.info.valid_sections |= cpu_to_le16(I40E_AQ_VSI_PROP_VLAN_VALID);
ctxt.info.port_vlan_flags |= I40E_AQ_VSI_PVLAN_MODE_ALL;
Expand Down Expand Up @@ -8365,7 +8531,7 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type,
__func__);
return NULL;
}
i40e_enable_pf_switch_lb(pf);
i40e_config_bridge_mode(veb);
}
for (i = 0; i < I40E_MAX_VEB && !veb; i++) {
if (pf->veb[i] && pf->veb[i]->seid == vsi->uplink_seid)
Expand Down
5 changes: 2 additions & 3 deletions drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
* Copyright(c) 2013 - 2014 Intel Corporation.
* Copyright(c) 2013 - 2015 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,
Expand Down Expand Up @@ -752,7 +752,7 @@ void i40e_enable_pf_switch_lb(struct i40e_pf *pf)
*
* disable switch loop back or die - no point in a return value
**/
static void i40e_disable_pf_switch_lb(struct i40e_pf *pf)
void i40e_disable_pf_switch_lb(struct i40e_pf *pf)
{
struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi];
struct i40e_vsi_context ctxt;
Expand Down Expand Up @@ -891,7 +891,6 @@ int i40e_alloc_vfs(struct i40e_pf *pf, u16 num_alloc_vfs)
}
pf->num_alloc_vfs = num_alloc_vfs;

i40e_enable_pf_switch_lb(pf);
err_alloc:
if (ret)
i40e_free_vfs(pf);
Expand Down
3 changes: 2 additions & 1 deletion drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*******************************************************************************
*
* Intel Ethernet Controller XL710 Family Linux Driver
* Copyright(c) 2013 - 2014 Intel Corporation.
* Copyright(c) 2013 - 2015 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,
Expand Down Expand Up @@ -127,5 +127,6 @@ int i40e_ndo_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool enable);
void i40e_vc_notify_link_state(struct i40e_pf *pf);
void i40e_vc_notify_reset(struct i40e_pf *pf);
void i40e_enable_pf_switch_lb(struct i40e_pf *pf);
void i40e_disable_pf_switch_lb(struct i40e_pf *pf);

#endif /* _I40E_VIRTCHNL_PF_H_ */

0 comments on commit 5161601

Please sign in to comment.