]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/md/md.c
Merge branch 'acpi-tables'
[linux.git] / drivers / md / md.c
index 24638ccedce42b8d9fb346fc7a01ff8622f4c26d..1be7abeb24fdc4a4bdf736a9e223db546fd4fceb 100644 (file)
@@ -376,6 +376,11 @@ static blk_qc_t md_make_request(struct request_queue *q, struct bio *bio)
        struct mddev *mddev = q->queuedata;
        unsigned int sectors;
 
+       if (unlikely(test_bit(MD_BROKEN, &mddev->flags)) && (rw == WRITE)) {
+               bio_io_error(bio);
+               return BLK_QC_T_NONE;
+       }
+
        blk_queue_split(q, &bio);
 
        if (mddev == NULL || mddev->pers == NULL) {
@@ -1232,6 +1237,8 @@ static int super_90_validate(struct mddev *mddev, struct md_rdev *rdev)
                        mddev->new_layout = mddev->layout;
                        mddev->new_chunk_sectors = mddev->chunk_sectors;
                }
+               if (mddev->level == 0)
+                       mddev->layout = -1;
 
                if (sb->state & (1<<MD_SB_CLEAN))
                        mddev->recovery_cp = MaxSector;
@@ -1647,6 +1654,10 @@ static int super_1_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor_
                rdev->ppl.sector = rdev->sb_start + rdev->ppl.offset;
        }
 
+       if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_RAID0_LAYOUT) &&
+           sb->level != 0)
+               return -EINVAL;
+
        if (!refdev) {
                ret = 1;
        } else {
@@ -1757,6 +1768,10 @@ static int super_1_validate(struct mddev *mddev, struct md_rdev *rdev)
                        mddev->new_chunk_sectors = mddev->chunk_sectors;
                }
 
+               if (mddev->level == 0 &&
+                   !(le32_to_cpu(sb->feature_map) & MD_FEATURE_RAID0_LAYOUT))
+                       mddev->layout = -1;
+
                if (le32_to_cpu(sb->feature_map) & MD_FEATURE_JOURNAL)
                        set_bit(MD_HAS_JOURNAL, &mddev->flags);
 
@@ -1826,8 +1841,15 @@ static int super_1_validate(struct mddev *mddev, struct md_rdev *rdev)
                                if (!(le32_to_cpu(sb->feature_map) &
                                      MD_FEATURE_RECOVERY_BITMAP))
                                        rdev->saved_raid_disk = -1;
-                       } else
-                               set_bit(In_sync, &rdev->flags);
+                       } else {
+                               /*
+                                * If the array is FROZEN, then the device can't
+                                * be in_sync with rest of array.
+                                */
+                               if (!test_bit(MD_RECOVERY_FROZEN,
+                                             &mddev->recovery))
+                                       set_bit(In_sync, &rdev->flags);
+                       }
                        rdev->raid_disk = role;
                        break;
                }
@@ -3664,11 +3686,7 @@ int strict_strtoul_scaled(const char *cp, unsigned long *res, int scale)
                return -EINVAL;
        if (decimals < 0)
                decimals = 0;
-       while (decimals < scale) {
-               result *= 10;
-               decimals ++;
-       }
-       *res = result;
+       *res = result * int_pow(10, scale - decimals);
        return 0;
 }
 
@@ -4155,12 +4173,17 @@ __ATTR_PREALLOC(resync_start, S_IRUGO|S_IWUSR,
  * active-idle
  *     like active, but no writes have been seen for a while (100msec).
  *
+ * broken
+ *     RAID0/LINEAR-only: same as clean, but array is missing a member.
+ *     It's useful because RAID0/LINEAR mounted-arrays aren't stopped
+ *     when a member is gone, so this state will at least alert the
+ *     user that something is wrong.
  */
 enum array_state { clear, inactive, suspended, readonly, read_auto, clean, active,
-                  write_pending, active_idle, bad_word};
+                  write_pending, active_idle, broken, bad_word};
 static char *array_states[] = {
        "clear", "inactive", "suspended", "readonly", "read-auto", "clean", "active",
-       "write-pending", "active-idle", NULL };
+       "write-pending", "active-idle", "broken", NULL };
 
 static int match_word(const char *word, char **list)
 {
@@ -4176,7 +4199,7 @@ array_state_show(struct mddev *mddev, char *page)
 {
        enum array_state st = inactive;
 
-       if (mddev->pers)
+       if (mddev->pers && !test_bit(MD_NOT_READY, &mddev->flags)) {
                switch(mddev->ro) {
                case 1:
                        st = readonly;
@@ -4196,7 +4219,10 @@ array_state_show(struct mddev *mddev, char *page)
                                st = active;
                        spin_unlock(&mddev->lock);
                }
-       else {
+
+               if (test_bit(MD_BROKEN, &mddev->flags) && st == clean)
+                       st = broken;
+       } else {
                if (list_empty(&mddev->disks) &&
                    mddev->raid_disks == 0 &&
                    mddev->dev_sectors == 0)
@@ -4310,6 +4336,7 @@ array_state_store(struct mddev *mddev, const char *buf, size_t len)
                break;
        case write_pending:
        case active_idle:
+       case broken:
                /* these cannot be set */
                break;
        }
@@ -5182,6 +5209,34 @@ static struct md_sysfs_entry md_consistency_policy =
 __ATTR(consistency_policy, S_IRUGO | S_IWUSR, consistency_policy_show,
        consistency_policy_store);
 
+static ssize_t fail_last_dev_show(struct mddev *mddev, char *page)
+{
+       return sprintf(page, "%d\n", mddev->fail_last_dev);
+}
+
+/*
+ * Setting fail_last_dev to true to allow last device to be forcibly removed
+ * from RAID1/RAID10.
+ */
+static ssize_t
+fail_last_dev_store(struct mddev *mddev, const char *buf, size_t len)
+{
+       int ret;
+       bool value;
+
+       ret = kstrtobool(buf, &value);
+       if (ret)
+               return ret;
+
+       if (value != mddev->fail_last_dev)
+               mddev->fail_last_dev = value;
+
+       return len;
+}
+static struct md_sysfs_entry md_fail_last_dev =
+__ATTR(fail_last_dev, S_IRUGO | S_IWUSR, fail_last_dev_show,
+       fail_last_dev_store);
+
 static struct attribute *md_default_attrs[] = {
        &md_level.attr,
        &md_layout.attr,
@@ -5198,6 +5253,7 @@ static struct attribute *md_default_attrs[] = {
        &md_array_size.attr,
        &max_corr_read_errors.attr,
        &md_consistency_policy.attr,
+       &md_fail_last_dev.attr,
        NULL,
 };
 
@@ -5744,9 +5800,6 @@ int md_run(struct mddev *mddev)
                md_update_sb(mddev, 0);
 
        md_new_event(mddev);
-       sysfs_notify_dirent_safe(mddev->sysfs_state);
-       sysfs_notify_dirent_safe(mddev->sysfs_action);
-       sysfs_notify(&mddev->kobj, NULL, "degraded");
        return 0;
 
 bitmap_abort:
@@ -5767,6 +5820,7 @@ static int do_md_run(struct mddev *mddev)
 {
        int err;
 
+       set_bit(MD_NOT_READY, &mddev->flags);
        err = md_run(mddev);
        if (err)
                goto out;
@@ -5787,9 +5841,14 @@ static int do_md_run(struct mddev *mddev)
 
        set_capacity(mddev->gendisk, mddev->array_sectors);
        revalidate_disk(mddev->gendisk);
+       clear_bit(MD_NOT_READY, &mddev->flags);
        mddev->changed = 1;
        kobject_uevent(&disk_to_dev(mddev->gendisk)->kobj, KOBJ_CHANGE);
+       sysfs_notify_dirent_safe(mddev->sysfs_state);
+       sysfs_notify_dirent_safe(mddev->sysfs_action);
+       sysfs_notify(&mddev->kobj, NULL, "degraded");
 out:
+       clear_bit(MD_NOT_READY, &mddev->flags);
        return err;
 }
 
@@ -6849,6 +6908,9 @@ static int set_array_info(struct mddev *mddev, mdu_array_info_t *info)
        mddev->external      = 0;
 
        mddev->layout        = info->layout;
+       if (mddev->level == 0)
+               /* Cannot trust RAID0 layout info here */
+               mddev->layout = -1;
        mddev->chunk_sectors = info->chunk_size >> 9;
 
        if (mddev->persistent) {
@@ -8900,6 +8962,7 @@ void md_check_recovery(struct mddev *mddev)
 
        if (mddev_trylock(mddev)) {
                int spares = 0;
+               bool try_set_sync = mddev->safemode != 0;
 
                if (!mddev->external && mddev->safemode == 1)
                        mddev->safemode = 0;
@@ -8945,7 +9008,7 @@ void md_check_recovery(struct mddev *mddev)
                        }
                }
 
-               if (!mddev->external && !mddev->in_sync) {
+               if (try_set_sync && !mddev->external && !mddev->in_sync) {
                        spin_lock(&mddev->lock);
                        set_in_sync(mddev);
                        spin_unlock(&mddev->lock);
@@ -9043,7 +9106,8 @@ void md_reap_sync_thread(struct mddev *mddev)
        /* resync has finished, collect result */
        md_unregister_thread(&mddev->sync_thread);
        if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery) &&
-           !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) {
+           !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery) &&
+           mddev->degraded != mddev->raid_disks) {
                /* success...*/
                /* activate any spares */
                if (mddev->pers->spare_active(mddev)) {