]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
net: wwan: t7xx: Fix FSM command timeout issue
authorJinjian Song <jinjian.song@fibocom.com>
Tue, 24 Dec 2024 04:15:52 +0000 (12:15 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 9 Jan 2025 12:30:02 +0000 (13:30 +0100)
[ Upstream commit 4f619d518db9cd1a933c3a095a5f95d0c1584ae8 ]

When driver processes the internal state change command, it use an
asynchronous thread to process the command operation. If the main
thread detects that the task has timed out, the asynchronous thread
will panic when executing the completion notification because the
main thread completion object has been released.

BUG: unable to handle page fault for address: fffffffffffffff8
PGD 1f283a067 P4D 1f283a067 PUD 1f283c067 PMD 0
Oops: 0000 [#1] PREEMPT SMP NOPTI
RIP: 0010:complete_all+0x3e/0xa0
[...]
Call Trace:
 <TASK>
 ? __die_body+0x68/0xb0
 ? page_fault_oops+0x379/0x3e0
 ? exc_page_fault+0x69/0xa0
 ? asm_exc_page_fault+0x22/0x30
 ? complete_all+0x3e/0xa0
 fsm_main_thread+0xa3/0x9c0 [mtk_t7xx (HASH:1400 5)]
 ? __pfx_autoremove_wake_function+0x10/0x10
 kthread+0xd8/0x110
 ? __pfx_fsm_main_thread+0x10/0x10 [mtk_t7xx (HASH:1400 5)]
 ? __pfx_kthread+0x10/0x10
 ret_from_fork+0x38/0x50
 ? __pfx_kthread+0x10/0x10
 ret_from_fork_asm+0x1b/0x30
 </TASK>
[...]
CR2: fffffffffffffff8
---[ end trace 0000000000000000 ]---

Use the reference counter to ensure safe release as Sergey suggests:
https://lore.kernel.org/all/da90f64c-260a-4329-87bf-1f9ff20a5951@gmail.com/

Fixes: 13e920d93e37 ("net: wwan: t7xx: Add core components")
Signed-off-by: Jinjian Song <jinjian.song@fibocom.com>
Acked-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
Link: https://patch.msgid.link/20241224041552.8711-1-jinjian.song@fibocom.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/net/wwan/t7xx/t7xx_state_monitor.c
drivers/net/wwan/t7xx/t7xx_state_monitor.h

index 0bcca08ff2bd08674e64f963a1aeaf8da52183b4..44a2081c5249b86ecadbc63dc8967e351e0f0eba 100644 (file)
@@ -97,14 +97,21 @@ void t7xx_fsm_broadcast_state(struct t7xx_fsm_ctl *ctl, enum md_state state)
        fsm_state_notify(ctl->md, state);
 }
 
+static void fsm_release_command(struct kref *ref)
+{
+       struct t7xx_fsm_command *cmd = container_of(ref, typeof(*cmd), refcnt);
+
+       kfree(cmd);
+}
+
 static void fsm_finish_command(struct t7xx_fsm_ctl *ctl, struct t7xx_fsm_command *cmd, int result)
 {
        if (cmd->flag & FSM_CMD_FLAG_WAIT_FOR_COMPLETION) {
-               *cmd->ret = result;
-               complete_all(cmd->done);
+               cmd->result = result;
+               complete_all(&cmd->done);
        }
 
-       kfree(cmd);
+       kref_put(&cmd->refcnt, fsm_release_command);
 }
 
 static void fsm_del_kf_event(struct t7xx_fsm_event *event)
@@ -387,7 +394,6 @@ static int fsm_main_thread(void *data)
 
 int t7xx_fsm_append_cmd(struct t7xx_fsm_ctl *ctl, enum t7xx_fsm_cmd_state cmd_id, unsigned int flag)
 {
-       DECLARE_COMPLETION_ONSTACK(done);
        struct t7xx_fsm_command *cmd;
        unsigned long flags;
        int ret;
@@ -399,11 +405,13 @@ int t7xx_fsm_append_cmd(struct t7xx_fsm_ctl *ctl, enum t7xx_fsm_cmd_state cmd_id
        INIT_LIST_HEAD(&cmd->entry);
        cmd->cmd_id = cmd_id;
        cmd->flag = flag;
+       kref_init(&cmd->refcnt);
        if (flag & FSM_CMD_FLAG_WAIT_FOR_COMPLETION) {
-               cmd->done = &done;
-               cmd->ret = &ret;
+               init_completion(&cmd->done);
+               kref_get(&cmd->refcnt);
        }
 
+       kref_get(&cmd->refcnt);
        spin_lock_irqsave(&ctl->command_lock, flags);
        list_add_tail(&cmd->entry, &ctl->command_queue);
        spin_unlock_irqrestore(&ctl->command_lock, flags);
@@ -413,11 +421,11 @@ int t7xx_fsm_append_cmd(struct t7xx_fsm_ctl *ctl, enum t7xx_fsm_cmd_state cmd_id
        if (flag & FSM_CMD_FLAG_WAIT_FOR_COMPLETION) {
                unsigned long wait_ret;
 
-               wait_ret = wait_for_completion_timeout(&done,
+               wait_ret = wait_for_completion_timeout(&cmd->done,
                                                       msecs_to_jiffies(FSM_CMD_TIMEOUT_MS));
-               if (!wait_ret)
-                       return -ETIMEDOUT;
 
+               ret = wait_ret ? cmd->result : -ETIMEDOUT;
+               kref_put(&cmd->refcnt, fsm_release_command);
                return ret;
        }
 
index b1af0259d4c557ee7472c7bcfa82bf39b478f51c..d9f2522e6b2c4d9a8d2d8a909bda02576de1f9ae 100644 (file)
@@ -107,8 +107,9 @@ struct t7xx_fsm_command {
        struct list_head        entry;
        enum t7xx_fsm_cmd_state cmd_id;
        unsigned int            flag;
-       struct completion       *done;
-       int                     *ret;
+       struct completion       done;
+       int                     result;
+       struct kref             refcnt;
 };
 
 struct t7xx_fsm_notifier {