aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/block
diff options
context:
space:
mode:
authorJosh Durgin <josh.durgin@inktank.com>2014-04-08 14:12:11 -0400
committerIlya Dryomov <idryomov@redhat.com>2014-10-14 13:03:27 -0400
commit4e752f0ab0e8114f4edd7574081dc625d679dd15 (patch)
tree80ef03b61b96c8eacf052607f28fcea03d5cce9f /drivers/block
parent7dd440c9e0711d828442c3e129ab8bcb9aeeac23 (diff)
rbd: access snapshot context and mapping size safely
These fields may both change while the image is mapped if a snapshot is created or deleted or the image is resized. They are guarded by rbd_dev->header_rwsem, so hold that while reading them, and store a local copy to refer to outside of the critical section. The local copy will stay consistent since the snapshot context is reference counted, and the mapping size is just a u64. This prevents torn loads from giving us inconsistent values. Move reading header.snapc into the caller of rbd_img_request_create() so that we only need to take the semaphore once. The read-only caller, rbd_parent_request_create() can just pass NULL for snapc, since the snapshot context is only relevant for writes. Signed-off-by: Josh Durgin <josh.durgin@inktank.com>
Diffstat (limited to 'drivers/block')
-rw-r--r--drivers/block/rbd.c34
1 files changed, 21 insertions, 13 deletions
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index ce457db5d847..eea44ce2d537 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -2057,7 +2057,8 @@ static bool rbd_dev_parent_get(struct rbd_device *rbd_dev)
2057static struct rbd_img_request *rbd_img_request_create( 2057static struct rbd_img_request *rbd_img_request_create(
2058 struct rbd_device *rbd_dev, 2058 struct rbd_device *rbd_dev,
2059 u64 offset, u64 length, 2059 u64 offset, u64 length,
2060 bool write_request) 2060 bool write_request,
2061 struct ceph_snap_context *snapc)
2061{ 2062{
2062 struct rbd_img_request *img_request; 2063 struct rbd_img_request *img_request;
2063 2064
@@ -2065,12 +2066,6 @@ static struct rbd_img_request *rbd_img_request_create(
2065 if (!img_request) 2066 if (!img_request)
2066 return NULL; 2067 return NULL;
2067 2068
2068 if (write_request) {
2069 down_read(&rbd_dev->header_rwsem);
2070 ceph_get_snap_context(rbd_dev->header.snapc);
2071 up_read(&rbd_dev->header_rwsem);
2072 }
2073
2074 img_request->rq = NULL; 2069 img_request->rq = NULL;
2075 img_request->rbd_dev = rbd_dev; 2070 img_request->rbd_dev = rbd_dev;
2076 img_request->offset = offset; 2071 img_request->offset = offset;
@@ -2078,7 +2073,7 @@ static struct rbd_img_request *rbd_img_request_create(
2078 img_request->flags = 0; 2073 img_request->flags = 0;
2079 if (write_request) { 2074 if (write_request) {
2080 img_request_write_set(img_request); 2075 img_request_write_set(img_request);
2081 img_request->snapc = rbd_dev->header.snapc; 2076 img_request->snapc = snapc;
2082 } else { 2077 } else {
2083 img_request->snap_id = rbd_dev->spec->snap_id; 2078 img_request->snap_id = rbd_dev->spec->snap_id;
2084 } 2079 }
@@ -2134,8 +2129,8 @@ static struct rbd_img_request *rbd_parent_request_create(
2134 rbd_assert(obj_request->img_request); 2129 rbd_assert(obj_request->img_request);
2135 rbd_dev = obj_request->img_request->rbd_dev; 2130 rbd_dev = obj_request->img_request->rbd_dev;
2136 2131
2137 parent_request = rbd_img_request_create(rbd_dev->parent, 2132 parent_request = rbd_img_request_create(rbd_dev->parent, img_offset,
2138 img_offset, length, false); 2133 length, false, NULL);
2139 if (!parent_request) 2134 if (!parent_request)
2140 return NULL; 2135 return NULL;
2141 2136
@@ -3183,9 +3178,11 @@ out:
3183static void rbd_handle_request(struct rbd_device *rbd_dev, struct request *rq) 3178static void rbd_handle_request(struct rbd_device *rbd_dev, struct request *rq)
3184{ 3179{
3185 struct rbd_img_request *img_request; 3180 struct rbd_img_request *img_request;
3181 struct ceph_snap_context *snapc = NULL;
3186 u64 offset = (u64)blk_rq_pos(rq) << SECTOR_SHIFT; 3182 u64 offset = (u64)blk_rq_pos(rq) << SECTOR_SHIFT;
3187 u64 length = blk_rq_bytes(rq); 3183 u64 length = blk_rq_bytes(rq);
3188 bool wr = rq_data_dir(rq) == WRITE; 3184 bool wr = rq_data_dir(rq) == WRITE;
3185 u64 mapping_size;
3189 int result; 3186 int result;
3190 3187
3191 /* Ignore/skip any zero-length requests */ 3188 /* Ignore/skip any zero-length requests */
@@ -3226,14 +3223,23 @@ static void rbd_handle_request(struct rbd_device *rbd_dev, struct request *rq)
3226 goto err_rq; /* Shouldn't happen */ 3223 goto err_rq; /* Shouldn't happen */
3227 } 3224 }
3228 3225
3229 if (offset + length > rbd_dev->mapping.size) { 3226 down_read(&rbd_dev->header_rwsem);
3227 mapping_size = rbd_dev->mapping.size;
3228 if (wr) {
3229 snapc = rbd_dev->header.snapc;
3230 ceph_get_snap_context(snapc);
3231 }
3232 up_read(&rbd_dev->header_rwsem);
3233
3234 if (offset + length > mapping_size) {
3230 rbd_warn(rbd_dev, "beyond EOD (%llu~%llu > %llu)", offset, 3235 rbd_warn(rbd_dev, "beyond EOD (%llu~%llu > %llu)", offset,
3231 length, rbd_dev->mapping.size); 3236 length, mapping_size);
3232 result = -EIO; 3237 result = -EIO;
3233 goto err_rq; 3238 goto err_rq;
3234 } 3239 }
3235 3240
3236 img_request = rbd_img_request_create(rbd_dev, offset, length, wr); 3241 img_request = rbd_img_request_create(rbd_dev, offset, length, wr,
3242 snapc);
3237 if (!img_request) { 3243 if (!img_request) {
3238 result = -ENOMEM; 3244 result = -ENOMEM;
3239 goto err_rq; 3245 goto err_rq;
@@ -3256,6 +3262,8 @@ err_rq:
3256 if (result) 3262 if (result)
3257 rbd_warn(rbd_dev, "%s %llx at %llx result %d", 3263 rbd_warn(rbd_dev, "%s %llx at %llx result %d",
3258 wr ? "write" : "read", length, offset, result); 3264 wr ? "write" : "read", length, offset, result);
3265 if (snapc)
3266 ceph_put_snap_context(snapc);
3259 blk_end_request_all(rq, result); 3267 blk_end_request_all(rq, result);
3260} 3268}
3261 3269