Mercurial > illumos > illumos-gate
changeset 10632:704f6d4f41c9
6885017 audio output tries to queue up too many fragments
6885023 audio framework can lose taskqs
6885091 audio1575 gets offset wrong by about 1 fragment
author | Garrett D'Amore <gdamore@opensolaris.org> |
---|---|
date | Wed, 23 Sep 2009 20:55:51 -0700 |
parents | abfe11cf0ce1 |
children | 62ff2de09191 |
files | usr/src/uts/common/io/audio/drv/audio1575/audio1575.c usr/src/uts/common/io/audio/drv/audiop16x/audiop16x.c usr/src/uts/common/io/audio/impl/audio_client.c usr/src/uts/common/io/audio/impl/audio_client.h usr/src/uts/common/io/audio/impl/audio_ctrl.c usr/src/uts/common/io/audio/impl/audio_ddi.c usr/src/uts/common/io/audio/impl/audio_impl.h usr/src/uts/common/io/audio/impl/audio_input.c usr/src/uts/common/io/audio/impl/audio_oss.c usr/src/uts/common/io/audio/impl/audio_output.c usr/src/uts/common/io/audio/impl/audio_sun.c |
diffstat | 11 files changed, 93 insertions(+), 200 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/uts/common/io/audio/drv/audio1575/audio1575.c Wed Sep 23 22:00:34 2009 -0400 +++ b/usr/src/uts/common/io/audio/drv/audio1575/audio1575.c Wed Sep 23 20:55:51 2009 -0700 @@ -680,7 +680,7 @@ mutex_enter(&statep->lock); audio1575_update_port(port); - val = port->count + port->picb; + val = port->count + (port->picb / port->nchan); mutex_exit(&statep->lock); return (val);
--- a/usr/src/uts/common/io/audio/drv/audiop16x/audiop16x.c Wed Sep 23 22:00:34 2009 -0400 +++ b/usr/src/uts/common/io/audio/drv/audiop16x/audiop16x.c Wed Sep 23 20:55:51 2009 -0700 @@ -564,21 +564,9 @@ } /* - * We choose 6 fragments for a specific reason. Since the - * device only has full and half interrupts, and since the - * framework will try to queue up 4 frags automatically, this - * ensures that we will be able to queue all 4 fragments, and - * it avoids a potential underrun that you would get with 8 - * fragments. (More than 8 fragments is guaranteed to cause - * underruns in Boomer.) - * - * Boomer needs to get smarter about dealing with devices with - * fewer fragment counts. This device, for instance, should - * really be represented with just two fragments. That wll - * cause an infinite loop in Boomer, when Boomer tries to - * queue up 4 fragments. + * This needs the very latest Boomer changes. */ - port->nfrags = 6; + port->nfrags = 2; port->fragfr = 48000 / port->intrs; /* * The device operates in pairs of dwords at a time, for
--- a/usr/src/uts/common/io/audio/impl/audio_client.c Wed Sep 23 22:00:34 2009 -0400 +++ b/usr/src/uts/common/io/audio/impl/audio_client.c Wed Sep 23 20:55:51 2009 -0700 @@ -674,10 +674,6 @@ sp->s_gain_eff = sp->s_gain_scaled; } mutex_exit(&sp->s_lock); - - /* - * No need to notify clients, since they can't see this update. - */ } int @@ -743,7 +739,7 @@ } mutex_exit(&sp->s_lock); - auclnt_notify_dev(sp->s_client->c_dev); + atomic_inc_uint(&sp->s_client->c_dev->d_serial); } uint8_t @@ -771,7 +767,7 @@ } mutex_exit(&sp->s_lock); - auclnt_notify_dev(sp->s_client->c_dev); + atomic_inc_uint(&sp->s_client->c_dev->d_serial); } boolean_t @@ -832,7 +828,7 @@ auclnt_stop(sp); - auclnt_notify_dev(sp->s_client->c_dev); + atomic_inc_uint(&sp->s_client->c_dev->d_serial); } void @@ -973,64 +969,6 @@ } } -void -auimpl_client_task(void *arg) -{ - audio_client_t *c = arg; - - mutex_enter(&c->c_lock); - - for (;;) { - if (c->c_closing) { - break; - } - - if (c->c_do_output) { - c->c_do_output = B_FALSE; - - mutex_exit(&c->c_lock); - if (c->c_output != NULL) - c->c_output(c); - mutex_enter(&c->c_lock); - continue; - } - - if (c->c_do_input) { - c->c_do_input = B_FALSE; - - mutex_exit(&c->c_lock); - if (c->c_input != NULL) - c->c_input(c); - mutex_enter(&c->c_lock); - continue; - } - - if (c->c_do_notify) { - c->c_do_notify = B_FALSE; - - mutex_exit(&c->c_lock); - if (c->c_notify != NULL) - c->c_notify(c); - mutex_enter(&c->c_lock); - continue; - } - - if (c->c_do_drain) { - c->c_do_drain = B_FALSE; - - mutex_exit(&c->c_lock); - if (c->c_drain != NULL) - c->c_drain(c); - mutex_enter(&c->c_lock); - continue; - } - - /* if we got here, we had no work to do */ - cv_wait(&c->c_cv, &c->c_lock); - } - mutex_exit(&c->c_lock); -} - int auclnt_start_drain(audio_client_t *c) { @@ -1084,8 +1022,6 @@ list_t *list = &auimpl_clients; minor_t minor; audio_dev_t *d; - char scratch[80]; - static uint64_t unique = 0; /* validate minor number */ minor = getminor(dev) & AUDIO_MN_TYPE_MASK; @@ -1119,14 +1055,6 @@ c->c_origminor = getminor(dev); c->c_ops = *ops; - (void) snprintf(scratch, sizeof (scratch), "auclnt%" PRIx64, - atomic_inc_64_nv(&unique)); - c->c_tq = ddi_taskq_create(NULL, scratch, 1, TASKQ_DEFAULTPRI, 0); - if (c->c_tq == NULL) { - audio_dev_warn(d, "client taskq_create failed"); - goto failed; - } - /* * We hold the client lock here. */ @@ -1153,9 +1081,6 @@ failed: auimpl_dev_release(d); - if (c->c_tq != NULL) { - ddi_taskq_destroy(c->c_tq); - } audio_stream_fini(&c->c_ostream); audio_stream_fini(&c->c_istream); mutex_destroy(&c->c_lock); @@ -1179,8 +1104,6 @@ auimpl_dev_release(c->c_dev); c->c_dev = NULL; - ddi_taskq_destroy(c->c_tq); - mutex_destroy(&c->c_lock); cv_destroy(&c->c_cv); @@ -1223,13 +1146,8 @@ while (c->c_refcnt) { cv_wait(&c->c_cv, &c->c_lock); } - c->c_closing = B_TRUE; - cv_broadcast(&c->c_cv); mutex_exit(&c->c_lock); - /* make sure taskq has drained */ - ddi_taskq_wait(c->c_tq); - /* release any engines that we were holding */ auimpl_engine_close(&c->c_ostream); auimpl_engine_close(&c->c_istream); @@ -1286,6 +1204,12 @@ mutex_exit(&c->c_lock); } +unsigned +auclnt_dev_get_serial(audio_dev_t *d) +{ + return (d->d_serial); +} + void auclnt_dev_walk_clients(audio_dev_t *d, int (*walker)(audio_client_t *, void *), @@ -1356,12 +1280,6 @@ } } - if (ddi_taskq_dispatch(c->c_tq, auimpl_client_task, c, DDI_NOSLEEP) != - DDI_SUCCESS) { - audio_dev_warn(d, "unable to start client taskq"); - rv = ENOMEM; - } - done: if (rv != 0) { /* close any engines that we opened */ @@ -1510,24 +1428,6 @@ return (caps); } -void -auclnt_notify_dev(audio_dev_t *dev) -{ - list_t *l = &dev->d_clients; - audio_client_t *c; - - rw_enter(&auimpl_client_lock, RW_READER); - for (c = list_head(l); c != NULL; c = list_next(l, c)) { - if (!c->c_is_active) - continue; - mutex_enter(&c->c_lock); - c->c_do_notify = B_TRUE; - cv_broadcast(&c->c_cv); - mutex_exit(&c->c_lock); - } - rw_exit(&auimpl_client_lock); -} - uint64_t auclnt_get_samples(audio_stream_t *sp) {
--- a/usr/src/uts/common/io/audio/impl/audio_client.h Wed Sep 23 22:00:34 2009 -0400 +++ b/usr/src/uts/common/io/audio/impl/audio_client.h Wed Sep 23 20:55:51 2009 -0700 @@ -47,11 +47,11 @@ int (*aco_mmap)(audio_client_t *, ...); void (*aco_input)(audio_client_t *); void (*aco_output)(audio_client_t *); - void (*aco_notify)(audio_client_t *); void (*aco_drain)(audio_client_t *); void (*aco_wput)(audio_client_t *, mblk_t *); void (*aco_wsrv)(audio_client_t *); + void (*aco_rsrv)(audio_client_t *); } audio_client_ops_t; void *auclnt_get_private(audio_client_t *); @@ -149,7 +149,6 @@ audio_dev_t *auclnt_get_dev(audio_client_t *); audio_dev_t *auclnt_hold_dev_by_index(int); void auclnt_release_dev(audio_dev_t *); -void auclnt_notify_dev(audio_dev_t *); int auclnt_get_dev_index(audio_dev_t *); int auclnt_get_dev_number(audio_dev_t *); void auclnt_set_dev_number(audio_dev_t *, int); @@ -177,6 +176,14 @@ int (*)(audio_client_t *, void *), void *); /* + * This is used to check for updates to volume and control status. + * Its a polling-based interface because that's what our clients (OSS) + * need, and its far lighter weight than forcing an asynchronous + * callback on everything. + */ +unsigned auclnt_dev_get_serial(audio_dev_t *); + +/* * Audio control functions for use by clients. */
--- a/usr/src/uts/common/io/audio/impl/audio_ctrl.c Wed Sep 23 22:00:34 2009 -0400 +++ b/usr/src/uts/common/io/audio/impl/audio_ctrl.c Wed Sep 23 20:55:51 2009 -0700 @@ -28,6 +28,7 @@ #include <sys/sysmacros.h> #include <sys/ddi.h> #include <sys/sunddi.h> +#include <sys/atomic.h> #include "audio_impl.h" @@ -264,15 +265,14 @@ * values since they have changed. * * There will be a routine that allows a client to register - * a callback. All callbacks registered to this device should - * get called from this routine. + * a callback. For now we just update the serial number. * * d - The device that needs updates. */ void audio_dev_update_controls(audio_dev_t *d) { - auclnt_notify_dev(d); + atomic_inc_uint(&d->d_serial); }
--- a/usr/src/uts/common/io/audio/impl/audio_ddi.c Wed Sep 23 22:00:34 2009 -0400 +++ b/usr/src/uts/common/io/audio/impl/audio_ddi.c Wed Sep 23 20:55:51 2009 -0700 @@ -33,6 +33,7 @@ #include <sys/mkdev.h> #include <sys/conf.h> #include <sys/note.h> +#include <sys/atomic.h> #include <sys/ddi.h> #include <sys/sunddi.h> @@ -183,7 +184,7 @@ /* now we can receive upcalls */ auimpl_client_activate(c); - auclnt_notify_dev(c->c_dev); + atomic_inc_uint(&c->c_dev->d_serial); return (0); } @@ -244,7 +245,7 @@ /* now we can receive upcalls */ auimpl_client_activate(c); - auclnt_notify_dev(c->c_dev); + atomic_inc_uint(&c->c_dev->d_serial); return (0); } @@ -289,7 +290,7 @@ auimpl_client_destroy(c); /* notify peers that a change has occurred */ - auclnt_notify_dev(d); + atomic_inc_uint(&d->d_serial); /* now we can drop the release we had on the device */ auimpl_dev_release(d); @@ -340,7 +341,7 @@ auimpl_client_destroy(c); /* notify peers that a change has occurred */ - auclnt_notify_dev(d); + atomic_inc_uint(&d->d_serial); /* now we can drop the release we had on the device */ auimpl_dev_release(d); @@ -441,6 +442,21 @@ return (0); } +static int +audio_rsrv(queue_t *rq) +{ + audio_client_t *c; + + c = rq->q_ptr; + if (c->c_rsrv) { + c->c_rsrv(c); + } else { + flushq(rq, FLUSHALL); + } + return (0); +} + + static struct dev_ops audio_dev_ops = { DEVO_REV, /* rev */ 0, /* refcnt */ @@ -488,7 +504,7 @@ helper->minfo.mi_idnum = 0; /* only for strlog(1M) */ helper->minfo.mi_idname = helper->name; helper->minfo.mi_minpsz = 0; - helper->minfo.mi_maxpsz = 2048; + helper->minfo.mi_maxpsz = 8192; helper->minfo.mi_hiwat = 65536; helper->minfo.mi_lowat = 32768; @@ -500,8 +516,8 @@ helper->wqinit.qi_minfo = &helper->minfo; helper->wqinit.qi_mstat = NULL; - helper->rqinit.qi_putp = NULL; - helper->rqinit.qi_srvp = NULL; + helper->rqinit.qi_putp = putq; + helper->rqinit.qi_srvp = audio_rsrv; helper->rqinit.qi_qopen = audio_stropen; helper->rqinit.qi_qclose = audio_strclose; helper->rqinit.qi_qadmin = NULL;
--- a/usr/src/uts/common/io/audio/impl/audio_impl.h Wed Sep 23 22:00:34 2009 -0400 +++ b/usr/src/uts/common/io/audio/impl/audio_impl.h Wed Sep 23 20:55:51 2009 -0700 @@ -183,12 +183,6 @@ kmutex_t c_lock; kcondvar_t c_cv; - ddi_taskq_t *c_tq; - boolean_t c_do_output; - boolean_t c_do_input; - boolean_t c_do_notify; - boolean_t c_do_drain; - boolean_t c_closing; boolean_t c_is_active; /* @@ -211,6 +205,7 @@ #define c_drain c_ops.aco_drain #define c_wput c_ops.aco_wput #define c_wsrv c_ops.aco_wsrv +#define c_rsrv c_ops.aco_rsrv struct pollhead c_pollhead; @@ -359,6 +354,8 @@ audio_ctrl_t *d_pcmvol_ctrl; uint64_t d_pcmvol; + volatile unsigned d_serial; + /* * Linkage onto global list of devices. */
--- a/usr/src/uts/common/io/audio/impl/audio_input.c Wed Sep 23 22:00:34 2009 -0400 +++ b/usr/src/uts/common/io/audio/impl/audio_input.c Wed Sep 23 20:55:51 2009 -0700 @@ -141,7 +141,6 @@ auimpl_input_callback(audio_engine_t *eng) { int fragfr = eng->e_fragfr; - boolean_t overrun; audio_client_t *c; /* consume all fragments in the buffer */ @@ -187,9 +186,6 @@ eng->e_errors++; sp->s_errors += count - space; count = space; - overrun = B_TRUE; - } else { - overrun = B_FALSE; } auimpl_produce_fragment(sp, count); @@ -199,13 +195,9 @@ mutex_exit(&sp->s_lock); - mutex_enter(&c->c_lock); - if (overrun) { - c->c_do_notify = B_TRUE; + if (c->c_input != NULL) { + c->c_input(c); } - c->c_do_input = B_TRUE; - cv_broadcast(&c->c_cv); - mutex_exit(&c->c_lock); } /*
--- a/usr/src/uts/common/io/audio/impl/audio_oss.c Wed Sep 23 22:00:34 2009 -0400 +++ b/usr/src/uts/common/io/audio/impl/audio_oss.c Wed Sep 23 20:55:51 2009 -0700 @@ -78,7 +78,6 @@ struct ossdev { audio_dev_t *d_dev; - uint_t d_modify_cnt; /* flag apps of ctrl changes */ uint_t d_nctrl; /* num actual controls */ uint_t d_nalloc; /* num allocated controls */ audio_ctrl_t **d_ctrls; /* array of control handles */ @@ -1345,20 +1344,15 @@ sound_mixer_info(audio_client_t *c, mixer_info *mi) { audio_dev_t *d; - ossdev_t *odev; - ossclient_t *sc; const char *name; - sc = auclnt_get_private(c); - odev = sc->o_ossdev; - d = auclnt_get_dev(c); name = auclnt_get_dev_name(d); (void) snprintf(mi->id, sizeof (mi->id), "%s", name); (void) snprintf(mi->name, sizeof (mi->name), "%s", name); (void) snprintf(mi->handle, sizeof (mi->handle), "%s", name); - mi->modify_counter = odev->d_modify_cnt; + mi->modify_counter = (int)auclnt_dev_get_serial(d); mi->card_number = auclnt_get_dev_index(d); mi->port_number = 0; return (0); @@ -1438,7 +1432,7 @@ (void) snprintf(mi->name, sizeof (mi->name), "%s", name); (void) snprintf(mi->id, sizeof (mi->id), "%s", name); (void) snprintf(mi->handle, sizeof (mi->handle), "%s", name); - mi->modify_counter = odev->d_modify_cnt; + mi->modify_counter = (int)auclnt_dev_get_serial(d); mi->card_number = auclnt_get_dev_index(d); mi->legacy_device = auclnt_get_dev_number(d); if (mi->legacy_device >= 0) { @@ -1924,19 +1918,6 @@ auclnt_pollwakeup(c, POLLIN | POLLRDNORM); } -static void -oss_notify(audio_client_t *c) -{ - audio_dev_t *d; - ossdev_t *odev; - - d = auclnt_get_dev(c); - if ((odev = auclnt_get_dev_minor_data(d, AUDIO_MINOR_DSP)) == NULL) { - return; - } - odev->d_modify_cnt++; -} - static int ossmix_open(audio_client_t *c, int oflag) { @@ -2578,7 +2559,6 @@ NULL, /* mmap */ oss_input, oss_output, - NULL, /* notify */ NULL, /* drain */ }; @@ -2595,7 +2575,6 @@ NULL, /* mmap */ NULL, /* input */ NULL, /* output */ - oss_notify, NULL, /* drain */ NULL, /* wput */ NULL, /* wsrv */ @@ -2615,7 +2594,6 @@ NULL, /* mmap */ NULL, /* input */ NULL, /* output */ - NULL, /* notify */ NULL, /* drain */ NULL, /* wput */ NULL, /* wsrv */
--- a/usr/src/uts/common/io/audio/impl/audio_output.c Wed Sep 23 22:00:34 2009 -0400 +++ b/usr/src/uts/common/io/audio/impl/audio_output.c Wed Sep 23 20:55:51 2009 -0700 @@ -288,7 +288,6 @@ int avail; int used; int offset; - boolean_t underrun = B_FALSE; boolean_t drained = B_FALSE; audio_client_t *c = sp->s_client; @@ -378,7 +377,6 @@ drained = B_TRUE; } } else { - underrun = B_TRUE; sp->s_errors += need; eng->e_stream_underruns++; } @@ -397,17 +395,20 @@ * chance of deadlock. */ - mutex_enter(&c->c_lock); - c->c_do_output = B_TRUE; - - if (underrun) - c->c_do_notify = B_TRUE; + /* + * NB: The only lock we are holding now is the engine + * lock. But the client can't go away because the + * closer would have to get the engine lock to remove + * the client's stream from engine. So we're safe. + */ - if (drained) - c->c_do_drain = B_TRUE; + if (c->c_output != NULL) { + c->c_output(c); + } - cv_broadcast(&c->c_cv); - mutex_exit(&c->c_lock); + if (drained && (c->c_drain != NULL)) { + c->c_drain(c); + } } /* @@ -450,7 +451,7 @@ cnt = eng->e_head - eng->e_tail; /* stay a bit ahead */ - while (cnt < (fragfr * 4)) { + while (cnt < ((fragfr * 3) / 2)) { auimpl_output_callback_impl(eng); cnt = eng->e_head - eng->e_tail; }
--- a/usr/src/uts/common/io/audio/impl/audio_sun.c Wed Sep 23 22:00:34 2009 -0400 +++ b/usr/src/uts/common/io/audio/impl/audio_sun.c Wed Sep 23 20:55:51 2009 -0700 @@ -539,7 +539,7 @@ while (auclnt_get_count(sp) >= count) { - if ((!canputnext(rq)) || + if ((!canput(rq)) || ((mp = allocb(nbytes, BPRI_MED)) == NULL)) { /* * This will apply back pressure to the @@ -560,7 +560,7 @@ (void) auclnt_consume_data(sp, (caddr_t)mp->b_wptr, count); mp->b_wptr += nbytes; - putnext(rq, mp); + (void) putq(rq, mp); } } @@ -968,14 +968,12 @@ static int devaudio_sigpoll(audio_client_t *c, void *arg) { - daproc_t *proc = arg; - daclient_t *dc; + pid_t pid = (pid_t)(uintptr_t)arg; if (auclnt_get_minor_type(c) == AUDIO_MINOR_DEVAUDIOCTL) { - dc = auclnt_get_private(c); /* we only need to notify peers in our own process */ - if ((dc != NULL) && (dc->dc_proc == proc)) { - (void) putnextctl1(auclnt_get_rq(c), M_PCSIG, SIGPOLL); + if (auclnt_get_pid(c) == pid) { + (void) putctl1(auclnt_get_rq(c), M_PCSIG, SIGPOLL); } } return (AUDIO_WALK_CONTINUE); @@ -995,7 +993,8 @@ while ((mp = mplist) != NULL) { mplist = mp->b_next; mp->b_next = NULL; - miocack(auclnt_get_wq(c), mp, 0, 0); + mioc2ack(mp, NULL, 0, 0); + (void) putq(auclnt_get_rq(c), mp); } } @@ -1025,7 +1024,7 @@ if (eofs) { auclnt_dev_walk_clients(auclnt_get_dev(c), - devaudio_sigpoll, proc); + devaudio_sigpoll, (void *)(uintptr_t)auclnt_get_pid(c)); } } @@ -1337,6 +1336,21 @@ } static void +devaudio_rsrv(audio_client_t *c) +{ + queue_t *rq = auclnt_get_rq(c); + mblk_t *mp; + + while ((mp = getq(rq)) != NULL) { + + if ((queclass(mp) != QPCTL) && (!canputnext(rq))) { + return; + } + putnext(rq, mp); + } +} + +static void devaudio_wsrv(audio_client_t *c) { queue_t *wq = auclnt_get_wq(c); @@ -1421,10 +1435,10 @@ NULL, /* mmap */ devaudio_input, devaudio_output, - NULL, /* notify */ devaudio_drain, devaudio_wput, - devaudio_wsrv + devaudio_wsrv, + devaudio_rsrv }; static struct audio_client_ops devaudioctl_ops = { @@ -1440,10 +1454,10 @@ NULL, /* mmap */ NULL, /* output */ NULL, /* input */ - NULL, /* notify */ NULL, /* drain */ devaudioctl_wput, NULL, + NULL, }; void