svn commit: r308424 - head/sys/arm/broadcom/bcm2835

Oleksandr Tymoshenko gonzo at FreeBSD.org
Mon Nov 7 17:38:41 UTC 2016


Author: gonzo
Date: Mon Nov  7 17:38:39 2016
New Revision: 308424
URL: https://svnweb.freebsd.org/changeset/base/308424

Log:
  Fix locking in bcm2835_audio driver
  
  - Move all VCHI activity to worker thread: channel methods are called with
      non-sleepable lock held and VCHI uses sleepable lock.
  
  - In worker thread use sx(9) lock instead of mutex(9) for the same reason.
  
  PR:		213801, 205979

Modified:
  head/sys/arm/broadcom/bcm2835/bcm2835_audio.c

Modified: head/sys/arm/broadcom/bcm2835/bcm2835_audio.c
==============================================================================
--- head/sys/arm/broadcom/bcm2835/bcm2835_audio.c	Mon Nov  7 17:34:19 2016	(r308423)
+++ head/sys/arm/broadcom/bcm2835/bcm2835_audio.c	Mon Nov  7 17:38:39 2016	(r308424)
@@ -104,14 +104,17 @@ struct bcm2835_audio_info {
 	struct intr_config_hook intr_hook;
 
 	/* VCHI data */
-	struct mtx vchi_lock;
+	struct sx vchi_lock;
 
 	VCHI_INSTANCE_T vchi_instance;
 	VCHI_CONNECTION_T *vchi_connection;
 	VCHI_SERVICE_HANDLE_T vchi_handle;
 
-	struct mtx data_lock;
-	struct cv data_cv;
+	struct sx worker_lock;
+	struct cv worker_cv;
+
+	bool parameters_update_pending;
+	bool controls_update_pending;
 
 	/* Unloadign module */
 	int unloading;
@@ -121,8 +124,8 @@ struct bcm2835_audio_info {
 #define bcm2835_audio_unlock(_ess) snd_mtxunlock((_ess)->lock)
 #define bcm2835_audio_lock_assert(_ess) snd_mtxassert((_ess)->lock)
 
-#define VCHIQ_VCHI_LOCK(sc)		mtx_lock(&(sc)->vchi_lock)
-#define VCHIQ_VCHI_UNLOCK(sc)		mtx_unlock(&(sc)->vchi_lock)
+#define VCHIQ_VCHI_LOCK(sc)		sx_xlock(&(sc)->vchi_lock)
+#define VCHIQ_VCHI_UNLOCK(sc)		sx_xunlock(&(sc)->vchi_lock)
 
 static const char *
 dest_description(uint32_t dest)
@@ -175,7 +178,7 @@ bcm2835_audio_callback(void *param, cons
 		chn_intr(sc->pch.channel);
 
 		if (perr || ch->free_buffer >= VCHIQ_AUDIO_PACKET_SIZE)
-			cv_signal(&sc->data_cv);
+			cv_signal(&sc->worker_cv);
 	} else
 		printf("%s: unknown m.type: %d\n", __func__, m.type);
 }
@@ -261,8 +264,6 @@ bcm2835_audio_start(struct bcm2835_audio
 	if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
 		vchi_service_use(sc->vchi_handle);
 
-		bcm2835_audio_reset_channel(ch);
-
 		m.type = VC_AUDIO_MSG_TYPE_START;
 		ret = vchi_msg_queue(sc->vchi_handle,
 		    &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
@@ -324,7 +325,7 @@ bcm2835_audio_open(struct bcm2835_audio_
 }
 
 static void
-bcm2835_audio_update_controls(struct bcm2835_audio_info *sc)
+bcm2835_audio_update_controls(struct bcm2835_audio_info *sc, uint32_t volume, uint32_t dest)
 {
 	VC_AUDIO_MSG_T m;
 	int ret, db;
@@ -334,10 +335,10 @@ bcm2835_audio_update_controls(struct bcm
 		vchi_service_use(sc->vchi_handle);
 
 		m.type = VC_AUDIO_MSG_TYPE_CONTROL;
-		m.u.control.dest = sc->dest;
-		if (sc->volume > 99)
-			sc->volume = 99;
-		db = db_levels[sc->volume/5];
+		m.u.control.dest = dest;
+		if (volume > 99)
+			volume = 99;
+		db = db_levels[volume/5];
 		m.u.control.volume = VCHIQ_AUDIO_VOLUME(db);
 
 		ret = vchi_msg_queue(sc->vchi_handle,
@@ -352,7 +353,7 @@ bcm2835_audio_update_controls(struct bcm
 }
 
 static void
-bcm2835_audio_update_params(struct bcm2835_audio_info *sc, struct bcm2835_audio_chinfo *ch)
+bcm2835_audio_update_params(struct bcm2835_audio_info *sc, uint32_t fmt, uint32_t speed)
 {
 	VC_AUDIO_MSG_T m;
 	int ret;
@@ -362,9 +363,9 @@ bcm2835_audio_update_params(struct bcm28
 		vchi_service_use(sc->vchi_handle);
 
 		m.type = VC_AUDIO_MSG_TYPE_CONFIG;
-		m.u.config.channels = AFMT_CHANNEL(ch->fmt);
-		m.u.config.samplerate = ch->spd;
-		m.u.config.bps = AFMT_BIT(ch->fmt);
+		m.u.config.channels = AFMT_CHANNEL(fmt);
+		m.u.config.samplerate = speed;
+		m.u.config.bps = AFMT_BIT(fmt);
 
 		ret = vchi_msg_queue(sc->vchi_handle,
 		    &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
@@ -474,29 +475,61 @@ bcm2835_audio_worker(void *data)
 {
 	struct bcm2835_audio_info *sc = (struct bcm2835_audio_info *)data;
 	struct bcm2835_audio_chinfo *ch = &sc->pch;
-	mtx_lock(&sc->data_lock);
+	uint32_t speed, format;
+	uint32_t volume, dest;
+	bool parameters_changed, controls_changed;
+
+	sx_slock(&sc->worker_lock);
 	while(1) {
 
 		if (sc->unloading)
 			break;
 
+		parameters_changed = false;
+		controls_changed = false;
+		bcm2835_audio_lock(sc);
+		if (sc->parameters_update_pending) {
+			/* TODO: update parameters */
+			speed = ch->spd;
+			format = ch->fmt;
+			sc->parameters_update_pending = false;
+			parameters_changed = true;
+		}
+
+		if (sc->controls_update_pending) {
+			volume = sc->volume;
+			dest = sc->dest;
+			sc->controls_update_pending = false;
+			controls_changed = true;
+		}
+
+		bcm2835_audio_unlock(sc);
+
+		if (parameters_changed) {
+			bcm2835_audio_update_params(sc, format, speed);
+		}
+
+		if (controls_changed) {
+			bcm2835_audio_update_controls(sc, volume, dest);
+		}
+
 		if (ch->playback_state == PLAYBACK_IDLE) {
-			cv_wait_sig(&sc->data_cv, &sc->data_lock);
+			cv_wait_sig(&sc->worker_cv, &sc->worker_lock);
 			continue;
 		}
 
 		if (ch->playback_state == PLAYBACK_STOPPING) {
+			bcm2835_audio_stop(ch);
 			bcm2835_audio_reset_channel(&sc->pch);
 			ch->playback_state = PLAYBACK_IDLE;
 			continue;
 		}
 
 		if (ch->free_buffer < vchiq_unbuffered_bytes(ch)) {
-			cv_timedwait_sig(&sc->data_cv, &sc->data_lock, 10);
+			cv_timedwait_sig(&sc->worker_cv, &sc->worker_lock, 10);
 			continue;
 		}
 
-
 		bcm2835_audio_write_samples(ch);
 
 		if (ch->playback_state == PLAYBACK_STARTING) {
@@ -507,7 +540,7 @@ bcm2835_audio_worker(void *data)
 			}
 		}
 	}
-	mtx_unlock(&sc->data_lock);
+	sx_sunlock(&sc->worker_lock);
 
 	kproc_exit(0);
 }
@@ -547,11 +580,13 @@ bcmchan_init(kobj_t obj, void *devinfo, 
 	buffer = malloc(sc->bufsz, M_DEVBUF, M_WAITOK | M_ZERO);
 
 	if (sndbuf_setup(ch->buffer, buffer, sc->bufsz) != 0) {
+		device_printf(sc->dev, "sndbuf_setup failed\n");
 		free(buffer, M_DEVBUF);
 		return NULL;
 	}
 
-	bcm2835_audio_update_params(sc, ch);
+	sc->parameters_update_pending = true;
+	cv_signal(&sc->worker_cv);
 
 	return ch;
 }
@@ -576,12 +611,12 @@ bcmchan_setformat(kobj_t obj, void *data
 	struct bcm2835_audio_info *sc = ch->parent;
 
 	bcm2835_audio_lock(sc);
-
 	ch->fmt = format;
-	bcm2835_audio_update_params(sc, ch);
-
+	sc->parameters_update_pending = true;
 	bcm2835_audio_unlock(sc);
 
+	cv_signal(&sc->worker_cv);
+
 	return 0;
 }
 
@@ -592,12 +627,12 @@ bcmchan_setspeed(kobj_t obj, void *data,
 	struct bcm2835_audio_info *sc = ch->parent;
 
 	bcm2835_audio_lock(sc);
-
 	ch->spd = speed;
-	bcm2835_audio_update_params(sc, ch);
-
+	sc->parameters_update_pending = true;
 	bcm2835_audio_unlock(sc);
 
+	cv_signal(&sc->worker_cv);
+
 	return ch->spd;
 }
 
@@ -618,26 +653,30 @@ bcmchan_trigger(kobj_t obj, void *data, 
 	if (!PCMTRIG_COMMON(go))
 		return (0);
 
-	bcm2835_audio_lock(sc);
 
 	switch (go) {
 	case PCMTRIG_START:
+		bcm2835_audio_lock(sc);
+		bcm2835_audio_reset_channel(ch);
 		ch->playback_state = PLAYBACK_STARTING;
+		bcm2835_audio_unlock(sc);
+		/* kickstart data flow */
+		chn_intr(sc->pch.channel);
 		/* wakeup worker thread */
-		cv_signal(&sc->data_cv);
+		cv_signal(&sc->worker_cv);
 		break;
 
 	case PCMTRIG_STOP:
 	case PCMTRIG_ABORT:
+		bcm2835_audio_lock(sc);
 		ch->playback_state = PLAYBACK_STOPPING;
-		bcm2835_audio_stop(ch);
+		bcm2835_audio_unlock(sc);
+		cv_signal(&sc->worker_cv);
 		break;
 
 	default:
 		break;
 	}
-
-	bcm2835_audio_unlock(sc);
 	return 0;
 }
 
@@ -695,8 +734,11 @@ bcmmix_set(struct snd_mixer *m, unsigned
 
 	switch (dev) {
 	case SOUND_MIXER_VOLUME:
+		bcm2835_audio_lock(sc);
 		sc->volume = left;
-		bcm2835_audio_update_controls(sc);
+		sc->controls_update_pending = true;
+		bcm2835_audio_unlock(sc);
+		cv_signal(&sc->worker_cv);
 		break;
 
 	default:
@@ -729,9 +771,13 @@ sysctl_bcm2835_audio_dest(SYSCTL_HANDLER
 	if ((val < 0) || (val > 2))
 		return (EINVAL);
 
+	bcm2835_audio_lock(sc);
 	sc->dest = val;
+	sc->controls_update_pending = true;
+	bcm2835_audio_unlock(sc);
+
+	cv_signal(&sc->worker_cv);
 	device_printf(sc->dev, "destination set to %s\n", dest_description(val));
-	bcm2835_audio_update_controls(sc);
 
 	return (0);
 }
@@ -821,9 +867,9 @@ bcm2835_audio_attach(device_t dev)
 
 	sc->lock = snd_mtxcreate(device_get_nameunit(dev), "bcm2835_audio softc");
 
-	mtx_init(&sc->vchi_lock, "bcm2835_audio", "vchi_lock", MTX_DEF);
-	mtx_init(&sc->data_lock, "data_mtx", "data_mtx", MTX_DEF);
-	cv_init(&sc->data_cv, "data_cv");
+	sx_init(&sc->vchi_lock, device_get_nameunit(dev));
+	sx_init(&sc->worker_lock, "bcm_audio_worker_lock");
+	cv_init(&sc->worker_cv, "worker_cv");
 	sc->vchi_handle = VCHIQ_SERVICE_HANDLE_INVALID;
 
 	/* 
@@ -850,16 +896,18 @@ bcm2835_audio_detach(device_t dev)
 	sc = pcm_getdevinfo(dev);
 
 	/* Stop worker thread */
+	sx_xlock(&sc->worker_lock);
 	sc->unloading = 1;
-	cv_signal(&sc->data_cv);
+	sx_xunlock(&sc->worker_lock);
+	cv_signal(&sc->worker_cv);
 
 	r = pcm_unregister(dev);
 	if (r)
 		return r;
 
-	mtx_destroy(&sc->vchi_lock);
-	mtx_destroy(&sc->data_lock);
-	cv_destroy(&sc->data_cv);
+	sx_destroy(&sc->vchi_lock);
+	sx_destroy(&sc->worker_lock);
+	cv_destroy(&sc->worker_cv);
 
 	bcm2835_audio_release(sc);
 


More information about the svn-src-head mailing list