]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
fixes for 4.9
authorSasha Levin <sashal@kernel.org>
Mon, 4 Nov 2019 16:02:15 +0000 (11:02 -0500)
committerSasha Levin <sashal@kernel.org>
Mon, 4 Nov 2019 16:02:15 +0000 (11:02 -0500)
Signed-off-by: Sasha Levin <sashal@kernel.org>
queue-4.9/alsa-timer-fix-mutex-deadlock-at-releasing-card.patch [new file with mode: 0644]
queue-4.9/alsa-timer-follow-standard-export_symbol-declaration.patch [new file with mode: 0644]
queue-4.9/alsa-timer-limit-max-instances-per-timer.patch [new file with mode: 0644]
queue-4.9/alsa-timer-simplify-error-path-in-snd_timer_open.patch [new file with mode: 0644]
queue-4.9/series

diff --git a/queue-4.9/alsa-timer-fix-mutex-deadlock-at-releasing-card.patch b/queue-4.9/alsa-timer-fix-mutex-deadlock-at-releasing-card.patch
new file mode 100644 (file)
index 0000000..b000460
--- /dev/null
@@ -0,0 +1,133 @@
+From 635f7ef2cc4ea7adc147380526ce75f4bfe9a8b9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 30 Oct 2019 22:42:57 +0100
+Subject: ALSA: timer: Fix mutex deadlock at releasing card
+
+From: Takashi Iwai <tiwai@suse.de>
+
+[ Upstream commit a39331867335d4a94b6165e306265c9e24aca073 ]
+
+When a card is disconnected while in use, the system waits until all
+opened files are closed then releases the card.  This is done via
+put_device() of the card device in each device release code.
+
+The recently reported mutex deadlock bug happens in this code path;
+snd_timer_close() for the timer device deals with the global
+register_mutex and it calls put_device() there.  When this timer
+device is the last one, the card gets freed and it eventually calls
+snd_timer_free(), which has again the protection with the global
+register_mutex -- boom.
+
+Basically put_device() call itself is race-free, so a relative simple
+workaround is to move this put_device() call out of the mutex.  For
+achieving that, in this patch, snd_timer_close_locked() got a new
+argument to store the card device pointer in return, and each caller
+invokes put_device() with the returned object after the mutex unlock.
+
+Reported-and-tested-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/core/timer.c | 24 +++++++++++++++++-------
+ 1 file changed, 17 insertions(+), 7 deletions(-)
+
+diff --git a/sound/core/timer.c b/sound/core/timer.c
+index 6eb4e97662d9c..19d90aa082184 100644
+--- a/sound/core/timer.c
++++ b/sound/core/timer.c
+@@ -239,7 +239,8 @@ static int snd_timer_check_master(struct snd_timer_instance *master)
+       return 0;
+ }
+-static int snd_timer_close_locked(struct snd_timer_instance *timeri);
++static int snd_timer_close_locked(struct snd_timer_instance *timeri,
++                                struct device **card_devp_to_put);
+ /*
+  * open a timer instance
+@@ -251,6 +252,7 @@ int snd_timer_open(struct snd_timer_instance **ti,
+ {
+       struct snd_timer *timer;
+       struct snd_timer_instance *timeri = NULL;
++      struct device *card_dev_to_put = NULL;
+       int err;
+       mutex_lock(&register_mutex);
+@@ -274,7 +276,7 @@ int snd_timer_open(struct snd_timer_instance **ti,
+               list_add_tail(&timeri->open_list, &snd_timer_slave_list);
+               err = snd_timer_check_slave(timeri);
+               if (err < 0) {
+-                      snd_timer_close_locked(timeri);
++                      snd_timer_close_locked(timeri, &card_dev_to_put);
+                       timeri = NULL;
+               }
+               goto unlock;
+@@ -326,7 +328,7 @@ int snd_timer_open(struct snd_timer_instance **ti,
+                       timeri = NULL;
+                       if (timer->card)
+-                              put_device(&timer->card->card_dev);
++                              card_dev_to_put = &timer->card->card_dev;
+                       module_put(timer->module);
+                       goto unlock;
+               }
+@@ -336,12 +338,15 @@ int snd_timer_open(struct snd_timer_instance **ti,
+       timer->num_instances++;
+       err = snd_timer_check_master(timeri);
+       if (err < 0) {
+-              snd_timer_close_locked(timeri);
++              snd_timer_close_locked(timeri, &card_dev_to_put);
+               timeri = NULL;
+       }
+  unlock:
+       mutex_unlock(&register_mutex);
++      /* put_device() is called after unlock for avoiding deadlock */
++      if (card_dev_to_put)
++              put_device(card_dev_to_put);
+       *ti = timeri;
+       return err;
+ }
+@@ -351,7 +356,8 @@ EXPORT_SYMBOL(snd_timer_open);
+  * close a timer instance
+  * call this with register_mutex down.
+  */
+-static int snd_timer_close_locked(struct snd_timer_instance *timeri)
++static int snd_timer_close_locked(struct snd_timer_instance *timeri,
++                                struct device **card_devp_to_put)
+ {
+       struct snd_timer *timer = NULL;
+       struct snd_timer_instance *slave, *tmp;
+@@ -403,7 +409,7 @@ static int snd_timer_close_locked(struct snd_timer_instance *timeri)
+                       timer->hw.close(timer);
+               /* release a card refcount for safe disconnection */
+               if (timer->card)
+-                      put_device(&timer->card->card_dev);
++                      *card_devp_to_put = &timer->card->card_dev;
+               module_put(timer->module);
+       }
+@@ -415,14 +421,18 @@ static int snd_timer_close_locked(struct snd_timer_instance *timeri)
+  */
+ int snd_timer_close(struct snd_timer_instance *timeri)
+ {
++      struct device *card_dev_to_put = NULL;
+       int err;
+       if (snd_BUG_ON(!timeri))
+               return -ENXIO;
+       mutex_lock(&register_mutex);
+-      err = snd_timer_close_locked(timeri);
++      err = snd_timer_close_locked(timeri, &card_dev_to_put);
+       mutex_unlock(&register_mutex);
++      /* put_device() is called after unlock for avoiding deadlock */
++      if (card_dev_to_put)
++              put_device(card_dev_to_put);
+       return err;
+ }
+ EXPORT_SYMBOL(snd_timer_close);
+-- 
+2.20.1
+
diff --git a/queue-4.9/alsa-timer-follow-standard-export_symbol-declaration.patch b/queue-4.9/alsa-timer-follow-standard-export_symbol-declaration.patch
new file mode 100644 (file)
index 0000000..ef48383
--- /dev/null
@@ -0,0 +1,147 @@
+From 67272ec0784f5ee30b9c64022cf207b93995c46e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 16 Jun 2017 16:16:05 +0200
+Subject: ALSA: timer: Follow standard EXPORT_SYMBOL() declarations
+
+From: Takashi Iwai <tiwai@suse.de>
+
+[ Upstream commit 988563929d5b65c021439880ac6bd1b207722f26 ]
+
+Just a tidy up to follow the standard EXPORT_SYMBOL*() declarations
+in order to improve grep-ability.
+
+- Move EXPORT_SYMBOL*() to the position right after its definition
+
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/core/timer.c | 27 +++++++++++++--------------
+ 1 file changed, 13 insertions(+), 14 deletions(-)
+
+diff --git a/sound/core/timer.c b/sound/core/timer.c
+index 152254193c697..cfcc6718aafce 100644
+--- a/sound/core/timer.c
++++ b/sound/core/timer.c
+@@ -318,6 +318,7 @@ int snd_timer_open(struct snd_timer_instance **ti,
+       *ti = timeri;
+       return 0;
+ }
++EXPORT_SYMBOL(snd_timer_open);
+ /*
+  * close a timer instance
+@@ -383,6 +384,7 @@ int snd_timer_close(struct snd_timer_instance *timeri)
+       mutex_unlock(&register_mutex);
+       return 0;
+ }
++EXPORT_SYMBOL(snd_timer_close);
+ unsigned long snd_timer_resolution(struct snd_timer_instance *timeri)
+ {
+@@ -397,6 +399,7 @@ unsigned long snd_timer_resolution(struct snd_timer_instance *timeri)
+       }
+       return 0;
+ }
++EXPORT_SYMBOL(snd_timer_resolution);
+ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
+ {
+@@ -588,6 +591,7 @@ int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
+       else
+               return snd_timer_start1(timeri, true, ticks);
+ }
++EXPORT_SYMBOL(snd_timer_start);
+ /*
+  * stop the timer instance.
+@@ -601,6 +605,7 @@ int snd_timer_stop(struct snd_timer_instance *timeri)
+       else
+               return snd_timer_stop1(timeri, true);
+ }
++EXPORT_SYMBOL(snd_timer_stop);
+ /*
+  * start again..  the tick is kept.
+@@ -616,6 +621,7 @@ int snd_timer_continue(struct snd_timer_instance *timeri)
+       else
+               return snd_timer_start1(timeri, false, 0);
+ }
++EXPORT_SYMBOL(snd_timer_continue);
+ /*
+  * pause.. remember the ticks left
+@@ -627,6 +633,7 @@ int snd_timer_pause(struct snd_timer_instance * timeri)
+       else
+               return snd_timer_stop1(timeri, false);
+ }
++EXPORT_SYMBOL(snd_timer_pause);
+ /*
+  * reschedule the timer
+@@ -808,6 +815,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
+       if (use_tasklet)
+               tasklet_schedule(&timer->task_queue);
+ }
++EXPORT_SYMBOL(snd_timer_interrupt);
+ /*
+@@ -858,6 +866,7 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
+               *rtimer = timer;
+       return 0;
+ }
++EXPORT_SYMBOL(snd_timer_new);
+ static int snd_timer_free(struct snd_timer *timer)
+ {
+@@ -977,6 +986,7 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam
+       }
+       spin_unlock_irqrestore(&timer->lock, flags);
+ }
++EXPORT_SYMBOL(snd_timer_notify);
+ /*
+  * exported functions for global timers
+@@ -992,11 +1002,13 @@ int snd_timer_global_new(char *id, int device, struct snd_timer **rtimer)
+       tid.subdevice = 0;
+       return snd_timer_new(NULL, id, &tid, rtimer);
+ }
++EXPORT_SYMBOL(snd_timer_global_new);
+ int snd_timer_global_free(struct snd_timer *timer)
+ {
+       return snd_timer_free(timer);
+ }
++EXPORT_SYMBOL(snd_timer_global_free);
+ int snd_timer_global_register(struct snd_timer *timer)
+ {
+@@ -1006,6 +1018,7 @@ int snd_timer_global_register(struct snd_timer *timer)
+       dev.device_data = timer;
+       return snd_timer_dev_register(&dev);
+ }
++EXPORT_SYMBOL(snd_timer_global_register);
+ /*
+  *  System timer
+@@ -2121,17 +2134,3 @@ static void __exit alsa_timer_exit(void)
+ module_init(alsa_timer_init)
+ module_exit(alsa_timer_exit)
+-
+-EXPORT_SYMBOL(snd_timer_open);
+-EXPORT_SYMBOL(snd_timer_close);
+-EXPORT_SYMBOL(snd_timer_resolution);
+-EXPORT_SYMBOL(snd_timer_start);
+-EXPORT_SYMBOL(snd_timer_stop);
+-EXPORT_SYMBOL(snd_timer_continue);
+-EXPORT_SYMBOL(snd_timer_pause);
+-EXPORT_SYMBOL(snd_timer_new);
+-EXPORT_SYMBOL(snd_timer_notify);
+-EXPORT_SYMBOL(snd_timer_global_new);
+-EXPORT_SYMBOL(snd_timer_global_free);
+-EXPORT_SYMBOL(snd_timer_global_register);
+-EXPORT_SYMBOL(snd_timer_interrupt);
+-- 
+2.20.1
+
diff --git a/queue-4.9/alsa-timer-limit-max-instances-per-timer.patch b/queue-4.9/alsa-timer-limit-max-instances-per-timer.patch
new file mode 100644 (file)
index 0000000..1b0c32d
--- /dev/null
@@ -0,0 +1,253 @@
+From 5da6a4bd1d2a53d4ffd0ef9edc675a26382955c0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 5 Nov 2017 10:07:43 +0100
+Subject: ALSA: timer: Limit max instances per timer
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Takashi Iwai <tiwai@suse.de>
+
+[ Upstream commit 9b7d869ee5a77ed4a462372bb89af622e705bfb8 ]
+
+Currently we allow unlimited number of timer instances, and it may
+bring the system hogging way too much CPU when too many timer
+instances are opened and processed concurrently.  This may end up with
+a soft-lockup report as triggered by syzkaller, especially when
+hrtimer backend is deployed.
+
+Since such insane number of instances aren't demanded by the normal
+use case of ALSA sequencer and it merely  opens a risk only for abuse,
+this patch introduces the upper limit for the number of instances per
+timer backend.  As default, it's set to 1000, but for the fine-grained
+timer like hrtimer, it's set to 100.
+
+Reported-by: syzbot
+Tested-by: Jérôme Glisse <jglisse@redhat.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/sound/timer.h |  2 ++
+ sound/core/hrtimer.c  |  1 +
+ sound/core/timer.c    | 67 ++++++++++++++++++++++++++++++++++---------
+ 3 files changed, 57 insertions(+), 13 deletions(-)
+
+diff --git a/include/sound/timer.h b/include/sound/timer.h
+index c4d76ff056c6e..7ae226ab69908 100644
+--- a/include/sound/timer.h
++++ b/include/sound/timer.h
+@@ -90,6 +90,8 @@ struct snd_timer {
+       struct list_head ack_list_head;
+       struct list_head sack_list_head; /* slow ack list head */
+       struct tasklet_struct task_queue;
++      int max_instances;      /* upper limit of timer instances */
++      int num_instances;      /* current number of timer instances */
+ };
+ struct snd_timer_instance {
+diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c
+index e2f27022b363c..d7dddb75e7bb8 100644
+--- a/sound/core/hrtimer.c
++++ b/sound/core/hrtimer.c
+@@ -159,6 +159,7 @@ static int __init snd_hrtimer_init(void)
+       timer->hw = hrtimer_hw;
+       timer->hw.resolution = resolution;
+       timer->hw.ticks = NANO_SEC / resolution;
++      timer->max_instances = 100; /* lower the limit */
+       err = snd_timer_global_register(timer);
+       if (err < 0) {
+diff --git a/sound/core/timer.c b/sound/core/timer.c
+index cfcc6718aafce..95c5838747754 100644
+--- a/sound/core/timer.c
++++ b/sound/core/timer.c
+@@ -179,7 +179,7 @@ static void snd_timer_request(struct snd_timer_id *tid)
+  *
+  * call this with register_mutex down.
+  */
+-static void snd_timer_check_slave(struct snd_timer_instance *slave)
++static int snd_timer_check_slave(struct snd_timer_instance *slave)
+ {
+       struct snd_timer *timer;
+       struct snd_timer_instance *master;
+@@ -189,16 +189,21 @@ static void snd_timer_check_slave(struct snd_timer_instance *slave)
+               list_for_each_entry(master, &timer->open_list_head, open_list) {
+                       if (slave->slave_class == master->slave_class &&
+                           slave->slave_id == master->slave_id) {
++                              if (master->timer->num_instances >=
++                                  master->timer->max_instances)
++                                      return -EBUSY;
+                               list_move_tail(&slave->open_list,
+                                              &master->slave_list_head);
++                              master->timer->num_instances++;
+                               spin_lock_irq(&slave_active_lock);
+                               slave->master = master;
+                               slave->timer = master->timer;
+                               spin_unlock_irq(&slave_active_lock);
+-                              return;
++                              return 0;
+                       }
+               }
+       }
++      return 0;
+ }
+ /*
+@@ -207,7 +212,7 @@ static void snd_timer_check_slave(struct snd_timer_instance *slave)
+  *
+  * call this with register_mutex down.
+  */
+-static void snd_timer_check_master(struct snd_timer_instance *master)
++static int snd_timer_check_master(struct snd_timer_instance *master)
+ {
+       struct snd_timer_instance *slave, *tmp;
+@@ -215,7 +220,11 @@ static void snd_timer_check_master(struct snd_timer_instance *master)
+       list_for_each_entry_safe(slave, tmp, &snd_timer_slave_list, open_list) {
+               if (slave->slave_class == master->slave_class &&
+                   slave->slave_id == master->slave_id) {
++                      if (master->timer->num_instances >=
++                          master->timer->max_instances)
++                              return -EBUSY;
+                       list_move_tail(&slave->open_list, &master->slave_list_head);
++                      master->timer->num_instances++;
+                       spin_lock_irq(&slave_active_lock);
+                       spin_lock(&master->timer->lock);
+                       slave->master = master;
+@@ -227,8 +236,11 @@ static void snd_timer_check_master(struct snd_timer_instance *master)
+                       spin_unlock_irq(&slave_active_lock);
+               }
+       }
++      return 0;
+ }
++static int snd_timer_close_locked(struct snd_timer_instance *timeri);
++
+ /*
+  * open a timer instance
+  * when opening a master, the slave id must be here given.
+@@ -239,6 +251,7 @@ int snd_timer_open(struct snd_timer_instance **ti,
+ {
+       struct snd_timer *timer;
+       struct snd_timer_instance *timeri = NULL;
++      int err;
+       if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) {
+               /* open a slave instance */
+@@ -258,10 +271,14 @@ int snd_timer_open(struct snd_timer_instance **ti,
+               timeri->slave_id = tid->device;
+               timeri->flags |= SNDRV_TIMER_IFLG_SLAVE;
+               list_add_tail(&timeri->open_list, &snd_timer_slave_list);
+-              snd_timer_check_slave(timeri);
++              err = snd_timer_check_slave(timeri);
++              if (err < 0) {
++                      snd_timer_close_locked(timeri);
++                      timeri = NULL;
++              }
+               mutex_unlock(&register_mutex);
+               *ti = timeri;
+-              return 0;
++              return err;
+       }
+       /* open a master instance */
+@@ -287,6 +304,10 @@ int snd_timer_open(struct snd_timer_instance **ti,
+                       return -EBUSY;
+               }
+       }
++      if (timer->num_instances >= timer->max_instances) {
++              mutex_unlock(&register_mutex);
++              return -EBUSY;
++      }
+       timeri = snd_timer_instance_new(owner, timer);
+       if (!timeri) {
+               mutex_unlock(&register_mutex);
+@@ -313,25 +334,27 @@ int snd_timer_open(struct snd_timer_instance **ti,
+       }
+       list_add_tail(&timeri->open_list, &timer->open_list_head);
+-      snd_timer_check_master(timeri);
++      timer->num_instances++;
++      err = snd_timer_check_master(timeri);
++      if (err < 0) {
++              snd_timer_close_locked(timeri);
++              timeri = NULL;
++      }
+       mutex_unlock(&register_mutex);
+       *ti = timeri;
+-      return 0;
++      return err;
+ }
+ EXPORT_SYMBOL(snd_timer_open);
+ /*
+  * close a timer instance
++ * call this with register_mutex down.
+  */
+-int snd_timer_close(struct snd_timer_instance *timeri)
++static int snd_timer_close_locked(struct snd_timer_instance *timeri)
+ {
+       struct snd_timer *timer = NULL;
+       struct snd_timer_instance *slave, *tmp;
+-      if (snd_BUG_ON(!timeri))
+-              return -ENXIO;
+-
+-      mutex_lock(&register_mutex);
+       list_del(&timeri->open_list);
+       /* force to stop the timer */
+@@ -339,6 +362,7 @@ int snd_timer_close(struct snd_timer_instance *timeri)
+       timer = timeri->timer;
+       if (timer) {
++              timer->num_instances--;
+               /* wait, until the active callback is finished */
+               spin_lock_irq(&timer->lock);
+               while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
+@@ -354,6 +378,7 @@ int snd_timer_close(struct snd_timer_instance *timeri)
+               list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head,
+                                        open_list) {
+                       list_move_tail(&slave->open_list, &snd_timer_slave_list);
++                      timer->num_instances--;
+                       slave->master = NULL;
+                       slave->timer = NULL;
+                       list_del_init(&slave->ack_list);
+@@ -381,9 +406,24 @@ int snd_timer_close(struct snd_timer_instance *timeri)
+               module_put(timer->module);
+       }
+-      mutex_unlock(&register_mutex);
+       return 0;
+ }
++
++/*
++ * close a timer instance
++ */
++int snd_timer_close(struct snd_timer_instance *timeri)
++{
++      int err;
++
++      if (snd_BUG_ON(!timeri))
++              return -ENXIO;
++
++      mutex_lock(&register_mutex);
++      err = snd_timer_close_locked(timeri);
++      mutex_unlock(&register_mutex);
++      return err;
++}
+ EXPORT_SYMBOL(snd_timer_close);
+ unsigned long snd_timer_resolution(struct snd_timer_instance *timeri)
+@@ -854,6 +894,7 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
+       spin_lock_init(&timer->lock);
+       tasklet_init(&timer->task_queue, snd_timer_tasklet,
+                    (unsigned long)timer);
++      timer->max_instances = 1000; /* default limit per timer */
+       if (card != NULL) {
+               timer->module = card->module;
+               err = snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops);
+-- 
+2.20.1
+
diff --git a/queue-4.9/alsa-timer-simplify-error-path-in-snd_timer_open.patch b/queue-4.9/alsa-timer-simplify-error-path-in-snd_timer_open.patch
new file mode 100644 (file)
index 0000000..528b23a
--- /dev/null
@@ -0,0 +1,130 @@
+From 782f1b929d8feb20e52fd731fd2335c78d97fa20 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 28 Mar 2019 17:11:10 +0100
+Subject: ALSA: timer: Simplify error path in snd_timer_open()
+
+From: Takashi Iwai <tiwai@suse.de>
+
+[ Upstream commit 41672c0c24a62699d20aab53b98d843b16483053 ]
+
+Just a minor refactoring to use the standard goto for error paths in
+snd_timer_open() instead of open code.  The first mutex_lock() is
+moved to the beginning of the function to make the code clearer.
+
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/core/timer.c | 39 ++++++++++++++++++++-------------------
+ 1 file changed, 20 insertions(+), 19 deletions(-)
+
+diff --git a/sound/core/timer.c b/sound/core/timer.c
+index 95c5838747754..6eb4e97662d9c 100644
+--- a/sound/core/timer.c
++++ b/sound/core/timer.c
+@@ -253,19 +253,20 @@ int snd_timer_open(struct snd_timer_instance **ti,
+       struct snd_timer_instance *timeri = NULL;
+       int err;
++      mutex_lock(&register_mutex);
+       if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) {
+               /* open a slave instance */
+               if (tid->dev_sclass <= SNDRV_TIMER_SCLASS_NONE ||
+                   tid->dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER) {
+                       pr_debug("ALSA: timer: invalid slave class %i\n",
+                                tid->dev_sclass);
+-                      return -EINVAL;
++                      err = -EINVAL;
++                      goto unlock;
+               }
+-              mutex_lock(&register_mutex);
+               timeri = snd_timer_instance_new(owner, NULL);
+               if (!timeri) {
+-                      mutex_unlock(&register_mutex);
+-                      return -ENOMEM;
++                      err = -ENOMEM;
++                      goto unlock;
+               }
+               timeri->slave_class = tid->dev_sclass;
+               timeri->slave_id = tid->device;
+@@ -276,13 +277,10 @@ int snd_timer_open(struct snd_timer_instance **ti,
+                       snd_timer_close_locked(timeri);
+                       timeri = NULL;
+               }
+-              mutex_unlock(&register_mutex);
+-              *ti = timeri;
+-              return err;
++              goto unlock;
+       }
+       /* open a master instance */
+-      mutex_lock(&register_mutex);
+       timer = snd_timer_find(tid);
+ #ifdef CONFIG_MODULES
+       if (!timer) {
+@@ -293,25 +291,26 @@ int snd_timer_open(struct snd_timer_instance **ti,
+       }
+ #endif
+       if (!timer) {
+-              mutex_unlock(&register_mutex);
+-              return -ENODEV;
++              err = -ENODEV;
++              goto unlock;
+       }
+       if (!list_empty(&timer->open_list_head)) {
+               timeri = list_entry(timer->open_list_head.next,
+                                   struct snd_timer_instance, open_list);
+               if (timeri->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) {
+-                      mutex_unlock(&register_mutex);
+-                      return -EBUSY;
++                      err = -EBUSY;
++                      timeri = NULL;
++                      goto unlock;
+               }
+       }
+       if (timer->num_instances >= timer->max_instances) {
+-              mutex_unlock(&register_mutex);
+-              return -EBUSY;
++              err = -EBUSY;
++              goto unlock;
+       }
+       timeri = snd_timer_instance_new(owner, timer);
+       if (!timeri) {
+-              mutex_unlock(&register_mutex);
+-              return -ENOMEM;
++              err = -ENOMEM;
++              goto unlock;
+       }
+       /* take a card refcount for safe disconnection */
+       if (timer->card)
+@@ -320,16 +319,16 @@ int snd_timer_open(struct snd_timer_instance **ti,
+       timeri->slave_id = slave_id;
+       if (list_empty(&timer->open_list_head) && timer->hw.open) {
+-              int err = timer->hw.open(timer);
++              err = timer->hw.open(timer);
+               if (err) {
+                       kfree(timeri->owner);
+                       kfree(timeri);
++                      timeri = NULL;
+                       if (timer->card)
+                               put_device(&timer->card->card_dev);
+                       module_put(timer->module);
+-                      mutex_unlock(&register_mutex);
+-                      return err;
++                      goto unlock;
+               }
+       }
+@@ -340,6 +339,8 @@ int snd_timer_open(struct snd_timer_instance **ti,
+               snd_timer_close_locked(timeri);
+               timeri = NULL;
+       }
++
++ unlock:
+       mutex_unlock(&register_mutex);
+       *ti = timeri;
+       return err;
+-- 
+2.20.1
+
index 96c3e53c0aff47710f4a921336bde80b21af7411..af755090e3f60f850ac7bd3974f76c6d6f451519 100644 (file)
@@ -56,3 +56,7 @@ sch_netem-fix-rcu-splat-in-netem_enqueue.patch
 sctp-fix-the-issue-that-flags-are-ignored-when-using-kernel_connect.patch
 sctp-not-bind-the-socket-in-sctp_connect.patch
 xfs-correctly-invert-xfs_buftarg-lru-isolation-logic.patch
+alsa-timer-follow-standard-export_symbol-declaration.patch
+alsa-timer-limit-max-instances-per-timer.patch
+alsa-timer-simplify-error-path-in-snd_timer_open.patch
+alsa-timer-fix-mutex-deadlock-at-releasing-card.patch