summaryrefslogtreecommitdiff
path: root/drivers/soc
diff options
context:
space:
mode:
authorIwona Winiarska <iwona.winiarska@intel.com>2021-12-03 16:36:13 +0300
committerIwona Winiarska <iwona.winiarska@intel.com>2021-12-15 15:11:48 +0300
commit60fde6cf7114cce8285fdef4f91d97b8f3bf4b42 (patch)
tree0ffb3a35bf0e81bcc04ba6dfb8020412344e65ee /drivers/soc
parent3c306f12ffb53ff5d3d1d448f2aeedda75b01acf (diff)
downloadlinux-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.c78
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++)