[PATCH] HSI: cmt_speech: fix lost wakeup in cs_char_read()

FAN YE <[email protected]>
Newsgroups org.kernel.feeds.b4-sent,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
A reader can sleep past an available message in cs_char_read(): the
queue check happens before prepare_to_wait_exclusive() queues the
task, and cs_notify() only wakes tasks already on csdata->wait, so a
message queued in between wakes nobody and the reader waits for
whichever message shows up next. cs_notify() runs from ssi_pio_thread(),
so it can land in that window on SMP, or on a preemptible kernel even
with a single CPU.

Re-check the queues after being queued and skip schedule() if one
already has an entry, mirroring what wait_event() does internally.

Fixes: 7f62fe8a5851 ("HSI: cmt_speech: Add cmt-speech driver")
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: FAN YE <[email protected]>
---
Reproduced under QEMU/TCG with a diagnostic build. This driver has no
hardware and there is no virtual HSI controller, so the harness hands
cs_hsi_client_probe() a fake hsi_client and calls cs_notify() from an RT
kthread woken while the reader still holds csdata->lock -- the shape of
ssi_pio_thread(), which is what runs msg->complete() on omap_ssi.
waitqueue_active() is read inside cs_notify() just before
wake_up_interruptible(). One CPU throughout:

  arm, CONFIG_PREEMPT
    unpatched  waitqueue empty at the wakeup; the reader then sleeps in
               cs_char_read() on the already queued message and wakes
               only when an unrelated one arrives -- 6.0 s here, which
               is simply when the test sends it
    patched    same lost wakeup, read() returns at once (0-1 ms)
  arm, CONFIG_PREEMPT_VOLUNTARY
    both       waitqueue not empty, so the race cannot form on one CPU:
               without CONFIG_PREEMPTION spin_unlock_bh() is not a
               preemption point and the kthread runs only once the
               reader is already queued; read() returns at once
  x86_64, preempt=full and preempt=lazy
    both       as arm CONFIG_PREEMPT

Rebuilt and re-run from scratch on a second machine, same results.
Built W=1 for arm/omap2plus and x86_64; checkpatch --strict clean.
---
 drivers/hsi/clients/cmt_speech.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/drivers/hsi/clients/cmt_speech.c b/drivers/hsi/clients/cmt_speech.c
index 7226677ebde7..5be4a4477c44 100644
--- a/drivers/hsi/clients/cmt_speech.c
+++ b/drivers/hsi/clients/cmt_speech.c
@@ -1132,6 +1132,7 @@ static ssize_t cs_char_read(struct file *file, char __user *buf, size_t count,
 	struct cs_char *csdata = file->private_data;
 	u32 data;
 	ssize_t retval;
+	bool empty;
 
 	if (count < sizeof(data))
 		return -EINVAL;
@@ -1161,7 +1162,18 @@ static ssize_t cs_char_read(struct file *file, char __user *buf, size_t count,
 		}
 		prepare_to_wait_exclusive(&csdata->wait, &wait,
 						TASK_INTERRUPTIBLE);
-		schedule();
+		/*
+		 * Re-check after being queued: cs_notify() may have queued
+		 * an entry and woken csdata->wait in the window between the
+		 * empty check above and prepare_to_wait_exclusive() adding
+		 * us to it.
+		 */
+		spin_lock_bh(&csdata->lock);
+		empty = list_empty(&csdata->chardev_queue) &&
+			list_empty(&csdata->dataind_queue);
+		spin_unlock_bh(&csdata->lock);
+		if (empty)
+			schedule();
 		finish_wait(&csdata->wait, &wait);
 	}
 

---
base-commit: 26260251022fbc2f248a3d747a9b2b961b18d2d8
change-id: 20260822-cmt-speech-lost-wakeup-3e1900082660

Best regards,
--  
FAN YE <[email protected]>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.