mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Swapnil Sapkal <swapnil.sapkal@amd.com>
To: Arnaldo Carvalho de Melo <acme@kernel.org>, Tianyi Chen <hi@tychen.cc>
Cc: Namhyung Kim <namhyung@kernel.org>, <irogers@google.com>,
	<ravi.bangoria@amd.com>, <linux-perf-users@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>
Subject: Re: [PATCH] perf sched stats: Reject mismatched or incomplete snapshots
Date: Mon, 28 Sep 2026 18:57:43 +0530	[thread overview]
Message-ID: <a7b96474-98b0-49b4-a524-89bda593275f@amd.com> (raw)
In-Reply-To: <arZyC1oiFA8bZ9Zc@x2>

Hi Arnaldo, Tianyi,

Sorry for the delay in response. I was on vacation last week.

On 25-09-2026 18:37, Arnaldo Carvalho de Melo wrote:
> On Thu, Sep 17, 2026 at 12:11:25AM +0800, Tianyi Chen wrote:
>> The before and after records are paired by list position. If a CPU or
>> domain disappears, counters can be subtracted from a different record
>> or left as absolute values. An extra record can also advance the cursor
>> past the list.
>>
>> Identify the second snapshot by its timestamp or CPU ordering, and
>> require matching CPU/domain IDs and versions before subtracting. Check
>> that every record has a counterpart before printing, and propagate
>> errors from either input of diff.
>>
>> Add a shell test with synthetic snapshots, including equal timestamps,
>> CPU filtering, and missing or reordered CPU/domain records.
>>
>> Fixes: 5a357ae6ad63 ("perf sched stats: Add support for report subcommand")
> 
> Swapnil, can you please ack/review this one?
> 
> - Arnaldo
> 

I have reviewed and tested the changes. It looks good to me. Please feel 
free to add

Reviewed-and-tested-by: Swapnil Sapkal <swapnil.sapkal@amd.com>

--
Thanks and Regards,
Swapnil

>> Assisted-by: LLM
>> Signed-off-by: Tianyi Chen <hi@tychen.cc>
>> ---
>> The new shell test passes with GCC and Clang ASan/UBSan and fails on
>> unpatched perf. Live and record/report/diff checks with temporary
>> /proc/schedstat fixtures also pass.
>>
>>   tools/perf/Documentation/perf-sched.txt       |   6 +-
>>   tools/perf/builtin-sched.c                    | 189 ++++++++++++------
>>   tools/perf/tests/shell/schedstat_snapshots.sh | 155 ++++++++++++++
>>   3 files changed, 283 insertions(+), 67 deletions(-)
>>   create mode 100755 tools/perf/tests/shell/schedstat_snapshots.sh
>>
>> diff --git a/tools/perf/Documentation/perf-sched.txt b/tools/perf/Documentation/perf-sched.txt
>> index 4da06215163a..fe0abef2bd13 100644
>> --- a/tools/perf/Documentation/perf-sched.txt
>> +++ b/tools/perf/Documentation/perf-sched.txt
>> @@ -95,8 +95,10 @@ There are several variants of 'perf sched':
>>      events, ``try_to_wakeup()`` call among others. This is useful in understanding the
>>      scheduler behavior for the workload.
>>   
>> -   Note: The tool will not give correct results if there is topological reordering or
>> -         online/offline of cpus in between capturing snapshots of `/proc/schedstat`.
>> +   Note: Reports reject incomplete snapshots and mismatched CPU or domain IDs.
>> +         Topology changes that retain the same IDs, including CPUs going offline
>> +         and returning online between snapshots, cannot be detected and may still
>> +         produce incorrect results.
>>   
>>       Example usage:
>>           perf sched stats record -- sleep 1
>> diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
>> index dd39a4fb6c7a..9e7d170b30ad 100644
>> --- a/tools/perf/builtin-sched.c
>> +++ b/tools/perf/builtin-sched.c
>> @@ -4281,18 +4281,18 @@ struct schedstat_cpu {
>>   };
>>   
>>   static struct list_head cpu_head = LIST_HEAD_INIT(cpu_head);
>> -static struct schedstat_cpu *cpu_second_pass;
>> -static struct schedstat_domain *domain_second_pass;
>> +static struct list_head *cpu_second_pass;
>> +static struct list_head *domain_second_pass;
>> +static u64 schedstat_timestamp;
>>   static bool after_workload_flag;
>>   static bool verbose_field;
>>   
>>   static void free_schedstat(struct list_head *head);
>>   
>> -static void store_schedstat_cpu_diff(struct schedstat_cpu *after_workload)
>> +static void store_schedstat_cpu_diff(struct perf_record_schedstat_cpu *before,
>> +				    struct perf_record_schedstat_cpu *after)
>>   {
>> -	struct perf_record_schedstat_cpu *before = cpu_second_pass->cpu_data;
>> -	struct perf_record_schedstat_cpu *after = after_workload->cpu_data;
>> -	__u16 version = after_workload->cpu_data->version;
>> +	__u16 version = after->version;
>>   
>>   #define CPU_FIELD(_type, _name, _desc, _format, _is_pct, _pct_of, _ver)	\
>>   	(before->_ver._name = after->_ver._name - before->_ver._name)
>> @@ -4308,11 +4308,10 @@ static void store_schedstat_cpu_diff(struct schedstat_cpu *after_workload)
>>   #undef CPU_FIELD
>>   }
>>   
>> -static void store_schedstat_domain_diff(struct schedstat_domain *after_workload)
>> +static void store_schedstat_domain_diff(struct perf_record_schedstat_domain *before,
>> +				       struct perf_record_schedstat_domain *after)
>>   {
>> -	struct perf_record_schedstat_domain *before = domain_second_pass->domain_data;
>> -	struct perf_record_schedstat_domain *after = after_workload->domain_data;
>> -	__u16 version = after_workload->domain_data->version;
>> +	__u16 version = after->version;
>>   
>>   #define DOMAIN_FIELD(_type, _name, _desc, _format, _is_jiffies, _ver)	\
>>   	(before->_ver._name = after->_ver._name - before->_ver._name)
>> @@ -4814,12 +4813,35 @@ static int show_schedstat_data(struct list_head *head1, struct cpu_domain_map **
>>    * other after completion of the workload. The above linked list stores the diff of the cpu and
>>    * domain statistics.
>>    */
>> +static int schedstat_snapshot_error(void)
>> +{
>> +	pr_err("Incompatible or incomplete schedstat snapshots\n");
>> +	return -EINVAL;
>> +}
>> +
>> +static bool schedstat_domains_complete(void)
>> +{
>> +	struct schedstat_cpu *cpu;
>> +
>> +	if (!domain_second_pass)
>> +		return true;
>> +	cpu = list_entry(cpu_second_pass, struct schedstat_cpu, cpu_list);
>> +	return domain_second_pass == &cpu->domain_head;
>> +}
>> +
>> +static int schedstat_snapshots_complete(void)
>> +{
>> +	if (!after_workload_flag || !cpu_second_pass ||
>> +	    cpu_second_pass->next != &cpu_head || !schedstat_domains_complete())
>> +		return schedstat_snapshot_error();
>> +	return 0;
>> +}
>> +
>>   static int perf_sched__process_schedstat(const struct perf_tool *tool __maybe_unused,
>>   					 struct perf_session *session __maybe_unused,
>>   					 union perf_event *event)
>>   {
>>   	struct perf_cpu this_cpu;
>> -	static __u32 initial_cpu;
>>   
>>   	switch (event->header.type) {
>>   	case PERF_RECORD_SCHEDSTAT_CPU:
>> @@ -4836,63 +4858,91 @@ static int perf_sched__process_schedstat(const struct perf_tool *tool __maybe_un
>>   		return 0;
>>   
>>   	if (event->header.type == PERF_RECORD_SCHEDSTAT_CPU) {
>> -		struct schedstat_cpu *temp = zalloc(sizeof(*temp));
>> -
>> -		if (!temp)
>> -			return -ENOMEM;
>> -
>> -		temp->cpu_data = zalloc(sizeof(*temp->cpu_data));
>> -		if (!temp->cpu_data)
>> -			return -ENOMEM;
>> +		struct perf_record_schedstat_cpu *data = &event->schedstat_cpu;
>> +		struct schedstat_cpu *cpu;
>>   
>> -		memcpy(temp->cpu_data, &event->schedstat_cpu, sizeof(*temp->cpu_data));
>> -
>> -		if (!list_empty(&cpu_head) && temp->cpu_data->cpu == initial_cpu)
>> -			after_workload_flag = true;
>> -
>> -		if (!after_workload_flag) {
>> -			if (list_empty(&cpu_head))
>> -				initial_cpu = temp->cpu_data->cpu;
>> -
>> -			list_add_tail(&temp->cpu_list, &cpu_head);
>> -			INIT_LIST_HEAD(&temp->domain_head);
>> -		} else {
>> -			if (temp->cpu_data->cpu == initial_cpu) {
>> -				cpu_second_pass = list_first_entry(&cpu_head, struct schedstat_cpu,
>> -								   cpu_list);
>> -				cpu_second_pass->cpu_data->timestamp =
>> -					temp->cpu_data->timestamp - cpu_second_pass->cpu_data->timestamp;
>> -			} else {
>> -				cpu_second_pass = list_next_entry(cpu_second_pass, cpu_list);
>> +		if (list_empty(&cpu_head)) {
>> +			after_workload_flag = false;
>> +			cpu_second_pass = &cpu_head;
>> +			domain_second_pass = NULL;
>> +			schedstat_timestamp = data->timestamp;
>> +		} else if (!after_workload_flag) {
>> +			cpu = list_last_entry(&cpu_head, struct schedstat_cpu, cpu_list);
>> +			/* Snapshots share a timestamp and list CPUs in increasing order. */
>> +			if (data->timestamp != schedstat_timestamp ||
>> +			    data->cpu <= cpu->cpu_data->cpu) {
>> +				after_workload_flag = true;
>> +				schedstat_timestamp = data->timestamp;
>>   			}
>> -			domain_second_pass = list_first_entry(&cpu_second_pass->domain_head,
>> -							      struct schedstat_domain, domain_list);
>> -			store_schedstat_cpu_diff(temp);
>> -			free(temp->cpu_data);
>> -			free(temp);
>>   		}
>> -	} else if (event->header.type == PERF_RECORD_SCHEDSTAT_DOMAIN) {
>> -		struct schedstat_cpu *cpu_tail;
>> -		struct schedstat_domain *temp = zalloc(sizeof(*temp));
>>   
>> -		if (!temp)
>> -			return -ENOMEM;
>> +		if (after_workload_flag) {
>> +			if (data->timestamp != schedstat_timestamp || !schedstat_domains_complete())
>> +				return schedstat_snapshot_error();
>> +			cpu_second_pass = cpu_second_pass->next;
>> +			if (cpu_second_pass == &cpu_head)
>> +				return schedstat_snapshot_error();
>> +			cpu = list_entry(cpu_second_pass, struct schedstat_cpu, cpu_list);
>> +			if (data->cpu != cpu->cpu_data->cpu ||
>> +			    data->version != cpu->cpu_data->version ||
>> +			    data->timestamp < cpu->cpu_data->timestamp)
>> +				return schedstat_snapshot_error();
>> +			cpu->cpu_data->timestamp = data->timestamp - cpu->cpu_data->timestamp;
>> +			store_schedstat_cpu_diff(cpu->cpu_data, data);
>> +			domain_second_pass = cpu->domain_head.next;
>> +			return 0;
>> +		}
>>   
>> -		temp->domain_data = zalloc(sizeof(*temp->domain_data));
>> -		if (!temp->domain_data)
>> +		cpu = zalloc(sizeof(*cpu));
>> +		if (!cpu)
>>   			return -ENOMEM;
>> +		cpu->cpu_data = memdup(data, sizeof(*data));
>> +		if (!cpu->cpu_data) {
>> +			free(cpu);
>> +			return -ENOMEM;
>> +		}
>> +		INIT_LIST_HEAD(&cpu->domain_head);
>> +		list_add_tail(&cpu->cpu_list, &cpu_head);
>> +	} else {
>> +		struct perf_record_schedstat_domain *data = &event->schedstat_domain;
>> +		struct schedstat_domain *domain;
>> +		struct schedstat_cpu *cpu;
>> +
>> +		if (list_empty(&cpu_head) || data->timestamp != schedstat_timestamp)
>> +			return schedstat_snapshot_error();
>> +		if (after_workload_flag) {
>> +			cpu = list_entry(cpu_second_pass, struct schedstat_cpu, cpu_list);
>> +			if (domain_second_pass == &cpu->domain_head)
>> +				return schedstat_snapshot_error();
>> +			domain = list_entry(domain_second_pass, struct schedstat_domain,
>> +					    domain_list);
>> +			if (data->cpu != domain->domain_data->cpu ||
>> +			    data->domain != domain->domain_data->domain ||
>> +			    data->version != domain->domain_data->version)
>> +				return schedstat_snapshot_error();
>> +			store_schedstat_domain_diff(domain->domain_data, data);
>> +			domain_second_pass = domain_second_pass->next;
>> +			return 0;
>> +		}
>>   
>> -		memcpy(temp->domain_data, &event->schedstat_domain, sizeof(*temp->domain_data));
>> -
>> -		if (!after_workload_flag) {
>> -			cpu_tail = list_last_entry(&cpu_head, struct schedstat_cpu, cpu_list);
>> -			list_add_tail(&temp->domain_list, &cpu_tail->domain_head);
>> -		} else {
>> -			store_schedstat_domain_diff(temp);
>> -			domain_second_pass = list_next_entry(domain_second_pass, domain_list);
>> -			free(temp->domain_data);
>> -			free(temp);
>> +		cpu = list_last_entry(&cpu_head, struct schedstat_cpu, cpu_list);
>> +		if (data->cpu != cpu->cpu_data->cpu || data->version != cpu->cpu_data->version)
>> +			return schedstat_snapshot_error();
>> +		if (!list_empty(&cpu->domain_head)) {
>> +			domain = list_last_entry(&cpu->domain_head, struct schedstat_domain,
>> +						 domain_list);
>> +			if (data->domain <= domain->domain_data->domain)
>> +				return schedstat_snapshot_error();
>>   		}
>> +		domain = zalloc(sizeof(*domain));
>> +		if (!domain)
>> +			return -ENOMEM;
>> +		domain->domain_data = memdup(data, sizeof(*data));
>> +		if (!domain->domain_data) {
>> +			free(domain);
>> +			return -ENOMEM;
>> +		}
>> +		list_add_tail(&domain->domain_list, &cpu->domain_head);
>>   	}
>>   
>>   	return 0;
>> @@ -4947,6 +4997,8 @@ static int perf_sched__schedstat_report(struct perf_sched *sched)
>>   	user_requested_cpus = evlist__core(session->evlist)->user_requested_cpus;
>>   
>>   	err = perf_session__process_events(session);
>> +	if (!err)
>> +		err = schedstat_snapshots_complete();
>>   
>>   	if (!err) {
>>   		setup_pager();
>> @@ -4976,7 +5028,7 @@ static int perf_sched__schedstat_diff(struct perf_sched *sched,
>>   	struct list_head cpu_head_ses0, cpu_head_ses1;
>>   	struct perf_session *session[2];
>>   	struct perf_data data[2] = {0};
>> -	int ret = 0, err = 0;
>> +	int ret = 0;
>>   	static const char *defaults[] = {
>>   		"perf.data.old",
>>   		"perf.data",
>> @@ -5009,8 +5061,10 @@ static int perf_sched__schedstat_diff(struct perf_sched *sched,
>>   		goto out_delete_ses0;
>>   	}
>>   
>> -	err = perf_session__process_events(session[0]);
>> -	if (err) {
>> +	ret = perf_session__process_events(session[0]);
>> +	if (!ret)
>> +		ret = schedstat_snapshots_complete();
>> +	if (ret) {
>>   		free_schedstat(&cpu_head);
>>   		goto out_delete_ses0;
>>   	}
>> @@ -5028,8 +5082,10 @@ static int perf_sched__schedstat_diff(struct perf_sched *sched,
>>   		goto out_delete_ses1;
>>   	}
>>   
>> -	err = perf_session__process_events(session[1]);
>> -	if (err) {
>> +	ret = perf_session__process_events(session[1]);
>> +	if (!ret)
>> +		ret = schedstat_snapshots_complete();
>> +	if (ret) {
>>   		free_schedstat(&cpu_head);
>>   		goto out_delete_ses1;
>>   	}
>> @@ -5152,6 +5208,9 @@ static int perf_sched__schedstat_live(struct perf_sched *sched,
>>   					       user_requested_cpus);
>>   	if (err)
>>   		goto out;
>> +	err = schedstat_snapshots_complete();
>> +	if (err)
>> +		goto out;
>>   
>>   	setup_pager();
>>   
>> diff --git a/tools/perf/tests/shell/schedstat_snapshots.sh b/tools/perf/tests/shell/schedstat_snapshots.sh
>> new file mode 100755
>> index 000000000000..a4bdf2673d43
>> --- /dev/null
>> +++ b/tools/perf/tests/shell/schedstat_snapshots.sh
>> @@ -0,0 +1,155 @@
>> +#!/bin/sh
>> +# Validate CPU and domain pairing in perf sched stats snapshots
>> +# SPDX-License-Identifier: GPL-2.0
>> +
>> +set -e
>> +
>> +# shellcheck source=lib/setup_python.sh
>> +. "$(dirname "$0")/lib/setup_python.sh"
>> +
>> +if ! perf version --build-options | grep -q 'libtraceevent:.*on'; then
>> +	echo "[Skip] perf sched requires libtraceevent"
>> +	exit 2
>> +fi
>> +
>> +$PYTHON - <<'PY'
>> +import os
>> +import re
>> +import struct
>> +import subprocess
>> +import sys
>> +import tempfile
>> +
>> +# Native-endian perf.data with only NRCPUS and CPU_DOMAIN_INFO features.
>> +endian = '<' if sys.byteorder == 'little' else '>'
>> +
>> +
>> +def pack(fmt, *values):
>> +    return struct.pack(endian + fmt, *values)
>> +
>> +
>> +def string(value):
>> +    data = value.encode() + b'\0'
>> +    return pack('I', len(data)) + data
>> +
>> +
>> +def cpu(cpu_id, timestamp, value, version):
>> +    return pack('IHHQIHH6I3Q', 85, 0, 72, timestamp, cpu_id, version, 0,
>> +                *([value] * 9))
>> +
>> +
>> +def domain(cpu_id, domain_id, timestamp, value, version):
>> +    # All supported versions use the largest union member's record size.
>> +    return pack('IHHQIHH45I4x', 86, 0, 208, timestamp, cpu_id, version,
>> +                domain_id, *([value] * 45))
>> +
>> +
>> +def snapshot(timestamp, value, version=17, cpus=(0, 1, 2), domains=(0, 1)):
>> +    records = []
>> +    for cpu_id in cpus:
>> +        records.append(cpu(cpu_id, timestamp, value + cpu_id * 100, version))
>> +        for domain_id in domains:
>> +            records.append(domain(cpu_id, domain_id, timestamp,
>> +                                  value + cpu_id * 100, version))
>> +    return records
>> +
>> +
>> +def write_file(path, records, version=17):
>> +    metadata = pack('II', version, 2)
>> +    for cpu_id in range(3):
>> +        metadata += pack('II', cpu_id, 2)
>> +        for domain_id in range(2):
>> +            metadata += pack('I', domain_id)
>> +            if version >= 17:
>> +                metadata += string('SMT' if domain_id == 0 else 'MC')
>> +            metadata += string('7') + string('0-2')
>> +    features = [pack('II', 3, 3), metadata]
>> +    data = b''.join(records)
>> +    offset = 104 + len(data) + 16 * len(features)
>> +    sections = b''
>> +    for feature in features:
>> +        sections += pack('QQ', offset, len(feature))
>> +        offset += len(feature)
>> +    header = pack('13Q', 0x32454c4946524550, 104, 144, 104, 0,
>> +                  104, len(data), 0, 0, (1 << 7) | (1 << 32), 0, 0, 0)
>> +    with open(path, 'wb') as output:
>> +        output.write(header + data + sections + b''.join(features))
>> +
>> +
>> +def run(args, valid, domains=True):
>> +    result = subprocess.run(['perf', 'sched', 'stats'] + args,
>> +                            stdout=subprocess.PIPE, stderr=subprocess.PIPE,
>> +                            text=True, timeout=10)
>> +    if valid:
>> +        assert result.returncode == 0, result.stderr
>> +        assert re.search(r'^yld_count\s+:\s+10\b', result.stdout, re.M), result.stdout
>> +        if domains:
>> +            assert re.search(r'^busy_lb_count\s+:\s+10\b', result.stdout, re.M), result.stdout
>> +        else:
>> +            assert 'busy_lb_count' not in result.stdout, result.stdout
>> +    else:
>> +        assert result.returncode > 0, (args, result.returncode, result.stdout)
>> +        assert 'Incompatible or incomplete schedstat snapshots' in result.stderr
>> +        assert not result.stdout, result.stdout
>> +    assert 'Sanitizer' not in result.stderr, result.stderr
>> +
>> +
>> +with tempfile.TemporaryDirectory(prefix='perf-schedstat-') as directory:
>> +    good = os.path.join(directory, 'good.data')
>> +    test = os.path.join(directory, 'test.data')
>> +    before = snapshot(100, 1000)
>> +    after = snapshot(200, 1010)
>> +    write_file(good, before + after)
>> +
>> +    for version in (15, 16, 17):
>> +        for timestamp in (100, 200):
>> +            write_file(test, snapshot(100, 1000, version) +
>> +                       snapshot(timestamp, 1010, version), version)
>> +            run(['report', '-C', '0,1,2', '-i', test], True)
>> +            run(['diff', test, test], True)
>> +    print('Matching snapshots, including equal timestamps: [Success]')
>> +
>> +    write_file(test, snapshot(100, 1000, domains=()) +
>> +               snapshot(200, 1010, domains=()))
>> +    run(['report', '-C', '0,1,2', '-i', test], True, domains=False)
>> +    run(['diff', test, test], True, domains=False)
>> +    print('CPUs without domains: [Success]')
>> +
>> +    write_file(test, before + snapshot(200, 1010, cpus=(0, 1)))
>> +    run(['report', '-C', '0,1', '-i', test], True)
>> +    run(['report', '-C', '1', '-i', good], True)
>> +    write_file(test, snapshot(100, 1000, cpus=(0, 1)) +
>> +               snapshot(200, 1010, cpus=(0, 1)))
>> +    run(['diff', good, test], True)
>> +    print('CPU filtering and different CPU sets across files: [Success]')
>> +
>> +    write_file(test, snapshot(100, 0xfffffffa, cpus=(0,)) +
>> +               snapshot(200, 4, cpus=(0,)))
>> +    run(['report', '-C', '0', '-i', test], True)
>> +    print('Wrapping 32-bit counters: [Success]')
>> +
>> +    cases = {
>> +        'missing first CPU': before + snapshot(200, 1010, cpus=(1, 2)),
>> +        'missing middle CPU': before + snapshot(200, 1010, cpus=(0, 2)),
>> +        'missing last CPU': before + snapshot(200, 1010, cpus=(0, 1)),
>> +        'added CPU': snapshot(100, 1000, cpus=(0, 1)) + after,
>> +        'reordered CPUs': before + snapshot(200, 1010, cpus=(0, 2, 1)),
>> +        'equal timestamp, missing first CPU': before + snapshot(100, 1010, cpus=(1, 2)),
>> +        'missing first domain': before + snapshot(200, 1010, domains=(1,)),
>> +        'missing last domain': before + after[:-1],
>> +        'added domain': snapshot(100, 1000, domains=(0,)) + after,
>> +        'reordered domains': before + snapshot(200, 1010, domains=(1, 0)),
>> +        'domain without CPU': before[1:] + after,
>> +        'wrong domain CPU': before + [after[0], domain(1, 0, 200, 1010, 17)] + after[2:],
>> +        'changed version': before + snapshot(200, 1010, version=16),
>> +        'backwards timestamp': before + snapshot(50, 1010),
>> +        'third snapshot': before + after + snapshot(300, 1020),
>> +        'missing second snapshot': before,
>> +    }
>> +    for name, records in cases.items():
>> +        write_file(test, records)
>> +        run(['report', '-C', '0,1,2', '-i', test], False)
>> +        run(['diff', test, good], False)
>> +        run(['diff', good, test], False)
>> +        print(name + ': [Success]')
>> +PY
>>
>> base-commit: 91b0782fc9e9d2f0a40b5256146e014802fdbb36
>> -- 
>> 2.55.0


      reply	other threads:[~2026-09-28 13:28 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-16 16:11 Tianyi Chen
2026-09-25 13:07 ` Arnaldo Carvalho de Melo
2026-09-28 13:27   ` Swapnil Sapkal [this message]

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=a7b96474-98b0-49b4-a524-89bda593275f@amd.com \
    --to=swapnil.sapkal@amd.com \
    --cc=acme@kernel.org \
    --cc=hi@tychen.cc \
    --cc=irogers@google.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-perf-users@vger.kernel.org \
    --cc=namhyung@kernel.org \
    --cc=ravi.bangoria@amd.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®