aboutsummaryrefslogtreecommitdiff
path: root/sound/core/control.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/core/control.c')
-rw-r--r--sound/core/control.c178
1 files changed, 79 insertions, 99 deletions
diff --git a/sound/core/control.c b/sound/core/control.c
index f64a555f404f..4f55f64c42e1 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -79,7 +79,7 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
ctl->preferred_subdevice[i] = -1;
ctl->pid = get_pid(task_pid(current));
file->private_data = ctl;
- scoped_guard(write_lock_irqsave, &card->ctl_files_rwlock)
+ scoped_guard(write_lock_irqsave, &card->controls_rwlock)
list_add_tail(&ctl->list, &card->ctl_files);
snd_card_unref(card);
return 0;
@@ -117,7 +117,7 @@ static int snd_ctl_release(struct inode *inode, struct file *file)
file->private_data = NULL;
card = ctl->card;
- scoped_guard(write_lock_irqsave, &card->ctl_files_rwlock)
+ scoped_guard(write_lock_irqsave, &card->controls_rwlock)
list_del(&ctl->list);
scoped_guard(rwsem_write, &card->controls_rwsem) {
@@ -157,7 +157,7 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask,
if (card->shutdown)
return;
- guard(read_lock_irqsave)(&card->ctl_files_rwlock);
+ guard(read_lock_irqsave)(&card->controls_rwlock);
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
card->mixer_oss_change_count++;
#endif
@@ -237,11 +237,11 @@ static int snd_ctl_new(struct snd_kcontrol **kctl, unsigned int count,
if (!*kctl)
return -ENOMEM;
+ (*kctl)->count = count;
for (idx = 0; idx < count; idx++) {
(*kctl)->vd[idx].access = access;
(*kctl)->vd[idx].owner = file;
}
- (*kctl)->count = count;
return 0;
}
@@ -470,7 +470,7 @@ static int __snd_ctl_add_replace(struct snd_card *card,
if (id.index > UINT_MAX - kcontrol->count)
return -EINVAL;
- old = snd_ctl_find_id_locked(card, &id);
+ old = snd_ctl_find_id(card, &id);
if (!old) {
if (mode == CTL_REPLACE)
return -EINVAL;
@@ -491,10 +491,12 @@ static int __snd_ctl_add_replace(struct snd_card *card,
if (snd_ctl_find_hole(card, kcontrol->count) < 0)
return -ENOMEM;
- list_add_tail(&kcontrol->list, &card->controls);
- card->controls_count += kcontrol->count;
- kcontrol->id.numid = card->last_numid + 1;
- card->last_numid += kcontrol->count;
+ scoped_guard(write_lock_irq, &card->controls_rwlock) {
+ list_add_tail(&kcontrol->list, &card->controls);
+ card->controls_count += kcontrol->count;
+ kcontrol->id.numid = card->last_numid + 1;
+ card->last_numid += kcontrol->count;
+ }
add_hash_entries(card, kcontrol);
@@ -579,12 +581,15 @@ static int __snd_ctl_remove(struct snd_card *card,
if (snd_BUG_ON(!card || !kcontrol))
return -EINVAL;
- list_del(&kcontrol->list);
if (remove_hash)
remove_hash_entries(card, kcontrol);
- card->controls_count -= kcontrol->count;
+ scoped_guard(write_lock_irq, &card->controls_rwlock) {
+ list_del(&kcontrol->list);
+ card->controls_count -= kcontrol->count;
+ }
+
for (idx = 0; idx < kcontrol->count; idx++)
snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_REMOVE, kcontrol, idx);
snd_ctl_free_one(kcontrol);
@@ -634,7 +639,7 @@ int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
struct snd_kcontrol *kctl;
guard(rwsem_write)(&card->controls_rwsem);
- kctl = snd_ctl_find_id_locked(card, id);
+ kctl = snd_ctl_find_id(card, id);
if (kctl == NULL)
return -ENOENT;
return snd_ctl_remove_locked(card, kctl);
@@ -659,7 +664,7 @@ static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
int idx;
guard(rwsem_write)(&card->controls_rwsem);
- kctl = snd_ctl_find_id_locked(card, id);
+ kctl = snd_ctl_find_id(card, id);
if (kctl == NULL)
return -ENOENT;
if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_USER))
@@ -691,7 +696,7 @@ int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
int ret;
down_write(&card->controls_rwsem);
- kctl = snd_ctl_find_id_locked(card, id);
+ kctl = snd_ctl_find_id(card, id);
if (kctl == NULL) {
ret = -ENOENT;
goto unlock;
@@ -745,7 +750,7 @@ int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id,
int saved_numid;
guard(rwsem_write)(&card->controls_rwsem);
- kctl = snd_ctl_find_id_locked(card, src_id);
+ kctl = snd_ctl_find_id(card, src_id);
if (kctl == NULL)
return -ENOENT;
saved_numid = kctl->id.numid;
@@ -787,6 +792,7 @@ snd_ctl_find_numid_slow(struct snd_card *card, unsigned int numid)
{
struct snd_kcontrol *kctl;
+ guard(read_lock_irqsave)(&card->controls_rwlock);
list_for_each_entry(kctl, &card->controls, list) {
if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid)
return kctl;
@@ -796,72 +802,51 @@ snd_ctl_find_numid_slow(struct snd_card *card, unsigned int numid)
#endif /* !CONFIG_SND_CTL_FAST_LOOKUP */
/**
- * snd_ctl_find_numid_locked - find the control instance with the given number-id
+ * snd_ctl_find_numid - find the control instance with the given number-id
* @card: the card instance
* @numid: the number-id to search
*
* Finds the control instance with the given number-id from the card.
*
- * The caller must down card->controls_rwsem before calling this function
- * (if the race condition can happen).
- *
* Return: The pointer of the instance if found, or %NULL if not.
+ *
+ * Note that this function takes card->controls_rwlock lock internally.
*/
-struct snd_kcontrol *
-snd_ctl_find_numid_locked(struct snd_card *card, unsigned int numid)
+struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card,
+ unsigned int numid)
{
if (snd_BUG_ON(!card || !numid))
return NULL;
- lockdep_assert_held(&card->controls_rwsem);
+
#ifdef CONFIG_SND_CTL_FAST_LOOKUP
return xa_load(&card->ctl_numids, numid);
#else
return snd_ctl_find_numid_slow(card, numid);
#endif
}
-EXPORT_SYMBOL(snd_ctl_find_numid_locked);
-
-/**
- * snd_ctl_find_numid - find the control instance with the given number-id
- * @card: the card instance
- * @numid: the number-id to search
- *
- * Finds the control instance with the given number-id from the card.
- *
- * Return: The pointer of the instance if found, or %NULL if not.
- *
- * Note that this function takes card->controls_rwsem lock internally.
- */
-struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card,
- unsigned int numid)
-{
- guard(rwsem_read)(&card->controls_rwsem);
- return snd_ctl_find_numid_locked(card, numid);
-}
EXPORT_SYMBOL(snd_ctl_find_numid);
/**
- * snd_ctl_find_id_locked - find the control instance with the given id
+ * snd_ctl_find_id - find the control instance with the given id
* @card: the card instance
* @id: the id to search
*
* Finds the control instance with the given id from the card.
*
- * The caller must down card->controls_rwsem before calling this function
- * (if the race condition can happen).
- *
* Return: The pointer of the instance if found, or %NULL if not.
+ *
+ * Note that this function takes card->controls_rwlock lock internally.
*/
-struct snd_kcontrol *snd_ctl_find_id_locked(struct snd_card *card,
- const struct snd_ctl_elem_id *id)
+struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
+ const struct snd_ctl_elem_id *id)
{
struct snd_kcontrol *kctl;
if (snd_BUG_ON(!card || !id))
return NULL;
- lockdep_assert_held(&card->controls_rwsem);
+
if (id->numid != 0)
- return snd_ctl_find_numid_locked(card, id->numid);
+ return snd_ctl_find_numid(card, id->numid);
#ifdef CONFIG_SND_CTL_FAST_LOOKUP
kctl = xa_load(&card->ctl_hash, get_ctl_id_hash(id));
if (kctl && elem_id_matches(kctl, id))
@@ -870,31 +855,13 @@ struct snd_kcontrol *snd_ctl_find_id_locked(struct snd_card *card,
return NULL; /* we can rely on only hash table */
#endif
/* no matching in hash table - try all as the last resort */
+ guard(read_lock_irqsave)(&card->controls_rwlock);
list_for_each_entry(kctl, &card->controls, list)
if (elem_id_matches(kctl, id))
return kctl;
return NULL;
}
-EXPORT_SYMBOL(snd_ctl_find_id_locked);
-
-/**
- * snd_ctl_find_id - find the control instance with the given id
- * @card: the card instance
- * @id: the id to search
- *
- * Finds the control instance with the given id from the card.
- *
- * Return: The pointer of the instance if found, or %NULL if not.
- *
- * Note that this function takes card->controls_rwsem lock internally.
- */
-struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
- const struct snd_ctl_elem_id *id)
-{
- guard(rwsem_read)(&card->controls_rwsem);
- return snd_ctl_find_id_locked(card, id);
-}
EXPORT_SYMBOL(snd_ctl_find_id);
static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
@@ -1167,10 +1134,7 @@ static int __snd_ctl_elem_info(struct snd_card *card,
#ifdef CONFIG_SND_DEBUG
info->access = 0;
#endif
- result = snd_power_ref_and_wait(card);
- if (!result)
- result = kctl->info(kctl, info);
- snd_power_unref(card);
+ result = kctl->info(kctl, info);
if (result >= 0) {
snd_BUG_ON(info->access);
index_offset = snd_ctl_get_ioff(kctl, &info->id);
@@ -1199,7 +1163,7 @@ static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
struct snd_kcontrol *kctl;
guard(rwsem_read)(&card->controls_rwsem);
- kctl = snd_ctl_find_id_locked(card, &info->id);
+ kctl = snd_ctl_find_id(card, &info->id);
if (!kctl)
return -ENOENT;
return __snd_ctl_elem_info(card, kctl, info, ctl);
@@ -1208,12 +1172,17 @@ static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
static int snd_ctl_elem_info_user(struct snd_ctl_file *ctl,
struct snd_ctl_elem_info __user *_info)
{
+ struct snd_card *card = ctl->card;
struct snd_ctl_elem_info info;
int result;
if (copy_from_user(&info, _info, sizeof(info)))
return -EFAULT;
+ result = snd_power_ref_and_wait(card);
+ if (result)
+ return result;
result = snd_ctl_elem_info(ctl, &info);
+ snd_power_unref(card);
if (result < 0)
return result;
/* drop internal access flags */
@@ -1235,7 +1204,7 @@ static int snd_ctl_elem_read(struct snd_card *card,
int ret;
guard(rwsem_read)(&card->controls_rwsem);
- kctl = snd_ctl_find_id_locked(card, &control->id);
+ kctl = snd_ctl_find_id(card, &control->id);
if (!kctl)
return -ENOENT;
@@ -1257,10 +1226,7 @@ static int snd_ctl_elem_read(struct snd_card *card,
if (!snd_ctl_skip_validation(&info))
fill_remaining_elem_value(control, &info, pattern);
- ret = snd_power_ref_and_wait(card);
- if (!ret)
- ret = kctl->get(kctl, control);
- snd_power_unref(card);
+ ret = kctl->get(kctl, control);
if (ret < 0)
return ret;
if (!snd_ctl_skip_validation(&info) &&
@@ -1283,9 +1249,13 @@ static int snd_ctl_elem_read_user(struct snd_card *card,
control = memdup_user(_control, sizeof(*control));
if (IS_ERR(control))
- return PTR_ERR(no_free_ptr(control));
+ return PTR_ERR(control);
+ result = snd_power_ref_and_wait(card);
+ if (result)
+ return result;
result = snd_ctl_elem_read(card, control);
+ snd_power_unref(card);
if (result < 0)
return result;
@@ -1300,10 +1270,10 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
struct snd_kcontrol *kctl;
struct snd_kcontrol_volatile *vd;
unsigned int index_offset;
- int result;
+ int result = 0;
down_write(&card->controls_rwsem);
- kctl = snd_ctl_find_id_locked(card, &control->id);
+ kctl = snd_ctl_find_id(card, &control->id);
if (kctl == NULL) {
up_write(&card->controls_rwsem);
return -ENOENT;
@@ -1318,9 +1288,8 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
}
snd_ctl_build_ioff(&control->id, kctl, index_offset);
- result = snd_power_ref_and_wait(card);
/* validate input values */
- if (IS_ENABLED(CONFIG_SND_CTL_INPUT_VALIDATION) && !result) {
+ if (IS_ENABLED(CONFIG_SND_CTL_INPUT_VALIDATION)) {
struct snd_ctl_elem_info info;
memset(&info, 0, sizeof(info));
@@ -1332,7 +1301,6 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
}
if (!result)
result = kctl->put(kctl, control);
- snd_power_unref(card);
if (result < 0) {
up_write(&card->controls_rwsem);
return result;
@@ -1358,10 +1326,14 @@ static int snd_ctl_elem_write_user(struct snd_ctl_file *file,
control = memdup_user(_control, sizeof(*control));
if (IS_ERR(control))
- return PTR_ERR(no_free_ptr(control));
+ return PTR_ERR(control);
card = file->card;
+ result = snd_power_ref_and_wait(card);
+ if (result < 0)
+ return result;
result = snd_ctl_elem_write(card, file, control);
+ snd_power_unref(card);
if (result < 0)
return result;
@@ -1381,7 +1353,7 @@ static int snd_ctl_elem_lock(struct snd_ctl_file *file,
if (copy_from_user(&id, _id, sizeof(id)))
return -EFAULT;
guard(rwsem_write)(&card->controls_rwsem);
- kctl = snd_ctl_find_id_locked(card, &id);
+ kctl = snd_ctl_find_id(card, &id);
if (!kctl)
return -ENOENT;
vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
@@ -1402,7 +1374,7 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
if (copy_from_user(&id, _id, sizeof(id)))
return -EFAULT;
guard(rwsem_write)(&card->controls_rwsem);
- kctl = snd_ctl_find_id_locked(card, &id);
+ kctl = snd_ctl_find_id(card, &id);
if (!kctl)
return -ENOENT;
vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
@@ -1830,7 +1802,7 @@ static int call_tlv_handler(struct snd_ctl_file *file, int op_flag,
{SNDRV_CTL_TLV_OP_CMD, SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND},
};
struct snd_kcontrol_volatile *vd = &kctl->vd[snd_ctl_get_ioff(kctl, id)];
- int i, ret;
+ int i;
/* Check support of the request for this element. */
for (i = 0; i < ARRAY_SIZE(pairs); ++i) {
@@ -1848,11 +1820,7 @@ static int call_tlv_handler(struct snd_ctl_file *file, int op_flag,
vd->owner != NULL && vd->owner != file)
return -EPERM;
- ret = snd_power_ref_and_wait(file->card);
- if (!ret)
- ret = kctl->tlv.c(kctl, op_flag, size, buf);
- snd_power_unref(file->card);
- return ret;
+ return kctl->tlv.c(kctl, op_flag, size, buf);
}
static int read_tlv_buf(struct snd_kcontrol *kctl, struct snd_ctl_elem_id *id,
@@ -1903,7 +1871,7 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
container_size = header.length;
container = buf->tlv;
- kctl = snd_ctl_find_numid_locked(file->card, header.numid);
+ kctl = snd_ctl_find_numid(file->card, header.numid);
if (kctl == NULL)
return -ENOENT;
@@ -1965,16 +1933,28 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
return snd_ctl_subscribe_events(ctl, ip);
case SNDRV_CTL_IOCTL_TLV_READ:
- scoped_guard(rwsem_read, &ctl->card->controls_rwsem)
+ err = snd_power_ref_and_wait(card);
+ if (err < 0)
+ return err;
+ scoped_guard(rwsem_read, &card->controls_rwsem)
err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_READ);
+ snd_power_unref(card);
return err;
case SNDRV_CTL_IOCTL_TLV_WRITE:
- scoped_guard(rwsem_write, &ctl->card->controls_rwsem)
+ err = snd_power_ref_and_wait(card);
+ if (err < 0)
+ return err;
+ scoped_guard(rwsem_write, &card->controls_rwsem)
err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_WRITE);
+ snd_power_unref(card);
return err;
case SNDRV_CTL_IOCTL_TLV_COMMAND:
- scoped_guard(rwsem_write, &ctl->card->controls_rwsem)
+ err = snd_power_ref_and_wait(card);
+ if (err < 0)
+ return err;
+ scoped_guard(rwsem_write, &card->controls_rwsem)
err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_CMD);
+ snd_power_unref(card);
return err;
case SNDRV_CTL_IOCTL_POWER:
return -ENOPROTOOPT;
@@ -2178,7 +2158,7 @@ int snd_ctl_get_preferred_subdevice(struct snd_card *card, int type)
struct snd_ctl_file *kctl;
int subdevice = -1;
- guard(read_lock_irqsave)(&card->ctl_files_rwlock);
+ guard(read_lock_irqsave)(&card->controls_rwlock);
list_for_each_entry(kctl, &card->ctl_files, list) {
if (kctl->pid == task_pid(current)) {
subdevice = kctl->preferred_subdevice[type];
@@ -2328,7 +2308,7 @@ static int snd_ctl_dev_disconnect(struct snd_device *device)
struct snd_card *card = device->device_data;
struct snd_ctl_file *ctl;
- scoped_guard(read_lock_irqsave, &card->ctl_files_rwlock) {
+ scoped_guard(read_lock_irqsave, &card->controls_rwlock) {
list_for_each_entry(ctl, &card->ctl_files, list) {
wake_up(&ctl->change_sleep);
snd_kill_fasync(ctl->fasync, SIGIO, POLL_ERR);