ALSA: control: Take controls_rwsem lock in snd_ctl_remove()

So far, snd_ctl_remove() requires its caller to take
card->controls_rwsem manually before the call for avoiding possible
races.  However, many callers don't care and miss the locking.

Basically it's cumbersome and error-prone to enforce it to each
caller.  Moreover, card->controls_rwsem is a field that should be used
only by internal or proper helpers, and it's not to be touched at
random external places.

This patch is an attempt to make those calls more consistent: now
snd_ctl_remove() takes the card->controls_rwsem internally, just like
other API functions for kctls.  Since a few callers already take the
controls_rwsem locks, the patch removes those locks at the same time,
too.

Link: https://lore.kernel.org/r/20230718141304.1032-5-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2023-07-18 16:12:57 +02:00
parent d8b366c406
commit 192c4cccd0
8 changed files with 21 additions and 21 deletions

View file

@ -39,6 +39,9 @@ static LIST_HEAD(snd_control_compat_ioctls);
#endif
static struct snd_ctl_layer_ops *snd_ctl_layer;
static int snd_ctl_remove_locked(struct snd_card *card,
struct snd_kcontrol *kcontrol);
static int snd_ctl_open(struct inode *inode, struct file *file)
{
unsigned long flags;
@ -483,7 +486,7 @@ static int __snd_ctl_add_replace(struct snd_card *card,
return -EBUSY;
}
err = snd_ctl_remove(card, old);
err = snd_ctl_remove_locked(card, old);
if (err < 0)
return err;
}
@ -589,20 +592,32 @@ static int __snd_ctl_remove(struct snd_card *card,
return 0;
}
static inline int snd_ctl_remove_locked(struct snd_card *card,
struct snd_kcontrol *kcontrol)
{
return __snd_ctl_remove(card, kcontrol, true);
}
/**
* snd_ctl_remove - remove the control from the card and release it
* @card: the card instance
* @kcontrol: the control instance to remove
*
* Removes the control from the card and then releases the instance.
* You don't need to call snd_ctl_free_one(). You must be in
* the write lock - down_write(&card->controls_rwsem).
* You don't need to call snd_ctl_free_one().
*
* Return: 0 if successful, or a negative error code on failure.
*
* Note that this function takes card->controls_rwsem lock internally.
*/
int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)
{
return __snd_ctl_remove(card, kcontrol, true);
int ret;
down_write(&card->controls_rwsem);
ret = snd_ctl_remove_locked(card, kcontrol);
up_write(&card->controls_rwsem);
return ret;
}
EXPORT_SYMBOL(snd_ctl_remove);
@ -627,7 +642,7 @@ int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
up_write(&card->controls_rwsem);
return -ENOENT;
}
ret = snd_ctl_remove(card, kctl);
ret = snd_ctl_remove_locked(card, kctl);
up_write(&card->controls_rwsem);
return ret;
}
@ -665,7 +680,7 @@ static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
ret = -EBUSY;
goto error;
}
ret = snd_ctl_remove(card, kctl);
ret = snd_ctl_remove_locked(card, kctl);
error:
up_write(&card->controls_rwsem);
return ret;

View file

@ -66,12 +66,10 @@ static int snd_jack_dev_free(struct snd_device *device)
struct snd_card *card = device->card;
struct snd_jack_kctl *jack_kctl, *tmp_jack_kctl;
down_write(&card->controls_rwsem);
list_for_each_entry_safe(jack_kctl, tmp_jack_kctl, &jack->kctl_list, list) {
list_del_init(&jack_kctl->list);
snd_ctl_remove(card, jack_kctl->kctl);
}
up_write(&card->controls_rwsem);
if (jack->private_free)
jack->private_free(jack);

View file

@ -814,9 +814,7 @@ static void free_chmap(struct snd_pcm_str *pstr)
if (pstr->chmap_kctl) {
struct snd_card *card = pstr->pcm->card;
down_write(&card->controls_rwsem);
snd_ctl_remove(card, pstr->chmap_kctl);
up_write(&card->controls_rwsem);
pstr->chmap_kctl = NULL;
}
}

View file

@ -1040,10 +1040,8 @@ snd_emu8000_create_mixer(struct snd_card *card, struct snd_emu8000 *emu)
__error:
for (i = 0; i < EMU8000_NUM_CONTROLS; i++) {
down_write(&card->controls_rwsem);
if (emu->controls[i])
snd_ctl_remove(card, emu->controls[i]);
up_write(&card->controls_rwsem);
}
return err;
}

View file

@ -1080,7 +1080,6 @@ static void snd_sb_qsound_destroy(struct snd_sb_csp * p)
card = p->chip->card;
down_write(&card->controls_rwsem);
if (p->qsound_switch) {
snd_ctl_remove(card, p->qsound_switch);
p->qsound_switch = NULL;
@ -1089,7 +1088,6 @@ static void snd_sb_qsound_destroy(struct snd_sb_csp * p)
snd_ctl_remove(card, p->qsound_space);
p->qsound_space = NULL;
}
up_write(&card->controls_rwsem);
/* cancel pending transfer of QSound parameters */
spin_lock_irqsave (&p->q_lock, flags);

View file

@ -977,11 +977,9 @@ static int snd_emu10k1_del_controls(struct snd_emu10k1 *emu,
in_kernel);
if (err < 0)
return err;
down_write(&card->controls_rwsem);
ctl = snd_emu10k1_look_for_ctl(emu, &id);
if (ctl)
snd_ctl_remove(card, ctl->kcontrol);
up_write(&card->controls_rwsem);
}
return 0;
}

View file

@ -1769,10 +1769,8 @@ void snd_hda_ctls_clear(struct hda_codec *codec)
int i;
struct hda_nid_item *items = codec->mixers.list;
down_write(&codec->card->controls_rwsem);
for (i = 0; i < codec->mixers.used; i++)
snd_ctl_remove(codec->card, items[i].kctl);
up_write(&codec->card->controls_rwsem);
snd_array_free(&codec->mixers);
snd_array_free(&codec->nids);
}

View file

@ -2564,7 +2564,6 @@ EXPORT_SYMBOL_GPL(snd_soc_tplg_component_load);
/* remove dynamic controls from the component driver */
int snd_soc_tplg_component_remove(struct snd_soc_component *comp)
{
struct snd_card *card = comp->card->snd_card;
struct snd_soc_dobj *dobj, *next_dobj;
int pass;
@ -2572,7 +2571,6 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp)
for (pass = SOC_TPLG_PASS_END; pass >= SOC_TPLG_PASS_START; pass--) {
/* remove mixer controls */
down_write(&card->controls_rwsem);
list_for_each_entry_safe(dobj, next_dobj, &comp->dobj_list,
list) {
@ -2607,7 +2605,6 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp)
break;
}
}
up_write(&card->controls_rwsem);
}
/* let caller know if FW can be freed when no objects are left */