]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blobdiff - drivers/md/md.c
lib/raid6: build proper files on corresponding arch
[mirror_ubuntu-jammy-kernel.git] / drivers / md / md.c
index 308e87b417e05a74e4429bac63e1eb2e7af43885..6aefa44348196bf45175f28f675bac0e0d414f31 100644 (file)
@@ -155,32 +155,17 @@ static int start_readonly;
  * like bio_clone, but with a local bio set
  */
 
-static void mddev_bio_destructor(struct bio *bio)
-{
-       struct mddev *mddev, **mddevp;
-
-       mddevp = (void*)bio;
-       mddev = mddevp[-1];
-
-       bio_free(bio, mddev->bio_set);
-}
-
 struct bio *bio_alloc_mddev(gfp_t gfp_mask, int nr_iovecs,
                            struct mddev *mddev)
 {
        struct bio *b;
-       struct mddev **mddevp;
 
        if (!mddev || !mddev->bio_set)
                return bio_alloc(gfp_mask, nr_iovecs);
 
-       b = bio_alloc_bioset(gfp_mask, nr_iovecs,
-                            mddev->bio_set);
+       b = bio_alloc_bioset(gfp_mask, nr_iovecs, mddev->bio_set);
        if (!b)
                return NULL;
-       mddevp = (void*)b;
-       mddevp[-1] = mddev;
-       b->bi_destructor = mddev_bio_destructor;
        return b;
 }
 EXPORT_SYMBOL_GPL(bio_alloc_mddev);
@@ -188,32 +173,10 @@ EXPORT_SYMBOL_GPL(bio_alloc_mddev);
 struct bio *bio_clone_mddev(struct bio *bio, gfp_t gfp_mask,
                            struct mddev *mddev)
 {
-       struct bio *b;
-       struct mddev **mddevp;
-
        if (!mddev || !mddev->bio_set)
                return bio_clone(bio, gfp_mask);
 
-       b = bio_alloc_bioset(gfp_mask, bio->bi_max_vecs,
-                            mddev->bio_set);
-       if (!b)
-               return NULL;
-       mddevp = (void*)b;
-       mddevp[-1] = mddev;
-       b->bi_destructor = mddev_bio_destructor;
-       __bio_clone(b, bio);
-       if (bio_integrity(bio)) {
-               int ret;
-
-               ret = bio_integrity_clone(b, bio, gfp_mask, mddev->bio_set);
-
-               if (ret < 0) {
-                       bio_put(b);
-                       return NULL;
-               }
-       }
-
-       return b;
+       return bio_clone_bioset(bio, gfp_mask, mddev->bio_set);
 }
 EXPORT_SYMBOL_GPL(bio_clone_mddev);
 
@@ -711,7 +674,18 @@ static struct md_rdev * find_rdev_nr(struct mddev *mddev, int nr)
        return NULL;
 }
 
-static struct md_rdev * find_rdev(struct mddev * mddev, dev_t dev)
+static struct md_rdev *find_rdev_nr_rcu(struct mddev *mddev, int nr)
+{
+       struct md_rdev *rdev;
+
+       rdev_for_each_rcu(rdev, mddev)
+               if (rdev->desc_nr == nr)
+                       return rdev;
+
+       return NULL;
+}
+
+static struct md_rdev *find_rdev(struct mddev *mddev, dev_t dev)
 {
        struct md_rdev *rdev;
 
@@ -722,6 +696,17 @@ static struct md_rdev * find_rdev(struct mddev * mddev, dev_t dev)
        return NULL;
 }
 
+static struct md_rdev *find_rdev_rcu(struct mddev *mddev, dev_t dev)
+{
+       struct md_rdev *rdev;
+
+       rdev_for_each_rcu(rdev, mddev)
+               if (rdev->bdev->bd_dev == dev)
+                       return rdev;
+
+       return NULL;
+}
+
 static struct md_personality *find_pers(int level, char *clevel)
 {
        struct md_personality *pers;
@@ -1429,12 +1414,11 @@ static __le32 calc_sb_1_csum(struct mdp_superblock_1 * sb)
        unsigned long long newcsum;
        int size = 256 + le32_to_cpu(sb->max_dev)*2;
        __le32 *isuper = (__le32*)sb;
-       int i;
 
        disk_csum = sb->sb_csum;
        sb->sb_csum = 0;
        newcsum = 0;
-       for (i=0; size>=4; size -= 4 )
+       for (; size >= 4; size -= 4)
                newcsum += le32_to_cpu(*isuper++);
 
        if (size == 2)
@@ -1832,10 +1816,10 @@ retry:
                        memset(bbp, 0xff, PAGE_SIZE);
 
                        for (i = 0 ; i < bb->count ; i++) {
-                               u64 internal_bb = *p++;
+                               u64 internal_bb = p[i];
                                u64 store_bb = ((BB_OFFSET(internal_bb) << 10)
                                                | BB_LEN(internal_bb));
-                               *bbp++ = cpu_to_le64(store_bb);
+                               bbp[i] = cpu_to_le64(store_bb);
                        }
                        bb->changed = 0;
                        if (read_seqretry(&bb->lock, seq))
@@ -2059,8 +2043,14 @@ EXPORT_SYMBOL(md_integrity_register);
 /* Disable data integrity if non-capable/non-matching disk is being added */
 void md_integrity_add_rdev(struct md_rdev *rdev, struct mddev *mddev)
 {
-       struct blk_integrity *bi_rdev = bdev_get_integrity(rdev->bdev);
-       struct blk_integrity *bi_mddev = blk_get_integrity(mddev->gendisk);
+       struct blk_integrity *bi_rdev;
+       struct blk_integrity *bi_mddev;
+
+       if (!mddev->gendisk)
+               return;
+
+       bi_rdev = bdev_get_integrity(rdev->bdev);
+       bi_mddev = blk_get_integrity(mddev->gendisk);
 
        if (!bi_mddev) /* nothing to do */
                return;
@@ -3791,6 +3781,8 @@ resync_start_store(struct mddev *mddev, const char *buf, size_t len)
                return -EINVAL;
 
        mddev->recovery_cp = n;
+       if (mddev->pers)
+               set_bit(MD_CHANGE_CLEAN, &mddev->flags);
        return len;
 }
 static struct md_sysfs_entry md_resync_start =
@@ -4268,6 +4260,13 @@ action_store(struct mddev *mddev, const char *page, size_t len)
                set_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
                set_bit(MD_RECOVERY_SYNC, &mddev->recovery);
        }
+       if (mddev->ro == 2) {
+               /* A write to sync_action is enough to justify
+                * canceling read-auto mode
+                */
+               mddev->ro = 0;
+               md_wakeup_thread(mddev->sync_thread);
+       }
        set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
        md_wakeup_thread(mddev->thread);
        sysfs_notify_dirent_safe(mddev->sysfs_action);
@@ -4278,7 +4277,8 @@ static ssize_t
 mismatch_cnt_show(struct mddev *mddev, char *page)
 {
        return sprintf(page, "%llu\n",
-                      (unsigned long long) mddev->resync_mismatches);
+                      (unsigned long long)
+                      atomic64_read(&mddev->resync_mismatches));
 }
 
 static struct md_sysfs_entry md_scan_mode =
@@ -4399,6 +4399,10 @@ sync_completed_show(struct mddev *mddev, char *page)
        if (!test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
                return sprintf(page, "none\n");
 
+       if (mddev->curr_resync == 1 ||
+           mddev->curr_resync == 2)
+               return sprintf(page, "delayed\n");
+
        if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) ||
            test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
                max_sectors = mddev->resync_max_sectors;
@@ -4748,6 +4752,8 @@ md_attr_store(struct kobject *kobj, struct attribute *attr,
        }
        mddev_get(mddev);
        spin_unlock(&all_mddevs_lock);
+       if (entry->store == new_dev_store)
+               flush_workqueue(md_misc_wq);
        rv = mddev_lock(mddev);
        if (!rv) {
                rv = entry->store(mddev, page, length);
@@ -5006,8 +5012,7 @@ int md_run(struct mddev *mddev)
        }
 
        if (mddev->bio_set == NULL)
-               mddev->bio_set = bioset_create(BIO_POOL_SIZE,
-                                              sizeof(struct mddev *));
+               mddev->bio_set = bioset_create(BIO_POOL_SIZE, 0);
 
        spin_lock(&pers_lock);
        pers = find_pers(mddev->level, mddev->clevel);
@@ -5245,7 +5250,7 @@ static void md_clean(struct mddev *mddev)
        mddev->new_layout = 0;
        mddev->new_chunk_sectors = 0;
        mddev->curr_resync = 0;
-       mddev->resync_mismatches = 0;
+       atomic64_set(&mddev->resync_mismatches, 0);
        mddev->suspend_lo = mddev->suspend_hi = 0;
        mddev->sync_speed_min = mddev->sync_speed_max = 0;
        mddev->recovery = 0;
@@ -5290,7 +5295,7 @@ void md_stop_writes(struct mddev *mddev)
 }
 EXPORT_SYMBOL_GPL(md_stop_writes);
 
-void md_stop(struct mddev *mddev)
+static void __md_stop(struct mddev *mddev)
 {
        mddev->ready = 0;
        mddev->pers->stop(mddev);
@@ -5300,6 +5305,18 @@ void md_stop(struct mddev *mddev)
        mddev->pers = NULL;
        clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
 }
+
+void md_stop(struct mddev *mddev)
+{
+       /* stop the array and free an attached data structures.
+        * This is called from dm-raid
+        */
+       __md_stop(mddev);
+       bitmap_destroy(mddev);
+       if (mddev->bio_set)
+               bioset_free(mddev->bio_set);
+}
+
 EXPORT_SYMBOL_GPL(md_stop);
 
 static int md_set_readonly(struct mddev *mddev, struct block_device *bdev)
@@ -5360,7 +5377,7 @@ static int do_md_stop(struct mddev * mddev, int mode,
                        set_disk_ro(disk, 0);
 
                __md_stop_writes(mddev);
-               md_stop(mddev);
+               __md_stop(mddev);
                mddev->queue->merge_bvec_fn = NULL;
                mddev->queue->backing_dev_info.congested_fn = NULL;
 
@@ -5547,8 +5564,9 @@ static int get_array_info(struct mddev * mddev, void __user * arg)
        int nr,working,insync,failed,spare;
        struct md_rdev *rdev;
 
-       nr=working=insync=failed=spare=0;
-       rdev_for_each(rdev, mddev) {
+       nr = working = insync = failed = spare = 0;
+       rcu_read_lock();
+       rdev_for_each_rcu(rdev, mddev) {
                nr++;
                if (test_bit(Faulty, &rdev->flags))
                        failed++;
@@ -5560,6 +5578,7 @@ static int get_array_info(struct mddev * mddev, void __user * arg)
                                spare++;
                }
        }
+       rcu_read_unlock();
 
        info.major_version = mddev->major_version;
        info.minor_version = mddev->minor_version;
@@ -5643,7 +5662,8 @@ static int get_disk_info(struct mddev * mddev, void __user * arg)
        if (copy_from_user(&info, arg, sizeof(info)))
                return -EFAULT;
 
-       rdev = find_rdev_nr(mddev, info.number);
+       rcu_read_lock();
+       rdev = find_rdev_nr_rcu(mddev, info.number);
        if (rdev) {
                info.major = MAJOR(rdev->bdev->bd_dev);
                info.minor = MINOR(rdev->bdev->bd_dev);
@@ -5662,6 +5682,7 @@ static int get_disk_info(struct mddev * mddev, void __user * arg)
                info.raid_disk = -1;
                info.state = (1<<MD_DISK_REMOVED);
        }
+       rcu_read_unlock();
 
        if (copy_to_user(arg, &info, sizeof(info)))
                return -EFAULT;
@@ -6270,18 +6291,22 @@ static int update_array_info(struct mddev *mddev, mdu_array_info_t *info)
 static int set_disk_faulty(struct mddev *mddev, dev_t dev)
 {
        struct md_rdev *rdev;
+       int err = 0;
 
        if (mddev->pers == NULL)
                return -ENODEV;
 
-       rdev = find_rdev(mddev, dev);
+       rcu_read_lock();
+       rdev = find_rdev_rcu(mddev, dev);
        if (!rdev)
-               return -ENODEV;
-
-       md_error(mddev, rdev);
-       if (!test_bit(Faulty, &rdev->flags))
-               return -EBUSY;
-       return 0;
+               err =  -ENODEV;
+       else {
+               md_error(mddev, rdev);
+               if (!test_bit(Faulty, &rdev->flags))
+                       err = -EBUSY;
+       }
+       rcu_read_unlock();
+       return err;
 }
 
 /*
@@ -6322,24 +6347,23 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode,
         * Commands dealing with the RAID driver but not any
         * particular array:
         */
-       switch (cmd)
-       {
-               case RAID_VERSION:
-                       err = get_version(argp);
-                       goto done;
+       switch (cmd) {
+       case RAID_VERSION:
+               err = get_version(argp);
+               goto done;
 
-               case PRINT_RAID_DEBUG:
-                       err = 0;
-                       md_print_devices();
-                       goto done;
+       case PRINT_RAID_DEBUG:
+               err = 0;
+               md_print_devices();
+               goto done;
 
 #ifndef MODULE
-               case RAID_AUTORUN:
-                       err = 0;
-                       autostart_arrays(arg);
-                       goto done;
+       case RAID_AUTORUN:
+               err = 0;
+               autostart_arrays(arg);
+               goto done;
 #endif
-               default:;
+       default:;
        }
 
        /*
@@ -6353,6 +6377,31 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode,
                goto abort;
        }
 
+       /* Some actions do not requires the mutex */
+       switch (cmd) {
+       case GET_ARRAY_INFO:
+               if (!mddev->raid_disks && !mddev->external)
+                       err = -ENODEV;
+               else
+                       err = get_array_info(mddev, argp);
+               goto abort;
+
+       case GET_DISK_INFO:
+               if (!mddev->raid_disks && !mddev->external)
+                       err = -ENODEV;
+               else
+                       err = get_disk_info(mddev, argp);
+               goto abort;
+
+       case SET_DISK_FAULTY:
+               err = set_disk_faulty(mddev, new_decode_dev(arg));
+               goto abort;
+       }
+
+       if (cmd == ADD_NEW_DISK)
+               /* need to ensure md_delayed_delete() has completed */
+               flush_workqueue(md_misc_wq);
+
        err = mddev_lock(mddev);
        if (err) {
                printk(KERN_INFO 
@@ -6361,50 +6410,44 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode,
                goto abort;
        }
 
-       switch (cmd)
-       {
-               case SET_ARRAY_INFO:
-                       {
-                               mdu_array_info_t info;
-                               if (!arg)
-                                       memset(&info, 0, sizeof(info));
-                               else if (copy_from_user(&info, argp, sizeof(info))) {
-                                       err = -EFAULT;
-                                       goto abort_unlock;
-                               }
-                               if (mddev->pers) {
-                                       err = update_array_info(mddev, &info);
-                                       if (err) {
-                                               printk(KERN_WARNING "md: couldn't update"
-                                                      " array info. %d\n", err);
-                                               goto abort_unlock;
-                                       }
-                                       goto done_unlock;
-                               }
-                               if (!list_empty(&mddev->disks)) {
-                                       printk(KERN_WARNING
-                                              "md: array %s already has disks!\n",
-                                              mdname(mddev));
-                                       err = -EBUSY;
-                                       goto abort_unlock;
-                               }
-                               if (mddev->raid_disks) {
-                                       printk(KERN_WARNING
-                                              "md: array %s already initialised!\n",
-                                              mdname(mddev));
-                                       err = -EBUSY;
-                                       goto abort_unlock;
-                               }
-                               err = set_array_info(mddev, &info);
-                               if (err) {
-                                       printk(KERN_WARNING "md: couldn't set"
-                                              " array info. %d\n", err);
-                                       goto abort_unlock;
-                               }
+       if (cmd == SET_ARRAY_INFO) {
+               mdu_array_info_t info;
+               if (!arg)
+                       memset(&info, 0, sizeof(info));
+               else if (copy_from_user(&info, argp, sizeof(info))) {
+                       err = -EFAULT;
+                       goto abort_unlock;
+               }
+               if (mddev->pers) {
+                       err = update_array_info(mddev, &info);
+                       if (err) {
+                               printk(KERN_WARNING "md: couldn't update"
+                                      " array info. %d\n", err);
+                               goto abort_unlock;
                        }
                        goto done_unlock;
-
-               default:;
+               }
+               if (!list_empty(&mddev->disks)) {
+                       printk(KERN_WARNING
+                              "md: array %s already has disks!\n",
+                              mdname(mddev));
+                       err = -EBUSY;
+                       goto abort_unlock;
+               }
+               if (mddev->raid_disks) {
+                       printk(KERN_WARNING
+                              "md: array %s already initialised!\n",
+                              mdname(mddev));
+                       err = -EBUSY;
+                       goto abort_unlock;
+               }
+               err = set_array_info(mddev, &info);
+               if (err) {
+                       printk(KERN_WARNING "md: couldn't set"
+                              " array info. %d\n", err);
+                       goto abort_unlock;
+               }
+               goto done_unlock;
        }
 
        /*
@@ -6423,60 +6466,51 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode,
        /*
         * Commands even a read-only array can execute:
         */
-       switch (cmd)
-       {
-               case GET_ARRAY_INFO:
-                       err = get_array_info(mddev, argp);
-                       goto done_unlock;
+       switch (cmd) {
+       case GET_BITMAP_FILE:
+               err = get_bitmap_file(mddev, argp);
+               goto done_unlock;
 
-               case GET_BITMAP_FILE:
-                       err = get_bitmap_file(mddev, argp);
-                       goto done_unlock;
+       case RESTART_ARRAY_RW:
+               err = restart_array(mddev);
+               goto done_unlock;
 
-               case GET_DISK_INFO:
-                       err = get_disk_info(mddev, argp);
-                       goto done_unlock;
+       case STOP_ARRAY:
+               err = do_md_stop(mddev, 0, bdev);
+               goto done_unlock;
 
-               case RESTART_ARRAY_RW:
-                       err = restart_array(mddev);
-                       goto done_unlock;
+       case STOP_ARRAY_RO:
+               err = md_set_readonly(mddev, bdev);
+               goto done_unlock;
 
-               case STOP_ARRAY:
-                       err = do_md_stop(mddev, 0, bdev);
+       case BLKROSET:
+               if (get_user(ro, (int __user *)(arg))) {
+                       err = -EFAULT;
                        goto done_unlock;
+               }
+               err = -EINVAL;
 
-               case STOP_ARRAY_RO:
-                       err = md_set_readonly(mddev, bdev);
+               /* if the bdev is going readonly the value of mddev->ro
+                * does not matter, no writes are coming
+                */
+               if (ro)
                        goto done_unlock;
 
-               case BLKROSET:
-                       if (get_user(ro, (int __user *)(arg))) {
-                               err = -EFAULT;
-                               goto done_unlock;
-                       }
-                       err = -EINVAL;
-
-                       /* if the bdev is going readonly the value of mddev->ro
-                        * does not matter, no writes are coming
-                        */
-                       if (ro)
-                               goto done_unlock;
-
-                       /* are we are already prepared for writes? */
-                       if (mddev->ro != 1)
-                               goto done_unlock;
+               /* are we are already prepared for writes? */
+               if (mddev->ro != 1)
+                       goto done_unlock;
 
-                       /* transitioning to readauto need only happen for
-                        * arrays that call md_write_start
-                        */
-                       if (mddev->pers) {
-                               err = restart_array(mddev);
-                               if (err == 0) {
-                                       mddev->ro = 2;
-                                       set_disk_ro(mddev->gendisk, 0);
-                               }
+               /* transitioning to readauto need only happen for
+                * arrays that call md_write_start
+                */
+               if (mddev->pers) {
+                       err = restart_array(mddev);
+                       if (err == 0) {
+                               mddev->ro = 2;
+                               set_disk_ro(mddev->gendisk, 0);
                        }
-                       goto done_unlock;
+               }
+               goto done_unlock;
        }
 
        /*
@@ -6498,41 +6532,36 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode,
                }
        }
 
-       switch (cmd)
+       switch (cmd) {
+       case ADD_NEW_DISK:
        {
-               case ADD_NEW_DISK:
-               {
-                       mdu_disk_info_t info;
-                       if (copy_from_user(&info, argp, sizeof(info)))
-                               err = -EFAULT;
-                       else
-                               err = add_new_disk(mddev, &info);
-                       goto done_unlock;
-               }
-
-               case HOT_REMOVE_DISK:
-                       err = hot_remove_disk(mddev, new_decode_dev(arg));
-                       goto done_unlock;
+               mdu_disk_info_t info;
+               if (copy_from_user(&info, argp, sizeof(info)))
+                       err = -EFAULT;
+               else
+                       err = add_new_disk(mddev, &info);
+               goto done_unlock;
+       }
 
-               case HOT_ADD_DISK:
-                       err = hot_add_disk(mddev, new_decode_dev(arg));
-                       goto done_unlock;
+       case HOT_REMOVE_DISK:
+               err = hot_remove_disk(mddev, new_decode_dev(arg));
+               goto done_unlock;
 
-               case SET_DISK_FAULTY:
-                       err = set_disk_faulty(mddev, new_decode_dev(arg));
-                       goto done_unlock;
+       case HOT_ADD_DISK:
+               err = hot_add_disk(mddev, new_decode_dev(arg));
+               goto done_unlock;
 
-               case RUN_ARRAY:
-                       err = do_md_run(mddev);
-                       goto done_unlock;
+       case RUN_ARRAY:
+               err = do_md_run(mddev);
+               goto done_unlock;
 
-               case SET_BITMAP_FILE:
-                       err = set_bitmap_file(mddev, (int)arg);
-                       goto done_unlock;
+       case SET_BITMAP_FILE:
+               err = set_bitmap_file(mddev, (int)arg);
+               goto done_unlock;
 
-               default:
-                       err = -EINVAL;
-                       goto abort_unlock;
+       default:
+               err = -EINVAL;
+               goto abort_unlock;
        }
 
 done_unlock:
@@ -6679,7 +6708,7 @@ static int md_thread(void * arg)
 
                clear_bit(THREAD_WAKEUP, &thread->flags);
                if (!kthread_should_stop())
-                       thread->run(thread->mddev);
+                       thread->run(thread);
        }
 
        return 0;
@@ -6694,8 +6723,8 @@ void md_wakeup_thread(struct md_thread *thread)
        }
 }
 
-struct md_thread *md_register_thread(void (*run) (struct mddev *), struct mddev *mddev,
-                                const char *name)
+struct md_thread *md_register_thread(void (*run) (struct md_thread *),
+               struct mddev *mddev, const char *name)
 {
        struct md_thread *thread;
 
@@ -6790,7 +6819,11 @@ static void status_resync(struct seq_file *seq, struct mddev * mddev)
        int scale;
        unsigned int per_milli;
 
-       resync = mddev->curr_resync - atomic_read(&mddev->recovery_active);
+       if (mddev->curr_resync <= 3)
+               resync = 0;
+       else
+               resync = mddev->curr_resync
+                       - atomic_read(&mddev->recovery_active);
 
        if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) ||
            test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
@@ -7016,7 +7049,7 @@ static int md_seq_show(struct seq_file *seq, void *v)
                                if (mddev->curr_resync > 2) {
                                        status_resync(seq, mddev);
                                        seq_printf(seq, "\n      ");
-                               } else if (mddev->curr_resync == 1 || mddev->curr_resync == 2)
+                               } else if (mddev->curr_resync >= 1)
                                        seq_printf(seq, "\tresync=DELAYED\n      ");
                                else if (mddev->recovery_cp < MaxSector)
                                        seq_printf(seq, "\tresync=PENDING\n      ");
@@ -7244,13 +7277,16 @@ EXPORT_SYMBOL_GPL(md_allow_write);
 
 #define SYNC_MARKS     10
 #define        SYNC_MARK_STEP  (3*HZ)
-void md_do_sync(struct mddev *mddev)
+#define UPDATE_FREQUENCY (5*60*HZ)
+void md_do_sync(struct md_thread *thread)
 {
+       struct mddev *mddev = thread->mddev;
        struct mddev *mddev2;
        unsigned int currspeed = 0,
                 window;
        sector_t max_sectors,j, io_sectors;
        unsigned long mark[SYNC_MARKS];
+       unsigned long update_time;
        sector_t mark_cnt[SYNC_MARKS];
        int last_mark,m;
        struct list_head *tmp;
@@ -7349,7 +7385,7 @@ void md_do_sync(struct mddev *mddev)
                 * which defaults to physical size, but can be virtual size
                 */
                max_sectors = mddev->resync_max_sectors;
-               mddev->resync_mismatches = 0;
+               atomic64_set(&mddev->resync_mismatches, 0);
                /* we don't use the checkpoint if there's a bitmap */
                if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
                        j = mddev->resync_min;
@@ -7405,8 +7441,12 @@ void md_do_sync(struct mddev *mddev)
                       "md: resuming %s of %s from checkpoint.\n",
                       desc, mdname(mddev));
                mddev->curr_resync = j;
-       }
+       } else
+               mddev->curr_resync = 3; /* no longer delayed */
        mddev->curr_resync_completed = j;
+       sysfs_notify(&mddev->kobj, NULL, "sync_completed");
+       md_new_event(mddev);
+       update_time = jiffies;
 
        blk_start_plug(&plug);
        while (j < max_sectors) {
@@ -7418,6 +7458,7 @@ void md_do_sync(struct mddev *mddev)
                    ((mddev->curr_resync > mddev->curr_resync_completed &&
                      (mddev->curr_resync - mddev->curr_resync_completed)
                      > (max_sectors >> 4)) ||
+                    time_after_eq(jiffies, update_time + UPDATE_FREQUENCY) ||
                     (j - mddev->curr_resync_completed)*2
                     >= mddev->resync_max - mddev->curr_resync_completed
                            )) {
@@ -7425,6 +7466,10 @@ void md_do_sync(struct mddev *mddev)
                        wait_event(mddev->recovery_wait,
                                   atomic_read(&mddev->recovery_active) == 0);
                        mddev->curr_resync_completed = j;
+                       if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) &&
+                           j > mddev->recovery_cp)
+                               mddev->recovery_cp = j;
+                       update_time = jiffies;
                        set_bit(MD_CHANGE_CLEAN, &mddev->flags);
                        sysfs_notify(&mddev->kobj, NULL, "sync_completed");
                }
@@ -7459,7 +7504,8 @@ void md_do_sync(struct mddev *mddev)
                        break;
 
                j += sectors;
-               if (j>1) mddev->curr_resync = j;
+               if (j > 2)
+                       mddev->curr_resync = j;
                mddev->curr_mark_cnt = io_sectors;
                if (last_check == 0)
                        /* this is the earliest that rebuild will be
@@ -7581,8 +7627,6 @@ static int remove_and_add_spares(struct mddev *mddev)
        int spares = 0;
        int removed = 0;
 
-       mddev->curr_resync_completed = 0;
-
        rdev_for_each(rdev, mddev)
                if (rdev->raid_disk >= 0 &&
                    !test_bit(Blocked, &rdev->flags) &&
@@ -7777,6 +7821,7 @@ void md_check_recovery(struct mddev *mddev)
                /* Set RUNNING before clearing NEEDED to avoid
                 * any transients in the value of "sync_action".
                 */
+               mddev->curr_resync_completed = 0;
                set_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
                /* Clear some bits that don't mean anything, but
                 * might be left set
@@ -7790,7 +7835,7 @@ void md_check_recovery(struct mddev *mddev)
                /* no recovery is running.
                 * remove any failed drives, then
                 * add spares if possible.
-                * Spare are also removed and re-added, to allow
+                * Spares are also removed and re-added, to allow
                 * the personality to fail the re-add.
                 */
 
@@ -7907,9 +7952,9 @@ int md_is_badblock(struct badblocks *bb, sector_t s, int sectors,
                   sector_t *first_bad, int *bad_sectors)
 {
        int hi;
-       int lo = 0;
+       int lo;
        u64 *p = bb->page;
-       int rv = 0;
+       int rv;
        sector_t target = s + sectors;
        unsigned seq;
 
@@ -7924,7 +7969,8 @@ int md_is_badblock(struct badblocks *bb, sector_t s, int sectors,
 
 retry:
        seq = read_seqbegin(&bb->lock);
-
+       lo = 0;
+       rv = 0;
        hi = bb->count;
 
        /* Binary search between lo and hi for 'target'