[PATCH] md: new sysfs interface for setting bits in the write-intent-bitmap
[sfrench/cifs-2.6.git] / drivers / md / md.c
index ee7320b4a30e0dc151b0787d595304f2de31852e..b95dd8a183ec9da71c0ab501f61577c770d3607f 100644 (file)
 */
 
 #include <linux/module.h>
-#include <linux/config.h>
 #include <linux/kthread.h>
 #include <linux/linkage.h>
 #include <linux/raid/md.h>
 #include <linux/raid/bitmap.h>
 #include <linux/sysctl.h>
-#include <linux/devfs_fs_kernel.h>
 #include <linux/buffer_head.h> /* for invalidate_bdev */
 #include <linux/suspend.h>
 #include <linux/poll.h>
@@ -112,7 +110,7 @@ static ctl_table raid_table[] = {
                .procname       = "speed_limit_min",
                .data           = &sysctl_speed_limit_min,
                .maxlen         = sizeof(int),
-               .mode           = 0644,
+               .mode           = S_IRUGO|S_IWUSR,
                .proc_handler   = &proc_dointvec,
        },
        {
@@ -120,7 +118,7 @@ static ctl_table raid_table[] = {
                .procname       = "speed_limit_max",
                .data           = &sysctl_speed_limit_max,
                .maxlen         = sizeof(int),
-               .mode           = 0644,
+               .mode           = S_IRUGO|S_IWUSR,
                .proc_handler   = &proc_dointvec,
        },
        { .ctl_name = 0 }
@@ -131,7 +129,7 @@ static ctl_table raid_dir_table[] = {
                .ctl_name       = DEV_RAID,
                .procname       = "raid",
                .maxlen         = 0,
-               .mode           = 0555,
+               .mode           = S_IRUGO|S_IXUGO,
                .child          = raid_table,
        },
        { .ctl_name = 0 }
@@ -1064,6 +1062,11 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version)
        if (rdev->sb_size & bmask)
                rdev-> sb_size = (rdev->sb_size | bmask)+1;
 
+       if (sb->level == cpu_to_le32(LEVEL_MULTIPATH))
+               rdev->desc_nr = -1;
+       else
+               rdev->desc_nr = le32_to_cpu(sb->dev_number);
+
        if (refdev == 0)
                ret = 1;
        else {
@@ -1173,7 +1176,6 @@ static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev)
        }
        if (mddev->level != LEVEL_MULTIPATH) {
                int role;
-               rdev->desc_nr = le32_to_cpu(sb->dev_number);
                role = le16_to_cpu(sb->dev_roles[rdev->desc_nr]);
                switch(role) {
                case 0xffff: /* spare */
@@ -1406,7 +1408,7 @@ static int lock_rdev(mdk_rdev_t *rdev, dev_t dev)
        struct block_device *bdev;
        char b[BDEVNAME_SIZE];
 
-       bdev = open_by_devnum(dev, FMODE_READ|FMODE_WRITE);
+       bdev = open_partition_by_devnum(dev, FMODE_READ|FMODE_WRITE);
        if (IS_ERR(bdev)) {
                printk(KERN_ERR "md: could not open %s.\n",
                        __bdevname(dev, b));
@@ -1416,7 +1418,7 @@ static int lock_rdev(mdk_rdev_t *rdev, dev_t dev)
        if (err) {
                printk(KERN_ERR "md: could not bd_claim %s.\n",
                        bdevname(bdev, b));
-               blkdev_put(bdev);
+               blkdev_put_partition(bdev);
                return err;
        }
        rdev->bdev = bdev;
@@ -1430,7 +1432,7 @@ static void unlock_rdev(mdk_rdev_t *rdev)
        if (!bdev)
                MD_BUG();
        bd_release(bdev);
-       blkdev_put(bdev);
+       blkdev_put_partition(bdev);
 }
 
 void md_autodetect_dev(dev_t dev);
@@ -1585,7 +1587,7 @@ static void sync_sbs(mddev_t * mddev, int nospares)
        }
 }
 
-void md_update_sb(mddev_t * mddev)
+static void md_update_sb(mddev_t * mddev, int force_change)
 {
        int err;
        struct list_head *tmp;
@@ -1595,18 +1597,36 @@ void md_update_sb(mddev_t * mddev)
 
 repeat:
        spin_lock_irq(&mddev->write_lock);
-       sync_req = mddev->in_sync;
-       mddev->utime = get_seconds();
-       if (mddev->sb_dirty == 3)
+
+       set_bit(MD_CHANGE_PENDING, &mddev->flags);
+       if (test_and_clear_bit(MD_CHANGE_DEVS, &mddev->flags))
+               force_change = 1;
+       if (test_and_clear_bit(MD_CHANGE_CLEAN, &mddev->flags))
                /* just a clean<-> dirty transition, possibly leave spares alone,
                 * though if events isn't the right even/odd, we will have to do
                 * spares after all
                 */
                nospares = 1;
+       if (force_change)
+               nospares = 0;
+       if (mddev->degraded)
+               /* If the array is degraded, then skipping spares is both
+                * dangerous and fairly pointless.
+                * Dangerous because a device that was removed from the array
+                * might have a event_count that still looks up-to-date,
+                * so it can be re-added without a resync.
+                * Pointless because if there are any spares to skip,
+                * then a recovery will happen and soon that array won't
+                * be degraded any more and the spare can go back to sleep then.
+                */
+               nospares = 0;
+
+       sync_req = mddev->in_sync;
+       mddev->utime = get_seconds();
 
        /* If this is just a dirty<->clean transition, and the array is clean
         * and 'events' is odd, we can roll back to the previous clean state */
-       if (mddev->sb_dirty == 3
+       if (nospares
            && (mddev->in_sync && mddev->recovery_cp == MaxSector)
            && (mddev->events & 1))
                mddev->events--;
@@ -1637,7 +1657,6 @@ repeat:
                MD_BUG();
                mddev->events --;
        }
-       mddev->sb_dirty = 2;
        sync_sbs(mddev, nospares);
 
        /*
@@ -1645,7 +1664,7 @@ repeat:
         * nonpersistent superblocks
         */
        if (!mddev->persistent) {
-               mddev->sb_dirty = 0;
+               clear_bit(MD_CHANGE_PENDING, &mddev->flags);
                spin_unlock_irq(&mddev->write_lock);
                wake_up(&mddev->sb_wait);
                return;
@@ -1682,20 +1701,20 @@ repeat:
                        break;
        }
        md_super_wait(mddev);
-       /* if there was a failure, sb_dirty was set to 1, and we re-write super */
+       /* if there was a failure, MD_CHANGE_DEVS was set, and we re-write super */
 
        spin_lock_irq(&mddev->write_lock);
-       if (mddev->in_sync != sync_req|| mddev->sb_dirty == 1) {
+       if (mddev->in_sync != sync_req ||
+           test_bit(MD_CHANGE_DEVS, &mddev->flags)) {
                /* have to write it out again */
                spin_unlock_irq(&mddev->write_lock);
                goto repeat;
        }
-       mddev->sb_dirty = 0;
+       clear_bit(MD_CHANGE_PENDING, &mddev->flags);
        spin_unlock_irq(&mddev->write_lock);
        wake_up(&mddev->sb_wait);
 
 }
-EXPORT_SYMBOL_GPL(md_update_sb);
 
 /* words written to sysfs files may, or my not, be \n terminated.
  * We want to accept with case. For this we use cmd_match.
@@ -1768,7 +1787,7 @@ state_store(mdk_rdev_t *rdev, const char *buf, size_t len)
                else {
                        mddev_t *mddev = rdev->mddev;
                        kick_rdev_from_array(rdev);
-                       md_update_sb(mddev);
+                       md_update_sb(mddev, 1);
                        md_new_event(mddev);
                        err = 0;
                }
@@ -1781,8 +1800,8 @@ state_store(mdk_rdev_t *rdev, const char *buf, size_t len)
        }
        return err ? err : len;
 }
-static struct rdev_sysfs_entry
-rdev_state = __ATTR(state, 0644, state_show, state_store);
+static struct rdev_sysfs_entry rdev_state =
+__ATTR(state, S_IRUGO|S_IWUSR, state_show, state_store);
 
 static ssize_t
 super_show(mdk_rdev_t *rdev, char *page)
@@ -1813,7 +1832,7 @@ errors_store(mdk_rdev_t *rdev, const char *buf, size_t len)
        return -EINVAL;
 }
 static struct rdev_sysfs_entry rdev_errors =
-__ATTR(errors, 0644, errors_show, errors_store);
+__ATTR(errors, S_IRUGO|S_IWUSR, errors_show, errors_store);
 
 static ssize_t
 slot_show(mdk_rdev_t *rdev, char *page)
@@ -1847,7 +1866,7 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len)
 
 
 static struct rdev_sysfs_entry rdev_slot =
-__ATTR(slot, 0644, slot_show, slot_store);
+__ATTR(slot, S_IRUGO|S_IWUSR, slot_show, slot_store);
 
 static ssize_t
 offset_show(mdk_rdev_t *rdev, char *page)
@@ -1869,7 +1888,7 @@ offset_store(mdk_rdev_t *rdev, const char *buf, size_t len)
 }
 
 static struct rdev_sysfs_entry rdev_offset =
-__ATTR(offset, 0644, offset_show, offset_store);
+__ATTR(offset, S_IRUGO|S_IWUSR, offset_show, offset_store);
 
 static ssize_t
 rdev_size_show(mdk_rdev_t *rdev, char *page)
@@ -1893,7 +1912,7 @@ rdev_size_store(mdk_rdev_t *rdev, const char *buf, size_t len)
 }
 
 static struct rdev_sysfs_entry rdev_size =
-__ATTR(size, 0644, rdev_size_show, rdev_size_store);
+__ATTR(size, S_IRUGO|S_IWUSR, rdev_size_show, rdev_size_store);
 
 static struct attribute *rdev_default_attrs[] = {
        &rdev_state.attr,
@@ -1924,6 +1943,8 @@ rdev_attr_store(struct kobject *kobj, struct attribute *attr,
 
        if (!entry->store)
                return -EIO;
+       if (!capable(CAP_SYS_ADMIN))
+               return -EACCES;
        return entry->store(rdev, page, length);
 }
 
@@ -2130,7 +2151,7 @@ safe_delay_store(mddev_t *mddev, const char *cbuf, size_t len)
        return len;
 }
 static struct md_sysfs_entry md_safe_delay =
-__ATTR(safe_mode_delay, 0644,safe_delay_show, safe_delay_store);
+__ATTR(safe_mode_delay, S_IRUGO|S_IWUSR,safe_delay_show, safe_delay_store);
 
 static ssize_t
 level_show(mddev_t *mddev, char *page)
@@ -2165,7 +2186,7 @@ level_store(mddev_t *mddev, const char *buf, size_t len)
 }
 
 static struct md_sysfs_entry md_level =
-__ATTR(level, 0644, level_show, level_store);
+__ATTR(level, S_IRUGO|S_IWUSR, level_show, level_store);
 
 
 static ssize_t
@@ -2190,7 +2211,7 @@ layout_store(mddev_t *mddev, const char *buf, size_t len)
        return len;
 }
 static struct md_sysfs_entry md_layout =
-__ATTR(layout, 0655, layout_show, layout_store);
+__ATTR(layout, S_IRUGO|S_IWUSR, layout_show, layout_store);
 
 
 static ssize_t
@@ -2221,7 +2242,7 @@ raid_disks_store(mddev_t *mddev, const char *buf, size_t len)
        return rv ? rv : len;
 }
 static struct md_sysfs_entry md_raid_disks =
-__ATTR(raid_disks, 0644, raid_disks_show, raid_disks_store);
+__ATTR(raid_disks, S_IRUGO|S_IWUSR, raid_disks_show, raid_disks_store);
 
 static ssize_t
 chunk_size_show(mddev_t *mddev, char *page)
@@ -2245,7 +2266,7 @@ chunk_size_store(mddev_t *mddev, const char *buf, size_t len)
        return len;
 }
 static struct md_sysfs_entry md_chunk_size =
-__ATTR(chunk_size, 0644, chunk_size_show, chunk_size_store);
+__ATTR(chunk_size, S_IRUGO|S_IWUSR, chunk_size_show, chunk_size_store);
 
 static ssize_t
 resync_start_show(mddev_t *mddev, char *page)
@@ -2269,7 +2290,7 @@ resync_start_store(mddev_t *mddev, const char *buf, size_t len)
        return len;
 }
 static struct md_sysfs_entry md_resync_start =
-__ATTR(resync_start, 0644, resync_start_show, resync_start_store);
+__ATTR(resync_start, S_IRUGO|S_IWUSR, resync_start_show, resync_start_store);
 
 /*
  * The array state can be:
@@ -2409,7 +2430,7 @@ array_state_store(mddev_t *mddev, const char *buf, size_t len)
                        spin_lock_irq(&mddev->write_lock);
                        if (atomic_read(&mddev->writes_pending) == 0) {
                                mddev->in_sync = 1;
-                               mddev->sb_dirty = 1;
+                               set_bit(MD_CHANGE_CLEAN, &mddev->flags);
                        }
                        spin_unlock_irq(&mddev->write_lock);
                } else {
@@ -2421,7 +2442,7 @@ array_state_store(mddev_t *mddev, const char *buf, size_t len)
        case active:
                if (mddev->pers) {
                        restart_array(mddev);
-                       mddev->sb_dirty = 0;
+                       clear_bit(MD_CHANGE_CLEAN, &mddev->flags);
                        wake_up(&mddev->sb_wait);
                        err = 0;
                } else {
@@ -2439,7 +2460,8 @@ array_state_store(mddev_t *mddev, const char *buf, size_t len)
        else
                return len;
 }
-static struct md_sysfs_entry md_array_state = __ATTR(array_state, 0644, array_state_show, array_state_store);
+static struct md_sysfs_entry md_array_state =
+__ATTR(array_state, S_IRUGO|S_IWUSR, array_state_show, array_state_store);
 
 static ssize_t
 null_show(mddev_t *mddev, char *page)
@@ -2499,7 +2521,37 @@ new_dev_store(mddev_t *mddev, const char *buf, size_t len)
 }
 
 static struct md_sysfs_entry md_new_device =
-__ATTR(new_dev, 0200, null_show, new_dev_store);
+__ATTR(new_dev, S_IWUSR, null_show, new_dev_store);
+
+static ssize_t
+bitmap_store(mddev_t *mddev, const char *buf, size_t len)
+{
+       char *end;
+       unsigned long chunk, end_chunk;
+
+       if (!mddev->bitmap)
+               goto out;
+       /* buf should be <chunk> <chunk> ... or <chunk>-<chunk> ... (range) */
+       while (*buf) {
+               chunk = end_chunk = simple_strtoul(buf, &end, 0);
+               if (buf == end) break;
+               if (*end == '-') { /* range */
+                       buf = end + 1;
+                       end_chunk = simple_strtoul(buf, &end, 0);
+                       if (buf == end) break;
+               }
+               if (*end && !isspace(*end)) break;
+               bitmap_dirty_bits(mddev->bitmap, chunk, end_chunk);
+               buf = end;
+               while (isspace(*buf)) buf++;
+       }
+       bitmap_unplug(mddev->bitmap); /* flush the bits to disk */
+out:
+       return len;
+}
+
+static struct md_sysfs_entry md_bitmap =
+__ATTR(bitmap_set_bits, S_IWUSR, null_show, bitmap_store);
 
 static ssize_t
 size_show(mddev_t *mddev, char *page)
@@ -2525,7 +2577,7 @@ size_store(mddev_t *mddev, const char *buf, size_t len)
 
        if (mddev->pers) {
                err = update_size(mddev, size);
-               md_update_sb(mddev);
+               md_update_sb(mddev, 1);
        } else {
                if (mddev->size == 0 ||
                    mddev->size > size)
@@ -2537,7 +2589,7 @@ size_store(mddev_t *mddev, const char *buf, size_t len)
 }
 
 static struct md_sysfs_entry md_size =
-__ATTR(component_size, 0644, size_show, size_store);
+__ATTR(component_size, S_IRUGO|S_IWUSR, size_show, size_store);
 
 
 /* Metdata version.
@@ -2585,7 +2637,7 @@ metadata_store(mddev_t *mddev, const char *buf, size_t len)
 }
 
 static struct md_sysfs_entry md_metadata =
-__ATTR(metadata_version, 0644, metadata_show, metadata_store);
+__ATTR(metadata_version, S_IRUGO|S_IWUSR, metadata_show, metadata_store);
 
 static ssize_t
 action_show(mddev_t *mddev, char *page)
@@ -2653,12 +2705,11 @@ mismatch_cnt_show(mddev_t *mddev, char *page)
                       (unsigned long long) mddev->resync_mismatches);
 }
 
-static struct md_sysfs_entry
-md_scan_mode = __ATTR(sync_action, S_IRUGO|S_IWUSR, action_show, action_store);
+static struct md_sysfs_entry md_scan_mode =
+__ATTR(sync_action, S_IRUGO|S_IWUSR, action_show, action_store);
 
 
-static struct md_sysfs_entry
-md_mismatches = __ATTR_RO(mismatch_cnt);
+static struct md_sysfs_entry md_mismatches = __ATTR_RO(mismatch_cnt);
 
 static ssize_t
 sync_min_show(mddev_t *mddev, char *page)
@@ -2717,15 +2768,14 @@ static ssize_t
 sync_speed_show(mddev_t *mddev, char *page)
 {
        unsigned long resync, dt, db;
-       resync = (mddev->curr_resync - atomic_read(&mddev->recovery_active));
+       resync = (mddev->curr_mark_cnt - atomic_read(&mddev->recovery_active));
        dt = ((jiffies - mddev->resync_mark) / HZ);
        if (!dt) dt++;
        db = resync - (mddev->resync_mark_cnt);
        return sprintf(page, "%ld\n", db/dt/2); /* K/sec */
 }
 
-static struct md_sysfs_entry
-md_sync_speed = __ATTR_RO(sync_speed);
+static struct md_sysfs_entry md_sync_speed = __ATTR_RO(sync_speed);
 
 static ssize_t
 sync_completed_show(mddev_t *mddev, char *page)
@@ -2741,8 +2791,7 @@ sync_completed_show(mddev_t *mddev, char *page)
        return sprintf(page, "%lu / %lu\n", resync, max_blocks);
 }
 
-static struct md_sysfs_entry
-md_sync_completed = __ATTR_RO(sync_completed);
+static struct md_sysfs_entry md_sync_completed = __ATTR_RO(sync_completed);
 
 static ssize_t
 suspend_lo_show(mddev_t *mddev, char *page)
@@ -2824,6 +2873,7 @@ static struct attribute *md_redundancy_attrs[] = {
        &md_sync_completed.attr,
        &md_suspend_lo.attr,
        &md_suspend_hi.attr,
+       &md_bitmap.attr,
        NULL,
 };
 static struct attribute_group md_redundancy_group = {
@@ -2859,6 +2909,8 @@ md_attr_store(struct kobject *kobj, struct attribute *attr,
 
        if (!entry->store)
                return -EIO;
+       if (!capable(CAP_SYS_ADMIN))
+               return -EACCES;
        rv = mddev_lock(mddev);
        if (!rv) {
                rv = entry->store(mddev, page, length);
@@ -2911,13 +2963,10 @@ static struct kobject *md_probe(dev_t dev, int *part, void *data)
        }
        disk->major = MAJOR(dev);
        disk->first_minor = unit << shift;
-       if (partitioned) {
+       if (partitioned)
                sprintf(disk->disk_name, "md_d%d", unit);
-               sprintf(disk->devfs_name, "md/d%d", unit);
-       } else {
+       else
                sprintf(disk->disk_name, "md%d", unit);
-               sprintf(disk->devfs_name, "md/%d", unit);
-       }
        disk->fops = &md_fops;
        disk->private_data = mddev;
        disk->queue = mddev->queue;
@@ -3096,10 +3145,9 @@ static int do_md_run(mddev_t * mddev)
                }
        
        set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
-       md_wakeup_thread(mddev->thread);
        
-       if (mddev->sb_dirty)
-               md_update_sb(mddev);
+       if (mddev->flags)
+               md_update_sb(mddev, 0);
 
        set_capacity(disk, mddev->array_size<<1);
 
@@ -3117,7 +3165,7 @@ static int do_md_run(mddev_t * mddev)
         * start recovery here.  If we leave it to md_check_recovery,
         * it will remove the drives and not do the right thing
         */
-       if (mddev->degraded) {
+       if (mddev->degraded && !mddev->sync_thread) {
                struct list_head *rtmp;
                int spares = 0;
                ITERATE_RDEV(mddev,rdev,rtmp)
@@ -3138,10 +3186,11 @@ static int do_md_run(mddev_t * mddev)
                                       mdname(mddev));
                                /* leave the spares where they are, it shouldn't hurt */
                                mddev->recovery = 0;
-                       } else
-                               md_wakeup_thread(mddev->sync_thread);
+                       }
                }
        }
+       md_wakeup_thread(mddev->thread);
+       md_wakeup_thread(mddev->sync_thread); /* possibly kick off a reshape */
 
        mddev->changed = 1;
        md_new_event(mddev);
@@ -3261,10 +3310,10 @@ static int do_md_stop(mddev_t * mddev, int mode)
                        if (mddev->ro)
                                mddev->ro = 0;
                }
-               if (!mddev->in_sync || mddev->sb_dirty) {
+               if (!mddev->in_sync || mddev->flags) {
                        /* mark array as shutdown cleanly */
                        mddev->in_sync = 1;
-                       md_update_sb(mddev);
+                       md_update_sb(mddev, 1);
                }
                if (mode == 1)
                        set_disk_ro(disk, 1);
@@ -3426,67 +3475,6 @@ static void autorun_devices(int part)
        printk(KERN_INFO "md: ... autorun DONE.\n");
 }
 
-/*
- * import RAID devices based on one partition
- * if possible, the array gets run as well.
- */
-
-static int autostart_array(dev_t startdev)
-{
-       char b[BDEVNAME_SIZE];
-       int err = -EINVAL, i;
-       mdp_super_t *sb = NULL;
-       mdk_rdev_t *start_rdev = NULL, *rdev;
-
-       start_rdev = md_import_device(startdev, 0, 0);
-       if (IS_ERR(start_rdev))
-               return err;
-
-
-       /* NOTE: this can only work for 0.90.0 superblocks */
-       sb = (mdp_super_t*)page_address(start_rdev->sb_page);
-       if (sb->major_version != 0 ||
-           sb->minor_version != 90 ) {
-               printk(KERN_WARNING "md: can only autostart 0.90.0 arrays\n");
-               export_rdev(start_rdev);
-               return err;
-       }
-
-       if (test_bit(Faulty, &start_rdev->flags)) {
-               printk(KERN_WARNING 
-                       "md: can not autostart based on faulty %s!\n",
-                       bdevname(start_rdev->bdev,b));
-               export_rdev(start_rdev);
-               return err;
-       }
-       list_add(&start_rdev->same_set, &pending_raid_disks);
-
-       for (i = 0; i < MD_SB_DISKS; i++) {
-               mdp_disk_t *desc = sb->disks + i;
-               dev_t dev = MKDEV(desc->major, desc->minor);
-
-               if (!dev)
-                       continue;
-               if (dev == startdev)
-                       continue;
-               if (MAJOR(dev) != desc->major || MINOR(dev) != desc->minor)
-                       continue;
-               rdev = md_import_device(dev, 0, 0);
-               if (IS_ERR(rdev))
-                       continue;
-
-               list_add(&rdev->same_set, &pending_raid_disks);
-       }
-
-       /*
-        * possibly return codes
-        */
-       autorun_devices(0);
-       return 0;
-
-}
-
-
 static int get_version(void __user * arg)
 {
        mdu_version_t ver;
@@ -3794,7 +3782,7 @@ static int hot_remove_disk(mddev_t * mddev, dev_t dev)
                goto busy;
 
        kick_rdev_from_array(rdev);
-       md_update_sb(mddev);
+       md_update_sb(mddev, 1);
        md_new_event(mddev);
 
        return 0;
@@ -3871,7 +3859,7 @@ static int hot_add_disk(mddev_t * mddev, dev_t dev)
 
        rdev->raid_disk = -1;
 
-       md_update_sb(mddev);
+       md_update_sb(mddev, 1);
 
        /*
         * Kick recovery, maybe this spare has to be added to the
@@ -4002,7 +3990,8 @@ static int set_array_info(mddev_t * mddev, mdu_array_info_t *info)
 
        mddev->max_disks     = MD_SB_DISKS;
 
-       mddev->sb_dirty      = 1;
+       mddev->flags         = 0;
+       set_bit(MD_CHANGE_DEVS, &mddev->flags);
 
        mddev->default_bitmap_offset = MD_SB_BYTES >> 9;
        mddev->bitmap_offset = 0;
@@ -4171,7 +4160,7 @@ static int update_array_info(mddev_t *mddev, mdu_array_info_t *info)
                        mddev->bitmap_offset = 0;
                }
        }
-       md_update_sb(mddev);
+       md_update_sb(mddev, 1);
        return rv;
 }
 
@@ -4245,27 +4234,6 @@ static int md_ioctl(struct inode *inode, struct file *file,
                goto abort;
        }
 
-
-       if (cmd == START_ARRAY) {
-               /* START_ARRAY doesn't need to lock the array as autostart_array
-                * does the locking, and it could even be a different array
-                */
-               static int cnt = 3;
-               if (cnt > 0 ) {
-                       printk(KERN_WARNING
-                              "md: %s(pid %d) used deprecated START_ARRAY ioctl. "
-                              "This will not be supported beyond July 2006\n",
-                              current->comm, current->pid);
-                       cnt--;
-               }
-               err = autostart_array(new_decode_dev(arg));
-               if (err) {
-                       printk(KERN_WARNING "md: autostart failed!\n");
-                       goto abort;
-               }
-               goto done;
-       }
-
        err = mddev_lock(mddev);
        if (err) {
                printk(KERN_INFO 
@@ -4591,6 +4559,8 @@ void md_error(mddev_t *mddev, mdk_rdev_t *rdev)
                __builtin_return_address(0),__builtin_return_address(1),
                __builtin_return_address(2),__builtin_return_address(3));
 */
+       if (!mddev->pers)
+               return;
        if (!mddev->pers->error_handler)
                return;
        mddev->pers->error_handler(mddev,rdev);
@@ -4688,12 +4658,13 @@ static void status_resync(struct seq_file *seq, mddev_t * mddev)
         */
        dt = ((jiffies - mddev->resync_mark) / HZ);
        if (!dt) dt++;
-       db = resync - (mddev->resync_mark_cnt/2);
-       rt = (dt * ((unsigned long)(max_blocks-resync) / (db/100+1)))/100;
+       db = (mddev->curr_mark_cnt - atomic_read(&mddev->recovery_active))
+               - mddev->resync_mark_cnt;
+       rt = (dt * ((unsigned long)(max_blocks-resync) / (db/2/100+1)))/100;
 
        seq_printf(seq, " finish=%lu.%lumin", rt / 60, (rt % 60)/6);
 
-       seq_printf(seq, " speed=%ldK/sec", db/dt);
+       seq_printf(seq, " speed=%ldK/sec", db/2/dt);
 }
 
 static void *md_seq_start(struct seq_file *seq, loff_t *pos)
@@ -5025,12 +4996,12 @@ void md_write_start(mddev_t *mddev, struct bio *bi)
                spin_lock_irq(&mddev->write_lock);
                if (mddev->in_sync) {
                        mddev->in_sync = 0;
-                       mddev->sb_dirty = 3;
+                       set_bit(MD_CHANGE_CLEAN, &mddev->flags);
                        md_wakeup_thread(mddev->thread);
                }
                spin_unlock_irq(&mddev->write_lock);
        }
-       wait_event(mddev->sb_wait, mddev->sb_dirty==0);
+       wait_event(mddev->sb_wait, mddev->flags==0);
 }
 
 void md_write_end(mddev_t *mddev)
@@ -5204,6 +5175,7 @@ void md_do_sync(mddev_t *mddev)
 
                j += sectors;
                if (j>1) mddev->curr_resync = j;
+               mddev->curr_mark_cnt = io_sectors;
                if (last_check == 0)
                        /* this is the earliers that rebuilt will be
                         * visible in /proc/mdstat
@@ -5299,7 +5271,6 @@ void md_do_sync(mddev_t *mddev)
                                    !test_bit(In_sync, &rdev->flags) &&
                                    rdev->recovery_offset < mddev->curr_resync)
                                        rdev->recovery_offset = mddev->curr_resync;
-                       mddev->sb_dirty = 1;
                }
        }
 
@@ -5356,7 +5327,7 @@ void md_check_recovery(mddev_t *mddev)
        }
 
        if ( ! (
-               mddev->sb_dirty ||
+               mddev->flags ||
                test_bit(MD_RECOVERY_NEEDED, &mddev->recovery) ||
                test_bit(MD_RECOVERY_DONE, &mddev->recovery) ||
                (mddev->safemode == 1) ||
@@ -5372,14 +5343,14 @@ void md_check_recovery(mddev_t *mddev)
                if (mddev->safemode && !atomic_read(&mddev->writes_pending) &&
                    !mddev->in_sync && mddev->recovery_cp == MaxSector) {
                        mddev->in_sync = 1;
-                       mddev->sb_dirty = 3;
+                       set_bit(MD_CHANGE_CLEAN, &mddev->flags);
                }
                if (mddev->safemode == 1)
                        mddev->safemode = 0;
                spin_unlock_irq(&mddev->write_lock);
 
-               if (mddev->sb_dirty)
-                       md_update_sb(mddev);
+               if (mddev->flags)
+                       md_update_sb(mddev, 0);
 
 
                if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) &&
@@ -5398,7 +5369,7 @@ void md_check_recovery(mddev_t *mddev)
                                /* activate any spares */
                                mddev->pers->spare_active(mddev);
                        }
-                       md_update_sb(mddev);
+                       md_update_sb(mddev, 1);
 
                        /* if array is no-longer degraded, then any saved_raid_disk
                         * information must be scrapped
@@ -5611,15 +5582,9 @@ static __exit void md_exit(void)
 {
        mddev_t *mddev;
        struct list_head *tmp;
-       int i;
+
        blk_unregister_region(MKDEV(MAJOR_NR,0), MAX_MD_DEVS);
        blk_unregister_region(MKDEV(mdp_major,0), MAX_MD_DEVS << MdpMinorShift);
-       for (i=0; i < MAX_MD_DEVS; i++)
-               devfs_remove("md/%d", i);
-       for (i=0; i < MAX_MD_DEVS; i++)
-               devfs_remove("md/d%d", i);
-
-       devfs_remove("md");
 
        unregister_blkdev(MAJOR_NR,"md");
        unregister_blkdev(mdp_major, "mdp");
@@ -5656,8 +5621,8 @@ static int set_ro(const char *val, struct kernel_param *kp)
        return -EINVAL;
 }
 
-module_param_call(start_ro, set_ro, get_ro, NULL, 0600);
-module_param(start_dirty_degraded, int, 0644);
+module_param_call(start_ro, set_ro, get_ro, NULL, S_IRUSR|S_IWUSR);
+module_param(start_dirty_degraded, int, S_IRUGO|S_IWUSR);
 
 
 EXPORT_SYMBOL(register_md_personality);