lkml.org 
[lkml]   [2021]   [Nov]   [15]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.10 161/575] USB: chipidea: fix interrupt deadlock
    Date
    From: Johan Hovold <johan@kernel.org>

    commit 9aaa81c3366e8393a62374e3a1c67c69edc07b8a upstream.

    Chipidea core was calling the interrupt handler from non-IRQ context
    with interrupts enabled, something which can lead to a deadlock if
    there's an actual interrupt trying to take a lock that's already held
    (e.g. the controller lock in udc_irq()).

    Add a wrapper that can be used to fake interrupts instead of calling the
    handler directly.

    Fixes: 3ecb3e09b042 ("usb: chipidea: Use extcon framework for VBUS and ID detect")
    Fixes: 876d4e1e8298 ("usb: chipidea: core: add wakeup support for extcon")
    Cc: Peter Chen <peter.chen@kernel.org>
    Cc: stable@vger.kernel.org # 4.4
    Signed-off-by: Johan Hovold <johan@kernel.org>
    Link: https://lore.kernel.org/r/20211021083447.20078-1-johan@kernel.org
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    drivers/usb/chipidea/core.c | 23 ++++++++++++++++-------
    1 file changed, 16 insertions(+), 7 deletions(-)

    --- a/drivers/usb/chipidea/core.c
    +++ b/drivers/usb/chipidea/core.c
    @@ -509,7 +509,7 @@ int hw_device_reset(struct ci_hdrc *ci)
    return 0;
    }

    -static irqreturn_t ci_irq(int irq, void *data)
    +static irqreturn_t ci_irq_handler(int irq, void *data)
    {
    struct ci_hdrc *ci = data;
    irqreturn_t ret = IRQ_NONE;
    @@ -562,6 +562,15 @@ static irqreturn_t ci_irq(int irq, void
    return ret;
    }

    +static void ci_irq(struct ci_hdrc *ci)
    +{
    + unsigned long flags;
    +
    + local_irq_save(flags);
    + ci_irq_handler(ci->irq, ci);
    + local_irq_restore(flags);
    +}
    +
    static int ci_cable_notifier(struct notifier_block *nb, unsigned long event,
    void *ptr)
    {
    @@ -571,7 +580,7 @@ static int ci_cable_notifier(struct noti
    cbl->connected = event;
    cbl->changed = true;

    - ci_irq(ci->irq, ci);
    + ci_irq(ci);
    return NOTIFY_DONE;
    }

    @@ -612,7 +621,7 @@ static int ci_usb_role_switch_set(struct
    if (cable) {
    cable->changed = true;
    cable->connected = false;
    - ci_irq(ci->irq, ci);
    + ci_irq(ci);
    spin_unlock_irqrestore(&ci->lock, flags);
    if (ci->wq && role != USB_ROLE_NONE)
    flush_workqueue(ci->wq);
    @@ -630,7 +639,7 @@ static int ci_usb_role_switch_set(struct
    if (cable) {
    cable->changed = true;
    cable->connected = true;
    - ci_irq(ci->irq, ci);
    + ci_irq(ci);
    }
    spin_unlock_irqrestore(&ci->lock, flags);
    pm_runtime_put_sync(ci->dev);
    @@ -1166,7 +1175,7 @@ static int ci_hdrc_probe(struct platform
    }
    }

    - ret = devm_request_irq(dev, ci->irq, ci_irq, IRQF_SHARED,
    + ret = devm_request_irq(dev, ci->irq, ci_irq_handler, IRQF_SHARED,
    ci->platdata->name, ci);
    if (ret)
    goto stop;
    @@ -1287,11 +1296,11 @@ static void ci_extcon_wakeup_int(struct

    if (!IS_ERR(cable_id->edev) && ci->is_otg &&
    (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS))
    - ci_irq(ci->irq, ci);
    + ci_irq(ci);

    if (!IS_ERR(cable_vbus->edev) && ci->is_otg &&
    (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS))
    - ci_irq(ci->irq, ci);
    + ci_irq(ci);
    }

    static int ci_controller_resume(struct device *dev)

    \
     
     \ /
      Last update: 2021-11-15 20:40    [W:2.040 / U:1.540 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site