1*1a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2801c135cSArtem B. Bityutskiy /* 3801c135cSArtem B. Bityutskiy * Copyright (c) International Business Machines Corp., 2006 4801c135cSArtem B. Bityutskiy * 5801c135cSArtem B. Bityutskiy * Author: Artem Bityutskiy (Битюцкий Артём), Joern Engel 6801c135cSArtem B. Bityutskiy */ 7801c135cSArtem B. Bityutskiy 8801c135cSArtem B. Bityutskiy /* 92ba3d76aSDmitry Pervushin * This is a small driver which implements fake MTD devices on top of UBI 102ba3d76aSDmitry Pervushin * volumes. This sounds strange, but it is in fact quite useful to make 112ba3d76aSDmitry Pervushin * MTD-oriented software (including all the legacy software) work on top of 122ba3d76aSDmitry Pervushin * UBI. 13801c135cSArtem B. Bityutskiy * 14801c135cSArtem B. Bityutskiy * Gluebi emulates MTD devices of "MTD_UBIVOLUME" type. Their minimal I/O unit 152ba3d76aSDmitry Pervushin * size (@mtd->writesize) is equivalent to the UBI minimal I/O unit. The 16801c135cSArtem B. Bityutskiy * eraseblock size is equivalent to the logical eraseblock size of the volume. 17801c135cSArtem B. Bityutskiy */ 18801c135cSArtem B. Bityutskiy 192ba3d76aSDmitry Pervushin #include <linux/err.h> 202ba3d76aSDmitry Pervushin #include <linux/list.h> 215a0e3ad6STejun Heo #include <linux/slab.h> 222ba3d76aSDmitry Pervushin #include <linux/sched.h> 233013ee31SArtem Bityutskiy #include <linux/math64.h> 242ba3d76aSDmitry Pervushin #include <linux/module.h> 252ba3d76aSDmitry Pervushin #include <linux/mutex.h> 262ba3d76aSDmitry Pervushin #include <linux/mtd/ubi.h> 272ba3d76aSDmitry Pervushin #include <linux/mtd/mtd.h> 282ba3d76aSDmitry Pervushin #include "ubi-media.h" 292ba3d76aSDmitry Pervushin 302ba3d76aSDmitry Pervushin #define err_msg(fmt, ...) \ 31e28453bbSArtem Bityutskiy pr_err("gluebi (pid %d): %s: " fmt "\n", \ 322ba3d76aSDmitry Pervushin current->pid, __func__, ##__VA_ARGS__) 332ba3d76aSDmitry Pervushin 342ba3d76aSDmitry Pervushin /** 352ba3d76aSDmitry Pervushin * struct gluebi_device - a gluebi device description data structure. 362ba3d76aSDmitry Pervushin * @mtd: emulated MTD device description object 372ba3d76aSDmitry Pervushin * @refcnt: gluebi device reference count 382ba3d76aSDmitry Pervushin * @desc: UBI volume descriptor 392ba3d76aSDmitry Pervushin * @ubi_num: UBI device number this gluebi device works on 402ba3d76aSDmitry Pervushin * @vol_id: ID of UBI volume this gluebi device works on 412ba3d76aSDmitry Pervushin * @list: link in a list of gluebi devices 422ba3d76aSDmitry Pervushin */ 432ba3d76aSDmitry Pervushin struct gluebi_device { 442ba3d76aSDmitry Pervushin struct mtd_info mtd; 452ba3d76aSDmitry Pervushin int refcnt; 462ba3d76aSDmitry Pervushin struct ubi_volume_desc *desc; 472ba3d76aSDmitry Pervushin int ubi_num; 482ba3d76aSDmitry Pervushin int vol_id; 492ba3d76aSDmitry Pervushin struct list_head list; 502ba3d76aSDmitry Pervushin }; 512ba3d76aSDmitry Pervushin 522ba3d76aSDmitry Pervushin /* List of all gluebi devices */ 532ba3d76aSDmitry Pervushin static LIST_HEAD(gluebi_devices); 542ba3d76aSDmitry Pervushin static DEFINE_MUTEX(devices_mutex); 552ba3d76aSDmitry Pervushin 562ba3d76aSDmitry Pervushin /** 572ba3d76aSDmitry Pervushin * find_gluebi_nolock - find a gluebi device. 582ba3d76aSDmitry Pervushin * @ubi_num: UBI device number 592ba3d76aSDmitry Pervushin * @vol_id: volume ID 602ba3d76aSDmitry Pervushin * 612ba3d76aSDmitry Pervushin * This function seraches for gluebi device corresponding to UBI device 622ba3d76aSDmitry Pervushin * @ubi_num and UBI volume @vol_id. Returns the gluebi device description 632ba3d76aSDmitry Pervushin * object in case of success and %NULL in case of failure. The caller has to 642ba3d76aSDmitry Pervushin * have the &devices_mutex locked. 652ba3d76aSDmitry Pervushin */ 662ba3d76aSDmitry Pervushin static struct gluebi_device *find_gluebi_nolock(int ubi_num, int vol_id) 672ba3d76aSDmitry Pervushin { 682ba3d76aSDmitry Pervushin struct gluebi_device *gluebi; 692ba3d76aSDmitry Pervushin 702ba3d76aSDmitry Pervushin list_for_each_entry(gluebi, &gluebi_devices, list) 712ba3d76aSDmitry Pervushin if (gluebi->ubi_num == ubi_num && gluebi->vol_id == vol_id) 722ba3d76aSDmitry Pervushin return gluebi; 732ba3d76aSDmitry Pervushin return NULL; 742ba3d76aSDmitry Pervushin } 75801c135cSArtem B. Bityutskiy 76801c135cSArtem B. Bityutskiy /** 77801c135cSArtem B. Bityutskiy * gluebi_get_device - get MTD device reference. 78801c135cSArtem B. Bityutskiy * @mtd: the MTD device description object 79801c135cSArtem B. Bityutskiy * 80801c135cSArtem B. Bityutskiy * This function is called every time the MTD device is being opened and 81801c135cSArtem B. Bityutskiy * implements the MTD get_device() operation. Returns zero in case of success 82801c135cSArtem B. Bityutskiy * and a negative error code in case of failure. 83801c135cSArtem B. Bityutskiy */ 84801c135cSArtem B. Bityutskiy static int gluebi_get_device(struct mtd_info *mtd) 85801c135cSArtem B. Bityutskiy { 862ba3d76aSDmitry Pervushin struct gluebi_device *gluebi; 872ba3d76aSDmitry Pervushin int ubi_mode = UBI_READONLY; 88801c135cSArtem B. Bityutskiy 892ba3d76aSDmitry Pervushin if (mtd->flags & MTD_WRITEABLE) 902ba3d76aSDmitry Pervushin ubi_mode = UBI_READWRITE; 912ba3d76aSDmitry Pervushin 922ba3d76aSDmitry Pervushin gluebi = container_of(mtd, struct gluebi_device, mtd); 932ba3d76aSDmitry Pervushin mutex_lock(&devices_mutex); 942ba3d76aSDmitry Pervushin if (gluebi->refcnt > 0) { 95801c135cSArtem B. Bityutskiy /* 96801c135cSArtem B. Bityutskiy * The MTD device is already referenced and this is just one 97801c135cSArtem B. Bityutskiy * more reference. MTD allows many users to open the same 98801c135cSArtem B. Bityutskiy * volume simultaneously and do not distinguish between 99061eebbaSAndrew Murray * readers/writers/exclusive/meta openers as UBI does. So we do 100061eebbaSAndrew Murray * not open the UBI volume again - just increase the reference 101801c135cSArtem B. Bityutskiy * counter and return. 102801c135cSArtem B. Bityutskiy */ 1032ba3d76aSDmitry Pervushin gluebi->refcnt += 1; 1042ba3d76aSDmitry Pervushin mutex_unlock(&devices_mutex); 105801c135cSArtem B. Bityutskiy return 0; 106801c135cSArtem B. Bityutskiy } 107801c135cSArtem B. Bityutskiy 108801c135cSArtem B. Bityutskiy /* 109801c135cSArtem B. Bityutskiy * This is the first reference to this UBI volume via the MTD device 110801c135cSArtem B. Bityutskiy * interface. Open the corresponding volume in read-write mode. 111801c135cSArtem B. Bityutskiy */ 1122ba3d76aSDmitry Pervushin gluebi->desc = ubi_open_volume(gluebi->ubi_num, gluebi->vol_id, 1132ba3d76aSDmitry Pervushin ubi_mode); 1142ba3d76aSDmitry Pervushin if (IS_ERR(gluebi->desc)) { 1152ba3d76aSDmitry Pervushin mutex_unlock(&devices_mutex); 1162ba3d76aSDmitry Pervushin return PTR_ERR(gluebi->desc); 1172ba3d76aSDmitry Pervushin } 1182ba3d76aSDmitry Pervushin gluebi->refcnt += 1; 1192ba3d76aSDmitry Pervushin mutex_unlock(&devices_mutex); 120801c135cSArtem B. Bityutskiy return 0; 121801c135cSArtem B. Bityutskiy } 122801c135cSArtem B. Bityutskiy 123801c135cSArtem B. Bityutskiy /** 124801c135cSArtem B. Bityutskiy * gluebi_put_device - put MTD device reference. 125801c135cSArtem B. Bityutskiy * @mtd: the MTD device description object 126801c135cSArtem B. Bityutskiy * 127801c135cSArtem B. Bityutskiy * This function is called every time the MTD device is being put. Returns 128801c135cSArtem B. Bityutskiy * zero in case of success and a negative error code in case of failure. 129801c135cSArtem B. Bityutskiy */ 130801c135cSArtem B. Bityutskiy static void gluebi_put_device(struct mtd_info *mtd) 131801c135cSArtem B. Bityutskiy { 1322ba3d76aSDmitry Pervushin struct gluebi_device *gluebi; 133801c135cSArtem B. Bityutskiy 1342ba3d76aSDmitry Pervushin gluebi = container_of(mtd, struct gluebi_device, mtd); 1352ba3d76aSDmitry Pervushin mutex_lock(&devices_mutex); 1362ba3d76aSDmitry Pervushin gluebi->refcnt -= 1; 1372ba3d76aSDmitry Pervushin if (gluebi->refcnt == 0) 1382ba3d76aSDmitry Pervushin ubi_close_volume(gluebi->desc); 1392ba3d76aSDmitry Pervushin mutex_unlock(&devices_mutex); 140801c135cSArtem B. Bityutskiy } 141801c135cSArtem B. Bityutskiy 142801c135cSArtem B. Bityutskiy /** 143801c135cSArtem B. Bityutskiy * gluebi_read - read operation of emulated MTD devices. 144801c135cSArtem B. Bityutskiy * @mtd: MTD device description object 145801c135cSArtem B. Bityutskiy * @from: absolute offset from where to read 146801c135cSArtem B. Bityutskiy * @len: how many bytes to read 147801c135cSArtem B. Bityutskiy * @retlen: count of read bytes is returned here 148801c135cSArtem B. Bityutskiy * @buf: buffer to store the read data 149801c135cSArtem B. Bityutskiy * 150801c135cSArtem B. Bityutskiy * This function returns zero in case of success and a negative error code in 151801c135cSArtem B. Bityutskiy * case of failure. 152801c135cSArtem B. Bityutskiy */ 153801c135cSArtem B. Bityutskiy static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len, 154801c135cSArtem B. Bityutskiy size_t *retlen, unsigned char *buf) 155801c135cSArtem B. Bityutskiy { 15641c04384SEzequiel Garcia int err = 0, lnum, offs, bytes_left; 1572ba3d76aSDmitry Pervushin struct gluebi_device *gluebi; 158801c135cSArtem B. Bityutskiy 1592ba3d76aSDmitry Pervushin gluebi = container_of(mtd, struct gluebi_device, mtd); 1603013ee31SArtem Bityutskiy lnum = div_u64_rem(from, mtd->erasesize, &offs); 16141c04384SEzequiel Garcia bytes_left = len; 16241c04384SEzequiel Garcia while (bytes_left) { 163801c135cSArtem B. Bityutskiy size_t to_read = mtd->erasesize - offs; 164801c135cSArtem B. Bityutskiy 16541c04384SEzequiel Garcia if (to_read > bytes_left) 16641c04384SEzequiel Garcia to_read = bytes_left; 167801c135cSArtem B. Bityutskiy 1682ba3d76aSDmitry Pervushin err = ubi_read(gluebi->desc, lnum, buf, offs, to_read); 169801c135cSArtem B. Bityutskiy if (err) 170801c135cSArtem B. Bityutskiy break; 171801c135cSArtem B. Bityutskiy 172801c135cSArtem B. Bityutskiy lnum += 1; 173801c135cSArtem B. Bityutskiy offs = 0; 17441c04384SEzequiel Garcia bytes_left -= to_read; 175801c135cSArtem B. Bityutskiy buf += to_read; 176801c135cSArtem B. Bityutskiy } 177801c135cSArtem B. Bityutskiy 17841c04384SEzequiel Garcia *retlen = len - bytes_left; 179801c135cSArtem B. Bityutskiy return err; 180801c135cSArtem B. Bityutskiy } 181801c135cSArtem B. Bityutskiy 182801c135cSArtem B. Bityutskiy /** 183801c135cSArtem B. Bityutskiy * gluebi_write - write operation of emulated MTD devices. 184801c135cSArtem B. Bityutskiy * @mtd: MTD device description object 185801c135cSArtem B. Bityutskiy * @to: absolute offset where to write 186801c135cSArtem B. Bityutskiy * @len: how many bytes to write 187801c135cSArtem B. Bityutskiy * @retlen: count of written bytes is returned here 188801c135cSArtem B. Bityutskiy * @buf: buffer with data to write 189801c135cSArtem B. Bityutskiy * 190801c135cSArtem B. Bityutskiy * This function returns zero in case of success and a negative error code in 191801c135cSArtem B. Bityutskiy * case of failure. 192801c135cSArtem B. Bityutskiy */ 193801c135cSArtem B. Bityutskiy static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len, 194801c135cSArtem B. Bityutskiy size_t *retlen, const u_char *buf) 195801c135cSArtem B. Bityutskiy { 19641c04384SEzequiel Garcia int err = 0, lnum, offs, bytes_left; 1972ba3d76aSDmitry Pervushin struct gluebi_device *gluebi; 198801c135cSArtem B. Bityutskiy 1992ba3d76aSDmitry Pervushin gluebi = container_of(mtd, struct gluebi_device, mtd); 2003013ee31SArtem Bityutskiy lnum = div_u64_rem(to, mtd->erasesize, &offs); 201801c135cSArtem B. Bityutskiy 202801c135cSArtem B. Bityutskiy if (len % mtd->writesize || offs % mtd->writesize) 203801c135cSArtem B. Bityutskiy return -EINVAL; 204801c135cSArtem B. Bityutskiy 20541c04384SEzequiel Garcia bytes_left = len; 20641c04384SEzequiel Garcia while (bytes_left) { 207801c135cSArtem B. Bityutskiy size_t to_write = mtd->erasesize - offs; 208801c135cSArtem B. Bityutskiy 20941c04384SEzequiel Garcia if (to_write > bytes_left) 21041c04384SEzequiel Garcia to_write = bytes_left; 211801c135cSArtem B. Bityutskiy 212b36a261eSRichard Weinberger err = ubi_leb_write(gluebi->desc, lnum, buf, offs, to_write); 213801c135cSArtem B. Bityutskiy if (err) 214801c135cSArtem B. Bityutskiy break; 215801c135cSArtem B. Bityutskiy 216801c135cSArtem B. Bityutskiy lnum += 1; 217801c135cSArtem B. Bityutskiy offs = 0; 21841c04384SEzequiel Garcia bytes_left -= to_write; 219801c135cSArtem B. Bityutskiy buf += to_write; 220801c135cSArtem B. Bityutskiy } 221801c135cSArtem B. Bityutskiy 22241c04384SEzequiel Garcia *retlen = len - bytes_left; 223801c135cSArtem B. Bityutskiy return err; 224801c135cSArtem B. Bityutskiy } 225801c135cSArtem B. Bityutskiy 226801c135cSArtem B. Bityutskiy /** 227801c135cSArtem B. Bityutskiy * gluebi_erase - erase operation of emulated MTD devices. 228801c135cSArtem B. Bityutskiy * @mtd: the MTD device description object 229801c135cSArtem B. Bityutskiy * @instr: the erase operation description 230801c135cSArtem B. Bityutskiy * 231801c135cSArtem B. Bityutskiy * This function calls the erase callback when finishes. Returns zero in case 232801c135cSArtem B. Bityutskiy * of success and a negative error code in case of failure. 233801c135cSArtem B. Bityutskiy */ 234801c135cSArtem B. Bityutskiy static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr) 235801c135cSArtem B. Bityutskiy { 236801c135cSArtem B. Bityutskiy int err, i, lnum, count; 2372ba3d76aSDmitry Pervushin struct gluebi_device *gluebi; 238801c135cSArtem B. Bityutskiy 23969423d99SAdrian Hunter if (mtd_mod_by_ws(instr->addr, mtd) || mtd_mod_by_ws(instr->len, mtd)) 240801c135cSArtem B. Bityutskiy return -EINVAL; 241801c135cSArtem B. Bityutskiy 24269423d99SAdrian Hunter lnum = mtd_div_by_eb(instr->addr, mtd); 24369423d99SAdrian Hunter count = mtd_div_by_eb(instr->len, mtd); 2442ba3d76aSDmitry Pervushin gluebi = container_of(mtd, struct gluebi_device, mtd); 245801c135cSArtem B. Bityutskiy 2462ba3d76aSDmitry Pervushin for (i = 0; i < count - 1; i++) { 2472ba3d76aSDmitry Pervushin err = ubi_leb_unmap(gluebi->desc, lnum + i); 248801c135cSArtem B. Bityutskiy if (err) 249801c135cSArtem B. Bityutskiy goto out_err; 250801c135cSArtem B. Bityutskiy } 251801c135cSArtem B. Bityutskiy /* 252801c135cSArtem B. Bityutskiy * MTD erase operations are synchronous, so we have to make sure the 253801c135cSArtem B. Bityutskiy * physical eraseblock is wiped out. 2542ba3d76aSDmitry Pervushin * 2552ba3d76aSDmitry Pervushin * Thus, perform leb_erase instead of leb_unmap operation - leb_erase 2562ba3d76aSDmitry Pervushin * will wait for the end of operations 257801c135cSArtem B. Bityutskiy */ 2582ba3d76aSDmitry Pervushin err = ubi_leb_erase(gluebi->desc, lnum + i); 259801c135cSArtem B. Bityutskiy if (err) 260801c135cSArtem B. Bityutskiy goto out_err; 261801c135cSArtem B. Bityutskiy 262801c135cSArtem B. Bityutskiy return 0; 263801c135cSArtem B. Bityutskiy 264801c135cSArtem B. Bityutskiy out_err: 26569423d99SAdrian Hunter instr->fail_addr = (long long)lnum * mtd->erasesize; 266801c135cSArtem B. Bityutskiy return err; 267801c135cSArtem B. Bityutskiy } 268801c135cSArtem B. Bityutskiy 269801c135cSArtem B. Bityutskiy /** 2702ba3d76aSDmitry Pervushin * gluebi_create - create a gluebi device for an UBI volume. 2712ba3d76aSDmitry Pervushin * @di: UBI device description object 2722ba3d76aSDmitry Pervushin * @vi: UBI volume description object 273801c135cSArtem B. Bityutskiy * 2742ba3d76aSDmitry Pervushin * This function is called when a new UBI volume is created in order to create 275801c135cSArtem B. Bityutskiy * corresponding fake MTD device. Returns zero in case of success and a 276801c135cSArtem B. Bityutskiy * negative error code in case of failure. 277801c135cSArtem B. Bityutskiy */ 2782ba3d76aSDmitry Pervushin static int gluebi_create(struct ubi_device_info *di, 2792ba3d76aSDmitry Pervushin struct ubi_volume_info *vi) 280801c135cSArtem B. Bityutskiy { 2812ba3d76aSDmitry Pervushin struct gluebi_device *gluebi, *g; 2822ba3d76aSDmitry Pervushin struct mtd_info *mtd; 283801c135cSArtem B. Bityutskiy 2842ba3d76aSDmitry Pervushin gluebi = kzalloc(sizeof(struct gluebi_device), GFP_KERNEL); 2852ba3d76aSDmitry Pervushin if (!gluebi) 286801c135cSArtem B. Bityutskiy return -ENOMEM; 287801c135cSArtem B. Bityutskiy 2882ba3d76aSDmitry Pervushin mtd = &gluebi->mtd; 2892ba3d76aSDmitry Pervushin mtd->name = kmemdup(vi->name, vi->name_len + 1, GFP_KERNEL); 2902ba3d76aSDmitry Pervushin if (!mtd->name) { 2912ba3d76aSDmitry Pervushin kfree(gluebi); 2922ba3d76aSDmitry Pervushin return -ENOMEM; 2932ba3d76aSDmitry Pervushin } 2942ba3d76aSDmitry Pervushin 2952ba3d76aSDmitry Pervushin gluebi->vol_id = vi->vol_id; 296c8cc4525SArtem Bityutskiy gluebi->ubi_num = vi->ubi_num; 297801c135cSArtem B. Bityutskiy mtd->type = MTD_UBIVOLUME; 2982ba3d76aSDmitry Pervushin if (!di->ro_mode) 299801c135cSArtem B. Bityutskiy mtd->flags = MTD_WRITEABLE; 300801c135cSArtem B. Bityutskiy mtd->owner = THIS_MODULE; 3012ba3d76aSDmitry Pervushin mtd->writesize = di->min_io_size; 3022ba3d76aSDmitry Pervushin mtd->erasesize = vi->usable_leb_size; 3033c3c10bbSArtem Bityutskiy mtd->_read = gluebi_read; 3043c3c10bbSArtem Bityutskiy mtd->_write = gluebi_write; 3053c3c10bbSArtem Bityutskiy mtd->_erase = gluebi_erase; 3063c3c10bbSArtem Bityutskiy mtd->_get_device = gluebi_get_device; 3073c3c10bbSArtem Bityutskiy mtd->_put_device = gluebi_put_device; 308801c135cSArtem B. Bityutskiy 309941dfb07SArtem Bityutskiy /* 3102ba3d76aSDmitry Pervushin * In case of dynamic a volume, MTD device size is just volume size. In 311941dfb07SArtem Bityutskiy * case of a static volume the size is equivalent to the amount of data 312cbd8a9d2SJan Altenberg * bytes. 313941dfb07SArtem Bityutskiy */ 3142ba3d76aSDmitry Pervushin if (vi->vol_type == UBI_DYNAMIC_VOLUME) 3152ba3d76aSDmitry Pervushin mtd->size = (unsigned long long)vi->usable_leb_size * vi->size; 316cbd8a9d2SJan Altenberg else 3172ba3d76aSDmitry Pervushin mtd->size = vi->used_bytes; 3182ba3d76aSDmitry Pervushin 3192ba3d76aSDmitry Pervushin /* Just a sanity check - make sure this gluebi device does not exist */ 3202ba3d76aSDmitry Pervushin mutex_lock(&devices_mutex); 3212ba3d76aSDmitry Pervushin g = find_gluebi_nolock(vi->ubi_num, vi->vol_id); 3222ba3d76aSDmitry Pervushin if (g) 323049333ceSArtem Bityutskiy err_msg("gluebi MTD device %d form UBI device %d volume %d already exists", 324049333ceSArtem Bityutskiy g->mtd.index, vi->ubi_num, vi->vol_id); 3252ba3d76aSDmitry Pervushin mutex_unlock(&devices_mutex); 326941dfb07SArtem Bityutskiy 327ee0e87b1SJamie Iles if (mtd_device_register(mtd, NULL, 0)) { 3282ba3d76aSDmitry Pervushin err_msg("cannot add MTD device"); 329801c135cSArtem B. Bityutskiy kfree(mtd->name); 3302ba3d76aSDmitry Pervushin kfree(gluebi); 331801c135cSArtem B. Bityutskiy return -ENFILE; 332801c135cSArtem B. Bityutskiy } 333801c135cSArtem B. Bityutskiy 3342ba3d76aSDmitry Pervushin mutex_lock(&devices_mutex); 3352ba3d76aSDmitry Pervushin list_add_tail(&gluebi->list, &gluebi_devices); 3362ba3d76aSDmitry Pervushin mutex_unlock(&devices_mutex); 337801c135cSArtem B. Bityutskiy return 0; 338801c135cSArtem B. Bityutskiy } 339801c135cSArtem B. Bityutskiy 340801c135cSArtem B. Bityutskiy /** 3412ba3d76aSDmitry Pervushin * gluebi_remove - remove a gluebi device. 3422ba3d76aSDmitry Pervushin * @vi: UBI volume description object 343801c135cSArtem B. Bityutskiy * 3442ba3d76aSDmitry Pervushin * This function is called when an UBI volume is removed and it removes 345801c135cSArtem B. Bityutskiy * corresponding fake MTD device. Returns zero in case of success and a 346801c135cSArtem B. Bityutskiy * negative error code in case of failure. 347801c135cSArtem B. Bityutskiy */ 3482ba3d76aSDmitry Pervushin static int gluebi_remove(struct ubi_volume_info *vi) 349801c135cSArtem B. Bityutskiy { 3502ba3d76aSDmitry Pervushin int err = 0; 3512ba3d76aSDmitry Pervushin struct mtd_info *mtd; 3522ba3d76aSDmitry Pervushin struct gluebi_device *gluebi; 353801c135cSArtem B. Bityutskiy 3542ba3d76aSDmitry Pervushin mutex_lock(&devices_mutex); 3552ba3d76aSDmitry Pervushin gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id); 3562ba3d76aSDmitry Pervushin if (!gluebi) { 357049333ceSArtem Bityutskiy err_msg("got remove notification for unknown UBI device %d volume %d", 358049333ceSArtem Bityutskiy vi->ubi_num, vi->vol_id); 3592ba3d76aSDmitry Pervushin err = -ENOENT; 3602ba3d76aSDmitry Pervushin } else if (gluebi->refcnt) 3612ba3d76aSDmitry Pervushin err = -EBUSY; 3622ba3d76aSDmitry Pervushin else 3632ba3d76aSDmitry Pervushin list_del(&gluebi->list); 3642ba3d76aSDmitry Pervushin mutex_unlock(&devices_mutex); 365801c135cSArtem B. Bityutskiy if (err) 366801c135cSArtem B. Bityutskiy return err; 3672ba3d76aSDmitry Pervushin 3682ba3d76aSDmitry Pervushin mtd = &gluebi->mtd; 369ee0e87b1SJamie Iles err = mtd_device_unregister(mtd); 3702ba3d76aSDmitry Pervushin if (err) { 371049333ceSArtem Bityutskiy err_msg("cannot remove fake MTD device %d, UBI device %d, volume %d, error %d", 372049333ceSArtem Bityutskiy mtd->index, gluebi->ubi_num, gluebi->vol_id, err); 3732ba3d76aSDmitry Pervushin mutex_lock(&devices_mutex); 3742ba3d76aSDmitry Pervushin list_add_tail(&gluebi->list, &gluebi_devices); 3752ba3d76aSDmitry Pervushin mutex_unlock(&devices_mutex); 3762ba3d76aSDmitry Pervushin return err; 3772ba3d76aSDmitry Pervushin } 3782ba3d76aSDmitry Pervushin 379801c135cSArtem B. Bityutskiy kfree(mtd->name); 3802ba3d76aSDmitry Pervushin kfree(gluebi); 381801c135cSArtem B. Bityutskiy return 0; 382801c135cSArtem B. Bityutskiy } 383941dfb07SArtem Bityutskiy 384941dfb07SArtem Bityutskiy /** 3852ba3d76aSDmitry Pervushin * gluebi_updated - UBI volume was updated notifier. 3862ba3d76aSDmitry Pervushin * @vi: volume info structure 387941dfb07SArtem Bityutskiy * 3882ba3d76aSDmitry Pervushin * This function is called every time an UBI volume is updated. It does nothing 3892ba3d76aSDmitry Pervushin * if te volume @vol is dynamic, and changes MTD device size if the 390941dfb07SArtem Bityutskiy * volume is static. This is needed because static volumes cannot be read past 3912ba3d76aSDmitry Pervushin * data they contain. This function returns zero in case of success and a 3922ba3d76aSDmitry Pervushin * negative error code in case of error. 393941dfb07SArtem Bityutskiy */ 3942ba3d76aSDmitry Pervushin static int gluebi_updated(struct ubi_volume_info *vi) 395941dfb07SArtem Bityutskiy { 3962ba3d76aSDmitry Pervushin struct gluebi_device *gluebi; 397941dfb07SArtem Bityutskiy 3982ba3d76aSDmitry Pervushin mutex_lock(&devices_mutex); 3992ba3d76aSDmitry Pervushin gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id); 4002ba3d76aSDmitry Pervushin if (!gluebi) { 4012ba3d76aSDmitry Pervushin mutex_unlock(&devices_mutex); 402049333ceSArtem Bityutskiy err_msg("got update notification for unknown UBI device %d volume %d", 403049333ceSArtem Bityutskiy vi->ubi_num, vi->vol_id); 4042ba3d76aSDmitry Pervushin return -ENOENT; 405941dfb07SArtem Bityutskiy } 4062ba3d76aSDmitry Pervushin 4072ba3d76aSDmitry Pervushin if (vi->vol_type == UBI_STATIC_VOLUME) 4082ba3d76aSDmitry Pervushin gluebi->mtd.size = vi->used_bytes; 4092ba3d76aSDmitry Pervushin mutex_unlock(&devices_mutex); 4102ba3d76aSDmitry Pervushin return 0; 4112ba3d76aSDmitry Pervushin } 4122ba3d76aSDmitry Pervushin 4132ba3d76aSDmitry Pervushin /** 4142ba3d76aSDmitry Pervushin * gluebi_resized - UBI volume was re-sized notifier. 4152ba3d76aSDmitry Pervushin * @vi: volume info structure 4162ba3d76aSDmitry Pervushin * 4172ba3d76aSDmitry Pervushin * This function is called every time an UBI volume is re-size. It changes the 4182ba3d76aSDmitry Pervushin * corresponding fake MTD device size. This function returns zero in case of 4192ba3d76aSDmitry Pervushin * success and a negative error code in case of error. 4202ba3d76aSDmitry Pervushin */ 4212ba3d76aSDmitry Pervushin static int gluebi_resized(struct ubi_volume_info *vi) 4222ba3d76aSDmitry Pervushin { 4232ba3d76aSDmitry Pervushin struct gluebi_device *gluebi; 4242ba3d76aSDmitry Pervushin 4252ba3d76aSDmitry Pervushin mutex_lock(&devices_mutex); 4262ba3d76aSDmitry Pervushin gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id); 4272ba3d76aSDmitry Pervushin if (!gluebi) { 4282ba3d76aSDmitry Pervushin mutex_unlock(&devices_mutex); 429049333ceSArtem Bityutskiy err_msg("got update notification for unknown UBI device %d volume %d", 430049333ceSArtem Bityutskiy vi->ubi_num, vi->vol_id); 4312ba3d76aSDmitry Pervushin return -ENOENT; 4322ba3d76aSDmitry Pervushin } 4332ba3d76aSDmitry Pervushin gluebi->mtd.size = vi->used_bytes; 4342ba3d76aSDmitry Pervushin mutex_unlock(&devices_mutex); 4352ba3d76aSDmitry Pervushin return 0; 4362ba3d76aSDmitry Pervushin } 4372ba3d76aSDmitry Pervushin 4382ba3d76aSDmitry Pervushin /** 4392ba3d76aSDmitry Pervushin * gluebi_notify - UBI notification handler. 4402ba3d76aSDmitry Pervushin * @nb: registered notifier block 4412ba3d76aSDmitry Pervushin * @l: notification type 4422ba3d76aSDmitry Pervushin * @ptr: pointer to the &struct ubi_notification object 4432ba3d76aSDmitry Pervushin */ 4442ba3d76aSDmitry Pervushin static int gluebi_notify(struct notifier_block *nb, unsigned long l, 4452ba3d76aSDmitry Pervushin void *ns_ptr) 4462ba3d76aSDmitry Pervushin { 4472ba3d76aSDmitry Pervushin struct ubi_notification *nt = ns_ptr; 4482ba3d76aSDmitry Pervushin 4492ba3d76aSDmitry Pervushin switch (l) { 4502ba3d76aSDmitry Pervushin case UBI_VOLUME_ADDED: 4512ba3d76aSDmitry Pervushin gluebi_create(&nt->di, &nt->vi); 4522ba3d76aSDmitry Pervushin break; 4532ba3d76aSDmitry Pervushin case UBI_VOLUME_REMOVED: 4542ba3d76aSDmitry Pervushin gluebi_remove(&nt->vi); 4552ba3d76aSDmitry Pervushin break; 4562ba3d76aSDmitry Pervushin case UBI_VOLUME_RESIZED: 4572ba3d76aSDmitry Pervushin gluebi_resized(&nt->vi); 4582ba3d76aSDmitry Pervushin break; 4592ba3d76aSDmitry Pervushin case UBI_VOLUME_UPDATED: 4602ba3d76aSDmitry Pervushin gluebi_updated(&nt->vi); 4612ba3d76aSDmitry Pervushin break; 4622ba3d76aSDmitry Pervushin default: 4632ba3d76aSDmitry Pervushin break; 4642ba3d76aSDmitry Pervushin } 4652ba3d76aSDmitry Pervushin return NOTIFY_OK; 4662ba3d76aSDmitry Pervushin } 4672ba3d76aSDmitry Pervushin 4682ba3d76aSDmitry Pervushin static struct notifier_block gluebi_notifier = { 4692ba3d76aSDmitry Pervushin .notifier_call = gluebi_notify, 4702ba3d76aSDmitry Pervushin }; 4712ba3d76aSDmitry Pervushin 4722ba3d76aSDmitry Pervushin static int __init ubi_gluebi_init(void) 4732ba3d76aSDmitry Pervushin { 4742ba3d76aSDmitry Pervushin return ubi_register_volume_notifier(&gluebi_notifier, 0); 4752ba3d76aSDmitry Pervushin } 4762ba3d76aSDmitry Pervushin 4772ba3d76aSDmitry Pervushin static void __exit ubi_gluebi_exit(void) 4782ba3d76aSDmitry Pervushin { 4792ba3d76aSDmitry Pervushin struct gluebi_device *gluebi, *g; 4802ba3d76aSDmitry Pervushin 4812ba3d76aSDmitry Pervushin list_for_each_entry_safe(gluebi, g, &gluebi_devices, list) { 4822ba3d76aSDmitry Pervushin int err; 4832ba3d76aSDmitry Pervushin struct mtd_info *mtd = &gluebi->mtd; 4842ba3d76aSDmitry Pervushin 485ee0e87b1SJamie Iles err = mtd_device_unregister(mtd); 4862ba3d76aSDmitry Pervushin if (err) 487049333ceSArtem Bityutskiy err_msg("error %d while removing gluebi MTD device %d, UBI device %d, volume %d - ignoring", 488049333ceSArtem Bityutskiy err, mtd->index, gluebi->ubi_num, 489049333ceSArtem Bityutskiy gluebi->vol_id); 4902ba3d76aSDmitry Pervushin kfree(mtd->name); 4912ba3d76aSDmitry Pervushin kfree(gluebi); 4922ba3d76aSDmitry Pervushin } 4932ba3d76aSDmitry Pervushin ubi_unregister_volume_notifier(&gluebi_notifier); 4942ba3d76aSDmitry Pervushin } 4952ba3d76aSDmitry Pervushin 4962ba3d76aSDmitry Pervushin module_init(ubi_gluebi_init); 4972ba3d76aSDmitry Pervushin module_exit(ubi_gluebi_exit); 4982ba3d76aSDmitry Pervushin MODULE_DESCRIPTION("MTD emulation layer over UBI volumes"); 4992ba3d76aSDmitry Pervushin MODULE_AUTHOR("Artem Bityutskiy, Joern Engel"); 5002ba3d76aSDmitry Pervushin MODULE_LICENSE("GPL"); 501