]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - drivers/mtd/mtd_blkdevs.c
mtd: blktrans: remove mtd_blkcore_priv, switch to per device queue and thread
[mirror_ubuntu-bionic-kernel.git] / drivers / mtd / mtd_blkdevs.c
CommitLineData
1da177e4 1/*
1da177e4
LT
2 * (C) 2003 David Woodhouse <dwmw2@infradead.org>
3 *
4 * Interface to Linux 2.5 block layer for MTD 'translation layers'.
5 *
6 */
7
8#include <linux/kernel.h>
9#include <linux/slab.h>
10#include <linux/module.h>
11#include <linux/list.h>
12#include <linux/fs.h>
13#include <linux/mtd/blktrans.h>
14#include <linux/mtd/mtd.h>
15#include <linux/blkdev.h>
16#include <linux/blkpg.h>
17#include <linux/spinlock.h>
18#include <linux/hdreg.h>
19#include <linux/init.h>
48b19268 20#include <linux/mutex.h>
99f9b243 21#include <linux/kthread.h>
1da177e4 22#include <asm/uaccess.h>
1da177e4 23
356d70f1 24#include "mtdcore.h"
1da177e4 25
356d70f1 26static LIST_HEAD(blktrans_majors);
1da177e4 27
1da177e4
LT
28
29static int do_blktrans_request(struct mtd_blktrans_ops *tr,
30 struct mtd_blktrans_dev *dev,
31 struct request *req)
32{
33 unsigned long block, nsect;
34 char *buf;
35
83096ebf 36 block = blk_rq_pos(req) << 9 >> tr->blkshift;
1011c1b9 37 nsect = blk_rq_cur_bytes(req) >> tr->blkshift;
19187672 38
1da177e4
LT
39 buf = req->buffer;
40
4aff5e23 41 if (!blk_fs_request(req))
f06d9a2b 42 return -EIO;
1da177e4 43
83096ebf
TH
44 if (blk_rq_pos(req) + blk_rq_cur_sectors(req) >
45 get_capacity(req->rq_disk))
f06d9a2b 46 return -EIO;
1da177e4 47
1122a26f
CH
48 if (blk_discard_rq(req))
49 return tr->discard(dev, block, nsect);
50
1da177e4
LT
51 switch(rq_data_dir(req)) {
52 case READ:
19187672 53 for (; nsect > 0; nsect--, block++, buf += tr->blksize)
1da177e4 54 if (tr->readsect(dev, block, buf))
f06d9a2b 55 return -EIO;
2d4dc890 56 rq_flush_dcache_pages(req);
f06d9a2b 57 return 0;
1da177e4
LT
58 case WRITE:
59 if (!tr->writesect)
f06d9a2b 60 return -EIO;
1da177e4 61
2d4dc890 62 rq_flush_dcache_pages(req);
19187672 63 for (; nsect > 0; nsect--, block++, buf += tr->blksize)
1da177e4 64 if (tr->writesect(dev, block, buf))
f06d9a2b
TH
65 return -EIO;
66 return 0;
1da177e4 67 default:
9a292308 68 printk(KERN_NOTICE "Unknown request %u\n", rq_data_dir(req));
f06d9a2b 69 return -EIO;
1da177e4
LT
70 }
71}
72
73static int mtd_blktrans_thread(void *arg)
74{
a8638622
ML
75 struct mtd_blktrans_dev *dev = arg;
76 struct request_queue *rq = dev->rq;
1498ada7 77 struct request *req = NULL;
1da177e4 78
1da177e4 79 spin_lock_irq(rq->queue_lock);
1498ada7 80
3e67fe45 81 while (!kthread_should_stop()) {
f06d9a2b 82 int res;
1da177e4 83
9934c8c0 84 if (!req && !(req = blk_fetch_request(rq))) {
1da177e4 85 set_current_state(TASK_INTERRUPTIBLE);
1da177e4 86 spin_unlock_irq(rq->queue_lock);
1da177e4 87 schedule();
1da177e4 88 spin_lock_irq(rq->queue_lock);
1da177e4
LT
89 continue;
90 }
91
1da177e4
LT
92 spin_unlock_irq(rq->queue_lock);
93
48b19268 94 mutex_lock(&dev->lock);
a8638622 95 res = do_blktrans_request(dev->tr, dev, req);
48b19268 96 mutex_unlock(&dev->lock);
1da177e4
LT
97
98 spin_lock_irq(rq->queue_lock);
99
1498ada7
TH
100 if (!__blk_end_request_cur(req, res))
101 req = NULL;
1da177e4 102 }
1498ada7
TH
103
104 if (req)
105 __blk_end_request_all(req, -EIO);
106
1da177e4
LT
107 spin_unlock_irq(rq->queue_lock);
108
3e67fe45 109 return 0;
1da177e4
LT
110}
111
112static void mtd_blktrans_request(struct request_queue *rq)
113{
a8638622
ML
114 struct mtd_blktrans_dev *dev = rq->queuedata;
115 wake_up_process(dev->thread);
1da177e4
LT
116}
117
118
af0e2a0a 119static int blktrans_open(struct block_device *bdev, fmode_t mode)
1da177e4 120{
af0e2a0a
AV
121 struct mtd_blktrans_dev *dev = bdev->bd_disk->private_data;
122 struct mtd_blktrans_ops *tr = dev->tr;
1da177e4
LT
123 int ret = -ENODEV;
124
8022c13c 125 if (!get_mtd_device(NULL, dev->mtd->index))
1da177e4
LT
126 goto out;
127
128 if (!try_module_get(tr->owner))
129 goto out_tr;
130
97894cda 131 /* FIXME: Locking. A hot pluggable device can go away
1da177e4
LT
132 (del_mtd_device can be called for it) without its module
133 being unloaded. */
134 dev->mtd->usecount++;
135
136 ret = 0;
137 if (tr->open && (ret = tr->open(dev))) {
138 dev->mtd->usecount--;
8022c13c 139 put_mtd_device(dev->mtd);
1da177e4
LT
140 out_tr:
141 module_put(tr->owner);
142 }
143 out:
144 return ret;
145}
146
af0e2a0a 147static int blktrans_release(struct gendisk *disk, fmode_t mode)
1da177e4 148{
af0e2a0a
AV
149 struct mtd_blktrans_dev *dev = disk->private_data;
150 struct mtd_blktrans_ops *tr = dev->tr;
1da177e4
LT
151 int ret = 0;
152
1da177e4
LT
153 if (tr->release)
154 ret = tr->release(dev);
155
156 if (!ret) {
157 dev->mtd->usecount--;
8022c13c 158 put_mtd_device(dev->mtd);
1da177e4
LT
159 module_put(tr->owner);
160 }
161
162 return ret;
163}
164
a885c8c4
CH
165static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo)
166{
167 struct mtd_blktrans_dev *dev = bdev->bd_disk->private_data;
168
169 if (dev->tr->getgeo)
170 return dev->tr->getgeo(dev, geo);
171 return -ENOTTY;
172}
1da177e4 173
af0e2a0a 174static int blktrans_ioctl(struct block_device *bdev, fmode_t mode,
1da177e4
LT
175 unsigned int cmd, unsigned long arg)
176{
af0e2a0a 177 struct mtd_blktrans_dev *dev = bdev->bd_disk->private_data;
1da177e4
LT
178 struct mtd_blktrans_ops *tr = dev->tr;
179
180 switch (cmd) {
181 case BLKFLSBUF:
182 if (tr->flush)
183 return tr->flush(dev);
184 /* The core code did the work, we had nothing to do. */
185 return 0;
1da177e4
LT
186 default:
187 return -ENOTTY;
188 }
189}
190
83d5cde4 191static const struct block_device_operations mtd_blktrans_ops = {
1da177e4 192 .owner = THIS_MODULE,
af0e2a0a
AV
193 .open = blktrans_open,
194 .release = blktrans_release,
195 .locked_ioctl = blktrans_ioctl,
a885c8c4 196 .getgeo = blktrans_getgeo,
1da177e4
LT
197};
198
199int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
200{
201 struct mtd_blktrans_ops *tr = new->tr;
71a928c0 202 struct mtd_blktrans_dev *d;
1da177e4
LT
203 int last_devnum = -1;
204 struct gendisk *gd;
a8638622 205 int ret;
1da177e4 206
b3561ea9 207 if (mutex_trylock(&mtd_table_mutex)) {
48b19268 208 mutex_unlock(&mtd_table_mutex);
1da177e4
LT
209 BUG();
210 }
211
71a928c0 212 list_for_each_entry(d, &tr->devs, list) {
1da177e4
LT
213 if (new->devnum == -1) {
214 /* Use first free number */
215 if (d->devnum != last_devnum+1) {
216 /* Found a free devnum. Plug it in here */
217 new->devnum = last_devnum+1;
218 list_add_tail(&new->list, &d->list);
219 goto added;
220 }
221 } else if (d->devnum == new->devnum) {
222 /* Required number taken */
223 return -EBUSY;
224 } else if (d->devnum > new->devnum) {
225 /* Required number was free */
226 list_add_tail(&new->list, &d->list);
227 goto added;
97894cda 228 }
1da177e4
LT
229 last_devnum = d->devnum;
230 }
a8638622
ML
231
232 ret = -EBUSY;
1da177e4
LT
233 if (new->devnum == -1)
234 new->devnum = last_devnum+1;
235
4d3a8534
BH
236 /* Check that the device and any partitions will get valid
237 * minor numbers and that the disk naming code below can cope
238 * with this number. */
239 if (new->devnum > (MINORMASK >> tr->part_bits) ||
240 (tr->part_bits && new->devnum >= 27 * 26))
a8638622 241 goto error1;
1da177e4 242
1da177e4
LT
243 list_add_tail(&new->list, &tr->devs);
244 added:
ce37ab42 245 mutex_init(&new->lock);
1da177e4
LT
246 if (!tr->writesect)
247 new->readonly = 1;
248
a8638622
ML
249
250 /* Create gendisk */
251 ret = -ENOMEM;
1da177e4 252 gd = alloc_disk(1 << tr->part_bits);
a8638622
ML
253
254 if (!gd)
255 goto error2;
256
257 new->disk = gd;
258 gd->private_data = new;
1da177e4
LT
259 gd->major = tr->major;
260 gd->first_minor = (new->devnum) << tr->part_bits;
261 gd->fops = &mtd_blktrans_ops;
97894cda 262
65a8de36
TP
263 if (tr->part_bits)
264 if (new->devnum < 26)
265 snprintf(gd->disk_name, sizeof(gd->disk_name),
266 "%s%c", tr->name, 'a' + new->devnum);
267 else
268 snprintf(gd->disk_name, sizeof(gd->disk_name),
269 "%s%c%c", tr->name,
270 'a' - 1 + new->devnum / 26,
271 'a' + new->devnum % 26);
272 else
273 snprintf(gd->disk_name, sizeof(gd->disk_name),
274 "%s%d", tr->name, new->devnum);
1da177e4 275
19187672 276 set_capacity(gd, (new->size * tr->blksize) >> 9);
1da177e4 277
a8638622
ML
278
279 /* Create the request queue */
280 spin_lock_init(&new->queue_lock);
281 new->rq = blk_init_queue(mtd_blktrans_request, &new->queue_lock);
282
283 if (!new->rq)
284 goto error3;
285
286 new->rq->queuedata = new;
287 blk_queue_logical_block_size(new->rq, tr->blksize);
288
289 if (tr->discard)
290 queue_flag_set_unlocked(QUEUE_FLAG_DISCARD,
291 new->rq);
292
293 gd->queue = new->rq;
294
295 /* Create processing thread */
296 /* TODO: workqueue ? */
297 new->thread = kthread_run(mtd_blktrans_thread, new,
298 "%s%d", tr->name, new->mtd->index);
299 if (IS_ERR(new->thread)) {
300 ret = PTR_ERR(new->thread);
301 goto error4;
302 }
d694846b 303 gd->driverfs_dev = &new->mtd->dev;
1da177e4
LT
304
305 if (new->readonly)
306 set_disk_ro(gd, 1);
307
308 add_disk(gd);
1da177e4 309 return 0;
a8638622
ML
310error4:
311 blk_cleanup_queue(new->rq);
312error3:
313 put_disk(new->disk);
314error2:
315 list_del(&new->list);
316error1:
317 kfree(new);
318 return ret;
1da177e4
LT
319}
320
321int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
322{
b3561ea9 323 if (mutex_trylock(&mtd_table_mutex)) {
48b19268 324 mutex_unlock(&mtd_table_mutex);
1da177e4
LT
325 BUG();
326 }
327
328 list_del(&old->list);
329
a8638622
ML
330 /* stop new requests to arrive */
331 del_gendisk(old->disk);
97894cda 332
a8638622
ML
333 /* Stop the thread */
334 kthread_stop(old->thread);
335
336 blk_cleanup_queue(old->rq);
1da177e4
LT
337 return 0;
338}
339
340static void blktrans_notify_remove(struct mtd_info *mtd)
341{
71a928c0
CM
342 struct mtd_blktrans_ops *tr;
343 struct mtd_blktrans_dev *dev, *next;
1da177e4 344
71a928c0
CM
345 list_for_each_entry(tr, &blktrans_majors, list)
346 list_for_each_entry_safe(dev, next, &tr->devs, list)
1da177e4
LT
347 if (dev->mtd == mtd)
348 tr->remove_dev(dev);
1da177e4
LT
349}
350
351static void blktrans_notify_add(struct mtd_info *mtd)
352{
71a928c0 353 struct mtd_blktrans_ops *tr;
1da177e4
LT
354
355 if (mtd->type == MTD_ABSENT)
356 return;
357
71a928c0 358 list_for_each_entry(tr, &blktrans_majors, list)
1da177e4 359 tr->add_mtd(tr, mtd);
1da177e4
LT
360}
361
362static struct mtd_notifier blktrans_notifier = {
363 .add = blktrans_notify_add,
364 .remove = blktrans_notify_remove,
365};
97894cda 366
1da177e4
LT
367int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
368{
f1332ba2
BH
369 struct mtd_info *mtd;
370 int ret;
1da177e4 371
97894cda 372 /* Register the notifier if/when the first device type is
1da177e4
LT
373 registered, to prevent the link/init ordering from fucking
374 us over. */
375 if (!blktrans_notifier.list.next)
376 register_mtd_user(&blktrans_notifier);
377
1da177e4 378
48b19268 379 mutex_lock(&mtd_table_mutex);
1da177e4
LT
380
381 ret = register_blkdev(tr->major, tr->name);
382 if (ret) {
383 printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n",
384 tr->name, tr->major, ret);
48b19268 385 mutex_unlock(&mtd_table_mutex);
1da177e4
LT
386 return ret;
387 }
eae9acd1 388
19187672 389 tr->blkshift = ffs(tr->blksize) - 1;
1da177e4 390
1da177e4
LT
391 INIT_LIST_HEAD(&tr->devs);
392 list_add(&tr->list, &blktrans_majors);
393
f1332ba2
BH
394 mtd_for_each_device(mtd)
395 if (mtd->type != MTD_ABSENT)
396 tr->add_mtd(tr, mtd);
1da177e4 397
48b19268 398 mutex_unlock(&mtd_table_mutex);
1da177e4
LT
399
400 return 0;
401}
402
403int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr)
404{
71a928c0 405 struct mtd_blktrans_dev *dev, *next;
1da177e4 406
48b19268 407 mutex_lock(&mtd_table_mutex);
1da177e4 408
1da177e4
LT
409
410 /* Remove it from the list of active majors */
411 list_del(&tr->list);
412
71a928c0 413 list_for_each_entry_safe(dev, next, &tr->devs, list)
1da177e4 414 tr->remove_dev(dev);
1da177e4 415
1da177e4 416 unregister_blkdev(tr->major, tr->name);
48b19268 417 mutex_unlock(&mtd_table_mutex);
1da177e4 418
373ebfbf 419 BUG_ON(!list_empty(&tr->devs));
1da177e4
LT
420 return 0;
421}
422
423static void __exit mtd_blktrans_exit(void)
424{
425 /* No race here -- if someone's currently in register_mtd_blktrans
426 we're screwed anyway. */
427 if (blktrans_notifier.list.next)
428 unregister_mtd_user(&blktrans_notifier);
429}
430
431module_exit(mtd_blktrans_exit);
432
433EXPORT_SYMBOL_GPL(register_mtd_blktrans);
434EXPORT_SYMBOL_GPL(deregister_mtd_blktrans);
435EXPORT_SYMBOL_GPL(add_mtd_blktrans_dev);
436EXPORT_SYMBOL_GPL(del_mtd_blktrans_dev);
437
438MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
439MODULE_LICENSE("GPL");
440MODULE_DESCRIPTION("Common interface to block layer for MTD 'translation layers'");