[PATCH 1/3] iio: gts: Simplify using __free

Matti Vaittinen posted 3 patches 1 year ago
[PATCH 1/3] iio: gts: Simplify using __free
Posted by Matti Vaittinen 1 year ago
The error path in the gain_to_scaletables() uses goto for unwinding an
allocation on failure. This can be slightly simplified by using the
automated free when exiting the scope.

Use __free(kfree) and drop the goto based error handling.

Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
---
This is derived from the:
https://lore.kernel.org/lkml/5efc30d832275778d1f48d7e2c75b1ecc63511d5.1732105157.git.mazziesaccount@gmail.com/
---
 drivers/iio/industrialio-gts-helper.c | 108 +++++++++++++++-----------
 1 file changed, 63 insertions(+), 45 deletions(-)

diff --git a/drivers/iio/industrialio-gts-helper.c b/drivers/iio/industrialio-gts-helper.c
index 291c0fc332c9..e15d0112e9e3 100644
--- a/drivers/iio/industrialio-gts-helper.c
+++ b/drivers/iio/industrialio-gts-helper.c
@@ -4,6 +4,7 @@
  * Copyright (c) 2023 Matti Vaittinen <mazziesaccount@gmail.com>
  */
 
+#include <linux/cleanup.h>
 #include <linux/device.h>
 #include <linux/errno.h>
 #include <linux/export.h>
@@ -165,11 +166,9 @@ static int iio_gts_gain_cmp(const void *a, const void *b)
 	return *(int *)a - *(int *)b;
 }
 
-static int gain_to_scaletables(struct iio_gts *gts, int **gains, int **scales)
+static int fill_and_sort_scaletables(struct iio_gts *gts, int **gains, int **scales)
 {
-	int i, j, new_idx, time_idx, ret = 0;
-	int *all_gains;
-	size_t gain_bytes;
+	int i, j, ret;
 
 	for (i = 0; i < gts->num_itime; i++) {
 		/*
@@ -189,8 +188,15 @@ static int gain_to_scaletables(struct iio_gts *gts, int **gains, int **scales)
 		}
 	}
 
-	gain_bytes = array_size(gts->num_hwgain, sizeof(int));
-	all_gains = kcalloc(gts->num_itime, gain_bytes, GFP_KERNEL);
+	return 0;
+}
+
+static int build_combined_table(struct iio_gts *gts, int **gains, size_t gain_bytes)
+{
+	int ret, i, j, new_idx, time_idx;
+	int *all_gains __free(kfree) = kcalloc(gts->num_itime, gain_bytes,
+					       GFP_KERNEL);
+
 	if (!all_gains)
 		return -ENOMEM;
 
@@ -232,10 +238,9 @@ static int gain_to_scaletables(struct iio_gts *gts, int **gains, int **scales)
 
 	gts->avail_all_scales_table = kcalloc(new_idx, 2 * sizeof(int),
 					      GFP_KERNEL);
-	if (!gts->avail_all_scales_table) {
-		ret = -ENOMEM;
-		goto free_out;
-	}
+	if (!gts->avail_all_scales_table)
+		return -ENOMEM;
+
 	gts->num_avail_all_scales = new_idx;
 
 	for (i = 0; i < gts->num_avail_all_scales; i++) {
@@ -246,14 +251,25 @@ static int gain_to_scaletables(struct iio_gts *gts, int **gains, int **scales)
 		if (ret) {
 			kfree(gts->avail_all_scales_table);
 			gts->num_avail_all_scales = 0;
-			goto free_out;
+			return ret;
 		}
 	}
 
-free_out:
-	kfree(all_gains);
+	return 0;
+}
 
-	return ret;
+static int gain_to_scaletables(struct iio_gts *gts, int **gains, int **scales)
+{
+	int ret;
+	size_t gain_bytes;
+
+	ret = fill_and_sort_scaletables(gts, gains, scales);
+	if (ret)
+		return ret;
+
+	gain_bytes = array_size(gts->num_hwgain, sizeof(int));
+
+	return build_combined_table(gts, gains, gain_bytes);
 }
 
 /**
@@ -337,26 +353,11 @@ static void iio_gts_us_to_int_micro(int *time_us, int *int_micro_times,
 	}
 }
 
-/**
- * iio_gts_build_avail_time_table - build table of available integration times
- * @gts:	Gain time scale descriptor
- *
- * Build the table which can represent the available times to be returned
- * to users using the read_avail-callback.
- *
- * NOTE: Space allocated for the tables must be freed using
- * iio_gts_purge_avail_time_table() when the tables are no longer needed.
- *
- * Return: 0 on success.
- */
-static int iio_gts_build_avail_time_table(struct iio_gts *gts)
+static int __iio_gts_build_avail_time_table(struct iio_gts *gts)
 {
-	int *times, i, j, idx = 0, *int_micro_times;
-
-	if (!gts->num_itime)
-		return 0;
+	int i, j, idx = 0, *int_micro_times;
+	int *times __free(kfree) = kcalloc(gts->num_itime, sizeof(int), GFP_KERNEL);
 
-	times = kcalloc(gts->num_itime, sizeof(int), GFP_KERNEL);
 	if (!times)
 		return -ENOMEM;
 
@@ -384,25 +385,42 @@ static int iio_gts_build_avail_time_table(struct iio_gts *gts)
 
 	/* create a list of times formatted as list of IIO_VAL_INT_PLUS_MICRO */
 	int_micro_times = kcalloc(idx, sizeof(int) * 2, GFP_KERNEL);
-	if (int_micro_times) {
-		/*
-		 * This is just to survive a unlikely corner-case where times in
-		 * the given time table were not unique. Else we could just
-		 * trust the gts->num_itime.
-		 */
-		gts->num_avail_time_tables = idx;
-		iio_gts_us_to_int_micro(times, int_micro_times, idx);
-	}
-
-	gts->avail_time_tables = int_micro_times;
-	kfree(times);
-
 	if (!int_micro_times)
 		return -ENOMEM;
 
+	/*
+	 * This is just to survive a unlikely corner-case where times in
+	 * the given time table were not unique. Else we could just
+	 * trust the gts->num_itime.
+	 */
+	gts->num_avail_time_tables = idx;
+	iio_gts_us_to_int_micro(times, int_micro_times, idx);
+
+	gts->avail_time_tables = int_micro_times;
+
 	return 0;
 }
 
+/**
+ * iio_gts_build_avail_time_table - build table of available integration times
+ * @gts:	Gain time scale descriptor
+ *
+ * Build the table which can represent the available times to be returned
+ * to users using the read_avail-callback.
+ *
+ * NOTE: Space allocated for the tables must be freed using
+ * iio_gts_purge_avail_time_table() when the tables are no longer needed.
+ *
+ * Return: 0 on success.
+ */
+static int iio_gts_build_avail_time_table(struct iio_gts *gts)
+{
+	if (!gts->num_itime)
+		return 0;
+
+	return __iio_gts_build_avail_time_table(gts);
+}
+
 /**
  * iio_gts_purge_avail_time_table - free-up the available integration time table
  * @gts:	Gain time scale descriptor
-- 
2.47.0

Re: [PATCH 1/3] iio: gts: Simplify using __free
Posted by Jonathan Cameron 1 year ago
On Thu, 28 Nov 2024 18:50:24 +0200
Matti Vaittinen <mazziesaccount@gmail.com> wrote:

> The error path in the gain_to_scaletables() uses goto for unwinding an
> allocation on failure. This can be slightly simplified by using the
> automated free when exiting the scope.
> 
> Use __free(kfree) and drop the goto based error handling.
> 
> Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
> ---
> This is derived from the:
> https://lore.kernel.org/lkml/5efc30d832275778d1f48d7e2c75b1ecc63511d5.1732105157.git.mazziesaccount@gmail.com/

Hi Matti

A few comments on specific parts of this below

Thanks,

Jonathan

> +static int build_combined_table(struct iio_gts *gts, int **gains, size_t gain_bytes)
> +{
> +	int ret, i, j, new_idx, time_idx;
> +	int *all_gains __free(kfree) = kcalloc(gts->num_itime, gain_bytes,
> +					       GFP_KERNEL);
> +
>  	if (!all_gains)
>  		return -ENOMEM;
>  
> @@ -232,10 +238,9 @@ static int gain_to_scaletables(struct iio_gts *gts, int **gains, int **scales)
>  
>  	gts->avail_all_scales_table = kcalloc(new_idx, 2 * sizeof(int),
>  					      GFP_KERNEL);

I'm not particularly keen in a partial application of __free magic.

Perhaps you can use a local variable for this and a no_free_ptr() to assign it after we know
there can't be an error that requires it to be freed.

> -	if (!gts->avail_all_scales_table) {
> -		ret = -ENOMEM;
> -		goto free_out;
> -	}
> +	if (!gts->avail_all_scales_table)
> +		return -ENOMEM;
> +
>  	gts->num_avail_all_scales = new_idx;
>  
>  	for (i = 0; i < gts->num_avail_all_scales; i++) {
> @@ -246,14 +251,25 @@ static int gain_to_scaletables(struct iio_gts *gts, int **gains, int **scales)
>  		if (ret) {
>  			kfree(gts->avail_all_scales_table);
>  			gts->num_avail_all_scales = 0;
> -			goto free_out;
> +			return ret;
>  		}
>  	}
>  
> -free_out:
> -	kfree(all_gains);
> +	return 0;
> +}
>  
> -	return ret;
> +static int gain_to_scaletables(struct iio_gts *gts, int **gains, int **scales)
> +{
> +	int ret;
> +	size_t gain_bytes;
> +
> +	ret = fill_and_sort_scaletables(gts, gains, scales);
> +	if (ret)
> +		return ret;
> +
> +	gain_bytes = array_size(gts->num_hwgain, sizeof(int));

array_size is documented as being for 2D arrays, not an array of a multi byte
type.  We should not use it for this purpose.

I'd be tempted to not worry about overflow, but if you do want to be sure then
copy what kcalloc does and use a check_mul_overflow()

https://elixir.bootlin.com/linux/v6.12.1/source/include/linux/slab.h#L919

You don't have to tidy that up in this patch though.

> +
> +	return build_combined_table(gts, gains, gain_bytes);
>  }

>  
> +/**
> + * iio_gts_build_avail_time_table - build table of available integration times
> + * @gts:	Gain time scale descriptor
> + *
> + * Build the table which can represent the available times to be returned
> + * to users using the read_avail-callback.
> + *
> + * NOTE: Space allocated for the tables must be freed using
> + * iio_gts_purge_avail_time_table() when the tables are no longer needed.
> + *
> + * Return: 0 on success.
> + */
> +static int iio_gts_build_avail_time_table(struct iio_gts *gts)
Hmm. I guess this wrapper exists because perhaps you aren't comfortable
yet with the __free() handling mid function.  It does not harm so I'm fine
having this.

> +{
> +	if (!gts->num_itime)
> +		return 0;
> +
> +	return __iio_gts_build_avail_time_table(gts);
> +}
> +
>  /**
>   * iio_gts_purge_avail_time_table - free-up the available integration time table
>   * @gts:	Gain time scale descriptor
Re: [PATCH 1/3] iio: gts: Simplify using __free
Posted by Matti Vaittinen 1 year ago
On 30/11/2024 19:51, Jonathan Cameron wrote:
> On Thu, 28 Nov 2024 18:50:24 +0200
> Matti Vaittinen <mazziesaccount@gmail.com> wrote:
> 
>> The error path in the gain_to_scaletables() uses goto for unwinding an
>> allocation on failure. This can be slightly simplified by using the
>> automated free when exiting the scope.
>>
>> Use __free(kfree) and drop the goto based error handling.
>>
>> Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
>> ---
>> This is derived from the:
>> https://lore.kernel.org/lkml/5efc30d832275778d1f48d7e2c75b1ecc63511d5.1732105157.git.mazziesaccount@gmail.com/
> 
> Hi Matti
> 
> A few comments on specific parts of this below
> 
> Thanks,
> 
> Jonathan
> 
>> +static int build_combined_table(struct iio_gts *gts, int **gains, size_t gain_bytes)
>> +{
>> +	int ret, i, j, new_idx, time_idx;
>> +	int *all_gains __free(kfree) = kcalloc(gts->num_itime, gain_bytes,
>> +					       GFP_KERNEL);
>> +
>>   	if (!all_gains)
>>   		return -ENOMEM;
>>   
>> @@ -232,10 +238,9 @@ static int gain_to_scaletables(struct iio_gts *gts, int **gains, int **scales)
>>   
>>   	gts->avail_all_scales_table = kcalloc(new_idx, 2 * sizeof(int),
>>   					      GFP_KERNEL);
> 
> I'm not particularly keen in a partial application of __free magic.

I am starting to think the partial application of __free would actually 
be what I preferred... (see below).

> Perhaps you can use a local variable for this and a no_free_ptr() to assign it after we know
> there can't be an error that requires it to be freed.

I am having second thoughts of this whole series. I do love the idea of 
__free() magic, when applied on a simple temporary allocations that are 
intended to be freed at the end of a function. Eg, for cases where we 
know the scope from the very beginning. With a consistent use of __free 
in such cases could make it much more obvious for a reader that this 
stuff is valid only for a duration of this block. I have a feeling that 
mixing the no_free_ptr() is a violation, and will obfuscate this.

I know I used it in this series while trying to simplify the flow - and 
I am already regretting this.

Additionally, I indeed am not okay with introducing variables in middle 
of a function. I do really feel quite strongly about that.

It seems that in many functions it makes sense to have some checks or 
potentially failing operations done before doing memory allocations. So, 
keeping the allocation at the start of a block can often require some 
additional "check/do these things before calling an internal function 
which does alloc + rest of the work" -wrappers.

It will then also mean that the internal function (called from a wrapper 
with checks) will lack of the aforementioned checks, and, is thus 
somehow unsafe. I am not saying such wrappers are always wrong - 
sometimes it may be ok - but it probably should be consistent approach 
and not a mixture of conventions depending on allocations...

Also, sometimes this would result some very strangely split functions 
with no well defined purpose.

As a result, I would definitely only use the "__free magic" in places 
where it does fit well for freeing a memory which is known to be needed 
only for a specific block. And, I would only use it where the alloc can 
be done at the beginning of a function in a rather natural way. This, 
however, is very likely to lead in mixed use of "__free magic" and 
regular allocs.

>> -	if (!gts->avail_all_scales_table) {
>> -		ret = -ENOMEM;
>> -		goto free_out;
>> -	}
>> +	if (!gts->avail_all_scales_table)
>> +		return -ENOMEM;
>> +
>>   	gts->num_avail_all_scales = new_idx;
>>   
>>   	for (i = 0; i < gts->num_avail_all_scales; i++) {
>> @@ -246,14 +251,25 @@ static int gain_to_scaletables(struct iio_gts *gts, int **gains, int **scales)
>>   		if (ret) {
>>   			kfree(gts->avail_all_scales_table);
>>   			gts->num_avail_all_scales = 0;
>> -			goto free_out;
>> +			return ret;
>>   		}
>>   	}
>>   
>> -free_out:
>> -	kfree(all_gains);
>> +	return 0;
>> +}
>>   
>> -	return ret;
>> +static int gain_to_scaletables(struct iio_gts *gts, int **gains, int **scales)
>> +{
>> +	int ret;
>> +	size_t gain_bytes;
>> +
>> +	ret = fill_and_sort_scaletables(gts, gains, scales);
>> +	if (ret)
>> +		return ret;
>> +
>> +	gain_bytes = array_size(gts->num_hwgain, sizeof(int));
> 
> array_size is documented as being for 2D arrays, not an array of a multi byte
> type.  We should not use it for this purpose.

Thanks for pointing this out. I was not familiar with that. I am 
actually pretty sure that using the array_size() has been recommended to 
me :)

> I'd be tempted to not worry about overflow, but if you do want to be sure then
> copy what kcalloc does and use a check_mul_overflow()
> 
> https://elixir.bootlin.com/linux/v6.12.1/source/include/linux/slab.h#L919

Thanks for the direct pointer :) I'll take a look at it!

> 
> You don't have to tidy that up in this patch though.
> 
>> +
>> +	return build_combined_table(gts, gains, gain_bytes);
>>   }
> 
>>   
>> +/**
>> + * iio_gts_build_avail_time_table - build table of available integration times
>> + * @gts:	Gain time scale descriptor
>> + *
>> + * Build the table which can represent the available times to be returned
>> + * to users using the read_avail-callback.
>> + *
>> + * NOTE: Space allocated for the tables must be freed using
>> + * iio_gts_purge_avail_time_table() when the tables are no longer needed.
>> + *
>> + * Return: 0 on success.
>> + */
>> +static int iio_gts_build_avail_time_table(struct iio_gts *gts)
> Hmm. I guess this wrapper exists because perhaps you aren't comfortable
> yet with the __free() handling mid function.  It does not harm so I'm fine
> having this.

Yes. This was the reason for this wrapper. But I'm not really happy 
about the wrappers either (even if I agree with you that it does not 
really hurt here). Furthermore, if you're feeling strongly about not 
mixing the __free and regular allocs, then I simply prefer not using the 
magic one. I don't think using the __free with allocations intended to 
last longer than the scope is clear. Ues, goto sequences may not always 
be simple, but at least people are used to be wary with them.

>> +{
>> +	if (!gts->num_itime)
>> +		return 0;
>> +
>> +	return __iio_gts_build_avail_time_table(gts);
>> +}
>> +
>>   /**
>>    * iio_gts_purge_avail_time_table - free-up the available integration time table
>>    * @gts:	Gain time scale descriptor
> 

Thanks a ton for the review :) It helped me to clarify my thoughts on this!

Yours,
	-- Matti