All of lore.kernel.org
 help / color / mirror / Atom feed
From: NeilBrown <neilb@suse.com>
To: lustre-devel@lists.lustre.org
Subject: [lustre-devel] [PATCH 37/37] lustre: obd_sysfs: error-check value stored in jobid_var
Date: Fri, 01 Mar 2019 13:35:52 +1100	[thread overview]
Message-ID: <87woljs4mv.fsf@notabene.neil.brown.name> (raw)
In-Reply-To: <4271D74A-4B36-4781-A1DA-F5512CBCAF15@whamcloud.com>

On Wed, Feb 27 2019, Andreas Dilger wrote:

> On Feb 18, 2019, at 17:09, NeilBrown <neilb@suse.com> wrote:
>> 
>> The jobid_var sysfs attribute only has 3 meaningful values.
>> Other values cause lustre_get_jobid() to return an error
>> which is uniformly ignored.
>> 
>> To improve usability and resilience, check that the value
>> written is acceptable before storing it.
>> 
>> Signed-off-by: NeilBrown <neilb@suse.com>
>
> This will no longer be true once https://review.whamcloud.com/31691
> commit 6488c0ec57de ("LU-10698 obdclass: allow specifying complex jobids")

Actually it will.  That patch changes the use of jobid_name, my patch
restricts the values of jobid_var.

I just realized why it is called "jobid_var" - in OpenSFS lustre, it can
be an environment variable name.  In drivers/staging lustre it cannot,
so the name is a little odd.

>
> Currently the "%j" function was removed from the kernel client, even
> though there is no technical reason it can't work (i.e. all of the code
> to implement it is available and exported).  This is actually super
> useful for HPC cluster administrators to monitor per-job IO bandwidth
> and IOPS on the server, and something that I think should be restored.

I think that you probably need to let go of that desire - I don't think
it is going to happen.  While the code may, as you say, work - it is
easy to dislike that approach, and would be hard to push against such
resistance.

I have an alternate approach, patch below.
Instead of
 export LUSTRE_JOBID=foobar
and process can run
 echo foobar > /sys/fs/lustre/jobid_this_session

and it will affect all processes in the current "session".

Could you warm to this approach at all?

Thanks,
NeilBrown

From: NeilBrown <neilb@suse.com>
Subject: [PATCH] lustre: obdclass: allow per-session jobids.

Lustre includes a jobid in all RPC message sent to the server.  This
is used to collected per-job statistics, where a "job" can involve
multiple processes on multiple nodes in a cluster.

Nodes in a cluster can be running processes for multiple jobs, so it
is best if different processes can have different jobids, and that
processes on different nodes can have the same job id.

This is not currently possible with the drivers/staging code.

Lustre traditionally uses an environment variable to name a job, but
having the kernel reach into the address space of a process to find
that environment variable is seen by some developers to be an
unacceptable design choice.

This patch provides an alternate method, leveraging the concept of a
"session id", as set with setsid().  Each login session already gets a
unique sid which is preserved for all processes in that session unless
explicitly changed (with setsid(1)).
When a process in a session writes to
/sys/fs/lustre/jobid_this_session, the string becomes the name for
that session.
If jobid_var is set to "manual", then the per-session jobid is used
for the jobid for all requests from processes in that session.

When a session ends, the jobid information will be purged within 5
minutes.

Signed-off-by: NeilBrown <neilb@suse.com>
---
 .../staging/lustre/lustre/include/lprocfs_status.h |   1 +
 drivers/staging/lustre/lustre/include/obd_class.h  |   3 +
 drivers/staging/lustre/lustre/obdclass/class_obd.c | 160 ++++++++++++++++++++-
 drivers/staging/lustre/lustre/obdclass/obd_sysfs.c |  41 ++++++
 4 files changed, 204 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/lustre/lustre/include/lprocfs_status.h b/drivers/staging/lustre/lustre/include/lprocfs_status.h
index 8565c28f08ee..1335a5722903 100644
--- a/drivers/staging/lustre/lustre/include/lprocfs_status.h
+++ b/drivers/staging/lustre/lustre/include/lprocfs_status.h
@@ -370,6 +370,7 @@ static inline void s2dhms(struct dhms *ts, time64_t secs64)
 #define JOBSTATS_DISABLE		"disable"
 #define JOBSTATS_PROCNAME_UID		"procname_uid"
 #define JOBSTATS_NODELOCAL		"nodelocal"
+#define JOBSTATS_MANUAL			"manual"
 
 /* obd_config.c */
 void lustre_register_client_process_config(int (*cpc)(struct lustre_cfg *lcfg));
diff --git a/drivers/staging/lustre/lustre/include/obd_class.h b/drivers/staging/lustre/lustre/include/obd_class.h
index 50b08c89ecc5..08003f3dd467 100644
--- a/drivers/staging/lustre/lustre/include/obd_class.h
+++ b/drivers/staging/lustre/lustre/include/obd_class.h
@@ -55,6 +55,9 @@ extern rwlock_t obd_dev_lock;
 struct obd_device *class_exp2obd(struct obd_export *exp);
 int class_handle_ioctl(unsigned int cmd, unsigned long arg);
 int lustre_get_jobid(char *jobid);
+char *jobid_current(void);
+int jobid_set_current(char *jobid);
+
 
 struct lu_device_type;
 
diff --git a/drivers/staging/lustre/lustre/obdclass/class_obd.c b/drivers/staging/lustre/lustre/obdclass/class_obd.c
index 1fcbda128a58..19ce3c858e59 100644
--- a/drivers/staging/lustre/lustre/obdclass/class_obd.c
+++ b/drivers/staging/lustre/lustre/obdclass/class_obd.c
@@ -79,6 +79,144 @@ EXPORT_SYMBOL(at_extra);
 char obd_jobid_var[JOBSTATS_JOBID_VAR_MAX_LEN + 1] = JOBSTATS_DISABLE;
 char obd_jobid_node[LUSTRE_JOBID_SIZE + 1];
 
+/*
+ * Jobid can be set for a session (see setsid(2)) by writing to
+ * a sysfs file from any process in that session.
+ * The jobids are stored in a hash table indexed by the relevant
+ * struct pid.  We periodically look for entries where the pid has
+ * no PIDTYPE_SID tasks any more, and prune them.  This happens within
+ * 5 seconds of a jobid being added, and every 5 minutes when jobids exist,
+ * but none are added.
+ */
+#define JOBID_EXPEDITED_CLEAN (5 * HZ)
+#define JOBID_BACKGROUND_CLEAN (5 * 60 * HZ)
+
+struct session_jobid {
+	struct pid		*session;
+	struct rhash_head	linkage;
+	struct rcu_head		rcu;
+	char			jobid[1];
+};
+
+const static struct rhashtable_params jobid_params = {
+	.key_len	= sizeof(struct pid *),
+	.key_offset	= offsetof(struct session_jobid, session),
+	.head_offset	= offsetof(struct session_jobid, linkage),
+};
+static struct rhashtable session_jobids;
+
+/*
+ * jobid_current must be called with rcu_read_lock held.
+ * if it returns non-NULL, the string can only be used
+ * until rcu_read_unlock is called.
+ */
+char *jobid_current(void)
+{
+	struct pid *sid = current->signal->pids[PIDTYPE_SID];
+	struct session_jobid *sj;
+
+	sj = rhashtable_lookup_fast(&session_jobids, &sid, jobid_params);
+	if (sj)
+		return sj->jobid;
+	return NULL;
+}
+
+static void jobid_prune_expedite(void);
+/*
+ * jobid_set_current will try to add a new entry
+ * to the table.  If one exists with the same key, the
+ * jobid will be replaced
+ */
+int jobid_set_current(char *jobid)
+{
+	struct pid *sid = current->signal->pids[PIDTYPE_SID];
+	struct session_jobid *sj, *origsj;
+	int ret;
+
+	sj = kmalloc(sizeof(*sj) + strlen(jobid), GFP_KERNEL);
+	if (!sj)
+		return -ENOMEM;
+	rcu_read_lock();
+	sj->session = get_pid(sid);
+	strcpy(sj->jobid, jobid);
+	origsj = rhashtable_lookup_get_insert_fast(&session_jobids,
+						   &sj->linkage,
+						   jobid_params);
+	if (origsj == NULL) {
+		/* successful insert */
+		rcu_read_unlock();
+		jobid_prune_expedite();
+		return 0;
+	}
+
+	if (IS_ERR(origsj)) {
+		put_pid(sj->session);
+		kfree(sj);
+		rcu_read_unlock();
+		return PTR_ERR(origsj);
+	}
+	ret = rhashtable_replace_fast(&session_jobids,
+				      &origsj->linkage,
+				      &sj->linkage,
+				      jobid_params);
+	if (ret) {
+		put_pid(sj->session);
+		kfree(sj);
+		rcu_read_unlock();
+		return ret;
+	}
+	put_pid(origsj->session);
+	rcu_read_unlock();
+	kfree_rcu(origsj, rcu);
+	jobid_prune_expedite();
+
+	return 0;
+}
+
+static void jobid_free(void *vsj, void *arg)
+{
+	struct session_jobid *sj = vsj;
+	put_pid(sj->session);
+	kfree(sj);
+}
+
+static void jobid_prune(struct work_struct *work);
+static DECLARE_DELAYED_WORK(jobid_prune_work, jobid_prune);
+static int jobid_prune_expedited;
+static void jobid_prune(struct work_struct *work)
+{
+	int remaining = 0;
+	struct rhashtable_iter iter;
+	struct session_jobid *sj;
+
+	jobid_prune_expedited = 0;
+	rhashtable_walk_enter(&session_jobids, &iter);
+	rhashtable_walk_start(&iter);
+	while ((sj = rhashtable_walk_next(&iter)) != NULL) {
+		if (!hlist_empty(&sj->session->tasks[PIDTYPE_SID])) {
+			remaining++;
+			continue;
+		}
+		if (rhashtable_remove_fast(&session_jobids,
+					   &sj->linkage, jobid_params) == 0) {
+			put_pid(sj->session);
+			kfree_rcu(sj, rcu);
+		}
+	}
+	rhashtable_walk_stop(&iter);
+	rhashtable_walk_exit(&iter);
+	if (remaining)
+		schedule_delayed_work(&jobid_prune_work, JOBID_BACKGROUND_CLEAN);
+}
+
+static void jobid_prune_expedite(void)
+{
+	if (!jobid_prune_expedited) {
+		jobid_prune_expedited = 1;
+		mod_delayed_work(system_wq, &jobid_prune_work, JOBID_EXPEDITED_CLEAN);
+	}
+}
+
 /* Get jobid of current process from stored variable or calculate
  * it from pid and user_id.
  *
@@ -108,6 +246,17 @@ int lustre_get_jobid(char *jobid)
 		goto out_cache_jobid;
 	}
 
+	if (strcmp(obd_jobid_var, JOBSTATS_MANUAL) == 0) {
+		char *jid;
+		rcu_read_lock();
+		jid = jobid_current();
+		if (jid)
+			strlcpy(tmp_jobid, jid, sizeof(tmp_jobid));
+		rcu_read_unlock();
+		if (jid)
+			goto out_cache_jobid;
+	}
+
 	return -ENOENT;
 
 out_cache_jobid:
@@ -663,10 +812,13 @@ static int __init obdclass_init(void)
 	if (err)
 		goto cleanup_zombie_impexp;
 
+	err = rhashtable_init(&session_jobids, &jobid_params);
+	if (err)
+		goto cleanup_class_handle;
 	err = misc_register(&obd_psdev);
 	if (err) {
 		CERROR("cannot register OBD miscdevices: err %d\n", err);
-		goto cleanup_class_handle;
+		goto cleanup_session_jobids;
 	}
 
 	/* Default the dirty page cache cap to 1/2 of system memory.
@@ -724,6 +876,9 @@ static int __init obdclass_init(void)
 cleanup_deregister:
 	misc_deregister(&obd_psdev);
 
+cleanup_session_jobids:
+	rhashtable_free_and_destroy(&session_jobids, jobid_free, NULL);
+
 cleanup_class_handle:
 	class_handle_cleanup();
 
@@ -743,6 +898,9 @@ static void obdclass_exit(void)
 	cl_global_fini();
 	lu_global_fini();
 
+	cancel_delayed_work_sync(&jobid_prune_work);
+	rhashtable_free_and_destroy(&session_jobids, jobid_free, NULL);
+
 	obd_cleanup_caches();
 
 	class_procfs_clean();
diff --git a/drivers/staging/lustre/lustre/obdclass/obd_sysfs.c b/drivers/staging/lustre/lustre/obdclass/obd_sysfs.c
index 69ccc6a55947..112782e56793 100644
--- a/drivers/staging/lustre/lustre/obdclass/obd_sysfs.c
+++ b/drivers/staging/lustre/lustre/obdclass/obd_sysfs.c
@@ -220,6 +220,7 @@ static ssize_t jobid_var_store(struct kobject *kobj, struct attribute *attr,
 		JOBSTATS_DISABLE,
 		JOBSTATS_PROCNAME_UID,
 		JOBSTATS_NODELOCAL,
+		JOBSTATS_MANUAL,
 		NULL
 	};
 	int i;
@@ -263,6 +264,44 @@ static ssize_t jobid_name_store(struct kobject *kobj, struct attribute *attr,
 	return count;
 }
 
+static ssize_t jobid_this_session_show(struct kobject *kobj,
+				       struct attribute *attr,
+				       char *buf)
+{
+	char *jid;
+	int ret = -ENOENT;
+
+	rcu_read_lock();
+	jid = jobid_current();
+	if (jid)
+		ret = snprintf(buf, PAGE_SIZE, "%s\n", jid);
+	rcu_read_unlock();
+	return ret;
+}
+
+static ssize_t jobid_this_session_store(struct kobject *kobj,
+					struct attribute *attr,
+					const char *buffer,
+					size_t count)
+{
+	char *jobid;
+	int len;
+	int ret;
+
+	if (!count || count > LUSTRE_JOBID_SIZE)
+		return -EINVAL;
+
+	jobid = kstrndup(buffer, count, GFP_KERNEL);
+	if (!jobid)
+		return -ENOMEM;
+	len = strcspn(jobid, " \n");
+	jobid[len] = '\0';
+	ret = jobid_set_current(jobid);
+	kfree(jobid);
+
+	return ret ?: count;
+}
+
 /* Root for /sys/kernel/debug/lustre */
 struct dentry *debugfs_lustre_root;
 EXPORT_SYMBOL_GPL(debugfs_lustre_root);
@@ -272,6 +311,7 @@ LUSTRE_RO_ATTR(pinger);
 LUSTRE_RO_ATTR(health_check);
 LUSTRE_RW_ATTR(jobid_var);
 LUSTRE_RW_ATTR(jobid_name);
+LUSTRE_RW_ATTR(jobid_this_session);
 
 static struct attribute *lustre_attrs[] = {
 	&lustre_attr_version.attr,
@@ -279,6 +319,7 @@ static struct attribute *lustre_attrs[] = {
 	&lustre_attr_health_check.attr,
 	&lustre_attr_jobid_name.attr,
 	&lustre_attr_jobid_var.attr,
+	&lustre_attr_jobid_this_session.attr,
 	&lustre_sattr_timeout.u.attr,
 	&lustre_attr_max_dirty_mb.attr,
 	&lustre_sattr_debug_peer_on_timeout.u.attr,
-- 
2.14.0.rc0.dirty

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 832 bytes
Desc: not available
URL: <http://lists.lustre.org/pipermail/lustre-devel-lustre.org/attachments/20190301/2a1dee12/attachment.sig>

  reply	other threads:[~2019-03-01  2:35 UTC|newest]

Thread overview: 105+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-02-19  0:09 [lustre-devel] [PATCH 00/37] More lustre patches from obdclass NeilBrown
2019-02-19  0:09 ` [lustre-devel] [PATCH 01/37] lustre: obdclass: char obd_ioctl_getdata type NeilBrown
2019-02-24 18:35   ` James Simmons
2019-02-19  0:09 ` [lustre-devel] [PATCH 02/37] lustre: llite: don't use class_setup_tunables() NeilBrown
2019-02-24 16:35   ` James Simmons
2019-02-25 22:27     ` NeilBrown
2019-02-26 22:18       ` James Simmons
2019-02-24 16:52   ` [lustre-devel] [PATCH 03/37] lustre: embed typ_kobj if obd_type James Simmons
2019-02-25 22:38     ` NeilBrown
2019-02-26 20:41       ` Simmons, James A.
2019-02-19  0:09 ` [lustre-devel] [PATCH 14/37] lustre: llog: change lgh_refcount to struct kref NeilBrown
2019-02-25 18:16   ` Andreas Dilger
2019-02-19  0:09 ` [lustre-devel] [PATCH 13/37] lustre: llog: remove lgh_hdr_lock NeilBrown
2019-02-24 20:29   ` James Simmons
2019-02-25 18:16   ` Andreas Dilger
2019-02-19  0:09 ` [lustre-devel] [PATCH 07/37] lustre: obd_type: discard obd_type_lock NeilBrown
2019-02-24 17:02   ` James Simmons
2019-02-19  0:09 ` [lustre-devel] [PATCH 08/37] lustre: obdclass: don't copy ops structures in to new type NeilBrown
2019-02-24 17:03   ` James Simmons
2019-02-19  0:09 ` [lustre-devel] [PATCH 16/37] lustre: obdclass: typo: Banlance -> Balance NeilBrown
2019-02-24 17:39   ` James Simmons
2019-02-19  0:09 ` [lustre-devel] [PATCH 05/37] lustre: obd_type: use typ_kobj.name as typ_name NeilBrown
2019-02-24 16:56   ` James Simmons
2019-02-19  0:09 ` [lustre-devel] [PATCH 03/37] lustre: embed typ_kobj if obd_type NeilBrown
2019-02-19  0:09 ` [lustre-devel] [PATCH 09/37] lustre: obdclass: fix module load locking NeilBrown
2019-02-24 17:04   ` James Simmons
2019-02-19  0:09 ` [lustre-devel] [PATCH 17/37] lustre: simplify lprocfs_read_frac_helper NeilBrown
2019-02-24 17:52   ` James Simmons
2019-02-26 23:59     ` NeilBrown
2019-02-27  1:06       ` Andreas Dilger
2019-02-19  0:09 ` [lustre-devel] [PATCH 18/37] lustre: obdclass: discard lprocfs_single/seq_release NeilBrown
2019-02-24 17:53   ` James Simmons
2019-02-19  0:09 ` [lustre-devel] [PATCH 12/37] lustre: remove unused function in linkea NeilBrown
2019-02-25 18:16   ` Andreas Dilger
2019-02-19  0:09 ` [lustre-devel] [PATCH 10/37] lustre: kernelcomm: pass correct gfp_t to kmalloc NeilBrown
2019-02-24 17:05   ` James Simmons
2019-02-25 18:16     ` Andreas Dilger
2019-02-19  0:09 ` [lustre-devel] [PATCH 21/37] lustre: remove several MAX_STRING_SIZE defines NeilBrown
2019-02-24 19:07   ` James Simmons
2019-02-27  0:41     ` NeilBrown
2019-02-25 18:16   ` Andreas Dilger
2019-02-19  0:09 ` [lustre-devel] [PATCH 19/37] lustre: discard lprocfs_strnstr() NeilBrown
2019-02-24 17:53   ` James Simmons
2019-02-19  0:09 ` [lustre-devel] [PATCH 15/37] lustre: llog_obd: Convert loc_refcount to refcount_t NeilBrown
2019-02-25 18:16   ` Andreas Dilger
2019-02-19  0:09 ` [lustre-devel] [PATCH 11/37] lustre: kernelcomm: make libcfs_kkuc_msg_put static NeilBrown
2019-02-24 17:15   ` James Simmons
2019-02-26 23:45     ` NeilBrown
2019-02-27 22:36       ` James Simmons
2019-02-27 22:37   ` James Simmons
2019-02-19  0:09 ` [lustre-devel] [PATCH 04/37] lustre: collect all resource releasing for obj_type NeilBrown
2019-02-24 16:54   ` James Simmons
2019-02-19  0:09 ` [lustre-devel] [PATCH 06/37] lustre: obd_type: discard obd_types linked list NeilBrown
2019-02-24 17:00   ` James Simmons
2019-02-19  0:09 ` [lustre-devel] [PATCH 20/37] lustre: convert rsi_sem to a spinlock NeilBrown
2019-02-25 18:16   ` Andreas Dilger
2019-02-27  0:22     ` NeilBrown
2019-02-27  1:00       ` Andreas Dilger
2019-02-19  0:09 ` [lustre-devel] [PATCH 32/37] lustre: portals_handle: rename ops to owner NeilBrown
2019-02-19  0:09 ` [lustre-devel] [PATCH 22/37] lustre: lprocfs: use log2.h macros instead of shift loop NeilBrown
2019-02-24 18:09   ` James Simmons
2019-02-26 20:55   ` James Simmons
2019-02-27  0:51     ` NeilBrown
2019-02-27  0:54       ` Andreas Dilger
2019-02-19  0:09 ` [lustre-devel] [PATCH 25/37] lustre: deprecate libcfs_debug_vmsg2 NeilBrown
2019-02-24 20:02   ` James Simmons
2019-02-25 18:16   ` Andreas Dilger
2019-02-19  0:09 ` [lustre-devel] [PATCH 30/37] lustre: handle: move refcount into the lustre_handle NeilBrown
2019-02-27  6:32   ` Andreas Dilger
2019-02-27 21:48     ` NeilBrown
2019-02-27 22:14       ` Andreas Dilger
2019-02-19  0:09 ` [lustre-devel] [PATCH 28/37] lustre: remove scope and source from class_incref and class_decref NeilBrown
2019-02-27  6:52   ` Andreas Dilger
2019-02-28  0:39     ` NeilBrown
2019-02-19  0:09 ` [lustre-devel] [PATCH 26/37] lustre: remove libcfs_debug_vmsg2 NeilBrown
2019-02-25 18:16   ` Andreas Dilger
2019-02-19  0:09 ` [lustre-devel] [PATCH 23/37] lustre: prefer to use tabs for alignment NeilBrown
2019-02-24 18:51   ` James Simmons
2019-02-25 18:16   ` Andreas Dilger
2019-02-19  0:09 ` [lustre-devel] [PATCH 27/37] lustre: discard lu_ref NeilBrown
2019-02-24 20:28   ` James Simmons
2019-02-27  1:17     ` NeilBrown
2019-02-27  5:35       ` Andreas Dilger
2019-03-01  6:45         ` Mike Pershin
2019-02-19  0:09 ` [lustre-devel] [PATCH 29/37] lustre: handles: discard h_owner in favour of h_ops NeilBrown
2019-02-27  6:37   ` Andreas Dilger
2019-02-27 21:41     ` NeilBrown
2019-02-28  6:41       ` Andreas Dilger
2019-02-19  0:09 ` [lustre-devel] [PATCH 33/37] lustre: portals_handle: remove locking from class_handle2object() NeilBrown
2019-02-19  0:09 ` [lustre-devel] [PATCH 34/37] lustre: portals_handle: use hlist for hash lists NeilBrown
2019-02-19  0:09 ` [lustre-devel] [PATCH 31/37] lustre: discard OBD_FREE_RCU NeilBrown
2019-02-19  0:09 ` [lustre-devel] [PATCH 24/37] lustre: lu_object: remove extra newline from debug printing NeilBrown
2019-02-24 19:08   ` James Simmons
2019-02-25 18:16   ` Andreas Dilger
2019-02-19  0:09 ` [lustre-devel] [PATCH 35/37] lustre: portals_handle: discard h_lock NeilBrown
2019-02-19  0:09 ` [lustre-devel] [PATCH 37/37] lustre: obd_sysfs: error-check value stored in jobid_var NeilBrown
2019-02-27  6:17   ` Andreas Dilger
2019-03-01  2:35     ` NeilBrown [this message]
2019-03-01  8:32       ` Andreas Dilger
2019-03-01 14:30         ` Patrick Farrell
2019-03-14  0:34           ` NeilBrown
2019-03-14 14:12             ` Patrick Farrell
2019-03-14 22:56               ` NeilBrown
2019-03-14 23:05               ` Andreas Dilger
2019-02-19  0:09 ` [lustre-devel] [PATCH 36/37] lustre: remove unused fields from struct obd_device NeilBrown

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=87woljs4mv.fsf@notabene.neil.brown.name \
    --to=neilb@suse.com \
    --cc=lustre-devel@lists.lustre.org \
    /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.