mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 02:39:48 +00:00
Merge branch 'topic/pcm-lock-refactor' into for-next
Pull PCM lock refactoring. Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
commit
205d6bcf9b
4 changed files with 170 additions and 126 deletions
|
@ -30,6 +30,7 @@
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
#include <linux/pm_qos.h>
|
#include <linux/pm_qos.h>
|
||||||
|
#include <linux/refcount.h>
|
||||||
|
|
||||||
#define snd_pcm_substream_chip(substream) ((substream)->private_data)
|
#define snd_pcm_substream_chip(substream) ((substream)->private_data)
|
||||||
#define snd_pcm_chip(pcm) ((pcm)->private_data)
|
#define snd_pcm_chip(pcm) ((pcm)->private_data)
|
||||||
|
@ -439,7 +440,7 @@ struct snd_pcm_group { /* keep linked substreams */
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
struct mutex mutex;
|
struct mutex mutex;
|
||||||
struct list_head substreams;
|
struct list_head substreams;
|
||||||
int count;
|
refcount_t refs;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pid;
|
struct pid;
|
||||||
|
|
|
@ -733,9 +733,7 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
substream->group = &substream->self_group;
|
substream->group = &substream->self_group;
|
||||||
spin_lock_init(&substream->self_group.lock);
|
snd_pcm_group_init(&substream->self_group);
|
||||||
mutex_init(&substream->self_group.mutex);
|
|
||||||
INIT_LIST_HEAD(&substream->self_group.substreams);
|
|
||||||
list_add_tail(&substream->link_list, &substream->self_group.substreams);
|
list_add_tail(&substream->link_list, &substream->self_group.substreams);
|
||||||
atomic_set(&substream->mmap_count, 0);
|
atomic_set(&substream->mmap_count, 0);
|
||||||
prev = substream;
|
prev = substream;
|
||||||
|
|
|
@ -66,5 +66,6 @@ static inline void snd_pcm_timer_done(struct snd_pcm_substream *substream) {}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void __snd_pcm_xrun(struct snd_pcm_substream *substream);
|
void __snd_pcm_xrun(struct snd_pcm_substream *substream);
|
||||||
|
void snd_pcm_group_init(struct snd_pcm_group *group);
|
||||||
|
|
||||||
#endif /* __SOUND_CORE_PCM_LOCAL_H */
|
#endif /* __SOUND_CORE_PCM_LOCAL_H */
|
||||||
|
|
|
@ -85,71 +85,30 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream);
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
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,
|
void snd_pcm_group_init(struct snd_pcm_group *group)
|
||||||
* 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
|
|
||||||
* sleep until all the readers are completed without blocking by writer.
|
|
||||||
*/
|
|
||||||
static inline void down_write_nonfifo(struct rw_semaphore *lock)
|
|
||||||
{
|
{
|
||||||
while (!down_write_trylock(lock))
|
spin_lock_init(&group->lock);
|
||||||
msleep(1);
|
mutex_init(&group->mutex);
|
||||||
|
INIT_LIST_HEAD(&group->substreams);
|
||||||
|
refcount_set(&group->refs, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define PCM_LOCK_DEFAULT 0
|
/* define group lock helpers */
|
||||||
#define PCM_LOCK_IRQ 1
|
#define DEFINE_PCM_GROUP_LOCK(action, mutex_action) \
|
||||||
#define PCM_LOCK_IRQSAVE 2
|
static void snd_pcm_group_ ## action(struct snd_pcm_group *group, bool nonatomic) \
|
||||||
|
{ \
|
||||||
static unsigned long __snd_pcm_stream_lock_mode(struct snd_pcm_substream *substream,
|
if (nonatomic) \
|
||||||
unsigned int mode)
|
mutex_ ## mutex_action(&group->mutex); \
|
||||||
{
|
else \
|
||||||
unsigned long flags = 0;
|
spin_ ## action(&group->lock); \
|
||||||
if (substream->pcm->nonatomic) {
|
|
||||||
down_read_nested(&snd_pcm_link_rwsem, SINGLE_DEPTH_NESTING);
|
|
||||||
mutex_lock(&substream->self_group.mutex);
|
|
||||||
} else {
|
|
||||||
switch (mode) {
|
|
||||||
case PCM_LOCK_DEFAULT:
|
|
||||||
read_lock(&snd_pcm_link_rwlock);
|
|
||||||
break;
|
|
||||||
case PCM_LOCK_IRQ:
|
|
||||||
read_lock_irq(&snd_pcm_link_rwlock);
|
|
||||||
break;
|
|
||||||
case PCM_LOCK_IRQSAVE:
|
|
||||||
read_lock_irqsave(&snd_pcm_link_rwlock, flags);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
spin_lock(&substream->self_group.lock);
|
|
||||||
}
|
|
||||||
return flags;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __snd_pcm_stream_unlock_mode(struct snd_pcm_substream *substream,
|
DEFINE_PCM_GROUP_LOCK(lock, lock);
|
||||||
unsigned int mode, unsigned long flags)
|
DEFINE_PCM_GROUP_LOCK(unlock, unlock);
|
||||||
{
|
DEFINE_PCM_GROUP_LOCK(lock_irq, lock);
|
||||||
if (substream->pcm->nonatomic) {
|
DEFINE_PCM_GROUP_LOCK(unlock_irq, unlock);
|
||||||
mutex_unlock(&substream->self_group.mutex);
|
|
||||||
up_read(&snd_pcm_link_rwsem);
|
|
||||||
} else {
|
|
||||||
spin_unlock(&substream->self_group.lock);
|
|
||||||
|
|
||||||
switch (mode) {
|
|
||||||
case PCM_LOCK_DEFAULT:
|
|
||||||
read_unlock(&snd_pcm_link_rwlock);
|
|
||||||
break;
|
|
||||||
case PCM_LOCK_IRQ:
|
|
||||||
read_unlock_irq(&snd_pcm_link_rwlock);
|
|
||||||
break;
|
|
||||||
case PCM_LOCK_IRQSAVE:
|
|
||||||
read_unlock_irqrestore(&snd_pcm_link_rwlock, flags);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* snd_pcm_stream_lock - Lock the PCM stream
|
* snd_pcm_stream_lock - Lock the PCM stream
|
||||||
|
@ -161,7 +120,7 @@ static void __snd_pcm_stream_unlock_mode(struct snd_pcm_substream *substream,
|
||||||
*/
|
*/
|
||||||
void snd_pcm_stream_lock(struct snd_pcm_substream *substream)
|
void snd_pcm_stream_lock(struct snd_pcm_substream *substream)
|
||||||
{
|
{
|
||||||
__snd_pcm_stream_lock_mode(substream, PCM_LOCK_DEFAULT);
|
snd_pcm_group_lock(&substream->self_group, substream->pcm->nonatomic);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(snd_pcm_stream_lock);
|
EXPORT_SYMBOL_GPL(snd_pcm_stream_lock);
|
||||||
|
|
||||||
|
@ -173,7 +132,7 @@ EXPORT_SYMBOL_GPL(snd_pcm_stream_lock);
|
||||||
*/
|
*/
|
||||||
void snd_pcm_stream_unlock(struct snd_pcm_substream *substream)
|
void snd_pcm_stream_unlock(struct snd_pcm_substream *substream)
|
||||||
{
|
{
|
||||||
__snd_pcm_stream_unlock_mode(substream, PCM_LOCK_DEFAULT, 0);
|
snd_pcm_group_unlock(&substream->self_group, substream->pcm->nonatomic);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock);
|
EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock);
|
||||||
|
|
||||||
|
@ -187,7 +146,8 @@ EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock);
|
||||||
*/
|
*/
|
||||||
void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream)
|
void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream)
|
||||||
{
|
{
|
||||||
__snd_pcm_stream_lock_mode(substream, PCM_LOCK_IRQ);
|
snd_pcm_group_lock_irq(&substream->self_group,
|
||||||
|
substream->pcm->nonatomic);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq);
|
EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq);
|
||||||
|
|
||||||
|
@ -199,13 +159,19 @@ EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq);
|
||||||
*/
|
*/
|
||||||
void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream)
|
void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream)
|
||||||
{
|
{
|
||||||
__snd_pcm_stream_unlock_mode(substream, PCM_LOCK_IRQ, 0);
|
snd_pcm_group_unlock_irq(&substream->self_group,
|
||||||
|
substream->pcm->nonatomic);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irq);
|
EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irq);
|
||||||
|
|
||||||
unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream)
|
unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream)
|
||||||
{
|
{
|
||||||
return __snd_pcm_stream_lock_mode(substream, PCM_LOCK_IRQSAVE);
|
unsigned long flags = 0;
|
||||||
|
if (substream->pcm->nonatomic)
|
||||||
|
mutex_lock(&substream->self_group.mutex);
|
||||||
|
else
|
||||||
|
spin_lock_irqsave(&substream->self_group.lock, flags);
|
||||||
|
return flags;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave);
|
EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave);
|
||||||
|
|
||||||
|
@ -219,7 +185,10 @@ EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave);
|
||||||
void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream,
|
void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream,
|
||||||
unsigned long flags)
|
unsigned long flags)
|
||||||
{
|
{
|
||||||
__snd_pcm_stream_unlock_mode(substream, PCM_LOCK_IRQSAVE, flags);
|
if (substream->pcm->nonatomic)
|
||||||
|
mutex_unlock(&substream->self_group.mutex);
|
||||||
|
else
|
||||||
|
spin_unlock_irqrestore(&substream->self_group.lock, flags);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irqrestore);
|
EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irqrestore);
|
||||||
|
|
||||||
|
@ -1124,6 +1093,68 @@ static int snd_pcm_action_single(const struct action_ops *ops,
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void snd_pcm_group_assign(struct snd_pcm_substream *substream,
|
||||||
|
struct snd_pcm_group *new_group)
|
||||||
|
{
|
||||||
|
substream->group = new_group;
|
||||||
|
list_move(&substream->link_list, &new_group->substreams);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unref and unlock the group, but keep the stream lock;
|
||||||
|
* when the group becomes empty and no longer referred, destroy itself
|
||||||
|
*/
|
||||||
|
static void snd_pcm_group_unref(struct snd_pcm_group *group,
|
||||||
|
struct snd_pcm_substream *substream)
|
||||||
|
{
|
||||||
|
bool do_free;
|
||||||
|
|
||||||
|
if (!group)
|
||||||
|
return;
|
||||||
|
do_free = refcount_dec_and_test(&group->refs) &&
|
||||||
|
list_empty(&group->substreams);
|
||||||
|
snd_pcm_group_unlock(group, substream->pcm->nonatomic);
|
||||||
|
if (do_free)
|
||||||
|
kfree(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Lock the group inside a stream lock and reference it;
|
||||||
|
* return the locked group object, or NULL if not linked
|
||||||
|
*/
|
||||||
|
static struct snd_pcm_group *
|
||||||
|
snd_pcm_stream_group_ref(struct snd_pcm_substream *substream)
|
||||||
|
{
|
||||||
|
bool nonatomic = substream->pcm->nonatomic;
|
||||||
|
struct snd_pcm_group *group;
|
||||||
|
bool trylock;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (!snd_pcm_stream_linked(substream))
|
||||||
|
return NULL;
|
||||||
|
group = substream->group;
|
||||||
|
/* block freeing the group object */
|
||||||
|
refcount_inc(&group->refs);
|
||||||
|
|
||||||
|
trylock = nonatomic ? mutex_trylock(&group->mutex) :
|
||||||
|
spin_trylock(&group->lock);
|
||||||
|
if (trylock)
|
||||||
|
break; /* OK */
|
||||||
|
|
||||||
|
/* re-lock for avoiding ABBA deadlock */
|
||||||
|
snd_pcm_stream_unlock(substream);
|
||||||
|
snd_pcm_group_lock(group, nonatomic);
|
||||||
|
snd_pcm_stream_lock(substream);
|
||||||
|
|
||||||
|
/* check the group again; the above opens a small race window */
|
||||||
|
if (substream->group == group)
|
||||||
|
break; /* OK */
|
||||||
|
/* group changed, try again */
|
||||||
|
snd_pcm_group_unref(group, substream);
|
||||||
|
}
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note: call with stream lock
|
* Note: call with stream lock
|
||||||
*/
|
*/
|
||||||
|
@ -1131,28 +1162,15 @@ static int snd_pcm_action(const struct action_ops *ops,
|
||||||
struct snd_pcm_substream *substream,
|
struct snd_pcm_substream *substream,
|
||||||
int state)
|
int state)
|
||||||
{
|
{
|
||||||
|
struct snd_pcm_group *group;
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
if (!snd_pcm_stream_linked(substream))
|
group = snd_pcm_stream_group_ref(substream);
|
||||||
return snd_pcm_action_single(ops, substream, state);
|
if (group)
|
||||||
|
|
||||||
if (substream->pcm->nonatomic) {
|
|
||||||
if (!mutex_trylock(&substream->group->mutex)) {
|
|
||||||
mutex_unlock(&substream->self_group.mutex);
|
|
||||||
mutex_lock(&substream->group->mutex);
|
|
||||||
mutex_lock(&substream->self_group.mutex);
|
|
||||||
}
|
|
||||||
res = snd_pcm_action_group(ops, substream, state, 1);
|
res = snd_pcm_action_group(ops, substream, state, 1);
|
||||||
mutex_unlock(&substream->group->mutex);
|
else
|
||||||
} else {
|
res = snd_pcm_action_single(ops, substream, state);
|
||||||
if (!spin_trylock(&substream->group->lock)) {
|
snd_pcm_group_unref(group, substream);
|
||||||
spin_unlock(&substream->self_group.lock);
|
|
||||||
spin_lock(&substream->group->lock);
|
|
||||||
spin_lock(&substream->self_group.lock);
|
|
||||||
}
|
|
||||||
res = snd_pcm_action_group(ops, substream, state, 1);
|
|
||||||
spin_unlock(&substream->group->lock);
|
|
||||||
}
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1179,6 +1197,7 @@ static int snd_pcm_action_nonatomic(const struct action_ops *ops,
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
|
/* Guarantee the group members won't change during non-atomic action */
|
||||||
down_read(&snd_pcm_link_rwsem);
|
down_read(&snd_pcm_link_rwsem);
|
||||||
if (snd_pcm_stream_linked(substream))
|
if (snd_pcm_stream_linked(substream))
|
||||||
res = snd_pcm_action_group(ops, substream, state, 0);
|
res = snd_pcm_action_group(ops, substream, state, 0);
|
||||||
|
@ -1802,6 +1821,7 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
|
||||||
struct snd_card *card;
|
struct snd_card *card;
|
||||||
struct snd_pcm_runtime *runtime;
|
struct snd_pcm_runtime *runtime;
|
||||||
struct snd_pcm_substream *s;
|
struct snd_pcm_substream *s;
|
||||||
|
struct snd_pcm_group *group;
|
||||||
wait_queue_entry_t wait;
|
wait_queue_entry_t wait;
|
||||||
int result = 0;
|
int result = 0;
|
||||||
int nonblock = 0;
|
int nonblock = 0;
|
||||||
|
@ -1818,7 +1838,6 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
|
||||||
} else if (substream->f_flags & O_NONBLOCK)
|
} else if (substream->f_flags & O_NONBLOCK)
|
||||||
nonblock = 1;
|
nonblock = 1;
|
||||||
|
|
||||||
down_read(&snd_pcm_link_rwsem);
|
|
||||||
snd_pcm_stream_lock_irq(substream);
|
snd_pcm_stream_lock_irq(substream);
|
||||||
/* resume pause */
|
/* resume pause */
|
||||||
if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
|
if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
|
||||||
|
@ -1843,6 +1862,7 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
|
||||||
}
|
}
|
||||||
/* find a substream to drain */
|
/* find a substream to drain */
|
||||||
to_check = NULL;
|
to_check = NULL;
|
||||||
|
group = snd_pcm_stream_group_ref(substream);
|
||||||
snd_pcm_group_for_each_entry(s, substream) {
|
snd_pcm_group_for_each_entry(s, substream) {
|
||||||
if (s->stream != SNDRV_PCM_STREAM_PLAYBACK)
|
if (s->stream != SNDRV_PCM_STREAM_PLAYBACK)
|
||||||
continue;
|
continue;
|
||||||
|
@ -1852,12 +1872,12 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
snd_pcm_group_unref(group, substream);
|
||||||
if (!to_check)
|
if (!to_check)
|
||||||
break; /* all drained */
|
break; /* all drained */
|
||||||
init_waitqueue_entry(&wait, current);
|
init_waitqueue_entry(&wait, current);
|
||||||
add_wait_queue(&to_check->sleep, &wait);
|
add_wait_queue(&to_check->sleep, &wait);
|
||||||
snd_pcm_stream_unlock_irq(substream);
|
snd_pcm_stream_unlock_irq(substream);
|
||||||
up_read(&snd_pcm_link_rwsem);
|
|
||||||
if (runtime->no_period_wakeup)
|
if (runtime->no_period_wakeup)
|
||||||
tout = MAX_SCHEDULE_TIMEOUT;
|
tout = MAX_SCHEDULE_TIMEOUT;
|
||||||
else {
|
else {
|
||||||
|
@ -1869,9 +1889,17 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
|
||||||
tout = msecs_to_jiffies(tout * 1000);
|
tout = msecs_to_jiffies(tout * 1000);
|
||||||
}
|
}
|
||||||
tout = schedule_timeout_interruptible(tout);
|
tout = schedule_timeout_interruptible(tout);
|
||||||
down_read(&snd_pcm_link_rwsem);
|
|
||||||
snd_pcm_stream_lock_irq(substream);
|
snd_pcm_stream_lock_irq(substream);
|
||||||
remove_wait_queue(&to_check->sleep, &wait);
|
group = snd_pcm_stream_group_ref(substream);
|
||||||
|
snd_pcm_group_for_each_entry(s, substream) {
|
||||||
|
if (s->runtime == to_check) {
|
||||||
|
remove_wait_queue(&to_check->sleep, &wait);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
snd_pcm_group_unref(group, substream);
|
||||||
|
|
||||||
if (card->shutdown) {
|
if (card->shutdown) {
|
||||||
result = -ENODEV;
|
result = -ENODEV;
|
||||||
break;
|
break;
|
||||||
|
@ -1891,7 +1919,6 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
|
||||||
|
|
||||||
unlock:
|
unlock:
|
||||||
snd_pcm_stream_unlock_irq(substream);
|
snd_pcm_stream_unlock_irq(substream);
|
||||||
up_read(&snd_pcm_link_rwsem);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -1930,13 +1957,19 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream)
|
||||||
static bool is_pcm_file(struct file *file)
|
static bool is_pcm_file(struct file *file)
|
||||||
{
|
{
|
||||||
struct inode *inode = file_inode(file);
|
struct inode *inode = file_inode(file);
|
||||||
|
struct snd_pcm *pcm;
|
||||||
unsigned int minor;
|
unsigned int minor;
|
||||||
|
|
||||||
if (!S_ISCHR(inode->i_mode) || imajor(inode) != snd_major)
|
if (!S_ISCHR(inode->i_mode) || imajor(inode) != snd_major)
|
||||||
return false;
|
return false;
|
||||||
minor = iminor(inode);
|
minor = iminor(inode);
|
||||||
return snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_PLAYBACK) ||
|
pcm = snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
|
||||||
snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_CAPTURE);
|
if (!pcm)
|
||||||
|
pcm = snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_CAPTURE);
|
||||||
|
if (!pcm)
|
||||||
|
return false;
|
||||||
|
snd_card_unref(pcm->card);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1947,7 +1980,8 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
|
||||||
int res = 0;
|
int res = 0;
|
||||||
struct snd_pcm_file *pcm_file;
|
struct snd_pcm_file *pcm_file;
|
||||||
struct snd_pcm_substream *substream1;
|
struct snd_pcm_substream *substream1;
|
||||||
struct snd_pcm_group *group;
|
struct snd_pcm_group *group, *target_group;
|
||||||
|
bool nonatomic = substream->pcm->nonatomic;
|
||||||
struct fd f = fdget(fd);
|
struct fd f = fdget(fd);
|
||||||
|
|
||||||
if (!f.file)
|
if (!f.file)
|
||||||
|
@ -1958,13 +1992,14 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
|
||||||
}
|
}
|
||||||
pcm_file = f.file->private_data;
|
pcm_file = f.file->private_data;
|
||||||
substream1 = pcm_file->substream;
|
substream1 = pcm_file->substream;
|
||||||
group = kmalloc(sizeof(*group), GFP_KERNEL);
|
group = kzalloc(sizeof(*group), GFP_KERNEL);
|
||||||
if (!group) {
|
if (!group) {
|
||||||
res = -ENOMEM;
|
res = -ENOMEM;
|
||||||
goto _nolock;
|
goto _nolock;
|
||||||
}
|
}
|
||||||
down_write_nonfifo(&snd_pcm_link_rwsem);
|
snd_pcm_group_init(group);
|
||||||
write_lock_irq(&snd_pcm_link_rwlock);
|
|
||||||
|
down_write(&snd_pcm_link_rwsem);
|
||||||
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 ||
|
||||||
substream->pcm->nonatomic != substream1->pcm->nonatomic) {
|
substream->pcm->nonatomic != substream1->pcm->nonatomic) {
|
||||||
|
@ -1975,23 +2010,23 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
|
||||||
res = -EALREADY;
|
res = -EALREADY;
|
||||||
goto _end;
|
goto _end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
snd_pcm_stream_lock_irq(substream);
|
||||||
if (!snd_pcm_stream_linked(substream)) {
|
if (!snd_pcm_stream_linked(substream)) {
|
||||||
substream->group = group;
|
snd_pcm_group_assign(substream, group);
|
||||||
group = NULL;
|
group = NULL; /* assigned, don't free this one below */
|
||||||
spin_lock_init(&substream->group->lock);
|
|
||||||
mutex_init(&substream->group->mutex);
|
|
||||||
INIT_LIST_HEAD(&substream->group->substreams);
|
|
||||||
list_add_tail(&substream->link_list, &substream->group->substreams);
|
|
||||||
substream->group->count = 1;
|
|
||||||
}
|
}
|
||||||
list_add_tail(&substream1->link_list, &substream->group->substreams);
|
target_group = substream->group;
|
||||||
substream->group->count++;
|
snd_pcm_stream_unlock_irq(substream);
|
||||||
substream1->group = substream->group;
|
|
||||||
|
snd_pcm_group_lock_irq(target_group, nonatomic);
|
||||||
|
snd_pcm_stream_lock(substream1);
|
||||||
|
snd_pcm_group_assign(substream1, target_group);
|
||||||
|
snd_pcm_stream_unlock(substream1);
|
||||||
|
snd_pcm_group_unlock_irq(target_group, nonatomic);
|
||||||
_end:
|
_end:
|
||||||
write_unlock_irq(&snd_pcm_link_rwlock);
|
|
||||||
up_write(&snd_pcm_link_rwsem);
|
up_write(&snd_pcm_link_rwsem);
|
||||||
_nolock:
|
_nolock:
|
||||||
snd_card_unref(substream1->pcm->card);
|
|
||||||
kfree(group);
|
kfree(group);
|
||||||
_badf:
|
_badf:
|
||||||
fdput(f);
|
fdput(f);
|
||||||
|
@ -2000,34 +2035,43 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
|
||||||
|
|
||||||
static void relink_to_local(struct snd_pcm_substream *substream)
|
static void relink_to_local(struct snd_pcm_substream *substream)
|
||||||
{
|
{
|
||||||
substream->group = &substream->self_group;
|
snd_pcm_stream_lock(substream);
|
||||||
INIT_LIST_HEAD(&substream->self_group.substreams);
|
snd_pcm_group_assign(substream, &substream->self_group);
|
||||||
list_add_tail(&substream->link_list, &substream->self_group.substreams);
|
snd_pcm_stream_unlock(substream);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int snd_pcm_unlink(struct snd_pcm_substream *substream)
|
static int snd_pcm_unlink(struct snd_pcm_substream *substream)
|
||||||
{
|
{
|
||||||
struct snd_pcm_substream *s;
|
struct snd_pcm_group *group;
|
||||||
|
bool nonatomic = substream->pcm->nonatomic;
|
||||||
|
bool do_free = false;
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
|
||||||
down_write_nonfifo(&snd_pcm_link_rwsem);
|
down_write(&snd_pcm_link_rwsem);
|
||||||
write_lock_irq(&snd_pcm_link_rwlock);
|
|
||||||
if (!snd_pcm_stream_linked(substream)) {
|
if (!snd_pcm_stream_linked(substream)) {
|
||||||
res = -EALREADY;
|
res = -EALREADY;
|
||||||
goto _end;
|
goto _end;
|
||||||
}
|
}
|
||||||
list_del(&substream->link_list);
|
|
||||||
substream->group->count--;
|
group = substream->group;
|
||||||
if (substream->group->count == 1) { /* detach the last stream, too */
|
snd_pcm_group_lock_irq(group, nonatomic);
|
||||||
snd_pcm_group_for_each_entry(s, substream) {
|
|
||||||
relink_to_local(s);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
kfree(substream->group);
|
|
||||||
}
|
|
||||||
relink_to_local(substream);
|
relink_to_local(substream);
|
||||||
|
|
||||||
|
/* detach the last stream, too */
|
||||||
|
if (list_is_singular(&group->substreams)) {
|
||||||
|
relink_to_local(list_first_entry(&group->substreams,
|
||||||
|
struct snd_pcm_substream,
|
||||||
|
link_list));
|
||||||
|
do_free = !refcount_read(&group->refs);
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_pcm_group_unlock_irq(group, nonatomic);
|
||||||
|
if (do_free)
|
||||||
|
kfree(group);
|
||||||
|
|
||||||
_end:
|
_end:
|
||||||
write_unlock_irq(&snd_pcm_link_rwlock);
|
|
||||||
up_write(&snd_pcm_link_rwsem);
|
up_write(&snd_pcm_link_rwsem);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue