forked from Minki/linux
ALSA: hda: ext: fix locking in stream_release
The snd_hdac_ext_stream_release() routine uses the bus reg_lock, but releases it before calling snd_hdac_stream_release() where the bus reg_lock is taken again. This creates a timing window where the link stream release could test an invalid 'opened' boolean status and fail to recouple the host and link parts. Fix by exposing a locked version of snd_hdac_stream_release() and use it without releasing the spinlock. Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com> Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com> Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com> Link: https://lore.kernel.org/r/20220919121041.43463-8-pierre-louis.bossart@linux.intel.com Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
53f4f6b4e5
commit
ac3467ad7f
@ -551,6 +551,7 @@ void snd_hdac_stream_init(struct hdac_bus *bus, struct hdac_stream *azx_dev,
|
||||
int idx, int direction, int tag);
|
||||
struct hdac_stream *snd_hdac_stream_assign(struct hdac_bus *bus,
|
||||
struct snd_pcm_substream *substream);
|
||||
void snd_hdac_stream_release_locked(struct hdac_stream *azx_dev);
|
||||
void snd_hdac_stream_release(struct hdac_stream *azx_dev);
|
||||
struct hdac_stream *snd_hdac_get_stream(struct hdac_bus *bus,
|
||||
int dir, int stream_tag);
|
||||
|
@ -384,8 +384,8 @@ void snd_hdac_ext_stream_release(struct hdac_ext_stream *hext_stream, int type)
|
||||
spin_lock_irq(&bus->reg_lock);
|
||||
if (hext_stream->decoupled && !hext_stream->link_locked)
|
||||
snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false);
|
||||
snd_hdac_stream_release_locked(&hext_stream->hstream);
|
||||
spin_unlock_irq(&bus->reg_lock);
|
||||
snd_hdac_stream_release(&hext_stream->hstream);
|
||||
break;
|
||||
|
||||
case HDAC_EXT_STREAM_TYPE_LINK:
|
||||
|
@ -365,6 +365,21 @@ struct hdac_stream *snd_hdac_stream_assign(struct hdac_bus *bus,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_stream_assign);
|
||||
|
||||
/**
|
||||
* snd_hdac_stream_release_locked - release the assigned stream
|
||||
* @azx_dev: HD-audio core stream to release
|
||||
*
|
||||
* Release the stream that has been assigned by snd_hdac_stream_assign().
|
||||
* The bus->reg_lock needs to be taken at a higher level
|
||||
*/
|
||||
void snd_hdac_stream_release_locked(struct hdac_stream *azx_dev)
|
||||
{
|
||||
azx_dev->opened = 0;
|
||||
azx_dev->running = 0;
|
||||
azx_dev->substream = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_stream_release_locked);
|
||||
|
||||
/**
|
||||
* snd_hdac_stream_release - release the assigned stream
|
||||
* @azx_dev: HD-audio core stream to release
|
||||
@ -376,9 +391,7 @@ void snd_hdac_stream_release(struct hdac_stream *azx_dev)
|
||||
struct hdac_bus *bus = azx_dev->bus;
|
||||
|
||||
spin_lock_irq(&bus->reg_lock);
|
||||
azx_dev->opened = 0;
|
||||
azx_dev->running = 0;
|
||||
azx_dev->substream = NULL;
|
||||
snd_hdac_stream_release_locked(azx_dev);
|
||||
spin_unlock_irq(&bus->reg_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_stream_release);
|
||||
|
Loading…
Reference in New Issue
Block a user