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