[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]>