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