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 (Битюцкий Артём)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/*
962306a36Sopenharmony_ci * This file contains implementation of volume creation, deletion, updating and
1062306a36Sopenharmony_ci * resizing.
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/err.h>
1462306a36Sopenharmony_ci#include <linux/math64.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <linux/export.h>
1762306a36Sopenharmony_ci#include "ubi.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic int self_check_volumes(struct ubi_device *ubi);
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic ssize_t vol_attribute_show(struct device *dev,
2262306a36Sopenharmony_ci				  struct device_attribute *attr, char *buf);
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* Device attributes corresponding to files in '/<sysfs>/class/ubi/ubiX_Y' */
2562306a36Sopenharmony_cistatic struct device_attribute attr_vol_reserved_ebs =
2662306a36Sopenharmony_ci	__ATTR(reserved_ebs, S_IRUGO, vol_attribute_show, NULL);
2762306a36Sopenharmony_cistatic struct device_attribute attr_vol_type =
2862306a36Sopenharmony_ci	__ATTR(type, S_IRUGO, vol_attribute_show, NULL);
2962306a36Sopenharmony_cistatic struct device_attribute attr_vol_name =
3062306a36Sopenharmony_ci	__ATTR(name, S_IRUGO, vol_attribute_show, NULL);
3162306a36Sopenharmony_cistatic struct device_attribute attr_vol_corrupted =
3262306a36Sopenharmony_ci	__ATTR(corrupted, S_IRUGO, vol_attribute_show, NULL);
3362306a36Sopenharmony_cistatic struct device_attribute attr_vol_alignment =
3462306a36Sopenharmony_ci	__ATTR(alignment, S_IRUGO, vol_attribute_show, NULL);
3562306a36Sopenharmony_cistatic struct device_attribute attr_vol_usable_eb_size =
3662306a36Sopenharmony_ci	__ATTR(usable_eb_size, S_IRUGO, vol_attribute_show, NULL);
3762306a36Sopenharmony_cistatic struct device_attribute attr_vol_data_bytes =
3862306a36Sopenharmony_ci	__ATTR(data_bytes, S_IRUGO, vol_attribute_show, NULL);
3962306a36Sopenharmony_cistatic struct device_attribute attr_vol_upd_marker =
4062306a36Sopenharmony_ci	__ATTR(upd_marker, S_IRUGO, vol_attribute_show, NULL);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/*
4362306a36Sopenharmony_ci * "Show" method for files in '/<sysfs>/class/ubi/ubiX_Y/'.
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci * Consider a situation:
4662306a36Sopenharmony_ci * A. process 1 opens a sysfs file related to volume Y, say
4762306a36Sopenharmony_ci *    /<sysfs>/class/ubi/ubiX_Y/reserved_ebs;
4862306a36Sopenharmony_ci * B. process 2 removes volume Y;
4962306a36Sopenharmony_ci * C. process 1 starts reading the /<sysfs>/class/ubi/ubiX_Y/reserved_ebs file;
5062306a36Sopenharmony_ci *
5162306a36Sopenharmony_ci * In this situation, this function will return %-ENODEV because it will find
5262306a36Sopenharmony_ci * out that the volume was removed from the @ubi->volumes array.
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_cistatic ssize_t vol_attribute_show(struct device *dev,
5562306a36Sopenharmony_ci				  struct device_attribute *attr, char *buf)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	int ret;
5862306a36Sopenharmony_ci	struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev);
5962306a36Sopenharmony_ci	struct ubi_device *ubi = vol->ubi;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	spin_lock(&ubi->volumes_lock);
6262306a36Sopenharmony_ci	if (!ubi->volumes[vol->vol_id]) {
6362306a36Sopenharmony_ci		spin_unlock(&ubi->volumes_lock);
6462306a36Sopenharmony_ci		return -ENODEV;
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci	/* Take a reference to prevent volume removal */
6762306a36Sopenharmony_ci	vol->ref_count += 1;
6862306a36Sopenharmony_ci	spin_unlock(&ubi->volumes_lock);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	if (attr == &attr_vol_reserved_ebs)
7162306a36Sopenharmony_ci		ret = sprintf(buf, "%d\n", vol->reserved_pebs);
7262306a36Sopenharmony_ci	else if (attr == &attr_vol_type) {
7362306a36Sopenharmony_ci		const char *tp;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci		if (vol->vol_type == UBI_DYNAMIC_VOLUME)
7662306a36Sopenharmony_ci			tp = "dynamic";
7762306a36Sopenharmony_ci		else
7862306a36Sopenharmony_ci			tp = "static";
7962306a36Sopenharmony_ci		ret = sprintf(buf, "%s\n", tp);
8062306a36Sopenharmony_ci	} else if (attr == &attr_vol_name)
8162306a36Sopenharmony_ci		ret = sprintf(buf, "%s\n", vol->name);
8262306a36Sopenharmony_ci	else if (attr == &attr_vol_corrupted)
8362306a36Sopenharmony_ci		ret = sprintf(buf, "%d\n", vol->corrupted);
8462306a36Sopenharmony_ci	else if (attr == &attr_vol_alignment)
8562306a36Sopenharmony_ci		ret = sprintf(buf, "%d\n", vol->alignment);
8662306a36Sopenharmony_ci	else if (attr == &attr_vol_usable_eb_size)
8762306a36Sopenharmony_ci		ret = sprintf(buf, "%d\n", vol->usable_leb_size);
8862306a36Sopenharmony_ci	else if (attr == &attr_vol_data_bytes)
8962306a36Sopenharmony_ci		ret = sprintf(buf, "%lld\n", vol->used_bytes);
9062306a36Sopenharmony_ci	else if (attr == &attr_vol_upd_marker)
9162306a36Sopenharmony_ci		ret = sprintf(buf, "%d\n", vol->upd_marker);
9262306a36Sopenharmony_ci	else
9362306a36Sopenharmony_ci		/* This must be a bug */
9462306a36Sopenharmony_ci		ret = -EINVAL;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	/* We've done the operation, drop volume and UBI device references */
9762306a36Sopenharmony_ci	spin_lock(&ubi->volumes_lock);
9862306a36Sopenharmony_ci	vol->ref_count -= 1;
9962306a36Sopenharmony_ci	ubi_assert(vol->ref_count >= 0);
10062306a36Sopenharmony_ci	spin_unlock(&ubi->volumes_lock);
10162306a36Sopenharmony_ci	return ret;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic struct attribute *volume_dev_attrs[] = {
10562306a36Sopenharmony_ci	&attr_vol_reserved_ebs.attr,
10662306a36Sopenharmony_ci	&attr_vol_type.attr,
10762306a36Sopenharmony_ci	&attr_vol_name.attr,
10862306a36Sopenharmony_ci	&attr_vol_corrupted.attr,
10962306a36Sopenharmony_ci	&attr_vol_alignment.attr,
11062306a36Sopenharmony_ci	&attr_vol_usable_eb_size.attr,
11162306a36Sopenharmony_ci	&attr_vol_data_bytes.attr,
11262306a36Sopenharmony_ci	&attr_vol_upd_marker.attr,
11362306a36Sopenharmony_ci	NULL
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_ciATTRIBUTE_GROUPS(volume_dev);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci/* Release method for volume devices */
11862306a36Sopenharmony_cistatic void vol_release(struct device *dev)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	ubi_eba_replace_table(vol, NULL);
12362306a36Sopenharmony_ci	ubi_fastmap_destroy_checkmap(vol);
12462306a36Sopenharmony_ci	kfree(vol);
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci/**
12862306a36Sopenharmony_ci * ubi_create_volume - create volume.
12962306a36Sopenharmony_ci * @ubi: UBI device description object
13062306a36Sopenharmony_ci * @req: volume creation request
13162306a36Sopenharmony_ci *
13262306a36Sopenharmony_ci * This function creates volume described by @req. If @req->vol_id id
13362306a36Sopenharmony_ci * %UBI_VOL_NUM_AUTO, this function automatically assign ID to the new volume
13462306a36Sopenharmony_ci * and saves it in @req->vol_id. Returns zero in case of success and a negative
13562306a36Sopenharmony_ci * error code in case of failure. Note, the caller has to have the
13662306a36Sopenharmony_ci * @ubi->device_mutex locked.
13762306a36Sopenharmony_ci */
13862306a36Sopenharmony_ciint ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	int i, err, vol_id = req->vol_id;
14162306a36Sopenharmony_ci	struct ubi_volume *vol;
14262306a36Sopenharmony_ci	struct ubi_vtbl_record vtbl_rec;
14362306a36Sopenharmony_ci	struct ubi_eba_table *eba_tbl = NULL;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (ubi->ro_mode)
14662306a36Sopenharmony_ci		return -EROFS;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	vol = kzalloc(sizeof(struct ubi_volume), GFP_KERNEL);
14962306a36Sopenharmony_ci	if (!vol)
15062306a36Sopenharmony_ci		return -ENOMEM;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	device_initialize(&vol->dev);
15362306a36Sopenharmony_ci	vol->dev.release = vol_release;
15462306a36Sopenharmony_ci	vol->dev.parent = &ubi->dev;
15562306a36Sopenharmony_ci	vol->dev.class = &ubi_class;
15662306a36Sopenharmony_ci	vol->dev.groups = volume_dev_groups;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if (req->flags & UBI_VOL_SKIP_CRC_CHECK_FLG)
15962306a36Sopenharmony_ci		vol->skip_check = 1;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	spin_lock(&ubi->volumes_lock);
16262306a36Sopenharmony_ci	if (vol_id == UBI_VOL_NUM_AUTO) {
16362306a36Sopenharmony_ci		/* Find unused volume ID */
16462306a36Sopenharmony_ci		dbg_gen("search for vacant volume ID");
16562306a36Sopenharmony_ci		for (i = 0; i < ubi->vtbl_slots; i++)
16662306a36Sopenharmony_ci			if (!ubi->volumes[i]) {
16762306a36Sopenharmony_ci				vol_id = i;
16862306a36Sopenharmony_ci				break;
16962306a36Sopenharmony_ci			}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci		if (vol_id == UBI_VOL_NUM_AUTO) {
17262306a36Sopenharmony_ci			ubi_err(ubi, "out of volume IDs");
17362306a36Sopenharmony_ci			err = -ENFILE;
17462306a36Sopenharmony_ci			goto out_unlock;
17562306a36Sopenharmony_ci		}
17662306a36Sopenharmony_ci		req->vol_id = vol_id;
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	dbg_gen("create device %d, volume %d, %llu bytes, type %d, name %s",
18062306a36Sopenharmony_ci		ubi->ubi_num, vol_id, (unsigned long long)req->bytes,
18162306a36Sopenharmony_ci		(int)req->vol_type, req->name);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/* Ensure that this volume does not exist */
18462306a36Sopenharmony_ci	err = -EEXIST;
18562306a36Sopenharmony_ci	if (ubi->volumes[vol_id]) {
18662306a36Sopenharmony_ci		ubi_err(ubi, "volume %d already exists", vol_id);
18762306a36Sopenharmony_ci		goto out_unlock;
18862306a36Sopenharmony_ci	}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	/* Ensure that the name is unique */
19162306a36Sopenharmony_ci	for (i = 0; i < ubi->vtbl_slots; i++)
19262306a36Sopenharmony_ci		if (ubi->volumes[i] &&
19362306a36Sopenharmony_ci		    ubi->volumes[i]->name_len == req->name_len &&
19462306a36Sopenharmony_ci		    !strcmp(ubi->volumes[i]->name, req->name)) {
19562306a36Sopenharmony_ci			ubi_err(ubi, "volume \"%s\" exists (ID %d)",
19662306a36Sopenharmony_ci				req->name, i);
19762306a36Sopenharmony_ci			goto out_unlock;
19862306a36Sopenharmony_ci		}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	/* Calculate how many eraseblocks are requested */
20162306a36Sopenharmony_ci	vol->usable_leb_size = ubi->leb_size - ubi->leb_size % req->alignment;
20262306a36Sopenharmony_ci	vol->reserved_pebs = div_u64(req->bytes + vol->usable_leb_size - 1,
20362306a36Sopenharmony_ci				     vol->usable_leb_size);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/* Reserve physical eraseblocks */
20662306a36Sopenharmony_ci	if (vol->reserved_pebs > ubi->avail_pebs) {
20762306a36Sopenharmony_ci		ubi_err(ubi, "not enough PEBs, only %d available",
20862306a36Sopenharmony_ci			ubi->avail_pebs);
20962306a36Sopenharmony_ci		if (ubi->corr_peb_count)
21062306a36Sopenharmony_ci			ubi_err(ubi, "%d PEBs are corrupted and not used",
21162306a36Sopenharmony_ci				ubi->corr_peb_count);
21262306a36Sopenharmony_ci		err = -ENOSPC;
21362306a36Sopenharmony_ci		goto out_unlock;
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci	ubi->avail_pebs -= vol->reserved_pebs;
21662306a36Sopenharmony_ci	ubi->rsvd_pebs += vol->reserved_pebs;
21762306a36Sopenharmony_ci	spin_unlock(&ubi->volumes_lock);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	vol->vol_id    = vol_id;
22062306a36Sopenharmony_ci	vol->alignment = req->alignment;
22162306a36Sopenharmony_ci	vol->data_pad  = ubi->leb_size % vol->alignment;
22262306a36Sopenharmony_ci	vol->vol_type  = req->vol_type;
22362306a36Sopenharmony_ci	vol->name_len  = req->name_len;
22462306a36Sopenharmony_ci	memcpy(vol->name, req->name, vol->name_len);
22562306a36Sopenharmony_ci	vol->ubi = ubi;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	/*
22862306a36Sopenharmony_ci	 * Finish all pending erases because there may be some LEBs belonging
22962306a36Sopenharmony_ci	 * to the same volume ID.
23062306a36Sopenharmony_ci	 */
23162306a36Sopenharmony_ci	err = ubi_wl_flush(ubi, vol_id, UBI_ALL);
23262306a36Sopenharmony_ci	if (err)
23362306a36Sopenharmony_ci		goto out_acc;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	eba_tbl = ubi_eba_create_table(vol, vol->reserved_pebs);
23662306a36Sopenharmony_ci	if (IS_ERR(eba_tbl)) {
23762306a36Sopenharmony_ci		err = PTR_ERR(eba_tbl);
23862306a36Sopenharmony_ci		goto out_acc;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	ubi_eba_replace_table(vol, eba_tbl);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
24462306a36Sopenharmony_ci		vol->used_ebs = vol->reserved_pebs;
24562306a36Sopenharmony_ci		vol->last_eb_bytes = vol->usable_leb_size;
24662306a36Sopenharmony_ci		vol->used_bytes =
24762306a36Sopenharmony_ci			(long long)vol->used_ebs * vol->usable_leb_size;
24862306a36Sopenharmony_ci	} else {
24962306a36Sopenharmony_ci		vol->used_ebs = div_u64_rem(vol->used_bytes,
25062306a36Sopenharmony_ci					    vol->usable_leb_size,
25162306a36Sopenharmony_ci					    &vol->last_eb_bytes);
25262306a36Sopenharmony_ci		if (vol->last_eb_bytes != 0)
25362306a36Sopenharmony_ci			vol->used_ebs += 1;
25462306a36Sopenharmony_ci		else
25562306a36Sopenharmony_ci			vol->last_eb_bytes = vol->usable_leb_size;
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	/* Make volume "available" before it becomes accessible via sysfs */
25962306a36Sopenharmony_ci	spin_lock(&ubi->volumes_lock);
26062306a36Sopenharmony_ci	ubi->volumes[vol_id] = vol;
26162306a36Sopenharmony_ci	ubi->vol_count += 1;
26262306a36Sopenharmony_ci	spin_unlock(&ubi->volumes_lock);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	/* Register character device for the volume */
26562306a36Sopenharmony_ci	cdev_init(&vol->cdev, &ubi_vol_cdev_operations);
26662306a36Sopenharmony_ci	vol->cdev.owner = THIS_MODULE;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	vol->dev.devt = MKDEV(MAJOR(ubi->cdev.dev), vol_id + 1);
26962306a36Sopenharmony_ci	dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id);
27062306a36Sopenharmony_ci	err = cdev_device_add(&vol->cdev, &vol->dev);
27162306a36Sopenharmony_ci	if (err) {
27262306a36Sopenharmony_ci		ubi_err(ubi, "cannot add device");
27362306a36Sopenharmony_ci		goto out_mapping;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	/* Fill volume table record */
27762306a36Sopenharmony_ci	memset(&vtbl_rec, 0, sizeof(struct ubi_vtbl_record));
27862306a36Sopenharmony_ci	vtbl_rec.reserved_pebs = cpu_to_be32(vol->reserved_pebs);
27962306a36Sopenharmony_ci	vtbl_rec.alignment     = cpu_to_be32(vol->alignment);
28062306a36Sopenharmony_ci	vtbl_rec.data_pad      = cpu_to_be32(vol->data_pad);
28162306a36Sopenharmony_ci	vtbl_rec.name_len      = cpu_to_be16(vol->name_len);
28262306a36Sopenharmony_ci	if (vol->vol_type == UBI_DYNAMIC_VOLUME)
28362306a36Sopenharmony_ci		vtbl_rec.vol_type = UBI_VID_DYNAMIC;
28462306a36Sopenharmony_ci	else
28562306a36Sopenharmony_ci		vtbl_rec.vol_type = UBI_VID_STATIC;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	if (vol->skip_check)
28862306a36Sopenharmony_ci		vtbl_rec.flags |= UBI_VTBL_SKIP_CRC_CHECK_FLG;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	memcpy(vtbl_rec.name, vol->name, vol->name_len);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec);
29362306a36Sopenharmony_ci	if (err)
29462306a36Sopenharmony_ci		goto out_sysfs;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	ubi_volume_notify(ubi, vol, UBI_VOLUME_ADDED);
29762306a36Sopenharmony_ci	self_check_volumes(ubi);
29862306a36Sopenharmony_ci	return err;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ciout_sysfs:
30162306a36Sopenharmony_ci	/*
30262306a36Sopenharmony_ci	 * We have registered our device, we should not free the volume
30362306a36Sopenharmony_ci	 * description object in this function in case of an error - it is
30462306a36Sopenharmony_ci	 * freed by the release function.
30562306a36Sopenharmony_ci	 */
30662306a36Sopenharmony_ci	cdev_device_del(&vol->cdev, &vol->dev);
30762306a36Sopenharmony_ciout_mapping:
30862306a36Sopenharmony_ci	spin_lock(&ubi->volumes_lock);
30962306a36Sopenharmony_ci	ubi->volumes[vol_id] = NULL;
31062306a36Sopenharmony_ci	ubi->vol_count -= 1;
31162306a36Sopenharmony_ci	spin_unlock(&ubi->volumes_lock);
31262306a36Sopenharmony_ciout_acc:
31362306a36Sopenharmony_ci	spin_lock(&ubi->volumes_lock);
31462306a36Sopenharmony_ci	ubi->rsvd_pebs -= vol->reserved_pebs;
31562306a36Sopenharmony_ci	ubi->avail_pebs += vol->reserved_pebs;
31662306a36Sopenharmony_ciout_unlock:
31762306a36Sopenharmony_ci	spin_unlock(&ubi->volumes_lock);
31862306a36Sopenharmony_ci	put_device(&vol->dev);
31962306a36Sopenharmony_ci	ubi_err(ubi, "cannot create volume %d, error %d", vol_id, err);
32062306a36Sopenharmony_ci	return err;
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci/**
32462306a36Sopenharmony_ci * ubi_remove_volume - remove volume.
32562306a36Sopenharmony_ci * @desc: volume descriptor
32662306a36Sopenharmony_ci * @no_vtbl: do not change volume table if not zero
32762306a36Sopenharmony_ci *
32862306a36Sopenharmony_ci * This function removes volume described by @desc. The volume has to be opened
32962306a36Sopenharmony_ci * in "exclusive" mode. Returns zero in case of success and a negative error
33062306a36Sopenharmony_ci * code in case of failure. The caller has to have the @ubi->device_mutex
33162306a36Sopenharmony_ci * locked.
33262306a36Sopenharmony_ci */
33362306a36Sopenharmony_ciint ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	struct ubi_volume *vol = desc->vol;
33662306a36Sopenharmony_ci	struct ubi_device *ubi = vol->ubi;
33762306a36Sopenharmony_ci	int i, err, vol_id = vol->vol_id, reserved_pebs = vol->reserved_pebs;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	dbg_gen("remove device %d, volume %d", ubi->ubi_num, vol_id);
34062306a36Sopenharmony_ci	ubi_assert(desc->mode == UBI_EXCLUSIVE);
34162306a36Sopenharmony_ci	ubi_assert(vol == ubi->volumes[vol_id]);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	if (ubi->ro_mode)
34462306a36Sopenharmony_ci		return -EROFS;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	spin_lock(&ubi->volumes_lock);
34762306a36Sopenharmony_ci	if (vol->ref_count > 1) {
34862306a36Sopenharmony_ci		/*
34962306a36Sopenharmony_ci		 * The volume is busy, probably someone is reading one of its
35062306a36Sopenharmony_ci		 * sysfs files.
35162306a36Sopenharmony_ci		 */
35262306a36Sopenharmony_ci		err = -EBUSY;
35362306a36Sopenharmony_ci		goto out_unlock;
35462306a36Sopenharmony_ci	}
35562306a36Sopenharmony_ci	ubi->volumes[vol_id] = NULL;
35662306a36Sopenharmony_ci	spin_unlock(&ubi->volumes_lock);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	if (!no_vtbl) {
35962306a36Sopenharmony_ci		err = ubi_change_vtbl_record(ubi, vol_id, NULL);
36062306a36Sopenharmony_ci		if (err)
36162306a36Sopenharmony_ci			goto out_err;
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	for (i = 0; i < vol->reserved_pebs; i++) {
36562306a36Sopenharmony_ci		err = ubi_eba_unmap_leb(ubi, vol, i);
36662306a36Sopenharmony_ci		if (err)
36762306a36Sopenharmony_ci			goto out_err;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	cdev_device_del(&vol->cdev, &vol->dev);
37162306a36Sopenharmony_ci	put_device(&vol->dev);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	spin_lock(&ubi->volumes_lock);
37462306a36Sopenharmony_ci	ubi->rsvd_pebs -= reserved_pebs;
37562306a36Sopenharmony_ci	ubi->avail_pebs += reserved_pebs;
37662306a36Sopenharmony_ci	ubi_update_reserved(ubi);
37762306a36Sopenharmony_ci	ubi->vol_count -= 1;
37862306a36Sopenharmony_ci	spin_unlock(&ubi->volumes_lock);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	ubi_volume_notify(ubi, vol, UBI_VOLUME_REMOVED);
38162306a36Sopenharmony_ci	if (!no_vtbl)
38262306a36Sopenharmony_ci		self_check_volumes(ubi);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	return 0;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ciout_err:
38762306a36Sopenharmony_ci	ubi_err(ubi, "cannot remove volume %d, error %d", vol_id, err);
38862306a36Sopenharmony_ci	spin_lock(&ubi->volumes_lock);
38962306a36Sopenharmony_ci	ubi->volumes[vol_id] = vol;
39062306a36Sopenharmony_ciout_unlock:
39162306a36Sopenharmony_ci	spin_unlock(&ubi->volumes_lock);
39262306a36Sopenharmony_ci	return err;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci/**
39662306a36Sopenharmony_ci * ubi_resize_volume - re-size volume.
39762306a36Sopenharmony_ci * @desc: volume descriptor
39862306a36Sopenharmony_ci * @reserved_pebs: new size in physical eraseblocks
39962306a36Sopenharmony_ci *
40062306a36Sopenharmony_ci * This function re-sizes the volume and returns zero in case of success, and a
40162306a36Sopenharmony_ci * negative error code in case of failure. The caller has to have the
40262306a36Sopenharmony_ci * @ubi->device_mutex locked.
40362306a36Sopenharmony_ci */
40462306a36Sopenharmony_ciint ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	int i, err, pebs;
40762306a36Sopenharmony_ci	struct ubi_volume *vol = desc->vol;
40862306a36Sopenharmony_ci	struct ubi_device *ubi = vol->ubi;
40962306a36Sopenharmony_ci	struct ubi_vtbl_record vtbl_rec;
41062306a36Sopenharmony_ci	struct ubi_eba_table *new_eba_tbl = NULL;
41162306a36Sopenharmony_ci	int vol_id = vol->vol_id;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	if (ubi->ro_mode)
41462306a36Sopenharmony_ci		return -EROFS;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	dbg_gen("re-size device %d, volume %d to from %d to %d PEBs",
41762306a36Sopenharmony_ci		ubi->ubi_num, vol_id, vol->reserved_pebs, reserved_pebs);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	if (vol->vol_type == UBI_STATIC_VOLUME &&
42062306a36Sopenharmony_ci	    reserved_pebs < vol->used_ebs) {
42162306a36Sopenharmony_ci		ubi_err(ubi, "too small size %d, %d LEBs contain data",
42262306a36Sopenharmony_ci			reserved_pebs, vol->used_ebs);
42362306a36Sopenharmony_ci		return -EINVAL;
42462306a36Sopenharmony_ci	}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	/* If the size is the same, we have nothing to do */
42762306a36Sopenharmony_ci	if (reserved_pebs == vol->reserved_pebs)
42862306a36Sopenharmony_ci		return 0;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	new_eba_tbl = ubi_eba_create_table(vol, reserved_pebs);
43162306a36Sopenharmony_ci	if (IS_ERR(new_eba_tbl))
43262306a36Sopenharmony_ci		return PTR_ERR(new_eba_tbl);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	spin_lock(&ubi->volumes_lock);
43562306a36Sopenharmony_ci	if (vol->ref_count > 1) {
43662306a36Sopenharmony_ci		spin_unlock(&ubi->volumes_lock);
43762306a36Sopenharmony_ci		err = -EBUSY;
43862306a36Sopenharmony_ci		goto out_free;
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci	spin_unlock(&ubi->volumes_lock);
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	/* Reserve physical eraseblocks */
44362306a36Sopenharmony_ci	pebs = reserved_pebs - vol->reserved_pebs;
44462306a36Sopenharmony_ci	if (pebs > 0) {
44562306a36Sopenharmony_ci		spin_lock(&ubi->volumes_lock);
44662306a36Sopenharmony_ci		if (pebs > ubi->avail_pebs) {
44762306a36Sopenharmony_ci			ubi_err(ubi, "not enough PEBs: requested %d, available %d",
44862306a36Sopenharmony_ci				pebs, ubi->avail_pebs);
44962306a36Sopenharmony_ci			if (ubi->corr_peb_count)
45062306a36Sopenharmony_ci				ubi_err(ubi, "%d PEBs are corrupted and not used",
45162306a36Sopenharmony_ci					ubi->corr_peb_count);
45262306a36Sopenharmony_ci			spin_unlock(&ubi->volumes_lock);
45362306a36Sopenharmony_ci			err = -ENOSPC;
45462306a36Sopenharmony_ci			goto out_free;
45562306a36Sopenharmony_ci		}
45662306a36Sopenharmony_ci		ubi->avail_pebs -= pebs;
45762306a36Sopenharmony_ci		ubi->rsvd_pebs += pebs;
45862306a36Sopenharmony_ci		ubi_eba_copy_table(vol, new_eba_tbl, vol->reserved_pebs);
45962306a36Sopenharmony_ci		ubi_eba_replace_table(vol, new_eba_tbl);
46062306a36Sopenharmony_ci		spin_unlock(&ubi->volumes_lock);
46162306a36Sopenharmony_ci	}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	if (pebs < 0) {
46462306a36Sopenharmony_ci		for (i = 0; i < -pebs; i++) {
46562306a36Sopenharmony_ci			err = ubi_eba_unmap_leb(ubi, vol, reserved_pebs + i);
46662306a36Sopenharmony_ci			if (err)
46762306a36Sopenharmony_ci				goto out_free;
46862306a36Sopenharmony_ci		}
46962306a36Sopenharmony_ci		spin_lock(&ubi->volumes_lock);
47062306a36Sopenharmony_ci		ubi->rsvd_pebs += pebs;
47162306a36Sopenharmony_ci		ubi->avail_pebs -= pebs;
47262306a36Sopenharmony_ci		ubi_update_reserved(ubi);
47362306a36Sopenharmony_ci		ubi_eba_copy_table(vol, new_eba_tbl, reserved_pebs);
47462306a36Sopenharmony_ci		ubi_eba_replace_table(vol, new_eba_tbl);
47562306a36Sopenharmony_ci		spin_unlock(&ubi->volumes_lock);
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	/*
47962306a36Sopenharmony_ci	 * When we shrink a volume we have to flush all pending (erase) work.
48062306a36Sopenharmony_ci	 * Otherwise it can happen that upon next attach UBI finds a LEB with
48162306a36Sopenharmony_ci	 * lnum > highest_lnum and refuses to attach.
48262306a36Sopenharmony_ci	 */
48362306a36Sopenharmony_ci	if (pebs < 0) {
48462306a36Sopenharmony_ci		err = ubi_wl_flush(ubi, vol_id, UBI_ALL);
48562306a36Sopenharmony_ci		if (err)
48662306a36Sopenharmony_ci			goto out_acc;
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	/* Change volume table record */
49062306a36Sopenharmony_ci	vtbl_rec = ubi->vtbl[vol_id];
49162306a36Sopenharmony_ci	vtbl_rec.reserved_pebs = cpu_to_be32(reserved_pebs);
49262306a36Sopenharmony_ci	err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec);
49362306a36Sopenharmony_ci	if (err)
49462306a36Sopenharmony_ci		goto out_acc;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	vol->reserved_pebs = reserved_pebs;
49762306a36Sopenharmony_ci	if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
49862306a36Sopenharmony_ci		vol->used_ebs = reserved_pebs;
49962306a36Sopenharmony_ci		vol->last_eb_bytes = vol->usable_leb_size;
50062306a36Sopenharmony_ci		vol->used_bytes =
50162306a36Sopenharmony_ci			(long long)vol->used_ebs * vol->usable_leb_size;
50262306a36Sopenharmony_ci	}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	ubi_volume_notify(ubi, vol, UBI_VOLUME_RESIZED);
50562306a36Sopenharmony_ci	self_check_volumes(ubi);
50662306a36Sopenharmony_ci	return err;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ciout_acc:
50962306a36Sopenharmony_ci	if (pebs > 0) {
51062306a36Sopenharmony_ci		spin_lock(&ubi->volumes_lock);
51162306a36Sopenharmony_ci		ubi->rsvd_pebs -= pebs;
51262306a36Sopenharmony_ci		ubi->avail_pebs += pebs;
51362306a36Sopenharmony_ci		spin_unlock(&ubi->volumes_lock);
51462306a36Sopenharmony_ci	}
51562306a36Sopenharmony_ci	return err;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ciout_free:
51862306a36Sopenharmony_ci	ubi_eba_destroy_table(new_eba_tbl);
51962306a36Sopenharmony_ci	return err;
52062306a36Sopenharmony_ci}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci/**
52362306a36Sopenharmony_ci * ubi_rename_volumes - re-name UBI volumes.
52462306a36Sopenharmony_ci * @ubi: UBI device description object
52562306a36Sopenharmony_ci * @rename_list: list of &struct ubi_rename_entry objects
52662306a36Sopenharmony_ci *
52762306a36Sopenharmony_ci * This function re-names or removes volumes specified in the re-name list.
52862306a36Sopenharmony_ci * Returns zero in case of success and a negative error code in case of
52962306a36Sopenharmony_ci * failure.
53062306a36Sopenharmony_ci */
53162306a36Sopenharmony_ciint ubi_rename_volumes(struct ubi_device *ubi, struct list_head *rename_list)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	int err;
53462306a36Sopenharmony_ci	struct ubi_rename_entry *re;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	err = ubi_vtbl_rename_volumes(ubi, rename_list);
53762306a36Sopenharmony_ci	if (err)
53862306a36Sopenharmony_ci		return err;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	list_for_each_entry(re, rename_list, list) {
54162306a36Sopenharmony_ci		if (re->remove) {
54262306a36Sopenharmony_ci			err = ubi_remove_volume(re->desc, 1);
54362306a36Sopenharmony_ci			if (err)
54462306a36Sopenharmony_ci				break;
54562306a36Sopenharmony_ci		} else {
54662306a36Sopenharmony_ci			struct ubi_volume *vol = re->desc->vol;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci			spin_lock(&ubi->volumes_lock);
54962306a36Sopenharmony_ci			vol->name_len = re->new_name_len;
55062306a36Sopenharmony_ci			memcpy(vol->name, re->new_name, re->new_name_len + 1);
55162306a36Sopenharmony_ci			spin_unlock(&ubi->volumes_lock);
55262306a36Sopenharmony_ci			ubi_volume_notify(ubi, vol, UBI_VOLUME_RENAMED);
55362306a36Sopenharmony_ci		}
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	if (!err)
55762306a36Sopenharmony_ci		self_check_volumes(ubi);
55862306a36Sopenharmony_ci	return err;
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci/**
56262306a36Sopenharmony_ci * ubi_add_volume - add volume.
56362306a36Sopenharmony_ci * @ubi: UBI device description object
56462306a36Sopenharmony_ci * @vol: volume description object
56562306a36Sopenharmony_ci *
56662306a36Sopenharmony_ci * This function adds an existing volume and initializes all its data
56762306a36Sopenharmony_ci * structures. Returns zero in case of success and a negative error code in
56862306a36Sopenharmony_ci * case of failure.
56962306a36Sopenharmony_ci */
57062306a36Sopenharmony_ciint ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol)
57162306a36Sopenharmony_ci{
57262306a36Sopenharmony_ci	int err, vol_id = vol->vol_id;
57362306a36Sopenharmony_ci	dev_t dev;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	dbg_gen("add volume %d", vol_id);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	/* Register character device for the volume */
57862306a36Sopenharmony_ci	cdev_init(&vol->cdev, &ubi_vol_cdev_operations);
57962306a36Sopenharmony_ci	vol->cdev.owner = THIS_MODULE;
58062306a36Sopenharmony_ci	dev = MKDEV(MAJOR(ubi->cdev.dev), vol->vol_id + 1);
58162306a36Sopenharmony_ci	err = cdev_add(&vol->cdev, dev, 1);
58262306a36Sopenharmony_ci	if (err) {
58362306a36Sopenharmony_ci		ubi_err(ubi, "cannot add character device for volume %d, error %d",
58462306a36Sopenharmony_ci			vol_id, err);
58562306a36Sopenharmony_ci		vol_release(&vol->dev);
58662306a36Sopenharmony_ci		return err;
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	vol->dev.release = vol_release;
59062306a36Sopenharmony_ci	vol->dev.parent = &ubi->dev;
59162306a36Sopenharmony_ci	vol->dev.devt = dev;
59262306a36Sopenharmony_ci	vol->dev.class = &ubi_class;
59362306a36Sopenharmony_ci	vol->dev.groups = volume_dev_groups;
59462306a36Sopenharmony_ci	dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id);
59562306a36Sopenharmony_ci	err = device_register(&vol->dev);
59662306a36Sopenharmony_ci	if (err) {
59762306a36Sopenharmony_ci		cdev_del(&vol->cdev);
59862306a36Sopenharmony_ci		put_device(&vol->dev);
59962306a36Sopenharmony_ci		return err;
60062306a36Sopenharmony_ci	}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	self_check_volumes(ubi);
60362306a36Sopenharmony_ci	return err;
60462306a36Sopenharmony_ci}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci/**
60762306a36Sopenharmony_ci * ubi_free_volume - free volume.
60862306a36Sopenharmony_ci * @ubi: UBI device description object
60962306a36Sopenharmony_ci * @vol: volume description object
61062306a36Sopenharmony_ci *
61162306a36Sopenharmony_ci * This function frees all resources for volume @vol but does not remove it.
61262306a36Sopenharmony_ci * Used only when the UBI device is detached.
61362306a36Sopenharmony_ci */
61462306a36Sopenharmony_civoid ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol)
61562306a36Sopenharmony_ci{
61662306a36Sopenharmony_ci	dbg_gen("free volume %d", vol->vol_id);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	ubi->volumes[vol->vol_id] = NULL;
61962306a36Sopenharmony_ci	cdev_del(&vol->cdev);
62062306a36Sopenharmony_ci	device_unregister(&vol->dev);
62162306a36Sopenharmony_ci}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci/**
62462306a36Sopenharmony_ci * self_check_volume - check volume information.
62562306a36Sopenharmony_ci * @ubi: UBI device description object
62662306a36Sopenharmony_ci * @vol_id: volume ID
62762306a36Sopenharmony_ci *
62862306a36Sopenharmony_ci * Returns zero if volume is all right and a negative error code if not.
62962306a36Sopenharmony_ci */
63062306a36Sopenharmony_cistatic int self_check_volume(struct ubi_device *ubi, int vol_id)
63162306a36Sopenharmony_ci{
63262306a36Sopenharmony_ci	int idx = vol_id2idx(ubi, vol_id);
63362306a36Sopenharmony_ci	int reserved_pebs, alignment, data_pad, vol_type, name_len, upd_marker;
63462306a36Sopenharmony_ci	const struct ubi_volume *vol;
63562306a36Sopenharmony_ci	long long n;
63662306a36Sopenharmony_ci	const char *name;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	spin_lock(&ubi->volumes_lock);
63962306a36Sopenharmony_ci	reserved_pebs = be32_to_cpu(ubi->vtbl[vol_id].reserved_pebs);
64062306a36Sopenharmony_ci	vol = ubi->volumes[idx];
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	if (!vol) {
64362306a36Sopenharmony_ci		if (reserved_pebs) {
64462306a36Sopenharmony_ci			ubi_err(ubi, "no volume info, but volume exists");
64562306a36Sopenharmony_ci			goto fail;
64662306a36Sopenharmony_ci		}
64762306a36Sopenharmony_ci		spin_unlock(&ubi->volumes_lock);
64862306a36Sopenharmony_ci		return 0;
64962306a36Sopenharmony_ci	}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	if (vol->reserved_pebs < 0 || vol->alignment < 0 || vol->data_pad < 0 ||
65262306a36Sopenharmony_ci	    vol->name_len < 0) {
65362306a36Sopenharmony_ci		ubi_err(ubi, "negative values");
65462306a36Sopenharmony_ci		goto fail;
65562306a36Sopenharmony_ci	}
65662306a36Sopenharmony_ci	if (vol->alignment > ubi->leb_size || vol->alignment == 0) {
65762306a36Sopenharmony_ci		ubi_err(ubi, "bad alignment");
65862306a36Sopenharmony_ci		goto fail;
65962306a36Sopenharmony_ci	}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	n = vol->alignment & (ubi->min_io_size - 1);
66262306a36Sopenharmony_ci	if (vol->alignment != 1 && n) {
66362306a36Sopenharmony_ci		ubi_err(ubi, "alignment is not multiple of min I/O unit");
66462306a36Sopenharmony_ci		goto fail;
66562306a36Sopenharmony_ci	}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	n = ubi->leb_size % vol->alignment;
66862306a36Sopenharmony_ci	if (vol->data_pad != n) {
66962306a36Sopenharmony_ci		ubi_err(ubi, "bad data_pad, has to be %lld", n);
67062306a36Sopenharmony_ci		goto fail;
67162306a36Sopenharmony_ci	}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	if (vol->vol_type != UBI_DYNAMIC_VOLUME &&
67462306a36Sopenharmony_ci	    vol->vol_type != UBI_STATIC_VOLUME) {
67562306a36Sopenharmony_ci		ubi_err(ubi, "bad vol_type");
67662306a36Sopenharmony_ci		goto fail;
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	if (vol->upd_marker && vol->corrupted) {
68062306a36Sopenharmony_ci		ubi_err(ubi, "update marker and corrupted simultaneously");
68162306a36Sopenharmony_ci		goto fail;
68262306a36Sopenharmony_ci	}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	if (vol->reserved_pebs > ubi->good_peb_count) {
68562306a36Sopenharmony_ci		ubi_err(ubi, "too large reserved_pebs");
68662306a36Sopenharmony_ci		goto fail;
68762306a36Sopenharmony_ci	}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	n = ubi->leb_size - vol->data_pad;
69062306a36Sopenharmony_ci	if (vol->usable_leb_size != ubi->leb_size - vol->data_pad) {
69162306a36Sopenharmony_ci		ubi_err(ubi, "bad usable_leb_size, has to be %lld", n);
69262306a36Sopenharmony_ci		goto fail;
69362306a36Sopenharmony_ci	}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	if (vol->name_len > UBI_VOL_NAME_MAX) {
69662306a36Sopenharmony_ci		ubi_err(ubi, "too long volume name, max is %d",
69762306a36Sopenharmony_ci			UBI_VOL_NAME_MAX);
69862306a36Sopenharmony_ci		goto fail;
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	n = strnlen(vol->name, vol->name_len + 1);
70262306a36Sopenharmony_ci	if (n != vol->name_len) {
70362306a36Sopenharmony_ci		ubi_err(ubi, "bad name_len %lld", n);
70462306a36Sopenharmony_ci		goto fail;
70562306a36Sopenharmony_ci	}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	n = (long long)vol->used_ebs * vol->usable_leb_size;
70862306a36Sopenharmony_ci	if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
70962306a36Sopenharmony_ci		if (vol->corrupted) {
71062306a36Sopenharmony_ci			ubi_err(ubi, "corrupted dynamic volume");
71162306a36Sopenharmony_ci			goto fail;
71262306a36Sopenharmony_ci		}
71362306a36Sopenharmony_ci		if (vol->used_ebs != vol->reserved_pebs) {
71462306a36Sopenharmony_ci			ubi_err(ubi, "bad used_ebs");
71562306a36Sopenharmony_ci			goto fail;
71662306a36Sopenharmony_ci		}
71762306a36Sopenharmony_ci		if (vol->last_eb_bytes != vol->usable_leb_size) {
71862306a36Sopenharmony_ci			ubi_err(ubi, "bad last_eb_bytes");
71962306a36Sopenharmony_ci			goto fail;
72062306a36Sopenharmony_ci		}
72162306a36Sopenharmony_ci		if (vol->used_bytes != n) {
72262306a36Sopenharmony_ci			ubi_err(ubi, "bad used_bytes");
72362306a36Sopenharmony_ci			goto fail;
72462306a36Sopenharmony_ci		}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci		if (vol->skip_check) {
72762306a36Sopenharmony_ci			ubi_err(ubi, "bad skip_check");
72862306a36Sopenharmony_ci			goto fail;
72962306a36Sopenharmony_ci		}
73062306a36Sopenharmony_ci	} else {
73162306a36Sopenharmony_ci		if (vol->used_ebs < 0 || vol->used_ebs > vol->reserved_pebs) {
73262306a36Sopenharmony_ci			ubi_err(ubi, "bad used_ebs");
73362306a36Sopenharmony_ci			goto fail;
73462306a36Sopenharmony_ci		}
73562306a36Sopenharmony_ci		if (vol->last_eb_bytes < 0 ||
73662306a36Sopenharmony_ci		    vol->last_eb_bytes > vol->usable_leb_size) {
73762306a36Sopenharmony_ci			ubi_err(ubi, "bad last_eb_bytes");
73862306a36Sopenharmony_ci			goto fail;
73962306a36Sopenharmony_ci		}
74062306a36Sopenharmony_ci		if (vol->used_bytes < 0 || vol->used_bytes > n ||
74162306a36Sopenharmony_ci		    vol->used_bytes < n - vol->usable_leb_size) {
74262306a36Sopenharmony_ci			ubi_err(ubi, "bad used_bytes");
74362306a36Sopenharmony_ci			goto fail;
74462306a36Sopenharmony_ci		}
74562306a36Sopenharmony_ci	}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	alignment  = be32_to_cpu(ubi->vtbl[vol_id].alignment);
74862306a36Sopenharmony_ci	data_pad   = be32_to_cpu(ubi->vtbl[vol_id].data_pad);
74962306a36Sopenharmony_ci	name_len   = be16_to_cpu(ubi->vtbl[vol_id].name_len);
75062306a36Sopenharmony_ci	upd_marker = ubi->vtbl[vol_id].upd_marker;
75162306a36Sopenharmony_ci	name       = &ubi->vtbl[vol_id].name[0];
75262306a36Sopenharmony_ci	if (ubi->vtbl[vol_id].vol_type == UBI_VID_DYNAMIC)
75362306a36Sopenharmony_ci		vol_type = UBI_DYNAMIC_VOLUME;
75462306a36Sopenharmony_ci	else
75562306a36Sopenharmony_ci		vol_type = UBI_STATIC_VOLUME;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	if (alignment != vol->alignment || data_pad != vol->data_pad ||
75862306a36Sopenharmony_ci	    upd_marker != vol->upd_marker || vol_type != vol->vol_type ||
75962306a36Sopenharmony_ci	    name_len != vol->name_len || strncmp(name, vol->name, name_len)) {
76062306a36Sopenharmony_ci		ubi_err(ubi, "volume info is different");
76162306a36Sopenharmony_ci		goto fail;
76262306a36Sopenharmony_ci	}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	spin_unlock(&ubi->volumes_lock);
76562306a36Sopenharmony_ci	return 0;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_cifail:
76862306a36Sopenharmony_ci	ubi_err(ubi, "self-check failed for volume %d", vol_id);
76962306a36Sopenharmony_ci	if (vol)
77062306a36Sopenharmony_ci		ubi_dump_vol_info(vol);
77162306a36Sopenharmony_ci	ubi_dump_vtbl_record(&ubi->vtbl[vol_id], vol_id);
77262306a36Sopenharmony_ci	dump_stack();
77362306a36Sopenharmony_ci	spin_unlock(&ubi->volumes_lock);
77462306a36Sopenharmony_ci	return -EINVAL;
77562306a36Sopenharmony_ci}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci/**
77862306a36Sopenharmony_ci * self_check_volumes - check information about all volumes.
77962306a36Sopenharmony_ci * @ubi: UBI device description object
78062306a36Sopenharmony_ci *
78162306a36Sopenharmony_ci * Returns zero if volumes are all right and a negative error code if not.
78262306a36Sopenharmony_ci */
78362306a36Sopenharmony_cistatic int self_check_volumes(struct ubi_device *ubi)
78462306a36Sopenharmony_ci{
78562306a36Sopenharmony_ci	int i, err = 0;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	if (!ubi_dbg_chk_gen(ubi))
78862306a36Sopenharmony_ci		return 0;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	for (i = 0; i < ubi->vtbl_slots; i++) {
79162306a36Sopenharmony_ci		err = self_check_volume(ubi, i);
79262306a36Sopenharmony_ci		if (err)
79362306a36Sopenharmony_ci			break;
79462306a36Sopenharmony_ci	}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	return err;
79762306a36Sopenharmony_ci}
798