diff options
Diffstat (limited to 'drivers/mtd/mtd_blkdevs.c')
-rw-r--r-- | drivers/mtd/mtd_blkdevs.c | 335 |
1 files changed, 217 insertions, 118 deletions
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index c82e09bbc5fd..03e19c1965cc 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c | |||
@@ -14,7 +14,6 @@ | |||
14 | #include <linux/mtd/mtd.h> | 14 | #include <linux/mtd/mtd.h> |
15 | #include <linux/blkdev.h> | 15 | #include <linux/blkdev.h> |
16 | #include <linux/blkpg.h> | 16 | #include <linux/blkpg.h> |
17 | #include <linux/freezer.h> | ||
18 | #include <linux/spinlock.h> | 17 | #include <linux/spinlock.h> |
19 | #include <linux/hdreg.h> | 18 | #include <linux/hdreg.h> |
20 | #include <linux/init.h> | 19 | #include <linux/init.h> |
@@ -25,12 +24,42 @@ | |||
25 | #include "mtdcore.h" | 24 | #include "mtdcore.h" |
26 | 25 | ||
27 | static LIST_HEAD(blktrans_majors); | 26 | static LIST_HEAD(blktrans_majors); |
27 | static DEFINE_MUTEX(blktrans_ref_mutex); | ||
28 | |||
29 | void blktrans_dev_release(struct kref *kref) | ||
30 | { | ||
31 | struct mtd_blktrans_dev *dev = | ||
32 | container_of(kref, struct mtd_blktrans_dev, ref); | ||
33 | |||
34 | dev->disk->private_data = NULL; | ||
35 | blk_cleanup_queue(dev->rq); | ||
36 | put_disk(dev->disk); | ||
37 | list_del(&dev->list); | ||
38 | kfree(dev); | ||
39 | } | ||
40 | |||
41 | static struct mtd_blktrans_dev *blktrans_dev_get(struct gendisk *disk) | ||
42 | { | ||
43 | struct mtd_blktrans_dev *dev; | ||
44 | |||
45 | mutex_lock(&blktrans_ref_mutex); | ||
46 | dev = disk->private_data; | ||
47 | |||
48 | if (!dev) | ||
49 | goto unlock; | ||
50 | kref_get(&dev->ref); | ||
51 | unlock: | ||
52 | mutex_unlock(&blktrans_ref_mutex); | ||
53 | return dev; | ||
54 | } | ||
55 | |||
56 | void blktrans_dev_put(struct mtd_blktrans_dev *dev) | ||
57 | { | ||
58 | mutex_lock(&blktrans_ref_mutex); | ||
59 | kref_put(&dev->ref, blktrans_dev_release); | ||
60 | mutex_unlock(&blktrans_ref_mutex); | ||
61 | } | ||
28 | 62 | ||
29 | struct mtd_blkcore_priv { | ||
30 | struct task_struct *thread; | ||
31 | struct request_queue *rq; | ||
32 | spinlock_t queue_lock; | ||
33 | }; | ||
34 | 63 | ||
35 | static int do_blktrans_request(struct mtd_blktrans_ops *tr, | 64 | static int do_blktrans_request(struct mtd_blktrans_ops *tr, |
36 | struct mtd_blktrans_dev *dev, | 65 | struct mtd_blktrans_dev *dev, |
@@ -61,7 +90,6 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, | |||
61 | return -EIO; | 90 | return -EIO; |
62 | rq_flush_dcache_pages(req); | 91 | rq_flush_dcache_pages(req); |
63 | return 0; | 92 | return 0; |
64 | |||
65 | case WRITE: | 93 | case WRITE: |
66 | if (!tr->writesect) | 94 | if (!tr->writesect) |
67 | return -EIO; | 95 | return -EIO; |
@@ -71,7 +99,6 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, | |||
71 | if (tr->writesect(dev, block, buf)) | 99 | if (tr->writesect(dev, block, buf)) |
72 | return -EIO; | 100 | return -EIO; |
73 | return 0; | 101 | return 0; |
74 | |||
75 | default: | 102 | default: |
76 | printk(KERN_NOTICE "Unknown request %u\n", rq_data_dir(req)); | 103 | printk(KERN_NOTICE "Unknown request %u\n", rq_data_dir(req)); |
77 | return -EIO; | 104 | return -EIO; |
@@ -80,14 +107,13 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, | |||
80 | 107 | ||
81 | static int mtd_blktrans_thread(void *arg) | 108 | static int mtd_blktrans_thread(void *arg) |
82 | { | 109 | { |
83 | struct mtd_blktrans_ops *tr = arg; | 110 | struct mtd_blktrans_dev *dev = arg; |
84 | struct request_queue *rq = tr->blkcore_priv->rq; | 111 | struct request_queue *rq = dev->rq; |
85 | struct request *req = NULL; | 112 | struct request *req = NULL; |
86 | 113 | ||
87 | spin_lock_irq(rq->queue_lock); | 114 | spin_lock_irq(rq->queue_lock); |
88 | 115 | ||
89 | while (!kthread_should_stop()) { | 116 | while (!kthread_should_stop()) { |
90 | struct mtd_blktrans_dev *dev; | ||
91 | int res; | 117 | int res; |
92 | 118 | ||
93 | if (!req && !(req = blk_fetch_request(rq))) { | 119 | if (!req && !(req = blk_fetch_request(rq))) { |
@@ -98,13 +124,10 @@ static int mtd_blktrans_thread(void *arg) | |||
98 | continue; | 124 | continue; |
99 | } | 125 | } |
100 | 126 | ||
101 | dev = req->rq_disk->private_data; | ||
102 | tr = dev->tr; | ||
103 | |||
104 | spin_unlock_irq(rq->queue_lock); | 127 | spin_unlock_irq(rq->queue_lock); |
105 | 128 | ||
106 | mutex_lock(&dev->lock); | 129 | mutex_lock(&dev->lock); |
107 | res = do_blktrans_request(tr, dev, req); | 130 | res = do_blktrans_request(dev->tr, dev, req); |
108 | mutex_unlock(&dev->lock); | 131 | mutex_unlock(&dev->lock); |
109 | 132 | ||
110 | spin_lock_irq(rq->queue_lock); | 133 | spin_lock_irq(rq->queue_lock); |
@@ -123,81 +146,112 @@ static int mtd_blktrans_thread(void *arg) | |||
123 | 146 | ||
124 | static void mtd_blktrans_request(struct request_queue *rq) | 147 | static void mtd_blktrans_request(struct request_queue *rq) |
125 | { | 148 | { |
126 | struct mtd_blktrans_ops *tr = rq->queuedata; | 149 | struct mtd_blktrans_dev *dev; |
127 | wake_up_process(tr->blkcore_priv->thread); | 150 | struct request *req = NULL; |
128 | } | 151 | |
152 | dev = rq->queuedata; | ||
129 | 153 | ||
154 | if (!dev) | ||
155 | while ((req = blk_fetch_request(rq)) != NULL) | ||
156 | __blk_end_request_all(req, -ENODEV); | ||
157 | else | ||
158 | wake_up_process(dev->thread); | ||
159 | } | ||
130 | 160 | ||
131 | static int blktrans_open(struct block_device *bdev, fmode_t mode) | 161 | static int blktrans_open(struct block_device *bdev, fmode_t mode) |
132 | { | 162 | { |
133 | struct mtd_blktrans_dev *dev = bdev->bd_disk->private_data; | 163 | struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk); |
134 | struct mtd_blktrans_ops *tr = dev->tr; | 164 | int ret; |
135 | int ret = -ENODEV; | 165 | |
136 | 166 | if (!dev) | |
137 | if (!get_mtd_device(NULL, dev->mtd->index)) | 167 | return -ERESTARTSYS; |
138 | goto out; | 168 | |
139 | 169 | mutex_lock(&dev->lock); | |
140 | if (!try_module_get(tr->owner)) | 170 | |
141 | goto out_tr; | 171 | if (!dev->mtd) { |
142 | 172 | ret = -ENXIO; | |
143 | /* FIXME: Locking. A hot pluggable device can go away | 173 | goto unlock; |
144 | (del_mtd_device can be called for it) without its module | ||
145 | being unloaded. */ | ||
146 | dev->mtd->usecount++; | ||
147 | |||
148 | ret = 0; | ||
149 | if (tr->open && (ret = tr->open(dev))) { | ||
150 | dev->mtd->usecount--; | ||
151 | put_mtd_device(dev->mtd); | ||
152 | out_tr: | ||
153 | module_put(tr->owner); | ||
154 | } | 174 | } |
155 | out: | 175 | |
176 | ret = !dev->open++ && dev->tr->open ? dev->tr->open(dev) : 0; | ||
177 | |||
178 | /* Take another reference on the device so it won't go away till | ||
179 | last release */ | ||
180 | if (!ret) | ||
181 | kref_get(&dev->ref); | ||
182 | unlock: | ||
183 | mutex_unlock(&dev->lock); | ||
184 | blktrans_dev_put(dev); | ||
156 | return ret; | 185 | return ret; |
157 | } | 186 | } |
158 | 187 | ||
159 | static int blktrans_release(struct gendisk *disk, fmode_t mode) | 188 | static int blktrans_release(struct gendisk *disk, fmode_t mode) |
160 | { | 189 | { |
161 | struct mtd_blktrans_dev *dev = disk->private_data; | 190 | struct mtd_blktrans_dev *dev = blktrans_dev_get(disk); |
162 | struct mtd_blktrans_ops *tr = dev->tr; | 191 | int ret = -ENXIO; |
163 | int ret = 0; | ||
164 | 192 | ||
165 | if (tr->release) | 193 | if (!dev) |
166 | ret = tr->release(dev); | 194 | return ret; |
167 | 195 | ||
168 | if (!ret) { | 196 | mutex_lock(&dev->lock); |
169 | dev->mtd->usecount--; | 197 | |
170 | put_mtd_device(dev->mtd); | 198 | /* Release one reference, we sure its not the last one here*/ |
171 | module_put(tr->owner); | 199 | kref_put(&dev->ref, blktrans_dev_release); |
172 | } | ||
173 | 200 | ||
201 | if (!dev->mtd) | ||
202 | goto unlock; | ||
203 | |||
204 | ret = !--dev->open && dev->tr->release ? dev->tr->release(dev) : 0; | ||
205 | unlock: | ||
206 | mutex_unlock(&dev->lock); | ||
207 | blktrans_dev_put(dev); | ||
174 | return ret; | 208 | return ret; |
175 | } | 209 | } |
176 | 210 | ||
177 | static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo) | 211 | static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo) |
178 | { | 212 | { |
179 | struct mtd_blktrans_dev *dev = bdev->bd_disk->private_data; | 213 | struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk); |
214 | int ret = -ENXIO; | ||
215 | |||
216 | if (!dev) | ||
217 | return ret; | ||
218 | |||
219 | mutex_lock(&dev->lock); | ||
220 | |||
221 | if (!dev->mtd) | ||
222 | goto unlock; | ||
180 | 223 | ||
181 | if (dev->tr->getgeo) | 224 | ret = dev->tr->getgeo ? dev->tr->getgeo(dev, geo) : 0; |
182 | return dev->tr->getgeo(dev, geo); | 225 | unlock: |
183 | return -ENOTTY; | 226 | mutex_unlock(&dev->lock); |
227 | blktrans_dev_put(dev); | ||
228 | return ret; | ||
184 | } | 229 | } |
185 | 230 | ||
186 | static int blktrans_ioctl(struct block_device *bdev, fmode_t mode, | 231 | static int blktrans_ioctl(struct block_device *bdev, fmode_t mode, |
187 | unsigned int cmd, unsigned long arg) | 232 | unsigned int cmd, unsigned long arg) |
188 | { | 233 | { |
189 | struct mtd_blktrans_dev *dev = bdev->bd_disk->private_data; | 234 | struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk); |
190 | struct mtd_blktrans_ops *tr = dev->tr; | 235 | int ret = -ENXIO; |
236 | |||
237 | if (!dev) | ||
238 | return ret; | ||
239 | |||
240 | mutex_lock(&dev->lock); | ||
241 | |||
242 | if (!dev->mtd) | ||
243 | goto unlock; | ||
191 | 244 | ||
192 | switch (cmd) { | 245 | switch (cmd) { |
193 | case BLKFLSBUF: | 246 | case BLKFLSBUF: |
194 | if (tr->flush) | 247 | ret = dev->tr->flush ? dev->tr->flush(dev) : 0; |
195 | return tr->flush(dev); | ||
196 | /* The core code did the work, we had nothing to do. */ | ||
197 | return 0; | ||
198 | default: | 248 | default: |
199 | return -ENOTTY; | 249 | ret = -ENOTTY; |
200 | } | 250 | } |
251 | unlock: | ||
252 | mutex_unlock(&dev->lock); | ||
253 | blktrans_dev_put(dev); | ||
254 | return ret; | ||
201 | } | 255 | } |
202 | 256 | ||
203 | static const struct block_device_operations mtd_blktrans_ops = { | 257 | static const struct block_device_operations mtd_blktrans_ops = { |
@@ -214,12 +268,14 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) | |||
214 | struct mtd_blktrans_dev *d; | 268 | struct mtd_blktrans_dev *d; |
215 | int last_devnum = -1; | 269 | int last_devnum = -1; |
216 | struct gendisk *gd; | 270 | struct gendisk *gd; |
271 | int ret; | ||
217 | 272 | ||
218 | if (mutex_trylock(&mtd_table_mutex)) { | 273 | if (mutex_trylock(&mtd_table_mutex)) { |
219 | mutex_unlock(&mtd_table_mutex); | 274 | mutex_unlock(&mtd_table_mutex); |
220 | BUG(); | 275 | BUG(); |
221 | } | 276 | } |
222 | 277 | ||
278 | mutex_lock(&blktrans_ref_mutex); | ||
223 | list_for_each_entry(d, &tr->devs, list) { | 279 | list_for_each_entry(d, &tr->devs, list) { |
224 | if (new->devnum == -1) { | 280 | if (new->devnum == -1) { |
225 | /* Use first free number */ | 281 | /* Use first free number */ |
@@ -231,6 +287,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) | |||
231 | } | 287 | } |
232 | } else if (d->devnum == new->devnum) { | 288 | } else if (d->devnum == new->devnum) { |
233 | /* Required number taken */ | 289 | /* Required number taken */ |
290 | mutex_unlock(&blktrans_ref_mutex); | ||
234 | return -EBUSY; | 291 | return -EBUSY; |
235 | } else if (d->devnum > new->devnum) { | 292 | } else if (d->devnum > new->devnum) { |
236 | /* Required number was free */ | 293 | /* Required number was free */ |
@@ -239,24 +296,38 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) | |||
239 | } | 296 | } |
240 | last_devnum = d->devnum; | 297 | last_devnum = d->devnum; |
241 | } | 298 | } |
299 | |||
300 | ret = -EBUSY; | ||
242 | if (new->devnum == -1) | 301 | if (new->devnum == -1) |
243 | new->devnum = last_devnum+1; | 302 | new->devnum = last_devnum+1; |
244 | 303 | ||
245 | if ((new->devnum << tr->part_bits) > 256) { | 304 | /* Check that the device and any partitions will get valid |
246 | return -EBUSY; | 305 | * minor numbers and that the disk naming code below can cope |
306 | * with this number. */ | ||
307 | if (new->devnum > (MINORMASK >> tr->part_bits) || | ||
308 | (tr->part_bits && new->devnum >= 27 * 26)) { | ||
309 | mutex_unlock(&blktrans_ref_mutex); | ||
310 | goto error1; | ||
247 | } | 311 | } |
248 | 312 | ||
249 | list_add_tail(&new->list, &tr->devs); | 313 | list_add_tail(&new->list, &tr->devs); |
250 | added: | 314 | added: |
315 | mutex_unlock(&blktrans_ref_mutex); | ||
316 | |||
251 | mutex_init(&new->lock); | 317 | mutex_init(&new->lock); |
318 | kref_init(&new->ref); | ||
252 | if (!tr->writesect) | 319 | if (!tr->writesect) |
253 | new->readonly = 1; | 320 | new->readonly = 1; |
254 | 321 | ||
322 | /* Create gendisk */ | ||
323 | ret = -ENOMEM; | ||
255 | gd = alloc_disk(1 << tr->part_bits); | 324 | gd = alloc_disk(1 << tr->part_bits); |
256 | if (!gd) { | 325 | |
257 | list_del(&new->list); | 326 | if (!gd) |
258 | return -ENOMEM; | 327 | goto error2; |
259 | } | 328 | |
329 | new->disk = gd; | ||
330 | gd->private_data = new; | ||
260 | gd->major = tr->major; | 331 | gd->major = tr->major; |
261 | gd->first_minor = (new->devnum) << tr->part_bits; | 332 | gd->first_minor = (new->devnum) << tr->part_bits; |
262 | gd->fops = &mtd_blktrans_ops; | 333 | gd->fops = &mtd_blktrans_ops; |
@@ -274,13 +345,35 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) | |||
274 | snprintf(gd->disk_name, sizeof(gd->disk_name), | 345 | snprintf(gd->disk_name, sizeof(gd->disk_name), |
275 | "%s%d", tr->name, new->devnum); | 346 | "%s%d", tr->name, new->devnum); |
276 | 347 | ||
277 | /* 2.5 has capacity in units of 512 bytes while still | ||
278 | having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */ | ||
279 | set_capacity(gd, (new->size * tr->blksize) >> 9); | 348 | set_capacity(gd, (new->size * tr->blksize) >> 9); |
280 | 349 | ||
281 | gd->private_data = new; | 350 | /* Create the request queue */ |
282 | new->blkcore_priv = gd; | 351 | spin_lock_init(&new->queue_lock); |
283 | gd->queue = tr->blkcore_priv->rq; | 352 | new->rq = blk_init_queue(mtd_blktrans_request, &new->queue_lock); |
353 | |||
354 | if (!new->rq) | ||
355 | goto error3; | ||
356 | |||
357 | new->rq->queuedata = new; | ||
358 | blk_queue_logical_block_size(new->rq, tr->blksize); | ||
359 | |||
360 | if (tr->discard) | ||
361 | queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, | ||
362 | new->rq); | ||
363 | |||
364 | gd->queue = new->rq; | ||
365 | |||
366 | __get_mtd_device(new->mtd); | ||
367 | __module_get(tr->owner); | ||
368 | |||
369 | /* Create processing thread */ | ||
370 | /* TODO: workqueue ? */ | ||
371 | new->thread = kthread_run(mtd_blktrans_thread, new, | ||
372 | "%s%d", tr->name, new->mtd->index); | ||
373 | if (IS_ERR(new->thread)) { | ||
374 | ret = PTR_ERR(new->thread); | ||
375 | goto error4; | ||
376 | } | ||
284 | gd->driverfs_dev = &new->mtd->dev; | 377 | gd->driverfs_dev = &new->mtd->dev; |
285 | 378 | ||
286 | if (new->readonly) | 379 | if (new->readonly) |
@@ -288,21 +381,65 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) | |||
288 | 381 | ||
289 | add_disk(gd); | 382 | add_disk(gd); |
290 | 383 | ||
384 | if (new->disk_attributes) { | ||
385 | ret = sysfs_create_group(&disk_to_dev(gd)->kobj, | ||
386 | new->disk_attributes); | ||
387 | WARN_ON(ret); | ||
388 | } | ||
291 | return 0; | 389 | return 0; |
390 | error4: | ||
391 | module_put(tr->owner); | ||
392 | __put_mtd_device(new->mtd); | ||
393 | blk_cleanup_queue(new->rq); | ||
394 | error3: | ||
395 | put_disk(new->disk); | ||
396 | error2: | ||
397 | list_del(&new->list); | ||
398 | error1: | ||
399 | kfree(new); | ||
400 | return ret; | ||
292 | } | 401 | } |
293 | 402 | ||
294 | int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) | 403 | int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) |
295 | { | 404 | { |
405 | unsigned long flags; | ||
406 | |||
296 | if (mutex_trylock(&mtd_table_mutex)) { | 407 | if (mutex_trylock(&mtd_table_mutex)) { |
297 | mutex_unlock(&mtd_table_mutex); | 408 | mutex_unlock(&mtd_table_mutex); |
298 | BUG(); | 409 | BUG(); |
299 | } | 410 | } |
300 | 411 | ||
301 | list_del(&old->list); | 412 | /* Stop new requests to arrive */ |
413 | del_gendisk(old->disk); | ||
414 | |||
415 | if (old->disk_attributes) | ||
416 | sysfs_remove_group(&disk_to_dev(old->disk)->kobj, | ||
417 | old->disk_attributes); | ||
418 | |||
419 | /* Stop the thread */ | ||
420 | kthread_stop(old->thread); | ||
421 | |||
422 | /* Kill current requests */ | ||
423 | spin_lock_irqsave(&old->queue_lock, flags); | ||
424 | old->rq->queuedata = NULL; | ||
425 | blk_start_queue(old->rq); | ||
426 | spin_unlock_irqrestore(&old->queue_lock, flags); | ||
427 | |||
428 | /* Ask trans driver for release to the mtd device */ | ||
429 | mutex_lock(&old->lock); | ||
430 | if (old->open && old->tr->release) { | ||
431 | old->tr->release(old); | ||
432 | old->open = 0; | ||
433 | } | ||
434 | |||
435 | __put_mtd_device(old->mtd); | ||
436 | module_put(old->tr->owner); | ||
302 | 437 | ||
303 | del_gendisk(old->blkcore_priv); | 438 | /* At that point, we don't touch the mtd anymore */ |
304 | put_disk(old->blkcore_priv); | 439 | old->mtd = NULL; |
305 | 440 | ||
441 | mutex_unlock(&old->lock); | ||
442 | blktrans_dev_put(old); | ||
306 | return 0; | 443 | return 0; |
307 | } | 444 | } |
308 | 445 | ||
@@ -335,7 +472,8 @@ static struct mtd_notifier blktrans_notifier = { | |||
335 | 472 | ||
336 | int register_mtd_blktrans(struct mtd_blktrans_ops *tr) | 473 | int register_mtd_blktrans(struct mtd_blktrans_ops *tr) |
337 | { | 474 | { |
338 | int ret, i; | 475 | struct mtd_info *mtd; |
476 | int ret; | ||
339 | 477 | ||
340 | /* Register the notifier if/when the first device type is | 478 | /* Register the notifier if/when the first device type is |
341 | registered, to prevent the link/init ordering from fucking | 479 | registered, to prevent the link/init ordering from fucking |
@@ -343,9 +481,6 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr) | |||
343 | if (!blktrans_notifier.list.next) | 481 | if (!blktrans_notifier.list.next) |
344 | register_mtd_user(&blktrans_notifier); | 482 | register_mtd_user(&blktrans_notifier); |
345 | 483 | ||
346 | tr->blkcore_priv = kzalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL); | ||
347 | if (!tr->blkcore_priv) | ||
348 | return -ENOMEM; | ||
349 | 484 | ||
350 | mutex_lock(&mtd_table_mutex); | 485 | mutex_lock(&mtd_table_mutex); |
351 | 486 | ||
@@ -353,49 +488,20 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr) | |||
353 | if (ret) { | 488 | if (ret) { |
354 | printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n", | 489 | printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n", |
355 | tr->name, tr->major, ret); | 490 | tr->name, tr->major, ret); |
356 | kfree(tr->blkcore_priv); | ||
357 | mutex_unlock(&mtd_table_mutex); | 491 | mutex_unlock(&mtd_table_mutex); |
358 | return ret; | 492 | return ret; |
359 | } | 493 | } |
360 | spin_lock_init(&tr->blkcore_priv->queue_lock); | ||
361 | |||
362 | tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock); | ||
363 | if (!tr->blkcore_priv->rq) { | ||
364 | unregister_blkdev(tr->major, tr->name); | ||
365 | kfree(tr->blkcore_priv); | ||
366 | mutex_unlock(&mtd_table_mutex); | ||
367 | return -ENOMEM; | ||
368 | } | ||
369 | |||
370 | tr->blkcore_priv->rq->queuedata = tr; | ||
371 | blk_queue_logical_block_size(tr->blkcore_priv->rq, tr->blksize); | ||
372 | if (tr->discard) | ||
373 | queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, | ||
374 | tr->blkcore_priv->rq); | ||
375 | 494 | ||
376 | tr->blkshift = ffs(tr->blksize) - 1; | 495 | tr->blkshift = ffs(tr->blksize) - 1; |
377 | 496 | ||
378 | tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr, | ||
379 | "%sd", tr->name); | ||
380 | if (IS_ERR(tr->blkcore_priv->thread)) { | ||
381 | ret = PTR_ERR(tr->blkcore_priv->thread); | ||
382 | blk_cleanup_queue(tr->blkcore_priv->rq); | ||
383 | unregister_blkdev(tr->major, tr->name); | ||
384 | kfree(tr->blkcore_priv); | ||
385 | mutex_unlock(&mtd_table_mutex); | ||
386 | return ret; | ||
387 | } | ||
388 | |||
389 | INIT_LIST_HEAD(&tr->devs); | 497 | INIT_LIST_HEAD(&tr->devs); |
390 | list_add(&tr->list, &blktrans_majors); | 498 | list_add(&tr->list, &blktrans_majors); |
391 | 499 | ||
392 | for (i=0; i<MAX_MTD_DEVICES; i++) { | 500 | mtd_for_each_device(mtd) |
393 | if (mtd_table[i] && mtd_table[i]->type != MTD_ABSENT) | 501 | if (mtd->type != MTD_ABSENT) |
394 | tr->add_mtd(tr, mtd_table[i]); | 502 | tr->add_mtd(tr, mtd); |
395 | } | ||
396 | 503 | ||
397 | mutex_unlock(&mtd_table_mutex); | 504 | mutex_unlock(&mtd_table_mutex); |
398 | |||
399 | return 0; | 505 | return 0; |
400 | } | 506 | } |
401 | 507 | ||
@@ -405,22 +511,15 @@ int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr) | |||
405 | 511 | ||
406 | mutex_lock(&mtd_table_mutex); | 512 | mutex_lock(&mtd_table_mutex); |
407 | 513 | ||
408 | /* Clean up the kernel thread */ | ||
409 | kthread_stop(tr->blkcore_priv->thread); | ||
410 | |||
411 | /* Remove it from the list of active majors */ | 514 | /* Remove it from the list of active majors */ |
412 | list_del(&tr->list); | 515 | list_del(&tr->list); |
413 | 516 | ||
414 | list_for_each_entry_safe(dev, next, &tr->devs, list) | 517 | list_for_each_entry_safe(dev, next, &tr->devs, list) |
415 | tr->remove_dev(dev); | 518 | tr->remove_dev(dev); |
416 | 519 | ||
417 | blk_cleanup_queue(tr->blkcore_priv->rq); | ||
418 | unregister_blkdev(tr->major, tr->name); | 520 | unregister_blkdev(tr->major, tr->name); |
419 | |||
420 | mutex_unlock(&mtd_table_mutex); | 521 | mutex_unlock(&mtd_table_mutex); |
421 | 522 | ||
422 | kfree(tr->blkcore_priv); | ||
423 | |||
424 | BUG_ON(!list_empty(&tr->devs)); | 523 | BUG_ON(!list_empty(&tr->devs)); |
425 | return 0; | 524 | return 0; |
426 | } | 525 | } |