From: Zane Leung <liangzhen@linux.spacemit.com>
To: gregkh@linuxfoundation.org, palmer@dabbelt.com, pjw@kernel.org,
alexander.shishkin@linux.intel.com, suzuki.poulose@arm.com,
mchitale@gmail.com, anup@brainfault.org, irogers@google.com
Cc: mike.leach@arm.com, james.clark@linaro.org, leo.yan@arm.com,
peterz@infradead.org, mingo@redhat.com, namhyung@kernel.org,
mark.rutland@arm.com, jolsa@kernel.org, adrian.hunter@intel.com,
alex@ghiti.fr, atish.patra@linux.dev,
andrew.jones@oss.qualcomm.com, sunilvl@oss.qualcomm.com,
liangzhen@linux.spacemit.com, zhuangqiubin@linux.spacemit.com,
coresight@lists.linaro.org, linux-riscv@lists.infradead.org,
linux-kernel@vger.kernel.org
Subject: [PATCH 23/24] perf tools: gtrace: Record and parse trace encoder metadata
Date: Fri, 23 Oct 2026 11:15:04 +0800 [thread overview]
Message-ID: <20261023031505.2020925-24-liangzhen@linux.spacemit.com> (raw)
In-Reply-To: <20261023031505.2020925-1-liangzhen@linux.spacemit.com>
Collect the read-only trace encoder control, features and timestamp
registers per CPU into the AUXTRACE_INFO metadata and validate the
requested context, syncmode, syncmax and timestamp options against the
hardware before recording. Parse the metadata back in the decoder.
Signed-off-by: Zane Leung <liangzhen@linux.spacemit.com>
---
tools/perf/arch/riscv/util/auxtrace.c | 203 +++++++++++++++++++++++++-
tools/perf/util/gtrace-decoder.c | 89 ++++++++++-
tools/perf/util/gtrace.h | 30 +++-
3 files changed, 311 insertions(+), 11 deletions(-)
diff --git a/tools/perf/arch/riscv/util/auxtrace.c b/tools/perf/arch/riscv/util/auxtrace.c
index 882632511d09..c11c5c34cbc8 100644
--- a/tools/perf/arch/riscv/util/auxtrace.c
+++ b/tools/perf/arch/riscv/util/auxtrace.c
@@ -9,6 +9,7 @@
#include <linux/bitops.h>
#include <linux/log2.h>
#include <linux/zalloc.h>
+#include <linux/bitfield.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
@@ -39,28 +40,217 @@ struct rvtrace_recording {
struct evlist *evlist;
};
+static const char * const metadata_encoder_ro[] = {
+ [RVTRACE_ENCODER_FORMAT] = "control/format",
+ [RVTRACE_ENCODER_CONTEXT] = "control/has_context",
+ [RVTRACE_ENCODER_SRCBITS] = "features/srcbits",
+ [RVTRACE_ENCODER_TIMESTAMP] = "timestamp/source",
+};
+
static size_t rvtrace_info_priv_size(struct auxtrace_record *itr __maybe_unused,
struct evlist *evlist __maybe_unused)
{
- return GTRACE_AUXTRACE_PRIV_SIZE;
+ int encoder;
+ struct perf_cpu_map *event_cpus = evlist->core.user_requested_cpus;
+ struct perf_cpu_map *intersect_cpus;
+
+ if (!perf_cpu_map__has_any_cpu(event_cpus)) {
+ /* cpu map is not "any" CPU , we have specific CPUs to work with */
+ struct perf_cpu_map *online_cpus = perf_cpu_map__new_online_cpus();
+
+ intersect_cpus = perf_cpu_map__intersect(event_cpus, online_cpus);
+ perf_cpu_map__put(online_cpus);
+ } else {
+ /* Event can be "any" CPU so count all online CPUs. */
+ intersect_cpus = perf_cpu_map__new_online_cpus();
+ }
+
+ encoder = perf_cpu_map__nr(intersect_cpus);
+ perf_cpu_map__put(intersect_cpus);
+
+ return (GTRACE_HEADER_SIZE + encoder * RVTRACE_ENCODER_PRIV_SIZE);
+
+}
+
+static bool gtrace_pmu_path_exists(struct perf_pmu *pmu, struct perf_cpu cpu, const char *path)
+{
+ char pmu_path[PATH_MAX];
+
+ /* Get RO metadata from sysfs */
+ snprintf(pmu_path, PATH_MAX, "cpu%d/%s", cpu.cpu, path);
+
+ return perf_pmu__file_exists(pmu, pmu_path);
+}
+
+static int rvtrace_get_ro(struct perf_pmu *pmu, struct perf_cpu cpu, const char *path, __u64 *val)
+{
+ char pmu_path[PATH_MAX];
+ int scan;
+
+ /* Get RO metadata from sysfs */
+ snprintf(pmu_path, PATH_MAX, "cpu%d/%s", cpu.cpu, path);
+
+ scan = perf_pmu__scan_file(pmu, pmu_path, "%llx", val);
+ if (scan != 1) {
+ pr_err("%s: error reading: %s\n", __func__, pmu_path);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void rvtrace_get_metadata(struct perf_cpu cpu, u32 *offset,
+ struct auxtrace_record *itr,
+ struct perf_record_auxtrace_info *info)
+{
+ struct rvtrace_recording *ptr = container_of(itr, struct rvtrace_recording, itr);
+ struct perf_pmu *rvtrace_pmu = ptr->rvtrace_pmu;
+ bool timestamp_exists;
+
+ info->priv[*offset + RVTRACE_ENCODER_CPU] = cpu.cpu;
+ info->priv[*offset + RVTRACE_ENCODER_NR_TRC_PARAMS] = RVTRACE_ENCODER_NR_TRC_PARAMS_LENGTH;
+
+ /* Get read-only information from sysFS */
+ rvtrace_get_ro(rvtrace_pmu, cpu, metadata_encoder_ro[RVTRACE_ENCODER_FORMAT],
+ &info->priv[*offset + RVTRACE_ENCODER_FORMAT]);
+ rvtrace_get_ro(rvtrace_pmu, cpu, metadata_encoder_ro[RVTRACE_ENCODER_CONTEXT],
+ &info->priv[*offset + RVTRACE_ENCODER_CONTEXT]);
+ rvtrace_get_ro(rvtrace_pmu, cpu, metadata_encoder_ro[RVTRACE_ENCODER_SRCBITS],
+ &info->priv[*offset + RVTRACE_ENCODER_SRCBITS]);
+
+ timestamp_exists = gtrace_pmu_path_exists(rvtrace_pmu, cpu,
+ metadata_encoder_ro[RVTRACE_ENCODER_TIMESTAMP]);
+ info->priv[*offset + RVTRACE_ENCODER_TIMESTAMP] = timestamp_exists ? 1 : 0;
+
+ /* Where the next CPU entry should start from */
+ *offset += RVTRACE_ENCODER_PRIV_MAX;
}
static int rvtrace_info_fill(struct auxtrace_record *itr,
struct perf_session *session __maybe_unused,
struct perf_record_auxtrace_info *auxtrace_info, size_t priv_size)
{
+ unsigned int i;
+ u32 offset;
+ u64 nr_cpu, type;
+ struct perf_cpu_map *cpu_map;
+ struct perf_cpu_map *event_cpus = evlist__core(session->evlist)->user_requested_cpus;
+ struct perf_cpu_map *online_cpus = perf_cpu_map__new_online_cpus();
struct rvtrace_recording *ptr = container_of(itr, struct rvtrace_recording, itr);
struct perf_pmu *rvtrace_pmu = ptr->rvtrace_pmu;
+ struct perf_cpu cpu;
+
+ if (priv_size != rvtrace_info_priv_size(itr, session->evlist))
+ return -EINVAL;
- if (priv_size != GTRACE_AUXTRACE_PRIV_SIZE)
+ if (!evlist__core(session->evlist)->nr_mmaps)
return -EINVAL;
+ if (perf_cpu_map__has_any_cpu(event_cpus)) {
+ cpu_map = online_cpus;
+ } else {
+ perf_cpu_map__for_each_cpu(cpu, i, event_cpus) {
+ if (!perf_cpu_map__has(online_cpus, cpu))
+ return -EINVAL;
+ }
+
+ cpu_map = event_cpus;
+ }
+
+ nr_cpu = perf_cpu_map__nr(cpu_map);
+ type = rvtrace_pmu->type;
+
auxtrace_info->type = PERF_AUXTRACE_GTRACE;
- auxtrace_info->priv[0] = rvtrace_pmu->type;
+ auxtrace_info->priv[GTRACE_PMU_TYPE_CPUS] = type << 32;
+ auxtrace_info->priv[GTRACE_PMU_TYPE_CPUS] |= nr_cpu;
+
+ offset = GTRACE_HEADER_MAX;
+
+ perf_cpu_map__for_each_cpu(cpu, i, cpu_map) {
+ assert(offset < priv_size);
+ rvtrace_get_metadata(cpu, &offset, itr, auxtrace_info);
+ }
+
+ perf_cpu_map__put(online_cpus);
+
+ return 0;
+}
+
+static int rvtrace_encoder_validate_context(struct perf_pmu *rvtrace_pmu, struct evsel *evsel,
+ struct perf_cpu cpu)
+{
+ int err;
+ u64 ctxt;
+ __u64 context;
+
+ evsel__get_config_val(evsel, "context", &ctxt);
+
+ if (!ctxt)
+ return 0;
+
+ err = rvtrace_get_ro(rvtrace_pmu, cpu,
+ metadata_encoder_ro[RVTRACE_ENCODER_CONTEXT], &context);
+ if (err)
+ return err;
+
+ if (!context) {
+ pr_err("context not supported");
+ return -EINVAL;
+ }
return 0;
}
+static int rvtrace_encoder_validate_timestamp(struct perf_pmu *rvtrace_pmu, struct evsel *evsel,
+ struct perf_cpu cpu)
+{
+ u64 ts;
+
+ evsel__get_config_val(evsel, "timestamp", &ts);
+
+ if (!ts)
+ return 0;
+
+ if (!gtrace_pmu_path_exists(rvtrace_pmu, cpu,
+ metadata_encoder_ro[RVTRACE_ENCODER_TIMESTAMP])) {
+ pr_err("timestamp not supported");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rvtrace_validate_config(struct perf_pmu *rvtrace_pmu, struct evsel *evsel)
+{
+ unsigned int idx;
+ int err = 0;
+ struct perf_cpu_map *event_cpus = evlist__core(evsel->evlist)->user_requested_cpus;
+ struct perf_cpu_map *intersect_cpus;
+ struct perf_cpu cpu;
+
+ if (!perf_cpu_map__has_any_cpu(event_cpus)) {
+ struct perf_cpu_map *online_cpus = perf_cpu_map__new_online_cpus();
+
+ intersect_cpus = perf_cpu_map__intersect(event_cpus, online_cpus);
+ perf_cpu_map__put(online_cpus);
+ } else {
+ intersect_cpus = perf_cpu_map__new_online_cpus();
+ }
+
+ perf_cpu_map__for_each_cpu_skip_any(cpu, idx, intersect_cpus) {
+ err = rvtrace_encoder_validate_context(rvtrace_pmu, evsel, cpu);
+ if (err)
+ break;
+
+ err = rvtrace_encoder_validate_timestamp(rvtrace_pmu, evsel, cpu);
+ if (err)
+ break;
+ }
+
+ perf_cpu_map__put(intersect_cpus);
+ return err;
+}
+
static int rvtrace_set_auxtrace_mmap_page(struct record_opts *opts)
{
bool privileged = perf_event_paranoid_check(-1);
@@ -127,6 +317,9 @@ static int rvtrace_recording_options(struct auxtrace_record *itr, struct evlist
evlist__to_front(evlist, rvtrace_evsel);
evsel__set_sample_bit(rvtrace_evsel, TIME);
+ if (opts->sample_time_set)
+ evsel__set_config_if_unset(rvtrace_evsel, "timestamp", 1);
+
/* Add dummy event to keep tracking */
err = parse_event(evlist, "dummy:u");
if (err)
@@ -139,6 +332,10 @@ static int rvtrace_recording_options(struct auxtrace_record *itr, struct evlist
tracking_evsel->core.attr.sample_period = 1;
evsel__set_sample_bit(tracking_evsel, TIME);
+ err = rvtrace_validate_config(rvtrace_pmu, rvtrace_evsel);
+ if (err)
+ return err;
+
return 0;
}
diff --git a/tools/perf/util/gtrace-decoder.c b/tools/perf/util/gtrace-decoder.c
index c25fbfc61928..d70cb7cd399f 100644
--- a/tools/perf/util/gtrace-decoder.c
+++ b/tools/perf/util/gtrace-decoder.c
@@ -12,6 +12,7 @@
#include "evlist.h"
#include "session.h"
#include "gtrace.h"
+#include "debug.h"
struct gtrace_decoder {
struct auxtrace auxtrace;
@@ -19,6 +20,8 @@ struct gtrace_decoder {
struct perf_session *session;
struct machine *machine;
u32 pmu_type;
+ int num_cpu;
+ u64 **metadata;
};
static int gtrace_process_event(struct perf_session *session __maybe_unused,
@@ -52,7 +55,10 @@ static void gtrace_free(struct perf_session *session)
auxtrace);
session->auxtrace = NULL;
- free(ptr);
+ for (int i = 0; i < ptr->num_cpu; i++)
+ zfree(&ptr->metadata[i]);
+ zfree(&ptr->metadata);
+ zfree(&ptr);
}
static bool gtrace_evsel_is_auxtrace(struct perf_session *session,
@@ -64,24 +70,88 @@ static bool gtrace_evsel_is_auxtrace(struct perf_session *session,
return evsel->core.attr.type == ptr->pmu_type;
}
+static const char * const gtrace_global_header_fmts[] = {
+ [GTRACE_PMU_TYPE_CPUS] = " PMU type/num cpus %llx\n",
+};
+
+static const char * const rvtrace_encoder_priv_fmts[] = {
+ [RVTRACE_ENCODER_CPU] = " CPU %lld\n",
+ [RVTRACE_ENCODER_NR_TRC_PARAMS] = " NR_TRC_PARAMS %lld\n",
+ [RVTRACE_ENCODER_FORMAT] = " FORMAT %lld\n",
+ [RVTRACE_ENCODER_CONTEXT] = " CONTEXT %lld\n",
+ [RVTRACE_ENCODER_SRCBITS] = " SRCBITS %lld\n",
+ [RVTRACE_ENCODER_TIMESTAMP] = " TIMESTAMP %lld\n",
+};
+
+static void gtrace_print_auxtrace_info(u64 *val, int num_cpu)
+{
+ int i, j, cpu = 0, nr_params = 0, fmt_offset = 0;
+
+ for (i = 0; i < GTRACE_HEADER_MAX; i++)
+ fprintf(stdout, gtrace_global_header_fmts[i], val[i]);
+
+ for (i = GTRACE_HEADER_MAX; cpu < num_cpu; cpu++) {
+ fprintf(stdout, rvtrace_encoder_priv_fmts[RVTRACE_ENCODER_CPU], val[i++]);
+ nr_params = val[i++];
+ fmt_offset = RVTRACE_ENCODER_FORMAT;
+ for (j = fmt_offset; j < nr_params + fmt_offset; j++, i++)
+ fprintf(stdout, rvtrace_encoder_priv_fmts[j], val[i]);
+ }
+}
+
int gtrace__process_auxtrace_info(union perf_event *event,
struct perf_session *session)
{
struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info;
struct gtrace_decoder *ptr;
-
- if (auxtrace_info->header.size < GTRACE_AUXTRACE_PRIV_SIZE +
- sizeof(struct perf_record_auxtrace_info))
+ u64 *priv;
+ u64 **metadata;
+ int num_cpu;
+ int priv_size;
+ int err;
+ int i;
+
+ priv = (u64 *) auxtrace_info->priv;
+ num_cpu = priv[GTRACE_PMU_TYPE_CPUS] & 0xffffffff;
+ if (dump_trace)
+ gtrace_print_auxtrace_info(priv, num_cpu);
+
+ priv_size = auxtrace_info->header.size - sizeof(struct perf_event_header) -
+ INFO_HEADER_SIZE - GTRACE_HEADER_SIZE;
+ if (num_cpu <= 0 || priv_size <= 0 ||
+ num_cpu > priv_size / (int)sizeof(u64))
return -EINVAL;
- ptr = zalloc(sizeof(*ptr));
- if (!ptr)
+ metadata = zalloc(sizeof(*metadata) * num_cpu);
+ if (!metadata)
return -ENOMEM;
+ i = GTRACE_HEADER_SIZE;
+
+ for (int j = 0; j < num_cpu; j++) {
+ metadata[j] = zalloc(sizeof(*metadata[j]) * RVTRACE_ENCODER_PRIV_MAX);
+ if (!metadata[j]) {
+ err = -ENOMEM;
+ goto err_free_metadata;
+ }
+
+ for (int k = 0; k < RVTRACE_ENCODER_PRIV_MAX; k++)
+ metadata[j][k] = priv[i + k];
+ i += RVTRACE_ENCODER_PRIV_MAX;
+ }
+
+ ptr = zalloc(sizeof(*ptr));
+ if (!ptr) {
+ err = -ENOMEM;
+ goto err_free_metadata;
+ }
+
ptr->session = session;
ptr->machine = &session->machines.host;
ptr->auxtrace_type = auxtrace_info->type;
- ptr->pmu_type = auxtrace_info->priv[0];
+ ptr->pmu_type = (unsigned int) ((priv[GTRACE_PMU_TYPE_CPUS] >> 32) & 0xffffffff);
+ ptr->num_cpu = num_cpu;
+ ptr->metadata = metadata;
ptr->auxtrace.process_event = gtrace_process_event;
ptr->auxtrace.process_auxtrace_event = gtrace_process_auxtrace_event;
@@ -92,4 +162,9 @@ int gtrace__process_auxtrace_info(union perf_event *event,
session->auxtrace = &ptr->auxtrace;
return 0;
+
+err_free_metadata:
+ for (int j = 0; j < num_cpu; j++)
+ zfree(&metadata);
+ return err;
}
diff --git a/tools/perf/util/gtrace.h b/tools/perf/util/gtrace.h
index 02db4f2ff9f5..76006e30e7c3 100644
--- a/tools/perf/util/gtrace.h
+++ b/tools/perf/util/gtrace.h
@@ -11,7 +11,35 @@
struct perf_session;
-#define GTRACE_AUXTRACE_PRIV_SIZE sizeof(u64)
+/* Supported trace protocol formats */
+enum gtrace_format {
+ GTRACE_FORMAT_ETRACE,
+ GTRACE_FORMAT_NTRACE,
+};
+
+enum {
+ GTRACE_PMU_TYPE_CPUS,
+ GTRACE_HEADER_MAX,
+};
+
+/* RISC-V Trace Encoder metadata */
+enum {
+ RVTRACE_ENCODER_CPU,
+ RVTRACE_ENCODER_NR_TRC_PARAMS,
+ RVTRACE_ENCODER_FORMAT,
+ RVTRACE_ENCODER_CONTEXT,
+ RVTRACE_ENCODER_SRCBITS,
+ RVTRACE_ENCODER_TIMESTAMP,
+ RVTRACE_ENCODER_PRIV_MAX,
+};
+
+#define RVTRACE_ENCODER_NR_TRC_PARAMS_LENGTH (RVTRACE_ENCODER_PRIV_MAX - RVTRACE_ENCODER_FORMAT)
+
+#define GTRACE_HEADER_SIZE (GTRACE_HEADER_MAX * sizeof(u64))
+#define RVTRACE_ENCODER_PRIV_SIZE (RVTRACE_ENCODER_PRIV_MAX * sizeof(u64))
+
+#define INFO_HEADER_SIZE (sizeof(((struct perf_record_auxtrace_info *)0)->type) + \
+ sizeof(((struct perf_record_auxtrace_info *)0)->reserved__))
int gtrace__process_auxtrace_info(union perf_event *event, struct perf_session *session);
#endif
--
2.34.1
next prev parent reply other threads:[~2026-10-23 3:17 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-23 3:14 [RFC PATCH 00/24] Add Linux RISC-V trace support via the gtrace framework Zane Leung
2026-10-23 3:14 ` [PATCH 01/24] gtrace: Move connection release into component unregister Zane Leung
2026-10-23 3:14 ` [PATCH 02/24] gtrace: Initial implementation of gtrace sysfs framework Zane Leung
2026-10-23 3:14 ` [PATCH 03/24] gtrace: Drop trace format field and helper function Zane Leung
2026-10-23 3:14 ` [PATCH 04/24] gtrace: Add RISC-V trace encoder sysfs support Zane Leung
2026-10-23 3:14 ` [PATCH 05/24] gtrace: Introduce some PMU format attributes Zane Leung
2026-10-23 3:14 ` [PATCH 06/24] gtrace: Replace owner PID with perf_event pointer in gtrace_path Zane Leung
2026-10-23 3:14 ` [PATCH 07/24] gtrace: Parse RISC-V Trace encoder config from perf event Zane Leung
2026-10-23 3:14 ` [PATCH 08/24] gtrace: add perf symlink support for gtrace components Zane Leung
2026-10-23 3:14 ` [PATCH 09/24] gtrace: Add port-level start/stop driver callbacks Zane Leung
2026-10-23 3:14 ` [PATCH 10/24] gtrace: Add RISC-V trace funnel driver Zane Leung
2026-10-23 3:14 ` [PATCH 11/24] gtrace: Add RISC-V trace timestamp support Zane Leung
2026-10-23 3:14 ` [PATCH 12/24] gtrace: Add RISC-V trace ATB bridge driver Zane Leung
2026-10-23 3:14 ` [PATCH 13/24] gtrace: Add CoreSight platform core APIs Zane Leung
2026-10-23 3:14 ` [PATCH 14/24] gtrace: Add CoreSight funnel driver Zane Leung
2026-10-23 3:14 ` [PATCH 15/24] gtrace: Only copy AUX data from the path sink Zane Leung
2026-10-23 3:14 ` [PATCH 16/24] perf: Flag whether gtrace AUX data carries CoreSight framing Zane Leung
2026-10-23 3:14 ` [PATCH 17/24] gtrace: Unify default sink selection across CPUs in perf setup Zane Leung
2026-10-23 3:14 ` [PATCH 18/24] gtrace: Add sink-specific buffer alloc/free callbacks Zane Leung
2026-10-23 3:15 ` [PATCH 19/24] gtrace: Add setup_sysfs_buf callback to setup sysfs sink buffer Zane Leung
2026-10-23 3:15 ` [PATCH 20/24] gtrace: Add CoreSight TMC driver support Zane Leung
2026-10-23 3:15 ` [PATCH 21/24] perf tools: riscv: making gtrace PMU listable Zane Leung
2026-10-23 3:15 ` [PATCH 22/24] perf tools: Add Nexus RISC-V Trace decoder Zane Leung
2026-10-23 3:15 ` Zane Leung [this message]
2026-10-23 3:15 ` [PATCH 24/24] perf tools: gtrace: Add AUX trace decoder and branch sample synthesis Zane Leung
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=20261023031505.2020925-24-liangzhen@linux.spacemit.com \
--to=liangzhen@linux.spacemit.com \
--cc=adrian.hunter@intel.com \
--cc=alex@ghiti.fr \
--cc=alexander.shishkin@linux.intel.com \
--cc=andrew.jones@oss.qualcomm.com \
--cc=anup@brainfault.org \
--cc=atish.patra@linux.dev \
--cc=coresight@lists.linaro.org \
--cc=gregkh@linuxfoundation.org \
--cc=irogers@google.com \
--cc=james.clark@linaro.org \
--cc=jolsa@kernel.org \
--cc=leo.yan@arm.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-riscv@lists.infradead.org \
--cc=mark.rutland@arm.com \
--cc=mchitale@gmail.com \
--cc=mike.leach@arm.com \
--cc=mingo@redhat.com \
--cc=namhyung@kernel.org \
--cc=palmer@dabbelt.com \
--cc=peterz@infradead.org \
--cc=pjw@kernel.org \
--cc=sunilvl@oss.qualcomm.com \
--cc=suzuki.poulose@arm.com \
--cc=zhuangqiubin@linux.spacemit.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
all inboxes | Powered by JetHome®