18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) International Business Machines Corp., 2006 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Artem Bityutskiy (Битюцкий Артём), Joern Engel 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci/* 98c2ecf20Sopenharmony_ci * This is a small driver which implements fake MTD devices on top of UBI 108c2ecf20Sopenharmony_ci * volumes. This sounds strange, but it is in fact quite useful to make 118c2ecf20Sopenharmony_ci * MTD-oriented software (including all the legacy software) work on top of 128c2ecf20Sopenharmony_ci * UBI. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Gluebi emulates MTD devices of "MTD_UBIVOLUME" type. Their minimal I/O unit 158c2ecf20Sopenharmony_ci * size (@mtd->writesize) is equivalent to the UBI minimal I/O unit. The 168c2ecf20Sopenharmony_ci * eraseblock size is equivalent to the logical eraseblock size of the volume. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/err.h> 208c2ecf20Sopenharmony_ci#include <linux/list.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/sched.h> 238c2ecf20Sopenharmony_ci#include <linux/math64.h> 248c2ecf20Sopenharmony_ci#include <linux/module.h> 258c2ecf20Sopenharmony_ci#include <linux/mutex.h> 268c2ecf20Sopenharmony_ci#include <linux/mtd/ubi.h> 278c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 288c2ecf20Sopenharmony_ci#include "ubi-media.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define err_msg(fmt, ...) \ 318c2ecf20Sopenharmony_ci pr_err("gluebi (pid %d): %s: " fmt "\n", \ 328c2ecf20Sopenharmony_ci current->pid, __func__, ##__VA_ARGS__) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/** 358c2ecf20Sopenharmony_ci * struct gluebi_device - a gluebi device description data structure. 368c2ecf20Sopenharmony_ci * @mtd: emulated MTD device description object 378c2ecf20Sopenharmony_ci * @refcnt: gluebi device reference count 388c2ecf20Sopenharmony_ci * @desc: UBI volume descriptor 398c2ecf20Sopenharmony_ci * @ubi_num: UBI device number this gluebi device works on 408c2ecf20Sopenharmony_ci * @vol_id: ID of UBI volume this gluebi device works on 418c2ecf20Sopenharmony_ci * @list: link in a list of gluebi devices 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_cistruct gluebi_device { 448c2ecf20Sopenharmony_ci struct mtd_info mtd; 458c2ecf20Sopenharmony_ci int refcnt; 468c2ecf20Sopenharmony_ci struct ubi_volume_desc *desc; 478c2ecf20Sopenharmony_ci int ubi_num; 488c2ecf20Sopenharmony_ci int vol_id; 498c2ecf20Sopenharmony_ci struct list_head list; 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* List of all gluebi devices */ 538c2ecf20Sopenharmony_cistatic LIST_HEAD(gluebi_devices); 548c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(devices_mutex); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/** 578c2ecf20Sopenharmony_ci * find_gluebi_nolock - find a gluebi device. 588c2ecf20Sopenharmony_ci * @ubi_num: UBI device number 598c2ecf20Sopenharmony_ci * @vol_id: volume ID 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci * This function seraches for gluebi device corresponding to UBI device 628c2ecf20Sopenharmony_ci * @ubi_num and UBI volume @vol_id. Returns the gluebi device description 638c2ecf20Sopenharmony_ci * object in case of success and %NULL in case of failure. The caller has to 648c2ecf20Sopenharmony_ci * have the &devices_mutex locked. 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_cistatic struct gluebi_device *find_gluebi_nolock(int ubi_num, int vol_id) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct gluebi_device *gluebi; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci list_for_each_entry(gluebi, &gluebi_devices, list) 718c2ecf20Sopenharmony_ci if (gluebi->ubi_num == ubi_num && gluebi->vol_id == vol_id) 728c2ecf20Sopenharmony_ci return gluebi; 738c2ecf20Sopenharmony_ci return NULL; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/** 778c2ecf20Sopenharmony_ci * gluebi_get_device - get MTD device reference. 788c2ecf20Sopenharmony_ci * @mtd: the MTD device description object 798c2ecf20Sopenharmony_ci * 808c2ecf20Sopenharmony_ci * This function is called every time the MTD device is being opened and 818c2ecf20Sopenharmony_ci * implements the MTD get_device() operation. Returns zero in case of success 828c2ecf20Sopenharmony_ci * and a negative error code in case of failure. 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_cistatic int gluebi_get_device(struct mtd_info *mtd) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct gluebi_device *gluebi; 878c2ecf20Sopenharmony_ci int ubi_mode = UBI_READONLY; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (mtd->flags & MTD_WRITEABLE) 908c2ecf20Sopenharmony_ci ubi_mode = UBI_READWRITE; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci gluebi = container_of(mtd, struct gluebi_device, mtd); 938c2ecf20Sopenharmony_ci mutex_lock(&devices_mutex); 948c2ecf20Sopenharmony_ci if (gluebi->refcnt > 0) { 958c2ecf20Sopenharmony_ci /* 968c2ecf20Sopenharmony_ci * The MTD device is already referenced and this is just one 978c2ecf20Sopenharmony_ci * more reference. MTD allows many users to open the same 988c2ecf20Sopenharmony_ci * volume simultaneously and do not distinguish between 998c2ecf20Sopenharmony_ci * readers/writers/exclusive/meta openers as UBI does. So we do 1008c2ecf20Sopenharmony_ci * not open the UBI volume again - just increase the reference 1018c2ecf20Sopenharmony_ci * counter and return. 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_ci gluebi->refcnt += 1; 1048c2ecf20Sopenharmony_ci mutex_unlock(&devices_mutex); 1058c2ecf20Sopenharmony_ci return 0; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* 1098c2ecf20Sopenharmony_ci * This is the first reference to this UBI volume via the MTD device 1108c2ecf20Sopenharmony_ci * interface. Open the corresponding volume in read-write mode. 1118c2ecf20Sopenharmony_ci */ 1128c2ecf20Sopenharmony_ci gluebi->desc = ubi_open_volume(gluebi->ubi_num, gluebi->vol_id, 1138c2ecf20Sopenharmony_ci ubi_mode); 1148c2ecf20Sopenharmony_ci if (IS_ERR(gluebi->desc)) { 1158c2ecf20Sopenharmony_ci mutex_unlock(&devices_mutex); 1168c2ecf20Sopenharmony_ci return PTR_ERR(gluebi->desc); 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci gluebi->refcnt += 1; 1198c2ecf20Sopenharmony_ci mutex_unlock(&devices_mutex); 1208c2ecf20Sopenharmony_ci return 0; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/** 1248c2ecf20Sopenharmony_ci * gluebi_put_device - put MTD device reference. 1258c2ecf20Sopenharmony_ci * @mtd: the MTD device description object 1268c2ecf20Sopenharmony_ci * 1278c2ecf20Sopenharmony_ci * This function is called every time the MTD device is being put. Returns 1288c2ecf20Sopenharmony_ci * zero in case of success and a negative error code in case of failure. 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_cistatic void gluebi_put_device(struct mtd_info *mtd) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct gluebi_device *gluebi; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci gluebi = container_of(mtd, struct gluebi_device, mtd); 1358c2ecf20Sopenharmony_ci mutex_lock(&devices_mutex); 1368c2ecf20Sopenharmony_ci gluebi->refcnt -= 1; 1378c2ecf20Sopenharmony_ci if (gluebi->refcnt == 0) 1388c2ecf20Sopenharmony_ci ubi_close_volume(gluebi->desc); 1398c2ecf20Sopenharmony_ci mutex_unlock(&devices_mutex); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci/** 1438c2ecf20Sopenharmony_ci * gluebi_read - read operation of emulated MTD devices. 1448c2ecf20Sopenharmony_ci * @mtd: MTD device description object 1458c2ecf20Sopenharmony_ci * @from: absolute offset from where to read 1468c2ecf20Sopenharmony_ci * @len: how many bytes to read 1478c2ecf20Sopenharmony_ci * @retlen: count of read bytes is returned here 1488c2ecf20Sopenharmony_ci * @buf: buffer to store the read data 1498c2ecf20Sopenharmony_ci * 1508c2ecf20Sopenharmony_ci * This function returns zero in case of success and a negative error code in 1518c2ecf20Sopenharmony_ci * case of failure. 1528c2ecf20Sopenharmony_ci */ 1538c2ecf20Sopenharmony_cistatic int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len, 1548c2ecf20Sopenharmony_ci size_t *retlen, unsigned char *buf) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci int err = 0, lnum, offs, bytes_left; 1578c2ecf20Sopenharmony_ci struct gluebi_device *gluebi; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci gluebi = container_of(mtd, struct gluebi_device, mtd); 1608c2ecf20Sopenharmony_ci lnum = div_u64_rem(from, mtd->erasesize, &offs); 1618c2ecf20Sopenharmony_ci bytes_left = len; 1628c2ecf20Sopenharmony_ci while (bytes_left) { 1638c2ecf20Sopenharmony_ci size_t to_read = mtd->erasesize - offs; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (to_read > bytes_left) 1668c2ecf20Sopenharmony_ci to_read = bytes_left; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci err = ubi_read(gluebi->desc, lnum, buf, offs, to_read); 1698c2ecf20Sopenharmony_ci if (err) 1708c2ecf20Sopenharmony_ci break; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci lnum += 1; 1738c2ecf20Sopenharmony_ci offs = 0; 1748c2ecf20Sopenharmony_ci bytes_left -= to_read; 1758c2ecf20Sopenharmony_ci buf += to_read; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci *retlen = len - bytes_left; 1798c2ecf20Sopenharmony_ci return err; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci/** 1838c2ecf20Sopenharmony_ci * gluebi_write - write operation of emulated MTD devices. 1848c2ecf20Sopenharmony_ci * @mtd: MTD device description object 1858c2ecf20Sopenharmony_ci * @to: absolute offset where to write 1868c2ecf20Sopenharmony_ci * @len: how many bytes to write 1878c2ecf20Sopenharmony_ci * @retlen: count of written bytes is returned here 1888c2ecf20Sopenharmony_ci * @buf: buffer with data to write 1898c2ecf20Sopenharmony_ci * 1908c2ecf20Sopenharmony_ci * This function returns zero in case of success and a negative error code in 1918c2ecf20Sopenharmony_ci * case of failure. 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_cistatic int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len, 1948c2ecf20Sopenharmony_ci size_t *retlen, const u_char *buf) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci int err = 0, lnum, offs, bytes_left; 1978c2ecf20Sopenharmony_ci struct gluebi_device *gluebi; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci gluebi = container_of(mtd, struct gluebi_device, mtd); 2008c2ecf20Sopenharmony_ci lnum = div_u64_rem(to, mtd->erasesize, &offs); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (len % mtd->writesize || offs % mtd->writesize) 2038c2ecf20Sopenharmony_ci return -EINVAL; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci bytes_left = len; 2068c2ecf20Sopenharmony_ci while (bytes_left) { 2078c2ecf20Sopenharmony_ci size_t to_write = mtd->erasesize - offs; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (to_write > bytes_left) 2108c2ecf20Sopenharmony_ci to_write = bytes_left; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci err = ubi_leb_write(gluebi->desc, lnum, buf, offs, to_write); 2138c2ecf20Sopenharmony_ci if (err) 2148c2ecf20Sopenharmony_ci break; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci lnum += 1; 2178c2ecf20Sopenharmony_ci offs = 0; 2188c2ecf20Sopenharmony_ci bytes_left -= to_write; 2198c2ecf20Sopenharmony_ci buf += to_write; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci *retlen = len - bytes_left; 2238c2ecf20Sopenharmony_ci return err; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci/** 2278c2ecf20Sopenharmony_ci * gluebi_erase - erase operation of emulated MTD devices. 2288c2ecf20Sopenharmony_ci * @mtd: the MTD device description object 2298c2ecf20Sopenharmony_ci * @instr: the erase operation description 2308c2ecf20Sopenharmony_ci * 2318c2ecf20Sopenharmony_ci * This function calls the erase callback when finishes. Returns zero in case 2328c2ecf20Sopenharmony_ci * of success and a negative error code in case of failure. 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_cistatic int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci int err, i, lnum, count; 2378c2ecf20Sopenharmony_ci struct gluebi_device *gluebi; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (mtd_mod_by_ws(instr->addr, mtd) || mtd_mod_by_ws(instr->len, mtd)) 2408c2ecf20Sopenharmony_ci return -EINVAL; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci lnum = mtd_div_by_eb(instr->addr, mtd); 2438c2ecf20Sopenharmony_ci count = mtd_div_by_eb(instr->len, mtd); 2448c2ecf20Sopenharmony_ci gluebi = container_of(mtd, struct gluebi_device, mtd); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci for (i = 0; i < count - 1; i++) { 2478c2ecf20Sopenharmony_ci err = ubi_leb_unmap(gluebi->desc, lnum + i); 2488c2ecf20Sopenharmony_ci if (err) 2498c2ecf20Sopenharmony_ci goto out_err; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci /* 2528c2ecf20Sopenharmony_ci * MTD erase operations are synchronous, so we have to make sure the 2538c2ecf20Sopenharmony_ci * physical eraseblock is wiped out. 2548c2ecf20Sopenharmony_ci * 2558c2ecf20Sopenharmony_ci * Thus, perform leb_erase instead of leb_unmap operation - leb_erase 2568c2ecf20Sopenharmony_ci * will wait for the end of operations 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_ci err = ubi_leb_erase(gluebi->desc, lnum + i); 2598c2ecf20Sopenharmony_ci if (err) 2608c2ecf20Sopenharmony_ci goto out_err; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return 0; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ciout_err: 2658c2ecf20Sopenharmony_ci instr->fail_addr = (long long)lnum * mtd->erasesize; 2668c2ecf20Sopenharmony_ci return err; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci/** 2708c2ecf20Sopenharmony_ci * gluebi_create - create a gluebi device for an UBI volume. 2718c2ecf20Sopenharmony_ci * @di: UBI device description object 2728c2ecf20Sopenharmony_ci * @vi: UBI volume description object 2738c2ecf20Sopenharmony_ci * 2748c2ecf20Sopenharmony_ci * This function is called when a new UBI volume is created in order to create 2758c2ecf20Sopenharmony_ci * corresponding fake MTD device. Returns zero in case of success and a 2768c2ecf20Sopenharmony_ci * negative error code in case of failure. 2778c2ecf20Sopenharmony_ci */ 2788c2ecf20Sopenharmony_cistatic int gluebi_create(struct ubi_device_info *di, 2798c2ecf20Sopenharmony_ci struct ubi_volume_info *vi) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci struct gluebi_device *gluebi, *g; 2828c2ecf20Sopenharmony_ci struct mtd_info *mtd; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci gluebi = kzalloc(sizeof(struct gluebi_device), GFP_KERNEL); 2858c2ecf20Sopenharmony_ci if (!gluebi) 2868c2ecf20Sopenharmony_ci return -ENOMEM; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci mtd = &gluebi->mtd; 2898c2ecf20Sopenharmony_ci mtd->name = kmemdup(vi->name, vi->name_len + 1, GFP_KERNEL); 2908c2ecf20Sopenharmony_ci if (!mtd->name) { 2918c2ecf20Sopenharmony_ci kfree(gluebi); 2928c2ecf20Sopenharmony_ci return -ENOMEM; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci gluebi->vol_id = vi->vol_id; 2968c2ecf20Sopenharmony_ci gluebi->ubi_num = vi->ubi_num; 2978c2ecf20Sopenharmony_ci mtd->type = MTD_UBIVOLUME; 2988c2ecf20Sopenharmony_ci if (!di->ro_mode) 2998c2ecf20Sopenharmony_ci mtd->flags = MTD_WRITEABLE; 3008c2ecf20Sopenharmony_ci mtd->owner = THIS_MODULE; 3018c2ecf20Sopenharmony_ci mtd->writesize = di->min_io_size; 3028c2ecf20Sopenharmony_ci mtd->erasesize = vi->usable_leb_size; 3038c2ecf20Sopenharmony_ci mtd->_read = gluebi_read; 3048c2ecf20Sopenharmony_ci mtd->_write = gluebi_write; 3058c2ecf20Sopenharmony_ci mtd->_erase = gluebi_erase; 3068c2ecf20Sopenharmony_ci mtd->_get_device = gluebi_get_device; 3078c2ecf20Sopenharmony_ci mtd->_put_device = gluebi_put_device; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* 3108c2ecf20Sopenharmony_ci * In case of dynamic a volume, MTD device size is just volume size. In 3118c2ecf20Sopenharmony_ci * case of a static volume the size is equivalent to the amount of data 3128c2ecf20Sopenharmony_ci * bytes. 3138c2ecf20Sopenharmony_ci */ 3148c2ecf20Sopenharmony_ci if (vi->vol_type == UBI_DYNAMIC_VOLUME) 3158c2ecf20Sopenharmony_ci mtd->size = (unsigned long long)vi->usable_leb_size * vi->size; 3168c2ecf20Sopenharmony_ci else 3178c2ecf20Sopenharmony_ci mtd->size = vi->used_bytes; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* Just a sanity check - make sure this gluebi device does not exist */ 3208c2ecf20Sopenharmony_ci mutex_lock(&devices_mutex); 3218c2ecf20Sopenharmony_ci g = find_gluebi_nolock(vi->ubi_num, vi->vol_id); 3228c2ecf20Sopenharmony_ci if (g) 3238c2ecf20Sopenharmony_ci err_msg("gluebi MTD device %d form UBI device %d volume %d already exists", 3248c2ecf20Sopenharmony_ci g->mtd.index, vi->ubi_num, vi->vol_id); 3258c2ecf20Sopenharmony_ci mutex_unlock(&devices_mutex); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (mtd_device_register(mtd, NULL, 0)) { 3288c2ecf20Sopenharmony_ci err_msg("cannot add MTD device"); 3298c2ecf20Sopenharmony_ci kfree(mtd->name); 3308c2ecf20Sopenharmony_ci kfree(gluebi); 3318c2ecf20Sopenharmony_ci return -ENFILE; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci mutex_lock(&devices_mutex); 3358c2ecf20Sopenharmony_ci list_add_tail(&gluebi->list, &gluebi_devices); 3368c2ecf20Sopenharmony_ci mutex_unlock(&devices_mutex); 3378c2ecf20Sopenharmony_ci return 0; 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci/** 3418c2ecf20Sopenharmony_ci * gluebi_remove - remove a gluebi device. 3428c2ecf20Sopenharmony_ci * @vi: UBI volume description object 3438c2ecf20Sopenharmony_ci * 3448c2ecf20Sopenharmony_ci * This function is called when an UBI volume is removed and it removes 3458c2ecf20Sopenharmony_ci * corresponding fake MTD device. Returns zero in case of success and a 3468c2ecf20Sopenharmony_ci * negative error code in case of failure. 3478c2ecf20Sopenharmony_ci */ 3488c2ecf20Sopenharmony_cistatic int gluebi_remove(struct ubi_volume_info *vi) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci int err = 0; 3518c2ecf20Sopenharmony_ci struct mtd_info *mtd; 3528c2ecf20Sopenharmony_ci struct gluebi_device *gluebi; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci mutex_lock(&devices_mutex); 3558c2ecf20Sopenharmony_ci gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id); 3568c2ecf20Sopenharmony_ci if (!gluebi) { 3578c2ecf20Sopenharmony_ci err_msg("got remove notification for unknown UBI device %d volume %d", 3588c2ecf20Sopenharmony_ci vi->ubi_num, vi->vol_id); 3598c2ecf20Sopenharmony_ci err = -ENOENT; 3608c2ecf20Sopenharmony_ci } else if (gluebi->refcnt) 3618c2ecf20Sopenharmony_ci err = -EBUSY; 3628c2ecf20Sopenharmony_ci else 3638c2ecf20Sopenharmony_ci list_del(&gluebi->list); 3648c2ecf20Sopenharmony_ci mutex_unlock(&devices_mutex); 3658c2ecf20Sopenharmony_ci if (err) 3668c2ecf20Sopenharmony_ci return err; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci mtd = &gluebi->mtd; 3698c2ecf20Sopenharmony_ci err = mtd_device_unregister(mtd); 3708c2ecf20Sopenharmony_ci if (err) { 3718c2ecf20Sopenharmony_ci err_msg("cannot remove fake MTD device %d, UBI device %d, volume %d, error %d", 3728c2ecf20Sopenharmony_ci mtd->index, gluebi->ubi_num, gluebi->vol_id, err); 3738c2ecf20Sopenharmony_ci mutex_lock(&devices_mutex); 3748c2ecf20Sopenharmony_ci list_add_tail(&gluebi->list, &gluebi_devices); 3758c2ecf20Sopenharmony_ci mutex_unlock(&devices_mutex); 3768c2ecf20Sopenharmony_ci return err; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci kfree(mtd->name); 3808c2ecf20Sopenharmony_ci kfree(gluebi); 3818c2ecf20Sopenharmony_ci return 0; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci/** 3858c2ecf20Sopenharmony_ci * gluebi_updated - UBI volume was updated notifier. 3868c2ecf20Sopenharmony_ci * @vi: volume info structure 3878c2ecf20Sopenharmony_ci * 3888c2ecf20Sopenharmony_ci * This function is called every time an UBI volume is updated. It does nothing 3898c2ecf20Sopenharmony_ci * if te volume @vol is dynamic, and changes MTD device size if the 3908c2ecf20Sopenharmony_ci * volume is static. This is needed because static volumes cannot be read past 3918c2ecf20Sopenharmony_ci * data they contain. This function returns zero in case of success and a 3928c2ecf20Sopenharmony_ci * negative error code in case of error. 3938c2ecf20Sopenharmony_ci */ 3948c2ecf20Sopenharmony_cistatic int gluebi_updated(struct ubi_volume_info *vi) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci struct gluebi_device *gluebi; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci mutex_lock(&devices_mutex); 3998c2ecf20Sopenharmony_ci gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id); 4008c2ecf20Sopenharmony_ci if (!gluebi) { 4018c2ecf20Sopenharmony_ci mutex_unlock(&devices_mutex); 4028c2ecf20Sopenharmony_ci err_msg("got update notification for unknown UBI device %d volume %d", 4038c2ecf20Sopenharmony_ci vi->ubi_num, vi->vol_id); 4048c2ecf20Sopenharmony_ci return -ENOENT; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci if (vi->vol_type == UBI_STATIC_VOLUME) 4088c2ecf20Sopenharmony_ci gluebi->mtd.size = vi->used_bytes; 4098c2ecf20Sopenharmony_ci mutex_unlock(&devices_mutex); 4108c2ecf20Sopenharmony_ci return 0; 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci/** 4148c2ecf20Sopenharmony_ci * gluebi_resized - UBI volume was re-sized notifier. 4158c2ecf20Sopenharmony_ci * @vi: volume info structure 4168c2ecf20Sopenharmony_ci * 4178c2ecf20Sopenharmony_ci * This function is called every time an UBI volume is re-size. It changes the 4188c2ecf20Sopenharmony_ci * corresponding fake MTD device size. This function returns zero in case of 4198c2ecf20Sopenharmony_ci * success and a negative error code in case of error. 4208c2ecf20Sopenharmony_ci */ 4218c2ecf20Sopenharmony_cistatic int gluebi_resized(struct ubi_volume_info *vi) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci struct gluebi_device *gluebi; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci mutex_lock(&devices_mutex); 4268c2ecf20Sopenharmony_ci gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id); 4278c2ecf20Sopenharmony_ci if (!gluebi) { 4288c2ecf20Sopenharmony_ci mutex_unlock(&devices_mutex); 4298c2ecf20Sopenharmony_ci err_msg("got update notification for unknown UBI device %d volume %d", 4308c2ecf20Sopenharmony_ci vi->ubi_num, vi->vol_id); 4318c2ecf20Sopenharmony_ci return -ENOENT; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci gluebi->mtd.size = vi->used_bytes; 4348c2ecf20Sopenharmony_ci mutex_unlock(&devices_mutex); 4358c2ecf20Sopenharmony_ci return 0; 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci/** 4398c2ecf20Sopenharmony_ci * gluebi_notify - UBI notification handler. 4408c2ecf20Sopenharmony_ci * @nb: registered notifier block 4418c2ecf20Sopenharmony_ci * @l: notification type 4428c2ecf20Sopenharmony_ci * @ptr: pointer to the &struct ubi_notification object 4438c2ecf20Sopenharmony_ci */ 4448c2ecf20Sopenharmony_cistatic int gluebi_notify(struct notifier_block *nb, unsigned long l, 4458c2ecf20Sopenharmony_ci void *ns_ptr) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci struct ubi_notification *nt = ns_ptr; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci switch (l) { 4508c2ecf20Sopenharmony_ci case UBI_VOLUME_ADDED: 4518c2ecf20Sopenharmony_ci gluebi_create(&nt->di, &nt->vi); 4528c2ecf20Sopenharmony_ci break; 4538c2ecf20Sopenharmony_ci case UBI_VOLUME_REMOVED: 4548c2ecf20Sopenharmony_ci gluebi_remove(&nt->vi); 4558c2ecf20Sopenharmony_ci break; 4568c2ecf20Sopenharmony_ci case UBI_VOLUME_RESIZED: 4578c2ecf20Sopenharmony_ci gluebi_resized(&nt->vi); 4588c2ecf20Sopenharmony_ci break; 4598c2ecf20Sopenharmony_ci case UBI_VOLUME_UPDATED: 4608c2ecf20Sopenharmony_ci gluebi_updated(&nt->vi); 4618c2ecf20Sopenharmony_ci break; 4628c2ecf20Sopenharmony_ci default: 4638c2ecf20Sopenharmony_ci break; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci return NOTIFY_OK; 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic struct notifier_block gluebi_notifier = { 4698c2ecf20Sopenharmony_ci .notifier_call = gluebi_notify, 4708c2ecf20Sopenharmony_ci}; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic int __init ubi_gluebi_init(void) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci return ubi_register_volume_notifier(&gluebi_notifier, 0); 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic void __exit ubi_gluebi_exit(void) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci struct gluebi_device *gluebi, *g; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci list_for_each_entry_safe(gluebi, g, &gluebi_devices, list) { 4828c2ecf20Sopenharmony_ci int err; 4838c2ecf20Sopenharmony_ci struct mtd_info *mtd = &gluebi->mtd; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci err = mtd_device_unregister(mtd); 4868c2ecf20Sopenharmony_ci if (err) 4878c2ecf20Sopenharmony_ci err_msg("error %d while removing gluebi MTD device %d, UBI device %d, volume %d - ignoring", 4888c2ecf20Sopenharmony_ci err, mtd->index, gluebi->ubi_num, 4898c2ecf20Sopenharmony_ci gluebi->vol_id); 4908c2ecf20Sopenharmony_ci kfree(mtd->name); 4918c2ecf20Sopenharmony_ci kfree(gluebi); 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci ubi_unregister_volume_notifier(&gluebi_notifier); 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cimodule_init(ubi_gluebi_init); 4978c2ecf20Sopenharmony_cimodule_exit(ubi_gluebi_exit); 4988c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MTD emulation layer over UBI volumes"); 4998c2ecf20Sopenharmony_ciMODULE_AUTHOR("Artem Bityutskiy, Joern Engel"); 5008c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 501