rbd: read image size for discard check safely
authorJosh Durgin <josh.durgin@inktank.com>
Fri, 4 Apr 2014 19:06:32 +0000 (12:06 -0700)
committerIlya Dryomov <idryomov@redhat.com>
Tue, 14 Oct 2014 17:03:32 +0000 (21:03 +0400)
In rbd_img_request_fill() the image size is only checked to determine
whether we can truncate an object instead of zeroing it for discard
requests. Take rbd_dev->header_rwsem while reading the image size, and
move this read into the discard check, so that non-discard ops don't
need to take the semaphore in this function.

Signed-off-by: Josh Durgin <josh.durgin@inktank.com>
drivers/block/rbd.c

index e2f7a708e20d5c27adf0173295364cb38174ee2f..31ace3dd33e46650078d6ecee1b5110a86e74936 100644 (file)
@@ -2332,7 +2332,6 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,
                (int)type, data_desc);
 
        img_offset = img_request->offset;
-       img_end = rbd_dev->header.image_size;
        resid = img_request->length;
        rbd_assert(resid > 0);
 
@@ -2397,13 +2396,20 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,
                        if (!offset && (length == object_size)
                                && (!img_request_layered_test(img_request) ||
                                        (rbd_dev->parent_overlap <=
-                                               obj_request->img_offset)))
+                                               obj_request->img_offset))) {
                                opcode = CEPH_OSD_OP_DELETE;
-                       else if ((offset + length == object_size) ||
-                               (obj_request->img_offset + length == img_end))
+                       } else if ((offset + length == object_size)) {
                                opcode = CEPH_OSD_OP_TRUNCATE;
-                       else
-                               opcode = CEPH_OSD_OP_ZERO;
+                       } else {
+                               down_read(&rbd_dev->header_rwsem);
+                               img_end = rbd_dev->header.image_size;
+                               up_read(&rbd_dev->header_rwsem);
+
+                               if (obj_request->img_offset + length == img_end)
+                                       opcode = CEPH_OSD_OP_TRUNCATE;
+                               else
+                                       opcode = CEPH_OSD_OP_ZERO;
+                       }
                } else if (img_request_write_test(img_request)) {
                        op_type = OBJ_OP_WRITE;
                        opcode = CEPH_OSD_OP_WRITE;