update_rename_array,
update_add_remove_disk,
update_reshape_container_disks,
+ update_reshape_migration,
update_takeover
};
enum imsm_update_type type;
int old_raid_disks;
int new_raid_disks;
+
+ int new_disks[1]; /* new_raid_disks - old_raid_disks makedev number */
+};
+
+struct imsm_update_reshape_migration {
+ enum imsm_update_type type;
+ int old_raid_disks;
+ int new_raid_disks;
+ /* fields for array migration changes
+ */
+ int subdev;
+ int new_level;
+ int new_layout;
+ int new_chunksize;
+
int new_disks[1]; /* new_raid_disks - old_raid_disks makedev number */
};
return _sys_dev_type[type];
}
-#ifndef MDASSEMBLE
static struct intel_hba * alloc_intel_hba(struct sys_dev *device)
{
struct intel_hba *result = malloc(sizeof(*result));
return result;
}
-
static int attach_hba_to_super(struct intel_super *super, struct sys_dev *device)
{
struct intel_hba *hba;
return NULL;
}
-#endif /* MDASSEMBLE */
+static int find_intel_hba_capability(int fd, struct intel_super *super,
+ char *devname);
+
static struct supertype *match_metadata_desc_imsm(char *arg)
{
struct supertype *st;
migr_chunk = migr_strip_blocks_resync(dev);
disks = imsm_num_data_members(dev, 0);
blocks_per_unit = stripes_per_unit * migr_chunk * disks;
- if (migr_type(dev) == MIGR_GEN_MIGR)
- return blocks_per_unit;
stripe = __le32_to_cpu(map->blocks_per_strip) * disks;
segment = blocks_per_unit / stripe;
block_rel = blocks_per_unit - segment * stripe;
struct imsm_map *map_to_analyse = map;
struct dl *dl;
char *devname;
+ unsigned int component_size_alligment;
int map_disks = info->array.raid_disks;
if (prev_map)
if (dl->raiddisk == info->disk.raid_disk)
break;
info->container_member = super->current_vol;
- info->array.raid_disks = map_to_analyse->num_members;
+ info->array.raid_disks = map->num_members;
info->array.level = get_imsm_raid_level(map_to_analyse);
info->array.layout = imsm_level_to_layout(info->array.level);
info->array.md_minor = -1;
info->data_offset = __le32_to_cpu(map_to_analyse->pba_of_lba0);
info->component_size =
__le32_to_cpu(map_to_analyse->blocks_per_member);
+
+ /* check component size aligment
+ */
+ component_size_alligment =
+ info->component_size % (info->array.chunk_size/512);
+
+ if (component_size_alligment &&
+ (info->array.level != 1) && (info->array.level != UnSet)) {
+ dprintf("imsm: reported component size alligned from %llu ",
+ info->component_size);
+ info->component_size -= component_size_alligment;
+ dprintf("to %llu (%i).\n",
+ info->component_size, component_size_alligment);
+ }
+
memset(info->uuid, 0, sizeof(info->uuid));
info->recovery_start = MaxSector;
int used_disks;
info->reshape_progress = blocks_per_unit * units;
+
+ /* checkpoint is written per disks unit
+ * recalculate it to reshape position
+ */
+ used_disks = imsm_num_data_members(dev, 0);
+ info->reshape_progress *= used_disks;
dprintf("IMSM: General Migration checkpoint : %llu "
"(%llu) -> read reshape progress : %llu\n",
units, blocks_per_unit, info->reshape_progress);
* use the same Intel hba
*/
if (!check_env("IMSM_NO_PLATFORM")) {
- if (first->hba->type != sec->hba->type) {
+ if (!first->hba || !sec->hba ||
+ (first->hba->type != sec->hba->type)) {
fprintf(stderr,
"HBAs of devices does not match %s != %s\n",
- get_sys_dev_type(first->hba->type),
- get_sys_dev_type(sec->hba->type));
+ first->hba ? get_sys_dev_type(first->hba->type) : NULL,
+ sec->hba ? get_sys_dev_type(sec->hba->type) : NULL);
return 3;
}
}
static void __free_imsm(struct intel_super *super, int free_disks);
/* load_imsm_mpb - read matrix metadata
- * allocates super->mpb to be freed by free_super
+ * allocates super->mpb to be freed by free_imsm
*/
static int load_imsm_mpb(int fd, struct intel_super *super, char *devname)
{
}
__free_imsm(super, 0);
+ /* reload capability and hba */
+
+ /* capability and hba must be updated with new super allocation */
+ find_intel_hba_capability(fd, super, devname);
super->len = ROUND_UP(anchor->mpb_size, 512);
if (posix_memalign(&super->buf, 512, super->len) != 0) {
if (devname)
free(super->buf);
super->buf = NULL;
}
+ /* unlink capability description */
+ super->orom = NULL;
if (free_disks)
free_imsm_disks(super);
free_devlist(super);
memset(super, 0, sizeof(*super));
super->current_vol = -1;
super->create_offset = ~((__u32 ) 0);
- if (!check_env("IMSM_NO_PLATFORM"))
- super->orom = find_imsm_orom();
}
-
return super;
}
-
/*
* find and allocate hba and OROM/EFI based on valid fd of RAID component device
*/
-static int find_intel_hba_capability(int fd, struct intel_super *super, int verbose)
+static int find_intel_hba_capability(int fd, struct intel_super *super, char *devname)
{
struct sys_dev *hba_name;
int rv = 0;
if ((fd < 0) || check_env("IMSM_NO_PLATFORM")) {
+ super->orom = NULL;
super->hba = NULL;
return 0;
}
hba_name = find_disk_attached_hba(fd, NULL);
if (!hba_name) {
- if (verbose) {
- char str[256];
-
- fd2devname(fd, str);
+ if (devname)
fprintf(stderr,
Name ": %s is not attached to Intel(R) RAID controller.\n",
- str);
- }
+ devname);
return 1;
}
rv = attach_hba_to_super(super, hba_name);
if (rv == 2) {
- if (verbose) {
- char str[256];
+ if (devname) {
+ struct intel_hba *hba = super->hba;
- fd2devname(fd, str);
fprintf(stderr, Name ": %s is attached to Intel(R) %s RAID "
"controller (%s),\n"
" but the container is assigned to Intel(R) "
"%s RAID controller (",
- str,
+ devname,
hba_name->path,
hba_name->pci_id ? : "Err!",
get_sys_dev_type(hba_name->type));
- struct intel_hba *hba = super->hba;
while (hba) {
fprintf(stderr, "%s", hba->pci_id ? : "Err!");
if (hba->next)
free_sys_dev(&hba_name);
return 2;
}
+ super->orom = find_imsm_capability(hba_name->type);
free_sys_dev(&hba_name);
+ if (!super->orom)
+ return 3;
return 0;
}
-
#ifndef MDASSEMBLE
/* find_missing - helper routine for load_super_imsm_all that identifies
* disks that have disappeared from the system. This routine relies on
struct intel_super *s = alloc_super();
char nm[32];
int dfd;
+ int rv;
err = 1;
if (!s)
if (dfd < 0)
goto error;
+ rv = find_intel_hba_capability(dfd, s, devname);
+ /* no orom/efi or non-intel hba of the disk */
+ if (rv != 0)
+ goto error;
+
err = load_and_parse_mpb(dfd, s, NULL, 1);
/* retry the load if we might have raced against mdmon */
sizeof(*super));
return 1;
}
-
+ /* Load hba and capabilities if they exist.
+ * But do not preclude loading metadata in case capabilities or hba are
+ * non-compliant and ignore_hw_compat is set.
+ */
+ rv = find_intel_hba_capability(fd, super, devname);
+ /* no orom/efi or non-intel hba of the disk */
+ if ((rv != 0) && (st->ignore_hw_compat == 0)) {
+ if (devname)
+ fprintf(stderr,
+ Name ": No OROM/EFI properties for %s\n", devname);
+ free_imsm(super);
+ return 2;
+ }
rv = load_and_parse_mpb(fd, super, devname, 0);
if (rv) {
* We do not need to test disks attachment for container based additions,
* they shall be already tested when container was created/assembled.
*/
- rv = find_intel_hba_capability(fd, super, 1);
- /* no intel hba of the disk */
+ rv = find_intel_hba_capability(fd, super, devname);
+ /* no orom/efi or non-intel hba of the disk */
if (rv != 0) {
dprintf("capability: %p fd: %d ret: %d\n",
super->orom, fd, rv);
} else {
dd->next = super->disks;
super->disks = dd;
+ super->updates_pending++;
}
return 0;
{
int fd;
unsigned long long ldsize;
- const struct imsm_orom *orom;
+ struct intel_super *super=NULL;
+ int rv = 0;
if (level != LEVEL_CONTAINER)
return 0;
if (!dev)
return 1;
- if (check_env("IMSM_NO_PLATFORM"))
- orom = NULL;
- else
- orom = find_imsm_orom();
- if (orom && raiddisks > orom->tds) {
- if (verbose)
- fprintf(stderr, Name ": %d exceeds maximum number of"
- " platform supported disks: %d\n",
- raiddisks, orom->tds);
- return 0;
- }
-
fd = open(dev, O_RDONLY|O_EXCL, 0);
if (fd < 0) {
if (verbose)
close(fd);
return 0;
}
+
+ /* capabilities retrieve could be possible
+ * note that there is no fd for the disks in array.
+ */
+ super = alloc_super();
+ if (!super) {
+ fprintf(stderr,
+ Name ": malloc of %zu failed.\n",
+ sizeof(*super));
+ close(fd);
+ return 0;
+ }
+
+ rv = find_intel_hba_capability(fd, super, verbose ? dev : NULL);
+ if (rv != 0) {
+#if DEBUG
+ char str[256];
+ fd2devname(fd, str);
+ dprintf("validate_geometry_imsm_container: fd: %d %s orom: %p rv: %d raiddisk: %d\n",
+ fd, str, super->orom, rv, raiddisks);
+#endif
+ /* no orom/efi or non-intel hba of the disk */
+ close(fd);
+ free_imsm(super);
+ return 0;
+ }
close(fd);
+ if (super->orom && raiddisks > super->orom->tds) {
+ if (verbose)
+ fprintf(stderr, Name ": %d exceeds maximum number of"
+ " platform supported disks: %d\n",
+ raiddisks, super->orom->tds);
+
+ free_imsm(super);
+ return 0;
+ }
*freesize = avail_size_imsm(st, ldsize >> 9);
+ free_imsm(super);
return 1;
}
*/
chunk = __le16_to_cpu(map->blocks_per_strip) >> 1;
+#ifndef MDASSEMBLE
if (!validate_geometry_imsm_orom(super,
get_imsm_raid_level(map), /* RAID level */
imsm_level_to_layout(get_imsm_raid_level(map)),
"Cannot proceed with the action(s).\n");
continue;
}
+#endif /* MDASSEMBLE */
this = malloc(sizeof(*this));
if (!this) {
fprintf(stderr, Name ": failed to allocate %zu bytes\n",
__u32 ord;
int slot;
struct imsm_map *map;
- char buf[MAX_RAID_SERIAL_LEN+3];
- unsigned int len, shift = 0;
/* new failures are always set in map[0] */
map = get_imsm_map(dev, 0);
if (is_failed(disk) && (ord & IMSM_ORD_REBUILD))
return 0;
- sprintf(buf, "%s:0", disk->serial);
- if ((len = strlen(buf)) >= MAX_RAID_SERIAL_LEN)
- shift = len - MAX_RAID_SERIAL_LEN + 1;
- strncpy((char *)disk->serial, &buf[shift], MAX_RAID_SERIAL_LEN);
-
disk->status |= FAILED_DISK;
set_imsm_ord_tbl_ent(map, slot, idx | IMSM_ORD_REBUILD);
if (map->failed_disk_num == 0xff)
* curr_migr_unit needs updating
*/
if (units32 == units &&
+ units32 != 0 &&
__le32_to_cpu(dev->vol.curr_migr_unit) != units32) {
dprintf("imsm: mark checkpoint (%u)\n", units32);
dev->vol.curr_migr_unit = __cpu_to_le32(units32);
return check_degraded;
}
+
+static int apply_reshape_migration_update(struct imsm_update_reshape_migration *u,
+ struct intel_super *super,
+ void ***space_list)
+{
+ struct intel_dev *id;
+ void **tofree = NULL;
+ int ret_val = 0;
+
+ dprintf("apply_reshape_migration_update()\n");
+ if ((u->subdev < 0) ||
+ (u->subdev > 1)) {
+ dprintf("imsm: Error: Wrong subdev: %i\n", u->subdev);
+ return ret_val;
+ }
+ if ((space_list == NULL) || (*space_list == NULL)) {
+ dprintf("imsm: Error: Memory is not allocated\n");
+ return ret_val;
+ }
+
+ for (id = super->devlist ; id; id = id->next) {
+ if (id->index == (unsigned)u->subdev) {
+ struct imsm_dev *dev = get_imsm_dev(super, u->subdev);
+ struct imsm_map *map;
+ struct imsm_dev *new_dev =
+ (struct imsm_dev *)*space_list;
+ struct imsm_map *migr_map = get_imsm_map(dev, 1);
+ int to_state;
+ struct dl *new_disk;
+
+ if (new_dev == NULL)
+ return ret_val;
+ *space_list = **space_list;
+ memcpy(new_dev, dev, sizeof_imsm_dev(dev, 0));
+ map = get_imsm_map(new_dev, 0);
+ if (migr_map) {
+ dprintf("imsm: Error: migration in progress");
+ return ret_val;
+ }
+
+ to_state = map->map_state;
+ if ((u->new_level == 5) && (map->raid_level == 0)) {
+ map->num_members++;
+ /* this should not happen */
+ if (u->new_disks[0] < 0) {
+ map->failed_disk_num =
+ map->num_members - 1;
+ to_state = IMSM_T_STATE_DEGRADED;
+ } else
+ to_state = IMSM_T_STATE_NORMAL;
+ }
+ migrate(new_dev, to_state, MIGR_GEN_MIGR);
+ if (u->new_level > -1)
+ map->raid_level = u->new_level;
+ migr_map = get_imsm_map(new_dev, 1);
+ if ((u->new_level == 5) &&
+ (migr_map->raid_level == 0)) {
+ int ord = map->num_members - 1;
+ migr_map->num_members--;
+ if (u->new_disks[0] < 0)
+ ord |= IMSM_ORD_REBUILD;
+ set_imsm_ord_tbl_ent(map,
+ map->num_members - 1,
+ ord);
+ }
+ id->dev = new_dev;
+ tofree = (void **)dev;
+
+ /* update chunk size
+ */
+ if (u->new_chunksize > 0)
+ map->blocks_per_strip =
+ __cpu_to_le16(u->new_chunksize * 2);
+
+ /* add disk
+ */
+ if ((u->new_level != 5) ||
+ (migr_map->raid_level != 0) ||
+ (migr_map->raid_level == map->raid_level))
+ goto skip_disk_add;
+
+ if (u->new_disks[0] >= 0) {
+ /* use passes spare
+ */
+ new_disk = get_disk_super(super,
+ major(u->new_disks[0]),
+ minor(u->new_disks[0]));
+ dprintf("imsm: new disk for reshape is: %i:%i "
+ "(%p, index = %i)\n",
+ major(u->new_disks[0]),
+ minor(u->new_disks[0]),
+ new_disk, new_disk->index);
+ if (new_disk == NULL)
+ goto error_disk_add;
+
+ new_disk->index = map->num_members - 1;
+ /* slot to fill in autolayout
+ */
+ new_disk->raiddisk = new_disk->index;
+ new_disk->disk.status |= CONFIGURED_DISK;
+ new_disk->disk.status &= ~SPARE_DISK;
+ } else
+ goto error_disk_add;
+
+skip_disk_add:
+ *tofree = *space_list;
+ /* calculate new size
+ */
+ imsm_set_array_size(new_dev);
+
+ ret_val = 1;
+ }
+ }
+
+ if (tofree)
+ *space_list = tofree;
+ return ret_val;
+
+error_disk_add:
+ dprintf("Error: imsm: Cannot find disk.\n");
+ return ret_val;
+}
+
+
static int apply_reshape_container_disks_update(struct imsm_update_reshape *u,
struct intel_super *super,
void ***space_list)
super->updates_pending++;
break;
}
+ case update_reshape_migration: {
+ struct imsm_update_reshape_migration *u = (void *)update->buf;
+ if (apply_reshape_migration_update(
+ u, super, &update->space_list))
+ super->updates_pending++;
+ break;
+ }
case update_activate_spare: {
struct imsm_update_activate_spare *u = (void *) update->buf;
struct imsm_dev *dev = get_imsm_dev(super, u->array);
}
super->updates_pending++;
-
/* count failures (excluding rebuilds and the victim)
* to determine map[0] state
*/
}
}
+static struct mdinfo *get_spares_for_grow(struct supertype *st);
+
static void imsm_prepare_update(struct supertype *st,
struct metadata_update *update)
{
dprintf("New anchor length is %llu\n", (unsigned long long)len);
break;
}
+ case update_reshape_migration: {
+ /* for migration level 0->5 we need to add disks
+ * so the same as for container operation we will copy
+ * device to the bigger location.
+ * in memory prepared device and new disk area are prepared
+ * for usage in process update
+ */
+ struct imsm_update_reshape_migration *u = (void *)update->buf;
+ struct intel_dev *id;
+ void **space_tail = (void **)&update->space_list;
+ int size;
+ void *s;
+ int current_level = -1;
+
+ dprintf("imsm: imsm_prepare_update() for update_reshape\n");
+
+ /* add space for bigger array in update
+ */
+ for (id = super->devlist; id; id = id->next) {
+ if (id->index == (unsigned)u->subdev) {
+ size = sizeof_imsm_dev(id->dev, 1);
+ if (u->new_raid_disks > u->old_raid_disks)
+ size += sizeof(__u32)*2*
+ (u->new_raid_disks - u->old_raid_disks);
+ s = malloc(size);
+ if (!s)
+ break;
+ *space_tail = s;
+ space_tail = s;
+ *space_tail = NULL;
+ break;
+ }
+ }
+ if (update->space_list == NULL)
+ break;
+
+ /* add space for disk in update
+ */
+ size = sizeof(struct dl);
+ s = malloc(size);
+ if (!s) {
+ free(update->space_list);
+ update->space_list = NULL;
+ break;
+ }
+ *space_tail = s;
+ space_tail = s;
+ *space_tail = NULL;
+
+ /* add spare device to update
+ */
+ for (id = super->devlist ; id; id = id->next)
+ if (id->index == (unsigned)u->subdev) {
+ struct imsm_dev *dev;
+ struct imsm_map *map;
+
+ dev = get_imsm_dev(super, u->subdev);
+ map = get_imsm_map(dev, 0);
+ current_level = map->raid_level;
+ break;
+ }
+ if ((u->new_level == 5) && (u->new_level != current_level)) {
+ struct mdinfo *spares;
+
+ spares = get_spares_for_grow(st);
+ if (spares) {
+ struct dl *dl;
+ struct mdinfo *dev;
+
+ dev = spares->devs;
+ if (dev) {
+ u->new_disks[0] =
+ makedev(dev->disk.major,
+ dev->disk.minor);
+ dl = get_disk_super(super,
+ dev->disk.major,
+ dev->disk.minor);
+ dl->index = u->old_raid_disks;
+ dev = dev->next;
+ }
+ sysfs_free(spares);
+ }
+ }
+ len = disks_to_mpb_size(u->new_raid_disks);
+ dprintf("New anchor length is %llu\n", (unsigned long long)len);
+ break;
+ }
case update_create_array: {
struct imsm_update_create_array *u = (void *) update->buf;
struct intel_dev *dv;
geo->raid_disks > 1 ? "s" : "");
break;
}
+ /* check if component size is aligned to chunk size
+ */
+ if (info->component_size %
+ (info->array.chunk_size/512)) {
+ dprintf("Component size is not aligned to "
+ "chunk size\n");
+ break;
+ }
}
if (*old_raid_disks &&
return 0;
}
+/******************************************************************************
+ * function: imsm_create_metadata_update_for_migration()
+ * Creates update for IMSM array.
+ *
+ ******************************************************************************/
+static int imsm_create_metadata_update_for_migration(
+ struct supertype *st,
+ struct geo_params *geo,
+ struct imsm_update_reshape_migration **updatep)
+{
+ struct intel_super *super = st->sb;
+ int update_memory_size = 0;
+ struct imsm_update_reshape_migration *u = NULL;
+ struct imsm_dev *dev;
+ int previous_level = -1;
+
+ dprintf("imsm_create_metadata_update_for_migration(enter)"
+ " New Level = %i\n", geo->level);
+
+ /* size of all update data without anchor */
+ update_memory_size = sizeof(struct imsm_update_reshape_migration);
+
+ u = calloc(1, update_memory_size);
+ if (u == NULL) {
+ dprintf("error: cannot get memory for "
+ "imsm_create_metadata_update_for_migration\n");
+ return 0;
+ }
+ u->type = update_reshape_migration;
+ u->subdev = super->current_vol;
+ u->new_level = geo->level;
+ u->new_layout = geo->layout;
+ u->new_raid_disks = u->old_raid_disks = geo->raid_disks;
+ u->new_disks[0] = -1;
+ u->new_chunksize = -1;
+
+ dev = get_imsm_dev(super, u->subdev);
+ if (dev) {
+ struct imsm_map *map;
+
+ map = get_imsm_map(dev, 0);
+ if (map) {
+ int current_chunk_size =
+ __le16_to_cpu(map->blocks_per_strip) / 2;
+
+ if (geo->chunksize != current_chunk_size) {
+ u->new_chunksize = geo->chunksize / 1024;
+ dprintf("imsm: "
+ "chunk size change from %i to %i\n",
+ current_chunk_size, u->new_chunksize);
+ }
+ previous_level = map->raid_level;
+ }
+ }
+ if ((geo->level == 5) && (previous_level == 0)) {
+ struct mdinfo *spares = NULL;
+
+ u->new_raid_disks++;
+ spares = get_spares_for_grow(st);
+ if ((spares == NULL) || (spares->array.spare_disks < 1)) {
+ free(u);
+ sysfs_free(spares);
+ update_memory_size = 0;
+ dprintf("error: cannot get spare device "
+ "for requested migration");
+ return 0;
+ }
+ sysfs_free(spares);
+ }
+ dprintf("imsm: reshape update preparation : OK\n");
+ *updatep = u;
+
+ return update_memory_size;
+}
+
static void imsm_update_metadata_locally(struct supertype *st,
void *buf, int len)
{
check_devs = 1;
}
break;
- case 5:
- if (geo->level == 0)
- change = CH_MIGRATION;
- break;
case 10:
if (geo->level == 0) {
change = CH_TAKEOVER;
return 0;
}
+static int warn_user_about_risk(void)
+{
+ int rv = 0;
+
+ fprintf(stderr,
+ "\nThis is an experimental feature. Data on the RAID volume(s) "
+ "can be lost!!!\n\n"
+ "To continue command execution please make sure that\n"
+ "the grow process will not be interrupted. Use safe power\n"
+ "supply to avoid unexpected system reboot. Make sure that\n"
+ "reshaped container is not assembled automatically during\n"
+ "system boot.\n"
+ "If reshape is interrupted, assemble array manually\n"
+ "using e.g. '-Ac' option and up to date mdadm.conf file.\n"
+ "Assembly in scan mode is not possible in such case.\n"
+ "Growing container with boot array is not possible.\n"
+ "If boot array reshape is interrupted, whole file system\n"
+ "can be lost.\n\n");
+ rv = ask("Do you want to continue? ");
+ fprintf(stderr, "\n");
+
+ return rv;
+}
+
static int imsm_reshape_super(struct supertype *st, long long size, int level,
int layout, int chunksize, int raid_disks,
int delta_disks, char *backup, char *dev,
/* On container level we can only increase number of devices. */
dprintf("imsm: info: Container operation\n");
int old_raid_disks = 0;
+
+ /* this warning will be removed when imsm checkpointing
+ * will be implemented, and restoring from check-point
+ * operation will be transparent for reboot process
+ */
+ if (warn_user_about_risk() == 0)
+ return ret_val;
+
if (imsm_reshape_is_allowed_on_container(
st, &geo, &old_raid_disks)) {
struct imsm_update_reshape *u = NULL;
case CH_TAKEOVER:
ret_val = imsm_takeover(st, &geo);
break;
- case CH_MIGRATION:
+ case CH_MIGRATION: {
+ struct imsm_update_reshape_migration *u = NULL;
+ int len =
+ imsm_create_metadata_update_for_migration(
+ st, &geo, &u);
+ if (len < 1) {
+ dprintf("imsm: "
+ "Cannot prepare update\n");
+ break;
+ }
ret_val = 0;
- break;
+ /* update metadata locally */
+ imsm_update_metadata_locally(st, u, len);
+ /* and possibly remotely */
+ if (st->update_tail)
+ append_metadata_update(st, u, len);
+ else
+ free(u);
+ }
+ break;
default:
ret_val = 1;
}