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