* [PATCH 1/2] ALSA: timer: Handle disconnection more safely
@ 2016-01-21 16:53 Takashi Iwai
2016-01-21 16:53 ` [PATCH 2/2] ALSA: timer: Introduce disconnect op to snd_timer_instance Takashi Iwai
0 siblings, 1 reply; 2+ messages in thread
From: Takashi Iwai @ 2016-01-21 16:53 UTC (permalink / raw)
To: alsa-devel
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>
---
sound/core/timer.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 48 insertions(+)
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 {
int qtail;
int qused;
int queue_size;
+ bool disconnected;
struct snd_timer_read *queue;
struct snd_timer_tread *tqueue;
spinlock_t qlock;
@@ -290,6 +291,9 @@ int snd_timer_open(struct snd_timer_instance **ti,
mutex_unlock(®ister_mutex);
return -ENOMEM;
}
+ /* take a card refcount for safe disconnection */
+ if (timer->card)
+ get_device(&timer->card->card_dev);
timeri->slave_class = tid->dev_sclass;
timeri->slave_id = slave_id;
if (list_empty(&timer->open_list_head) && timer->hw.open)
@@ -359,6 +363,9 @@ int snd_timer_close(struct snd_timer_instance *timeri)
}
spin_unlock(&timer->lock);
spin_unlock_irq(&slave_active_lock);
+ /* release a card refcount for safe disconnection */
+ if (timer->card)
+ put_device(&timer->card->card_dev);
mutex_unlock(®ister_mutex);
}
out:
@@ -474,6 +481,8 @@ int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
timer = timeri->timer;
if (timer == NULL)
return -EINVAL;
+ if (timer->card && timer->card->shutdown)
+ return -ENODEV;
spin_lock_irqsave(&timer->lock, flags);
timeri->ticks = timeri->cticks = ticks;
timeri->pticks = 0;
@@ -505,6 +514,10 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri, int event)
spin_lock_irqsave(&timer->lock, flags);
list_del_init(&timeri->ack_list);
list_del_init(&timeri->active_list);
+ if (timer->card && timer->card->shutdown) {
+ spin_unlock_irqrestore(&timer->lock, flags);
+ return 0;
+ }
if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) &&
!(--timer->running)) {
timer->hw.stop(timer);
@@ -565,6 +578,8 @@ int snd_timer_continue(struct snd_timer_instance *timeri)
timer = timeri->timer;
if (! timer)
return -EINVAL;
+ if (timer->card && timer->card->shutdown)
+ return -ENODEV;
spin_lock_irqsave(&timer->lock, flags);
if (!timeri->cticks)
timeri->cticks = 1;
@@ -628,6 +643,9 @@ static void snd_timer_tasklet(unsigned long arg)
unsigned long resolution, ticks;
unsigned long flags;
+ if (timer->card && timer->card->shutdown)
+ return;
+
spin_lock_irqsave(&timer->lock, flags);
/* now process all callbacks */
while (!list_empty(&timer->sack_list_head)) {
@@ -668,6 +686,9 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
if (timer == NULL)
return;
+ if (timer->card && timer->card->shutdown)
+ return;
+
spin_lock_irqsave(&timer->lock, flags);
/* remember the current resolution */
@@ -878,11 +899,28 @@ static int snd_timer_dev_register(struct snd_device *dev)
return 0;
}
+/* just for reference in snd_timer_dev_disconnect() below */
+static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
+ int event, struct timespec *tstamp,
+ unsigned long resolution);
+
static int snd_timer_dev_disconnect(struct snd_device *device)
{
struct snd_timer *timer = device->device_data;
+ struct snd_timer_instance *ti;
+
mutex_lock(®ister_mutex);
list_del_init(&timer->device_list);
+ /* wake up pending sleepers */
+ list_for_each_entry(ti, &timer->open_list_head, open_list) {
+ /* FIXME: better to have a ti.disconnect() op */
+ if (ti->ccallback == snd_timer_user_ccallback) {
+ struct snd_timer_user *tu = ti->callback_data;
+
+ tu->disconnected = true;
+ wake_up(&tu->qchange_sleep);
+ }
+ }
mutex_unlock(®ister_mutex);
return 0;
}
@@ -893,6 +931,8 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam
unsigned long resolution = 0;
struct snd_timer_instance *ti, *ts;
+ if (timer->card && timer->card->shutdown)
+ return;
if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE))
return;
if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART ||
@@ -1051,6 +1091,8 @@ static void snd_timer_proc_read(struct snd_info_entry *entry,
mutex_lock(®ister_mutex);
list_for_each_entry(timer, &snd_timer_list, device_list) {
+ if (timer->card && timer->card->shutdown)
+ continue;
switch (timer->tmr_class) {
case SNDRV_TIMER_CLASS_GLOBAL:
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,
remove_wait_queue(&tu->qchange_sleep, &wait);
+ if (tu->disconnected) {
+ err = -ENODEV;
+ break;
+ }
if (signal_pending(current)) {
err = -ERESTARTSYS;
break;
@@ -1925,6 +1971,8 @@ static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait)
mask = 0;
if (tu->qused)
mask |= POLLIN | POLLRDNORM;
+ if (tu->disconnected)
+ mask |= POLLERR;
return mask;
}
--
2.7.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* [PATCH 2/2] ALSA: timer: Introduce disconnect op to snd_timer_instance
2016-01-21 16:53 [PATCH 1/2] ALSA: timer: Handle disconnection more safely Takashi Iwai
@ 2016-01-21 16:53 ` Takashi Iwai
0 siblings, 0 replies; 2+ messages in thread
From: Takashi Iwai @ 2016-01-21 16:53 UTC (permalink / raw)
To: alsa-devel
Instead of the previous ugly hack, introduce a new op, disconnect, to
snd_timer_instance object for handling the wake up of pending tasks
more cleanly.
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=109431
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
include/sound/timer.h | 1 +
sound/core/timer.c | 23 +++++++++++------------
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/include/sound/timer.h b/include/sound/timer.h
index 7990469a44ce..c4d76ff056c6 100644
--- a/include/sound/timer.h
+++ b/include/sound/timer.h
@@ -104,6 +104,7 @@ struct snd_timer_instance {
int event,
struct timespec * tstamp,
unsigned long resolution);
+ void (*disconnect)(struct snd_timer_instance *timeri);
void *callback_data;
unsigned long ticks; /* auto-load ticks when expired */
unsigned long cticks; /* current ticks */
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 681fb051b9eb..af1f68f7e315 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -899,11 +899,6 @@ static int snd_timer_dev_register(struct snd_device *dev)
return 0;
}
-/* just for reference in snd_timer_dev_disconnect() below */
-static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
- int event, struct timespec *tstamp,
- unsigned long resolution);
-
static int snd_timer_dev_disconnect(struct snd_device *device)
{
struct snd_timer *timer = device->device_data;
@@ -913,13 +908,8 @@ static int snd_timer_dev_disconnect(struct snd_device *device)
list_del_init(&timer->device_list);
/* wake up pending sleepers */
list_for_each_entry(ti, &timer->open_list_head, open_list) {
- /* FIXME: better to have a ti.disconnect() op */
- if (ti->ccallback == snd_timer_user_ccallback) {
- struct snd_timer_user *tu = ti->callback_data;
-
- tu->disconnected = true;
- wake_up(&tu->qchange_sleep);
- }
+ if (ti->disconnect)
+ ti->disconnect(ti);
}
mutex_unlock(®ister_mutex);
return 0;
@@ -1227,6 +1217,14 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
wake_up(&tu->qchange_sleep);
}
+static void snd_timer_user_disconnect(struct snd_timer_instance *timeri)
+{
+ struct snd_timer_user *tu = timeri->callback_data;
+
+ tu->disconnected = true;
+ wake_up(&tu->qchange_sleep);
+}
+
static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
unsigned long resolution,
unsigned long ticks)
@@ -1600,6 +1598,7 @@ static int snd_timer_user_tselect(struct file *file,
? snd_timer_user_tinterrupt : snd_timer_user_interrupt;
tu->timeri->ccallback = snd_timer_user_ccallback;
tu->timeri->callback_data = (void *)tu;
+ tu->timeri->disconnect = snd_timer_user_disconnect;
}
__err:
--
2.7.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2016-01-21 16:53 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-01-21 16:53 [PATCH 1/2] ALSA: timer: Handle disconnection more safely Takashi Iwai
2016-01-21 16:53 ` [PATCH 2/2] ALSA: timer: Introduce disconnect op to snd_timer_instance Takashi Iwai
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).