All of lore.kernel.org
 help / color / mirror / Atom feed
From: Peter Zijlstra <peterz@infradead.org>
To: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
Cc: Will Deacon <will@kernel.org>,
	Linus Torvalds <torvalds@linux-foundation.org>,
	Michael Ellerman <mpe@ellerman.id.au>,
	dja@axtens.net,
	Linux Kernel Mailing List <linux-kernel@vger.kernel.org>,
	linuxppc-dev@lists.ozlabs.org,
	Christophe Leroy <christophe.leroy@c-s.fr>,
	linux-arch <linux-arch@vger.kernel.org>,
	Mark Rutland <mark.rutland@arm.com>,
	Segher Boessenkool <segher@kernel.crashing.org>,
	Arnd Bergmann <arnd@arndb.de>,
	Christian Borntraeger <borntraeger@de.ibm.com>
Subject: Re: READ_ONCE() + STACKPROTECTOR_STRONG == :/ (was Re: [GIT PULL] Please pull powerpc/linux.git powerpc-5.5-2 tag (topic/kasan-bitops))
Date: Fri, 13 Dec 2019 13:56:18 +0100	[thread overview]
Message-ID: <20191213125618.GD2844@hirez.programming.kicks-ass.net> (raw)
In-Reply-To: <20191213104706.xnpqaehmtean3mkd@ltop.local>

On Fri, Dec 13, 2019 at 11:47:06AM +0100, Luc Van Oostenryck wrote:
> On Thu, Dec 12, 2019 at 09:53:38PM +0100, Peter Zijlstra wrote:
> > Now, looking at the current GCC source:
> > 
> >   https://github.com/gcc-mirror/gcc/blob/97d7270f894395e513667a031a0c309d1819d05e/gcc/c/c-parser.c#L3707
> > 
> > it seems that __typeof__() is supposed to strip all qualifiers from
> > _Atomic types. That lead me to try:
> > 
> > 	typeof(_Atomic typeof(p)) __p = (p);
> > 
> > But alas, I still get the same junk you got for ool_store_release() :/
> 
> I was checking this to see if Sparse was ready to support this.
> I was a bit surprised because at first sigth GCC was doing as
> it claims (typeof striping const & volatile on _Atomic types)
> but your exampe wasn't working. But it's working if an
> intermediate var is used:
> 	_Atomic typeof(p) tmp;
> 	typeof(tmp) __p = (p);
> or, uglier but probably more practical:
> 	typeof(({_Atomic typeof(p) tmp; })) __p = (p);
> 
> Go figure!

Excellent! I had to change it to something like:

#define unqual_typeof(x)    typeof(({_Atomic typeof(x) ___x __maybe_unused; ___x; }))

but that does indeed work!

Now I suppose we should wrap that in a symbol that indicates our
compiler does indeed support _Atomic, otherwise things will come apart.

That is, my gcc-4.6 doesn't seem to have it, while gcc-4.8 does, which
is exactly the range that needs the daft READ_ONCE() construct, how
convenient :/

Something a little like this perhaps?

---

diff --git a/arch/arm64/include/asm/barrier.h b/arch/arm64/include/asm/barrier.h
index 7d9cc5ec4971..c389af602da8 100644
--- a/arch/arm64/include/asm/barrier.h
+++ b/arch/arm64/include/asm/barrier.h
@@ -75,9 +75,9 @@ static inline unsigned long array_index_mask_nospec(unsigned long idx,
 
 #define __smp_store_release(p, v)					\
 do {									\
-	typeof(p) __p = (p);						\
-	union { typeof(*p) __val; char __c[1]; } __u =			\
-		{ .__val = (__force typeof(*p)) (v) };			\
+	unqual_typeof(p) __p = (p);					\
+	union { unqual_typeof(*p) __val; char __c[1]; } __u =	\
+		{ .__val = (__force unqual_typeof(*p)) (v) };	\
 	compiletime_assert_atomic_type(*p);				\
 	kasan_check_write(__p, sizeof(*p));				\
 	switch (sizeof(*p)) {						\
@@ -110,8 +110,8 @@ do {									\
 
 #define __smp_load_acquire(p)						\
 ({									\
-	union { typeof(*p) __val; char __c[1]; } __u;			\
-	typeof(p) __p = (p);						\
+	union { unqual_typeof(*p) __val; char __c[1]; } __u;		\
+	unqual_typeof(p) __p = (p);					\
 	compiletime_assert_atomic_type(*p);				\
 	kasan_check_read(__p, sizeof(*p));				\
 	switch (sizeof(*p)) {						\
@@ -141,8 +141,8 @@ do {									\
 
 #define smp_cond_load_relaxed(ptr, cond_expr)				\
 ({									\
-	typeof(ptr) __PTR = (ptr);					\
-	typeof(*ptr) VAL;						\
+	unqual_typeof(ptr) __PTR = (ptr);				\
+	unqual_typeof(*ptr) VAL;					\
 	for (;;) {							\
 		VAL = READ_ONCE(*__PTR);				\
 		if (cond_expr)						\
@@ -154,8 +154,8 @@ do {									\
 
 #define smp_cond_load_acquire(ptr, cond_expr)				\
 ({									\
-	typeof(ptr) __PTR = (ptr);					\
-	typeof(*ptr) VAL;						\
+	unqual_typeof(ptr) __PTR = (ptr);				\
+	unqual_typeof(*ptr) VAL;					\
 	for (;;) {							\
 		VAL = smp_load_acquire(__PTR);				\
 		if (cond_expr)						\
diff --git a/include/asm-generic/barrier.h b/include/asm-generic/barrier.h
index 85b28eb80b11..dd5bb055f5ab 100644
--- a/include/asm-generic/barrier.h
+++ b/include/asm-generic/barrier.h
@@ -228,8 +228,8 @@ do {									\
  */
 #ifndef smp_cond_load_relaxed
 #define smp_cond_load_relaxed(ptr, cond_expr) ({		\
-	typeof(ptr) __PTR = (ptr);				\
-	typeof(*ptr) VAL;					\
+	unqual_typeof(ptr) __PTR = (ptr);			\
+	unqual_typeof(*ptr) VAL;				\
 	for (;;) {						\
 		VAL = READ_ONCE(*__PTR);			\
 		if (cond_expr)					\
@@ -250,7 +250,7 @@ do {									\
  */
 #ifndef smp_cond_load_acquire
 #define smp_cond_load_acquire(ptr, cond_expr) ({		\
-	typeof(*ptr) _val;					\
+	unqual_typeof(*ptr) _val;				\
 	_val = smp_cond_load_relaxed(ptr, cond_expr);		\
 	smp_acquire__after_ctrl_dep();				\
 	_val;							\
diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h
index 0eb2a1cc411d..15fd7ea3882a 100644
--- a/include/linux/compiler-gcc.h
+++ b/include/linux/compiler-gcc.h
@@ -179,3 +179,10 @@
 #endif
 
 #define __no_fgcse __attribute__((optimize("-fno-gcse")))
+
+#if GCC_VERSION < 40800
+/*
+ * GCC-4.6 doesn't support _Atomic, which is required to strip qualifiers.
+ */
+#define unqual_typeof(x)	typeof(x)
+#endif
diff --git a/include/linux/compiler.h b/include/linux/compiler.h
index ad8c76144a3c..9736993f2ba1 100644
--- a/include/linux/compiler.h
+++ b/include/linux/compiler.h
@@ -279,7 +279,7 @@ void __write_once_size(volatile void *p, void *res, int size)
 
 #define __READ_ONCE(x, check)						\
 ({									\
-	union { typeof(x) __val; char __c[1]; } __u;			\
+	union { unqual_typeof(x) __val; char __c[1]; } __u;		\
 	if (check)							\
 		__read_once_size(&(x), __u.__c, sizeof(x));		\
 	else								\
@@ -302,12 +302,12 @@ unsigned long read_word_at_a_time(const void *addr)
 	return *(unsigned long *)addr;
 }
 
-#define WRITE_ONCE(x, val) \
-({							\
-	union { typeof(x) __val; char __c[1]; } __u =	\
-		{ .__val = (__force typeof(x)) (val) }; \
-	__write_once_size(&(x), __u.__c, sizeof(x));	\
-	__u.__val;					\
+#define WRITE_ONCE(x, val)					\
+({								\
+	union { unqual_typeof(x) __val; char __c[1]; } __u =	\
+		{ .__val = (__force unqual_typeof(x)) (val) };	\
+	__write_once_size(&(x), __u.__c, sizeof(x));		\
+	__u.__val;						\
 })
 
 #include <linux/kcsan.h>
diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h
index 72393a8c1a6c..fe8012c54251 100644
--- a/include/linux/compiler_types.h
+++ b/include/linux/compiler_types.h
@@ -243,4 +243,11 @@ struct ftrace_likely_data {
 #define __diag_error(compiler, version, option, comment) \
 	__diag_ ## compiler(version, error, option)
 
+#ifndef unqual_typeof
+/*
+ * GCC __typeof__() strips all qualifiers from _Atomic types.
+ */
+#define unqual_typeof(x)	typeof(({_Atomic typeof(x) ___x __maybe_unused; ___x; }))
+#endif
+
 #endif /* __LINUX_COMPILER_TYPES_H */

WARNING: multiple messages have this Message-ID (diff)
From: Peter Zijlstra <peterz@infradead.org>
To: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
Cc: linux-arch <linux-arch@vger.kernel.org>,
	Will Deacon <will@kernel.org>,
	linuxppc-dev@lists.ozlabs.org,
	Linux Kernel Mailing List <linux-kernel@vger.kernel.org>,
	Christian Borntraeger <borntraeger@de.ibm.com>,
	Arnd Bergmann <arnd@arndb.de>,
	Mark Rutland <mark.rutland@arm.com>,
	Linus Torvalds <torvalds@linux-foundation.org>,
	dja@axtens.net
Subject: Re: READ_ONCE() + STACKPROTECTOR_STRONG == :/ (was Re: [GIT PULL] Please pull powerpc/linux.git powerpc-5.5-2 tag (topic/kasan-bitops))
Date: Fri, 13 Dec 2019 13:56:18 +0100	[thread overview]
Message-ID: <20191213125618.GD2844@hirez.programming.kicks-ass.net> (raw)
In-Reply-To: <20191213104706.xnpqaehmtean3mkd@ltop.local>

On Fri, Dec 13, 2019 at 11:47:06AM +0100, Luc Van Oostenryck wrote:
> On Thu, Dec 12, 2019 at 09:53:38PM +0100, Peter Zijlstra wrote:
> > Now, looking at the current GCC source:
> > 
> >   https://github.com/gcc-mirror/gcc/blob/97d7270f894395e513667a031a0c309d1819d05e/gcc/c/c-parser.c#L3707
> > 
> > it seems that __typeof__() is supposed to strip all qualifiers from
> > _Atomic types. That lead me to try:
> > 
> > 	typeof(_Atomic typeof(p)) __p = (p);
> > 
> > But alas, I still get the same junk you got for ool_store_release() :/
> 
> I was checking this to see if Sparse was ready to support this.
> I was a bit surprised because at first sigth GCC was doing as
> it claims (typeof striping const & volatile on _Atomic types)
> but your exampe wasn't working. But it's working if an
> intermediate var is used:
> 	_Atomic typeof(p) tmp;
> 	typeof(tmp) __p = (p);
> or, uglier but probably more practical:
> 	typeof(({_Atomic typeof(p) tmp; })) __p = (p);
> 
> Go figure!

Excellent! I had to change it to something like:

#define unqual_typeof(x)    typeof(({_Atomic typeof(x) ___x __maybe_unused; ___x; }))

but that does indeed work!

Now I suppose we should wrap that in a symbol that indicates our
compiler does indeed support _Atomic, otherwise things will come apart.

That is, my gcc-4.6 doesn't seem to have it, while gcc-4.8 does, which
is exactly the range that needs the daft READ_ONCE() construct, how
convenient :/

Something a little like this perhaps?

---

diff --git a/arch/arm64/include/asm/barrier.h b/arch/arm64/include/asm/barrier.h
index 7d9cc5ec4971..c389af602da8 100644
--- a/arch/arm64/include/asm/barrier.h
+++ b/arch/arm64/include/asm/barrier.h
@@ -75,9 +75,9 @@ static inline unsigned long array_index_mask_nospec(unsigned long idx,
 
 #define __smp_store_release(p, v)					\
 do {									\
-	typeof(p) __p = (p);						\
-	union { typeof(*p) __val; char __c[1]; } __u =			\
-		{ .__val = (__force typeof(*p)) (v) };			\
+	unqual_typeof(p) __p = (p);					\
+	union { unqual_typeof(*p) __val; char __c[1]; } __u =	\
+		{ .__val = (__force unqual_typeof(*p)) (v) };	\
 	compiletime_assert_atomic_type(*p);				\
 	kasan_check_write(__p, sizeof(*p));				\
 	switch (sizeof(*p)) {						\
@@ -110,8 +110,8 @@ do {									\
 
 #define __smp_load_acquire(p)						\
 ({									\
-	union { typeof(*p) __val; char __c[1]; } __u;			\
-	typeof(p) __p = (p);						\
+	union { unqual_typeof(*p) __val; char __c[1]; } __u;		\
+	unqual_typeof(p) __p = (p);					\
 	compiletime_assert_atomic_type(*p);				\
 	kasan_check_read(__p, sizeof(*p));				\
 	switch (sizeof(*p)) {						\
@@ -141,8 +141,8 @@ do {									\
 
 #define smp_cond_load_relaxed(ptr, cond_expr)				\
 ({									\
-	typeof(ptr) __PTR = (ptr);					\
-	typeof(*ptr) VAL;						\
+	unqual_typeof(ptr) __PTR = (ptr);				\
+	unqual_typeof(*ptr) VAL;					\
 	for (;;) {							\
 		VAL = READ_ONCE(*__PTR);				\
 		if (cond_expr)						\
@@ -154,8 +154,8 @@ do {									\
 
 #define smp_cond_load_acquire(ptr, cond_expr)				\
 ({									\
-	typeof(ptr) __PTR = (ptr);					\
-	typeof(*ptr) VAL;						\
+	unqual_typeof(ptr) __PTR = (ptr);				\
+	unqual_typeof(*ptr) VAL;					\
 	for (;;) {							\
 		VAL = smp_load_acquire(__PTR);				\
 		if (cond_expr)						\
diff --git a/include/asm-generic/barrier.h b/include/asm-generic/barrier.h
index 85b28eb80b11..dd5bb055f5ab 100644
--- a/include/asm-generic/barrier.h
+++ b/include/asm-generic/barrier.h
@@ -228,8 +228,8 @@ do {									\
  */
 #ifndef smp_cond_load_relaxed
 #define smp_cond_load_relaxed(ptr, cond_expr) ({		\
-	typeof(ptr) __PTR = (ptr);				\
-	typeof(*ptr) VAL;					\
+	unqual_typeof(ptr) __PTR = (ptr);			\
+	unqual_typeof(*ptr) VAL;				\
 	for (;;) {						\
 		VAL = READ_ONCE(*__PTR);			\
 		if (cond_expr)					\
@@ -250,7 +250,7 @@ do {									\
  */
 #ifndef smp_cond_load_acquire
 #define smp_cond_load_acquire(ptr, cond_expr) ({		\
-	typeof(*ptr) _val;					\
+	unqual_typeof(*ptr) _val;				\
 	_val = smp_cond_load_relaxed(ptr, cond_expr);		\
 	smp_acquire__after_ctrl_dep();				\
 	_val;							\
diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h
index 0eb2a1cc411d..15fd7ea3882a 100644
--- a/include/linux/compiler-gcc.h
+++ b/include/linux/compiler-gcc.h
@@ -179,3 +179,10 @@
 #endif
 
 #define __no_fgcse __attribute__((optimize("-fno-gcse")))
+
+#if GCC_VERSION < 40800
+/*
+ * GCC-4.6 doesn't support _Atomic, which is required to strip qualifiers.
+ */
+#define unqual_typeof(x)	typeof(x)
+#endif
diff --git a/include/linux/compiler.h b/include/linux/compiler.h
index ad8c76144a3c..9736993f2ba1 100644
--- a/include/linux/compiler.h
+++ b/include/linux/compiler.h
@@ -279,7 +279,7 @@ void __write_once_size(volatile void *p, void *res, int size)
 
 #define __READ_ONCE(x, check)						\
 ({									\
-	union { typeof(x) __val; char __c[1]; } __u;			\
+	union { unqual_typeof(x) __val; char __c[1]; } __u;		\
 	if (check)							\
 		__read_once_size(&(x), __u.__c, sizeof(x));		\
 	else								\
@@ -302,12 +302,12 @@ unsigned long read_word_at_a_time(const void *addr)
 	return *(unsigned long *)addr;
 }
 
-#define WRITE_ONCE(x, val) \
-({							\
-	union { typeof(x) __val; char __c[1]; } __u =	\
-		{ .__val = (__force typeof(x)) (val) }; \
-	__write_once_size(&(x), __u.__c, sizeof(x));	\
-	__u.__val;					\
+#define WRITE_ONCE(x, val)					\
+({								\
+	union { unqual_typeof(x) __val; char __c[1]; } __u =	\
+		{ .__val = (__force unqual_typeof(x)) (val) };	\
+	__write_once_size(&(x), __u.__c, sizeof(x));		\
+	__u.__val;						\
 })
 
 #include <linux/kcsan.h>
diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h
index 72393a8c1a6c..fe8012c54251 100644
--- a/include/linux/compiler_types.h
+++ b/include/linux/compiler_types.h
@@ -243,4 +243,11 @@ struct ftrace_likely_data {
 #define __diag_error(compiler, version, option, comment) \
 	__diag_ ## compiler(version, error, option)
 
+#ifndef unqual_typeof
+/*
+ * GCC __typeof__() strips all qualifiers from _Atomic types.
+ */
+#define unqual_typeof(x)	typeof(({_Atomic typeof(x) ___x __maybe_unused; ___x; }))
+#endif
+
 #endif /* __LINUX_COMPILER_TYPES_H */

  reply	other threads:[~2019-12-13 20:37 UTC|newest]

Thread overview: 86+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-12-06 12:46 [GIT PULL] Please pull powerpc/linux.git powerpc-5.5-2 tag (topic/kasan-bitops) Michael Ellerman
2019-12-06 12:46 ` Michael Ellerman
2019-12-06 13:16 ` Peter Zijlstra
2019-12-06 13:16   ` Peter Zijlstra
2019-12-10  5:38   ` Michael Ellerman
2019-12-10  5:38     ` Michael Ellerman
2019-12-10 10:15     ` Peter Zijlstra
2019-12-10 10:15       ` Peter Zijlstra
2019-12-11  0:29       ` Michael Ellerman
2019-12-11  0:29         ` Michael Ellerman
2019-12-12  5:42   ` READ_ONCE() + STACKPROTECTOR_STRONG == :/ (was Re: [GIT PULL] Please pull powerpc/linux.git powerpc-5.5-2 tag (topic/kasan-bitops)) Michael Ellerman
2019-12-12  5:42     ` Michael Ellerman
2019-12-12  8:01     ` Peter Zijlstra
2019-12-12  8:01       ` Peter Zijlstra
2019-12-12 10:07       ` Will Deacon
2019-12-12 10:07         ` Will Deacon
2019-12-12 10:46         ` Peter Zijlstra
2019-12-12 10:46           ` Peter Zijlstra
2019-12-12 17:04           ` Will Deacon
2019-12-12 17:04             ` Will Deacon
2019-12-12 17:16             ` Will Deacon
2019-12-12 17:16               ` Will Deacon
2019-12-12 17:41           ` Linus Torvalds
2019-12-12 17:41             ` Linus Torvalds
2019-12-12 17:50             ` Segher Boessenkool
2019-12-12 17:50               ` Segher Boessenkool
2019-12-12 18:06             ` Will Deacon
2019-12-12 18:06               ` Will Deacon
2019-12-12 18:29               ` Christian Borntraeger
2019-12-12 18:29                 ` Christian Borntraeger
2019-12-12 18:43               ` Linus Torvalds
2019-12-12 18:43                 ` Linus Torvalds
2019-12-12 19:34                 ` Will Deacon
2019-12-12 19:34                   ` Will Deacon
2019-12-12 20:21                   ` Peter Zijlstra
2019-12-12 20:21                     ` Peter Zijlstra
2019-12-12 20:53                     ` Peter Zijlstra
2019-12-12 20:53                       ` Peter Zijlstra
2019-12-13 10:47                       ` Luc Van Oostenryck
2019-12-13 10:47                         ` Luc Van Oostenryck
2019-12-13 12:56                         ` Peter Zijlstra [this message]
2019-12-13 12:56                           ` Peter Zijlstra
2019-12-13 14:28                           ` Luc Van Oostenryck
2019-12-13 14:28                             ` Luc Van Oostenryck
2019-12-12 20:49                   ` Linus Torvalds
2019-12-12 20:49                     ` Linus Torvalds
2019-12-13 13:17                     ` Arnd Bergmann
2019-12-13 13:17                       ` Arnd Bergmann
2019-12-13 21:32                       ` Arnd Bergmann
2019-12-13 21:32                         ` Arnd Bergmann
2019-12-13 22:01                         ` Linus Torvalds
2019-12-13 22:01                           ` Linus Torvalds
2019-12-16 10:28                       ` Will Deacon
2019-12-16 10:28                         ` Will Deacon
2019-12-16 11:47                         ` Peter Zijlstra
2019-12-16 11:47                           ` Peter Zijlstra
2019-12-16 12:06                         ` Arnd Bergmann
2019-12-16 12:06                           ` Arnd Bergmann
2019-12-17 17:07                     ` Will Deacon
2019-12-17 17:07                       ` Will Deacon
2019-12-17 18:04                       ` Linus Torvalds
2019-12-17 18:04                         ` Linus Torvalds
2019-12-17 18:05                         ` Linus Torvalds
2019-12-17 18:05                           ` Linus Torvalds
2019-12-17 18:31                           ` Will Deacon
2019-12-17 18:31                             ` Will Deacon
2019-12-17 18:32                         ` Linus Torvalds
2019-12-17 18:32                           ` Linus Torvalds
2019-12-18 12:17                           ` Michael Ellerman
2019-12-18 12:17                             ` Michael Ellerman
2019-12-19 12:11                           ` Will Deacon
2019-12-19 12:11                             ` Will Deacon
2019-12-18 10:22                     ` Christian Borntraeger
2019-12-18 10:22                       ` Christian Borntraeger
2019-12-18 10:35                       ` Will Deacon
2019-12-18 10:35                         ` Will Deacon
2019-12-13 12:07           ` Michael Ellerman
2019-12-13 12:07             ` Michael Ellerman
2019-12-13 13:53             ` Segher Boessenkool
2019-12-13 13:53               ` Segher Boessenkool
2019-12-13 21:06               ` Michael Ellerman
2019-12-13 21:06                 ` Michael Ellerman
2019-12-12 15:10     ` Segher Boessenkool
2019-12-12 15:10       ` Segher Boessenkool
2019-12-06 22:15 ` [GIT PULL] Please pull powerpc/linux.git powerpc-5.5-2 tag (topic/kasan-bitops) pr-tracker-bot
2019-12-06 22:15   ` pr-tracker-bot

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=20191213125618.GD2844@hirez.programming.kicks-ass.net \
    --to=peterz@infradead.org \
    --cc=arnd@arndb.de \
    --cc=borntraeger@de.ibm.com \
    --cc=christophe.leroy@c-s.fr \
    --cc=dja@axtens.net \
    --cc=linux-arch@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linuxppc-dev@lists.ozlabs.org \
    --cc=luc.vanoostenryck@gmail.com \
    --cc=mark.rutland@arm.com \
    --cc=mpe@ellerman.id.au \
    --cc=segher@kernel.crashing.org \
    --cc=torvalds@linux-foundation.org \
    --cc=will@kernel.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.