diff options
author | NeilBrown <neilb@suse.de> | 2014-12-15 12:56:57 +1100 |
---|---|---|
committer | NeilBrown <neilb@suse.de> | 2015-02-04 08:35:52 +1100 |
commit | 64590f45ddc7147fa1968147a1f5b5c436b728fe (patch) | |
tree | c33e8ce09d739bac929e8ca943a253cb03cafd12 /drivers/md/md.c | |
parent | 5c675f83c68fbdf9c0e103c1090b06be747fa62c (diff) | |
download | op-kernel-dev-64590f45ddc7147fa1968147a1f5b5c436b728fe.zip op-kernel-dev-64590f45ddc7147fa1968147a1f5b5c436b728fe.tar.gz |
md: make merge_bvec_fn more robust in face of personality changes.
There is no locking around calls to merge_bvec_fn(), so
it is possible that calls which coincide with a level (or personality)
change could go wrong.
So create a central dispatch point for these functions and use
rcu_read_lock().
If the array is suspended, reject any merge that can be rejected.
If not, we know it is safe to call the function.
Signed-off-by: NeilBrown <neilb@suse.de>
Diffstat (limited to 'drivers/md/md.c')
-rw-r--r-- | drivers/md/md.c | 24 |
1 files changed, 24 insertions, 0 deletions
diff --git a/drivers/md/md.c b/drivers/md/md.c index d45f52e..9f0ff71 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -339,6 +339,29 @@ static int md_congested(void *data, int bits) return mddev_congested(mddev, bits); } +static int md_mergeable_bvec(struct request_queue *q, + struct bvec_merge_data *bvm, + struct bio_vec *biovec) +{ + struct mddev *mddev = q->queuedata; + int ret; + rcu_read_lock(); + if (mddev->suspended) { + /* Must always allow one vec */ + if (bvm->bi_size == 0) + ret = biovec->bv_len; + else + ret = 0; + } else { + struct md_personality *pers = mddev->pers; + if (pers && pers->mergeable_bvec) + ret = pers->mergeable_bvec(mddev, bvm, biovec); + else + ret = biovec->bv_len; + } + rcu_read_unlock(); + return ret; +} /* * Generic flush handling for md */ @@ -4925,6 +4948,7 @@ int md_run(struct mddev *mddev) if (mddev->queue) { mddev->queue->backing_dev_info.congested_data = mddev; mddev->queue->backing_dev_info.congested_fn = md_congested; + blk_queue_merge_bvec(mddev->queue, md_mergeable_bvec); } if (mddev->pers->sync_request) { if (mddev->kobj.sd && |