Commit f58ee41e authored by David S. Miller's avatar David S. Miller

Merge branch 'qed-vlan-filtering'

Yuval Mintz says:

====================
qed{,e}: Add vlan filtering offload

This series adds vlan filtering offload to qede.
First patch introduces small additional infrastructure needed in
qed to support it, while second contains the main bulk of driver changes.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents e12a285c 7c1bfcad
...@@ -124,6 +124,8 @@ struct qed_sp_vport_update_params { ...@@ -124,6 +124,8 @@ struct qed_sp_vport_update_params {
u8 update_vport_active_tx_flg; u8 update_vport_active_tx_flg;
u8 vport_active_tx_flg; u8 vport_active_tx_flg;
u8 update_approx_mcast_flg; u8 update_approx_mcast_flg;
u8 update_accept_any_vlan_flg;
u8 accept_any_vlan;
unsigned long bins[8]; unsigned long bins[8];
struct qed_rss_params *rss_params; struct qed_rss_params *rss_params;
struct qed_filter_accept_flags accept_flags; struct qed_filter_accept_flags accept_flags;
...@@ -393,7 +395,9 @@ qed_sp_vport_update(struct qed_hwfn *p_hwfn, ...@@ -393,7 +395,9 @@ qed_sp_vport_update(struct qed_hwfn *p_hwfn,
p_cmn->update_rx_active_flg = p_params->update_vport_active_rx_flg; p_cmn->update_rx_active_flg = p_params->update_vport_active_rx_flg;
p_cmn->tx_active_flg = p_params->vport_active_tx_flg; p_cmn->tx_active_flg = p_params->vport_active_tx_flg;
p_cmn->update_tx_active_flg = p_params->update_vport_active_tx_flg; p_cmn->update_tx_active_flg = p_params->update_vport_active_tx_flg;
p_cmn->accept_any_vlan = p_params->accept_any_vlan;
p_cmn->update_accept_any_vlan_flg =
p_params->update_accept_any_vlan_flg;
rc = qed_sp_vport_update_rss(p_hwfn, p_ramrod, p_rss_params); rc = qed_sp_vport_update_rss(p_hwfn, p_ramrod, p_rss_params);
if (rc) { if (rc) {
/* Return spq entry which is taken in qed_sp_init_request()*/ /* Return spq entry which is taken in qed_sp_init_request()*/
...@@ -444,6 +448,8 @@ static int qed_sp_vport_stop(struct qed_hwfn *p_hwfn, ...@@ -444,6 +448,8 @@ static int qed_sp_vport_stop(struct qed_hwfn *p_hwfn,
static int qed_filter_accept_cmd(struct qed_dev *cdev, static int qed_filter_accept_cmd(struct qed_dev *cdev,
u8 vport, u8 vport,
struct qed_filter_accept_flags accept_flags, struct qed_filter_accept_flags accept_flags,
u8 update_accept_any_vlan,
u8 accept_any_vlan,
enum spq_mode comp_mode, enum spq_mode comp_mode,
struct qed_spq_comp_cb *p_comp_data) struct qed_spq_comp_cb *p_comp_data)
{ {
...@@ -454,6 +460,8 @@ static int qed_filter_accept_cmd(struct qed_dev *cdev, ...@@ -454,6 +460,8 @@ static int qed_filter_accept_cmd(struct qed_dev *cdev,
memset(&vport_update_params, 0, sizeof(vport_update_params)); memset(&vport_update_params, 0, sizeof(vport_update_params));
vport_update_params.vport_id = vport; vport_update_params.vport_id = vport;
vport_update_params.accept_flags = accept_flags; vport_update_params.accept_flags = accept_flags;
vport_update_params.update_accept_any_vlan_flg = update_accept_any_vlan;
vport_update_params.accept_any_vlan = accept_any_vlan;
for_each_hwfn(cdev, i) { for_each_hwfn(cdev, i) {
struct qed_hwfn *p_hwfn = &cdev->hwfns[i]; struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
...@@ -471,6 +479,10 @@ static int qed_filter_accept_cmd(struct qed_dev *cdev, ...@@ -471,6 +479,10 @@ static int qed_filter_accept_cmd(struct qed_dev *cdev,
"Accept filter configured, flags = [Rx]%x [Tx]%x\n", "Accept filter configured, flags = [Rx]%x [Tx]%x\n",
accept_flags.rx_accept_filter, accept_flags.rx_accept_filter,
accept_flags.tx_accept_filter); accept_flags.tx_accept_filter);
if (update_accept_any_vlan)
DP_VERBOSE(p_hwfn, QED_MSG_SP,
"accept_any_vlan=%d configured\n",
accept_any_vlan);
} }
return 0; return 0;
...@@ -1347,6 +1359,9 @@ static int qed_update_vport(struct qed_dev *cdev, ...@@ -1347,6 +1359,9 @@ static int qed_update_vport(struct qed_dev *cdev,
params->update_vport_active_flg; params->update_vport_active_flg;
sp_params.vport_active_rx_flg = params->vport_active_flg; sp_params.vport_active_rx_flg = params->vport_active_flg;
sp_params.vport_active_tx_flg = params->vport_active_flg; sp_params.vport_active_tx_flg = params->vport_active_flg;
sp_params.accept_any_vlan = params->accept_any_vlan;
sp_params.update_accept_any_vlan_flg =
params->update_accept_any_vlan_flg;
/* RSS - is a bit tricky, since upper-layer isn't familiar with hwfns. /* RSS - is a bit tricky, since upper-layer isn't familiar with hwfns.
* We need to re-fix the rss values per engine for CMT. * We need to re-fix the rss values per engine for CMT.
...@@ -1566,7 +1581,7 @@ static int qed_configure_filter_rx_mode(struct qed_dev *cdev, ...@@ -1566,7 +1581,7 @@ static int qed_configure_filter_rx_mode(struct qed_dev *cdev,
else if (type == QED_FILTER_RX_MODE_TYPE_MULTI_PROMISC) else if (type == QED_FILTER_RX_MODE_TYPE_MULTI_PROMISC)
accept_flags.rx_accept_filter |= QED_ACCEPT_MCAST_UNMATCHED; accept_flags.rx_accept_filter |= QED_ACCEPT_MCAST_UNMATCHED;
return qed_filter_accept_cmd(cdev, 0, accept_flags, return qed_filter_accept_cmd(cdev, 0, accept_flags, false, false,
QED_SPQ_MODE_CB, NULL); QED_SPQ_MODE_CB, NULL);
} }
......
...@@ -100,6 +100,12 @@ struct qede_stats { ...@@ -100,6 +100,12 @@ struct qede_stats {
u64 tx_mac_ctrl_frames; u64 tx_mac_ctrl_frames;
}; };
struct qede_vlan {
struct list_head list;
u16 vid;
bool configured;
};
struct qede_dev { struct qede_dev {
struct qed_dev *cdev; struct qed_dev *cdev;
struct net_device *ndev; struct net_device *ndev;
...@@ -154,6 +160,10 @@ struct qede_dev { ...@@ -154,6 +160,10 @@ struct qede_dev {
u16 q_num_rx_buffers; /* Must be a power of two */ u16 q_num_rx_buffers; /* Must be a power of two */
u16 q_num_tx_buffers; /* Must be a power of two */ u16 q_num_tx_buffers; /* Must be a power of two */
struct list_head vlan_list;
u16 configured_vlans;
u16 non_configured_vlans;
bool accept_any_vlan;
struct delayed_work sp_task; struct delayed_work sp_task;
unsigned long sp_flags; unsigned long sp_flags;
}; };
......
...@@ -1140,6 +1140,21 @@ static int qede_set_ucast_rx_mac(struct qede_dev *edev, ...@@ -1140,6 +1140,21 @@ static int qede_set_ucast_rx_mac(struct qede_dev *edev,
return edev->ops->filter_config(edev->cdev, &filter_cmd); return edev->ops->filter_config(edev->cdev, &filter_cmd);
} }
static int qede_set_ucast_rx_vlan(struct qede_dev *edev,
enum qed_filter_xcast_params_type opcode,
u16 vid)
{
struct qed_filter_params filter_cmd;
memset(&filter_cmd, 0, sizeof(filter_cmd));
filter_cmd.type = QED_FILTER_TYPE_UCAST;
filter_cmd.filter.ucast.type = opcode;
filter_cmd.filter.ucast.vlan_valid = 1;
filter_cmd.filter.ucast.vlan = vid;
return edev->ops->filter_config(edev->cdev, &filter_cmd);
}
void qede_fill_by_demand_stats(struct qede_dev *edev) void qede_fill_by_demand_stats(struct qede_dev *edev)
{ {
struct qed_eth_stats stats; struct qed_eth_stats stats;
...@@ -1252,6 +1267,247 @@ static struct rtnl_link_stats64 *qede_get_stats64( ...@@ -1252,6 +1267,247 @@ static struct rtnl_link_stats64 *qede_get_stats64(
return stats; return stats;
} }
static void qede_config_accept_any_vlan(struct qede_dev *edev, bool action)
{
struct qed_update_vport_params params;
int rc;
/* Proceed only if action actually needs to be performed */
if (edev->accept_any_vlan == action)
return;
memset(&params, 0, sizeof(params));
params.vport_id = 0;
params.accept_any_vlan = action;
params.update_accept_any_vlan_flg = 1;
rc = edev->ops->vport_update(edev->cdev, &params);
if (rc) {
DP_ERR(edev, "Failed to %s accept-any-vlan\n",
action ? "enable" : "disable");
} else {
DP_INFO(edev, "%s accept-any-vlan\n",
action ? "enabled" : "disabled");
edev->accept_any_vlan = action;
}
}
static int qede_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
{
struct qede_dev *edev = netdev_priv(dev);
struct qede_vlan *vlan, *tmp;
int rc;
DP_VERBOSE(edev, NETIF_MSG_IFUP, "Adding vlan 0x%04x\n", vid);
vlan = kzalloc(sizeof(*vlan), GFP_KERNEL);
if (!vlan) {
DP_INFO(edev, "Failed to allocate struct for vlan\n");
return -ENOMEM;
}
INIT_LIST_HEAD(&vlan->list);
vlan->vid = vid;
vlan->configured = false;
/* Verify vlan isn't already configured */
list_for_each_entry(tmp, &edev->vlan_list, list) {
if (tmp->vid == vlan->vid) {
DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN),
"vlan already configured\n");
kfree(vlan);
return -EEXIST;
}
}
/* If interface is down, cache this VLAN ID and return */
if (edev->state != QEDE_STATE_OPEN) {
DP_VERBOSE(edev, NETIF_MSG_IFDOWN,
"Interface is down, VLAN %d will be configured when interface is up\n",
vid);
if (vid != 0)
edev->non_configured_vlans++;
list_add(&vlan->list, &edev->vlan_list);
return 0;
}
/* Check for the filter limit.
* Note - vlan0 has a reserved filter and can be added without
* worrying about quota
*/
if ((edev->configured_vlans < edev->dev_info.num_vlan_filters) ||
(vlan->vid == 0)) {
rc = qede_set_ucast_rx_vlan(edev,
QED_FILTER_XCAST_TYPE_ADD,
vlan->vid);
if (rc) {
DP_ERR(edev, "Failed to configure VLAN %d\n",
vlan->vid);
kfree(vlan);
return -EINVAL;
}
vlan->configured = true;
/* vlan0 filter isn't consuming out of our quota */
if (vlan->vid != 0)
edev->configured_vlans++;
} else {
/* Out of quota; Activate accept-any-VLAN mode */
if (!edev->non_configured_vlans)
qede_config_accept_any_vlan(edev, true);
edev->non_configured_vlans++;
}
list_add(&vlan->list, &edev->vlan_list);
return 0;
}
static void qede_del_vlan_from_list(struct qede_dev *edev,
struct qede_vlan *vlan)
{
/* vlan0 filter isn't consuming out of our quota */
if (vlan->vid != 0) {
if (vlan->configured)
edev->configured_vlans--;
else
edev->non_configured_vlans--;
}
list_del(&vlan->list);
kfree(vlan);
}
static int qede_configure_vlan_filters(struct qede_dev *edev)
{
int rc = 0, real_rc = 0, accept_any_vlan = 0;
struct qed_dev_eth_info *dev_info;
struct qede_vlan *vlan = NULL;
if (list_empty(&edev->vlan_list))
return 0;
dev_info = &edev->dev_info;
/* Configure non-configured vlans */
list_for_each_entry(vlan, &edev->vlan_list, list) {
if (vlan->configured)
continue;
/* We have used all our credits, now enable accept_any_vlan */
if ((vlan->vid != 0) &&
(edev->configured_vlans == dev_info->num_vlan_filters)) {
accept_any_vlan = 1;
continue;
}
DP_VERBOSE(edev, NETIF_MSG_IFUP, "Adding vlan %d\n", vlan->vid);
rc = qede_set_ucast_rx_vlan(edev, QED_FILTER_XCAST_TYPE_ADD,
vlan->vid);
if (rc) {
DP_ERR(edev, "Failed to configure VLAN %u\n",
vlan->vid);
real_rc = rc;
continue;
}
vlan->configured = true;
/* vlan0 filter doesn't consume our VLAN filter's quota */
if (vlan->vid != 0) {
edev->non_configured_vlans--;
edev->configured_vlans++;
}
}
/* enable accept_any_vlan mode if we have more VLANs than credits,
* or remove accept_any_vlan mode if we've actually removed
* a non-configured vlan, and all remaining vlans are truly configured.
*/
if (accept_any_vlan)
qede_config_accept_any_vlan(edev, true);
else if (!edev->non_configured_vlans)
qede_config_accept_any_vlan(edev, false);
return real_rc;
}
static int qede_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
{
struct qede_dev *edev = netdev_priv(dev);
struct qede_vlan *vlan = NULL;
int rc;
DP_VERBOSE(edev, NETIF_MSG_IFDOWN, "Removing vlan 0x%04x\n", vid);
/* Find whether entry exists */
list_for_each_entry(vlan, &edev->vlan_list, list)
if (vlan->vid == vid)
break;
if (!vlan || (vlan->vid != vid)) {
DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN),
"Vlan isn't configured\n");
return 0;
}
if (edev->state != QEDE_STATE_OPEN) {
/* As interface is already down, we don't have a VPORT
* instance to remove vlan filter. So just update vlan list
*/
DP_VERBOSE(edev, NETIF_MSG_IFDOWN,
"Interface is down, removing VLAN from list only\n");
qede_del_vlan_from_list(edev, vlan);
return 0;
}
/* Remove vlan */
rc = qede_set_ucast_rx_vlan(edev, QED_FILTER_XCAST_TYPE_DEL, vid);
if (rc) {
DP_ERR(edev, "Failed to remove VLAN %d\n", vid);
return -EINVAL;
}
qede_del_vlan_from_list(edev, vlan);
/* We have removed a VLAN - try to see if we can
* configure non-configured VLAN from the list.
*/
rc = qede_configure_vlan_filters(edev);
return rc;
}
static void qede_vlan_mark_nonconfigured(struct qede_dev *edev)
{
struct qede_vlan *vlan = NULL;
if (list_empty(&edev->vlan_list))
return;
list_for_each_entry(vlan, &edev->vlan_list, list) {
if (!vlan->configured)
continue;
vlan->configured = false;
/* vlan0 filter isn't consuming out of our quota */
if (vlan->vid != 0) {
edev->non_configured_vlans++;
edev->configured_vlans--;
}
DP_VERBOSE(edev, NETIF_MSG_IFDOWN,
"marked vlan %d as non-configured\n",
vlan->vid);
}
edev->accept_any_vlan = false;
}
static const struct net_device_ops qede_netdev_ops = { static const struct net_device_ops qede_netdev_ops = {
.ndo_open = qede_open, .ndo_open = qede_open,
.ndo_stop = qede_close, .ndo_stop = qede_close,
...@@ -1260,6 +1516,8 @@ static const struct net_device_ops qede_netdev_ops = { ...@@ -1260,6 +1516,8 @@ static const struct net_device_ops qede_netdev_ops = {
.ndo_set_mac_address = qede_set_mac_addr, .ndo_set_mac_address = qede_set_mac_addr,
.ndo_validate_addr = eth_validate_addr, .ndo_validate_addr = eth_validate_addr,
.ndo_change_mtu = qede_change_mtu, .ndo_change_mtu = qede_change_mtu,
.ndo_vlan_rx_add_vid = qede_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = qede_vlan_rx_kill_vid,
.ndo_get_stats64 = qede_get_stats64, .ndo_get_stats64 = qede_get_stats64,
}; };
...@@ -1304,6 +1562,8 @@ static struct qede_dev *qede_alloc_etherdev(struct qed_dev *cdev, ...@@ -1304,6 +1562,8 @@ static struct qede_dev *qede_alloc_etherdev(struct qed_dev *cdev,
edev->num_tc = edev->dev_info.num_tc; edev->num_tc = edev->dev_info.num_tc;
INIT_LIST_HEAD(&edev->vlan_list);
return edev; return edev;
} }
...@@ -1335,7 +1595,7 @@ static void qede_init_ndev(struct qede_dev *edev) ...@@ -1335,7 +1595,7 @@ static void qede_init_ndev(struct qede_dev *edev)
NETIF_F_HIGHDMA; NETIF_F_HIGHDMA;
ndev->features = hw_features | NETIF_F_RXHASH | NETIF_F_RXCSUM | ndev->features = hw_features | NETIF_F_RXHASH | NETIF_F_RXCSUM |
NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HIGHDMA | NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HIGHDMA |
NETIF_F_HW_VLAN_CTAG_TX; NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_CTAG_TX;
ndev->hw_features = hw_features; ndev->hw_features = hw_features;
...@@ -2342,6 +2602,7 @@ static void qede_unload(struct qede_dev *edev, enum qede_unload_mode mode) ...@@ -2342,6 +2602,7 @@ static void qede_unload(struct qede_dev *edev, enum qede_unload_mode mode)
DP_INFO(edev, "Stopped Queues\n"); DP_INFO(edev, "Stopped Queues\n");
qede_vlan_mark_nonconfigured(edev);
edev->ops->fastpath_stop(edev->cdev); edev->ops->fastpath_stop(edev->cdev);
/* Release the interrupts */ /* Release the interrupts */
...@@ -2410,6 +2671,9 @@ static int qede_load(struct qede_dev *edev, enum qede_load_mode mode) ...@@ -2410,6 +2671,9 @@ static int qede_load(struct qede_dev *edev, enum qede_load_mode mode)
edev->state = QEDE_STATE_OPEN; edev->state = QEDE_STATE_OPEN;
mutex_unlock(&edev->qede_lock); mutex_unlock(&edev->qede_lock);
/* Program un-configured VLANs */
qede_configure_vlan_filters(edev);
/* Ask for link-up using current configuration */ /* Ask for link-up using current configuration */
memset(&link_params, 0, sizeof(link_params)); memset(&link_params, 0, sizeof(link_params));
link_params.link_up = true; link_params.link_up = true;
...@@ -2670,6 +2934,17 @@ static void qede_config_rx_mode(struct net_device *ndev) ...@@ -2670,6 +2934,17 @@ static void qede_config_rx_mode(struct net_device *ndev)
goto out; goto out;
} }
/* take care of VLAN mode */
if (ndev->flags & IFF_PROMISC) {
qede_config_accept_any_vlan(edev, true);
} else if (!edev->non_configured_vlans) {
/* It's possible that accept_any_vlan mode is set due to a
* previous setting of IFF_PROMISC. If vlan credits are
* sufficient, disable accept_any_vlan.
*/
qede_config_accept_any_vlan(edev, false);
}
rx_mode.filter.accept_flags = accept_flags; rx_mode.filter.accept_flags = accept_flags;
edev->ops->filter_config(edev->cdev, &rx_mode); edev->ops->filter_config(edev->cdev, &rx_mode);
out: out:
......
...@@ -33,6 +33,8 @@ struct qed_update_vport_params { ...@@ -33,6 +33,8 @@ struct qed_update_vport_params {
u8 vport_id; u8 vport_id;
u8 update_vport_active_flg; u8 update_vport_active_flg;
u8 vport_active_flg; u8 vport_active_flg;
u8 update_accept_any_vlan_flg;
u8 accept_any_vlan;
u8 update_rss_flg; u8 update_rss_flg;
struct qed_update_vport_rss_params rss_params; struct qed_update_vport_rss_params rss_params;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment