summaryrefslogtreecommitdiffstats
path: root/sound/core
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2019-04-16 18:18:47 +0200
committerTakashi Iwai <tiwai@suse.de>2019-04-17 07:16:15 +0200
commitf4fa968950aef7efb98a0899bb0132405e2edaf1 (patch)
tree2053bcf32985ef7539b02f10246bf67c4543903a /sound/core
parentc989954938761a2939a21fcbc768af182de6be58 (diff)
downloadlinux-f4fa968950aef7efb98a0899bb0132405e2edaf1.tar.bz2
ALSA: core: Don't refer to snd_cards array directly
The snd_cards[] array holds the card pointers that have been currently registered, and it's exported for the external modules that may need to refer a card object. But accessing to this array can be racy against the driver probe or removal, as the card registration or free may happen concurrently. This patch gets rid of the direct access to snd_cards[] array and provides a helper function to give the card object from the index number with a refcount management. Then the caller can access to the given card object safely, and releases it via snd_card_unref(). While we're at it, add a proper comment to snd_card_unref() and make it an inlined function for type-safety, too. Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/core')
-rw-r--r--sound/core/init.c23
-rw-r--r--sound/core/oss/mixer_oss.c16
-rw-r--r--sound/core/sound.c5
3 files changed, 37 insertions, 7 deletions
diff --git a/sound/core/init.c b/sound/core/init.c
index 0c4dc40376a7..356d41e4f3a4 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -49,8 +49,7 @@ static const struct file_operations snd_shutdown_f_ops;
/* locked for registering/using */
static DECLARE_BITMAP(snd_cards_lock, SNDRV_CARDS);
-struct snd_card *snd_cards[SNDRV_CARDS];
-EXPORT_SYMBOL(snd_cards);
+static struct snd_card *snd_cards[SNDRV_CARDS];
static DEFINE_MUTEX(snd_card_mutex);
@@ -268,6 +267,26 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
}
EXPORT_SYMBOL(snd_card_new);
+/**
+ * snd_card_ref - Get the card object from the index
+ * @idx: the card index
+ *
+ * Returns a card object corresponding to the given index or NULL if not found.
+ * Release the object via snd_card_unref().
+ */
+struct snd_card *snd_card_ref(int idx)
+{
+ struct snd_card *card;
+
+ mutex_lock(&snd_card_mutex);
+ card = snd_cards[idx];
+ if (card)
+ get_device(&card->card_dev);
+ mutex_unlock(&snd_card_mutex);
+ return card;
+}
+EXPORT_SYMBOL_GPL(snd_card_ref);
+
/* return non-zero if a card is already locked */
int snd_card_locked(int card)
{
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
index 64d904bee8bb..c8618678649c 100644
--- a/sound/core/oss/mixer_oss.c
+++ b/sound/core/oss/mixer_oss.c
@@ -1403,24 +1403,32 @@ static int snd_mixer_oss_notify_handler(struct snd_card *card, int cmd)
static int __init alsa_mixer_oss_init(void)
{
+ struct snd_card *card;
int idx;
snd_mixer_oss_notify_callback = snd_mixer_oss_notify_handler;
for (idx = 0; idx < SNDRV_CARDS; idx++) {
- if (snd_cards[idx])
- snd_mixer_oss_notify_handler(snd_cards[idx], SND_MIXER_OSS_NOTIFY_REGISTER);
+ card = snd_card_ref(idx);
+ if (card) {
+ snd_mixer_oss_notify_handler(card, SND_MIXER_OSS_NOTIFY_REGISTER);
+ snd_card_unref(card);
+ }
}
return 0;
}
static void __exit alsa_mixer_oss_exit(void)
{
+ struct snd_card *card;
int idx;
snd_mixer_oss_notify_callback = NULL;
for (idx = 0; idx < SNDRV_CARDS; idx++) {
- if (snd_cards[idx])
- snd_mixer_oss_notify_handler(snd_cards[idx], SND_MIXER_OSS_NOTIFY_FREE);
+ card = snd_card_ref(idx);
+ if (card) {
+ snd_mixer_oss_notify_handler(card, SND_MIXER_OSS_NOTIFY_FREE);
+ snd_card_unref(card);
+ }
}
}
diff --git a/sound/core/sound.c b/sound/core/sound.c
index b30f027eb0fe..a9ad4379523b 100644
--- a/sound/core/sound.c
+++ b/sound/core/sound.c
@@ -134,8 +134,11 @@ static struct snd_minor *autoload_device(unsigned int minor)
if (dev == SNDRV_MINOR_CONTROL) {
/* /dev/aloadC? */
int card = SNDRV_MINOR_CARD(minor);
- if (snd_cards[card] == NULL)
+ struct snd_card *ref = snd_card_ref(card);
+ if (!ref)
snd_request_card(card);
+ else
+ snd_card_unref(ref);
} else if (dev == SNDRV_MINOR_GLOBAL) {
/* /dev/aloadSEQ */
snd_request_other(minor);