The branch main has been updated by christos:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=24576c39789ca13846b9450155923ee0e377aeb4

commit 24576c39789ca13846b9450155923ee0e377aeb4
Author:     Goran Mekić <[email protected]>
AuthorDate: 2026-07-06 12:06:17 +0000
Commit:     Christos Margiolis <[email protected]>
CommitDate: 2026-07-06 12:07:41 +0000

    sound: track kqueue low watermark per-knote for mmaped channels
    
    Use kn->kn_sdata to track the last bs->total value for each knote
    attached to an mmaped channel. An event is delivered only when the total
    byte counter has advanced by at least c->lw since the last delivery.
    After delivery kn_sdata is updated to the current total.
    
    Each knote tracks its own watermark independently, so multiple knotes
    attached to the same mmaped channel all receive events correctly.
    
    Non-mmap channels keep the existing level-triggered behavior via
    chn_polltrigger().
    
    MFC after:      1 week
    Reviewed by:    christos
    Differential Revision:  https://reviews.freebsd.org/D57833
---
 sys/dev/sound/pcm/channel.c | 14 +++++++++-----
 sys/dev/sound/pcm/channel.h |  2 +-
 sys/dev/sound/pcm/dsp.c     | 14 +++++++++++++-
 3 files changed, 23 insertions(+), 7 deletions(-)

diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c
index a0ee16a14386..40a08d48fb21 100644
--- a/sys/dev/sound/pcm/channel.c
+++ b/sys/dev/sound/pcm/channel.c
@@ -270,11 +270,15 @@ chn_lockdestroy(struct pcm_channel *c)
 /**
  * @brief Determine channel is ready for I/O
  *
+ * @param ref_total    for mmaped channels, the previous byte counter
+ *      *              snapshot from which to compute the delta to
+ *      *              bufsoft->total; ignored for non-mmaped channels.
+ *
  * @retval 1 = ready for I/O
  * @retval 0 = not ready for I/O
  */
 int
-chn_polltrigger(struct pcm_channel *c)
+chn_polltrigger(struct pcm_channel *c, u_int64_t ref_total)
 {
        struct snd_dbuf *bs = c->bufsoft;
        u_int delta;
@@ -282,10 +286,10 @@ chn_polltrigger(struct pcm_channel *c)
        CHN_LOCKASSERT(c);
 
        if (c->flags & CHN_F_MMAP) {
-               if (bs->prev_total < c->lw)
+               if (ref_total < c->lw)
                        delta = c->lw;
                else
-                       delta = bs->total - bs->prev_total;
+                       delta = bs->total - ref_total;
        } else {
                if (c->direction == PCMDIR_PLAY)
                        delta = sndbuf_getfree(bs);
@@ -316,7 +320,7 @@ chn_wakeup(struct pcm_channel *c)
 
        if (CHN_EMPTY(c, children.busy)) {
                KNOTE_LOCKED(&bs->sel.si_note, 0);
-               if (SEL_WAITING(&bs->sel) && chn_polltrigger(c))
+               if (SEL_WAITING(&bs->sel) && chn_polltrigger(c, bs->prev_total))
                        selwakeuppri(&bs->sel, PRIBIO);
                CHN_BROADCAST(&c->intr_cv);
        } else {
@@ -830,7 +834,7 @@ chn_poll(struct pcm_channel *c, int ev, struct thread *td)
        }
 
        ret = 0;
-       if (chn_polltrigger(c)) {
+       if (chn_polltrigger(c, bs->prev_total)) {
                chn_pollreset(c);
                ret = ev;
        } else
diff --git a/sys/dev/sound/pcm/channel.h b/sys/dev/sound/pcm/channel.h
index 6fa4338dce4a..c7f5bf93b8e5 100644
--- a/sys/dev/sound/pcm/channel.h
+++ b/sys/dev/sound/pcm/channel.h
@@ -260,7 +260,7 @@ int chn_read(struct pcm_channel *c, struct uio *buf);
 u_int32_t chn_start(struct pcm_channel *c, int force);
 int chn_sync(struct pcm_channel *c, int threshold);
 int chn_flush(struct pcm_channel *c);
-int chn_polltrigger(struct pcm_channel *c);
+int chn_polltrigger(struct pcm_channel *c, u_int64_t ref_total);
 int chn_poll(struct pcm_channel *c, int ev, struct thread *td);
 
 char *chn_mkname(char *buf, size_t len, struct pcm_channel *c);
diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c
index 8bb07d87fac9..da8bf485bc9b 100644
--- a/sys/dev/sound/pcm/dsp.c
+++ b/sys/dev/sound/pcm/dsp.c
@@ -3010,7 +3010,13 @@ dsp_kqevent(struct knote *kn, long hint)
                return (1);
        }
        kn->kn_data = 0;
-       if (chn_polltrigger(ch)) {
+       /*
+        * For mmaped channels pass the knote's own reference point so the
+        * low watermark is tracked per-knote.  Non-mmaped channels ignore
+        * the reference and fire based on the current amount of ready/free
+        * data in the buffer, so all knotes see the same live state.
+        */
+       if (chn_polltrigger(ch, (u_int64_t)kn->kn_sdata)) {
                if (kn->kn_filter == EVFILT_READ) {
                        kn->kn_data = sndbuf_getready(ch->bufsoft);
                        if (ch->flags & CHN_F_MMAP)
@@ -3025,6 +3031,7 @@ dsp_kqevent(struct knote *kn, long hint)
                                kn->kn_kevent.ext[0] = 
sndbuf_getready(ch->bufsoft) / ch->bufsoft->align;
                }
                kn->kn_kevent.ext[1] = ch->xruns;
+               kn->kn_sdata = ch->bufsoft->total;
        }
 
        return (kn->kn_data > 0);
@@ -3070,6 +3077,11 @@ dsp_kqfilter(struct cdev *dev, struct knote *kn)
                knlist_add(&ch->bufsoft->sel.si_note, kn, 1);
                CHN_UNLOCK(ch);
                kn->kn_hook = ch;
+               /*
+                * Start tracking from the current position so the first event
+                * fires after c->lw additional bytes have been transferred.
+                */
+               kn->kn_sdata = ch->bufsoft->prev_total;
        } else
                err = EINVAL;
        PCM_GIANT_LEAVE(d);

Reply via email to