Commit 9001b8e4 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'sound-4.5-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound

Pull sound fixes from Takashi Iwai:
 "This update contains again a few more fixes for ALSA core stuff
  although it's no longer high flux: two race fixes in sequencer and one
  PCM race fix for non-atomic PCM ops.

  In addition, HD-audio gained a similar fix for race at reloading the
  driver"

* tag 'sound-4.5-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound:
  ALSA: pcm: Fix rwsem deadlock for non-atomic PCM stream
  ALSA: seq: Fix double port list deletion
  ALSA: hda - Cancel probe work instead of flush at remove
  ALSA: seq: Fix leak of pool buffer at concurrent writes
parents 705d43db 67ec1072
...@@ -74,6 +74,18 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream); ...@@ -74,6 +74,18 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream);
static DEFINE_RWLOCK(snd_pcm_link_rwlock); static DEFINE_RWLOCK(snd_pcm_link_rwlock);
static DECLARE_RWSEM(snd_pcm_link_rwsem); static DECLARE_RWSEM(snd_pcm_link_rwsem);
/* Writer in rwsem may block readers even during its waiting in queue,
* and this may lead to a deadlock when the code path takes read sem
* twice (e.g. one in snd_pcm_action_nonatomic() and another in
* snd_pcm_stream_lock()). As a (suboptimal) workaround, let writer to
* spin until it gets the lock.
*/
static inline void down_write_nonblock(struct rw_semaphore *lock)
{
while (!down_write_trylock(lock))
cond_resched();
}
/** /**
* snd_pcm_stream_lock - Lock the PCM stream * snd_pcm_stream_lock - Lock the PCM stream
* @substream: PCM substream * @substream: PCM substream
...@@ -1813,7 +1825,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) ...@@ -1813,7 +1825,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
res = -ENOMEM; res = -ENOMEM;
goto _nolock; goto _nolock;
} }
down_write(&snd_pcm_link_rwsem); down_write_nonblock(&snd_pcm_link_rwsem);
write_lock_irq(&snd_pcm_link_rwlock); write_lock_irq(&snd_pcm_link_rwlock);
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN || if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN ||
substream->runtime->status->state != substream1->runtime->status->state || substream->runtime->status->state != substream1->runtime->status->state ||
...@@ -1860,7 +1872,7 @@ static int snd_pcm_unlink(struct snd_pcm_substream *substream) ...@@ -1860,7 +1872,7 @@ static int snd_pcm_unlink(struct snd_pcm_substream *substream)
struct snd_pcm_substream *s; struct snd_pcm_substream *s;
int res = 0; int res = 0;
down_write(&snd_pcm_link_rwsem); down_write_nonblock(&snd_pcm_link_rwsem);
write_lock_irq(&snd_pcm_link_rwlock); write_lock_irq(&snd_pcm_link_rwlock);
if (!snd_pcm_stream_linked(substream)) { if (!snd_pcm_stream_linked(substream)) {
res = -EALREADY; res = -EALREADY;
......
...@@ -383,15 +383,20 @@ int snd_seq_pool_init(struct snd_seq_pool *pool) ...@@ -383,15 +383,20 @@ int snd_seq_pool_init(struct snd_seq_pool *pool)
if (snd_BUG_ON(!pool)) if (snd_BUG_ON(!pool))
return -EINVAL; return -EINVAL;
if (pool->ptr) /* should be atomic? */
return 0;
pool->ptr = vmalloc(sizeof(struct snd_seq_event_cell) * pool->size); cellptr = vmalloc(sizeof(struct snd_seq_event_cell) * pool->size);
if (!pool->ptr) if (!cellptr)
return -ENOMEM; return -ENOMEM;
/* add new cells to the free cell list */ /* add new cells to the free cell list */
spin_lock_irqsave(&pool->lock, flags); spin_lock_irqsave(&pool->lock, flags);
if (pool->ptr) {
spin_unlock_irqrestore(&pool->lock, flags);
vfree(cellptr);
return 0;
}
pool->ptr = cellptr;
pool->free = NULL; pool->free = NULL;
for (cell = 0; cell < pool->size; cell++) { for (cell = 0; cell < pool->size; cell++) {
......
...@@ -535,19 +535,22 @@ static void delete_and_unsubscribe_port(struct snd_seq_client *client, ...@@ -535,19 +535,22 @@ static void delete_and_unsubscribe_port(struct snd_seq_client *client,
bool is_src, bool ack) bool is_src, bool ack)
{ {
struct snd_seq_port_subs_info *grp; struct snd_seq_port_subs_info *grp;
struct list_head *list;
bool empty;
grp = is_src ? &port->c_src : &port->c_dest; grp = is_src ? &port->c_src : &port->c_dest;
list = is_src ? &subs->src_list : &subs->dest_list;
down_write(&grp->list_mutex); down_write(&grp->list_mutex);
write_lock_irq(&grp->list_lock); write_lock_irq(&grp->list_lock);
if (is_src) empty = list_empty(list);
list_del(&subs->src_list); if (!empty)
else list_del_init(list);
list_del(&subs->dest_list);
grp->exclusive = 0; grp->exclusive = 0;
write_unlock_irq(&grp->list_lock); write_unlock_irq(&grp->list_lock);
up_write(&grp->list_mutex); up_write(&grp->list_mutex);
unsubscribe_port(client, port, grp, &subs->info, ack); if (!empty)
unsubscribe_port(client, port, grp, &subs->info, ack);
} }
/* connect two ports */ /* connect two ports */
......
...@@ -2168,10 +2168,10 @@ static void azx_remove(struct pci_dev *pci) ...@@ -2168,10 +2168,10 @@ static void azx_remove(struct pci_dev *pci)
struct hda_intel *hda; struct hda_intel *hda;
if (card) { if (card) {
/* flush the pending probing work */ /* cancel the pending probing work */
chip = card->private_data; chip = card->private_data;
hda = container_of(chip, struct hda_intel, chip); hda = container_of(chip, struct hda_intel, chip);
flush_work(&hda->probe_work); cancel_work_sync(&hda->probe_work);
snd_card_free(card); snd_card_free(card);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment