--- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include #include @@ -45,6 +47,9 @@ /* Static info about the MTD, used in cleanup_module */ static LIST_HEAD(blkmtd_device_list); +/* Protects blkmtd_device_list and early-boot paramline updates */ +static DEFINE_MUTEX(block2mtd_mutex); +static struct workqueue_struct *block2mtd_wq; static struct page *page_read(struct address_space *mapping, pgoff_t index) @@ -461,31 +466,89 @@ return 0; } +struct block2mtd_setup_work { + struct work_struct work; + struct completion done; + char *val; + int ret; +}; + +static void block2mtd_setup_workfn(struct work_struct *work) +{ + struct block2mtd_setup_work *w = + container_of(work, struct block2mtd_setup_work, work); + + mutex_lock(&block2mtd_mutex); + w->ret = block2mtd_setup2(w->val); + mutex_unlock(&block2mtd_mutex); + complete(&w->done); +} + +/* Queue setup2 on block2mtd_wq; caller holds a module ref until return */ +static int block2mtd_setup_defer(const char *val) +{ + struct block2mtd_setup_work *w; + int ret; + + if (!block2mtd_wq) + return -ENODEV; + + w = kzalloc(sizeof(*w), GFP_KERNEL); + if (!w) + return -ENOMEM; + + w->val = kstrdup(val, GFP_KERNEL); + if (!w->val) { + kfree(w); + return -ENOMEM; + } + + init_completion(&w->done); + INIT_WORK(&w->work, block2mtd_setup_workfn); + queue_work(block2mtd_wq, &w->work); + wait_for_completion(&w->done); + + ret = w->ret; + kfree(w->val); + kfree(w); + return ret; +} static int block2mtd_setup(const char *val, const struct kernel_param *kp) { -#ifdef MODULE - return block2mtd_setup2(val); -#else - /* If more parameters are later passed in via - /sys/module/block2mtd/parameters/block2mtd - and block2mtd_init() has already been called, - we can parse the argument now. */ - - if (block2mtd_init_called) - return block2mtd_setup2(val); - - /* During early boot stage, we only save the parameters - here. We must parse them later: if the param passed - from kernel boot command line, block2mtd_setup() is - called so early that it is not possible to resolve - the device (even kmalloc() fails). Deter that work to - block2mtd_setup2(). */ + int ret = 0; - strscpy(block2mtd_paramline, val, sizeof(block2mtd_paramline)); + if (!try_module_get(kp->mod)) + return -ENODEV; - return 0; + /* Do not open a block device under param_lock */ + kernel_param_unlock(kp->mod); + +#ifndef MODULE + mutex_lock(&block2mtd_mutex); + if (!block2mtd_init_called) { + /* Early boot: cannot resolve block devices yet. */ + strscpy(block2mtd_paramline, val, sizeof(block2mtd_paramline)); + mutex_unlock(&block2mtd_mutex); + kernel_param_lock(kp->mod); + module_put(kp->mod); + return 0; + } + mutex_unlock(&block2mtd_mutex); #endif + + if (block2mtd_wq) { + ret = block2mtd_setup_defer(val); + } else { + /* Pre-init (e.g. insmod args): safe to run setup2 here */ + mutex_lock(&block2mtd_mutex); + ret = block2mtd_setup2(val); + mutex_unlock(&block2mtd_mutex); + } + + kernel_param_lock(kp->mod); + module_put(kp->mod); + return ret; } @@ -496,10 +559,17 @@ { int ret = 0; + block2mtd_wq = alloc_ordered_workqueue("block2mtd", 0); + if (!block2mtd_wq) + return -ENOMEM; + #ifndef MODULE + mutex_lock(&block2mtd_mutex); if (strlen(block2mtd_paramline)) ret = block2mtd_setup2(block2mtd_paramline); + /* Avoid racing sysfs with the early paramline */ block2mtd_init_called = 1; + mutex_unlock(&block2mtd_mutex); #endif return ret; @@ -510,9 +580,16 @@ { struct list_head *pos, *next; - /* Remove the MTD devices */ + if (block2mtd_wq) { + flush_workqueue(block2mtd_wq); + destroy_workqueue(block2mtd_wq); + block2mtd_wq = NULL; + } + + mutex_lock(&block2mtd_mutex); list_for_each_safe(pos, next, &blkmtd_device_list) { struct block2mtd_dev *dev = list_entry(pos, typeof(*dev), list); + block2mtd_sync(&dev->mtd); mtd_device_unregister(&dev->mtd); mutex_destroy(&dev->write_mutex); @@ -522,6 +599,7 @@ list_del(&dev->list); block2mtd_free_device(dev); } + mutex_unlock(&block2mtd_mutex); } late_initcall(block2mtd_init);