Commit 21c3c5d2800733b7a276725b8e1ae49a694adc1a

Authored by Tejun Heo
1 parent bb813f4c93

block: don't request module during elevator init

Block layer allows selecting an elevator which is built as a module to
be selected as system default via kernel param "elevator=".  This is
achieved by automatically invoking request_module() whenever a new
block device is initialized and the elevator is not available.

This led to an interesting deadlock problem involving async and module
init.  Block device probing running off an async job invokes
request_module().  While the module is being loaded, it performs
async_synchronize_full() which ends up waiting for the async job which
is already waiting for request_module() to finish, leading to
deadlock.

Invoking request_module() from deep in block device init path is
already nasty in itself.  It seems best to avoid these situations from
the beginning by moving on-demand module loading out of block init
path.

The previous patch made sure that the default elevator module is
loaded early during boot if available.  This patch removes on-demand
loading of the default elevator from elevator init path.  As the
module would have been loaded during boot, userland-visible behavior
difference should be minimal.

For more details, please refer to the following thread.

  http://thread.gmane.org/gmane.linux.kernel/1420814

v2: The bool parameter was named @request_module which conflicted with
    request_module().  This built okay w/ CONFIG_MODULES because
    request_module() was defined as a macro.  W/o CONFIG_MODULES, it
    causes build breakage.  Rename the parameter to @try_loading.
    Reported by Fengguang.

Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Arjan van de Ven <arjan@linux.intel.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Alex Riesen <raa.lkml@gmail.com>
Cc: Fengguang Wu <fengguang.wu@intel.com>

Showing 1 changed file with 12 additions and 7 deletions Side-by-side Diff

... ... @@ -100,14 +100,14 @@
100 100 module_put(e->elevator_owner);
101 101 }
102 102  
103   -static struct elevator_type *elevator_get(const char *name)
  103 +static struct elevator_type *elevator_get(const char *name, bool try_loading)
104 104 {
105 105 struct elevator_type *e;
106 106  
107 107 spin_lock(&elv_list_lock);
108 108  
109 109 e = elevator_find(name);
110   - if (!e) {
  110 + if (!e && try_loading) {
111 111 spin_unlock(&elv_list_lock);
112 112 request_module("%s-iosched", name);
113 113 spin_lock(&elv_list_lock);
114 114  
115 115  
116 116  
117 117  
... ... @@ -207,25 +207,30 @@
207 207 q->boundary_rq = NULL;
208 208  
209 209 if (name) {
210   - e = elevator_get(name);
  210 + e = elevator_get(name, true);
211 211 if (!e)
212 212 return -EINVAL;
213 213 }
214 214  
  215 + /*
  216 + * Use the default elevator specified by config boot param or
  217 + * config option. Don't try to load modules as we could be running
  218 + * off async and request_module() isn't allowed from async.
  219 + */
215 220 if (!e && *chosen_elevator) {
216   - e = elevator_get(chosen_elevator);
  221 + e = elevator_get(chosen_elevator, false);
217 222 if (!e)
218 223 printk(KERN_ERR "I/O scheduler %s not found\n",
219 224 chosen_elevator);
220 225 }
221 226  
222 227 if (!e) {
223   - e = elevator_get(CONFIG_DEFAULT_IOSCHED);
  228 + e = elevator_get(CONFIG_DEFAULT_IOSCHED, false);
224 229 if (!e) {
225 230 printk(KERN_ERR
226 231 "Default I/O scheduler not found. " \
227 232 "Using noop.\n");
228   - e = elevator_get("noop");
  233 + e = elevator_get("noop", false);
229 234 }
230 235 }
231 236  
... ... @@ -967,7 +972,7 @@
967 972 return -ENXIO;
968 973  
969 974 strlcpy(elevator_name, name, sizeof(elevator_name));
970   - e = elevator_get(strstrip(elevator_name));
  975 + e = elevator_get(strstrip(elevator_name), true);
971 976 if (!e) {
972 977 printk(KERN_ERR "elevator: type %s not found\n", elevator_name);
973 978 return -EINVAL;