Blame view

drivers/mtd/mtd_blkdevs.c 13.3 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
a1452a377   David Woodhouse   mtd: Update copyr...
2
   * Interface to Linux block layer for MTD 'translation layers'.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
   *
a1452a377   David Woodhouse   mtd: Update copyr...
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
   * Copyright © 2003-2010 David Woodhouse <dwmw2@infradead.org>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
   *
   */
  
  #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>
48b192686   Ingo Molnar   [PATCH] sem2mutex...
34
  #include <linux/mutex.h>
99f9b2431   Eric W. Biederman   [MTD] mtd_blkdevs...
35
  #include <linux/kthread.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
  #include <asm/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37

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

356d70f19   Ben Dooks   [MTD] mtdcore.c: ...
40
  static LIST_HEAD(blktrans_majors);
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
41
  static DEFINE_MUTEX(blktrans_ref_mutex);
7f53f12f0   H Hartley Sweeten   mtd: mtd_blkdevs:...
42
  static void blktrans_dev_release(struct kref *kref)
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
43
44
45
46
47
  {
  	struct mtd_blktrans_dev *dev =
  		container_of(kref, struct mtd_blktrans_dev, ref);
  
  	dev->disk->private_data = NULL;
e4d64cab9   Maxim Levitsky   mtd: blktrans: do...
48
  	blk_cleanup_queue(dev->rq);
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
  	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;
  }
7f53f12f0   H Hartley Sweeten   mtd: mtd_blkdevs:...
68
  static void blktrans_dev_put(struct mtd_blktrans_dev *dev)
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
69
70
71
72
73
  {
  	mutex_lock(&blktrans_ref_mutex);
  	kref_put(&dev->ref, blktrans_dev_release);
  	mutex_unlock(&blktrans_ref_mutex);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
76
77
78
79
80
81
  
  static int do_blktrans_request(struct mtd_blktrans_ops *tr,
  			       struct mtd_blktrans_dev *dev,
  			       struct request *req)
  {
  	unsigned long block, nsect;
  	char *buf;
83096ebf1   Tejun Heo   block: convert to...
82
  	block = blk_rq_pos(req) << 9 >> tr->blkshift;
1011c1b9f   Tejun Heo   block: blk_rq_[cu...
83
  	nsect = blk_rq_cur_bytes(req) >> tr->blkshift;
191876729   Richard Purdie   [MTD] Allow varia...
84

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
  	buf = req->buffer;
33659ebba   Christoph Hellwig   block: remove wra...
86
  	if (req->cmd_type != REQ_TYPE_FS)
f06d9a2b5   Tejun Heo   block: replace en...
87
  		return -EIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88

83096ebf1   Tejun Heo   block: convert to...
89
90
  	if (blk_rq_pos(req) + blk_rq_cur_sectors(req) >
  	    get_capacity(req->rq_disk))
f06d9a2b5   Tejun Heo   block: replace en...
91
  		return -EIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92

33659ebba   Christoph Hellwig   block: remove wra...
93
  	if (req->cmd_flags & REQ_DISCARD)
1122a26f2   Christoph Hellwig   block: use normal...
94
  		return tr->discard(dev, block, nsect);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
96
  	switch(rq_data_dir(req)) {
  	case READ:
191876729   Richard Purdie   [MTD] Allow varia...
97
  		for (; nsect > 0; nsect--, block++, buf += tr->blksize)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
  			if (tr->readsect(dev, block, buf))
f06d9a2b5   Tejun Heo   block: replace en...
99
  				return -EIO;
2d4dc890b   Ilya Loginov   block: add helper...
100
  		rq_flush_dcache_pages(req);
f06d9a2b5   Tejun Heo   block: replace en...
101
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
103
  	case WRITE:
  		if (!tr->writesect)
f06d9a2b5   Tejun Heo   block: replace en...
104
  			return -EIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105

2d4dc890b   Ilya Loginov   block: add helper...
106
  		rq_flush_dcache_pages(req);
191876729   Richard Purdie   [MTD] Allow varia...
107
  		for (; nsect > 0; nsect--, block++, buf += tr->blksize)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
  			if (tr->writesect(dev, block, buf))
f06d9a2b5   Tejun Heo   block: replace en...
109
110
  				return -EIO;
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
  	default:
9a2923082   Jeff Garzik   [MTD] fix printk ...
112
113
  		printk(KERN_NOTICE "Unknown request %u
  ", rq_data_dir(req));
f06d9a2b5   Tejun Heo   block: replace en...
114
  		return -EIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
116
  	}
  }
c7519dbf6   Jarkko Lavinen   mtd_blkdevs: Add ...
117
118
119
120
  int mtd_blktrans_cease_background(struct mtd_blktrans_dev *dev)
  {
  	if (kthread_should_stop())
  		return 1;
7bf7e370d   Artem Bityutskiy   Merge branch 'mas...
121
  	return dev->bg_stop;
c7519dbf6   Jarkko Lavinen   mtd_blkdevs: Add ...
122
123
  }
  EXPORT_SYMBOL_GPL(mtd_blktrans_cease_background);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
125
  static int mtd_blktrans_thread(void *arg)
  {
a86386225   Maxim Levitsky   mtd: blktrans: re...
126
  	struct mtd_blktrans_dev *dev = arg;
c7519dbf6   Jarkko Lavinen   mtd_blkdevs: Add ...
127
  	struct mtd_blktrans_ops *tr = dev->tr;
a86386225   Maxim Levitsky   mtd: blktrans: re...
128
  	struct request_queue *rq = dev->rq;
1498ada7a   Tejun Heo   mtd_blkdevs: dequ...
129
  	struct request *req = NULL;
c7519dbf6   Jarkko Lavinen   mtd_blkdevs: Add ...
130
  	int background_done = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
  	spin_lock_irq(rq->queue_lock);
1498ada7a   Tejun Heo   mtd_blkdevs: dequ...
133

3e67fe454   Christoph Hellwig   [MTD] Finish conv...
134
  	while (!kthread_should_stop()) {
f06d9a2b5   Tejun Heo   block: replace en...
135
  		int res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136

7bf7e370d   Artem Bityutskiy   Merge branch 'mas...
137
  		dev->bg_stop = false;
9934c8c04   Tejun Heo   block: implement ...
138
  		if (!req && !(req = blk_fetch_request(rq))) {
c7519dbf6   Jarkko Lavinen   mtd_blkdevs: Add ...
139
140
141
142
143
144
145
146
147
148
  			if (tr->background && !background_done) {
  				spin_unlock_irq(rq->queue_lock);
  				mutex_lock(&dev->lock);
  				tr->background(dev);
  				mutex_unlock(&dev->lock);
  				spin_lock_irq(rq->queue_lock);
  				/*
  				 * Do background processing just once per idle
  				 * period.
  				 */
7bf7e370d   Artem Bityutskiy   Merge branch 'mas...
149
  				background_done = !dev->bg_stop;
c7519dbf6   Jarkko Lavinen   mtd_blkdevs: Add ...
150
151
  				continue;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
  			set_current_state(TASK_INTERRUPTIBLE);
12aebf3e1   Maxim Levitsky   mtd: blktrans: fi...
153
154
155
  
  			if (kthread_should_stop())
  				set_current_state(TASK_RUNNING);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
  			spin_unlock_irq(rq->queue_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
  			schedule();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
  			spin_lock_irq(rq->queue_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
159
160
  			continue;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
161
  		spin_unlock_irq(rq->queue_lock);
48b192686   Ingo Molnar   [PATCH] sem2mutex...
162
  		mutex_lock(&dev->lock);
a86386225   Maxim Levitsky   mtd: blktrans: re...
163
  		res = do_blktrans_request(dev->tr, dev, req);
48b192686   Ingo Molnar   [PATCH] sem2mutex...
164
  		mutex_unlock(&dev->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
166
  
  		spin_lock_irq(rq->queue_lock);
1498ada7a   Tejun Heo   mtd_blkdevs: dequ...
167
168
  		if (!__blk_end_request_cur(req, res))
  			req = NULL;
c7519dbf6   Jarkko Lavinen   mtd_blkdevs: Add ...
169
170
  
  		background_done = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
  	}
1498ada7a   Tejun Heo   mtd_blkdevs: dequ...
172
173
174
  
  	if (req)
  		__blk_end_request_all(req, -EIO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
  	spin_unlock_irq(rq->queue_lock);
3e67fe454   Christoph Hellwig   [MTD] Finish conv...
176
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
178
179
180
  }
  
  static void mtd_blktrans_request(struct request_queue *rq)
  {
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
181
182
183
184
  	struct mtd_blktrans_dev *dev;
  	struct request *req = NULL;
  
  	dev = rq->queuedata;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185

048d87199   Maxim Levitsky   mtd: blktrans: Ho...
186
187
188
  	if (!dev)
  		while ((req = blk_fetch_request(rq)) != NULL)
  			__blk_end_request_all(req, -ENODEV);
7bf7e370d   Artem Bityutskiy   Merge branch 'mas...
189
190
  	else {
  		dev->bg_stop = true;
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
191
  		wake_up_process(dev->thread);
7bf7e370d   Artem Bityutskiy   Merge branch 'mas...
192
  	}
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
193
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194

af0e2a0a8   Al Viro   [PATCH] switch mt...
195
  static int blktrans_open(struct block_device *bdev, fmode_t mode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
  {
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
197
  	struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk);
008c751ec   Maxim Levitsky   mtd: allow to unl...
198
  	int ret = 0;
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
199
200
  
  	if (!dev)
6e9624b8c   Arnd Bergmann   block: push down ...
201
  		return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
202
203
  
  	mutex_lock(&dev->lock);
008c751ec   Maxim Levitsky   mtd: allow to unl...
204
  	if (dev->open++)
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
205
  		goto unlock;
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
206

008c751ec   Maxim Levitsky   mtd: allow to unl...
207
208
  	kref_get(&dev->ref);
  	__module_get(dev->tr->owner);
94735ec40   Artem Bityutskiy   mtd: mtd_blkdevs:...
209
210
211
212
213
214
215
  	if (!dev->mtd)
  		goto unlock;
  
  	if (dev->tr->open) {
  		ret = dev->tr->open(dev);
  		if (ret)
  			goto error_put;
008c751ec   Maxim Levitsky   mtd: allow to unl...
216
  	}
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
217

94735ec40   Artem Bityutskiy   mtd: mtd_blkdevs:...
218
219
220
  	ret = __get_mtd_device(dev->mtd);
  	if (ret)
  		goto error_release;
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
221
222
223
  unlock:
  	mutex_unlock(&dev->lock);
  	blktrans_dev_put(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
  	return ret;
94735ec40   Artem Bityutskiy   mtd: mtd_blkdevs:...
225
226
227
228
229
230
231
232
233
234
  
  error_release:
  	if (dev->tr->release)
  		dev->tr->release(dev);
  error_put:
  	module_put(dev->tr->owner);
  	kref_put(&dev->ref, blktrans_dev_release);
  	mutex_unlock(&dev->lock);
  	blktrans_dev_put(dev);
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
235
  }
af0e2a0a8   Al Viro   [PATCH] switch mt...
236
  static int blktrans_release(struct gendisk *disk, fmode_t mode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
  {
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
238
  	struct mtd_blktrans_dev *dev = blktrans_dev_get(disk);
008c751ec   Maxim Levitsky   mtd: allow to unl...
239
  	int ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240

048d87199   Maxim Levitsky   mtd: blktrans: Ho...
241
242
  	if (!dev)
  		return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243

048d87199   Maxim Levitsky   mtd: blktrans: Ho...
244
  	mutex_lock(&dev->lock);
008c751ec   Maxim Levitsky   mtd: allow to unl...
245
  	if (--dev->open)
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
246
  		goto unlock;
008c751ec   Maxim Levitsky   mtd: allow to unl...
247
248
249
250
251
252
253
  	kref_put(&dev->ref, blktrans_dev_release);
  	module_put(dev->tr->owner);
  
  	if (dev->mtd) {
  		ret = dev->tr->release ? dev->tr->release(dev) : 0;
  		__put_mtd_device(dev->mtd);
  	}
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
254
255
256
  unlock:
  	mutex_unlock(&dev->lock);
  	blktrans_dev_put(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
257
258
  	return ret;
  }
a885c8c43   Christoph Hellwig   [PATCH] Add block...
259
260
  static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo)
  {
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
261
262
263
264
265
266
267
  	struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk);
  	int ret = -ENXIO;
  
  	if (!dev)
  		return ret;
  
  	mutex_lock(&dev->lock);
a885c8c43   Christoph Hellwig   [PATCH] Add block...
268

048d87199   Maxim Levitsky   mtd: blktrans: Ho...
269
270
271
272
273
274
275
276
  	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;
a885c8c43   Christoph Hellwig   [PATCH] Add block...
277
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278

af0e2a0a8   Al Viro   [PATCH] switch mt...
279
  static int blktrans_ioctl(struct block_device *bdev, fmode_t mode,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
281
  			      unsigned int cmd, unsigned long arg)
  {
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
282
283
284
285
286
287
288
289
290
291
  	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;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
293
294
  
  	switch (cmd) {
  	case BLKFLSBUF:
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
295
  		ret = dev->tr->flush ? dev->tr->flush(dev) : 0;
007c2d876   Dan Carpenter   mtd: return succe...
296
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
297
  	default:
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
298
  		ret = -ENOTTY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
299
  	}
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
300
301
302
303
  unlock:
  	mutex_unlock(&dev->lock);
  	blktrans_dev_put(dev);
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
  }
83d5cde47   Alexey Dobriyan   const: make block...
305
  static const struct block_device_operations mtd_blktrans_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
  	.owner		= THIS_MODULE,
af0e2a0a8   Al Viro   [PATCH] switch mt...
307
308
  	.open		= blktrans_open,
  	.release	= blktrans_release,
8a6cfeb6d   Arnd Bergmann   block: push down ...
309
  	.ioctl		= blktrans_ioctl,
a885c8c43   Christoph Hellwig   [PATCH] Add block...
310
  	.getgeo		= blktrans_getgeo,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
311
312
313
314
315
  };
  
  int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
  {
  	struct mtd_blktrans_ops *tr = new->tr;
71a928c0e   Chris Malley   [MTD] Use list_fo...
316
  	struct mtd_blktrans_dev *d;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
317
318
  	int last_devnum = -1;
  	struct gendisk *gd;
a86386225   Maxim Levitsky   mtd: blktrans: re...
319
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320

b3561ea94   Jean Delvare   Clean up mutex_tr...
321
  	if (mutex_trylock(&mtd_table_mutex)) {
48b192686   Ingo Molnar   [PATCH] sem2mutex...
322
  		mutex_unlock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
323
324
  		BUG();
  	}
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
325
  	mutex_lock(&blktrans_ref_mutex);
71a928c0e   Chris Malley   [MTD] Use list_fo...
326
  	list_for_each_entry(d, &tr->devs, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
327
328
329
330
331
332
333
334
335
336
  		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 */
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
337
  			mutex_unlock(&blktrans_ref_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338
339
340
341
342
  			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...
343
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344
345
  		last_devnum = d->devnum;
  	}
a86386225   Maxim Levitsky   mtd: blktrans: re...
346
347
  
  	ret = -EBUSY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
348
349
  	if (new->devnum == -1)
  		new->devnum = last_devnum+1;
4d3a8534b   Ben Hutchings   mtd: Raise limit ...
350
351
352
353
  	/* 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) ||
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
354
355
  	    (tr->part_bits && new->devnum >= 27 * 26)) {
  		mutex_unlock(&blktrans_ref_mutex);
a86386225   Maxim Levitsky   mtd: blktrans: re...
356
  		goto error1;
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
357
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359
360
  	list_add_tail(&new->list, &tr->devs);
   added:
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
361
  	mutex_unlock(&blktrans_ref_mutex);
ce37ab42a   David Woodhouse   [MTD] Always init...
362
  	mutex_init(&new->lock);
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
363
  	kref_init(&new->ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
364
365
  	if (!tr->writesect)
  		new->readonly = 1;
a86386225   Maxim Levitsky   mtd: blktrans: re...
366
367
  	/* Create gendisk */
  	ret = -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
  	gd = alloc_disk(1 << tr->part_bits);
a86386225   Maxim Levitsky   mtd: blktrans: re...
369
370
371
372
373
374
  
  	if (!gd)
  		goto error2;
  
  	new->disk = gd;
  	gd->private_data = new;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
376
377
  	gd->major = tr->major;
  	gd->first_minor = (new->devnum) << tr->part_bits;
  	gd->fops = &mtd_blktrans_ops;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
378

65a8de36b   Todd Poynor   [MTD] mtd_blkdevs...
379
380
381
382
383
384
385
386
387
388
389
390
  	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
391

191876729   Richard Purdie   [MTD] Allow varia...
392
  	set_capacity(gd, (new->size * tr->blksize) >> 9);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
393

a86386225   Maxim Levitsky   mtd: blktrans: re...
394
395
396
397
398
399
400
401
402
  	/* 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);
115ee88c1   Jarkko Lavinen   mtd_blkdevs: Set ...
403
404
405
406
  	if (tr->discard) {
  		queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, new->rq);
  		new->rq->limits.max_discard_sectors = UINT_MAX;
  	}
a86386225   Maxim Levitsky   mtd: blktrans: re...
407
408
409
410
411
412
413
414
415
416
417
  
  	gd->queue = new->rq;
  
  	/* 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;
  	}
d694846b6   David Woodhouse   [MTD] set blkdev ...
418
  	gd->driverfs_dev = &new->mtd->dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
419
420
421
422
423
  
  	if (new->readonly)
  		set_disk_ro(gd, 1);
  
  	add_disk(gd);
026ec5788   Maxim Levitsky   mtd: blktrans: al...
424

133fa8c7d   Maxim Levitsky   mtd: Few follow u...
425
426
  	if (new->disk_attributes) {
  		ret = sysfs_create_group(&disk_to_dev(gd)->kobj,
026ec5788   Maxim Levitsky   mtd: blktrans: al...
427
  					new->disk_attributes);
133fa8c7d   Maxim Levitsky   mtd: Few follow u...
428
429
  		WARN_ON(ret);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
430
  	return 0;
a86386225   Maxim Levitsky   mtd: blktrans: re...
431
432
433
434
435
436
437
  error4:
  	blk_cleanup_queue(new->rq);
  error3:
  	put_disk(new->disk);
  error2:
  	list_del(&new->list);
  error1:
a86386225   Maxim Levitsky   mtd: blktrans: re...
438
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
439
440
441
442
  }
  
  int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
  {
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
443
  	unsigned long flags;
b3561ea94   Jean Delvare   Clean up mutex_tr...
444
  	if (mutex_trylock(&mtd_table_mutex)) {
48b192686   Ingo Molnar   [PATCH] sem2mutex...
445
  		mutex_unlock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
446
447
  		BUG();
  	}
026ec5788   Maxim Levitsky   mtd: blktrans: al...
448
449
450
  	if (old->disk_attributes)
  		sysfs_remove_group(&disk_to_dev(old->disk)->kobj,
  						old->disk_attributes);
dba76c033   Maxim Levitsky   mtd: blkdevs: fix...
451
452
  	/* Stop new requests to arrive */
  	del_gendisk(old->disk);
a86386225   Maxim Levitsky   mtd: blktrans: re...
453
454
  	/* Stop the thread */
  	kthread_stop(old->thread);
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
455
456
457
458
459
  	/* 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);
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
460

008c751ec   Maxim Levitsky   mtd: allow to unl...
461
462
  	/* If the device is currently open, tell trans driver to close it,
  		then put mtd device, and don't touch it again */
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
463
  	mutex_lock(&old->lock);
008c751ec   Maxim Levitsky   mtd: allow to unl...
464
465
466
467
  	if (old->open) {
  		if (old->tr->release)
  			old->tr->release(old);
  		__put_mtd_device(old->mtd);
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
468
  	}
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
469
470
471
472
  	old->mtd = NULL;
  
  	mutex_unlock(&old->lock);
  	blktrans_dev_put(old);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
474
475
476
477
  	return 0;
  }
  
  static void blktrans_notify_remove(struct mtd_info *mtd)
  {
71a928c0e   Chris Malley   [MTD] Use list_fo...
478
479
  	struct mtd_blktrans_ops *tr;
  	struct mtd_blktrans_dev *dev, *next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
480

71a928c0e   Chris Malley   [MTD] Use list_fo...
481
482
  	list_for_each_entry(tr, &blktrans_majors, list)
  		list_for_each_entry_safe(dev, next, &tr->devs, list)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
483
484
  			if (dev->mtd == mtd)
  				tr->remove_dev(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
485
486
487
488
  }
  
  static void blktrans_notify_add(struct mtd_info *mtd)
  {
71a928c0e   Chris Malley   [MTD] Use list_fo...
489
  	struct mtd_blktrans_ops *tr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
490
491
492
  
  	if (mtd->type == MTD_ABSENT)
  		return;
71a928c0e   Chris Malley   [MTD] Use list_fo...
493
  	list_for_each_entry(tr, &blktrans_majors, list)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
494
  		tr->add_mtd(tr, mtd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495
496
497
498
499
500
  }
  
  static struct mtd_notifier blktrans_notifier = {
  	.add = blktrans_notify_add,
  	.remove = blktrans_notify_remove,
  };
97894cda5   Thomas Gleixner   [MTD] core: Clean...
501

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
502
503
  int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
  {
f1332ba2f   Ben Hutchings   mtd: Introduce an...
504
505
  	struct mtd_info *mtd;
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
506

97894cda5   Thomas Gleixner   [MTD] core: Clean...
507
  	/* Register the notifier if/when the first device type is
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
508
509
510
511
  	   registered, to prevent the link/init ordering from fucking
  	   us over. */
  	if (!blktrans_notifier.list.next)
  		register_mtd_user(&blktrans_notifier);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
512

48b192686   Ingo Molnar   [PATCH] sem2mutex...
513
  	mutex_lock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
514
515
  
  	ret = register_blkdev(tr->major, tr->name);
6fe4c5903   Frank Li   MTD: Fix wrong ch...
516
  	if (ret < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
517
518
519
  		printk(KERN_WARNING "Unable to register %s block device on major %d: %d
  ",
  		       tr->name, tr->major, ret);
48b192686   Ingo Molnar   [PATCH] sem2mutex...
520
  		mutex_unlock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
521
522
  		return ret;
  	}
eae9acd13   David Woodhouse   Support 'discard ...
523

6fe4c5903   Frank Li   MTD: Fix wrong ch...
524
525
  	if (ret)
  		tr->major = ret;
191876729   Richard Purdie   [MTD] Allow varia...
526
  	tr->blkshift = ffs(tr->blksize) - 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
527

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
528
529
  	INIT_LIST_HEAD(&tr->devs);
  	list_add(&tr->list, &blktrans_majors);
f1332ba2f   Ben Hutchings   mtd: Introduce an...
530
531
532
  	mtd_for_each_device(mtd)
  		if (mtd->type != MTD_ABSENT)
  			tr->add_mtd(tr, mtd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
533

48b192686   Ingo Molnar   [PATCH] sem2mutex...
534
  	mutex_unlock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
535
536
537
538
539
  	return 0;
  }
  
  int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr)
  {
71a928c0e   Chris Malley   [MTD] Use list_fo...
540
  	struct mtd_blktrans_dev *dev, *next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
541

48b192686   Ingo Molnar   [PATCH] sem2mutex...
542
  	mutex_lock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
543

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
544
545
  	/* Remove it from the list of active majors */
  	list_del(&tr->list);
71a928c0e   Chris Malley   [MTD] Use list_fo...
546
  	list_for_each_entry_safe(dev, next, &tr->devs, list)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
547
  		tr->remove_dev(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
548

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
549
  	unregister_blkdev(tr->major, tr->name);
48b192686   Ingo Molnar   [PATCH] sem2mutex...
550
  	mutex_unlock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
551

373ebfbf1   Eric Sesterhenn   BUG_ON() Conversi...
552
  	BUG_ON(!list_empty(&tr->devs));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
  	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'");