/* Not really Monitor but ... */ int Wait(char *dev) { struct stat stb; char devnm[32]; int rv = 1; int frozen_remaining = 3; if (stat(dev, &stb) != 0) { pr_err("Cannot find %s: %s\n", dev, strerror(errno)); return 2; } strcpy(devnm, stat2devnm(&stb)); while(1) { struct mdstat_ent *ms = mdstat_read(1, 0); struct mdstat_ent *e; for (e=ms ; e; e=e->next) if (strcmp(e->devnm, devnm) == 0) break; if (e && e->percent == RESYNC_NONE) { /* We could be in the brief pause before something * starts. /proc/mdstat doesn't show that, but * sync_action does. */ struct mdinfo mdi; char buf[21]; sysfs_init(&mdi, -1, devnm); if (sysfs_get_str(&mdi, NULL, "sync_action", buf, 20) > 0 && strcmp(buf,"idle\n") != 0) { e->percent = RESYNC_UNKNOWN; if (strcmp(buf, "frozen\n") == 0) { if (frozen_remaining == 0) e->percent = RESYNC_NONE; else frozen_remaining -= 1; } } } if (!e || e->percent == RESYNC_NONE) { if (e && e->metadata_version && strncmp(e->metadata_version, "external:", 9) == 0) { if (is_subarray(&e->metadata_version[9])) ping_monitor(&e->metadata_version[9]); else ping_monitor(devnm); } free_mdstat(ms); return rv; } free_mdstat(ms); rv = 0; mdstat_wait(5); } }
void unblock_monitor(char *container, const int unfreeze) { struct mdstat_ent *ent, *e; struct mdinfo *sra = NULL; int to_ping = 0; ent = mdstat_read(0, 0); if (!ent) { fprintf(stderr, Name ": failed to read /proc/mdstat while unblocking container\n"); return; } /* unfreeze container contents */ for (e = ent; e; e = e->next) { if (!is_container_member(e, container)) continue; sysfs_free(sra); sra = sysfs_read(-1, e->devnum, GET_VERSION|GET_LEVEL); if (sra->array.level > 0) to_ping++; if (unblock_subarray(sra, unfreeze)) fprintf(stderr, Name ": Failed to unfreeze %s\n", e->dev); } if (to_ping) ping_monitor(container); sysfs_free(sra); free_mdstat(ent); }
/* ping monitor using device number */ int ping_monitor_by_id(int devnum) { int err = -1; char *container = devnum2devname(devnum); if (container) { err = ping_monitor(container); free(container); } return err; }
/* Not really Monitor but ... */ int Wait(char *dev) { struct stat stb; int devnum; int rv = 1; if (stat(dev, &stb) != 0) { fprintf(stderr, Name ": Cannot find %s: %s\n", dev, strerror(errno)); return 2; } devnum = stat2devnum(&stb); while(1) { struct mdstat_ent *ms = mdstat_read(1, 0); struct mdstat_ent *e; for (e=ms ; e; e=e->next) if (e->devnum == devnum) break; if (!e || e->percent < 0) { if (e && e->metadata_version && strncmp(e->metadata_version, "external:", 9) == 0) { if (is_subarray(&e->metadata_version[9])) ping_monitor(&e->metadata_version[9]); else ping_monitor(devnum2devname(devnum)); } free_mdstat(ms); return rv; } free_mdstat(ms); rv = 0; mdstat_wait(5); } }
/** * block_monitor - prevent mdmon spare assignment * @container - container to block * @freeze - flag to additionally freeze sync_action * * This is used by the reshape code to freeze the container, and the * auto-rebuild implementation to atomically move spares. * In both cases we need to stop mdmon from assigning spares to replace * failed devices as we might have other plans for the spare. * For the reshape case we also need to 'freeze' sync_action so that * no recovery happens until we have fully prepared for the reshape. * * We tell mdmon that the array is frozen by marking the 'metadata' name * with a leading '-'. The previously told mdmon "Don't make this array * read/write, leave it readonly". Now it means a more general "Don't * reconfigure this array at all". * As older versions of mdmon (which might run from initrd) don't understand * this, we first check that the running mdmon is new enough. */ int block_monitor(char *container, const int freeze) { int devnum = devname2devnum(container); struct mdstat_ent *ent, *e, *e2; struct mdinfo *sra = NULL; char *version = NULL; char buf[64]; int rv = 0; if (!mdmon_running(devnum)) { /* if mdmon is not active we assume that any instance that is * later started will match the current mdadm version, if this * assumption is violated we may inadvertantly rebuild an array * that was meant for reshape, or start rebuild on a spare that * was to be moved to another container */ /* pass */; } else { int ver; version = ping_monitor_version(container); ver = version ? mdadm_version(version) : -1; free(version); if (ver < 3002000) { fprintf(stderr, Name ": mdmon instance for %s cannot be disabled\n", container); return -1; } } ent = mdstat_read(0, 0); if (!ent) { fprintf(stderr, Name ": failed to read /proc/mdstat while disabling mdmon\n"); return -1; } /* freeze container contents */ for (e = ent; e; e = e->next) { if (!is_container_member(e, container)) continue; sysfs_free(sra); sra = sysfs_read(-1, e->devnum, GET_VERSION); if (!sra) { fprintf(stderr, Name ": failed to read sysfs for subarray%s\n", to_subarray(e, container)); break; } /* can't reshape an array that we can't monitor */ if (sra->text_version[0] == '-') break; if (freeze && sysfs_freeze_array(sra) < 1) break; /* flag this array to not be modified by mdmon (close race with * takeover in reshape case and spare reassignment in the * auto-rebuild case) */ if (block_subarray(sra)) break; ping_monitor(container); /* check that we did not race with recovery */ if ((freeze && !sysfs_attribute_available(sra, NULL, "sync_action")) || (freeze && sysfs_attribute_available(sra, NULL, "sync_action") && sysfs_get_str(sra, NULL, "sync_action", buf, 20) > 0 && strcmp(buf, "frozen\n") == 0)) /* pass */; else { unblock_subarray(sra, 0); break; } /* Double check against races - there should be no spares * or part-spares */ sysfs_free(sra); sra = sysfs_read(-1, e->devnum, GET_DEVS | GET_STATE); if (sra && sra->array.spare_disks > 0) { unblock_subarray(sra, freeze); break; } } if (e) { fprintf(stderr, Name ": failed to freeze subarray%s\n", to_subarray(e, container)); /* thaw the partially frozen container */ for (e2 = ent; e2 && e2 != e; e2 = e2->next) { if (!is_container_member(e2, container)) continue; sysfs_free(sra); sra = sysfs_read(-1, e2->devnum, GET_VERSION); if (unblock_subarray(sra, freeze)) fprintf(stderr, Name ": Failed to unfreeze %s\n", e2->dev); } ping_monitor(container); /* cleared frozen */ rv = -1; } sysfs_free(sra); free_mdstat(ent); return rv; }
int WaitClean(char *dev, int sock, int verbose) { int fd; struct mdinfo *mdi; int rv = 1; char devnm[32]; fd = open(dev, O_RDONLY); if (fd < 0) { if (verbose) pr_err("Couldn't open %s: %s\n", dev, strerror(errno)); return 1; } strcpy(devnm, fd2devnm(fd)); mdi = sysfs_read(fd, devnm, GET_VERSION|GET_LEVEL|GET_SAFEMODE); if (!mdi) { if (verbose) pr_err("Failed to read sysfs attributes for %s\n", dev); close(fd); return 0; } switch(mdi->array.level) { case LEVEL_LINEAR: case LEVEL_MULTIPATH: case 0: /* safemode delay is irrelevant for these levels */ rv = 0; } /* for internal metadata the kernel handles the final clean * transition, containers can never be dirty */ if (!is_subarray(mdi->text_version)) rv = 0; /* safemode disabled ? */ if (mdi->safe_mode_delay == 0) rv = 0; if (rv) { int state_fd = sysfs_open(fd2devnm(fd), NULL, "array_state"); char buf[20]; int delay = 5000; /* minimize the safe_mode_delay and prepare to wait up to 5s * for writes to quiesce */ sysfs_set_safemode(mdi, 1); /* wait for array_state to be clean */ while (1) { rv = read(state_fd, buf, sizeof(buf)); if (rv < 0) break; if (sysfs_match_word(buf, clean_states) <= 4) break; rv = sysfs_wait(state_fd, &delay); if (rv < 0 && errno != EINTR) break; lseek(state_fd, 0, SEEK_SET); } if (rv < 0) rv = 1; else if (fping_monitor(sock) == 0 || ping_monitor(mdi->text_version) == 0) { /* we need to ping to close the window between array * state transitioning to clean and the metadata being * marked clean */ rv = 0; } else rv = 1; if (rv && verbose) pr_err("Error waiting for %s to be clean\n", dev); /* restore the original safe_mode_delay */ sysfs_set_safemode(mdi, mdi->safe_mode_delay); close(state_fd); } sysfs_free(mdi); close(fd); return rv; }
int Manage_ro(char *devname, int fd, int readonly) { /* switch to readonly or rw * * requires >= 0.90.0 * first check that array is runing * use RESTART_ARRAY_RW or STOP_ARRAY_RO * */ mdu_array_info_t array; #ifndef MDASSEMBLE struct mdinfo *mdi; #endif if (md_get_version(fd) < 9000) { fprintf(stderr, Name ": need md driver version 0.90.0 or later\n"); return 1; } #ifndef MDASSEMBLE /* If this is an externally-manage array, we need to modify the * metadata_version so that mdmon doesn't undo our change. */ mdi = sysfs_read(fd, -1, GET_LEVEL|GET_VERSION); if (mdi && mdi->array.major_version == -1 && is_subarray(mdi->text_version)) { char vers[64]; strcpy(vers, "external:"); strcat(vers, mdi->text_version); if (readonly > 0) { int rv; /* We set readonly ourselves. */ vers[9] = '-'; sysfs_set_str(mdi, NULL, "metadata_version", vers); close(fd); rv = sysfs_set_str(mdi, NULL, "array_state", "readonly"); if (rv < 0) { fprintf(stderr, Name ": failed to set readonly for %s: %s\n", devname, strerror(errno)); vers[9] = mdi->text_version[0]; sysfs_set_str(mdi, NULL, "metadata_version", vers); return 1; } } else { char *cp; /* We cannot set read/write - must signal mdmon */ vers[9] = '/'; sysfs_set_str(mdi, NULL, "metadata_version", vers); cp = strchr(vers+10, '/'); if (*cp) *cp = 0; ping_monitor(vers+10); if (mdi->array.level <= 0) sysfs_set_str(mdi, NULL, "array_state", "active"); } return 0; } #endif if (ioctl(fd, GET_ARRAY_INFO, &array)) { fprintf(stderr, Name ": %s does not appear to be active.\n", devname); return 1; } if (readonly>0) { if (ioctl(fd, STOP_ARRAY_RO, NULL)) { fprintf(stderr, Name ": failed to set readonly for %s: %s\n", devname, strerror(errno)); return 1; } } else if (readonly < 0) { if (ioctl(fd, RESTART_ARRAY_RW, NULL)) { fprintf(stderr, Name ": failed to set writable for %s: %s\n", devname, strerror(errno)); return 1; } } return 0; }
int Manage_runstop(char *devname, int fd, int runstop, int quiet) { /* Run or stop the array. array must already be configured * required >= 0.90.0 * Only print failure messages if quiet == 0; * quiet > 0 means really be quiet * quiet < 0 means we will try again if it fails. */ mdu_param_t param; /* unused */ if (runstop == -1 && md_get_version(fd) < 9000) { if (ioctl(fd, STOP_MD, 0)) { if (quiet == 0) fprintf(stderr, Name ": stopping device %s " "failed: %s\n", devname, strerror(errno)); return 1; } } if (md_get_version(fd) < 9000) { fprintf(stderr, Name ": need md driver version 0.90.0 or later\n"); return 1; } /* if (ioctl(fd, GET_ARRAY_INFO, &array)) { fprintf(stderr, Name ": %s does not appear to be active.\n", devname); return 1; } */ if (runstop>0) { if (ioctl(fd, RUN_ARRAY, ¶m)) { fprintf(stderr, Name ": failed to run array %s: %s\n", devname, strerror(errno)); return 1; } if (quiet <= 0) fprintf(stderr, Name ": started %s\n", devname); } else if (runstop < 0){ struct map_ent *map = NULL; struct stat stb; struct mdinfo *mdi; int devnum; int err; int count; /* If this is an mdmon managed array, just write 'inactive' * to the array state and let mdmon clear up. */ devnum = fd2devnum(fd); /* Get EXCL access first. If this fails, then attempting * to stop is probably a bad idea. */ close(fd); fd = open(devname, O_RDONLY|O_EXCL); if (fd < 0 || fd2devnum(fd) != devnum) { if (fd >= 0) close(fd); fprintf(stderr, Name ": Cannot get exclusive access to %s:" "Perhaps a running " "process, mounted filesystem " "or active volume group?\n", devname); return 1; } mdi = sysfs_read(fd, -1, GET_LEVEL|GET_VERSION); if (mdi && mdi->array.level > 0 && is_subarray(mdi->text_version)) { int err; /* This is mdmon managed. */ close(fd); count = 25; while (count && (err = sysfs_set_str(mdi, NULL, "array_state", "inactive")) < 0 && errno == EBUSY) { usleep(200000); count--; } if (err && !quiet) { fprintf(stderr, Name ": failed to stop array %s: %s\n", devname, strerror(errno)); return 1; } /* Give monitor a chance to act */ ping_monitor(mdi->text_version); fd = open_dev_excl(devnum); if (fd < 0) { fprintf(stderr, Name ": failed to completely stop %s" ": Device is busy\n", devname); return 1; } } else if (mdi && mdi->array.major_version == -1 && mdi->array.minor_version == -2 && !is_subarray(mdi->text_version)) { struct mdstat_ent *mds, *m; /* container, possibly mdmon-managed. * Make sure mdmon isn't opening it, which * would interfere with the 'stop' */ ping_monitor(mdi->sys_name); /* now check that there are no existing arrays * which are members of this array */ mds = mdstat_read(0, 0); for (m=mds; m; m=m->next) if (m->metadata_version && strncmp(m->metadata_version, "external:", 9)==0 && is_subarray(m->metadata_version+9) && devname2devnum(m->metadata_version+10) == devnum) { if (!quiet) fprintf(stderr, Name ": Cannot stop container %s: " "member %s still active\n", devname, m->dev); free_mdstat(mds); if (mdi) sysfs_free(mdi); return 1; } } /* As we have an O_EXCL open, any use of the device * which blocks STOP_ARRAY is probably a transient use, * so it is reasonable to retry for a while - 5 seconds. */ count = 25; err = 0; while (count && fd >= 0 && (err = ioctl(fd, STOP_ARRAY, NULL)) < 0 && errno == EBUSY) { usleep(200000); count --; } if (fd >= 0 && err) { if (quiet == 0) { fprintf(stderr, Name ": failed to stop array %s: %s\n", devname, strerror(errno)); if (errno == EBUSY) fprintf(stderr, "Perhaps a running " "process, mounted filesystem " "or active volume group?\n"); } if (mdi) sysfs_free(mdi); return 1; } /* prior to 2.6.28, KOBJ_CHANGE was not sent when an md array * was stopped, so We'll do it here just to be sure. Drop any * partitions as well... */ if (fd >= 0) ioctl(fd, BLKRRPART, 0); if (mdi) sysfs_uevent(mdi, "change"); if (devnum != NoMdDev && (stat("/dev/.udev", &stb) != 0 || check_env("MDADM_NO_UDEV"))) { struct map_ent *mp = map_by_devnum(&map, devnum); remove_devices(devnum, mp ? mp->path : NULL); } if (quiet <= 0) fprintf(stderr, Name ": stopped %s\n", devname); map_lock(&map); map_remove(&map, devnum); map_unlock(&map); } return 0; }