int unblock_subarray(struct mdinfo *sra, const int unfreeze) { char buf[64]; int rc = 0; if (sra) { sprintf(buf, "external:%s\n", sra->text_version); buf[9] = '/'; } else buf[9] = '-'; if (buf[9] == '-' || sysfs_set_str(sra, NULL, "metadata_version", buf) || (unfreeze && sysfs_attribute_available(sra, NULL, "sync_action") && sysfs_set_str(sra, NULL, "sync_action", "idle"))) rc = -1; return rc; }
int sysfs_freeze_array(struct mdinfo *sra) { /* Try to freeze resync/rebuild on this array/container. * Return -1 if the array is busy, * return 0 if this kernel doesn't support 'frozen' * return 1 if it worked. */ char buf[20]; if (!sysfs_attribute_available(sra, NULL, "sync_action")) return 1; /* no sync_action == frozen */ if (sysfs_get_str(sra, NULL, "sync_action", buf, 20) <= 0) return 0; if (strcmp(buf, "frozen\n") == 0) /* Already frozen */ return 0; if (strcmp(buf, "idle\n") != 0 && strcmp(buf, "recover\n") != 0) return -1; if (sysfs_set_str(sra, NULL, "sync_action", "frozen") < 0) return 0; return 1; }
/** * 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; }