Thank you for your contribution! Sashiko AI review found 3 potential issue(s) 
to consider:
- [High] platform/x86/intel/ifs: ifs_init() fails on SAF_GEN=0 CPUs
- [High] platform/x86/intel/ifs: do_core_test() updates grp_cpumask before 
validation
- [Low] platform/x86/intel/ifs: fix documentation typo and inconsistencies in 
ifs.h
--

commit 00e0613f0da8feb277c2b0b17ada7d9df4902b12
Author: Qiuxu Zhuo <[email protected]>

platform/x86/intel/ifs: Update IFS documentation

This series updates the Intel In-Field Scan (IFS) driver to support group-based
simultaneous testing using topology_cluster_cpumask(), validates TID_BIT_SHIFT
against the cluster topology, and adds per-group sysfs result attributes and
documentation.

> diff --git a/drivers/platform/x86/intel/ifs/core.c 
> b/drivers/platform/x86/intel/ifs/core.c
> index 3dafa28a10104..5c9c86e048371 100644
> --- a/drivers/platform/x86/intel/ifs/core.c
> +++ b/drivers/platform/x86/intel/ifs/core.c

[ ... ]

> +/*
> + * Test whether logical processors indicated by TID_BIT_SHIFT that share the
> + * same SCAN test engine match the cpumask from topology_cluster_cpumask().
> + */
> +static bool topology_cluster_matched(u32 msrval)
> +{
> +     u32 dist, shift = FIELD_GET(MSR_INTEGRITY_CAPS_TID_BIT_SHIFT_MASK, 
> msrval);
> +     const cpumask_t *cpumask = topology_cluster_cpumask(0);
> +     u32 delta, step = 0, first = cpu_data(0).topo.apicid;
> +     unsigned int cpu, cpus;
> +
> +     /* Get the minimal APIC ID step among the CPUs in the same cluster. */
> +     for_each_cpu_andnot(cpu, cpumask, cpumask_of(0)) {
> +             delta = abs(cpu_data(cpu).topo.apicid - first);
> +
> +             if (!step) {
> +                     step = delta;
> +                     continue;
> +             }
> +
> +             if (step > delta)
> +                     step = delta;
> +     }
> +
> +     /* A Hyper-Threading (HT) system with HT disabled. */
> +     if (!step)
> +             step = 2;
> +
> +     cpus = cpumask_weight(cpumask);
> +     dist = step * cpus;
> +
> +     if (dist != (1 << shift)) {
> +             pr_err_once("APICID step %u, group cpus %u, TID_BIT_SHIFT 
> %u\n", step, cpus, shift);
> +             return false;
> +     }
> +
> +     return true;
> +}

[ ... ]

> @@ -134,11 +157,33 @@ static int __init ifs_init(void)
>                       continue;
>               ifs_devices[i].rw_data.generation = 
> FIELD_GET(MSR_INTEGRITY_CAPS_SAF_GEN_MASK,
>                                                             msrval);
> +             /*
> +              * For Sapphire Rapids and Emerald Rapids (SAF_GEN=0), the 
> ALL_LP_JOIN
> +              * indicator is unavailable. However, all the threads within a 
> core still
> +              * need to run IFS simultaneously, so the variable 
> 'all_lp_join' is
> +              * forced to true.
> +              *
> +              * For Granite Rapids and later generations (SAF_GEN=2), the 
> ALL_LP_JOIN
> +              * indicator is available, the variable 'all_lp_join' is 
> determined by
> +              * this indicator.
> +              */
> +             ifs_devices[i].rw_data.all_lp_join = 
> ifs_devices[i].rw_data.generation ?
> +                                             (msrval & 
> MSR_INTEGRITY_CAPS_ALL_LP_JOIN) : true;
> +
> +             if (ifs_devices[i].rw_data.all_lp_join && 
> !topology_cluster_matched(msrval)) {
> +                     pr_err_once("TID_BIT_SHIFT mismatched 
> topology_cluster_cpumask().\n");
> +                     ret = -EINVAL;
> +                     goto err_exit;
> +             }

[Severity: High]
Does this cause ifs_init() to fail with -EINVAL on all generation == 0
platforms and on systems where any CPU in cluster 0 is offline at module load
time?

In ifs_init(), when ifs_devices[i].rw_data.generation is 0 (such as on
Sapphire Rapids, Emerald Rapids, Sierra Forest, and Clearwater Forest),
all_lp_join is forced to true and topology_cluster_matched(msrval) is called
unconditionally.

However, TID_BIT_SHIFT (MSR_INTEGRITY_CAPS_TID_BIT_SHIFT_MASK) was only
introduced starting with Granite Rapids (generation == 2) and is 0 on
generation == 0 processors. In topology_cluster_matched():

    cpus = cpumask_weight(cpumask);
    dist = step * cpus;

    if (dist != (1 << shift)) {
        ...
        return false;
    }

Because step is at least 1 when siblings are present (with cpus >= 2) or set
to 2 when !step (with cpus == 1), dist is always >= 2, whereas (1 << shift)
is 1 when shift is 0. As a result, topology_cluster_matched() always returns
false on generation == 0 CPUs, aborting driver initialization with -EINVAL
(and on Sierra Forest and Clearwater Forest, forcing all_lp_join to true also
causes build_cpugroup_mask() to use the 4-core L2 cluster mask instead of a
single core).

In addition, topology_cluster_matched() reads topology_cluster_cpumask(0)
without holding cpus_read_lock() and assumes all logical processors in
CPU 0's cluster are online. Since cpu_l2c_shared_map only tracks online CPUs,
if any sibling thread or core in cluster 0 is offline when the module loads
(for example, 3 of 4 CPUs online in a Dual-Core Module giving step = 1 and
cpus = 3, or only CPU 0 online giving step = 2 and cpus = 1 when shift = 2),
dist != (1 << shift) will also evaluate to true and fail initialization.

> diff --git a/drivers/platform/x86/intel/ifs/ifs.h 
> b/drivers/platform/x86/intel/ifs/ifs.h
> index f369fb0d3d82f..551202f86cea4 100644
> --- a/drivers/platform/x86/intel/ifs/ifs.h
> +++ b/drivers/platform/x86/intel/ifs/ifs.h
> @@ -81,30 +81,52 @@
>   * The IFS driver provides sysfs interfaces via 
> /sys/devices/virtual/misc/intel_ifs_<n>/
>   * to control execution:
>   *
> - * Test a specific core::
> + * Test a specific group::
>   *
> - *   # echo <cpu#> > /sys/devices/virtual/misc/intel_ifs_<n>/run_test
> + *   # echo <CPU#> > /sys/devices/virtual/misc/intel_ifs_<n>/run_test
>   *
> - * when HT is enabled any of the sibling cpu# can be specified to test
> - * its corresponding physical core. Since the tests are per physical core,
> - * the result of testing any thread is same. All siblings must be online
> - * to run a core test. It is only necessary to test one thread.
> + * Any CPU in the same group that shares the same SCAN test engin can be
> + * specified to test its corresponding group. All CPUs in the same group
> + * must be online to run a group test. It is only necessary to test one
> + * CPU in the group.

[ ... ]

> + * More additional details of the last test for all CPUs in the same group 
> are provided in /sys::
> + *
> + *   $ cat /sys/devices/virtual/misc/intel_ifs_0/addnl_details_list
> + *   0xc44191a 0xc44191a
>   *
>   * The details file reports the hex value of the test specific status MSR.
>   * Hardware defined error codes are documented in volume 4 of the Intel
> @@ -127,48 +149,22 @@
>   * 2) Hardware allows for some number of cores to be tested in parallel.
>   * The driver does not make use of this, it only tests one core at a time.

[Severity: Low]
This isn't a runtime bug, but there are a few small inconsistencies in the
documentation update in drivers/platform/x86/intel/ifs/ifs.h:

1. "shares the same SCAN test engin can be" has a typo ("engin" instead of
   "engine").

2. Under "Driver design choices", item 2 still says "it only tests one core
   at a time" rather than one group at a time.

3. The commit message refers to "{status, details, additional_details,
   cpus_ran}_list", whereas the sysfs attribute documented here and
   implemented in drivers/platform/x86/intel/ifs/sysfs.c is named
   addnl_details_list.

> diff --git a/drivers/platform/x86/intel/ifs/runtest.c 
> b/drivers/platform/x86/intel/ifs/runtest.c
> index a882cd42c191a..c57b9ed0b7be7 100644
> --- a/drivers/platform/x86/intel/ifs/runtest.c
> +++ b/drivers/platform/x86/intel/ifs/runtest.c

[ ... ]

> +static void build_cpugroup_mask(struct device *dev, int cpu)
> +{
> +     struct ifs_data *ifsd = ifs_get_data(dev);
> +
> +     cpumask_clear(&ifsd->grp_cpumask);
> +
> +     /*
> +      * Some CPUs, such as Sierra Forest and Clearwater Forest, have 
> multiple cores
> +      * sharing the same SCAN test engine, but they don't require a 
> rendezvous.
> +      */
> +     if (!ifsd->all_lp_join) {
> +             cpumask_set_cpu(cpu, &ifsd->grp_cpumask);
> +             return;
> +     }
> +
> +     cpumask_copy(&ifsd->grp_cpumask, topology_cluster_cpumask(cpu));
> +}
> +
>  /*
> - * Initiate per core test. It wakes up work queue threads on the target cpu 
> and
> - * its sibling cpu. Once all sibling threads wake up, the scan test gets 
> executed and
> + * Initiate per core test. It wakes up work queue threads on the target CPU 
> and
> + * its sibling CPU. Once all sibling threads wake up, the scan test gets 
> executed and
>   * wait for all sibling threads to finish the scan test.
>   */
>  int do_core_test(int cpu, struct device *dev)
> @@ -628,9 +493,10 @@ int do_core_test(int cpu, struct device *dev)
>  
>       /* Prevent CPUs from being taken offline during the scan test */
>       cpus_read_lock();
> +     build_cpugroup_mask(dev, cpu);
>  
>       if (!cpu_online(cpu)) {
> -             dev_info(dev, "cannot test on the offline cpu %d\n", cpu);
> +             dev_info(dev, "cannot test on the offline CPU %d\n", cpu);
>               ret = -EINVAL;
>               goto out;
>       }

[Severity: High]
Should build_cpugroup_mask(dev, cpu) be called only after verifying that cpu
is online and that a test will actually be executed?

In run_test_store() -> do_core_test(), run_test_store() only checks
cpu >= nr_cpu_ids before calling do_core_test(cpu, dev), and do_core_test()
calls build_cpugroup_mask(dev, cpu) before checking !cpu_online(cpu) or
!ifsd->loaded:

1. When ifsd->all_lp_join is true, build_cpugroup_mask() calls:

       cpumask_copy(&ifsd->grp_cpumask, topology_cluster_cpumask(cpu));

   which accesses per_cpu(cpu_l2c_shared_map, cpu). Because
   smp_prepare_cpus_common() only allocates cpu_l2c_shared_map for CPUs in
   cpu_possible_mask, writing a CPU number with cpu < nr_cpu_ids that is not
   in cpu_possible_mask when CONFIG_CPUMASK_OFFSTACK=y will dereference a
   NULL cpumask pointer before the !cpu_online(cpu) check is reached.

2. If do_core_test() returns early with -EINVAL (because cpu is offline) or
   with -EPERM (because !ifsd->loaded for IFS_TYPE_SAF),
   build_cpugroup_mask() has already overwritten ifsd->grp_cpumask.
   Subsequent reads of cpus_ran_list, status_list, details_list, and
   addnl_details_list in drivers/platform/x86/intel/ifs/sysfs.c will then
   report the CPUs and stale per-CPU state of the rejected request instead
   of "none" or the last test that actually ran.

-- 
Sashiko AI review ยท 
https://sashiko.dev/#/patchset/[email protected]?part=16

Reply via email to