]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - monitor.c
Create.c: fix uclibc build
[thirdparty/mdadm.git] / monitor.c
index 47432b25f8a2774f5a80760e0a9c6e46fae5c5ee..be0bec785080351412f4a1fcd07e031e1d356b68 100644 (file)
--- a/monitor.c
+++ b/monitor.c
 #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 };
+       "clean", "active", "write-pending", "active-idle", "broken", NULL };
 static char *sync_actions[] = {
        "idle", "reshape", "resync", "recover", "check", "repair", NULL
 };
 
+enum bb_action {
+       RECORD_BB = 1,
+       COMPARE_BB,
+};
+
 static int write_attr(char *attr, int fd)
 {
        return write(fd, attr, strlen(attr));
@@ -42,11 +46,11 @@ static void add_fd(fd_set *fds, int *maxfd, int fd)
        if (fd < 0)
                return;
        if (fstat(fd, &st) == -1) {
-               dprintf("%s: Invalid fd %d\n", __func__, fd);
+               dprintf("Invalid fd %d\n", fd);
                return;
        }
        if (st.st_nlink == 0) {
-               dprintf("%s: fd %d was deleted\n", __func__, fd);
+               dprintf("fd %d was deleted\n", fd);
                return;
        }
        if (fd > *maxfd)
@@ -77,16 +81,15 @@ static int read_attr(char *buf, int len, int fd)
 
 static void read_resync_start(int fd, unsigned long long *v)
 {
-       char buf[30];
+       char buf[SYSFS_MAX_BUF_SIZE];
        int n;
 
-       n = read_attr(buf, 30, fd);
+       n = read_attr(buf, sizeof(buf), fd);
        if (n <= 0) {
-               dprintf("%s: Failed to read resync_start (%d)\n",
-                       __func__, fd);
+               dprintf("Failed to read resync_start (%d)\n", fd);
                return;
        }
-       if (strncmp(buf, "none", 4) == 0)
+       if (str_is_none(buf) == true)
                *v = MaxSector;
        else
                *v = strtoull(buf, NULL, 10);
@@ -95,11 +98,11 @@ static void read_resync_start(int fd, unsigned long long *v)
 static unsigned long long read_sync_completed(int fd)
 {
        unsigned long long val;
-       char buf[50];
+       char buf[SYSFS_MAX_BUF_SIZE];
        int n;
        char *ep;
 
-       n = read_attr(buf, 50, fd);
+       n = read_attr(buf, sizeof(buf), fd);
 
        if (n <= 0)
                return 0;
@@ -112,8 +115,8 @@ static unsigned long long read_sync_completed(int fd)
 
 static enum array_state read_state(int fd)
 {
-       char buf[20];
-       int n = read_attr(buf, 20, fd);
+       char buf[SYSFS_MAX_BUF_SIZE];
+       int n = read_attr(buf, sizeof(buf), fd);
 
        if (n <= 0)
                return bad_word;
@@ -122,8 +125,8 @@ static enum array_state read_state(int fd)
 
 static enum sync_action read_action( int fd)
 {
-       char buf[20];
-       int n = read_attr(buf, 20, fd);
+       char buf[SYSFS_MAX_BUF_SIZE];
+       int n = read_attr(buf, sizeof(buf), fd);
 
        if (n <= 0)
                return bad_action;
@@ -132,8 +135,8 @@ static enum sync_action read_action( int fd)
 
 int read_dev_state(int fd)
 {
-       char buf[60];
-       int n = read_attr(buf, 60, fd);
+       char buf[SYSFS_MAX_BUF_SIZE];
+       int n = read_attr(buf, sizeof(buf), fd);
        char *cp;
        int rv = 0;
 
@@ -159,6 +162,180 @@ int read_dev_state(int fd)
        return rv;
 }
 
+int process_ubb(struct active_array *a, struct mdinfo *mdi, const unsigned long
+               long sector, const int length, const char *buf,
+               const int buf_len)
+{
+       struct superswitch *ss = a->container->ss;
+
+       /*
+        * record bad block in metadata first, then acknowledge it to the driver
+        * via sysfs file
+        */
+       if ((ss->record_bad_block(a, mdi->disk.raid_disk, sector, length)) &&
+           (write(mdi->bb_fd, buf, buf_len) == buf_len))
+               return 1;
+
+       /*
+        * failed to store or acknowledge bad block, switch of bad block support
+        * to get it out of blocked state
+        */
+       sysfs_set_str(&a->info, mdi, "state", "-external_bbl");
+       return -1;
+}
+
+int compare_bb(struct active_array *a, struct mdinfo *mdi, const unsigned long
+              long sector, const unsigned int length, void *arg)
+{
+       struct superswitch *ss = a->container->ss;
+       struct md_bb *bb = (struct md_bb *) arg;
+       int record = 1;
+       int i;
+
+       for (i = 0; i < bb->count; i++) {
+               unsigned long long start = bb->entries[i].sector;
+               unsigned long long len = bb->entries[i].length;
+
+               /*
+                * bad block in metadata exactly matches bad block in kernel
+                * list, just remove it from a list
+                */
+               if ((start == sector) && (len == length)) {
+                       if (i < bb->count - 1)
+                               bb->entries[i] = bb->entries[bb->count - 1];
+                       bb->count -= 1;
+                       record = 0;
+                       break;
+               }
+               /*
+                * bad block in metadata spans bad block in kernel list,
+                * clear it and record new bad block
+                */
+               if ((sector >= start) && (sector + length <= start + len)) {
+                       ss->clear_bad_block(a, mdi->disk.raid_disk, start, len);
+                       break;
+               }
+       }
+
+       /* record all bad blocks not in metadata list */
+       if (record && (ss->record_bad_block(a, mdi->disk.raid_disk, sector,
+                                            length) <= 0)) {
+               sysfs_set_str(&a->info, mdi, "state", "-external_bbl");
+               return -1;
+       }
+
+       return 1;
+}
+
+static int read_bb_file(int fd, struct active_array *a, struct mdinfo *mdi,
+                       enum bb_action action, void *arg)
+{
+       char buf[30];
+       int n = 0;
+       int ret = 0;
+       int read_again = 0;
+       int off = 0;
+       int pos = 0;
+       int preserve_pos = (action == RECORD_BB ? 0 : 1);
+
+       if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
+               return -1;
+
+       do {
+               read_again = 0;
+               n = read(fd, buf + pos, sizeof(buf) - 1 - pos);
+               if (n < 0)
+                       return -1;
+               n += pos;
+
+               buf[n] = '\0';
+               off = 0;
+
+               while (off < n) {
+                       unsigned long long sector;
+                       int length;
+                       char newline;
+                       int consumed;
+                       int matched;
+                       int rc;
+
+                       /* kernel sysfs file format: "sector length\n" */
+                       matched = sscanf(buf + off, "%llu %d%c%n", &sector,
+                                        &length, &newline, &consumed);
+                       if ((matched != 3) && (off > 0)) {
+                               /* truncated entry, read again */
+                               if (preserve_pos) {
+                                       pos = sizeof(buf) - off - 1;
+                                       memmove(buf, buf + off, pos);
+                               } else {
+                                       if (lseek(fd, 0, SEEK_SET) ==
+                                           (off_t) -1)
+                                               return -1;
+                               }
+                               read_again = 1;
+                               break;
+                       }
+                       if (matched != 3)
+                               return -1;
+                       if (newline != '\n')
+                               return -1;
+                       if (length <= 0)
+                               return -1;
+
+                       if (action == RECORD_BB)
+                               rc = process_ubb(a, mdi, sector, length,
+                                                 buf + off, consumed);
+                       else if (action == COMPARE_BB)
+                               rc = compare_bb(a, mdi, sector, length, arg);
+                       else
+                               rc = -1;
+
+                       if (rc < 0)
+                               return rc;
+                       ret += rc;
+                       off += consumed;
+               }
+       } while (read_again);
+
+       return ret;
+}
+
+static int process_dev_ubb(struct active_array *a, struct mdinfo *mdi)
+{
+       return read_bb_file(mdi->ubb_fd, a, mdi, RECORD_BB, NULL);
+}
+
+static int check_for_cleared_bb(struct active_array *a, struct mdinfo *mdi)
+{
+       struct superswitch *ss = a->container->ss;
+       struct md_bb *bb;
+       int i;
+
+       if (!ss->get_bad_blocks)
+               return -1;
+
+       /*
+        * Get a list of bad blocks for an array, then read list of
+        * acknowledged bad blocks from kernel and compare it against metadata
+        * list, clear all bad blocks remaining in metadata list
+        */
+       bb = ss->get_bad_blocks(a, mdi->disk.raid_disk);
+       if (!bb)
+               return -1;
+
+       if (read_bb_file(mdi->bb_fd, a, mdi, COMPARE_BB, bb) < 0)
+               return -1;
+
+       for (i = 0; i < bb->count; i++) {
+               unsigned long long sector = bb->entries[i].sector;
+               int length = bb->entries[i].length;
+
+               ss->clear_bad_block(a, mdi->disk.raid_disk, sector, length);
+       }
+
+       return 0;
+}
+
 static void signal_manager(void)
 {
        /* tgkill(getpid(), mon_tid, SIGUSR1); */
@@ -225,7 +402,7 @@ static void signal_manager(void)
 
 #define ARRAY_DIRTY 1
 #define ARRAY_BUSY 2
-static int read_and_act(struct active_array *a)
+static int read_and_act(struct active_array *a, fd_set *fds)
 {
        unsigned long long sync_completed;
        int check_degraded = 0;
@@ -234,6 +411,8 @@ static int read_and_act(struct active_array *a)
        struct mdinfo *mdi;
        int ret = 0;
        int count = 0;
+       struct timeval tv;
+       bool write_checkpoint = false;
 
        a->next_state = bad_word;
        a->next_action = bad_action;
@@ -256,15 +435,31 @@ static int read_and_act(struct active_array *a)
                                          &mdi->recovery_start);
                        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
+               /*
+                * If array is blocked and metadata handler is able to handle
+                * BB, check if you can acknowledge them to md driver. If
+                * successful, clear faulty state and unblock the array.
                 */
-               a->container->ss->set_array_state(a, 0);
+               if ((mdi->curr_state & DS_BLOCKED) &&
+                   a->container->ss->record_bad_block &&
+                   (process_dev_ubb(a, mdi) > 0)) {
+                       mdi->next_state |= DS_UNBLOCK;
+               }
+               if (FD_ISSET(mdi->bb_fd, fds))
+                       check_for_cleared_bb(a, mdi);
        }
+
+       gettimeofday(&tv, NULL);
+       dprintf("(%d): %ld.%06ld state:%s prev:%s action:%s prev: %s start:%llu\n",
+               a->info.container_member,
+               tv.tv_sec, tv.tv_usec,
+               array_states[a->curr_state],
+               array_states[a->prev_state],
+               sync_actions[a->curr_action],
+               sync_actions[a->prev_action],
+               a->info.resync_start
+               );
+
        if ((a->curr_state == bad_word || a->curr_state <= inactive) &&
            a->prev_state > inactive) {
                /* array has been stopped */
@@ -284,7 +479,7 @@ static int read_and_act(struct active_array *a)
                a->next_state = clean;
                ret |= ARRAY_DIRTY;
        }
-       if (a->curr_state == clean) {
+       if ((a->curr_state == clean) || (a->curr_state == broken)) {
                a->container->ss->set_array_state(a, 1);
        }
        if (a->curr_state == active ||
@@ -370,68 +565,59 @@ static int read_and_act(struct active_array *a)
                }
        }
 
-       /* 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
-                */
+       /* Update reshape checkpoint, depending if it finished or progressed */
+       if (a->curr_action == idle && a->prev_action == reshape) {
+               char buf[SYSFS_MAX_BUF_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 reshape really finished, set checkpoint to the end to finalize it.
+                * Do not set checkpoint if reshape is broken.
+                * Reshape will restart from last 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)
+               if (sysfs_get_str(&a->info, NULL, "reshape_position", buf, sizeof(buf)) >= 0)
+                       if (str_is_none(buf) == true)
                                a->last_checkpoint = a->info.component_size;
-               }
-               a->container->ss->set_array_state(a, a->curr_state <= clean);
-               a->last_checkpoint = sync_completed;
+
+               write_checkpoint = true;
        }
 
-       if (sync_completed > a->last_checkpoint)
+       if (a->curr_action >= reshape && sync_completed > a->last_checkpoint) {
+               /* Update checkpoint if neither reshape nor idle action */
                a->last_checkpoint = sync_completed;
 
+               write_checkpoint = true;
+       }
+
+       /* Save checkpoint */
+       if (write_checkpoint) {
+               a->container->ss->set_array_state(a, a->curr_state <= clean);
+
+               if (a->curr_action <= reshape)
+                       a->last_checkpoint = sync_completed;
+       }
+
+       if (sync_completed >= a->info.component_size)
+               a->last_checkpoint = 0;
+
        a->container->ss->sync_metadata(a->container);
-       dprintf("%s(%d): state:%s action:%s next(", __func__, a->info.container_member,
+       dprintf("(%d): state:%s action:%s next(", 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]);
+               dprintf_cont(" 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]);
+               dprintf_cont(" 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);
+                       dprintf_cont(" %d:-blocked", mdi->disk.raid_disk);
                        write_attr("-blocked", mdi->state_fd);
                }
 
@@ -444,19 +630,21 @@ static int read_and_act(struct active_array *a)
                         */
                        remove_result = write_attr("remove", mdi->state_fd);
                        if (remove_result > 0) {
-                               dprintf(" %d:removed", mdi->disk.raid_disk);
+                               dprintf_cont(" %d:removed", mdi->disk.raid_disk);
                                close(mdi->state_fd);
                                close(mdi->recovery_fd);
+                               close(mdi->bb_fd);
+                               close(mdi->ubb_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_cont(" %d:+in_sync", mdi->disk.raid_disk);
                }
        }
-       dprintf(" )\n");
+       dprintf_cont(" )\n");
 
        /* move curr_ to prev_ */
        a->prev_state = a->curr_state;
@@ -576,8 +764,11 @@ static int wait_and_act(struct supertype *container, int nowait)
                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)
+               for (mdi = a->info.devs ; mdi ; mdi = mdi->next) {
                        add_fd(&rfds, &maxfd, mdi->state_fd);
+                       add_fd(&rfds, &maxfd, mdi->bb_fd);
+                       add_fd(&rfds, &maxfd, mdi->ubb_fd);
+               }
 
                ap = &(*ap)->next;
        }
@@ -627,10 +818,18 @@ static int wait_and_act(struct supertype *container, int nowait)
                monitor_loop_cnt |= 1;
                rv = pselect(maxfd+1, NULL, NULL, &rfds, &ts, &set);
                monitor_loop_cnt += 1;
-               if (rv == -1 && errno == EINTR)
-                       rv = 0;
+               if (rv == -1) {
+                       if (errno == EINTR) {
+                               rv = 0;
+                               FD_ZERO(&rfds);
+                               dprintf("monitor: caught signal\n");
+                       } else
+                               dprintf("monitor: error %d in pselect\n",
+                                       errno);
+               }
                #ifdef DEBUG
-               dprint_wake_reasons(&rfds);
+               else
+                       dprint_wake_reasons(&rfds);
                #endif
                container->retry_soon = 0;
        }
@@ -664,7 +863,7 @@ static int wait_and_act(struct supertype *container, int nowait)
                        signal_manager();
                }
                if (a->container && !a->to_remove) {
-                       int ret = read_and_act(a);
+                       int ret = read_and_act(a, &rfds);
                        rv |= 1;
                        dirty_arrays += !!(ret & ARRAY_DIRTY);
                        /* when terminating stop manipulating the array after it