]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
md: avoid taking the mutex on some ioctls.
authorNeilBrown <neilb@suse.de>
Thu, 19 Jul 2012 10:50:00 +0000 (20:50 +1000)
committerNeilBrown <neilb@suse.de>
Thu, 19 Jul 2012 10:50:00 +0000 (20:50 +1000)
Some ioctls don't need to take the mutex and doing so can cause
a delay as it is held during super-block update.
So move those ioctls out of the mutex and rely on rcu locking
to ensure we don't access stale data.

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

index fcd098794d37d16c7b48b12c54b5ba6f5e6ba60e..2ebc44169e2fe1690721fefb1786bee8bd2de41e 100644 (file)
@@ -711,6 +711,17 @@ static struct md_rdev * find_rdev_nr(struct mddev *mddev, int nr)
        return NULL;
 }
 
+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 +733,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;
@@ -5544,8 +5566,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++;
@@ -5557,6 +5580,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;
@@ -5640,7 +5664,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);
@@ -5659,6 +5684,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;
@@ -6267,18 +6293,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;
 }
 
 /*
@@ -6350,6 +6380,21 @@ 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:
+                       err = get_array_info(mddev, argp);
+                       goto abort;
+
+               case GET_DISK_INFO:
+                       err = get_disk_info(mddev, argp);
+                       goto abort;
+
+               case SET_DISK_FAULTY:
+                       err = set_disk_faulty(mddev, new_decode_dev(arg));
+                       goto abort;
+       }
+
        err = mddev_lock(mddev);
        if (err) {
                printk(KERN_INFO 
@@ -6422,18 +6467,10 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode,
         */
        switch (cmd)
        {
-               case GET_ARRAY_INFO:
-                       err = get_array_info(mddev, argp);
-                       goto done_unlock;
-
                case GET_BITMAP_FILE:
                        err = get_bitmap_file(mddev, argp);
                        goto done_unlock;
 
-               case GET_DISK_INFO:
-                       err = get_disk_info(mddev, argp);
-                       goto done_unlock;
-
                case RESTART_ARRAY_RW:
                        err = restart_array(mddev);
                        goto done_unlock;
@@ -6515,10 +6552,6 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode,
                        err = hot_add_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 RUN_ARRAY:
                        err = do_md_run(mddev);
                        goto done_unlock;