Merge branch 'topic/seq-autoload' into for-next

This commit is contained in:
Takashi Iwai 2014-10-20 08:45:52 +02:00
commit b566762a67
3 changed files with 80 additions and 34 deletions

View file

@ -108,9 +108,13 @@ int snd_seq_event_port_detach(int client, int port);
#ifdef CONFIG_MODULES
void snd_seq_autoload_lock(void);
void snd_seq_autoload_unlock(void);
void snd_seq_autoload_init(void);
#define snd_seq_autoload_exit() snd_seq_autoload_lock()
#else
#define snd_seq_autoload_lock()
#define snd_seq_autoload_unlock()
#define snd_seq_autoload_init()
#define snd_seq_autoload_exit()
#endif
#endif /* __SOUND_SEQ_KERNEL_H */

View file

@ -86,7 +86,6 @@ static int __init alsa_seq_init(void)
{
int err;
snd_seq_autoload_lock();
if ((err = client_init_data()) < 0)
goto error;
@ -110,8 +109,8 @@ static int __init alsa_seq_init(void)
if ((err = snd_seq_system_client_init()) < 0)
goto error;
snd_seq_autoload_init();
error:
snd_seq_autoload_unlock();
return err;
}
@ -131,6 +130,8 @@ static void __exit alsa_seq_exit(void)
/* release event memory */
snd_sequencer_memory_done();
snd_seq_autoload_exit();
}
module_init(alsa_seq_init)

View file

@ -56,6 +56,7 @@ MODULE_LICENSE("GPL");
#define DRIVER_LOADED (1<<0)
#define DRIVER_REQUESTED (1<<1)
#define DRIVER_LOCKED (1<<2)
#define DRIVER_REQUESTING (1<<3)
struct ops_list {
char id[ID_LEN]; /* driver id */
@ -127,42 +128,82 @@ static void snd_seq_device_info(struct snd_info_entry *entry,
#ifdef CONFIG_MODULES
/* avoid auto-loading during module_init() */
static int snd_seq_in_init;
static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */
void snd_seq_autoload_lock(void)
{
snd_seq_in_init++;
atomic_inc(&snd_seq_in_init);
}
void snd_seq_autoload_unlock(void)
{
snd_seq_in_init--;
atomic_dec(&snd_seq_in_init);
}
static void autoload_drivers(void)
{
/* avoid reentrance */
if (atomic_inc_return(&snd_seq_in_init) == 1) {
struct ops_list *ops;
mutex_lock(&ops_mutex);
list_for_each_entry(ops, &opslist, list) {
if ((ops->driver & DRIVER_REQUESTING) &&
!(ops->driver & DRIVER_REQUESTED)) {
ops->used++;
mutex_unlock(&ops_mutex);
ops->driver |= DRIVER_REQUESTED;
request_module("snd-%s", ops->id);
mutex_lock(&ops_mutex);
ops->used--;
}
}
mutex_unlock(&ops_mutex);
}
atomic_dec(&snd_seq_in_init);
}
static void call_autoload(struct work_struct *work)
{
autoload_drivers();
}
static DECLARE_WORK(autoload_work, call_autoload);
static void try_autoload(struct ops_list *ops)
{
if (!ops->driver) {
ops->driver |= DRIVER_REQUESTING;
schedule_work(&autoload_work);
}
}
static void queue_autoload_drivers(void)
{
struct ops_list *ops;
mutex_lock(&ops_mutex);
list_for_each_entry(ops, &opslist, list)
try_autoload(ops);
mutex_unlock(&ops_mutex);
}
void snd_seq_autoload_init(void)
{
atomic_dec(&snd_seq_in_init);
#ifdef CONFIG_SND_SEQUENCER_MODULE
/* initial autoload only when snd-seq is a module */
queue_autoload_drivers();
#endif
}
#else
#define try_autoload(ops) /* NOP */
#endif
void snd_seq_device_load_drivers(void)
{
#ifdef CONFIG_MODULES
struct ops_list *ops;
/* Calling request_module during module_init()
* may cause blocking.
*/
if (snd_seq_in_init)
return;
mutex_lock(&ops_mutex);
list_for_each_entry(ops, &opslist, list) {
if (! (ops->driver & DRIVER_LOADED) &&
! (ops->driver & DRIVER_REQUESTED)) {
ops->used++;
mutex_unlock(&ops_mutex);
ops->driver |= DRIVER_REQUESTED;
request_module("snd-%s", ops->id);
mutex_lock(&ops_mutex);
ops->used--;
}
}
mutex_unlock(&ops_mutex);
queue_autoload_drivers();
flush_work(&autoload_work);
#endif
}
@ -214,13 +255,14 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize,
ops->num_devices++;
mutex_unlock(&ops->reg_mutex);
unlock_driver(ops);
if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) {
snd_seq_device_free(dev);
return err;
}
try_autoload(ops);
unlock_driver(ops);
if (result)
*result = dev;
@ -318,16 +360,12 @@ int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry,
entry->init_device == NULL || entry->free_device == NULL)
return -EINVAL;
snd_seq_autoload_lock();
ops = find_driver(id, 1);
if (ops == NULL) {
snd_seq_autoload_unlock();
if (ops == NULL)
return -ENOMEM;
}
if (ops->driver & DRIVER_LOADED) {
pr_warn("ALSA: seq: driver_register: driver '%s' already exists\n", id);
unlock_driver(ops);
snd_seq_autoload_unlock();
return -EBUSY;
}
@ -344,7 +382,6 @@ int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry,
mutex_unlock(&ops->reg_mutex);
unlock_driver(ops);
snd_seq_autoload_unlock();
return 0;
}
@ -554,6 +591,9 @@ static int __init alsa_seq_device_init(void)
static void __exit alsa_seq_device_exit(void)
{
#ifdef CONFIG_MODULES
cancel_work_sync(&autoload_work);
#endif
remove_drivers();
#ifdef CONFIG_PROC_FS
snd_info_free_entry(info_entry);
@ -570,6 +610,7 @@ EXPORT_SYMBOL(snd_seq_device_new);
EXPORT_SYMBOL(snd_seq_device_register_driver);
EXPORT_SYMBOL(snd_seq_device_unregister_driver);
#ifdef CONFIG_MODULES
EXPORT_SYMBOL(snd_seq_autoload_init);
EXPORT_SYMBOL(snd_seq_autoload_lock);
EXPORT_SYMBOL(snd_seq_autoload_unlock);
#endif