]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/block/virtio_blk.c
blk-mq: update ->init_request and ->exit_request prototypes
[karo-tx-linux.git] / drivers / block / virtio_blk.c
index 264c5eac12b028eab2d30d2e35bd05e1fa968745..94173de1efaab18b07924ed0c8431dbb6ba5ca34 100644 (file)
@@ -5,6 +5,7 @@
 #include <linux/hdreg.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
+#include <linux/interrupt.h>
 #include <linux/virtio.h>
 #include <linux/virtio_blk.h>
 #include <linux/scatterlist.h>
@@ -12,6 +13,7 @@
 #include <scsi/scsi_cmnd.h>
 #include <linux/idr.h>
 #include <linux/blk-mq.h>
+#include <linux/blk-mq-virtio.h>
 #include <linux/numa.h>
 
 #define PART_BITS 4
@@ -52,11 +54,13 @@ struct virtio_blk {
 };
 
 struct virtblk_req {
-       struct request *req;
-       struct virtio_blk_outhdr out_hdr;
+#ifdef CONFIG_VIRTIO_BLK_SCSI
+       struct scsi_request sreq;       /* for SCSI passthrough, must be first */
+       u8 sense[SCSI_SENSE_BUFFERSIZE];
        struct virtio_scsi_inhdr in_hdr;
+#endif
+       struct virtio_blk_outhdr out_hdr;
        u8 status;
-       u8 sense[SCSI_SENSE_BUFFERSIZE];
        struct scatterlist sg[];
 };
 
@@ -72,28 +76,88 @@ static inline int virtblk_result(struct virtblk_req *vbr)
        }
 }
 
-static int __virtblk_add_req(struct virtqueue *vq,
-                            struct virtblk_req *vbr,
-                            struct scatterlist *data_sg,
-                            bool have_data)
+/*
+ * If this is a packet command we need a couple of additional headers.  Behind
+ * the normal outhdr we put a segment with the scsi command block, and before
+ * the normal inhdr we put the sense data and the inhdr with additional status
+ * information.
+ */
+#ifdef CONFIG_VIRTIO_BLK_SCSI
+static int virtblk_add_req_scsi(struct virtqueue *vq, struct virtblk_req *vbr,
+               struct scatterlist *data_sg, bool have_data)
 {
        struct scatterlist hdr, status, cmd, sense, inhdr, *sgs[6];
        unsigned int num_out = 0, num_in = 0;
-       __virtio32 type = vbr->out_hdr.type & ~cpu_to_virtio32(vq->vdev, VIRTIO_BLK_T_OUT);
 
        sg_init_one(&hdr, &vbr->out_hdr, sizeof(vbr->out_hdr));
        sgs[num_out++] = &hdr;
+       sg_init_one(&cmd, vbr->sreq.cmd, vbr->sreq.cmd_len);
+       sgs[num_out++] = &cmd;
+
+       if (have_data) {
+               if (vbr->out_hdr.type & cpu_to_virtio32(vq->vdev, VIRTIO_BLK_T_OUT))
+                       sgs[num_out++] = data_sg;
+               else
+                       sgs[num_out + num_in++] = data_sg;
+       }
+
+       sg_init_one(&sense, vbr->sense, SCSI_SENSE_BUFFERSIZE);
+       sgs[num_out + num_in++] = &sense;
+       sg_init_one(&inhdr, &vbr->in_hdr, sizeof(vbr->in_hdr));
+       sgs[num_out + num_in++] = &inhdr;
+       sg_init_one(&status, &vbr->status, sizeof(vbr->status));
+       sgs[num_out + num_in++] = &status;
+
+       return virtqueue_add_sgs(vq, sgs, num_out, num_in, vbr, GFP_ATOMIC);
+}
+
+static inline void virtblk_scsi_request_done(struct request *req)
+{
+       struct virtblk_req *vbr = blk_mq_rq_to_pdu(req);
+       struct virtio_blk *vblk = req->q->queuedata;
+       struct scsi_request *sreq = &vbr->sreq;
+
+       sreq->resid_len = virtio32_to_cpu(vblk->vdev, vbr->in_hdr.residual);
+       sreq->sense_len = virtio32_to_cpu(vblk->vdev, vbr->in_hdr.sense_len);
+       sreq->result = virtio32_to_cpu(vblk->vdev, vbr->in_hdr.errors);
+}
+
+static int virtblk_ioctl(struct block_device *bdev, fmode_t mode,
+                            unsigned int cmd, unsigned long data)
+{
+       struct gendisk *disk = bdev->bd_disk;
+       struct virtio_blk *vblk = disk->private_data;
 
        /*
-        * If this is a packet command we need a couple of additional headers.
-        * Behind the normal outhdr we put a segment with the scsi command
-        * block, and before the normal inhdr we put the sense data and the
-        * inhdr with additional status information.
+        * Only allow the generic SCSI ioctls if the host can support it.
         */
-       if (type == cpu_to_virtio32(vq->vdev, VIRTIO_BLK_T_SCSI_CMD)) {
-               sg_init_one(&cmd, vbr->req->cmd, vbr->req->cmd_len);
-               sgs[num_out++] = &cmd;
-       }
+       if (!virtio_has_feature(vblk->vdev, VIRTIO_BLK_F_SCSI))
+               return -ENOTTY;
+
+       return scsi_cmd_blk_ioctl(bdev, mode, cmd,
+                                 (void __user *)data);
+}
+#else
+static inline int virtblk_add_req_scsi(struct virtqueue *vq,
+               struct virtblk_req *vbr, struct scatterlist *data_sg,
+               bool have_data)
+{
+       return -EIO;
+}
+static inline void virtblk_scsi_request_done(struct request *req)
+{
+}
+#define virtblk_ioctl  NULL
+#endif /* CONFIG_VIRTIO_BLK_SCSI */
+
+static int virtblk_add_req(struct virtqueue *vq, struct virtblk_req *vbr,
+               struct scatterlist *data_sg, bool have_data)
+{
+       struct scatterlist hdr, status, *sgs[3];
+       unsigned int num_out = 0, num_in = 0;
+
+       sg_init_one(&hdr, &vbr->out_hdr, sizeof(vbr->out_hdr));
+       sgs[num_out++] = &hdr;
 
        if (have_data) {
                if (vbr->out_hdr.type & cpu_to_virtio32(vq->vdev, VIRTIO_BLK_T_OUT))
@@ -102,14 +166,6 @@ static int __virtblk_add_req(struct virtqueue *vq,
                        sgs[num_out + num_in++] = data_sg;
        }
 
-       if (type == cpu_to_virtio32(vq->vdev, VIRTIO_BLK_T_SCSI_CMD)) {
-               memcpy(vbr->sense, vbr->req->sense, SCSI_SENSE_BUFFERSIZE);
-               sg_init_one(&sense, vbr->sense, SCSI_SENSE_BUFFERSIZE);
-               sgs[num_out + num_in++] = &sense;
-               sg_init_one(&inhdr, &vbr->in_hdr, sizeof(vbr->in_hdr));
-               sgs[num_out + num_in++] = &inhdr;
-       }
-
        sg_init_one(&status, &vbr->status, sizeof(vbr->status));
        sgs[num_out + num_in++] = &status;
 
@@ -119,18 +175,15 @@ static int __virtblk_add_req(struct virtqueue *vq,
 static inline void virtblk_request_done(struct request *req)
 {
        struct virtblk_req *vbr = blk_mq_rq_to_pdu(req);
-       struct virtio_blk *vblk = req->q->queuedata;
-       int error = virtblk_result(vbr);
-
-       if (req->cmd_type == REQ_TYPE_BLOCK_PC) {
-               req->resid_len = virtio32_to_cpu(vblk->vdev, vbr->in_hdr.residual);
-               req->sense_len = virtio32_to_cpu(vblk->vdev, vbr->in_hdr.sense_len);
-               req->errors = virtio32_to_cpu(vblk->vdev, vbr->in_hdr.errors);
-       } else if (req->cmd_type == REQ_TYPE_DRV_PRIV) {
-               req->errors = (error != 0);
+
+       switch (req_op(req)) {
+       case REQ_OP_SCSI_IN:
+       case REQ_OP_SCSI_OUT:
+               virtblk_scsi_request_done(req);
+               break;
        }
 
-       blk_mq_end_request(req, error);
+       blk_mq_end_request(req, virtblk_result(vbr));
 }
 
 static void virtblk_done(struct virtqueue *vq)
@@ -146,7 +199,9 @@ static void virtblk_done(struct virtqueue *vq)
        do {
                virtqueue_disable_cb(vq);
                while ((vbr = virtqueue_get_buf(vblk->vqs[qid].vq, &len)) != NULL) {
-                       blk_mq_complete_request(vbr->req, vbr->req->errors);
+                       struct request *req = blk_mq_rq_from_pdu(vbr);
+
+                       blk_mq_complete_request(req);
                        req_done = true;
                }
                if (unlikely(virtqueue_is_broken(vq)))
@@ -170,49 +225,50 @@ static int virtio_queue_rq(struct blk_mq_hw_ctx *hctx,
        int qid = hctx->queue_num;
        int err;
        bool notify = false;
+       u32 type;
 
        BUG_ON(req->nr_phys_segments + 2 > vblk->sg_elems);
 
-       vbr->req = req;
-       if (req_op(req) == REQ_OP_FLUSH) {
-               vbr->out_hdr.type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_FLUSH);
-               vbr->out_hdr.sector = 0;
-               vbr->out_hdr.ioprio = cpu_to_virtio32(vblk->vdev, req_get_ioprio(vbr->req));
-       } else {
-               switch (req->cmd_type) {
-               case REQ_TYPE_FS:
-                       vbr->out_hdr.type = 0;
-                       vbr->out_hdr.sector = cpu_to_virtio64(vblk->vdev, blk_rq_pos(vbr->req));
-                       vbr->out_hdr.ioprio = cpu_to_virtio32(vblk->vdev, req_get_ioprio(vbr->req));
-                       break;
-               case REQ_TYPE_BLOCK_PC:
-                       vbr->out_hdr.type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_SCSI_CMD);
-                       vbr->out_hdr.sector = 0;
-                       vbr->out_hdr.ioprio = cpu_to_virtio32(vblk->vdev, req_get_ioprio(vbr->req));
-                       break;
-               case REQ_TYPE_DRV_PRIV:
-                       vbr->out_hdr.type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_GET_ID);
-                       vbr->out_hdr.sector = 0;
-                       vbr->out_hdr.ioprio = cpu_to_virtio32(vblk->vdev, req_get_ioprio(vbr->req));
-                       break;
-               default:
-                       /* We don't put anything else in the queue. */
-                       BUG();
-               }
+       switch (req_op(req)) {
+       case REQ_OP_READ:
+       case REQ_OP_WRITE:
+               type = 0;
+               break;
+       case REQ_OP_FLUSH:
+               type = VIRTIO_BLK_T_FLUSH;
+               break;
+       case REQ_OP_SCSI_IN:
+       case REQ_OP_SCSI_OUT:
+               type = VIRTIO_BLK_T_SCSI_CMD;
+               break;
+       case REQ_OP_DRV_IN:
+               type = VIRTIO_BLK_T_GET_ID;
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               return BLK_MQ_RQ_QUEUE_ERROR;
        }
 
+       vbr->out_hdr.type = cpu_to_virtio32(vblk->vdev, type);
+       vbr->out_hdr.sector = type ?
+               0 : cpu_to_virtio64(vblk->vdev, blk_rq_pos(req));
+       vbr->out_hdr.ioprio = cpu_to_virtio32(vblk->vdev, req_get_ioprio(req));
+
        blk_mq_start_request(req);
 
-       num = blk_rq_map_sg(hctx->queue, vbr->req, vbr->sg);
+       num = blk_rq_map_sg(hctx->queue, req, vbr->sg);
        if (num) {
-               if (rq_data_dir(vbr->req) == WRITE)
+               if (rq_data_dir(req) == WRITE)
                        vbr->out_hdr.type |= cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_OUT);
                else
                        vbr->out_hdr.type |= cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_IN);
        }
 
        spin_lock_irqsave(&vblk->vqs[qid].lock, flags);
-       err = __virtblk_add_req(vblk->vqs[qid].vq, vbr, vbr->sg, num);
+       if (req_op(req) == REQ_OP_SCSI_IN || req_op(req) == REQ_OP_SCSI_OUT)
+               err = virtblk_add_req_scsi(vblk->vqs[qid].vq, vbr, vbr->sg, num);
+       else
+               err = virtblk_add_req(vblk->vqs[qid].vq, vbr, vbr->sg, num);
        if (err) {
                virtqueue_kick(vblk->vqs[qid].vq);
                blk_mq_stop_hw_queue(hctx);
@@ -242,37 +298,21 @@ static int virtblk_get_id(struct gendisk *disk, char *id_str)
        struct request *req;
        int err;
 
-       req = blk_get_request(q, READ, GFP_KERNEL);
+       req = blk_get_request(q, REQ_OP_DRV_IN, GFP_KERNEL);
        if (IS_ERR(req))
                return PTR_ERR(req);
-       req->cmd_type = REQ_TYPE_DRV_PRIV;
 
        err = blk_rq_map_kern(q, req, id_str, VIRTIO_BLK_ID_BYTES, GFP_KERNEL);
        if (err)
                goto out;
 
-       err = blk_execute_rq(vblk->disk->queue, vblk->disk, req, false);
+       blk_execute_rq(vblk->disk->queue, vblk->disk, req, false);
+       err = virtblk_result(blk_mq_rq_to_pdu(req));
 out:
        blk_put_request(req);
        return err;
 }
 
-static int virtblk_ioctl(struct block_device *bdev, fmode_t mode,
-                            unsigned int cmd, unsigned long data)
-{
-       struct gendisk *disk = bdev->bd_disk;
-       struct virtio_blk *vblk = disk->private_data;
-
-       /*
-        * Only allow the generic SCSI ioctls if the host can support it.
-        */
-       if (!virtio_has_feature(vblk->vdev, VIRTIO_BLK_F_SCSI))
-               return -ENOTTY;
-
-       return scsi_cmd_blk_ioctl(bdev, mode, cmd,
-                                 (void __user *)data);
-}
-
 /* We provide getgeo only to please some old bootloader/partitioning tools */
 static int virtblk_getgeo(struct block_device *bd, struct hd_geometry *geo)
 {
@@ -385,6 +425,7 @@ static int init_vq(struct virtio_blk *vblk)
        struct virtqueue **vqs;
        unsigned short num_vqs;
        struct virtio_device *vdev = vblk->vdev;
+       struct irq_affinity desc = { 0, };
 
        err = virtio_cread_feature(vdev, VIRTIO_BLK_F_MQ,
                                   struct virtio_blk_config, num_queues,
@@ -411,7 +452,8 @@ static int init_vq(struct virtio_blk *vblk)
        }
 
        /* Discover virtqueues and write information to configuration.  */
-       err = vdev->config->find_vqs(vdev, num_vqs, vqs, callbacks, names);
+       err = vdev->config->find_vqs(vdev, num_vqs, vqs, callbacks, names,
+                       &desc);
        if (err)
                goto out;
 
@@ -531,21 +573,31 @@ static const struct device_attribute dev_attr_cache_type_rw =
        __ATTR(cache_type, S_IRUGO|S_IWUSR,
               virtblk_cache_type_show, virtblk_cache_type_store);
 
-static int virtblk_init_request(void *data, struct request *rq,
-               unsigned int hctx_idx, unsigned int request_idx,
-               unsigned int numa_node)
+static int virtblk_init_request(struct blk_mq_tag_set *set, struct request *rq,
+               unsigned int hctx_idx, unsigned int numa_node)
 {
-       struct virtio_blk *vblk = data;
+       struct virtio_blk *vblk = set->driver_data;
        struct virtblk_req *vbr = blk_mq_rq_to_pdu(rq);
 
+#ifdef CONFIG_VIRTIO_BLK_SCSI
+       vbr->sreq.sense = vbr->sense;
+#endif
        sg_init_table(vbr->sg, vblk->sg_elems);
        return 0;
 }
 
-static struct blk_mq_ops virtio_mq_ops = {
+static int virtblk_map_queues(struct blk_mq_tag_set *set)
+{
+       struct virtio_blk *vblk = set->driver_data;
+
+       return blk_mq_virtio_map_queues(set, vblk->vdev, 0);
+}
+
+static const struct blk_mq_ops virtio_mq_ops = {
        .queue_rq       = virtio_queue_rq,
        .complete       = virtblk_request_done,
        .init_request   = virtblk_init_request,
+       .map_queues     = virtblk_map_queues,
 };
 
 static unsigned int virtblk_queue_depth;
@@ -821,7 +873,10 @@ static const struct virtio_device_id id_table[] = {
 
 static unsigned int features_legacy[] = {
        VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, VIRTIO_BLK_F_GEOMETRY,
-       VIRTIO_BLK_F_RO, VIRTIO_BLK_F_BLK_SIZE, VIRTIO_BLK_F_SCSI,
+       VIRTIO_BLK_F_RO, VIRTIO_BLK_F_BLK_SIZE,
+#ifdef CONFIG_VIRTIO_BLK_SCSI
+       VIRTIO_BLK_F_SCSI,
+#endif
        VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_CONFIG_WCE,
        VIRTIO_BLK_F_MQ,
 }