Blame view

drivers/mtd/mtd_blkdevs.c 13.5 KB
fd534e9b5   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
  /*
a1452a377   David Woodhouse   mtd: Update copyr...
3
   * Interface to Linux block layer for MTD 'translation layers'.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
   *
a1452a377   David Woodhouse   mtd: Update copyr...
5
   * Copyright © 2003-2010 David Woodhouse <dwmw2@infradead.org>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
7
8
9
10
11
12
13
14
15
   */
  
  #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>
891b7c5fb   Jens Axboe   mtd_blkdevs: conv...
16
  #include <linux/blk-mq.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
  #include <linux/blkpg.h>
  #include <linux/spinlock.h>
  #include <linux/hdreg.h>
48b192686   Ingo Molnar   [PATCH] sem2mutex...
20
  #include <linux/mutex.h>
7c0f6ba68   Linus Torvalds   Replace <asm/uacc...
21
  #include <linux/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22

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

356d70f19   Ben Dooks   [MTD] mtdcore.c: ...
25
  static LIST_HEAD(blktrans_majors);
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
26
  static DEFINE_MUTEX(blktrans_ref_mutex);
7f53f12f0   H Hartley Sweeten   mtd: mtd_blkdevs:...
27
  static void blktrans_dev_release(struct kref *kref)
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
28
29
30
31
32
  {
  	struct mtd_blktrans_dev *dev =
  		container_of(kref, struct mtd_blktrans_dev, ref);
  
  	dev->disk->private_data = NULL;
e4d64cab9   Maxim Levitsky   mtd: blktrans: do...
33
  	blk_cleanup_queue(dev->rq);
891b7c5fb   Jens Axboe   mtd_blkdevs: conv...
34
35
  	blk_mq_free_tag_set(dev->tag_set);
  	kfree(dev->tag_set);
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
  	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:...
55
  static void blktrans_dev_put(struct mtd_blktrans_dev *dev)
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
56
57
58
59
60
  {
  	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
61

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62

2a842acab   Christoph Hellwig   block: introduce ...
63
  static blk_status_t do_blktrans_request(struct mtd_blktrans_ops *tr,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64
65
66
67
68
  			       struct mtd_blktrans_dev *dev,
  			       struct request *req)
  {
  	unsigned long block, nsect;
  	char *buf;
83096ebf1   Tejun Heo   block: convert to...
69
  	block = blk_rq_pos(req) << 9 >> tr->blkshift;
1011c1b9f   Tejun Heo   block: blk_rq_[cu...
70
  	nsect = blk_rq_cur_bytes(req) >> tr->blkshift;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71

2a842acab   Christoph Hellwig   block: introduce ...
72
73
74
75
76
  	if (req_op(req) == REQ_OP_FLUSH) {
  		if (tr->flush(dev))
  			return BLK_STS_IOERR;
  		return BLK_STS_OK;
  	}
566c0d6a7   Roman Peniaev   mtd: mtd_blkdevs:...
77

83096ebf1   Tejun Heo   block: convert to...
78
79
  	if (blk_rq_pos(req) + blk_rq_cur_sectors(req) >
  	    get_capacity(req->rq_disk))
2a842acab   Christoph Hellwig   block: introduce ...
80
  		return BLK_STS_IOERR;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81

aebf526b5   Christoph Hellwig   block: fold cmd_t...
82
83
  	switch (req_op(req)) {
  	case REQ_OP_DISCARD:
2a842acab   Christoph Hellwig   block: introduce ...
84
85
86
  		if (tr->discard(dev, block, nsect))
  			return BLK_STS_IOERR;
  		return BLK_STS_OK;
aebf526b5   Christoph Hellwig   block: fold cmd_t...
87
  	case REQ_OP_READ:
34ab96e6c   Christoph Hellwig   mtd_blkdevs: hand...
88
89
90
91
  		buf = kmap(bio_page(req->bio)) + bio_offset(req->bio);
  		for (; nsect > 0; nsect--, block++, buf += tr->blksize) {
  			if (tr->readsect(dev, block, buf)) {
  				kunmap(bio_page(req->bio));
2a842acab   Christoph Hellwig   block: introduce ...
92
  				return BLK_STS_IOERR;
34ab96e6c   Christoph Hellwig   mtd_blkdevs: hand...
93
94
95
  			}
  		}
  		kunmap(bio_page(req->bio));
2d4dc890b   Ilya Loginov   block: add helper...
96
  		rq_flush_dcache_pages(req);
2a842acab   Christoph Hellwig   block: introduce ...
97
  		return BLK_STS_OK;
aebf526b5   Christoph Hellwig   block: fold cmd_t...
98
  	case REQ_OP_WRITE:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99
  		if (!tr->writesect)
2a842acab   Christoph Hellwig   block: introduce ...
100
  			return BLK_STS_IOERR;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101

2d4dc890b   Ilya Loginov   block: add helper...
102
  		rq_flush_dcache_pages(req);
34ab96e6c   Christoph Hellwig   mtd_blkdevs: hand...
103
104
105
106
  		buf = kmap(bio_page(req->bio)) + bio_offset(req->bio);
  		for (; nsect > 0; nsect--, block++, buf += tr->blksize) {
  			if (tr->writesect(dev, block, buf)) {
  				kunmap(bio_page(req->bio));
2a842acab   Christoph Hellwig   block: introduce ...
107
  				return BLK_STS_IOERR;
34ab96e6c   Christoph Hellwig   mtd_blkdevs: hand...
108
109
110
  			}
  		}
  		kunmap(bio_page(req->bio));
9a5154477   Abhishek Sahu   mtd: blkdevs: Fix...
111
  		return BLK_STS_OK;
aebf526b5   Christoph Hellwig   block: fold cmd_t...
112
  	default:
2a842acab   Christoph Hellwig   block: introduce ...
113
  		return BLK_STS_IOERR;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
115
  	}
  }
c7519dbf6   Jarkko Lavinen   mtd_blkdevs: Add ...
116
117
  int mtd_blktrans_cease_background(struct mtd_blktrans_dev *dev)
  {
7bf7e370d   Artem Bityutskiy   Merge branch 'mas...
118
  	return dev->bg_stop;
c7519dbf6   Jarkko Lavinen   mtd_blkdevs: Add ...
119
120
  }
  EXPORT_SYMBOL_GPL(mtd_blktrans_cease_background);
891b7c5fb   Jens Axboe   mtd_blkdevs: conv...
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
  static struct request *mtd_next_request(struct mtd_blktrans_dev *dev)
  {
  	struct request *rq;
  
  	rq = list_first_entry_or_null(&dev->rq_list, struct request, queuelist);
  	if (rq) {
  		list_del_init(&rq->queuelist);
  		blk_mq_start_request(rq);
  		return rq;
  	}
  
  	return NULL;
  }
  
  static void mtd_blktrans_work(struct mtd_blktrans_dev *dev)
  	__releases(&dev->queue_lock)
  	__acquires(&dev->queue_lock)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
  {
c7519dbf6   Jarkko Lavinen   mtd_blkdevs: Add ...
139
  	struct mtd_blktrans_ops *tr = dev->tr;
1498ada7a   Tejun Heo   mtd_blkdevs: dequ...
140
  	struct request *req = NULL;
c7519dbf6   Jarkko Lavinen   mtd_blkdevs: Add ...
141
  	int background_done = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142

22a8578fc   Ezequiel Garcia   mtd: mtd_blkdevs:...
143
  	while (1) {
2a842acab   Christoph Hellwig   block: introduce ...
144
  		blk_status_t res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145

7bf7e370d   Artem Bityutskiy   Merge branch 'mas...
146
  		dev->bg_stop = false;
891b7c5fb   Jens Axboe   mtd_blkdevs: conv...
147
  		if (!req && !(req = mtd_next_request(dev))) {
c7519dbf6   Jarkko Lavinen   mtd_blkdevs: Add ...
148
  			if (tr->background && !background_done) {
891b7c5fb   Jens Axboe   mtd_blkdevs: conv...
149
  				spin_unlock_irq(&dev->queue_lock);
c7519dbf6   Jarkko Lavinen   mtd_blkdevs: Add ...
150
151
152
  				mutex_lock(&dev->lock);
  				tr->background(dev);
  				mutex_unlock(&dev->lock);
891b7c5fb   Jens Axboe   mtd_blkdevs: conv...
153
  				spin_lock_irq(&dev->queue_lock);
c7519dbf6   Jarkko Lavinen   mtd_blkdevs: Add ...
154
155
156
157
  				/*
  				 * Do background processing just once per idle
  				 * period.
  				 */
7bf7e370d   Artem Bityutskiy   Merge branch 'mas...
158
  				background_done = !dev->bg_stop;
c7519dbf6   Jarkko Lavinen   mtd_blkdevs: Add ...
159
160
  				continue;
  			}
22a8578fc   Ezequiel Garcia   mtd: mtd_blkdevs:...
161
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
  		}
891b7c5fb   Jens Axboe   mtd_blkdevs: conv...
163
  		spin_unlock_irq(&dev->queue_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164

48b192686   Ingo Molnar   [PATCH] sem2mutex...
165
  		mutex_lock(&dev->lock);
a86386225   Maxim Levitsky   mtd: blktrans: re...
166
  		res = do_blktrans_request(dev->tr, dev, req);
48b192686   Ingo Molnar   [PATCH] sem2mutex...
167
  		mutex_unlock(&dev->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168

891b7c5fb   Jens Axboe   mtd_blkdevs: conv...
169
170
  		if (!blk_update_request(req, res, blk_rq_cur_bytes(req))) {
  			__blk_mq_end_request(req, res);
1498ada7a   Tejun Heo   mtd_blkdevs: dequ...
171
  			req = NULL;
891b7c5fb   Jens Axboe   mtd_blkdevs: conv...
172
  		}
c7519dbf6   Jarkko Lavinen   mtd_blkdevs: Add ...
173
174
  
  		background_done = 0;
891b7c5fb   Jens Axboe   mtd_blkdevs: conv...
175
  		spin_lock_irq(&dev->queue_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
  }
891b7c5fb   Jens Axboe   mtd_blkdevs: conv...
178
179
  static blk_status_t mtd_queue_rq(struct blk_mq_hw_ctx *hctx,
  				 const struct blk_mq_queue_data *bd)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
  {
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
181
  	struct mtd_blktrans_dev *dev;
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
182

891b7c5fb   Jens Axboe   mtd_blkdevs: conv...
183
184
185
186
187
188
189
190
191
192
  	dev = hctx->queue->queuedata;
  	if (!dev) {
  		blk_mq_start_request(bd->rq);
  		return BLK_STS_IOERR;
  	}
  
  	spin_lock_irq(&dev->queue_lock);
  	list_add_tail(&bd->rq->queuelist, &dev->rq_list);
  	mtd_blktrans_work(dev);
  	spin_unlock_irq(&dev->queue_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193

891b7c5fb   Jens Axboe   mtd_blkdevs: conv...
194
  	return BLK_STS_OK;
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
195
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196

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

073db4a51   Brian Norris   mtd: fix: avoid r...
205
  	mutex_lock(&mtd_table_mutex);
f3c63795e   Brian Norris   mtd: blkdevs: fix...
206
  	mutex_lock(&dev->lock);
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
207

342ff28f5   Brian Norris   mtd: mtd_blkdevs:...
208
  	if (dev->open)
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
209
  		goto unlock;
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
210

008c751ec   Maxim Levitsky   mtd: allow to unl...
211
212
  	kref_get(&dev->ref);
  	__module_get(dev->tr->owner);
94735ec40   Artem Bityutskiy   mtd: mtd_blkdevs:...
213
214
215
216
217
218
219
  	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...
220
  	}
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
221

94735ec40   Artem Bityutskiy   mtd: mtd_blkdevs:...
222
223
224
  	ret = __get_mtd_device(dev->mtd);
  	if (ret)
  		goto error_release;
70d5098a4   Alexander Stein   mtd: mtdblock: ca...
225
  	dev->file_mode = mode;
94735ec40   Artem Bityutskiy   mtd: mtd_blkdevs:...
226

048d87199   Maxim Levitsky   mtd: blktrans: Ho...
227
  unlock:
342ff28f5   Brian Norris   mtd: mtd_blkdevs:...
228
  	dev->open++;
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
229
  	mutex_unlock(&dev->lock);
f3c63795e   Brian Norris   mtd: blkdevs: fix...
230
  	mutex_unlock(&mtd_table_mutex);
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
231
  	blktrans_dev_put(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
  	return ret;
94735ec40   Artem Bityutskiy   mtd: mtd_blkdevs:...
233
234
235
236
237
238
239
240
  
  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);
f3c63795e   Brian Norris   mtd: blkdevs: fix...
241
  	mutex_unlock(&mtd_table_mutex);
94735ec40   Artem Bityutskiy   mtd: mtd_blkdevs:...
242
243
  	blktrans_dev_put(dev);
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
  }
db2a144be   Al Viro   block_device_oper...
245
  static void blktrans_release(struct gendisk *disk, fmode_t mode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
  {
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
247
  	struct mtd_blktrans_dev *dev = blktrans_dev_get(disk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
248

048d87199   Maxim Levitsky   mtd: blktrans: Ho...
249
  	if (!dev)
db2a144be   Al Viro   block_device_oper...
250
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251

073db4a51   Brian Norris   mtd: fix: avoid r...
252
  	mutex_lock(&mtd_table_mutex);
f3c63795e   Brian Norris   mtd: blkdevs: fix...
253
  	mutex_lock(&dev->lock);
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
254

008c751ec   Maxim Levitsky   mtd: allow to unl...
255
  	if (--dev->open)
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
256
  		goto unlock;
008c751ec   Maxim Levitsky   mtd: allow to unl...
257
258
259
260
  	kref_put(&dev->ref, blktrans_dev_release);
  	module_put(dev->tr->owner);
  
  	if (dev->mtd) {
a8ca889ed   Al Viro   mtd_blktrans_ops-...
261
262
  		if (dev->tr->release)
  			dev->tr->release(dev);
008c751ec   Maxim Levitsky   mtd: allow to unl...
263
264
  		__put_mtd_device(dev->mtd);
  	}
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
265
266
  unlock:
  	mutex_unlock(&dev->lock);
f3c63795e   Brian Norris   mtd: blkdevs: fix...
267
  	mutex_unlock(&mtd_table_mutex);
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
268
  	blktrans_dev_put(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
  }
a885c8c43   Christoph Hellwig   [PATCH] Add block...
270
271
  static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo)
  {
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
272
273
274
275
276
277
278
  	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...
279

048d87199   Maxim Levitsky   mtd: blktrans: Ho...
280
281
  	if (!dev->mtd)
  		goto unlock;
c4a3f13c2   Brian Norris   mtd: blktrans: us...
282
  	ret = dev->tr->getgeo ? dev->tr->getgeo(dev, geo) : -ENOTTY;
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
283
284
285
286
  unlock:
  	mutex_unlock(&dev->lock);
  	blktrans_dev_put(dev);
  	return ret;
a885c8c43   Christoph Hellwig   [PATCH] Add block...
287
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288

af0e2a0a8   Al Viro   [PATCH] switch mt...
289
  static int blktrans_ioctl(struct block_device *bdev, fmode_t mode,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
291
  			      unsigned int cmd, unsigned long arg)
  {
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
292
293
294
295
296
297
298
299
300
301
  	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
302
303
304
  
  	switch (cmd) {
  	case BLKFLSBUF:
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
305
  		ret = dev->tr->flush ? dev->tr->flush(dev) : 0;
007c2d876   Dan Carpenter   mtd: return succe...
306
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307
  	default:
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
308
  		ret = -ENOTTY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
309
  	}
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
310
311
312
313
  unlock:
  	mutex_unlock(&dev->lock);
  	blktrans_dev_put(dev);
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
314
  }
9329c5eb5   Ezequiel Garcia   mtd: mtd_blkdev: ...
315
  static const struct block_device_operations mtd_block_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
316
  	.owner		= THIS_MODULE,
af0e2a0a8   Al Viro   [PATCH] switch mt...
317
318
  	.open		= blktrans_open,
  	.release	= blktrans_release,
8a6cfeb6d   Arnd Bergmann   block: push down ...
319
  	.ioctl		= blktrans_ioctl,
a885c8c43   Christoph Hellwig   [PATCH] Add block...
320
  	.getgeo		= blktrans_getgeo,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
321
  };
891b7c5fb   Jens Axboe   mtd_blkdevs: conv...
322
323
324
  static const struct blk_mq_ops mtd_mq_ops = {
  	.queue_rq	= mtd_queue_rq,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
325
326
327
  int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
  {
  	struct mtd_blktrans_ops *tr = new->tr;
71a928c0e   Chris Malley   [MTD] Use list_fo...
328
  	struct mtd_blktrans_dev *d;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
329
330
  	int last_devnum = -1;
  	struct gendisk *gd;
a86386225   Maxim Levitsky   mtd: blktrans: re...
331
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
332

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

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
371
372
  	list_add_tail(&new->list, &tr->devs);
   added:
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
373
  	mutex_unlock(&blktrans_ref_mutex);
ce37ab42a   David Woodhouse   [MTD] Always init...
374
  	mutex_init(&new->lock);
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
375
  	kref_init(&new->ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376
377
  	if (!tr->writesect)
  		new->readonly = 1;
a86386225   Maxim Levitsky   mtd: blktrans: re...
378
379
  	/* Create gendisk */
  	ret = -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
  	gd = alloc_disk(1 << tr->part_bits);
a86386225   Maxim Levitsky   mtd: blktrans: re...
381
382
383
384
385
386
  
  	if (!gd)
  		goto error2;
  
  	new->disk = gd;
  	gd->private_data = new;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387
388
  	gd->major = tr->major;
  	gd->first_minor = (new->devnum) << tr->part_bits;
9329c5eb5   Ezequiel Garcia   mtd: mtd_blkdev: ...
389
  	gd->fops = &mtd_block_ops;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
390

65a8de36b   Todd Poynor   [MTD] mtd_blkdevs...
391
392
393
394
395
396
397
398
399
400
401
402
  	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
403

2ce401d56   Peng Fan   mtd: blktrans: fi...
404
  	set_capacity(gd, ((u64)new->size * tr->blksize) >> 9);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405

a86386225   Maxim Levitsky   mtd: blktrans: re...
406
407
  	/* Create the request queue */
  	spin_lock_init(&new->queue_lock);
891b7c5fb   Jens Axboe   mtd_blkdevs: conv...
408
  	INIT_LIST_HEAD(&new->rq_list);
a86386225   Maxim Levitsky   mtd: blktrans: re...
409

891b7c5fb   Jens Axboe   mtd_blkdevs: conv...
410
411
  	new->tag_set = kzalloc(sizeof(*new->tag_set), GFP_KERNEL);
  	if (!new->tag_set)
a86386225   Maxim Levitsky   mtd: blktrans: re...
412
  		goto error3;
891b7c5fb   Jens Axboe   mtd_blkdevs: conv...
413
414
415
416
417
418
419
  	new->rq = blk_mq_init_sq_queue(new->tag_set, &mtd_mq_ops, 2,
  				BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_BLOCKING);
  	if (IS_ERR(new->rq)) {
  		ret = PTR_ERR(new->rq);
  		new->rq = NULL;
  		goto error4;
  	}
566c0d6a7   Roman Peniaev   mtd: mtd_blkdevs:...
420
  	if (tr->flush)
fec3ff5d1   Jens Axboe   mtd: switch to us...
421
  		blk_queue_write_cache(new->rq, true, false);
566c0d6a7   Roman Peniaev   mtd: mtd_blkdevs:...
422

a86386225   Maxim Levitsky   mtd: blktrans: re...
423
424
  	new->rq->queuedata = new;
  	blk_queue_logical_block_size(new->rq, tr->blksize);
8b904b5b6   Bart Van Assche   block: Use blk_qu...
425
426
  	blk_queue_flag_set(QUEUE_FLAG_NONROT, new->rq);
  	blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, new->rq);
16f7eca58   Dan McGee   mtd: mark block d...
427

115ee88c1   Jarkko Lavinen   mtd_blkdevs: Set ...
428
  	if (tr->discard) {
8b904b5b6   Bart Van Assche   block: Use blk_qu...
429
  		blk_queue_flag_set(QUEUE_FLAG_DISCARD, new->rq);
2bb4cd5cc   Jens Axboe   block: have drive...
430
  		blk_queue_max_discard_sectors(new->rq, UINT_MAX);
115ee88c1   Jarkko Lavinen   mtd_blkdevs: Set ...
431
  	}
a86386225   Maxim Levitsky   mtd: blktrans: re...
432
433
  
  	gd->queue = new->rq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
434
435
  	if (new->readonly)
  		set_disk_ro(gd, 1);
fef912bf8   Hannes Reinecke   block: genhd: add...
436
  	device_add_disk(&new->mtd->dev, gd, NULL);
026ec5788   Maxim Levitsky   mtd: blktrans: al...
437

133fa8c7d   Maxim Levitsky   mtd: Few follow u...
438
439
  	if (new->disk_attributes) {
  		ret = sysfs_create_group(&disk_to_dev(gd)->kobj,
026ec5788   Maxim Levitsky   mtd: blktrans: al...
440
  					new->disk_attributes);
133fa8c7d   Maxim Levitsky   mtd: Few follow u...
441
442
  		WARN_ON(ret);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
443
  	return 0;
a86386225   Maxim Levitsky   mtd: blktrans: re...
444
  error4:
891b7c5fb   Jens Axboe   mtd_blkdevs: conv...
445
  	kfree(new->tag_set);
a86386225   Maxim Levitsky   mtd: blktrans: re...
446
447
448
449
450
  error3:
  	put_disk(new->disk);
  error2:
  	list_del(&new->list);
  error1:
a86386225   Maxim Levitsky   mtd: blktrans: re...
451
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
452
453
454
455
  }
  
  int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
  {
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
456
  	unsigned long flags;
b3561ea94   Jean Delvare   Clean up mutex_tr...
457
  	if (mutex_trylock(&mtd_table_mutex)) {
48b192686   Ingo Molnar   [PATCH] sem2mutex...
458
  		mutex_unlock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
459
460
  		BUG();
  	}
026ec5788   Maxim Levitsky   mtd: blktrans: al...
461
462
463
  	if (old->disk_attributes)
  		sysfs_remove_group(&disk_to_dev(old->disk)->kobj,
  						old->disk_attributes);
dba76c033   Maxim Levitsky   mtd: blkdevs: fix...
464
465
  	/* Stop new requests to arrive */
  	del_gendisk(old->disk);
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
466
467
468
  	/* Kill current requests */
  	spin_lock_irqsave(&old->queue_lock, flags);
  	old->rq->queuedata = NULL;
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
469
  	spin_unlock_irqrestore(&old->queue_lock, flags);
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
470

891b7c5fb   Jens Axboe   mtd_blkdevs: conv...
471
472
473
474
475
  	/* freeze+quiesce queue to ensure all requests are flushed */
  	blk_mq_freeze_queue(old->rq);
  	blk_mq_quiesce_queue(old->rq);
  	blk_mq_unquiesce_queue(old->rq);
  	blk_mq_unfreeze_queue(old->rq);
008c751ec   Maxim Levitsky   mtd: allow to unl...
476
477
  	/* 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...
478
  	mutex_lock(&old->lock);
008c751ec   Maxim Levitsky   mtd: allow to unl...
479
480
481
482
  	if (old->open) {
  		if (old->tr->release)
  			old->tr->release(old);
  		__put_mtd_device(old->mtd);
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
483
  	}
048d87199   Maxim Levitsky   mtd: blktrans: Ho...
484
485
486
487
  	old->mtd = NULL;
  
  	mutex_unlock(&old->lock);
  	blktrans_dev_put(old);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
488
489
490
491
492
  	return 0;
  }
  
  static void blktrans_notify_remove(struct mtd_info *mtd)
  {
71a928c0e   Chris Malley   [MTD] Use list_fo...
493
494
  	struct mtd_blktrans_ops *tr;
  	struct mtd_blktrans_dev *dev, *next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495

71a928c0e   Chris Malley   [MTD] Use list_fo...
496
497
  	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
498
499
  			if (dev->mtd == mtd)
  				tr->remove_dev(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
500
501
502
503
  }
  
  static void blktrans_notify_add(struct mtd_info *mtd)
  {
71a928c0e   Chris Malley   [MTD] Use list_fo...
504
  	struct mtd_blktrans_ops *tr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505
506
507
  
  	if (mtd->type == MTD_ABSENT)
  		return;
71a928c0e   Chris Malley   [MTD] Use list_fo...
508
  	list_for_each_entry(tr, &blktrans_majors, list)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
509
  		tr->add_mtd(tr, mtd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
510
511
512
513
514
515
  }
  
  static struct mtd_notifier blktrans_notifier = {
  	.add = blktrans_notify_add,
  	.remove = blktrans_notify_remove,
  };
97894cda5   Thomas Gleixner   [MTD] core: Clean...
516

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
517
518
  int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
  {
f1332ba2f   Ben Hutchings   mtd: Introduce an...
519
520
  	struct mtd_info *mtd;
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
521

97894cda5   Thomas Gleixner   [MTD] core: Clean...
522
  	/* Register the notifier if/when the first device type is
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523
524
525
526
  	   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
527

48b192686   Ingo Molnar   [PATCH] sem2mutex...
528
  	mutex_lock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
529
530
  
  	ret = register_blkdev(tr->major, tr->name);
6fe4c5903   Frank Li   MTD: Fix wrong ch...
531
  	if (ret < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
532
533
534
  		printk(KERN_WARNING "Unable to register %s block device on major %d: %d
  ",
  		       tr->name, tr->major, ret);
48b192686   Ingo Molnar   [PATCH] sem2mutex...
535
  		mutex_unlock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
536
537
  		return ret;
  	}
eae9acd13   David Woodhouse   Support 'discard ...
538

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

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
543
544
  	INIT_LIST_HEAD(&tr->devs);
  	list_add(&tr->list, &blktrans_majors);
f1332ba2f   Ben Hutchings   mtd: Introduce an...
545
546
547
  	mtd_for_each_device(mtd)
  		if (mtd->type != MTD_ABSENT)
  			tr->add_mtd(tr, mtd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
548

48b192686   Ingo Molnar   [PATCH] sem2mutex...
549
  	mutex_unlock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
550
551
552
553
554
  	return 0;
  }
  
  int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr)
  {
71a928c0e   Chris Malley   [MTD] Use list_fo...
555
  	struct mtd_blktrans_dev *dev, *next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
556

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

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

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

373ebfbf1   Eric Sesterhenn   BUG_ON() Conversi...
567
  	BUG_ON(!list_empty(&tr->devs));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
  	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'");