All of lore.kernel.org
 help / color / mirror / Atom feed
From: Andreas Dilger <adilger@dilger.ca>
To: Tahsin Erdogan <tahsin@google.com>
Cc: "Darrick J . Wong" <darrick.wong@oracle.com>,
	Jan Kara <jack@suse.com>, Theodore Ts'o <tytso@mit.edu>,
	linux-ext4 <linux-ext4@vger.kernel.org>
Subject: Re: [PATCH v4 27/28] ext4: xattr inode deduplication
Date: Wed, 14 Jun 2017 17:26:26 -0600	[thread overview]
Message-ID: <6E41DA31-A524-4E0E-BD0B-5C994399BBC6@dilger.ca> (raw)
In-Reply-To: <20170614143459.22359-1-tahsin@google.com>

[-- Attachment #1: Type: text/plain, Size: 12726 bytes --]

[reduced CC list to linux-ext4]

On Jun 14, 2017, at 8:34 AM, Tahsin Erdogan <tahsin@google.com> wrote:
> 
> Ext4 now supports xattr values that are up to 64k in size (vfs limit).
> Large xattr values are stored in external inodes each one holding a
> single value. Once written the data blocks of these inodes are immutable.
> 
> The real world use cases are expected to have a lot of value duplication
> such as inherited acls etc. To reduce data duplication on disk, this patch
> implements a deduplicator that allows sharing of xattr inodes.
> 
> The deduplication is based on an in-memory hash lookup that is a best
> effort sharing scheme. When a xattr inode is read from disk (i.e.
> getxattr() call), its crc32c hash is added to a hash table. Before
> creating a new xattr inode for a value being set, the hash table is
> checked to see if an existing inode holds an identical value. If such an
> inode is found, the ref count on that inode is incremented. On value
> removal the ref count is decremented and if it reaches zero the inode is
> deleted.
> 
> The quota charging for such inodes is manually managed. Every reference
> holder is charged the full size as if there was no sharing happening.
> This is consistent with how xattr blocks are also charged.
> 
> Signed-off-by: Tahsin Erdogan <tahsin@google.com>
> ---
> v4:
> - eliminated xattr entry in the xattr inode to avoid complexity and
>   recursion in xattr update path. Now the ref count and hash are stored
>   in i_[c/m/a]time.tv_sec fields.
> - some clean up in ext4_xattr_set_entry() to reduce code duplication and
>   complexity
> 
> v3:
> - use s_csum_seed for hash calculations when available
> - return error on stored vs calculated hash mismatch
> 
> v2:
> - make dependency on crc32c dynamic
> - update ext4_has_metadata_csum() and ext4_has_group_desc_csum() so that
>   they do not misinterpret existence of EXT4_SB(sb)->s_chksum_driver
> 
> fs/ext4/acl.c   |    5 +-
> fs/ext4/ext4.h  |   22 +-
> fs/ext4/inode.c |    9 +-
> fs/ext4/super.c |   25 +-
> fs/ext4/xattr.c | 1000 +++++++++++++++++++++++++++++++++++++++++--------------
> fs/ext4/xattr.h |   17 +-
> fs/mbcache.c    |    9 +-
> 7 files changed, 806 insertions(+), 281 deletions(-)
> 
> diff --git a/fs/ext4/super.c b/fs/ext4/super.c
> index b02a23ec92ca..9fcd29e21dc7 100644
> --- a/fs/ext4/super.c
> +++ b/fs/ext4/super.c
> @@ -4067,6 +4075,15 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
> 		goto failed_mount_wq;
> 	}
> 
> +	if (ext4_has_feature_ea_inode(sb)) {
> +		sbi->s_ea_inode_cache = ext4_xattr_create_cache();
> +		if (!sbi->s_ea_inode_cache) {
> +			ext4_msg(sb, KERN_ERR,
> +				 "Failed to create an s_ea_inode_cache");
> +			goto failed_mount_wq;
> +		}
> +	}

It would be preferable to allow a mount option like "no_mbcache" to disable
the use of shared xattrs.  In the Lustre case at least, there will never be
shared large xattrs, and we've had a bunch of performance issues with mbcache
due to lock contention among many server threads doing concurrent lookups and
inserting many thousands of unique entries into the cache.

> diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
> index abc7d5f84e5f..2f9bcafd9aed 100644
> --- a/fs/ext4/xattr.c
> +++ b/fs/ext4/xattr.c
> @@ -280,6 +283,34 @@ ext4_xattr_find_entry(struct ext4_xattr_entry **pentry, int name_index,
> 	return cmp ? -ENODATA : 0;
> }
> 
> +static u32
> +ext4_xattr_inode_hash(struct ext4_sb_info *sbi, const void *buffer, size_t size)
> +{
> +	return ext4_chksum(sbi, sbi->s_csum_seed ?: ~0, buffer, size);
> +}

This should follow the existing convention of always using s_csum_seed to seed
the checksum, and change ext4_fill_super() to initialize s_csum_seed to ~0 if
ext4_has_metadata_csum() is false, or always use the same value regardless of
whether ext4_has_metadata_csum() is set or not.

> +static u64 ext4_xattr_inode_get_ref(struct inode *ea_inode)
> +{
> +	return ((u64)ea_inode->i_ctime.tv_sec << 32) |
> +	       ((u32)ea_inode->i_mtime.tv_sec);
> +}

If it really necessary to have more than 2^32 references on a single shared
inode then it would be better to avoid the re-use of i_mtime, which breaks
the backref for unshared xattrs, and using i_size isn't enough of a guarantee
that this is the correct parent inode in case of on-disk corruption.

If you think that > 2^32 references to a single xattr is really needed, you
can use i_ctime_extra, since this will almost certainly only be used on ext4
filesystems with 256-byte or larger inodes.  It is highly unlikely that there
are filesystems with multi-billions of shared xattrs that are ext2-formatted.

This allows for a period of transition between existing single-user xattr inodes
(which use i_mtime for the parent back-ref) and the shared xattr inodes, instead
of requiring a full e2fsck when upgrading from an older version of xattr inodes
to a new kernel, and then doing the same if there is a need to downgrade again.

> +static void ext4_xattr_inode_set_ref(struct inode *ea_inode, u64 ref_count)
> +{
> +	ea_inode->i_ctime.tv_sec = (u32)(ref_count >> 32);
> +	ea_inode->i_mtime.tv_sec = (u32)ref_count;
> +}
> +
> +static u32 ext4_xattr_inode_get_hash(struct inode *ea_inode)
> +{
> +	return (u32)ea_inode->i_atime.tv_sec;
> +}
> +
> +static void ext4_xattr_inode_set_hash(struct inode *ea_inode, u32 hash)
> +{
> +	ea_inode->i_atime.tv_sec = hash;
> +}
> +
> /*
>  * Read the EA value from an inode.
>  */
> @@ -298,13 +331,24 @@ static int ext4_xattr_inode_read(struct inode *ea_inode, void *buf, size_t size)
> 		if (!bh)
> 			return -EFSCORRUPTED;
> 
> -		memcpy(buf, bh->b_data, csize);
> +		memcpy(copy_pos, bh->b_data, csize);
> 		brelse(bh);
> 
> -		buf += csize;
> +		copy_pos += csize;
> 		block += 1;
> 		copied += csize;
> 	}
> +
> +	calc_hash = ext4_xattr_inode_hash(EXT4_SB(ea_inode->i_sb), buf, size);
> +
> +	/* Verify stored hash matches calculated hash. */
> +	stored_hash = ext4_xattr_inode_get_hash(ea_inode);
> +	if (calc_hash != stored_hash) {
> +		ext4_warning_inode(ea_inode,
> +			"EA inode calc_hash=%#x does not match stored_hash=%#x",
> +			calc_hash, stored_hash);
> +		return -EFSCORRUPTED;
> +	}

Should this be contingent on ext4_has_metadata_csum() feature being enabled, or
alternately check if EXT4_XATTR_INODE_GET_PARENT() and i_generation match before
returning an error.  This will allow a smooth transition from existing filesystems
that do not store the hash, but have only a single-use xattr inode with a parent
backref.

> @@ -329,14 +373,6 @@ static int ext4_xattr_inode_iget(struct inode *parent, unsigned long ea_ino,
> 		goto error;
> 	}
> 
> -	if (EXT4_XATTR_INODE_GET_PARENT(inode) != parent->i_ino ||
> -	    inode->i_generation != parent->i_generation) {
> -		ext4_error(parent->i_sb, "Backpointer from EA inode %lu "
> -			   "to parent is invalid.", ea_ino);
> -		err = -EINVAL;
> -		goto error;
> -	}

This check here should be moved up to ext4_xattr_inode_read().

> +static int __ext4_xattr_set_credits(struct super_block *sb,

> +				    struct buffer_head *block_bh,
> +				    size_t value_len)
> +{
> +	int credits;
> +	int blocks;
> +
> +	/*
> +	 * 1) Owner inode update
> +	 * 2) Ref count update on old xattr block
> +	 * 3) new xattr block
> +	 * 4) block bitmap update for new xattr block
> +	 * 5) group descriptor for new xattr block
> +	 */
> +	credits = 5;
> +
> +	/* We are done if ea_inode feature is not enabled. */
> +	if (!ext4_has_feature_ea_inode(sb))
> +		return credits;
> +
> +	/* New ea_inode, inode map, block bitmap, group descriptor. */
> +	credits += 4;
> +
> +	/* Data blocks. */
> +	blocks = (value_len + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
> +
> +	/* Indirection block. */
> +	blocks += 1;

Strictly speaking, this is only needed "if (blocks > EXT4_NDIR_BLOCKS)".

> +
> +	/* Block bitmap and group descriptor updates for each block. */
> +	credits += blocks * 2;
> +
> +	/* Blocks themselves. */
> +	credits += blocks;
> +
> +	/* Dereference ea_inode holding old xattr value.
> +	 * Old ea_inode, inode map, block bitmap, group descriptor.
> +	 */
> +	credits += 4;
> +
> +	/* Data blocks for old ea_inode. */
> +	blocks = XATTR_SIZE_MAX >> sb->s_blocksize_bits;
> +
> +	/* Indirection block for old ea_inode. */
> +	blocks += 1;
> +
> +	/* Block bitmap and group descriptor updates for each block. */
> +	credits += blocks * 2;
> +
> +	/* Quota updates. */
> +	credits += EXT4_MAXQUOTAS_TRANS_BLOCKS(sb);
> +
> +	/* We may need to clone the existing xattr block in which case we need
> +	 * to increment ref counts for existing ea_inodes referenced by it.
> +	 */

Just to clarify here, in the case of cloning an existing xattr block, are the
refcounts being _incremented_ or _decremented_ on the existing ea_inodes?  I'm
trying to figure out if we really need to have credits for both old and new
xattr inodes, as well as these additional credits.  Since this is reserving
about 110 blocks for every setxattr, this can add significant pressure on the
journal if there are lots of threads creating files and/or setting xattrs.

> +	if (block_bh) {
> +		struct ext4_xattr_entry *entry = BFIRST(block_bh);
> +
> +		for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry))
> +			if (entry->e_value_inum)
> +				/* Ref count update on ea_inode. */
> +				credits += 1;
> +	}
> +	return credits;
> +}
> 
> @@ -965,67 +1257,121 @@ static struct inode *ext4_xattr_inode_create(handle_t	+static struct inode *
> +ext4_xattr_inode_cache_find(struct inode *inode, const void *value,
> +			    size_t value_len, u32 hash)
> {
> +	struct inode *ea_inode;
> +	struct mb_cache_entry *ce;
> +	struct mb_cache *ea_inode_cache = EA_INODE_CACHE(inode);
> +	void *ea_data = NULL;
> 	int err;

This function should just return NULL if ea_inode_cache is NULL (e.g. in
the case of "no_mbcache" mount option).

> +	ce = mb_cache_entry_find_first(ea_inode_cache, hash);
> +	while (ce) {
> +		ea_inode = ext4_iget(inode->i_sb, ce->e_value);
> +		if (IS_ERR(ea_inode)) {
> +			ea_inode = NULL;
> +			goto next;
> +		}
> 
> +		if (is_bad_inode(ea_inode) ||
> +		    !(EXT4_I(ea_inode)->i_flags & EXT4_EA_INODE_FL) ||
> +		    i_size_read(ea_inode) != value_len)
> +			goto next;
> 
> +		if (!ea_data)
> +			ea_data = ext4_kvmalloc(value_len, GFP_NOFS);
> +
> +		if (!ea_data) {
> +			iput(ea_inode);
> +			return NULL;
> +		}
> +
> +		err = ext4_xattr_inode_read(ea_inode, ea_data, value_len);
> +		if (unlikely(err))
> +			goto next;
> +
> +		if (!memcmp(value, ea_data, value_len)) {
> +			mb_cache_entry_touch(ea_inode_cache, ce);
> +			mb_cache_entry_put(ea_inode_cache, ce);
> +			kvfree(ea_data);
> +			return ea_inode;
> +		}
> +next:
> +		iput(ea_inode);
> +		ce = mb_cache_entry_find_next(ea_inode_cache, ce);
> +	}
> +	kvfree(ea_data);
> +	return NULL;
> }
> 
> /*
>  * Add value of the EA in an inode.
>  */
> -static int ext4_xattr_inode_set(handle_t *handle, struct inode *inode,
> -				unsigned long *ea_ino, const void *value,
> -				size_t value_len)
> +static int ext4_xattr_inode_lookup_create(handle_t *handle, struct inode *inode,
> +					  const void *value, size_t value_len,
> +					  struct inode **ret_inode)
> {
> 	struct inode *ea_inode;
> +	u32 hash;
> 	int err;
> 
> +	hash = ext4_xattr_inode_hash(EXT4_SB(inode->i_sb), value, value_len);
> +	ea_inode = ext4_xattr_inode_cache_find(inode, value, value_len, hash);
> +	if (ea_inode) {
> +		err = ext4_xattr_inode_inc_ref(handle, ea_inode);
> +		if (err) {
> +			iput(ea_inode);
> +			return err;
> +		}
> +
> +		*ret_inode = ea_inode;
> +		return 0;
> +	}
> +
> 	/* Create an inode for the EA value */
> -	ea_inode = ext4_xattr_inode_create(handle, inode);
> +	ea_inode = ext4_xattr_inode_create(handle, inode, hash);
> 	if (IS_ERR(ea_inode))
> 		return PTR_ERR(ea_inode);
> 
> 	err = ext4_xattr_inode_write(handle, ea_inode, value, value_len);
> -	if (err)
> -		clear_nlink(ea_inode);
> -	else
> -		*ea_ino = ea_inode->i_ino;
> +	if (err) {
> +		ext4_xattr_inode_dec_ref(handle, ea_inode);
> +		iput(ea_inode);
> +		return err;
> +	}
> 
> -	iput(ea_inode);
> +	mb_cache_entry_create(EA_INODE_CACHE(inode), GFP_NOFS, hash,
> +			      ea_inode->i_ino, true /* reusable */);

Should skip mb_cache if EA_INODE_CACHE(inode) is NULL, or have a wrapper
like ext4_xattr_inode_cache_insert() to match ext4_xattr_inode_cache_find()
that does the same.

Cheers, Andreas






[-- Attachment #2: Message signed with OpenPGP --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

  reply	other threads:[~2017-06-14 23:26 UTC|newest]

Thread overview: 100+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-05-31  8:14 [PATCH 01/28] ext4: xattr-in-inode support Tahsin Erdogan
2017-05-31  8:14 ` [PATCH 02/28] ext4: fix lockdep warning about recursive inode locking Tahsin Erdogan
2017-05-31  8:14 ` [PATCH 03/28] ext4: lock inode before calling ext4_orphan_add() Tahsin Erdogan
2017-05-31  8:14 ` [PATCH 04/28] ext4: do not set posix acls on xattr inodes Tahsin Erdogan
2017-05-31  8:14 ` [PATCH 05/28] ext4: attach jinode after creation of xattr inode Tahsin Erdogan
2017-05-31  8:14 ` [PATCH 06/28] ext4: ea_inode owner should be the same as the inode owner Tahsin Erdogan
2017-05-31  8:14 ` [PATCH 07/28] ext4: call journal revoke when freeing ea_inode blocks Tahsin Erdogan
2017-05-31 16:12   ` Darrick J. Wong
2017-05-31 16:12     ` [Ocfs2-devel] " Darrick J. Wong
2017-05-31 16:12     ` Darrick J. Wong
2017-05-31 21:01     ` Tahsin Erdogan
2017-06-05 22:08     ` Andreas Dilger
2017-05-31  8:14 ` [PATCH 08/28] ext4: fix ref counting for ea_inode Tahsin Erdogan
2017-05-31  8:14 ` [PATCH 09/28] ext4: extended attribute value size limit is enforced by vfs Tahsin Erdogan
2017-05-31 16:03   ` Darrick J. Wong
2017-05-31 16:03     ` [Ocfs2-devel] " Darrick J. Wong
2017-05-31 16:03     ` Darrick J. Wong
2017-05-31 16:13     ` Tahsin Erdogan
2017-05-31  8:14 ` [PATCH 10/28] ext4: change ext4_xattr_inode_iget() signature Tahsin Erdogan
2017-05-31  8:15 ` [PATCH 11/28] ext4: clean up ext4_xattr_inode_get() Tahsin Erdogan
2017-05-31  8:15 ` [PATCH 12/28] ext4: add missing le32_to_cpu(e_value_inum) conversions Tahsin Erdogan
2017-05-31  8:15 ` [PATCH 13/28] ext4: ext4_xattr_value_same() should return false for external data Tahsin Erdogan
2017-05-31  8:15 ` [PATCH 14/28] ext4: fix ext4_xattr_make_inode_space() value size calculation Tahsin Erdogan
2017-05-31  8:15 ` [PATCH 15/28] ext4: fix ext4_xattr_move_to_block() Tahsin Erdogan
2017-05-31  8:15 ` [PATCH 16/28] ext4: fix ext4_xattr_cmp() Tahsin Erdogan
2017-05-31  8:15 ` [PATCH 17/28] ext4: fix credits calculation for xattr inode Tahsin Erdogan
2017-05-31  8:15 ` [PATCH 18/28] ext4: retry storing value in external inode with xattr block too Tahsin Erdogan
2017-06-20  8:56   ` [PATCH v2 18/31] " Tahsin Erdogan
2017-05-31  8:15 ` [PATCH 19/28] ext4: ext4_xattr_delete_inode() should return accurate errors Tahsin Erdogan
2017-05-31  8:15 ` [PATCH 20/28] ext4: improve journal credit handling in set xattr paths Tahsin Erdogan
2017-06-20  8:59   ` [PATCH v2 20/31] " Tahsin Erdogan
2017-05-31  8:15 ` [PATCH 21/28] ext4: modify ext4_xattr_ino_array to hold struct inode * Tahsin Erdogan
2017-05-31  8:15 ` [PATCH 22/28] ext4: move struct ext4_xattr_inode_array to xattr.h Tahsin Erdogan
2017-05-31  8:15 ` [PATCH 23/28] mbcache: make mbcache more generic Tahsin Erdogan
2017-06-15  7:41   ` Jan Kara
2017-06-15  7:41     ` [Ocfs2-devel] " Jan Kara
2017-06-15 18:25     ` Tahsin Erdogan
2017-06-19  8:50       ` Jan Kara
2017-06-19  8:50         ` [Ocfs2-devel] " Jan Kara
2017-06-20  9:01         ` [PATCH v2 23/31] mbcache: make mbcache naming " Tahsin Erdogan
2017-06-21 17:43           ` Andreas Dilger
2017-06-21 18:33           ` Andreas Dilger
2017-06-21 21:39             ` Tahsin Erdogan
2017-05-31  8:15 ` [PATCH 24/28] ext4: rename mb block cache functions Tahsin Erdogan
2017-05-31  8:15 ` [PATCH 25/28] ext4: add ext4_is_quota_file() Tahsin Erdogan
2017-05-31  8:15 ` [PATCH 26/28] ext4: cleanup transaction restarts during inode deletion Tahsin Erdogan
2017-06-14 14:17   ` [PATCH v2 " Tahsin Erdogan
2017-06-15  0:11     ` Andreas Dilger
2017-06-15  0:11       ` [Ocfs2-devel] " Andreas Dilger
2017-06-20  9:04       ` [PATCH v3 " Tahsin Erdogan
2017-06-20  9:29         ` Tahsin Erdogan
2017-05-31  8:15 ` [PATCH 27/28] ext4: xattr inode deduplication Tahsin Erdogan
2017-05-31 15:40   ` kbuild test robot
2017-05-31 15:40     ` [Ocfs2-devel] " kbuild test robot
2017-05-31 15:50   ` kbuild test robot
2017-05-31 15:50     ` [Ocfs2-devel] " kbuild test robot
2017-05-31 16:00   ` Darrick J. Wong
2017-05-31 16:00     ` [Ocfs2-devel] " Darrick J. Wong
2017-05-31 16:00     ` Darrick J. Wong
2017-05-31 22:33     ` [PATCH v2 " Tahsin Erdogan
2017-06-02  5:41       ` Darrick J. Wong
2017-06-02  5:41         ` [Ocfs2-devel] " Darrick J. Wong
2017-06-02  5:41         ` Darrick J. Wong
2017-06-02 12:46         ` Tahsin Erdogan
2017-06-02 17:59           ` Darrick J. Wong
2017-06-02 17:59             ` [Ocfs2-devel] " Darrick J. Wong
2017-06-02 17:59             ` Darrick J. Wong
2017-06-02 23:35             ` [PATCH v3 " Tahsin Erdogan
2017-06-14 14:34               ` [PATCH v4 " Tahsin Erdogan
2017-06-14 23:26                 ` Andreas Dilger [this message]
2017-06-20  9:07                   ` [PATCH v5 " Tahsin Erdogan
2017-06-20  9:49                     ` Tahsin Erdogan
2017-06-21 17:42                       ` Andreas Dilger
2017-06-21 21:14                     ` Andreas Dilger
2017-06-21 21:34                       ` Tahsin Erdogan
2017-06-21 21:42                         ` Andreas Dilger
2017-07-04 18:39                     ` Theodore Ts'o
2017-07-05 17:30                       ` Tahsin Erdogan
2017-07-06  4:19                         ` Theodore Ts'o
2017-05-31  8:15 ` [PATCH 28/28] quota: add extra inode count to dquot transfer functions Tahsin Erdogan
2017-06-15  7:57   ` Jan Kara
2017-06-15  7:57     ` [Ocfs2-devel] " Jan Kara
2017-06-17  1:50     ` Tahsin Erdogan
2017-06-19  9:03       ` Jan Kara
2017-06-19  9:03         ` [Ocfs2-devel] " Jan Kara
2017-06-19 11:46         ` Tahsin Erdogan
2017-06-19 12:36           ` Jan Kara
2017-06-19 12:36             ` [Ocfs2-devel] " Jan Kara
2017-06-20  9:12             ` [PATCH v2 28/31] quota: add get_inode_usage callback to transfer multi-inode charges Tahsin Erdogan
2017-06-20 12:01               ` Tahsin Erdogan
2017-06-20 15:28               ` Jan Kara
2017-06-20 18:08                 ` [PATCH v3 " Tahsin Erdogan
2017-06-21  4:48                   ` Theodore Ts'o
2017-06-21 11:22                     ` Tahsin Erdogan
2017-06-20  9:53             ` [PATCH 28/28] quota: add extra inode count to dquot transfer functions Tahsin Erdogan
2017-05-31 16:42 ` [PATCH 01/28] ext4: xattr-in-inode support Darrick J. Wong
2017-05-31 16:42   ` [Ocfs2-devel] " Darrick J. Wong
2017-05-31 16:42   ` Darrick J. Wong
2017-05-31 19:59   ` Tahsin Erdogan
2017-06-01 15:50     ` [PATCH v2 " Tahsin Erdogan

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=6E41DA31-A524-4E0E-BD0B-5C994399BBC6@dilger.ca \
    --to=adilger@dilger.ca \
    --cc=darrick.wong@oracle.com \
    --cc=jack@suse.com \
    --cc=linux-ext4@vger.kernel.org \
    --cc=tahsin@google.com \
    --cc=tytso@mit.edu \
    /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.