diff options
author | Josh Durgin <josh.durgin@inktank.com> | 2014-04-08 14:12:11 -0400 |
---|---|---|
committer | Ilya Dryomov <idryomov@redhat.com> | 2014-10-14 13:03:27 -0400 |
commit | 4e752f0ab0e8114f4edd7574081dc625d679dd15 (patch) | |
tree | 80ef03b61b96c8eacf052607f28fcea03d5cce9f /drivers/block | |
parent | 7dd440c9e0711d828442c3e129ab8bcb9aeeac23 (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.c | 34 |
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) | |||
2057 | static struct rbd_img_request *rbd_img_request_create( | 2057 | static 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: | |||
3183 | static void rbd_handle_request(struct rbd_device *rbd_dev, struct request *rq) | 3178 | static 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 | ||