case 6:
case 0:
if (chunk == 0) {
- chunk = 512;
+ if (st && st->ss->default_chunk)
+ chunk = st->ss->default_chunk(st);
+
+ chunk = chunk ? : 512;
+
if (verbose > 0)
- fprintf(stderr, Name ": chunk size defaults to 512K\n");
+ fprintf(stderr, Name ": chunk size defaults to %dK\n", chunk);
}
break;
case LEVEL_LINEAR:
close(fd);
return rv;
}
+
+int Kill_subarray(char *dev, char *subarray, int quiet)
+{
+ /* Delete a subarray out of a container, the subarry must be
+ * inactive. The subarray string must be a subarray index
+ * number.
+ *
+ * 0 = successfully deleted subarray from all container members
+ * 1 = failed to sync metadata to one or more devices
+ * 2 = failed to find the container, subarray, or other resource
+ * issue
+ */
+ struct supertype supertype, *st = &supertype;
+ int fd, rv = 2;
+
+ memset(st, 0, sizeof(*st));
+
+ if (snprintf(st->subarray, sizeof(st->subarray), "%s", subarray) >=
+ sizeof(st->subarray)) {
+ if (!quiet)
+ fprintf(stderr,
+ Name ": Input overflow for subarray '%s' > %zu bytes\n",
+ subarray, sizeof(st->subarray) - 1);
+ return 2;
+ }
+
+ fd = open_subarray(dev, st, quiet);
+ if (fd < 0)
+ return 2;
+
+ if (!st->ss->kill_subarray) {
+ if (!quiet)
+ fprintf(stderr,
+ Name ": Operation not supported for %s metadata\n",
+ st->ss->name);
+ goto free_super;
+ }
+
+ if (is_subarray_active(subarray, st->devname)) {
+ if (!quiet)
+ fprintf(stderr,
+ Name ": Subarray-%s still active, aborting\n",
+ subarray);
+ goto free_super;
+ }
+
+ if (mdmon_running(st->devnum))
+ st->update_tail = &st->updates;
+
+ /* ok we've found our victim, drop the axe */
+ rv = st->ss->kill_subarray(st);
+ if (rv) {
+ if (!quiet)
+ fprintf(stderr,
+ Name ": Failed to delete subarray-%s from %s\n",
+ subarray, dev);
+ goto free_super;
+ }
+
+ /* FIXME these routines do not report success/failure */
+ if (st->update_tail)
+ flush_metadata_updates(st);
+ else
+ st->ss->sync_metadata(st);
+
+ if (!quiet)
+ fprintf(stderr,
+ Name ": Deleted subarray-%s from %s, UUIDs may have changed\n",
+ subarray, dev);
+
+ rv = 0;
+
+ free_super:
+ st->ss->free_super(st);
+ close(fd);
+
+ return rv;
+}
}
return rv;
}
+
+int Update_subarray(char *dev, char *subarray, char *update, mddev_ident_t ident, int quiet)
+{
+ struct supertype supertype, *st = &supertype;
+ int fd, rv = 2;
+
+ memset(st, 0, sizeof(*st));
+ if (snprintf(st->subarray, sizeof(st->subarray), "%s", subarray) >=
+ sizeof(st->subarray)) {
+ if (!quiet)
+ fprintf(stderr,
+ Name ": Input overflow for subarray '%s' > %zu bytes\n",
+ subarray, sizeof(st->subarray) - 1);
+ return 2;
+ }
+
+ fd = open_subarray(dev, st, quiet);
+ if (fd < 0)
+ return 2;
+
+ if (!st->ss->update_subarray) {
+ if (!quiet)
+ fprintf(stderr,
+ Name ": Operation not supported for %s metadata\n",
+ st->ss->name);
+ goto free_super;
+ }
+
+ if (mdmon_running(st->devnum))
+ st->update_tail = &st->updates;
+
+ rv = st->ss->update_subarray(st, update, ident);
+
+ if (rv) {
+ if (!quiet)
+ fprintf(stderr, Name ": Failed to update %s of subarray-%s in %s\n",
+ update, subarray, dev);
+ } else if (st->update_tail)
+ flush_metadata_updates(st);
+ else
+ st->ss->sync_metadata(st);
+
+ if (rv == 0 && strcmp(update, "name") == 0 && !quiet)
+ fprintf(stderr,
+ Name ": Updated subarray-%s name from %s, UUIDs may have changed\n",
+ subarray, dev);
+
+ free_super:
+ st->ss->free_super(st);
+ close(fd);
+
+ return rv;
+}
#endif
{"examine-bitmap", 0, 0, 'X'},
{"auto-detect", 0, 0, AutoDetect},
{"detail-platform", 0, 0, DetailPlatform},
+ {"kill-subarray", 1, 0, KillSubarray},
+ {"update-subarray", 1, 0, UpdateSubarray},
/* synonyms */
{"monitor", 0, 0, 'F'},
new->info.state_fd = sysfs_open(new->devnum, NULL, "array_state");
new->resync_start_fd = sysfs_open(new->devnum, NULL, "resync_start");
new->metadata_fd = sysfs_open(new->devnum, NULL, "metadata_version");
+ new->sync_completed_fd = sysfs_open(new->devnum, NULL, "sync_completed");
dprintf("%s: inst: %d action: %d state: %d\n", __func__, atoi(inst),
new->action_fd, new->info.state_fd);
the block where the superblock would be is overwritten even if it
doesn't appear to be valid.
+.TP
+.B \-\-kill\-subarray=
+If the device is a container and the argument to \-\-kill\-subarray
+specifies an inactive subarray in the container, then the subarray is
+deleted. Deleting all subarrays will leave an 'empty-container' or
+spare superblock on the drives. See \-\-zero\-superblock for completely
+removing a superblock. Note that some formats depend on the subarray
+index for generating a UUID, this command will fail if it would change
+the UUID of an active subarray.
+
+.TP
+.B \-\-update\-subarray=
+If the device is a container and the argument to \-\-update\-subarray
+specifies a subarray in the container, then attempt to update the given
+superblock field in the subarray. See below in
+.B MISC MODE
+for details.
+
.TP
.BR \-t ", " \-\-test
When used with
For each md device given, or each device in /proc/mdstat if
.B \-\-scan
is given, arrange for the array to be marked clean as soon as possible.
-Also, quiesce resync so that the monitor for external metadata arrays
-(mdmon) has an opportunity to checkpoint the resync position.
.I mdadm
will return with success if the array uses external metadata and we
successfully waited. For native arrays this returns immediately as the
-kernel handles both dirty-clean transitions and resync checkpointing in
-the kernel at shutdown. No action is taken if safe-mode handling is
-disabled.
+kernel handles dirty-clean transitions at shutdown. No action is taken
+if safe-mode handling is disabled.
.SH For Incremental Assembly mode:
.TP
metadata failed to find its platform components on this system
.RE
+.TP
+.B \-\-update\-subarray=
+If the device is a container and the argument to \-\-update\-subarray
+specifies a subarray in the container, then attempt to update the given
+superblock field in the subarray. Similar to updating an array in
+"assemble" mode, the field to update is selected by
+.B \-U
+or
+.B \-\-update=
+option. Currently only
+.B name
+is supported.
+
+The
+.B name
+option updates the subarray name in the metadata, it may not affect the
+device node name or the device node symlink until the subarray is
+re\-assembled. If updating
+.B name
+would change the UUID of an active subarray this operation is blocked,
+and the command will end in an error.
+
.TP
.B \-\-examine
The device should be a component of an md array.
int dosyslog = 0;
int rebuild_map = 0;
int auto_update_home = 0;
+ char *subarray = NULL;
int print_help = 0;
FILE *outf;
case 'W':
case Waitclean:
case DetailPlatform:
+ case KillSubarray:
+ case UpdateSubarray:
+ if (opt == KillSubarray || opt == UpdateSubarray) {
+ if (subarray) {
+ fprintf(stderr, Name ": subarray can only be specified once\n");
+ exit(2);
+ }
+ subarray = optarg;
+ }
case 'K': if (!mode) newmode = MISC; break;
}
if (mode && newmode == mode) {
case O(CREATE,'N'):
case O(ASSEMBLE,'N'):
+ case O(MISC,'N'):
if (ident.name[0]) {
fprintf(stderr, Name ": name cannot be set twice. "
"Second value %s.\n", optarg);
exit(2);
}
+ if (mode == MISC && !subarray) {
+ fprintf(stderr, Name ": -N/--name only valid with --update-subarray in misc mode\n");
+ exit(2);
+ }
if (strlen(optarg) > 32) {
fprintf(stderr, Name ": name '%s' is too long, 32 chars max.\n",
optarg);
continue;
case O(ASSEMBLE,'U'): /* update the superblock */
+ case O(MISC,'U'):
if (update) {
fprintf(stderr, Name ": Can only update one aspect of superblock, both %s and %s given.\n",
update, optarg);
exit(2);
}
+ if (mode == MISC && !subarray) {
+ fprintf(stderr, Name ": Only subarrays can be updated in misc mode\n");
+ exit(2);
+ }
update = optarg;
if (strcmp(update, "sparc2.2")==0)
continue;
case O(MISC,'W'):
case O(MISC, Waitclean):
case O(MISC, DetailPlatform):
+ case O(MISC, KillSubarray):
+ case O(MISC, UpdateSubarray):
if (devmode && devmode != opt &&
(devmode == 'E' || (opt == 'E' && devmode != 'Q'))) {
- fprintf(stderr, Name ": --examine/-E cannot be given with -%c\n",
- devmode =='E'?opt:devmode);
+ fprintf(stderr, Name ": --examine/-E cannot be given with ");
+ if (devmode == 'E') {
+ if (option_index >= 0)
+ fprintf(stderr, "--%s\n",
+ long_options[option_index].name);
+ else
+ fprintf(stderr, "-%c\n", opt);
+ } else if (isalpha(devmode))
+ fprintf(stderr, "-%c\n", devmode);
+ else
+ fprintf(stderr, "previous option\n");
exit(2);
}
devmode = opt;
rv |= Wait(dv->devname); continue;
case Waitclean:
rv |= WaitClean(dv->devname, -1, verbose-quiet); continue;
+ case KillSubarray:
+ rv |= Kill_subarray(dv->devname, subarray, quiet);
+ continue;
+ case UpdateSubarray:
+ if (update == NULL) {
+ fprintf(stderr,
+ Name ": -U/--update must be specified with --update-subarray\n");
+ rv |= 1;
+ continue;
+ }
+ rv |= Update_subarray(dv->devname, subarray, update, &ident, quiet);
+ continue;
}
mdfd = open_mddev(dv->devname, 1);
if (mdfd>=0) {
/* for option that don't have short equivilents, we assign arbitrary
* small numbers. '1' means an undecorated option, so we start at '2'.
+ * (note we must stop before we get to 65 i.e. 'A')
*/
enum special_options {
AssumeClean = 2,
ReAdd,
NoDegraded,
Sparc22,
- BackupFile,
+ BackupFile, /* 8 */
HomeHost,
AutoHomeHost,
Symlinks,
AutoDetect,
Waitclean,
DetailPlatform,
+ KillSubarray,
+ UpdateSubarray, /* 16 */
};
/* structures read from config file */
struct mdinfo *(*container_content)(struct supertype *st);
/* Allow a metadata handler to override mdadm's default layouts */
int (*default_layout)(int level); /* optional */
+ /* query the supertype for default chunk size */
+ int (*default_chunk)(struct supertype *st); /* optional */
+ /* Permit subarray's to be deleted from inactive containers */
+ int (*kill_subarray)(struct supertype *st); /* optional */
+ /* Permit subarray's to be modified */
+ int (*update_subarray)(struct supertype *st, char *update, mddev_ident_t ident); /* optional */
/* for mdmon */
int (*open_new)(struct supertype *c, struct active_array *a,
int dosyslog, int test, char *pidfile, int increments);
extern int Kill(char *dev, struct supertype *st, int force, int quiet, int noexcl);
+extern int Kill_subarray(char *dev, char *subarray, int quiet);
+extern int Update_subarray(char *dev, char *subarray, char *update, mddev_ident_t ident, int quiet);
extern int Wait(char *dev);
extern int WaitClean(char *dev, int sock, int verbose);
#define METADATA 3
extern int open_mddev(char *dev, int report_errors);
extern int open_container(int fd);
+extern int is_container_member(struct mdstat_ent *ent, char *devname);
+extern int is_subarray_active(char *subarray, char *devname);
+extern int open_subarray(char *dev, struct supertype *st, int quiet);
+extern struct superswitch *version_to_superswitch(char *vers);
extern char *pid_dir;
extern int mdmon_running(int devnum);
return mon_tid;
}
-static struct superswitch *find_metadata_methods(char *vers)
-{
- if (strcmp(vers, "ddf") == 0)
- return &super_ddf;
- if (strcmp(vers, "imsm") == 0)
- return &super_imsm;
- return NULL;
-}
-
static int make_pidfile(char *devname)
{
char path[100];
return 0;
}
-int is_container_member(struct mdstat_ent *mdstat, char *container)
-{
- if (mdstat->metadata_version == NULL ||
- strncmp(mdstat->metadata_version, "external:", 9) != 0 ||
- !is_subarray(mdstat->metadata_version+9) ||
- strncmp(mdstat->metadata_version+10, container, strlen(container)) != 0 ||
- mdstat->metadata_version[10+strlen(container)] != '/')
- return 0;
-
- return 1;
-}
-
static void try_kill_monitor(pid_t pid, char *devname, int sock)
{
char buf[100];
exit(3);
}
- container->ss = find_metadata_methods(mdi->text_version);
+ container->ss = version_to_superswitch(mdi->text_version);
if (container->ss == NULL) {
- fprintf(stderr, "mdmon: %s uses unknown metadata: %s\n",
+ fprintf(stderr, "mdmon: %s uses unsupported metadata: %s\n",
devname, mdi->text_version);
exit(3);
}
int action_fd;
int resync_start_fd;
int metadata_fd; /* for monitoring rw/ro status */
+ int sync_completed_fd; /* for checkpoint notification events */
+ unsigned long long last_checkpoint; /* sync_completed fires for many
+ * reasons this field makes sure the
+ * kernel has made progress before
+ * moving the checkpoint. It is
+ * cleared by the metadata handler
+ * when it determines recovery is
+ * terminated.
+ */
enum array_state prev_state, curr_state, next_state;
enum sync_action prev_action, curr_action, next_action;
return strtoull(buf, NULL, 10);
}
+static unsigned long long read_sync_completed(int fd)
+{
+ unsigned long long val;
+ char buf[50];
+ int n;
+ char *ep;
+
+ n = read_attr(buf, 50, fd);
+
+ if (n <= 0)
+ return 0;
+ buf[n] = 0;
+ val = strtoull(buf, &ep, 0);
+ if (ep == buf || (*ep != 0 && *ep != '\n' && *ep != ' '))
+ return 0;
+ return val;
+}
+
static enum array_state read_state(int fd)
{
char buf[20];
static int read_and_act(struct active_array *a)
{
+ unsigned long long sync_completed;
int check_degraded = 0;
int deactivate = 0;
struct mdinfo *mdi;
a->curr_state = read_state(a->info.state_fd);
a->curr_action = read_action(a->action_fd);
a->info.resync_start = read_resync_start(a->resync_start_fd);
+ sync_completed = read_sync_completed(a->sync_completed_fd);
for (mdi = a->info.devs; mdi ; mdi = mdi->next) {
mdi->next_state = 0;
if (mdi->state_fd >= 0) {
}
}
+ /* Check for recovery checkpoint notifications. We need to be a
+ * minimum distance away from the last checkpoint to prevent
+ * over checkpointing. Note reshape checkpointing is not
+ * handled here.
+ */
+ if (sync_completed > a->last_checkpoint &&
+ sync_completed - a->last_checkpoint > a->info.component_size >> 4 &&
+ a->curr_action > reshape) {
+ /* A (non-reshape) sync_action has reached a checkpoint.
+ * Record the updated position in the metadata
+ */
+ a->last_checkpoint = sync_completed;
+ a->container->ss->set_array_state(a, a->curr_state <= clean);
+ } else if (sync_completed > a->last_checkpoint)
+ a->last_checkpoint = sync_completed;
+
a->container->ss->sync_metadata(a->container);
dprintf("%s(%d): state:%s action:%s next(", __func__, a->info.container_member,
array_states[a->curr_state], sync_actions[a->curr_action]);
add_fd(&rfds, &maxfd, a->info.state_fd);
add_fd(&rfds, &maxfd, a->action_fd);
+ add_fd(&rfds, &maxfd, a->sync_completed_fd);
for (mdi = a->info.devs ; mdi ; mdi = mdi->next)
add_fd(&rfds, &maxfd, mdi->state_fd);
return !!(orom->sss & (1 << (fs - 1)));
}
+/**
+ * fls - find last (most-significant) bit set
+ * @x: the word to search
+ * The funciton is borrowed from Linux kernel code
+ * include/asm-generic/bitops/fls.h
+ */
+static inline int fls(int x)
+{
+ int r = 32;
+
+ if (!x)
+ return 0;
+ if (!(x & 0xffff0000u)) {
+ x <<= 16;
+ r -= 16;
+ }
+ if (!(x & 0xff000000u)) {
+ x <<= 8;
+ r -= 8;
+ }
+ if (!(x & 0xf0000000u)) {
+ x <<= 4;
+ r -= 4;
+ }
+ if (!(x & 0xc0000000u)) {
+ x <<= 2;
+ r -= 2;
+ }
+ if (!(x & 0x80000000u)) {
+ x <<= 1;
+ r -= 1;
+ }
+ return r;
+}
+
+/**
+ * imsm_orom_default_chunk - return the largest chunk size supported via orom
+ * @orom: orom pointer from find_imsm_orom
+ */
+static inline int imsm_orom_default_chunk(const struct imsm_orom *orom)
+{
+ int fs = fls(orom->sss);
+
+ if (!fs)
+ return 0;
+
+ return min(512, (1 << fs));
+}
+
struct sys_dev {
char *path;
struct sys_dev *next;
}
if (st->subarray[0]) {
+ unsigned long val;
struct vcl *v;
+ char *ep;
+
+ val = strtoul(st->subarray, &ep, 10);
+ if (*ep != '\0') {
+ free(super);
+ return 1;
+ }
for (v = super->conflist; v; v = v->next)
- if (v->vcnum == atoi(st->subarray))
+ if (v->vcnum == val)
super->currentconf = v;
if (!super->currentconf) {
free(super);
return 1;
}
if (st->subarray[0]) {
+ unsigned long val;
struct vcl *v;
+ char *ep;
+
+ val = strtoul(st->subarray, &ep, 10);
+ if (*ep != '\0') {
+ free(super);
+ return 1;
+ }
for (v = super->conflist; v; v = v->next)
- if (v->vcnum == atoi(st->subarray))
+ if (v->vcnum == val)
super->currentconf = v;
- if (!super->currentconf)
+ if (!super->currentconf) {
+ free(super);
return 1;
+ }
}
+
*sbp = super;
if (st->ss == NULL) {
st->ss = &super_ddf;
enum imsm_update_type {
update_activate_spare,
update_create_array,
+ update_kill_array,
+ update_rename_array,
update_add_disk,
};
struct imsm_dev dev;
};
+struct imsm_update_kill_array {
+ enum imsm_update_type type;
+ int dev_idx;
+};
+
+struct imsm_update_rename_array {
+ enum imsm_update_type type;
+ __u8 name[MAX_RAID_SERIAL_LEN];
+ int dev_idx;
+};
+
struct imsm_update_add_disk {
enum imsm_update_type type;
};
static void print_imsm_dev(struct imsm_dev *dev, char *uuid, int disk_idx)
{
__u64 sz;
- int slot;
+ int slot, i;
struct imsm_map *map = get_imsm_map(dev, 0);
__u32 ord;
printf(" UUID : %s\n", uuid);
printf(" RAID Level : %d\n", get_imsm_raid_level(map));
printf(" Members : %d\n", map->num_members);
+ printf(" Slots : [");
+ for (i = 0; i < map->num_members; i++) {
+ ord = get_imsm_ord_tbl_ent(dev, i);
+ printf("%s", ord & IMSM_ORD_REBUILD ? "_" : "U");
+ }
+ printf("]\n");
slot = get_imsm_disk_slot(map, disk_idx);
if (slot >= 0) {
ord = get_imsm_ord_tbl_ent(dev, slot);
}
if (st->subarray[0]) {
- if (atoi(st->subarray) <= super->anchor->num_raid_devs)
- super->current_vol = atoi(st->subarray);
+ unsigned long val;
+ char *ep;
+
+ err = 1;
+ val = strtoul(st->subarray, &ep, 10);
+ if (*ep != '\0') {
+ free_imsm(super);
+ goto error;
+ }
+
+ if (val < super->anchor->num_raid_devs)
+ super->current_vol = val;
else {
free_imsm(super);
- err = 1;
goto error;
}
}
}
if (st->subarray[0]) {
- if (atoi(st->subarray) <= super->anchor->num_raid_devs)
- super->current_vol = atoi(st->subarray);
+ unsigned long val;
+ char *ep;
+
+ val = strtoul(st->subarray, &ep, 10);
+ if (*ep != '\0') {
+ free_imsm(super);
+ return 1;
+ }
+
+ if (val < super->anchor->num_raid_devs)
+ super->current_vol = val;
else {
free_imsm(super);
return 1;
}
}
+static int check_name(struct intel_super *super, char *name, int quiet)
+{
+ struct imsm_super *mpb = super->anchor;
+ char *reason = NULL;
+ int i;
+
+ if (strlen(name) > MAX_RAID_SERIAL_LEN)
+ reason = "must be 16 characters or less";
+
+ for (i = 0; i < mpb->num_raid_devs; i++) {
+ struct imsm_dev *dev = get_imsm_dev(super, i);
+
+ if (strncmp((char *) dev->volume, name, MAX_RAID_SERIAL_LEN) == 0) {
+ reason = "already exists";
+ break;
+ }
+ }
+
+ if (reason && !quiet)
+ fprintf(stderr, Name ": imsm volume name %s\n", reason);
+
+ return !reason;
+}
+
static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info,
unsigned long long size, char *name,
char *homehost, int *uuid)
if (super->current_vol == 0)
mpb->num_disks = 0;
- for (i = 0; i < super->current_vol; i++) {
- dev = get_imsm_dev(super, i);
- if (strncmp((char *) dev->volume, name,
- MAX_RAID_SERIAL_LEN) == 0) {
- fprintf(stderr, Name": '%s' is already defined for this container\n",
- name);
- return 0;
- }
- }
-
+ if (!check_name(super, name, 0))
+ return 0;
sprintf(st->subarray, "%d", idx);
dv = malloc(sizeof(*dv));
if (!dv) {
close(cfd);
return 0;
}
+
+static int default_chunk_imsm(struct supertype *st)
+{
+ struct intel_super *super = st->sb;
+
+ if (!super->orom)
+ return 0;
+
+ return imsm_orom_default_chunk(super->orom);
+}
+
+static void handle_missing(struct intel_super *super, struct imsm_dev *dev);
+
+static int kill_subarray_imsm(struct supertype *st)
+{
+ /* remove the subarray currently referenced by ->current_vol */
+ __u8 i;
+ struct intel_dev **dp;
+ struct intel_super *super = st->sb;
+ __u8 current_vol = super->current_vol;
+ struct imsm_super *mpb = super->anchor;
+
+ if (super->current_vol < 0)
+ return 2;
+ super->current_vol = -1; /* invalidate subarray cursor */
+
+ /* block deletions that would change the uuid of active subarrays
+ *
+ * FIXME when immutable ids are available, but note that we'll
+ * also need to fixup the invalidated/active subarray indexes in
+ * mdstat
+ */
+ for (i = 0; i < mpb->num_raid_devs; i++) {
+ char subarray[4];
+
+ if (i < current_vol)
+ continue;
+ sprintf(subarray, "%u", i);
+ if (is_subarray_active(subarray, st->devname)) {
+ fprintf(stderr,
+ Name ": deleting subarray-%d would change the UUID of active subarray-%d, aborting\n",
+ current_vol, i);
+
+ return 2;
+ }
+ }
+
+ if (st->update_tail) {
+ struct imsm_update_kill_array *u = malloc(sizeof(*u));
+
+ if (!u)
+ return 2;
+ u->type = update_kill_array;
+ u->dev_idx = current_vol;
+ append_metadata_update(st, u, sizeof(*u));
+
+ return 0;
+ }
+
+ for (dp = &super->devlist; *dp;)
+ if ((*dp)->index == current_vol) {
+ *dp = (*dp)->next;
+ } else {
+ handle_missing(super, (*dp)->dev);
+ if ((*dp)->index > current_vol)
+ (*dp)->index--;
+ dp = &(*dp)->next;
+ }
+
+ /* no more raid devices, all active components are now spares,
+ * but of course failed are still failed
+ */
+ if (--mpb->num_raid_devs == 0) {
+ struct dl *d;
+
+ for (d = super->disks; d; d = d->next)
+ if (d->index > -2) {
+ d->index = -1;
+ d->disk.status = SPARE_DISK;
+ }
+ }
+
+ super->updates_pending++;
+
+ return 0;
+}
+
+static int update_subarray_imsm(struct supertype *st, char *update, mddev_ident_t ident)
+{
+ /* update the subarray currently referenced by ->current_vol */
+ struct intel_super *super = st->sb;
+ struct imsm_super *mpb = super->anchor;
+
+ if (super->current_vol < 0)
+ return 2;
+
+ if (strcmp(update, "name") == 0) {
+ char *name = ident->name;
+
+ if (is_subarray_active(st->subarray, st->devname)) {
+ fprintf(stderr,
+ Name ": Unable to update name of active subarray\n");
+ return 2;
+ }
+
+ if (!check_name(super, name, 0))
+ return 2;
+
+ if (st->update_tail) {
+ struct imsm_update_rename_array *u = malloc(sizeof(*u));
+
+ if (!u)
+ return 2;
+ u->type = update_rename_array;
+ u->dev_idx = super->current_vol;
+ snprintf((char *) u->name, MAX_RAID_SERIAL_LEN, "%s", name);
+ append_metadata_update(st, u, sizeof(*u));
+ } else {
+ struct imsm_dev *dev;
+ int i;
+
+ dev = get_imsm_dev(super, super->current_vol);
+ snprintf((char *) dev->volume, MAX_RAID_SERIAL_LEN, "%s", name);
+ for (i = 0; i < mpb->num_raid_devs; i++) {
+ dev = get_imsm_dev(super, i);
+ handle_missing(super, dev);
+ }
+ super->updates_pending++;
+ }
+ } else
+ return 2;
+
+ return 0;
+}
#endif /* MDASSEMBLE */
static int is_rebuilding(struct imsm_dev *dev)
memmove(&disk->serial[0], &disk->serial[1], MAX_RAID_SERIAL_LEN - 1);
}
+static void handle_missing(struct intel_super *super, struct imsm_dev *dev)
+{
+ __u8 map_state;
+ struct dl *dl;
+ int failed;
+
+ if (!super->missing)
+ return;
+ failed = imsm_count_failed(super, dev);
+ map_state = imsm_check_degraded(super, dev, failed);
+
+ dprintf("imsm: mark missing\n");
+ end_migration(dev, map_state);
+ for (dl = super->missing; dl; dl = dl->next)
+ mark_missing(dev, &dl->disk, dl->index);
+ super->updates_pending++;
+}
+
/* Handle dirty -> clean transititions and resync. Degraded and rebuild
* states are handled in imsm_set_disk() with one exception, when a
* resync is stopped due to a new failure this routine will set the
__u32 blocks_per_unit;
/* before we activate this array handle any missing disks */
- if (consistent == 2 && super->missing) {
- struct dl *dl;
-
- dprintf("imsm: mark missing\n");
- end_migration(dev, map_state);
- for (dl = super->missing; dl; dl = dl->next)
- mark_missing(dev, &dl->disk, dl->index);
- super->updates_pending++;
- }
+ if (consistent == 2)
+ handle_missing(super, dev);
if (consistent == 2 &&
(!is_resync_complete(&a->info) ||
dprintf("imsm: mark resync done\n");
end_migration(dev, map_state);
super->updates_pending++;
+ a->last_checkpoint = 0;
}
} else if (!is_resyncing(dev) && !failed) {
/* mark the start of the init process if nothing is failed */
/* check if we can update curr_migr_unit from resync_start, recovery_start */
blocks_per_unit = blocks_per_migr_unit(dev);
- if (blocks_per_unit && failed <= 1) {
+ if (blocks_per_unit) {
__u32 units32;
__u64 units;
- if (migr_type(dev) == MIGR_REBUILD)
- units = min_recovery_start(&a->info) / blocks_per_unit;
- else
- units = a->info.resync_start / blocks_per_unit;
+ units = a->last_checkpoint / blocks_per_unit;
units32 = units;
/* check that we did not overflow 32-bits, and that
map = get_imsm_map(dev, 0);
map->failed_disk_num = ~0;
super->updates_pending++;
+ a->last_checkpoint = 0;
} else if (map_state == IMSM_T_STATE_DEGRADED &&
map->map_state != map_state &&
!dev->vol.migr_state) {
dprintf("imsm: mark degraded\n");
map->map_state = map_state;
super->updates_pending++;
+ a->last_checkpoint = 0;
} else if (map_state == IMSM_T_STATE_FAILED &&
map->map_state != map_state) {
dprintf("imsm: mark failed\n");
end_migration(dev, map_state);
super->updates_pending++;
+ a->last_checkpoint = 0;
}
}
}
break;
}
+ case update_kill_array: {
+ struct imsm_update_kill_array *u = (void *) update->buf;
+ int victim = u->dev_idx;
+ struct active_array *a;
+ struct intel_dev **dp;
+ struct imsm_dev *dev;
+
+ /* sanity check that we are not affecting the uuid of
+ * active arrays, or deleting an active array
+ *
+ * FIXME when immutable ids are available, but note that
+ * we'll also need to fixup the invalidated/active
+ * subarray indexes in mdstat
+ */
+ for (a = st->arrays; a; a = a->next)
+ if (a->info.container_member >= victim)
+ break;
+ /* by definition if mdmon is running at least one array
+ * is active in the container, so checking
+ * mpb->num_raid_devs is just extra paranoia
+ */
+ dev = get_imsm_dev(super, victim);
+ if (a || !dev || mpb->num_raid_devs == 1) {
+ dprintf("failed to delete subarray-%d\n", victim);
+ break;
+ }
+
+ for (dp = &super->devlist; *dp;)
+ if ((*dp)->index == super->current_vol) {
+ *dp = (*dp)->next;
+ } else {
+ if ((*dp)->index > victim)
+ (*dp)->index--;
+ dp = &(*dp)->next;
+ }
+ mpb->num_raid_devs--;
+ super->updates_pending++;
+ break;
+ }
+ case update_rename_array: {
+ struct imsm_update_rename_array *u = (void *) update->buf;
+ char name[MAX_RAID_SERIAL_LEN+1];
+ int target = u->dev_idx;
+ struct active_array *a;
+ struct imsm_dev *dev;
+
+ /* sanity check that we are not affecting the uuid of
+ * an active array
+ */
+ snprintf(name, MAX_RAID_SERIAL_LEN, "%s", (char *) u->name);
+ name[MAX_RAID_SERIAL_LEN] = '\0';
+ for (a = st->arrays; a; a = a->next)
+ if (a->info.container_member == target)
+ break;
+ dev = get_imsm_dev(super, u->dev_idx);
+ if (a || !dev || !check_name(super, name, 1)) {
+ dprintf("failed to rename subarray-%d\n", target);
+ break;
+ }
+
+ snprintf((char *) dev->volume, MAX_RAID_SERIAL_LEN, name);
+ super->updates_pending++;
+ break;
+ }
case update_add_disk:
/* we may be able to repair some arrays if disks are
.brief_detail_super = brief_detail_super_imsm,
.write_init_super = write_init_super_imsm,
.validate_geometry = validate_geometry_imsm,
+ .default_chunk = default_chunk_imsm,
.add_to_super = add_to_super_imsm,
.detail_platform = detail_platform_imsm,
+ .kill_subarray = kill_subarray_imsm,
+ .update_subarray = update_subarray_imsm,
#endif
.match_home = match_home_imsm,
.uuid_from_super= uuid_from_super_imsm,
tm.tv_sec = 5;
tm.tv_usec = 0;
- /* give mdmon a chance to checkpoint resync */
- sysfs_set_str(mdi, NULL, "sync_action", "idle");
-
FD_ZERO(&fds);
/* wait for array_state to be clean */
return -1;
}
+struct superswitch *version_to_superswitch(char *vers)
+{
+ int i;
+
+ for (i = 0; superlist[i]; i++) {
+ struct superswitch *ss = superlist[i];
+
+ if (strcmp(vers, ss->name) == 0)
+ return ss;
+ }
+
+ return NULL;
+}
+
+int is_container_member(struct mdstat_ent *mdstat, char *container)
+{
+ if (mdstat->metadata_version == NULL ||
+ strncmp(mdstat->metadata_version, "external:", 9) != 0 ||
+ !is_subarray(mdstat->metadata_version+9) ||
+ strncmp(mdstat->metadata_version+10, container, strlen(container)) != 0 ||
+ mdstat->metadata_version[10+strlen(container)] != '/')
+ return 0;
+
+ return 1;
+}
+
+int is_subarray_active(char *subarray, char *container)
+{
+ struct mdstat_ent *mdstat = mdstat_read(0, 0);
+ struct mdstat_ent *ent;
+
+ for (ent = mdstat; ent; ent = ent->next) {
+ if (is_container_member(ent, container)) {
+ char *inst = &ent->metadata_version[10+strlen(container)+1];
+
+ if (strcmp(inst, subarray) == 0)
+ break;
+ }
+ }
+
+ free_mdstat(mdstat);
+
+ return ent != NULL;
+}
+
+/* open_subarray - opens a subarray in a container
+ * @dev: container device name
+ * @st: supertype with only ->subarray set
+ * @quiet: block reporting errors flag
+ *
+ * On success returns an fd to a container and fills in *st
+ */
+int open_subarray(char *dev, struct supertype *st, int quiet)
+{
+ struct mdinfo *mdi;
+ int fd, err = 1;
+
+ fd = open(dev, O_RDWR|O_EXCL);
+ if (fd < 0) {
+ if (!quiet)
+ fprintf(stderr, Name ": Couldn't open %s, aborting\n",
+ dev);
+ return 2;
+ }
+
+ st->devnum = fd2devnum(fd);
+ if (st->devnum == NoMdDev) {
+ if (!quiet)
+ fprintf(stderr,
+ Name ": Failed to determine device number for %s\n",
+ dev);
+ goto close_fd;
+ }
+
+ mdi = sysfs_read(fd, st->devnum, GET_VERSION|GET_LEVEL);
+ if (!mdi) {
+ if (!quiet)
+ fprintf(stderr, Name ": Failed to read sysfs for %s\n",
+ dev);
+ goto close_fd;
+ }
+
+ if (mdi->array.level != UnSet) {
+ if (!quiet)
+ fprintf(stderr, Name ": %s is not a container\n", dev);
+ goto free_sysfs;
+ }
+
+ st->ss = version_to_superswitch(mdi->text_version);
+ if (!st->ss) {
+ if (!quiet)
+ fprintf(stderr,
+ Name ": Operation not supported for %s metadata\n",
+ mdi->text_version);
+ goto free_sysfs;
+ }
+
+ st->devname = devnum2devname(st->devnum);
+ if (!st->devname) {
+ if (!quiet)
+ fprintf(stderr, Name ": Failed to allocate device name\n");
+ goto free_sysfs;
+ }
+
+ if (st->ss->load_super(st, fd, NULL)) {
+ if (!quiet)
+ fprintf(stderr, Name ": Failed to find subarray-%s in %s\n",
+ st->subarray, dev);
+ goto free_name;
+ }
+
+ if (!st->loaded_container) {
+ if (!quiet)
+ fprintf(stderr, Name ": %s is not a container\n", dev);
+ goto free_super;
+ }
+
+ err = 0;
+
+ free_super:
+ if (err)
+ st->ss->free_super(st);
+ free_name:
+ if (err)
+ free(st->devname);
+ free_sysfs:
+ sysfs_free(mdi);
+ close_fd:
+ if (err)
+ close(fd);
+
+ if (err)
+ return -1;
+ else
+ return fd;
+}
+
int add_disk(int mdfd, struct supertype *st,
struct mdinfo *sra, struct mdinfo *info)
{