1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (c) International Business Machines Corp., 2006 |
4 | * |
5 | * Author: Artem Bityutskiy (Битюцкий Артём), Joern Engel |
6 | */ |
7 | |
8 | /* |
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. |
13 | * |
14 | * Gluebi emulates MTD devices of "MTD_UBIVOLUME" type. Their minimal I/O unit |
15 | * size (@mtd->writesize) is equivalent to the UBI minimal I/O unit. The |
16 | * eraseblock size is equivalent to the logical eraseblock size of the volume. |
17 | */ |
18 | |
19 | #include <linux/err.h> |
20 | #include <linux/list.h> |
21 | #include <linux/slab.h> |
22 | #include <linux/sched.h> |
23 | #include <linux/math64.h> |
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, ...) \ |
31 | pr_err("gluebi (pid %d): %s: " fmt "\n", \ |
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 | } |
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 | { |
86 | struct gluebi_device *gluebi; |
87 | int ubi_mode = UBI_READONLY; |
88 | |
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) { |
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 |
99 | * readers/writers/exclusive/meta openers as UBI does. So we do |
100 | * not open the UBI volume again - just increase the reference |
101 | * counter and return. |
102 | */ |
103 | gluebi->refcnt += 1; |
104 | mutex_unlock(lock: &devices_mutex); |
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 | */ |
112 | gluebi->desc = ubi_open_volume(ubi_num: gluebi->ubi_num, vol_id: gluebi->vol_id, |
113 | mode: ubi_mode); |
114 | if (IS_ERR(ptr: gluebi->desc)) { |
115 | mutex_unlock(lock: &devices_mutex); |
116 | return PTR_ERR(ptr: gluebi->desc); |
117 | } |
118 | gluebi->refcnt += 1; |
119 | mutex_unlock(lock: &devices_mutex); |
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 | { |
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(desc: gluebi->desc); |
139 | mutex_unlock(lock: &devices_mutex); |
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 | { |
156 | int err = 0, lnum, offs, bytes_left; |
157 | struct gluebi_device *gluebi; |
158 | |
159 | gluebi = container_of(mtd, struct gluebi_device, mtd); |
160 | lnum = div_u64_rem(dividend: from, divisor: mtd->erasesize, remainder: &offs); |
161 | bytes_left = len; |
162 | while (bytes_left) { |
163 | size_t to_read = mtd->erasesize - offs; |
164 | |
165 | if (to_read > bytes_left) |
166 | to_read = bytes_left; |
167 | |
168 | err = ubi_read(desc: gluebi->desc, lnum, buf, offset: offs, len: to_read); |
169 | if (err) |
170 | break; |
171 | |
172 | lnum += 1; |
173 | offs = 0; |
174 | bytes_left -= to_read; |
175 | buf += to_read; |
176 | } |
177 | |
178 | *retlen = len - bytes_left; |
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, |
194 | size_t *retlen, const u_char *buf) |
195 | { |
196 | int err = 0, lnum, offs, bytes_left; |
197 | struct gluebi_device *gluebi; |
198 | |
199 | gluebi = container_of(mtd, struct gluebi_device, mtd); |
200 | lnum = div_u64_rem(dividend: to, divisor: mtd->erasesize, remainder: &offs); |
201 | |
202 | if (len % mtd->writesize || offs % mtd->writesize) |
203 | return -EINVAL; |
204 | |
205 | bytes_left = len; |
206 | while (bytes_left) { |
207 | size_t to_write = mtd->erasesize - offs; |
208 | |
209 | if (to_write > bytes_left) |
210 | to_write = bytes_left; |
211 | |
212 | err = ubi_leb_write(desc: gluebi->desc, lnum, buf, offset: offs, len: to_write); |
213 | if (err) |
214 | break; |
215 | |
216 | lnum += 1; |
217 | offs = 0; |
218 | bytes_left -= to_write; |
219 | buf += to_write; |
220 | } |
221 | |
222 | *retlen = len - bytes_left; |
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; |
237 | struct gluebi_device *gluebi; |
238 | |
239 | if (mtd_mod_by_ws(sz: instr->addr, mtd) || mtd_mod_by_ws(sz: instr->len, mtd)) |
240 | return -EINVAL; |
241 | |
242 | lnum = mtd_div_by_eb(sz: instr->addr, mtd); |
243 | count = mtd_div_by_eb(sz: instr->len, mtd); |
244 | gluebi = container_of(mtd, struct gluebi_device, mtd); |
245 | |
246 | for (i = 0; i < count - 1; i++) { |
247 | err = ubi_leb_unmap(desc: gluebi->desc, lnum: lnum + i); |
248 | if (err) |
249 | goto out_err; |
250 | } |
251 | /* |
252 | * MTD erase operations are synchronous, so we have to make sure the |
253 | * physical eraseblock is wiped out. |
254 | * |
255 | * Thus, perform leb_erase instead of leb_unmap operation - leb_erase |
256 | * will wait for the end of operations |
257 | */ |
258 | err = ubi_leb_erase(desc: gluebi->desc, lnum: lnum + i); |
259 | if (err) |
260 | goto out_err; |
261 | |
262 | return 0; |
263 | |
264 | out_err: |
265 | instr->fail_addr = (long long)lnum * mtd->erasesize; |
266 | return err; |
267 | } |
268 | |
269 | /** |
270 | * gluebi_create - create a gluebi device for an UBI volume. |
271 | * @di: UBI device description object |
272 | * @vi: UBI volume description object |
273 | * |
274 | * This function is called when a new UBI volume is created in order to create |
275 | * corresponding fake MTD device. Returns zero in case of success and a |
276 | * negative error code in case of failure. |
277 | */ |
278 | static int gluebi_create(struct ubi_device_info *di, |
279 | struct ubi_volume_info *vi) |
280 | { |
281 | struct gluebi_device *gluebi, *g; |
282 | struct mtd_info *mtd; |
283 | |
284 | gluebi = kzalloc(size: sizeof(struct gluebi_device), GFP_KERNEL); |
285 | if (!gluebi) |
286 | return -ENOMEM; |
287 | |
288 | mtd = &gluebi->mtd; |
289 | mtd->name = kmemdup(p: vi->name, size: vi->name_len + 1, GFP_KERNEL); |
290 | if (!mtd->name) { |
291 | kfree(objp: gluebi); |
292 | return -ENOMEM; |
293 | } |
294 | |
295 | gluebi->vol_id = vi->vol_id; |
296 | gluebi->ubi_num = vi->ubi_num; |
297 | mtd->type = MTD_UBIVOLUME; |
298 | if (!di->ro_mode) |
299 | mtd->flags = MTD_WRITEABLE; |
300 | mtd->owner = THIS_MODULE; |
301 | mtd->writesize = di->min_io_size; |
302 | mtd->erasesize = vi->usable_leb_size; |
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; |
308 | |
309 | /* |
310 | * In case of dynamic a volume, MTD device size is just volume size. In |
311 | * case of a static volume the size is equivalent to the amount of data |
312 | * bytes. |
313 | */ |
314 | if (vi->vol_type == UBI_DYNAMIC_VOLUME) |
315 | mtd->size = (unsigned long long)vi->usable_leb_size * vi->size; |
316 | else |
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(ubi_num: vi->ubi_num, vol_id: vi->vol_id); |
322 | if (g) |
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); |
325 | mutex_unlock(lock: &devices_mutex); |
326 | |
327 | if (mtd_device_register(mtd, NULL, 0)) { |
328 | err_msg("cannot add MTD device" ); |
329 | kfree(objp: mtd->name); |
330 | kfree(objp: gluebi); |
331 | return -ENFILE; |
332 | } |
333 | |
334 | mutex_lock(&devices_mutex); |
335 | list_add_tail(new: &gluebi->list, head: &gluebi_devices); |
336 | mutex_unlock(lock: &devices_mutex); |
337 | return 0; |
338 | } |
339 | |
340 | /** |
341 | * gluebi_remove - remove a gluebi device. |
342 | * @vi: UBI volume description object |
343 | * |
344 | * This function is called when an UBI volume is removed and it removes |
345 | * corresponding fake MTD device. Returns zero in case of success and a |
346 | * negative error code in case of failure. |
347 | */ |
348 | static int gluebi_remove(struct ubi_volume_info *vi) |
349 | { |
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(ubi_num: vi->ubi_num, vol_id: vi->vol_id); |
356 | if (!gluebi) { |
357 | err_msg("got remove notification for unknown UBI device %d volume %d" , |
358 | vi->ubi_num, vi->vol_id); |
359 | err = -ENOENT; |
360 | } else if (gluebi->refcnt) |
361 | err = -EBUSY; |
362 | else |
363 | list_del(entry: &gluebi->list); |
364 | mutex_unlock(lock: &devices_mutex); |
365 | if (err) |
366 | return err; |
367 | |
368 | mtd = &gluebi->mtd; |
369 | err = mtd_device_unregister(master: mtd); |
370 | if (err) { |
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); |
373 | mutex_lock(&devices_mutex); |
374 | list_add_tail(new: &gluebi->list, head: &gluebi_devices); |
375 | mutex_unlock(lock: &devices_mutex); |
376 | return err; |
377 | } |
378 | |
379 | kfree(objp: mtd->name); |
380 | kfree(objp: gluebi); |
381 | return 0; |
382 | } |
383 | |
384 | /** |
385 | * gluebi_updated - UBI volume was updated notifier. |
386 | * @vi: volume info structure |
387 | * |
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 |
390 | * volume is static. This is needed because static volumes cannot be read past |
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(ubi_num: vi->ubi_num, vol_id: vi->vol_id); |
400 | if (!gluebi) { |
401 | mutex_unlock(lock: &devices_mutex); |
402 | err_msg("got update notification for unknown UBI device %d volume %d" , |
403 | vi->ubi_num, vi->vol_id); |
404 | return -ENOENT; |
405 | } |
406 | |
407 | if (vi->vol_type == UBI_STATIC_VOLUME) |
408 | gluebi->mtd.size = vi->used_bytes; |
409 | mutex_unlock(lock: &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(ubi_num: vi->ubi_num, vol_id: vi->vol_id); |
427 | if (!gluebi) { |
428 | mutex_unlock(lock: &devices_mutex); |
429 | err_msg("got update notification for unknown UBI device %d volume %d" , |
430 | vi->ubi_num, vi->vol_id); |
431 | return -ENOENT; |
432 | } |
433 | gluebi->mtd.size = vi->used_bytes; |
434 | mutex_unlock(lock: &devices_mutex); |
435 | return 0; |
436 | } |
437 | |
438 | /** |
439 | * gluebi_notify - UBI notification handler. |
440 | * @nb: registered notifier block |
441 | * @l: notification type |
442 | * @ns_ptr: pointer to the &struct ubi_notification object |
443 | */ |
444 | static int gluebi_notify(struct notifier_block *nb, unsigned long l, |
445 | void *ns_ptr) |
446 | { |
447 | struct ubi_notification *nt = ns_ptr; |
448 | |
449 | switch (l) { |
450 | case UBI_VOLUME_ADDED: |
451 | gluebi_create(di: &nt->di, vi: &nt->vi); |
452 | break; |
453 | case UBI_VOLUME_REMOVED: |
454 | gluebi_remove(vi: &nt->vi); |
455 | break; |
456 | case UBI_VOLUME_RESIZED: |
457 | gluebi_resized(vi: &nt->vi); |
458 | break; |
459 | case UBI_VOLUME_UPDATED: |
460 | gluebi_updated(vi: &nt->vi); |
461 | break; |
462 | default: |
463 | break; |
464 | } |
465 | return NOTIFY_OK; |
466 | } |
467 | |
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(nb: &gluebi_notifier, ignore_existing: 0); |
475 | } |
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 | |
485 | err = mtd_device_unregister(master: mtd); |
486 | if (err) |
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); |
490 | kfree(objp: mtd->name); |
491 | kfree(objp: gluebi); |
492 | } |
493 | ubi_unregister_volume_notifier(nb: &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" ); |
501 | |