DPDK patches and discussions
 help / color / mirror / Atom feed
From: Suanming Mou <suanmingm@nvidia.com>
To: Matan Azrad <matan@nvidia.com>,
	Shahaf Shuler <shahafs@nvidia.com>,
	Viacheslav Ovsiienko <viacheslavo@nvidia.com>
Cc: dev@dpdk.org
Subject: [dpdk-dev] [PATCH v2 1/8] net/mlx5: locate aging pools in the general container
Date: Tue, 20 Oct 2020 11:02:21 +0800
Message-ID: <1603162949-150001-2-git-send-email-suanmingm@nvidia.com> (raw)
In-Reply-To: <1603162949-150001-1-git-send-email-suanmingm@nvidia.com>

Commit [1] introduced different container for the aging counter
pools. In order to save container memory the aging counter pools
can be located in the general pool container.

This patch locates the aging counter pools in the general pool
container. Remove the aging container management.

[1] commit fd143711a6ea ("net/mlx5: separate aging counter pool range")

Signed-off-by: Suanming Mou <suanmingm@nvidia.com>
Acked-by: Matan Azrad <matan@nvidia.com>
---
 drivers/net/mlx5/mlx5.c            |  7 ++--
 drivers/net/mlx5/mlx5.h            | 17 +++++----
 drivers/net/mlx5/mlx5_flow.c       | 19 +++-------
 drivers/net/mlx5/mlx5_flow_dv.c    | 76 ++++++++++++++++++--------------------
 drivers/net/mlx5/mlx5_flow_verbs.c |  4 +-
 5 files changed, 56 insertions(+), 67 deletions(-)

diff --git a/drivers/net/mlx5/mlx5.c b/drivers/net/mlx5/mlx5.c
index 74a537b..a305e37 100644
--- a/drivers/net/mlx5/mlx5.c
+++ b/drivers/net/mlx5/mlx5.c
@@ -480,7 +480,7 @@ struct mlx5_flow_id_pool *
 static void
 mlx5_flow_counters_mng_init(struct mlx5_dev_ctx_shared *sh)
 {
-	int i;
+	int i, j;
 
 	memset(&sh->cmng, 0, sizeof(sh->cmng));
 	TAILQ_INIT(&sh->cmng.flow_counters);
@@ -490,7 +490,8 @@ struct mlx5_flow_id_pool *
 		sh->cmng.ccont[i].last_pool_idx = POOL_IDX_INVALID;
 		TAILQ_INIT(&sh->cmng.ccont[i].pool_list);
 		rte_spinlock_init(&sh->cmng.ccont[i].resize_sl);
-		TAILQ_INIT(&sh->cmng.ccont[i].counters);
+		for (j = 0; j < MLX5_COUNTER_TYPE_MAX; j++)
+			TAILQ_INIT(&sh->cmng.ccont[i].counters[j]);
 		rte_spinlock_init(&sh->cmng.ccont[i].csl);
 	}
 }
@@ -535,7 +536,7 @@ struct mlx5_flow_id_pool *
 	}
 	for (i = 0; i < MLX5_CCONT_TYPE_MAX; ++i) {
 		struct mlx5_flow_counter_pool *pool;
-		uint32_t batch = !!(i > 1);
+		uint32_t batch = (i == MLX5_CCONT_TYPE_BATCH);
 
 		if (!sh->cmng.ccont[i].pools)
 			continue;
diff --git a/drivers/net/mlx5/mlx5.h b/drivers/net/mlx5/mlx5.h
index afa2f31..26c603b 100644
--- a/drivers/net/mlx5/mlx5.h
+++ b/drivers/net/mlx5/mlx5.h
@@ -272,7 +272,6 @@ struct mlx5_drop {
 #define MLX5_COUNTERS_PER_POOL 512
 #define MLX5_MAX_PENDING_QUERIES 4
 #define MLX5_CNT_CONTAINER_RESIZE 64
-#define MLX5_CNT_AGE_OFFSET 0x80000000
 #define CNT_SIZE (sizeof(struct mlx5_flow_counter))
 #define CNTEXT_SIZE (sizeof(struct mlx5_flow_counter_ext))
 #define AGE_SIZE (sizeof(struct mlx5_age_param))
@@ -280,7 +279,6 @@ struct mlx5_drop {
 #define CNT_POOL_TYPE_AGE	(1 << 1)
 #define IS_EXT_POOL(pool) (((pool)->type) & CNT_POOL_TYPE_EXT)
 #define IS_AGE_POOL(pool) (((pool)->type) & CNT_POOL_TYPE_AGE)
-#define MLX_CNT_IS_AGE(counter) ((counter) & MLX5_CNT_AGE_OFFSET ? 1 : 0)
 #define MLX5_CNT_LEN(pool) \
 	(CNT_SIZE + \
 	(IS_AGE_POOL(pool) ? AGE_SIZE : 0) + \
@@ -321,17 +319,20 @@ enum {
 	AGE_TMOUT, /* Timeout, wait for rte_flow_get_aged_flows and destroy. */
 };
 
-#define MLX5_CNT_CONTAINER(sh, batch, age) (&(sh)->cmng.ccont \
-					    [(batch) * 2 + (age)])
+#define MLX5_CNT_CONTAINER(sh, batch) (&(sh)->cmng.ccont[batch])
 
 enum {
 	MLX5_CCONT_TYPE_SINGLE,
-	MLX5_CCONT_TYPE_SINGLE_FOR_AGE,
 	MLX5_CCONT_TYPE_BATCH,
-	MLX5_CCONT_TYPE_BATCH_FOR_AGE,
 	MLX5_CCONT_TYPE_MAX,
 };
 
+enum mlx5_counter_type {
+	MLX5_COUNTER_TYPE_ORIGIN,
+	MLX5_COUNTER_TYPE_AGE,
+	MLX5_COUNTER_TYPE_MAX,
+};
+
 /* Counter age parameter. */
 struct mlx5_age_param {
 	uint16_t state; /**< Age state (atomically accessed). */
@@ -426,7 +427,8 @@ struct mlx5_pools_container {
 	int max_id; /* The maximum counter ID in the pools. */
 	rte_spinlock_t resize_sl; /* The resize lock. */
 	rte_spinlock_t csl; /* The counter free list lock. */
-	struct mlx5_counters counters; /* Free counter list. */
+	struct mlx5_counters counters[MLX5_COUNTER_TYPE_MAX];
+	/* Free counter list. */
 	struct mlx5_counter_pools pool_list; /* Counter pool list. */
 	struct mlx5_flow_counter_pool **pools; /* Counter pool array. */
 	struct mlx5_counter_stats_mem_mng *mem_mng;
@@ -440,7 +442,6 @@ struct mlx5_flow_counter_mng {
 	uint8_t pending_queries;
 	uint8_t batch;
 	uint16_t pool_index;
-	uint8_t age;
 	uint8_t query_thread_on;
 	LIST_HEAD(mem_mngs, mlx5_counter_stats_mem_mng) mem_mngs;
 	LIST_HEAD(stat_raws, mlx5_counter_stats_raw) free_stat_raws;
diff --git a/drivers/net/mlx5/mlx5_flow.c b/drivers/net/mlx5/mlx5_flow.c
index c56dac8..598422c 100644
--- a/drivers/net/mlx5/mlx5_flow.c
+++ b/drivers/net/mlx5/mlx5_flow.c
@@ -6649,7 +6649,6 @@ struct mlx5_meter_domains_infos *
 	uint16_t offset;
 	int ret;
 	uint8_t batch = sh->cmng.batch;
-	uint8_t age = sh->cmng.age;
 	uint16_t pool_index = sh->cmng.pool_index;
 	struct mlx5_pools_container *cont;
 	struct mlx5_flow_counter_pool *pool;
@@ -6658,7 +6657,7 @@ struct mlx5_meter_domains_infos *
 	if (sh->cmng.pending_queries >= MLX5_MAX_PENDING_QUERIES)
 		goto set_alarm;
 next_container:
-	cont = MLX5_CNT_CONTAINER(sh, batch, age);
+	cont = MLX5_CNT_CONTAINER(sh, batch);
 	rte_spinlock_lock(&cont->resize_sl);
 	if (!cont->pools) {
 		rte_spinlock_unlock(&cont->resize_sl);
@@ -6667,11 +6666,6 @@ struct mlx5_meter_domains_infos *
 			goto set_alarm;
 		batch ^= 0x1;
 		pool_index = 0;
-		if (batch == 0 && pool_index == 0) {
-			age ^= 0x1;
-			sh->cmng.batch = batch;
-			sh->cmng.age = age;
-		}
 		goto next_container;
 	}
 	pool = cont->pools[pool_index];
@@ -6720,13 +6714,10 @@ struct mlx5_meter_domains_infos *
 	if (pool_index >= rte_atomic16_read(&cont->n_valid)) {
 		batch ^= 0x1;
 		pool_index = 0;
-		if (batch == 0 && pool_index == 0)
-			age ^= 0x1;
 	}
 set_alarm:
 	sh->cmng.batch = batch;
 	sh->cmng.pool_index = pool_index;
-	sh->cmng.age = age;
 	mlx5_set_query_alarm(sh);
 }
 
@@ -6817,10 +6808,12 @@ struct mlx5_meter_domains_infos *
 	struct mlx5_flow_counter_pool *pool =
 		(struct mlx5_flow_counter_pool *)(uintptr_t)async_id;
 	struct mlx5_counter_stats_raw *raw_to_free;
-	uint8_t age = !!IS_AGE_POOL(pool);
 	uint8_t query_gen = pool->query_gen ^ 1;
 	struct mlx5_pools_container *cont =
-		MLX5_CNT_CONTAINER(sh, !IS_EXT_POOL(pool), age);
+		MLX5_CNT_CONTAINER(sh, !IS_EXT_POOL(pool));
+	enum mlx5_counter_type cnt_type =
+		IS_AGE_POOL(pool) ? MLX5_COUNTER_TYPE_AGE :
+				    MLX5_COUNTER_TYPE_ORIGIN;
 
 	if (unlikely(status)) {
 		raw_to_free = pool->raw_hw;
@@ -6835,7 +6828,7 @@ struct mlx5_meter_domains_infos *
 		rte_io_wmb();
 		if (!TAILQ_EMPTY(&pool->counters[query_gen])) {
 			rte_spinlock_lock(&cont->csl);
-			TAILQ_CONCAT(&cont->counters,
+			TAILQ_CONCAT(&cont->counters[cnt_type],
 				     &pool->counters[query_gen], next);
 			rte_spinlock_unlock(&cont->csl);
 		}
diff --git a/drivers/net/mlx5/mlx5_flow_dv.c b/drivers/net/mlx5/mlx5_flow_dv.c
index d3a3f23..90b98cc 100644
--- a/drivers/net/mlx5/mlx5_flow_dv.c
+++ b/drivers/net/mlx5/mlx5_flow_dv.c
@@ -4605,16 +4605,14 @@ struct field_modify_info modify_tcp[] = {
 	struct mlx5_priv *priv = dev->data->dev_private;
 	struct mlx5_pools_container *cont;
 	struct mlx5_flow_counter_pool *pool;
-	uint32_t batch = 0, age = 0;
+	uint32_t batch = 0;
 
 	idx--;
-	age = MLX_CNT_IS_AGE(idx);
-	idx = age ? idx - MLX5_CNT_AGE_OFFSET : idx;
 	if (idx >= MLX5_CNT_BATCH_OFFSET) {
 		idx -= MLX5_CNT_BATCH_OFFSET;
 		batch = 1;
 	}
-	cont = MLX5_CNT_CONTAINER(priv->sh, batch, age);
+	cont = MLX5_CNT_CONTAINER(priv->sh, batch);
 	MLX5_ASSERT(idx / MLX5_COUNTERS_PER_POOL < cont->n);
 	pool = cont->pools[idx / MLX5_COUNTERS_PER_POOL];
 	MLX5_ASSERT(pool);
@@ -4767,19 +4765,15 @@ struct field_modify_info modify_tcp[] = {
  *   Pointer to the Ethernet device structure.
  * @param[in] batch
  *   Whether the pool is for counter that was allocated by batch command.
- * @param[in] age
- *   Whether the pool is for Aging counter.
  *
  * @return
  *   0 on success, otherwise negative errno value and rte_errno is set.
  */
 static int
-flow_dv_container_resize(struct rte_eth_dev *dev,
-				uint32_t batch, uint32_t age)
+flow_dv_container_resize(struct rte_eth_dev *dev, uint32_t batch)
 {
 	struct mlx5_priv *priv = dev->data->dev_private;
-	struct mlx5_pools_container *cont = MLX5_CNT_CONTAINER(priv->sh, batch,
-							       age);
+	struct mlx5_pools_container *cont = MLX5_CNT_CONTAINER(priv->sh, batch);
 	struct mlx5_counter_stats_mem_mng *mem_mng = NULL;
 	void *old_pools = cont->pools;
 	uint32_t resize = cont->n + MLX5_CNT_CONTAINER_RESIZE;
@@ -4897,12 +4891,11 @@ struct field_modify_info modify_tcp[] = {
 {
 	struct mlx5_priv *priv = dev->data->dev_private;
 	struct mlx5_flow_counter_pool *pool;
-	struct mlx5_pools_container *cont = MLX5_CNT_CONTAINER(priv->sh, batch,
-							       age);
+	struct mlx5_pools_container *cont = MLX5_CNT_CONTAINER(priv->sh, batch);
 	int16_t n_valid = rte_atomic16_read(&cont->n_valid);
 	uint32_t size = sizeof(*pool);
 
-	if (cont->n == n_valid && flow_dv_container_resize(dev, batch, age))
+	if (cont->n == n_valid && flow_dv_container_resize(dev, batch))
 		return NULL;
 	size += MLX5_COUNTERS_PER_POOL * CNT_SIZE;
 	size += (batch ? 0 : MLX5_COUNTERS_PER_POOL * CNTEXT_SIZE);
@@ -5031,10 +5024,12 @@ struct field_modify_info modify_tcp[] = {
 	struct mlx5_devx_obj *last_min_dcs;
 	struct mlx5_devx_obj *dcs = NULL;
 	struct mlx5_flow_counter *cnt;
+	enum mlx5_counter_type cnt_type =
+			age ? MLX5_COUNTER_TYPE_AGE : MLX5_COUNTER_TYPE_ORIGIN;
 	uint32_t add2other;
 	uint32_t i;
 
-	cont = MLX5_CNT_CONTAINER(priv->sh, batch, age);
+	cont = MLX5_CNT_CONTAINER(priv->sh, batch);
 	if (!batch) {
 retry:
 		add2other = 0;
@@ -5043,25 +5038,20 @@ struct field_modify_info modify_tcp[] = {
 		if (!dcs)
 			return NULL;
 		pool = flow_dv_find_pool_by_id(cont, dcs->id);
-		/* Check if counter belongs to exist pool ID range. */
+		/*
+		 * If pool eixsts but with other type, counter will be added
+		 * to the other pool, need to reallocate new counter in the
+		 * ragne with same type later.
+		 */
 		if (!pool) {
-			pool = flow_dv_find_pool_by_id
-			       (MLX5_CNT_CONTAINER
-			       (priv->sh, batch, (age ^ 0x1)), dcs->id);
-			/*
-			 * Pool exists, counter will be added to the other
-			 * container, need to reallocate it later.
-			 */
-			if (pool) {
-				add2other = 1;
-			} else {
-				pool = flow_dv_pool_create(dev, dcs, batch,
-							   age);
-				if (!pool) {
-					mlx5_devx_cmd_destroy(dcs);
-					return NULL;
-				}
+			pool = flow_dv_pool_create(dev, dcs, batch,
+						   age);
+			if (!pool) {
+				mlx5_devx_cmd_destroy(dcs);
+				return NULL;
 			}
+		} else if ((!!IS_AGE_POOL(pool)) != age) {
+			add2other = 1;
 		}
 		if ((dcs->id < pool->min_dcs->id ||
 		    pool->min_dcs->id &
@@ -5128,7 +5118,7 @@ struct field_modify_info modify_tcp[] = {
 		TAILQ_INSERT_HEAD(&tmp_tq, cnt, next);
 	}
 	rte_spinlock_lock(&cont->csl);
-	TAILQ_CONCAT(&cont->counters, &tmp_tq, next);
+	TAILQ_CONCAT(&cont->counters[cnt_type], &tmp_tq, next);
 	rte_spinlock_unlock(&cont->csl);
 	*cnt_free = MLX5_POOL_GET_CNT(pool, 0);
 	(*cnt_free)->pool = pool;
@@ -5201,8 +5191,9 @@ struct field_modify_info modify_tcp[] = {
 	 * shared counters from the single container.
 	 */
 	uint32_t batch = (group && !shared && !priv->counter_fallback) ? 1 : 0;
-	struct mlx5_pools_container *cont = MLX5_CNT_CONTAINER(priv->sh, batch,
-							       age);
+	struct mlx5_pools_container *cont = MLX5_CNT_CONTAINER(priv->sh, batch);
+	enum mlx5_counter_type cnt_type =
+			age ? MLX5_COUNTER_TYPE_AGE : MLX5_COUNTER_TYPE_ORIGIN;
 	uint32_t cnt_idx;
 
 	if (!priv->config.devx) {
@@ -5225,9 +5216,9 @@ struct field_modify_info modify_tcp[] = {
 	}
 	/* Get free counters from container. */
 	rte_spinlock_lock(&cont->csl);
-	cnt_free = TAILQ_FIRST(&cont->counters);
+	cnt_free = TAILQ_FIRST(&cont->counters[cnt_type]);
 	if (cnt_free)
-		TAILQ_REMOVE(&cont->counters, cnt_free, next);
+		TAILQ_REMOVE(&cont->counters[cnt_type], cnt_free, next);
 	rte_spinlock_unlock(&cont->csl);
 	if (!cnt_free && !flow_dv_counter_pool_prepare(dev, &cnt_free,
 						       batch, age))
@@ -5258,7 +5249,6 @@ struct field_modify_info modify_tcp[] = {
 	cnt_idx = MLX5_MAKE_CNT_IDX(pool->index,
 				MLX5_CNT_ARRAY_IDX(pool, cnt_free));
 	cnt_idx += batch * MLX5_CNT_BATCH_OFFSET;
-	cnt_idx += age * MLX5_CNT_AGE_OFFSET;
 	/* Update the counter reset values. */
 	if (_flow_dv_query_count(dev, cnt_idx, &cnt_free->hits,
 				 &cnt_free->bytes))
@@ -5283,7 +5273,7 @@ struct field_modify_info modify_tcp[] = {
 	if (cnt_free) {
 		cnt_free->pool = pool;
 		rte_spinlock_lock(&cont->csl);
-		TAILQ_INSERT_TAIL(&cont->counters, cnt_free, next);
+		TAILQ_INSERT_TAIL(&cont->counters[cnt_type], cnt_free, next);
 		rte_spinlock_unlock(&cont->csl);
 	}
 	return 0;
@@ -5363,6 +5353,7 @@ struct field_modify_info modify_tcp[] = {
 	struct mlx5_flow_counter_pool *pool = NULL;
 	struct mlx5_flow_counter *cnt;
 	struct mlx5_flow_counter_ext *cnt_ext = NULL;
+	enum mlx5_counter_type cnt_type;
 
 	if (!counter)
 		return;
@@ -5391,12 +5382,15 @@ struct field_modify_info modify_tcp[] = {
 	 * function both operate with the different list.
 	 *
 	 */
-	if (!priv->counter_fallback)
+	if (!priv->counter_fallback) {
 		TAILQ_INSERT_TAIL(&pool->counters[pool->query_gen], cnt, next);
-	else
+	} else {
+		cnt_type = IS_AGE_POOL(pool) ? MLX5_COUNTER_TYPE_AGE :
+					       MLX5_COUNTER_TYPE_ORIGIN;
 		TAILQ_INSERT_TAIL(&((MLX5_CNT_CONTAINER
-				  (priv->sh, 0, 0))->counters),
+				  (priv->sh, 0))->counters[cnt_type]),
 				  cnt, next);
+	}
 }
 
 /**
diff --git a/drivers/net/mlx5/mlx5_flow_verbs.c b/drivers/net/mlx5/mlx5_flow_verbs.c
index 276bcb5..698fb2b 100644
--- a/drivers/net/mlx5/mlx5_flow_verbs.c
+++ b/drivers/net/mlx5/mlx5_flow_verbs.c
@@ -159,7 +159,7 @@
 			      struct mlx5_flow_counter_pool **ppool)
 {
 	struct mlx5_priv *priv = dev->data->dev_private;
-	struct mlx5_pools_container *cont = MLX5_CNT_CONTAINER(priv->sh, 0, 0);
+	struct mlx5_pools_container *cont = MLX5_CNT_CONTAINER(priv->sh, 0);
 	struct mlx5_flow_counter_pool *pool;
 
 	idx--;
@@ -254,7 +254,7 @@
 flow_verbs_counter_new(struct rte_eth_dev *dev, uint32_t shared, uint32_t id)
 {
 	struct mlx5_priv *priv = dev->data->dev_private;
-	struct mlx5_pools_container *cont = MLX5_CNT_CONTAINER(priv->sh, 0, 0);
+	struct mlx5_pools_container *cont = MLX5_CNT_CONTAINER(priv->sh, 0);
 	struct mlx5_flow_counter_pool *pool = NULL;
 	struct mlx5_flow_counter_ext *cnt_ext = NULL;
 	struct mlx5_flow_counter *cnt = NULL;
-- 
1.8.3.1


  reply	other threads:[~2020-10-20  3:02 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-10-06 11:38 [dpdk-dev] [PATCH 0/6] net/mlx5: make counter thread safe Suanming Mou
2020-10-06 11:38 ` [dpdk-dev] [PATCH 1/6] net/mlx5: locate aging pools in the general container Suanming Mou
2020-10-06 11:38 ` [dpdk-dev] [PATCH 2/6] net/mlx5: optimize shared counter memory Suanming Mou
2020-10-06 11:38 ` [dpdk-dev] [PATCH 3/6] net/mlx5: remove single counter container Suanming Mou
2020-10-06 11:38 ` [dpdk-dev] [PATCH 4/6] net/mlx5: synchronize flow counter pool creation Suanming Mou
2020-10-06 11:38 ` [dpdk-dev] [PATCH 5/6] net/mlx5: make three level table thread safe Suanming Mou
2020-10-06 11:38 ` [dpdk-dev] [PATCH 6/6] net/mlx5: make shared counters " Suanming Mou
2020-10-20  3:02 ` [dpdk-dev] [PATCH v2 0/8] net/mlx5: make counter " Suanming Mou
2020-10-20  3:02   ` Suanming Mou [this message]
2020-10-20  3:02   ` [dpdk-dev] [PATCH v2 2/8] net/mlx5: optimize shared counter memory Suanming Mou
2020-10-20  3:02   ` [dpdk-dev] [PATCH v2 3/8] net/mlx5: remove single counter container Suanming Mou
2020-10-20  3:02   ` [dpdk-dev] [PATCH v2 4/8] net/mlx5: synchronize flow counter pool creation Suanming Mou
2020-10-20  3:02   ` [dpdk-dev] [PATCH v2 5/8] net/mlx5: make three level table thread safe Suanming Mou
2020-10-20  3:02   ` [dpdk-dev] [PATCH v2 6/8] net/mlx5: make shared counters " Suanming Mou
2020-10-20  3:02   ` [dpdk-dev] [PATCH v2 7/8] net/mlx5: rename flow counter macro Suanming Mou
2020-10-20  3:02   ` [dpdk-dev] [PATCH v2 8/8] net/mlx5: optimize counter extend memory Suanming Mou
2020-10-20 22:59   ` [dpdk-dev] [PATCH v2 0/8] net/mlx5: make counter thread safe Raslan Darawsheh

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1603162949-150001-2-git-send-email-suanmingm@nvidia.com \
    --to=suanmingm@nvidia.com \
    --cc=dev@dpdk.org \
    --cc=matan@nvidia.com \
    --cc=shahafs@nvidia.com \
    --cc=viacheslavo@nvidia.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

DPDK patches and discussions

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://inbox.dpdk.org/dev/0 dev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 dev dev/ https://inbox.dpdk.org/dev \
		dev@dpdk.org
	public-inbox-index dev

Example config snippet for mirrors.
Newsgroup available over NNTP:
	nntp://inbox.dpdk.org/inbox.dpdk.dev


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git