From: Arnaldo Carvalho de Melo <acme@kernel.org>
To: Ian Rogers <irogers@google.com>
Cc: Peter Zijlstra <peterz@infradead.org>,
Ingo Molnar <mingo@redhat.com>,
Namhyung Kim <namhyung@kernel.org>,
Mark Rutland <mark.rutland@arm.com>,
Alexander Shishkin <alexander.shishkin@linux.intel.com>,
Jiri Olsa <jolsa@kernel.org>,
Adrian Hunter <adrian.hunter@intel.com>,
Kan Liang <kan.liang@linux.intel.com>,
James Clark <james.clark@linaro.org>,
Howard Chu <howardchu95@gmail.com>,
Athira Jajeev <atrajeev@linux.vnet.ibm.com>,
Michael Petlan <mpetlan@redhat.com>,
Veronika Molnarova <vmolnaro@redhat.com>,
Dapeng Mi <dapeng1.mi@linux.intel.com>,
Thomas Richter <tmricht@linux.ibm.com>,
Ilya Leoshkevich <iii@linux.ibm.com>,
Colin Ian King <colin.i.king@gmail.com>,
Weilin Wang <weilin.wang@intel.com>,
Andi Kleen <ak@linux.intel.com>,
Josh Poimboeuf <jpoimboe@redhat.com>,
linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org
Subject: Re: [PATCH v5 14/21] perf lock: Move common lock contention code to new file
Date: Thu, 31 Oct 2024 16:36:38 -0300 [thread overview]
Message-ID: <ZyPcRn9UdigvUmuE@x1> (raw)
In-Reply-To: <20241031014252.753588-15-irogers@google.com>
On Wed, Oct 30, 2024 at 06:42:45PM -0700, Ian Rogers wrote:
> Avoid references from util code to builtin-lock that require python
> stubs. Move the functions and related variables to
> util/lock-contention.c. Add max_stack_depth parameter to
> match_callstack_filter to avoid sharing a global variable.
Acked-by: Arnaldo Carvalho de Melo <acme@redhat.com>
> Signed-off-by: Ian Rogers <irogers@google.com>
> ---
> tools/perf/builtin-lock.c | 137 +--------------------
> tools/perf/util/Build | 1 +
> tools/perf/util/bpf_lock_contention.c | 2 +-
> tools/perf/util/lock-contention.c | 170 ++++++++++++++++++++++++++
> tools/perf/util/lock-contention.h | 37 ++----
> tools/perf/util/python.c | 17 ---
> 6 files changed, 185 insertions(+), 179 deletions(-)
> create mode 100644 tools/perf/util/lock-contention.c
>
> diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
> index 062e2b56a2ab..f66948b1fbed 100644
> --- a/tools/perf/builtin-lock.c
> +++ b/tools/perf/builtin-lock.c
> @@ -46,15 +46,6 @@
> static struct perf_session *session;
> static struct target target;
>
> -/* based on kernel/lockdep.c */
> -#define LOCKHASH_BITS 12
> -#define LOCKHASH_SIZE (1UL << LOCKHASH_BITS)
> -
> -static struct hlist_head *lockhash_table;
> -
> -#define __lockhashfn(key) hash_long((unsigned long)key, LOCKHASH_BITS)
> -#define lockhashentry(key) (lockhash_table + __lockhashfn((key)))
> -
> static struct rb_root thread_stats;
>
> static bool combine_locks;
> @@ -67,24 +58,13 @@ static unsigned long bpf_map_entries = MAX_ENTRIES;
> static int max_stack_depth = CONTENTION_STACK_DEPTH;
> static int stack_skip = CONTENTION_STACK_SKIP;
> static int print_nr_entries = INT_MAX / 2;
> -static LIST_HEAD(callstack_filters);
> static const char *output_name = NULL;
> static FILE *lock_output;
>
> -struct callstack_filter {
> - struct list_head list;
> - char name[];
> -};
> -
> static struct lock_filter filters;
>
> static enum lock_aggr_mode aggr_mode = LOCK_AGGR_ADDR;
>
> -static bool needs_callstack(void)
> -{
> - return !list_empty(&callstack_filters);
> -}
> -
> static struct thread_stat *thread_stat_find(u32 tid)
> {
> struct rb_node *node;
> @@ -477,93 +457,6 @@ static struct lock_stat *pop_from_result(void)
> return container_of(node, struct lock_stat, rb);
> }
>
> -struct lock_stat *lock_stat_find(u64 addr)
> -{
> - struct hlist_head *entry = lockhashentry(addr);
> - struct lock_stat *ret;
> -
> - hlist_for_each_entry(ret, entry, hash_entry) {
> - if (ret->addr == addr)
> - return ret;
> - }
> - return NULL;
> -}
> -
> -struct lock_stat *lock_stat_findnew(u64 addr, const char *name, int flags)
> -{
> - struct hlist_head *entry = lockhashentry(addr);
> - struct lock_stat *ret, *new;
> -
> - hlist_for_each_entry(ret, entry, hash_entry) {
> - if (ret->addr == addr)
> - return ret;
> - }
> -
> - new = zalloc(sizeof(struct lock_stat));
> - if (!new)
> - goto alloc_failed;
> -
> - new->addr = addr;
> - new->name = strdup(name);
> - if (!new->name) {
> - free(new);
> - goto alloc_failed;
> - }
> -
> - new->flags = flags;
> - new->wait_time_min = ULLONG_MAX;
> -
> - hlist_add_head(&new->hash_entry, entry);
> - return new;
> -
> -alloc_failed:
> - pr_err("memory allocation failed\n");
> - return NULL;
> -}
> -
> -bool match_callstack_filter(struct machine *machine, u64 *callstack)
> -{
> - struct map *kmap;
> - struct symbol *sym;
> - u64 ip;
> - const char *arch = perf_env__arch(machine->env);
> -
> - if (list_empty(&callstack_filters))
> - return true;
> -
> - for (int i = 0; i < max_stack_depth; i++) {
> - struct callstack_filter *filter;
> -
> - /*
> - * In powerpc, the callchain saved by kernel always includes
> - * first three entries as the NIP (next instruction pointer),
> - * LR (link register), and the contents of LR save area in the
> - * second stack frame. In certain scenarios its possible to have
> - * invalid kernel instruction addresses in either LR or the second
> - * stack frame's LR. In that case, kernel will store that address as
> - * zero.
> - *
> - * The below check will continue to look into callstack,
> - * incase first or second callstack index entry has 0
> - * address for powerpc.
> - */
> - if (!callstack || (!callstack[i] && (strcmp(arch, "powerpc") ||
> - (i != 1 && i != 2))))
> - break;
> -
> - ip = callstack[i];
> - sym = machine__find_kernel_symbol(machine, ip, &kmap);
> - if (sym == NULL)
> - continue;
> -
> - list_for_each_entry(filter, &callstack_filters, list) {
> - if (strstr(sym->name, filter->name))
> - return true;
> - }
> - }
> - return false;
> -}
> -
> struct trace_lock_handler {
> /* it's used on CONFIG_LOCKDEP */
> int (*acquire_event)(struct evsel *evsel,
> @@ -1165,7 +1058,7 @@ static int report_lock_contention_begin_event(struct evsel *evsel,
> if (callstack == NULL)
> return -ENOMEM;
>
> - if (!match_callstack_filter(machine, callstack)) {
> + if (!match_callstack_filter(machine, callstack, max_stack_depth)) {
> free(callstack);
> return 0;
> }
> @@ -2449,34 +2342,6 @@ static int parse_lock_addr(const struct option *opt __maybe_unused, const char *
> return ret;
> }
>
> -static int parse_call_stack(const struct option *opt __maybe_unused, const char *str,
> - int unset __maybe_unused)
> -{
> - char *s, *tmp, *tok;
> - int ret = 0;
> -
> - s = strdup(str);
> - if (s == NULL)
> - return -1;
> -
> - for (tok = strtok_r(s, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) {
> - struct callstack_filter *entry;
> -
> - entry = malloc(sizeof(*entry) + strlen(tok) + 1);
> - if (entry == NULL) {
> - pr_err("Memory allocation failure\n");
> - free(s);
> - return -1;
> - }
> -
> - strcpy(entry->name, tok);
> - list_add_tail(&entry->list, &callstack_filters);
> - }
> -
> - free(s);
> - return ret;
> -}
> -
> static int parse_output(const struct option *opt __maybe_unused, const char *str,
> int unset __maybe_unused)
> {
> diff --git a/tools/perf/util/Build b/tools/perf/util/Build
> index 340544a6f5ec..3c6cd8d81d88 100644
> --- a/tools/perf/util/Build
> +++ b/tools/perf/util/Build
> @@ -121,6 +121,7 @@ perf-util-y += topdown.o
> perf-util-y += iostat.o
> perf-util-y += stream.o
> perf-util-y += kvm-stat.o
> +perf-util-y += lock-contention.o
> perf-util-$(CONFIG_AUXTRACE) += auxtrace.o
> perf-util-y += intel-pt-decoder/
> perf-util-$(CONFIG_AUXTRACE) += intel-pt.o
> diff --git a/tools/perf/util/bpf_lock_contention.c b/tools/perf/util/bpf_lock_contention.c
> index 41a1ad087895..37e17c56f106 100644
> --- a/tools/perf/util/bpf_lock_contention.c
> +++ b/tools/perf/util/bpf_lock_contention.c
> @@ -458,7 +458,7 @@ int lock_contention_read(struct lock_contention *con)
> if (con->save_callstack) {
> bpf_map_lookup_elem(stack, &key.stack_id, stack_trace);
>
> - if (!match_callstack_filter(machine, stack_trace)) {
> + if (!match_callstack_filter(machine, stack_trace, con->max_stack)) {
> con->nr_filtered += data.count;
> goto next;
> }
> diff --git a/tools/perf/util/lock-contention.c b/tools/perf/util/lock-contention.c
> new file mode 100644
> index 000000000000..841bb18b1f06
> --- /dev/null
> +++ b/tools/perf/util/lock-contention.c
> @@ -0,0 +1,170 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include "debug.h"
> +#include "env.h"
> +#include "lock-contention.h"
> +#include "machine.h"
> +#include "symbol.h"
> +
> +#include <limits.h>
> +#include <string.h>
> +
> +#include <linux/hash.h>
> +#include <linux/zalloc.h>
> +
> +#define __lockhashfn(key) hash_long((unsigned long)key, LOCKHASH_BITS)
> +#define lockhashentry(key) (lockhash_table + __lockhashfn((key)))
> +
> +struct callstack_filter {
> + struct list_head list;
> + char name[];
> +};
> +
> +static LIST_HEAD(callstack_filters);
> +struct hlist_head *lockhash_table;
> +
> +int parse_call_stack(const struct option *opt __maybe_unused, const char *str,
> + int unset __maybe_unused)
> +{
> + char *s, *tmp, *tok;
> + int ret = 0;
> +
> + s = strdup(str);
> + if (s == NULL)
> + return -1;
> +
> + for (tok = strtok_r(s, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) {
> + struct callstack_filter *entry;
> +
> + entry = malloc(sizeof(*entry) + strlen(tok) + 1);
> + if (entry == NULL) {
> + pr_err("Memory allocation failure\n");
> + free(s);
> + return -1;
> + }
> +
> + strcpy(entry->name, tok);
> + list_add_tail(&entry->list, &callstack_filters);
> + }
> +
> + free(s);
> + return ret;
> +}
> +
> +bool needs_callstack(void)
> +{
> + return !list_empty(&callstack_filters);
> +}
> +
> +struct lock_stat *lock_stat_find(u64 addr)
> +{
> + struct hlist_head *entry = lockhashentry(addr);
> + struct lock_stat *ret;
> +
> + hlist_for_each_entry(ret, entry, hash_entry) {
> + if (ret->addr == addr)
> + return ret;
> + }
> + return NULL;
> +}
> +
> +struct lock_stat *lock_stat_findnew(u64 addr, const char *name, int flags)
> +{
> + struct hlist_head *entry = lockhashentry(addr);
> + struct lock_stat *ret, *new;
> +
> + hlist_for_each_entry(ret, entry, hash_entry) {
> + if (ret->addr == addr)
> + return ret;
> + }
> +
> + new = zalloc(sizeof(struct lock_stat));
> + if (!new)
> + goto alloc_failed;
> +
> + new->addr = addr;
> + new->name = strdup(name);
> + if (!new->name) {
> + free(new);
> + goto alloc_failed;
> + }
> +
> + new->flags = flags;
> + new->wait_time_min = ULLONG_MAX;
> +
> + hlist_add_head(&new->hash_entry, entry);
> + return new;
> +
> +alloc_failed:
> + pr_err("memory allocation failed\n");
> + return NULL;
> +}
> +
> +bool match_callstack_filter(struct machine *machine, u64 *callstack, int max_stack_depth)
> +{
> + struct map *kmap;
> + struct symbol *sym;
> + u64 ip;
> + const char *arch = perf_env__arch(machine->env);
> +
> + if (list_empty(&callstack_filters))
> + return true;
> +
> + for (int i = 0; i < max_stack_depth; i++) {
> + struct callstack_filter *filter;
> +
> + /*
> + * In powerpc, the callchain saved by kernel always includes
> + * first three entries as the NIP (next instruction pointer),
> + * LR (link register), and the contents of LR save area in the
> + * second stack frame. In certain scenarios its possible to have
> + * invalid kernel instruction addresses in either LR or the second
> + * stack frame's LR. In that case, kernel will store that address as
> + * zero.
> + *
> + * The below check will continue to look into callstack,
> + * incase first or second callstack index entry has 0
> + * address for powerpc.
> + */
> + if (!callstack || (!callstack[i] && (strcmp(arch, "powerpc") ||
> + (i != 1 && i != 2))))
> + break;
> +
> + ip = callstack[i];
> + sym = machine__find_kernel_symbol(machine, ip, &kmap);
> + if (sym == NULL)
> + continue;
> +
> + list_for_each_entry(filter, &callstack_filters, list) {
> + if (strstr(sym->name, filter->name))
> + return true;
> + }
> + }
> + return false;
> +}
> +
> +#ifndef HAVE_BPF_SKEL
> +int lock_contention_prepare(struct lock_contention *con __maybe_unused)
> +{
> + return 0;
> +}
> +
> +int lock_contention_start(void)
> +{
> + return 0;
> +}
> +
> +int lock_contention_stop(void)
> +{
> + return 0;
> +}
> +
> +int lock_contention_finish(struct lock_contention *con __maybe_unused)
> +{
> + return 0;
> +}
> +
> +int lock_contention_read(struct lock_contention *con __maybe_unused)
> +{
> + return 0;
> +}
> +#endif /* !HAVE_BPF_SKEL */
> diff --git a/tools/perf/util/lock-contention.h b/tools/perf/util/lock-contention.h
> index 1a7248ff3889..bfa5c7db0a5d 100644
> --- a/tools/perf/util/lock-contention.h
> +++ b/tools/perf/util/lock-contention.h
> @@ -67,10 +67,11 @@ struct lock_stat {
> */
> #define MAX_LOCK_DEPTH 48
>
> -struct lock_stat *lock_stat_find(u64 addr);
> -struct lock_stat *lock_stat_findnew(u64 addr, const char *name, int flags);
> +/* based on kernel/lockdep.c */
> +#define LOCKHASH_BITS 12
> +#define LOCKHASH_SIZE (1UL << LOCKHASH_BITS)
>
> -bool match_callstack_filter(struct machine *machine, u64 *callstack);
> +extern struct hlist_head *lockhash_table;
>
> /*
> * struct lock_seq_stat:
> @@ -148,7 +149,14 @@ struct lock_contention {
> bool save_callstack;
> };
>
> -#ifdef HAVE_BPF_SKEL
> +struct option;
> +int parse_call_stack(const struct option *opt, const char *str, int unset);
> +bool needs_callstack(void);
> +
> +struct lock_stat *lock_stat_find(u64 addr);
> +struct lock_stat *lock_stat_findnew(u64 addr, const char *name, int flags);
> +
> +bool match_callstack_filter(struct machine *machine, u64 *callstack, int max_stack_depth);
>
> int lock_contention_prepare(struct lock_contention *con);
> int lock_contention_start(void);
> @@ -156,25 +164,4 @@ int lock_contention_stop(void);
> int lock_contention_read(struct lock_contention *con);
> int lock_contention_finish(struct lock_contention *con);
>
> -#else /* !HAVE_BPF_SKEL */
> -
> -static inline int lock_contention_prepare(struct lock_contention *con __maybe_unused)
> -{
> - return 0;
> -}
> -
> -static inline int lock_contention_start(void) { return 0; }
> -static inline int lock_contention_stop(void) { return 0; }
> -static inline int lock_contention_finish(struct lock_contention *con __maybe_unused)
> -{
> - return 0;
> -}
> -
> -static inline int lock_contention_read(struct lock_contention *con __maybe_unused)
> -{
> - return 0;
> -}
> -
> -#endif /* HAVE_BPF_SKEL */
> -
> #endif /* PERF_LOCK_CONTENTION_H */
> diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
> index 35d84a96dbec..91fd444615cd 100644
> --- a/tools/perf/util/python.c
> +++ b/tools/perf/util/python.c
> @@ -18,7 +18,6 @@
> #include "mmap.h"
> #include "util/kwork.h"
> #include "util/sample.h"
> -#include "util/lock-contention.h"
> #include <internal/lib.h>
> #include "../builtin.h"
>
> @@ -1311,22 +1310,6 @@ struct kwork_work *perf_kwork_add_work(struct perf_kwork *kwork __maybe_unused,
> return NULL;
> }
>
> -bool match_callstack_filter(struct machine *machine __maybe_unused, u64 *callstack __maybe_unused)
> -{
> - return false;
> -}
> -
> -struct lock_stat *lock_stat_find(u64 addr __maybe_unused)
> -{
> - return NULL;
> -}
> -
> -struct lock_stat *lock_stat_findnew(u64 addr __maybe_unused, const char *name __maybe_unused,
> - int flags __maybe_unused)
> -{
> - return NULL;
> -}
> -
> int cmd_inject(int argc __maybe_unused, const char *argv[] __maybe_unused)
> {
> return -1;
> --
> 2.47.0.163.g1226f6d8fa-goog
>
next prev parent reply other threads:[~2024-10-31 19:36 UTC|newest]
Thread overview: 53+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-10-31 1:42 [PATCH v5 00/21] Python module cleanup Ian Rogers
2024-10-31 1:42 ` [PATCH v5 01/21] perf python: Remove python 2 scripting support Ian Rogers
2024-10-31 19:19 ` Arnaldo Carvalho de Melo
2024-10-31 1:42 ` [PATCH v5 02/21] perf python: Constify variables and parameters Ian Rogers
2024-10-31 19:21 ` Arnaldo Carvalho de Melo
2024-10-31 1:42 ` [PATCH v5 03/21] perf python: Remove unused #include Ian Rogers
2024-10-31 19:19 ` Arnaldo Carvalho de Melo
2024-10-31 1:42 ` [PATCH v5 04/21] perf script: Move scripting_max_stack out of builtin Ian Rogers
2024-10-31 19:20 ` Arnaldo Carvalho de Melo
2024-10-31 1:42 ` [PATCH v5 05/21] perf kvm: Move functions used in util " Ian Rogers
2024-10-31 19:24 ` Arnaldo Carvalho de Melo
2024-10-31 1:42 ` [PATCH v5 06/21] perf script: Move find_scripts to browser/scripts.c Ian Rogers
2024-10-31 19:18 ` Arnaldo Carvalho de Melo
2024-10-31 20:51 ` Ian Rogers
2024-11-04 19:47 ` Namhyung Kim
2024-11-04 20:34 ` Ian Rogers
2024-11-04 20:39 ` Arnaldo Carvalho de Melo
2024-11-04 20:48 ` Ian Rogers
2024-11-04 21:00 ` Namhyung Kim
2024-11-04 21:06 ` Ian Rogers
2024-11-04 22:09 ` Namhyung Kim
2024-11-04 22:20 ` Ian Rogers
2024-11-04 23:22 ` Namhyung Kim
2024-11-04 23:28 ` Ian Rogers
2024-11-05 6:14 ` Namhyung Kim
2024-10-31 1:42 ` [PATCH v5 07/21] perf stat: Move stat_config into config.c Ian Rogers
2024-10-31 19:19 ` Arnaldo Carvalho de Melo
2024-10-31 1:42 ` [PATCH v5 08/21] perf script: Move script_spec code to trace-event-scripting.c Ian Rogers
2024-10-31 19:21 ` Arnaldo Carvalho de Melo
2024-10-31 1:42 ` [PATCH v5 09/21] perf script: Move script_fetch_insn " Ian Rogers
2024-10-31 19:33 ` Arnaldo Carvalho de Melo
2024-10-31 1:42 ` [PATCH v5 10/21] perf script: Move perf_sample__sprintf_flags " Ian Rogers
2024-10-31 1:42 ` [PATCH v5 11/21] perf x86: Define arch_fetch_insn in NO_AUXTRACE builds Ian Rogers
2024-10-31 9:14 ` Adrian Hunter
2024-10-31 1:42 ` [PATCH v5 12/21] perf intel-pt: Remove stale build comment Ian Rogers
2024-10-31 9:13 ` Adrian Hunter
2024-10-31 1:42 ` [PATCH v5 13/21] perf env: Move arch errno function to only use in env Ian Rogers
2024-10-31 19:34 ` Arnaldo Carvalho de Melo
2024-10-31 1:42 ` [PATCH v5 14/21] perf lock: Move common lock contention code to new file Ian Rogers
2024-10-31 19:36 ` Arnaldo Carvalho de Melo [this message]
2024-10-31 1:42 ` [PATCH v5 15/21] perf bench: Remove reference to cmd_inject Ian Rogers
2024-10-31 1:42 ` [PATCH v5 16/21] perf kwork: Make perf_kwork_add_work a callback Ian Rogers
2024-10-31 1:42 ` [PATCH v5 17/21] perf build: Remove test library from python shared object Ian Rogers
2024-10-31 19:21 ` Arnaldo Carvalho de Melo
2024-10-31 1:42 ` [PATCH v5 18/21] perf python: Add parse_events function Ian Rogers
2024-10-31 1:42 ` [PATCH v5 19/21] perf python: Add __str__ and __repr__ functions to evlist Ian Rogers
2024-10-31 19:22 ` Arnaldo Carvalho de Melo
2024-10-31 1:42 ` [PATCH v5 20/21] perf python: Add __str__ and __repr__ functions to evsel Ian Rogers
2024-10-31 19:38 ` Arnaldo Carvalho de Melo
2024-10-31 1:42 ` [PATCH v5 21/21] perf python: Correctly throw IndexError Ian Rogers
2024-10-31 19:23 ` Arnaldo Carvalho de Melo
2024-10-31 19:39 ` [PATCH v5 00/21] Python module cleanup Arnaldo Carvalho de Melo
2024-10-31 20:55 ` Ian Rogers
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=ZyPcRn9UdigvUmuE@x1 \
--to=acme@kernel.org \
--cc=adrian.hunter@intel.com \
--cc=ak@linux.intel.com \
--cc=alexander.shishkin@linux.intel.com \
--cc=atrajeev@linux.vnet.ibm.com \
--cc=colin.i.king@gmail.com \
--cc=dapeng1.mi@linux.intel.com \
--cc=howardchu95@gmail.com \
--cc=iii@linux.ibm.com \
--cc=irogers@google.com \
--cc=james.clark@linaro.org \
--cc=jolsa@kernel.org \
--cc=jpoimboe@redhat.com \
--cc=kan.liang@linux.intel.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-perf-users@vger.kernel.org \
--cc=mark.rutland@arm.com \
--cc=mingo@redhat.com \
--cc=mpetlan@redhat.com \
--cc=namhyung@kernel.org \
--cc=peterz@infradead.org \
--cc=tmricht@linux.ibm.com \
--cc=vmolnaro@redhat.com \
--cc=weilin.wang@intel.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 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.