aboutsummaryrefslogtreecommitdiff
path: root/drivers/block
diff options
context:
space:
mode:
authorJosh Durgin <josh.durgin@dreamhost.com>2011-12-05 14:03:05 -0800
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-01-17 08:51:18 -0800
commitfc5eb627a897ac41581692abf60ffea874df2e55 (patch)
tree5b0a4a9571d5b59bb491f049826aef06abef1931 /drivers/block
parent894910deddfe36e66079bea42ae9d728b3f4b5cc (diff)
rbd: use reference counting for the snap context
This prevents a race between requests with a given snap context and header updates that free it. The osd client was already expecting the snap context to be reference counted, since it get()s it in ceph_osdc_build_request and put()s it when the request completes. Also remove the second down_read()/up_read() on header_rwsem in rbd_do_request, which wasn't actually preventing this race or protecting any other data. Signed-off-by: Josh Durgin <josh.durgin@dreamhost.com> Reviewed-by: Alex Elder <elder@inktank.com> (cherry picked from commit d1d25646543134d756a02ffe4e02073faa761f2c) Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/block')
-rw-r--r--drivers/block/rbd.c35
1 files changed, 17 insertions, 18 deletions
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 1dcfd092087..dc26c2e48d8 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -615,7 +615,7 @@ done:
static void rbd_header_free(struct rbd_image_header *header)
{
- kfree(header->snapc);
+ ceph_put_snap_context(header->snapc);
kfree(header->snap_names);
kfree(header->snap_sizes);
}
@@ -893,13 +893,10 @@ static int rbd_do_request(struct request *rq,
dout("rbd_do_request obj=%s ofs=%lld len=%lld\n", obj, len, ofs);
- down_read(&dev->header_rwsem);
-
osdc = &dev->rbd_client->client->osdc;
req = ceph_osdc_alloc_request(osdc, flags, snapc, ops,
false, GFP_NOIO, pages, bio);
if (!req) {
- up_read(&dev->header_rwsem);
ret = -ENOMEM;
goto done_pages;
}
@@ -934,7 +931,6 @@ static int rbd_do_request(struct request *rq,
snapc,
&mtime,
req->r_oid, req->r_oid_len);
- up_read(&dev->header_rwsem);
if (linger_req) {
ceph_osdc_set_request_linger(osdc, req);
@@ -1446,6 +1442,7 @@ static void rbd_rq_fn(struct request_queue *q)
u64 ofs;
int num_segs, cur_seg = 0;
struct rbd_req_coll *coll;
+ struct ceph_snap_context *snapc;
/* peek at request from block layer */
if (!rq)
@@ -1472,21 +1469,20 @@ static void rbd_rq_fn(struct request_queue *q)
spin_unlock_irq(q->queue_lock);
- if (rbd_dev->snap_id != CEPH_NOSNAP) {
- bool snap_exists;
+ down_read(&rbd_dev->header_rwsem);
- down_read(&rbd_dev->header_rwsem);
- snap_exists = rbd_dev->snap_exists;
+ if (rbd_dev->snap_id != CEPH_NOSNAP && !rbd_dev->snap_exists) {
up_read(&rbd_dev->header_rwsem);
-
- if (!snap_exists) {
- dout("request for non-existent snapshot");
- spin_lock_irq(q->queue_lock);
- __blk_end_request_all(rq, -ENXIO);
- continue;
- }
+ dout("request for non-existent snapshot");
+ spin_lock_irq(q->queue_lock);
+ __blk_end_request_all(rq, -ENXIO);
+ continue;
}
+ snapc = ceph_get_snap_context(rbd_dev->header.snapc);
+
+ up_read(&rbd_dev->header_rwsem);
+
dout("%s 0x%x bytes at 0x%llx\n",
do_write ? "write" : "read",
size, blk_rq_pos(rq) * SECTOR_SIZE);
@@ -1496,6 +1492,7 @@ static void rbd_rq_fn(struct request_queue *q)
if (!coll) {
spin_lock_irq(q->queue_lock);
__blk_end_request_all(rq, -ENOMEM);
+ ceph_put_snap_context(snapc);
continue;
}
@@ -1519,7 +1516,7 @@ static void rbd_rq_fn(struct request_queue *q)
/* init OSD command: write or read */
if (do_write)
rbd_req_write(rq, rbd_dev,
- rbd_dev->header.snapc,
+ snapc,
ofs,
op_size, bio,
coll, cur_seg);
@@ -1542,6 +1539,8 @@ next_seg:
if (bp)
bio_pair_release(bp);
spin_lock_irq(q->queue_lock);
+
+ ceph_put_snap_context(snapc);
}
}
@@ -1737,7 +1736,7 @@ static int __rbd_update_snaps(struct rbd_device *rbd_dev)
if head moves */
follow_seq = 1;
- kfree(rbd_dev->header.snapc);
+ ceph_put_snap_context(rbd_dev->header.snapc);
kfree(rbd_dev->header.snap_names);
kfree(rbd_dev->header.snap_sizes);