[alsa-devel] [PATCH 02/11] fireface: postpone sound card registration

Stefan Richter stefanr at s5r6.in-berlin.de
Thu Dec 24 20:21:35 CET 2015


On Dec 20 Takashi Sakamoto wrote:
> Just after appearing on IEEE 1394 bus, this unit generates several bus
> resets. This is due to loading firmware from on-board flash memory and
> initialize hardware. It's better to postpone sound card registration.
> 
> This commit schedules workqueue to process actual probe processing
> 1 seconds after the last bus-reset. The card instance is kept at unit
> probe callback and released at card free callback. Therefore, when the
> actual probe processing fails, the memory block is wasted. This is due to
> simplify driver implementation.
> 
> Signed-off-by: Takashi Sakamoto <o-takashi at sakamocchi.jp>
> ---
>  sound/firewire/fireface/fireface.c | 59 ++++++++++++++++++++++++++++++++++----
>  sound/firewire/fireface/fireface.h |  3 ++
>  2 files changed, 56 insertions(+), 6 deletions(-)
> 
> diff --git a/sound/firewire/fireface/fireface.c b/sound/firewire/fireface/fireface.c
> index c8b7fe7..cf10e97 100644
> --- a/sound/firewire/fireface/fireface.c
> +++ b/sound/firewire/fireface/fireface.c
> @@ -10,6 +10,8 @@
>  
>  #define OUI_RME	0x000a35
>  
> +#define PROBE_DELAY_MS		(1 * MSEC_PER_SEC)
> +
>  MODULE_DESCRIPTION("RME Fireface series Driver");
>  MODULE_AUTHOR("Takashi Sakamoto <o-takashi at sakamocchi.jp>");
>  MODULE_LICENSE("GPL v2");
> @@ -32,16 +34,47 @@ static void ff_card_free(struct snd_card *card)
>  {
>  	struct snd_ff *ff = card->private_data;
>  
> +	/* The workqueue for registration uses the memory block. */
> +	cancel_work_sync(&ff->dwork.work);
> +
>  	fw_unit_put(ff->unit);
>  
>  	mutex_destroy(&ff->mutex);
>  }

The same comments apply as to patch "ALSA: dice: postpone card
registration":

  - use cancel_delayed_work_sync(&ff->dwork);

  - ff_card_free() is called via snd_card_free_when_closed() at
    snd_ff_remove() while &ff->mutex is held.  On the other hand, the
    worker do_probe() attempts to take the mutex too.  Hence this can
    deadlock.

    cancel_delayed_work_sync(&ff->dwork) needs to be called outside a
    ff->mutex protected section, certainly at the beginning of
    snd_ff_remove().

  - mutex_destroy(&ff->mutex); cannot be called here.
    snd_ff_remove() is still holding the mutex.

[No further comments below, only quoting the patch.]

> +static void do_probe(struct work_struct *work)
> +{
> +	struct snd_ff *ff = container_of(work, struct snd_ff, dwork.work);
> +	int err;
> +
> +	mutex_lock(&ff->mutex);
> +
> +	if (ff->card->shutdown || ff->probed)
> +		goto end;
> +
> +	err = snd_card_register(ff->card);
> +	if (err < 0)
> +		goto end;
> +
> +	ff->probed = true;
> +end:
> +	mutex_unlock(&ff->mutex);
> +
> +	/*
> +	 * It's a difficult work to manage a race condition between workqueue,
> +	 * unit event handlers and processes. The memory block for this card
> +	 * is released as the same way that usual sound cards are going to be
> +	 * released.
> +	 */
> +}
> +
>  static int snd_ff_probe(struct fw_unit *unit,
>  			   const struct ieee1394_device_id *entry)
>  {
> +	struct fw_card *fw_card = fw_parent_device(unit)->card;
>  	struct snd_card *card;
>  	struct snd_ff *ff;
> +	unsigned long delay;
>  	int err;
>  
>  	err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
> @@ -60,26 +93,40 @@ static int snd_ff_probe(struct fw_unit *unit,
>  
>  	name_card(ff);
>  
> -	err = snd_card_register(card);
> -	if (err < 0) {
> -		snd_card_free(card);
> -		return err;
> -	}
> +	/* Register this sound card later. */
> +	INIT_DEFERRABLE_WORK(&ff->dwork, do_probe);
> +	delay = msecs_to_jiffies(PROBE_DELAY_MS) +
> +				fw_card->reset_jiffies - get_jiffies_64();
> +	schedule_delayed_work(&ff->dwork, delay);
>  
>  	return 0;
>  }
>  
>  static void snd_ff_update(struct fw_unit *unit)
>  {
> -	return;
> +	struct snd_ff *ff = dev_get_drvdata(&unit->device);
> +	struct fw_card *fw_card = fw_parent_device(unit)->card;
> +	unsigned long delay;
> +
> +	/* Postpone a workqueue for deferred registration. */
> +	if (!ff->probed) {
> +		delay = msecs_to_jiffies(PROBE_DELAY_MS) -
> +				(get_jiffies_64() - fw_card->reset_jiffies);
> +		mod_delayed_work(ff->dwork.wq, &ff->dwork, delay);
> +	}
>  }
>  
>  static void snd_ff_remove(struct fw_unit *unit)
>  {
>  	struct snd_ff *ff = dev_get_drvdata(&unit->device);
>  
> +	/* For a race condition of struct snd_card.shutdown. */
> +	mutex_lock(&ff->mutex);
> +
>  	/* No need to wait for releasing card object in this context. */
>  	snd_card_free_when_closed(ff->card);
> +
> +	mutex_unlock(&ff->mutex);
>  }
>  
>  static const struct ieee1394_device_id snd_ff_id_table[] = {
> diff --git a/sound/firewire/fireface/fireface.h b/sound/firewire/fireface/fireface.h
> index ab596c7..74877ef 100644
> --- a/sound/firewire/fireface/fireface.h
> +++ b/sound/firewire/fireface/fireface.h
> @@ -35,5 +35,8 @@ struct snd_ff {
>  	struct snd_card *card;
>  	struct fw_unit *unit;
>  	struct mutex mutex;
> +
> +	bool probed;
> +	struct delayed_work dwork;
>  };
>  #endif

-- 
Stefan Richter
-=====-===== ==-- ==---
http://arcgraph.de/sr/


More information about the Alsa-devel mailing list