lkml.org 
[lkml]   [2022]   [Jan]   [24]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.15 056/846] iio: trigger: Fix a scheduling whilst atomic issue seen on tsc2046
    Date
    From: Jonathan Cameron <Jonathan.Cameron@huawei.com>

    commit 9020ef659885f2622cfb386cc229b6d618362895 upstream.

    IIO triggers are software IRQ chips that split an incoming IRQ into
    separate IRQs routed to all devices using the trigger.
    When all consumers are done then a trigger callback reenable() is
    called. There are a few circumstances under which this can happen
    in atomic context.

    1) A single user of the trigger that calls the iio_trigger_done()
    function from interrupt context.
    2) A race between disconnecting the last device from a trigger and
    the trigger itself sucessfully being disabled.

    To avoid a resulting scheduling whilst atomic, close this second corner
    by using schedule_work() to ensure the reenable is not done in atomic
    context.

    Note that drivers must be careful to manage the interaction of
    set_state() and reenable() callbacks to ensure appropriate reference
    counting if they are relying on the same hardware controls.

    Deliberately taking this the slow path rather than via a fixes tree
    because the error has hard to hit and I would like it to soak for a while
    before hitting a release kernel.

    Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
    Cc: Pengutronix Kernel Team <kernel@pengutronix.de>
    Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
    Tested-by: Oleksij Rempel <o.rempel@pengutronix.de>
    Cc: <Stable@vger.kernel.org>
    Link: https://lore.kernel.org/r/20211017172209.112387-1-jic23@kernel.org
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    drivers/iio/industrialio-trigger.c | 36 +++++++++++++++++++++++++++++++++++-
    include/linux/iio/trigger.h | 2 ++
    2 files changed, 37 insertions(+), 1 deletion(-)

    --- a/drivers/iio/industrialio-trigger.c
    +++ b/drivers/iio/industrialio-trigger.c
    @@ -162,6 +162,39 @@ static struct iio_trigger *iio_trigger_a
    return trig;
    }

    +static void iio_reenable_work_fn(struct work_struct *work)
    +{
    + struct iio_trigger *trig = container_of(work, struct iio_trigger,
    + reenable_work);
    +
    + /*
    + * This 'might' occur after the trigger state is set to disabled -
    + * in that case the driver should skip reenabling.
    + */
    + trig->ops->reenable(trig);
    +}
    +
    +/*
    + * In general, reenable callbacks may need to sleep and this path is
    + * not performance sensitive, so just queue up a work item
    + * to reneable the trigger for us.
    + *
    + * Races that can cause this.
    + * 1) A handler occurs entirely in interrupt context so the counter
    + * the final decrement is still in this interrupt.
    + * 2) The trigger has been removed, but one last interrupt gets through.
    + *
    + * For (1) we must call reenable, but not in atomic context.
    + * For (2) it should be safe to call reenanble, if drivers never blindly
    + * reenable after state is off.
    + */
    +static void iio_trigger_notify_done_atomic(struct iio_trigger *trig)
    +{
    + if (atomic_dec_and_test(&trig->use_count) && trig->ops &&
    + trig->ops->reenable)
    + schedule_work(&trig->reenable_work);
    +}
    +
    void iio_trigger_poll(struct iio_trigger *trig)
    {
    int i;
    @@ -173,7 +206,7 @@ void iio_trigger_poll(struct iio_trigger
    if (trig->subirqs[i].enabled)
    generic_handle_irq(trig->subirq_base + i);
    else
    - iio_trigger_notify_done(trig);
    + iio_trigger_notify_done_atomic(trig);
    }
    }
    }
    @@ -535,6 +568,7 @@ struct iio_trigger *viio_trigger_alloc(s
    trig->dev.type = &iio_trig_type;
    trig->dev.bus = &iio_bus_type;
    device_initialize(&trig->dev);
    + INIT_WORK(&trig->reenable_work, iio_reenable_work_fn);

    mutex_init(&trig->pool_lock);
    trig->subirq_base = irq_alloc_descs(-1, 0,
    --- a/include/linux/iio/trigger.h
    +++ b/include/linux/iio/trigger.h
    @@ -55,6 +55,7 @@ struct iio_trigger_ops {
    * @attached_own_device:[INTERN] if we are using our own device as trigger,
    * i.e. if we registered a poll function to the same
    * device as the one providing the trigger.
    + * @reenable_work: [INTERN] work item used to ensure reenable can sleep.
    **/
    struct iio_trigger {
    const struct iio_trigger_ops *ops;
    @@ -74,6 +75,7 @@ struct iio_trigger {
    unsigned long pool[BITS_TO_LONGS(CONFIG_IIO_CONSUMERS_PER_TRIGGER)];
    struct mutex pool_lock;
    bool attached_own_device;
    + struct work_struct reenable_work;
    };



    \
     
     \ /
      Last update: 2022-01-24 21:44    [W:4.040 / U:0.740 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site