From: Boaz Harrosh <bharrosh@panasas.com>
To: Boaz Harrosh <bharrosh@panasas.com>
Cc: Trond Myklebust <Trond.Myklebust@netapp.com>,
Benny Halevy <bhalevy@tonian.com>,
NFS list <linux-nfs@vger.kernel.org>,
open-osd <osd-dev@open-osd.org>,
"Bhamare, Sachin" <sbhamare@panasas.com>,
Steve Dickson <steved@redhat.com>,
"Welch, Brent" <welch@panasas.com>
Subject: Re: [PATCH 1/4] pnfsd-exofs: Add autologin support to exofs
Date: Thu, 15 Mar 2012 23:30:09 -0700 [thread overview]
Message-ID: <4F62DDF1.9060000@panasas.com> (raw)
In-Reply-To: <4F62DB6C.6040202@panasas.com>
On 03/15/2012 11:19 PM, Boaz Harrosh wrote:
>
Hi Benny I made a mistake with this one it is actually:
From: Sachin Bhamare <sbhamare@panasas.com>
Eventually we'll fix this. please also do so in your
tree.
Thanks
Boaz
> Introduce sysfs infrastructure for exofs cluster filesystem.
>
> Each OSD target shows up as below in the sysfs hierarchy:
> /sys/fs/exofs/<osdname>_<partition_id>/devX
>
> where in devX 0 <= X < device_table_size. They are ordered
> in device-table order as specified to the mkfs.exofs command
>
> Each OSD device devX has following attributes :
> osdname - ReadOnly
> systemid - ReadOnly
> uri - Read/Write
>
> It is up to user-mode to update devX/uri for support of
> autologin.
>
> Also fixed is the xdr encoding of device_info to support
> uri encoding.
>
> Signed-off-by: Sachin Bhamare <sbhamare@panasas.com>
> Signed-off-by: Boaz Harrosh <bharrosh@panasas.com>
> ---
> fs/exofs/Kbuild | 2 +-
> fs/exofs/exofs.h | 4 +
> fs/exofs/export.c | 8 ++-
> fs/exofs/super.c | 14 +++
> fs/exofs/sys.c | 202 ++++++++++++++++++++++++++++++++++++++++
> fs/exofs/sys.h | 41 ++++++++
> fs/exportfs/pnfs_osd_xdr_srv.c | 45 ++++++++-
> include/linux/pnfs_osd_xdr.h | 5 +
> 8 files changed, 316 insertions(+), 5 deletions(-)
> create mode 100644 fs/exofs/sys.c
> create mode 100644 fs/exofs/sys.h
>
> diff --git a/fs/exofs/Kbuild b/fs/exofs/Kbuild
> index 1118068..5f6f1eb 100644
> --- a/fs/exofs/Kbuild
> +++ b/fs/exofs/Kbuild
> @@ -16,6 +16,6 @@
> libore-y := ore.o ore_raid.o
> obj-$(CONFIG_ORE) += libore.o
>
> -exofs-y := inode.o file.o symlink.o namei.o dir.o super.o
> +exofs-y := inode.o file.o symlink.o namei.o dir.o super.o sys.o
> exofs-$(CONFIG_PNFSD) += export.o
> obj-$(CONFIG_EXOFS_FS) += exofs.o
> diff --git a/fs/exofs/exofs.h b/fs/exofs/exofs.h
> index 94047b9..b298c7a 100644
> --- a/fs/exofs/exofs.h
> +++ b/fs/exofs/exofs.h
> @@ -56,6 +56,9 @@
> struct exofs_dev {
> struct ore_dev ored;
> unsigned did;
> + unsigned urilen;
> + uint8_t *uri;
> + struct kobject ed_kobj;
> };
> /*
> * our extension to the in-memory superblock
> @@ -73,6 +76,7 @@ struct exofs_sb_info {
> struct ore_layout layout; /* Default files layout */
> struct ore_comp one_comp; /* id & cred of partition id=0*/
> struct ore_components oc; /* comps for the partition */
> + struct kobject s_kobj; /* holds per-sbi kobject */
> };
>
> /*
> diff --git a/fs/exofs/export.c b/fs/exofs/export.c
> index 621bd11..a53f575 100644
> --- a/fs/exofs/export.c
> +++ b/fs/exofs/export.c
> @@ -321,6 +321,7 @@ int exofs_get_device_info(struct super_block *sb, struct exp_xdr_stream *xdr,
> {
> struct exofs_sb_info *sbi = sb->s_fs_info;
> struct pnfs_osd_deviceaddr devaddr;
> + struct exofs_dev *edev;
> const struct osd_dev_info *odi;
> u64 devno = devid->devid;
> __be32 *start;
> @@ -334,7 +335,8 @@ int exofs_get_device_info(struct super_block *sb, struct exp_xdr_stream *xdr,
> return -ENODEV;
> }
>
> - odi = osduld_device_info(sbi->oc.ods[devno]->od);
> + edev = container_of(sbi->oc.ods[devno], typeof(*edev), ored);
> + odi = osduld_device_info(edev->ored.od);
>
> devaddr.oda_systemid.len = odi->systemid_len;
> devaddr.oda_systemid.data = (void *)odi->systemid; /* !const cast */
> @@ -342,6 +344,10 @@ int exofs_get_device_info(struct super_block *sb, struct exp_xdr_stream *xdr,
> devaddr.oda_osdname.len = odi->osdname_len ;
> devaddr.oda_osdname.data = (void *)odi->osdname;/* !const cast */
>
> + devaddr.oda_targetaddr.ota_available = OBJ_OTA_AVAILABLE;
> + devaddr.oda_targetaddr.ota_netaddr.r_addr.data = (void *)edev->uri;
> + devaddr.oda_targetaddr.ota_netaddr.r_addr.len = edev->urilen;
> +
> /* skip opaque size, will be filled-in later */
> start = exp_xdr_reserve_qwords(xdr, 1);
> if (!start) {
> diff --git a/fs/exofs/super.c b/fs/exofs/super.c
> index 3c0d83a..a4834f7 100644
> --- a/fs/exofs/super.c
> +++ b/fs/exofs/super.c
> @@ -39,6 +39,7 @@
> #include <linux/exportfs.h>
> #include <linux/slab.h>
>
> +#include "sys.h"
> #include "exofs.h"
>
> #define EXOFS_DBGMSG2(M...) do {} while (0)
> @@ -472,6 +473,7 @@ static void exofs_put_super(struct super_block *sb)
> _exofs_print_device("Unmounting", NULL, ore_comp_dev(&sbi->oc, 0),
> sbi->one_comp.obj.partition);
>
> + exofs_sysfs_cluster_del(sbi);
> bdi_destroy(&sbi->bdi);
> exofs_free_sbi(sbi);
> sb->s_fs_info = NULL;
> @@ -632,6 +634,11 @@ static int exofs_read_lookup_dev_table(struct exofs_sb_info *sbi,
> memcpy(&sbi->oc.ods[numdevs], &sbi->oc.ods[0],
> (numdevs - 1) * sizeof(sbi->oc.ods[0]));
>
> + /* create sysfs entries to hold the current exofs cluster instance.
> + * There will be one sysfs dirent for cluster osdname per exofs mount
> + */
> + exofs_sysfs_cluster_add(&dt->dt_dev_table[0], sbi);
> +
> for (i = 0; i < numdevs; i++) {
> struct exofs_fscb fscb;
> struct osd_dev_info odi;
> @@ -657,6 +664,7 @@ static int exofs_read_lookup_dev_table(struct exofs_sb_info *sbi,
> eds[i].ored.od = fscb_od;
> ++sbi->oc.numdevs;
> fscb_od = NULL;
> + exofs_sysfs_odev_add(&eds[i], sbi);
> continue;
> }
>
> @@ -682,6 +690,7 @@ static int exofs_read_lookup_dev_table(struct exofs_sb_info *sbi,
> odi.osdname);
> goto out;
> }
> + exofs_sysfs_odev_add(&eds[i], sbi);
>
> /* TODO: verify other information is correct and FS-uuid
> * matches. Benny what did you say about device table
> @@ -845,6 +854,7 @@ static int exofs_fill_super(struct super_block *sb, void *data, int silent)
> goto free_sbi;
> }
>
> + exofs_sysfs_print();
> _exofs_print_device("Mounting", opts->dev_name,
> ore_comp_dev(&sbi->oc, 0),
> sbi->one_comp.obj.partition);
> @@ -1024,6 +1034,9 @@ static int __init init_exofs(void)
> if (err)
> goto out_d;
>
> + /* We don't fail if sysfs creation failed */
> + exofs_sysfs_init();
> +
> return 0;
> out_d:
> destroy_inodecache();
> @@ -1033,6 +1046,7 @@ out:
>
> static void __exit exit_exofs(void)
> {
> + exofs_sysfs_uninit();
> unregister_filesystem(&exofs_type);
> destroy_inodecache();
> }
> diff --git a/fs/exofs/sys.c b/fs/exofs/sys.c
> new file mode 100644
> index 0000000..17c9c07
> --- /dev/null
> +++ b/fs/exofs/sys.c
> @@ -0,0 +1,202 @@
> +/*
> + * Copyright (C) 2012
> + * Sachin Bhamare <sbhamare@panasas.com>
> + *
> + * This file is part of exofs.
> + *
> + * exofs is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation. Since it is based on ext2, and the only
> + * valid version of GPL for the Linux kernel is version 2, the only valid
> + * version of GPL for exofs is version 2.
> + *
> + * exofs is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with exofs; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#include <linux/kobject.h>
> +#include <linux/device.h>
> +
> +#include "sys.h"
> +#include "exofs.h"
> +
> +struct odev_attr {
> + struct attribute attr;
> + ssize_t (*show)(struct exofs_dev *, char *);
> + ssize_t (*store)(struct exofs_dev *, const char *, size_t);
> +};
> +
> +static ssize_t odev_attr_show(struct kobject *kobj, struct attribute *attr,
> + char *buf)
> +{
> + struct exofs_dev *edp = container_of(kobj, struct exofs_dev, ed_kobj);
> + struct odev_attr *a = container_of(attr, struct odev_attr, attr);
> +
> + return a->show ? a->show(edp, buf) : 0;
> +}
> +
> +static ssize_t odev_attr_store(struct kobject *kobj, struct attribute *attr,
> + const char *buf, size_t len)
> +{
> + struct exofs_dev *edp = container_of(kobj, struct exofs_dev, ed_kobj);
> + struct odev_attr *a = container_of(attr, struct odev_attr, attr);
> +
> + return a->store ? a->store(edp, buf, len) : len;
> +}
> +
> +static const struct sysfs_ops odev_attr_ops = {
> + .show = odev_attr_show,
> + .store = odev_attr_store,
> +};
> +
> +
> +static struct kset *exofs_kset;
> +
> +static ssize_t osdname_show(struct exofs_dev *edp, char *buf)
> +{
> + struct osd_dev *odev = edp->ored.od;
> + const struct osd_dev_info *odi = osduld_device_info(odev);
> +
> + return snprintf(buf, odi->osdname_len + 1, "%s", odi->osdname);
> +}
> +
> +static ssize_t systemid_show(struct exofs_dev *edp, char *buf)
> +{
> + struct osd_dev *odev = edp->ored.od;
> + const struct osd_dev_info *odi = osduld_device_info(odev);
> +
> + memcpy(buf, odi->systemid, odi->systemid_len);
> + return odi->systemid_len;
> +}
> +
> +static ssize_t uri_show(struct exofs_dev *edp, char *buf)
> +{
> + return snprintf(buf, edp->urilen, "%s", edp->uri);
> +}
> +
> +static ssize_t uri_store(struct exofs_dev *edp, const char *buf, size_t len)
> +{
> + edp->urilen = strlen(buf) + 1;
> + edp->uri = krealloc(edp->uri, edp->urilen, GFP_KERNEL);
> + strncpy(edp->uri, buf, edp->urilen);
> + return edp->urilen;
> +}
> +
> +#define OSD_ATTR(name, mode, show, store) \
> + static struct odev_attr odev_attr_##name = \
> + __ATTR(name, mode, show, store)
> +
> +OSD_ATTR(osdname, S_IRUGO, osdname_show, NULL);
> +OSD_ATTR(systemid, S_IRUGO, systemid_show, NULL);
> +OSD_ATTR(uri, S_IRWXU, uri_show, uri_store);
> +
> +static struct attribute *odev_attrs[] = {
> + &odev_attr_osdname.attr,
> + &odev_attr_systemid.attr,
> + &odev_attr_uri.attr,
> + NULL,
> +};
> +
> +static struct kobj_type odev_ktype = {
> + .default_attrs = odev_attrs,
> + .sysfs_ops = &odev_attr_ops,
> +};
> +
> +static struct kobj_type uuid_ktype = {
> +};
> +
> +void exofs_sysfs_print()
> +{
> +#ifdef CONFIG_EXOFS_DEBUG
> + struct kobject *k_name, *k_tmp;
> +
> + list_for_each_entry_safe(k_name, k_tmp, &exofs_kset->list, entry) {
> + printk(KERN_INFO "%s: name %s ref %d\n",
> + __func__, kobject_name(k_name),
> + (int)atomic_read(&k_name->kref.refcount));
> + }
> +#endif
> +}
> +/*
> + * This function removes all kobjects under exofs_kset
> + * At the end of it, exofs_kset kobject will have a refcount
> + * of 1 which gets decremented only on exofs module unload
> + */
> +void exofs_sysfs_cluster_del(struct exofs_sb_info *sbi)
> +{
> + struct kobject *k_name, *k_tmp;
> + struct kobject *s_kobj = &sbi->s_kobj;
> +
> + list_for_each_entry_safe(k_name, k_tmp, &exofs_kset->list, entry) {
> + /* Remove all that are children of this SBI */
> + if (k_name->parent == s_kobj)
> + kobject_put(k_name);
> + }
> + kobject_put(s_kobj);
> +}
> +
> +/*
> + * This function creates sysfs entries to hold the current exofs cluster
> + * instance (uniquely identified by osdname,pid tuple).
> + * This function gets called once per exofs mount instance.
> + */
> +int exofs_sysfs_cluster_add(struct exofs_dt_device_info *dt_dev,
> + struct exofs_sb_info *sbi)
> +{
> + struct kobject *s_kobj;
> + int retval = 0;
> + uint64_t pid = sbi->one_comp.obj.partition;
> +
> + /* allocate new uuid dirent */
> + s_kobj = &sbi->s_kobj;
> + s_kobj->kset = exofs_kset;
> + retval = kobject_init_and_add(s_kobj, &uuid_ktype,
> + &exofs_kset->kobj, "%s_%llx", dt_dev->osdname, pid);
> + if (retval) {
> + EXOFS_ERR("ERROR: Failed to create sysfs entry for "
> + "uuid-%s_%llx => %d\n", dt_dev->osdname, pid, retval);
> + return -ENOMEM;
> + }
> + return 0;
> +}
> +
> +int exofs_sysfs_odev_add(struct exofs_dev *edev, struct exofs_sb_info *sbi)
> +{
> + struct kobject *d_kobj;
> + int retval = 0;
> +
> + /* create osd device group which contains following attributes
> + * osdname, systemid & uri
> + */
> + d_kobj = &edev->ed_kobj;
> + d_kobj->kset = exofs_kset;
> + retval = kobject_init_and_add(d_kobj, &odev_ktype,
> + &sbi->s_kobj, "dev%u", edev->did);
> + if (retval) {
> + EXOFS_ERR("ERROR: Failed to create sysfs entry for"
> + " device dev%u\n", edev->did);
> + return retval;
> + }
> + return 0;
> +}
> +
> +int exofs_sysfs_init(void)
> +{
> + exofs_kset = kset_create_and_add("exofs", NULL, fs_kobj);
> + if (!exofs_kset) {
> + EXOFS_ERR("ERROR: kset_create_and_add exofs failed\n");
> + return -ENOMEM;
> + }
> + return 0;
> +}
> +
> +void exofs_sysfs_uninit(void)
> +{
> + kset_unregister(exofs_kset);
> +}
> diff --git a/fs/exofs/sys.h b/fs/exofs/sys.h
> new file mode 100644
> index 0000000..cfabc1d
> --- /dev/null
> +++ b/fs/exofs/sys.h
> @@ -0,0 +1,41 @@
> +/*
> + * Copyright (C) 2012
> + * Sachin Bhamare <sbhamare@panasas.com>
> + *
> + * This file is part of exofs.
> + *
> + * exofs is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation. Since it is based on ext2, and the only
> + * valid version of GPL for the Linux kernel is version 2, the only valid
> + * version of GPL for exofs is version 2.
> + *
> + * exofs is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with exofs; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#ifndef __SYS_DOT_H__
> +#define __SYS_DOT_H__
> +
> +struct exofs_dev;
> +struct exofs_sb_info;
> +struct exofs_dt_device_info;
> +
> +int exofs_sysfs_odev_add(struct exofs_dev *edev,
> + struct exofs_sb_info *sbi);
> +int exofs_sysfs_cluster_add(struct exofs_dt_device_info *dt_dev,
> + struct exofs_sb_info *sbi);
> +void exofs_sysfs_cluster_del(struct exofs_sb_info *sbi);
> +
> +void exofs_sysfs_print(void);
> +int exofs_sysfs_init(void);
> +void exofs_sysfs_uninit(void);
> +
> +#endif /* __SYS_DOT_H__ */
> +
> diff --git a/fs/exportfs/pnfs_osd_xdr_srv.c b/fs/exportfs/pnfs_osd_xdr_srv.c
> index 12a3bda..d0795c44 100644
> --- a/fs/exportfs/pnfs_osd_xdr_srv.c
> +++ b/fs/exportfs/pnfs_osd_xdr_srv.c
> @@ -179,6 +179,42 @@ static enum nfsstat4 _encode_string(struct exp_xdr_stream *xdr,
> return 0;
> }
>
> +/* struct pnfs_osd_targetaddr {
> + * u32 ota_available;
> + * struct pnfs_osd_net_addr ota_netaddr;
> + * };
> + */
> +static inline enum nfsstat4 pnfs_osd_xdr_encode_targetaddr(
> + struct exp_xdr_stream *xdr,
> + struct pnfs_osd_targetaddr *taddr)
> +{
> + __be32 *p;
> +
> + /* ota_available */
> + p = exp_xdr_reserve_space(xdr, 4);
> + if (!p)
> + return NFS4ERR_TOOSMALL;
> + p = exp_xdr_encode_u32(p, taddr->ota_available);
> +
> + /* encode r_netid */
> + p = exp_xdr_reserve_space(xdr, 4 + taddr->ota_netaddr.r_netid.len);
> + if (!p)
> + return NFS4ERR_TOOSMALL;
> +
> + p = exp_xdr_encode_opaque(p,
> + taddr->ota_netaddr.r_netid.data,
> + taddr->ota_netaddr.r_netid.len);
> +
> + /* encode r_addr */
> + p = exp_xdr_reserve_space(xdr, 4 + taddr->ota_netaddr.r_addr.len);
> + if (!p)
> + return NFS4ERR_TOOSMALL;
> + p = exp_xdr_encode_opaque(p,
> + taddr->ota_netaddr.r_addr.data,
> + taddr->ota_netaddr.r_addr.len);
> + return 0;
> +}
> +
> /* struct pnfs_osd_deviceaddr {
> * struct pnfs_osd_targetid oda_targetid;
> * struct pnfs_osd_targetaddr oda_targetaddr;
> @@ -194,17 +230,20 @@ enum nfsstat4 pnfs_osd_xdr_encode_deviceaddr(
> __be32 *p;
> enum nfsstat4 err;
>
> - p = exp_xdr_reserve_space(xdr, 4 + 4 + sizeof(devaddr->oda_lun));
> + p = exp_xdr_reserve_space(xdr, sizeof(u32));
> if (!p)
> return NFS4ERR_TOOSMALL;
>
> /* Empty oda_targetid */
> p = exp_xdr_encode_u32(p, OBJ_TARGET_ANON);
>
> - /* Empty oda_targetaddr for now */
> - p = exp_xdr_encode_u32(p, 0);
> + /* oda_targetaddr */
> + err = pnfs_osd_xdr_encode_targetaddr(xdr, &devaddr->oda_targetaddr);
> + if (err)
> + return err;
>
> /* oda_lun */
> + p = exp_xdr_reserve_space(xdr, sizeof(devaddr->oda_lun));
> exp_xdr_encode_bytes(p, devaddr->oda_lun, sizeof(devaddr->oda_lun));
>
> err = _encode_string(xdr, &devaddr->oda_systemid);
> diff --git a/include/linux/pnfs_osd_xdr.h b/include/linux/pnfs_osd_xdr.h
> index e0557c1..eeedf73 100644
> --- a/include/linux/pnfs_osd_xdr.h
> +++ b/include/linux/pnfs_osd_xdr.h
> @@ -154,6 +154,11 @@ enum pnfs_osd_targetid_type {
> OBJ_TARGET_SCSI_DEVICE_ID = 3,
> };
>
> +enum pnfs_osd_target_ota {
> + OBJ_OTA_UNAVAILABLE = 0,
> + OBJ_OTA_AVAILABLE = 1,
> +};
> +
> /* union pnfs_osd_targetid4 switch (pnfs_osd_targetid_type4 oti_type) {
> * case OBJ_TARGET_SCSI_NAME:
> * string oti_scsi_name<>;
next prev parent reply other threads:[~2012-03-16 6:30 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-03-16 6:17 [PATCHSET 0/4] Auto-login support for the pnfs-objects protocol Boaz Harrosh
2012-03-16 6:19 ` [PATCH 1/4] pnfsd-exofs: Add autologin support to exofs Boaz Harrosh
2012-03-16 6:30 ` Boaz Harrosh [this message]
2012-03-16 6:21 ` [PATCH 2/4] pnfs-obj: Remove unused variable from objlayout_get_deviceinfo() Boaz Harrosh
2012-03-16 6:23 ` [PATCH 3/4] pnfs-obj: autologin: Add support for protocol autologin Boaz Harrosh
2012-03-16 21:40 ` Myklebust, Trond
2012-03-19 20:58 ` Boaz Harrosh
2012-03-19 21:12 ` Myklebust, Trond
2012-03-19 23:23 ` Boaz Harrosh
2012-03-20 1:16 ` Boaz Harrosh
2012-03-20 3:10 ` Boaz Harrosh
2012-03-20 3:26 ` Boaz Harrosh
2012-03-20 3:47 ` [PATCH version2] " Boaz Harrosh
2012-03-16 6:27 ` [PATCH 4/4] osd_login: Add autologin script for objlayoutdriver Boaz Harrosh
2012-03-23 2:36 ` SQUASHME: " Boaz Harrosh
2012-03-23 2:57 ` [PATCH version2] " Boaz Harrosh
2012-05-01 18:50 ` [PATCH 4/4] " Steve Dickson
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=4F62DDF1.9060000@panasas.com \
--to=bharrosh@panasas.com \
--cc=Trond.Myklebust@netapp.com \
--cc=bhalevy@tonian.com \
--cc=linux-nfs@vger.kernel.org \
--cc=osd-dev@open-osd.org \
--cc=sbhamare@panasas.com \
--cc=steved@redhat.com \
--cc=welch@panasas.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
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).