[RFC] UBI volume re-name
Richard Titmuss
richard_titmuss at logitech.com
Tue Jul 15 07:00:39 EDT 2008
Hi Artem,
That's excellent, and exactly the functionality I need. I have also
started a patch to add -N support to ubinfo, ubirmvol and ubiupdatevol.
I've got a little distracted with another couple of projects right now,
but hope to return to looking at UBI again next week.
Thanks,
Richard
Artem Bityutskiy wrote:
> Hi,
>
> here is implementation of the volume re-name ioctl suggested
> by Richard Titmuss. I think it was quite nice idea which allows
> users to upgrade UBI volumes atomically:
>
> 1. Suppose we want to update volumes A and B atomically.
> 2. Create temporary volumes A1 and B1
> 3. Put new contents to volumes A1 and B1
> 4. Atomically rename A1->A and A->A1, B1->B and B->B1.
> 5. Now A and B have new contents
> 6. Remove A1 and B1.
>
> Of course volume id of A1 and B1 would change, but if the
> volumes are only referred by name, then it'll be fine.
>
> >From b285d9230c753890e9f57c43174559e4d1122c11 Mon Sep 17 00:00:00 2001
> From: Artem Bityutskiy <Artem.Bityutskiy at nokia.com>
> Date: Sun, 13 Jul 2008 21:47:47 +0300
> Subject: [PATCH] UBI: implement multiple volumes rename
>
> Quite useful ioctl which allows to make atomic system upgrades.
> The idea belongs to Richard Titmuss <richard_titmuss at logitech.com>
>
> Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy at nokia.com>
> ---
> drivers/mtd/ubi/cdev.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++
> drivers/mtd/ubi/ubi.h | 5 ++-
> drivers/mtd/ubi/vmt.c | 30 +++++++++++++++
> drivers/mtd/ubi/vtbl.c | 46 +++++++++++++++++++++++
> include/mtd/ubi-user.h | 42 +++++++++++++++++++++
> 5 files changed, 217 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c
> index 333c98e..b29c362 100644
> --- a/drivers/mtd/ubi/cdev.c
> +++ b/drivers/mtd/ubi/cdev.c
> @@ -599,6 +599,75 @@ static int verify_rsvol_req(const struct ubi_device *ubi,
> return 0;
> }
>
> +/**
> + * rename_volumes - rename UBI volumes.
> + * @ubi: UBI device description object
> + * @req: volumes re-name request
> + *
> + * This is a helper function for the volume re-name IOCTL which validates the
> + * the request, opens the volume and calls corresponding volumes management
> + * function. Returns zero in case of success and a negative error code in case
> + * of failure.
> + */
> +static int rename_volumes(struct ubi_device *ubi,
> + struct ubi_rnvol_req *req)
> +{
> + int i, n, err;
> + struct ubi_volume_desc *desc[UBI_MAX_RNVOL];
> +
> + if (req->count < 0 || req->count > UBI_MAX_RNVOL)
> + return -EINVAL;
> +
> + /* Validate volume IDs and names in the request */
> + for (i = 0; i < req->count; i++) {
> + if (req->vols[i].vol_id < 0 ||
> + req->vols[i].vol_id >= ubi->vtbl_slots)
> + return -EINVAL;
> + if (req->vols[i].name_len < 0)
> + return -EINVAL;
> + if (req->vols[i].name_len > UBI_VOL_NAME_MAX)
> + return -ENAMETOOLONG;
> + req->vols[i].name[req->vols[i].name_len] = '\0';
> + n = strlen(req->vols[i].name);
> + if (n != req->vols[i].name_len)
> + err = -EINVAL;
> + }
> +
> + /* Make sure volume IDs and names are unique */
> + for (i = 0; i < req->count - 1; i++) {
> + for (n = i + 1; n < req->count; n++) {
> + if (req->vols[i].vol_id == req->vols[n].vol_id) {
> + dbg_err("duplicated volume id %d",
> + req->vols[i].vol_id);
> + return -EINVAL;
> + }
> + if (!strcmp(req->vols[i].name, req->vols[n].name)) {
> + dbg_err("duplicated volume name %s",
> + req->vols[i].name);
> + return -EINVAL;
> + }
> + }
> + }
> +
> + /* Open all involeved volumes in exclusive mode */
> + for (i = 0; i < req->count; i++) {
> + desc[i] = ubi_open_volume(ubi->ubi_num, req->vols[i].vol_id,
> + UBI_EXCLUSIVE);
> + if (IS_ERR(desc[i])) {
> + err = PTR_ERR(desc[i]);
> + goto out;
> + }
> + }
> +
> + i = req->count;
> + err = ubi_rename_volumes(ubi, req);
> +
> +out:
> + for (n = 0; n < i; n++)
> + ubi_close_volume(desc[n]);
> + return err;
> +}
> +
> static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
> unsigned int cmd, unsigned long arg)
> {
> @@ -711,6 +780,32 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
> break;
> }
>
> + /* Re-name volumes command */
> + case UBI_IOCRNVOL:
> + {
> + struct ubi_rnvol_req *req;
> +
> + dbg_msg("re-name volumes");
> + req = kmalloc(sizeof(struct ubi_rnvol_req), GFP_KERNEL);
> + if (!req) {
> + err = -ENOMEM;
> + break;
> + };
> +
> + err = copy_from_user(req, argp, sizeof(struct ubi_rnvol_req));
> + if (err) {
> + err = -EFAULT;
> + kfree(req);
> + break;
> + }
> +
> + mutex_lock(&ubi->volumes_mutex);
> + err = rename_volumes(ubi, req);
> + mutex_unlock(&ubi->volumes_mutex);
> + kfree(req);
> + break;
> + }
> +
> default:
> err = -ENOTTY;
> break;
> diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
> index 940f6b7..e41f508 100644
> --- a/drivers/mtd/ubi/ubi.h
> +++ b/drivers/mtd/ubi/ubi.h
> @@ -273,7 +273,7 @@ struct ubi_wl_entry;
> * @vtbl_size: size of the volume table in bytes
> * @vtbl: in-RAM volume table copy
> * @volumes_mutex: protects on-flash volume table and serializes volume
> - * changes, like creation, deletion, update, resize
> + * changes, like creation, deletion, update, re-size and re-name
> *
> * @max_ec: current highest erase counter value
> * @mean_ec: current mean erase counter value
> @@ -427,12 +427,15 @@ extern struct mutex ubi_devices_mutex;
> /* vtbl.c */
> int ubi_change_vtbl_record(struct ubi_device *ubi, int idx,
> struct ubi_vtbl_record *vtbl_rec);
> +int ubi_vtbl_rename_volumes(struct ubi_device *ubi,
> + const struct ubi_rnvol_req *req);
> int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si);
>
> /* vmt.c */
> int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req);
> int ubi_remove_volume(struct ubi_volume_desc *desc);
> int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs);
> +int ubi_rename_volumes(struct ubi_device *ubi, const struct ubi_rnvol_req *req);
> int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol);
> void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol);
>
> diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c
> index bfa7c5d..03a6042 100644
> --- a/drivers/mtd/ubi/vmt.c
> +++ b/drivers/mtd/ubi/vmt.c
> @@ -602,6 +602,36 @@ out_free:
> }
>
> /**
> + * ubi_vtbl_rename_volumes - re-name UBI volumes.
> + * @req: volumes re-name request
> + *
> + * This function re-names multiple volumes specified in @req. Returns zero in
> + * case of success and a negative error code in case of failure.
> + */
> +int ubi_rename_volumes(struct ubi_device *ubi, const struct ubi_rnvol_req *req)
> +{
> + int i, err;
> +
> + err = ubi_vtbl_rename_volumes(ubi, req);
> + if (err)
> + return err;
> +
> + spin_lock(&ubi->volumes_lock);
> + for (i = 0; i < req->count; i++) {
> + int vol_id = req->vols[i].vol_id;
> + int name_len = req->vols[i].name_len;
> + const char *name = req->vols[i].name;
> + struct ubi_volume *vol = ubi->volumes[vol_id];
> +
> + vol->name_len = name_len;
> + memcpy(vol->name, name, name_len + 1);
> + }
> + spin_unlock(&ubi->volumes_lock);
> +
> + return 0;
> +}
> +
> +/**
> * ubi_add_volume - add volume.
> * @ubi: UBI device description object
> * @vol: volume description object
> diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c
> index d9af11a..8629574 100644
> --- a/drivers/mtd/ubi/vtbl.c
> +++ b/drivers/mtd/ubi/vtbl.c
> @@ -115,6 +115,52 @@ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx,
> }
>
> /**
> + * ubi_vtbl_rename_volumes - rename UBI volumes in the volume table.
> + * @ubi: UBI device description object
> + * @req: volumes re-name request
> + *
> + * This function re-names multiple volumes specified in @req in the volume
> + * table. Returns zero in case of success and a negative error code in case of
> + * failure.
> + */
> +int ubi_vtbl_rename_volumes(struct ubi_device *ubi,
> + const struct ubi_rnvol_req *req)
> +{
> + int i, err;
> + struct ubi_volume *layout_vol;
> +
> + for (i = 0; i < req->count; i++) {
> + uint32_t crc;
> + int vol_id = req->vols[i].vol_id;
> + int name_len = req->vols[i].name_len;
> + const char *name = req->vols[i].name;
> + struct ubi_vtbl_record *vtbl_rec = &ubi->vtbl[vol_id];
> +
> + vtbl_rec->name_len = cpu_to_be16(name_len);
> + memset(vtbl_rec->name, 0, UBI_VOL_NAME_MAX + 1);
> + memcpy(vtbl_rec->name, name, name_len);
> + crc = crc32(UBI_CRC32_INIT, vtbl_rec,
> + UBI_VTBL_RECORD_SIZE_CRC);
> + vtbl_rec->crc = cpu_to_be32(crc);
> + }
> +
> + layout_vol = ubi->volumes[vol_id2idx(ubi, UBI_LAYOUT_VOLUME_ID)];
> + for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) {
> + err = ubi_eba_unmap_leb(ubi, layout_vol, i);
> + if (err)
> + return err;
> +
> + err = ubi_eba_write_leb(ubi, layout_vol, i, ubi->vtbl, 0,
> + ubi->vtbl_size, UBI_LONGTERM);
> + if (err)
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +
> +/**
> * vtbl_check - check if volume table is not corrupted and contains sensible
> * data.
> * @ubi: UBI device description object
> diff --git a/include/mtd/ubi-user.h b/include/mtd/ubi-user.h
> index a7421f1..bd93b57 100644
> --- a/include/mtd/ubi-user.h
> +++ b/include/mtd/ubi-user.h
> @@ -58,6 +58,13 @@
> * device should be used. A &struct ubi_rsvol_req object has to be properly
> * filled and a pointer to it has to be passed to the IOCTL.
> *
> + * UBI volumes re-name
> + * ~~~~~~~~~~~~~~~~~~~
> + *
> + * To rename several volumes atomically at one go, the %UBI_IOCRNVOL command
> + * of the UBI character device should be used. A &struct ubi_rnvol_req object
> + * has to be properly filled and a pointer to it has to be passed to the IOCTL.
> + *
> * UBI volume update
> * ~~~~~~~~~~~~~~~~~
> *
> @@ -104,6 +111,8 @@
> #define UBI_IOCRMVOL _IOW(UBI_IOC_MAGIC, 1, int32_t)
> /* Re-size an UBI volume */
> #define UBI_IOCRSVOL _IOW(UBI_IOC_MAGIC, 2, struct ubi_rsvol_req)
> +/* Re-name volumes */
> +#define UBI_IOCRNVOL _IOW(UBI_IOC_MAGIC, 3, struct ubi_rnvol_req)
>
> /* IOCTL commands of the UBI control character device */
>
> @@ -128,6 +137,9 @@
> /* Maximum MTD device name length supported by UBI */
> #define MAX_UBI_MTD_NAME_LEN 127
>
> +/* Maximum amount of UBI volumes that can be renamed at one go */
> +#define UBI_MAX_RNVOL 32
> +
> /*
> * UBI data type hint constants.
> *
> @@ -251,6 +263,36 @@ struct ubi_rsvol_req {
> } __attribute__ ((packed));
>
> /**
> + * struct ubi_rnvol_req - volumes rename request.
> + * @count: count of volumes to rename
> + * @padding1: reserved for future, not used, has to be zeroed
> + * @vol_id: ID of the volume to rename
> + * @name_len: name length
> + * @padding2: reserved for future, not used, has to be zeroed
> + * @name: new volume name
> + *
> + * UBI allows to rename up to %32 volumes at one go. The count of volumes to
> + * rename is specified in the @count field. The ID of the volumes to rename
> + * and the new names are specified in the @vol_id and @name fields.
> + *
> + * The UBI volume rename operation is atomic, which means that should power cut
> + * happen, the volumes will have either old name or new name. So the possible
> + * use-cases of this command is atomic upgrade. Indeed, to upgrade, say, volumes
> + * A and B one may create temporary volumes A1 and B1 with the new contents,
> + * then atomically rename A1->A and A->A1, B1->B and B->B1.
> + */
> +struct ubi_rnvol_req {
> + int32_t count;
> + uint8_t padding1[12];
> + struct {
> + int32_t vol_id;
> + int16_t name_len;
> + uint8_t padding2[2];
> + char name[UBI_MAX_VOLUME_NAME + 1];
> + } vols[UBI_MAX_RNVOL];
> +} __attribute__ ((packed));
> +
> +/**
> * struct ubi_leb_change_req - a data structure used in atomic logical
> * eraseblock change requests.
> * @lnum: logical eraseblock number to change
>
More information about the linux-mtd
mailing list