aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2016-01-21 11:19:31 -0500
committerTakashi Iwai <tiwai@suse.de>2016-01-21 11:41:50 -0500
commit230323dac060123c340cf75997971145a42661ee (patch)
treeaaa5dc1f15cbe38b1d051460d5a485c2d1c63c15 /sound
parent991f86d7ae4e1f8c15806e62f97af519e3cdd860 (diff)
ALSA: timer: Handle disconnection more safely
Currently ALSA timer device doesn't take the disconnection into account very well; it merely unlinks the timer device at disconnection callback but does nothing else. Because of this, when an application accessing the timer device is disconnected, it may release the resource before actually closed. In most cases, it results in a warning message indicating a leftover timer instance like: ALSA: timer xxxx is busy? But basically this is an open race. This patch tries to address it. The strategy is like other ALSA devices: namely, - Manage card's refcount at each open/close - Wake up the pending tasks at disconnection - Check the shutdown flag appropriately at each possible call Note that this patch has one ugly hack to handle the wakeup of pending tasks. It'd be cleaner to introduce a new disconnect op to snd_timer_instance ops. But since it would lead to internal ABI breakage and it eventually increase my own work when backporting to stable kernels, I took a different path to implement locally in timer.c. A cleanup patch will follow at next for 4.5 kernel. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=109431 Cc: <stable@vger.kernel.org> # v3.15+ Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r--sound/core/timer.c48
1 files changed, 48 insertions, 0 deletions
diff --git a/sound/core/timer.c b/sound/core/timer.c
index cb25aded5349..681fb051b9eb 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -65,6 +65,7 @@ struct snd_timer_user {
65 int qtail; 65 int qtail;
66 int qused; 66 int qused;
67 int queue_size; 67 int queue_size;
68 bool disconnected;
68 struct snd_timer_read *queue; 69 struct snd_timer_read *queue;
69 struct snd_timer_tread *tqueue; 70 struct snd_timer_tread *tqueue;
70 spinlock_t qlock; 71 spinlock_t qlock;
@@ -290,6 +291,9 @@ int snd_timer_open(struct snd_timer_instance **ti,
290 mutex_unlock(&register_mutex); 291 mutex_unlock(&register_mutex);
291 return -ENOMEM; 292 return -ENOMEM;
292 } 293 }
294 /* take a card refcount for safe disconnection */
295 if (timer->card)
296 get_device(&timer->card->card_dev);
293 timeri->slave_class = tid->dev_sclass; 297 timeri->slave_class = tid->dev_sclass;
294 timeri->slave_id = slave_id; 298 timeri->slave_id = slave_id;
295 if (list_empty(&timer->open_list_head) && timer->hw.open) 299 if (list_empty(&timer->open_list_head) && timer->hw.open)
@@ -359,6 +363,9 @@ int snd_timer_close(struct snd_timer_instance *timeri)
359 } 363 }
360 spin_unlock(&timer->lock); 364 spin_unlock(&timer->lock);
361 spin_unlock_irq(&slave_active_lock); 365 spin_unlock_irq(&slave_active_lock);
366 /* release a card refcount for safe disconnection */
367 if (timer->card)
368 put_device(&timer->card->card_dev);
362 mutex_unlock(&register_mutex); 369 mutex_unlock(&register_mutex);
363 } 370 }
364 out: 371 out:
@@ -474,6 +481,8 @@ int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
474 timer = timeri->timer; 481 timer = timeri->timer;
475 if (timer == NULL) 482 if (timer == NULL)
476 return -EINVAL; 483 return -EINVAL;
484 if (timer->card && timer->card->shutdown)
485 return -ENODEV;
477 spin_lock_irqsave(&timer->lock, flags); 486 spin_lock_irqsave(&timer->lock, flags);
478 timeri->ticks = timeri->cticks = ticks; 487 timeri->ticks = timeri->cticks = ticks;
479 timeri->pticks = 0; 488 timeri->pticks = 0;
@@ -505,6 +514,10 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri, int event)
505 spin_lock_irqsave(&timer->lock, flags); 514 spin_lock_irqsave(&timer->lock, flags);
506 list_del_init(&timeri->ack_list); 515 list_del_init(&timeri->ack_list);
507 list_del_init(&timeri->active_list); 516 list_del_init(&timeri->active_list);
517 if (timer->card && timer->card->shutdown) {
518 spin_unlock_irqrestore(&timer->lock, flags);
519 return 0;
520 }
508 if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) && 521 if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) &&
509 !(--timer->running)) { 522 !(--timer->running)) {
510 timer->hw.stop(timer); 523 timer->hw.stop(timer);
@@ -565,6 +578,8 @@ int snd_timer_continue(struct snd_timer_instance *timeri)
565 timer = timeri->timer; 578 timer = timeri->timer;
566 if (! timer) 579 if (! timer)
567 return -EINVAL; 580 return -EINVAL;
581 if (timer->card && timer->card->shutdown)
582 return -ENODEV;
568 spin_lock_irqsave(&timer->lock, flags); 583 spin_lock_irqsave(&timer->lock, flags);
569 if (!timeri->cticks) 584 if (!timeri->cticks)
570 timeri->cticks = 1; 585 timeri->cticks = 1;
@@ -628,6 +643,9 @@ static void snd_timer_tasklet(unsigned long arg)
628 unsigned long resolution, ticks; 643 unsigned long resolution, ticks;
629 unsigned long flags; 644 unsigned long flags;
630 645
646 if (timer->card && timer->card->shutdown)
647 return;
648
631 spin_lock_irqsave(&timer->lock, flags); 649 spin_lock_irqsave(&timer->lock, flags);
632 /* now process all callbacks */ 650 /* now process all callbacks */
633 while (!list_empty(&timer->sack_list_head)) { 651 while (!list_empty(&timer->sack_list_head)) {
@@ -668,6 +686,9 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
668 if (timer == NULL) 686 if (timer == NULL)
669 return; 687 return;
670 688
689 if (timer->card && timer->card->shutdown)
690 return;
691
671 spin_lock_irqsave(&timer->lock, flags); 692 spin_lock_irqsave(&timer->lock, flags);
672 693
673 /* remember the current resolution */ 694 /* remember the current resolution */
@@ -878,11 +899,28 @@ static int snd_timer_dev_register(struct snd_device *dev)
878 return 0; 899 return 0;
879} 900}
880 901
902/* just for reference in snd_timer_dev_disconnect() below */
903static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
904 int event, struct timespec *tstamp,
905 unsigned long resolution);
906
881static int snd_timer_dev_disconnect(struct snd_device *device) 907static int snd_timer_dev_disconnect(struct snd_device *device)
882{ 908{
883 struct snd_timer *timer = device->device_data; 909 struct snd_timer *timer = device->device_data;
910 struct snd_timer_instance *ti;
911
884 mutex_lock(&register_mutex); 912 mutex_lock(&register_mutex);
885 list_del_init(&timer->device_list); 913 list_del_init(&timer->device_list);
914 /* wake up pending sleepers */
915 list_for_each_entry(ti, &timer->open_list_head, open_list) {
916 /* FIXME: better to have a ti.disconnect() op */
917 if (ti->ccallback == snd_timer_user_ccallback) {
918 struct snd_timer_user *tu = ti->callback_data;
919
920 tu->disconnected = true;
921 wake_up(&tu->qchange_sleep);
922 }
923 }
886 mutex_unlock(&register_mutex); 924 mutex_unlock(&register_mutex);
887 return 0; 925 return 0;
888} 926}
@@ -893,6 +931,8 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam
893 unsigned long resolution = 0; 931 unsigned long resolution = 0;
894 struct snd_timer_instance *ti, *ts; 932 struct snd_timer_instance *ti, *ts;
895 933
934 if (timer->card && timer->card->shutdown)
935 return;
896 if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)) 936 if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE))
897 return; 937 return;
898 if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART || 938 if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART ||
@@ -1051,6 +1091,8 @@ static void snd_timer_proc_read(struct snd_info_entry *entry,
1051 1091
1052 mutex_lock(&register_mutex); 1092 mutex_lock(&register_mutex);
1053 list_for_each_entry(timer, &snd_timer_list, device_list) { 1093 list_for_each_entry(timer, &snd_timer_list, device_list) {
1094 if (timer->card && timer->card->shutdown)
1095 continue;
1054 switch (timer->tmr_class) { 1096 switch (timer->tmr_class) {
1055 case SNDRV_TIMER_CLASS_GLOBAL: 1097 case SNDRV_TIMER_CLASS_GLOBAL:
1056 snd_iprintf(buffer, "G%i: ", timer->tmr_device); 1098 snd_iprintf(buffer, "G%i: ", timer->tmr_device);
@@ -1876,6 +1918,10 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
1876 1918
1877 remove_wait_queue(&tu->qchange_sleep, &wait); 1919 remove_wait_queue(&tu->qchange_sleep, &wait);
1878 1920
1921 if (tu->disconnected) {
1922 err = -ENODEV;
1923 break;
1924 }
1879 if (signal_pending(current)) { 1925 if (signal_pending(current)) {
1880 err = -ERESTARTSYS; 1926 err = -ERESTARTSYS;
1881 break; 1927 break;
@@ -1925,6 +1971,8 @@ static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait)
1925 mask = 0; 1971 mask = 0;
1926 if (tu->qused) 1972 if (tu->qused)
1927 mask |= POLLIN | POLLRDNORM; 1973 mask |= POLLIN | POLLRDNORM;
1974 if (tu->disconnected)
1975 mask |= POLLERR;
1928 1976
1929 return mask; 1977 return mask;
1930} 1978}