ALSA: hda: hdac_ext_stream: fix potential locking issues
The code for hdac_ext_stream seems inherited from hdac_stream, and similar locking issues are present: the use of the bus->reg_lock spinlock is inconsistent, with only writes to specific fields being protected. Apply similar fix as in hdac_stream by protecting all accesses to 'link_locked' and 'decoupled' fields, with a new helper snd_hdac_ext_stream_decouple_locked() added to simplify code changes. Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> Link: https://lore.kernel.org/r/20210924192417.169243-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Родитель
1465d06a6d
Коммит
868ddfcef3
|
@ -88,6 +88,8 @@ struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus,
|
||||||
struct snd_pcm_substream *substream,
|
struct snd_pcm_substream *substream,
|
||||||
int type);
|
int type);
|
||||||
void snd_hdac_ext_stream_release(struct hdac_ext_stream *azx_dev, int type);
|
void snd_hdac_ext_stream_release(struct hdac_ext_stream *azx_dev, int type);
|
||||||
|
void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus,
|
||||||
|
struct hdac_ext_stream *azx_dev, bool decouple);
|
||||||
void snd_hdac_ext_stream_decouple(struct hdac_bus *bus,
|
void snd_hdac_ext_stream_decouple(struct hdac_bus *bus,
|
||||||
struct hdac_ext_stream *azx_dev, bool decouple);
|
struct hdac_ext_stream *azx_dev, bool decouple);
|
||||||
void snd_hdac_ext_stop_streams(struct hdac_bus *bus);
|
void snd_hdac_ext_stop_streams(struct hdac_bus *bus);
|
||||||
|
|
|
@ -106,20 +106,14 @@ void snd_hdac_stream_free_all(struct hdac_bus *bus)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(snd_hdac_stream_free_all);
|
EXPORT_SYMBOL_GPL(snd_hdac_stream_free_all);
|
||||||
|
|
||||||
/**
|
void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus,
|
||||||
* snd_hdac_ext_stream_decouple - decouple the hdac stream
|
struct hdac_ext_stream *stream,
|
||||||
* @bus: HD-audio core bus
|
bool decouple)
|
||||||
* @stream: HD-audio ext core stream object to initialize
|
|
||||||
* @decouple: flag to decouple
|
|
||||||
*/
|
|
||||||
void snd_hdac_ext_stream_decouple(struct hdac_bus *bus,
|
|
||||||
struct hdac_ext_stream *stream, bool decouple)
|
|
||||||
{
|
{
|
||||||
struct hdac_stream *hstream = &stream->hstream;
|
struct hdac_stream *hstream = &stream->hstream;
|
||||||
u32 val;
|
u32 val;
|
||||||
int mask = AZX_PPCTL_PROCEN(hstream->index);
|
int mask = AZX_PPCTL_PROCEN(hstream->index);
|
||||||
|
|
||||||
spin_lock_irq(&bus->reg_lock);
|
|
||||||
val = readw(bus->ppcap + AZX_REG_PP_PPCTL) & mask;
|
val = readw(bus->ppcap + AZX_REG_PP_PPCTL) & mask;
|
||||||
|
|
||||||
if (decouple && !val)
|
if (decouple && !val)
|
||||||
|
@ -128,6 +122,20 @@ void snd_hdac_ext_stream_decouple(struct hdac_bus *bus,
|
||||||
snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, 0);
|
snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, 0);
|
||||||
|
|
||||||
stream->decoupled = decouple;
|
stream->decoupled = decouple;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple_locked);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* snd_hdac_ext_stream_decouple - decouple the hdac stream
|
||||||
|
* @bus: HD-audio core bus
|
||||||
|
* @stream: HD-audio ext core stream object to initialize
|
||||||
|
* @decouple: flag to decouple
|
||||||
|
*/
|
||||||
|
void snd_hdac_ext_stream_decouple(struct hdac_bus *bus,
|
||||||
|
struct hdac_ext_stream *stream, bool decouple)
|
||||||
|
{
|
||||||
|
spin_lock_irq(&bus->reg_lock);
|
||||||
|
snd_hdac_ext_stream_decouple_locked(bus, stream, decouple);
|
||||||
spin_unlock_irq(&bus->reg_lock);
|
spin_unlock_irq(&bus->reg_lock);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple);
|
EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple);
|
||||||
|
@ -252,6 +260,7 @@ hdac_ext_link_stream_assign(struct hdac_bus *bus,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spin_lock_irq(&bus->reg_lock);
|
||||||
list_for_each_entry(stream, &bus->stream_list, list) {
|
list_for_each_entry(stream, &bus->stream_list, list) {
|
||||||
struct hdac_ext_stream *hstream = container_of(stream,
|
struct hdac_ext_stream *hstream = container_of(stream,
|
||||||
struct hdac_ext_stream,
|
struct hdac_ext_stream,
|
||||||
|
@ -266,17 +275,16 @@ hdac_ext_link_stream_assign(struct hdac_bus *bus,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hstream->link_locked) {
|
if (!hstream->link_locked) {
|
||||||
snd_hdac_ext_stream_decouple(bus, hstream, true);
|
snd_hdac_ext_stream_decouple_locked(bus, hstream, true);
|
||||||
res = hstream;
|
res = hstream;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (res) {
|
if (res) {
|
||||||
spin_lock_irq(&bus->reg_lock);
|
|
||||||
res->link_locked = 1;
|
res->link_locked = 1;
|
||||||
res->link_substream = substream;
|
res->link_substream = substream;
|
||||||
spin_unlock_irq(&bus->reg_lock);
|
|
||||||
}
|
}
|
||||||
|
spin_unlock_irq(&bus->reg_lock);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,6 +300,7 @@ hdac_ext_host_stream_assign(struct hdac_bus *bus,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spin_lock_irq(&bus->reg_lock);
|
||||||
list_for_each_entry(stream, &bus->stream_list, list) {
|
list_for_each_entry(stream, &bus->stream_list, list) {
|
||||||
struct hdac_ext_stream *hstream = container_of(stream,
|
struct hdac_ext_stream *hstream = container_of(stream,
|
||||||
struct hdac_ext_stream,
|
struct hdac_ext_stream,
|
||||||
|
@ -301,18 +310,17 @@ hdac_ext_host_stream_assign(struct hdac_bus *bus,
|
||||||
|
|
||||||
if (!stream->opened) {
|
if (!stream->opened) {
|
||||||
if (!hstream->decoupled)
|
if (!hstream->decoupled)
|
||||||
snd_hdac_ext_stream_decouple(bus, hstream, true);
|
snd_hdac_ext_stream_decouple_locked(bus, hstream, true);
|
||||||
res = hstream;
|
res = hstream;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (res) {
|
if (res) {
|
||||||
spin_lock_irq(&bus->reg_lock);
|
|
||||||
res->hstream.opened = 1;
|
res->hstream.opened = 1;
|
||||||
res->hstream.running = 0;
|
res->hstream.running = 0;
|
||||||
res->hstream.substream = substream;
|
res->hstream.substream = substream;
|
||||||
spin_unlock_irq(&bus->reg_lock);
|
|
||||||
}
|
}
|
||||||
|
spin_unlock_irq(&bus->reg_lock);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -378,15 +386,17 @@ void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HDAC_EXT_STREAM_TYPE_HOST:
|
case HDAC_EXT_STREAM_TYPE_HOST:
|
||||||
|
spin_lock_irq(&bus->reg_lock);
|
||||||
if (stream->decoupled && !stream->link_locked)
|
if (stream->decoupled && !stream->link_locked)
|
||||||
snd_hdac_ext_stream_decouple(bus, stream, false);
|
snd_hdac_ext_stream_decouple_locked(bus, stream, false);
|
||||||
|
spin_unlock_irq(&bus->reg_lock);
|
||||||
snd_hdac_stream_release(&stream->hstream);
|
snd_hdac_stream_release(&stream->hstream);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HDAC_EXT_STREAM_TYPE_LINK:
|
case HDAC_EXT_STREAM_TYPE_LINK:
|
||||||
if (stream->decoupled && !stream->hstream.opened)
|
|
||||||
snd_hdac_ext_stream_decouple(bus, stream, false);
|
|
||||||
spin_lock_irq(&bus->reg_lock);
|
spin_lock_irq(&bus->reg_lock);
|
||||||
|
if (stream->decoupled && !stream->hstream.opened)
|
||||||
|
snd_hdac_ext_stream_decouple_locked(bus, stream, false);
|
||||||
stream->link_locked = 0;
|
stream->link_locked = 0;
|
||||||
stream->link_substream = NULL;
|
stream->link_substream = NULL;
|
||||||
spin_unlock_irq(&bus->reg_lock);
|
spin_unlock_irq(&bus->reg_lock);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче