rbd bug fix and discard support
Subsystems (2)
cover rbd bug fix and discard support
The first patch in this series fixes a potential race, which may have been related to http://tracker.ceph.com/issues/7465, though there's not enough information to tell. I put it before the discard-related patches so it could be cherry-picked to older kernels as needed.
The next three patches are rebased versions of Guangliang's discard patches, which have already been reviewd.
The rest are bug fixes which made discard work in my testing for format 1 and layered format 2 images.
Guangliang Zhao (3):
rbd: skip the copyup when an entire object writing
rbd: extend the operation type
rbd: add discard support for rbd
Josh Durgin (8):
rbd: access snapshot context and mapping size safely
rbd: read image size for discard check safely
rbd: fix snapshot context reference count for discards
rbd: tolerate -ENOENT for discard operations
rbd: make discard trigger copy-on-write
rbd: extract a method for adding object operations
rbd: use helpers to handle discard for layered images correctly
rbd: set the remaining discard properties to enable support
drivers/block/rbd.c | 385 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 269 insertions(+), 116 deletions(-)
Signature · 1 line
1.7.10.4
PATCH 1/11 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.
--- drivers/block/rbd.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-)
drivers/block/rbd.c +19 -12
@@ -1961,7 +1961,8 @@ static bool rbd_dev_parent_get(struct rbd_device *rbd_dev)static struct rbd_img_request *rbd_img_request_create (struct rbd_device *rbd_dev,u64 offset,u64 length, -bool write_request) +bool write_request, +struct ceph_snap_context *snapc) {struct rbd_img_request *img_request;
@@ -1969,12 +1970,6 @@ static struct rbd_img_request *rbd_img_request_create(if (!img_request)return NULL ; -if (write_request) { -down_read (&rbd_dev->header_rwsem); -ceph_get_snap_context (rbd_dev->header.snapc); -up_read (&rbd_dev->header_rwsem); - } - img_request->rq =NULL ; img_request->rbd_dev = rbd_dev; img_request->offset = offset;
@@ -1982,7 +1977,7 @@ static struct rbd_img_request *rbd_img_request_create( img_request->flags =0 ;if (write_request) {img_request_write_set (img_request); - img_request->snapc = rbd_dev->header.snapc; + img_request->snapc = snapc; }else { img_request->snap_id = rbd_dev->spec->snap_id; }
@@ -2038,8 +2033,8 @@ static struct rbd_img_request *rbd_parent_request_create(rbd_assert (obj_request->img_request); rbd_dev = obj_request->img_request->rbd_dev; - parent_request =rbd_img_request_create (rbd_dev->parent, - img_offset, length, false); + parent_request =rbd_img_request_create (rbd_dev->parent, img_offset, + length, false,NULL );if (!parent_request)return NULL ;
@@ -3065,8 +3060,10 @@ static void rbd_request_fn(struct request_queue *q)while ((rq =blk_fetch_request (q))) {bool write_request =rq_data_dir (rq) ==WRITE ;struct rbd_img_request *img_request; +struct ceph_snap_context *snapc =NULL ;u64 offset;u64 length; +u64 mapping_size;/* Ignore any non-FS requests that filter through. */
@@ -3120,8 +3117,16 @@ static void rbd_request_fn(struct request_queue *q) goto end_request;/* Shouldn't happen */ } +down_read (&rbd_dev->header_rwsem); + mapping_size = rbd_dev->mapping.size; +if (op_type !=OBJ_OP_READ ) { + snapc = rbd_dev->header.snapc; +ceph_get_snap_context (snapc); + } +up_read (&rbd_dev->header_rwsem); + result = -EIO ; -if (offset + length > rbd_dev->mapping.size) { +if (offset + length > mapping_size) {rbd_warn (rbd_dev,"beyond EOD (%llu~%llu > %llu)\n" , offset, length, rbd_dev->mapping.size); goto end_request;
@@ -3129,7 +3134,7 @@ static void rbd_request_fn(struct request_queue *q) result = -ENOMEM ; img_request =rbd_img_request_create (rbd_dev, offset, length, - write_request); + write_request, snapc);if (!img_request) goto end_request;
@@ -3147,6 +3152,8 @@ end_request:rbd_warn (rbd_dev,"%s %llx at %llx result %d\n" , write_request ?"write" :"read" , length, offset, result); +if (snapc) +ceph_put_snap_context (snapc);__blk_end_request_all (rq, result); }
Signature · 1 line
1.7.10.4
PATCH 2/11 rbd: skip the copyup when an entire object writing
In reply to Josh DurginFrom: Guangliang Zhao <[email protected]>
It need to copyup the parent's content when layered writing, but an entire object write would overwrite it, so skip it.
--- drivers/block/rbd.c | 49 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 15 deletions(-)
drivers/block/rbd.c +34 -15
@@ -2645,34 +2645,53 @@ out:return ret; } -static int rbd_img_obj_request_submit (struct rbd_obj_request *obj_request) +static bool rbd_img_obj_request_simple (struct rbd_obj_request *obj_request) {struct rbd_img_request *img_request;struct rbd_device *rbd_dev; -bool known; +u64 obj_size;rbd_assert (obj_request_img_data_test (obj_request)); img_request = obj_request->img_request;rbd_assert (img_request); rbd_dev = img_request->rbd_dev; + obj_size = (u64 )1 << rbd_dev->header.obj_order; +/* Read requests didn't need special handling */ +if (!img_request_write_test (img_request)) +return true; +/* No-layered writes are simple requests*/ +if (!img_request_layered_test (img_request)) +return true;/* -* Only writes to layered images need special handling. -* Reads and non-layered writes are simple object requests. * Layered writes that start beyond the end of the overlap -* with the parent have no parent data, so they too are -* simple object requests. Finally, if the target object is -* known to already exist, its parent data has already been -* copied, so a write to the object can also be handled as a -* simple object request. +* with the parent have no parent data, so they are simple +* object requests. */ -if (!img_request_write_test (img_request) || - !img_request_layered_test (img_request) || - rbd_dev->parent_overlap <= obj_request->img_offset || - ((known =obj_request_known_test (obj_request)) && -obj_request_exists_test (obj_request))) { +if (rbd_dev->parent_overlap <= obj_request->img_offset) +return true; +/* +* If the obj_request aligns with the boundary and equals +* to the size of an object, it doesn't need copyup, because +* the obj_request will overwrite it finally. +*/ +if ((!obj_request->offset) && (obj_request->length == obj_size)) +return true; +/* +* If the target object is known to already exist, its parent +* data has already been copied, so a write to the object can +* also be handled as a simple object request +*/ +if (obj_request_known_test (obj_request) && +obj_request_exists_test (obj_request)) +return true; +return false; +} +static int rbd_img_obj_request_submit (struct rbd_obj_request *obj_request) +{ +if (rbd_img_obj_request_simple (obj_request)) {struct rbd_device *rbd_dev;struct ceph_osd_client *osdc;
@@ -2688,7 +2707,7 @@ static int rbd_img_obj_request_submit(struct rbd_obj_request *obj_request)* start by reading the data for the full target object from * the parent so we can use it for a copyup to the target. */ -if (known) +if (obj_request_known_test (obj_request))return rbd_img_obj_parent_read_full (obj_request);/* We don't know whether the target exists. Go find out. */
Signature · 1 line
1.7.10.4
PATCH 3/11 rbd: extend the operation type
In reply to Josh DurginFrom: Guangliang Zhao <[email protected]>
It could only handle the read and write operations now, extend it for the coming discard support.
--- drivers/block/rbd.c | 94 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 34 deletions(-)
drivers/block/rbd.c +60 -34
@@ -209,6 +209,11 @@ enum obj_request_type {OBJ_REQUEST_NODATA ,OBJ_REQUEST_BIO ,OBJ_REQUEST_PAGES }; +enum obj_operation_type { +OBJ_OP_WRITE , +OBJ_OP_READ , +}; +enum obj_req_flags {OBJ_REQ_DONE ,/* completion flag: not done = 0, done = 1 */ OBJ_REQ_IMG_DATA, /* object usage: standalone = 0, image = 1 */
@@ -715,6 +720,18 @@ static int parse_rbd_opts_token(char *c, void *private)return 0 ; } +static char *obj_op_name (enum obj_operation_type op_type) +{ +switch (op_type) { +case OBJ_OP_READ : +return "read" ; +case OBJ_OP_WRITE : +return "write" ; +default : +return "invalid op code" ; + } +} +/* * Get a ceph client with specific addr and configuration, if one does * not exist create it. Either way, ceph_opts is consumed by this
@@ -1727,7 +1744,7 @@ static void rbd_osd_req_format_write(struct rbd_obj_request *obj_request)*/ static struct ceph_osd_request *rbd_osd_req_create (struct rbd_device *rbd_dev, -bool write_request, +enum obj_operation_type op_type,unsigned int num_ops,struct rbd_obj_request *obj_request) {
@@ -1735,16 +1752,14 @@ static struct ceph_osd_request *rbd_osd_req_create(struct ceph_osd_client *osdc;struct ceph_osd_request *osd_req; -if (obj_request_img_data_test (obj_request)) { +if (obj_request_img_data_test (obj_request) && op_type ==OBJ_OP_WRITE ) {struct rbd_img_request *img_request = obj_request->img_request; -rbd_assert (write_request == -img_request_write_test (img_request)); -if (write_request) - snapc = img_request->snapc; +rbd_assert (img_request_write_test (img_request)); + snapc = img_request->snapc; } -rbd_assert (num_ops ==1 || (write_request && num_ops ==2 )); +rbd_assert (num_ops ==1 || ((op_type ==OBJ_OP_WRITE ) && num_ops ==2 ));/* Allocate and initialize the request, for the num_ops ops */
@@ -1754,7 +1769,7 @@ static struct ceph_osd_request *rbd_osd_req_create(if (!osd_req)return NULL ;/* ENOMEM */ -if (write_request) +if (op_type ==OBJ_OP_WRITE ) osd_req->r_flags =CEPH_OSD_FLAG_WRITE |CEPH_OSD_FLAG_ONDISK ;else osd_req->r_flags =CEPH_OSD_FLAG_READ ;
@@ -1961,7 +1976,7 @@ static bool rbd_dev_parent_get(struct rbd_device *rbd_dev)static struct rbd_img_request *rbd_img_request_create (struct rbd_device *rbd_dev,u64 offset,u64 length, -bool write_request, +enum obj_operation_type op_type,struct ceph_snap_context *snapc) {struct rbd_img_request *img_request;
@@ -1975,7 +1990,7 @@ static struct rbd_img_request *rbd_img_request_create( img_request->offset = offset; img_request->length = length; img_request->flags =0 ; -if (write_request) { +if (op_type ==OBJ_OP_WRITE ) {img_request_write_set (img_request); img_request->snapc = snapc; }else {
@@ -1992,8 +2007,7 @@ static struct rbd_img_request *rbd_img_request_create(kref_init (&img_request->kref);dout ("%s: rbd_dev %p %s %llu/%llu -> img %p\n" , __func__, rbd_dev, - write_request ?"write" :"read" , offset, length, - img_request); +obj_op_name (op_type), offset, length, img_request);return img_request; }
@@ -2034,7 +2048,7 @@ static struct rbd_img_request *rbd_parent_request_create( rbd_dev = obj_request->img_request->rbd_dev; parent_request =rbd_img_request_create (rbd_dev->parent, img_offset, - length, false,NULL ); + length,OBJ_OP_READ ,NULL );if (!parent_request)return NULL ;
@@ -2075,11 +2089,14 @@ static bool rbd_img_obj_end_request(struct rbd_obj_request *obj_request) result = obj_request->result;if (result) {struct rbd_device *rbd_dev = img_request->rbd_dev; +enum obj_operation_type op_type; + + op_type =img_request_write_test (img_request) ?OBJ_OP_WRITE : +OBJ_OP_READ ;rbd_warn (rbd_dev,"%s %llx at %llx (%llx)\n" , -img_request_write_test (img_request) ?"write" :"read" , - obj_request->length, obj_request->img_offset, - obj_request->offset); +obj_op_name (op_type), obj_request->length, + obj_request->img_offset, obj_request->offset);rbd_warn (rbd_dev," result %d xferred %x\n" , result, xferred);if (!img_request->result)
@@ -2157,10 +2174,10 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,struct rbd_device *rbd_dev = img_request->rbd_dev;struct rbd_obj_request *obj_request =NULL ;struct rbd_obj_request *next_obj_request; -bool write_request =img_request_write_test (img_request);struct bio *bio_list =NULL ;unsigned int bio_offset =0 ;struct page **pages =NULL ; +enum obj_operation_type op_type;u64 img_offset;u64 resid;u16 opcode;
@@ -2168,7 +2185,6 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,dout ("%s: img %p type %d data_desc %p\n" , __func__, img_request, (int )type, data_desc); - opcode = write_request ?CEPH_OSD_OP_WRITE :CEPH_OSD_OP_READ ; img_offset = img_request->offset; resid = img_request->length;rbd_assert (resid >0 );
@@ -2230,15 +2246,21 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request, pages += page_count; } - osd_req =rbd_osd_req_create (rbd_dev, write_request, - (write_request ?2 :1 ), - obj_request); +if (img_request_write_test (img_request)) { + op_type =OBJ_OP_WRITE ; + opcode =CEPH_OSD_OP_WRITE ; + }else { + op_type =OBJ_OP_READ ; + opcode =CEPH_OSD_OP_READ ; + } + + osd_req =rbd_osd_req_create (rbd_dev, op_type, obj_request);if (!osd_req) goto out_unwind; obj_request->osd_req = osd_req; obj_request->callback = rbd_img_obj_callback; -if (write_request) { +if (op_type ==OBJ_OP_WRITE ) {osd_req_op_alloc_hint_init (osd_req, which,rbd_obj_bytes (&rbd_dev->header),rbd_obj_bytes (&rbd_dev->header));
@@ -2255,7 +2277,7 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request, obj_request->pages, length, offset & ~PAGE_MASK, false, false); -if (write_request) +if (op_type ==OBJ_OP_WRITE )rbd_osd_req_format_write (obj_request);else rbd_osd_req_format_read (obj_request);
@@ -2625,7 +2647,7 @@ static int rbd_img_obj_exists_submit(struct rbd_obj_request *obj_request)rbd_assert (obj_request->img_request); rbd_dev = obj_request->img_request->rbd_dev; - stat_request->osd_req =rbd_osd_req_create (rbd_dev, false,1 , + stat_request->osd_req =rbd_osd_req_create (rbd_dev,OBJ_OP_READ ,1 , stat_request);if (!stat_request->osd_req) goto out;
@@ -2848,7 +2870,7 @@ static int rbd_obj_notify_ack_sync(struct rbd_device *rbd_dev, u64 notify_id)return -ENOMEM ; ret = -ENOMEM ; - obj_request->osd_req =rbd_osd_req_create (rbd_dev, false,1 , + obj_request->osd_req =rbd_osd_req_create (rbd_dev,OBJ_OP_READ ,1 , obj_request);if (!obj_request->osd_req) goto out;
@@ -2912,7 +2934,7 @@ static int __rbd_dev_header_watch_sync(struct rbd_device *rbd_dev, bool start)if (!obj_request) goto out_cancel; - obj_request->osd_req =rbd_osd_req_create (rbd_dev, true,1 , + obj_request->osd_req =rbd_osd_req_create (rbd_dev,OBJ_OP_WRITE ,1 , obj_request);if (!obj_request->osd_req) goto out_cancel;
@@ -3021,7 +3043,7 @@ static int rbd_obj_method_sync(struct rbd_device *rbd_dev, obj_request->pages = pages; obj_request->page_count = page_count; - obj_request->osd_req =rbd_osd_req_create (rbd_dev, false,1 , + obj_request->osd_req =rbd_osd_req_create (rbd_dev,OBJ_OP_READ ,1 , obj_request);if (!obj_request->osd_req) goto out;
@@ -3077,7 +3099,7 @@ static void rbd_request_fn(struct request_queue *q)int result;while ((rq =blk_fetch_request (q))) { -bool write_request =rq_data_dir (rq) ==WRITE ; +enum obj_operation_type op_type;struct rbd_img_request *img_request;struct ceph_snap_context *snapc =NULL ;u64 offset;
@@ -3106,9 +3128,14 @@ static void rbd_request_fn(struct request_queue *q)spin_unlock_irq (q->queue_lock); -/* Disallow writes to a read-only device */ +if (rq->cmd_flags &REQ_WRITE ) + op_type =OBJ_OP_WRITE ; +else + op_type =OBJ_OP_READ ; + +/* Only allow reads to a read-only device */ -if (write_request) { +if (op_type !=OBJ_OP_READ ) { result = -EROFS ;if (read_only) goto end_request;
@@ -3153,7 +3180,7 @@ static void rbd_request_fn(struct request_queue *q) result = -ENOMEM ; img_request =rbd_img_request_create (rbd_dev, offset, length, - write_request, snapc); + op_type, snapc);if (!img_request) goto end_request;
@@ -3169,8 +3196,7 @@ end_request:spin_lock_irq (q->queue_lock);if (result <0 ) {rbd_warn (rbd_dev,"%s %llx at %llx result %d\n" , - write_request ?"write" :"read" , - length, offset, result); +obj_op_name (op_type), length, offset, result);if (snapc)ceph_put_snap_context (snapc);
@@ -3267,7 +3293,7 @@ static int rbd_obj_read_sync(struct rbd_device *rbd_dev, obj_request->pages = pages; obj_request->page_count = page_count; - obj_request->osd_req =rbd_osd_req_create (rbd_dev, false,1 , + obj_request->osd_req =rbd_osd_req_create (rbd_dev,OBJ_OP_READ ,1 , obj_request);if (!obj_request->osd_req) goto out;
Signature · 1 line
1.7.10.4
PATCH 4/11 rbd: add discard support for rbd
In reply to Josh DurginFrom: Guangliang Zhao <[email protected]>
This patch add the discard support for rbd driver.
There are three types operation in the driver:
1. The objects would be removed if they completely contained within the discard range.
2. The objects would be truncated if they partly contained within the discard range, and align with their boundary.
3. Others would be zeroed.
A discard request from blkdev_issue_discard() is defined which REQ_WRITE and REQ_DISCARD both marked and no data, so we must check the REQ_DISCARD first when getting the request type.
This resolve:
http://tracker.ceph.com/issues/190
--- drivers/block/rbd.c | 109 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 92 insertions(+), 17 deletions(-)
drivers/block/rbd.c +92 -17
@@ -212,6 +212,7 @@ enum obj_request_type {enum obj_operation_type {OBJ_OP_WRITE ,OBJ_OP_READ , +OBJ_OP_DISCARD , };enum obj_req_flags {
@@ -280,6 +281,7 @@ enum img_req_flags {IMG_REQ_WRITE ,/* I/O direction: read = 0, write = 1 */ IMG_REQ_CHILD ,/* initiator: block = 0, child image = 1 */ IMG_REQ_LAYERED ,/* ENOENT handling: normal = 0, layered = 1 */ + IMG_REQ_DISCARD, /* discard: normal = 0, discard request = 1 */ }; struct rbd_img_request {
@@ -727,6 +729,8 @@ static char* obj_op_name(enum obj_operation_type op_type)return "read" ;case OBJ_OP_WRITE :return "write" ; +case OBJ_OP_DISCARD : +return "discard" ;default :return "invalid op code" ; }
@@ -1521,6 +1525,21 @@ static bool img_request_write_test(struct rbd_img_request *img_request)return test_bit (IMG_REQ_WRITE , &img_request->flags) !=0 ; } +/* +* Set the discard flag when the img_request is an discard request +*/ +static void img_request_discard_set (struct rbd_img_request *img_request) +{ +set_bit (IMG_REQ_DISCARD , &img_request->flags); +smp_mb (); +} + +static bool img_request_discard_test (struct rbd_img_request *img_request) +{ +smp_mb (); +return test_bit (IMG_REQ_DISCARD , &img_request->flags) !=0 ; +} +static void img_request_child_set (struct rbd_img_request *img_request) {set_bit (IMG_REQ_CHILD , &img_request->flags);
@@ -1643,6 +1662,18 @@ static void rbd_osd_write_callback(struct rbd_obj_request *obj_request)obj_request_done_set (obj_request); } +static void rbd_osd_discard_callback (struct rbd_obj_request *obj_request) +{ +dout ("%s: obj %p result %d %llu\n" , __func__, obj_request, + obj_request->result, obj_request->length); +/* +* There is no such thing as a successful short discard. Set +* it to our originally-requested length. +*/ + obj_request->xferred = obj_request->length; +obj_request_done_set (obj_request); +} +/* * For a simple stat call there's nothing to do. We'll do more if * this is part of a write sequence for a layered image.
@@ -1694,6 +1725,11 @@ static void rbd_osd_req_callback(struct ceph_osd_request *osd_req,case CEPH_OSD_OP_STAT :rbd_osd_stat_callback (obj_request);break ; +case CEPH_OSD_OP_DELETE : +case CEPH_OSD_OP_TRUNCATE : +case CEPH_OSD_OP_ZERO : +rbd_osd_discard_callback (obj_request); +break ;case CEPH_OSD_OP_CALL :case CEPH_OSD_OP_NOTIFY_ACK :case CEPH_OSD_OP_WATCH :
@@ -1752,10 +1788,14 @@ static struct ceph_osd_request *rbd_osd_req_create(struct ceph_osd_client *osdc;struct ceph_osd_request *osd_req; -if (obj_request_img_data_test (obj_request) && op_type ==OBJ_OP_WRITE ) { +if (obj_request_img_data_test (obj_request) && + (op_type ==OBJ_OP_DISCARD || op_type ==OBJ_OP_WRITE )) {struct rbd_img_request *img_request = obj_request->img_request; - -rbd_assert (img_request_write_test (img_request)); +if (op_type ==OBJ_OP_WRITE ) { +rbd_assert (img_request_write_test (img_request)); + }else { +rbd_assert (img_request_discard_test (img_request)); + } snapc = img_request->snapc; }
@@ -1769,7 +1809,7 @@ static struct ceph_osd_request *rbd_osd_req_create(if (!osd_req)return NULL ;/* ENOMEM */ -if (op_type ==OBJ_OP_WRITE ) +if (op_type ==OBJ_OP_WRITE || op_type ==OBJ_OP_DISCARD ) osd_req->r_flags =CEPH_OSD_FLAG_WRITE |CEPH_OSD_FLAG_ONDISK ;else osd_req->r_flags =CEPH_OSD_FLAG_READ ;
@@ -1990,7 +2030,10 @@ static struct rbd_img_request *rbd_img_request_create( img_request->offset = offset; img_request->length = length; img_request->flags =0 ; -if (op_type ==OBJ_OP_WRITE ) { +if (op_type ==OBJ_OP_DISCARD ) { +img_request_discard_set (img_request); + img_request->snapc = snapc; + }else if (op_type ==OBJ_OP_WRITE ) {img_request_write_set (img_request); img_request->snapc = snapc; }else {
@@ -2091,8 +2134,12 @@ static bool rbd_img_obj_end_request(struct rbd_obj_request *obj_request)struct rbd_device *rbd_dev = img_request->rbd_dev;enum obj_operation_type op_type; - op_type =img_request_write_test (img_request) ?OBJ_OP_WRITE : -OBJ_OP_READ ; +if (img_request_discard_test (img_request)) + op_type =OBJ_OP_DISCARD ; +else if (img_request_write_test (img_request)) + op_type =OBJ_OP_WRITE ; +else + op_type =OBJ_OP_READ ; rbd_warn(rbd_dev, "%s %llx at %llx (%llx)\n", obj_op_name(op_type), obj_request->length,
@@ -2178,7 +2225,9 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,unsigned int bio_offset =0 ;struct page **pages =NULL ;enum obj_operation_type op_type; +u64 object_size =rbd_obj_bytes (&rbd_dev->header);u64 img_offset; +u64 img_end;u64 resid;u16 opcode;
@@ -2186,6 +2235,7 @@ 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 );
@@ -2193,8 +2243,7 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request, bio_list = data_desc;rbd_assert (img_offset == bio_list->bi_iter.bi_sector <<SECTOR_SHIFT ); - }else { -rbd_assert (type ==OBJ_REQUEST_PAGES ); + }else if (type ==OBJ_REQUEST_PAGES ) { pages = data_desc; }
@@ -2235,7 +2284,7 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request, GFP_ATOMIC);if (!obj_request->bio_list) goto out_unwind; - }else { + }else if (type ==OBJ_REQUEST_PAGES ) {unsigned int page_count; obj_request->pages = pages;
@@ -2246,7 +2295,19 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request, pages += page_count; } -if (img_request_write_test (img_request)) { +if (img_request_discard_test (img_request)) { + op_type =OBJ_OP_DISCARD ; +if (!offset && (length == object_size) + && (!img_request_layered_test (img_request) || + (rbd_dev->parent_overlap <= + obj_request->img_offset))) + opcode =CEPH_OSD_OP_DELETE ; +else if ((offset + length == object_size) || + (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 ; }else {
@@ -2254,7 +2315,9 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request, opcode =CEPH_OSD_OP_READ ; } - osd_req =rbd_osd_req_create (rbd_dev, op_type, obj_request); + osd_req =rbd_osd_req_create (rbd_dev, op_type, + (op_type ==OBJ_OP_WRITE ) ?2 :1 , + obj_request);if (!osd_req) goto out_unwind; obj_request->osd_req = osd_req;
@@ -2272,12 +2335,13 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,if (type ==OBJ_REQUEST_BIO )osd_req_op_extent_osd_data_bio (osd_req, which, obj_request->bio_list, length); -else +else if (type ==OBJ_REQUEST_PAGES )osd_req_op_extent_osd_data_pages (osd_req, which, obj_request->pages, length, offset & ~PAGE_MASK , false, false); -if (op_type ==OBJ_OP_WRITE ) +/* Discards are also writes */ +if (op_type ==OBJ_OP_WRITE || op_type ==OBJ_OP_DISCARD )rbd_osd_req_format_write (obj_request);else rbd_osd_req_format_read (obj_request);
@@ -3128,7 +3192,9 @@ static void rbd_request_fn(struct request_queue *q)spin_unlock_irq (q->queue_lock); -if (rq->cmd_flags &REQ_WRITE ) +if (rq->cmd_flags &REQ_DISCARD ) + op_type =OBJ_OP_DISCARD ; +else if (rq->cmd_flags &REQ_WRITE ) op_type =OBJ_OP_WRITE ;else op_type =OBJ_OP_READ ;
@@ -3186,8 +3252,12 @@ static void rbd_request_fn(struct request_queue *q) img_request->rq = rq; - result =rbd_img_request_fill (img_request,OBJ_REQUEST_BIO , - rq->bio); +if (op_type ==OBJ_OP_DISCARD ) + result =rbd_img_request_fill (img_request, +OBJ_REQUEST_NODATA ,NULL ); +else + result =rbd_img_request_fill (img_request, +OBJ_REQUEST_BIO , rq->bio);if (!result) result =rbd_img_request_submit (img_request);if (result)
@@ -3497,6 +3567,11 @@ static int rbd_init_disk(struct rbd_device *rbd_dev)blk_queue_io_min (q, segment_size);blk_queue_io_opt (q, segment_size); +/* enable the discard support */ +queue_flag_set_unlocked (QUEUE_FLAG_DISCARD , q); + q->limits.discard_granularity = segment_size; + q->limits.discard_alignment = segment_size; +blk_queue_merge_bvec (q, rbd_merge_bvec); disk->queue = q;
Signature · 1 line
1.7.10.4
PATCH 5/11 rbd: read image size for discard check safely
In reply to Josh DurginIn 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.
--- drivers/block/rbd.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-)
drivers/block/rbd.c +12 -6
@@ -2235,7 +2235,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 );
@@ -2300,13 +2299,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 ;
Signature · 1 line
1.7.10.4
PATCH 6/11 rbd: fix snapshot context reference count for discards
In reply to Josh DurginDiscards take a reference to the snapshot context of an image when they are created. This reference needs to be cleaned up when the request is done just as it is for regular writes.
--- drivers/block/rbd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/block/rbd.c +2 -1
@@ -2074,7 +2074,8 @@ static void rbd_img_request_destroy(struct kref *kref)rbd_dev_parent_put (img_request->rbd_dev); } -if (img_request_write_test (img_request)) +if (img_request_write_test (img_request) || +img_request_discard_test (img_request))ceph_put_snap_context (img_request->snapc);kmem_cache_free (rbd_img_request_cache, img_request);
Signature · 1 line
1.7.10.4
PATCH 7/11 rbd: tolerate -ENOENT for discard operations
In reply to Josh DurginDiscard may try to delete an object from a non-layered image that does not exist. If this occurs, the image already has no data in that range, so change the result to success.
--- drivers/block/rbd.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/block/rbd.c +3 -0
@@ -1671,6 +1671,9 @@ static void rbd_osd_discard_callback(struct rbd_obj_request *obj_request)* it to our originally-requested length. */ obj_request->xferred = obj_request->length; +/* discarding a non-existent object is not a problem */ +if (obj_request->result == -ENOENT ) + obj_request->result =0 ;obj_request_done_set (obj_request); }
Signature · 1 line
1.7.10.4
PATCH 8/11 rbd: make discard trigger copy-on-write
In reply to Josh DurginDiscard requests are a form of write, so they should go through the same process as plain write requests and trigger copy-on-write for layered images.
--- drivers/block/rbd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/block/rbd.c +2 -1
@@ -2755,7 +2755,8 @@ static bool rbd_img_obj_request_simple(struct rbd_obj_request *obj_request) obj_size = (u64 )1 << rbd_dev->header.obj_order;/* Read requests didn't need special handling */ -if (!img_request_write_test (img_request)) +if (!img_request_write_test (img_request) && + !img_request_discard_test (img_request))return true;/* No-layered writes are simple requests*/ if (!img_request_layered_test (img_request))
Signature · 1 line
1.7.10.4
PATCH 9/11 rbd: extract a method for adding object operations
In reply to Josh Durginrbd_img_request_fill() creates a ceph_osd_request and has logic for adding the appropriate osd ops to it based on the request type and image properties.
For layered images, the original rbd_obj_request is resent with a copyup operation in front, using a new ceph_osd_request. The logic for adding the original operations should be the same as when first sending them, so move it to a helper function.
op_type only needs to be checked once, so create a helper for that as well and call it outside the loop in rbd_img_request_fill().
--- drivers/block/rbd.c | 132 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 77 insertions(+), 55 deletions(-)
drivers/block/rbd.c +77 -55
@@ -1576,6 +1576,17 @@ static bool img_request_layered_test(struct rbd_img_request *img_request)return test_bit (IMG_REQ_LAYERED , &img_request->flags) !=0 ; } +static enum obj_operation_type +rbd_img_request_op_type (struct rbd_img_request *img_request) +{ +if (img_request_write_test (img_request)) +return OBJ_OP_WRITE ; +else if (img_request_discard_test (img_request)) +return OBJ_OP_DISCARD ; +else +return OBJ_OP_READ ; +} +static void rbd_img_obj_request_read_callback (struct rbd_obj_request *obj_request) {
@@ -2211,6 +2222,68 @@ out: }/* +* Add individual osd ops to the given ceph_osd_request and prepare +* them for submission. num_ops is the current number of +* osd operations already to the object request. +*/ +static void rbd_img_obj_request_fill (struct rbd_obj_request *obj_request, +struct ceph_osd_request *osd_request, +enum obj_operation_type op_type, +unsigned int num_ops) +{ +struct rbd_img_request *img_request = obj_request->img_request; +struct rbd_device *rbd_dev = img_request->rbd_dev; +u64 object_size =rbd_obj_bytes (&rbd_dev->header); +u64 offset = obj_request->offset; +u64 length = obj_request->length; +u64 img_end; +u16 opcode; + +if (op_type ==OBJ_OP_DISCARD ) { +if (!offset && (length == object_size) + && (!img_request_layered_test (img_request) || + (rbd_dev->parent_overlap <= + obj_request->img_offset))) { + opcode =CEPH_OSD_OP_DELETE ; + }else if ((offset + length == object_size)) { + opcode =CEPH_OSD_OP_TRUNCATE ; + }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 (op_type ==OBJ_OP_WRITE ) { + opcode =CEPH_OSD_OP_WRITE ; +osd_req_op_alloc_hint_init (osd_request, num_ops, + object_size, object_size); + num_ops++; + }else { + opcode =CEPH_OSD_OP_READ ; + } + +osd_req_op_extent_init (osd_request, num_ops, opcode, offset, length, +0 ,0 ); +if (obj_request->type ==OBJ_REQUEST_BIO ) +osd_req_op_extent_osd_data_bio (osd_request, num_ops, + obj_request->bio_list, length); +else if (obj_request->type ==OBJ_REQUEST_PAGES ) +osd_req_op_extent_osd_data_pages (osd_request, num_ops, + obj_request->pages, length, + offset & ~PAGE_MASK , false, false); + +/* Discards are also writes */ +if (op_type ==OBJ_OP_WRITE || op_type ==OBJ_OP_DISCARD ) +rbd_osd_req_format_write (obj_request); +else +rbd_osd_req_format_read (obj_request); +} + +/* * Split up an image request into one or more object requests, each * to a different object. The "type" parameter indicates whether * "data_desc" is the pointer to the head of a list of bio
@@ -2229,11 +2302,8 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,unsigned int bio_offset =0 ;struct page **pages =NULL ;enum obj_operation_type op_type; -u64 object_size =rbd_obj_bytes (&rbd_dev->header);u64 img_offset; -u64 img_end;u64 resid; -u16 opcode;dout ("%s: img %p type %d data_desc %p\n" , __func__, img_request, (int )type, data_desc);
@@ -2241,6 +2311,7 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request, img_offset = img_request->offset; resid = img_request->length;rbd_assert (resid >0 ); + op_type =rbd_img_request_op_type (img_request);if (type ==OBJ_REQUEST_BIO ) { bio_list = data_desc;
@@ -2255,7 +2326,6 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,const char *object_name;u64 offset;u64 length; -unsigned int which =0 ; object_name =rbd_segment_name (rbd_dev, img_offset);if (!object_name)
@@ -2298,66 +2368,18 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request, pages += page_count; } -if (img_request_discard_test (img_request)) { - op_type =OBJ_OP_DISCARD ; -if (!offset && (length == object_size) - && (!img_request_layered_test (img_request) || - (rbd_dev->parent_overlap <= - obj_request->img_offset))) { - opcode =CEPH_OSD_OP_DELETE ; - }else if ((offset + length == object_size)) { - opcode =CEPH_OSD_OP_TRUNCATE ; - }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 ; - }else { - op_type =OBJ_OP_READ ; - opcode =CEPH_OSD_OP_READ ; - } - osd_req =rbd_osd_req_create (rbd_dev, op_type, (op_type ==OBJ_OP_WRITE ) ?2 :1 , obj_request);if (!osd_req) goto out_unwind; + obj_request->osd_req = osd_req; obj_request->callback = rbd_img_obj_callback; - -if (op_type ==OBJ_OP_WRITE ) { -osd_req_op_alloc_hint_init (osd_req, which, -rbd_obj_bytes (&rbd_dev->header), -rbd_obj_bytes (&rbd_dev->header)); - which++; - } - -osd_req_op_extent_init (osd_req, which, opcode, offset, length, -0 ,0 ); -if (type ==OBJ_REQUEST_BIO ) -osd_req_op_extent_osd_data_bio (osd_req, which, - obj_request->bio_list, length); -else if (type ==OBJ_REQUEST_PAGES ) -osd_req_op_extent_osd_data_pages (osd_req, which, - obj_request->pages, length, - offset & ~PAGE_MASK , false, false); - -/* Discards are also writes */ -if (op_type ==OBJ_OP_WRITE || op_type ==OBJ_OP_DISCARD ) -rbd_osd_req_format_write (obj_request); -else -rbd_osd_req_format_read (obj_request); - obj_request->img_offset = img_offset; +rbd_img_obj_request_fill (obj_request, osd_req, op_type,0 ); + img_offset += length; resid -= length; }
Signature · 1 line
1.7.10.4
PATCH 10/11 rbd: use helpers to handle discard for layered images correctly
In reply to Josh DurginOnly allocate two osd ops for discard requests, since the preallocation hint is only added for regular writes. Use rbd_img_obj_request_fill() to recreate the original write or discard osd operations, isolating that logic to one place, and change the assert in rbd_osd_req_create_copyup() to accept discard requests as well.
--- drivers/block/rbd.c | 47 +++++++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 28 deletions(-)
drivers/block/rbd.c +19 -28
@@ -1838,9 +1838,10 @@ static struct ceph_osd_request *rbd_osd_req_create( }/* -* Create a copyup osd request based on the information in the -* object request supplied. A copyup request has three osd ops, -* a copyup method call, a hint op, and a write op. +* Create a copyup osd request based on the information in the object +* request supplied. A copyup request has two or three osd ops, a +* copyup method call, potentially a hint op, and a write or truncate +* or zero op. */ static struct ceph_osd_request *rbd_osd_req_create_copyup (struct rbd_obj_request *obj_request)
@@ -1850,18 +1851,24 @@ rbd_osd_req_create_copyup(struct rbd_obj_request *obj_request)struct rbd_device *rbd_dev;struct ceph_osd_client *osdc;struct ceph_osd_request *osd_req; +int num_osd_ops =3 ;rbd_assert (obj_request_img_data_test (obj_request)); img_request = obj_request->img_request;rbd_assert (img_request); -rbd_assert (img_request_write_test (img_request)); +rbd_assert (img_request_write_test (img_request) || +img_request_discard_test (img_request)); -/* Allocate and initialize the request, for the three ops */ +if (img_request_discard_test (img_request)) + num_osd_ops =2 ; + +/* Allocate and initialize the request, for all the ops */ snapc = img_request->snapc; rbd_dev = img_request->rbd_dev; osdc = &rbd_dev->rbd_client->client->osdc; - osd_req =ceph_osdc_alloc_request (osdc, snapc,3 , false,GFP_ATOMIC ); + osd_req =ceph_osdc_alloc_request (osdc, snapc, num_osd_ops, + false,GFP_ATOMIC );if (!osd_req)return NULL ;/* ENOMEM */
@@ -2401,7 +2408,8 @@ rbd_img_obj_copyup_callback(struct rbd_obj_request *obj_request)struct page **pages;u32 page_count; -rbd_assert (obj_request->type ==OBJ_REQUEST_BIO ); +rbd_assert (obj_request->type ==OBJ_REQUEST_BIO || + obj_request->type ==OBJ_REQUEST_NODATA );rbd_assert (obj_request_img_data_test (obj_request)); img_request = obj_request->img_request;rbd_assert (img_request);
@@ -2439,11 +2447,10 @@ rbd_img_obj_parent_read_full_callback(struct rbd_img_request *img_request)struct ceph_osd_client *osdc;struct rbd_device *rbd_dev;struct page **pages; +enum obj_operation_type op_type;u32 page_count;int img_result;u64 parent_length; -u64 offset; -u64 length;rbd_assert (img_request_child_test (img_request));
@@ -2507,26 +2514,10 @@ rbd_img_obj_parent_read_full_callback(struct rbd_img_request *img_request)osd_req_op_cls_request_data_pages (osd_req,0 , pages, parent_length,0 , false, false); -/* Then the hint op */ - -osd_req_op_alloc_hint_init (osd_req,1 ,rbd_obj_bytes (&rbd_dev->header), -rbd_obj_bytes (&rbd_dev->header)); - -/* And the original write request op */ - - offset = orig_request->offset; - length = orig_request->length; -osd_req_op_extent_init (osd_req,2 ,CEPH_OSD_OP_WRITE , - offset, length,0 ,0 ); -if (orig_request->type ==OBJ_REQUEST_BIO ) -osd_req_op_extent_osd_data_bio (osd_req,2 , - orig_request->bio_list, length); -else -osd_req_op_extent_osd_data_pages (osd_req,2 , - orig_request->pages, length, - offset & ~PAGE_MASK , false, false); +/* Add the other op(s) */ -rbd_osd_req_format_write (orig_request); + op_type =rbd_img_request_op_type (orig_request->img_request); +rbd_img_obj_request_fill (orig_request, osd_req, op_type,1 );/* All set, send it off. */
Signature · 1 line
1.7.10.4
PATCH 11/11 rbd: set the remaining discard properties to enable support
In reply to Josh Durginmax_discard_sectors must be set for the queue to support discard. Operations implementing discard for rbd zero data, so report that.
--- drivers/block/rbd.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/block/rbd.c +2 -0
@@ -3595,6 +3595,8 @@ static int rbd_init_disk(struct rbd_device *rbd_dev)queue_flag_set_unlocked (QUEUE_FLAG_DISCARD , q); q->limits.discard_granularity = segment_size; q->limits.discard_alignment = segment_size; + q->limits.max_discard_sectors = segment_size /SECTOR_SIZE ; + q->limits.discard_zeroes_data =1 ;blk_queue_merge_bvec (q, rbd_merge_bvec); disk->queue = q;
Signature · 1 line
1.7.10.4