Commit 0864b79a authored by Rusty Russell's avatar Rusty Russell

virtio: block: dynamic maximum segments

Enhance the driver to handle whatever maximum segment number the host
tells us to handle.  Do to this, we need to allocate the scatterlist
dynamically.

We set max_phys_segments and max_hw_segments to the same value (1 if
the host doesn't tell us, since that's safest and all known hosts do
tell us).

Note that kmalloc'ing the structure for large sg_elems might be
problematic: the fix for this is sg_table, but that requires more
work.
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent 4b7f7e20
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
#include <linux/virtio_blk.h> #include <linux/virtio_blk.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#define VIRTIO_MAX_SG (3+MAX_PHYS_SEGMENTS)
#define PART_BITS 4 #define PART_BITS 4
static int major, index; static int major, index;
...@@ -26,8 +25,11 @@ struct virtio_blk ...@@ -26,8 +25,11 @@ struct virtio_blk
mempool_t *pool; mempool_t *pool;
/* What host tells us, plus 2 for header & tailer. */
unsigned int sg_elems;
/* Scatterlist: can be too big for stack. */ /* Scatterlist: can be too big for stack. */
struct scatterlist sg[VIRTIO_MAX_SG]; struct scatterlist sg[/*sg_elems*/];
}; };
struct virtblk_req struct virtblk_req
...@@ -97,8 +99,6 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk, ...@@ -97,8 +99,6 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
if (blk_barrier_rq(vbr->req)) if (blk_barrier_rq(vbr->req))
vbr->out_hdr.type |= VIRTIO_BLK_T_BARRIER; vbr->out_hdr.type |= VIRTIO_BLK_T_BARRIER;
/* This init could be done at vblk creation time */
sg_init_table(vblk->sg, VIRTIO_MAX_SG);
sg_set_buf(&vblk->sg[0], &vbr->out_hdr, sizeof(vbr->out_hdr)); sg_set_buf(&vblk->sg[0], &vbr->out_hdr, sizeof(vbr->out_hdr));
num = blk_rq_map_sg(q, vbr->req, vblk->sg+1); num = blk_rq_map_sg(q, vbr->req, vblk->sg+1);
sg_set_buf(&vblk->sg[num+1], &vbr->status, sizeof(vbr->status)); sg_set_buf(&vblk->sg[num+1], &vbr->status, sizeof(vbr->status));
...@@ -130,7 +130,7 @@ static void do_virtblk_request(struct request_queue *q) ...@@ -130,7 +130,7 @@ static void do_virtblk_request(struct request_queue *q)
while ((req = elv_next_request(q)) != NULL) { while ((req = elv_next_request(q)) != NULL) {
vblk = req->rq_disk->private_data; vblk = req->rq_disk->private_data;
BUG_ON(req->nr_phys_segments > ARRAY_SIZE(vblk->sg)); BUG_ON(req->nr_phys_segments + 2 > vblk->sg_elems);
/* If this request fails, stop queue and wait for something to /* If this request fails, stop queue and wait for something to
finish to restart it. */ finish to restart it. */
...@@ -196,12 +196,22 @@ static int virtblk_probe(struct virtio_device *vdev) ...@@ -196,12 +196,22 @@ static int virtblk_probe(struct virtio_device *vdev)
int err; int err;
u64 cap; u64 cap;
u32 v; u32 v;
u32 blk_size; u32 blk_size, sg_elems;
if (index_to_minor(index) >= 1 << MINORBITS) if (index_to_minor(index) >= 1 << MINORBITS)
return -ENOSPC; return -ENOSPC;
vdev->priv = vblk = kmalloc(sizeof(*vblk), GFP_KERNEL); /* We need to know how many segments before we allocate. */
err = virtio_config_val(vdev, VIRTIO_BLK_F_SEG_MAX,
offsetof(struct virtio_blk_config, seg_max),
&sg_elems);
if (err)
sg_elems = 1;
/* We need an extra sg elements at head and tail. */
sg_elems += 2;
vdev->priv = vblk = kmalloc(sizeof(*vblk) +
sizeof(vblk->sg[0]) * sg_elems, GFP_KERNEL);
if (!vblk) { if (!vblk) {
err = -ENOMEM; err = -ENOMEM;
goto out; goto out;
...@@ -210,6 +220,8 @@ static int virtblk_probe(struct virtio_device *vdev) ...@@ -210,6 +220,8 @@ static int virtblk_probe(struct virtio_device *vdev)
INIT_LIST_HEAD(&vblk->reqs); INIT_LIST_HEAD(&vblk->reqs);
spin_lock_init(&vblk->lock); spin_lock_init(&vblk->lock);
vblk->vdev = vdev; vblk->vdev = vdev;
vblk->sg_elems = sg_elems;
sg_init_table(vblk->sg, vblk->sg_elems);
/* We expect one virtqueue, for output. */ /* We expect one virtqueue, for output. */
vblk->vq = vdev->config->find_vq(vdev, 0, blk_done); vblk->vq = vdev->config->find_vq(vdev, 0, blk_done);
...@@ -277,6 +289,10 @@ static int virtblk_probe(struct virtio_device *vdev) ...@@ -277,6 +289,10 @@ static int virtblk_probe(struct virtio_device *vdev)
} }
set_capacity(vblk->disk, cap); set_capacity(vblk->disk, cap);
/* We can handle whatever the host told us to handle. */
blk_queue_max_phys_segments(vblk->disk->queue, vblk->sg_elems-2);
blk_queue_max_hw_segments(vblk->disk->queue, vblk->sg_elems-2);
/* No real sector limit. */ /* No real sector limit. */
blk_queue_max_sectors(vblk->disk->queue, -1U); blk_queue_max_sectors(vblk->disk->queue, -1U);
...@@ -290,12 +306,6 @@ static int virtblk_probe(struct virtio_device *vdev) ...@@ -290,12 +306,6 @@ static int virtblk_probe(struct virtio_device *vdev)
else else
blk_queue_max_segment_size(vblk->disk->queue, -1UL); blk_queue_max_segment_size(vblk->disk->queue, -1UL);
err = virtio_config_val(vdev, VIRTIO_BLK_F_SEG_MAX,
offsetof(struct virtio_blk_config, seg_max),
&v);
if (!err)
blk_queue_max_hw_segments(vblk->disk->queue, v);
/* Host can optionally specify the block size of the device */ /* Host can optionally specify the block size of the device */
err = virtio_config_val(vdev, VIRTIO_BLK_F_BLK_SIZE, err = virtio_config_val(vdev, VIRTIO_BLK_F_BLK_SIZE,
offsetof(struct virtio_blk_config, blk_size), offsetof(struct virtio_blk_config, blk_size),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment