aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/mtd_blkdevs.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/mtd_blkdevs.c')
-rw-r--r--drivers/mtd/mtd_blkdevs.c335
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
27static LIST_HEAD(blktrans_majors); 26static LIST_HEAD(blktrans_majors);
27static DEFINE_MUTEX(blktrans_ref_mutex);
28
29void 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
41static 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);
51unlock:
52 mutex_unlock(&blktrans_ref_mutex);
53 return dev;
54}
55
56void 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
29struct mtd_blkcore_priv {
30 struct task_struct *thread;
31 struct request_queue *rq;
32 spinlock_t queue_lock;
33};
34 63
35static int do_blktrans_request(struct mtd_blktrans_ops *tr, 64static 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
81static int mtd_blktrans_thread(void *arg) 108static 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
124static void mtd_blktrans_request(struct request_queue *rq) 147static 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
131static int blktrans_open(struct block_device *bdev, fmode_t mode) 161static 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);
182unlock:
183 mutex_unlock(&dev->lock);
184 blktrans_dev_put(dev);
156 return ret; 185 return ret;
157} 186}
158 187
159static int blktrans_release(struct gendisk *disk, fmode_t mode) 188static 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;
205unlock:
206 mutex_unlock(&dev->lock);
207 blktrans_dev_put(dev);
174 return ret; 208 return ret;
175} 209}
176 210
177static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo) 211static 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); 225unlock:
183 return -ENOTTY; 226 mutex_unlock(&dev->lock);
227 blktrans_dev_put(dev);
228 return ret;
184} 229}
185 230
186static int blktrans_ioctl(struct block_device *bdev, fmode_t mode, 231static 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 }
251unlock:
252 mutex_unlock(&dev->lock);
253 blktrans_dev_put(dev);
254 return ret;
201} 255}
202 256
203static const struct block_device_operations mtd_blktrans_ops = { 257static 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;
390error4:
391 module_put(tr->owner);
392 __put_mtd_device(new->mtd);
393 blk_cleanup_queue(new->rq);
394error3:
395 put_disk(new->disk);
396error2:
397 list_del(&new->list);
398error1:
399 kfree(new);
400 return ret;
292} 401}
293 402
294int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) 403int 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
336int register_mtd_blktrans(struct mtd_blktrans_ops *tr) 473int 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}