Index: geom_raid.c =================================================================== --- sbin/geom/class/raid/geom_raid.c (revision 311924) +++ sbin/geom/class/raid/geom_raid.c (working copy) @@ -87,6 +87,13 @@ struct g_command class_commands[] = { }, "[-fv] name" }, + { "resize", G_FLAG_VERBOSE, NULL, + { + { 'S', "size", G_VAL_OPTIONAL, G_TYPE_NUMBER }, + G_OPT_SENTINEL + }, + "[-v] [-S size] name" + }, G_CMD_SENTINEL }; Index: g_raid.c =================================================================== --- sys/geom/raid/g_raid.c (revision 311924) +++ sys/geom/raid/g_raid.c (working copy) @@ -261,6 +261,8 @@ g_raid_volume_event2str(int event) return ("START"); case G_RAID_VOLUME_E_STARTMD: return ("STARTMD"); + case G_RAID_VOLUME_E_RESIZED: + return ("RESIZED"); default: return ("INVALID"); } @@ -1736,6 +1738,7 @@ g_raid_update_volume(struct g_raid_volume *vol, u_ g_raid_launch_provider(vol); break; case G_RAID_VOLUME_E_START: + case G_RAID_VOLUME_E_RESIZED: if (vol->v_tr) G_RAID_TR_START(vol->v_tr); return (0); Index: g_raid.h =================================================================== --- sys/geom/raid/g_raid.h (revision 311924) +++ sys/geom/raid/g_raid.h (working copy) @@ -218,6 +218,7 @@ struct g_raid_subdisk { #define G_RAID_VOLUME_E_UP 0x01 #define G_RAID_VOLUME_E_START 0x10 #define G_RAID_VOLUME_E_STARTMD 0x11 +#define G_RAID_VOLUME_E_RESIZED 0x12 #define G_RAID_VOLUME_RL_RAID0 0x00 #define G_RAID_VOLUME_RL_RAID1 0x01 Index: md_intel.c =================================================================== --- sys/geom/raid/md_intel.c (revision 311924) +++ sys/geom/raid/md_intel.c (working copy) @@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include "geom/raid/g_raid.h" #include "g_raid_md_if.h" @@ -1680,6 +1681,31 @@ g_raid_md_event_intel(struct g_raid_md_object *md, return (-2); } +/* Returns maximum size a subdisk can extend to. + * Bounds are other subdisks of the same disk + * and reserved area at the end of disk. + */ +static off_t +g_raid_subdisk_cangrowto(struct g_raid_subdisk *sdisk, off_t reserved) +{ + struct g_raid_subdisk *sd; + off_t offset, tmp; + + /* Begin from the end of containing disk */ + tmp = 512 * (intel_get_disk_sectors( +&(((struct g_raid_md_intel_perdisk *)sdisk->sd_disk->d_md_data))->pd_disk_meta)) + - reserved; + + /* Find nearest next subdisk, if any */ + offset = sdisk->sd_offset; + TAILQ_FOREACH(sd, &sdisk->sd_disk->d_subdisks, sd_next) { + if (sd->sd_offset > offset && sd->sd_offset < tmp) + tmp = sd->sd_offset; + } + return (tmp - offset); +} + + static int g_raid_md_ctl_intel(struct g_raid_md_object *md, struct gctl_req *req) @@ -2337,6 +2363,165 @@ g_raid_md_ctl_intel(struct g_raid_md_object *md, g_raid_md_write_intel(md, NULL, NULL, NULL); return (error); } + if (strcmp(verb, "resize") == 0) { + if (*nargs > 1) { + gctl_error(req, "Invalid number of arguments."); + return (-1); + } + + volname = gctl_get_asciiparam(req, "arg0"); + if (volname == NULL) { + gctl_error(req, "No volume name."); + return (-2); + } + + /* Search for volume. */ + TAILQ_FOREACH(vol, &sc->sc_volumes, v_next) { + if (strcmp(vol->v_name, volname) == 0) + break; + pp = vol->v_provider; + if (pp == NULL) + continue; + if (strcmp(pp->name, volname) == 0) + break; + if (strncmp(pp->name, "raid/", 5) == 0 && + strcmp(pp->name + 5, volname) == 0) + break; + } + if (vol == NULL) { + i = strtol(volname, &tmp, 10); + if (tmp[0] == 0) { + TAILQ_FOREACH(vol, &sc->sc_volumes, v_next) { + if (vol->v_global_id == i) + break; + } + } + } + if (vol == NULL) { + gctl_error(req, "Volume '%s' not found.", volname); + return (-3); + } + + if (vol->v_raid_level != G_RAID_VOLUME_RL_RAID1) { + gctl_error(req, "Unsupported RAID level " + "'%s' for resize.", + g_raid_volume_level2str(vol->v_raid_level, + vol->v_raid_level_qualifier)); + return (-5); + } + + numdisks = vol->v_disks_count; + if (numdisks == 0) { + gctl_error(req, "Volume '%s' has no disks.", + vol->v_name); + return (-6); + } + + sectorsize = vol->v_sectorsize; + + /* Prevent resize of volume being in unstable state. */ + for (i = 0; i < numdisks; i++) { + if (vol->v_subdisks[i].sd_state != + G_RAID_SUBDISK_S_ACTIVE) { + gctl_error(req, + "Device %s is busy: subdisk %d not active", + vol->v_name, i); + return (-4); + } + } + + /* Handle size argument. */ + size = 0; + len = sizeof(*sizearg); + sizearg = gctl_get_param(req, "size", &len); + + if (sizearg != NULL && len == sizeof(*sizearg)) + size = *sizearg; + + if (vol->v_mediasize == size) + return (0); + + /* + * If new size not specified, compute growth minimax + * for all subdisks of the volume. + * If new size is specified to grow the volume, verify it. + */ + strip = -1; + if (size == 0 || vol->v_mediasize < size) { + for (i = 0; i < numdisks; i++) { + off = g_raid_subdisk_cangrowto(&vol->v_subdisks[i], 4096); + G_RAID_DEBUG1(1, sc, "resize volume %s: " + "subdisk %d can grow from %llu to %llu", + vol->v_name, i, + (unsigned long long)(vol->v_subdisks[i].sd_size), + (unsigned long long)off); + if (size > off) { + gctl_error(req, "Size too big: %lld > %lld.", + (long long)size, (long long)off); + return (-14); + } + if (strip > off || strip == -1) + strip = off; + } + if (size == 0) + size = strip; + } + + /* Round size down to sector. */ + size -= (size % sectorsize); + + if (size <= 0) { + gctl_error(req, "Size too small: %lld.", + (long long)size); + return (-13); + } + + if (size > 0xffffffffllu * sectorsize) { + gctl_error(req, "Size too big: %lld.", + (long long)size); + return (-14); + } + + /* Deny shrinking of an opened provider. */ + if ((g_debugflags & 16) == 0 && + vol->v_provider_open > 0 && vol->v_mediasize > size) { + gctl_error(req, "Device %s is busy.", vol->v_name); + return (-4); + } + + G_RAID_DEBUG1(1, sc, "resize volume %s: " + "old size %lld, new size %lld", vol->v_name, + (long long)vol->v_mediasize, (long long)size); + + /* XXX Locking? */ + vol->v_mediasize = size; + for (i = 0; i < numdisks; i++) { + sd = &vol->v_subdisks[i]; + if (sd->sd_size < size && i != 0) { + /* Grow a subdisk and mark it for rebuild. */ + sd->sd_rebuild_pos = sd->sd_size; + sd->sd_size = size; + g_raid_change_subdisk_state(sd, + G_RAID_SUBDISK_S_REBUILD); + } + else + sd->sd_size = size; + } + + /* Write updated metadata. */ + g_raid_md_write_intel(md, NULL, NULL, NULL); + + /* Update provider size. */ + g_topology_lock(); + g_resize_provider(vol->v_provider, size); + g_topology_unlock(); + + /* Start rebuild of growed subdisks, if any. */ + g_raid_event_send(vol, G_RAID_VOLUME_E_RESIZED, + G_RAID_EVENT_VOLUME); + + return (0); + } return (-100); }