[PATCH v2] iio: chemical: scd30: Replace manual locking with RAII locking

Maxwell Doose posted 1 patch 5 days, 11 hours ago
There is a newer version of this series
drivers/iio/chemical/scd30_core.c | 66 ++++++++++++++++++++-----------
1 file changed, 42 insertions(+), 24 deletions(-)
[PATCH v2] iio: chemical: scd30: Replace manual locking with RAII locking
Posted by Maxwell Doose 5 days, 11 hours ago
scd30_core.c currently uses manual mutex_lock() and mutex_unlock()
calls. Replace them with the newer guard(mutex)() for cleaner RAII
patterns and to improve maintainability.

Add new helper function scd30_trigger_handler_helper_locked() containing
the critical section for scd30_trigger_handler().

In addition, small refactor to replace "?:" operator with regular
if/else returns.

Signed-off-by: Maxwell Doose <m32285159@gmail.com>
---
 v2:
 - Fix callback issue as noted by Jonathan v1.
 - Refactor critical section of scd30_trigger_handler() into helper
   called scd30_trigger_handler_helper_locked().
 - Revert removal of goto in scd30_trigger_handler().

 drivers/iio/chemical/scd30_core.c | 66 ++++++++++++++++++++-----------
 1 file changed, 42 insertions(+), 24 deletions(-)

diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c
index a665fcb78806..bb8bdea7c833 100644
--- a/drivers/iio/chemical/scd30_core.c
+++ b/drivers/iio/chemical/scd30_core.c
@@ -368,11 +368,13 @@ static ssize_t calibration_auto_enable_show(struct device *dev, struct device_at
 	int ret;
 	u16 val;
 
-	mutex_lock(&state->lock);
-	ret = scd30_command_read(state, CMD_ASC, &val);
-	mutex_unlock(&state->lock);
+	guard(mutex)(&state->lock);
 
-	return ret ?: sysfs_emit(buf, "%d\n", val);
+	ret = scd30_command_read(state, CMD_ASC, &val);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%d\n", val);
 }
 
 static ssize_t calibration_auto_enable_store(struct device *dev, struct device_attribute *attr,
@@ -387,11 +389,13 @@ static ssize_t calibration_auto_enable_store(struct device *dev, struct device_a
 	if (ret)
 		return ret;
 
-	mutex_lock(&state->lock);
-	ret = scd30_command_write(state, CMD_ASC, val);
-	mutex_unlock(&state->lock);
+	guard(mutex)(&state->lock);
 
-	return ret ?: len;
+	ret = scd30_command_write(state, CMD_ASC, val);
+	if (ret)
+		return ret;
+
+	return len;
 }
 
 static ssize_t calibration_forced_value_show(struct device *dev, struct device_attribute *attr,
@@ -402,11 +406,13 @@ static ssize_t calibration_forced_value_show(struct device *dev, struct device_a
 	int ret;
 	u16 val;
 
-	mutex_lock(&state->lock);
-	ret = scd30_command_read(state, CMD_FRC, &val);
-	mutex_unlock(&state->lock);
+	guard(mutex)(&state->lock);
 
-	return ret ?: sysfs_emit(buf, "%d\n", val);
+	ret = scd30_command_read(state, CMD_FRC, &val);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%d\n", val);
 }
 
 static ssize_t calibration_forced_value_store(struct device *dev, struct device_attribute *attr,
@@ -424,11 +430,13 @@ static ssize_t calibration_forced_value_store(struct device *dev, struct device_
 	if (val < SCD30_FRC_MIN_PPM || val > SCD30_FRC_MAX_PPM)
 		return -EINVAL;
 
-	mutex_lock(&state->lock);
-	ret = scd30_command_write(state, CMD_FRC, val);
-	mutex_unlock(&state->lock);
+	guard(mutex)(&state->lock);
 
-	return ret ?: len;
+	ret = scd30_command_write(state, CMD_FRC, val);
+	if (ret)
+		return ret;
+
+	return len;
 }
 
 static IIO_DEVICE_ATTR_RO(sampling_frequency_available, 0);
@@ -579,24 +587,34 @@ static irqreturn_t scd30_irq_thread_handler(int irq, void *priv)
 	return IRQ_HANDLED;
 }
 
+/* Meant ONLY for scd30_trigger_handler() */
+static int scd30_trigger_handler_helper_locked(struct iio_dev *indio_dev,
+					       int *scan_data, int arr_size)
+{
+	struct scd30_state *state = iio_priv(indio_dev);
+	int ret;
+
+	guard(mutex)(&state->lock);
+
+	if (!iio_trigger_using_own(indio_dev))
+		ret = scd30_read_poll(state);
+	else
+		ret = scd30_read_meas(state);
+	memcpy(scan_data, state->meas, arr_size);
+	return ret;
+}
+
 static irqreturn_t scd30_trigger_handler(int irq, void *p)
 {
 	struct iio_poll_func *pf = p;
 	struct iio_dev *indio_dev = pf->indio_dev;
-	struct scd30_state *state = iio_priv(indio_dev);
 	struct {
 		int data[SCD30_MEAS_COUNT];
 		aligned_s64 ts;
 	} scan = { };
 	int ret;
 
-	mutex_lock(&state->lock);
-	if (!iio_trigger_using_own(indio_dev))
-		ret = scd30_read_poll(state);
-	else
-		ret = scd30_read_meas(state);
-	memcpy(scan.data, state->meas, sizeof(state->meas));
-	mutex_unlock(&state->lock);
+	ret = scd30_trigger_handler_helper_locked(indio_dev, scan.data, sizeof(scan.data));
 	if (ret)
 		goto out;
 
-- 
2.54.0
Re: [PATCH v2] iio: chemical: scd30: Replace manual locking with RAII locking
Posted by Jonathan Cameron 4 days, 12 hours ago
On Tue, 19 May 2026 08:19:43 -0500
Maxwell Doose <m32285159@gmail.com> wrote:

> scd30_core.c currently uses manual mutex_lock() and mutex_unlock()
> calls. Replace them with the newer guard(mutex)() for cleaner RAII
> patterns and to improve maintainability.
> 
> Add new helper function scd30_trigger_handler_helper_locked() containing
> the critical section for scd30_trigger_handler().
> 
> In addition, small refactor to replace "?:" operator with regular
> if/else returns.
> 
> Signed-off-by: Maxwell Doose <m32285159@gmail.com>
Hi Maxwell

One of those cases where it made us look at code for first time in a while.
The original handling merrily copied garbage data.  Let's clean that up whilst
we are here.

Thanks,

Jonathan
>  static IIO_DEVICE_ATTR_RO(sampling_frequency_available, 0);
> @@ -579,24 +587,34 @@ static irqreturn_t scd30_irq_thread_handler(int irq, void *priv)
>  	return IRQ_HANDLED;
>  }
>  
> +/* Meant ONLY for scd30_trigger_handler() */
> +static int scd30_trigger_handler_helper_locked(struct iio_dev *indio_dev,
> +					       int *scan_data, int arr_size)
> +{
> +	struct scd30_state *state = iio_priv(indio_dev);
> +	int ret;
> +
> +	guard(mutex)(&state->lock);
> +
> +	if (!iio_trigger_using_own(indio_dev))
> +		ret = scd30_read_poll(state);
> +	else
> +		ret = scd30_read_meas(state);
> +	memcpy(scan_data, state->meas, arr_size);

Why is that a good thing to do if we are in an error condition?
I've have thought the data wasn't valid.

	if (ret)
		return ret;

	memcpy()..

	return 0;

Or is there something useful in that data?
Looks like some less than ideal code in the original driver but
I think it makes sense to clean that up now. Just make sure to put
a note in the patch description.





> +	return ret;
> +}
> +
>  static irqreturn_t scd30_trigger_handler(int irq, void *p)
>  {
>  	struct iio_poll_func *pf = p;
>  	struct iio_dev *indio_dev = pf->indio_dev;
> -	struct scd30_state *state = iio_priv(indio_dev);
>  	struct {
>  		int data[SCD30_MEAS_COUNT];
>  		aligned_s64 ts;
>  	} scan = { };
>  	int ret;
>  
> -	mutex_lock(&state->lock);
> -	if (!iio_trigger_using_own(indio_dev))
> -		ret = scd30_read_poll(state);
> -	else
> -		ret = scd30_read_meas(state);
> -	memcpy(scan.data, state->meas, sizeof(state->meas));
> -	mutex_unlock(&state->lock);
> +	ret = scd30_trigger_handler_helper_locked(indio_dev, scan.data, sizeof(scan.data));
>  	if (ret)
>  		goto out;
>
Re: [PATCH v2] iio: chemical: scd30: Replace manual locking with RAII locking
Posted by Maxwell Doose 4 days, 8 hours ago
On Wed, May 20, 2026 at 7:28 AM Jonathan Cameron <jic23@kernel.org> wrote:
>
> On Tue, 19 May 2026 08:19:43 -0500
> Maxwell Doose <m32285159@gmail.com> wrote:
>
> > scd30_core.c currently uses manual mutex_lock() and mutex_unlock()
> > calls. Replace them with the newer guard(mutex)() for cleaner RAII
> > patterns and to improve maintainability.
> >
> > Add new helper function scd30_trigger_handler_helper_locked() containing
> > the critical section for scd30_trigger_handler().
> >
> > In addition, small refactor to replace "?:" operator with regular
> > if/else returns.
> >
> > Signed-off-by: Maxwell Doose <m32285159@gmail.com>
> Hi Maxwell
>
> One of those cases where it made us look at code for first time in a while.
> The original handling merrily copied garbage data.  Let's clean that up whilst
> we are here.
>
> Thanks,
>
> Jonathan
> >  static IIO_DEVICE_ATTR_RO(sampling_frequency_available, 0);
> > @@ -579,24 +587,34 @@ static irqreturn_t scd30_irq_thread_handler(int irq, void *priv)
> >       return IRQ_HANDLED;
> >  }
> >
> > +/* Meant ONLY for scd30_trigger_handler() */
> > +static int scd30_trigger_handler_helper_locked(struct iio_dev *indio_dev,
> > +                                            int *scan_data, int arr_size)
> > +{
> > +     struct scd30_state *state = iio_priv(indio_dev);
> > +     int ret;
> > +
> > +     guard(mutex)(&state->lock);
> > +
> > +     if (!iio_trigger_using_own(indio_dev))
> > +             ret = scd30_read_poll(state);
> > +     else
> > +             ret = scd30_read_meas(state);
> > +     memcpy(scan_data, state->meas, arr_size);
>
> Why is that a good thing to do if we are in an error condition?
> I've have thought the data wasn't valid.
>
>         if (ret)
>                 return ret;
>
>         memcpy()..
>
>         return 0;
>
> Or is there something useful in that data?
> Looks like some less than ideal code in the original driver but
> I think it makes sense to clean that up now. Just make sure to put
> a note in the patch description.
>

That's likely a result of me being lazy and copy-pasting some of the
code and touching it up so we can make the stack frame leaner. TBH I
would say moving it may be out of scope for this patch? I don't know,
I think it may be worth leaving since the original code did this. I'll
still check the docs later to see if there is actually something
useful in the data.

best regards,
max

>
> > +     return ret;
> > +}
> > +
> >  static irqreturn_t scd30_trigger_handler(int irq, void *p)
> >  {
> >       struct iio_poll_func *pf = p;
> >       struct iio_dev *indio_dev = pf->indio_dev;
> > -     struct scd30_state *state = iio_priv(indio_dev);
> >       struct {
> >               int data[SCD30_MEAS_COUNT];
> >               aligned_s64 ts;
> >       } scan = { };
> >       int ret;
> >
> > -     mutex_lock(&state->lock);
> > -     if (!iio_trigger_using_own(indio_dev))
> > -             ret = scd30_read_poll(state);
> > -     else
> > -             ret = scd30_read_meas(state);
> > -     memcpy(scan.data, state->meas, sizeof(state->meas));
> > -     mutex_unlock(&state->lock);
> > +     ret = scd30_trigger_handler_helper_locked(indio_dev, scan.data, sizeof(scan.data));
> >       if (ret)
> >               goto out;
> >
>
Re: [PATCH v2] iio: chemical: scd30: Replace manual locking with RAII locking
Posted by Stepan Ionichev 4 days, 12 hours ago
The "_locked" suffix in scd30_trigger_handler_helper_locked() reads as
"caller holds the lock" by the usual kernel convention, but the helper
takes state->lock itself via guard(mutex). Dropping the suffix (or
renaming to e.g. scd30_read_meas_data()) would match the actual
ownership.

Also, the int arr_size argument is forwarded straight to memcpy()
so it is really a byte count, not an array length -- size_t bytes
would describe it more accurately and avoid a future caller passing
ARRAY_SIZE().

Stepan
Re: [PATCH v2] iio: chemical: scd30: Replace manual locking with RAII locking
Posted by Joshua Crofts 5 days, 11 hours ago
On Tue, 19 May 2026 at 15:28, Maxwell Doose <m32285159@gmail.com> wrote:
>
> scd30_core.c currently uses manual mutex_lock() and mutex_unlock()
> calls. Replace them with the newer guard(mutex)() for cleaner RAII
> patterns and to improve maintainability.
>
> Add new helper function scd30_trigger_handler_helper_locked() containing
> the critical section for scd30_trigger_handler().
>
> In addition, small refactor to replace "?:" operator with regular
> if/else returns.
>
> Signed-off-by: Maxwell Doose <m32285159@gmail.com>
> ---

LGTM, Sashiko also seems to be alright with it.

On another note - this driver sure needs cleaning up.

Reviewed-by: Joshua Crofts <joshua.crofts1@gmail.com>

-- 
Kind regards

CJD
Re: [PATCH v2] iio: chemical: scd30: Replace manual locking with RAII locking
Posted by Maxwell Doose 5 days, 3 hours ago
On Tue, May 19, 2026 at 8:41 AM Joshua Crofts <joshua.crofts1@gmail.com> wrote:
>
> On Tue, 19 May 2026 at 15:28, Maxwell Doose <m32285159@gmail.com> wrote:
> >
> > scd30_core.c currently uses manual mutex_lock() and mutex_unlock()
> > calls. Replace them with the newer guard(mutex)() for cleaner RAII
> > patterns and to improve maintainability.
> >
> > Add new helper function scd30_trigger_handler_helper_locked() containing
> > the critical section for scd30_trigger_handler().
> >
> > In addition, small refactor to replace "?:" operator with regular
> > if/else returns.
> >
> > Signed-off-by: Maxwell Doose <m32285159@gmail.com>
> > ---
>
> LGTM, Sashiko also seems to be alright with it.
>
> On another note - this driver sure needs cleaning up.
>

Agreed. I've stated previously that it may be a good idea to get some
newer kernel devs in here since there's a lot of things worth patching
in here (when I say newer kernel devs, I don't mean the absolute
beginners. should've at least submitted a patch in staging).

best regards,
max
> Reviewed-by: Joshua Crofts <joshua.crofts1@gmail.com>
>
> --
> Kind regards
>
> CJD