Skip to content

Commit

Permalink
UBI: implement multiple volumes rename
Browse files Browse the repository at this point in the history
Quite useful ioctl which allows to make atomic system upgrades.
The idea belongs to Richard Titmuss <[email protected]>

Signed-off-by: Artem Bityutskiy <[email protected]>
  • Loading branch information
Artem Bityutskiy authored and Artem Bityutskiy committed Jul 24, 2008
1 parent c856635 commit f40ac9c
Show file tree
Hide file tree
Showing 6 changed files with 375 additions and 15 deletions.
1 change: 1 addition & 0 deletions drivers/mtd/ubi/build.c
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)

mutex_init(&ubi->buf_mutex);
mutex_init(&ubi->ckvol_mutex);
mutex_init(&ubi->mult_mutex);
mutex_init(&ubi->volumes_mutex);
spin_lock_init(&ubi->volumes_lock);

Expand Down
188 changes: 187 additions & 1 deletion drivers/mtd/ubi/cdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,166 @@ 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 list_head rename_list;
struct ubi_rename_entry *re, *re1;

if (req->count < 0 || req->count > UBI_MAX_RNVOL)
return -EINVAL;

if (req->count == 0)
return 0;

/* Validate volume IDs and names in the request */
for (i = 0; i < req->count; i++) {
if (req->ents[i].vol_id < 0 ||
req->ents[i].vol_id >= ubi->vtbl_slots)
return -EINVAL;
if (req->ents[i].name_len < 0)
return -EINVAL;
if (req->ents[i].name_len > UBI_VOL_NAME_MAX)
return -ENAMETOOLONG;
req->ents[i].name[req->ents[i].name_len] = '\0';
n = strlen(req->ents[i].name);
if (n != req->ents[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->ents[i].vol_id == req->ents[n].vol_id) {
dbg_err("duplicated volume id %d",
req->ents[i].vol_id);
return -EINVAL;
}
if (!strcmp(req->ents[i].name, req->ents[n].name)) {
dbg_err("duplicated volume name \"%s\"",
req->ents[i].name);
return -EINVAL;
}
}
}

/* Create the re-name list */
INIT_LIST_HEAD(&rename_list);
for (i = 0; i < req->count; i++) {
int vol_id = req->ents[i].vol_id;
int name_len = req->ents[i].name_len;
const char *name = req->ents[i].name;

re = kzalloc(sizeof(struct ubi_rename_entry), GFP_KERNEL);
if (!re) {
err = -ENOMEM;
goto out_free;
}

re->desc = ubi_open_volume(ubi->ubi_num, vol_id, UBI_EXCLUSIVE);
if (IS_ERR(re->desc)) {
err = PTR_ERR(re->desc);
dbg_err("cannot open volume %d, error %d", vol_id, err);
kfree(re);
goto out_free;
}

/* Skip this re-naming if the name does not really change */
if (re->desc->vol->name_len == name_len &&
!memcmp(re->desc->vol->name, name, name_len)) {
ubi_close_volume(re->desc);
kfree(re);
continue;
}

re->new_name_len = name_len;
memcpy(re->new_name, name, name_len);
list_add_tail(&re->list, &rename_list);
dbg_msg("will rename volume %d from \"%s\" to \"%s\"",
vol_id, re->desc->vol->name, name);
}

if (list_empty(&rename_list))
return 0;

/* Find out the volumes which have to be removed */
list_for_each_entry(re, &rename_list, list) {
struct ubi_volume_desc *desc;
int no_remove_needed = 0;

/*
* Volume @re->vol_id is going to be re-named to
* @re->new_name, while its current name is @name. If a volume
* with name @re->new_name currently exists, it has to be
* removed, unless it is also re-named in the request (@req).
*/
list_for_each_entry(re1, &rename_list, list) {
if (re->new_name_len == re1->desc->vol->name_len &&
!memcmp(re->new_name, re1->desc->vol->name,
re1->desc->vol->name_len)) {
no_remove_needed = 1;
break;
}
}

if (no_remove_needed)
continue;

/*
* It seems we need to remove volume with name @re->new_name,
* if it exists.
*/
desc = ubi_open_volume_nm(ubi->ubi_num, re->new_name, UBI_EXCLUSIVE);
if (IS_ERR(desc)) {
err = PTR_ERR(desc);
if (err == -ENODEV)
/* Re-naming into a non-existing volume name */
continue;

/* The volume exists but busy, or an error occurred */
dbg_err("cannot open volume \"%s\", error %d",
re->new_name, err);
goto out_free;
}

re = kzalloc(sizeof(struct ubi_rename_entry), GFP_KERNEL);
if (!re) {
err = -ENOMEM;
ubi_close_volume(desc);
goto out_free;
}

re->remove = 1;
re->desc = desc;
list_add(&re->list, &rename_list);
dbg_msg("will remove volume %d, name \"%s\"",
re->desc->vol->vol_id, re->desc->vol->name);
}

mutex_lock(&ubi->volumes_mutex);
err = ubi_rename_volumes(ubi, &rename_list);
mutex_unlock(&ubi->volumes_mutex);

out_free:
list_for_each_entry_safe(re, re1, &rename_list, list) {
ubi_close_volume(re->desc);
list_del(&re->list);
kfree(re);
}
return err;
}

static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
Expand Down Expand Up @@ -670,7 +830,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
}

mutex_lock(&ubi->volumes_mutex);
err = ubi_remove_volume(desc);
err = ubi_remove_volume(desc, 0);
mutex_unlock(&ubi->volumes_mutex);

/*
Expand Down Expand Up @@ -717,6 +877,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->mult_mutex);
err = rename_volumes(ubi, req);
mutex_unlock(&ubi->mult_mutex);
kfree(req);
break;
}

default:
err = -ENOTTY;
break;
Expand Down
33 changes: 30 additions & 3 deletions drivers/mtd/ubi/ubi.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,27 @@ struct ubi_ltree_entry {
struct rw_semaphore mutex;
};

/**
* struct ubi_rename_entry - volume re-name description data structure.
* @new_name_len: new volume name length
* @new_name: new volume name
* @remove: if not zero, this volume should be removed, not re-named
* @desc: descriptor of the volume
* @list: links re-name entries into a list
*
* This data structure is utilized in the multiple volume re-name code. Namely,
* UBI first creates a list of &struct ubi_rename_entry objects from the
* &struct ubi_rnvol_req request object, and then utilizes this list to do all
* the job.
*/
struct ubi_rename_entry {
int new_name_len;
char new_name[UBI_VOL_NAME_MAX + 1];
int remove;
struct ubi_volume_desc *desc;
struct list_head list;
};

struct ubi_volume_desc;

/**
Expand Down Expand Up @@ -206,7 +227,7 @@ struct ubi_volume {
int alignment;
int data_pad;
int name_len;
char name[UBI_VOL_NAME_MAX+1];
char name[UBI_VOL_NAME_MAX + 1];

int upd_ebs;
int ch_lnum;
Expand Down Expand Up @@ -272,7 +293,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
Expand Down Expand Up @@ -330,6 +351,8 @@ struct ubi_wl_entry;
* @peb_buf1: a buffer of PEB size used for different purposes
* @peb_buf2: another buffer of PEB size used for different purposes
* @buf_mutex: proptects @peb_buf1 and @peb_buf2
* @ckvol_mutex: serializes static volume checking when opening
* @mult_mutex: serializes operations on multiple volumes, like re-nameing
* @dbg_peb_buf: buffer of PEB size used for debugging
* @dbg_buf_mutex: proptects @dbg_peb_buf
*/
Expand Down Expand Up @@ -410,6 +433,7 @@ struct ubi_device {
void *peb_buf2;
struct mutex buf_mutex;
struct mutex ckvol_mutex;
struct mutex mult_mutex;
#ifdef CONFIG_MTD_UBI_DEBUG
void *dbg_peb_buf;
struct mutex dbg_buf_mutex;
Expand All @@ -426,12 +450,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,
struct list_head *rename_list);
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_remove_volume(struct ubi_volume_desc *desc, int no_vtbl);
int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs);
int ubi_rename_volumes(struct ubi_device *ubi, struct list_head *rename_list);
int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol);
void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol);

Expand Down
57 changes: 49 additions & 8 deletions drivers/mtd/ubi/vmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -402,13 +402,14 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
/**
* ubi_remove_volume - remove volume.
* @desc: volume descriptor
* @no_vtbl: do not change volume table if not zero
*
* This function removes volume described by @desc. The volume has to be opened
* in "exclusive" mode. Returns zero in case of success and a negative error
* code in case of failure. The caller has to have the @ubi->volumes_mutex
* locked.
*/
int ubi_remove_volume(struct ubi_volume_desc *desc)
int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl)
{
struct ubi_volume *vol = desc->vol;
struct ubi_device *ubi = vol->ubi;
Expand Down Expand Up @@ -437,9 +438,11 @@ int ubi_remove_volume(struct ubi_volume_desc *desc)
if (err)
goto out_err;

err = ubi_change_vtbl_record(ubi, vol_id, NULL);
if (err)
goto out_err;
if (!no_vtbl) {
err = ubi_change_vtbl_record(ubi, vol_id, NULL);
if (err)
goto out_err;
}

for (i = 0; i < vol->reserved_pebs; i++) {
err = ubi_eba_unmap_leb(ubi, vol, i);
Expand All @@ -465,7 +468,8 @@ int ubi_remove_volume(struct ubi_volume_desc *desc)
ubi->vol_count -= 1;
spin_unlock(&ubi->volumes_lock);

err = paranoid_check_volumes(ubi);
if (!no_vtbl)
err = paranoid_check_volumes(ubi);
return err;

out_err:
Expand Down Expand Up @@ -601,6 +605,44 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
return err;
}

/**
* ubi_rename_volumes - re-name UBI volumes.
* @ubi: UBI device description object
* @renam_list: list of &struct ubi_rename_entry objects
*
* This function re-names or removes volumes specified in the re-name list.
* Returns zero in case of success and a negative error code in case of
* failure.
*/
int ubi_rename_volumes(struct ubi_device *ubi, struct list_head *rename_list)
{
int err;
struct ubi_rename_entry *re;

err = ubi_vtbl_rename_volumes(ubi, rename_list);
if (err)
return err;

list_for_each_entry(re, rename_list, list) {
if (re->remove) {
err = ubi_remove_volume(re->desc, 1);
if (err)
break;
} else {
struct ubi_volume *vol = re->desc->vol;

spin_lock(&ubi->volumes_lock);
vol->name_len = re->new_name_len;
memcpy(vol->name, re->new_name, re->new_name_len + 1);
spin_unlock(&ubi->volumes_lock);
}
}

if (!err)
paranoid_check_volumes(ubi);
return err;
}

/**
* ubi_add_volume - add volume.
* @ubi: UBI device description object
Expand Down Expand Up @@ -826,10 +868,9 @@ static int paranoid_check_volume(struct ubi_device *ubi, int vol_id)

fail:
ubi_err("paranoid check failed for volume %d", vol_id);
if (vol) {
if (vol)
ubi_dbg_dump_vol_info(vol);
ubi_dbg_dump_vtbl_record(&ubi->vtbl[vol_id], vol_id);
}
ubi_dbg_dump_vtbl_record(&ubi->vtbl[vol_id], vol_id);
spin_unlock(&ubi->volumes_lock);
return -EINVAL;
}
Expand Down
Loading

0 comments on commit f40ac9c

Please sign in to comment.