Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

soc: qcom: llcc: Add per-slice counter and common llcc slice descriptor

Fix incorrect slice activation/deactivation accounting by replacing the
bitmap-based activation tracking with per-slice atomic reference counters.
This resolves mismatches that occur when multiple client drivers vote for
the same slice or when llcc_slice_getd() is called multiple times.

As part of this fix, simplify slice descriptor handling by eliminating
dynamic allocation. llcc_slice_getd() now returns a pointer to a
preallocated descriptor, removing the need for repeated allocation/free
cycles and ensuring consistent reference tracking across all users.

Signed-off-by: Unnathi Chalicheemala <unnathi.chalicheemala@oss.qualcomm.com>
Signed-off-by: Francisco Munoz Ruiz <francisco.ruiz@oss.qualcomm.com>
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Link: https://lore.kernel.org/r/20260305-external_llcc_changes1set-v1-1-6347e52e648e@oss.qualcomm.com
Signed-off-by: Bjorn Andersson <andersson@kernel.org>

authored by

Unnathi Chalicheemala and committed by
Bjorn Andersson
45c2a55d 3fa036c0

+32 -33
+28 -29
drivers/soc/qcom/llcc-qcom.c
··· 5 5 */ 6 6 7 7 #include <linux/bitfield.h> 8 - #include <linux/bitmap.h> 9 8 #include <linux/bitops.h> 10 9 #include <linux/cleanup.h> 11 10 #include <linux/device.h> ··· 4534 4535 struct llcc_slice_desc *llcc_slice_getd(u32 uid) 4535 4536 { 4536 4537 const struct llcc_slice_config *cfg; 4537 - struct llcc_slice_desc *desc; 4538 - u32 sz, count; 4538 + u32 sz, i; 4539 4539 4540 4540 if (IS_ERR(drv_data)) 4541 4541 return ERR_CAST(drv_data); ··· 4542 4544 cfg = drv_data->cfg; 4543 4545 sz = drv_data->cfg_size; 4544 4546 4545 - for (count = 0; cfg && count < sz; count++, cfg++) 4547 + for (i = 0; cfg && i < sz; i++, cfg++) 4546 4548 if (cfg->usecase_id == uid) 4547 4549 break; 4548 4550 4549 - if (count == sz || !cfg) 4551 + if (i == sz) 4550 4552 return ERR_PTR(-ENODEV); 4551 4553 4552 - desc = kzalloc_obj(*desc); 4553 - if (!desc) 4554 - return ERR_PTR(-ENOMEM); 4555 - 4556 - desc->slice_id = cfg->slice_id; 4557 - desc->slice_size = cfg->max_cap; 4558 - 4559 - return desc; 4554 + return &drv_data->desc[i]; 4560 4555 } 4561 4556 EXPORT_SYMBOL_GPL(llcc_slice_getd); 4562 4557 ··· 4560 4569 void llcc_slice_putd(struct llcc_slice_desc *desc) 4561 4570 { 4562 4571 if (!IS_ERR_OR_NULL(desc)) 4563 - kfree(desc); 4572 + return; 4564 4573 } 4565 4574 EXPORT_SYMBOL_GPL(llcc_slice_putd); 4566 4575 ··· 4636 4645 return -EINVAL; 4637 4646 4638 4647 mutex_lock(&drv_data->lock); 4639 - if (test_bit(desc->slice_id, drv_data->bitmap)) { 4648 + /* Already active; try to take another reference. */ 4649 + if (refcount_inc_not_zero(&desc->refcount)) { 4640 4650 mutex_unlock(&drv_data->lock); 4641 4651 return 0; 4642 4652 } ··· 4651 4659 return ret; 4652 4660 } 4653 4661 4654 - __set_bit(desc->slice_id, drv_data->bitmap); 4662 + /* Set first reference */ 4663 + refcount_set(&desc->refcount, 1); 4655 4664 mutex_unlock(&drv_data->lock); 4656 4665 4657 4666 return ret; ··· 4678 4685 return -EINVAL; 4679 4686 4680 4687 mutex_lock(&drv_data->lock); 4681 - if (!test_bit(desc->slice_id, drv_data->bitmap)) { 4688 + /* refcount > 1, drop one ref and we’re done. */ 4689 + if (refcount_dec_not_one(&desc->refcount)) { 4682 4690 mutex_unlock(&drv_data->lock); 4683 4691 return 0; 4684 4692 } 4693 + 4685 4694 act_ctrl_val = ACT_CTRL_OPCODE_DEACTIVATE << ACT_CTRL_OPCODE_SHIFT; 4686 4695 4687 4696 ret = llcc_update_act_ctrl(desc->slice_id, act_ctrl_val, ··· 4693 4698 return ret; 4694 4699 } 4695 4700 4696 - __clear_bit(desc->slice_id, drv_data->bitmap); 4701 + /* Finalize: atomically transition 1 -> 0 */ 4702 + WARN_ON_ONCE(!refcount_dec_if_one(&desc->refcount)); 4697 4703 mutex_unlock(&drv_data->lock); 4698 4704 4699 4705 return ret; ··· 4738 4742 u32 attr1_val; 4739 4743 u32 attr0_val; 4740 4744 u32 max_cap_cacheline; 4741 - struct llcc_slice_desc desc; 4745 + struct llcc_slice_desc *desc; 4742 4746 4743 4747 attr1_val = config->cache_mode; 4744 4748 attr1_val |= config->probe_target_ways << ATTR1_PROBE_TARGET_WAYS_SHIFT; ··· 4887 4891 } 4888 4892 4889 4893 if (config->activate_on_init) { 4890 - desc.slice_id = config->slice_id; 4891 - ret = llcc_slice_activate(&desc); 4894 + desc = llcc_slice_getd(config->usecase_id); 4895 + if (IS_ERR(desc)) 4896 + return PTR_ERR(desc); 4897 + 4898 + ret = llcc_slice_activate(desc); 4892 4899 } 4893 4900 4894 4901 return ret; ··· 5204 5205 5205 5206 llcc_cfg = cfg->sct_data; 5206 5207 sz = cfg->size; 5207 - 5208 - for (i = 0; i < sz; i++) 5209 - if (llcc_cfg[i].slice_id > drv_data->max_slices) 5210 - drv_data->max_slices = llcc_cfg[i].slice_id; 5211 - 5212 - drv_data->bitmap = devm_bitmap_zalloc(dev, drv_data->max_slices, 5213 - GFP_KERNEL); 5214 - if (!drv_data->bitmap) { 5208 + drv_data->desc = devm_kcalloc(dev, sz, sizeof(struct llcc_slice_desc), GFP_KERNEL); 5209 + if (!drv_data->desc) { 5215 5210 ret = -ENOMEM; 5216 5211 goto err; 5212 + } 5213 + 5214 + for (i = 0; i < sz; i++) { 5215 + drv_data->desc[i].slice_id = llcc_cfg[i].slice_id; 5216 + drv_data->desc[i].slice_size = llcc_cfg[i].max_cap; 5217 + refcount_set(&drv_data->desc[i].refcount, 0); 5217 5218 } 5218 5219 5219 5220 drv_data->cfg = llcc_cfg;
+4 -4
include/linux/soc/qcom/llcc-qcom.h
··· 91 91 * struct llcc_slice_desc - Cache slice descriptor 92 92 * @slice_id: llcc slice id 93 93 * @slice_size: Size allocated for the llcc slice 94 + * @refcount: Atomic counter to track activate/deactivate calls 94 95 */ 95 96 struct llcc_slice_desc { 96 97 u32 slice_id; 97 98 size_t slice_size; 99 + refcount_t refcount; 98 100 }; 99 101 100 102 /** ··· 154 152 * @edac_reg_offset: Offset of the LLCC EDAC registers 155 153 * @lock: mutex associated with each slice 156 154 * @cfg_size: size of the config data table 157 - * @max_slices: max slices as read from device tree 158 155 * @num_banks: Number of llcc banks 159 - * @bitmap: Bit map to track the active slice ids 160 156 * @ecc_irq: interrupt for llcc cache error detection and reporting 161 157 * @ecc_irq_configured: 'True' if firmware has already configured the irq propagation 158 + * @desc: Array pointer of pre-allocated LLCC slice descriptors 162 159 * @version: Indicates the LLCC version 163 160 */ 164 161 struct llcc_drv_data { ··· 168 167 const struct llcc_edac_reg_offset *edac_reg_offset; 169 168 struct mutex lock; 170 169 u32 cfg_size; 171 - u32 max_slices; 172 170 u32 num_banks; 173 - unsigned long *bitmap; 174 171 int ecc_irq; 175 172 bool ecc_irq_configured; 176 173 u32 version; 174 + struct llcc_slice_desc *desc; 177 175 }; 178 176 179 177 #if IS_ENABLED(CONFIG_QCOM_LLCC)