Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 27 Jul 2026 01:08:45 +0300
From:      "Christos Margiolis" <christos@FreeBSD.org>
To:        "Ronald Klop" <ronald-lists@klop.ws>
Cc:        <dev-commits-src-all@FreeBSD.org>, <src-committers@FreeBSD.org>, "Kevin Bowling" <kbowling@FreeBSD.org>, <dev-commits-src-main@FreeBSD.org>
Subject:   Re: git: 967e86d1ef2a - main - sound: Scale PCM secondary buffers by byte rate
Message-ID:  <DK8UGIJQ1GO9.2XPOWIG8HM79G@FreeBSD.org>
In-Reply-To: <20302724.1251.1784904383819@localhost>
References:  <6a636fb1.26412.26a07c1d@gitrepo.freebsd.org> <20302724.1251.1784904383819@localhost>

index | next in thread | previous in thread | raw e-mail

On Fri Jul 24, 2026 at 5:46 PM EEST, Ronald Klop wrote:
> Is this a good candidate for
> Relnotes: yes
> in the commit message?

I don't think so to be honest.

> Regards,
> Ronald.
>
>
>  
> Van: Christos Margiolis <christos@FreeBSD.org>
> Datum: vrijdag, 24 juli 2026 15:59
> Aan: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
> CC: Kevin Bowling <kbowling@FreeBSD.org>
> Onderwerp: git: 967e86d1ef2a - main - sound: Scale PCM secondary buffers by byte rate
>> 
>> The branch main has been updated by christos:
>> 
>> URL: https://cgit.FreeBSD.org/src/commit/?id=967e86d1ef2ac8711c0ae7be353a9c08186f4e6f
>> 
>> commit 967e86d1ef2ac8711c0ae7be353a9c08186f4e6f
>> Author:     Kevin Bowling <kbowling@FreeBSD.org>
>> AuthorDate: 2026-07-24 13:57:27 +0000
>> Commit:     Christos Margiolis <christos@FreeBSD.org>
>> CommitDate: 2026-07-24 13:58:14 +0000
>> 
>>     sound: Scale PCM secondary buffers by byte rate
>>     
>>     The fixed 128 KiB secondary buffer cap dates from stereo-sized streams.
>>     High channel-count or high sample-width OSS streams can consume most of
>>     that budget in one graph quantum, leaving too little room for capture
>>     catch-up or playback headroom.
>>     
>>     Keep 128 KiB as the low-rate floor, but derive the effective soft-ring
>>     cap from the channel byte rate, clamped to 4 MiB. Use that per-channel
>>     cap when resizing the soft buffer and when clamping
>>     SNDCTL_DSP_SETFRAGMENT requests.
>>     
>>     Also clamp SNDCTL_DSP_LOW_WATER to the current soft-buffer size so an
>>     impossible readiness threshold cannot make poll/select wait forever.
>>     
>>     MFC after:      3 weeks
>>     Reviewed by:    christos
>>     Differential Revision:  https://reviews.freebsd.org/D58064
>> ---
>>  sys/dev/sound/pcm/channel.c | 62 +++++++++++++++++++++++++++++++++------------
>>  sys/dev/sound/pcm/channel.h | 30 +++++++++++++++++-----
>>  sys/dev/sound/pcm/dsp.c     | 38 +++++++++++++++++++--------
>>  3 files changed, 96 insertions(+), 34 deletions(-)
>> 
>> diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c
>> index ff7620c772c3..21c223c61205 100644
>> --- a/sys/dev/sound/pcm/channel.c
>> +++ b/sys/dev/sound/pcm/channel.c
>> @@ -1657,15 +1657,30 @@ round_pow2(u_int32_t v)
>>     return ret;
>>  }
>>  
>> +u_int32_t
>> +chn_2ndbufmaxsize(struct pcm_channel *c)
>> +{
>> +   struct snd_dbuf *bs;
>> +   uint64_t maxsize;
>> +
>> +   CHN_LOCKASSERT(c);
>> +
>> +   bs = c->bufsoft;
>> +   maxsize = (uint64_t)bs->align * bs->spd * CHN_2NDBUFTIME_MS / 1000;
>> +   RANGE(maxsize, CHN_2NDBUFSIZE_MIN, CHN_2NDBUFSIZE_MAX);
>> +
>> +   return ((u_int32_t)maxsize);
>> +}
>> +
>>  static u_int32_t
>> -round_blksz(u_int32_t v, int round)
>> +round_blksz(u_int32_t v, int round, u_int32_t maxsize)
>>  {
>>     u_int32_t ret, tmp;
>>  
>>     if (round < 1)
>>         round = 1;
>>  
>> -   ret = min(round_pow2(v), CHN_2NDBUFMAXSIZE >> 1);
>> +   ret = min(round_pow2(v), maxsize >> 1);
>>  
>>     if (ret > v && (ret >> 1) > 0 && (ret >> 1) >= ((v * 3) >> 2))
>>         ret >>= 1;
>> @@ -1780,14 +1795,16 @@ chn_calclatency(int dir, int latency, int bps, u_int32_t datarate,
>>     if (latency < CHN_LATENCY_MIN || latency > CHN_LATENCY_MAX ||
>>         bps < 1 || datarate < 1 ||
>>         !(dir == PCMDIR_PLAY || dir == PCMDIR_REC)) {
>> +       if (max < CHN_2NDBUFSIZE_MIN)
>> +           max = CHN_2NDBUFSIZE_MIN;
>>         if (rblksz != NULL)
>> -           *rblksz = CHN_2NDBUFMAXSIZE >> 1;
>> +           *rblksz = max >> 1;
>>         if (rblkcnt != NULL)
>>             *rblkcnt = 2;
>>         printf("%s(): FAILED dir=%d latency=%d bps=%d "
>>             "datarate=%u max=%u\n",
>>             __func__, dir, latency, bps, datarate, max);
>> -       return CHN_2NDBUFMAXSIZE;
>> +       return max;
>>     }
>>  
>>     lprofile = chn_latency_profile;
>> @@ -1804,7 +1821,7 @@ chn_calclatency(int dir, int latency, int bps, u_int32_t datarate,
>>         datarate));
>>     if (bufsz > max)
>>         bufsz = max;
>> -   blksz = round_blksz(bufsz >> blkcnt, bps);
>> +   blksz = round_blksz(bufsz >> blkcnt, bps, max);
>>  
>>     if (rblksz != NULL)
>>         *rblksz = blksz;
>> @@ -1821,6 +1838,7 @@ chn_resizebuf(struct pcm_channel *c, int latency,
>>     struct snd_dbuf *b, *bs, *pb;
>>     int sblksz, sblkcnt, hblksz, hblkcnt, limit = 0, nsblksz, nsblkcnt;
>>     int ret;
>> +   u_int32_t maxsize;
>>  
>>     CHN_LOCKASSERT(c);
>>  
>> @@ -1843,14 +1861,15 @@ chn_resizebuf(struct pcm_channel *c, int latency,
>>  
>>     bs = c->bufsoft;
>>     b = c->bufhard;
>> +   maxsize = chn_2ndbufmaxsize(c);
>>  
>>     if (!(blksz == 0 || blkcnt == -1) &&
>>         (blksz < 16 || blksz < bs->align || blkcnt < 2 ||
>> -       (blksz * blkcnt) > CHN_2NDBUFMAXSIZE))
>> +       (uint64_t)blksz * blkcnt > maxsize))
>>         return EINVAL;
>>  
>>     chn_calclatency(c->direction, latency, bs->align,
>> -       bs->align * bs->spd, CHN_2NDBUFMAXSIZE,
>> +       bs->align * bs->spd, maxsize,
>>         &sblksz, &sblkcnt);
>>  
>>     if (blksz == 0 || blkcnt == -1) {
>> @@ -1871,7 +1890,7 @@ chn_resizebuf(struct pcm_channel *c, int latency,
>>          * defeat the purpose of having custom control. The least
>>          * we can do is round it to the nearest ^2 and align it.
>>          */
>> -       sblksz = round_blksz(blksz, bs->align);
>> +       sblksz = round_blksz(blksz, bs->align, maxsize);
>>         sblkcnt = round_pow2(blkcnt);
>>     }
>>  
>> @@ -1890,18 +1909,28 @@ chn_resizebuf(struct pcm_channel *c, int latency,
>>                 sndbuf_xbytes(pb->blksz, pb, bs) * 2 : 0;
>>         }
>>     } else {
>> +       /*
>> +        * The byte-rate-scaled cap applies to the secondary buffer
>> +        * only.  It exists to absorb userland scheduling latency,
>> +        * which the secondary buffer alone must cover; hardware
>> +        * buffer geometry keeps the historical cap, since enlarging
>> +        * it would change fragment sizes and interrupt cadence
>> +        * visible to drivers, and remains bounded by b->maxsize
>> +        * below.
>> +        */
>>         hblkcnt = 2;
>>         if (c->flags & CHN_F_HAS_SIZE) {
>>             hblksz = round_blksz(sndbuf_xbytes(sblksz, bs, b),
>> -               b->align);
>> +               b->align, CHN_2NDBUFSIZE_MIN);
>>             hblkcnt = round_pow2(bs->blkcnt);
>>         } else
>>             chn_calclatency(c->direction, latency,
>>                 b->align, b->align * b->spd,
>> -               CHN_2NDBUFMAXSIZE, &hblksz, &hblkcnt);
>> +               CHN_2NDBUFSIZE_MIN, &hblksz, &hblkcnt);
>>  
>>         if ((hblksz << 1) > b->maxsize)
>> -           hblksz = round_blksz(b->maxsize >> 1, b->align);
>> +           hblksz = round_blksz(b->maxsize >> 1, b->align,
>> +               CHN_2NDBUFSIZE_MIN);
>>  
>>         while ((hblksz * hblkcnt) > b->maxsize) {
>>             if (hblkcnt < 4)
>> @@ -1922,7 +1951,8 @@ chn_resizebuf(struct pcm_channel *c, int latency,
>>  
>>         if (!CHN_EMPTY(c, children)) {
>>             nsblksz = round_blksz(
>> -               sndbuf_xbytes(b->blksz, b, bs), bs->align);
>> +               sndbuf_xbytes(b->blksz, b, bs), bs->align,
>> +               maxsize);
>>             nsblkcnt = b->blkcnt;
>>             if (c->direction == PCMDIR_PLAY) {
>>                 do {
>> @@ -1938,13 +1968,13 @@ chn_resizebuf(struct pcm_channel *c, int latency,
>>             limit = sndbuf_xbytes(b->blksz, b, bs) * 2;
>>     }
>>  
>> -   if (limit > CHN_2NDBUFMAXSIZE)
>> -       limit = CHN_2NDBUFMAXSIZE;
>> +   if ((u_int32_t)limit > maxsize)
>> +       limit = maxsize;
>>  
>> -   while ((sblksz * sblkcnt) < limit)
>> +   while ((uint64_t)sblksz * sblkcnt < (uint64_t)limit)
>>         sblkcnt <<= 1;
>>  
>> -   while ((sblksz * sblkcnt) > CHN_2NDBUFMAXSIZE) {
>> +   while ((uint64_t)sblksz * sblkcnt > maxsize) {
>>         if (sblkcnt < 4)
>>             sblksz >>= 1;
>>         else
>> diff --git a/sys/dev/sound/pcm/channel.h b/sys/dev/sound/pcm/channel.h
>> index c7f5bf93b8e5..c78c25dac13b 100644
>> --- a/sys/dev/sound/pcm/channel.h
>> +++ b/sys/dev/sound/pcm/channel.h
>> @@ -430,15 +430,31 @@ enum {
>>  #define CHN_TIMEOUT_MIN        1
>>  #define CHN_TIMEOUT_MAX        10
>>  
>> -/*
>> - * This should be large enough to hold all pcm data between
>> - * tsleeps in chn_{read,write} at the highest sample rate.
>> - * (which is usually 48kHz * 16bit * stereo = 192000 bytes/sec)
>> - */
>> +/* Default block size for the secondary buffer. */
>>  #define CHN_2NDBUFBLKSIZE  (2 * 1024)
>>  /* The total number of blocks per secondary bufhard. */
>>  #define CHN_2NDBUFBLKNUM   (32)
>> -/* The size of a whole secondary bufhard. */
>> -#define CHN_2NDBUFMAXSIZE  (131072)
>> +/*
>> + * The secondary buffer cap scales with the channel byte rate, targeting
>> + * CHN_2NDBUFTIME_MS of stream so that all pcm data between tsleeps in
>> + * chn_{read,write} fits, clamped to [CHN_2NDBUFSIZE_MIN,
>> + * CHN_2NDBUFSIZE_MAX].
>> + *
>> + * The floor is the historical secondary buffer size and preserves memory
>> + * use for low byte-rate streams; it holds ~680 ms at the once-typical
>> + * 48kHz * 16bit * stereo rate (192000 bytes/sec), well above the target
>> + * (~38 KiB there).
>> + *
>> + * The ceiling bounds per-channel buffer memory.  Buffers are allocated at
>> + * the derived size, so only streams that actually run at high byte rates
>> + * approach it.  It holds the full target through MADI-class streams
>> + * (64ch * 32-bit * 48kHz, ~12.3 MB/s); beyond that, coverage shrinks
>> + * proportionally (e.g. ~85 ms at 64ch * 32-bit * 192kHz).
>> + */
>> +#define CHN_2NDBUFSIZE_MIN (131072)
>> +#define CHN_2NDBUFSIZE_MAX (4 * 1024 * 1024)
>> +#define CHN_2NDBUFTIME_MS  200
>> +
>> +u_int32_t chn_2ndbufmaxsize(struct pcm_channel *);
>>  
>>  #define CHANNEL_DECLARE(name) static DEFINE_CLASS(name, name ## _methods, sizeof(struct kobj))
>> diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c
>> index 0a5063410d24..f49df6e8a8c6 100644
>> --- a/sys/dev/sound/pcm/dsp.c
>> +++ b/sys/dev/sound/pcm/dsp.c
>> @@ -109,6 +109,25 @@ static int dsp_oss_setsong(struct pcm_channel *wrch, struct pcm_channel *rdch, o
>>  static int dsp_oss_setname(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *name);
>>  #endif
>>  
>> +static uint32_t
>> +dsp_clamp_fragments(uint32_t maxfrags, uint32_t fragsz, uint32_t maxsize)
>> +{
>> +   if (maxfrags == 0)
>> +       maxfrags = maxsize / fragsz;
>> +   if (maxfrags < 2)
>> +       maxfrags = 2;
>> +   if ((uint64_t)maxfrags * fragsz > maxsize)
>> +       maxfrags = maxsize / fragsz;
>> +   return (maxfrags);
>> +}
>> +
>> +static unsigned int
>> +dsp_low_water(struct pcm_channel *ch, int lw)
>> +{
>> +   RANGE(lw, 1, ch->bufsoft->bufsize);
>> +   return ((unsigned int)lw);
>> +}
>> +
>>  int
>>  dsp_make_dev(device_t dev)
>>  {
>> @@ -1244,18 +1263,13 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
>>             RANGE(fragln, 4, 16);
>>             fragsz = 1 << fragln;
>>  
>> -           if (maxfrags == 0)
>> -               maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
>> -           if (maxfrags < 2)
>> -               maxfrags = 2;
>> -           if (maxfrags * fragsz > CHN_2NDBUFMAXSIZE)
>> -               maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
>> -
>>             DEB(printf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz));
>>             PCM_ACQUIRE_QUICK(d);
>>                 if (rdch) {
>>                 CHN_LOCK(rdch);
>> -               ret = chn_setblocksize(rdch, maxfrags, fragsz);
>> +               ret = chn_setblocksize(rdch,
>> +                   dsp_clamp_fragments(maxfrags, fragsz,
>> +                   chn_2ndbufmaxsize(rdch)), fragsz);
>>                 r_maxfrags = rdch->bufsoft->blkcnt;
>>                 r_fragsz = rdch->bufsoft->blksz;
>>                 CHN_UNLOCK(rdch);
>> @@ -1265,7 +1279,9 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
>>             }
>>                 if (wrch && ret == 0) {
>>                 CHN_LOCK(wrch);
>> -               ret = chn_setblocksize(wrch, maxfrags, fragsz);
>> +               ret = chn_setblocksize(wrch,
>> +                   dsp_clamp_fragments(maxfrags, fragsz,
>> +                   chn_2ndbufmaxsize(wrch)), fragsz);
>>                 maxfrags = wrch->bufsoft->blkcnt;
>>                 fragsz = wrch->bufsoft->blksz;
>>                 CHN_UNLOCK(wrch);
>> @@ -1653,12 +1669,12 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
>>      */
>>         if (wrch != NULL) {
>>             CHN_LOCK(wrch);
>> -           wrch->lw = (*arg_i > 1) ? *arg_i : 1;
>> +           wrch->lw = dsp_low_water(wrch, *arg_i);
>>             CHN_UNLOCK(wrch);
>>         }
>>         if (rdch != NULL) {
>>             CHN_LOCK(rdch);
>> -           rdch->lw = (*arg_i > 1) ? *arg_i : 1;
>> +           rdch->lw = dsp_low_water(rdch, *arg_i);
>>             CHN_UNLOCK(rdch);
>>         }
>>         break;
>>  
>> 
>> 
>> 
>
>  



home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?DK8UGIJQ1GO9.2XPOWIG8HM79G>