Hi, On 8-Jun-25 9:57 PM, Hans de Goede wrote: > Hi Sebastian, > On 30-Apr-25 12:54 AM, Sebastian Reichel wrote: >> Also use fwnode based parsing for "ocv-capacity-celsius" and >> "resistance-temp-table", so that any DT specific bits are >> removed from the power-supply core. >> >> Reviewed-by: AngeloGioacchino Del Regno >> Signed-off-by: Sebastian Reichel > > I have been testing this converting the ug3105 driver to > use power_supply_batinfo_ocv2cap(), replacing the hardcoded > ocv -> capacity table in that driver. > > While testing I hit a bug and while looking closer at this > patch it needs more work on top of fixing that bug. > > See comments inline, also I've attached 3 fixup patches > which can be squashed into this patch to address the remarks. > >> --- >> drivers/power/supply/power_supply_core.c | 109 ++++++++++++++++++------------- >> 1 file changed, 63 insertions(+), 46 deletions(-) >> >> diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c >> index 89947f1fe610d8a75756e1e4e5339b06349f9ab8..a8d1fe66e2486a833ccaa3ed77b861c6e52c5760 100644 >> --- a/drivers/power/supply/power_supply_core.c >> +++ b/drivers/power/supply/power_supply_core.c >> @@ -585,32 +585,19 @@ int power_supply_get_battery_info(struct power_supply *psy, >> { >> struct power_supply_resistance_temp_table *resist_table; >> struct power_supply_battery_info *info; >> - struct device_node *battery_np = NULL; >> - struct fwnode_reference_args args; >> - struct fwnode_handle *fwnode = NULL; >> + struct fwnode_handle *srcnode, *fwnode; >> const char *value; >> - int err, len, index; >> - const __be32 *list; >> + int err, len, index, proplen; >> + u32 *propdata; > > propname which is also a local-variable for a temporary > malloc-ed buffer uses __free(kfree) instead of explicit > kfree() calls IMHO it would be good to do that here too. > > This requires declaring it inside the > "for (index = 0; index < len; index++)" loop like how > this is done for propname, so that it gets freed on > every loop iteration. > > >> u32 min_max[2]; >> >> - if (psy->dev.of_node) { >> - battery_np = of_parse_phandle(psy->dev.of_node, "monitored-battery", 0); >> - if (!battery_np) >> - return -ENODEV; >> + srcnode = dev_fwnode(&psy->dev); >> + if (!srcnode && psy->dev.parent) >> + srcnode = dev_fwnode(psy->dev.parent); >> >> - fwnode = fwnode_handle_get(of_fwnode_handle(battery_np)); >> - } else if (psy->dev.parent) { >> - err = fwnode_property_get_reference_args( >> - dev_fwnode(psy->dev.parent), >> - "monitored-battery", NULL, 0, 0, &args); >> - if (err) >> - return err; >> - >> - fwnode = args.fwnode; >> - } >> - >> - if (!fwnode) >> - return -ENOENT; >> + fwnode = fwnode_find_reference(srcnode, "monitored-battery", 0); >> + if (IS_ERR(fwnode)) >> + return PTR_ERR(fwnode); >> >> err = fwnode_property_read_string(fwnode, "compatible", &value); >> if (err) >> @@ -740,15 +727,7 @@ int power_supply_get_battery_info(struct power_supply *psy, >> info->temp_max = min_max[1]; >> } >> >> - /* >> - * The below code uses raw of-data parsing to parse >> - * /schemas/types.yaml#/definitions/uint32-matrix >> - * data, so for now this is only support with of. >> - */ >> - if (!battery_np) >> - goto out_ret_pointer; >> - >> - len = of_property_count_u32_elems(battery_np, "ocv-capacity-celsius"); >> + len = fwnode_property_count_u32(fwnode, "ocv-capacity-celsius"); >> if (len < 0 && len != -EINVAL) { >> err = len; >> goto out_put_node; >> @@ -757,13 +736,13 @@ int power_supply_get_battery_info(struct power_supply *psy, >> err = -EINVAL; >> goto out_put_node; >> } else if (len > 0) { >> - of_property_read_u32_array(battery_np, "ocv-capacity-celsius", >> + fwnode_property_read_u32_array(fwnode, "ocv-capacity-celsius", >> info->ocv_temp, len); >> } >> >> for (index = 0; index < len; index++) { >> struct power_supply_battery_ocv_table *table; >> - int i, tab_len, size; >> + int i, tab_len; >> >> char *propname __free(kfree) = kasprintf(GFP_KERNEL, "ocv-capacity-table-%d", >> index); >> @@ -772,60 +751,98 @@ int power_supply_get_battery_info(struct power_supply *psy, >> err = -ENOMEM; >> goto out_put_node; >> } >> - list = of_get_property(battery_np, propname, &size); >> - if (!list || !size) { >> + proplen = fwnode_property_count_u32(fwnode, propname); >> + if (proplen < 0 || proplen % 2 != 0) { >> dev_err(&psy->dev, "failed to get %s\n", propname); >> power_supply_put_battery_info(psy, info); >> err = -EINVAL; >> goto out_put_node; >> } >> + propdata = kcalloc(proplen, sizeof(*propdata), GFP_KERNEL); > > As mentioned above I suggest to use the following here instead: > > u32 *propdata __free(kfree) = kcalloc(proplen, sizeof(*propdata), GFP_KERNEL); > > >> + if (!propdata) { >> + kfree(propname); > > propname must NOT be free-ed here since it is marked __free(kfree), > freeing it here will cause a double-free bug. > >> + power_supply_put_battery_info(psy, info); >> + err = -EINVAL; >> + goto out_put_node; >> + } >> + err = fwnode_property_read_u32_array(fwnode, propname, propdata, proplen); >> + if (err < 0) { >> + dev_err(&psy->dev, "failed to get %s\n", propname); >> + kfree(propname); > > same as above. > >> + kfree(propdata); > > with the suggested "u32 *propdata __free(kfree)" this can and must be dropped. > >> + power_supply_put_battery_info(psy, info); >> + goto out_put_node; >> + } >> >> - tab_len = size / (2 * sizeof(__be32)); >> + tab_len = proplen / 2; >> info->ocv_table_size[index] = tab_len; >> >> info->ocv_table[index] = table = >> devm_kcalloc(&psy->dev, tab_len, sizeof(*table), GFP_KERNEL); >> if (!info->ocv_table[index]) { >> + kfree(propdata); > > with the suggested "u32 *propdata __free(kfree)" this can and must be dropped. > >> power_supply_put_battery_info(psy, info); >> err = -ENOMEM; >> goto out_put_node; >> } >> >> for (i = 0; i < tab_len; i++) { >> - table[i].ocv = be32_to_cpu(*list); >> - list++; >> - table[i].capacity = be32_to_cpu(*list); >> - list++; >> + table[i].ocv = propdata[i*2]; >> + table[i].capacity = propdata[i*2+1]; >> } >> + >> + kfree(propdata); > > with the suggested "u32 *propdata __free(kfree)" this can and must be dropped. > >> } >> >> - list = of_get_property(battery_np, "resistance-temp-table", &len); >> - if (!list || !len) >> + proplen = fwnode_property_count_u32(fwnode, "resistance-temp-table"); > > This will return -EINVAL when the property does not exist, making > power_supply_get_battery_info() always fail when there is no > "resistance-temp-table" in the battery fwnode. See the attached fixup > patch for a suggested fix. > >> + if (proplen < 0 || proplen % 2 != 0) { >> + power_supply_put_battery_info(psy, info); >> + err = -ENOMEM; > > -ENOMEM is the wrong error code here. > >> goto out_ret_pointer; > > This should be "goto out_put_node" since this is an error path. > >> + } else if (proplen == 0) { >> + goto out_ret_pointer; >> + } >> >> - info->resist_table_size = len / (2 * sizeof(__be32)); >> + propdata = kcalloc(proplen, sizeof(*propdata), GFP_KERNEL); > > As mentioned above I suggest to use the following here instead: > > u32 *propdata __free(kfree) = kcalloc(proplen, sizeof(*propdata), GFP_KERNEL); Ok, so this does not work, because we jump over this causing propdata to not get initialized, but the __free(kfree) still triggers. Lesson learned: do not use modern C style mixing of declarations and statements together with __free(). Attached are updated fixup patches which do not make things crash. Regards, Hans > >> + if (!propdata) { >> + power_supply_put_battery_info(psy, info); >> + err = -ENOMEM; >> + goto out_ret_pointer; > > This should be "goto out_put_node" since this is an error path. > >> + } >> + >> + err = fwnode_property_read_u32_array(fwnode, "resistance-temp-table", >> + propdata, proplen); >> + if (err < 0) { >> + kfree(propdata); > > with the suggested "u32 *propdata __free(kfree)" this can and must be dropped. > >> + power_supply_put_battery_info(psy, info); >> + goto out_put_node; >> + } >> + >> + info->resist_table_size = proplen / 2; >> info->resist_table = resist_table = devm_kcalloc(&psy->dev, >> info->resist_table_size, >> sizeof(*resist_table), >> GFP_KERNEL); >> if (!info->resist_table) { >> + kfree(propdata); > > with the suggested "u32 *propdata __free(kfree)" this can and must be dropped. > >> power_supply_put_battery_info(psy, info); >> err = -ENOMEM; >> goto out_put_node; >> } >> >> for (index = 0; index < info->resist_table_size; index++) { >> - resist_table[index].temp = be32_to_cpu(*list++); >> - resist_table[index].resistance = be32_to_cpu(*list++); >> + resist_table[index].temp = propdata[index*2]; >> + resist_table[index].resistance = propdata[index*2+1]; >> } >> >> + kfree(propdata); > > with the suggested "u32 *propdata __free(kfree)" this can and must be dropped. > >> + >> out_ret_pointer: >> /* Finally return the whole thing */ >> *info_out = info; >> >> out_put_node: >> fwnode_handle_put(fwnode); >> - of_node_put(battery_np); >> return err; >> } >> EXPORT_SYMBOL_GPL(power_supply_get_battery_info); >> > > Regards, > > Hans > >