Skip to content

Conversation

@Cuda-Chen
Copy link
Collaborator

@Cuda-Chen Cuda-Chen commented Nov 17, 2025

Summary by cubic

Implements VirtIO sound capture (RX) with PortAudio input, RX virtqueue handling, and circular buffering so input audio can be delivered to the guest. Also exposes separate input/output streams with accurate device info.

  • New Features
    • Added RX virtqueue handlers (normal/flush) with default flush stream_id = 1.
    • Opened PortAudio input stream and RX callback to capture frames and enqueue to the driver.
    • Implemented circular intermediate buffer (buf_sz, buf_idx) and RX enqueue/dequeue helpers.
    • Exposed two streams (output + input), mono S16, with per-stream jack/pcm/chmap info.
    • Added TX and RX worker threads, mutex/cond vars, and VSND_QUEUE_TX/RX notifications.
    • Applied direction-aware flush in pcm_release and set RX response length/status.
    • Added RX start gating to handle Linux driver pre-buffering and only dequeue after pcm_start.
    • Added CI sound tests for playback and capture and wired them into GitHub Actions.

Written for commit 8c68308. Summary will update automatically on new commits.

Copy link
Collaborator

@jserv jserv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebase to avoid unnecessary merge commits.

@jserv

This comment was marked as resolved.

@Cuda-Chen Cuda-Chen force-pushed the add-virtio-snd-rx branch 2 times, most recently from 54a45b5 to 05b22f9 Compare November 17, 2025 10:31
virtio-snd.c Outdated
Comment on lines 1200 to 1217
static void __virtio_snd_rx_frame_enqueue(void *payload,
uint32_t n,
uint32_t stream_id)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

__virtio_snd_rx_frame_enqueue() never increments buf_ev_notity or signals the condition variable, but __virtio_snd_rx_frame_dequeue() waits on it:

  // In __virtio_snd_rx_frame_dequeue:
  while (props->lock.buf_ev_notity < 1)
      pthread_cond_wait(&props->lock.readable, ...);  // BLOCKS FOREVER

The TX path correctly does:

  props->lock.buf_ev_notity++;
  pthread_cond_signal(&props->lock.readable);

Fix: Add after list_push() in __virtio_snd_rx_frame_enqueue:

  props->lock.buf_ev_notity++;
  pthread_cond_signal(&props->lock.readable);

Copy link
Collaborator Author

@Cuda-Chen Cuda-Chen Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The buf_ev_notity will be increased and then signals the update of CV after consuming all the virtq elements when calling virtio_snd_rx_desc_normal_handler():

// In VSND_GEN_RX_QUEUE_HANDLER macro

virtio_snd_prop_t *props = &vsnd_props[stream_id];                   
props->lock.buf_ev_notity--;                                         
pthread_cond_signal(&props->lock.writable);

The virtio_snd_tx_desc_normal_handler() also has the same design.

The reason is that the driver should populate a buffer size of PCM frames (except the end of stream) as mentioned by VirtIO standard:

5.14.6.8.2.2 Driver Requirements: Input Stream

  • The driver SHOULD populate the rx queue with period_bytes sized empty buffers before starting the stream.
  • The driver MUST NOT place device-readable buffers into the rx queue.

Comment on lines +995 to +1028
static void __virtio_snd_rx_frame_dequeue(void *out,
uint32_t n,
uint32_t stream_id)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

__virtio_snd_rx_frame_dequeue() does list_del(&node->q) but never free(node):

  if (node->pos >= node->len)
      list_del(&node->q);  // Missing: free(node);

@jserv
Copy link
Collaborator

jserv commented Dec 17, 2025

Debug Printf Pollution
Multiple fprintf(stderr, ...) debug statements throughout the RX path should be removed before merge.

@Cuda-Chen Cuda-Chen force-pushed the add-virtio-snd-rx branch 2 times, most recently from be341b2 to 1a14913 Compare December 17, 2025 06:16
@jserv
Copy link
Collaborator

jserv commented Dec 17, 2025

Can we perform loopback tests with virtio sound device?

@Cuda-Chen
Copy link
Collaborator Author

Cuda-Chen commented Dec 17, 2025

Can we perform loopback tests with virtio sound device?

You mean aloop or a device loopback testing command like arecord -C | aplay -?

@jserv
Copy link
Collaborator

jserv commented Dec 17, 2025

You mean aloop of a device loopback testing command like arecord -C | aplay -?

The headless verification is crucial for CI/CD integration.

@Cuda-Chen Cuda-Chen force-pushed the add-virtio-snd-rx branch 3 times, most recently from fd4f538 to a2348ec Compare December 18, 2025 11:02
@Cuda-Chen Cuda-Chen marked this pull request as ready for review December 18, 2025 11:46
@Cuda-Chen Cuda-Chen requested a review from jserv December 18, 2025 11:46
cubic-dev-ai[bot]

This comment was marked as resolved.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 4 files

Prompt for AI agents (all 3 issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="virtio-snd.c">

<violation number="1" location="virtio-snd.c:373">
P2: The global `rx_ev_start` has a data race - it&#39;s read in the RX thread (in `__virtio_snd_rx_frame_dequeue`) while being written from the control path without synchronization. Consider using atomic operations or protecting access with `props-&gt;lock.lock` mutex.</violation>
</file>

<file name="README.md">

<violation number="1" location="README.md:17">
P3: Grammar error: &quot;until `semu` being rebooted&quot; should be &quot;until `semu` is rebooted&quot;.</violation>

<violation number="2" location="README.md:21">
P3: Grammar error: subject-verb agreement. &quot;ALSA usually get stuck&quot; should be &quot;ALSA usually gets stuck&quot;.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

@sysprog21 sysprog21 deleted a comment from cubic-dev-ai bot Dec 19, 2025
Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 issues found across 4 files

Prompt for AI agents (all 5 issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="README.md">

<violation number="1" location="README.md:17">
P3: Grammar issue: &#39;stuck&#39; should be &#39;to get stuck&#39; and &#39;being rebooted&#39; should be &#39;is rebooted&#39;.</violation>

<violation number="2" location="README.md:21">
P3: Subject-verb agreement error: &quot;ALSA usually get stuck&quot; should be &quot;ALSA usually gets stuck&quot;.</violation>
</file>

<file name=".github/workflows/main.yml">

<violation number="1" location=".github/workflows/main.yml:72">
P1: The sound test runs unconditionally for all matrix variants, including when `matrix.dependency == &#39;none&#39;` where no sound library is installed. This will likely cause the test to fail. Consider adding a condition similar to the install step.</violation>
</file>

<file name="virtio-snd.c">

<violation number="1" location="virtio-snd.c:857">
P1: Potential double-free: `props-&gt;intermediate` is freed in error path but not set to NULL. If `virtio_snd_read_pcm_release` is called later (state is already PREPARE), it will free the same pointer again.</violation>

<violation number="2" location="virtio-snd.c:1225">
P1: Missing NULL checks after malloc. If memory allocation fails, dereferencing `node` or `node-&gt;addr` will cause a crash.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

run: sudo .ci/test-netdev.sh
shell: bash
timeout-minutes: 10
- name: sound test
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: The sound test runs unconditionally for all matrix variants, including when matrix.dependency == 'none' where no sound library is installed. This will likely cause the test to fail. Consider adding a condition similar to the install step.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/main.yml, line 72:

<comment>The sound test runs unconditionally for all matrix variants, including when `matrix.dependency == &#39;none&#39;` where no sound library is installed. This will likely cause the test to fail. Consider adding a condition similar to the install step.</comment>

<file context>
@@ -69,6 +69,10 @@ jobs:
       run: sudo .ci/test-netdev.sh
       shell: bash
       timeout-minutes: 10
+    - name: sound test
+      run: .ci/test-sound.sh
+      shell: bash
</file context>
Suggested change
- name: sound test
- name: sound test
if: matrix.dependency != 'none'
Fix with Cubic

cubic-dev-ai[bot]

This comment was marked as resolved.

cubic-dev-ai[bot]

This comment was marked as resolved.

cubic-dev-ai[bot]

This comment was marked as resolved.

@sysprog21 sysprog21 deleted a comment from cubic-dev-ai bot Dec 22, 2025
cubic-dev-ai[bot]

This comment was marked as resolved.

cubic-dev-ai[bot]

This comment was marked as resolved.

cubic-dev-ai[bot]

This comment was marked as resolved.

cubic-dev-ai[bot]

This comment was marked as resolved.

cubic-dev-ai[bot]

This comment was marked as resolved.

@sysprog21 sysprog21 deleted a comment from cubic-dev-ai bot Dec 22, 2025
@sysprog21 sysprog21 deleted a comment from cubic-dev-ai bot Dec 22, 2025
Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 4 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name=".ci/test-sound.sh">

<violation number="1" location=".ci/test-sound.sh:47">
P1: Exit code is captured from `echo` instead of `test_sound`. With `set -e`, if `test_sound` fails the script exits immediately, making the error messages dead code. The exit status should be captured right after `test_sound`.</violation>
</file>

<file name="README.md">

<violation number="1" location="README.md:16">
P3: Grammatical issue: The sentence structure is broken. &quot;As a confirmed issue that... causing... will get stuck&quot; doesn&#39;t form a proper sentence. Consider rephrasing to: &quot;Due to a confirmed issue where `semu` cannot send/receive PCM frames in time, the ALSA stack may get stuck in XRUN state until you reboot `semu`.&quot;</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 4 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="README.md">

<violation number="1" location="README.md:16">
P3: This sentence has grammatical issues that make it hard to parse. Consider restructuring to: &quot;Due to a confirmed issue, `semu` cannot send/receive PCM frames in time, causing the ALSA stack to get stuck in XRUN state until you reboot `semu`.&quot;</violation>
</file>

<file name="virtio-snd.c">

<violation number="1" location="virtio-snd.c:1230">
P1: PortAudio callbacks must be non-blocking. `__virtio_snd_rx_frame_enqueue` uses `pthread_cond_wait` which can block indefinitely when called from the real-time audio callback `virtio_snd_rx_stream_cb`. This can cause audio glitches, buffer underruns, or deadlocks. Consider using a lock-free ring buffer or non-blocking queue for the audio callback path.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

virtio_snd_prop_t *props = &vsnd_props[stream_id];

pthread_mutex_lock(&props->lock.lock);
while (props->lock.buf_ev_notity > 0)
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: PortAudio callbacks must be non-blocking. __virtio_snd_rx_frame_enqueue uses pthread_cond_wait which can block indefinitely when called from the real-time audio callback virtio_snd_rx_stream_cb. This can cause audio glitches, buffer underruns, or deadlocks. Consider using a lock-free ring buffer or non-blocking queue for the audio callback path.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At virtio-snd.c, line 1230:

<comment>PortAudio callbacks must be non-blocking. `__virtio_snd_rx_frame_enqueue` uses `pthread_cond_wait` which can block indefinitely when called from the real-time audio callback `virtio_snd_rx_stream_cb`. This can cause audio glitches, buffer underruns, or deadlocks. Consider using a lock-free ring buffer or non-blocking queue for the audio callback path.</comment>

<file context>
@@ -952,6 +1220,37 @@ static void __virtio_snd_frame_enqueue(void *payload,
+    virtio_snd_prop_t *props = &amp;vsnd_props[stream_id];
+
+    pthread_mutex_lock(&amp;props-&gt;lock.lock);
+    while (props-&gt;lock.buf_ev_notity &gt; 0)
+        pthread_cond_wait(&amp;props-&gt;lock.writable, &amp;props-&gt;lock.lock);
+
</file context>

✅ Addressed in 8c68308

Implement VirtIO sound device capture.

Due to 'semu' emulation part thus 'semu' cannot
send or receive PCM frames in time, the capture feature usually
doesn't work.
virtio-snd.c Outdated
Comment on lines 1230 to 1231
while (props->lock.buf_ev_notity > 0)
pthread_cond_wait(&props->lock.writable, &props->lock.lock);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is called from virtio_snd_rx_stream_cb, which is a PortAudio real-time callback. You NEVER block in an audio callback. Ever. PortAudio explicitly documents this - callbacks must be non-blocking. This will cause:

  • Audio glitches and underruns
  • Potential deadlocks
  • Priority inversion with the audio thread

Fix: Use a lock-free ring buffer or non-blocking trylock with overflow handling.

Comment on lines +1033 to +1034
while (!(props->lock.buf_ev_notity > 0 && rx_ev_start == 1))
pthread_cond_wait(&props->lock.readable, &props->lock.lock);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Read in __virtio_snd_rx_frame_dequeue without holding mutex.

The read in the wait condition isn't protected. Classic TOCTOU race.

Comment on lines +302 to +303
uint32_t buf_sz;
uint32_t buf_idx;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are initialized in virtio_snd_read_pcm_prepare but never actually used for circular buffering.

virtio-snd.c Outdated
Comment on lines 1234 to 1241
vsnd_buf_queue_node_t *node = malloc(sizeof(*node));
if (!node)
goto rx_frame_enque_finally;
node->addr = malloc(sizeof(*node->addr) * n);
if (!node->addr) {
free(node);
goto rx_frame_enque_finally;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Later:

  props->lock.buf_ev_notity++;  // STILL SIGNALS even on alloc failure!
  pthread_cond_signal(&props->lock.readable);

The finally block still increments buf_ev_notity and signals, claiming data exists when nothing was enqueued.

Copy link
Collaborator

@jserv jserv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VSND_GEN_RX_QUEUE_HANDLER is 116 lines of macro that's 95% identical to VSND_GEN_TX_QUEUE_HANDLER. This is not "good taste."

@jserv
Copy link
Collaborator

jserv commented Dec 24, 2025

commit message:

Due to 'semu' emulation part thus 'semu' cannot send or receive PCM frames in time, the capture feature usually
doesn't work.

You're adding 400+ lines of code that "usually doesn't work"? This should be a draft/experimental branch, not a merge candidate. If it doesn't work, why are we adding CI tests that codify broken behavior?

@jserv
Copy link
Collaborator

jserv commented Dec 24, 2025

Missing Lock on TX Notify Path:

  case VSND_QUEUE_TX:
      tx_ev_notify++;  // NO LOCK!
      pthread_cond_signal(&virtio_snd_tx_cond);
      break;

  // virtio-snd.c:1455 - RX path (new code)
  case VSND_QUEUE_RX:
      pthread_mutex_lock(&virtio_snd_rx_mutex);  // HAS LOCK
      rx_ev_notify++;
      pthread_cond_signal(&virtio_snd_rx_cond);
      pthread_mutex_unlock(&virtio_snd_rx_mutex);
      break;

Inconsistent synchronization patterns between TX and RX. TX is already racy - RX is correct but highlights the TX bug.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="virtio-snd.c">

<violation number="1" location="virtio-snd.c:507">
P1: Memory leak: when `bad_msg_err != 0`, the `goto finally` skips the cleanup loop that frees nodes in list `q`. The cleanup code should be moved after the `finally:` label to ensure nodes are always freed.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

idx++; \
} \
\
if (bad_msg_err != 0) \
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Memory leak: when bad_msg_err != 0, the goto finally skips the cleanup loop that frees nodes in list q. The cleanup code should be moved after the finally: label to ensure nodes are always freed.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At virtio-snd.c, line 507:

<comment>Memory leak: when `bad_msg_err != 0`, the `goto finally` skips the cleanup loop that frees nodes in list `q`. The cleanup code should be moved after the `finally:` label to ensure nodes are always freed.</comment>

<file context>
@@ -414,6 +414,116 @@ typedef struct {
+            idx++;                                                           \
+        }                                                                    \
+                                                                             \
+        if (bad_msg_err != 0)                                                \
+            goto finally;                                                    \
+        IIF(WRITE)                                                           \
</file context>

✅ Addressed in 28b9238

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 1 file (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="virtio-snd.c">

<violation number="1" location="virtio-snd.c:1132">
P1: Using `pthread_mutex_trylock` silently drops audio frames when the lock is unavailable. This will cause audio data loss and artifacts in the capture stream. Consider either using blocking `pthread_mutex_lock` with a timeout, or implementing a lock-free ring buffer for the real-time audio callback.</violation>

<violation number="2" location="virtio-snd.c:1133">
P2: Debug `fprintf` in the real-time audio callback path should be removed. Calling `fprintf` from a PortAudio callback can cause audio glitches as it may block.</violation>

<violation number="3" location="virtio-snd.c:1152">
P2: Debug `fprintf` in the real-time audio callback path should be removed.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

pthread_cond_signal(&props->lock.readable);

rx_frame_enque_finally:
fprintf(stderr, "enque end\n");
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Debug fprintf in the real-time audio callback path should be removed.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At virtio-snd.c, line 1152:

<comment>Debug `fprintf` in the real-time audio callback path should be removed.</comment>

<file context>
@@ -1119,29 +1126,32 @@ static void __virtio_snd_rx_frame_enqueue(const void *payload,
-rx_frame_enque_finally:
-    pthread_mutex_unlock(&amp;props-&gt;lock.lock);
+    rx_frame_enque_finally:
+        fprintf(stderr, &quot;enque end\n&quot;);
+        pthread_mutex_unlock(&amp;props-&gt;lock.lock);
+    }
</file context>
Fix with Cubic

while (props->lock.buf_ev_notity > 0)
pthread_cond_wait(&props->lock.writable, &props->lock.lock);*/
if (pthread_mutex_trylock(&props->lock.lock) == 0) {
fprintf(stderr, "enque start \n");
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Debug fprintf in the real-time audio callback path should be removed. Calling fprintf from a PortAudio callback can cause audio glitches as it may block.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At virtio-snd.c, line 1133:

<comment>Debug `fprintf` in the real-time audio callback path should be removed. Calling `fprintf` from a PortAudio callback can cause audio glitches as it may block.</comment>

<file context>
@@ -1119,29 +1126,32 @@ static void __virtio_snd_rx_frame_enqueue(const void *payload,
-    list_push(&amp;node-&gt;q, &amp;props-&gt;buf_queue_head);
+        pthread_cond_wait(&amp;props-&gt;lock.writable, &amp;props-&gt;lock.lock);*/
+    if (pthread_mutex_trylock(&amp;props-&gt;lock.lock) == 0) {
+        fprintf(stderr, &quot;enque start \n&quot;);
+        /* Add a PCM frame to queue */
+        vsnd_buf_queue_node_t *node = malloc(sizeof(*node));
</file context>
Fix with Cubic

/*pthread_mutex_lock(&props->lock.lock);
while (props->lock.buf_ev_notity > 0)
pthread_cond_wait(&props->lock.writable, &props->lock.lock);*/
if (pthread_mutex_trylock(&props->lock.lock) == 0) {
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Using pthread_mutex_trylock silently drops audio frames when the lock is unavailable. This will cause audio data loss and artifacts in the capture stream. Consider either using blocking pthread_mutex_lock with a timeout, or implementing a lock-free ring buffer for the real-time audio callback.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At virtio-snd.c, line 1132:

<comment>Using `pthread_mutex_trylock` silently drops audio frames when the lock is unavailable. This will cause audio data loss and artifacts in the capture stream. Consider either using blocking `pthread_mutex_lock` with a timeout, or implementing a lock-free ring buffer for the real-time audio callback.</comment>

<file context>
@@ -1119,29 +1126,32 @@ static void __virtio_snd_rx_frame_enqueue(const void *payload,
-    node-&gt;pos = 0;
-    list_push(&amp;node-&gt;q, &amp;props-&gt;buf_queue_head);
+        pthread_cond_wait(&amp;props-&gt;lock.writable, &amp;props-&gt;lock.lock);*/
+    if (pthread_mutex_trylock(&amp;props-&gt;lock.lock) == 0) {
+        fprintf(stderr, &quot;enque start \n&quot;);
+        /* Add a PCM frame to queue */
</file context>
Fix with Cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants