]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - monitor.c
mdmon: bad block support for external metadata - store bad blocks
[thirdparty/mdadm.git] / monitor.c
index 60c5d5a286eb1b3a5210def0f0532edc7c2c4bff..872cc16dc72435af2f5f97a801bb5b30547278b7 100644 (file)
--- a/monitor.c
+++ b/monitor.c
@@ -31,6 +31,10 @@ static char *sync_actions[] = {
        "idle", "reshape", "resync", "recover", "check", "repair", NULL
 };
 
+enum bb_action {
+       RECORD_BB = 1,
+};
+
 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)
@@ -82,8 +86,7 @@ static void read_resync_start(int fd, unsigned long long *v)
 
        n = read_attr(buf, 30, 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)
@@ -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[100];
+       int n = read_attr(buf, sizeof(buf), fd);
        char *cp;
        int rv = 0;
 
@@ -159,6 +162,104 @@ 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;
+}
+
+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
+                               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 void signal_manager(void)
 {
        /* tgkill(getpid(), mon_tid, SIGUSR1); */
@@ -234,6 +335,7 @@ static int read_and_act(struct active_array *a)
        struct mdinfo *mdi;
        int ret = 0;
        int count = 0;
+       struct timeval tv;
 
        a->next_state = bad_word;
        a->next_action = bad_action;
@@ -256,16 +358,30 @@ 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 (a->curr_state <= inactive &&
+
+       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 */
                a->container->ss->set_array_state(a, 1);
@@ -288,8 +404,7 @@ static int read_and_act(struct active_array *a)
                a->container->ss->set_array_state(a, 1);
        }
        if (a->curr_state == active ||
-           a->curr_state == suspended ||
-           a->curr_state == bad_word)
+           a->curr_state == suspended)
                ret |= ARRAY_DIRTY;
        if (a->curr_state == readonly) {
                /* Well, I'm ready to handle things.  If readonly
@@ -417,22 +532,25 @@ static int read_and_act(struct active_array *a)
        if (sync_completed > a->last_checkpoint)
                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);
                }
 
@@ -445,19 +563,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;
@@ -577,8 +697,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;
        }
@@ -628,10 +751,17 @@ 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;
+                               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;
        }