|  | /* | 
|  | * mdmon - monitor external metadata arrays | 
|  | * | 
|  | * Copyright (C) 2007-2009 Neil Brown <neilb@suse.de> | 
|  | * Copyright (C) 2007-2009 Intel Corporation | 
|  | * | 
|  | * This program is free software; you can redistribute it and/or modify it | 
|  | * under the terms and conditions of the GNU General Public License, | 
|  | * version 2, as published by the Free Software Foundation. | 
|  | * | 
|  | * This program is distributed in the hope it will be useful, but WITHOUT | 
|  | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
|  | * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | 
|  | * more details. | 
|  | * | 
|  | * You should have received a copy of the GNU General Public License along with | 
|  | * this program; if not, write to the Free Software Foundation, Inc., | 
|  | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | 
|  | */ | 
|  |  | 
|  | #include "mdadm.h" | 
|  | #include "mdmon.h" | 
|  | #include <sys/syscall.h> | 
|  | #include <sys/select.h> | 
|  | #include <signal.h> | 
|  |  | 
|  | static char *array_states[] = { | 
|  | "clear", "inactive", "suspended", "readonly", "read-auto", | 
|  | "clean", "active", "write-pending", "active-idle", NULL }; | 
|  | static char *sync_actions[] = { | 
|  | "idle", "reshape", "resync", "recover", "check", "repair", NULL | 
|  | }; | 
|  |  | 
|  | static int write_attr(char *attr, int fd) | 
|  | { | 
|  | return write(fd, attr, strlen(attr)); | 
|  | } | 
|  |  | 
|  | static void add_fd(fd_set *fds, int *maxfd, int fd) | 
|  | { | 
|  | if (fd < 0) | 
|  | return; | 
|  | if (fd > *maxfd) | 
|  | *maxfd = fd; | 
|  | FD_SET(fd, fds); | 
|  | } | 
|  |  | 
|  | static int read_attr(char *buf, int len, int fd) | 
|  | { | 
|  | int n; | 
|  |  | 
|  | if (fd < 0) { | 
|  | buf[0] = 0; | 
|  | return 0; | 
|  | } | 
|  | lseek(fd, 0, 0); | 
|  | n = read(fd, buf, len - 1); | 
|  |  | 
|  | if (n <= 0) { | 
|  | buf[0] = 0; | 
|  | return 0; | 
|  | } | 
|  | buf[n] = 0; | 
|  | if (buf[n-1] == '\n') | 
|  | buf[n-1] = 0; | 
|  | return n; | 
|  | } | 
|  |  | 
|  | static unsigned long long read_resync_start(int fd) | 
|  | { | 
|  | char buf[30]; | 
|  | int n; | 
|  |  | 
|  | n = read_attr(buf, 30, fd); | 
|  | if (n <= 0) | 
|  | return 0; | 
|  | if (strncmp(buf, "none", 4) == 0) | 
|  | return MaxSector; | 
|  | else | 
|  | 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]; | 
|  | int n = read_attr(buf, 20, fd); | 
|  |  | 
|  | if (n <= 0) | 
|  | return bad_word; | 
|  | return (enum array_state) sysfs_match_word(buf, array_states); | 
|  | } | 
|  |  | 
|  | static enum sync_action read_action( int fd) | 
|  | { | 
|  | char buf[20]; | 
|  | int n = read_attr(buf, 20, fd); | 
|  |  | 
|  | if (n <= 0) | 
|  | return bad_action; | 
|  | return (enum sync_action) sysfs_match_word(buf, sync_actions); | 
|  | } | 
|  |  | 
|  | int read_dev_state(int fd) | 
|  | { | 
|  | char buf[60]; | 
|  | int n = read_attr(buf, 60, fd); | 
|  | char *cp; | 
|  | int rv = 0; | 
|  |  | 
|  | if (n <= 0) | 
|  | return 0; | 
|  |  | 
|  | cp = buf; | 
|  | while (cp) { | 
|  | if (sysfs_attr_match(cp, "faulty")) | 
|  | rv |= DS_FAULTY; | 
|  | if (sysfs_attr_match(cp, "in_sync")) | 
|  | rv |= DS_INSYNC; | 
|  | if (sysfs_attr_match(cp, "write_mostly")) | 
|  | rv |= DS_WRITE_MOSTLY; | 
|  | if (sysfs_attr_match(cp, "spare")) | 
|  | rv |= DS_SPARE; | 
|  | if (sysfs_attr_match(cp, "blocked")) | 
|  | rv |= DS_BLOCKED; | 
|  | cp = strchr(cp, ','); | 
|  | if (cp) | 
|  | cp++; | 
|  | } | 
|  | return rv; | 
|  | } | 
|  |  | 
|  | static void signal_manager(void) | 
|  | { | 
|  | /* tgkill(getpid(), mon_tid, SIGUSR1); */ | 
|  | int pid = getpid(); | 
|  | syscall(SYS_tgkill, pid, mgr_tid, SIGUSR1); | 
|  | } | 
|  |  | 
|  | /* Monitor a set of active md arrays - all of which share the | 
|  | * same metadata - and respond to events that require | 
|  | * metadata update. | 
|  | * | 
|  | * New arrays are detected by another thread which allocates | 
|  | * required memory and attaches the data structure to our list. | 
|  | * | 
|  | * Events: | 
|  | *  Array stops. | 
|  | *    This is detected by array_state going to 'clear' or 'inactive'. | 
|  | *    while we thought it was active. | 
|  | *    Response is to mark metadata as clean and 'clear' the array(??) | 
|  | *  write-pending | 
|  | *    array_state if 'write-pending' | 
|  | *    We mark metadata as 'dirty' then set array to 'active'. | 
|  | *  active_idle | 
|  | *    Either ignore, or mark clean, then mark metadata as clean. | 
|  | * | 
|  | *  device fails | 
|  | *    detected by rd-N/state reporting "faulty" | 
|  | *    mark device as 'failed' in metadata, let the kernel release the | 
|  | *    device by writing '-blocked' to rd/state, and finally write 'remove' to | 
|  | *    rd/state.  Before a disk can be replaced it must be failed and removed | 
|  | *    from all container members, this will be preemptive for the other | 
|  | *    arrays... safe? | 
|  | * | 
|  | *  sync completes | 
|  | *    sync_action was 'resync' and becomes 'idle' and resync_start becomes | 
|  | *    MaxSector | 
|  | *    Notify metadata that sync is complete. | 
|  | * | 
|  | *  recovery completes | 
|  | *    sync_action changes from 'recover' to 'idle' | 
|  | *    Check each device state and mark metadata if 'faulty' or 'in_sync'. | 
|  | * | 
|  | *  deal with resync | 
|  | *    This only happens on finding a new array... mdadm will have set | 
|  | *    'resync_start' to the correct value.  If 'resync_start' indicates that an | 
|  | *    resync needs to occur set the array to the 'active' state rather than the | 
|  | *    initial read-auto state. | 
|  | * | 
|  | * | 
|  | * | 
|  | * We wait for a change (poll/select) on array_state, sync_action, and | 
|  | * each rd-X/state file. | 
|  | * When we get any change, we check everything.  So read each state file, | 
|  | * then decide what to do. | 
|  | * | 
|  | * The core action is to write new metadata to all devices in the array. | 
|  | * This is done at most once on any wakeup. | 
|  | * After that we might: | 
|  | *   - update the array_state | 
|  | *   - set the role of some devices. | 
|  | *   - request a sync_action | 
|  | * | 
|  | */ | 
|  |  | 
|  | #define ARRAY_DIRTY 1 | 
|  | #define ARRAY_BUSY 2 | 
|  | static int read_and_act(struct active_array *a) | 
|  | { | 
|  | unsigned long long sync_completed; | 
|  | int check_degraded = 0; | 
|  | int check_reshape = 0; | 
|  | int deactivate = 0; | 
|  | struct mdinfo *mdi; | 
|  | int ret = 0; | 
|  | int count = 0; | 
|  |  | 
|  | a->next_state = bad_word; | 
|  | a->next_action = bad_action; | 
|  |  | 
|  | 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; | 
|  | mdi->curr_state = 0; | 
|  | if (mdi->state_fd >= 0) { | 
|  | mdi->recovery_start = read_resync_start(mdi->recovery_fd); | 
|  | mdi->curr_state = read_dev_state(mdi->state_fd); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (a->curr_state > inactive && | 
|  | a->prev_state == inactive) { | 
|  | /* array has been started | 
|  | * possible that container operation has to be completed | 
|  | */ | 
|  | a->container->ss->set_array_state(a, 0); | 
|  | } | 
|  | if (a->curr_state <= inactive && | 
|  | a->prev_state > inactive) { | 
|  | /* array has been stopped */ | 
|  | a->container->ss->set_array_state(a, 1); | 
|  | a->next_state = clear; | 
|  | deactivate = 1; | 
|  | } | 
|  | if (a->curr_state == write_pending) { | 
|  | a->container->ss->set_array_state(a, 0); | 
|  | a->next_state = active; | 
|  | ret |= ARRAY_DIRTY; | 
|  | } | 
|  | if (a->curr_state == active_idle) { | 
|  | /* Set array to 'clean' FIRST, then mark clean | 
|  | * in the metadata | 
|  | */ | 
|  | a->next_state = clean; | 
|  | ret |= ARRAY_DIRTY; | 
|  | } | 
|  | if (a->curr_state == clean) { | 
|  | a->container->ss->set_array_state(a, 1); | 
|  | } | 
|  | if (a->curr_state == active || | 
|  | a->curr_state == suspended || | 
|  | a->curr_state == bad_word) | 
|  | ret |= ARRAY_DIRTY; | 
|  | if (a->curr_state == readonly) { | 
|  | /* Well, I'm ready to handle things.  If readonly | 
|  | * wasn't requested, transition to read-auto. | 
|  | */ | 
|  | char buf[64]; | 
|  | read_attr(buf, sizeof(buf), a->metadata_fd); | 
|  | if (strncmp(buf, "external:-", 10) == 0) { | 
|  | /* explicit request for readonly array.  Leave it alone */ | 
|  | ; | 
|  | } else { | 
|  | if (a->container->ss->set_array_state(a, 2)) | 
|  | a->next_state = read_auto; /* array is clean */ | 
|  | else { | 
|  | a->next_state = active; /* Now active for recovery etc */ | 
|  | ret |= ARRAY_DIRTY; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!deactivate && | 
|  | a->curr_action == idle && | 
|  | a->prev_action == resync) { | 
|  | /* A resync has finished.  The endpoint is recorded in | 
|  | * 'sync_start'.  We don't update the metadata | 
|  | * until the array goes inactive or readonly though. | 
|  | * Just check if we need to fiddle spares. | 
|  | */ | 
|  | a->container->ss->set_array_state(a, a->curr_state <= clean); | 
|  | check_degraded = 1; | 
|  | } | 
|  |  | 
|  | if (!deactivate && | 
|  | a->curr_action == idle && | 
|  | a->prev_action == recover) { | 
|  | /* A recovery has finished.  Some disks may be in sync now, | 
|  | * and the array may no longer be degraded | 
|  | */ | 
|  | for (mdi = a->info.devs ; mdi ; mdi = mdi->next) { | 
|  | a->container->ss->set_disk(a, mdi->disk.raid_disk, | 
|  | mdi->curr_state); | 
|  | if (! (mdi->curr_state & DS_INSYNC)) | 
|  | check_degraded = 1; | 
|  | count++; | 
|  | } | 
|  | if (count != a->info.array.raid_disks) | 
|  | check_degraded = 1; | 
|  | } | 
|  |  | 
|  | if (!deactivate && | 
|  | a->curr_action == reshape && | 
|  | a->prev_action != reshape) | 
|  | /* reshape was requested by mdadm.  Need to see if | 
|  | * new devices have been added.  Manager does that | 
|  | * when it sees check_reshape | 
|  | */ | 
|  | check_reshape = 1; | 
|  |  | 
|  | /* Check for failures and if found: | 
|  | * 1/ Record the failure in the metadata and unblock the device. | 
|  | *    FIXME update the kernel to stop notifying on failed drives when | 
|  | *    the array is readonly and we have cleared 'blocked' | 
|  | * 2/ Try to remove the device if the array is writable, or can be | 
|  | *    made writable. | 
|  | */ | 
|  | for (mdi = a->info.devs ; mdi ; mdi = mdi->next) { | 
|  | if (mdi->curr_state & DS_FAULTY) { | 
|  | a->container->ss->set_disk(a, mdi->disk.raid_disk, | 
|  | mdi->curr_state); | 
|  | check_degraded = 1; | 
|  | if (mdi->curr_state & DS_BLOCKED) | 
|  | mdi->next_state |= DS_UNBLOCK; | 
|  | if (a->curr_state == read_auto) { | 
|  | a->container->ss->set_array_state(a, 0); | 
|  | a->next_state = active; | 
|  | } | 
|  | if (a->curr_state > readonly) | 
|  | mdi->next_state |= DS_REMOVE; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* 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 handled | 
|  | * in the second branch. | 
|  | */ | 
|  | 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 ((a->curr_action == idle && a->prev_action == reshape) || | 
|  | (a->curr_action == reshape | 
|  | && sync_completed > a->last_checkpoint) ) { | 
|  | /* Reshape has progressed or completed so we need to | 
|  | * update the array state - and possibly the array size | 
|  | */ | 
|  | if (sync_completed != 0) | 
|  | a->last_checkpoint = sync_completed; | 
|  | /* We might need to update last_checkpoint depending on | 
|  | * the reason that reshape finished. | 
|  | * if array reshape is really finished: | 
|  | *        set check point to the end, this allows | 
|  | *        set_array_state() to finalize reshape in metadata | 
|  | * if reshape if broken: do not set checkpoint to the end | 
|  | *        this allows for reshape restart from checkpoint | 
|  | */ | 
|  | if ((a->curr_action != reshape) && | 
|  | (a->prev_action == reshape)) { | 
|  | char buf[40]; | 
|  | if ((sysfs_get_str(&a->info, NULL, | 
|  | "reshape_position", | 
|  | buf, | 
|  | sizeof(buf)) >= 0) && | 
|  | strncmp(buf, "none", 4) == 0) | 
|  | a->last_checkpoint = a->info.component_size; | 
|  | } | 
|  | a->container->ss->set_array_state(a, a->curr_state <= clean); | 
|  | a->last_checkpoint = sync_completed; | 
|  | } | 
|  |  | 
|  | 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]); | 
|  |  | 
|  | /* Effect state changes in the array */ | 
|  | if (a->next_state != bad_word) { | 
|  | dprintf(" state:%s", array_states[a->next_state]); | 
|  | write_attr(array_states[a->next_state], a->info.state_fd); | 
|  | } | 
|  | if (a->next_action != bad_action) { | 
|  | write_attr(sync_actions[a->next_action], a->action_fd); | 
|  | dprintf(" action:%s", sync_actions[a->next_action]); | 
|  | } | 
|  | for (mdi = a->info.devs; mdi ; mdi = mdi->next) { | 
|  | if (mdi->next_state & DS_UNBLOCK) { | 
|  | dprintf(" %d:-blocked", mdi->disk.raid_disk); | 
|  | write_attr("-blocked", mdi->state_fd); | 
|  | } | 
|  |  | 
|  | if ((mdi->next_state & DS_REMOVE) && mdi->state_fd >= 0) { | 
|  | int remove_result; | 
|  |  | 
|  | /* The kernel may not be able to immediately remove the | 
|  | * disk.  In that case we wait a little while and | 
|  | * try again. | 
|  | */ | 
|  | remove_result = write_attr("remove", mdi->state_fd); | 
|  | if (remove_result > 0) { | 
|  | dprintf(" %d:removed", mdi->disk.raid_disk); | 
|  | close(mdi->state_fd); | 
|  | close(mdi->recovery_fd); | 
|  | mdi->state_fd = -1; | 
|  | } else | 
|  | ret |= ARRAY_BUSY; | 
|  | } | 
|  | if (mdi->next_state & DS_INSYNC) { | 
|  | write_attr("+in_sync", mdi->state_fd); | 
|  | dprintf(" %d:+in_sync", mdi->disk.raid_disk); | 
|  | } | 
|  | } | 
|  | dprintf(" )\n"); | 
|  |  | 
|  | /* move curr_ to prev_ */ | 
|  | a->prev_state = a->curr_state; | 
|  |  | 
|  | a->prev_action = a->curr_action; | 
|  |  | 
|  | for (mdi = a->info.devs; mdi ; mdi = mdi->next) { | 
|  | mdi->prev_state = mdi->curr_state; | 
|  | mdi->next_state = 0; | 
|  | } | 
|  |  | 
|  | if (check_degraded || check_reshape) { | 
|  | /* manager will do the actual check */ | 
|  | if (check_degraded) | 
|  | a->check_degraded = 1; | 
|  | if (check_reshape) | 
|  | a->check_reshape = 1; | 
|  | signal_manager(); | 
|  | } | 
|  |  | 
|  | if (deactivate) | 
|  | a->container = NULL; | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static struct mdinfo * | 
|  | find_device(struct active_array *a, int major, int minor) | 
|  | { | 
|  | struct mdinfo *mdi; | 
|  |  | 
|  | for (mdi = a->info.devs ; mdi ; mdi = mdi->next) | 
|  | if (mdi->disk.major == major && mdi->disk.minor == minor) | 
|  | return mdi; | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | static void reconcile_failed(struct active_array *aa, struct mdinfo *failed) | 
|  | { | 
|  | struct active_array *a; | 
|  | struct mdinfo *victim; | 
|  |  | 
|  | for (a = aa; a; a = a->next) { | 
|  | if (!a->container || a->to_remove) | 
|  | continue; | 
|  | victim = find_device(a, failed->disk.major, failed->disk.minor); | 
|  | if (!victim) | 
|  | continue; | 
|  |  | 
|  | if (!(victim->curr_state & DS_FAULTY)) | 
|  | write_attr("faulty", victim->state_fd); | 
|  | } | 
|  | } | 
|  |  | 
|  | #ifdef DEBUG | 
|  | static void dprint_wake_reasons(fd_set *fds) | 
|  | { | 
|  | int i; | 
|  | char proc_path[256]; | 
|  | char link[256]; | 
|  | char *basename; | 
|  | int rv; | 
|  |  | 
|  | fprintf(stderr, "monitor: wake ( "); | 
|  | for (i = 0; i < FD_SETSIZE; i++) { | 
|  | if (FD_ISSET(i, fds)) { | 
|  | sprintf(proc_path, "/proc/%d/fd/%d", | 
|  | (int) getpid(), i); | 
|  |  | 
|  | rv = readlink(proc_path, link, sizeof(link) - 1); | 
|  | if (rv < 0) { | 
|  | fprintf(stderr, "%d:unknown ", i); | 
|  | continue; | 
|  | } | 
|  | link[rv] = '\0'; | 
|  | basename = strrchr(link, '/'); | 
|  | fprintf(stderr, "%d:%s ", | 
|  | i, basename ? ++basename : link); | 
|  | } | 
|  | } | 
|  | fprintf(stderr, ")\n"); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | int monitor_loop_cnt; | 
|  |  | 
|  | static int wait_and_act(struct supertype *container, int nowait) | 
|  | { | 
|  | fd_set rfds; | 
|  | int maxfd = 0; | 
|  | struct active_array **aap = &container->arrays; | 
|  | struct active_array *a, **ap; | 
|  | int rv; | 
|  | struct mdinfo *mdi; | 
|  | static unsigned int dirty_arrays = ~0; /* start at some non-zero value */ | 
|  |  | 
|  | FD_ZERO(&rfds); | 
|  |  | 
|  | for (ap = aap ; *ap ;) { | 
|  | a = *ap; | 
|  | /* once an array has been deactivated we want to | 
|  | * ask the manager to discard it. | 
|  | */ | 
|  | if (!a->container || a->to_remove) { | 
|  | if (discard_this) { | 
|  | ap = &(*ap)->next; | 
|  | continue; | 
|  | } | 
|  | *ap = a->next; | 
|  | a->next = NULL; | 
|  | discard_this = a; | 
|  | signal_manager(); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | 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); | 
|  |  | 
|  | ap = &(*ap)->next; | 
|  | } | 
|  |  | 
|  | if (manager_ready && (*aap == NULL || (sigterm && !dirty_arrays))) { | 
|  | /* No interesting arrays, or we have been told to | 
|  | * terminate and everything is clean.  Lets see about | 
|  | * exiting.  Note that blocking at this point is not a | 
|  | * problem as there are no active arrays, there is | 
|  | * nothing that we need to be ready to do. | 
|  | */ | 
|  | int fd; | 
|  | if (sigterm) | 
|  | fd = open_dev_excl(container->devnum); | 
|  | else | 
|  | fd = open_dev_flags(container->devnum, O_RDONLY|O_EXCL); | 
|  | if (fd >= 0 || errno != EBUSY) { | 
|  | /* OK, we are safe to leave */ | 
|  | if (sigterm && !dirty_arrays) | 
|  | dprintf("caught sigterm, all clean... exiting\n"); | 
|  | else | 
|  | dprintf("no arrays to monitor... exiting\n"); | 
|  | if (!sigterm) | 
|  | /* On SIGTERM, someone (the take-over mdmon) will | 
|  | * clean up | 
|  | */ | 
|  | remove_pidfile(container->devname); | 
|  | exit_now = 1; | 
|  | signal_manager(); | 
|  | close(fd); | 
|  | exit(0); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!nowait) { | 
|  | sigset_t set; | 
|  | struct timespec ts; | 
|  | ts.tv_sec = 24*3600; | 
|  | ts.tv_nsec = 0; | 
|  | if (*aap == NULL || container->retry_soon) { | 
|  | /* just waiting to get O_EXCL access */ | 
|  | ts.tv_sec = 0; | 
|  | ts.tv_nsec = 20000000ULL; | 
|  | } | 
|  | sigprocmask(SIG_UNBLOCK, NULL, &set); | 
|  | sigdelset(&set, SIGUSR1); | 
|  | monitor_loop_cnt |= 1; | 
|  | rv = pselect(maxfd+1, NULL, NULL, &rfds, &ts, &set); | 
|  | monitor_loop_cnt += 1; | 
|  | if (rv == -1 && errno == EINTR) | 
|  | rv = 0; | 
|  | #ifdef DEBUG | 
|  | dprint_wake_reasons(&rfds); | 
|  | #endif | 
|  | container->retry_soon = 0; | 
|  | } | 
|  |  | 
|  | if (update_queue) { | 
|  | struct metadata_update *this; | 
|  |  | 
|  | for (this = update_queue; this ; this = this->next) | 
|  | container->ss->process_update(container, this); | 
|  |  | 
|  | update_queue_handled = update_queue; | 
|  | update_queue = NULL; | 
|  | signal_manager(); | 
|  | container->ss->sync_metadata(container); | 
|  | } | 
|  |  | 
|  | rv = 0; | 
|  | dirty_arrays = 0; | 
|  | for (a = *aap; a ; a = a->next) { | 
|  |  | 
|  | if (a->replaces && !discard_this) { | 
|  | struct active_array **ap; | 
|  | for (ap = &a->next; *ap && *ap != a->replaces; | 
|  | ap = & (*ap)->next) | 
|  | ; | 
|  | if (*ap) | 
|  | *ap = (*ap)->next; | 
|  | discard_this = a->replaces; | 
|  | a->replaces = NULL; | 
|  | /* FIXME check if device->state_fd need to be cleared?*/ | 
|  | signal_manager(); | 
|  | } | 
|  | if (a->container && !a->to_remove) { | 
|  | int ret = read_and_act(a); | 
|  | rv |= 1; | 
|  | dirty_arrays += !!(ret & ARRAY_DIRTY); | 
|  | /* when terminating stop manipulating the array after it | 
|  | * is clean, but make sure read_and_act() is given a | 
|  | * chance to handle 'active_idle' | 
|  | */ | 
|  | if (sigterm && !(ret & ARRAY_DIRTY)) | 
|  | a->container = NULL; /* stop touching this array */ | 
|  | if (ret & ARRAY_BUSY) | 
|  | container->retry_soon = 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* propagate failures across container members */ | 
|  | for (a = *aap; a ; a = a->next) { | 
|  | if (!a->container || a->to_remove) | 
|  | continue; | 
|  | for (mdi = a->info.devs ; mdi ; mdi = mdi->next) | 
|  | if (mdi->curr_state & DS_FAULTY) | 
|  | reconcile_failed(*aap, mdi); | 
|  | } | 
|  |  | 
|  | return rv; | 
|  | } | 
|  |  | 
|  | void do_monitor(struct supertype *container) | 
|  | { | 
|  | int rv; | 
|  | int first = 1; | 
|  | do { | 
|  | rv = wait_and_act(container, first); | 
|  | first = 0; | 
|  | } while (rv >= 0); | 
|  | } |