staging: comedi: don't allow write() on async command set up for "read"
[firefly-linux-kernel-4.4.55.git] / drivers / block / rbd.c
index de1520ccc0d46afdf5bbfefffa33e83913ce694c..0a54c588e433751f39084459193a5da7700d34db 100644 (file)
@@ -1672,6 +1672,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)
 {
@@ -1767,6 +1778,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);
 }
 
@@ -1920,9 +1934,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)
@@ -1932,18 +1947,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 */
 
@@ -2304,6 +2325,67 @@ out:
                rbd_img_request_complete(img_request);
 }
 
+/*
+ * 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) ||
+                    !obj_request_overlaps_parent(obj_request))) {
+                       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
@@ -2323,11 +2405,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);
@@ -2335,6 +2414,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;
@@ -2349,7 +2429,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)
@@ -2392,66 +2471,19 @@ 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;
-               rbd_img_request_get(img_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));
-                       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);
+               obj_request->img_offset = img_offset;
 
-               /* 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);
+               rbd_img_obj_request_fill(obj_request, osd_req, op_type, 0);
 
-               obj_request->img_offset = img_offset;
+               rbd_img_request_get(img_request);
 
                img_offset += length;
                resid -= length;
@@ -2474,7 +2506,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);
@@ -2512,11 +2545,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));
 
@@ -2580,26 +2612,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. */
 
@@ -2848,7 +2864,8 @@ static bool img_obj_request_simple(struct rbd_obj_request *obj_request)
        rbd_dev = img_request->rbd_dev;
 
        /* Reads */
-       if (!img_request_write_test(img_request))
+       if (!img_request_write_test(img_request) &&
+           !img_request_discard_test(img_request))
                return true;
 
        /* Non-layered writes */
@@ -3747,6 +3764,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;
@@ -5223,7 +5242,8 @@ static int rbd_dev_device_setup(struct rbd_device *rbd_dev)
        set_capacity(rbd_dev->disk, rbd_dev->mapping.size / SECTOR_SIZE);
        set_disk_ro(rbd_dev->disk, rbd_dev->mapping.read_only);
 
-       rbd_dev->rq_wq = alloc_workqueue("%s", 0, 0, rbd_dev->disk->disk_name);
+       rbd_dev->rq_wq = alloc_workqueue("%s", WQ_MEM_RECLAIM, 0,
+                                        rbd_dev->disk->disk_name);
        if (!rbd_dev->rq_wq) {
                ret = -ENOMEM;
                goto err_out_mapping;