Linux Perf Users
 help / color / mirror / Atom feed
From: James Clark <james.clark@linaro.org>
To: Amir Ayupov <aaupov@fb.com>
Cc: linux-doc@vger.kernel.org, Mike Leach <mike.leach@arm.com>,
	Jonathan Corbet <corbet@lwn.net>,
	Shuah Khan <skhan@linuxfoundation.org>,
	Swapnil Sapkal <swapnil.sapkal@amd.com>,
	linux-perf-users@vger.kernel.org, coresight@lists.linaro.org,
	linux-arm-kernel@lists.infradead.org,
	Suzuki K Poulose <suzuki.poulose@arm.com>,
	Leo Yan <leo.yan@arm.com>, Peter Zijlstra <peterz@infradead.org>,
	Ingo Molnar <mingo@redhat.com>,
	Arnaldo Carvalho de Melo <acme@kernel.org>,
	Namhyung Kim <namhyung@kernel.org>,
	Mark Rutland <mark.rutland@arm.com>,
	Alexander Shishkin <alexander.shishkin@linux.intel.com>,
	Jiri Olsa <jolsa@kernel.org>, Ian Rogers <irogers@google.com>,
	Adrian Hunter <adrian.hunter@intel.com>,
	John Garry <john.g.garry@oracle.com>,
	Will Deacon <will@kernel.org>
Subject: Re: [PATCH v2 3/5] perf cs-etm: Add branch history to existing samples
Date: Tue, 18 Aug 2026 16:13:09 +0100	[thread overview]
Message-ID: <3113c709-4e4c-40ea-9850-91073a519e90@linaro.org> (raw)
In-Reply-To: <f40e4f98-d6eb-44d6-8229-0afdfcc0caf6@linaro.org>



On 18/08/2026 16:09, James Clark wrote:
> 
> 
> On 17/08/2026 23:22, Amir Ayupov wrote:
>> Implement --itrace=L for CoreSight ETM: decode timestamped trace up to
>> each existing PMU sample and attach the branch history that led to it.
>> The sample keeps its own ip, callchain and event identity, and a sample
>> that already carries a branch stack is left alone.
>>
>> Samples are correlated with the trace by time, so this requires virtual
>> ETM timestamps that are correlated to perf time; timeless decoding is
>> rejected. The decode loop, which the previous patch left on its own in
>> cs_etm__process_timestamped_queues(), grows a timestamp argument and
>> stops once the decode frontier reaches it, so on return the
>> thread stack holds the branches that executed before the sample and none
>> that executed after. Attaching then reduces to the same
>> thread_stack__br_sample_late() call intel-pt uses.
>>
>> No explicit sample-to-queue matching is needed:
>> thread_stack__br_sample_late() keys on the thread, and the thread stack
>> is already emptied whenever the decoder reports a discontinuity. The one
>> case that was not covered is a queue whose trace runs out: flush the
>> thread stack there too, otherwise samples recorded after the last trace
>> would pick up stale history.
>>
>> Take the branch history when attaching it rather than leaving it in the
>> thread stack. With AUX pause and resume, a pause sample ends a completed
>> trace window and that window belongs to the sample. Execution while AUX
>> is paused is not traced, so retaining the window would let a later sample
>> reuse branches from before the untraced gap. Consuming it ensures that a
>> sample with no newly decoded trace gets an empty branch stack instead.
>>
>> As with intel-pt, the internal reconstruction ring is kept deeper than
>> the requested output depth to cover branches decoded between the sampled
>> ip and the point at which the sample time was recorded, so --itrace=L<n>
>> can actually return n entries. Kernel-inclusive trace gets the same
>> conservative 1024-entry headroom that intel-pt uses.
>>
>> Assisted-by: Devmate:GPT-5.6
>> Signed-off-by: Amir Ayupov <aaupov@fb.com>
>> ---
>>   tools/perf/util/cs-etm.c       | 185 +++++++++++++++++++++++++++++++--
>>   tools/perf/util/thread-stack.c |  17 +++
>>   tools/perf/util/thread-stack.h |   1 +
>>   3 files changed, 195 insertions(+), 8 deletions(-)
>>
>> diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
>> index 4d895f11deb7f..00407a80933e1 100644
>> --- a/tools/perf/util/cs-etm.c
>> +++ b/tools/perf/util/cs-etm.c
>> @@ -72,6 +72,11 @@ struct cs_etm_auxtrace {
>>       bool use_callchain;
>>       int num_cpu;
>> +    /* Output depth requested with --itrace=L<n> */
>> +    unsigned int br_stack_sz;
>> +    /* Internal reconstruction depth, see cs_etm__br_stack_init() */
>> +    unsigned int br_stack_sz_plus;> +    struct branch_stack *br_stack;
>>       u64 latest_kernel_timestamp;
>>       u32 auxtrace_type;
>>       u32 branches_filter;
>> @@ -91,6 +96,7 @@ struct cs_etm_traceid_queue {
>>       u64 kernel_start;
>>       union perf_event *event_buf;
>>       unsigned int br_stack_sz;
>> +    unsigned int br_stack_sz_plus;
>>       struct branch_stack *last_branch;
>>       struct ip_callchain *callchain;
>>       struct cs_etm_packet *prev_packet;
>> @@ -141,7 +147,8 @@ struct cs_etm_queue {
>>   };
>>   static int cs_etm__update_queues(struct cs_etm_auxtrace *etm);
>> -static int cs_etm__process_timestamped_queues(struct cs_etm_auxtrace 
>> *etm);
>> +static int cs_etm__process_timestamped_queues(struct cs_etm_auxtrace 
>> *etm,
>> +                          u64 timestamp);
>>   static int cs_etm__flush_timestamped_queues(struct cs_etm_auxtrace 
>> *etm);
>>   static int cs_etm__process_timeless_queues(struct cs_etm_auxtrace *etm,
>>                          pid_t tid);
>> @@ -165,6 +172,7 @@ static int cs_etm__metadata_set_trace_id(u8 
>> trace_chan_id, u64 *cpu_metadata);
>>   #define TO_QUEUE_NR(cs_queue_nr) (cs_queue_nr >> 16)
>>   #define TO_TRACE_CHAN_ID(cs_queue_nr) (cs_queue_nr & 0x0000ffff)
>>   #define SINK_UNSET ((u32) -1)
>> +#define MAX_TIMESTAMP (~0ULL)
>>   static u32 cs_etm__get_v7_protocol_version(u32 etmidr)
>>   {
>> @@ -674,7 +682,8 @@ static int cs_etm__init_traceid_queue(struct 
>> cs_etm_queue *etmq,
>>           if (!tidq->last_branch)
>>               goto out_free;
>> -        tidq->br_stack_sz = etm->synth_opts.last_branch_sz;
>> +        tidq->br_stack_sz = etm->br_stack_sz;
>> +        tidq->br_stack_sz_plus = etm->br_stack_sz_plus;
>>       }
>>       if (etm->synth_opts.callchain) {
>> @@ -794,7 +803,7 @@ static void cs_etm__packet_swap(struct 
>> cs_etm_auxtrace *etm,
>>       struct cs_etm_packet *tmp;
>>       if (etm->synth_opts.branches || etm->synth_opts.last_branch ||
>> -        etm->synth_opts.instructions) {
>> +        etm->synth_opts.add_last_branch || etm- 
>> >synth_opts.instructions) {
>>           /*
>>            * Swap PACKET with PREV_PACKET: PACKET becomes PREV_PACKET for
>>            * the next incoming packet.
>> @@ -963,7 +972,7 @@ static int cs_etm__flush_events(struct 
>> perf_session *session,
>>       if (ret)
>>           return ret;
>> -    ret = cs_etm__process_timestamped_queues(etm);
>> +    ret = cs_etm__process_timestamped_queues(etm, MAX_TIMESTAMP);
>>       if (ret)
>>           return ret;
>> @@ -1060,6 +1069,7 @@ static void cs_etm__free(struct perf_session 
>> *session)
>>           zfree(&aux->metadata[i]);
>>       zfree(&aux->metadata);
>> +    zfree(&aux->br_stack);
>>       zfree(&aux);
>>   }
>> @@ -1597,7 +1607,8 @@ static void cs_etm__add_stack_event(struct 
>> cs_etm_queue *etmq,
>>       u64 from, to;
>>       int size;
>> -    if (!etm->synth_opts.branches && !etm->synth_opts.instructions)
>> +    if (!etm->synth_opts.branches && !etm->synth_opts.instructions &&
>> +        !etm->synth_opts.add_last_branch)
>>           return;
>>       if (!cs_etm__packet_has_taken_branch(tidq->prev_packet))
>> @@ -1614,7 +1625,7 @@ static void cs_etm__add_stack_event(struct 
>> cs_etm_queue *etmq,
>>                       tidq->prev_packet->flags, from, to, size,
>>                       etmq->buffer->buffer_nr + 1,
>>                       etmq->etm->use_callchain,
>> -                    tidq->br_stack_sz, 0);
>> +                    tidq->br_stack_sz_plus, 0);
>>       } else {
>>           thread_stack__set_trace_nr(tidq->frontend_thread,
>>                          tidq->prev_packet->cpu,
>> @@ -2817,7 +2828,8 @@ static int cs_etm__update_queues(struct 
>> cs_etm_auxtrace *etm)
>>       return ret;
>>   }
>> -static int cs_etm__process_timestamped_queues(struct cs_etm_auxtrace 
>> *etm)
>> +static int cs_etm__process_timestamped_queues(struct cs_etm_auxtrace 
>> *etm,
>> +                          u64 timestamp)
>>   {
>>       int ret = 0;
>>       unsigned int cs_queue_nr, queue_nr;
>> @@ -2831,6 +2843,9 @@ static int 
>> cs_etm__process_timestamped_queues(struct cs_etm_auxtrace *etm)
>>           if (!etm->heap.heap_cnt)
>>               break;
>> +        if (etm->heap.heap_array[0].ordinal >= timestamp)
>> +            break;
>> +
>>           /* Take the entry at the top of the min heap */
>>           cs_queue_nr = etm->heap.heap_array[0].queue_nr;
>>           queue_nr = TO_QUEUE_NR(cs_queue_nr);
>> @@ -2878,8 +2893,25 @@ static int 
>> cs_etm__process_timestamped_queues(struct cs_etm_auxtrace *etm)
>>            * No more auxtrace_buffers to process in this etmq, simply
>>            * move on to another entry in the auxtrace_heap.
>>            */
>> -        if (!ret)
>> +        if (!ret) {
>> +            /*
>> +             * The trace for this physical queue is exhausted. Drop
>> +             * branch history for every trace ID it carried so that
>> +             * samples arriving later cannot pick up entries decoded
>> +             * before the gap.
>> +             */
>> +            if (etm->synth_opts.add_last_branch) {
>> +                struct int_node *inode;
>> +
>> +                intlist__for_each_entry(inode, etmq- 
>> >traceid_queues_list) {
>> +                    int idx = (int)(intptr_t)inode->priv;
>> +
>> +                    tidq = etmq->traceid_queues[idx];
>> +                    thread_stack__flush(tidq->frontend_thread);
>> +                }
>> +            }
>>               continue;
>> +        }
>>           ret = cs_etm__decode_data_block(etmq);
>>           if (ret)
>> @@ -3011,6 +3043,116 @@ static int 
>> cs_etm__process_switch_cpu_wide(struct cs_etm_auxtrace *etm,
>>       return 0;
>>   }
>> +static bool cs_etm__tracing_kernel(struct cs_etm_auxtrace *etm,
>> +                   struct perf_session *session)
>> +{
>> +    struct evsel *evsel;
>> +
>> +    evlist__for_each_entry(session->evlist, evsel) {
>> +        if (evsel->core.attr.type == etm->pmu_type &&
>> +            !evsel->core.attr.exclude_kernel)
>> +            return true;
>> +    }
>> +
>> +    return false;
>> +}
>> +
>> +static int cs_etm__br_stack_init(struct cs_etm_auxtrace *etm,
>> +                 struct perf_session *session)
>> +{
>> +    struct evsel *evsel;
>> +
>> +    evlist__for_each_entry(session->evlist, evsel) {
>> +        /*
>> +         * Only timestamped events can be matched against the decoded
>> +         * trace, so do not advertise a branch stack on any other.
>> +         */
>> +        if (!(evsel->core.attr.sample_type & PERF_SAMPLE_TIME))
>> +            continue;
> 
> Do you not also want to check for the coresight virtual timestamp option 
> here for the same reason? Although I do see that checked somewhere else 
> below.
> 
>> +        if (!(evsel->core.attr.sample_type & PERF_SAMPLE_BRANCH_STACK))
>> +            evsel->synth_sample_type |= PERF_SAMPLE_BRANCH_STACK;
>> +    }
>> +
>> +    /*
>> +     * Additional branch stack depth to cater for the branches decoded
>> +     * between the sampled ip and the point at which the sample time was
>> +     * recorded. Those are trimmed by thread_stack__br_sample_late(), so
> 
> How does the trimmer know what branches came after an IP? I could 
> understand trimming between two timestamps, but not between one IP and 
> one timestamp.
> 
>> +     * the extra depth keeps the requested output depth achievable. If
>> +     * kernel space is not traced, only the branch into the kernel needs
>> +     * to be accounted for.
>> +     */
> 
> I'm not sure if this description is missing something, but I can't 
> understand why this needs to be done. Or how kernel tracing affects it.
> 
>> +    if (cs_etm__tracing_kernel(etm, session))
>> +        etm->br_stack_sz_plus += 1024;
>> +    else
>> +        etm->br_stack_sz_plus += 1;
>> +
>> +    etm->br_stack = zalloc(sizeof(struct branch_stack) +
>> +                   etm->br_stack_sz * sizeof(struct branch_entry));
>> +    if (!etm->br_stack)
>> +        return -ENOMEM;
>> +
>> +    return 0;
>> +}
>> +
>> +/*
>> + * Add decoded branch history to an existing sample. The sample keeps 
>> its own
>> + * ip, callchain and event identity; only an absent branch stack is 
>> filled in.
>> + */
>> +static int cs_etm__process_sample(struct cs_etm_auxtrace *etm,
>> +                  struct perf_session *session,
>> +                  struct perf_sample *sample)
>> +{
>> +    struct machine *machine = &session->machines.host;
>> +    struct thread *thread;
>> +    int err;
>> +
>> +    if (!etm->synth_opts.add_last_branch || sample->branch_stack ||
>> +        !sample->ip || !sample->time || sample->time == (u64)-1)
>> +        return 0;
>> +
>> +    /* Adding branch history to existing samples supports the host 
>> only */
>> +    if (sample->cpumode == PERF_RECORD_MISC_GUEST_KERNEL ||
>> +        sample->cpumode == PERF_RECORD_MISC_GUEST_USER)
>> +        return 0;
>> +
>> +    err = cs_etm__update_queues(etm);
>> +    if (err)
>> +        return err;
>> +
>> +    /*
>> +     * Decode every queue up to this sample's time. Afterwards the 
>> thread
>> +     * stack holds the branches that executed before the sample, and
>> +     * nothing that executed after it.
>> +     */
>> +    err = cs_etm__process_timestamped_queues(etm, sample->time);
>> +    if (err)
>> +        return err;
>> +
>> +    thread = machine__findnew_thread(machine, sample->pid, sample->tid);
>> +    if (!thread)
>> +        return -ENOMEM;
>> +
>> +    /*
>> +     * Take the branch history rather than copying it. The trace window
>> +     * belongs to the sample that ends it, so once it has been 
>> attached a
>> +     * later sample with nothing newly decoded finds an empty stack 
>> rather
>> +     * than being given an earlier window's branches. That is the common
>> +     * case whenever the trace is duty cycled, by AUX pause/resume or by
>> +     * ETM strobing.
>> +     */
>> +    thread_stack__br_sample_late(thread, sample->cpu, etm->br_stack,
>> +                     etm->br_stack_sz, sample->ip,
>> +                     machine__kernel_start(machine));
>> +    thread_stack__br_stack_consume(thread, sample->cpu);
>> +
>> +    if (etm->br_stack->nr)
>> +        sample->branch_stack = etm->br_stack;
> 
> How does this work? We have a queue for each CPU, and decoding happens 
> in parallel (kind of), but when a Perf sample arrives we just attach the 
> stack from the global br_stack? Shouldn't we look at the CPU of the 
> sample and use cs_etm__get_queue() to get the right queue and branch stack?

And not even a queue for each CPU, but a queue for each CPU with set of 
queues/lists in there for each trace ID. So technically one CPU's trace 
could be in the traceID queue of a different CPU when sinks are shared.

Wouldn't you have to do a search to find the right branch stack?

> 
> Maybe it's not functionally different if br_stack always happens to be 
> set from the queue related to the last Perf sample, but it would be 
> nicer to not have to assume.
> 
> You might want to check "[PATCH 00/14] perf cs-etm: Per-thread mode 
> fixes and snapshot wrap support" because it changes to a per-CPU queue 
> even for per-thread mode which could help.
> 
>> +
>> +    thread__put(thread);
>> +
>> +    return 0;
>> +}
>> +
>>   static int cs_etm__process_event(struct perf_session *session,
>>                    union perf_event *event,
>>                    struct perf_sample *sample,
>> @@ -3049,6 +3191,9 @@ static int cs_etm__process_event(struct 
>> perf_session *session,
>>       case PERF_RECORD_SWITCH_CPU_WIDE:
>>           return cs_etm__process_switch_cpu_wide(etm, event);
>> +    case PERF_RECORD_SAMPLE:
>> +        return cs_etm__process_sample(etm, session, sample);
>> +
> 
> Don't we want to generalise this and process trace up to the timestamp 
> of _any_ event. Can we move the cs_etm__process_timestamped_queues() 
> call into cs_etm__process_event().
> 
> Surely we want to always decode up to any event if coresight virtual 
> timestamps are enabled? That way we access the right mmaps too and the 
> decode order doesn't change depending on the branch stack options.
> 
>>       case PERF_RECORD_AUX:
>>           /*
>>            * Record the latest kernel timestamp available in the header
>> @@ -3752,11 +3897,34 @@ int cs_etm__process_auxtrace_info_full(union 
>> perf_event *event,
>>       etm->use_thread_stack = etm->synth_opts.thread_stack ||
>>                   etm->synth_opts.last_branch ||
>> +                etm->synth_opts.add_last_branch ||
>>                   etm->synth_opts.callchain;
>>       etm->use_callchain = etm->synth_opts.thread_stack ||
>>                    etm->synth_opts.callchain;
>> +    if (etm->synth_opts.last_branch || etm- 
>> >synth_opts.add_last_branch) {
>> +        etm->br_stack_sz = etm->synth_opts.last_branch_sz;
>> +        etm->br_stack_sz_plus = etm->br_stack_sz;
>> +    }
>> +
>> +    if (etm->synth_opts.add_last_branch) {
>> +        /*
>> +         * Existing samples are matched to decoded trace by time, so
>> +         * the trace must carry timestamps that are correlated to perf
>> +         * time and the queues must be decoded in time order.
>> +         */
>> +        if (etm->timeless_decoding || !etm->has_virtual_ts) {
>> +            pr_err("CS ETM Trace: --itrace=L requires virtual 
>> timestamped trace\n");
>> +            err = -EINVAL;
>> +            goto err_free_queues;
>> +        }
>> +
>> +        err = cs_etm__br_stack_init(etm, session);
>> +        if (err)
>> +            goto err_free_queues;
>> +    }
>> +
>>       err = cs_etm__synth_events(etm, session);
>>       if (err)
>>           goto err_free_queues;
>> @@ -3812,6 +3980,7 @@ int cs_etm__process_auxtrace_info_full(union 
>> perf_event *event,
>>       auxtrace_queues__free(&etm->queues);
>>       session->auxtrace = NULL;
>>   err_free_etm:
>> +    zfree(&etm->br_stack);
>>       zfree(&etm);
>>   err_free_metadata:
>>       /* No need to check @metadata[j], free(NULL) is supported */
>> diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread- 
>> stack.c
>> index 1360f44421ef8..2713a2ad70b69 100644
>> --- a/tools/perf/util/thread-stack.c
>> +++ b/tools/perf/util/thread-stack.c
>> @@ -614,6 +614,23 @@ void thread_stack__sample_late(struct thread 
>> *thread, int cpu,
>>       }
>>   }
>> +/*
>> + * Branch history belongs to the sample that ends the trace window, so a
>> + * decoder that attaches it to an existing sample should take it 
>> rather than
>> + * copy it. A later sample with no newly decoded trace then finds an 
>> empty
>> + * branch stack instead of the previous window's branches.
>> + */
>> +void thread_stack__br_stack_consume(struct thread *thread, int cpu)
>> +{
>> +    struct thread_stack *ts = thread__stack(thread, cpu);
>> +
>> +    if (!ts || !ts->br_stack_rb)
>> +        return;
>> +
>> +    ts->br_stack_pos = 0;
>> +    ts->br_stack_rb->nr = 0;
>> +}
>> +
>>   void thread_stack__br_sample(struct thread *thread, int cpu,
>>                    struct branch_stack *dst, unsigned int sz)
>>   {
>> diff --git a/tools/perf/util/thread-stack.h b/tools/perf/util/thread- 
>> stack.h
>> index b3cd09beb62f0..2aec292bd1bcb 100644
>> --- a/tools/perf/util/thread-stack.h
>> +++ b/tools/perf/util/thread-stack.h
>> @@ -88,6 +88,7 @@ void thread_stack__sample(struct thread *thread, int 
>> cpu, struct ip_callchain *c
>>   void thread_stack__sample_late(struct thread *thread, int cpu,
>>                      struct ip_callchain *chain, size_t sz, u64 ip,
>>                      u64 kernel_start);
>> +void thread_stack__br_stack_consume(struct thread *thread, int cpu);
>>   void thread_stack__br_sample(struct thread *thread, int cpu,
>>                    struct branch_stack *dst, unsigned int sz);
>>   void thread_stack__br_sample_late(struct thread *thread, int cpu,
> 


  reply	other threads:[~2026-08-18 15:13 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-17 22:22 [PATCH v2 0/5] perf: Add CoreSight branch history to existing samples Amir Ayupov
2026-08-17 22:22 ` [PATCH v2 1/5] perf dlfilter: Add non-empty branch stack filter Amir Ayupov
2026-08-17 22:32   ` sashiko-bot
2026-08-24 10:10   ` Adrian Hunter
2026-08-17 22:22 ` [PATCH v2 2/5] perf cs-etm: Split up cs_etm__process_timestamped_queues() Amir Ayupov
2026-08-17 22:34   ` sashiko-bot
2026-08-18 14:38   ` James Clark
2026-08-17 22:22 ` [PATCH v2 3/5] perf cs-etm: Add branch history to existing samples Amir Ayupov
2026-08-17 22:41   ` sashiko-bot
2026-08-18 15:09   ` James Clark
2026-08-18 15:13     ` James Clark [this message]
2026-08-24 10:02   ` Adrian Hunter
2026-08-17 22:22 ` [PATCH v2 4/5] perf test cs-etm: Test branch history on " Amir Ayupov
2026-08-17 22:33   ` sashiko-bot
2026-08-18 14:24   ` James Clark
2026-08-17 22:22 ` [PATCH v2 5/5] Documentation: coresight: Document context-sensitive PGO workflow Amir Ayupov
2026-08-17 22:26   ` sashiko-bot
2026-08-18 13:56 ` [PATCH v2 0/5] perf: Add CoreSight branch history to existing samples James Clark

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=3113c709-4e4c-40ea-9850-91073a519e90@linaro.org \
    --to=james.clark@linaro.org \
    --cc=aaupov@fb.com \
    --cc=acme@kernel.org \
    --cc=adrian.hunter@intel.com \
    --cc=alexander.shishkin@linux.intel.com \
    --cc=corbet@lwn.net \
    --cc=coresight@lists.linaro.org \
    --cc=irogers@google.com \
    --cc=john.g.garry@oracle.com \
    --cc=jolsa@kernel.org \
    --cc=leo.yan@arm.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-perf-users@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=mike.leach@arm.com \
    --cc=mingo@redhat.com \
    --cc=namhyung@kernel.org \
    --cc=peterz@infradead.org \
    --cc=skhan@linuxfoundation.org \
    --cc=suzuki.poulose@arm.com \
    --cc=swapnil.sapkal@amd.com \
    --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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox