Re: [PATCH] ACPI: CPPC: Fix MASK_VAL() usage

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




On 02/08/2024 17:13, Rafael J. Wysocki wrote:
> On Mon, Jul 15, 2024 at 5:33 PM Clément Léger <cleger@xxxxxxxxxxxx> wrote:
>>
>> MASK_VAL() was added a way to handle bit_offset and bit_width for
>> registers located in system memory address space. However, while suited
>> for reading, it does not work for writing and result in corrupted
>> registers when writing values with bit_offset > 0. Moreover, when a
>> register is collocated with another one at the same address but with a
>> different mask, the current code results in the other registers being
>> overwritten with 0s. The write procedure for SYSTEM_MEMORY registers
>> should actually read the value, mask it, update it and write it with the
>> updated value. Moreover, since registers can be located in the same
>> word, we must take care of locking the access before doing it. We should
>> potentially use a global lock since we don't know in if register
>> addresses aren't shared with another _CPC package but better not
>> encourage vendors to do so. Assume that registers can use the same word
>> inside a _CPC package and thus, use a per _CPC package lock.
>>
>> Fixes: 2f4a4d63a193 ("ACPI: CPPC: Use access_width over bit_width for system memory accesses")
>> Signed-off-by: Clément Léger <cleger@xxxxxxxxxxxx>
>>
>> ---
>>  drivers/acpi/cppc_acpi.c | 44 ++++++++++++++++++++++++++++++++++++----
>>  include/acpi/cppc_acpi.h |  2 ++
>>  2 files changed, 42 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
>> index 1d857978f5f4..2e99cf1842ee 100644
>> --- a/drivers/acpi/cppc_acpi.c
>> +++ b/drivers/acpi/cppc_acpi.c
>> @@ -170,8 +170,11 @@ show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, wraparound_time);
>>  #define GET_BIT_WIDTH(reg) ((reg)->access_width ? (8 << ((reg)->access_width - 1)) : (reg)->bit_width)
>>
>>  /* Shift and apply the mask for CPC reads/writes */
>> -#define MASK_VAL(reg, val) (((val) >> (reg)->bit_offset) &                     \
>> +#define MASK_VAL_READ(reg, val) (((val) >> (reg)->bit_offset) &                                \
>>                                         GENMASK(((reg)->bit_width) - 1, 0))
>> +#define MASK_VAL_WRITE(reg, prev_val, val)                                             \
>> +       ((((val) & GENMASK(((reg)->bit_width) - 1, 0)) << (reg)->bit_offset) |          \
>> +       ((prev_val) & ~(GENMASK(((reg)->bit_width) - 1, 0) << (reg)->bit_offset)))      \
>>
>>  static ssize_t show_feedback_ctrs(struct kobject *kobj,
>>                 struct kobj_attribute *attr, char *buf)
>> @@ -857,6 +860,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
>>
>>         /* Store CPU Logical ID */
>>         cpc_ptr->cpu_id = pr->id;
>> +       spin_lock_init(&cpc_ptr->rmw_lock);
>>
>>         /* Parse PSD data for this CPU */
>>         ret = acpi_get_psd(cpc_ptr, handle);
>> @@ -1062,7 +1066,7 @@ static int cpc_read(int cpu, struct cpc_register_resource *reg_res, u64 *val)
>>         }
>>
>>         if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
>> -               *val = MASK_VAL(reg, *val);
>> +               *val = MASK_VAL_READ(reg, *val);
>>
>>         return 0;
>>  }
>> @@ -1071,9 +1075,11 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
>>  {
>>         int ret_val = 0;
>>         int size;
>> +       u64 prev_val;
>>         void __iomem *vaddr = NULL;
>>         int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
>>         struct cpc_reg *reg = &reg_res->cpc_entry.reg;
>> +       struct cpc_desc *cpc_desc;
>>
>>         size = GET_BIT_WIDTH(reg);
>>
>> @@ -1106,8 +1112,34 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
>>                 return acpi_os_write_memory((acpi_physical_address)reg->address,
>>                                 val, size);
>>
>> -       if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
>> -               val = MASK_VAL(reg, val);
>> +       if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
>> +               cpc_desc = per_cpu(cpc_desc_ptr, cpu);
>> +               if (!cpc_desc) {
>> +                       pr_debug("No CPC descriptor for CPU:%d\n", cpu);
>> +                       return -ENODEV;
>> +               }
>> +
>> +               spin_lock(&cpc_desc->rmw_lock);
>> +               switch (size) {
>> +               case 8:
>> +                       prev_val = readb_relaxed(vaddr);
>> +                       break;
>> +               case 16:
>> +                       prev_val = readw_relaxed(vaddr);
>> +                       break;
>> +               case 32:
>> +                       prev_val = readl_relaxed(vaddr);
>> +                       break;
>> +               case 64:
>> +                       prev_val = readq_relaxed(vaddr);
>> +                       break;
>> +               default:
>> +                       ret_val = -EFAULT;
>> +                       goto out_unlock;
> 
> I would do
> 
>                       spin_unlock(&cpc_desc->rmw_lock);
>                       return -EFAUL;
> 
> here to avoid the check below which is redundant in this path and the
> label would not be necessary then.

Acked, I'll send a V2 with these modifications.

Thanks,

Clément

> 
> LGTM otherwise.
> 
>> +               };
>> +               val = MASK_VAL_WRITE(reg, prev_val, val);
>> +               val |= prev_val;
>> +       }
>>
>>         switch (size) {
>>         case 8:
>> @@ -1134,6 +1166,10 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
>>                 break;
>>         }
>>
>> +out_unlock:
>> +       if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
>> +               spin_unlock(&cpc_desc->rmw_lock);
>> +
>>         return ret_val;
>>  }
>>
>> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
>> index 930b6afba6f4..e1720d930666 100644
>> --- a/include/acpi/cppc_acpi.h
>> +++ b/include/acpi/cppc_acpi.h
>> @@ -64,6 +64,8 @@ struct cpc_desc {
>>         int cpu_id;
>>         int write_cmd_status;
>>         int write_cmd_id;
>> +       /* Lock used for RMW operations in cpc_write() */
>> +       spinlock_t rmw_lock;
>>         struct cpc_register_resource cpc_regs[MAX_CPC_REG_ENT];
>>         struct acpi_psd_package domain_info;
>>         struct kobject kobj;
>> --
>> 2.45.2
>>




[Index of Archives]     [Linux IBM ACPI]     [Linux Power Management]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]
  Powered by Linux