diff options
author | Iwona Winiarska <iwona.winiarska@intel.com> | 2021-12-03 16:36:13 +0300 |
---|---|---|
committer | Iwona Winiarska <iwona.winiarska@intel.com> | 2021-12-15 15:11:48 +0300 |
commit | 60fde6cf7114cce8285fdef4f91d97b8f3bf4b42 (patch) | |
tree | 0ffb3a35bf0e81bcc04ba6dfb8020412344e65ee /drivers/soc | |
parent | 3c306f12ffb53ff5d3d1d448f2aeedda75b01acf (diff) | |
download | linux-60fde6cf7114cce8285fdef4f91d97b8f3bf4b42.tar.xz |
soc: aspeed: lpc-mbox: Avoid calling kfifo_to_user in atomic context
kfifo_to_user() can sleep, which means that calling it in atomic context
can cause deadlock.
aspeed-lpc-mbox is calling it under spin_lock_irqsave(), causing the
following lockdep splat:
[ 44.939994] BUG: sleeping function called from invalid context at include/linux/uaccess.h:174
[ 44.949549] in_atomic(): 1, irqs_disabled(): 128, non_block: 0, pid: 282, name: host-misc-manag
[ 44.959280] INFO: lockdep is turned off.
[ 44.963657] irq event stamp: 5950
[ 44.967361] hardirqs last enabled at (5949): [<802ba0cc>] get_page_from_freelist+0xf1c/0x1358
[ 44.977005] hardirqs last disabled at (5950): [<80a605e4>] _raw_spin_lock_irqsave+0x80/0x84
[ 44.986350] softirqs last enabled at (5908): [<80838a80>] release_sock+0x8c/0xa8
[ 44.994723] softirqs last disabled at (5906): [<80838a1c>] release_sock+0x28/0xa8
[ 45.003099] CPU: 0 PID: 282 Comm: host-misc-manag Tainted: G W 5.15.0-14fcc87-dirty-7b64388 #1
[ 45.014198] Hardware name: Generic DT based system
[ 45.019553] Backtrace:
[ 45.022296] [<80a48e84>] (dump_backtrace) from [<80a49268>] (show_stack+0x20/0x24)
[ 45.030771] r7:8231dcf0 r6:00000080 r5:80cb88f4 r4:600f0093
[ 45.037094] [<80a49248>] (show_stack) from [<80a5592c>] (dump_stack_lvl+0x60/0x78)
[ 45.045565] [<80a558cc>] (dump_stack_lvl) from [<80a5595c>] (dump_stack+0x18/0x1c)
[ 45.054037] r7:8231dcf0 r6:000000ae r5:80c9b97c r4:848ea000
[ 45.060360] [<80a55944>] (dump_stack) from [<80160d3c>] (___might_sleep+0x1b0/0x2c0)
[ 45.069015] [<80160b8c>] (___might_sleep) from [<80160ebc>] (__might_sleep+0x70/0xac)
[ 45.077779] r6:00000000 r5:000000ae r4:80c9b97c
[ 45.082938] [<80160e4c>] (__might_sleep) from [<802a1cf8>] (__might_fault+0x48/0xb0)
[ 45.091606] r6:02363388 r5:00000000 r4:ffffe000
[ 45.096756] [<802a1cb0>] (__might_fault) from [<8058d564>] (kfifo_copy_to_user+0x74/0x1dc)
[ 45.106006] r5:00000020 r4:00000020
[ 45.110002] [<8058d4f0>] (kfifo_copy_to_user) from [<8058dc04>] (__kfifo_to_user+0x58/0x78)
[ 45.119340] r9:848ea000 r8:02363388 r7:00000020 r6:848ebe90 r5:00000020 r4:8231dcf0
[ 45.127992] [<8058dbac>] (__kfifo_to_user) from [<8060fc40>] (aspeed_mbox_read+0xe0/0x2d8)
[ 45.137242] r7:848ebe90 r6:8231dd04 r5:200f0013 r4:00000020
[ 45.143567] [<8060fb60>] (aspeed_mbox_read) from [<802d6024>] (vfs_read+0xc0/0x320)
[ 45.152149] r10:00000003 r9:848ea000 r8:8060fb60 r7:00000020 r6:00000001 r5:848ebf68
[ 45.160897] r4:848a9b40
[ 45.163727] [<802d5f64>] (vfs_read) from [<802d6e04>] (ksys_read+0x70/0xf4)
[ 45.171529] r10:00000003 r9:848ea000 r8:80100224 r7:00000000 r6:00000000 r5:848a9b40
[ 45.180277] r4:848a9b40
[ 45.183108] [<802d6d94>] (ksys_read) from [<802d6ea0>] (sys_read+0x18/0x1c)
[ 45.190906] r7:00000003 r6:7e9f9ad8 r5:76f26070 r4:0236357c
[ 45.197229] [<802d6e88>] (sys_read) from [<80100060>] (ret_fast_syscall+0x0/0x1c)
[ 45.205602] Exception stack(0x848ebfa8 to 0x848ebff0)
[ 45.211258] bfa0: 0236357c 76f26070 00000009 02363388 00000020 00000000
[ 45.220401] bfc0: 0236357c 76f26070 7e9f9ad8 00000003 02366874 02363388 00000020 7e9f9b30
[ 45.229538] bfe0: 00526be4 7e9f9ab0 00504b88 76c1836c
Rearrange the code to use mutex instead of spinlock to protect
kifo_to_user, since we only need to serialize against other callers of
aspeed_mbox_read().
Move kfifo_reset under spinlock to satisfy kfifo locking rules.
Remove redundant MBX_USE_INTERRUPT.
Signed-off-by: Iwona Winiarska <iwona.winiarska@intel.com>
Change-Id: I4b3fc32d65f2614bec92460bda55daded99e096d
Diffstat (limited to 'drivers/soc')
-rw-r--r-- | drivers/soc/aspeed/aspeed-lpc-mbox.c | 78 |
1 files changed, 33 insertions, 45 deletions
diff --git a/drivers/soc/aspeed/aspeed-lpc-mbox.c b/drivers/soc/aspeed/aspeed-lpc-mbox.c index 76f0c7933bf7..7941792abacb 100644 --- a/drivers/soc/aspeed/aspeed-lpc-mbox.c +++ b/drivers/soc/aspeed/aspeed-lpc-mbox.c @@ -17,8 +17,6 @@ #define DEVICE_NAME "aspeed-mbox" -#define MBX_USE_INTERRUPT 1 - #define ASPEED_MBOX_CTRL_RECV BIT(7) #define ASPEED_MBOX_CTRL_MASK BIT(1) #define ASPEED_MBOX_CTRL_SEND BIT(0) @@ -118,26 +116,22 @@ static void put_fifo_with_discard(struct aspeed_mbox *mbox, u8 val) static int aspeed_mbox_open(struct inode *inode, struct file *file) { -#if MBX_USE_INTERRUPT struct aspeed_mbox *mbox = file_mbox(file); int i; -#endif if (atomic_inc_return(&aspeed_mbox_open_count) == 1) { -#if MBX_USE_INTERRUPT /* * Reset the FIFO while opening to clear the old cached data * and load the FIFO with latest mailbox register values. */ - kfifo_reset(&mbox->fifo); spin_lock_irq(&mbox->lock); + kfifo_reset(&mbox->fifo); for (i = 0; i < mbox->configs.num_regs; i++) { put_fifo_with_discard(mbox, aspeed_mbox_inb(mbox, mbox->configs.data_offset + (i * 4))); } spin_unlock_irq(&mbox->lock); -#endif return 0; } @@ -149,10 +143,9 @@ static ssize_t aspeed_mbox_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct aspeed_mbox *mbox = file_mbox(file); + unsigned int copied = 0; char __user *p = buf; ssize_t ret; - unsigned int copied; - unsigned long flags; int i; if (!access_ok(buf, count)) @@ -161,50 +154,47 @@ static ssize_t aspeed_mbox_read(struct file *file, char __user *buf, if (count + *ppos > mbox->configs.num_regs) return -EINVAL; -#if MBX_USE_INTERRUPT - /* - * Restrict count as per the number of mailbox registers - * to use kfifo. - */ - if (count != mbox->configs.num_regs) - goto reg_read; + /* Restrict count as per the number of mailbox registers to use kfifo. */ + if (count != mbox->configs.num_regs) { + mutex_lock(&mbox->mutex); + for (i = *ppos; count > 0 && i < mbox->configs.num_regs; i++) { + u8 reg = aspeed_mbox_inb(mbox, mbox->configs.data_offset + (i * 4)); - if (kfifo_is_empty(&mbox->fifo)) { - if (file->f_flags & O_NONBLOCK){ - return -EAGAIN; - } - ret = wait_event_interruptible(mbox->queue, - !kfifo_is_empty(&mbox->fifo)); - if (ret == -ERESTARTSYS){ - return -EINTR; - } - } + ret = __put_user(reg, p); + if (ret) + goto out_unlock; - spin_lock_irqsave(&mbox->lock, flags); - ret = kfifo_to_user(&mbox->fifo, buf, count, &copied); - spin_unlock_irqrestore(&mbox->lock, flags); - return ret ? ret : copied; - -#endif + p++; + count--; + } + mutex_unlock(&mbox->mutex); + return p - buf; + } -reg_read: mutex_lock(&mbox->mutex); - - for (i = *ppos; count > 0 && i < mbox->configs.num_regs; i++) { - uint8_t reg = aspeed_mbox_inb(mbox, mbox->configs.data_offset + (i * 4)); - - ret = __put_user(reg, p); - if (ret) + if (kfifo_is_empty(&mbox->fifo)) { + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; goto out_unlock; + } - p++; - count--; + ret = wait_event_interruptible(mbox->queue, !kfifo_is_empty(&mbox->fifo)); + if (ret == -ERESTARTSYS) { + ret = -EINTR; + goto out_unlock; + } } - ret = p - buf; + + /* + * Kfifo allows single reader to access the kfifo concurrently with + * single writer, which means that we only need to serialize against + * other callers of aspeed_mbox_read. + */ + ret = kfifo_to_user(&mbox->fifo, buf, count, &copied); out_unlock: mutex_unlock(&mbox->mutex); - return ret; + return ret ? ret : copied; } static ssize_t aspeed_mbox_write(struct file *file, const char __user *buf, @@ -293,7 +283,6 @@ static const struct file_operations aspeed_mbox_fops = { static irqreturn_t aspeed_mbox_irq(int irq, void *arg) { struct aspeed_mbox *mbox = arg; -#if MBX_USE_INTERRUPT int i; dev_dbg(mbox->miscdev.parent, "BMC_CTRL11: 0x%02x\n", @@ -313,7 +302,6 @@ static irqreturn_t aspeed_mbox_irq(int irq, void *arg) aspeed_mbox_inb(mbox, mbox->configs.data_offset + (i * 4))); } spin_unlock(&mbox->lock); -#endif /* Clear interrupt status */ for (i = 0; i < mbox->configs.num_regs / 8; i++) |