]>
Commit | Line | Data |
---|---|---|
1a59d1b8 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
801c135c AB |
2 | /* |
3 | * Copyright (c) International Business Machines Corp., 2006 | |
4 | * | |
801c135c AB |
5 | * Author: Artem Bityutskiy (Битюцкий Артём), Joern Engel |
6 | */ | |
7 | ||
8 | /* | |
2ba3d76a DP |
9 | * This is a small driver which implements fake MTD devices on top of UBI |
10 | * volumes. This sounds strange, but it is in fact quite useful to make | |
11 | * MTD-oriented software (including all the legacy software) work on top of | |
12 | * UBI. | |
801c135c AB |
13 | * |
14 | * Gluebi emulates MTD devices of "MTD_UBIVOLUME" type. Their minimal I/O unit | |
2ba3d76a | 15 | * size (@mtd->writesize) is equivalent to the UBI minimal I/O unit. The |
801c135c AB |
16 | * eraseblock size is equivalent to the logical eraseblock size of the volume. |
17 | */ | |
18 | ||
2ba3d76a DP |
19 | #include <linux/err.h> |
20 | #include <linux/list.h> | |
5a0e3ad6 | 21 | #include <linux/slab.h> |
2ba3d76a | 22 | #include <linux/sched.h> |
3013ee31 | 23 | #include <linux/math64.h> |
2ba3d76a DP |
24 | #include <linux/module.h> |
25 | #include <linux/mutex.h> | |
26 | #include <linux/mtd/ubi.h> | |
27 | #include <linux/mtd/mtd.h> | |
28 | #include "ubi-media.h" | |
29 | ||
30 | #define err_msg(fmt, ...) \ | |
e28453bb | 31 | pr_err("gluebi (pid %d): %s: " fmt "\n", \ |
2ba3d76a DP |
32 | current->pid, __func__, ##__VA_ARGS__) |
33 | ||
34 | /** | |
35 | * struct gluebi_device - a gluebi device description data structure. | |
36 | * @mtd: emulated MTD device description object | |
37 | * @refcnt: gluebi device reference count | |
38 | * @desc: UBI volume descriptor | |
39 | * @ubi_num: UBI device number this gluebi device works on | |
40 | * @vol_id: ID of UBI volume this gluebi device works on | |
41 | * @list: link in a list of gluebi devices | |
42 | */ | |
43 | struct gluebi_device { | |
44 | struct mtd_info mtd; | |
45 | int refcnt; | |
46 | struct ubi_volume_desc *desc; | |
47 | int ubi_num; | |
48 | int vol_id; | |
49 | struct list_head list; | |
50 | }; | |
51 | ||
52 | /* List of all gluebi devices */ | |
53 | static LIST_HEAD(gluebi_devices); | |
54 | static DEFINE_MUTEX(devices_mutex); | |
55 | ||
56 | /** | |
57 | * find_gluebi_nolock - find a gluebi device. | |
58 | * @ubi_num: UBI device number | |
59 | * @vol_id: volume ID | |
60 | * | |
61 | * This function seraches for gluebi device corresponding to UBI device | |
62 | * @ubi_num and UBI volume @vol_id. Returns the gluebi device description | |
63 | * object in case of success and %NULL in case of failure. The caller has to | |
64 | * have the &devices_mutex locked. | |
65 | */ | |
66 | static struct gluebi_device *find_gluebi_nolock(int ubi_num, int vol_id) | |
67 | { | |
68 | struct gluebi_device *gluebi; | |
69 | ||
70 | list_for_each_entry(gluebi, &gluebi_devices, list) | |
71 | if (gluebi->ubi_num == ubi_num && gluebi->vol_id == vol_id) | |
72 | return gluebi; | |
73 | return NULL; | |
74 | } | |
801c135c AB |
75 | |
76 | /** | |
77 | * gluebi_get_device - get MTD device reference. | |
78 | * @mtd: the MTD device description object | |
79 | * | |
80 | * This function is called every time the MTD device is being opened and | |
81 | * implements the MTD get_device() operation. Returns zero in case of success | |
82 | * and a negative error code in case of failure. | |
83 | */ | |
84 | static int gluebi_get_device(struct mtd_info *mtd) | |
85 | { | |
2ba3d76a DP |
86 | struct gluebi_device *gluebi; |
87 | int ubi_mode = UBI_READONLY; | |
801c135c | 88 | |
2ba3d76a DP |
89 | if (mtd->flags & MTD_WRITEABLE) |
90 | ubi_mode = UBI_READWRITE; | |
91 | ||
92 | gluebi = container_of(mtd, struct gluebi_device, mtd); | |
93 | mutex_lock(&devices_mutex); | |
94 | if (gluebi->refcnt > 0) { | |
801c135c AB |
95 | /* |
96 | * The MTD device is already referenced and this is just one | |
97 | * more reference. MTD allows many users to open the same | |
98 | * volume simultaneously and do not distinguish between | |
061eebba AM |
99 | * readers/writers/exclusive/meta openers as UBI does. So we do |
100 | * not open the UBI volume again - just increase the reference | |
801c135c AB |
101 | * counter and return. |
102 | */ | |
2ba3d76a DP |
103 | gluebi->refcnt += 1; |
104 | mutex_unlock(&devices_mutex); | |
801c135c AB |
105 | return 0; |
106 | } | |
107 | ||
108 | /* | |
109 | * This is the first reference to this UBI volume via the MTD device | |
110 | * interface. Open the corresponding volume in read-write mode. | |
111 | */ | |
2ba3d76a DP |
112 | gluebi->desc = ubi_open_volume(gluebi->ubi_num, gluebi->vol_id, |
113 | ubi_mode); | |
114 | if (IS_ERR(gluebi->desc)) { | |
115 | mutex_unlock(&devices_mutex); | |
2ba3d76a DP |
116 | return PTR_ERR(gluebi->desc); |
117 | } | |
118 | gluebi->refcnt += 1; | |
119 | mutex_unlock(&devices_mutex); | |
801c135c AB |
120 | return 0; |
121 | } | |
122 | ||
123 | /** | |
124 | * gluebi_put_device - put MTD device reference. | |
125 | * @mtd: the MTD device description object | |
126 | * | |
127 | * This function is called every time the MTD device is being put. Returns | |
128 | * zero in case of success and a negative error code in case of failure. | |
129 | */ | |
130 | static void gluebi_put_device(struct mtd_info *mtd) | |
131 | { | |
2ba3d76a DP |
132 | struct gluebi_device *gluebi; |
133 | ||
134 | gluebi = container_of(mtd, struct gluebi_device, mtd); | |
135 | mutex_lock(&devices_mutex); | |
136 | gluebi->refcnt -= 1; | |
137 | if (gluebi->refcnt == 0) | |
138 | ubi_close_volume(gluebi->desc); | |
2ba3d76a | 139 | mutex_unlock(&devices_mutex); |
801c135c AB |
140 | } |
141 | ||
142 | /** | |
143 | * gluebi_read - read operation of emulated MTD devices. | |
144 | * @mtd: MTD device description object | |
145 | * @from: absolute offset from where to read | |
146 | * @len: how many bytes to read | |
147 | * @retlen: count of read bytes is returned here | |
148 | * @buf: buffer to store the read data | |
149 | * | |
150 | * This function returns zero in case of success and a negative error code in | |
151 | * case of failure. | |
152 | */ | |
153 | static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len, | |
154 | size_t *retlen, unsigned char *buf) | |
155 | { | |
41c04384 | 156 | int err = 0, lnum, offs, bytes_left; |
2ba3d76a | 157 | struct gluebi_device *gluebi; |
801c135c | 158 | |
2ba3d76a | 159 | gluebi = container_of(mtd, struct gluebi_device, mtd); |
3013ee31 | 160 | lnum = div_u64_rem(from, mtd->erasesize, &offs); |
41c04384 EG |
161 | bytes_left = len; |
162 | while (bytes_left) { | |
801c135c AB |
163 | size_t to_read = mtd->erasesize - offs; |
164 | ||
41c04384 EG |
165 | if (to_read > bytes_left) |
166 | to_read = bytes_left; | |
801c135c | 167 | |
2ba3d76a | 168 | err = ubi_read(gluebi->desc, lnum, buf, offs, to_read); |
801c135c AB |
169 | if (err) |
170 | break; | |
171 | ||
172 | lnum += 1; | |
173 | offs = 0; | |
41c04384 | 174 | bytes_left -= to_read; |
801c135c AB |
175 | buf += to_read; |
176 | } | |
177 | ||
41c04384 | 178 | *retlen = len - bytes_left; |
801c135c AB |
179 | return err; |
180 | } | |
181 | ||
182 | /** | |
183 | * gluebi_write - write operation of emulated MTD devices. | |
184 | * @mtd: MTD device description object | |
185 | * @to: absolute offset where to write | |
186 | * @len: how many bytes to write | |
187 | * @retlen: count of written bytes is returned here | |
188 | * @buf: buffer with data to write | |
189 | * | |
190 | * This function returns zero in case of success and a negative error code in | |
191 | * case of failure. | |
192 | */ | |
193 | static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len, | |
2ba3d76a | 194 | size_t *retlen, const u_char *buf) |
801c135c | 195 | { |
41c04384 | 196 | int err = 0, lnum, offs, bytes_left; |
2ba3d76a | 197 | struct gluebi_device *gluebi; |
801c135c | 198 | |
2ba3d76a | 199 | gluebi = container_of(mtd, struct gluebi_device, mtd); |
3013ee31 | 200 | lnum = div_u64_rem(to, mtd->erasesize, &offs); |
801c135c AB |
201 | |
202 | if (len % mtd->writesize || offs % mtd->writesize) | |
203 | return -EINVAL; | |
204 | ||
41c04384 EG |
205 | bytes_left = len; |
206 | while (bytes_left) { | |
801c135c AB |
207 | size_t to_write = mtd->erasesize - offs; |
208 | ||
41c04384 EG |
209 | if (to_write > bytes_left) |
210 | to_write = bytes_left; | |
801c135c | 211 | |
b36a261e | 212 | err = ubi_leb_write(gluebi->desc, lnum, buf, offs, to_write); |
801c135c AB |
213 | if (err) |
214 | break; | |
215 | ||
216 | lnum += 1; | |
217 | offs = 0; | |
41c04384 | 218 | bytes_left -= to_write; |
801c135c AB |
219 | buf += to_write; |
220 | } | |
221 | ||
41c04384 | 222 | *retlen = len - bytes_left; |
801c135c AB |
223 | return err; |
224 | } | |
225 | ||
226 | /** | |
227 | * gluebi_erase - erase operation of emulated MTD devices. | |
228 | * @mtd: the MTD device description object | |
229 | * @instr: the erase operation description | |
230 | * | |
231 | * This function calls the erase callback when finishes. Returns zero in case | |
232 | * of success and a negative error code in case of failure. | |
233 | */ | |
234 | static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr) | |
235 | { | |
236 | int err, i, lnum, count; | |
2ba3d76a | 237 | struct gluebi_device *gluebi; |
801c135c | 238 | |
69423d99 | 239 | if (mtd_mod_by_ws(instr->addr, mtd) || mtd_mod_by_ws(instr->len, mtd)) |
801c135c AB |
240 | return -EINVAL; |
241 | ||
69423d99 AH |
242 | lnum = mtd_div_by_eb(instr->addr, mtd); |
243 | count = mtd_div_by_eb(instr->len, mtd); | |
2ba3d76a | 244 | gluebi = container_of(mtd, struct gluebi_device, mtd); |
801c135c | 245 | |
2ba3d76a DP |
246 | for (i = 0; i < count - 1; i++) { |
247 | err = ubi_leb_unmap(gluebi->desc, lnum + i); | |
801c135c AB |
248 | if (err) |
249 | goto out_err; | |
250 | } | |
801c135c AB |
251 | /* |
252 | * MTD erase operations are synchronous, so we have to make sure the | |
253 | * physical eraseblock is wiped out. | |
2ba3d76a DP |
254 | * |
255 | * Thus, perform leb_erase instead of leb_unmap operation - leb_erase | |
256 | * will wait for the end of operations | |
801c135c | 257 | */ |
2ba3d76a | 258 | err = ubi_leb_erase(gluebi->desc, lnum + i); |
801c135c AB |
259 | if (err) |
260 | goto out_err; | |
261 | ||
801c135c AB |
262 | return 0; |
263 | ||
264 | out_err: | |
69423d99 | 265 | instr->fail_addr = (long long)lnum * mtd->erasesize; |
801c135c AB |
266 | return err; |
267 | } | |
268 | ||
269 | /** | |
2ba3d76a DP |
270 | * gluebi_create - create a gluebi device for an UBI volume. |
271 | * @di: UBI device description object | |
272 | * @vi: UBI volume description object | |
801c135c | 273 | * |
2ba3d76a | 274 | * This function is called when a new UBI volume is created in order to create |
801c135c AB |
275 | * corresponding fake MTD device. Returns zero in case of success and a |
276 | * negative error code in case of failure. | |
277 | */ | |
2ba3d76a DP |
278 | static int gluebi_create(struct ubi_device_info *di, |
279 | struct ubi_volume_info *vi) | |
801c135c | 280 | { |
2ba3d76a DP |
281 | struct gluebi_device *gluebi, *g; |
282 | struct mtd_info *mtd; | |
801c135c | 283 | |
2ba3d76a DP |
284 | gluebi = kzalloc(sizeof(struct gluebi_device), GFP_KERNEL); |
285 | if (!gluebi) | |
801c135c AB |
286 | return -ENOMEM; |
287 | ||
2ba3d76a DP |
288 | mtd = &gluebi->mtd; |
289 | mtd->name = kmemdup(vi->name, vi->name_len + 1, GFP_KERNEL); | |
290 | if (!mtd->name) { | |
291 | kfree(gluebi); | |
292 | return -ENOMEM; | |
293 | } | |
294 | ||
295 | gluebi->vol_id = vi->vol_id; | |
c8cc4525 | 296 | gluebi->ubi_num = vi->ubi_num; |
801c135c | 297 | mtd->type = MTD_UBIVOLUME; |
2ba3d76a | 298 | if (!di->ro_mode) |
801c135c | 299 | mtd->flags = MTD_WRITEABLE; |
801c135c | 300 | mtd->owner = THIS_MODULE; |
2ba3d76a DP |
301 | mtd->writesize = di->min_io_size; |
302 | mtd->erasesize = vi->usable_leb_size; | |
3c3c10bb AB |
303 | mtd->_read = gluebi_read; |
304 | mtd->_write = gluebi_write; | |
305 | mtd->_erase = gluebi_erase; | |
306 | mtd->_get_device = gluebi_get_device; | |
307 | mtd->_put_device = gluebi_put_device; | |
801c135c | 308 | |
941dfb07 | 309 | /* |
2ba3d76a | 310 | * In case of dynamic a volume, MTD device size is just volume size. In |
941dfb07 | 311 | * case of a static volume the size is equivalent to the amount of data |
cbd8a9d2 | 312 | * bytes. |
941dfb07 | 313 | */ |
2ba3d76a DP |
314 | if (vi->vol_type == UBI_DYNAMIC_VOLUME) |
315 | mtd->size = (unsigned long long)vi->usable_leb_size * vi->size; | |
cbd8a9d2 | 316 | else |
2ba3d76a DP |
317 | mtd->size = vi->used_bytes; |
318 | ||
319 | /* Just a sanity check - make sure this gluebi device does not exist */ | |
320 | mutex_lock(&devices_mutex); | |
321 | g = find_gluebi_nolock(vi->ubi_num, vi->vol_id); | |
322 | if (g) | |
049333ce AB |
323 | err_msg("gluebi MTD device %d form UBI device %d volume %d already exists", |
324 | g->mtd.index, vi->ubi_num, vi->vol_id); | |
2ba3d76a | 325 | mutex_unlock(&devices_mutex); |
941dfb07 | 326 | |
ee0e87b1 | 327 | if (mtd_device_register(mtd, NULL, 0)) { |
2ba3d76a | 328 | err_msg("cannot add MTD device"); |
801c135c | 329 | kfree(mtd->name); |
2ba3d76a | 330 | kfree(gluebi); |
801c135c AB |
331 | return -ENFILE; |
332 | } | |
333 | ||
2ba3d76a DP |
334 | mutex_lock(&devices_mutex); |
335 | list_add_tail(&gluebi->list, &gluebi_devices); | |
336 | mutex_unlock(&devices_mutex); | |
801c135c AB |
337 | return 0; |
338 | } | |
339 | ||
340 | /** | |
2ba3d76a DP |
341 | * gluebi_remove - remove a gluebi device. |
342 | * @vi: UBI volume description object | |
801c135c | 343 | * |
2ba3d76a | 344 | * This function is called when an UBI volume is removed and it removes |
801c135c AB |
345 | * corresponding fake MTD device. Returns zero in case of success and a |
346 | * negative error code in case of failure. | |
347 | */ | |
2ba3d76a | 348 | static int gluebi_remove(struct ubi_volume_info *vi) |
801c135c | 349 | { |
2ba3d76a DP |
350 | int err = 0; |
351 | struct mtd_info *mtd; | |
352 | struct gluebi_device *gluebi; | |
353 | ||
354 | mutex_lock(&devices_mutex); | |
355 | gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id); | |
356 | if (!gluebi) { | |
049333ce AB |
357 | err_msg("got remove notification for unknown UBI device %d volume %d", |
358 | vi->ubi_num, vi->vol_id); | |
2ba3d76a DP |
359 | err = -ENOENT; |
360 | } else if (gluebi->refcnt) | |
361 | err = -EBUSY; | |
362 | else | |
363 | list_del(&gluebi->list); | |
364 | mutex_unlock(&devices_mutex); | |
365 | if (err) | |
366 | return err; | |
801c135c | 367 | |
2ba3d76a | 368 | mtd = &gluebi->mtd; |
ee0e87b1 | 369 | err = mtd_device_unregister(mtd); |
2ba3d76a | 370 | if (err) { |
049333ce AB |
371 | err_msg("cannot remove fake MTD device %d, UBI device %d, volume %d, error %d", |
372 | mtd->index, gluebi->ubi_num, gluebi->vol_id, err); | |
2ba3d76a DP |
373 | mutex_lock(&devices_mutex); |
374 | list_add_tail(&gluebi->list, &gluebi_devices); | |
375 | mutex_unlock(&devices_mutex); | |
801c135c | 376 | return err; |
2ba3d76a DP |
377 | } |
378 | ||
801c135c | 379 | kfree(mtd->name); |
2ba3d76a | 380 | kfree(gluebi); |
801c135c AB |
381 | return 0; |
382 | } | |
941dfb07 AB |
383 | |
384 | /** | |
2ba3d76a DP |
385 | * gluebi_updated - UBI volume was updated notifier. |
386 | * @vi: volume info structure | |
941dfb07 | 387 | * |
2ba3d76a DP |
388 | * This function is called every time an UBI volume is updated. It does nothing |
389 | * if te volume @vol is dynamic, and changes MTD device size if the | |
941dfb07 | 390 | * volume is static. This is needed because static volumes cannot be read past |
2ba3d76a DP |
391 | * data they contain. This function returns zero in case of success and a |
392 | * negative error code in case of error. | |
393 | */ | |
394 | static int gluebi_updated(struct ubi_volume_info *vi) | |
395 | { | |
396 | struct gluebi_device *gluebi; | |
397 | ||
398 | mutex_lock(&devices_mutex); | |
399 | gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id); | |
400 | if (!gluebi) { | |
401 | mutex_unlock(&devices_mutex); | |
049333ce AB |
402 | err_msg("got update notification for unknown UBI device %d volume %d", |
403 | vi->ubi_num, vi->vol_id); | |
2ba3d76a DP |
404 | return -ENOENT; |
405 | } | |
406 | ||
407 | if (vi->vol_type == UBI_STATIC_VOLUME) | |
408 | gluebi->mtd.size = vi->used_bytes; | |
409 | mutex_unlock(&devices_mutex); | |
410 | return 0; | |
411 | } | |
412 | ||
413 | /** | |
414 | * gluebi_resized - UBI volume was re-sized notifier. | |
415 | * @vi: volume info structure | |
416 | * | |
417 | * This function is called every time an UBI volume is re-size. It changes the | |
418 | * corresponding fake MTD device size. This function returns zero in case of | |
419 | * success and a negative error code in case of error. | |
420 | */ | |
421 | static int gluebi_resized(struct ubi_volume_info *vi) | |
422 | { | |
423 | struct gluebi_device *gluebi; | |
424 | ||
425 | mutex_lock(&devices_mutex); | |
426 | gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id); | |
427 | if (!gluebi) { | |
428 | mutex_unlock(&devices_mutex); | |
049333ce AB |
429 | err_msg("got update notification for unknown UBI device %d volume %d", |
430 | vi->ubi_num, vi->vol_id); | |
2ba3d76a DP |
431 | return -ENOENT; |
432 | } | |
433 | gluebi->mtd.size = vi->used_bytes; | |
434 | mutex_unlock(&devices_mutex); | |
435 | return 0; | |
436 | } | |
437 | ||
438 | /** | |
439 | * gluebi_notify - UBI notification handler. | |
440 | * @nb: registered notifier block | |
441 | * @l: notification type | |
442 | * @ptr: pointer to the &struct ubi_notification object | |
941dfb07 | 443 | */ |
2ba3d76a DP |
444 | static int gluebi_notify(struct notifier_block *nb, unsigned long l, |
445 | void *ns_ptr) | |
941dfb07 | 446 | { |
2ba3d76a DP |
447 | struct ubi_notification *nt = ns_ptr; |
448 | ||
449 | switch (l) { | |
450 | case UBI_VOLUME_ADDED: | |
451 | gluebi_create(&nt->di, &nt->vi); | |
452 | break; | |
453 | case UBI_VOLUME_REMOVED: | |
454 | gluebi_remove(&nt->vi); | |
455 | break; | |
456 | case UBI_VOLUME_RESIZED: | |
457 | gluebi_resized(&nt->vi); | |
458 | break; | |
459 | case UBI_VOLUME_UPDATED: | |
460 | gluebi_updated(&nt->vi); | |
461 | break; | |
462 | default: | |
463 | break; | |
464 | } | |
465 | return NOTIFY_OK; | |
466 | } | |
941dfb07 | 467 | |
2ba3d76a DP |
468 | static struct notifier_block gluebi_notifier = { |
469 | .notifier_call = gluebi_notify, | |
470 | }; | |
471 | ||
472 | static int __init ubi_gluebi_init(void) | |
473 | { | |
474 | return ubi_register_volume_notifier(&gluebi_notifier, 0); | |
941dfb07 | 475 | } |
2ba3d76a DP |
476 | |
477 | static void __exit ubi_gluebi_exit(void) | |
478 | { | |
479 | struct gluebi_device *gluebi, *g; | |
480 | ||
481 | list_for_each_entry_safe(gluebi, g, &gluebi_devices, list) { | |
482 | int err; | |
483 | struct mtd_info *mtd = &gluebi->mtd; | |
484 | ||
ee0e87b1 | 485 | err = mtd_device_unregister(mtd); |
2ba3d76a | 486 | if (err) |
049333ce AB |
487 | err_msg("error %d while removing gluebi MTD device %d, UBI device %d, volume %d - ignoring", |
488 | err, mtd->index, gluebi->ubi_num, | |
489 | gluebi->vol_id); | |
2ba3d76a DP |
490 | kfree(mtd->name); |
491 | kfree(gluebi); | |
492 | } | |
493 | ubi_unregister_volume_notifier(&gluebi_notifier); | |
494 | } | |
495 | ||
496 | module_init(ubi_gluebi_init); | |
497 | module_exit(ubi_gluebi_exit); | |
498 | MODULE_DESCRIPTION("MTD emulation layer over UBI volumes"); | |
499 | MODULE_AUTHOR("Artem Bityutskiy, Joern Engel"); | |
500 | MODULE_LICENSE("GPL"); |