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