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>
#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;
return -EBUSY;
}
- err = snd_ctl_remove(card, old);
+ err = snd_ctl_remove_locked(card, old);
if (err < 0)
return err;
}
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);
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;
}
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;
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);
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;
}
}
__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;
}
card = p->chip->card;
- down_write(&card->controls_rwsem);
if (p->qsound_switch) {
snd_ctl_remove(card, p->qsound_switch);
p->qsound_switch = NULL;
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);
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;
}
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);
}
/* 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;
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) {
break;
}
}
- up_write(&card->controls_rwsem);
}
/* let caller know if FW can be freed when no objects are left */