All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Ananyev, Konstantin" <konstantin.ananyev@intel.com>
To: David Marchand <david.marchand@redhat.com>,
	"dev@dpdk.org" <dev@dpdk.org>
Cc: "jerinjacobk@gmail.com" <jerinjacobk@gmail.com>,
	"Richardson, Bruce" <bruce.richardson@intel.com>,
	"mdr@ashroe.eu" <mdr@ashroe.eu>,
	"ktraynor@redhat.com" <ktraynor@redhat.com>,
	"Stokes, Ian" <ian.stokes@intel.com>,
	"i.maximets@ovn.org" <i.maximets@ovn.org>,
	"Thomas Monjalon" <thomas@monjalon.net>,
	"Mcnamara, John" <john.mcnamara@intel.com>,
	"Kovacevic, Marko" <marko.kovacevic@intel.com>,
	"Burakov, Anatoly" <anatoly.burakov@intel.com>,
	Olivier Matz <olivier.matz@6wind.com>,
	"Andrew Rybchenko" <arybchenko@solarflare.com>,
	Neil Horman <nhorman@tuxdriver.com>
Subject: Re: [dpdk-dev] [PATCH v3 6/9] eal: register non-EAL threads as lcores
Date: Mon, 22 Jun 2020 15:49:26 +0000	[thread overview]
Message-ID: <BYAPR11MB33010EBC5471E7157AA664359A970@BYAPR11MB3301.namprd11.prod.outlook.com> (raw)
In-Reply-To: <20200622132531.21857-7-david.marchand@redhat.com>


Hi David,

> diff --git a/lib/librte_eal/common/eal_common_lcore.c b/lib/librte_eal/common/eal_common_lcore.c
> index 86d32a3dd7..7db05428e7 100644
> --- a/lib/librte_eal/common/eal_common_lcore.c
> +++ b/lib/librte_eal/common/eal_common_lcore.c
> @@ -6,12 +6,13 @@
>  #include <limits.h>
>  #include <string.h>
> 
> -#include <rte_errno.h>
> -#include <rte_log.h>
> -#include <rte_eal.h>
> -#include <rte_lcore.h>
>  #include <rte_common.h>
>  #include <rte_debug.h>
> +#include <rte_eal.h>
> +#include <rte_errno.h>
> +#include <rte_lcore.h>
> +#include <rte_log.h>
> +#include <rte_spinlock.h>
> 
>  #include "eal_private.h"
>  #include "eal_thread.h"
> @@ -220,3 +221,38 @@ rte_socket_id_by_idx(unsigned int idx)
>  	}
>  	return config->numa_nodes[idx];
>  }
> +
> +static rte_spinlock_t lcore_lock = RTE_SPINLOCK_INITIALIZER;
> +
> +unsigned int
> +eal_lcore_non_eal_allocate(void)
> +{
> +	struct rte_config *cfg = rte_eal_get_configuration();
> +	unsigned int lcore_id;
> +
> +	rte_spinlock_lock(&lcore_lock);

I think it will break current DPDK MP modes.
The problem here - rte_config (and lcore_role[]) is in shared memory,
while the lock is local.
Simplest way probably to move lcore_lock to rte_config.

> +	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
> +		if (cfg->lcore_role[lcore_id] != ROLE_OFF)
> +			continue;
> +		cfg->lcore_role[lcore_id] = ROLE_NON_EAL;
> +		cfg->lcore_count++;
> +		break;
> +	}
> +	if (lcore_id == RTE_MAX_LCORE)
> +		RTE_LOG(DEBUG, EAL, "No lcore available.\n");
> +	rte_spinlock_unlock(&lcore_lock);
> +	return lcore_id;
> +}
> +
> +void
> +eal_lcore_non_eal_release(unsigned int lcore_id)
> +{
> +	struct rte_config *cfg = rte_eal_get_configuration();
> +
> +	rte_spinlock_lock(&lcore_lock);
> +	if (cfg->lcore_role[lcore_id] == ROLE_NON_EAL) {
> +		cfg->lcore_role[lcore_id] = ROLE_OFF;
> +		cfg->lcore_count--;
> +	}
> +	rte_spinlock_unlock(&lcore_lock);
> +}
> diff --git a/lib/librte_eal/common/eal_common_thread.c b/lib/librte_eal/common/eal_common_thread.c
> index a7ae0691bf..1cbddc4b5b 100644
> --- a/lib/librte_eal/common/eal_common_thread.c
> +++ b/lib/librte_eal/common/eal_common_thread.c
> @@ -236,3 +236,36 @@ rte_ctrl_thread_create(pthread_t *thread, const char *name,
>  	pthread_join(*thread, NULL);
>  	return -ret;
>  }
> +
> +void
> +rte_thread_register(void)
> +{
> +	unsigned int lcore_id;
> +	rte_cpuset_t cpuset;
> +
> +	/* EAL init flushes all lcores, we can't register before. */
> +	assert(internal_config.init_complete == 1);
> +	if (pthread_getaffinity_np(pthread_self(), sizeof(cpuset),
> +			&cpuset) != 0)
> +		CPU_ZERO(&cpuset);
> +	lcore_id = eal_lcore_non_eal_allocate();
> +	if (lcore_id >= RTE_MAX_LCORE)
> +		lcore_id = LCORE_ID_ANY;
> +	rte_thread_init(lcore_id, &cpuset);

So we just setting affinity to the same value, right?
Not a big deal, but might be easier to allow rte_thread_init()
to accept cpuset==NULL (and just don't change thread affinity in that case)

> +	if (lcore_id != LCORE_ID_ANY)
> +		RTE_LOG(DEBUG, EAL, "Registered non-EAL thread as lcore %u.\n",
> +			lcore_id);
> +}
> +
> +void
> +rte_thread_unregister(void)
> +{
> +	unsigned int lcore_id = rte_lcore_id();
> +
> +	if (lcore_id != LCORE_ID_ANY)
> +		eal_lcore_non_eal_release(lcore_id);
> +	rte_thread_uninit();
> +	if (lcore_id != LCORE_ID_ANY)
> +		RTE_LOG(DEBUG, EAL, "Unregistered non-EAL thread (was lcore %u).\n",
> +			lcore_id);
> +}
> diff --git a/lib/librte_eal/common/eal_private.h b/lib/librte_eal/common/eal_private.h
> index 0592fcd694..73238ff157 100644
> --- a/lib/librte_eal/common/eal_private.h
> +++ b/lib/librte_eal/common/eal_private.h
> @@ -396,6 +396,24 @@ uint64_t get_tsc_freq(void);
>   */
>  uint64_t get_tsc_freq_arch(void);
> 
> +/**
> + * Allocate a free lcore to associate to a non-EAL thread.
> + *
> + * @return
> + *   - the id of a lcore with role ROLE_NON_EAL on success.
> + *   - RTE_MAX_LCORE if none was available.
> + */
> +unsigned int eal_lcore_non_eal_allocate(void);
> +
> +/**
> + * Release the lcore used by a non-EAL thread.
> + * Counterpart of eal_lcore_non_eal_allocate().
> + *
> + * @param lcore_id
> + *   The lcore with role ROLE_NON_EAL to release.
> + */
> +void eal_lcore_non_eal_release(unsigned int lcore_id);
> +
>  /**
>   * Prepare physical memory mapping
>   * i.e. hugepages on Linux and
> diff --git a/lib/librte_eal/include/rte_lcore.h b/lib/librte_eal/include/rte_lcore.h
> index 3968c40693..ea86220394 100644
> --- a/lib/librte_eal/include/rte_lcore.h
> +++ b/lib/librte_eal/include/rte_lcore.h
> @@ -31,6 +31,7 @@ enum rte_lcore_role_t {
>  	ROLE_RTE,
>  	ROLE_OFF,
>  	ROLE_SERVICE,
> +	ROLE_NON_EAL,
>  };
> 
>  /**
> @@ -67,7 +68,8 @@ rte_lcore_has_role(unsigned int lcore_id, enum rte_lcore_role_t role);
>   *   to run threads with lcore IDs 0, 1, 2 and 3 on physical core 10..
>   *
>   * @return
> - *  Logical core ID (in EAL thread) or LCORE_ID_ANY (in non-EAL thread)
> + *  Logical core ID (in EAL thread or registered non-EAL thread) or
> + *  LCORE_ID_ANY (in unregistered non-EAL thread)
>   */
>  static inline unsigned
>  rte_lcore_id(void)
> @@ -279,6 +281,20 @@ int rte_thread_setname(pthread_t id, const char *name);
>  __rte_experimental
>  int rte_thread_getname(pthread_t id, char *name, size_t len);
> 
> +/**
> + * Register current non-EAL thread as a lcore.
> + */
> +__rte_experimental
> +void
> +rte_thread_register(void);
> +
> +/**
> + * Unregister current thread and release lcore if one was associated.
> + */
> +__rte_experimental
> +void
> +rte_thread_unregister(void);
> +
>  /**
>   * Create a control thread.
>   *
> diff --git a/lib/librte_eal/rte_eal_version.map b/lib/librte_eal/rte_eal_version.map
> index 5831eea4b0..39c41d445d 100644
> --- a/lib/librte_eal/rte_eal_version.map
> +++ b/lib/librte_eal/rte_eal_version.map
> @@ -396,6 +396,8 @@ EXPERIMENTAL {
> 
>  	# added in 20.08
>  	__rte_trace_mem_per_thread_free;
> +	rte_thread_register;
> +	rte_thread_unregister;
>  };
> 
>  INTERNAL {
> diff --git a/lib/librte_mempool/rte_mempool.h b/lib/librte_mempool/rte_mempool.h
> index 652d19f9f1..9e0ee052b3 100644
> --- a/lib/librte_mempool/rte_mempool.h
> +++ b/lib/librte_mempool/rte_mempool.h
> @@ -28,9 +28,9 @@
>   * rte_mempool_get() or rte_mempool_put() are designed to be called from an EAL
>   * thread due to the internal per-lcore cache. Due to the lack of caching,
>   * rte_mempool_get() or rte_mempool_put() performance will suffer when called
> - * by non-EAL threads. Instead, non-EAL threads should call
> - * rte_mempool_generic_get() or rte_mempool_generic_put() with a user cache
> - * created with rte_mempool_cache_create().
> + * by unregistered non-EAL threads. Instead, unregistered non-EAL threads
> + * should call rte_mempool_generic_get() or rte_mempool_generic_put() with a
> + * user cache created with rte_mempool_cache_create().
>   */
> 
>  #include <stdio.h>
> @@ -1233,7 +1233,7 @@ void rte_mempool_dump(FILE *f, struct rte_mempool *mp);
>  /**
>   * Create a user-owned mempool cache.
>   *
> - * This can be used by non-EAL threads to enable caching when they
> + * This can be used by unregistered non-EAL threads to enable caching when they
>   * interact with a mempool.
>   *
>   * @param size
> @@ -1264,7 +1264,8 @@ rte_mempool_cache_free(struct rte_mempool_cache *cache);
>   * @param lcore_id
>   *   The logical core id.
>   * @return
> - *   A pointer to the mempool cache or NULL if disabled or non-EAL thread.
> + *   A pointer to the mempool cache or NULL if disabled or unregistered non-EAL
> + *   thread.
>   */
>  static __rte_always_inline struct rte_mempool_cache *
>  rte_mempool_default_cache(struct rte_mempool *mp, unsigned lcore_id)
> --
> 2.23.0


  reply	other threads:[~2020-06-22 15:49 UTC|newest]

Thread overview: 133+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-06-10 14:44 [dpdk-dev] [PATCH 0/7] Register external threads as lcore David Marchand
2020-06-10 14:45 ` [dpdk-dev] [PATCH 1/7] eal: relocate per thread symbols to common David Marchand
2020-06-10 14:45 ` [dpdk-dev] [PATCH 2/7] eal: fix multiple definition of per lcore thread id David Marchand
2020-06-15  6:46   ` Kinsella, Ray
2020-06-10 14:45 ` [dpdk-dev] [PATCH 3/7] eal: introduce thread init helper David Marchand
2020-06-10 14:45 ` [dpdk-dev] [PATCH 4/7] eal: introduce thread uninit helper David Marchand
2020-06-10 14:45 ` [dpdk-dev] [PATCH 5/7] eal: register non-EAL threads as lcore David Marchand
2020-06-15  6:43   ` Kinsella, Ray
2020-06-10 14:45 ` [dpdk-dev] [PATCH 6/7] eal: dump lcores David Marchand
2020-06-15  6:40   ` Kinsella, Ray
2020-06-10 14:45 ` [dpdk-dev] [PATCH 7/7] eal: add lcore hotplug notifications David Marchand
2020-06-15  6:34   ` Kinsella, Ray
2020-06-15  7:13     ` David Marchand
2020-06-10 15:09 ` [dpdk-dev] [PATCH 0/7] Register external threads as lcore Jerin Jacob
2020-06-10 15:13   ` Bruce Richardson
2020-06-10 15:18   ` David Marchand
2020-06-10 15:33     ` Jerin Jacob
2020-06-15  7:11       ` David Marchand
2020-06-19 16:22 ` [dpdk-dev] [PATCH v2 0/9] Register non-EAL " David Marchand
2020-06-19 16:22   ` [dpdk-dev] [PATCH v2 1/9] eal: relocate per thread symbols to common David Marchand
2020-06-19 16:22   ` [dpdk-dev] [PATCH v2 2/9] eal: fix multiple definition of per lcore thread id David Marchand
2020-06-19 16:22   ` [dpdk-dev] [PATCH v2 3/9] eal: introduce thread init helper David Marchand
2020-06-19 16:22   ` [dpdk-dev] [PATCH v2 4/9] eal: introduce thread uninit helper David Marchand
2020-06-19 16:22   ` [dpdk-dev] [PATCH v2 5/9] eal: move lcore role code David Marchand
2020-06-19 16:22   ` [dpdk-dev] [PATCH v2 6/9] eal: register non-EAL threads as lcores David Marchand
2020-06-19 16:22   ` [dpdk-dev] [PATCH v2 7/9] eal: add lcore init callbacks David Marchand
2020-06-19 16:22   ` [dpdk-dev] [PATCH v2 8/9] eal: add lcore iterators David Marchand
2020-06-20  2:21     ` Stephen Hemminger
2020-06-19 16:22   ` [dpdk-dev] [PATCH v2 9/9] mempool/bucket: handle non-EAL lcores David Marchand
2020-06-22 13:25 ` [dpdk-dev] [PATCH v3 0/9] Register non-EAL threads as lcore David Marchand
2020-06-22 13:25   ` [dpdk-dev] [PATCH v3 1/9] eal: relocate per thread symbols to common David Marchand
2020-06-22 13:25   ` [dpdk-dev] [PATCH v3 2/9] eal: fix multiple definition of per lcore thread id David Marchand
2020-06-22 13:25   ` [dpdk-dev] [PATCH v3 3/9] eal: introduce thread init helper David Marchand
2020-06-22 13:25   ` [dpdk-dev] [PATCH v3 4/9] eal: introduce thread uninit helper David Marchand
2020-06-22 13:25   ` [dpdk-dev] [PATCH v3 5/9] eal: move lcore role code David Marchand
2020-06-22 13:25   ` [dpdk-dev] [PATCH v3 6/9] eal: register non-EAL threads as lcores David Marchand
2020-06-22 15:49     ` Ananyev, Konstantin [this message]
2020-06-22 16:37       ` Ananyev, Konstantin
2020-06-23  7:49       ` David Marchand
2020-06-23  9:14         ` Bruce Richardson
2020-06-23 12:49           ` David Marchand
2020-06-23 13:15         ` Ananyev, Konstantin
2020-06-24  9:23           ` David Marchand
2020-06-24  9:56             ` Bruce Richardson
2020-06-24 10:08               ` Thomas Monjalon
2020-06-24 10:45                 ` Ananyev, Konstantin
2020-06-24 10:39             ` Ananyev, Konstantin
2020-06-24 10:48               ` David Marchand
2020-06-24 11:59                 ` Ananyev, Konstantin
2020-06-26 14:43                   ` David Marchand
2020-06-30 10:35                     ` Thomas Monjalon
2020-06-30 12:07                       ` Ananyev, Konstantin
2020-06-30 12:44                         ` Olivier Matz
2020-06-30 14:37                           ` Thomas Monjalon
2020-06-30 19:02                           ` Ananyev, Konstantin
2020-06-30 14:35                         ` Thomas Monjalon
2020-06-30 18:57                           ` Ananyev, Konstantin
2020-07-01  7:48                             ` David Marchand
2020-07-01 11:58                               ` Ananyev, Konstantin
2020-07-02 13:06                                 ` David Marchand
2020-07-03 15:15                                   ` Thomas Monjalon
2020-07-03 16:40                                   ` Ananyev, Konstantin
2020-07-04 15:00                                     ` David Marchand
2020-07-04 21:24                                       ` Ananyev, Konstantin
2020-06-23 17:02     ` Andrew Rybchenko
2020-06-22 13:25   ` [dpdk-dev] [PATCH v3 7/9] eal: add lcore init callbacks David Marchand
2020-06-22 13:25   ` [dpdk-dev] [PATCH v3 8/9] eal: add lcore iterators David Marchand
2020-06-22 13:25   ` [dpdk-dev] [PATCH v3 9/9] mempool/bucket: handle non-EAL lcores David Marchand
2020-06-23 17:28     ` Andrew Rybchenko
2020-06-26 14:13       ` David Marchand
2020-06-26 14:34         ` Andrew Rybchenko
2020-06-26 14:47 ` [dpdk-dev] [PATCH v4 0/9] Register non-EAL threads as lcore David Marchand
2020-06-26 14:47   ` [dpdk-dev] [PATCH v4 1/9] eal: relocate per thread symbols to common David Marchand
2020-06-30  9:33     ` Olivier Matz
2020-06-26 14:47   ` [dpdk-dev] [PATCH v4 2/9] eal: fix multiple definition of per lcore thread id David Marchand
2020-06-30  9:34     ` Olivier Matz
2020-06-26 14:47   ` [dpdk-dev] [PATCH v4 3/9] eal: introduce thread init helper David Marchand
2020-06-30  9:37     ` Olivier Matz
2020-06-30 12:04       ` David Marchand
2020-06-26 14:47   ` [dpdk-dev] [PATCH v4 4/9] eal: introduce thread uninit helper David Marchand
2020-06-26 15:00     ` Jerin Jacob
2020-06-29  9:07       ` David Marchand
2020-06-29  8:59     ` [dpdk-dev] [EXT] " Sunil Kumar Kori
2020-06-29  9:25       ` David Marchand
2020-06-30  9:42     ` [dpdk-dev] " Olivier Matz
2020-07-01  8:00       ` David Marchand
2020-06-26 14:47   ` [dpdk-dev] [PATCH v4 5/9] eal: move lcore role code David Marchand
2020-06-30  9:45     ` Olivier Matz
2020-06-26 14:47   ` [dpdk-dev] [PATCH v4 6/9] eal: register non-EAL threads as lcores David Marchand
2020-06-29 14:27     ` Ananyev, Konstantin
2020-06-30 10:07     ` Olivier Matz
2020-07-01  7:13       ` David Marchand
2020-07-01  9:11         ` Olivier Matz
2020-06-26 14:47   ` [dpdk-dev] [PATCH v4 7/9] eal: add lcore init callbacks David Marchand
2020-06-29 12:46     ` Ananyev, Konstantin
2020-06-30 10:09     ` Olivier Matz
2020-06-30 10:15     ` Olivier Matz
2020-06-26 14:47   ` [dpdk-dev] [PATCH v4 8/9] eal: add lcore iterators David Marchand
2020-06-30 10:11     ` Olivier Matz
2020-06-26 14:47   ` [dpdk-dev] [PATCH v4 9/9] mempool/bucket: handle non-EAL lcores David Marchand
2020-06-26 14:52     ` Andrew Rybchenko
2020-07-06 14:15 ` [dpdk-dev] [PATCH v5 00/10] Register non-EAL threads as lcore David Marchand
2020-07-06 14:15   ` [dpdk-dev] [PATCH v5 01/10] eal: relocate per thread symbols to common David Marchand
2020-07-06 14:15   ` [dpdk-dev] [PATCH v5 02/10] eal: fix multiple definition of per lcore thread id David Marchand
2020-07-06 14:15   ` [dpdk-dev] [PATCH v5 03/10] eal: introduce thread init helper David Marchand
2020-07-06 14:16   ` [dpdk-dev] [PATCH v5 04/10] eal: introduce thread uninit helper David Marchand
2020-07-06 14:16   ` [dpdk-dev] [PATCH v5 05/10] eal: move lcore role code David Marchand
2020-07-06 14:16   ` [dpdk-dev] [PATCH v5 06/10] eal: register non-EAL threads as lcores David Marchand
2020-07-06 14:16   ` [dpdk-dev] [PATCH v5 07/10] eal: add lcore init callbacks David Marchand
2020-07-06 14:16   ` [dpdk-dev] [PATCH v5 08/10] eal: add lcore iterators David Marchand
2020-07-06 14:16   ` [dpdk-dev] [PATCH v5 09/10] mempool/bucket: handle non-EAL lcores David Marchand
2020-07-06 14:16   ` [dpdk-dev] [PATCH v5 10/10] eal: add multiprocess disable API David Marchand
2020-07-06 20:52 ` [dpdk-dev] [PATCH v6 00/10] Register non-EAL threads as lcore David Marchand
2020-07-06 20:52   ` [dpdk-dev] [PATCH v6 01/10] eal: relocate per thread symbols to common David Marchand
2020-07-06 20:52   ` [dpdk-dev] [PATCH v6 02/10] eal: fix multiple definition of per lcore thread id David Marchand
2020-07-06 20:52   ` [dpdk-dev] [PATCH v6 03/10] eal: introduce thread init helper David Marchand
2020-07-06 20:52   ` [dpdk-dev] [PATCH v6 04/10] eal: introduce thread uninit helper David Marchand
2020-07-06 20:52   ` [dpdk-dev] [PATCH v6 05/10] eal: move lcore role code David Marchand
2020-07-06 20:52   ` [dpdk-dev] [PATCH v6 06/10] eal: register non-EAL threads as lcores David Marchand
2022-11-03  9:02     ` Morten Brørup
2022-11-03 10:15       ` Thomas Monjalon
2020-07-06 20:52   ` [dpdk-dev] [PATCH v6 07/10] eal: add lcore init callbacks David Marchand
2022-12-15  9:05     ` Morten Brørup
2022-12-15  9:09       ` David Marchand
2022-12-15 10:21         ` Morten Brørup
2022-12-16  8:09           ` David Marchand
2022-12-16  9:55             ` Morten Brørup
2020-07-06 20:52   ` [dpdk-dev] [PATCH v6 08/10] eal: add lcore iterators David Marchand
2020-07-06 20:52   ` [dpdk-dev] [PATCH v6 09/10] mempool/bucket: handle non-EAL lcores David Marchand
2020-07-06 20:52   ` [dpdk-dev] [PATCH v6 10/10] eal: add multiprocess disable API David Marchand
2020-07-06 23:22   ` [dpdk-dev] [PATCH v6 00/10] Register non-EAL threads as lcore Ananyev, Konstantin
2020-07-08 13:05     ` David Marchand
2020-07-08 13:06   ` David Marchand

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=BYAPR11MB33010EBC5471E7157AA664359A970@BYAPR11MB3301.namprd11.prod.outlook.com \
    --to=konstantin.ananyev@intel.com \
    --cc=anatoly.burakov@intel.com \
    --cc=arybchenko@solarflare.com \
    --cc=bruce.richardson@intel.com \
    --cc=david.marchand@redhat.com \
    --cc=dev@dpdk.org \
    --cc=i.maximets@ovn.org \
    --cc=ian.stokes@intel.com \
    --cc=jerinjacobk@gmail.com \
    --cc=john.mcnamara@intel.com \
    --cc=ktraynor@redhat.com \
    --cc=marko.kovacevic@intel.com \
    --cc=mdr@ashroe.eu \
    --cc=nhorman@tuxdriver.com \
    --cc=olivier.matz@6wind.com \
    --cc=thomas@monjalon.net \
    /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
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.