diff options
author | Iwona Winiarska <iwona.winiarska@intel.com> | 2021-12-01 00:20:37 +0300 |
---|---|---|
committer | Iwona Winiarska <iwona.winiarska@intel.com> | 2021-12-15 14:57:09 +0300 |
commit | c1000fe8c34d287c6526041e7f890548bfba993e (patch) | |
tree | 79ce42f43023f8ade866ef133bce81d269c8c2d2 /drivers/soc | |
parent | d4457cf3ab689cd9b1848a9e96fd1a8a60b743e5 (diff) | |
download | linux-c1000fe8c34d287c6526041e7f890548bfba993e.tar.xz |
soc: aspeed: espi-slave: Avoid calling put_user in atomic context
put_user() can sleep, which means that calling it in atomic context can
cause deadlock.
aspeed-espi-slave is calling it under spin_lock_irqsave(), resulting in
the following lockdep splat:
[ 18.796723] BUG: sleeping function called from invalid context at drivers/soc/aspeed/aspeed-espi-slave.c:309
[ 18.807742] in_atomic(): 1, irqs_disabled(): 128, non_block: 0, pid: 311, name: host-misc-manag
[ 18.817486] INFO: lockdep is turned off.
[ 18.821869] irq event stamp: 0
[ 18.825289] hardirqs last enabled at (0): [<00000000>] 0x0
[ 18.831551] hardirqs last disabled at (0): [<8011ed40>] copy_process+0x7b0/0x1be8
[ 18.839941] softirqs last enabled at (0): [<8011ed40>] copy_process+0x7b0/0x1be8
[ 18.848308] softirqs last disabled at (0): [<00000000>] 0x0
[ 18.854538] CPU: 0 PID: 311 Comm: host-misc-manag Not tainted 5.15.0-14fcc87-dirty-7b64388 #1
[ 18.864070] Hardware name: Generic DT based system
[ 18.869421] Backtrace:
[ 18.872155] [<80a48ec4>] (dump_backtrace) from [<80a492a8>] (show_stack+0x20/0x24)
[ 18.880638] r7:0242c06c r6:00000080 r5:80cb88f4 r4:600d0093
[ 18.886969] [<80a49288>] (show_stack) from [<80a5596c>] (dump_stack_lvl+0x60/0x78)
[ 18.895440] [<80a5590c>] (dump_stack_lvl) from [<80a5599c>] (dump_stack+0x18/0x1c)
[ 18.903907] r7:0242c06c r6:00000135 r5:80d0a958 r4:8ffc2000
[ 18.910226] [<80a55984>] (dump_stack) from [<80160d3c>] (___might_sleep+0x1b0/0x2c0)
[ 18.918901] [<80160b8c>] (___might_sleep) from [<80160ebc>] (__might_sleep+0x70/0xac)
[ 18.927818] r6:00000000 r5:00000135 r4:80d0a958
[ 18.933024] [<80160e4c>] (__might_sleep) from [<802a1cf8>] (__might_fault+0x48/0xb0)
[ 18.941690] r6:8238da58 r5:00000000 r4:ffffe000
[ 18.946848] [<802a1cb0>] (__might_fault) from [<8060ed78>] (aspeed_espi_pltrstn_read+0xac/0x254)
[ 18.956679] r5:8238da80 r4:ffffe000
[ 18.960669] [<8060eccc>] (aspeed_espi_pltrstn_read) from [<802d6024>] (vfs_read+0xc0/0x320)
[ 18.970011] r10:00000003 r9:8ffc2000 r8:8060eccc r7:00000001 r6:00000001 r5:8ffc3f68
[ 18.978754] r4:84eb48c0
[ 18.981582] [<802d5f64>] (vfs_read) from [<802d6e04>] (ksys_read+0x70/0xf4)
[ 18.989378] r10:00000003 r9:8ffc2000 r8:80100224 r7:00000000 r6:00000000 r5:84eb48c0
[ 18.998122] r4:84eb48c0
[ 19.000946] [<802d6d94>] (ksys_read) from [<802d6ea0>] (sys_read+0x18/0x1c)
[ 19.008735] r7:00000003 r6:7ec07a00 r5:76fc3070 r4:0242c220
[ 19.015055] [<802d6e88>] (sys_read) from [<80100060>] (ret_fast_syscall+0x0/0x1c)
[ 19.023419] Exception stack(0x8ffc3fa8 to 0x8ffc3ff0)
[ 19.029063] 3fa0: 0242c220 76fc3070 00000007 0242c06c 00000001 00000000
[ 19.038196] 3fc0: 0242c220 76fc3070 7ec07a00 00000003 0242c23c 0242c06c 00000001 00000000
[ 19.047330] 3fe0: 00446be4 7ec079d8 00424b88 76cb536c
Rearrange the code to avoid calling *_user() function under spin_lock.
Also, use the regular *_irq() variant instead if *_irqsave(), since
it's only used for serializing process context with IRQ handler.
Signed-off-by: Iwona Winiarska <iwona.winiarska@intel.com>
Change-Id: Iaf17ddd17c020c8ee8c60c2fc98a57b97133e7a6
Diffstat (limited to 'drivers/soc')
-rw-r--r-- | drivers/soc/aspeed/aspeed-espi-slave.c | 44 |
1 files changed, 20 insertions, 24 deletions
diff --git a/drivers/soc/aspeed/aspeed-espi-slave.c b/drivers/soc/aspeed/aspeed-espi-slave.c index ea68734afa7b..52d900893d32 100644 --- a/drivers/soc/aspeed/aspeed-espi-slave.c +++ b/drivers/soc/aspeed/aspeed-espi-slave.c @@ -294,24 +294,17 @@ static ssize_t aspeed_espi_pltrstn_read(struct file *filp, char __user *buf, { struct aspeed_espi *priv = to_aspeed_espi(filp); DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - char old_sample; + char data, old_sample; int ret = 0; - spin_lock_irqsave(&priv->pltrstn_lock, flags); + spin_lock_irq(&priv->pltrstn_lock); if (filp->f_flags & O_NONBLOCK) { if (!priv->pltrstn_in_avail) { ret = -EAGAIN; goto out_unlock; } - char data = priv->pltrstn; - ret = put_user(data, (unsigned long __user *)buf); - if (!ret){ - ret = sizeof(data); - } else{ - ret = -EAGAIN; - } + data = priv->pltrstn; priv->pltrstn_in_avail = false; } else { add_wait_queue(&priv->pltrstn_waitq, &wait); @@ -320,22 +313,18 @@ static ssize_t aspeed_espi_pltrstn_read(struct file *filp, char __user *buf, old_sample = priv->pltrstn; do { - char new_sample = priv->pltrstn; - - if (old_sample != new_sample) { - ret = put_user(new_sample, - (unsigned long __user *)buf); - if (!ret) - ret = sizeof(new_sample); - } else if (signal_pending(current)) { - ret = -ERESTARTSYS; + if (old_sample != priv->pltrstn) { + data = priv->pltrstn; + priv->pltrstn_in_avail = false; + break; } - if (!ret) { - spin_unlock_irqrestore(&priv->pltrstn_lock, - flags); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + } else { + spin_unlock_irq(&priv->pltrstn_lock); schedule(); - spin_lock_irqsave(&priv->pltrstn_lock, flags); + spin_lock_irq(&priv->pltrstn_lock); } } while (!ret); @@ -343,7 +332,14 @@ static ssize_t aspeed_espi_pltrstn_read(struct file *filp, char __user *buf, set_current_state(TASK_RUNNING); } out_unlock: - spin_unlock_irqrestore(&priv->pltrstn_lock, flags); + spin_unlock_irq(&priv->pltrstn_lock); + + if (ret) + return ret; + + ret = put_user(data, buf); + if (!ret) + ret = sizeof(data); return ret; } |