xref: /kernel/linux/linux-5.10/drivers/mtd/ubi/vmt.c (revision 8c2ecf20)
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 (Битюцкий Артём)
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/*
98c2ecf20Sopenharmony_ci * This file contains implementation of volume creation, deletion, updating and
108c2ecf20Sopenharmony_ci * resizing.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/err.h>
148c2ecf20Sopenharmony_ci#include <linux/math64.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/export.h>
178c2ecf20Sopenharmony_ci#include "ubi.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic int self_check_volumes(struct ubi_device *ubi);
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic ssize_t vol_attribute_show(struct device *dev,
228c2ecf20Sopenharmony_ci				  struct device_attribute *attr, char *buf);
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/* Device attributes corresponding to files in '/<sysfs>/class/ubi/ubiX_Y' */
258c2ecf20Sopenharmony_cistatic struct device_attribute attr_vol_reserved_ebs =
268c2ecf20Sopenharmony_ci	__ATTR(reserved_ebs, S_IRUGO, vol_attribute_show, NULL);
278c2ecf20Sopenharmony_cistatic struct device_attribute attr_vol_type =
288c2ecf20Sopenharmony_ci	__ATTR(type, S_IRUGO, vol_attribute_show, NULL);
298c2ecf20Sopenharmony_cistatic struct device_attribute attr_vol_name =
308c2ecf20Sopenharmony_ci	__ATTR(name, S_IRUGO, vol_attribute_show, NULL);
318c2ecf20Sopenharmony_cistatic struct device_attribute attr_vol_corrupted =
328c2ecf20Sopenharmony_ci	__ATTR(corrupted, S_IRUGO, vol_attribute_show, NULL);
338c2ecf20Sopenharmony_cistatic struct device_attribute attr_vol_alignment =
348c2ecf20Sopenharmony_ci	__ATTR(alignment, S_IRUGO, vol_attribute_show, NULL);
358c2ecf20Sopenharmony_cistatic struct device_attribute attr_vol_usable_eb_size =
368c2ecf20Sopenharmony_ci	__ATTR(usable_eb_size, S_IRUGO, vol_attribute_show, NULL);
378c2ecf20Sopenharmony_cistatic struct device_attribute attr_vol_data_bytes =
388c2ecf20Sopenharmony_ci	__ATTR(data_bytes, S_IRUGO, vol_attribute_show, NULL);
398c2ecf20Sopenharmony_cistatic struct device_attribute attr_vol_upd_marker =
408c2ecf20Sopenharmony_ci	__ATTR(upd_marker, S_IRUGO, vol_attribute_show, NULL);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/*
438c2ecf20Sopenharmony_ci * "Show" method for files in '/<sysfs>/class/ubi/ubiX_Y/'.
448c2ecf20Sopenharmony_ci *
458c2ecf20Sopenharmony_ci * Consider a situation:
468c2ecf20Sopenharmony_ci * A. process 1 opens a sysfs file related to volume Y, say
478c2ecf20Sopenharmony_ci *    /<sysfs>/class/ubi/ubiX_Y/reserved_ebs;
488c2ecf20Sopenharmony_ci * B. process 2 removes volume Y;
498c2ecf20Sopenharmony_ci * C. process 1 starts reading the /<sysfs>/class/ubi/ubiX_Y/reserved_ebs file;
508c2ecf20Sopenharmony_ci *
518c2ecf20Sopenharmony_ci * In this situation, this function will return %-ENODEV because it will find
528c2ecf20Sopenharmony_ci * out that the volume was removed from the @ubi->volumes array.
538c2ecf20Sopenharmony_ci */
548c2ecf20Sopenharmony_cistatic ssize_t vol_attribute_show(struct device *dev,
558c2ecf20Sopenharmony_ci				  struct device_attribute *attr, char *buf)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	int ret;
588c2ecf20Sopenharmony_ci	struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev);
598c2ecf20Sopenharmony_ci	struct ubi_device *ubi = vol->ubi;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	spin_lock(&ubi->volumes_lock);
628c2ecf20Sopenharmony_ci	if (!ubi->volumes[vol->vol_id]) {
638c2ecf20Sopenharmony_ci		spin_unlock(&ubi->volumes_lock);
648c2ecf20Sopenharmony_ci		return -ENODEV;
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci	/* Take a reference to prevent volume removal */
678c2ecf20Sopenharmony_ci	vol->ref_count += 1;
688c2ecf20Sopenharmony_ci	spin_unlock(&ubi->volumes_lock);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	if (attr == &attr_vol_reserved_ebs)
718c2ecf20Sopenharmony_ci		ret = sprintf(buf, "%d\n", vol->reserved_pebs);
728c2ecf20Sopenharmony_ci	else if (attr == &attr_vol_type) {
738c2ecf20Sopenharmony_ci		const char *tp;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci		if (vol->vol_type == UBI_DYNAMIC_VOLUME)
768c2ecf20Sopenharmony_ci			tp = "dynamic";
778c2ecf20Sopenharmony_ci		else
788c2ecf20Sopenharmony_ci			tp = "static";
798c2ecf20Sopenharmony_ci		ret = sprintf(buf, "%s\n", tp);
808c2ecf20Sopenharmony_ci	} else if (attr == &attr_vol_name)
818c2ecf20Sopenharmony_ci		ret = sprintf(buf, "%s\n", vol->name);
828c2ecf20Sopenharmony_ci	else if (attr == &attr_vol_corrupted)
838c2ecf20Sopenharmony_ci		ret = sprintf(buf, "%d\n", vol->corrupted);
848c2ecf20Sopenharmony_ci	else if (attr == &attr_vol_alignment)
858c2ecf20Sopenharmony_ci		ret = sprintf(buf, "%d\n", vol->alignment);
868c2ecf20Sopenharmony_ci	else if (attr == &attr_vol_usable_eb_size)
878c2ecf20Sopenharmony_ci		ret = sprintf(buf, "%d\n", vol->usable_leb_size);
888c2ecf20Sopenharmony_ci	else if (attr == &attr_vol_data_bytes)
898c2ecf20Sopenharmony_ci		ret = sprintf(buf, "%lld\n", vol->used_bytes);
908c2ecf20Sopenharmony_ci	else if (attr == &attr_vol_upd_marker)
918c2ecf20Sopenharmony_ci		ret = sprintf(buf, "%d\n", vol->upd_marker);
928c2ecf20Sopenharmony_ci	else
938c2ecf20Sopenharmony_ci		/* This must be a bug */
948c2ecf20Sopenharmony_ci		ret = -EINVAL;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	/* We've done the operation, drop volume and UBI device references */
978c2ecf20Sopenharmony_ci	spin_lock(&ubi->volumes_lock);
988c2ecf20Sopenharmony_ci	vol->ref_count -= 1;
998c2ecf20Sopenharmony_ci	ubi_assert(vol->ref_count >= 0);
1008c2ecf20Sopenharmony_ci	spin_unlock(&ubi->volumes_lock);
1018c2ecf20Sopenharmony_ci	return ret;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic struct attribute *volume_dev_attrs[] = {
1058c2ecf20Sopenharmony_ci	&attr_vol_reserved_ebs.attr,
1068c2ecf20Sopenharmony_ci	&attr_vol_type.attr,
1078c2ecf20Sopenharmony_ci	&attr_vol_name.attr,
1088c2ecf20Sopenharmony_ci	&attr_vol_corrupted.attr,
1098c2ecf20Sopenharmony_ci	&attr_vol_alignment.attr,
1108c2ecf20Sopenharmony_ci	&attr_vol_usable_eb_size.attr,
1118c2ecf20Sopenharmony_ci	&attr_vol_data_bytes.attr,
1128c2ecf20Sopenharmony_ci	&attr_vol_upd_marker.attr,
1138c2ecf20Sopenharmony_ci	NULL
1148c2ecf20Sopenharmony_ci};
1158c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(volume_dev);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci/* Release method for volume devices */
1188c2ecf20Sopenharmony_cistatic void vol_release(struct device *dev)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	ubi_eba_replace_table(vol, NULL);
1238c2ecf20Sopenharmony_ci	ubi_fastmap_destroy_checkmap(vol);
1248c2ecf20Sopenharmony_ci	kfree(vol);
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci/**
1288c2ecf20Sopenharmony_ci * ubi_create_volume - create volume.
1298c2ecf20Sopenharmony_ci * @ubi: UBI device description object
1308c2ecf20Sopenharmony_ci * @req: volume creation request
1318c2ecf20Sopenharmony_ci *
1328c2ecf20Sopenharmony_ci * This function creates volume described by @req. If @req->vol_id id
1338c2ecf20Sopenharmony_ci * %UBI_VOL_NUM_AUTO, this function automatically assign ID to the new volume
1348c2ecf20Sopenharmony_ci * and saves it in @req->vol_id. Returns zero in case of success and a negative
1358c2ecf20Sopenharmony_ci * error code in case of failure. Note, the caller has to have the
1368c2ecf20Sopenharmony_ci * @ubi->device_mutex locked.
1378c2ecf20Sopenharmony_ci */
1388c2ecf20Sopenharmony_ciint ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	int i, err, vol_id = req->vol_id;
1418c2ecf20Sopenharmony_ci	struct ubi_volume *vol;
1428c2ecf20Sopenharmony_ci	struct ubi_vtbl_record vtbl_rec;
1438c2ecf20Sopenharmony_ci	struct ubi_eba_table *eba_tbl = NULL;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	if (ubi->ro_mode)
1468c2ecf20Sopenharmony_ci		return -EROFS;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	vol = kzalloc(sizeof(struct ubi_volume), GFP_KERNEL);
1498c2ecf20Sopenharmony_ci	if (!vol)
1508c2ecf20Sopenharmony_ci		return -ENOMEM;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	device_initialize(&vol->dev);
1538c2ecf20Sopenharmony_ci	vol->dev.release = vol_release;
1548c2ecf20Sopenharmony_ci	vol->dev.parent = &ubi->dev;
1558c2ecf20Sopenharmony_ci	vol->dev.class = &ubi_class;
1568c2ecf20Sopenharmony_ci	vol->dev.groups = volume_dev_groups;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	if (req->flags & UBI_VOL_SKIP_CRC_CHECK_FLG)
1598c2ecf20Sopenharmony_ci		vol->skip_check = 1;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	spin_lock(&ubi->volumes_lock);
1628c2ecf20Sopenharmony_ci	if (vol_id == UBI_VOL_NUM_AUTO) {
1638c2ecf20Sopenharmony_ci		/* Find unused volume ID */
1648c2ecf20Sopenharmony_ci		dbg_gen("search for vacant volume ID");
1658c2ecf20Sopenharmony_ci		for (i = 0; i < ubi->vtbl_slots; i++)
1668c2ecf20Sopenharmony_ci			if (!ubi->volumes[i]) {
1678c2ecf20Sopenharmony_ci				vol_id = i;
1688c2ecf20Sopenharmony_ci				break;
1698c2ecf20Sopenharmony_ci			}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci		if (vol_id == UBI_VOL_NUM_AUTO) {
1728c2ecf20Sopenharmony_ci			ubi_err(ubi, "out of volume IDs");
1738c2ecf20Sopenharmony_ci			err = -ENFILE;
1748c2ecf20Sopenharmony_ci			goto out_unlock;
1758c2ecf20Sopenharmony_ci		}
1768c2ecf20Sopenharmony_ci		req->vol_id = vol_id;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	dbg_gen("create device %d, volume %d, %llu bytes, type %d, name %s",
1808c2ecf20Sopenharmony_ci		ubi->ubi_num, vol_id, (unsigned long long)req->bytes,
1818c2ecf20Sopenharmony_ci		(int)req->vol_type, req->name);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	/* Ensure that this volume does not exist */
1848c2ecf20Sopenharmony_ci	err = -EEXIST;
1858c2ecf20Sopenharmony_ci	if (ubi->volumes[vol_id]) {
1868c2ecf20Sopenharmony_ci		ubi_err(ubi, "volume %d already exists", vol_id);
1878c2ecf20Sopenharmony_ci		goto out_unlock;
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	/* Ensure that the name is unique */
1918c2ecf20Sopenharmony_ci	for (i = 0; i < ubi->vtbl_slots; i++)
1928c2ecf20Sopenharmony_ci		if (ubi->volumes[i] &&
1938c2ecf20Sopenharmony_ci		    ubi->volumes[i]->name_len == req->name_len &&
1948c2ecf20Sopenharmony_ci		    !strcmp(ubi->volumes[i]->name, req->name)) {
1958c2ecf20Sopenharmony_ci			ubi_err(ubi, "volume \"%s\" exists (ID %d)",
1968c2ecf20Sopenharmony_ci				req->name, i);
1978c2ecf20Sopenharmony_ci			goto out_unlock;
1988c2ecf20Sopenharmony_ci		}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	/* Calculate how many eraseblocks are requested */
2018c2ecf20Sopenharmony_ci	vol->usable_leb_size = ubi->leb_size - ubi->leb_size % req->alignment;
2028c2ecf20Sopenharmony_ci	vol->reserved_pebs = div_u64(req->bytes + vol->usable_leb_size - 1,
2038c2ecf20Sopenharmony_ci				     vol->usable_leb_size);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	/* Reserve physical eraseblocks */
2068c2ecf20Sopenharmony_ci	if (vol->reserved_pebs > ubi->avail_pebs) {
2078c2ecf20Sopenharmony_ci		ubi_err(ubi, "not enough PEBs, only %d available",
2088c2ecf20Sopenharmony_ci			ubi->avail_pebs);
2098c2ecf20Sopenharmony_ci		if (ubi->corr_peb_count)
2108c2ecf20Sopenharmony_ci			ubi_err(ubi, "%d PEBs are corrupted and not used",
2118c2ecf20Sopenharmony_ci				ubi->corr_peb_count);
2128c2ecf20Sopenharmony_ci		err = -ENOSPC;
2138c2ecf20Sopenharmony_ci		goto out_unlock;
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci	ubi->avail_pebs -= vol->reserved_pebs;
2168c2ecf20Sopenharmony_ci	ubi->rsvd_pebs += vol->reserved_pebs;
2178c2ecf20Sopenharmony_ci	spin_unlock(&ubi->volumes_lock);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	vol->vol_id    = vol_id;
2208c2ecf20Sopenharmony_ci	vol->alignment = req->alignment;
2218c2ecf20Sopenharmony_ci	vol->data_pad  = ubi->leb_size % vol->alignment;
2228c2ecf20Sopenharmony_ci	vol->vol_type  = req->vol_type;
2238c2ecf20Sopenharmony_ci	vol->name_len  = req->name_len;
2248c2ecf20Sopenharmony_ci	memcpy(vol->name, req->name, vol->name_len);
2258c2ecf20Sopenharmony_ci	vol->ubi = ubi;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	/*
2288c2ecf20Sopenharmony_ci	 * Finish all pending erases because there may be some LEBs belonging
2298c2ecf20Sopenharmony_ci	 * to the same volume ID.
2308c2ecf20Sopenharmony_ci	 */
2318c2ecf20Sopenharmony_ci	err = ubi_wl_flush(ubi, vol_id, UBI_ALL);
2328c2ecf20Sopenharmony_ci	if (err)
2338c2ecf20Sopenharmony_ci		goto out_acc;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	eba_tbl = ubi_eba_create_table(vol, vol->reserved_pebs);
2368c2ecf20Sopenharmony_ci	if (IS_ERR(eba_tbl)) {
2378c2ecf20Sopenharmony_ci		err = PTR_ERR(eba_tbl);
2388c2ecf20Sopenharmony_ci		goto out_acc;
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	ubi_eba_replace_table(vol, eba_tbl);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
2448c2ecf20Sopenharmony_ci		vol->used_ebs = vol->reserved_pebs;
2458c2ecf20Sopenharmony_ci		vol->last_eb_bytes = vol->usable_leb_size;
2468c2ecf20Sopenharmony_ci		vol->used_bytes =
2478c2ecf20Sopenharmony_ci			(long long)vol->used_ebs * vol->usable_leb_size;
2488c2ecf20Sopenharmony_ci	} else {
2498c2ecf20Sopenharmony_ci		vol->used_ebs = div_u64_rem(vol->used_bytes,
2508c2ecf20Sopenharmony_ci					    vol->usable_leb_size,
2518c2ecf20Sopenharmony_ci					    &vol->last_eb_bytes);
2528c2ecf20Sopenharmony_ci		if (vol->last_eb_bytes != 0)
2538c2ecf20Sopenharmony_ci			vol->used_ebs += 1;
2548c2ecf20Sopenharmony_ci		else
2558c2ecf20Sopenharmony_ci			vol->last_eb_bytes = vol->usable_leb_size;
2568c2ecf20Sopenharmony_ci	}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	/* Make volume "available" before it becomes accessible via sysfs */
2598c2ecf20Sopenharmony_ci	spin_lock(&ubi->volumes_lock);
2608c2ecf20Sopenharmony_ci	ubi->volumes[vol_id] = vol;
2618c2ecf20Sopenharmony_ci	ubi->vol_count += 1;
2628c2ecf20Sopenharmony_ci	spin_unlock(&ubi->volumes_lock);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	/* Register character device for the volume */
2658c2ecf20Sopenharmony_ci	cdev_init(&vol->cdev, &ubi_vol_cdev_operations);
2668c2ecf20Sopenharmony_ci	vol->cdev.owner = THIS_MODULE;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	vol->dev.devt = MKDEV(MAJOR(ubi->cdev.dev), vol_id + 1);
2698c2ecf20Sopenharmony_ci	dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id);
2708c2ecf20Sopenharmony_ci	err = cdev_device_add(&vol->cdev, &vol->dev);
2718c2ecf20Sopenharmony_ci	if (err) {
2728c2ecf20Sopenharmony_ci		ubi_err(ubi, "cannot add device");
2738c2ecf20Sopenharmony_ci		goto out_mapping;
2748c2ecf20Sopenharmony_ci	}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	/* Fill volume table record */
2778c2ecf20Sopenharmony_ci	memset(&vtbl_rec, 0, sizeof(struct ubi_vtbl_record));
2788c2ecf20Sopenharmony_ci	vtbl_rec.reserved_pebs = cpu_to_be32(vol->reserved_pebs);
2798c2ecf20Sopenharmony_ci	vtbl_rec.alignment     = cpu_to_be32(vol->alignment);
2808c2ecf20Sopenharmony_ci	vtbl_rec.data_pad      = cpu_to_be32(vol->data_pad);
2818c2ecf20Sopenharmony_ci	vtbl_rec.name_len      = cpu_to_be16(vol->name_len);
2828c2ecf20Sopenharmony_ci	if (vol->vol_type == UBI_DYNAMIC_VOLUME)
2838c2ecf20Sopenharmony_ci		vtbl_rec.vol_type = UBI_VID_DYNAMIC;
2848c2ecf20Sopenharmony_ci	else
2858c2ecf20Sopenharmony_ci		vtbl_rec.vol_type = UBI_VID_STATIC;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	if (vol->skip_check)
2888c2ecf20Sopenharmony_ci		vtbl_rec.flags |= UBI_VTBL_SKIP_CRC_CHECK_FLG;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	memcpy(vtbl_rec.name, vol->name, vol->name_len);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec);
2938c2ecf20Sopenharmony_ci	if (err)
2948c2ecf20Sopenharmony_ci		goto out_sysfs;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	ubi_volume_notify(ubi, vol, UBI_VOLUME_ADDED);
2978c2ecf20Sopenharmony_ci	self_check_volumes(ubi);
2988c2ecf20Sopenharmony_ci	return err;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ciout_sysfs:
3018c2ecf20Sopenharmony_ci	/*
3028c2ecf20Sopenharmony_ci	 * We have registered our device, we should not free the volume
3038c2ecf20Sopenharmony_ci	 * description object in this function in case of an error - it is
3048c2ecf20Sopenharmony_ci	 * freed by the release function.
3058c2ecf20Sopenharmony_ci	 */
3068c2ecf20Sopenharmony_ci	cdev_device_del(&vol->cdev, &vol->dev);
3078c2ecf20Sopenharmony_ciout_mapping:
3088c2ecf20Sopenharmony_ci	spin_lock(&ubi->volumes_lock);
3098c2ecf20Sopenharmony_ci	ubi->volumes[vol_id] = NULL;
3108c2ecf20Sopenharmony_ci	ubi->vol_count -= 1;
3118c2ecf20Sopenharmony_ci	spin_unlock(&ubi->volumes_lock);
3128c2ecf20Sopenharmony_ciout_acc:
3138c2ecf20Sopenharmony_ci	spin_lock(&ubi->volumes_lock);
3148c2ecf20Sopenharmony_ci	ubi->rsvd_pebs -= vol->reserved_pebs;
3158c2ecf20Sopenharmony_ci	ubi->avail_pebs += vol->reserved_pebs;
3168c2ecf20Sopenharmony_ciout_unlock:
3178c2ecf20Sopenharmony_ci	spin_unlock(&ubi->volumes_lock);
3188c2ecf20Sopenharmony_ci	put_device(&vol->dev);
3198c2ecf20Sopenharmony_ci	ubi_err(ubi, "cannot create volume %d, error %d", vol_id, err);
3208c2ecf20Sopenharmony_ci	return err;
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci/**
3248c2ecf20Sopenharmony_ci * ubi_remove_volume - remove volume.
3258c2ecf20Sopenharmony_ci * @desc: volume descriptor
3268c2ecf20Sopenharmony_ci * @no_vtbl: do not change volume table if not zero
3278c2ecf20Sopenharmony_ci *
3288c2ecf20Sopenharmony_ci * This function removes volume described by @desc. The volume has to be opened
3298c2ecf20Sopenharmony_ci * in "exclusive" mode. Returns zero in case of success and a negative error
3308c2ecf20Sopenharmony_ci * code in case of failure. The caller has to have the @ubi->device_mutex
3318c2ecf20Sopenharmony_ci * locked.
3328c2ecf20Sopenharmony_ci */
3338c2ecf20Sopenharmony_ciint ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl)
3348c2ecf20Sopenharmony_ci{
3358c2ecf20Sopenharmony_ci	struct ubi_volume *vol = desc->vol;
3368c2ecf20Sopenharmony_ci	struct ubi_device *ubi = vol->ubi;
3378c2ecf20Sopenharmony_ci	int i, err, vol_id = vol->vol_id, reserved_pebs = vol->reserved_pebs;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	dbg_gen("remove device %d, volume %d", ubi->ubi_num, vol_id);
3408c2ecf20Sopenharmony_ci	ubi_assert(desc->mode == UBI_EXCLUSIVE);
3418c2ecf20Sopenharmony_ci	ubi_assert(vol == ubi->volumes[vol_id]);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	if (ubi->ro_mode)
3448c2ecf20Sopenharmony_ci		return -EROFS;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	spin_lock(&ubi->volumes_lock);
3478c2ecf20Sopenharmony_ci	if (vol->ref_count > 1) {
3488c2ecf20Sopenharmony_ci		/*
3498c2ecf20Sopenharmony_ci		 * The volume is busy, probably someone is reading one of its
3508c2ecf20Sopenharmony_ci		 * sysfs files.
3518c2ecf20Sopenharmony_ci		 */
3528c2ecf20Sopenharmony_ci		err = -EBUSY;
3538c2ecf20Sopenharmony_ci		goto out_unlock;
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci	ubi->volumes[vol_id] = NULL;
3568c2ecf20Sopenharmony_ci	spin_unlock(&ubi->volumes_lock);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	if (!no_vtbl) {
3598c2ecf20Sopenharmony_ci		err = ubi_change_vtbl_record(ubi, vol_id, NULL);
3608c2ecf20Sopenharmony_ci		if (err)
3618c2ecf20Sopenharmony_ci			goto out_err;
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	for (i = 0; i < vol->reserved_pebs; i++) {
3658c2ecf20Sopenharmony_ci		err = ubi_eba_unmap_leb(ubi, vol, i);
3668c2ecf20Sopenharmony_ci		if (err)
3678c2ecf20Sopenharmony_ci			goto out_err;
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	cdev_device_del(&vol->cdev, &vol->dev);
3718c2ecf20Sopenharmony_ci	put_device(&vol->dev);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	spin_lock(&ubi->volumes_lock);
3748c2ecf20Sopenharmony_ci	ubi->rsvd_pebs -= reserved_pebs;
3758c2ecf20Sopenharmony_ci	ubi->avail_pebs += reserved_pebs;
3768c2ecf20Sopenharmony_ci	ubi_update_reserved(ubi);
3778c2ecf20Sopenharmony_ci	ubi->vol_count -= 1;
3788c2ecf20Sopenharmony_ci	spin_unlock(&ubi->volumes_lock);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	ubi_volume_notify(ubi, vol, UBI_VOLUME_REMOVED);
3818c2ecf20Sopenharmony_ci	if (!no_vtbl)
3828c2ecf20Sopenharmony_ci		self_check_volumes(ubi);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	return 0;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ciout_err:
3878c2ecf20Sopenharmony_ci	ubi_err(ubi, "cannot remove volume %d, error %d", vol_id, err);
3888c2ecf20Sopenharmony_ci	spin_lock(&ubi->volumes_lock);
3898c2ecf20Sopenharmony_ci	ubi->volumes[vol_id] = vol;
3908c2ecf20Sopenharmony_ciout_unlock:
3918c2ecf20Sopenharmony_ci	spin_unlock(&ubi->volumes_lock);
3928c2ecf20Sopenharmony_ci	return err;
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci/**
3968c2ecf20Sopenharmony_ci * ubi_resize_volume - re-size volume.
3978c2ecf20Sopenharmony_ci * @desc: volume descriptor
3988c2ecf20Sopenharmony_ci * @reserved_pebs: new size in physical eraseblocks
3998c2ecf20Sopenharmony_ci *
4008c2ecf20Sopenharmony_ci * This function re-sizes the volume and returns zero in case of success, and a
4018c2ecf20Sopenharmony_ci * negative error code in case of failure. The caller has to have the
4028c2ecf20Sopenharmony_ci * @ubi->device_mutex locked.
4038c2ecf20Sopenharmony_ci */
4048c2ecf20Sopenharmony_ciint ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	int i, err, pebs;
4078c2ecf20Sopenharmony_ci	struct ubi_volume *vol = desc->vol;
4088c2ecf20Sopenharmony_ci	struct ubi_device *ubi = vol->ubi;
4098c2ecf20Sopenharmony_ci	struct ubi_vtbl_record vtbl_rec;
4108c2ecf20Sopenharmony_ci	struct ubi_eba_table *new_eba_tbl = NULL;
4118c2ecf20Sopenharmony_ci	struct ubi_eba_table *old_eba_tbl = NULL;
4128c2ecf20Sopenharmony_ci	int vol_id = vol->vol_id;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	if (ubi->ro_mode)
4158c2ecf20Sopenharmony_ci		return -EROFS;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	dbg_gen("re-size device %d, volume %d to from %d to %d PEBs",
4188c2ecf20Sopenharmony_ci		ubi->ubi_num, vol_id, vol->reserved_pebs, reserved_pebs);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	if (vol->vol_type == UBI_STATIC_VOLUME &&
4218c2ecf20Sopenharmony_ci	    reserved_pebs < vol->used_ebs) {
4228c2ecf20Sopenharmony_ci		ubi_err(ubi, "too small size %d, %d LEBs contain data",
4238c2ecf20Sopenharmony_ci			reserved_pebs, vol->used_ebs);
4248c2ecf20Sopenharmony_ci		return -EINVAL;
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	/* If the size is the same, we have nothing to do */
4288c2ecf20Sopenharmony_ci	if (reserved_pebs == vol->reserved_pebs)
4298c2ecf20Sopenharmony_ci		return 0;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	new_eba_tbl = ubi_eba_create_table(vol, reserved_pebs);
4328c2ecf20Sopenharmony_ci	if (IS_ERR(new_eba_tbl))
4338c2ecf20Sopenharmony_ci		return PTR_ERR(new_eba_tbl);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	spin_lock(&ubi->volumes_lock);
4368c2ecf20Sopenharmony_ci	if (vol->ref_count > 1) {
4378c2ecf20Sopenharmony_ci		spin_unlock(&ubi->volumes_lock);
4388c2ecf20Sopenharmony_ci		err = -EBUSY;
4398c2ecf20Sopenharmony_ci		goto out_free;
4408c2ecf20Sopenharmony_ci	}
4418c2ecf20Sopenharmony_ci	spin_unlock(&ubi->volumes_lock);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	/* Reserve physical eraseblocks */
4448c2ecf20Sopenharmony_ci	pebs = reserved_pebs - vol->reserved_pebs;
4458c2ecf20Sopenharmony_ci	if (pebs > 0) {
4468c2ecf20Sopenharmony_ci		spin_lock(&ubi->volumes_lock);
4478c2ecf20Sopenharmony_ci		if (pebs > ubi->avail_pebs) {
4488c2ecf20Sopenharmony_ci			ubi_err(ubi, "not enough PEBs: requested %d, available %d",
4498c2ecf20Sopenharmony_ci				pebs, ubi->avail_pebs);
4508c2ecf20Sopenharmony_ci			if (ubi->corr_peb_count)
4518c2ecf20Sopenharmony_ci				ubi_err(ubi, "%d PEBs are corrupted and not used",
4528c2ecf20Sopenharmony_ci					ubi->corr_peb_count);
4538c2ecf20Sopenharmony_ci			spin_unlock(&ubi->volumes_lock);
4548c2ecf20Sopenharmony_ci			err = -ENOSPC;
4558c2ecf20Sopenharmony_ci			goto out_free;
4568c2ecf20Sopenharmony_ci		}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci		ubi->avail_pebs -= pebs;
4598c2ecf20Sopenharmony_ci		ubi->rsvd_pebs += pebs;
4608c2ecf20Sopenharmony_ci		ubi_eba_copy_table(vol, new_eba_tbl, vol->reserved_pebs);
4618c2ecf20Sopenharmony_ci		old_eba_tbl = vol->eba_tbl;
4628c2ecf20Sopenharmony_ci		vol->eba_tbl = new_eba_tbl;
4638c2ecf20Sopenharmony_ci		vol->reserved_pebs = reserved_pebs;
4648c2ecf20Sopenharmony_ci		spin_unlock(&ubi->volumes_lock);
4658c2ecf20Sopenharmony_ci	}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	if (pebs < 0) {
4688c2ecf20Sopenharmony_ci		for (i = 0; i < -pebs; i++) {
4698c2ecf20Sopenharmony_ci			err = ubi_eba_unmap_leb(ubi, vol, reserved_pebs + i);
4708c2ecf20Sopenharmony_ci			if (err)
4718c2ecf20Sopenharmony_ci				goto out_free;
4728c2ecf20Sopenharmony_ci		}
4738c2ecf20Sopenharmony_ci		spin_lock(&ubi->volumes_lock);
4748c2ecf20Sopenharmony_ci		ubi->rsvd_pebs += pebs;
4758c2ecf20Sopenharmony_ci		ubi->avail_pebs -= pebs;
4768c2ecf20Sopenharmony_ci		ubi_update_reserved(ubi);
4778c2ecf20Sopenharmony_ci		ubi_eba_copy_table(vol, new_eba_tbl, reserved_pebs);
4788c2ecf20Sopenharmony_ci		old_eba_tbl = vol->eba_tbl;
4798c2ecf20Sopenharmony_ci		vol->eba_tbl = new_eba_tbl;
4808c2ecf20Sopenharmony_ci		vol->reserved_pebs = reserved_pebs;
4818c2ecf20Sopenharmony_ci		spin_unlock(&ubi->volumes_lock);
4828c2ecf20Sopenharmony_ci	}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	/*
4858c2ecf20Sopenharmony_ci	 * When we shrink a volume we have to flush all pending (erase) work.
4868c2ecf20Sopenharmony_ci	 * Otherwise it can happen that upon next attach UBI finds a LEB with
4878c2ecf20Sopenharmony_ci	 * lnum > highest_lnum and refuses to attach.
4888c2ecf20Sopenharmony_ci	 */
4898c2ecf20Sopenharmony_ci	if (pebs < 0) {
4908c2ecf20Sopenharmony_ci		err = ubi_wl_flush(ubi, vol_id, UBI_ALL);
4918c2ecf20Sopenharmony_ci		if (err)
4928c2ecf20Sopenharmony_ci			goto out_acc;
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	/* Change volume table record */
4968c2ecf20Sopenharmony_ci	vtbl_rec = ubi->vtbl[vol_id];
4978c2ecf20Sopenharmony_ci	vtbl_rec.reserved_pebs = cpu_to_be32(reserved_pebs);
4988c2ecf20Sopenharmony_ci	err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec);
4998c2ecf20Sopenharmony_ci	if (err)
5008c2ecf20Sopenharmony_ci		goto out_acc;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
5038c2ecf20Sopenharmony_ci		vol->used_ebs = reserved_pebs;
5048c2ecf20Sopenharmony_ci		vol->last_eb_bytes = vol->usable_leb_size;
5058c2ecf20Sopenharmony_ci		vol->used_bytes =
5068c2ecf20Sopenharmony_ci			(long long)vol->used_ebs * vol->usable_leb_size;
5078c2ecf20Sopenharmony_ci	}
5088c2ecf20Sopenharmony_ci	/* destroy old table */
5098c2ecf20Sopenharmony_ci	ubi_eba_destroy_table(old_eba_tbl);
5108c2ecf20Sopenharmony_ci	ubi_volume_notify(ubi, vol, UBI_VOLUME_RESIZED);
5118c2ecf20Sopenharmony_ci	self_check_volumes(ubi);
5128c2ecf20Sopenharmony_ci	return err;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ciout_acc:
5158c2ecf20Sopenharmony_ci	spin_lock(&ubi->volumes_lock);
5168c2ecf20Sopenharmony_ci	vol->reserved_pebs = reserved_pebs - pebs;
5178c2ecf20Sopenharmony_ci	if (pebs > 0) {
5188c2ecf20Sopenharmony_ci		ubi->rsvd_pebs -= pebs;
5198c2ecf20Sopenharmony_ci		ubi->avail_pebs += pebs;
5208c2ecf20Sopenharmony_ci		ubi_eba_copy_table(vol, old_eba_tbl, vol->reserved_pebs);
5218c2ecf20Sopenharmony_ci	} else {
5228c2ecf20Sopenharmony_ci		ubi_eba_copy_table(vol, old_eba_tbl, reserved_pebs);
5238c2ecf20Sopenharmony_ci	}
5248c2ecf20Sopenharmony_ci	vol->eba_tbl = old_eba_tbl;
5258c2ecf20Sopenharmony_ci	spin_unlock(&ubi->volumes_lock);
5268c2ecf20Sopenharmony_ciout_free:
5278c2ecf20Sopenharmony_ci	ubi_eba_destroy_table(new_eba_tbl);
5288c2ecf20Sopenharmony_ci	return err;
5298c2ecf20Sopenharmony_ci}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci/**
5328c2ecf20Sopenharmony_ci * ubi_rename_volumes - re-name UBI volumes.
5338c2ecf20Sopenharmony_ci * @ubi: UBI device description object
5348c2ecf20Sopenharmony_ci * @rename_list: list of &struct ubi_rename_entry objects
5358c2ecf20Sopenharmony_ci *
5368c2ecf20Sopenharmony_ci * This function re-names or removes volumes specified in the re-name list.
5378c2ecf20Sopenharmony_ci * Returns zero in case of success and a negative error code in case of
5388c2ecf20Sopenharmony_ci * failure.
5398c2ecf20Sopenharmony_ci */
5408c2ecf20Sopenharmony_ciint ubi_rename_volumes(struct ubi_device *ubi, struct list_head *rename_list)
5418c2ecf20Sopenharmony_ci{
5428c2ecf20Sopenharmony_ci	int err;
5438c2ecf20Sopenharmony_ci	struct ubi_rename_entry *re;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	err = ubi_vtbl_rename_volumes(ubi, rename_list);
5468c2ecf20Sopenharmony_ci	if (err)
5478c2ecf20Sopenharmony_ci		return err;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	list_for_each_entry(re, rename_list, list) {
5508c2ecf20Sopenharmony_ci		if (re->remove) {
5518c2ecf20Sopenharmony_ci			err = ubi_remove_volume(re->desc, 1);
5528c2ecf20Sopenharmony_ci			if (err)
5538c2ecf20Sopenharmony_ci				break;
5548c2ecf20Sopenharmony_ci		} else {
5558c2ecf20Sopenharmony_ci			struct ubi_volume *vol = re->desc->vol;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci			spin_lock(&ubi->volumes_lock);
5588c2ecf20Sopenharmony_ci			vol->name_len = re->new_name_len;
5598c2ecf20Sopenharmony_ci			memcpy(vol->name, re->new_name, re->new_name_len + 1);
5608c2ecf20Sopenharmony_ci			spin_unlock(&ubi->volumes_lock);
5618c2ecf20Sopenharmony_ci			ubi_volume_notify(ubi, vol, UBI_VOLUME_RENAMED);
5628c2ecf20Sopenharmony_ci		}
5638c2ecf20Sopenharmony_ci	}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	if (!err)
5668c2ecf20Sopenharmony_ci		self_check_volumes(ubi);
5678c2ecf20Sopenharmony_ci	return err;
5688c2ecf20Sopenharmony_ci}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci/**
5718c2ecf20Sopenharmony_ci * ubi_add_volume - add volume.
5728c2ecf20Sopenharmony_ci * @ubi: UBI device description object
5738c2ecf20Sopenharmony_ci * @vol: volume description object
5748c2ecf20Sopenharmony_ci *
5758c2ecf20Sopenharmony_ci * This function adds an existing volume and initializes all its data
5768c2ecf20Sopenharmony_ci * structures. Returns zero in case of success and a negative error code in
5778c2ecf20Sopenharmony_ci * case of failure.
5788c2ecf20Sopenharmony_ci */
5798c2ecf20Sopenharmony_ciint ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol)
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	int err, vol_id = vol->vol_id;
5828c2ecf20Sopenharmony_ci	dev_t dev;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	dbg_gen("add volume %d", vol_id);
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	/* Register character device for the volume */
5878c2ecf20Sopenharmony_ci	cdev_init(&vol->cdev, &ubi_vol_cdev_operations);
5888c2ecf20Sopenharmony_ci	vol->cdev.owner = THIS_MODULE;
5898c2ecf20Sopenharmony_ci	dev = MKDEV(MAJOR(ubi->cdev.dev), vol->vol_id + 1);
5908c2ecf20Sopenharmony_ci	err = cdev_add(&vol->cdev, dev, 1);
5918c2ecf20Sopenharmony_ci	if (err) {
5928c2ecf20Sopenharmony_ci		ubi_err(ubi, "cannot add character device for volume %d, error %d",
5938c2ecf20Sopenharmony_ci			vol_id, err);
5948c2ecf20Sopenharmony_ci		vol_release(&vol->dev);
5958c2ecf20Sopenharmony_ci		return err;
5968c2ecf20Sopenharmony_ci	}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	vol->dev.release = vol_release;
5998c2ecf20Sopenharmony_ci	vol->dev.parent = &ubi->dev;
6008c2ecf20Sopenharmony_ci	vol->dev.devt = dev;
6018c2ecf20Sopenharmony_ci	vol->dev.class = &ubi_class;
6028c2ecf20Sopenharmony_ci	vol->dev.groups = volume_dev_groups;
6038c2ecf20Sopenharmony_ci	dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id);
6048c2ecf20Sopenharmony_ci	err = device_register(&vol->dev);
6058c2ecf20Sopenharmony_ci	if (err) {
6068c2ecf20Sopenharmony_ci		cdev_del(&vol->cdev);
6078c2ecf20Sopenharmony_ci		put_device(&vol->dev);
6088c2ecf20Sopenharmony_ci		return err;
6098c2ecf20Sopenharmony_ci	}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	self_check_volumes(ubi);
6128c2ecf20Sopenharmony_ci	return err;
6138c2ecf20Sopenharmony_ci}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci/**
6168c2ecf20Sopenharmony_ci * ubi_free_volume - free volume.
6178c2ecf20Sopenharmony_ci * @ubi: UBI device description object
6188c2ecf20Sopenharmony_ci * @vol: volume description object
6198c2ecf20Sopenharmony_ci *
6208c2ecf20Sopenharmony_ci * This function frees all resources for volume @vol but does not remove it.
6218c2ecf20Sopenharmony_ci * Used only when the UBI device is detached.
6228c2ecf20Sopenharmony_ci */
6238c2ecf20Sopenharmony_civoid ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol)
6248c2ecf20Sopenharmony_ci{
6258c2ecf20Sopenharmony_ci	dbg_gen("free volume %d", vol->vol_id);
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	ubi->volumes[vol->vol_id] = NULL;
6288c2ecf20Sopenharmony_ci	cdev_del(&vol->cdev);
6298c2ecf20Sopenharmony_ci	device_unregister(&vol->dev);
6308c2ecf20Sopenharmony_ci}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci/**
6338c2ecf20Sopenharmony_ci * self_check_volume - check volume information.
6348c2ecf20Sopenharmony_ci * @ubi: UBI device description object
6358c2ecf20Sopenharmony_ci * @vol_id: volume ID
6368c2ecf20Sopenharmony_ci *
6378c2ecf20Sopenharmony_ci * Returns zero if volume is all right and a a negative error code if not.
6388c2ecf20Sopenharmony_ci */
6398c2ecf20Sopenharmony_cistatic int self_check_volume(struct ubi_device *ubi, int vol_id)
6408c2ecf20Sopenharmony_ci{
6418c2ecf20Sopenharmony_ci	int idx = vol_id2idx(ubi, vol_id);
6428c2ecf20Sopenharmony_ci	int reserved_pebs, alignment, data_pad, vol_type, name_len, upd_marker;
6438c2ecf20Sopenharmony_ci	const struct ubi_volume *vol;
6448c2ecf20Sopenharmony_ci	long long n;
6458c2ecf20Sopenharmony_ci	const char *name;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	spin_lock(&ubi->volumes_lock);
6488c2ecf20Sopenharmony_ci	reserved_pebs = be32_to_cpu(ubi->vtbl[vol_id].reserved_pebs);
6498c2ecf20Sopenharmony_ci	vol = ubi->volumes[idx];
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	if (!vol) {
6528c2ecf20Sopenharmony_ci		if (reserved_pebs) {
6538c2ecf20Sopenharmony_ci			ubi_err(ubi, "no volume info, but volume exists");
6548c2ecf20Sopenharmony_ci			goto fail;
6558c2ecf20Sopenharmony_ci		}
6568c2ecf20Sopenharmony_ci		spin_unlock(&ubi->volumes_lock);
6578c2ecf20Sopenharmony_ci		return 0;
6588c2ecf20Sopenharmony_ci	}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	if (vol->reserved_pebs < 0 || vol->alignment < 0 || vol->data_pad < 0 ||
6618c2ecf20Sopenharmony_ci	    vol->name_len < 0) {
6628c2ecf20Sopenharmony_ci		ubi_err(ubi, "negative values");
6638c2ecf20Sopenharmony_ci		goto fail;
6648c2ecf20Sopenharmony_ci	}
6658c2ecf20Sopenharmony_ci	if (vol->alignment > ubi->leb_size || vol->alignment == 0) {
6668c2ecf20Sopenharmony_ci		ubi_err(ubi, "bad alignment");
6678c2ecf20Sopenharmony_ci		goto fail;
6688c2ecf20Sopenharmony_ci	}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	n = vol->alignment & (ubi->min_io_size - 1);
6718c2ecf20Sopenharmony_ci	if (vol->alignment != 1 && n) {
6728c2ecf20Sopenharmony_ci		ubi_err(ubi, "alignment is not multiple of min I/O unit");
6738c2ecf20Sopenharmony_ci		goto fail;
6748c2ecf20Sopenharmony_ci	}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	n = ubi->leb_size % vol->alignment;
6778c2ecf20Sopenharmony_ci	if (vol->data_pad != n) {
6788c2ecf20Sopenharmony_ci		ubi_err(ubi, "bad data_pad, has to be %lld", n);
6798c2ecf20Sopenharmony_ci		goto fail;
6808c2ecf20Sopenharmony_ci	}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	if (vol->vol_type != UBI_DYNAMIC_VOLUME &&
6838c2ecf20Sopenharmony_ci	    vol->vol_type != UBI_STATIC_VOLUME) {
6848c2ecf20Sopenharmony_ci		ubi_err(ubi, "bad vol_type");
6858c2ecf20Sopenharmony_ci		goto fail;
6868c2ecf20Sopenharmony_ci	}
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	if (vol->upd_marker && vol->corrupted) {
6898c2ecf20Sopenharmony_ci		ubi_err(ubi, "update marker and corrupted simultaneously");
6908c2ecf20Sopenharmony_ci		goto fail;
6918c2ecf20Sopenharmony_ci	}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	if (vol->reserved_pebs > ubi->good_peb_count) {
6948c2ecf20Sopenharmony_ci		ubi_err(ubi, "too large reserved_pebs");
6958c2ecf20Sopenharmony_ci		goto fail;
6968c2ecf20Sopenharmony_ci	}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	n = ubi->leb_size - vol->data_pad;
6998c2ecf20Sopenharmony_ci	if (vol->usable_leb_size != ubi->leb_size - vol->data_pad) {
7008c2ecf20Sopenharmony_ci		ubi_err(ubi, "bad usable_leb_size, has to be %lld", n);
7018c2ecf20Sopenharmony_ci		goto fail;
7028c2ecf20Sopenharmony_ci	}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	if (vol->name_len > UBI_VOL_NAME_MAX) {
7058c2ecf20Sopenharmony_ci		ubi_err(ubi, "too long volume name, max is %d",
7068c2ecf20Sopenharmony_ci			UBI_VOL_NAME_MAX);
7078c2ecf20Sopenharmony_ci		goto fail;
7088c2ecf20Sopenharmony_ci	}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	n = strnlen(vol->name, vol->name_len + 1);
7118c2ecf20Sopenharmony_ci	if (n != vol->name_len) {
7128c2ecf20Sopenharmony_ci		ubi_err(ubi, "bad name_len %lld", n);
7138c2ecf20Sopenharmony_ci		goto fail;
7148c2ecf20Sopenharmony_ci	}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	n = (long long)vol->used_ebs * vol->usable_leb_size;
7178c2ecf20Sopenharmony_ci	if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
7188c2ecf20Sopenharmony_ci		if (vol->corrupted) {
7198c2ecf20Sopenharmony_ci			ubi_err(ubi, "corrupted dynamic volume");
7208c2ecf20Sopenharmony_ci			goto fail;
7218c2ecf20Sopenharmony_ci		}
7228c2ecf20Sopenharmony_ci		if (vol->used_ebs != vol->reserved_pebs) {
7238c2ecf20Sopenharmony_ci			ubi_err(ubi, "bad used_ebs");
7248c2ecf20Sopenharmony_ci			goto fail;
7258c2ecf20Sopenharmony_ci		}
7268c2ecf20Sopenharmony_ci		if (vol->last_eb_bytes != vol->usable_leb_size) {
7278c2ecf20Sopenharmony_ci			ubi_err(ubi, "bad last_eb_bytes");
7288c2ecf20Sopenharmony_ci			goto fail;
7298c2ecf20Sopenharmony_ci		}
7308c2ecf20Sopenharmony_ci		if (vol->used_bytes != n) {
7318c2ecf20Sopenharmony_ci			ubi_err(ubi, "bad used_bytes");
7328c2ecf20Sopenharmony_ci			goto fail;
7338c2ecf20Sopenharmony_ci		}
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci		if (vol->skip_check) {
7368c2ecf20Sopenharmony_ci			ubi_err(ubi, "bad skip_check");
7378c2ecf20Sopenharmony_ci			goto fail;
7388c2ecf20Sopenharmony_ci		}
7398c2ecf20Sopenharmony_ci	} else {
7408c2ecf20Sopenharmony_ci		if (vol->used_ebs < 0 || vol->used_ebs > vol->reserved_pebs) {
7418c2ecf20Sopenharmony_ci			ubi_err(ubi, "bad used_ebs");
7428c2ecf20Sopenharmony_ci			goto fail;
7438c2ecf20Sopenharmony_ci		}
7448c2ecf20Sopenharmony_ci		if (vol->last_eb_bytes < 0 ||
7458c2ecf20Sopenharmony_ci		    vol->last_eb_bytes > vol->usable_leb_size) {
7468c2ecf20Sopenharmony_ci			ubi_err(ubi, "bad last_eb_bytes");
7478c2ecf20Sopenharmony_ci			goto fail;
7488c2ecf20Sopenharmony_ci		}
7498c2ecf20Sopenharmony_ci		if (vol->used_bytes < 0 || vol->used_bytes > n ||
7508c2ecf20Sopenharmony_ci		    vol->used_bytes < n - vol->usable_leb_size) {
7518c2ecf20Sopenharmony_ci			ubi_err(ubi, "bad used_bytes");
7528c2ecf20Sopenharmony_ci			goto fail;
7538c2ecf20Sopenharmony_ci		}
7548c2ecf20Sopenharmony_ci	}
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	alignment  = be32_to_cpu(ubi->vtbl[vol_id].alignment);
7578c2ecf20Sopenharmony_ci	data_pad   = be32_to_cpu(ubi->vtbl[vol_id].data_pad);
7588c2ecf20Sopenharmony_ci	name_len   = be16_to_cpu(ubi->vtbl[vol_id].name_len);
7598c2ecf20Sopenharmony_ci	upd_marker = ubi->vtbl[vol_id].upd_marker;
7608c2ecf20Sopenharmony_ci	name       = &ubi->vtbl[vol_id].name[0];
7618c2ecf20Sopenharmony_ci	if (ubi->vtbl[vol_id].vol_type == UBI_VID_DYNAMIC)
7628c2ecf20Sopenharmony_ci		vol_type = UBI_DYNAMIC_VOLUME;
7638c2ecf20Sopenharmony_ci	else
7648c2ecf20Sopenharmony_ci		vol_type = UBI_STATIC_VOLUME;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	if (alignment != vol->alignment || data_pad != vol->data_pad ||
7678c2ecf20Sopenharmony_ci	    upd_marker != vol->upd_marker || vol_type != vol->vol_type ||
7688c2ecf20Sopenharmony_ci	    name_len != vol->name_len || strncmp(name, vol->name, name_len)) {
7698c2ecf20Sopenharmony_ci		ubi_err(ubi, "volume info is different");
7708c2ecf20Sopenharmony_ci		goto fail;
7718c2ecf20Sopenharmony_ci	}
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	spin_unlock(&ubi->volumes_lock);
7748c2ecf20Sopenharmony_ci	return 0;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_cifail:
7778c2ecf20Sopenharmony_ci	ubi_err(ubi, "self-check failed for volume %d", vol_id);
7788c2ecf20Sopenharmony_ci	if (vol)
7798c2ecf20Sopenharmony_ci		ubi_dump_vol_info(vol);
7808c2ecf20Sopenharmony_ci	ubi_dump_vtbl_record(&ubi->vtbl[vol_id], vol_id);
7818c2ecf20Sopenharmony_ci	dump_stack();
7828c2ecf20Sopenharmony_ci	spin_unlock(&ubi->volumes_lock);
7838c2ecf20Sopenharmony_ci	return -EINVAL;
7848c2ecf20Sopenharmony_ci}
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci/**
7878c2ecf20Sopenharmony_ci * self_check_volumes - check information about all volumes.
7888c2ecf20Sopenharmony_ci * @ubi: UBI device description object
7898c2ecf20Sopenharmony_ci *
7908c2ecf20Sopenharmony_ci * Returns zero if volumes are all right and a a negative error code if not.
7918c2ecf20Sopenharmony_ci */
7928c2ecf20Sopenharmony_cistatic int self_check_volumes(struct ubi_device *ubi)
7938c2ecf20Sopenharmony_ci{
7948c2ecf20Sopenharmony_ci	int i, err = 0;
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	if (!ubi_dbg_chk_gen(ubi))
7978c2ecf20Sopenharmony_ci		return 0;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	for (i = 0; i < ubi->vtbl_slots; i++) {
8008c2ecf20Sopenharmony_ci		err = self_check_volume(ubi, i);
8018c2ecf20Sopenharmony_ci		if (err)
8028c2ecf20Sopenharmony_ci			break;
8038c2ecf20Sopenharmony_ci	}
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	return err;
8068c2ecf20Sopenharmony_ci}
807