From: Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
To: akpm@linux-foundation.org
Cc: linux-kernel@vger.kernel.org, hch@infradead.org,
Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>,
Adrian Bunk <bunk@stusta.de>
Subject: [patch 02/10] Linux Kernel Markers, architecture independent code.
Date: Wed, 09 May 2007 21:55:57 -0400 [thread overview]
Message-ID: <20070510020915.609367244@polymtl.ca> (raw)
In-Reply-To: 20070510015555.973107048@polymtl.ca
[-- Attachment #1: linux-kernel-markers-architecture-independant-code.patch --]
[-- Type: text/plain, Size: 16532 bytes --]
[bunk@stusta.de: marker exports must be EXPORT_SYMBOL_GPL]
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
---
include/asm-generic/vmlinux.lds.h | 13 +
include/linux/marker.h | 124 +++++++++++++++++
include/linux/module.h | 4
kernel/module.c | 273 ++++++++++++++++++++++++++++++++++++++
4 files changed, 414 insertions(+)
Index: linux-2.6-lttng/include/asm-generic/vmlinux.lds.h
===================================================================
--- linux-2.6-lttng.orig/include/asm-generic/vmlinux.lds.h 2007-05-09 18:14:52.000000000 -0400
+++ linux-2.6-lttng/include/asm-generic/vmlinux.lds.h 2007-05-09 18:15:55.000000000 -0400
@@ -121,6 +121,19 @@
__ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) { \
*(__ksymtab_strings) \
} \
+ /* Kernel markers : pointers */ \
+ .markers : AT(ADDR(.markers) - LOAD_OFFSET) { \
+ VMLINUX_SYMBOL(__start___markers) = .; \
+ *(.markers) \
+ VMLINUX_SYMBOL(__stop___markers) = .; \
+ } \
+ .markers.c : AT(ADDR(.markers.c) - LOAD_OFFSET) { \
+ VMLINUX_SYMBOL(__start___markers_c) = .; \
+ *(.markers.c) \
+ VMLINUX_SYMBOL(__stop___markers_c) = .; \
+ } \
+ __end_rodata = .; \
+ . = ALIGN(4096); \
\
/* Built-in module parameters. */ \
__param : AT(ADDR(__param) - LOAD_OFFSET) { \
Index: linux-2.6-lttng/include/linux/marker.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6-lttng/include/linux/marker.h 2007-05-09 18:15:55.000000000 -0400
@@ -0,0 +1,124 @@
+#ifndef _LINUX_MARKER_H
+#define _LINUX_MARKER_H
+
+/*
+ * marker.h
+ *
+ * Code markup for dynamic and static tracing.
+ *
+ * See Documentation/marker.txt.
+ *
+ * (C) Copyright 2006 Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
+ *
+ * This file is released under the GPLv2.
+ * See the file COPYING for more details.
+ */
+
+#ifdef __KERNEL__
+
+struct __mark_marker_data;
+
+typedef void marker_probe_func(const struct __mark_marker_data *mdata,
+ const char *fmt, ...);
+
+struct __mark_marker_data {
+ const char *name;
+ const char *format;
+ const char *args;
+ int flags;
+ marker_probe_func *call;
+ void *pdata;
+} __attribute__((packed));
+
+struct __mark_marker {
+ struct __mark_marker_data *mdata;
+ void *enable;
+} __attribute__((packed));
+
+#ifdef CONFIG_MARKERS
+
+/* Marker flags : selects the mechanism used to connect the probes to the
+ * markers and what can be executed within the probes. This is primarily
+ * used at reentrancy-unfriendly sites. */
+#define MF_OPTIMIZED (1 << 0) /* Use optimized markers */
+#define MF_LOCKDEP (1 << 1) /* Can call lockdep */
+#define MF_PRINTK (1 << 2) /* vprintk can be called in the probe */
+#define _MF_NR 3 /* Number of marker flags */
+
+/* Generic marker flavor always available */
+#define trace_mark_generic(flags, name, format, args...) \
+ do { \
+ static const char __mstrtab_name_##name[] \
+ __attribute__((section("__markers_strings"))) \
+ = #name; \
+ static const char __mstrtab_format_##name[] \
+ __attribute__((section("__markers_strings"))) \
+ = format; \
+ static const char __mstrtab_args_##name[] \
+ __attribute__((section("__markers_strings"))) \
+ = #args; \
+ static struct __mark_marker_data __mark_data_##name \
+ __attribute__((section("__markers_data"))) = \
+ { __mstrtab_name_##name, __mstrtab_format_##name, \
+ __mstrtab_args_##name, \
+ (flags) & ~MF_OPTIMIZED, __mark_empty_function, NULL }; \
+ static char __marker_enable_##name = 0; \
+ static const struct __mark_marker __mark_##name \
+ __attribute__((section("__markers"))) = \
+ { &__mark_data_##name, &__marker_enable_##name } ; \
+ asm volatile ( "" : : "i" (&__mark_##name)); \
+ __mark_check_format(format, ## args); \
+ if (unlikely(__marker_enable_##name)) { \
+ preempt_disable(); \
+ (*__mark_data_##name.call)(&__mark_data_##name, \
+ format, ## args); \
+ preempt_enable(); \
+ } \
+ } while (0)
+
+#define MARK_GENERIC_ENABLE_IMMEDIATE_OFFSET 0
+#define MARK_GENERIC_ENABLE_TYPE char
+/* Dereference enable as lvalue from a pointer to its instruction */
+#define MARK_GENERIC_ENABLE(a) \
+ *(MARK_GENERIC_ENABLE_TYPE*) \
+ ((char*)a+MARK_GENERIC_ENABLE_IMMEDIATE_OFFSET)
+
+static inline int marker_generic_set_enable(void *address, char enable)
+{
+ MARK_GENERIC_ENABLE(address) = enable;
+ return 0;
+}
+
+#else /* !CONFIG_MARKERS */
+#define MARK_GENERIC(flags, name, format, args...) \
+ __mark_check_format(format, ## args)
+#endif /* CONFIG_MARKERS */
+
+#ifdef CONFIG_MARKERS_ENABLE_OPTIMIZATION
+#include <asm/marker.h> /* optimized marker flavor */
+#else
+#include <asm-generic/marker.h> /* fallback on generic markers */
+#endif
+
+#define MARK_MAX_FORMAT_LEN 1024
+/* Pass this as a format string for a marker with no argument */
+#define MARK_NOARGS " "
+
+/* To be used for string format validity checking with sparse */
+static inline
+void __mark_check_format(const char *fmt, ...)
+{ }
+
+extern marker_probe_func __mark_empty_function;
+
+extern int _marker_set_probe(int flags, const char *name, const char *format,
+ marker_probe_func *probe, void *pdata);
+
+#define marker_set_probe(name, format, probe, pdata) \
+ _marker_set_probe(MF_DEFAULT, name, format, probe, pdata)
+
+extern int marker_remove_probe(const char *name);
+extern int marker_list_probe(marker_probe_func *probe);
+
+#endif /* __KERNEL__ */
+#endif
Index: linux-2.6-lttng/include/linux/module.h
===================================================================
--- linux-2.6-lttng.orig/include/linux/module.h 2007-05-09 18:14:52.000000000 -0400
+++ linux-2.6-lttng/include/linux/module.h 2007-05-09 18:15:55.000000000 -0400
@@ -356,6 +356,9 @@
/* The command line arguments (may be mangled). People like
keeping pointers to this stuff */
char *args;
+
+ const struct __mark_marker *markers;
+ unsigned int num_markers;
};
/* FIXME: It'd be nice to isolate modules during init, too, so they
@@ -467,6 +470,7 @@
int unregister_module_notifier(struct notifier_block * nb);
extern void print_modules(void);
+extern void list_modules(void);
#else /* !CONFIG_MODULES... */
#define EXPORT_SYMBOL(sym)
Index: linux-2.6-lttng/kernel/module.c
===================================================================
--- linux-2.6-lttng.orig/kernel/module.c 2007-05-09 18:15:34.000000000 -0400
+++ linux-2.6-lttng/kernel/module.c 2007-05-09 18:15:55.000000000 -0400
@@ -142,6 +142,8 @@
extern const unsigned long __start___kcrctab_gpl_future[];
extern const unsigned long __start___kcrctab_unused[];
extern const unsigned long __start___kcrctab_unused_gpl[];
+extern const struct __mark_marker __start___markers[];
+extern const struct __mark_marker __stop___markers[];
#ifndef CONFIG_MODVERSIONS
#define symversion(base, idx) NULL
@@ -302,6 +304,229 @@
return NULL;
}
+#ifdef CONFIG_MARKERS
+
+/* Empty callback provided as a probe to the markers. By providing this to a
+ * disabled marker, we makes sure the execution flow is always valid even
+ * though the function pointer change and the marker enabling are two distinct
+ * operations that modifies the execution flow of preemptible code. */
+void __mark_empty_function(const struct __mark_marker_data *mdata,
+ const char *fmt, ...)
+{
+}
+EXPORT_SYMBOL_GPL(__mark_empty_function);
+
+/* Set the enable bit of the marker, choosing the generic or architecture
+ * specific functions depending on the marker's flags.
+ */
+static int marker_set_enable(void *address, char enable, int flags)
+{
+ if (flags & MF_OPTIMIZED)
+ return marker_optimized_set_enable(address, enable);
+ else
+ return marker_generic_set_enable(address, enable);
+}
+
+/* Sets the probe callback and enables the markers corresponding to a range of
+ * markers. The enable bit and function address are set out of order, and it's
+ * ok : the state is always coherent because of the empty callback we provide.
+ */
+static int _marker_set_probe_range(int flags, const char *name,
+ const char *format,
+ marker_probe_func *probe,
+ void *pdata,
+ const struct __mark_marker *begin,
+ const struct __mark_marker *end)
+{
+ const struct __mark_marker *iter;
+ int found = 0;
+
+ for (iter = begin; iter < end; iter++) {
+ if (strcmp(name, iter->mdata->name) == 0) {
+ if (format
+ && strcmp(format, iter->mdata->format) != 0) {
+ printk(KERN_NOTICE
+ "Format mismatch for probe %s "
+ "(%s), marker (%s)\n",
+ name,
+ format,
+ iter->mdata->format);
+ continue;
+ }
+ if (flags & MF_LOCKDEP
+ && !(iter->mdata->flags & MF_LOCKDEP)) {
+ printk(KERN_NOTICE
+ "Incompatible lockdep flags for "
+ "probe %s\n",
+ name);
+ continue;
+ }
+ if (flags & MF_PRINTK
+ && !(iter->mdata->flags & MF_PRINTK)) {
+ printk(KERN_NOTICE
+ "Incompatible printk flags for "
+ "probe %s\n",
+ name);
+ continue;
+ }
+ if (probe == __mark_empty_function) {
+ if (iter->mdata->call
+ != __mark_empty_function) {
+ iter->mdata->call =
+ __mark_empty_function;
+ }
+ marker_set_enable(iter->enable, 0,
+ iter->mdata->flags);
+ } else {
+ if (iter->mdata->call
+ != __mark_empty_function) {
+ if (iter->mdata->call != probe) {
+ printk(KERN_NOTICE
+ "Marker %s busy, "
+ "probe %p already "
+ "installed\n",
+ name,
+ iter->mdata->call);
+ continue;
+ }
+ } else {
+ found++;
+ iter->mdata->call = probe;
+ }
+ iter->mdata->pdata = pdata;
+ smp_wmb();
+ marker_set_enable(iter->enable, 1,
+ iter->mdata->flags);
+ }
+ found++;
+ }
+ }
+ return found;
+}
+
+/* Sets a range of markers to a disabled state : unset the enable bit and
+ * provide the empty callback. */
+static int marker_remove_probe_range(const char *name,
+ const struct __mark_marker *begin,
+ const struct __mark_marker *end)
+{
+ const struct __mark_marker *iter;
+ int found = 0;
+
+ for (iter = begin; iter < end; iter++) {
+ if (strcmp(name, iter->mdata->name) == 0) {
+ marker_set_enable(iter->enable, 0,
+ iter->mdata->flags);
+ iter->mdata->call = __mark_empty_function;
+ found++;
+ }
+ }
+ return found;
+}
+
+/* Provides a listing of the markers present in the kernel with their type
+ * (optimized or generic), state (enabled or disabled), callback and format
+ * string. */
+static int marker_list_probe_range(marker_probe_func *probe,
+ const struct __mark_marker *begin,
+ const struct __mark_marker *end)
+{
+ const struct __mark_marker *iter;
+ int found = 0;
+
+ for (iter = begin; iter < end; iter++) {
+ if (probe)
+ if (probe != iter->mdata->call) continue;
+ printk("name %s \n", iter->mdata->name);
+ if (iter->mdata->flags & MF_OPTIMIZED)
+ printk(" enable %u optimized ",
+ MARK_OPTIMIZED_ENABLE(iter->enable));
+ else
+ printk(" enable %u generic ",
+ MARK_GENERIC_ENABLE(iter->enable));
+ printk(" func 0x%p format \"%s\"\n",
+ iter->mdata->call, iter->mdata->format);
+ found++;
+ }
+ return found;
+}
+
+/* Calls _marker_set_probe_range for the core markers and modules markers.
+ * Marker enabling/disabling use the modlist_lock to synchronise. */
+int _marker_set_probe(int flags, const char *name, const char *format,
+ marker_probe_func *probe,
+ void *pdata)
+{
+ struct module *mod;
+ int found = 0;
+
+ mutex_lock(&module_mutex);
+ /* Core kernel markers */
+ found += _marker_set_probe_range(flags, name, format, probe,
+ pdata,
+ __start___markers, __stop___markers);
+ /* Markers in modules. */
+ list_for_each_entry(mod, &modules, list) {
+ if (!mod->taints)
+ found += _marker_set_probe_range(flags, name, format,
+ probe, pdata,
+ mod->markers, mod->markers+mod->num_markers);
+ }
+ mutex_unlock(&module_mutex);
+ return found;
+}
+EXPORT_SYMBOL_GPL(_marker_set_probe);
+
+/* Calls _marker_remove_probe_range for the core markers and modules markers.
+ * Marker enabling/disabling use the modlist_lock to synchronise. */
+int marker_remove_probe(const char *name)
+{
+ struct module *mod;
+ int found = 0;
+
+ mutex_lock(&module_mutex);
+ /* Core kernel markers */
+ found += marker_remove_probe_range(name,
+ __start___markers, __stop___markers);
+ /* Markers in modules. */
+ list_for_each_entry(mod, &modules, list) {
+ if (!mod->taints)
+ found += marker_remove_probe_range(name,
+ mod->markers, mod->markers+mod->num_markers);
+ }
+ mutex_unlock(&module_mutex);
+ return found;
+}
+EXPORT_SYMBOL_GPL(marker_remove_probe);
+
+/* Calls _marker_list_probe_range for the core markers and modules markers.
+ * Marker listing uses the modlist_lock to synchronise.
+ * TODO : should output this listing to a procfs file. */
+int marker_list_probe(marker_probe_func *probe)
+{
+ struct module *mod;
+ int found = 0;
+
+ mutex_lock(&module_mutex);
+ /* Core kernel markers */
+ printk("Listing kernel markers\n");
+ found += marker_list_probe_range(probe,
+ __start___markers, __stop___markers);
+ /* Markers in modules. */
+ printk("Listing module markers\n");
+ list_for_each_entry(mod, &modules, list) {
+ if (!mod->taints) {
+ printk("Listing markers for module %s\n", mod->name);
+ found += marker_list_probe_range(probe,
+ mod->markers, mod->markers+mod->num_markers);
+ }
+ }
+ mutex_unlock(&module_mutex);
+ return found;
+}
+EXPORT_SYMBOL_GPL(marker_list_probe);
+#endif
+
#ifdef CONFIG_SMP
/* Number of blocks used and allocated. */
static unsigned int pcpu_num_used, pcpu_num_allocated;
@@ -1659,6 +1884,9 @@
unsigned int unusedcrcindex;
unsigned int unusedgplindex;
unsigned int unusedgplcrcindex;
+ unsigned int markersindex;
+ unsigned int markersdataindex;
+ unsigned int markersstringsindex;
struct module *mod;
long err = 0;
void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */
@@ -1755,6 +1983,10 @@
#ifdef ARCH_UNWIND_SECTION_NAME
unwindex = find_sec(hdr, sechdrs, secstrings, ARCH_UNWIND_SECTION_NAME);
#endif
+ markersindex = find_sec(hdr, sechdrs, secstrings, "__markers");
+ markersdataindex = find_sec(hdr, sechdrs, secstrings, "__markers_data");
+ markersstringsindex = find_sec(hdr, sechdrs, secstrings,
+ "__markers_strings");
/* Don't keep modinfo section */
sechdrs[infoindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
@@ -1765,6 +1997,22 @@
#endif
if (unwindex)
sechdrs[unwindex].sh_flags |= SHF_ALLOC;
+#ifdef CONFIG_MARKERS
+ if (markersindex)
+ sechdrs[markersindex].sh_flags |= SHF_ALLOC;
+ if (markersdataindex)
+ sechdrs[markersdataindex].sh_flags |= SHF_ALLOC;
+ if (markersstringsindex)
+ sechdrs[markersstringsindex].sh_flags |= SHF_ALLOC;
+#else
+ if (markersindex)
+ sechdrs[markersindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
+ if (markersdataindex)
+ sechdrs[markersdataindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
+ if (markersstringsindex)
+ sechdrs[markersstringsindex].sh_flags
+ &= ~(unsigned long)SHF_ALLOC;
+#endif
/* Check module struct version now, before we try to use module. */
if (!check_modstruct_version(sechdrs, versindex, mod)) {
@@ -1905,6 +2153,11 @@
mod->gpl_future_syms = (void *)sechdrs[gplfutureindex].sh_addr;
if (gplfuturecrcindex)
mod->gpl_future_crcs = (void *)sechdrs[gplfuturecrcindex].sh_addr;
+ if (markersindex) {
+ mod->markers = (void *)sechdrs[markersindex].sh_addr;
+ mod->num_markers =
+ sechdrs[markersindex].sh_size / sizeof(*mod->markers);
+ }
mod->unused_syms = (void *)sechdrs[unusedindex].sh_addr;
if (unusedcrcindex)
@@ -2399,6 +2652,26 @@
.show = m_show
};
+void list_modules(void)
+{
+ /* Enumerate loaded modules */
+ struct list_head *i;
+ struct module *mod;
+ unsigned long refcount = 0;
+
+ mutex_lock(&module_mutex);
+ list_for_each(i, &modules) {
+ mod = list_entry(i, struct module, list);
+#ifdef CONFIG_MODULE_UNLOAD
+ refcount = local_read(&mod->ref[0].count);
+#endif //CONFIG_MODULE_UNLOAD
+ trace_mark(list_module, "%s %d %lu",
+ mod->name, mod->state, refcount);
+ }
+ mutex_unlock(&module_mutex);
+}
+EXPORT_SYMBOL_GPL(list_modules);
+
/* Given an address, look for it in the module exception tables. */
const struct exception_table_entry *search_module_extables(unsigned long addr)
{
--
Mathieu Desnoyers
Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
next prev parent reply other threads:[~2007-05-10 2:12 UTC|newest]
Thread overview: 61+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-05-10 1:55 [patch 00/10] Linux Kernel Markers for 2.6.21-mm2 Mathieu Desnoyers
2007-05-10 1:55 ` [patch 01/10] Linux Kernel Markers - Add kconfig menus for the marker code Mathieu Desnoyers
2007-05-10 6:57 ` Christoph Hellwig
2007-05-10 1:55 ` Mathieu Desnoyers [this message]
2007-05-10 5:10 ` [patch 02/10] Linux Kernel Markers, architecture independent code Alexey Dobriyan
2007-05-10 12:58 ` Mathieu Desnoyers
2007-05-10 13:12 ` Mathieu Desnoyers
2007-05-10 19:00 ` Alexey Dobriyan
2007-05-10 19:46 ` Mathieu Desnoyers
2007-05-10 1:55 ` [patch 03/10] Allow userspace applications to use marker.h to parse the markers section in the kernel binary Mathieu Desnoyers
2007-05-10 6:51 ` Christoph Hellwig
2007-05-10 22:14 ` David Smith
2007-06-23 8:09 ` Christoph Hellwig
2007-06-23 9:25 ` Alan Cox
2007-06-23 9:32 ` Christoph Hellwig
2007-06-23 9:49 ` Alan Cox
2007-06-23 10:06 ` Christoph Hellwig
2007-06-23 14:55 ` Alan Cox
2007-05-10 1:55 ` [patch 04/10] Linux Kernel Markers - PowerPC optimized version Mathieu Desnoyers
2007-05-10 6:57 ` Christoph Hellwig
2007-05-10 1:56 ` [patch 05/10] Linux Kernel Markers - i386 " Mathieu Desnoyers
2007-05-10 9:06 ` Andi Kleen
2007-05-10 15:55 ` Mathieu Desnoyers
2007-05-10 16:28 ` Alan Cox
2007-05-10 16:59 ` Mathieu Desnoyers
2007-05-11 4:57 ` Ananth N Mavinakayanahalli
2007-05-11 18:55 ` Mathieu Desnoyers
2007-05-12 5:29 ` Suparna Bhattacharya
2007-05-11 6:04 ` Andi Kleen
2007-05-11 18:02 ` Mathieu Desnoyers
2007-05-11 21:56 ` Alan Cox
2007-05-13 15:20 ` Mathieu Desnoyers
2007-05-10 1:56 ` [patch 06/10] Linux Kernel Markers - Non optimized architectures Mathieu Desnoyers
2007-05-10 5:13 ` Alexey Dobriyan
2007-05-10 6:56 ` Christoph Hellwig
2007-05-10 13:11 ` Mathieu Desnoyers
2007-05-10 13:40 ` Alan Cox
2007-05-10 14:25 ` Mathieu Desnoyers
2007-05-10 15:33 ` Nicholas Berry
2007-05-10 16:09 ` Alan Cox
2007-05-10 1:56 ` [patch 07/10] Linux Kernel Markers - Documentation Mathieu Desnoyers
2007-05-10 6:58 ` Christoph Hellwig
2007-05-10 11:41 ` Alan Cox
2007-05-10 11:41 ` Christoph Hellwig
2007-05-10 12:48 ` Alan Cox
2007-05-10 12:52 ` Pekka Enberg
2007-05-10 13:04 ` Alan Cox
2007-05-10 13:16 ` Pekka J Enberg
2007-05-10 13:43 ` Alan Cox
2007-05-10 14:04 ` Pekka J Enberg
2007-05-10 14:12 ` Mathieu Desnoyers
2007-05-10 14:14 ` Mathieu Desnoyers
2007-05-11 15:05 ` Valdis.Kletnieks
2007-05-10 12:00 ` Christoph Hellwig
2007-05-10 15:51 ` Scott Preece
2007-05-10 1:56 ` [patch 08/10] Defines the linker macro EXTRA_RWDATA for the marker data section Mathieu Desnoyers
2007-05-10 1:56 ` [patch 09/10] Linux Kernel Markers - Use EXTRA_RWDATA in architectures Mathieu Desnoyers
2007-05-10 1:56 ` [patch 10/10] Port of blktrace to the Linux Kernel Markers Mathieu Desnoyers
2007-05-10 6:53 ` Christoph Hellwig
2007-05-10 9:20 ` Jens Axboe
2007-05-10 2:30 ` [patch 00/10] Linux Kernel Markers for 2.6.21-mm2 Andrew Morton
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=20070510020915.609367244@polymtl.ca \
--to=mathieu.desnoyers@polymtl.ca \
--cc=akpm@linux-foundation.org \
--cc=bunk@stusta.de \
--cc=hch@infradead.org \
--cc=linux-kernel@vger.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox