aboutsummaryrefslogtreecommitdiffstats
path: root/sound/core
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2014-10-15 08:06:25 -0400
committerTakashi Iwai <tiwai@suse.de>2014-10-18 14:25:12 -0400
commit68ab61084de3220e2fb0a698c890ba91decddc85 (patch)
tree3a39c406c22fc8a5646c27e6a27994041ef62bc0 /sound/core
parent54841a06c54eb55918948c12ab9b5f02cacb6ab3 (diff)
ALSA: seq: bind seq driver automatically
Currently the sequencer module binding is performed independently from the card module itself. The reason behind it is to keep the sequencer stuff optional and allow the system running without it (e.g. for using PCM or rawmidi only). This works in most cases, but a remaining problem is that the binding isn't done automatically when a new driver module is probed. Typically this becomes visible when a hotplug driver like usb audio is used. This patch tries to address this and other potential issues. First, the seq-binder (seq_device.c) tries to load a missing driver module at creating a new device object. This is done asynchronously in a workq for avoiding the deadlock (modprobe call in module init path). This action, however, should be enabled only when the sequencer stuff was already initialized, i.e. snd-seq module was already loaded. For that, a new function, snd_seq_autoload_init() is introduced here; this clears the blocking of autoloading, and also tries to load all pending driver modules. Reported-by: Adam Goode <agoode@chromium.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/core')
-rw-r--r--sound/core/seq/seq.c3
-rw-r--r--sound/core/seq/seq_device.c92
2 files changed, 72 insertions, 23 deletions
diff --git a/sound/core/seq/seq.c b/sound/core/seq/seq.c
index 712110561082..bebdd2e920ca 100644
--- a/sound/core/seq/seq.c
+++ b/sound/core/seq/seq.c
@@ -110,6 +110,7 @@ static int __init alsa_seq_init(void)
110 if ((err = snd_seq_system_client_init()) < 0) 110 if ((err = snd_seq_system_client_init()) < 0)
111 goto error; 111 goto error;
112 112
113 snd_seq_autoload_init();
113 error: 114 error:
114 snd_seq_autoload_unlock(); 115 snd_seq_autoload_unlock();
115 return err; 116 return err;
@@ -131,6 +132,8 @@ static void __exit alsa_seq_exit(void)
131 132
132 /* release event memory */ 133 /* release event memory */
133 snd_sequencer_memory_done(); 134 snd_sequencer_memory_done();
135
136 snd_seq_autoload_exit();
134} 137}
135 138
136module_init(alsa_seq_init) 139module_init(alsa_seq_init)
diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c
index 775ea9390110..a8e2c6016800 100644
--- a/sound/core/seq/seq_device.c
+++ b/sound/core/seq/seq_device.c
@@ -56,6 +56,7 @@ MODULE_LICENSE("GPL");
56#define DRIVER_LOADED (1<<0) 56#define DRIVER_LOADED (1<<0)
57#define DRIVER_REQUESTED (1<<1) 57#define DRIVER_REQUESTED (1<<1)
58#define DRIVER_LOCKED (1<<2) 58#define DRIVER_LOCKED (1<<2)
59#define DRIVER_REQUESTING (1<<3)
59 60
60struct ops_list { 61struct ops_list {
61 char id[ID_LEN]; /* driver id */ 62 char id[ID_LEN]; /* driver id */
@@ -127,7 +128,7 @@ static void snd_seq_device_info(struct snd_info_entry *entry,
127 128
128#ifdef CONFIG_MODULES 129#ifdef CONFIG_MODULES
129/* avoid auto-loading during module_init() */ 130/* avoid auto-loading during module_init() */
130static atomic_t snd_seq_in_init = ATOMIC_INIT(0); 131static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */
131void snd_seq_autoload_lock(void) 132void snd_seq_autoload_lock(void)
132{ 133{
133 atomic_inc(&snd_seq_in_init); 134 atomic_inc(&snd_seq_in_init);
@@ -137,32 +138,72 @@ void snd_seq_autoload_unlock(void)
137{ 138{
138 atomic_dec(&snd_seq_in_init); 139 atomic_dec(&snd_seq_in_init);
139} 140}
140#endif
141 141
142void snd_seq_device_load_drivers(void) 142static void autoload_drivers(void)
143{ 143{
144#ifdef CONFIG_MODULES 144 /* avoid reentrance */
145 struct ops_list *ops; 145 if (atomic_inc_return(&snd_seq_in_init) == 1) {
146 struct ops_list *ops;
147
148 mutex_lock(&ops_mutex);
149 list_for_each_entry(ops, &opslist, list) {
150 if ((ops->driver & DRIVER_REQUESTING) &&
151 !(ops->driver & DRIVER_REQUESTED)) {
152 ops->used++;
153 mutex_unlock(&ops_mutex);
154 ops->driver |= DRIVER_REQUESTED;
155 request_module("snd-%s", ops->id);
156 mutex_lock(&ops_mutex);
157 ops->used--;
158 }
159 }
160 mutex_unlock(&ops_mutex);
161 }
162 atomic_dec(&snd_seq_in_init);
163}
146 164
147 /* Calling request_module during module_init() 165static void call_autoload(struct work_struct *work)
148 * may cause blocking. 166{
149 */ 167 autoload_drivers();
150 if (atomic_read(&snd_seq_in_init)) 168}
151 return;
152 169
153 mutex_lock(&ops_mutex); 170static DECLARE_WORK(autoload_work, call_autoload);
154 list_for_each_entry(ops, &opslist, list) { 171
155 if (! (ops->driver & DRIVER_LOADED) && 172static void try_autoload(struct ops_list *ops)
156 ! (ops->driver & DRIVER_REQUESTED)) { 173{
157 ops->used++; 174 if (!ops->driver) {
158 mutex_unlock(&ops_mutex); 175 ops->driver |= DRIVER_REQUESTING;
159 ops->driver |= DRIVER_REQUESTED; 176 schedule_work(&autoload_work);
160 request_module("snd-%s", ops->id);
161 mutex_lock(&ops_mutex);
162 ops->used--;
163 }
164 } 177 }
178}
179
180static void queue_autoload_drivers(void)
181{
182 struct ops_list *ops;
183
184 mutex_lock(&ops_mutex);
185 list_for_each_entry(ops, &opslist, list)
186 try_autoload(ops);
165 mutex_unlock(&ops_mutex); 187 mutex_unlock(&ops_mutex);
188}
189
190void snd_seq_autoload_init(void)
191{
192 atomic_dec(&snd_seq_in_init);
193#ifdef CONFIG_SND_SEQUENCER_MODULE
194 /* initial autoload only when snd-seq is a module */
195 queue_autoload_drivers();
196#endif
197}
198#else
199#define try_autoload(ops) /* NOP */
200#endif
201
202void snd_seq_device_load_drivers(void)
203{
204#ifdef CONFIG_MODULES
205 queue_autoload_drivers();
206 flush_work(&autoload_work);
166#endif 207#endif
167} 208}
168 209
@@ -214,13 +255,14 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize,
214 ops->num_devices++; 255 ops->num_devices++;
215 mutex_unlock(&ops->reg_mutex); 256 mutex_unlock(&ops->reg_mutex);
216 257
217 unlock_driver(ops);
218
219 if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) { 258 if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) {
220 snd_seq_device_free(dev); 259 snd_seq_device_free(dev);
221 return err; 260 return err;
222 } 261 }
223 262
263 try_autoload(ops);
264 unlock_driver(ops);
265
224 if (result) 266 if (result)
225 *result = dev; 267 *result = dev;
226 268
@@ -554,6 +596,9 @@ static int __init alsa_seq_device_init(void)
554 596
555static void __exit alsa_seq_device_exit(void) 597static void __exit alsa_seq_device_exit(void)
556{ 598{
599#ifdef CONFIG_MODULES
600 cancel_work_sync(&autoload_work);
601#endif
557 remove_drivers(); 602 remove_drivers();
558#ifdef CONFIG_PROC_FS 603#ifdef CONFIG_PROC_FS
559 snd_info_free_entry(info_entry); 604 snd_info_free_entry(info_entry);
@@ -570,6 +615,7 @@ EXPORT_SYMBOL(snd_seq_device_new);
570EXPORT_SYMBOL(snd_seq_device_register_driver); 615EXPORT_SYMBOL(snd_seq_device_register_driver);
571EXPORT_SYMBOL(snd_seq_device_unregister_driver); 616EXPORT_SYMBOL(snd_seq_device_unregister_driver);
572#ifdef CONFIG_MODULES 617#ifdef CONFIG_MODULES
618EXPORT_SYMBOL(snd_seq_autoload_init);
573EXPORT_SYMBOL(snd_seq_autoload_lock); 619EXPORT_SYMBOL(snd_seq_autoload_lock);
574EXPORT_SYMBOL(snd_seq_autoload_unlock); 620EXPORT_SYMBOL(snd_seq_autoload_unlock);
575#endif 621#endif