Blame view

drivers/mtd/mtd_blkdevs.c 10.2 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
97894cda5   Thomas Gleixner   [MTD] core: Clean...
2
   * $Id: mtd_blkdevs.c,v 1.27 2005/11/07 11:14:20 gleixner Exp $
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
   *
   * (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>
831441862   Rafael J. Wysocki   Freezer: make ker...
19
  #include <linux/freezer.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
20
21
22
  #include <linux/spinlock.h>
  #include <linux/hdreg.h>
  #include <linux/init.h>
48b192686   Ingo Molnar   [PATCH] sem2mutex...
23
  #include <linux/mutex.h>
99f9b2431   Eric W. Biederman   [MTD] mtd_blkdevs...
24
  #include <linux/kthread.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
  #include <asm/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26

356d70f19   Ben Dooks   [MTD] mtdcore.c: ...
27
  #include "mtdcore.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28

356d70f19   Ben Dooks   [MTD] mtdcore.c: ...
29
  static LIST_HEAD(blktrans_majors);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
  
  struct mtd_blkcore_priv {
3e67fe454   Christoph Hellwig   [MTD] Finish conv...
32
  	struct task_struct *thread;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
34
35
36
37
38
39
40
41
42
  	struct request_queue *rq;
  	spinlock_t queue_lock;
  };
  
  static int do_blktrans_request(struct mtd_blktrans_ops *tr,
  			       struct mtd_blktrans_dev *dev,
  			       struct request *req)
  {
  	unsigned long block, nsect;
  	char *buf;
191876729   Richard Purdie   [MTD] Allow varia...
43
44
  	block = req->sector << 9 >> tr->blkshift;
  	nsect = req->current_nr_sectors << 9 >> tr->blkshift;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
  	buf = req->buffer;
4aff5e233   Jens Axboe   [PATCH] Split str...
46
  	if (!blk_fs_request(req))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
  		return 0;
191876729   Richard Purdie   [MTD] Allow varia...
48
  	if (req->sector + req->current_nr_sectors > get_capacity(req->rq_disk))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
50
51
52
  		return 0;
  
  	switch(rq_data_dir(req)) {
  	case READ:
191876729   Richard Purdie   [MTD] Allow varia...
53
  		for (; nsect > 0; nsect--, block++, buf += tr->blksize)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54
55
56
57
58
59
60
  			if (tr->readsect(dev, block, buf))
  				return 0;
  		return 1;
  
  	case WRITE:
  		if (!tr->writesect)
  			return 0;
191876729   Richard Purdie   [MTD] Allow varia...
61
  		for (; nsect > 0; nsect--, block++, buf += tr->blksize)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
64
65
66
  			if (tr->writesect(dev, block, buf))
  				return 0;
  		return 1;
  
  	default:
9a2923082   Jeff Garzik   [MTD] fix printk ...
67
68
  		printk(KERN_NOTICE "Unknown request %u
  ", rq_data_dir(req));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
70
71
72
73
74
75
76
77
78
  		return 0;
  	}
  }
  
  static int mtd_blktrans_thread(void *arg)
  {
  	struct mtd_blktrans_ops *tr = arg;
  	struct request_queue *rq = tr->blkcore_priv->rq;
  
  	/* we might get involved when memory gets low, so use PF_MEMALLOC */
831441862   Rafael J. Wysocki   Freezer: make ker...
79
  	current->flags |= PF_MEMALLOC;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
  	spin_lock_irq(rq->queue_lock);
3e67fe454   Christoph Hellwig   [MTD] Finish conv...
82
  	while (!kthread_should_stop()) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
84
85
  		struct request *req;
  		struct mtd_blktrans_dev *dev;
  		int res = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
87
88
89
  
  		req = elv_next_request(rq);
  
  		if (!req) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
  			set_current_state(TASK_INTERRUPTIBLE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
  			spin_unlock_irq(rq->queue_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
  			schedule();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
  			spin_lock_irq(rq->queue_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
95
96
97
98
99
100
  			continue;
  		}
  
  		dev = req->rq_disk->private_data;
  		tr = dev->tr;
  
  		spin_unlock_irq(rq->queue_lock);
48b192686   Ingo Molnar   [PATCH] sem2mutex...
101
  		mutex_lock(&dev->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
  		res = do_blktrans_request(tr, dev, req);
48b192686   Ingo Molnar   [PATCH] sem2mutex...
103
  		mutex_unlock(&dev->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104
105
106
107
108
109
  
  		spin_lock_irq(rq->queue_lock);
  
  		end_request(req, res);
  	}
  	spin_unlock_irq(rq->queue_lock);
3e67fe454   Christoph Hellwig   [MTD] Finish conv...
110
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
112
113
114
115
  }
  
  static void mtd_blktrans_request(struct request_queue *rq)
  {
  	struct mtd_blktrans_ops *tr = rq->queuedata;
3e67fe454   Christoph Hellwig   [MTD] Finish conv...
116
  	wake_up_process(tr->blkcore_priv->thread);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
  }
  
  
  static int blktrans_open(struct inode *i, struct file *f)
  {
  	struct mtd_blktrans_dev *dev;
  	struct mtd_blktrans_ops *tr;
  	int ret = -ENODEV;
  
  	dev = i->i_bdev->bd_disk->private_data;
  	tr = dev->tr;
  
  	if (!try_module_get(dev->mtd->owner))
  		goto out;
  
  	if (!try_module_get(tr->owner))
  		goto out_tr;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
134
  	/* FIXME: Locking. A hot pluggable device can go away
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
  	   (del_mtd_device can be called for it) without its module
  	   being unloaded. */
  	dev->mtd->usecount++;
  
  	ret = 0;
  	if (tr->open && (ret = tr->open(dev))) {
  		dev->mtd->usecount--;
  		module_put(dev->mtd->owner);
  	out_tr:
  		module_put(tr->owner);
  	}
   out:
  	return ret;
  }
  
  static int blktrans_release(struct inode *i, struct file *f)
  {
  	struct mtd_blktrans_dev *dev;
  	struct mtd_blktrans_ops *tr;
  	int ret = 0;
  
  	dev = i->i_bdev->bd_disk->private_data;
  	tr = dev->tr;
  
  	if (tr->release)
  		ret = tr->release(dev);
  
  	if (!ret) {
  		dev->mtd->usecount--;
  		module_put(dev->mtd->owner);
  		module_put(tr->owner);
  	}
  
  	return ret;
  }
a885c8c43   Christoph Hellwig   [PATCH] Add block...
170
171
172
173
174
175
176
177
  static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo)
  {
  	struct mtd_blktrans_dev *dev = bdev->bd_disk->private_data;
  
  	if (dev->tr->getgeo)
  		return dev->tr->getgeo(dev, geo);
  	return -ENOTTY;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178

97894cda5   Thomas Gleixner   [MTD] core: Clean...
179
  static int blktrans_ioctl(struct inode *inode, struct file *file,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
181
182
183
184
185
186
187
188
189
190
  			      unsigned int cmd, unsigned long arg)
  {
  	struct mtd_blktrans_dev *dev = inode->i_bdev->bd_disk->private_data;
  	struct mtd_blktrans_ops *tr = dev->tr;
  
  	switch (cmd) {
  	case BLKFLSBUF:
  		if (tr->flush)
  			return tr->flush(dev);
  		/* The core code did the work, we had nothing to do. */
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
192
193
194
  	default:
  		return -ENOTTY;
  	}
  }
8f026f75a   Ben Dooks   [MTD] mtd_blkdevs...
195
  static struct block_device_operations mtd_blktrans_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
197
198
199
  	.owner		= THIS_MODULE,
  	.open		= blktrans_open,
  	.release	= blktrans_release,
  	.ioctl		= blktrans_ioctl,
a885c8c43   Christoph Hellwig   [PATCH] Add block...
200
  	.getgeo		= blktrans_getgeo,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
202
203
204
205
206
207
208
  };
  
  int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
  {
  	struct mtd_blktrans_ops *tr = new->tr;
  	struct list_head *this;
  	int last_devnum = -1;
  	struct gendisk *gd;
b3561ea94   Jean Delvare   Clean up mutex_tr...
209
  	if (mutex_trylock(&mtd_table_mutex)) {
48b192686   Ingo Molnar   [PATCH] sem2mutex...
210
  		mutex_unlock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
  		BUG();
  	}
  
  	list_for_each(this, &tr->devs) {
  		struct mtd_blktrans_dev *d = list_entry(this, struct mtd_blktrans_dev, list);
  		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 */
  			return -EBUSY;
  		} else if (d->devnum > new->devnum) {
  			/* Required number was free */
  			list_add_tail(&new->list, &d->list);
  			goto added;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
231
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
233
234
235
236
237
238
239
  		last_devnum = d->devnum;
  	}
  	if (new->devnum == -1)
  		new->devnum = last_devnum+1;
  
  	if ((new->devnum << tr->part_bits) > 256) {
  		return -EBUSY;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
241
  	list_add_tail(&new->list, &tr->devs);
   added:
ce37ab42a   David Woodhouse   [MTD] Always init...
242
  	mutex_init(&new->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243
244
245
246
247
248
249
250
251
252
253
  	if (!tr->writesect)
  		new->readonly = 1;
  
  	gd = alloc_disk(1 << tr->part_bits);
  	if (!gd) {
  		list_del(&new->list);
  		return -ENOMEM;
  	}
  	gd->major = tr->major;
  	gd->first_minor = (new->devnum) << tr->part_bits;
  	gd->fops = &mtd_blktrans_ops;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
254

65a8de36b   Todd Poynor   [MTD] mtd_blkdevs...
255
256
257
258
259
260
261
262
263
264
265
266
  	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);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267
268
269
  
  	/* 2.5 has capacity in units of 512 bytes while still
  	   having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */
191876729   Richard Purdie   [MTD] Allow varia...
270
  	set_capacity(gd, (new->size * tr->blksize) >> 9);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271
272
273
274
275
276
277
278
279
  
  	gd->private_data = new;
  	new->blkcore_priv = gd;
  	gd->queue = tr->blkcore_priv->rq;
  
  	if (new->readonly)
  		set_disk_ro(gd, 1);
  
  	add_disk(gd);
97894cda5   Thomas Gleixner   [MTD] core: Clean...
280

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
282
283
284
285
  	return 0;
  }
  
  int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
  {
b3561ea94   Jean Delvare   Clean up mutex_tr...
286
  	if (mutex_trylock(&mtd_table_mutex)) {
48b192686   Ingo Molnar   [PATCH] sem2mutex...
287
  		mutex_unlock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288
289
290
291
292
293
294
  		BUG();
  	}
  
  	list_del(&old->list);
  
  	del_gendisk(old->blkcore_priv);
  	put_disk(old->blkcore_priv);
97894cda5   Thomas Gleixner   [MTD] core: Clean...
295

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
  	return 0;
  }
  
  static void blktrans_notify_remove(struct mtd_info *mtd)
  {
  	struct list_head *this, *this2, *next;
  
  	list_for_each(this, &blktrans_majors) {
  		struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
  
  		list_for_each_safe(this2, next, &tr->devs) {
  			struct mtd_blktrans_dev *dev = list_entry(this2, struct mtd_blktrans_dev, list);
  
  			if (dev->mtd == mtd)
  				tr->remove_dev(dev);
  		}
  	}
  }
  
  static void blktrans_notify_add(struct mtd_info *mtd)
  {
  	struct list_head *this;
  
  	if (mtd->type == MTD_ABSENT)
  		return;
  
  	list_for_each(this, &blktrans_majors) {
  		struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
  
  		tr->add_mtd(tr, mtd);
  	}
  
  }
  
  static struct mtd_notifier blktrans_notifier = {
  	.add = blktrans_notify_add,
  	.remove = blktrans_notify_remove,
  };
97894cda5   Thomas Gleixner   [MTD] core: Clean...
334

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
336
337
  int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
  {
  	int ret, i;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
338
  	/* Register the notifier if/when the first device type is
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
339
340
341
342
  	   registered, to prevent the link/init ordering from fucking
  	   us over. */
  	if (!blktrans_notifier.list.next)
  		register_mtd_user(&blktrans_notifier);
95b93a0cd   Burman Yan   [MTD] replace kma...
343
  	tr->blkcore_priv = kzalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344
345
  	if (!tr->blkcore_priv)
  		return -ENOMEM;
48b192686   Ingo Molnar   [PATCH] sem2mutex...
346
  	mutex_lock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
348
349
350
351
352
353
  
  	ret = register_blkdev(tr->major, tr->name);
  	if (ret) {
  		printk(KERN_WARNING "Unable to register %s block device on major %d: %d
  ",
  		       tr->name, tr->major, ret);
  		kfree(tr->blkcore_priv);
48b192686   Ingo Molnar   [PATCH] sem2mutex...
354
  		mutex_unlock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
355
356
357
  		return ret;
  	}
  	spin_lock_init(&tr->blkcore_priv->queue_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
359
360
361
362
  
  	tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
  	if (!tr->blkcore_priv->rq) {
  		unregister_blkdev(tr->major, tr->name);
  		kfree(tr->blkcore_priv);
48b192686   Ingo Molnar   [PATCH] sem2mutex...
363
  		mutex_unlock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
364
365
366
367
  		return -ENOMEM;
  	}
  
  	tr->blkcore_priv->rq->queuedata = tr;
191876729   Richard Purdie   [MTD] Allow varia...
368
369
  	blk_queue_hardsect_size(tr->blkcore_priv->rq, tr->blksize);
  	tr->blkshift = ffs(tr->blksize) - 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
370

3e67fe454   Christoph Hellwig   [MTD] Finish conv...
371
372
373
  	tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,
  			"%sd", tr->name);
  	if (IS_ERR(tr->blkcore_priv->thread)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
374
375
376
  		blk_cleanup_queue(tr->blkcore_priv->rq);
  		unregister_blkdev(tr->major, tr->name);
  		kfree(tr->blkcore_priv);
48b192686   Ingo Molnar   [PATCH] sem2mutex...
377
  		mutex_unlock(&mtd_table_mutex);
3e67fe454   Christoph Hellwig   [MTD] Finish conv...
378
  		return PTR_ERR(tr->blkcore_priv->thread);
97894cda5   Thomas Gleixner   [MTD] core: Clean...
379
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
381
382
383
384
385
386
387
  	INIT_LIST_HEAD(&tr->devs);
  	list_add(&tr->list, &blktrans_majors);
  
  	for (i=0; i<MAX_MTD_DEVICES; i++) {
  		if (mtd_table[i] && mtd_table[i]->type != MTD_ABSENT)
  			tr->add_mtd(tr, mtd_table[i]);
  	}
48b192686   Ingo Molnar   [PATCH] sem2mutex...
388
  	mutex_unlock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389
390
391
392
393
394
395
  
  	return 0;
  }
  
  int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr)
  {
  	struct list_head *this, *next;
48b192686   Ingo Molnar   [PATCH] sem2mutex...
396
  	mutex_lock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
397
398
  
  	/* Clean up the kernel thread */
3e67fe454   Christoph Hellwig   [MTD] Finish conv...
399
  	kthread_stop(tr->blkcore_priv->thread);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
400
401
402
403
404
405
406
407
  
  	/* Remove it from the list of active majors */
  	list_del(&tr->list);
  
  	list_for_each_safe(this, next, &tr->devs) {
  		struct mtd_blktrans_dev *dev = list_entry(this, struct mtd_blktrans_dev, list);
  		tr->remove_dev(dev);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
408
409
  	blk_cleanup_queue(tr->blkcore_priv->rq);
  	unregister_blkdev(tr->major, tr->name);
48b192686   Ingo Molnar   [PATCH] sem2mutex...
410
  	mutex_unlock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
411
412
  
  	kfree(tr->blkcore_priv);
373ebfbf1   Eric Sesterhenn   BUG_ON() Conversi...
413
  	BUG_ON(!list_empty(&tr->devs));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
  	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'");