Merge tag 'lsk-v4.4-17.03-android' of git://git.linaro.org/kernel/linux-linaro-stable.git
[firefly-linux-kernel-4.4.55.git] / drivers / md / linear.c
index fa7d577f3d1257a6a029ae897702d74853d54919..6ba3227e29b27ff924240ef125654c227fd88992 100644 (file)
@@ -52,60 +52,26 @@ static inline struct dev_info *which_dev(struct mddev *mddev, sector_t sector)
        return conf->disks + lo;
 }
 
-/**
- *     linear_mergeable_bvec -- tell bio layer if two requests can be merged
- *     @q: request queue
- *     @bvm: properties of new bio
- *     @biovec: the request that could be merged to it.
- *
- *     Return amount of bytes we can take at this offset
+/*
+ * In linear_congested() conf->raid_disks is used as a copy of
+ * mddev->raid_disks to iterate conf->disks[], because conf->raid_disks
+ * and conf->disks[] are created in linear_conf(), they are always
+ * consitent with each other, but mddev->raid_disks does not.
  */
-static int linear_mergeable_bvec(struct mddev *mddev,
-                                struct bvec_merge_data *bvm,
-                                struct bio_vec *biovec)
-{
-       struct dev_info *dev0;
-       unsigned long maxsectors, bio_sectors = bvm->bi_size >> 9;
-       sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev);
-       int maxbytes = biovec->bv_len;
-       struct request_queue *subq;
-
-       dev0 = which_dev(mddev, sector);
-       maxsectors = dev0->end_sector - sector;
-       subq = bdev_get_queue(dev0->rdev->bdev);
-       if (subq->merge_bvec_fn) {
-               bvm->bi_bdev = dev0->rdev->bdev;
-               bvm->bi_sector -= dev0->end_sector - dev0->rdev->sectors;
-               maxbytes = min(maxbytes, subq->merge_bvec_fn(subq, bvm,
-                                                            biovec));
-       }
-
-       if (maxsectors < bio_sectors)
-               maxsectors = 0;
-       else
-               maxsectors -= bio_sectors;
-
-       if (maxsectors <= (PAGE_SIZE >> 9 ) && bio_sectors == 0)
-               return maxbytes;
-
-       if (maxsectors > (maxbytes >> 9))
-               return maxbytes;
-       else
-               return maxsectors << 9;
-}
-
 static int linear_congested(struct mddev *mddev, int bits)
 {
        struct linear_conf *conf;
        int i, ret = 0;
 
-       conf = mddev->private;
+       rcu_read_lock();
+       conf = rcu_dereference(mddev->private);
 
-       for (i = 0; i < mddev->raid_disks && !ret ; i++) {
+       for (i = 0; i < conf->raid_disks && !ret ; i++) {
                struct request_queue *q = bdev_get_queue(conf->disks[i].rdev->bdev);
                ret |= bdi_congested(&q->backing_dev_info, bits);
        }
 
+       rcu_read_unlock();
        return ret;
 }
 
@@ -185,6 +151,19 @@ static struct linear_conf *linear_conf(struct mddev *mddev, int raid_disks)
                        conf->disks[i-1].end_sector +
                        conf->disks[i].rdev->sectors;
 
+       /*
+        * conf->raid_disks is copy of mddev->raid_disks. The reason to
+        * keep a copy of mddev->raid_disks in struct linear_conf is,
+        * mddev->raid_disks may not be consistent with pointers number of
+        * conf->disks[] when it is updated in linear_add() and used to
+        * iterate old conf->disks[] earray in linear_congested().
+        * Here conf->raid_disks is always consitent with number of
+        * pointers in conf->disks[] array, and mddev->private is updated
+        * with rcu_assign_pointer() in linear_addr(), such race can be
+        * avoided.
+        */
+       conf->raid_disks = raid_disks;
+
        return conf;
 
 out:
@@ -237,15 +216,23 @@ static int linear_add(struct mddev *mddev, struct md_rdev *rdev)
        if (!newconf)
                return -ENOMEM;
 
+       /* newconf->raid_disks already keeps a copy of * the increased
+        * value of mddev->raid_disks, WARN_ONCE() is just used to make
+        * sure of this. It is possible that oldconf is still referenced
+        * in linear_congested(), therefore kfree_rcu() is used to free
+        * oldconf until no one uses it anymore.
+        */
        mddev_suspend(mddev);
-       oldconf = mddev->private;
+       oldconf = rcu_dereference(mddev->private);
        mddev->raid_disks++;
-       mddev->private = newconf;
+       WARN_ONCE(mddev->raid_disks != newconf->raid_disks,
+               "copied raid_disks doesn't match mddev->raid_disks");
+       rcu_assign_pointer(mddev->private, newconf);
        md_set_array_sectors(mddev, linear_size(mddev, 0, 0));
        set_capacity(mddev->gendisk, mddev->array_sectors);
        mddev_resume(mddev);
        revalidate_disk(mddev->gendisk);
-       kfree(oldconf);
+       kfree_rcu(oldconf, rcu);
        return 0;
 }
 
@@ -297,7 +284,7 @@ static void linear_make_request(struct mddev *mddev, struct bio *bio)
                if (unlikely((split->bi_rw & REQ_DISCARD) &&
                         !blk_queue_discard(bdev_get_queue(split->bi_bdev)))) {
                        /* Just ignore it */
-                       bio_endio(split, 0);
+                       bio_endio(split);
                } else
                        generic_make_request(split);
        } while (split != bio);
@@ -338,7 +325,6 @@ static struct md_personality linear_personality =
        .size           = linear_size,
        .quiesce        = linear_quiesce,
        .congested      = linear_congested,
-       .mergeable_bvec = linear_mergeable_bvec,
 };
 
 static int __init linear_init (void)