md: use mddev->lock to protect updates to resync_{min,max}.
authorNeilBrown <neilb@suse.de>
Mon, 15 Dec 2014 01:57:01 +0000 (12:57 +1100)
committerNeilBrown <neilb@suse.de>
Thu, 5 Feb 2015 22:32:56 +0000 (09:32 +1100)
There are interdependencies between these two sysfs attributes
and whether a resync is currently running.

Rather than depending on reconfig_mutex to ensure no races when
testing these interdependencies are met, use the spinlock.
This will allow the mutex to be remove from protecting this
code in a subsequent patch.

Signed-off-by: NeilBrown <neilb@suse.de>
drivers/md/md.c
drivers/md/md.h

index 0f9bc9a02a1b402899b579b02bdd257e22136a5c..0f00c1e2d829afa95fda45b4f1cd1ec58010b38d 100644 (file)
@@ -4269,22 +4269,36 @@ static ssize_t
 min_sync_store(struct mddev *mddev, const char *buf, size_t len)
 {
        unsigned long long min;
+       int err;
+       int chunk;
+
        if (kstrtoull(buf, 10, &min))
                return -EINVAL;
+
+       spin_lock(&mddev->lock);
+       err = -EINVAL;
        if (min > mddev->resync_max)
-               return -EINVAL;
+               goto out_unlock;
+
+       err = -EBUSY;
        if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
-               return -EBUSY;
+               goto out_unlock;
 
        /* Must be a multiple of chunk_size */
-       if (mddev->chunk_sectors) {
+       chunk = mddev->chunk_sectors;
+       if (chunk) {
                sector_t temp = min;
-               if (sector_div(temp, mddev->chunk_sectors))
-                       return -EINVAL;
+
+               err = -EINVAL;
+               if (sector_div(temp, chunk))
+                       goto out_unlock;
        }
        mddev->resync_min = min;
+       err = 0;
 
-       return len;
+out_unlock:
+       spin_unlock(&mddev->lock);
+       return err ?: len;
 }
 
 static struct md_sysfs_entry md_min_sync =
@@ -4302,29 +4316,42 @@ max_sync_show(struct mddev *mddev, char *page)
 static ssize_t
 max_sync_store(struct mddev *mddev, const char *buf, size_t len)
 {
+       int err;
+       spin_lock(&mddev->lock);
        if (strncmp(buf, "max", 3) == 0)
                mddev->resync_max = MaxSector;
        else {
                unsigned long long max;
+               int chunk;
+
+               err = -EINVAL;
                if (kstrtoull(buf, 10, &max))
-                       return -EINVAL;
+                       goto out_unlock;
                if (max < mddev->resync_min)
-                       return -EINVAL;
+                       goto out_unlock;
+
+               err = -EBUSY;
                if (max < mddev->resync_max &&
                    mddev->ro == 0 &&
                    test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
-                       return -EBUSY;
+                       goto out_unlock;
 
                /* Must be a multiple of chunk_size */
-               if (mddev->chunk_sectors) {
+               chunk = mddev->chunk_sectors;
+               if (chunk) {
                        sector_t temp = max;
-                       if (sector_div(temp, mddev->chunk_sectors))
-                               return -EINVAL;
+
+                       err = -EINVAL;
+                       if (sector_div(temp, chunk))
+                               goto out_unlock;
                }
                mddev->resync_max = max;
        }
        wake_up(&mddev->recovery_wait);
-       return len;
+       err = 0;
+out_unlock:
+       spin_unlock(&mddev->lock);
+       return err ?: len;
 }
 
 static struct md_sysfs_entry md_max_sync =
@@ -7585,6 +7612,7 @@ void md_do_sync(struct md_thread *thread)
  skip:
        set_bit(MD_CHANGE_DEVS, &mddev->flags);
 
+       spin_lock(&mddev->lock);
        if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery)) {
                /* We completed so min/max setting can be forgotten if used. */
                if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
@@ -7593,6 +7621,8 @@ void md_do_sync(struct md_thread *thread)
        } else if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
                mddev->resync_min = mddev->curr_resync_completed;
        mddev->curr_resync = 0;
+       spin_unlock(&mddev->lock);
+
        wake_up(&resync_wait);
        set_bit(MD_RECOVERY_DONE, &mddev->recovery);
        md_wakeup_thread(mddev->thread);
@@ -7793,7 +7823,9 @@ void md_check_recovery(struct mddev *mddev)
                 * any transients in the value of "sync_action".
                 */
                mddev->curr_resync_completed = 0;
+               spin_lock(&mddev->lock);
                set_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
+               spin_unlock(&mddev->lock);
                /* Clear some bits that don't mean anything, but
                 * might be left set
                 */
index b4fbd6a63fcf0e7de7658d784afe436a428a91c6..6bf3faa951ec8bceb3926f0286d7a92b062db8d5 100644 (file)
@@ -394,6 +394,8 @@ struct mddev {
         *   pers (also protected by reconfig_mutex and pending IO).
         *   clearing ->bitmap
         *   clearing ->bitmap_info.file
+        *   changing ->resync_{min,max}
+        *   setting MD_RECOVERY_RUNNING (which interacts with resync_{min,max})
         */
        spinlock_t                      lock;
        wait_queue_head_t               sb_wait;        /* for waiting on superblock updates */