Page MenuHomeFreeBSD

D58064.id181583.diff
No OneTemporary

D58064.id181583.diff

diff --git a/sys/dev/sound/pcm/channel.h b/sys/dev/sound/pcm/channel.h
--- a/sys/dev/sound/pcm/channel.h
+++ b/sys/dev/sound/pcm/channel.h
@@ -430,15 +430,31 @@
#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/channel.c b/sys/dev/sound/pcm/channel.c
--- a/sys/dev/sound/pcm/channel.c
+++ b/sys/dev/sound/pcm/channel.c
@@ -1657,15 +1657,33 @@
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;
+ if (maxsize < CHN_2NDBUFSIZE_MIN)
+ maxsize = CHN_2NDBUFSIZE_MIN;
+ if (maxsize > CHN_2NDBUFSIZE_MAX)
+ maxsize = 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 +1798,16 @@
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 +1824,7 @@
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 +1841,7 @@
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 +1864,15 @@
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 +1893,7 @@
* 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 +1912,28 @@
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 +1954,8 @@
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 +1971,13 @@
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/dsp.c b/sys/dev/sound/pcm/dsp.c
--- a/sys/dev/sound/pcm/dsp.c
+++ b/sys/dev/sound/pcm/dsp.c
@@ -109,6 +109,26 @@
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)
+{
+ if (lw < 1)
+ return (1);
+ return (min((unsigned int)lw, ch->bufsoft->bufsize));
+}
+
int
dsp_make_dev(device_t dev)
{
@@ -1244,18 +1264,13 @@
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 +1280,9 @@
}
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 +1670,12 @@
*/
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;

File Metadata

Mime Type
text/plain
Expires
Mon, Jul 13, 10:12 PM (10 h, 56 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
35047118
Default Alt Text
D58064.id181583.diff (8 KB)

Event Timeline