mtd_blkdevs.c 11.4 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 * (C) 2003 David Woodhouse <dwmw2@infradead.org>
 *
 * Interface to Linux 2.5 block layer for MTD 'translation layers'.
 *
 */

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/fs.h>
#include <linux/mtd/blktrans.h>
#include <linux/mtd/mtd.h>
#include <linux/blkdev.h>
#include <linux/blkpg.h>
#include <linux/spinlock.h>
#include <linux/hdreg.h>
#include <linux/init.h>
I
Ingo Molnar 已提交
20
#include <linux/mutex.h>
21
#include <linux/kthread.h>
L
Linus Torvalds 已提交
22 23
#include <asm/uaccess.h>

24
#include "mtdcore.h"
L
Linus Torvalds 已提交
25

26
static LIST_HEAD(blktrans_majors);
M
Maxim Levitsky 已提交
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
static DEFINE_MUTEX(blktrans_ref_mutex);

void blktrans_dev_release(struct kref *kref)
{
	struct mtd_blktrans_dev *dev =
		container_of(kref, struct mtd_blktrans_dev, ref);

	dev->disk->private_data = NULL;
	put_disk(dev->disk);
	list_del(&dev->list);
	kfree(dev);
}

static struct mtd_blktrans_dev *blktrans_dev_get(struct gendisk *disk)
{
	struct mtd_blktrans_dev *dev;

	mutex_lock(&blktrans_ref_mutex);
	dev = disk->private_data;

	if (!dev)
		goto unlock;
	kref_get(&dev->ref);
unlock:
	mutex_unlock(&blktrans_ref_mutex);
	return dev;
}

void blktrans_dev_put(struct mtd_blktrans_dev *dev)
{
	mutex_lock(&blktrans_ref_mutex);
	kref_put(&dev->ref, blktrans_dev_release);
	mutex_unlock(&blktrans_ref_mutex);
}
L
Linus Torvalds 已提交
61 62 63 64 65 66 67 68 69


static int do_blktrans_request(struct mtd_blktrans_ops *tr,
			       struct mtd_blktrans_dev *dev,
			       struct request *req)
{
	unsigned long block, nsect;
	char *buf;

70
	block = blk_rq_pos(req) << 9 >> tr->blkshift;
71
	nsect = blk_rq_cur_bytes(req) >> tr->blkshift;
72

L
Linus Torvalds 已提交
73 74
	buf = req->buffer;

75
	if (!blk_fs_request(req))
76
		return -EIO;
L
Linus Torvalds 已提交
77

78 79
	if (blk_rq_pos(req) + blk_rq_cur_sectors(req) >
	    get_capacity(req->rq_disk))
80
		return -EIO;
L
Linus Torvalds 已提交
81

82 83 84
	if (blk_discard_rq(req))
		return tr->discard(dev, block, nsect);

L
Linus Torvalds 已提交
85 86
	switch(rq_data_dir(req)) {
	case READ:
87
		for (; nsect > 0; nsect--, block++, buf += tr->blksize)
L
Linus Torvalds 已提交
88
			if (tr->readsect(dev, block, buf))
89
				return -EIO;
90
		rq_flush_dcache_pages(req);
91
		return 0;
L
Linus Torvalds 已提交
92 93
	case WRITE:
		if (!tr->writesect)
94
			return -EIO;
L
Linus Torvalds 已提交
95

96
		rq_flush_dcache_pages(req);
97
		for (; nsect > 0; nsect--, block++, buf += tr->blksize)
L
Linus Torvalds 已提交
98
			if (tr->writesect(dev, block, buf))
99 100
				return -EIO;
		return 0;
L
Linus Torvalds 已提交
101
	default:
J
Jeff Garzik 已提交
102
		printk(KERN_NOTICE "Unknown request %u\n", rq_data_dir(req));
103
		return -EIO;
L
Linus Torvalds 已提交
104 105 106 107 108
	}
}

static int mtd_blktrans_thread(void *arg)
{
109 110
	struct mtd_blktrans_dev *dev = arg;
	struct request_queue *rq = dev->rq;
111
	struct request *req = NULL;
L
Linus Torvalds 已提交
112 113

	spin_lock_irq(rq->queue_lock);
114

115
	while (!kthread_should_stop()) {
116
		int res;
L
Linus Torvalds 已提交
117

118
		if (!req && !(req = blk_fetch_request(rq))) {
L
Linus Torvalds 已提交
119 120 121 122 123 124 125 126 127
			set_current_state(TASK_INTERRUPTIBLE);
			spin_unlock_irq(rq->queue_lock);
			schedule();
			spin_lock_irq(rq->queue_lock);
			continue;
		}

		spin_unlock_irq(rq->queue_lock);

I
Ingo Molnar 已提交
128
		mutex_lock(&dev->lock);
129
		res = do_blktrans_request(dev->tr, dev, req);
I
Ingo Molnar 已提交
130
		mutex_unlock(&dev->lock);
L
Linus Torvalds 已提交
131 132 133

		spin_lock_irq(rq->queue_lock);

134 135
		if (!__blk_end_request_cur(req, res))
			req = NULL;
L
Linus Torvalds 已提交
136
	}
137 138 139 140

	if (req)
		__blk_end_request_all(req, -EIO);

L
Linus Torvalds 已提交
141 142
	spin_unlock_irq(rq->queue_lock);

143
	return 0;
L
Linus Torvalds 已提交
144 145 146 147
}

static void mtd_blktrans_request(struct request_queue *rq)
{
M
Maxim Levitsky 已提交
148 149 150 151
	struct mtd_blktrans_dev *dev;
	struct request *req = NULL;

	dev = rq->queuedata;
L
Linus Torvalds 已提交
152

M
Maxim Levitsky 已提交
153 154 155 156 157 158
	if (!dev)
		while ((req = blk_fetch_request(rq)) != NULL)
			__blk_end_request_all(req, -ENODEV);
	else
		wake_up_process(dev->thread);
}
L
Linus Torvalds 已提交
159

A
Al Viro 已提交
160
static int blktrans_open(struct block_device *bdev, fmode_t mode)
L
Linus Torvalds 已提交
161
{
M
Maxim Levitsky 已提交
162 163 164 165 166 167 168 169 170 171 172
	struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk);
	int ret;

	if (!dev)
		return -ERESTARTSYS;

	mutex_lock(&dev->lock);

	if (!dev->mtd) {
		ret = -ENXIO;
		goto unlock;
L
Linus Torvalds 已提交
173
	}
M
Maxim Levitsky 已提交
174 175 176 177 178 179 180 181 182 183

	ret = !dev->open++ && dev->tr->open ? dev->tr->open(dev) : 0;

	/* Take another reference on the device so it won't go away till
		last release */
	if (!ret)
		kref_get(&dev->ref);
unlock:
	mutex_unlock(&dev->lock);
	blktrans_dev_put(dev);
L
Linus Torvalds 已提交
184 185 186
	return ret;
}

A
Al Viro 已提交
187
static int blktrans_release(struct gendisk *disk, fmode_t mode)
L
Linus Torvalds 已提交
188
{
M
Maxim Levitsky 已提交
189 190
	struct mtd_blktrans_dev *dev = blktrans_dev_get(disk);
	int ret = -ENXIO;
L
Linus Torvalds 已提交
191

M
Maxim Levitsky 已提交
192 193
	if (!dev)
		return ret;
L
Linus Torvalds 已提交
194

M
Maxim Levitsky 已提交
195 196 197 198
	mutex_lock(&dev->lock);

	/* Release one reference, we sure its not the last one here*/
	kref_put(&dev->ref, blktrans_dev_release);
L
Linus Torvalds 已提交
199

M
Maxim Levitsky 已提交
200 201 202 203 204 205 206
	if (!dev->mtd)
		goto unlock;

	ret = !--dev->open && dev->tr->release ? dev->tr->release(dev) : 0;
unlock:
	mutex_unlock(&dev->lock);
	blktrans_dev_put(dev);
L
Linus Torvalds 已提交
207 208 209
	return ret;
}

210 211
static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
M
Maxim Levitsky 已提交
212 213 214 215 216 217 218
	struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk);
	int ret = -ENXIO;

	if (!dev)
		return ret;

	mutex_lock(&dev->lock);
219

M
Maxim Levitsky 已提交
220 221 222 223 224 225 226 227
	if (!dev->mtd)
		goto unlock;

	ret = dev->tr->getgeo ? dev->tr->getgeo(dev, geo) : 0;
unlock:
	mutex_unlock(&dev->lock);
	blktrans_dev_put(dev);
	return ret;
228
}
L
Linus Torvalds 已提交
229

A
Al Viro 已提交
230
static int blktrans_ioctl(struct block_device *bdev, fmode_t mode,
L
Linus Torvalds 已提交
231 232
			      unsigned int cmd, unsigned long arg)
{
M
Maxim Levitsky 已提交
233 234 235 236 237 238 239 240 241 242
	struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk);
	int ret = -ENXIO;

	if (!dev)
		return ret;

	mutex_lock(&dev->lock);

	if (!dev->mtd)
		goto unlock;
L
Linus Torvalds 已提交
243 244 245

	switch (cmd) {
	case BLKFLSBUF:
M
Maxim Levitsky 已提交
246
		ret = dev->tr->flush ? dev->tr->flush(dev) : 0;
L
Linus Torvalds 已提交
247
	default:
M
Maxim Levitsky 已提交
248
		ret = -ENOTTY;
L
Linus Torvalds 已提交
249
	}
M
Maxim Levitsky 已提交
250 251 252 253
unlock:
	mutex_unlock(&dev->lock);
	blktrans_dev_put(dev);
	return ret;
L
Linus Torvalds 已提交
254 255
}

256
static const struct block_device_operations mtd_blktrans_ops = {
L
Linus Torvalds 已提交
257
	.owner		= THIS_MODULE,
A
Al Viro 已提交
258 259 260
	.open		= blktrans_open,
	.release	= blktrans_release,
	.locked_ioctl	= blktrans_ioctl,
261
	.getgeo		= blktrans_getgeo,
L
Linus Torvalds 已提交
262 263 264 265 266
};

int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
{
	struct mtd_blktrans_ops *tr = new->tr;
267
	struct mtd_blktrans_dev *d;
L
Linus Torvalds 已提交
268 269
	int last_devnum = -1;
	struct gendisk *gd;
270
	int ret;
L
Linus Torvalds 已提交
271

J
Jean Delvare 已提交
272
	if (mutex_trylock(&mtd_table_mutex)) {
I
Ingo Molnar 已提交
273
		mutex_unlock(&mtd_table_mutex);
L
Linus Torvalds 已提交
274 275 276
		BUG();
	}

M
Maxim Levitsky 已提交
277
	mutex_lock(&blktrans_ref_mutex);
278
	list_for_each_entry(d, &tr->devs, list) {
L
Linus Torvalds 已提交
279 280 281 282 283 284 285 286 287 288
		if (new->devnum == -1) {
			/* Use first free number */
			if (d->devnum != last_devnum+1) {
				/* Found a free devnum. Plug it in here */
				new->devnum = last_devnum+1;
				list_add_tail(&new->list, &d->list);
				goto added;
			}
		} else if (d->devnum == new->devnum) {
			/* Required number taken */
M
Maxim Levitsky 已提交
289
			mutex_unlock(&blktrans_ref_mutex);
L
Linus Torvalds 已提交
290 291 292 293 294
			return -EBUSY;
		} else if (d->devnum > new->devnum) {
			/* Required number was free */
			list_add_tail(&new->list, &d->list);
			goto added;
295
		}
L
Linus Torvalds 已提交
296 297
		last_devnum = d->devnum;
	}
298 299

	ret = -EBUSY;
L
Linus Torvalds 已提交
300 301 302
	if (new->devnum == -1)
		new->devnum = last_devnum+1;

303 304 305 306
	/* Check that the device and any partitions will get valid
	 * minor numbers and that the disk naming code below can cope
	 * with this number. */
	if (new->devnum > (MINORMASK >> tr->part_bits) ||
M
Maxim Levitsky 已提交
307 308
	    (tr->part_bits && new->devnum >= 27 * 26)) {
		mutex_unlock(&blktrans_ref_mutex);
309
		goto error1;
M
Maxim Levitsky 已提交
310
	}
L
Linus Torvalds 已提交
311 312 313

	list_add_tail(&new->list, &tr->devs);
 added:
M
Maxim Levitsky 已提交
314 315
	mutex_unlock(&blktrans_ref_mutex);

316
	mutex_init(&new->lock);
M
Maxim Levitsky 已提交
317
	kref_init(&new->ref);
L
Linus Torvalds 已提交
318 319 320
	if (!tr->writesect)
		new->readonly = 1;

321 322
	/* Create gendisk */
	ret = -ENOMEM;
L
Linus Torvalds 已提交
323
	gd = alloc_disk(1 << tr->part_bits);
324 325 326 327 328 329

	if (!gd)
		goto error2;

	new->disk = gd;
	gd->private_data = new;
L
Linus Torvalds 已提交
330 331 332
	gd->major = tr->major;
	gd->first_minor = (new->devnum) << tr->part_bits;
	gd->fops = &mtd_blktrans_ops;
333

334 335 336 337 338 339 340 341 342 343 344 345
	if (tr->part_bits)
		if (new->devnum < 26)
			snprintf(gd->disk_name, sizeof(gd->disk_name),
				 "%s%c", tr->name, 'a' + new->devnum);
		else
			snprintf(gd->disk_name, sizeof(gd->disk_name),
				 "%s%c%c", tr->name,
				 'a' - 1 + new->devnum / 26,
				 'a' + new->devnum % 26);
	else
		snprintf(gd->disk_name, sizeof(gd->disk_name),
			 "%s%d", tr->name, new->devnum);
L
Linus Torvalds 已提交
346

347
	set_capacity(gd, (new->size * tr->blksize) >> 9);
L
Linus Torvalds 已提交
348

349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
	/* Create the request queue */
	spin_lock_init(&new->queue_lock);
	new->rq = blk_init_queue(mtd_blktrans_request, &new->queue_lock);

	if (!new->rq)
		goto error3;

	new->rq->queuedata = new;
	blk_queue_logical_block_size(new->rq, tr->blksize);

	if (tr->discard)
		queue_flag_set_unlocked(QUEUE_FLAG_DISCARD,
					new->rq);

	gd->queue = new->rq;

M
Maxim Levitsky 已提交
365 366 367
	__get_mtd_device(new->mtd);
	__module_get(tr->owner);

368 369 370 371 372 373 374 375
	/* Create processing thread */
	/* TODO: workqueue ? */
	new->thread = kthread_run(mtd_blktrans_thread, new,
			"%s%d", tr->name, new->mtd->index);
	if (IS_ERR(new->thread)) {
		ret = PTR_ERR(new->thread);
		goto error4;
	}
376
	gd->driverfs_dev = &new->mtd->dev;
L
Linus Torvalds 已提交
377 378 379 380 381 382

	if (new->readonly)
		set_disk_ro(gd, 1);

	add_disk(gd);
	return 0;
383
error4:
M
Maxim Levitsky 已提交
384 385
	module_put(tr->owner);
	__put_mtd_device(new->mtd);
386 387 388 389 390 391 392 393
	blk_cleanup_queue(new->rq);
error3:
	put_disk(new->disk);
error2:
	list_del(&new->list);
error1:
	kfree(new);
	return ret;
L
Linus Torvalds 已提交
394 395 396 397
}

int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
{
M
Maxim Levitsky 已提交
398 399
	unsigned long flags;

J
Jean Delvare 已提交
400
	if (mutex_trylock(&mtd_table_mutex)) {
I
Ingo Molnar 已提交
401
		mutex_unlock(&mtd_table_mutex);
L
Linus Torvalds 已提交
402 403 404
		BUG();
	}

M
Maxim Levitsky 已提交
405
	/* Stop new requests to arrive */
406
	del_gendisk(old->disk);
407

408 409 410
	/* Stop the thread */
	kthread_stop(old->thread);

M
Maxim Levitsky 已提交
411 412 413 414 415
	/* Kill current requests */
	spin_lock_irqsave(&old->queue_lock, flags);
	old->rq->queuedata = NULL;
	blk_start_queue(old->rq);
	spin_unlock_irqrestore(&old->queue_lock, flags);
416
	blk_cleanup_queue(old->rq);
M
Maxim Levitsky 已提交
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432

	/* Ask trans driver for release to the mtd device */
	mutex_lock(&old->lock);
	if (old->open && old->tr->release) {
		old->tr->release(old);
		old->open = 0;
	}

	__put_mtd_device(old->mtd);
	module_put(old->tr->owner);

	/* At that point, we don't touch the mtd anymore */
	old->mtd = NULL;

	mutex_unlock(&old->lock);
	blktrans_dev_put(old);
L
Linus Torvalds 已提交
433 434 435 436 437
	return 0;
}

static void blktrans_notify_remove(struct mtd_info *mtd)
{
438 439
	struct mtd_blktrans_ops *tr;
	struct mtd_blktrans_dev *dev, *next;
L
Linus Torvalds 已提交
440

441 442
	list_for_each_entry(tr, &blktrans_majors, list)
		list_for_each_entry_safe(dev, next, &tr->devs, list)
L
Linus Torvalds 已提交
443 444 445 446 447 448
			if (dev->mtd == mtd)
				tr->remove_dev(dev);
}

static void blktrans_notify_add(struct mtd_info *mtd)
{
449
	struct mtd_blktrans_ops *tr;
L
Linus Torvalds 已提交
450 451 452 453

	if (mtd->type == MTD_ABSENT)
		return;

454
	list_for_each_entry(tr, &blktrans_majors, list)
L
Linus Torvalds 已提交
455 456 457 458 459 460 461
		tr->add_mtd(tr, mtd);
}

static struct mtd_notifier blktrans_notifier = {
	.add = blktrans_notify_add,
	.remove = blktrans_notify_remove,
};
462

L
Linus Torvalds 已提交
463 464
int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
{
465 466
	struct mtd_info *mtd;
	int ret;
L
Linus Torvalds 已提交
467

468
	/* Register the notifier if/when the first device type is
L
Linus Torvalds 已提交
469 470 471 472 473 474
	   registered, to prevent the link/init ordering from fucking
	   us over. */
	if (!blktrans_notifier.list.next)
		register_mtd_user(&blktrans_notifier);


I
Ingo Molnar 已提交
475
	mutex_lock(&mtd_table_mutex);
L
Linus Torvalds 已提交
476 477 478 479 480

	ret = register_blkdev(tr->major, tr->name);
	if (ret) {
		printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n",
		       tr->name, tr->major, ret);
I
Ingo Molnar 已提交
481
		mutex_unlock(&mtd_table_mutex);
L
Linus Torvalds 已提交
482 483
		return ret;
	}
484

485
	tr->blkshift = ffs(tr->blksize) - 1;
L
Linus Torvalds 已提交
486 487 488 489

	INIT_LIST_HEAD(&tr->devs);
	list_add(&tr->list, &blktrans_majors);

490 491 492
	mtd_for_each_device(mtd)
		if (mtd->type != MTD_ABSENT)
			tr->add_mtd(tr, mtd);
L
Linus Torvalds 已提交
493

I
Ingo Molnar 已提交
494
	mutex_unlock(&mtd_table_mutex);
L
Linus Torvalds 已提交
495 496 497 498 499
	return 0;
}

int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr)
{
500
	struct mtd_blktrans_dev *dev, *next;
L
Linus Torvalds 已提交
501

I
Ingo Molnar 已提交
502
	mutex_lock(&mtd_table_mutex);
L
Linus Torvalds 已提交
503 504 505 506

	/* Remove it from the list of active majors */
	list_del(&tr->list);

507
	list_for_each_entry_safe(dev, next, &tr->devs, list)
L
Linus Torvalds 已提交
508 509 510
		tr->remove_dev(dev);

	unregister_blkdev(tr->major, tr->name);
I
Ingo Molnar 已提交
511
	mutex_unlock(&mtd_table_mutex);
L
Linus Torvalds 已提交
512

513
	BUG_ON(!list_empty(&tr->devs));
L
Linus Torvalds 已提交
514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534
	return 0;
}

static void __exit mtd_blktrans_exit(void)
{
	/* No race here -- if someone's currently in register_mtd_blktrans
	   we're screwed anyway. */
	if (blktrans_notifier.list.next)
		unregister_mtd_user(&blktrans_notifier);
}

module_exit(mtd_blktrans_exit);

EXPORT_SYMBOL_GPL(register_mtd_blktrans);
EXPORT_SYMBOL_GPL(deregister_mtd_blktrans);
EXPORT_SYMBOL_GPL(add_mtd_blktrans_dev);
EXPORT_SYMBOL_GPL(del_mtd_blktrans_dev);

MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Common interface to block layer for MTD 'translation layers'");