diff options
author | Ezequiel Garcia <ezequiel.garcia@free-electrons.com> | 2014-02-25 11:25:22 -0500 |
---|---|---|
committer | Artem Bityutskiy <artem.bityutskiy@linux.intel.com> | 2014-02-28 09:29:48 -0500 |
commit | 9d54c8a33eec78289b1b3f6e10874719c27ce0a7 (patch) | |
tree | ba34d64d9dcc1b923f0556da5678ab854b4c529d /drivers/mtd/ubi | |
parent | 5547fec74a566e1f5e00a937b9a367f7c6a94a8b (diff) |
UBI: R/O block driver on top of UBI volumes
This commit introduces read-only block device emulation on top of UBI volumes.
Given UBI takes care of wear leveling and bad block management it's possible
to add a thin layer to enable block device access to UBI volumes.
This allows to use a block-oriented filesystem on a flash device.
The UBI block devices are meant to be used in conjunction with any
regular, block-oriented file system (e.g. ext4), although it's primarily
targeted at read-only file systems, such as squashfs.
Block devices are created upon user request through new ioctls:
UBI_IOCVOLATTBLK to attach and UBI_IOCVOLDETBLK to detach.
Also, a new UBI module parameter is added 'ubi.block'. This parameter is
needed in order to attach a block device on boot-up time, allowing to
mount the rootfs on a ubiblock device.
For instance, you could have these kernel parameters:
ubi.mtd=5 ubi.block=0,0 root=/dev/ubiblock0_0
Or, if you compile ubi as a module:
$ modprobe ubi mtd=/dev/mtd5 block=/dev/ubi0_0
Artem: amend commentaries and massage the patch a little bit.
Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
Diffstat (limited to 'drivers/mtd/ubi')
-rw-r--r-- | drivers/mtd/ubi/Kconfig | 15 | ||||
-rw-r--r-- | drivers/mtd/ubi/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/ubi/block.c | 646 | ||||
-rw-r--r-- | drivers/mtd/ubi/build.c | 11 | ||||
-rw-r--r-- | drivers/mtd/ubi/cdev.c | 20 | ||||
-rw-r--r-- | drivers/mtd/ubi/ubi.h | 14 |
6 files changed, 707 insertions, 0 deletions
diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig index 36663af56d89..783fb186d876 100644 --- a/drivers/mtd/ubi/Kconfig +++ b/drivers/mtd/ubi/Kconfig | |||
@@ -87,4 +87,19 @@ config MTD_UBI_GLUEBI | |||
87 | work on top of UBI. Do not enable this unless you use legacy | 87 | work on top of UBI. Do not enable this unless you use legacy |
88 | software. | 88 | software. |
89 | 89 | ||
90 | config MTD_UBI_BLOCK | ||
91 | bool "Read-only block devices on top of UBI volumes" | ||
92 | default n | ||
93 | help | ||
94 | This option enables read-only UBI block devices support. UBI block | ||
95 | devices will be layered on top of UBI volumes, which means that the | ||
96 | UBI driver will transparently handle things like bad eraseblocks and | ||
97 | bit-flips. You can put any block-oriented file system on top of UBI | ||
98 | volumes in read-only mode (e.g., ext4), but it is probably most | ||
99 | practical for read-only file systems, like squashfs. | ||
100 | |||
101 | When selected, this feature will be built in the UBI driver. | ||
102 | |||
103 | If in doubt, say "N". | ||
104 | |||
90 | endif # MTD_UBI | 105 | endif # MTD_UBI |
diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile index b46b0c978581..4e3c3d70d8c3 100644 --- a/drivers/mtd/ubi/Makefile +++ b/drivers/mtd/ubi/Makefile | |||
@@ -3,5 +3,6 @@ obj-$(CONFIG_MTD_UBI) += ubi.o | |||
3 | ubi-y += vtbl.o vmt.o upd.o build.o cdev.o kapi.o eba.o io.o wl.o attach.o | 3 | ubi-y += vtbl.o vmt.o upd.o build.o cdev.o kapi.o eba.o io.o wl.o attach.o |
4 | ubi-y += misc.o debug.o | 4 | ubi-y += misc.o debug.o |
5 | ubi-$(CONFIG_MTD_UBI_FASTMAP) += fastmap.o | 5 | ubi-$(CONFIG_MTD_UBI_FASTMAP) += fastmap.o |
6 | ubi-$(CONFIG_MTD_UBI_BLOCK) += block.o | ||
6 | 7 | ||
7 | obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o | 8 | obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o |
diff --git a/drivers/mtd/ubi/block.c b/drivers/mtd/ubi/block.c new file mode 100644 index 000000000000..cea7d1cf9d04 --- /dev/null +++ b/drivers/mtd/ubi/block.c | |||
@@ -0,0 +1,646 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2014 Ezequiel Garcia | ||
3 | * Copyright (c) 2011 Free Electrons | ||
4 | * | ||
5 | * Driver parameter handling strongly based on drivers/mtd/ubi/build.c | ||
6 | * Copyright (c) International Business Machines Corp., 2006 | ||
7 | * Copyright (c) Nokia Corporation, 2007 | ||
8 | * Authors: Artem Bityutskiy, Frank Haverkamp | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation, version 2. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See | ||
17 | * the GNU General Public License for more details. | ||
18 | */ | ||
19 | |||
20 | /* | ||
21 | * Read-only block devices on top of UBI volumes | ||
22 | * | ||
23 | * A simple implementation to allow a block device to be layered on top of a | ||
24 | * UBI volume. The implementation is provided by creating a static 1-to-1 | ||
25 | * mapping between the block device and the UBI volume. | ||
26 | * | ||
27 | * The addressed byte is obtained from the addressed block sector, which is | ||
28 | * mapped linearly into the corresponding LEB: | ||
29 | * | ||
30 | * LEB number = addressed byte / LEB size | ||
31 | * | ||
32 | * This feature is compiled in the UBI core, and adds a new 'block' parameter | ||
33 | * to allow early block device attaching. Runtime block attach/detach for UBI | ||
34 | * volumes is provided through two new UBI ioctls: UBI_IOCVOLATTBLK and | ||
35 | * UBI_IOCVOLDETBLK. | ||
36 | */ | ||
37 | |||
38 | #include <linux/module.h> | ||
39 | #include <linux/init.h> | ||
40 | #include <linux/err.h> | ||
41 | #include <linux/kernel.h> | ||
42 | #include <linux/list.h> | ||
43 | #include <linux/mutex.h> | ||
44 | #include <linux/slab.h> | ||
45 | #include <linux/vmalloc.h> | ||
46 | #include <linux/mtd/ubi.h> | ||
47 | #include <linux/workqueue.h> | ||
48 | #include <linux/blkdev.h> | ||
49 | #include <linux/hdreg.h> | ||
50 | #include <asm/div64.h> | ||
51 | |||
52 | #include "ubi-media.h" | ||
53 | #include "ubi.h" | ||
54 | |||
55 | /* Maximum number of supported devices */ | ||
56 | #define UBIBLOCK_MAX_DEVICES 32 | ||
57 | |||
58 | /* Maximum length of the 'block=' parameter */ | ||
59 | #define UBIBLOCK_PARAM_LEN 63 | ||
60 | |||
61 | /* Maximum number of comma-separated items in the 'block=' parameter */ | ||
62 | #define UBIBLOCK_PARAM_COUNT 2 | ||
63 | |||
64 | struct ubiblock_param { | ||
65 | int ubi_num; | ||
66 | int vol_id; | ||
67 | char name[UBIBLOCK_PARAM_LEN+1]; | ||
68 | }; | ||
69 | |||
70 | /* Numbers of elements set in the @ubiblock_param array */ | ||
71 | static int ubiblock_devs __initdata; | ||
72 | |||
73 | /* MTD devices specification parameters */ | ||
74 | static struct ubiblock_param ubiblock_param[UBIBLOCK_MAX_DEVICES] __initdata; | ||
75 | |||
76 | struct ubiblock { | ||
77 | struct ubi_volume_desc *desc; | ||
78 | int ubi_num; | ||
79 | int vol_id; | ||
80 | int refcnt; | ||
81 | int leb_size; | ||
82 | |||
83 | struct gendisk *gd; | ||
84 | struct request_queue *rq; | ||
85 | |||
86 | struct workqueue_struct *wq; | ||
87 | struct work_struct work; | ||
88 | |||
89 | struct mutex dev_mutex; | ||
90 | spinlock_t queue_lock; | ||
91 | struct list_head list; | ||
92 | }; | ||
93 | |||
94 | /* Linked list of all ubiblock instances */ | ||
95 | static LIST_HEAD(ubiblock_devices); | ||
96 | static DEFINE_MUTEX(devices_mutex); | ||
97 | static int ubiblock_major; | ||
98 | |||
99 | static int __init ubiblock_set_param(const char *val, | ||
100 | const struct kernel_param *kp) | ||
101 | { | ||
102 | int i, ret; | ||
103 | size_t len; | ||
104 | struct ubiblock_param *param; | ||
105 | char buf[UBIBLOCK_PARAM_LEN]; | ||
106 | char *pbuf = &buf[0]; | ||
107 | char *tokens[UBIBLOCK_PARAM_COUNT]; | ||
108 | |||
109 | if (!val) | ||
110 | return -EINVAL; | ||
111 | |||
112 | len = strnlen(val, UBIBLOCK_PARAM_LEN); | ||
113 | if (len == 0) { | ||
114 | ubi_warn("block: empty 'block=' parameter - ignored\n"); | ||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | if (len == UBIBLOCK_PARAM_LEN) { | ||
119 | ubi_err("block: parameter \"%s\" is too long, max. is %d\n", | ||
120 | val, UBIBLOCK_PARAM_LEN); | ||
121 | return -EINVAL; | ||
122 | } | ||
123 | |||
124 | strcpy(buf, val); | ||
125 | |||
126 | /* Get rid of the final newline */ | ||
127 | if (buf[len - 1] == '\n') | ||
128 | buf[len - 1] = '\0'; | ||
129 | |||
130 | for (i = 0; i < UBIBLOCK_PARAM_COUNT; i++) | ||
131 | tokens[i] = strsep(&pbuf, ","); | ||
132 | |||
133 | param = &ubiblock_param[ubiblock_devs]; | ||
134 | if (tokens[1]) { | ||
135 | /* Two parameters: can be 'ubi, vol_id' or 'ubi, vol_name' */ | ||
136 | ret = kstrtoint(tokens[0], 10, ¶m->ubi_num); | ||
137 | if (ret < 0) | ||
138 | return -EINVAL; | ||
139 | |||
140 | /* Second param can be a number or a name */ | ||
141 | ret = kstrtoint(tokens[1], 10, ¶m->vol_id); | ||
142 | if (ret < 0) { | ||
143 | param->vol_id = -1; | ||
144 | strcpy(param->name, tokens[1]); | ||
145 | } | ||
146 | |||
147 | } else { | ||
148 | /* One parameter: must be device path */ | ||
149 | strcpy(param->name, tokens[0]); | ||
150 | param->ubi_num = -1; | ||
151 | param->vol_id = -1; | ||
152 | } | ||
153 | |||
154 | ubiblock_devs++; | ||
155 | |||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | static const struct kernel_param_ops ubiblock_param_ops = { | ||
160 | .set = ubiblock_set_param, | ||
161 | }; | ||
162 | module_param_cb(block, &ubiblock_param_ops, NULL, 0); | ||
163 | MODULE_PARM_DESC(block, "Attach block devices to UBI volumes. Parameter format: block=<path|dev,num|dev,name>.\n" | ||
164 | "Multiple \"block\" parameters may be specified.\n" | ||
165 | "UBI volumes may be specified by their number, name, or path to the device node.\n" | ||
166 | "Examples\n" | ||
167 | "Using the UBI volume path:\n" | ||
168 | "ubi.block=/dev/ubi0_0\n" | ||
169 | "Using the UBI device, and the volume name:\n" | ||
170 | "ubi.block=0,rootfs\n" | ||
171 | "Using both UBI device number and UBI volume number:\n" | ||
172 | "ubi.block=0,0\n"); | ||
173 | |||
174 | static struct ubiblock *find_dev_nolock(int ubi_num, int vol_id) | ||
175 | { | ||
176 | struct ubiblock *dev; | ||
177 | |||
178 | list_for_each_entry(dev, &ubiblock_devices, list) | ||
179 | if (dev->ubi_num == ubi_num && dev->vol_id == vol_id) | ||
180 | return dev; | ||
181 | return NULL; | ||
182 | } | ||
183 | |||
184 | static int ubiblock_read_to_buf(struct ubiblock *dev, char *buffer, | ||
185 | int leb, int offset, int len) | ||
186 | { | ||
187 | int ret; | ||
188 | |||
189 | ret = ubi_read(dev->desc, leb, buffer, offset, len); | ||
190 | if (ret) { | ||
191 | ubi_err("%s ubi_read error %d", | ||
192 | dev->gd->disk_name, ret); | ||
193 | return ret; | ||
194 | } | ||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | static int ubiblock_read(struct ubiblock *dev, char *buffer, | ||
199 | sector_t sec, int len) | ||
200 | { | ||
201 | int ret, leb, offset; | ||
202 | int bytes_left = len; | ||
203 | int to_read = len; | ||
204 | loff_t pos = sec << 9; | ||
205 | |||
206 | /* Get LEB:offset address to read from */ | ||
207 | offset = do_div(pos, dev->leb_size); | ||
208 | leb = pos; | ||
209 | |||
210 | while (bytes_left) { | ||
211 | /* | ||
212 | * We can only read one LEB at a time. Therefore if the read | ||
213 | * length is larger than one LEB size, we split the operation. | ||
214 | */ | ||
215 | if (offset + to_read > dev->leb_size) | ||
216 | to_read = dev->leb_size - offset; | ||
217 | |||
218 | ret = ubiblock_read_to_buf(dev, buffer, leb, offset, to_read); | ||
219 | if (ret) | ||
220 | return ret; | ||
221 | |||
222 | buffer += to_read; | ||
223 | bytes_left -= to_read; | ||
224 | to_read = bytes_left; | ||
225 | leb += 1; | ||
226 | offset = 0; | ||
227 | } | ||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | static int do_ubiblock_request(struct ubiblock *dev, struct request *req) | ||
232 | { | ||
233 | int len, ret; | ||
234 | sector_t sec; | ||
235 | |||
236 | if (req->cmd_type != REQ_TYPE_FS) | ||
237 | return -EIO; | ||
238 | |||
239 | if (blk_rq_pos(req) + blk_rq_cur_sectors(req) > | ||
240 | get_capacity(req->rq_disk)) | ||
241 | return -EIO; | ||
242 | |||
243 | if (rq_data_dir(req) != READ) | ||
244 | return -ENOSYS; /* Write not implemented */ | ||
245 | |||
246 | sec = blk_rq_pos(req); | ||
247 | len = blk_rq_cur_bytes(req); | ||
248 | |||
249 | /* | ||
250 | * Let's prevent the device from being removed while we're doing I/O | ||
251 | * work. Notice that this means we serialize all the I/O operations, | ||
252 | * but it's probably of no impact given the NAND core serializes | ||
253 | * flash access anyway. | ||
254 | */ | ||
255 | mutex_lock(&dev->dev_mutex); | ||
256 | ret = ubiblock_read(dev, req->buffer, sec, len); | ||
257 | mutex_unlock(&dev->dev_mutex); | ||
258 | |||
259 | return ret; | ||
260 | } | ||
261 | |||
262 | static void ubiblock_do_work(struct work_struct *work) | ||
263 | { | ||
264 | struct ubiblock *dev = | ||
265 | container_of(work, struct ubiblock, work); | ||
266 | struct request_queue *rq = dev->rq; | ||
267 | struct request *req; | ||
268 | int res; | ||
269 | |||
270 | spin_lock_irq(rq->queue_lock); | ||
271 | |||
272 | req = blk_fetch_request(rq); | ||
273 | while (req) { | ||
274 | |||
275 | spin_unlock_irq(rq->queue_lock); | ||
276 | res = do_ubiblock_request(dev, req); | ||
277 | spin_lock_irq(rq->queue_lock); | ||
278 | |||
279 | /* | ||
280 | * If we're done with this request, | ||
281 | * we need to fetch a new one | ||
282 | */ | ||
283 | if (!__blk_end_request_cur(req, res)) | ||
284 | req = blk_fetch_request(rq); | ||
285 | } | ||
286 | |||
287 | spin_unlock_irq(rq->queue_lock); | ||
288 | } | ||
289 | |||
290 | static void ubiblock_request(struct request_queue *rq) | ||
291 | { | ||
292 | struct ubiblock *dev; | ||
293 | struct request *req; | ||
294 | |||
295 | dev = rq->queuedata; | ||
296 | |||
297 | if (!dev) | ||
298 | while ((req = blk_fetch_request(rq)) != NULL) | ||
299 | __blk_end_request_all(req, -ENODEV); | ||
300 | else | ||
301 | queue_work(dev->wq, &dev->work); | ||
302 | } | ||
303 | |||
304 | static int ubiblock_open(struct block_device *bdev, fmode_t mode) | ||
305 | { | ||
306 | struct ubiblock *dev = bdev->bd_disk->private_data; | ||
307 | int ret; | ||
308 | |||
309 | mutex_lock(&dev->dev_mutex); | ||
310 | if (dev->refcnt > 0) { | ||
311 | /* | ||
312 | * The volume is already open, just increase the reference | ||
313 | * counter. | ||
314 | */ | ||
315 | goto out_done; | ||
316 | } | ||
317 | |||
318 | /* | ||
319 | * We want users to be aware they should only mount us as read-only. | ||
320 | * It's just a paranoid check, as write requests will get rejected | ||
321 | * in any case. | ||
322 | */ | ||
323 | if (mode & FMODE_WRITE) { | ||
324 | ret = -EPERM; | ||
325 | goto out_unlock; | ||
326 | } | ||
327 | |||
328 | dev->desc = ubi_open_volume(dev->ubi_num, dev->vol_id, UBI_READONLY); | ||
329 | if (IS_ERR(dev->desc)) { | ||
330 | ubi_err("%s failed to open ubi volume %d_%d", | ||
331 | dev->gd->disk_name, dev->ubi_num, dev->vol_id); | ||
332 | ret = PTR_ERR(dev->desc); | ||
333 | dev->desc = NULL; | ||
334 | goto out_unlock; | ||
335 | } | ||
336 | |||
337 | out_done: | ||
338 | dev->refcnt++; | ||
339 | mutex_unlock(&dev->dev_mutex); | ||
340 | return 0; | ||
341 | |||
342 | out_unlock: | ||
343 | mutex_unlock(&dev->dev_mutex); | ||
344 | return ret; | ||
345 | } | ||
346 | |||
347 | static void ubiblock_release(struct gendisk *gd, fmode_t mode) | ||
348 | { | ||
349 | struct ubiblock *dev = gd->private_data; | ||
350 | |||
351 | mutex_lock(&dev->dev_mutex); | ||
352 | dev->refcnt--; | ||
353 | if (dev->refcnt == 0) { | ||
354 | ubi_close_volume(dev->desc); | ||
355 | dev->desc = NULL; | ||
356 | } | ||
357 | mutex_unlock(&dev->dev_mutex); | ||
358 | } | ||
359 | |||
360 | static int ubiblock_getgeo(struct block_device *bdev, struct hd_geometry *geo) | ||
361 | { | ||
362 | /* Some tools might require this information */ | ||
363 | geo->heads = 1; | ||
364 | geo->cylinders = 1; | ||
365 | geo->sectors = get_capacity(bdev->bd_disk); | ||
366 | geo->start = 0; | ||
367 | return 0; | ||
368 | } | ||
369 | |||
370 | static const struct block_device_operations ubiblock_ops = { | ||
371 | .owner = THIS_MODULE, | ||
372 | .open = ubiblock_open, | ||
373 | .release = ubiblock_release, | ||
374 | .getgeo = ubiblock_getgeo, | ||
375 | }; | ||
376 | |||
377 | int ubiblock_add(struct ubi_volume_info *vi) | ||
378 | { | ||
379 | struct ubiblock *dev; | ||
380 | struct gendisk *gd; | ||
381 | int disk_capacity; | ||
382 | int ret; | ||
383 | |||
384 | /* Check that the volume isn't already handled */ | ||
385 | mutex_lock(&devices_mutex); | ||
386 | if (find_dev_nolock(vi->ubi_num, vi->vol_id)) { | ||
387 | mutex_unlock(&devices_mutex); | ||
388 | return -EEXIST; | ||
389 | } | ||
390 | mutex_unlock(&devices_mutex); | ||
391 | |||
392 | dev = kzalloc(sizeof(struct ubiblock), GFP_KERNEL); | ||
393 | if (!dev) | ||
394 | return -ENOMEM; | ||
395 | |||
396 | mutex_init(&dev->dev_mutex); | ||
397 | |||
398 | dev->ubi_num = vi->ubi_num; | ||
399 | dev->vol_id = vi->vol_id; | ||
400 | dev->leb_size = vi->usable_leb_size; | ||
401 | |||
402 | /* Initialize the gendisk of this ubiblock device */ | ||
403 | gd = alloc_disk(1); | ||
404 | if (!gd) { | ||
405 | ubi_err("block: alloc_disk failed"); | ||
406 | ret = -ENODEV; | ||
407 | goto out_free_dev; | ||
408 | } | ||
409 | |||
410 | gd->fops = &ubiblock_ops; | ||
411 | gd->major = ubiblock_major; | ||
412 | gd->first_minor = dev->ubi_num * UBI_MAX_VOLUMES + dev->vol_id; | ||
413 | gd->private_data = dev; | ||
414 | sprintf(gd->disk_name, "ubiblock%d_%d", dev->ubi_num, dev->vol_id); | ||
415 | disk_capacity = (vi->size * vi->usable_leb_size) >> 9; | ||
416 | set_capacity(gd, disk_capacity); | ||
417 | dev->gd = gd; | ||
418 | |||
419 | spin_lock_init(&dev->queue_lock); | ||
420 | dev->rq = blk_init_queue(ubiblock_request, &dev->queue_lock); | ||
421 | if (!dev->rq) { | ||
422 | ubi_err("block: blk_init_queue failed"); | ||
423 | ret = -ENODEV; | ||
424 | goto out_put_disk; | ||
425 | } | ||
426 | |||
427 | dev->rq->queuedata = dev; | ||
428 | dev->gd->queue = dev->rq; | ||
429 | |||
430 | /* | ||
431 | * Create one workqueue per volume (per registered block device). | ||
432 | * Rembember workqueues are cheap, they're not threads. | ||
433 | */ | ||
434 | dev->wq = alloc_workqueue(gd->disk_name, 0, 0); | ||
435 | if (!dev->wq) | ||
436 | goto out_free_queue; | ||
437 | INIT_WORK(&dev->work, ubiblock_do_work); | ||
438 | |||
439 | mutex_lock(&devices_mutex); | ||
440 | list_add_tail(&dev->list, &ubiblock_devices); | ||
441 | mutex_unlock(&devices_mutex); | ||
442 | |||
443 | /* Must be the last step: anyone can call file ops from now on */ | ||
444 | add_disk(dev->gd); | ||
445 | ubi_msg("%s created from ubi%d:%d(%s)", | ||
446 | dev->gd->disk_name, dev->ubi_num, dev->vol_id, vi->name); | ||
447 | return 0; | ||
448 | |||
449 | out_free_queue: | ||
450 | blk_cleanup_queue(dev->rq); | ||
451 | out_put_disk: | ||
452 | put_disk(dev->gd); | ||
453 | out_free_dev: | ||
454 | kfree(dev); | ||
455 | |||
456 | return ret; | ||
457 | } | ||
458 | |||
459 | static void ubiblock_cleanup(struct ubiblock *dev) | ||
460 | { | ||
461 | del_gendisk(dev->gd); | ||
462 | blk_cleanup_queue(dev->rq); | ||
463 | ubi_msg("%s released", dev->gd->disk_name); | ||
464 | put_disk(dev->gd); | ||
465 | } | ||
466 | |||
467 | int ubiblock_del(struct ubi_volume_info *vi) | ||
468 | { | ||
469 | struct ubiblock *dev; | ||
470 | |||
471 | mutex_lock(&devices_mutex); | ||
472 | dev = find_dev_nolock(vi->ubi_num, vi->vol_id); | ||
473 | if (!dev) { | ||
474 | mutex_unlock(&devices_mutex); | ||
475 | return -ENODEV; | ||
476 | } | ||
477 | |||
478 | /* Found a device, let's lock it so we can check if it's busy */ | ||
479 | mutex_lock(&dev->dev_mutex); | ||
480 | if (dev->refcnt > 0) { | ||
481 | mutex_unlock(&dev->dev_mutex); | ||
482 | mutex_unlock(&devices_mutex); | ||
483 | return -EBUSY; | ||
484 | } | ||
485 | |||
486 | /* Remove from device list */ | ||
487 | list_del(&dev->list); | ||
488 | mutex_unlock(&devices_mutex); | ||
489 | |||
490 | /* Flush pending work and stop this workqueue */ | ||
491 | destroy_workqueue(dev->wq); | ||
492 | |||
493 | ubiblock_cleanup(dev); | ||
494 | mutex_unlock(&dev->dev_mutex); | ||
495 | kfree(dev); | ||
496 | return 0; | ||
497 | } | ||
498 | |||
499 | static void ubiblock_resize(struct ubi_volume_info *vi) | ||
500 | { | ||
501 | struct ubiblock *dev; | ||
502 | int disk_capacity; | ||
503 | |||
504 | /* | ||
505 | * Need to lock the device list until we stop using the device, | ||
506 | * otherwise the device struct might get released in 'ubiblock_del()'. | ||
507 | */ | ||
508 | mutex_lock(&devices_mutex); | ||
509 | dev = find_dev_nolock(vi->ubi_num, vi->vol_id); | ||
510 | if (!dev) { | ||
511 | mutex_unlock(&devices_mutex); | ||
512 | return; | ||
513 | } | ||
514 | |||
515 | mutex_lock(&dev->dev_mutex); | ||
516 | disk_capacity = (vi->size * vi->usable_leb_size) >> 9; | ||
517 | set_capacity(dev->gd, disk_capacity); | ||
518 | ubi_msg("%s resized to %d LEBs", dev->gd->disk_name, vi->size); | ||
519 | mutex_unlock(&dev->dev_mutex); | ||
520 | mutex_unlock(&devices_mutex); | ||
521 | } | ||
522 | |||
523 | static int ubiblock_notify(struct notifier_block *nb, | ||
524 | unsigned long notification_type, void *ns_ptr) | ||
525 | { | ||
526 | struct ubi_notification *nt = ns_ptr; | ||
527 | |||
528 | switch (notification_type) { | ||
529 | case UBI_VOLUME_ADDED: | ||
530 | /* | ||
531 | * We want to enforce explicit block device attaching for | ||
532 | * volumes, so when a volume is added we do nothing. | ||
533 | */ | ||
534 | break; | ||
535 | case UBI_VOLUME_REMOVED: | ||
536 | ubiblock_del(&nt->vi); | ||
537 | break; | ||
538 | case UBI_VOLUME_RESIZED: | ||
539 | ubiblock_resize(&nt->vi); | ||
540 | break; | ||
541 | default: | ||
542 | break; | ||
543 | } | ||
544 | return NOTIFY_OK; | ||
545 | } | ||
546 | |||
547 | static struct notifier_block ubiblock_notifier = { | ||
548 | .notifier_call = ubiblock_notify, | ||
549 | }; | ||
550 | |||
551 | static struct ubi_volume_desc * __init | ||
552 | open_volume_desc(const char *name, int ubi_num, int vol_id) | ||
553 | { | ||
554 | if (ubi_num == -1) | ||
555 | /* No ubi num, name must be a vol device path */ | ||
556 | return ubi_open_volume_path(name, UBI_READONLY); | ||
557 | else if (vol_id == -1) | ||
558 | /* No vol_id, must be vol_name */ | ||
559 | return ubi_open_volume_nm(ubi_num, name, UBI_READONLY); | ||
560 | else | ||
561 | return ubi_open_volume(ubi_num, vol_id, UBI_READONLY); | ||
562 | } | ||
563 | |||
564 | static int __init ubiblock_attach_from_param(void) | ||
565 | { | ||
566 | int i, ret; | ||
567 | struct ubiblock_param *p; | ||
568 | struct ubi_volume_desc *desc; | ||
569 | struct ubi_volume_info vi; | ||
570 | |||
571 | for (i = 0; i < ubiblock_devs; i++) { | ||
572 | p = &ubiblock_param[i]; | ||
573 | |||
574 | desc = open_volume_desc(p->name, p->ubi_num, p->vol_id); | ||
575 | if (IS_ERR(desc)) { | ||
576 | ubi_err("block: can't open volume, err=%ld\n", | ||
577 | PTR_ERR(desc)); | ||
578 | ret = PTR_ERR(desc); | ||
579 | break; | ||
580 | } | ||
581 | |||
582 | ubi_get_volume_info(desc, &vi); | ||
583 | ubi_close_volume(desc); | ||
584 | |||
585 | ret = ubiblock_add(&vi); | ||
586 | if (ret) { | ||
587 | ubi_err("block: can't add '%s' volume, err=%d\n", | ||
588 | vi.name, ret); | ||
589 | break; | ||
590 | } | ||
591 | } | ||
592 | return ret; | ||
593 | } | ||
594 | |||
595 | static void ubiblock_detach_all(void) | ||
596 | { | ||
597 | struct ubiblock *next; | ||
598 | struct ubiblock *dev; | ||
599 | |||
600 | list_for_each_entry_safe(dev, next, &ubiblock_devices, list) { | ||
601 | /* Flush pending work and stop workqueue */ | ||
602 | destroy_workqueue(dev->wq); | ||
603 | /* The module is being forcefully removed */ | ||
604 | WARN_ON(dev->desc); | ||
605 | /* Remove from device list */ | ||
606 | list_del(&dev->list); | ||
607 | ubiblock_cleanup(dev); | ||
608 | kfree(dev); | ||
609 | } | ||
610 | } | ||
611 | |||
612 | int __init ubiblock_init(void) | ||
613 | { | ||
614 | int ret; | ||
615 | |||
616 | ubiblock_major = register_blkdev(0, "ubiblock"); | ||
617 | if (ubiblock_major < 0) | ||
618 | return ubiblock_major; | ||
619 | |||
620 | /* Attach block devices from 'block=' module param */ | ||
621 | ret = ubiblock_attach_from_param(); | ||
622 | if (ret) | ||
623 | goto err_detach; | ||
624 | |||
625 | /* | ||
626 | * Block devices needs to be attached to volumes explicitly | ||
627 | * upon user request. So we ignore existing volumes. | ||
628 | */ | ||
629 | ret = ubi_register_volume_notifier(&ubiblock_notifier, 1); | ||
630 | if (ret) | ||
631 | goto err_unreg; | ||
632 | return 0; | ||
633 | |||
634 | err_unreg: | ||
635 | unregister_blkdev(ubiblock_major, "ubiblock"); | ||
636 | err_detach: | ||
637 | ubiblock_detach_all(); | ||
638 | return ret; | ||
639 | } | ||
640 | |||
641 | void __exit ubiblock_exit(void) | ||
642 | { | ||
643 | ubi_unregister_volume_notifier(&ubiblock_notifier); | ||
644 | ubiblock_detach_all(); | ||
645 | unregister_blkdev(ubiblock_major, "ubiblock"); | ||
646 | } | ||
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 57deae961429..6e30a3c280d0 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c | |||
@@ -1298,6 +1298,15 @@ static int __init ubi_init(void) | |||
1298 | } | 1298 | } |
1299 | } | 1299 | } |
1300 | 1300 | ||
1301 | err = ubiblock_init(); | ||
1302 | if (err) { | ||
1303 | ubi_err("block: cannot initialize, error %d", err); | ||
1304 | |||
1305 | /* See comment above re-ubi_is_module(). */ | ||
1306 | if (ubi_is_module()) | ||
1307 | goto out_detach; | ||
1308 | } | ||
1309 | |||
1301 | return 0; | 1310 | return 0; |
1302 | 1311 | ||
1303 | out_detach: | 1312 | out_detach: |
@@ -1326,6 +1335,8 @@ static void __exit ubi_exit(void) | |||
1326 | { | 1335 | { |
1327 | int i; | 1336 | int i; |
1328 | 1337 | ||
1338 | ubiblock_exit(); | ||
1339 | |||
1329 | for (i = 0; i < UBI_MAX_DEVICES; i++) | 1340 | for (i = 0; i < UBI_MAX_DEVICES; i++) |
1330 | if (ubi_devices[i]) { | 1341 | if (ubi_devices[i]) { |
1331 | mutex_lock(&ubi_devices_mutex); | 1342 | mutex_lock(&ubi_devices_mutex); |
diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 8ca49f2043e4..39d377489c29 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c | |||
@@ -561,6 +561,26 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd, | |||
561 | break; | 561 | break; |
562 | } | 562 | } |
563 | 563 | ||
564 | /* Attach a block device to an UBI volume */ | ||
565 | case UBI_IOCVOLATTBLK: | ||
566 | { | ||
567 | struct ubi_volume_info vi; | ||
568 | |||
569 | ubi_get_volume_info(desc, &vi); | ||
570 | err = ubiblock_add(&vi); | ||
571 | break; | ||
572 | } | ||
573 | |||
574 | /* Dettach a block device from an UBI volume */ | ||
575 | case UBI_IOCVOLDETBLK: | ||
576 | { | ||
577 | struct ubi_volume_info vi; | ||
578 | |||
579 | ubi_get_volume_info(desc, &vi); | ||
580 | err = ubiblock_del(&vi); | ||
581 | break; | ||
582 | } | ||
583 | |||
564 | default: | 584 | default: |
565 | err = -ENOTTY; | 585 | err = -ENOTTY; |
566 | break; | 586 | break; |
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 8ea6297a208f..e76ff98440a4 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h | |||
@@ -864,6 +864,20 @@ int ubi_update_fastmap(struct ubi_device *ubi); | |||
864 | int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai, | 864 | int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai, |
865 | int fm_anchor); | 865 | int fm_anchor); |
866 | 866 | ||
867 | /* block.c */ | ||
868 | #ifdef CONFIG_MTD_UBI_BLOCK | ||
869 | int ubiblock_init(void); | ||
870 | void ubiblock_exit(void); | ||
871 | int ubiblock_add(struct ubi_volume_info *vi); | ||
872 | int ubiblock_del(struct ubi_volume_info *vi); | ||
873 | #else | ||
874 | static inline int ubiblock_init(void) { return 0; } | ||
875 | static inline void ubiblock_exit(void) {} | ||
876 | static inline int ubiblock_add(struct ubi_volume_info *vi) { return -ENOTTY; } | ||
877 | static inline int ubiblock_del(struct ubi_volume_info *vi) { return -ENOTTY; } | ||
878 | #endif | ||
879 | |||
880 | |||
867 | /* | 881 | /* |
868 | * ubi_rb_for_each_entry - walk an RB-tree. | 882 | * ubi_rb_for_each_entry - walk an RB-tree. |
869 | * @rb: a pointer to type 'struct rb_node' to use as a loop counter | 883 | * @rb: a pointer to type 'struct rb_node' to use as a loop counter |