* [PATCH 3/4] [next-queue]net: i40e: Enable mqprio full offload mode in the i40e driver for configuring TCs and queue mapping
From: Amritha Nambiar @ 2017-05-20 0:58 UTC (permalink / raw)
To: intel-wired-lan
Cc: alexander.h.duyck, kiran.patil, amritha.nambiar,
sridhar.samudrala, mitch.a.williams, neerav.parikh,
jeffrey.t.kirsher, netdev
In-Reply-To: <149524122523.11022.4541073724650541658.stgit@anamdev.jf.intel.com>
The i40e driver is modified to enable the new mqprio hardware
offload mode and factor the TCs and queue configuration by
creating channel VSIs. In this mode, the priority to traffic
class mapping and the user specified queue ranges are used
to configure the traffic classes when the 'hw' option is set
to 2.
Example:
# tc qdisc add dev eth0 root mqprio num_tc 4\
map 0 0 0 0 1 2 2 3 queues 2@0 2@2 1@4 1@5 hw 2
# tc qdisc show dev eth0
qdisc mqprio 8038: root tc 4 map 0 0 0 0 1 2 2 3 0 0 0 0 0 0 0 0
queues:(0:1) (2:3) (4:4) (5:5)
The HW channels created are removed and all the queue configuration
is set to default when the qdisc is detached from the root of the
device.
#tc qdisc del dev eth0 root
This patch also disables setting up channels via ethtool (ethtool -L)
when the TCs are confgured using mqprio scheduler.
Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
---
drivers/net/ethernet/intel/i40e/i40e.h | 4
drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 6
drivers/net/ethernet/intel/i40e/i40e_main.c | 311 ++++++++++++++++++++++--
3 files changed, 292 insertions(+), 29 deletions(-)
diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index 0915b02..a62f65a 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -54,6 +54,8 @@
#include <linux/clocksource.h>
#include <linux/net_tstamp.h>
#include <linux/ptp_clock_kernel.h>
+#include <net/pkt_cls.h>
+
#include "i40e_type.h"
#include "i40e_prototype.h"
#include "i40e_client.h"
@@ -685,6 +687,7 @@ struct i40e_vsi {
enum i40e_vsi_type type; /* VSI type, e.g., LAN, FCoE, etc */
s16 vf_id; /* Virtual function ID for SRIOV VSIs */
+ struct tc_mqprio_qopt_offload mqprio_qopt; /* queue parameters */
struct i40e_tc_configuration tc_config;
struct i40e_aqc_vsi_properties_data info;
@@ -710,6 +713,7 @@ struct i40e_vsi {
u16 cnt_q_avail; /* num of queues available for channel usage */
u16 orig_rss_size;
u16 current_rss_size;
+ bool reconfig_rss;
/* keeps track of next_base_queue to be used for channel setup */
atomic_t next_base_queue;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
index 3d58762..ab52979 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
@@ -3841,6 +3841,12 @@ static int i40e_set_channels(struct net_device *dev,
if (vsi->type != I40E_VSI_MAIN)
return -EINVAL;
+ /* We do not support setting channels via ethtool when TCs are
+ * configured through mqprio
+ */
+ if (pf->flags & I40E_FLAG_TC_MQPRIO)
+ return -EINVAL;
+
/* verify they are not requesting separate vectors */
if (!count || ch->rx_count || ch->tx_count)
return -EINVAL;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index e1bea45..7f61d4f 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -68,6 +68,7 @@ static int i40e_reset(struct i40e_pf *pf);
static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired);
static void i40e_fdir_sb_setup(struct i40e_pf *pf);
static int i40e_veb_get_bw_info(struct i40e_veb *veb);
+static int i40e_vsi_config_rss(struct i40e_vsi *vsi);
/* i40e_pci_tbl - PCI Device ID Table
*
@@ -1560,6 +1561,105 @@ static int i40e_set_mac(struct net_device *netdev, void *p)
}
/**
+ * i40e_vsi_setup_queue_map_mqprio - Prepares VSI tc_config to have queue
+ * configurations based on MQPRIO options.
+ * @vsi: the VSI being configured,
+ * @ctxt: VSI context structure
+ * @enabled_tc: number of traffic classes to enable
+ **/
+static int i40e_vsi_setup_queue_map_mqprio(struct i40e_vsi *vsi,
+ struct i40e_vsi_context *ctxt,
+ u8 enabled_tc)
+{
+ u8 netdev_tc = 0, offset = 0;
+ u16 qcount = 0, max_qcount, qmap, sections = 0;
+ int i, override_q, pow, num_qps, ret;
+
+ if (vsi->type != I40E_VSI_MAIN)
+ return -EINVAL;
+
+ sections = I40E_AQ_VSI_PROP_QUEUE_MAP_VALID;
+ sections |= I40E_AQ_VSI_PROP_SCHED_VALID;
+
+ vsi->tc_config.numtc = vsi->mqprio_qopt.qopt.num_tc;
+ vsi->tc_config.enabled_tc = enabled_tc ? enabled_tc : 1;
+
+ num_qps = vsi->mqprio_qopt.qopt.count[0];
+
+ /* find the next higher power-of-2 of num queue pairs */
+ pow = ilog2(num_qps);
+ if (!is_power_of_2(num_qps))
+ pow++;
+
+ qmap = (offset << I40E_AQ_VSI_TC_QUE_OFFSET_SHIFT) |
+ (pow << I40E_AQ_VSI_TC_QUE_NUMBER_SHIFT);
+
+ /* Setup queue offset/count for all TCs for given VSI */
+ max_qcount = vsi->mqprio_qopt.qopt.count[0];
+
+ for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
+ /* See if the given TC is enabled for the given VSI */
+ if (vsi->tc_config.enabled_tc & BIT(i)) {
+ offset = vsi->mqprio_qopt.qopt.offset[i];
+ qcount = vsi->mqprio_qopt.qopt.count[i];
+
+ if (qcount > max_qcount)
+ max_qcount = qcount;
+
+ vsi->tc_config.tc_info[i].qoffset = offset;
+ vsi->tc_config.tc_info[i].qcount = qcount;
+ vsi->tc_config.tc_info[i].netdev_tc = netdev_tc++;
+
+ } else {
+ /* TC is not enabled so set the offset to
+ * default queue and allocate one queue
+ * for the given TC.
+ */
+ vsi->tc_config.tc_info[i].qoffset = 0;
+ vsi->tc_config.tc_info[i].qcount = 1;
+ vsi->tc_config.tc_info[i].netdev_tc = 0;
+ }
+ }
+
+ /* Set actual Tx/Rx queue pairs */
+ vsi->num_queue_pairs = offset + qcount;
+
+ /* Setup queue TC[0].qmap for given VSI context */
+ ctxt->info.tc_mapping[0] = cpu_to_le16(qmap);
+
+ ctxt->info.mapping_flags |=
+ cpu_to_le16(I40E_AQ_VSI_QUE_MAP_CONTIG);
+
+ ctxt->info.queue_mapping[0] = cpu_to_le16(vsi->base_queue);
+ ctxt->info.valid_sections |= cpu_to_le16(sections);
+
+ /* Reconfigure RSS for main VSI with max queue count */
+ vsi->rss_size = max_qcount;
+
+ ret = i40e_vsi_config_rss(vsi);
+ if (ret) {
+ dev_info(&vsi->back->pdev->dev,
+ "Failed to reconfig rss for num_queues (%u)\n",
+ max_qcount);
+ return ret;
+ }
+ vsi->reconfig_rss = true;
+ dev_dbg(&vsi->back->pdev->dev,
+ "Reconfigured rss with num_queues (%u)\n", max_qcount);
+
+ /* Find queue count available for channel VSIs and starting offset
+ * for channel VSIs
+ */
+ override_q = vsi->mqprio_qopt.qopt.count[0];
+ if (override_q && (override_q < vsi->num_queue_pairs)) {
+ vsi->cnt_q_avail = vsi->num_queue_pairs - override_q;
+ atomic_set(&vsi->next_base_queue, override_q);
+ }
+
+ return 0;
+}
+
+/**
* i40e_vsi_setup_queue_map - Setup a VSI queue map based on enabled_tc
* @vsi: the VSI being setup
* @ctxt: VSI context structure
@@ -1597,7 +1697,7 @@ static void i40e_vsi_setup_queue_map(struct i40e_vsi *vsi,
numtc = 1;
}
} else {
- /* At least TC0 is enabled in case of non-DCB case */
+ /* At least TC0 is enabled in non-DCB, non-MQPRIO case */
numtc = 1;
}
@@ -3150,6 +3250,7 @@ static void i40e_vsi_config_dcb_rings(struct i40e_vsi *vsi)
rx_ring->dcb_tc = 0;
tx_ring->dcb_tc = 0;
}
+ return;
}
for (n = 0; n < I40E_MAX_TRAFFIC_CLASS; n++) {
@@ -4777,6 +4878,25 @@ static u8 i40e_dcb_get_enabled_tc(struct i40e_dcbx_config *dcbcfg)
}
/**
+ * i40e_mqprio_get_enabled_tc - Get enabled traffic classes
+ * @pf: PF being queried
+ *
+ * Query the current MQPRIO configuration and return the number of
+ * traffic classes enabled.
+ **/
+static u8 i40e_mqprio_get_enabled_tc(struct i40e_pf *pf)
+{
+ struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi];
+ u8 num_tc = vsi->mqprio_qopt.qopt.num_tc;
+ u8 enabled_tc = 1, i;
+
+ for (i = 1; i < num_tc; i++)
+ enabled_tc |= BIT(i);
+
+ return enabled_tc;
+}
+
+/**
* i40e_pf_get_num_tc - Get enabled traffic classes for PF
* @pf: PF being queried
*
@@ -4789,7 +4909,10 @@ static u8 i40e_pf_get_num_tc(struct i40e_pf *pf)
u8 num_tc = 0;
struct i40e_dcbx_config *dcbcfg = &hw->local_dcbx_config;
- /* If DCB is not enabled then always in single TC */
+ if (pf->flags & I40E_FLAG_TC_MQPRIO)
+ return pf->vsi[pf->lan_vsi]->mqprio_qopt.qopt.num_tc;
+
+ /* If neither MQPRIO nor DCB is enabled, then always in single TC */
if (!(pf->flags & I40E_FLAG_DCB_ENABLED))
return 1;
@@ -4818,7 +4941,12 @@ static u8 i40e_pf_get_num_tc(struct i40e_pf *pf)
**/
static u8 i40e_pf_get_tc_map(struct i40e_pf *pf)
{
- /* If DCB is not enabled for this PF then just return default TC */
+ if (pf->flags & I40E_FLAG_TC_MQPRIO)
+ return i40e_mqprio_get_enabled_tc(pf);
+
+ /* If neither MQPRIO nor DCB is enabled for this PF then just return
+ * default TC
+ */
if (!(pf->flags & I40E_FLAG_DCB_ENABLED))
return I40E_DEFAULT_TRAFFIC_CLASS;
@@ -4912,6 +5040,10 @@ static int i40e_vsi_configure_bw_alloc(struct i40e_vsi *vsi, u8 enabled_tc,
for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++)
bw_data.tc_bw_credits[i] = bw_share[i];
+ if ((vsi->back->flags & I40E_FLAG_TC_MQPRIO) ||
+ !vsi->mqprio_qopt.qopt.hw)
+ return 0;
+
ret = i40e_aq_config_vsi_tc_bw(&vsi->back->hw, vsi->seid, &bw_data,
NULL);
if (ret) {
@@ -4970,6 +5102,9 @@ static void i40e_vsi_config_netdev_tc(struct i40e_vsi *vsi, u8 enabled_tc)
vsi->tc_config.tc_info[i].qoffset);
}
+ if (pf->flags & I40E_FLAG_TC_MQPRIO)
+ return;
+
/* Assign UP2TC map for the VSI */
for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
/* Get the actual TC# for the UP */
@@ -5020,7 +5155,8 @@ static int i40e_vsi_config_tc(struct i40e_vsi *vsi, u8 enabled_tc)
int i;
/* Check if enabled_tc is same as existing or new TCs */
- if (vsi->tc_config.enabled_tc == enabled_tc)
+ if (vsi->tc_config.enabled_tc == enabled_tc &&
+ vsi->mqprio_qopt.qopt.hw != TC_MQPRIO_HW_OFFLOAD)
return ret;
/* Enable ETS TCs with equal BW Share for now across all VSIs */
@@ -5043,7 +5179,30 @@ static int i40e_vsi_config_tc(struct i40e_vsi *vsi, u8 enabled_tc)
ctxt.vf_num = 0;
ctxt.uplink_seid = vsi->uplink_seid;
ctxt.info = vsi->info;
- i40e_vsi_setup_queue_map(vsi, &ctxt, enabled_tc, false);
+
+ if (vsi->back->flags & I40E_FLAG_TC_MQPRIO) {
+ ret = i40e_vsi_setup_queue_map_mqprio(vsi, &ctxt, enabled_tc);
+ if (ret)
+ goto out;
+
+ } else {
+ i40e_vsi_setup_queue_map(vsi, &ctxt, enabled_tc, false);
+ }
+
+ /* On destroying the qdisc, reset vsi->rss_size, as number of enabled
+ * queues changed.
+ */
+ if (!vsi->mqprio_qopt.qopt.hw && vsi->reconfig_rss) {
+ vsi->rss_size = min_t(int, vsi->back->alloc_rss_size,
+ vsi->num_queue_pairs);
+ ret = i40e_vsi_config_rss(vsi);
+ if (ret) {
+ dev_info(&vsi->back->pdev->dev,
+ "Failed to reconfig rss for num_queues\n");
+ return ret;
+ }
+ vsi->reconfig_rss = false;
+ }
if (vsi->back->flags & I40E_FLAG_IWARP_ENABLED) {
ctxt.info.valid_sections |=
@@ -5051,7 +5210,9 @@ static int i40e_vsi_config_tc(struct i40e_vsi *vsi, u8 enabled_tc)
ctxt.info.queueing_opt_flags |= I40E_AQ_VSI_QUE_OPT_TCP_ENA;
}
- /* Update the VSI after updating the VSI queue-mapping information */
+ /* Update the VSI after updating the VSI queue-mapping
+ * information
+ */
ret = i40e_aq_update_vsi_params(&vsi->back->hw, &ctxt, NULL);
if (ret) {
dev_info(&vsi->back->pdev->dev,
@@ -6168,48 +6329,142 @@ void i40e_down(struct i40e_vsi *vsi)
}
/**
+ * i40e_validate_mqprio_queue_mapping - validate queue mapping info
+ * @vsi: the VSI being configured
+ * @mqprio_qopt: queue parametrs
+ **/
+int i40e_validate_mqprio_queue_mapping(struct i40e_vsi *vsi,
+ struct tc_mqprio_qopt_offload *mqprio_qopt)
+{
+ int i;
+
+ if ((mqprio_qopt->qopt.offset[0] != 0) ||
+ (mqprio_qopt->qopt.num_tc < 1))
+ return -EINVAL;
+
+ for (i = 0; ; i++) {
+ if (!mqprio_qopt->qopt.count[i])
+ return -EINVAL;
+
+ if (mqprio_qopt->min_rate[i] || mqprio_qopt->max_rate[i])
+ return -EINVAL;
+
+ if (i >= mqprio_qopt->qopt.num_tc - 1)
+ break;
+
+ if (mqprio_qopt->qopt.offset[i + 1] !=
+ (mqprio_qopt->qopt.offset[i] + mqprio_qopt->qopt.count[i]))
+ return -EINVAL;
+ }
+
+ if (vsi->num_queue_pairs <
+ (mqprio_qopt->qopt.offset[i] + mqprio_qopt->qopt.count[i])) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
* i40e_setup_tc - configure multiple traffic classes
* @netdev: net device to configure
- * @tc: number of traffic classes to enable
+ * @tc: pointer to struct tc_to_netdev
**/
-static int i40e_setup_tc(struct net_device *netdev, u8 tc)
+static int i40e_setup_tc(struct net_device *netdev, struct tc_to_netdev *tc)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_vsi *vsi = np->vsi;
struct i40e_pf *pf = vsi->back;
- u8 enabled_tc = 0;
+ u8 enabled_tc = 0, num_tc = 0, hw = 0;
int ret = -EINVAL;
int i;
- /* Check if DCB enabled to continue */
- if (!(pf->flags & I40E_FLAG_DCB_ENABLED)) {
- netdev_info(netdev, "DCB is not enabled for adapter\n");
- goto exit;
+ if (tc->type == TC_SETUP_MQPRIO) {
+ hw = tc->mqprio->hw;
+ num_tc = tc->mqprio->num_tc;
+ } else if (tc->type == TC_SETUP_MQPRIO_EXT) {
+ hw = tc->mqprio_qopt->qopt.hw;
+ num_tc = tc->mqprio_qopt->qopt.num_tc;
}
- /* Check if MFP enabled */
- if (pf->flags & I40E_FLAG_MFP_ENABLED) {
- netdev_info(netdev, "Configuring TC not supported in MFP mode\n");
- goto exit;
+ if (!hw) {
+ pf->flags &= ~I40E_FLAG_TC_MQPRIO;
+ if (tc->type == TC_SETUP_MQPRIO_EXT)
+ memcpy(&vsi->mqprio_qopt, tc->mqprio_qopt,
+ sizeof(*tc->mqprio_qopt));
+ goto config_tc;
}
- /* Check whether tc count is within enabled limit */
- if (tc > i40e_pf_get_num_tc(pf)) {
- netdev_info(netdev, "TC count greater than enabled on link for adapter\n");
- goto exit;
+ switch (hw) {
+ case TC_MQPRIO_HW_OFFLOAD_TCS:
+ pf->flags &= ~I40E_FLAG_TC_MQPRIO;
+ /* Check if DCB enabled to continue */
+ if (!(pf->flags & I40E_FLAG_DCB_ENABLED)) {
+ netdev_info(netdev,
+ "DCB is not enabled for adapter\n");
+ goto exit;
+ }
+
+ /* Check if MFP enabled */
+ if (pf->flags & I40E_FLAG_MFP_ENABLED) {
+ netdev_info(netdev,
+ "Configuring TC not supported in MFP mode\n");
+ goto exit;
+ }
+
+ /* Check whether tc count is within enabled limit */
+ if (num_tc > i40e_pf_get_num_tc(pf)) {
+ netdev_info(netdev,
+ "TC count greater than enabled on link for adapter\n");
+ goto exit;
+ }
+ break;
+ case TC_MQPRIO_HW_OFFLOAD:
+ if (pf->flags & I40E_FLAG_DCB_ENABLED) {
+ netdev_info(netdev,
+ "Full offload of TC Mqprio options is not supported when DCB is enabled\n");
+ goto exit;
+ }
+
+ /* Check if MFP enabled */
+ if (pf->flags & I40E_FLAG_MFP_ENABLED) {
+ netdev_info(netdev,
+ "Configuring TC not supported in MFP mode\n");
+ goto exit;
+ }
+
+ ret = i40e_validate_mqprio_queue_mapping(vsi,
+ tc->mqprio_qopt);
+ if (ret)
+ goto exit;
+
+ memcpy(&vsi->mqprio_qopt, tc->mqprio_qopt,
+ sizeof(*tc->mqprio_qopt));
+
+ pf->flags |= I40E_FLAG_TC_MQPRIO;
+ pf->flags &= ~I40E_FLAG_DCB_ENABLED;
+
+ break;
+ default:
+ return -EINVAL;
}
+config_tc:
/* Generate TC map for number of tc requested */
- for (i = 0; i < tc; i++)
+ for (i = 0; i < num_tc; i++)
enabled_tc |= BIT(i);
/* Requesting same TC configuration as already enabled */
- if (enabled_tc == vsi->tc_config.enabled_tc)
+ if (enabled_tc == vsi->tc_config.enabled_tc &&
+ hw != TC_MQPRIO_HW_OFFLOAD)
return 0;
/* Quiesce VSI queues */
i40e_quiesce_vsi(vsi);
+ if (!hw && !(pf->flags & I40E_FLAG_TC_MQPRIO))
+ i40e_remove_queue_channel(vsi);
+
/* Configure VSI for enabled TCs */
ret = i40e_vsi_config_tc(vsi, enabled_tc);
if (ret) {
@@ -6229,8 +6484,11 @@ static int i40e_setup_tc(struct net_device *netdev, u8 tc)
/* Unquiesce VSI */
i40e_unquiesce_vsi(vsi);
+ return ret;
exit:
+ /* Reset the configuration data */
+ memset(&vsi->tc_config, 0, sizeof(vsi->tc_config));
i40e_unquiesce_vsi(vsi);
return ret;
}
@@ -6238,12 +6496,7 @@ static int i40e_setup_tc(struct net_device *netdev, u8 tc)
static int __i40e_setup_tc(struct net_device *netdev, u32 handle, __be16 proto,
struct tc_to_netdev *tc)
{
- if (tc->type != TC_SETUP_MQPRIO)
- return -EINVAL;
-
- tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS;
-
- return i40e_setup_tc(netdev, tc->mqprio->num_tc);
+ return i40e_setup_tc(netdev, tc);
}
/**
^ permalink raw reply related
* [PATCH 2/4] [next-queue]net: i40e: Add infrastructure for queue channel support with the TCs and queue configurations offloaded via mqprio scheduler
From: Amritha Nambiar @ 2017-05-20 0:58 UTC (permalink / raw)
To: intel-wired-lan
Cc: alexander.h.duyck, kiran.patil, amritha.nambiar,
sridhar.samudrala, mitch.a.williams, neerav.parikh,
jeffrey.t.kirsher, netdev
In-Reply-To: <149524122523.11022.4541073724650541658.stgit@anamdev.jf.intel.com>
This patch sets up the infrastructure for offloading TCs and
queue configurations to the hardware by creating HW channels(VSI).
A new channel is created for each of the traffic class
configuration offloaded via mqprio framework except for the first TC
(TC0). TC0 for the main VSI is also reconfigured as per user provided
queue parameters. Queue counts that are not power-of-2 are handled by
reconfiguring RSS by reprogramming LUTs using the queue count value.
This patch also handles configuring the TX rings for the channels,
setting up the RX queue map for channel.
Also, the channels so created are removed and all the queue
configuration is set to default when the qdisc is detached from the
root of the device.
Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
Signed-off-by: Kiran Patil <kiran.patil@intel.com>
---
drivers/net/ethernet/intel/i40e/i40e.h | 36 +
drivers/net/ethernet/intel/i40e/i40e_main.c | 740 +++++++++++++++++++++++++++
drivers/net/ethernet/intel/i40e/i40e_txrx.h | 2
3 files changed, 771 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index 395ca94..0915b02 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -330,6 +330,24 @@ struct i40e_flex_pit {
u8 pit_index;
};
+struct i40e_channel {
+ struct list_head list;
+ bool initialized;
+ u8 type;
+ u16 vsi_number;
+ u16 stat_counter_idx;
+ u16 base_queue;
+ u16 num_queue_pairs; /* Requested by user */
+ u16 allowed_queue_pairs;
+ u16 seid;
+
+ u8 enabled_tc;
+ struct i40e_aqc_vsi_properties_data info;
+
+ /* track this channel belongs to which VSI */
+ struct i40e_vsi *parent_vsi;
+};
+
/* struct that defines the Ethernet device */
struct i40e_pf {
struct pci_dev *pdev;
@@ -442,6 +460,7 @@ struct i40e_pf {
#define I40E_FLAG_CLIENT_L2_CHANGE BIT_ULL(56)
#define I40E_FLAG_WOL_MC_MAGIC_PKT_WAKE BIT_ULL(57)
#define I40E_FLAG_LEGACY_RX BIT_ULL(58)
+#define I40E_FLAG_TC_MQPRIO BIT_ULL(59)
struct i40e_client_instance *cinst;
bool stat_offsets_loaded;
@@ -523,6 +542,9 @@ struct i40e_pf {
u32 ioremap_len;
u32 fd_inv;
u16 phy_led_val;
+
+#define I40E_MAX_QUEUES_PER_CH 64
+ u16 override_q_count;
};
/**
@@ -684,6 +706,16 @@ struct i40e_vsi {
bool current_isup; /* Sync 'link up' logging */
enum i40e_aq_link_speed current_speed; /* Sync link speed logging */
+ /* channel specific fields */
+ u16 cnt_q_avail; /* num of queues available for channel usage */
+ u16 orig_rss_size;
+ u16 current_rss_size;
+
+ /* keeps track of next_base_queue to be used for channel setup */
+ atomic_t next_base_queue;
+
+ struct list_head ch_list;
+
void *priv; /* client driver data reference. */
/* VSI specific handlers */
@@ -716,6 +748,9 @@ struct i40e_q_vector {
bool arm_wb_state;
#define ITR_COUNTDOWN_START 100
u8 itr_countdown; /* when 0 should adjust ITR */
+
+ /* Following field(s) are specific to channel usage */
+ bool is_an_atq;
} ____cacheline_internodealigned_in_smp;
/* lan device */
@@ -972,4 +1007,5 @@ i40e_status i40e_get_npar_bw_setting(struct i40e_pf *pf);
i40e_status i40e_set_npar_bw_setting(struct i40e_pf *pf);
i40e_status i40e_commit_npar_bw_setting(struct i40e_pf *pf);
void i40e_print_link_message(struct i40e_vsi *vsi, bool isup);
+int i40e_create_queue_channel(struct i40e_vsi *vsi, struct i40e_channel *ch);
#endif /* _I40E_H_ */
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 8d1d3b85..e1bea45 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -2864,7 +2864,7 @@ static void i40e_config_xps_tx_ring(struct i40e_ring *ring)
struct i40e_vsi *vsi = ring->vsi;
cpumask_var_t mask;
- if (!ring->q_vector || !ring->netdev)
+ if (!ring->q_vector || !ring->netdev || ring->ch)
return;
/* Single TC mode enable XPS */
@@ -2937,7 +2937,17 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring)
* initialization. This has to be done regardless of
* DCB as by default everything is mapped to TC0.
*/
- tx_ctx.rdylist = le16_to_cpu(vsi->info.qs_handle[ring->dcb_tc]);
+
+ if (ring->ch) {
+ tx_ctx.rdylist =
+ le16_to_cpu(ring->ch->info.qs_handle[ring->dcb_tc]);
+
+ dev_dbg(&vsi->back->pdev->dev, "ch, pf_q %d, rdylist %d\n",
+ pf_q, tx_ctx.rdylist);
+ } else {
+ tx_ctx.rdylist = le16_to_cpu(vsi->info.qs_handle[ring->dcb_tc]);
+ }
+
tx_ctx.rdylist_act = 0;
/* clear the context in the HMC */
@@ -2959,12 +2969,25 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring)
}
/* Now associate this queue with this PCI function */
- if (vsi->type == I40E_VSI_VMDQ2) {
- qtx_ctl = I40E_QTX_CTL_VM_QUEUE;
- qtx_ctl |= ((vsi->id) << I40E_QTX_CTL_VFVM_INDX_SHIFT) &
- I40E_QTX_CTL_VFVM_INDX_MASK;
+ if (ring->ch) {
+ if (ring->ch->type == I40E_VSI_VMDQ2)
+ qtx_ctl = I40E_QTX_CTL_VM_QUEUE;
+ else if (ring->ch->type == I40E_VSI_SRIOV)
+ qtx_ctl = I40E_QTX_CTL_VF_QUEUE;
+
+ qtx_ctl |= (ring->ch->vsi_number <<
+ I40E_QTX_CTL_VFVM_INDX_SHIFT) &
+ I40E_QTX_CTL_VFVM_INDX_MASK;
+ dev_dbg(&vsi->back->pdev->dev, "ch, pf_q %d, qtx_ctl 0x%x\n",
+ pf_q, qtx_ctl);
} else {
- qtx_ctl = I40E_QTX_CTL_PF_QUEUE;
+ if (vsi->type == I40E_VSI_VMDQ2) {
+ qtx_ctl = I40E_QTX_CTL_VM_QUEUE;
+ qtx_ctl |= ((vsi->id) << I40E_QTX_CTL_VFVM_INDX_SHIFT) &
+ I40E_QTX_CTL_VFVM_INDX_MASK;
+ } else {
+ qtx_ctl = I40E_QTX_CTL_PF_QUEUE;
+ }
}
qtx_ctl |= ((hw->pf_id << I40E_QTX_CTL_PF_INDX_SHIFT) &
@@ -5060,6 +5083,699 @@ static int i40e_vsi_config_tc(struct i40e_vsi *vsi, u8 enabled_tc)
}
/**
+ * i40e_remove_queue_channel - Remove queue channel for the TC
+ * @vsi: VSI to be configured
+ *
+ * Remove queue channel for the TC
+ **/
+static void i40e_remove_queue_channel(struct i40e_vsi *vsi)
+{
+ struct i40e_channel *ch, *ch_tmp;
+ int ret, i;
+
+ /* Reset rss size that was stored when reconfiguring rss for
+ * channel VSIs with non-power-of-2 queue count.
+ */
+ vsi->current_rss_size = 0;
+
+ /* perform cleanup for channels if they exist */
+ if (list_empty(&vsi->ch_list))
+ return;
+
+ list_for_each_entry_safe(ch, ch_tmp, &vsi->ch_list, list) {
+ struct i40e_vsi *p_vsi;
+
+ list_del(&ch->list);
+ p_vsi = ch->parent_vsi;
+ if (!p_vsi || !ch->initialized) {
+ kfree(ch);
+ continue;
+ }
+ /* Reset queue contexts */
+ for (i = 0; i < ch->num_queue_pairs; i++) {
+ struct i40e_ring *tx_ring, *rx_ring;
+ u16 pf_q;
+
+ pf_q = ch->base_queue + i;
+ tx_ring = vsi->tx_rings[pf_q];
+ tx_ring->ch = NULL;
+
+ rx_ring = vsi->rx_rings[pf_q];
+ rx_ring->ch = NULL;
+ }
+
+ /* delete VSI from FW */
+ ret = i40e_aq_delete_element(&vsi->back->hw, ch->seid,
+ NULL);
+ if (ret)
+ dev_err(&vsi->back->pdev->dev,
+ "unable to remove channel (%d) for parent VSI(%d)\n",
+ ch->seid, p_vsi->seid);
+ kfree(ch);
+ }
+}
+
+/**
+ * i40e_is_any_channel - channel exist or not
+ * @vsi: ptr to VSI to which channels are associated with
+ *
+ * Returns true or false if channel(s) exist for associated VSI or not
+ **/
+static bool i40e_is_any_channel(struct i40e_vsi *vsi)
+{
+ struct i40e_channel *ch, *ch_tmp;
+
+ list_for_each_entry_safe(ch, ch_tmp, &vsi->ch_list, list) {
+ if (ch->initialized)
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * i40e_get_max_queues_for_channel
+ * @vsi: ptr to VSI to which channels are associated with
+ *
+ * Helper function which returns max_queues count ever used for any of the
+ * channel which are parent of specified VSI
+ **/
+static int i40e_get_max_queues_for_channel(struct i40e_vsi *vsi)
+{
+ struct i40e_channel *ch, *ch_tmp;
+ int max = 0;
+
+ list_for_each_entry_safe(ch, ch_tmp, &vsi->ch_list, list) {
+ if (!ch->initialized)
+ continue;
+ if (ch->allowed_queue_pairs > max)
+ max = ch->allowed_queue_pairs;
+ }
+
+ return max;
+}
+
+/**
+ * i40e_validate_num_queues - validate num_queues w.r.t channel
+ * @pf: ptr to PF device
+ * @num_queues: number of queues
+ * @vsi: the parent VSI
+ * @reconfig_rss: indicates should the RSS be reconfigured or not
+ *
+ * This function validates number of queues in the context of new channel
+ * which is being established and determines if RSS should be reconfigured
+ * or not for parent VSI.
+ **/
+static int i40e_validate_num_queues(struct i40e_pf *pf, int num_queues,
+ struct i40e_vsi *vsi, bool *reconfig_rss)
+{
+ int max_ch_queues;
+
+ if (!reconfig_rss)
+ return -EINVAL;
+
+ *reconfig_rss = false;
+
+ if (num_queues > I40E_MAX_QUEUES_PER_CH) {
+ dev_info(&pf->pdev->dev,
+ "Failed to create VMDq VSI. User requested num_queues (%d) > I40E_MAX_QUEUES_PER_VSI (%u)\n",
+ num_queues, I40E_MAX_QUEUES_PER_CH);
+ return -EINVAL;
+ }
+
+ if (vsi->current_rss_size) {
+ if (num_queues > vsi->current_rss_size) {
+ dev_info(&pf->pdev->dev,
+ "Error: num_queues (%d) > vsi's current_size(%d)\n",
+ num_queues, vsi->current_rss_size);
+ return -EINVAL;
+ } else if ((num_queues < vsi->current_rss_size) &&
+ (!is_power_of_2(num_queues))) {
+ dev_info(&pf->pdev->dev,
+ "Error: num_queues (%d) < vsi's current_size(%d), but not power of 2\n",
+ num_queues, vsi->current_rss_size);
+ return -EINVAL;
+ }
+ }
+
+ if (!is_power_of_2(num_queues)) {
+ /* Find the max num_queues configures for channel if channel
+ * exist.
+ * if channel exist, then enforce 'num_queues' to be more than
+ * max ever num_queues configured for channel.
+ */
+ max_ch_queues = i40e_get_max_queues_for_channel(vsi);
+ if (num_queues < max_ch_queues) {
+ dev_info(&pf->pdev->dev,
+ "Error: num_queues (%d) > main_vsi's current_size(%d)\n",
+ num_queues, vsi->current_rss_size);
+ return -EINVAL;
+ }
+ *reconfig_rss = true;
+ }
+
+ return 0;
+}
+
+/**
+ * i40e_vsi_reconfig_rss - reconfig RSS based on specified rss_size
+ * @vsi: the VSI being setup
+ * @rss_size: size of RSS, accordingly LUT gets reprogrammed
+ *
+ * This function reconfigures RSS by reprogramming LUTs using 'rss_size'
+ **/
+static int i40e_vsi_reconfig_rss(struct i40e_vsi *vsi, u16 rss_size)
+{
+ struct i40e_pf *pf = vsi->back;
+ u8 seed[I40E_HKEY_ARRAY_SIZE];
+ struct i40e_hw *hw = &pf->hw;
+ int local_rss_size;
+ u8 *lut;
+ int ret;
+
+ if (!vsi->rss_size)
+ return -EINVAL;
+
+ if (rss_size > vsi->rss_size)
+ return -EINVAL;
+
+ local_rss_size = min_t(int, vsi->rss_size, rss_size);
+ lut = kzalloc(vsi->rss_table_size, GFP_KERNEL);
+ if (!lut)
+ return -ENOMEM;
+
+ /* Ignoring user configured lut if there is one */
+ i40e_fill_rss_lut(pf, lut, vsi->rss_table_size, local_rss_size);
+
+ /* Use user configured hash key if there is one, otherwise
+ * use default.
+ */
+ if (vsi->rss_hkey_user)
+ memcpy(seed, vsi->rss_hkey_user, I40E_HKEY_ARRAY_SIZE);
+ else
+ netdev_rss_key_fill((void *)seed, I40E_HKEY_ARRAY_SIZE);
+
+ ret = i40e_config_rss(vsi, seed, lut, vsi->rss_table_size);
+ if (ret) {
+ dev_info(&pf->pdev->dev,
+ "Cannot set RSS lut, err %s aq_err %s\n",
+ i40e_stat_str(hw, ret),
+ i40e_aq_str(hw, hw->aq.asq_last_status));
+ kfree(lut);
+ return ret;
+ }
+ kfree(lut);
+
+ /* Do the update w.r.t. storing rss_size */
+ if (!vsi->orig_rss_size)
+ vsi->orig_rss_size = vsi->rss_size;
+ vsi->current_rss_size = local_rss_size;
+
+ return ret;
+}
+
+/**
+ * i40e_channel_setup_queue_map - Setup a channel queue map based on enabled_tc
+ * @pf: ptr to PF device
+ * @vsi: the VSI being setup
+ * @ctxt: VSI context structure
+ * @enabled_tc: Enabled TCs bitmap
+ * @ch: ptr to channel structure
+ *
+ * Setup queue map based on enabled_tc for specific channel
+ **/
+static void i40e_channel_setup_queue_map(struct i40e_pf *pf,
+ struct i40e_vsi_context *ctxt,
+ u8 enabled_tc, struct i40e_channel *ch)
+{
+ u16 qcount, num_tc_qps, qmap;
+ int pow, num_qps;
+ u16 sections = 0;
+ /* At least TC0 is enabled in case of non-DCB case, non-MQPRIO */
+ u16 numtc = 1;
+ u8 offset = 0;
+
+ sections = I40E_AQ_VSI_PROP_QUEUE_MAP_VALID;
+ sections |= I40E_AQ_VSI_PROP_SCHED_VALID;
+
+ if (pf->flags & I40E_FLAG_MSIX_ENABLED) {
+ qcount = min_t(int, ch->num_queue_pairs,
+ pf->num_lan_msix);
+ ch->allowed_queue_pairs = qcount;
+ } else {
+ qcount = 1;
+ }
+
+ /* find num of qps per traffic class */
+ num_tc_qps = qcount / numtc;
+ num_tc_qps = min_t(int, num_tc_qps, i40e_pf_get_max_q_per_tc(pf));
+ num_qps = qcount;
+
+ /* find the next higher power-of-2 of num queue pairs */
+ pow = ilog2(num_qps);
+ if (!is_power_of_2(num_qps))
+ pow++;
+
+ qmap = (offset << I40E_AQ_VSI_TC_QUE_OFFSET_SHIFT) |
+ (pow << I40E_AQ_VSI_TC_QUE_NUMBER_SHIFT);
+
+ /* Setup queue TC[0].qmap for given VSI context */
+ ctxt->info.tc_mapping[0] = cpu_to_le16(qmap);
+
+ ctxt->info.up_enable_bits = enabled_tc;
+ ctxt->info.mapping_flags |= cpu_to_le16(I40E_AQ_VSI_QUE_MAP_CONTIG);
+ ctxt->info.queue_mapping[0] = cpu_to_le16(ch->base_queue);
+ ctxt->info.valid_sections |= cpu_to_le16(sections);
+}
+
+/**
+ * i40e_add_channel - add a channel by adding VSI
+ * @pf: ptr to PF device
+ * @uplink_seid: underlying HW switching element (VEB) ID
+ * @ch: ptr to channel structure
+ *
+ * Add a channel (VSI) using add_vsi and queue_map
+ **/
+static int i40e_add_channel(struct i40e_pf *pf, u16 uplink_seid,
+ struct i40e_channel *ch)
+{
+ struct i40e_hw *hw = &pf->hw;
+ struct i40e_vsi_context ctxt;
+ u8 enabled_tc = 0x1; /* TC0 enabled */
+ int ret;
+
+ if (!(ch->type == I40E_VSI_SRIOV || ch->type == I40E_VSI_VMDQ2)) {
+ dev_info(&pf->pdev->dev,
+ "add new vsi failed, ch->type %d\n", ch->type);
+ return -EINVAL;
+ }
+
+ memset(&ctxt, 0, sizeof(ctxt));
+ ctxt.pf_num = hw->pf_id;
+ ctxt.vf_num = 0;
+ ctxt.uplink_seid = uplink_seid;
+ ctxt.connection_type = I40E_AQ_VSI_CONN_TYPE_NORMAL;
+ if (ch->type == I40E_VSI_SRIOV)
+ ctxt.flags = I40E_AQ_VSI_TYPE_VF;
+ else if (ch->type == I40E_VSI_VMDQ2)
+ ctxt.flags = I40E_AQ_VSI_TYPE_VMDQ2;
+
+ if (pf->flags & I40E_FLAG_VEB_MODE_ENABLED) {
+ 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);
+ }
+
+ /* Set queue map for a given VSI context */
+ i40e_channel_setup_queue_map(pf, &ctxt, enabled_tc, ch);
+
+ /* Now time to create VSI */
+ ret = i40e_aq_add_vsi(hw, &ctxt, NULL);
+ if (ret) {
+ dev_info(&pf->pdev->dev,
+ "add new vsi failed, err %s aq_err %s\n",
+ i40e_stat_str(&pf->hw, ret),
+ i40e_aq_str(&pf->hw,
+ pf->hw.aq.asq_last_status));
+ return -ENOENT;
+ }
+
+ /* Success, update channel */
+ ch->enabled_tc = enabled_tc;
+ ch->seid = ctxt.seid;
+ ch->vsi_number = ctxt.vsi_number;
+ ch->stat_counter_idx = cpu_to_le16(ctxt.info.stat_counter_idx);
+
+ /* copy just the sections touched not the entire info
+ * since not all sections are valid as returned by
+ * update vsi params
+ */
+ ch->info.mapping_flags = ctxt.info.mapping_flags;
+ memcpy(&ch->info.queue_mapping,
+ &ctxt.info.queue_mapping, sizeof(ctxt.info.queue_mapping));
+ memcpy(&ch->info.tc_mapping, ctxt.info.tc_mapping,
+ sizeof(ctxt.info.tc_mapping));
+
+ /* Now it's time to update 'num_queue_pairs' if it is more than
+ * 'allowed_queue_pairs' because RX queue map is setup based on
+ * value of 'allowed_queue_pairs' (min of num_queue_pairs,
+ * num_lan_msix). This update is needed so that TX rings are setup
+ * correctly.
+ */
+ if (ch->num_queue_pairs > ch->allowed_queue_pairs)
+ ch->num_queue_pairs = ch->allowed_queue_pairs;
+
+ return 0;
+}
+
+static int i40e_channel_config_bw(struct i40e_vsi *vsi, struct i40e_channel *ch,
+ u8 *bw_share)
+{
+ struct i40e_aqc_configure_vsi_tc_bw_data bw_data;
+ i40e_status ret;
+ int i;
+
+ bw_data.tc_valid_bits = ch->enabled_tc;
+ for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++)
+ bw_data.tc_bw_credits[i] = bw_share[i];
+
+ ret = i40e_aq_config_vsi_tc_bw(&vsi->back->hw, ch->seid,
+ &bw_data, NULL);
+ if (ret) {
+ dev_info(&vsi->back->pdev->dev,
+ "Config VSI BW allocation per TC failed, aq_err: %d for new_vsi->seid %u\n",
+ vsi->back->hw.aq.asq_last_status, ch->seid);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++)
+ ch->info.qs_handle[i] = bw_data.qs_handles[i];
+
+ return 0;
+}
+
+/**
+ * i40e_channel_config_tx_ring - config TX ring associated with new channel
+ * @pf: ptr to PF device
+ * @vsi: the VSI being setup
+ * @ch: ptr to channel structure
+ *
+ * Configure TX rings associated with channel (VSI) since queues are being
+ * from parent VSI.
+ **/
+static int i40e_channel_config_tx_ring(struct i40e_pf *pf,
+ struct i40e_vsi *vsi,
+ struct i40e_channel *ch)
+{
+ i40e_status ret;
+ int i;
+ u8 bw_share[I40E_MAX_TRAFFIC_CLASS] = {0};
+
+ /* Enable ETS TCs with equal BW Share for now across all VSIs */
+ for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
+ if (ch->enabled_tc & BIT(i))
+ bw_share[i] = 1;
+ }
+
+ /* configure BW for new VSi */
+ ret = i40e_channel_config_bw(vsi, ch, bw_share);
+ if (ret) {
+ dev_info(&vsi->back->pdev->dev,
+ "Failed configuring TC map %d for channel (seid %u)\n",
+ ch->enabled_tc, ch->seid);
+ return ret;
+ }
+
+ for (i = 0; i < ch->num_queue_pairs; i++) {
+ struct i40e_ring *tx_ring, *rx_ring;
+ u16 pf_q;
+
+ pf_q = ch->base_queue + i;
+
+ /* Get to TX ring ptr of main VSI, for re-setup TX queue
+ * context
+ */
+ tx_ring = vsi->tx_rings[pf_q];
+ tx_ring->ch = ch;
+
+ /* Get the RX ring ptr */
+ rx_ring = vsi->rx_rings[pf_q];
+ rx_ring->ch = ch;
+ }
+
+ return 0;
+}
+
+/**
+ * i40e_setup_hw_channel - setup new channel
+ * @pf: ptr to PF device
+ * @vsi: the VSI being setup
+ * @ch: ptr to channel structure
+ * @uplink_seid: underlying HW switching element (VEB) ID
+ * @type: type of channel to be created (VMDq2/VF)
+ *
+ * Setup new channel (VSI) based on specified type (VMDq2/VF)
+ * and configures TX rings accordingly
+ **/
+static inline int i40e_setup_hw_channel(struct i40e_pf *pf,
+ struct i40e_vsi *vsi,
+ struct i40e_channel *ch,
+ u16 uplink_seid, u8 type)
+{
+ struct i40e_q_vector *q_vector;
+ int base_queue = 0;
+ int ret, i;
+
+ ch->initialized = false;
+ ch->base_queue = atomic_read(&vsi->next_base_queue);
+ ch->type = type;
+
+ /* Proceed with creation of channel (VMDq2/VF) VSI */
+ ret = i40e_add_channel(pf, uplink_seid, ch);
+ if (ret) {
+ dev_info(&pf->pdev->dev,
+ "failed to add_channel using uplink_seid %u\n",
+ uplink_seid);
+ return ret;
+ }
+
+ /* Mark the successful creation of channel */
+ ch->initialized = true;
+
+ /* Mark q_vectors indicating that they are part of newly created
+ * channel (VSI)
+ */
+ base_queue = ch->base_queue;
+ for (i = 0; i < ch->num_queue_pairs; i++) {
+ q_vector = vsi->tx_rings[base_queue + i]->q_vector;
+
+ if (!q_vector)
+ continue;
+
+ q_vector->is_an_atq = true;
+ }
+
+ /* Reconfigure TX queues using QTX_CTL register */
+ ret = i40e_channel_config_tx_ring(pf, vsi, ch);
+ if (ret) {
+ dev_info(&pf->pdev->dev,
+ "failed to configure TX rings for channel %u\n",
+ ch->seid);
+ return ret;
+ }
+
+ /* update 'next_base_queue' */
+ atomic_set(&vsi->next_base_queue,
+ atomic_read(&vsi->next_base_queue) + ch->num_queue_pairs);
+
+ dev_dbg(&pf->pdev->dev,
+ "Added channel: vsi_seid %u, vsi_number %u, stat_counter_idx %u, num_queue_pairs %u, pf->next_base_queue %d\n",
+ ch->seid, ch->vsi_number, ch->stat_counter_idx,
+ ch->num_queue_pairs,
+ atomic_read(&vsi->next_base_queue));
+
+ return ret;
+}
+
+/**
+ * i40e_setup_channel - setup new channel using uplink element
+ * @pf: ptr to PF device
+ * @type: type of channel to be created (VMDq2/VF)
+ * @uplink_seid: underlying HW switching element (VEB) ID
+ * @ch: ptr to channel structure
+ *
+ * Setup new channel (VSI) based on specified type (VMDq2/VF)
+ * and uplink switching element (uplink_seid)
+ **/
+static bool i40e_setup_channel(struct i40e_pf *pf, struct i40e_vsi *vsi,
+ struct i40e_channel *ch)
+{
+ u16 seid = vsi->seid;
+ u8 vsi_type;
+ int ret;
+
+ if (vsi->type == I40E_VSI_MAIN) {
+ vsi_type = I40E_VSI_VMDQ2;
+ } else if (vsi->type == I40E_VSI_SRIOV) {
+ vsi_type = I40E_VSI_SRIOV;
+ } else {
+ dev_err(&pf->pdev->dev, "unsupported vsi type(%d) of parent vsi\n",
+ vsi->type);
+ return false;
+ }
+
+ /* underlying switching element */
+ seid = pf->vsi[pf->lan_vsi]->uplink_seid;
+
+ /* create channel (VSI), configure TX rings */
+ ret = i40e_setup_hw_channel(pf, vsi, ch, seid, vsi_type);
+ if (ret) {
+ dev_err(&pf->pdev->dev, "failed to setup hw_channel\n");
+ return false;
+ }
+
+ return ch->initialized ? true : false;
+}
+
+/**
+ * i40e_create_queue_channel - function to create channel
+ * @vsi: VSI to be configured
+ * @ch: ptr to channel (it contains channel specific params)
+ *
+ * This function creates channel (VSI) using num_queues specified by user,
+ * reconfigs RSS if needed.
+ **/
+int i40e_create_queue_channel(struct i40e_vsi *vsi,
+ struct i40e_channel *ch)
+{
+ struct i40e_pf *pf = vsi->back;
+ bool reconfig_rss;
+ int err;
+
+ if (!ch)
+ return -EINVAL;
+
+ if (!ch->num_queue_pairs) {
+ dev_err(&pf->pdev->dev, "Invalid num_queues requested: %d\n",
+ ch->num_queue_pairs);
+ return -EINVAL;
+ }
+
+ /* validate user requested num_queues for channel */
+ err = i40e_validate_num_queues(pf, ch->num_queue_pairs, vsi,
+ &reconfig_rss);
+ if (err) {
+ dev_info(&pf->pdev->dev, "Failed to validate num_queues (%d)\n",
+ ch->num_queue_pairs);
+ return -EINVAL;
+ }
+
+ /* By default we are in VEPA mode, if this is the first VF/VMDq
+ * VSI to be added switch to VEB mode.
+ */
+ if ((!(pf->flags & I40E_FLAG_VEB_MODE_ENABLED)) ||
+ (!i40e_is_any_channel(vsi))) {
+ if (!is_power_of_2(vsi->tc_config.tc_info[0].qcount)) {
+ dev_info(&pf->pdev->dev,
+ "Failed to create channel. Override queues (%u) not power of 2\n",
+ vsi->tc_config.tc_info[0].qcount);
+ return -EINVAL;
+ }
+
+ if (vsi->type == I40E_VSI_SRIOV) {
+ if (!(pf->flags & I40E_FLAG_VEB_MODE_ENABLED)) {
+ dev_info(&pf->pdev->dev,
+ "Expected to be VEB mode by this time\n");
+ return -EINVAL;
+ }
+ }
+ if (!(pf->flags & I40E_FLAG_VEB_MODE_ENABLED)) {
+ pf->flags |= I40E_FLAG_VEB_MODE_ENABLED;
+
+ if (vsi->type == I40E_VSI_MAIN) {
+ if (pf->flags & I40E_FLAG_TC_MQPRIO)
+ i40e_do_reset(pf,
+ BIT_ULL(__I40E_PF_RESET_REQUESTED),
+ true);
+ else
+ i40e_do_reset_safe(pf,
+ BIT_ULL(__I40E_PF_RESET_REQUESTED));
+ }
+ }
+ /* now onwards for main VSI, number of queues will be value
+ * of TC0's queue count
+ */
+ }
+
+ /* By this time, vsi->cnt_q_avail shall be set to non-zero and
+ * it should be more than num_queues
+ */
+ if (!vsi->cnt_q_avail || (vsi->cnt_q_avail < ch->num_queue_pairs)) {
+ dev_info(&pf->pdev->dev,
+ "Error: cnt_q_avail (%u) less than num_queues %d\n",
+ vsi->cnt_q_avail, ch->num_queue_pairs);
+ return -EINVAL;
+ }
+
+ /* reconfig_rss only if vsi type is MAIN_VSI, in case of SR_IOV VF VSI,
+ * reconfig_rss to be handled by VF driver, if 'reconfig_rss' flag
+ * is set to true
+ */
+ if (reconfig_rss && (vsi->type == I40E_VSI_MAIN)) {
+ err = i40e_vsi_reconfig_rss(vsi, ch->num_queue_pairs);
+ if (err) {
+ dev_info(&pf->pdev->dev,
+ "Error: unable to reconfig rss for num_queues (%u)\n",
+ ch->num_queue_pairs);
+ return -EINVAL;
+ }
+ }
+
+ if (!i40e_setup_channel(pf, vsi, ch)) {
+ dev_info(&pf->pdev->dev, "Failed to setup channel\n");
+ return -EINVAL;
+ }
+
+ dev_info(&pf->pdev->dev,
+ "Setup channel (id:%u) utilizing num_queues %d\n",
+ ch->seid, ch->num_queue_pairs);
+
+ /* in case of VF, this will be main SRIOV VSI */
+ ch->parent_vsi = vsi;
+
+ /* and update main_vsi's count for queue_available to use */
+ vsi->cnt_q_avail -= ch->num_queue_pairs;
+
+ return 0;
+}
+
+/**
+ * i40e_configure_queue_channel - Add queue channel for the given TCs
+ * @vsi: VSI to be configured
+ *
+ * Configures queue channel mapping to the given TCs
+ **/
+static int i40e_configure_queue_channel(struct i40e_vsi *vsi)
+{
+ struct i40e_channel *ch;
+ int ret = 0, i;
+
+ INIT_LIST_HEAD(&vsi->ch_list);
+
+ /* Create app vsi with the TCs. Main VSI with TC0 is already set up */
+ for (i = 1; i < I40E_MAX_TRAFFIC_CLASS; i++)
+ if (vsi->tc_config.enabled_tc & BIT(i)) {
+ ch = kzalloc(sizeof(*ch), GFP_KERNEL);
+ if (!ch) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ INIT_LIST_HEAD(&ch->list);
+ ch->num_queue_pairs =
+ vsi->tc_config.tc_info[i].qcount;
+ ch->base_queue =
+ vsi->tc_config.tc_info[i].qoffset;
+
+ list_add_tail(&ch->list, &vsi->ch_list);
+
+ ret = i40e_create_queue_channel(vsi, ch);
+ if (ret) {
+ dev_err(&vsi->back->pdev->dev,
+ "Failed creating queue channel with TC%d: queues %d\n",
+ i, ch->num_queue_pairs);
+ goto err_free;
+ }
+ }
+ return ret;
+
+err_free:
+ i40e_remove_queue_channel(vsi);
+ return ret;
+}
+
+/**
* i40e_veb_config_tc - Configure TCs for given VEB
* @veb: given VEB
* @enabled_tc: TC bitmap
@@ -5502,10 +6218,20 @@ static int i40e_setup_tc(struct net_device *netdev, u8 tc)
goto exit;
}
+ if (pf->flags & I40E_FLAG_TC_MQPRIO) {
+ ret = i40e_configure_queue_channel(vsi);
+ if (ret) {
+ netdev_info(netdev,
+ "Failed configuring queue channels\n");
+ goto exit;
+ }
+ }
+
/* Unquiesce VSI */
i40e_unquiesce_vsi(vsi);
exit:
+ i40e_unquiesce_vsi(vsi);
return ret;
}
diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h
index f5de511..02e1e84 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h
@@ -420,6 +420,8 @@ struct i40e_ring {
* i40e_clean_rx_ring_irq() is called
* for this ring.
*/
+
+ struct i40e_channel *ch;
} ____cacheline_internodealigned_in_smp;
static inline bool ring_uses_build_skb(struct i40e_ring *ring)
^ permalink raw reply related
* [PATCH 1/4] [next-queue]net: mqprio: Introduce new hardware offload mode in mqprio for offloading full TC configurations
From: Amritha Nambiar @ 2017-05-20 0:58 UTC (permalink / raw)
To: intel-wired-lan
Cc: alexander.h.duyck, kiran.patil, amritha.nambiar,
sridhar.samudrala, mitch.a.williams, neerav.parikh,
jeffrey.t.kirsher, netdev
In-Reply-To: <149524122523.11022.4541073724650541658.stgit@anamdev.jf.intel.com>
This patch introduces a new hardware offload mode in mqprio
which makes full use of the mqprio options, the TCs, the
queue configurations and the bandwidth rates for the TCs.
This is achieved by setting the value 2 for the "hw" option.
This new offload mode supports new attributes for traffic
class such as minimum and maximum values for bandwidth rate limits.
Introduces a new datastructure 'tc_mqprio_qopt_offload' for offloading
mqprio queue options and use this to be shared between the kernel and
device driver. This contains a copy of the exisiting datastructure
for mqprio queue options. This new datastructure can be extended when
adding new attributes for traffic class such as bandwidth rate limits. The
existing datastructure for mqprio queue options will be shared between the
kernel and userspace.
This patch enables configuring additional attributes associated
with a traffic class such as minimum and maximum bandwidth
rates and can be offloaded to the hardware in the new offload mode.
The min and max limits for bandwidth rates are provided
by the user along with the the TCs and the queue configurations
when creating the mqprio qdisc.
Example:
# tc qdisc add dev eth0 root mqprio num_tc 2 map 0 0 0 0 1 1 1 1\
queues 4@0 4@4 min_rate 0Mbit 0Mbit max_rate 55Mbit 60Mbit hw 2
To dump the bandwidth rates:
# tc qdisc show dev eth0
qdisc mqprio 804a: root tc 2 map 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0
queues:(0:3) (4:7)
min rates:0bit 0bit
max rates:55Mbit 60Mbit
Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
---
include/linux/netdevice.h | 2
include/net/pkt_cls.h | 7 ++
include/uapi/linux/pkt_sched.h | 13 +++
net/sched/sch_mqprio.c | 169 +++++++++++++++++++++++++++++++++++++---
4 files changed, 180 insertions(+), 11 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 0150b2d..17b9066 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -779,6 +779,7 @@ enum {
TC_SETUP_CLSFLOWER,
TC_SETUP_MATCHALL,
TC_SETUP_CLSBPF,
+ TC_SETUP_MQPRIO_EXT,
};
struct tc_cls_u32_offload;
@@ -791,6 +792,7 @@ struct tc_to_netdev {
struct tc_cls_matchall_offload *cls_mall;
struct tc_cls_bpf_offload *cls_bpf;
struct tc_mqprio_qopt *mqprio;
+ struct tc_mqprio_qopt_offload *mqprio_qopt;
};
bool egress_dev;
};
diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h
index 2c213a6..5ab8052 100644
--- a/include/net/pkt_cls.h
+++ b/include/net/pkt_cls.h
@@ -549,6 +549,13 @@ struct tc_cls_bpf_offload {
u32 gen_flags;
};
+struct tc_mqprio_qopt_offload {
+ /* struct tc_mqprio_qopt must always be the first element */
+ struct tc_mqprio_qopt qopt;
+ u32 flags;
+ u64 min_rate[TC_QOPT_MAX_QUEUE];
+ u64 max_rate[TC_QOPT_MAX_QUEUE];
+};
/* This structure holds cookie structure that is passed from user
* to the kernel for actions and classifiers
diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h
index 099bf55..cf2a146 100644
--- a/include/uapi/linux/pkt_sched.h
+++ b/include/uapi/linux/pkt_sched.h
@@ -620,6 +620,7 @@ struct tc_drr_stats {
enum {
TC_MQPRIO_HW_OFFLOAD_NONE, /* no offload requested */
TC_MQPRIO_HW_OFFLOAD_TCS, /* offload TCs, no queue counts */
+ TC_MQPRIO_HW_OFFLOAD, /* fully supported offload */
__TC_MQPRIO_HW_OFFLOAD_MAX
};
@@ -633,6 +634,18 @@ struct tc_mqprio_qopt {
__u16 offset[TC_QOPT_MAX_QUEUE];
};
+#define TC_MQPRIO_F_MIN_RATE 0x1
+#define TC_MQPRIO_F_MAX_RATE 0x2
+
+enum {
+ TCA_MQPRIO_UNSPEC,
+ TCA_MQPRIO_MIN_RATE64,
+ TCA_MQPRIO_MAX_RATE64,
+ __TCA_MQPRIO_MAX,
+};
+
+#define TCA_MQPRIO_MAX (__TCA_MQPRIO_MAX - 1)
+
/* SFB */
enum {
diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c
index 0a4cf27..6457ec9 100644
--- a/net/sched/sch_mqprio.c
+++ b/net/sched/sch_mqprio.c
@@ -18,10 +18,13 @@
#include <net/netlink.h>
#include <net/pkt_sched.h>
#include <net/sch_generic.h>
+#include <net/pkt_cls.h>
struct mqprio_sched {
struct Qdisc **qdiscs;
int hw_offload;
+ u32 flags;
+ u64 min_rate[TC_QOPT_MAX_QUEUE], max_rate[TC_QOPT_MAX_QUEUE];
};
static void mqprio_destroy(struct Qdisc *sch)
@@ -39,10 +42,21 @@ static void mqprio_destroy(struct Qdisc *sch)
}
if (priv->hw_offload && dev->netdev_ops->ndo_setup_tc) {
- struct tc_mqprio_qopt offload = { 0 };
- struct tc_to_netdev tc = { .type = TC_SETUP_MQPRIO,
- { .mqprio = &offload } };
+ struct tc_mqprio_qopt_offload offload = { 0 };
+ struct tc_to_netdev tc = { 0 };
+ switch (priv->hw_offload) {
+ case TC_MQPRIO_HW_OFFLOAD_TCS:
+ tc.type = TC_SETUP_MQPRIO;
+ tc.mqprio = &offload.qopt;
+ break;
+ case TC_MQPRIO_HW_OFFLOAD:
+ tc.type = TC_SETUP_MQPRIO_EXT;
+ tc.mqprio_qopt = &offload;
+ break;
+ default:
+ return;
+ }
dev->netdev_ops->ndo_setup_tc(dev, sch->handle, 0, &tc);
} else {
netdev_set_num_tc(dev, 0);
@@ -99,6 +113,24 @@ static int mqprio_parse_opt(struct net_device *dev, struct tc_mqprio_qopt *qopt)
return 0;
}
+static const struct nla_policy mqprio_policy[TCA_MQPRIO_MAX + 1] = {
+ [TCA_MQPRIO_MIN_RATE64] = { .type = NLA_NESTED },
+ [TCA_MQPRIO_MAX_RATE64] = { .type = NLA_NESTED },
+};
+
+static int parse_attr(struct nlattr *tb[], int maxtype, struct nlattr *nla,
+ const struct nla_policy *policy, int len)
+{
+ int nested_len = nla_len(nla) - NLA_ALIGN(len);
+
+ if (nested_len >= nla_attr_size(0))
+ return nla_parse(tb, maxtype, nla_data(nla) + NLA_ALIGN(len),
+ nested_len, policy, NULL);
+
+ memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
+ return 0;
+}
+
static int mqprio_init(struct Qdisc *sch, struct nlattr *opt)
{
struct net_device *dev = qdisc_dev(sch);
@@ -107,6 +139,10 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt)
struct Qdisc *qdisc;
int i, err = -EOPNOTSUPP;
struct tc_mqprio_qopt *qopt = NULL;
+ struct nlattr *tb[TCA_MQPRIO_MAX + 1];
+ struct nlattr *attr;
+ int rem;
+ int len = nla_len(opt) - NLA_ALIGN(sizeof(*qopt));
BUILD_BUG_ON(TC_MAX_QUEUE != TC_QOPT_MAX_QUEUE);
BUILD_BUG_ON(TC_BITMASK != TC_QOPT_BITMASK);
@@ -124,6 +160,51 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt)
if (mqprio_parse_opt(dev, qopt))
return -EINVAL;
+ if (len > 0) {
+ err = parse_attr(tb, TCA_MQPRIO_MAX, opt, mqprio_policy,
+ sizeof(*qopt));
+ if (err < 0)
+ return err;
+
+ if (tb[TCA_MQPRIO_MIN_RATE64]) {
+ if (qopt->hw != TC_MQPRIO_HW_OFFLOAD)
+ return -EINVAL;
+
+ i = 0;
+ nla_for_each_nested(attr, tb[TCA_MQPRIO_MIN_RATE64],
+ rem) {
+ if (nla_type(attr) != TCA_MQPRIO_MIN_RATE64)
+ return -EINVAL;
+
+ if (i >= qopt->num_tc)
+ return -EINVAL;
+
+ priv->min_rate[i] = *(u64 *)nla_data(attr);
+ i++;
+ }
+ priv->flags |= TC_MQPRIO_F_MIN_RATE;
+ }
+
+ if (tb[TCA_MQPRIO_MAX_RATE64]) {
+ if (qopt->hw != TC_MQPRIO_HW_OFFLOAD)
+ return -EINVAL;
+
+ i = 0;
+ nla_for_each_nested(attr, tb[TCA_MQPRIO_MAX_RATE64],
+ rem) {
+ if (nla_type(attr) != TCA_MQPRIO_MAX_RATE64)
+ return -EINVAL;
+
+ if (i >= qopt->num_tc)
+ return -EINVAL;
+
+ priv->max_rate[i] = *(u64 *)nla_data(attr);
+ i++;
+ }
+ priv->flags |= TC_MQPRIO_F_MAX_RATE;
+ }
+ }
+
/* pre-allocate qdisc, attachment can't fail */
priv->qdiscs = kcalloc(dev->num_tx_queues, sizeof(priv->qdiscs[0]),
GFP_KERNEL);
@@ -148,15 +229,36 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt)
* supplied and verified mapping
*/
if (qopt->hw) {
- struct tc_mqprio_qopt offload = *qopt;
- struct tc_to_netdev tc = { .type = TC_SETUP_MQPRIO,
- { .mqprio = &offload } };
+ struct tc_mqprio_qopt_offload offload = {.qopt = *qopt};
+ struct tc_to_netdev tc = { 0 };
+
+ switch (qopt->hw) {
+ case TC_MQPRIO_HW_OFFLOAD_TCS:
+ tc.type = TC_SETUP_MQPRIO;
+ tc.mqprio = &offload.qopt;
+ break;
+ case TC_MQPRIO_HW_OFFLOAD:
+ tc.type = TC_SETUP_MQPRIO_EXT;
+ tc.mqprio_qopt = &offload;
+
+ offload.flags = priv->flags;
+ if (priv->flags & TC_MQPRIO_F_MIN_RATE)
+ for (i = 0; i < offload.qopt.num_tc; i++)
+ offload.min_rate[i] = priv->min_rate[i];
+
+ if (priv->flags & TC_MQPRIO_F_MAX_RATE)
+ for (i = 0; i < offload.qopt.num_tc; i++)
+ offload.max_rate[i] = priv->max_rate[i];
+ break;
+ default:
+ return -EINVAL;
+ }
err = dev->netdev_ops->ndo_setup_tc(dev, sch->handle, 0, &tc);
if (err)
return err;
- priv->hw_offload = offload.hw;
+ priv->hw_offload = offload.qopt.hw;
} else {
netdev_set_num_tc(dev, qopt->num_tc);
for (i = 0; i < qopt->num_tc; i++)
@@ -226,11 +328,51 @@ static int mqprio_graft(struct Qdisc *sch, unsigned long cl, struct Qdisc *new,
return 0;
}
+static int dump_rates(struct mqprio_sched *priv,
+ struct tc_mqprio_qopt *opt, struct sk_buff *skb)
+{
+ struct nlattr *nest;
+ int i;
+
+ if (priv->flags & TC_MQPRIO_F_MIN_RATE) {
+ nest = nla_nest_start(skb, TCA_MQPRIO_MIN_RATE64);
+ if (!nest)
+ goto nla_put_failure;
+
+ for (i = 0; i < opt->num_tc; i++) {
+ if (nla_put(skb, TCA_MQPRIO_MIN_RATE64,
+ sizeof(priv->min_rate[i]),
+ &priv->min_rate[i]))
+ goto nla_put_failure;
+ }
+ nla_nest_end(skb, nest);
+ }
+
+ if (priv->flags & TC_MQPRIO_F_MAX_RATE) {
+ nest = nla_nest_start(skb, TCA_MQPRIO_MAX_RATE64);
+ if (!nest)
+ goto nla_put_failure;
+
+ for (i = 0; i < opt->num_tc; i++) {
+ if (nla_put(skb, TCA_MQPRIO_MAX_RATE64,
+ sizeof(priv->max_rate[i]),
+ &priv->max_rate[i]))
+ goto nla_put_failure;
+ }
+ nla_nest_end(skb, nest);
+ }
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(skb, nest);
+ return -1;
+}
+
static int mqprio_dump(struct Qdisc *sch, struct sk_buff *skb)
{
struct net_device *dev = qdisc_dev(sch);
struct mqprio_sched *priv = qdisc_priv(sch);
- unsigned char *b = skb_tail_pointer(skb);
+ struct nlattr *nla = (struct nlattr *)skb_tail_pointer(skb);
struct tc_mqprio_qopt opt = { 0 };
struct Qdisc *qdisc;
unsigned int i;
@@ -261,12 +403,17 @@ static int mqprio_dump(struct Qdisc *sch, struct sk_buff *skb)
opt.offset[i] = dev->tc_to_txq[i].offset;
}
- if (nla_put(skb, TCA_OPTIONS, sizeof(opt), &opt))
+ if (nla_put(skb, TCA_OPTIONS, NLA_ALIGN(sizeof(opt)), &opt))
goto nla_put_failure;
- return skb->len;
+ if (priv->flags & TC_MQPRIO_F_MIN_RATE ||
+ priv->flags & TC_MQPRIO_F_MAX_RATE)
+ if (dump_rates(priv, &opt, skb) != 0)
+ goto nla_put_failure;
+
+ return nla_nest_end(skb, nla);
nla_put_failure:
- nlmsg_trim(skb, b);
+ nlmsg_trim(skb, nla);
return -1;
}
^ permalink raw reply related
* [PATCH 0/4] Configuring traffic classes via new hardware offload mechanism in tc/mqprio
From: Amritha Nambiar @ 2017-05-20 0:58 UTC (permalink / raw)
To: intel-wired-lan
Cc: alexander.h.duyck, kiran.patil, amritha.nambiar,
sridhar.samudrala, mitch.a.williams, neerav.parikh,
jeffrey.t.kirsher, netdev
The following series introduces a new harware offload mode in tc/mqprio where the TCs, the queue configurations and bandwidth rate limits are offloaded to the hardware.
The i40e driver enables the new mqprio hardware offload mechanism factoring the TCs, queue configuration and bandwidth rates by creating HW channel VSIs.
In this mode, the priority to traffic class mapping and the user specified queue ranges are used to configure the traffic class when the 'hw' option is set to 2. This is achieved by creating HW channels(VSI). A new channel is created for each of the traffic class configuration offloaded via mqprio framework except for the first TC (TC0) which is for the main VSI. TC0 for the main VSI is also reconfigured as per user provided queue parameters. Finally, bandwidth rate limits are set on these traffic classes through the mqprio offload framework by sending these rates in addition to the number of TCs and the queue configurations.
Example:
# tc qdisc add dev eth0 root mqprio num_tc 2 map 0 0 0 0 1 1 1 1\
queues 4@0 4@4 min_rate 0Mbit 0Mbit max_rate 55Mbit 60Mbit hw 2
To dump the bandwidth rates:
# tc qdisc show dev eth0
qdisc mqprio 804a: root tc 2 map 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0
queues:(0:3) (4:7)
min rates:0bit 0bit
max rates:55Mbit 60Mbit
---
Amritha Nambiar (4):
[next-queue]net: mqprio: Introduce new hardware offload mode in mqprio for offloading full TC configurations
[next-queue]net: i40e: Add infrastructure for queue channel support with the TCs and queue configurations offloaded via mqprio scheduler
[next-queue]net: i40e: Enable mqprio full offload mode in the i40e driver for configuring TCs and queue mapping
[next-queue]net: i40e: Add support to set max bandwidth rates for TCs offloaded via tc/mqprio
drivers/net/ethernet/intel/i40e/i40e.h | 42 +
drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 6
drivers/net/ethernet/intel/i40e/i40e_main.c | 1365 +++++++++++++++++++++---
drivers/net/ethernet/intel/i40e/i40e_txrx.h | 2
include/linux/netdevice.h | 2
include/net/pkt_cls.h | 7
include/uapi/linux/pkt_sched.h | 13
net/sched/sch_mqprio.c | 169 +++
8 files changed, 1449 insertions(+), 157 deletions(-)
^ permalink raw reply
* Re: [PATCH v6 net-next 0/7] Extend socket timestamping API
From: Richard Cochran @ 2017-05-19 20:15 UTC (permalink / raw)
To: Miroslav Lichvar; +Cc: netdev, Willem de Bruijn
In-Reply-To: <20170519155241.15817-1-mlichvar@redhat.com>
On Fri, May 19, 2017 at 05:52:34PM +0200, Miroslav Lichvar wrote:
> Changes v5->v6:
> - fixed skb_is_swtx_tstamp() when OPT_TX_SWHW is disabled and improved
> its description
> - improved OPT_PKTINFO documentation
> - improved scm_timestamping documentation
For the series:
Acked-by: Richard Cochran <richardcochran@gmail.com>
^ permalink raw reply
* Re: [PATCH net-next v2] bridge: fix hello and hold timers starting/stopping
From: Nikolay Aleksandrov @ 2017-05-19 20:12 UTC (permalink / raw)
To: Ivan Vecera, netdev; +Cc: davem, sashok, stephen, bridge, lucien.xin
In-Reply-To: <20170519173043.10201-1-cera@cera.cz>
On 5/19/17 8:30 PM, Ivan Vecera wrote:
> Current bridge code incorrectly handles starting/stopping of hello and
> hold timers during STP enable/disable.
>
> 1. Timers are stopped in br_stp_start() during NO_STP->USER_STP
> transition. The timers are already stopped in NO_STP state so
> this is confusing no-op.
>
> 2. During USER_STP->NO_STP transition the timers are started. This
> does not make sense and is confusion because the timer should not be
> active in NO_STP state.
>
> Cc: davem@davemloft.net
> Cc: sashok@cumulusnetworks.com
> Cc: stephen@networkplumber.org
> Cc: bridge@lists.linux-foundation.org
> Cc: lucien.xin@gmail.com
> Cc: nikolay@cumulusnetworks.com
> Signed-off-by: Ivan Vecera <cera@cera.cz>
> ---
> net/bridge/br_stp_if.c | 11 -----------
> 1 file changed, 11 deletions(-)
>
LGTM, thanks!
Acked-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
^ permalink raw reply
* Alignment in BPF verifier
From: Edward Cree @ 2017-05-19 20:00 UTC (permalink / raw)
To: Alexei Starovoitov, David Miller, Daniel Borkmann
Cc: alexei.starovoitov, netdev
In-Reply-To: <a0a47297-4765-3ba0-3ea2-013dda40e84a@solarflare.com>
Well, I've managed to get somewhat confused by reg->id.
In particular, I'm unsure which bpf_reg_types can have an id, and what
exactly it means. There seems to be some code that checks around map value
pointers, which seems strange as maps have fixed sizes (and the comments in
enum bpf_reg_type make it seem like id is a PTR_TO_PACKET thing) - is this
maybe because of map-of-maps support, can the contained maps have differing
element sizes? Or do we allow *(map_value + var + imm), if map_value + var
was appropriately bounds-checked?
Does the 'id' identify the variable that was added to an object pointer, or
the object itself? Or does it blur these and identify (what the comment in
enum bpf_reg_type calls) "skb->data + (u16) var"?
Here's what I'm thinking of doing:
struct bpf_reg_state {
enum bpf_reg_type type;
union {
/* valid when type == PTR_TO_PACKET */
u16 range;
/* valid when type == CONST_PTR_TO_MAP | PTR_TO_MAP_VALUE |
* PTR_TO_MAP_VALUE_OR_NULL
*/
struct bpf_map *map_ptr;
};
/* Used to find other pointers with the same variable base, so they
* can share range and align knowledge.
*/
u32 id;
u32 off; /* fixed part of pointer offset */
/* For scalar types (CONST_IMM | UNKNOWN_VALUE), this represents our
* knowledge of the actual value.
* For pointer types, this represents the variable part of the offset
* from the pointed-to object, and is shared with all bpf_reg_states
* with the same id as us.
*/
struct tnum align;
/* Used to determine if any memory access using this register will
* result in a bad access. These two fields must be last.
* See states_equal()
* These refer to the same value as align, not necessarily the actual
* contents of the register.
*/
s64 min_value;
u64 max_value;
};
Does that sound reasonable? (And does my added comment on min/max_value
accurately describe the current semantics, or will I need to change that
as well?)
-Ed
PS. I think this approach would also mean several of the bpf_reg_types can
be folded together:
* PTR_TO_MAP_VALUE and PTR_TO_MAP_VALUE_ADJ are the same
* FRAME_PTR is just a PTR_TO_STACK with known-zero offset
* CONST_IMM is similarly a special case of UNKNOWN_VALUE
^ permalink raw reply
* Darlehen angebot 3 %
From: Frau SCHMIDT @ 2017-05-19 18:01 UTC (permalink / raw)
Sehr geehrte Damen und Herren,
Haben Sie Interesse über einer finanziellen Darlehen zu 3%?
kontaktieren Sie mich für mehr Details und Bedingungen. ich kann all
jenen helfen, wer ein Darlehen benötigen.
Ich kann Ihnen biete ein darlehen in hohe von 10.000.000 EUR
Meine mail: info@rschmidt.online
Mit freundlichen Grüßen
Frau SCHMIDT
^ permalink raw reply
* Re: [PATCH net] bonding: fix randomly populated arp target array
From: Andy Gospodarek @ 2017-05-19 19:23 UTC (permalink / raw)
To: Jarod Wilson
Cc: linux-kernel, Mahesh Bandewar, Jay Vosburgh, Veaceslav Falico,
netdev, stable
In-Reply-To: <20170519184646.42572-1-jarod@redhat.com>
On Fri, May 19, 2017 at 02:46:46PM -0400, Jarod Wilson wrote:
> In commit dc9c4d0fe023, the arp_target array moved from a static global
> to a local variable. By the nature of static globals, the array used to
> be initialized to all 0. At present, it's full of random data, which
> that gets interpreted as arp_target values, when none have actually been
> specified. Systems end up booting with spew along these lines:
>
> [ 32.161783] IPv6: ADDRCONF(NETDEV_UP): lacp0: link is not ready
> [ 32.168475] IPv6: ADDRCONF(NETDEV_UP): lacp0: link is not ready
> [ 32.175089] 8021q: adding VLAN 0 to HW filter on device lacp0
> [ 32.193091] IPv6: ADDRCONF(NETDEV_UP): lacp0: link is not ready
> [ 32.204892] lacp0: Setting MII monitoring interval to 100
> [ 32.211071] lacp0: Removing ARP target 216.124.228.17
> [ 32.216824] lacp0: Removing ARP target 218.160.255.255
> [ 32.222646] lacp0: Removing ARP target 185.170.136.184
> [ 32.228496] lacp0: invalid ARP target 255.255.255.255 specified for removal
> [ 32.236294] lacp0: option arp_ip_target: invalid value (-255.255.255.255)
> [ 32.243987] lacp0: Removing ARP target 56.125.228.17
> [ 32.249625] lacp0: Removing ARP target 218.160.255.255
> [ 32.255432] lacp0: Removing ARP target 15.157.233.184
> [ 32.261165] lacp0: invalid ARP target 255.255.255.255 specified for removal
> [ 32.268939] lacp0: option arp_ip_target: invalid value (-255.255.255.255)
> [ 32.276632] lacp0: Removing ARP target 16.0.0.0
> [ 32.281755] lacp0: Removing ARP target 218.160.255.255
> [ 32.287567] lacp0: Removing ARP target 72.125.228.17
> [ 32.293165] lacp0: Removing ARP target 218.160.255.255
> [ 32.298970] lacp0: Removing ARP target 8.125.228.17
> [ 32.304458] lacp0: Removing ARP target 218.160.255.255
>
> None of these were actually specified as ARP targets, and the driver does
> seem to clean up the mess okay, but it's rather noisy and confusing, leaks
> values to userspace, and the 255.255.255.255 spew shows up even when debug
> prints are disabled.
>
> The fix: just zero out arp_target at init time.
>
> While we're in here, init arp_all_targets_value in the right place.
>
Looks good. Thanks, Jarod!
Acked-by: Andy Gospodarek <andy@greyhouse.net>
> Fixes: dc9c4d0fe023 ("bonding: reduce scope of some global variables")
> CC: Mahesh Bandewar <maheshb@google.com>
> CC: Jay Vosburgh <j.vosburgh@gmail.com>
> CC: Veaceslav Falico <vfalico@gmail.com>
> CC: Andy Gospodarek <andy@greyhouse.net>
> CC: netdev@vger.kernel.org
> CC: stable@vger.kernel.org
> Signed-off-by: Jarod Wilson <jarod@redhat.com>
> ---
> drivers/net/bonding/bond_main.c | 5 ++---
> 1 file changed, 2 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
> index 2be78807fd6e..73313318399c 100644
> --- a/drivers/net/bonding/bond_main.c
> +++ b/drivers/net/bonding/bond_main.c
> @@ -4271,10 +4271,10 @@ static int bond_check_params(struct bond_params *params)
> int arp_validate_value, fail_over_mac_value, primary_reselect_value, i;
> struct bond_opt_value newval;
> const struct bond_opt_value *valptr;
> - int arp_all_targets_value;
> + int arp_all_targets_value = 0;
> u16 ad_actor_sys_prio = 0;
> u16 ad_user_port_key = 0;
> - __be32 arp_target[BOND_MAX_ARP_TARGETS];
> + __be32 arp_target[BOND_MAX_ARP_TARGETS] = { 0 };
> int arp_ip_count;
> int bond_mode = BOND_MODE_ROUNDROBIN;
> int xmit_hashtype = BOND_XMIT_POLICY_LAYER2;
> @@ -4501,7 +4501,6 @@ static int bond_check_params(struct bond_params *params)
> arp_validate_value = 0;
> }
>
> - arp_all_targets_value = 0;
> if (arp_all_targets) {
> bond_opt_initstr(&newval, arp_all_targets);
> valptr = bond_opt_parse(bond_opt_get(BOND_OPT_ARP_ALL_TARGETS),
> --
> 2.12.1
>
^ permalink raw reply
* Re: [PATCH net] fix BUG: scheduling while atomic in netlink broadcast
From: Akshay Narayan @ 2017-05-19 18:47 UTC (permalink / raw)
To: Cong Wang; +Cc: Linux Kernel Network Developers, David Miller
In-Reply-To: <CAM_iQpXraspjEvh9Wy5jOscFz7zKS4Z3XsigxCJFJ5_RuCOnyw@mail.gmail.com>
> I don't want to defend the use of yield() but it looks like there is other
> problem.
I believe this use of yield() should be replaced with cond_resched()
even if it turns out there is an unrelated problem.
> Does this module call netlink_broadcast() with __GFP_DIRECT_RECLAIM
> in IRQ context? If so you should adjust the gfp flags.
The module only calls netlink_broadcast() from a pluggable TCP
function; from my understanding this is not in the IRQ context. Full
trace, perhaps more clear, attached below.
May 19 14:30:44 ccp kernel: [ 178.885546] BUG: scheduling while
atomic: mm-link/3105/0x00000200
May 19 14:30:44 ccp kernel: [ 178.885552] Modules linked in:
ipt_MASQUERADE nf_nat_masquerade_ipv4 iptable_nat nf_conntrack_ipv4
nf_defrag_ipv4 nf_nat_ipv4 nf_nat libcrc32c xt_connmark nf_conntrack
ccp(OE) crct10dif_pclmul crc32_pclmul
ghash_clmulni_intel snd_intel8x0 pcbc snd_ac97_codec joydev ac97_bus
snd_pcm snd_seq_midi snd_seq_midi_event snd_rawmidi aesni_intel
snd_seq aes_x86_64 crypto_simd snd_seq_device snd_timer snd input_leds
i2c_piix4 glue_helper cryptd so
undcore mac_hid serio_raw vboxvideo ttm drm_kms_helper drm fb_sys_fops
syscopyarea sysfillrect sysimgblt vboxguest intel_rapl_perf parport_pc
ppdev lp parport ip_tables x_tables autofs4 hid_generic usbhid hid
e1000 ahci libahci psmouse
fjes pata_acpi video
May 19 14:30:44 ccp kernel: [ 178.885665] CPU: 0 PID: 3105 Comm:
mm-link Tainted: G W OE 4.10.0-21-generic #23-Ubuntu
May 19 14:30:44 ccp kernel: [ 178.885666] Hardware name: innotek GmbH
VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
May 19 14:30:44 ccp kernel: [ 178.885667] Call Trace:
May 19 14:30:44 ccp kernel: [ 178.885674] dump_stack+0x63/0x81
May 19 14:30:44 ccp kernel: [ 178.885678] __schedule_bug+0x54/0x70
May 19 14:30:44 ccp kernel: [ 178.885682] __schedule+0x536/0x6f0
May 19 14:30:44 ccp kernel: [ 178.885685] schedule+0x36/0x80
May 19 14:30:44 ccp kernel: [ 178.885687] sys_sched_yield+0x4f/0x60
May 19 14:30:44 ccp kernel: [ 178.885688] yield+0x33/0x40
May 19 14:30:44 ccp kernel: [ 178.885691]
netlink_broadcast_filtered+0x29b/0x3c0
May 19 14:30:44 ccp kernel: [ 178.885692] netlink_broadcast+0x1d/0x20
May 19 14:30:44 ccp kernel: [ 178.885697] nl_sendmsg+0xb8/0x664 [ccp]
May 19 14:30:44 ccp kernel: [ 178.885699] nl_send_ack_notif+0x7d/0x90 [ccp]
May 19 14:30:44 ccp kernel: [ 178.885702] tcp_ccp_cong_avoid+0x69/0x70 [ccp]
May 19 14:30:44 ccp kernel: [ 178.885704] tcp_ack+0x980/0xa60
May 19 14:30:44 ccp kernel: [ 178.885708] tcp_rcv_state_process+0x2be/0xda0
May 19 14:30:44 ccp kernel: [ 178.885712] ? security_sock_rcv_skb+0x3b/0x50
May 19 14:30:44 ccp kernel: [ 178.885715] ? sk_filter_trim_cap+0x3b/0x270
May 19 14:30:44 ccp kernel: [ 178.885717] tcp_v4_do_rcv+0xb2/0x200
May 19 14:30:44 ccp kernel: [ 178.885719] tcp_v4_rcv+0x90a/0xa00
May 19 14:30:44 ccp kernel: [ 178.885722] ip_local_deliver_finish+0x96/0x1c0
May 19 14:30:44 ccp kernel: [ 178.885725] ip_local_deliver+0x6f/0xe0
May 19 14:30:44 ccp kernel: [ 178.885727] ? ip_rcv_finish+0x3f0/0x3f0
May 19 14:30:44 ccp kernel: [ 178.885730] ip_rcv_finish+0x118/0x3f0
May 19 14:30:44 ccp kernel: [ 178.885732] ip_rcv+0x282/0x390
May 19 14:30:44 ccp kernel: [ 178.885735] ? inet_del_offload+0x40/0x40
May 19 14:30:44 ccp kernel: [ 178.885737] __netif_receive_skb_core+0x514/0xa40
May 19 14:30:44 ccp kernel: [ 178.885740] ? __check_object_size+0x10/0x1d7
May 19 14:30:44 ccp kernel: [ 178.885742] __netif_receive_skb+0x18/0x60
May 19 14:30:44 ccp kernel: [ 178.885744] netif_receive_skb_internal+0x32/0xa0
May 19 14:30:44 ccp kernel: [ 178.885746] netif_receive_skb+0x1c/0x70
May 19 14:30:44 ccp kernel: [ 178.885749] tun_get_user+0x425/0x800
May 19 14:30:44 ccp kernel: [ 178.885751] tun_chr_write_iter+0x57/0x70
May 19 14:30:44 ccp kernel: [ 178.885752] new_sync_write+0xd5/0x130
May 19 14:30:44 ccp kernel: [ 178.885754] __vfs_write+0x26/0x40
May 19 14:30:44 ccp kernel: [ 178.885756] vfs_write+0xb5/0x1a0
May 19 14:30:44 ccp kernel: [ 178.885757] SyS_write+0x55/0xc0
May 19 14:30:44 ccp kernel: [ 178.885760] entry_SYSCALL_64_fastpath+0x1e/0xad
May 19 14:30:44 ccp kernel: [ 178.885762] RIP: 0033:0x7f8d9abbf670
May 19 14:30:44 ccp kernel: [ 178.885763] RSP: 002b:00007ffc2f16d8b8
EFLAGS: 00000246 ORIG_RAX: 0000000000000001
May 19 14:30:44 ccp kernel: [ 178.885765] RAX: ffffffffffffffda RBX:
00007ffc2f16dde0 RCX: 00007f8d9abbf670
May 19 14:30:44 ccp kernel: [ 178.885767] RDX: 0000000000000038 RSI:
0000557403fd86e0 RDI: 0000000000000008
May 19 14:30:44 ccp kernel: [ 178.885768] RBP: 00007ffc2f16dcd0 R08:
0000557403fd8d40 R09: 0000557403fd8690
May 19 14:30:44 ccp kernel: [ 178.885769] R10: 000e20d41e89a5c5 R11:
0000000000000246 R12: 0000000000000108
May 19 14:30:44 ccp kernel: [ 178.885770] R13: 000000000000145c R14:
00007ffc2f16dd98 R15: 0000000000000003
^ permalink raw reply
* [PATCH net] bonding: fix randomly populated arp target array
From: Jarod Wilson @ 2017-05-19 18:46 UTC (permalink / raw)
To: linux-kernel
Cc: Jarod Wilson, Mahesh Bandewar, Jay Vosburgh, Veaceslav Falico,
Andy Gospodarek, netdev, stable
In commit dc9c4d0fe023, the arp_target array moved from a static global
to a local variable. By the nature of static globals, the array used to
be initialized to all 0. At present, it's full of random data, which
that gets interpreted as arp_target values, when none have actually been
specified. Systems end up booting with spew along these lines:
[ 32.161783] IPv6: ADDRCONF(NETDEV_UP): lacp0: link is not ready
[ 32.168475] IPv6: ADDRCONF(NETDEV_UP): lacp0: link is not ready
[ 32.175089] 8021q: adding VLAN 0 to HW filter on device lacp0
[ 32.193091] IPv6: ADDRCONF(NETDEV_UP): lacp0: link is not ready
[ 32.204892] lacp0: Setting MII monitoring interval to 100
[ 32.211071] lacp0: Removing ARP target 216.124.228.17
[ 32.216824] lacp0: Removing ARP target 218.160.255.255
[ 32.222646] lacp0: Removing ARP target 185.170.136.184
[ 32.228496] lacp0: invalid ARP target 255.255.255.255 specified for removal
[ 32.236294] lacp0: option arp_ip_target: invalid value (-255.255.255.255)
[ 32.243987] lacp0: Removing ARP target 56.125.228.17
[ 32.249625] lacp0: Removing ARP target 218.160.255.255
[ 32.255432] lacp0: Removing ARP target 15.157.233.184
[ 32.261165] lacp0: invalid ARP target 255.255.255.255 specified for removal
[ 32.268939] lacp0: option arp_ip_target: invalid value (-255.255.255.255)
[ 32.276632] lacp0: Removing ARP target 16.0.0.0
[ 32.281755] lacp0: Removing ARP target 218.160.255.255
[ 32.287567] lacp0: Removing ARP target 72.125.228.17
[ 32.293165] lacp0: Removing ARP target 218.160.255.255
[ 32.298970] lacp0: Removing ARP target 8.125.228.17
[ 32.304458] lacp0: Removing ARP target 218.160.255.255
None of these were actually specified as ARP targets, and the driver does
seem to clean up the mess okay, but it's rather noisy and confusing, leaks
values to userspace, and the 255.255.255.255 spew shows up even when debug
prints are disabled.
The fix: just zero out arp_target at init time.
While we're in here, init arp_all_targets_value in the right place.
Fixes: dc9c4d0fe023 ("bonding: reduce scope of some global variables")
CC: Mahesh Bandewar <maheshb@google.com>
CC: Jay Vosburgh <j.vosburgh@gmail.com>
CC: Veaceslav Falico <vfalico@gmail.com>
CC: Andy Gospodarek <andy@greyhouse.net>
CC: netdev@vger.kernel.org
CC: stable@vger.kernel.org
Signed-off-by: Jarod Wilson <jarod@redhat.com>
---
drivers/net/bonding/bond_main.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 2be78807fd6e..73313318399c 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -4271,10 +4271,10 @@ static int bond_check_params(struct bond_params *params)
int arp_validate_value, fail_over_mac_value, primary_reselect_value, i;
struct bond_opt_value newval;
const struct bond_opt_value *valptr;
- int arp_all_targets_value;
+ int arp_all_targets_value = 0;
u16 ad_actor_sys_prio = 0;
u16 ad_user_port_key = 0;
- __be32 arp_target[BOND_MAX_ARP_TARGETS];
+ __be32 arp_target[BOND_MAX_ARP_TARGETS] = { 0 };
int arp_ip_count;
int bond_mode = BOND_MODE_ROUNDROBIN;
int xmit_hashtype = BOND_XMIT_POLICY_LAYER2;
@@ -4501,7 +4501,6 @@ static int bond_check_params(struct bond_params *params)
arp_validate_value = 0;
}
- arp_all_targets_value = 0;
if (arp_all_targets) {
bond_opt_initstr(&newval, arp_all_targets);
valptr = bond_opt_parse(bond_opt_get(BOND_OPT_ARP_ALL_TARGETS),
--
2.12.1
^ permalink raw reply related
* Re: [PATCH net] sctp: fix ICMP processing if skb is non-linear
From: Xin Long @ 2017-05-19 18:40 UTC (permalink / raw)
To: Davide Caratti, Marcelo Ricardo Leitner
Cc: network dev, linux-sctp, David S. Miller
In-Reply-To: <cb2535a6a40a84684f26d7c7d26788cc3f8a2010.1495207495.git.dcaratti@redhat.com>
On Fri, May 19, 2017 at 11:34 PM, Davide Caratti <dcaratti@redhat.com> wrote:
> when the ICMP packet is carried by a paged skb, sctp_err_lookup() may fail
> validation even if the payload contents match an open socket: as a
> consequence, sometimes ICMPs are wrongly ignored. Use skb_header_pointer()
> to retrieve encapsulated SCTP headers, to ensure that ICMP payloads are
> validated correctly, also when skbs are non-linear.
>
> Signed-off-by: Davide Caratti <dcaratti@redhat.com>
> ---
> include/net/sctp/sctp.h | 2 +-
> net/sctp/input.c | 29 +++++++++++++++++++----------
> net/sctp/ipv6.c | 2 +-
> 3 files changed, 21 insertions(+), 12 deletions(-)
>
> diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h
> index 069582e..1b8c16b 100644
> --- a/include/net/sctp/sctp.h
> +++ b/include/net/sctp/sctp.h
> @@ -152,7 +152,7 @@ void sctp_v4_err(struct sk_buff *skb, u32 info);
> void sctp_hash_endpoint(struct sctp_endpoint *);
> void sctp_unhash_endpoint(struct sctp_endpoint *);
> struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *,
> - struct sctphdr *, struct sctp_association **,
> + struct sctp_association **,
> struct sctp_transport **);
> void sctp_err_finish(struct sock *, struct sctp_transport *);
> void sctp_icmp_frag_needed(struct sock *, struct sctp_association *,
> diff --git a/net/sctp/input.c b/net/sctp/input.c
> index 0e06a27..7f3f983 100644
> --- a/net/sctp/input.c
> +++ b/net/sctp/input.c
> @@ -469,19 +469,19 @@ void sctp_icmp_proto_unreachable(struct sock *sk,
>
> /* Common lookup code for icmp/icmpv6 error handler. */
> struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *skb,
> - struct sctphdr *sctphdr,
> struct sctp_association **app,
> struct sctp_transport **tpp)
> {
> + struct sctp_init_chunk _chunkhdr, *chunkhdr;
> + struct sctphdr _sctphdr, *sctphdr;
> union sctp_addr saddr;
> union sctp_addr daddr;
> struct sctp_af *af;
> struct sock *sk = NULL;
> struct sctp_association *asoc;
> struct sctp_transport *transport = NULL;
> - struct sctp_init_chunk *chunkhdr;
> - __u32 vtag = ntohl(sctphdr->vtag);
> - int len = skb->len - ((void *)sctphdr - (void *)skb->data);
> + int offset;
> + __u32 vtag;
>
> *app = NULL; *tpp = NULL;
>
> @@ -515,14 +515,23 @@ struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *skb,
> * or the chunk type or the Initiate Tag does not match, silently
> * discard the packet.
> */
> + offset = skb_transport_offset(skb);
> + sctphdr = skb_header_pointer(skb, offset, sizeof(_sctphdr), &_sctphdr);
> + if (unlikely(!sctphdr))
> + goto out;
> +
> + vtag = ntohl(sctphdr->vtag);
> if (vtag == 0) {
> - chunkhdr = (void *)sctphdr + sizeof(struct sctphdr);
> - if (len < sizeof(struct sctphdr) + sizeof(sctp_chunkhdr_t)
> - + sizeof(__be32) ||
> + offset += sizeof(_sctphdr);
will be nice to delete this line, and use
> + /* chunk header + first 4 octects of init header */
> + chunkhdr = skb_header_pointer(skb, offset,
chunkhdr = skb_header_pointer(skb, offset + sizeof(_sctphdr), ;)
wdyt?
> + sizeof(struct sctp_chunkhdr) +
> + sizeof(__be32), &_chunkhdr);
> + if (!chunkhdr ||
> chunkhdr->chunk_hdr.type != SCTP_CID_INIT ||
> - ntohl(chunkhdr->init_hdr.init_tag) != asoc->c.my_vtag) {
> + ntohl(chunkhdr->init_hdr.init_tag) != asoc->c.my_vtag)
> goto out;
> - }
> +
> } else if (vtag != asoc->c.peer_vtag) {
> goto out;
> }
> @@ -585,7 +594,7 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info)
> savesctp = skb->transport_header;
> skb_reset_network_header(skb);
> skb_set_transport_header(skb, ihlen);
> - sk = sctp_err_lookup(net, AF_INET, skb, sctp_hdr(skb), &asoc, &transport);
> + sk = sctp_err_lookup(net, AF_INET, skb, &asoc, &transport);
> /* Put back, the original values. */
> skb->network_header = saveip;
> skb->transport_header = savesctp;
> diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
> index 142b70e..d72c8d5 100644
> --- a/net/sctp/ipv6.c
> +++ b/net/sctp/ipv6.c
> @@ -157,7 +157,7 @@ static void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
> savesctp = skb->transport_header;
> skb_reset_network_header(skb);
> skb_set_transport_header(skb, offset);
> - sk = sctp_err_lookup(net, AF_INET6, skb, sctp_hdr(skb), &asoc, &transport);
> + sk = sctp_err_lookup(net, AF_INET6, skb, &asoc, &transport);
> /* Put back, the original pointers. */
> skb->network_header = saveip;
> skb->transport_header = savesctp;
> --
> 2.7.4
>
^ permalink raw reply
* Re: [PATCH] e1000e: use disable_hardirq() also for MSIX vectors in e1000_netpoll()
From: Cong Wang @ 2017-05-19 18:34 UTC (permalink / raw)
To: Konstantin Khlebnikov
Cc: Linux Kernel Network Developers, intel-wired-lan, Jeff Kirsher,
Dave Jones, David S. Miller, Sabrina Dubroca, Thomas Gleixner
In-Reply-To: <149517832870.37403.16008624383220981105.stgit@buzz>
On Fri, May 19, 2017 at 12:18 AM, Konstantin Khlebnikov
<khlebnikov@yandex-team.ru> wrote:
> Replace disable_irq() which waits for threaded irq handlers with
> disable_hardirq() which waits only for hardirq part.
>
> Signed-off-by: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>
> Fixes: 311191297125 ("e1000: use disable_hardirq() for e1000_netpoll()")
Thomas had a similar patch, I don't know why he never sends it
out formally. Anyway,
Acked-by: Cong Wang <xiyou.wangcong@gmail.com>
^ permalink raw reply
* [Patch net] vsock: use new wait API for vsock_stream_sendmsg()
From: Cong Wang @ 2017-05-19 18:21 UTC (permalink / raw)
To: netdev
Cc: Cong Wang, Stefan Hajnoczi, Jorgen Hansen, Michael S. Tsirkin,
Claudio Imbrenda
As reported by Michal, vsock_stream_sendmsg() could still
sleep at vsock_stream_has_space() after prepare_to_wait():
vsock_stream_has_space
vmci_transport_stream_has_space
vmci_qpair_produce_free_space
qp_lock
qp_acquire_queue_mutex
mutex_lock
Just switch to the new wait API like we did for commit
d9dc8b0f8b4e ("net: fix sleeping for sk_wait_event()").
Reported-by: Michal Kubecek <mkubecek@suse.cz>
Cc: Stefan Hajnoczi <stefanha@redhat.com>
Cc: Jorgen Hansen <jhansen@vmware.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Claudio Imbrenda <imbrenda@linux.vnet.ibm.com>
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
---
net/vmw_vsock/af_vsock.c | 21 ++++++++-------------
1 file changed, 8 insertions(+), 13 deletions(-)
diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
index 6f7f675..dfc8c51e 100644
--- a/net/vmw_vsock/af_vsock.c
+++ b/net/vmw_vsock/af_vsock.c
@@ -1540,8 +1540,7 @@ static int vsock_stream_sendmsg(struct socket *sock, struct msghdr *msg,
long timeout;
int err;
struct vsock_transport_send_notify_data send_data;
-
- DEFINE_WAIT(wait);
+ DEFINE_WAIT_FUNC(wait, woken_wake_function);
sk = sock->sk;
vsk = vsock_sk(sk);
@@ -1584,11 +1583,10 @@ static int vsock_stream_sendmsg(struct socket *sock, struct msghdr *msg,
if (err < 0)
goto out;
-
while (total_written < len) {
ssize_t written;
- prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
+ add_wait_queue(sk_sleep(sk), &wait);
while (vsock_stream_has_space(vsk) == 0 &&
sk->sk_err == 0 &&
!(sk->sk_shutdown & SEND_SHUTDOWN) &&
@@ -1597,33 +1595,30 @@ static int vsock_stream_sendmsg(struct socket *sock, struct msghdr *msg,
/* Don't wait for non-blocking sockets. */
if (timeout == 0) {
err = -EAGAIN;
- finish_wait(sk_sleep(sk), &wait);
+ remove_wait_queue(sk_sleep(sk), &wait);
goto out_err;
}
err = transport->notify_send_pre_block(vsk, &send_data);
if (err < 0) {
- finish_wait(sk_sleep(sk), &wait);
+ remove_wait_queue(sk_sleep(sk), &wait);
goto out_err;
}
release_sock(sk);
- timeout = schedule_timeout(timeout);
+ timeout = wait_woken(&wait, TASK_INTERRUPTIBLE, timeout);
lock_sock(sk);
if (signal_pending(current)) {
err = sock_intr_errno(timeout);
- finish_wait(sk_sleep(sk), &wait);
+ remove_wait_queue(sk_sleep(sk), &wait);
goto out_err;
} else if (timeout == 0) {
err = -EAGAIN;
- finish_wait(sk_sleep(sk), &wait);
+ remove_wait_queue(sk_sleep(sk), &wait);
goto out_err;
}
-
- prepare_to_wait(sk_sleep(sk), &wait,
- TASK_INTERRUPTIBLE);
}
- finish_wait(sk_sleep(sk), &wait);
+ remove_wait_queue(sk_sleep(sk), &wait);
/* These checks occur both as part of and after the loop
* conditional since we need to check before and after
--
2.5.5
^ permalink raw reply related
* Re: [PATCH net] fix BUG: scheduling while atomic in netlink broadcast
From: Cong Wang @ 2017-05-19 17:46 UTC (permalink / raw)
To: Akshay Narayan; +Cc: Linux Kernel Network Developers, David Miller
In-Reply-To: <CAN-7y0oY=LyeLTxhC5L+Vjy-NivmRJSBpqPP9+NYT7a=NFV0_A@mail.gmail.com>
On Fri, May 19, 2017 at 10:22 AM, Akshay Narayan <akshayn@mit.edu> wrote:
> netlink_broadcast_filtered() calls yield() when a slow listener causes
> the buffer to fill. yield() is the wrong choice here, as pointed out by
> Commit 8e3fabfde4 (sched: Update yield() docs); in some cases, its use causes
> "BUG: scheduling while atomic" and, when fewer cores are available,
> kernel hangs:
I don't want to defend the use of yield() but it looks like there is other
problem.
>
> (note: "ccp" is a kernel module which multicasts netlink messages upon
> certain TCP events)
Does this module call netlink_broadcast() with __GFP_DIRECT_RECLAIM
in IRQ context? If so you should adjust the gfp flags.
^ permalink raw reply
* Re: [PATCH net-next v2] bridge: fix hello and hold timers starting/stopping
From: Xin Long @ 2017-05-19 17:35 UTC (permalink / raw)
To: Ivan Vecera; +Cc: Nikolay Aleksandrov, network dev, bridge, davem
In-Reply-To: <20170519173043.10201-1-cera@cera.cz>
On Sat, May 20, 2017 at 1:30 AM, Ivan Vecera <cera@cera.cz> wrote:
> Current bridge code incorrectly handles starting/stopping of hello and
> hold timers during STP enable/disable.
>
> 1. Timers are stopped in br_stp_start() during NO_STP->USER_STP
> transition. The timers are already stopped in NO_STP state so
> this is confusing no-op.
>
> 2. During USER_STP->NO_STP transition the timers are started. This
> does not make sense and is confusion because the timer should not be
> active in NO_STP state.
>
> Cc: davem@davemloft.net
> Cc: sashok@cumulusnetworks.com
> Cc: stephen@networkplumber.org
> Cc: bridge@lists.linux-foundation.org
> Cc: lucien.xin@gmail.com
> Cc: nikolay@cumulusnetworks.com
> Signed-off-by: Ivan Vecera <cera@cera.cz>
Reviewed-by: Xin Long <lucien.xin@gmail.com>
> ---
> net/bridge/br_stp_if.c | 11 -----------
> 1 file changed, 11 deletions(-)
>
> diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c
> index 08341d2aa9c9..a05027027513 100644
> --- a/net/bridge/br_stp_if.c
> +++ b/net/bridge/br_stp_if.c
> @@ -150,7 +150,6 @@ static int br_stp_call_user(struct net_bridge *br, char *arg)
>
> static void br_stp_start(struct net_bridge *br)
> {
> - struct net_bridge_port *p;
> int err = -ENOENT;
>
> if (net_eq(dev_net(br->dev), &init_net))
> @@ -169,11 +168,6 @@ static void br_stp_start(struct net_bridge *br)
> if (!err) {
> br->stp_enabled = BR_USER_STP;
> br_debug(br, "userspace STP started\n");
> -
> - /* Stop hello and hold timers */
> - del_timer(&br->hello_timer);
> - list_for_each_entry(p, &br->port_list, list)
> - del_timer(&p->hold_timer);
> } else {
> br->stp_enabled = BR_KERNEL_STP;
> br_debug(br, "using kernel STP\n");
> @@ -187,7 +181,6 @@ static void br_stp_start(struct net_bridge *br)
>
> static void br_stp_stop(struct net_bridge *br)
> {
> - struct net_bridge_port *p;
> int err;
>
> if (br->stp_enabled == BR_USER_STP) {
> @@ -196,10 +189,6 @@ static void br_stp_stop(struct net_bridge *br)
> br_err(br, "failed to stop userspace STP (%d)\n", err);
>
> /* To start timers on any ports left in blocking */
> - mod_timer(&br->hello_timer, jiffies + br->hello_time);
> - list_for_each_entry(p, &br->port_list, list)
> - mod_timer(&p->hold_timer,
> - round_jiffies(jiffies + BR_HOLD_TIME));
> spin_lock_bh(&br->lock);
> br_port_state_selection(br);
> spin_unlock_bh(&br->lock);
> --
> 2.13.0
>
^ permalink raw reply
* [PATCH net-next v2] bridge: fix hello and hold timers starting/stopping
From: Ivan Vecera @ 2017-05-19 17:30 UTC (permalink / raw)
To: netdev; +Cc: davem, sashok, stephen, bridge, lucien.xin, nikolay
Current bridge code incorrectly handles starting/stopping of hello and
hold timers during STP enable/disable.
1. Timers are stopped in br_stp_start() during NO_STP->USER_STP
transition. The timers are already stopped in NO_STP state so
this is confusing no-op.
2. During USER_STP->NO_STP transition the timers are started. This
does not make sense and is confusion because the timer should not be
active in NO_STP state.
Cc: davem@davemloft.net
Cc: sashok@cumulusnetworks.com
Cc: stephen@networkplumber.org
Cc: bridge@lists.linux-foundation.org
Cc: lucien.xin@gmail.com
Cc: nikolay@cumulusnetworks.com
Signed-off-by: Ivan Vecera <cera@cera.cz>
---
net/bridge/br_stp_if.c | 11 -----------
1 file changed, 11 deletions(-)
diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c
index 08341d2aa9c9..a05027027513 100644
--- a/net/bridge/br_stp_if.c
+++ b/net/bridge/br_stp_if.c
@@ -150,7 +150,6 @@ static int br_stp_call_user(struct net_bridge *br, char *arg)
static void br_stp_start(struct net_bridge *br)
{
- struct net_bridge_port *p;
int err = -ENOENT;
if (net_eq(dev_net(br->dev), &init_net))
@@ -169,11 +168,6 @@ static void br_stp_start(struct net_bridge *br)
if (!err) {
br->stp_enabled = BR_USER_STP;
br_debug(br, "userspace STP started\n");
-
- /* Stop hello and hold timers */
- del_timer(&br->hello_timer);
- list_for_each_entry(p, &br->port_list, list)
- del_timer(&p->hold_timer);
} else {
br->stp_enabled = BR_KERNEL_STP;
br_debug(br, "using kernel STP\n");
@@ -187,7 +181,6 @@ static void br_stp_start(struct net_bridge *br)
static void br_stp_stop(struct net_bridge *br)
{
- struct net_bridge_port *p;
int err;
if (br->stp_enabled == BR_USER_STP) {
@@ -196,10 +189,6 @@ static void br_stp_stop(struct net_bridge *br)
br_err(br, "failed to stop userspace STP (%d)\n", err);
/* To start timers on any ports left in blocking */
- mod_timer(&br->hello_timer, jiffies + br->hello_time);
- list_for_each_entry(p, &br->port_list, list)
- mod_timer(&p->hold_timer,
- round_jiffies(jiffies + BR_HOLD_TIME));
spin_lock_bh(&br->lock);
br_port_state_selection(br);
spin_unlock_bh(&br->lock);
--
2.13.0
^ permalink raw reply related
* [PATCH net] fix BUG: scheduling while atomic in netlink broadcast
From: Akshay Narayan @ 2017-05-19 17:22 UTC (permalink / raw)
To: netdev; +Cc: davem
netlink_broadcast_filtered() calls yield() when a slow listener causes
the buffer to fill. yield() is the wrong choice here, as pointed out by
Commit 8e3fabfde4 (sched: Update yield() docs); in some cases, its use causes
"BUG: scheduling while atomic" and, when fewer cores are available,
kernel hangs:
(note: "ccp" is a kernel module which multicasts netlink messages upon
certain TCP events)
May 17 17:33:56 ccp kernel: [ 394.493978] BUG: scheduling while
atomic: iperf/4744/0x00000101
May 17 17:33:56 ccp kernel: [ 394.493979] Modules linked in: ccp(OE)
snd_intel8x0 snd_ac97_codec ac97_bus snd_pcm crct10dif_pclmul
crc32_pclmul ghash_clmulni_intel snd_seq_midi snd_seq_midi_event
snd_rawmidi snd_seq joydev pcbc snd_seq
_device snd_timer snd vboxvideo aesni_intel aes_x86_64 i2c_piix4
input_leds crypto_simd glue_helper cryptd soundcore mac_hid vboxguest
intel_rapl_perf serio_raw ttm drm_kms_helper drm fb_sys_fops
syscopyarea sysfillrect sysimgblt parpor
t_pc ppdev lp parport ip_tables x_tables autofs4 hid_generic usbhid
hid psmouse ahci e1000 libahci pata_acpi fjes video [last unloaded:
ccp]
May 17 17:33:56 ccp kernel: [ 394.493997] CPU: 0 PID: 4744 Comm:
iperf Tainted: G W OE 4.10.0-21-generic #23-Ubuntu
May 17 17:33:56 ccp kernel: [ 394.493997] Hardware name: innotek GmbH
VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
May 17 17:33:56 ccp kernel: [ 394.493997] Call Trace:
May 17 17:33:56 ccp kernel: [ 394.493998] <IRQ>
May 17 17:33:56 ccp kernel: [ 394.494000] dump_stack+0x63/0x81
May 17 17:33:56 ccp kernel: [ 394.494001] __schedule_bug+0x54/0x70
May 17 17:33:56 ccp kernel: [ 394.494002] __schedule+0x536/0x6f0
May 17 17:33:56 ccp kernel: [ 394.494004] schedule+0x36/0x80
May 17 17:33:56 ccp kernel: [ 394.494005] sys_sched_yield+0x4f/0x60
May 17 17:33:56 ccp kernel: [ 394.494005] yield+0x33/0x40
May 17 17:33:56 ccp kernel: [ 394.494006]
netlink_broadcast_filtered+0x29b/0x3c0
May 17 17:33:56 ccp kernel: [ 394.494007] netlink_broadcast+0x1d/0x20
...
May 17 17:33:56 ccp kernel: [ 394.494080] softirq: huh, entered
softirq 3 NET_RX ffffffff85fb5c60 with preempt_count 00000101, exited
with 00000000?
Changing this call to cond_resched() prevents this.
---
net/netlink/af_netlink.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 161b628..f70716c 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1441,7 +1441,7 @@ int netlink_broadcast_filtered(struct sock *ssk,
struct sk_buff *skb, u32 portid
if (info.delivered) {
if (info.congested && gfpflags_allow_blocking(allocation))
- yield();
+ cond_resched();
return 0;
}
return -ESRCH;
^ permalink raw reply related
* Re: [PATCH net] bridge: fix hello and hold timers starting/stopping
From: Xin Long @ 2017-05-19 17:26 UTC (permalink / raw)
To: Ivan Vecera; +Cc: Nikolay Aleksandrov, network dev, bridge, davem
In-Reply-To: <20170519162543.31670-1-cera@cera.cz>
On Sat, May 20, 2017 at 12:25 AM, Ivan Vecera <cera@cera.cz> wrote:
[...]
> @@ -197,13 +191,14 @@ static void br_stp_stop(struct net_bridge *br)
> br_err(br, "failed to stop userspace STP (%d)\n", err);
>
> /* To start timers on any ports left in blocking */
> - mod_timer(&br->hello_timer, jiffies + br->hello_time);
> - list_for_each_entry(p, &br->port_list, list)
> - mod_timer(&p->hold_timer,
> - round_jiffies(jiffies + BR_HOLD_TIME));
> spin_lock_bh(&br->lock);
> br_port_state_selection(br);
> spin_unlock_bh(&br->lock);
> + } else {
> + /* BR_KERNEL_STP - stop hello and hold timers */
> + del_timer(&br->hello_timer);
> + list_for_each_entry(p, &br->port_list, list)
> + del_timer(&p->hold_timer);
> }
>
> br->stp_enabled = BR_NO_STP;
I have a question here, br->stp_enabled is not atomic, and it is being
changed without holding br->lock here, while it may be checked in
br_hello_timer_expired, is it safe ?
(sorry if I misunderstood or overthought about it)
> --
> 2.13.0
>
^ permalink raw reply
* Re: [PATCH] net: sched: fix a use-after-free error on chain on the error exit path
From: Cong Wang @ 2017-05-19 17:17 UTC (permalink / raw)
To: Colin King
Cc: Jamal Hadi Salim, Jiri Pirko, David S . Miller,
Linux Kernel Network Developers, kernel-janitors, LKML
In-Reply-To: <20170518140702.6072-1-colin.king@canonical.com>
On Thu, May 18, 2017 at 7:07 AM, Colin King <colin.king@canonical.com> wrote:
> diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
> index 4020b8d932a1..82ebdc3fcb2e 100644
> --- a/net/sched/cls_api.c
> +++ b/net/sched/cls_api.c
> @@ -511,6 +511,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
> if (n->nlmsg_type == RTM_DELTFILTER && prio == 0) {
> tfilter_notify_chain(net, skb, n, chain, RTM_DELTFILTER);
> tcf_chain_destroy(chain);
Jiri, how does this work...? An action could hold a refcnt to a filter
chain, but here you destroy a whole chain without respecting
the refcnt???
> + chain = NULL;
> err = 0;
> goto errout;
Colin, not your fault, I think we may miss something more serious
when reviewing Jiri's patchset. ;)
Thanks.
^ permalink raw reply
* Re: [PATCH v2 1/3] bpf: Use 1<<16 as ceiling for immediate alignment in verifier.
From: Edward Cree @ 2017-05-19 17:17 UTC (permalink / raw)
To: Alexei Starovoitov, David Miller, Daniel Borkmann
Cc: alexei.starovoitov, netdev
In-Reply-To: <db8cd2e8-9f51-dc8b-5e55-4c75d13d4858@fb.com>
On 19/05/17 15:55, Alexei Starovoitov wrote:
> On 5/19/17 7:21 AM, Edward Cree wrote:
>> I'm currently translating the algos to C. But for the kernel patch,
>> I'll need to read & understand the existing verifier code, so it
>> might take a while :) (I don't suppose there's any design document
>> or hacking-notes you could point me at?)
>
> Dave just gave a good overview of the verifier:
> https://www.spinics.net/lists/xdp-newbies/msg00185.html
>
> Few more details in
> Documentation/networking/filter.txt
> and in top comments in kernel/bpf/verifier.c
>
> General bpf arch overview:
> http://docs.cilium.io/en/latest/bpf/#instruction-set
Thanks for the links! I'm digging into implementation now.
One question: is there a way to build the verifier as userland code
(or at least as a module), or will I have to reboot every time I
want to test a change?
-Ed
^ permalink raw reply
* Re: [RFC net-next PATCH 3/5] net: introduce XDP driver features interface
From: Daniel Borkmann @ 2017-05-19 17:13 UTC (permalink / raw)
To: Jesper Dangaard Brouer, Daniel Borkmann, Alexei Starovoitov
Cc: John Fastabend, netdev
In-Reply-To: <149512210317.14733.15039298820296846614.stgit@firesoul>
On 05/18/2017 05:41 PM, Jesper Dangaard Brouer wrote:
> There is a fundamental difference between normal eBPF programs
> and (XDP) eBPF programs getting attached in a driver. For normal
> eBPF programs it is easy to add a new bpf feature, like a bpf
> helper, because is it strongly tied to the feature being
> available in the current core kernel code. When drivers invoke a
> bpf_prog, then it is not sufficient to simply relying on whether
> a bpf_helper exists or not. When a driver haven't implemented a
> given feature yet, then it is possible to expose uninitialized
> parts of xdp_buff. The driver pass in a pointer to xdp_buff,
> usually "allocated" on the stack, which must not be exposed.
When xdp_buff is being extended, then we should at least zero
initialize all in-tree users that don't support or populate this
field, thus that it's not uninitialized memory. Better would be
to have a way to reject the prog in the first place until it's
implemented (but further comments on feature bits below).
> Only two user visible NETIF_F_XDP_* net_device feature flags are
> exposed via ethtool (-k) seen as "xdp" and "xdp-partial".
> The "xdp-partial" is detected when there is not feature equality
> between kernel and driver, and a netdev_warn is given.
I think having something like a NETIF_F_XDP_BIT for ethtool to
indicate support as "xdp" is quite useful. Avoids having to grep
the kernel tree for ndo_xdp callback. ;) A "xdp-partial" would
still be unclear/confusing to the user whether his program loads
or doesn't which is the only thing a user (or some loading infra)
cares about eventually, so one still needs to go trying to load
the XDP code to see whether that fails for the native case.
> The idea is that XDP_DRV_* feature bits define a contract between
> the driver and the kernel, giving a reliable way to know that XDP
> features a driver promised to implement. Thus, knowing what bpf
> side features are safe to allow.
>
> There are 3 levels of features: "required", "devel" and "optional".
>
> The motivation is pushing driver vendors forward to support all
> the new XDP features. Once a given feature bit is moved into
> the "required" features, the kernel will reject loading XDP
> program if feature isn't implemented by driver. Features under
> developement, require help from the bpf infrastrucure to detect
> when a given helper or direct-access is used, using a bpf_prog
> bit to mark a need for the feature, and pulling in this bit in
> the xdp_features_check(). When all drivers have implemented
> a "devel" feature, it can be moved to the "required" feature and
The problem is that once you add bits markers to bpf_prog like we
used to do in the past, then as you do in patch 4/5 with the
xdp_rxhash_needed bit, they will need to be turned /on/ unconditionally
when a prog has tail calls.
Meaning, xdp_features_check() would then bail out when you have
XDP_DRV_F_RXHASH set. This has the effect that while XDP prog X
was running fine on kernel Y, it will suddenly get rejected on
later kernel (Y + 1), without using the feature (but tail calls).
And more complex networking progs are likely to use tail calls,
so that would break all of them unfortunately as they cannot load
anymore.
> the bpf_prog bit can be refurbished. The "optional" features are
> for things that are handled safely runtime, but drivers will
> still get flagged as "xdp-partial" if not implementing those.
^ permalink raw reply
* Re: [PATCH net] bridge: fix hello and hold timers starting/stopping
From: Ivan Vecera @ 2017-05-19 17:06 UTC (permalink / raw)
To: Nikolay Aleksandrov; +Cc: Xin Long, network dev, bridge, David Miller
In-Reply-To: <4fb3ce7a-b3d8-b62e-2263-eb3fab2ff2fe@cumulusnetworks.com>
2017-05-19 18:55 GMT+02:00 Nikolay Aleksandrov <nikolay@cumulusnetworks.com>:
> On 5/19/17 7:25 PM, Ivan Vecera wrote:
>>
>> Current bridge code incorrectly handles starting/stopping of hello and
>> hold timers during STP enable/disable.
>>
>> 1. Timers are stopped in br_stp_start() during NO_STP->USER_STP
>> transition. This is not correct as the timers are stopped in NO_STP
>> case.
>
>
> This really is a noop, but ok.
Yes, stopping of stopped timers are safe but confusing.
>> 2. Timers are started in br_stp_stop() during USER_STP->NO_STP transition.
>> This is not also correct as the timers should be stopped in NO_STP
>> state.
>
>
> Indeed, but the actual end result is almost as them being stopped because
> in the timers there are specific checks if the STP == KERNEL_STP (see
> br_transmit_config()) and the hold_timers will simply expire and not rearm
> in any other mode. The only real problem is the hello_timer which continues
> to rearm itself, but with Xin's earlier patch that is taken care of too.
Yes, this is clean-up as well. The starting of timers are more
confusing than dangerous
but from a reader's point of view the starting of timers is non-sense
when STP is
going to be disabled.
>>
>> 3. Timers are NOT stopped in br_stp_stop() during KERNEL_STP->NO_STP
>> transition. They should be stopped as they are running in KERNEL_STP
>> state and should not run in NO_STP case.
>
>
> Same comment as for point 2.
This can be removed... and leave hello_timer handler to stop itself.
>> The patch is a follow-up for "bridge: start hello_timer when enabling
>> KERNEL_STP in br_stp_start" patch from Xin Long.
>>
>
> I'd say this is more of a cleanup/improvement after Xin's patch and thus
> would
> suggest targeting net-next. The only real issue is fixed by his patch.
Agree... will send resend against net-next.
Thanks for comments,
Ivan
^ permalink raw reply
* [PATCH iproute2 1/1] tc: fix Makefile to build skbmod
From: Roman Mashak @ 2017-05-19 17:05 UTC (permalink / raw)
To: stephen; +Cc: netdev, Roman Mashak
Signed-off-by: Roman Mashak <mrv@mojatatu.com>
---
tc/Makefile | 1 +
1 file changed, 1 insertion(+)
diff --git a/tc/Makefile b/tc/Makefile
index 9a6bb1d..678b302 100644
--- a/tc/Makefile
+++ b/tc/Makefile
@@ -45,6 +45,7 @@ TCMODULES += m_nat.o
TCMODULES += m_pedit.o
TCMODULES += m_ife.o
TCMODULES += m_skbedit.o
+TCMODULES += m_skbmod.o
TCMODULES += m_csum.o
TCMODULES += m_simple.o
TCMODULES += m_vlan.o
--
1.9.1
^ permalink raw reply related
* [PATCH net-next 9/9] ipv6: remove unused variables in esp6
From: Stephen Hemminger @ 2017-05-19 16:55 UTC (permalink / raw)
To: davem; +Cc: netdev, Stephen Hemminger
In-Reply-To: <20170519165556.483-1-sthemmin@microsoft.com>
Resolves warnings:
net/ipv6/esp6.c: In function ‘esp_ssg_unref’:
net/ipv6/esp6.c:121:10: warning: variable ‘seqhi’ set but not used [-Wunused-but-set-variable]
net/ipv6/esp6.c: In function ‘esp6_output_head’:
net/ipv6/esp6.c:227:21: warning: variable ‘esph’ set but not used [-Wunused-but-set-variable]
Signed-off-by: Stephen Hemminger <sthemmin@microsoft.com>
---
net/ipv6/esp6.c | 5 -----
1 file changed, 5 deletions(-)
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
index 1fe99ba8066c..53b6b870b935 100644
--- a/net/ipv6/esp6.c
+++ b/net/ipv6/esp6.c
@@ -118,7 +118,6 @@ static inline struct scatterlist *esp_req_sg(struct crypto_aead *aead,
static void esp_ssg_unref(struct xfrm_state *x, void *tmp)
{
- __be32 *seqhi;
struct crypto_aead *aead = x->data;
int seqhilen = 0;
u8 *iv;
@@ -128,7 +127,6 @@ static void esp_ssg_unref(struct xfrm_state *x, void *tmp)
if (x->props.flags & XFRM_STATE_ESN)
seqhilen += sizeof(__be32);
- seqhi = esp_tmp_seqhi(tmp);
iv = esp_tmp_iv(aead, tmp, seqhilen);
req = esp_tmp_req(aead, iv);
@@ -224,12 +222,9 @@ int esp6_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info
u8 *vaddr;
int nfrags;
struct page *page;
- struct ip_esp_hdr *esph;
struct sk_buff *trailer;
int tailen = esp->tailen;
- esph = ip_esp_hdr(skb);
-
if (!skb_cloned(skb)) {
if (tailen <= skb_availroom(skb)) {
nfrags = 1;
--
2.11.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox