Commit 205d6bcf authored by Takashi Iwai's avatar Takashi Iwai

Merge branch 'topic/pcm-lock-refactor' into for-next

Pull PCM lock refactoring.
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parents 3a554371 ef2056b8
...@@ -30,6 +30,7 @@ ...@@ -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 */ ...@@ -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) ...@@ -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) {} ...@@ -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); ...@@ -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, ...@@ -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); ...@@ -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); ...@@ -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); ...@@ -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); ...@@ -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, ...@@ -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, ...@@ -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);
mutex_unlock(&substream->group->mutex);
} else {
if (!spin_trylock(&substream->group->lock)) {
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); res = snd_pcm_action_group(ops, substream, state, 1);
spin_unlock(&substream->group->lock); else
} res = snd_pcm_action_single(ops, substream, state);
snd_pcm_group_unref(group, substream);
return res; return res;
} }
...@@ -1179,6 +1197,7 @@ static int snd_pcm_action_nonatomic(const struct action_ops *ops, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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); target_group = substream->group;
INIT_LIST_HEAD(&substream->group->substreams); snd_pcm_stream_unlock_irq(substream);
list_add_tail(&substream->link_list, &substream->group->substreams);
substream->group->count = 1; snd_pcm_group_lock_irq(target_group, nonatomic);
} snd_pcm_stream_lock(substream1);
list_add_tail(&substream1->link_list, &substream->group->substreams); snd_pcm_group_assign(substream1, target_group);
substream->group->count++; snd_pcm_stream_unlock(substream1);
substream1->group = substream->group; 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) ...@@ -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;
} }
......
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