From 8e2d74a4866fd497540daa1713ab98ab7f8e1dce Mon Sep 17 00:00:00 2001 From: Mathew Kanner Date: Thu, 27 Nov 2003 19:51:44 +0000 Subject: [PATCH] Fix a panic due to holding a lock over calls to uiomove. Pointed out by: Artur Poplawski Explained by: Don Lewis (truckman) Approved by: tanimura (mentor) Approved by: scottl (re) --- sys/dev/sound/pcm/buffer.c | 27 ------------------------ sys/dev/sound/pcm/buffer.h | 1 - sys/dev/sound/pcm/channel.c | 41 +++++++++++++++++++++++++++++++++++-- 3 files changed, 39 insertions(+), 30 deletions(-) diff --git a/sys/dev/sound/pcm/buffer.c b/sys/dev/sound/pcm/buffer.c index 30970abc1b4..0021c2534e2 100644 --- a/sys/dev/sound/pcm/buffer.c +++ b/sys/dev/sound/pcm/buffer.c @@ -499,33 +499,6 @@ sndbuf_dispose(struct snd_dbuf *b, u_int8_t *to, unsigned int count) return 0; } -int -sndbuf_uiomove(struct snd_dbuf *b, struct uio *uio, unsigned int count) -{ - int x, c, p, rd, err; - - err = 0; - rd = (uio->uio_rw == UIO_READ)? 1 : 0; - if (count > uio->uio_resid) - return EINVAL; - - if (count > (rd? sndbuf_getready(b) : sndbuf_getfree(b))) { - return EINVAL; - } - - while (err == 0 && count > 0) { - p = rd? sndbuf_getreadyptr(b) : sndbuf_getfreeptr(b); - c = MIN(count, sndbuf_getsize(b) - p); - x = uio->uio_resid; - err = uiomove(sndbuf_getbufofs(b, p), c, uio); - x -= uio->uio_resid; - count -= x; - x = rd? sndbuf_dispose(b, NULL, x) : sndbuf_acquire(b, NULL, x); - } - - return 0; -} - /* count is number of bytes we want added to destination buffer */ int sndbuf_feed(struct snd_dbuf *from, struct snd_dbuf *to, struct pcm_channel *channel, struct pcm_feeder *feeder, unsigned int count) diff --git a/sys/dev/sound/pcm/buffer.h b/sys/dev/sound/pcm/buffer.h index 2b706e6b34a..31d9e2ba45d 100644 --- a/sys/dev/sound/pcm/buffer.h +++ b/sys/dev/sound/pcm/buffer.h @@ -106,7 +106,6 @@ void sndbuf_updateprevtotal(struct snd_dbuf *b); int sndbuf_acquire(struct snd_dbuf *b, u_int8_t *from, unsigned int count); int sndbuf_dispose(struct snd_dbuf *b, u_int8_t *to, unsigned int count); -int sndbuf_uiomove(struct snd_dbuf *b, struct uio *uio, unsigned int count); int sndbuf_feed(struct snd_dbuf *from, struct snd_dbuf *to, struct pcm_channel *channel, struct pcm_feeder *feeder, unsigned int count); u_int32_t sndbuf_getflags(struct snd_dbuf *b); diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c index 7a1c90617ef..fba716e8ace 100644 --- a/sys/dev/sound/pcm/channel.c +++ b/sys/dev/sound/pcm/channel.c @@ -250,6 +250,8 @@ chn_write(struct pcm_channel *c, struct uio *buf) { int ret, timeout, newsize, count, sz; struct snd_dbuf *bs = c->bufsoft; + void *off; + int t, x,togo,p; CHN_LOCKASSERT(c); /* @@ -291,7 +293,24 @@ chn_write(struct pcm_channel *c, struct uio *buf) sz = MIN(sz, buf->uio_resid); KASSERT(sz > 0, ("confusion in chn_write")); /* printf("sz: %d\n", sz); */ - ret = sndbuf_uiomove(bs, buf, sz); + + /* + * The following assumes that the free space in + * the buffer can never be less around the + * unlock-uiomove-lock sequence. + */ + togo = sz; + while (ret == 0 && togo> 0) { + p = sndbuf_getfreeptr(bs); + t = MIN(togo, sndbuf_getsize(bs) - p); + off = sndbuf_getbufofs(bs, p); + CHN_UNLOCK(c); + ret = uiomove(off, t, buf); + CHN_LOCK(c); + togo -= t; + x = sndbuf_acquire(bs, NULL, t); + } + ret = 0; if (ret == 0 && !(c->flags & CHN_F_TRIGGERED)) chn_start(c, 0); } @@ -395,6 +414,8 @@ chn_read(struct pcm_channel *c, struct uio *buf) { int ret, timeout, sz, count; struct snd_dbuf *bs = c->bufsoft; + void *off; + int t, x,togo,p; CHN_LOCKASSERT(c); if (!(c->flags & CHN_F_TRIGGERED)) @@ -406,7 +427,23 @@ chn_read(struct pcm_channel *c, struct uio *buf) sz = MIN(buf->uio_resid, sndbuf_getready(bs)); if (sz > 0) { - ret = sndbuf_uiomove(bs, buf, sz); + /* + * The following assumes that the free space in + * the buffer can never be less around the + * unlock-uiomove-lock sequence. + */ + togo = sz; + while (ret == 0 && togo> 0) { + p = sndbuf_getreadyptr(bs); + t = MIN(togo, sndbuf_getsize(bs) - p); + off = sndbuf_getbufofs(bs, p); + CHN_UNLOCK(c); + ret = uiomove(off, t, buf); + CHN_LOCK(c); + togo -= t; + x = sndbuf_dispose(bs, NULL, t); + } + ret = 0; } else { if (c->flags & CHN_F_NBIO) { ret = EWOULDBLOCK;