Blame SOURCES/0217-netdrv-net-mlx5e-Eswitch-Use-per-vport-tables-for-mi.patch

d8f823
From 1dc76baa2155965c13e6d9c5def7a34fe5d91430 Mon Sep 17 00:00:00 2001
d8f823
From: Alaa Hleihel <ahleihel@redhat.com>
d8f823
Date: Tue, 19 May 2020 07:48:38 -0400
d8f823
Subject: [PATCH 217/312] [netdrv] net/mlx5e: Eswitch, Use per vport tables for
d8f823
 mirroring
d8f823
d8f823
Message-id: <20200519074934.6303-8-ahleihel@redhat.com>
d8f823
Patchwork-id: 310511
d8f823
Patchwork-instance: patchwork
d8f823
O-Subject: [RHEL8.3 BZ 1663246 07/63] net/mlx5e: Eswitch, Use per vport tables for mirroring
d8f823
Bugzilla: 1663246
d8f823
RH-Acked-by: Marcelo Leitner <mleitner@redhat.com>
d8f823
RH-Acked-by: Jarod Wilson <jarod@redhat.com>
d8f823
RH-Acked-by: John Linville <linville@redhat.com>
d8f823
RH-Acked-by: Ivan Vecera <ivecera@redhat.com>
d8f823
RH-Acked-by: Tony Camuso <tcamuso@redhat.com>
d8f823
RH-Acked-by: Kamal Heib <kheib@redhat.com>
d8f823
d8f823
Bugzilla: http://bugzilla.redhat.com/1663246
d8f823
Upstream: v5.7-rc1
d8f823
d8f823
commit 96e326878fa5e2727d14e9a23644119374619010
d8f823
Author: Eli Cohen <eli@mellanox.com>
d8f823
Date:   Tue Jan 14 17:30:41 2020 +0200
d8f823
d8f823
    net/mlx5e: Eswitch, Use per vport tables for mirroring
d8f823
d8f823
    When using port mirroring, we forward the traffic to another table and
d8f823
    use that table to forward to the mirrored vport. Since the hardware
d8f823
    loses the values of reg c, and in particular reg c0, we fail the match
d8f823
    on the input vport which previously existed in reg c0. To overcome this
d8f823
    situation, we use a set of per vport tables, positioned at the lowest
d8f823
    priority, and forward traffic to those tables. Since these tables are
d8f823
    per vport, we can avoid matching on reg c0.
d8f823
d8f823
    Fixes: c01cfd0f1115 ("net/mlx5: E-Switch, Add match on vport metadata for rule in fast path")
d8f823
    Signed-off-by: Eli Cohen <eli@mellanox.com>
d8f823
    Reviewed-by: Mark Bloch <markb@mellanox.com>
d8f823
    Reviewed-by: Paul Blakey <paulb@mellanox.com>
d8f823
    Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
d8f823
d8f823
Signed-off-by: Alaa Hleihel <ahleihel@redhat.com>
d8f823
Signed-off-by: Frantisek Hrbata <fhrbata@redhat.com>
d8f823
---
d8f823
 drivers/net/ethernet/mellanox/mlx5/core/eswitch.h  |  10 +
d8f823
 .../ethernet/mellanox/mlx5/core/eswitch_offloads.c | 206 ++++++++++++++++++++-
d8f823
 .../mellanox/mlx5/core/eswitch_offloads_chains.c   |  11 +-
d8f823
 drivers/net/ethernet/mellanox/mlx5/core/fs_core.c  |  11 ++
d8f823
 include/linux/mlx5/fs.h                            |   1 +
d8f823
 5 files changed, 221 insertions(+), 18 deletions(-)
d8f823
d8f823
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
d8f823
index dd7b9a96045c..255838c9ae5d 100644
d8f823
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
d8f823
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
d8f823
@@ -49,6 +49,7 @@
d8f823
 
d8f823
 /* The index of the last real chain (FT) + 1 as chain zero is valid as well */
d8f823
 #define FDB_NUM_CHAINS (FDB_FT_CHAIN + 1)
d8f823
+#define ESW_OFFLOADS_NUM_GROUPS  4
d8f823
 
d8f823
 #define FDB_TC_MAX_PRIO 16
d8f823
 #define FDB_TC_LEVELS_PER_PRIO 2
d8f823
@@ -206,6 +207,12 @@ struct mlx5_eswitch_fdb {
d8f823
 
d8f823
 #ifndef __GENKSYMS__
d8f823
 			struct mlx5_esw_chains_priv *esw_chains_priv;
d8f823
+			struct {
d8f823
+				DECLARE_HASHTABLE(table, 8);
d8f823
+				/* Protects vports.table */
d8f823
+				struct mutex lock;
d8f823
+			} vports;
d8f823
+
d8f823
 #else
d8f823
 			struct {
d8f823
 				struct mlx5_flow_table *fdb;
d8f823
@@ -661,6 +668,9 @@ void
d8f823
 esw_vport_destroy_offloads_acl_tables(struct mlx5_eswitch *esw,
d8f823
 				      struct mlx5_vport *vport);
d8f823
 
d8f823
+int mlx5_esw_vport_tbl_get(struct mlx5_eswitch *esw);
d8f823
+void mlx5_esw_vport_tbl_put(struct mlx5_eswitch *esw);
d8f823
+
d8f823
 #else  /* CONFIG_MLX5_ESWITCH */
d8f823
 /* eswitch API stubs */
d8f823
 static inline int  mlx5_eswitch_init(struct mlx5_core_dev *dev) { return 0; }
d8f823
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
d8f823
index 7c33ce7ec074..8b7a2b095ec3 100644
d8f823
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
d8f823
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
d8f823
@@ -50,6 +50,179 @@
d8f823
 #define MLX5_ESW_MISS_FLOWS (2)
d8f823
 #define UPLINK_REP_INDEX 0
d8f823
 
d8f823
+/* Per vport tables */
d8f823
+
d8f823
+#define MLX5_ESW_VPORT_TABLE_SIZE 128
d8f823
+
d8f823
+/* This struct is used as a key to the hash table and we need it to be packed
d8f823
+ * so hash result is consistent
d8f823
+ */
d8f823
+struct mlx5_vport_key {
d8f823
+	u32 chain;
d8f823
+	u16 prio;
d8f823
+	u16 vport;
d8f823
+	u16 vhca_id;
d8f823
+} __packed;
d8f823
+
d8f823
+struct mlx5_vport_table {
d8f823
+	struct hlist_node hlist;
d8f823
+	struct mlx5_flow_table *fdb;
d8f823
+	u32 num_rules;
d8f823
+	struct mlx5_vport_key key;
d8f823
+};
d8f823
+
d8f823
+static struct mlx5_flow_table *
d8f823
+esw_vport_tbl_create(struct mlx5_eswitch *esw, struct mlx5_flow_namespace *ns)
d8f823
+{
d8f823
+	struct mlx5_flow_table_attr ft_attr = {};
d8f823
+	struct mlx5_flow_table *fdb;
d8f823
+
d8f823
+	ft_attr.autogroup.max_num_groups = ESW_OFFLOADS_NUM_GROUPS;
d8f823
+	ft_attr.max_fte = MLX5_ESW_VPORT_TABLE_SIZE;
d8f823
+	ft_attr.prio = FDB_PER_VPORT;
d8f823
+	fdb = mlx5_create_auto_grouped_flow_table_attr_(ns, &ft_attr);
d8f823
+	if (IS_ERR(fdb)) {
d8f823
+		esw_warn(esw->dev, "Failed to create per vport FDB Table err %ld\n",
d8f823
+			 PTR_ERR(fdb));
d8f823
+	}
d8f823
+
d8f823
+	return fdb;
d8f823
+}
d8f823
+
d8f823
+static u32 flow_attr_to_vport_key(struct mlx5_eswitch *esw,
d8f823
+				  struct mlx5_esw_flow_attr *attr,
d8f823
+				  struct mlx5_vport_key *key)
d8f823
+{
d8f823
+	key->vport = attr->in_rep->vport;
d8f823
+	key->chain = attr->chain;
d8f823
+	key->prio = attr->prio;
d8f823
+	key->vhca_id = MLX5_CAP_GEN(esw->dev, vhca_id);
d8f823
+	return jhash(key, sizeof(*key), 0);
d8f823
+}
d8f823
+
d8f823
+/* caller must hold vports.lock */
d8f823
+static struct mlx5_vport_table *
d8f823
+esw_vport_tbl_lookup(struct mlx5_eswitch *esw, struct mlx5_vport_key *skey, u32 key)
d8f823
+{
d8f823
+	struct mlx5_vport_table *e;
d8f823
+
d8f823
+	hash_for_each_possible(esw->fdb_table.offloads.vports.table, e, hlist, key)
d8f823
+		if (!memcmp(&e->key, skey, sizeof(*skey)))
d8f823
+			return e;
d8f823
+
d8f823
+	return NULL;
d8f823
+}
d8f823
+
d8f823
+static void
d8f823
+esw_vport_tbl_put(struct mlx5_eswitch *esw, struct mlx5_esw_flow_attr *attr)
d8f823
+{
d8f823
+	struct mlx5_vport_table *e;
d8f823
+	struct mlx5_vport_key key;
d8f823
+	u32 hkey;
d8f823
+
d8f823
+	mutex_lock(&esw->fdb_table.offloads.vports.lock);
d8f823
+	hkey = flow_attr_to_vport_key(esw, attr, &key);
d8f823
+	e = esw_vport_tbl_lookup(esw, &key, hkey);
d8f823
+	if (!e || --e->num_rules)
d8f823
+		goto out;
d8f823
+
d8f823
+	hash_del(&e->hlist);
d8f823
+	mlx5_destroy_flow_table(e->fdb);
d8f823
+	kfree(e);
d8f823
+out:
d8f823
+	mutex_unlock(&esw->fdb_table.offloads.vports.lock);
d8f823
+}
d8f823
+
d8f823
+static struct mlx5_flow_table *
d8f823
+esw_vport_tbl_get(struct mlx5_eswitch *esw, struct mlx5_esw_flow_attr *attr)
d8f823
+{
d8f823
+	struct mlx5_core_dev *dev = esw->dev;
d8f823
+	struct mlx5_flow_namespace *ns;
d8f823
+	struct mlx5_flow_table *fdb;
d8f823
+	struct mlx5_vport_table *e;
d8f823
+	struct mlx5_vport_key skey;
d8f823
+	u32 hkey;
d8f823
+
d8f823
+	mutex_lock(&esw->fdb_table.offloads.vports.lock);
d8f823
+	hkey = flow_attr_to_vport_key(esw, attr, &skey);
d8f823
+	e = esw_vport_tbl_lookup(esw, &skey, hkey);
d8f823
+	if (e) {
d8f823
+		e->num_rules++;
d8f823
+		goto out;
d8f823
+	}
d8f823
+
d8f823
+	e = kzalloc(sizeof(*e), GFP_KERNEL);
d8f823
+	if (!e) {
d8f823
+		fdb = ERR_PTR(-ENOMEM);
d8f823
+		goto err_alloc;
d8f823
+	}
d8f823
+
d8f823
+	ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_FDB);
d8f823
+	if (!ns) {
d8f823
+		esw_warn(dev, "Failed to get FDB namespace\n");
d8f823
+		fdb = ERR_PTR(-ENOENT);
d8f823
+		goto err_ns;
d8f823
+	}
d8f823
+
d8f823
+	fdb = esw_vport_tbl_create(esw, ns);
d8f823
+	if (IS_ERR(fdb))
d8f823
+		goto err_ns;
d8f823
+
d8f823
+	e->fdb = fdb;
d8f823
+	e->num_rules = 1;
d8f823
+	e->key = skey;
d8f823
+	hash_add(esw->fdb_table.offloads.vports.table, &e->hlist, hkey);
d8f823
+out:
d8f823
+	mutex_unlock(&esw->fdb_table.offloads.vports.lock);
d8f823
+	return e->fdb;
d8f823
+
d8f823
+err_ns:
d8f823
+	kfree(e);
d8f823
+err_alloc:
d8f823
+	mutex_unlock(&esw->fdb_table.offloads.vports.lock);
d8f823
+	return fdb;
d8f823
+}
d8f823
+
d8f823
+int mlx5_esw_vport_tbl_get(struct mlx5_eswitch *esw)
d8f823
+{
d8f823
+	struct mlx5_esw_flow_attr attr = {};
d8f823
+	struct mlx5_eswitch_rep rep = {};
d8f823
+	struct mlx5_flow_table *fdb;
d8f823
+	struct mlx5_vport *vport;
d8f823
+	int i;
d8f823
+
d8f823
+	attr.prio = 1;
d8f823
+	attr.in_rep = &rep;
d8f823
+	mlx5_esw_for_all_vports(esw, i, vport) {
d8f823
+		attr.in_rep->vport = vport->vport;
d8f823
+		fdb = esw_vport_tbl_get(esw, &attr);
d8f823
+		if (!fdb)
d8f823
+			goto out;
d8f823
+	}
d8f823
+	return 0;
d8f823
+
d8f823
+out:
d8f823
+	mlx5_esw_vport_tbl_put(esw);
d8f823
+	return PTR_ERR(fdb);
d8f823
+}
d8f823
+
d8f823
+void mlx5_esw_vport_tbl_put(struct mlx5_eswitch *esw)
d8f823
+{
d8f823
+	struct mlx5_esw_flow_attr attr = {};
d8f823
+	struct mlx5_eswitch_rep rep = {};
d8f823
+	struct mlx5_vport *vport;
d8f823
+	int i;
d8f823
+
d8f823
+	attr.prio = 1;
d8f823
+	attr.in_rep = &rep;
d8f823
+	mlx5_esw_for_all_vports(esw, i, vport) {
d8f823
+		attr.in_rep->vport = vport->vport;
d8f823
+		esw_vport_tbl_put(esw, &attr);
d8f823
+	}
d8f823
+}
d8f823
+
d8f823
+/* End: Per vport tables */
d8f823
+
d8f823
 static struct mlx5_eswitch_rep *mlx5_eswitch_get_rep(struct mlx5_eswitch *esw,
d8f823
 						     u16 vport_num)
d8f823
 {
d8f823
@@ -191,8 +364,6 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw,
d8f823
 		i++;
d8f823
 	}
d8f823
 
d8f823
-	mlx5_eswitch_set_rule_source_port(esw, spec, attr);
d8f823
-
d8f823
 	if (attr->outer_match_level != MLX5_MATCH_NONE)
d8f823
 		spec->match_criteria_enable |= MLX5_MATCH_OUTER_HEADERS;
d8f823
 	if (attr->inner_match_level != MLX5_MATCH_NONE)
d8f823
@@ -201,8 +372,13 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw,
d8f823
 	if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
d8f823
 		flow_act.modify_hdr = attr->modify_hdr;
d8f823
 
d8f823
-	fdb = mlx5_esw_chains_get_table(esw, attr->chain, attr->prio,
d8f823
-					!!split);
d8f823
+	if (split) {
d8f823
+		fdb = esw_vport_tbl_get(esw, attr);
d8f823
+	} else {
d8f823
+		fdb = mlx5_esw_chains_get_table(esw, attr->chain, attr->prio,
d8f823
+						0);
d8f823
+		mlx5_eswitch_set_rule_source_port(esw, spec, attr);
d8f823
+	}
d8f823
 	if (IS_ERR(fdb)) {
d8f823
 		rule = ERR_CAST(fdb);
d8f823
 		goto err_esw_get;
d8f823
@@ -221,7 +397,10 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw,
d8f823
 	return rule;
d8f823
 
d8f823
 err_add_rule:
d8f823
-	mlx5_esw_chains_put_table(esw, attr->chain, attr->prio, !!split);
d8f823
+	if (split)
d8f823
+		esw_vport_tbl_put(esw, attr);
d8f823
+	else
d8f823
+		mlx5_esw_chains_put_table(esw, attr->chain, attr->prio, 0);
d8f823
 err_esw_get:
d8f823
 	if (!(attr->flags & MLX5_ESW_ATTR_FLAG_SLOW_PATH) && attr->dest_chain)
d8f823
 		mlx5_esw_chains_put_table(esw, attr->dest_chain, 1, 0);
d8f823
@@ -247,7 +426,7 @@ mlx5_eswitch_add_fwd_rule(struct mlx5_eswitch *esw,
d8f823
 		goto err_get_fast;
d8f823
 	}
d8f823
 
d8f823
-	fwd_fdb = mlx5_esw_chains_get_table(esw, attr->chain, attr->prio, 1);
d8f823
+	fwd_fdb = esw_vport_tbl_get(esw, attr);
d8f823
 	if (IS_ERR(fwd_fdb)) {
d8f823
 		rule = ERR_CAST(fwd_fdb);
d8f823
 		goto err_get_fwd;
d8f823
@@ -285,7 +464,7 @@ mlx5_eswitch_add_fwd_rule(struct mlx5_eswitch *esw,
d8f823
 
d8f823
 	return rule;
d8f823
 add_err:
d8f823
-	mlx5_esw_chains_put_table(esw, attr->chain, attr->prio, 1);
d8f823
+	esw_vport_tbl_put(esw, attr);
d8f823
 err_get_fwd:
d8f823
 	mlx5_esw_chains_put_table(esw, attr->chain, attr->prio, 0);
d8f823
 err_get_fast:
d8f823
@@ -312,11 +491,14 @@ __mlx5_eswitch_del_rule(struct mlx5_eswitch *esw,
d8f823
 	atomic64_dec(&esw->offloads.num_flows);
d8f823
 
d8f823
 	if (fwd_rule)  {
d8f823
-		mlx5_esw_chains_put_table(esw, attr->chain, attr->prio, 1);
d8f823
+		esw_vport_tbl_put(esw, attr);
d8f823
 		mlx5_esw_chains_put_table(esw, attr->chain, attr->prio, 0);
d8f823
 	} else {
d8f823
-		mlx5_esw_chains_put_table(esw, attr->chain, attr->prio,
d8f823
-					  !!split);
d8f823
+		if (split)
d8f823
+			esw_vport_tbl_put(esw, attr);
d8f823
+		else
d8f823
+			mlx5_esw_chains_put_table(esw, attr->chain, attr->prio,
d8f823
+						  0);
d8f823
 		if (attr->dest_chain)
d8f823
 			mlx5_esw_chains_put_table(esw, attr->dest_chain, 1, 0);
d8f823
 	}
d8f823
@@ -1938,6 +2120,9 @@ static int esw_offloads_steering_init(struct mlx5_eswitch *esw)
d8f823
 	if (err)
d8f823
 		goto create_fg_err;
d8f823
 
d8f823
+	mutex_init(&esw->fdb_table.offloads.vports.lock);
d8f823
+	hash_init(esw->fdb_table.offloads.vports.table);
d8f823
+
d8f823
 	return 0;
d8f823
 
d8f823
 create_fg_err:
d8f823
@@ -1954,6 +2139,7 @@ static int esw_offloads_steering_init(struct mlx5_eswitch *esw)
d8f823
 
d8f823
 static void esw_offloads_steering_cleanup(struct mlx5_eswitch *esw)
d8f823
 {
d8f823
+	mutex_destroy(&esw->fdb_table.offloads.vports.lock);
d8f823
 	esw_destroy_vport_rx_group(esw);
d8f823
 	esw_destroy_offloads_table(esw);
d8f823
 	esw_destroy_offloads_fdb_tables(esw);
d8f823
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_chains.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_chains.c
d8f823
index cdf435cd08fb..483186883ac4 100644
d8f823
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_chains.c
d8f823
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_chains.c
d8f823
@@ -21,8 +21,6 @@
d8f823
 #define fdb_ignore_flow_level_supported(esw) \
d8f823
 	(MLX5_CAP_ESW_FLOWTABLE_FDB((esw)->dev, ignore_flow_level))
d8f823
 
d8f823
-#define ESW_OFFLOADS_NUM_GROUPS  4
d8f823
-
d8f823
 /* Firmware currently has 4 pool of 4 sizes that it supports (ESW_POOLS),
d8f823
  * and a virtual memory region of 16M (ESW_SIZE), this region is duplicated
d8f823
  * for each flow table pool. We can allocate up to 16M of each pool,
d8f823
@@ -704,12 +702,9 @@ mlx5_esw_chains_open(struct mlx5_eswitch *esw)
d8f823
 
d8f823
 	/* Open level 1 for split rules now if prios isn't supported  */
d8f823
 	if (!mlx5_esw_chains_prios_supported(esw)) {
d8f823
-		ft = mlx5_esw_chains_get_table(esw, 0, 1, 1);
d8f823
-
d8f823
-		if (IS_ERR(ft)) {
d8f823
-			err = PTR_ERR(ft);
d8f823
+		err = mlx5_esw_vport_tbl_get(esw);
d8f823
+		if (err)
d8f823
 			goto level_1_err;
d8f823
-		}
d8f823
 	}
d8f823
 
d8f823
 	return 0;
d8f823
@@ -725,7 +720,7 @@ static void
d8f823
 mlx5_esw_chains_close(struct mlx5_eswitch *esw)
d8f823
 {
d8f823
 	if (!mlx5_esw_chains_prios_supported(esw))
d8f823
-		mlx5_esw_chains_put_table(esw, 0, 1, 1);
d8f823
+		mlx5_esw_vport_tbl_put(esw);
d8f823
 	mlx5_esw_chains_put_table(esw, 0, 1, 0);
d8f823
 	mlx5_esw_chains_put_table(esw, mlx5_esw_chains_get_ft_chain(esw), 1, 0);
d8f823
 }
d8f823
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
d8f823
index 7cc21f08cbcc..344e5470a81c 100644
d8f823
--- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
d8f823
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
d8f823
@@ -2720,6 +2720,17 @@ static int init_fdb_root_ns(struct mlx5_flow_steering *steering)
d8f823
 		goto out_err;
d8f823
 	}
d8f823
 
d8f823
+	/* We put this priority last, knowing that nothing will get here
d8f823
+	 * unless explicitly forwarded to. This is possible because the
d8f823
+	 * slow path tables have catch all rules and nothing gets passed
d8f823
+	 * those tables.
d8f823
+	 */
d8f823
+	maj_prio = fs_create_prio(&steering->fdb_root_ns->ns, FDB_PER_VPORT, 1);
d8f823
+	if (IS_ERR(maj_prio)) {
d8f823
+		err = PTR_ERR(maj_prio);
d8f823
+		goto out_err;
d8f823
+	}
d8f823
+
d8f823
 	set_prio_attrs(steering->fdb_root_ns);
d8f823
 	return 0;
d8f823
 
d8f823
diff --git a/include/linux/mlx5/fs.h b/include/linux/mlx5/fs.h
d8f823
index b918d9724fc2..b63d3a8b502e 100644
d8f823
--- a/include/linux/mlx5/fs.h
d8f823
+++ b/include/linux/mlx5/fs.h
d8f823
@@ -86,6 +86,7 @@ enum {
d8f823
 	FDB_TC_OFFLOAD,
d8f823
 	FDB_FT_OFFLOAD,
d8f823
 	FDB_SLOW_PATH,
d8f823
+	FDB_PER_VPORT,
d8f823
 };
d8f823
 
d8f823
 struct mlx5_pkt_reformat;
d8f823
-- 
d8f823
2.13.6
d8f823