162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) International Business Machines Corp., 2006 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: Artem Bityutskiy (Битюцкий Артём), Joern Engel 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* 962306a36Sopenharmony_ci * This is a small driver which implements fake MTD devices on top of UBI 1062306a36Sopenharmony_ci * volumes. This sounds strange, but it is in fact quite useful to make 1162306a36Sopenharmony_ci * MTD-oriented software (including all the legacy software) work on top of 1262306a36Sopenharmony_ci * UBI. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Gluebi emulates MTD devices of "MTD_UBIVOLUME" type. Their minimal I/O unit 1562306a36Sopenharmony_ci * size (@mtd->writesize) is equivalent to the UBI minimal I/O unit. The 1662306a36Sopenharmony_ci * eraseblock size is equivalent to the logical eraseblock size of the volume. 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/err.h> 2062306a36Sopenharmony_ci#include <linux/list.h> 2162306a36Sopenharmony_ci#include <linux/slab.h> 2262306a36Sopenharmony_ci#include <linux/sched.h> 2362306a36Sopenharmony_ci#include <linux/math64.h> 2462306a36Sopenharmony_ci#include <linux/module.h> 2562306a36Sopenharmony_ci#include <linux/mutex.h> 2662306a36Sopenharmony_ci#include <linux/mtd/ubi.h> 2762306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 2862306a36Sopenharmony_ci#include "ubi-media.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define err_msg(fmt, ...) \ 3162306a36Sopenharmony_ci pr_err("gluebi (pid %d): %s: " fmt "\n", \ 3262306a36Sopenharmony_ci current->pid, __func__, ##__VA_ARGS__) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/** 3562306a36Sopenharmony_ci * struct gluebi_device - a gluebi device description data structure. 3662306a36Sopenharmony_ci * @mtd: emulated MTD device description object 3762306a36Sopenharmony_ci * @refcnt: gluebi device reference count 3862306a36Sopenharmony_ci * @desc: UBI volume descriptor 3962306a36Sopenharmony_ci * @ubi_num: UBI device number this gluebi device works on 4062306a36Sopenharmony_ci * @vol_id: ID of UBI volume this gluebi device works on 4162306a36Sopenharmony_ci * @list: link in a list of gluebi devices 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_cistruct gluebi_device { 4462306a36Sopenharmony_ci struct mtd_info mtd; 4562306a36Sopenharmony_ci int refcnt; 4662306a36Sopenharmony_ci struct ubi_volume_desc *desc; 4762306a36Sopenharmony_ci int ubi_num; 4862306a36Sopenharmony_ci int vol_id; 4962306a36Sopenharmony_ci struct list_head list; 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* List of all gluebi devices */ 5362306a36Sopenharmony_cistatic LIST_HEAD(gluebi_devices); 5462306a36Sopenharmony_cistatic DEFINE_MUTEX(devices_mutex); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/** 5762306a36Sopenharmony_ci * find_gluebi_nolock - find a gluebi device. 5862306a36Sopenharmony_ci * @ubi_num: UBI device number 5962306a36Sopenharmony_ci * @vol_id: volume ID 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * This function seraches for gluebi device corresponding to UBI device 6262306a36Sopenharmony_ci * @ubi_num and UBI volume @vol_id. Returns the gluebi device description 6362306a36Sopenharmony_ci * object in case of success and %NULL in case of failure. The caller has to 6462306a36Sopenharmony_ci * have the &devices_mutex locked. 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_cistatic struct gluebi_device *find_gluebi_nolock(int ubi_num, int vol_id) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct gluebi_device *gluebi; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci list_for_each_entry(gluebi, &gluebi_devices, list) 7162306a36Sopenharmony_ci if (gluebi->ubi_num == ubi_num && gluebi->vol_id == vol_id) 7262306a36Sopenharmony_ci return gluebi; 7362306a36Sopenharmony_ci return NULL; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/** 7762306a36Sopenharmony_ci * gluebi_get_device - get MTD device reference. 7862306a36Sopenharmony_ci * @mtd: the MTD device description object 7962306a36Sopenharmony_ci * 8062306a36Sopenharmony_ci * This function is called every time the MTD device is being opened and 8162306a36Sopenharmony_ci * implements the MTD get_device() operation. Returns zero in case of success 8262306a36Sopenharmony_ci * and a negative error code in case of failure. 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_cistatic int gluebi_get_device(struct mtd_info *mtd) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct gluebi_device *gluebi; 8762306a36Sopenharmony_ci int ubi_mode = UBI_READONLY; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (mtd->flags & MTD_WRITEABLE) 9062306a36Sopenharmony_ci ubi_mode = UBI_READWRITE; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci gluebi = container_of(mtd, struct gluebi_device, mtd); 9362306a36Sopenharmony_ci mutex_lock(&devices_mutex); 9462306a36Sopenharmony_ci if (gluebi->refcnt > 0) { 9562306a36Sopenharmony_ci /* 9662306a36Sopenharmony_ci * The MTD device is already referenced and this is just one 9762306a36Sopenharmony_ci * more reference. MTD allows many users to open the same 9862306a36Sopenharmony_ci * volume simultaneously and do not distinguish between 9962306a36Sopenharmony_ci * readers/writers/exclusive/meta openers as UBI does. So we do 10062306a36Sopenharmony_ci * not open the UBI volume again - just increase the reference 10162306a36Sopenharmony_ci * counter and return. 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_ci gluebi->refcnt += 1; 10462306a36Sopenharmony_ci mutex_unlock(&devices_mutex); 10562306a36Sopenharmony_ci return 0; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* 10962306a36Sopenharmony_ci * This is the first reference to this UBI volume via the MTD device 11062306a36Sopenharmony_ci * interface. Open the corresponding volume in read-write mode. 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_ci gluebi->desc = ubi_open_volume(gluebi->ubi_num, gluebi->vol_id, 11362306a36Sopenharmony_ci ubi_mode); 11462306a36Sopenharmony_ci if (IS_ERR(gluebi->desc)) { 11562306a36Sopenharmony_ci mutex_unlock(&devices_mutex); 11662306a36Sopenharmony_ci return PTR_ERR(gluebi->desc); 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci gluebi->refcnt += 1; 11962306a36Sopenharmony_ci mutex_unlock(&devices_mutex); 12062306a36Sopenharmony_ci return 0; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/** 12462306a36Sopenharmony_ci * gluebi_put_device - put MTD device reference. 12562306a36Sopenharmony_ci * @mtd: the MTD device description object 12662306a36Sopenharmony_ci * 12762306a36Sopenharmony_ci * This function is called every time the MTD device is being put. Returns 12862306a36Sopenharmony_ci * zero in case of success and a negative error code in case of failure. 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_cistatic void gluebi_put_device(struct mtd_info *mtd) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct gluebi_device *gluebi; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci gluebi = container_of(mtd, struct gluebi_device, mtd); 13562306a36Sopenharmony_ci mutex_lock(&devices_mutex); 13662306a36Sopenharmony_ci gluebi->refcnt -= 1; 13762306a36Sopenharmony_ci if (gluebi->refcnt == 0) 13862306a36Sopenharmony_ci ubi_close_volume(gluebi->desc); 13962306a36Sopenharmony_ci mutex_unlock(&devices_mutex); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/** 14362306a36Sopenharmony_ci * gluebi_read - read operation of emulated MTD devices. 14462306a36Sopenharmony_ci * @mtd: MTD device description object 14562306a36Sopenharmony_ci * @from: absolute offset from where to read 14662306a36Sopenharmony_ci * @len: how many bytes to read 14762306a36Sopenharmony_ci * @retlen: count of read bytes is returned here 14862306a36Sopenharmony_ci * @buf: buffer to store the read data 14962306a36Sopenharmony_ci * 15062306a36Sopenharmony_ci * This function returns zero in case of success and a negative error code in 15162306a36Sopenharmony_ci * case of failure. 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_cistatic int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len, 15462306a36Sopenharmony_ci size_t *retlen, unsigned char *buf) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci int err = 0, lnum, offs, bytes_left; 15762306a36Sopenharmony_ci struct gluebi_device *gluebi; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci gluebi = container_of(mtd, struct gluebi_device, mtd); 16062306a36Sopenharmony_ci lnum = div_u64_rem(from, mtd->erasesize, &offs); 16162306a36Sopenharmony_ci bytes_left = len; 16262306a36Sopenharmony_ci while (bytes_left) { 16362306a36Sopenharmony_ci size_t to_read = mtd->erasesize - offs; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (to_read > bytes_left) 16662306a36Sopenharmony_ci to_read = bytes_left; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci err = ubi_read(gluebi->desc, lnum, buf, offs, to_read); 16962306a36Sopenharmony_ci if (err) 17062306a36Sopenharmony_ci break; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci lnum += 1; 17362306a36Sopenharmony_ci offs = 0; 17462306a36Sopenharmony_ci bytes_left -= to_read; 17562306a36Sopenharmony_ci buf += to_read; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci *retlen = len - bytes_left; 17962306a36Sopenharmony_ci return err; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci/** 18362306a36Sopenharmony_ci * gluebi_write - write operation of emulated MTD devices. 18462306a36Sopenharmony_ci * @mtd: MTD device description object 18562306a36Sopenharmony_ci * @to: absolute offset where to write 18662306a36Sopenharmony_ci * @len: how many bytes to write 18762306a36Sopenharmony_ci * @retlen: count of written bytes is returned here 18862306a36Sopenharmony_ci * @buf: buffer with data to write 18962306a36Sopenharmony_ci * 19062306a36Sopenharmony_ci * This function returns zero in case of success and a negative error code in 19162306a36Sopenharmony_ci * case of failure. 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_cistatic int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len, 19462306a36Sopenharmony_ci size_t *retlen, const u_char *buf) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci int err = 0, lnum, offs, bytes_left; 19762306a36Sopenharmony_ci struct gluebi_device *gluebi; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci gluebi = container_of(mtd, struct gluebi_device, mtd); 20062306a36Sopenharmony_ci lnum = div_u64_rem(to, mtd->erasesize, &offs); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (len % mtd->writesize || offs % mtd->writesize) 20362306a36Sopenharmony_ci return -EINVAL; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci bytes_left = len; 20662306a36Sopenharmony_ci while (bytes_left) { 20762306a36Sopenharmony_ci size_t to_write = mtd->erasesize - offs; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (to_write > bytes_left) 21062306a36Sopenharmony_ci to_write = bytes_left; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci err = ubi_leb_write(gluebi->desc, lnum, buf, offs, to_write); 21362306a36Sopenharmony_ci if (err) 21462306a36Sopenharmony_ci break; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci lnum += 1; 21762306a36Sopenharmony_ci offs = 0; 21862306a36Sopenharmony_ci bytes_left -= to_write; 21962306a36Sopenharmony_ci buf += to_write; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci *retlen = len - bytes_left; 22362306a36Sopenharmony_ci return err; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci/** 22762306a36Sopenharmony_ci * gluebi_erase - erase operation of emulated MTD devices. 22862306a36Sopenharmony_ci * @mtd: the MTD device description object 22962306a36Sopenharmony_ci * @instr: the erase operation description 23062306a36Sopenharmony_ci * 23162306a36Sopenharmony_ci * This function calls the erase callback when finishes. Returns zero in case 23262306a36Sopenharmony_ci * of success and a negative error code in case of failure. 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_cistatic int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci int err, i, lnum, count; 23762306a36Sopenharmony_ci struct gluebi_device *gluebi; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (mtd_mod_by_ws(instr->addr, mtd) || mtd_mod_by_ws(instr->len, mtd)) 24062306a36Sopenharmony_ci return -EINVAL; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci lnum = mtd_div_by_eb(instr->addr, mtd); 24362306a36Sopenharmony_ci count = mtd_div_by_eb(instr->len, mtd); 24462306a36Sopenharmony_ci gluebi = container_of(mtd, struct gluebi_device, mtd); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci for (i = 0; i < count - 1; i++) { 24762306a36Sopenharmony_ci err = ubi_leb_unmap(gluebi->desc, lnum + i); 24862306a36Sopenharmony_ci if (err) 24962306a36Sopenharmony_ci goto out_err; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci /* 25262306a36Sopenharmony_ci * MTD erase operations are synchronous, so we have to make sure the 25362306a36Sopenharmony_ci * physical eraseblock is wiped out. 25462306a36Sopenharmony_ci * 25562306a36Sopenharmony_ci * Thus, perform leb_erase instead of leb_unmap operation - leb_erase 25662306a36Sopenharmony_ci * will wait for the end of operations 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_ci err = ubi_leb_erase(gluebi->desc, lnum + i); 25962306a36Sopenharmony_ci if (err) 26062306a36Sopenharmony_ci goto out_err; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ciout_err: 26562306a36Sopenharmony_ci instr->fail_addr = (long long)lnum * mtd->erasesize; 26662306a36Sopenharmony_ci return err; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci/** 27062306a36Sopenharmony_ci * gluebi_create - create a gluebi device for an UBI volume. 27162306a36Sopenharmony_ci * @di: UBI device description object 27262306a36Sopenharmony_ci * @vi: UBI volume description object 27362306a36Sopenharmony_ci * 27462306a36Sopenharmony_ci * This function is called when a new UBI volume is created in order to create 27562306a36Sopenharmony_ci * corresponding fake MTD device. Returns zero in case of success and a 27662306a36Sopenharmony_ci * negative error code in case of failure. 27762306a36Sopenharmony_ci */ 27862306a36Sopenharmony_cistatic int gluebi_create(struct ubi_device_info *di, 27962306a36Sopenharmony_ci struct ubi_volume_info *vi) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct gluebi_device *gluebi, *g; 28262306a36Sopenharmony_ci struct mtd_info *mtd; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci gluebi = kzalloc(sizeof(struct gluebi_device), GFP_KERNEL); 28562306a36Sopenharmony_ci if (!gluebi) 28662306a36Sopenharmony_ci return -ENOMEM; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci mtd = &gluebi->mtd; 28962306a36Sopenharmony_ci mtd->name = kmemdup(vi->name, vi->name_len + 1, GFP_KERNEL); 29062306a36Sopenharmony_ci if (!mtd->name) { 29162306a36Sopenharmony_ci kfree(gluebi); 29262306a36Sopenharmony_ci return -ENOMEM; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci gluebi->vol_id = vi->vol_id; 29662306a36Sopenharmony_ci gluebi->ubi_num = vi->ubi_num; 29762306a36Sopenharmony_ci mtd->type = MTD_UBIVOLUME; 29862306a36Sopenharmony_ci if (!di->ro_mode) 29962306a36Sopenharmony_ci mtd->flags = MTD_WRITEABLE; 30062306a36Sopenharmony_ci mtd->owner = THIS_MODULE; 30162306a36Sopenharmony_ci mtd->writesize = di->min_io_size; 30262306a36Sopenharmony_ci mtd->erasesize = vi->usable_leb_size; 30362306a36Sopenharmony_ci mtd->_read = gluebi_read; 30462306a36Sopenharmony_ci mtd->_write = gluebi_write; 30562306a36Sopenharmony_ci mtd->_erase = gluebi_erase; 30662306a36Sopenharmony_ci mtd->_get_device = gluebi_get_device; 30762306a36Sopenharmony_ci mtd->_put_device = gluebi_put_device; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* 31062306a36Sopenharmony_ci * In case of dynamic a volume, MTD device size is just volume size. In 31162306a36Sopenharmony_ci * case of a static volume the size is equivalent to the amount of data 31262306a36Sopenharmony_ci * bytes. 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_ci if (vi->vol_type == UBI_DYNAMIC_VOLUME) 31562306a36Sopenharmony_ci mtd->size = (unsigned long long)vi->usable_leb_size * vi->size; 31662306a36Sopenharmony_ci else 31762306a36Sopenharmony_ci mtd->size = vi->used_bytes; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* Just a sanity check - make sure this gluebi device does not exist */ 32062306a36Sopenharmony_ci mutex_lock(&devices_mutex); 32162306a36Sopenharmony_ci g = find_gluebi_nolock(vi->ubi_num, vi->vol_id); 32262306a36Sopenharmony_ci if (g) 32362306a36Sopenharmony_ci err_msg("gluebi MTD device %d form UBI device %d volume %d already exists", 32462306a36Sopenharmony_ci g->mtd.index, vi->ubi_num, vi->vol_id); 32562306a36Sopenharmony_ci mutex_unlock(&devices_mutex); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (mtd_device_register(mtd, NULL, 0)) { 32862306a36Sopenharmony_ci err_msg("cannot add MTD device"); 32962306a36Sopenharmony_ci kfree(mtd->name); 33062306a36Sopenharmony_ci kfree(gluebi); 33162306a36Sopenharmony_ci return -ENFILE; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci mutex_lock(&devices_mutex); 33562306a36Sopenharmony_ci list_add_tail(&gluebi->list, &gluebi_devices); 33662306a36Sopenharmony_ci mutex_unlock(&devices_mutex); 33762306a36Sopenharmony_ci return 0; 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci/** 34162306a36Sopenharmony_ci * gluebi_remove - remove a gluebi device. 34262306a36Sopenharmony_ci * @vi: UBI volume description object 34362306a36Sopenharmony_ci * 34462306a36Sopenharmony_ci * This function is called when an UBI volume is removed and it removes 34562306a36Sopenharmony_ci * corresponding fake MTD device. Returns zero in case of success and a 34662306a36Sopenharmony_ci * negative error code in case of failure. 34762306a36Sopenharmony_ci */ 34862306a36Sopenharmony_cistatic int gluebi_remove(struct ubi_volume_info *vi) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci int err = 0; 35162306a36Sopenharmony_ci struct mtd_info *mtd; 35262306a36Sopenharmony_ci struct gluebi_device *gluebi; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci mutex_lock(&devices_mutex); 35562306a36Sopenharmony_ci gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id); 35662306a36Sopenharmony_ci if (!gluebi) { 35762306a36Sopenharmony_ci err_msg("got remove notification for unknown UBI device %d volume %d", 35862306a36Sopenharmony_ci vi->ubi_num, vi->vol_id); 35962306a36Sopenharmony_ci err = -ENOENT; 36062306a36Sopenharmony_ci } else if (gluebi->refcnt) 36162306a36Sopenharmony_ci err = -EBUSY; 36262306a36Sopenharmony_ci else 36362306a36Sopenharmony_ci list_del(&gluebi->list); 36462306a36Sopenharmony_ci mutex_unlock(&devices_mutex); 36562306a36Sopenharmony_ci if (err) 36662306a36Sopenharmony_ci return err; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci mtd = &gluebi->mtd; 36962306a36Sopenharmony_ci err = mtd_device_unregister(mtd); 37062306a36Sopenharmony_ci if (err) { 37162306a36Sopenharmony_ci err_msg("cannot remove fake MTD device %d, UBI device %d, volume %d, error %d", 37262306a36Sopenharmony_ci mtd->index, gluebi->ubi_num, gluebi->vol_id, err); 37362306a36Sopenharmony_ci mutex_lock(&devices_mutex); 37462306a36Sopenharmony_ci list_add_tail(&gluebi->list, &gluebi_devices); 37562306a36Sopenharmony_ci mutex_unlock(&devices_mutex); 37662306a36Sopenharmony_ci return err; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci kfree(mtd->name); 38062306a36Sopenharmony_ci kfree(gluebi); 38162306a36Sopenharmony_ci return 0; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci/** 38562306a36Sopenharmony_ci * gluebi_updated - UBI volume was updated notifier. 38662306a36Sopenharmony_ci * @vi: volume info structure 38762306a36Sopenharmony_ci * 38862306a36Sopenharmony_ci * This function is called every time an UBI volume is updated. It does nothing 38962306a36Sopenharmony_ci * if te volume @vol is dynamic, and changes MTD device size if the 39062306a36Sopenharmony_ci * volume is static. This is needed because static volumes cannot be read past 39162306a36Sopenharmony_ci * data they contain. This function returns zero in case of success and a 39262306a36Sopenharmony_ci * negative error code in case of error. 39362306a36Sopenharmony_ci */ 39462306a36Sopenharmony_cistatic int gluebi_updated(struct ubi_volume_info *vi) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct gluebi_device *gluebi; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci mutex_lock(&devices_mutex); 39962306a36Sopenharmony_ci gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id); 40062306a36Sopenharmony_ci if (!gluebi) { 40162306a36Sopenharmony_ci mutex_unlock(&devices_mutex); 40262306a36Sopenharmony_ci err_msg("got update notification for unknown UBI device %d volume %d", 40362306a36Sopenharmony_ci vi->ubi_num, vi->vol_id); 40462306a36Sopenharmony_ci return -ENOENT; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (vi->vol_type == UBI_STATIC_VOLUME) 40862306a36Sopenharmony_ci gluebi->mtd.size = vi->used_bytes; 40962306a36Sopenharmony_ci mutex_unlock(&devices_mutex); 41062306a36Sopenharmony_ci return 0; 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci/** 41462306a36Sopenharmony_ci * gluebi_resized - UBI volume was re-sized notifier. 41562306a36Sopenharmony_ci * @vi: volume info structure 41662306a36Sopenharmony_ci * 41762306a36Sopenharmony_ci * This function is called every time an UBI volume is re-size. It changes the 41862306a36Sopenharmony_ci * corresponding fake MTD device size. This function returns zero in case of 41962306a36Sopenharmony_ci * success and a negative error code in case of error. 42062306a36Sopenharmony_ci */ 42162306a36Sopenharmony_cistatic int gluebi_resized(struct ubi_volume_info *vi) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci struct gluebi_device *gluebi; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci mutex_lock(&devices_mutex); 42662306a36Sopenharmony_ci gluebi = find_gluebi_nolock(vi->ubi_num, vi->vol_id); 42762306a36Sopenharmony_ci if (!gluebi) { 42862306a36Sopenharmony_ci mutex_unlock(&devices_mutex); 42962306a36Sopenharmony_ci err_msg("got update notification for unknown UBI device %d volume %d", 43062306a36Sopenharmony_ci vi->ubi_num, vi->vol_id); 43162306a36Sopenharmony_ci return -ENOENT; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci gluebi->mtd.size = vi->used_bytes; 43462306a36Sopenharmony_ci mutex_unlock(&devices_mutex); 43562306a36Sopenharmony_ci return 0; 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci/** 43962306a36Sopenharmony_ci * gluebi_notify - UBI notification handler. 44062306a36Sopenharmony_ci * @nb: registered notifier block 44162306a36Sopenharmony_ci * @l: notification type 44262306a36Sopenharmony_ci * @ns_ptr: pointer to the &struct ubi_notification object 44362306a36Sopenharmony_ci */ 44462306a36Sopenharmony_cistatic int gluebi_notify(struct notifier_block *nb, unsigned long l, 44562306a36Sopenharmony_ci void *ns_ptr) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci struct ubi_notification *nt = ns_ptr; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci switch (l) { 45062306a36Sopenharmony_ci case UBI_VOLUME_ADDED: 45162306a36Sopenharmony_ci gluebi_create(&nt->di, &nt->vi); 45262306a36Sopenharmony_ci break; 45362306a36Sopenharmony_ci case UBI_VOLUME_REMOVED: 45462306a36Sopenharmony_ci gluebi_remove(&nt->vi); 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci case UBI_VOLUME_RESIZED: 45762306a36Sopenharmony_ci gluebi_resized(&nt->vi); 45862306a36Sopenharmony_ci break; 45962306a36Sopenharmony_ci case UBI_VOLUME_UPDATED: 46062306a36Sopenharmony_ci gluebi_updated(&nt->vi); 46162306a36Sopenharmony_ci break; 46262306a36Sopenharmony_ci default: 46362306a36Sopenharmony_ci break; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci return NOTIFY_OK; 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistatic struct notifier_block gluebi_notifier = { 46962306a36Sopenharmony_ci .notifier_call = gluebi_notify, 47062306a36Sopenharmony_ci}; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic int __init ubi_gluebi_init(void) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci return ubi_register_volume_notifier(&gluebi_notifier, 0); 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic void __exit ubi_gluebi_exit(void) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci struct gluebi_device *gluebi, *g; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci list_for_each_entry_safe(gluebi, g, &gluebi_devices, list) { 48262306a36Sopenharmony_ci int err; 48362306a36Sopenharmony_ci struct mtd_info *mtd = &gluebi->mtd; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci err = mtd_device_unregister(mtd); 48662306a36Sopenharmony_ci if (err) 48762306a36Sopenharmony_ci err_msg("error %d while removing gluebi MTD device %d, UBI device %d, volume %d - ignoring", 48862306a36Sopenharmony_ci err, mtd->index, gluebi->ubi_num, 48962306a36Sopenharmony_ci gluebi->vol_id); 49062306a36Sopenharmony_ci kfree(mtd->name); 49162306a36Sopenharmony_ci kfree(gluebi); 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci ubi_unregister_volume_notifier(&gluebi_notifier); 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cimodule_init(ubi_gluebi_init); 49762306a36Sopenharmony_cimodule_exit(ubi_gluebi_exit); 49862306a36Sopenharmony_ciMODULE_DESCRIPTION("MTD emulation layer over UBI volumes"); 49962306a36Sopenharmony_ciMODULE_AUTHOR("Artem Bityutskiy, Joern Engel"); 50062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 501