md: fix possible corruption of array metadata on shutdown.
[linux-flexiantxendom0.git] / drivers / md / md.c
index f378b8a..065ab4f 100644 (file)
@@ -44,6 +44,7 @@
 #include <linux/hdreg.h>
 #include <linux/proc_fs.h>
 #include <linux/random.h>
+#include <linux/module.h>
 #include <linux/reboot.h>
 #include <linux/file.h>
 #include <linux/compat.h>
@@ -332,18 +333,17 @@ static DEFINE_SPINLOCK(all_mddevs_lock);
  * call has finished, the bio has been linked into some internal structure
  * and so is visible to ->quiesce(), so we don't need the refcount any more.
  */
-static int md_make_request(struct request_queue *q, struct bio *bio)
+static void md_make_request(struct request_queue *q, struct bio *bio)
 {
        const int rw = bio_data_dir(bio);
        struct mddev *mddev = q->queuedata;
-       int rv;
        int cpu;
        unsigned int sectors;
 
        if (mddev == NULL || mddev->pers == NULL
            || !mddev->ready) {
                bio_io_error(bio);
-               return 0;
+               return;
        }
        smp_rmb(); /* Ensure implications of  'active' are visible */
        rcu_read_lock();
@@ -368,7 +368,7 @@ static int md_make_request(struct request_queue *q, struct bio *bio)
         * go away inside make_request
         */
        sectors = bio_sectors(bio);
-       rv = mddev->pers->make_request(mddev, bio);
+       mddev->pers->make_request(mddev, bio);
 
        cpu = part_stat_lock();
        part_stat_inc(cpu, &mddev->gendisk->part0, ios[rw]);
@@ -377,8 +377,6 @@ static int md_make_request(struct request_queue *q, struct bio *bio)
 
        if (atomic_dec_and_test(&mddev->active_io) && mddev->suspended)
                wake_up(&mddev->sb_wait);
-
-       return rv;
 }
 
 /* mddev_suspend makes sure no new requests are submitted
@@ -477,8 +475,7 @@ static void md_submit_flush_data(struct work_struct *ws)
                bio_endio(bio, 0);
        else {
                bio->bi_rw &= ~REQ_FLUSH;
-               if (mddev->pers->make_request(mddev, bio))
-                       generic_make_request(bio);
+               mddev->pers->make_request(mddev, bio);
        }
 
        mddev->flush_bio = NULL;
@@ -573,7 +570,7 @@ static void mddev_put(struct mddev *mddev)
            mddev->ctime == 0 && !mddev->hold_active) {
                /* Array is not configured at all, and not held active,
                 * so destroy it */
-               list_del(&mddev->all_mddevs);
+               list_del_init(&mddev->all_mddevs);
                bs = mddev->bio_set;
                mddev->bio_set = NULL;
                if (mddev->gendisk) {
@@ -741,8 +738,8 @@ static void mddev_unlock(struct mddev * mddev)
        } else
                mutex_unlock(&mddev->reconfig_mutex);
 
-       /* was we've dropped the mutex we need a spinlock to
-        * make sur the thread doesn't disappear
+       /* As we've dropped the mutex we need a spinlock to
+        * make sure the thread doesn't disappear
         */
        spin_lock(&pers_lock);
        md_wakeup_thread(mddev->thread);
@@ -771,9 +768,9 @@ static struct md_rdev * find_rdev(struct mddev * mddev, dev_t dev)
        return NULL;
 }
 
-static struct mdk_personality *find_pers(int level, char *clevel)
+static struct md_personality *find_pers(int level, char *clevel)
 {
-       struct mdk_personality *pers;
+       struct md_personality *pers;
        list_for_each_entry(pers, &pers_list, list) {
                if (level != LEVEL_NONE && pers->level == level)
                        return pers;
@@ -1804,13 +1801,13 @@ retry:
                                                | BB_LEN(internal_bb));
                                *bbp++ = cpu_to_le64(store_bb);
                        }
+                       bb->changed = 0;
                        if (read_seqretry(&bb->lock, seq))
                                goto retry;
 
                        bb->sector = (rdev->sb_start +
                                      (int)le32_to_cpu(sb->bblog_offset));
                        bb->size = le16_to_cpu(sb->bblog_size);
-                       bb->changed = 0;
                }
        }
 
@@ -2365,6 +2362,7 @@ repeat:
                        clear_bit(MD_CHANGE_PENDING, &mddev->flags);
                        list_for_each_entry(rdev, &mddev->disks, same_set) {
                                if (rdev->badblocks.changed) {
+                                       rdev->badblocks.changed = 0;
                                        md_ack_all_badblocks(&rdev->badblocks);
                                        md_error(mddev, rdev);
                                }
@@ -2449,7 +2447,8 @@ repeat:
                if (rdev->sb_loaded != 1)
                        continue; /* no noise on spare devices */
 
-               if (!test_bit(Faulty, &rdev->flags)) {
+               if (!test_bit(Faulty, &rdev->flags) &&
+                   rdev->saved_raid_disk == -1) {
                        md_super_write(mddev,rdev,
                                       rdev->sb_start, rdev->sb_size,
                                       rdev->sb_page);
@@ -2465,9 +2464,12 @@ repeat:
                                rdev->badblocks.size = 0;
                        }
 
-               } else
+               } else if (test_bit(Faulty, &rdev->flags))
                        pr_debug("md: %s (skipping faulty)\n",
                                 bdevname(rdev->bdev, b));
+               else
+                       pr_debug("(skipping incremental s/r ");
+
                if (mddev->level == LEVEL_MULTIPATH)
                        /* only need to write one superblock... */
                        break;
@@ -2545,7 +2547,8 @@ state_show(struct md_rdev *rdev, char *page)
                sep = ",";
        }
        if (test_bit(Blocked, &rdev->flags) ||
-           rdev->badblocks.unacked_exist) {
+           (rdev->badblocks.unacked_exist
+            && !test_bit(Faulty, &rdev->flags))) {
                len += sprintf(page+len, "%sblocked", sep);
                sep = ",";
        }
@@ -2722,6 +2725,7 @@ slot_store(struct md_rdev *rdev, const char *buf, size_t len)
                        rdev->saved_raid_disk = slot;
                else
                        rdev->saved_raid_disk = -1;
+               clear_bit(In_sync, &rdev->flags);
                err = rdev->mddev->pers->
                        hot_add_disk(rdev->mddev, rdev);
                if (err) {
@@ -3272,7 +3276,7 @@ __ATTR(safe_mode_delay, S_IRUGO|S_IWUSR,safe_delay_show, safe_delay_store);
 static ssize_t
 level_show(struct mddev *mddev, char *page)
 {
-       struct mdk_personality *p = mddev->pers;
+       struct md_personality *p = mddev->pers;
        if (p)
                return sprintf(page, "%s\n", p->name);
        else if (mddev->clevel[0])
@@ -3288,7 +3292,7 @@ level_store(struct mddev *mddev, const char *buf, size_t len)
 {
        char clevel[16];
        ssize_t rv = len;
-       struct mdk_personality *pers;
+       struct md_personality *pers;
        long level;
        void *priv;
        struct md_rdev *rdev;
@@ -3786,6 +3790,8 @@ array_state_store(struct mddev *mddev, const char *buf, size_t len)
        if (err)
                return err;
        else {
+               if (mddev->hold_active == UNTIL_IOCTL)
+                       mddev->hold_active = 0;
                sysfs_notify_dirent_safe(mddev->sysfs_state);
                return len;
        }
@@ -4485,11 +4491,20 @@ md_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
 
        if (!entry->show)
                return -EIO;
+       spin_lock(&all_mddevs_lock);
+       if (list_empty(&mddev->all_mddevs)) {
+               spin_unlock(&all_mddevs_lock);
+               return -EBUSY;
+       }
+       mddev_get(mddev);
+       spin_unlock(&all_mddevs_lock);
+
        rv = mddev_lock(mddev);
        if (!rv) {
                rv = entry->show(mddev, page);
                mddev_unlock(mddev);
        }
+       mddev_put(mddev);
        return rv;
 }
 
@@ -4505,13 +4520,19 @@ md_attr_store(struct kobject *kobj, struct attribute *attr,
                return -EIO;
        if (!capable(CAP_SYS_ADMIN))
                return -EACCES;
+       spin_lock(&all_mddevs_lock);
+       if (list_empty(&mddev->all_mddevs)) {
+               spin_unlock(&all_mddevs_lock);
+               return -EBUSY;
+       }
+       mddev_get(mddev);
+       spin_unlock(&all_mddevs_lock);
        rv = mddev_lock(mddev);
-       if (mddev->hold_active == UNTIL_IOCTL)
-               mddev->hold_active = 0;
        if (!rv) {
                rv = entry->store(mddev, page, length);
                mddev_unlock(mddev);
        }
+       mddev_put(mddev);
        return rv;
 }
 
@@ -4700,7 +4721,7 @@ int md_run(struct mddev *mddev)
 {
        int err;
        struct md_rdev *rdev;
-       struct mdk_personality *pers;
+       struct md_personality *pers;
 
        if (list_empty(&mddev->disks))
                /* cannot run an array with no devices.. */
@@ -6362,7 +6383,7 @@ static const struct block_device_operations md_fops =
 
 static int md_thread(void * arg)
 {
-       mdk_thread_t *thread = arg;
+       struct md_thread *thread = arg;
 
        /*
         * md_thread is a 'system-thread', it's priority should be very
@@ -6401,7 +6422,7 @@ static int md_thread(void * arg)
        return 0;
 }
 
-void md_wakeup_thread(mdk_thread_t *thread)
+void md_wakeup_thread(struct md_thread *thread)
 {
        if (thread) {
                pr_debug("md: waking up MD thread %s.\n", thread->tsk->comm);
@@ -6410,12 +6431,12 @@ void md_wakeup_thread(mdk_thread_t *thread)
        }
 }
 
-mdk_thread_t *md_register_thread(void (*run) (struct mddev *), struct mddev *mddev,
+struct md_thread *md_register_thread(void (*run) (struct mddev *), struct mddev *mddev,
                                 const char *name)
 {
-       mdk_thread_t *thread;
+       struct md_thread *thread;
 
-       thread = kzalloc(sizeof(mdk_thread_t), GFP_KERNEL);
+       thread = kzalloc(sizeof(struct md_thread), GFP_KERNEL);
        if (!thread)
                return NULL;
 
@@ -6435,9 +6456,9 @@ mdk_thread_t *md_register_thread(void (*run) (struct mddev *), struct mddev *mdd
        return thread;
 }
 
-void md_unregister_thread(mdk_thread_t **threadp)
+void md_unregister_thread(struct md_thread **threadp)
 {
-       mdk_thread_t *thread = *threadp;
+       struct md_thread *thread = *threadp;
        if (!thread)
                return;
        pr_debug("interrupting MD-thread pid %d\n", task_pid_nr(thread->tsk));
@@ -6655,7 +6676,7 @@ static int md_seq_show(struct seq_file *seq, void *v)
        struct bitmap *bitmap;
 
        if (v == (void*)1) {
-               struct mdk_personality *pers;
+               struct md_personality *pers;
                seq_printf(seq, "Personalities : ");
                spin_lock(&pers_lock);
                list_for_each_entry(pers, &pers_list, list)
@@ -6811,7 +6832,7 @@ static const struct file_operations md_seq_fops = {
        .poll           = mdstat_poll,
 };
 
-int register_md_personality(struct mdk_personality *p)
+int register_md_personality(struct md_personality *p)
 {
        spin_lock(&pers_lock);
        list_add_tail(&p->list, &pers_list);
@@ -6820,7 +6841,7 @@ int register_md_personality(struct mdk_personality *p)
        return 0;
 }
 
-int unregister_md_personality(struct mdk_personality *p)
+int unregister_md_personality(struct md_personality *p)
 {
        printk(KERN_INFO "md: %s personality unregistered\n", p->name);
        spin_lock(&pers_lock);
@@ -7340,8 +7361,7 @@ static int remove_and_add_spares(struct mddev *mddev)
                                        spares++;
                                        md_new_event(mddev);
                                        set_bit(MD_CHANGE_DEVS, &mddev->flags);
-                               } else
-                                       break;
+                               }
                        }
                }
        }
@@ -7365,15 +7385,19 @@ static void reap_sync_thread(struct mddev *mddev)
        if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) &&
            mddev->pers->finish_reshape)
                mddev->pers->finish_reshape(mddev);
-       md_update_sb(mddev, 1);
 
-       /* if array is no-longer degraded, then any saved_raid_disk
-        * information must be scrapped
+       /* If array is no-longer degraded, then any saved_raid_disk
+        * information must be scrapped.  Also if any device is now
+        * In_sync we must scrape the saved_raid_disk for that device
+        * do the superblock for an incrementally recovered device
+        * written out.
         */
-       if (!mddev->degraded)
-               list_for_each_entry(rdev, &mddev->disks, same_set)
+       list_for_each_entry(rdev, &mddev->disks, same_set)
+               if (!mddev->degraded ||
+                   test_bit(In_sync, &rdev->flags))
                        rdev->saved_raid_disk = -1;
 
+       md_update_sb(mddev, 1);
        clear_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
        clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
        clear_bit(MD_RECOVERY_RESHAPE, &mddev->recovery);
@@ -7834,6 +7858,7 @@ int rdev_set_badblocks(struct md_rdev *rdev, sector_t s, int sectors,
                                  s + rdev->data_offset, sectors, acknowledged);
        if (rv) {
                /* Make sure they get written out promptly */
+               sysfs_notify_dirent_safe(rdev->sysfs_state);
                set_bit(MD_CHANGE_CLEAN, &rdev->mddev->flags);
                md_wakeup_thread(rdev->mddev->thread);
        }
@@ -8073,30 +8098,24 @@ static int md_notify_reboot(struct notifier_block *this,
        struct mddev *mddev;
        int need_delay = 0;
 
-       if ((code == SYS_DOWN) || (code == SYS_HALT) || (code == SYS_POWER_OFF)) {
-
-               printk(KERN_INFO "md: stopping all md devices.\n");
-
-               for_each_mddev(mddev, tmp) {
-                       if (mddev_trylock(mddev)) {
-                               /* Force a switch to readonly even array
-                                * appears to still be in use.  Hence
-                                * the '100'.
-                                */
-                               md_set_readonly(mddev, 100);
-                               mddev_unlock(mddev);
-                       }
-                       need_delay = 1;
+       for_each_mddev(mddev, tmp) {
+               if (mddev_trylock(mddev)) {
+                       if (mddev->pers)
+                               __md_stop_writes(mddev);
+                       mddev->safemode = 2;
+                       mddev_unlock(mddev);
                }
-               /*
-                * certain more exotic SCSI devices are known to be
-                * volatile wrt too early system reboots. While the
-                * right place to handle this issue is the given
-                * driver, we do want to have a safe RAID driver ...
-                */
-               if (need_delay)
-                       mdelay(1000*1);
+               need_delay = 1;
        }
+       /*
+        * certain more exotic SCSI devices are known to be
+        * volatile wrt too early system reboots. While the
+        * right place to handle this issue is the given
+        * driver, we do want to have a safe RAID driver ...
+        */
+       if (need_delay)
+               mdelay(1000*1);
+
        return NOTIFY_DONE;
 }