mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 18:51:56 -04:00
Merge branch 'virtio-next' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux
Pull virtio changes from Rusty Russell: "New workflow: same git trees pulled by linux-next get sent straight to Linus. Git is awkward at shuffling patches compared with quilt or mq, but that doesn't happen often once things get into my -next branch." * 'virtio-next' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux: (24 commits) lguest: fix occasional crash in example launcher. virtio-blk: Disable callback in virtblk_done() virtio_mmio: Don't attempt to create empty virtqueues virtio_mmio: fix off by one error allocating queue drivers/virtio/virtio_pci.c: fix error return code virtio: don't crash when device is buggy virtio: remove CONFIG_VIRTIO_RING virtio: add help to CONFIG_VIRTIO option. virtio: support reserved vqs virtio: introduce an API to set affinity for a virtqueue virtio-ring: move queue_index to vring_virtqueue virtio_balloon: not EXPERIMENTAL any more. virtio-balloon: dependency fix virtio-blk: fix NULL checking in virtblk_alloc_req() virtio-blk: Add REQ_FLUSH and REQ_FUA support to bio path virtio-blk: Add bio-based IO path for virtio-blk virtio: console: fix error handling in init() function tools: Fix pthread flag for Makefile of trace-agent used by virtio-trace tools: Add guest trace agent as a user tool virtio/console: Allocate scatterlist according to the current pipe size ...
This commit is contained in:
commit
dc92b1f9ab
25 changed files with 1391 additions and 106 deletions
|
|
@ -14,6 +14,9 @@
|
|||
|
||||
#define PART_BITS 4
|
||||
|
||||
static bool use_bio;
|
||||
module_param(use_bio, bool, S_IRUGO);
|
||||
|
||||
static int major;
|
||||
static DEFINE_IDA(vd_index_ida);
|
||||
|
||||
|
|
@ -23,6 +26,7 @@ struct virtio_blk
|
|||
{
|
||||
struct virtio_device *vdev;
|
||||
struct virtqueue *vq;
|
||||
wait_queue_head_t queue_wait;
|
||||
|
||||
/* The disk structure for the kernel. */
|
||||
struct gendisk *disk;
|
||||
|
|
@ -51,53 +55,244 @@ struct virtio_blk
|
|||
struct virtblk_req
|
||||
{
|
||||
struct request *req;
|
||||
struct bio *bio;
|
||||
struct virtio_blk_outhdr out_hdr;
|
||||
struct virtio_scsi_inhdr in_hdr;
|
||||
struct work_struct work;
|
||||
struct virtio_blk *vblk;
|
||||
int flags;
|
||||
u8 status;
|
||||
struct scatterlist sg[];
|
||||
};
|
||||
|
||||
static void blk_done(struct virtqueue *vq)
|
||||
enum {
|
||||
VBLK_IS_FLUSH = 1,
|
||||
VBLK_REQ_FLUSH = 2,
|
||||
VBLK_REQ_DATA = 4,
|
||||
VBLK_REQ_FUA = 8,
|
||||
};
|
||||
|
||||
static inline int virtblk_result(struct virtblk_req *vbr)
|
||||
{
|
||||
switch (vbr->status) {
|
||||
case VIRTIO_BLK_S_OK:
|
||||
return 0;
|
||||
case VIRTIO_BLK_S_UNSUPP:
|
||||
return -ENOTTY;
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
static inline struct virtblk_req *virtblk_alloc_req(struct virtio_blk *vblk,
|
||||
gfp_t gfp_mask)
|
||||
{
|
||||
struct virtio_blk *vblk = vq->vdev->priv;
|
||||
struct virtblk_req *vbr;
|
||||
unsigned int len;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(vblk->disk->queue->queue_lock, flags);
|
||||
while ((vbr = virtqueue_get_buf(vblk->vq, &len)) != NULL) {
|
||||
int error;
|
||||
vbr = mempool_alloc(vblk->pool, gfp_mask);
|
||||
if (!vbr)
|
||||
return NULL;
|
||||
|
||||
switch (vbr->status) {
|
||||
case VIRTIO_BLK_S_OK:
|
||||
error = 0;
|
||||
break;
|
||||
case VIRTIO_BLK_S_UNSUPP:
|
||||
error = -ENOTTY;
|
||||
break;
|
||||
default:
|
||||
error = -EIO;
|
||||
vbr->vblk = vblk;
|
||||
if (use_bio)
|
||||
sg_init_table(vbr->sg, vblk->sg_elems);
|
||||
|
||||
return vbr;
|
||||
}
|
||||
|
||||
static void virtblk_add_buf_wait(struct virtio_blk *vblk,
|
||||
struct virtblk_req *vbr,
|
||||
unsigned long out,
|
||||
unsigned long in)
|
||||
{
|
||||
DEFINE_WAIT(wait);
|
||||
|
||||
for (;;) {
|
||||
prepare_to_wait_exclusive(&vblk->queue_wait, &wait,
|
||||
TASK_UNINTERRUPTIBLE);
|
||||
|
||||
spin_lock_irq(vblk->disk->queue->queue_lock);
|
||||
if (virtqueue_add_buf(vblk->vq, vbr->sg, out, in, vbr,
|
||||
GFP_ATOMIC) < 0) {
|
||||
spin_unlock_irq(vblk->disk->queue->queue_lock);
|
||||
io_schedule();
|
||||
} else {
|
||||
virtqueue_kick(vblk->vq);
|
||||
spin_unlock_irq(vblk->disk->queue->queue_lock);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (vbr->req->cmd_type) {
|
||||
case REQ_TYPE_BLOCK_PC:
|
||||
vbr->req->resid_len = vbr->in_hdr.residual;
|
||||
vbr->req->sense_len = vbr->in_hdr.sense_len;
|
||||
vbr->req->errors = vbr->in_hdr.errors;
|
||||
break;
|
||||
case REQ_TYPE_SPECIAL:
|
||||
vbr->req->errors = (error != 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
__blk_end_request_all(vbr->req, error);
|
||||
finish_wait(&vblk->queue_wait, &wait);
|
||||
}
|
||||
|
||||
static inline void virtblk_add_req(struct virtblk_req *vbr,
|
||||
unsigned int out, unsigned int in)
|
||||
{
|
||||
struct virtio_blk *vblk = vbr->vblk;
|
||||
|
||||
spin_lock_irq(vblk->disk->queue->queue_lock);
|
||||
if (unlikely(virtqueue_add_buf(vblk->vq, vbr->sg, out, in, vbr,
|
||||
GFP_ATOMIC) < 0)) {
|
||||
spin_unlock_irq(vblk->disk->queue->queue_lock);
|
||||
virtblk_add_buf_wait(vblk, vbr, out, in);
|
||||
return;
|
||||
}
|
||||
virtqueue_kick(vblk->vq);
|
||||
spin_unlock_irq(vblk->disk->queue->queue_lock);
|
||||
}
|
||||
|
||||
static int virtblk_bio_send_flush(struct virtblk_req *vbr)
|
||||
{
|
||||
unsigned int out = 0, in = 0;
|
||||
|
||||
vbr->flags |= VBLK_IS_FLUSH;
|
||||
vbr->out_hdr.type = VIRTIO_BLK_T_FLUSH;
|
||||
vbr->out_hdr.sector = 0;
|
||||
vbr->out_hdr.ioprio = 0;
|
||||
sg_set_buf(&vbr->sg[out++], &vbr->out_hdr, sizeof(vbr->out_hdr));
|
||||
sg_set_buf(&vbr->sg[out + in++], &vbr->status, sizeof(vbr->status));
|
||||
|
||||
virtblk_add_req(vbr, out, in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int virtblk_bio_send_data(struct virtblk_req *vbr)
|
||||
{
|
||||
struct virtio_blk *vblk = vbr->vblk;
|
||||
unsigned int num, out = 0, in = 0;
|
||||
struct bio *bio = vbr->bio;
|
||||
|
||||
vbr->flags &= ~VBLK_IS_FLUSH;
|
||||
vbr->out_hdr.type = 0;
|
||||
vbr->out_hdr.sector = bio->bi_sector;
|
||||
vbr->out_hdr.ioprio = bio_prio(bio);
|
||||
|
||||
sg_set_buf(&vbr->sg[out++], &vbr->out_hdr, sizeof(vbr->out_hdr));
|
||||
|
||||
num = blk_bio_map_sg(vblk->disk->queue, bio, vbr->sg + out);
|
||||
|
||||
sg_set_buf(&vbr->sg[num + out + in++], &vbr->status,
|
||||
sizeof(vbr->status));
|
||||
|
||||
if (num) {
|
||||
if (bio->bi_rw & REQ_WRITE) {
|
||||
vbr->out_hdr.type |= VIRTIO_BLK_T_OUT;
|
||||
out += num;
|
||||
} else {
|
||||
vbr->out_hdr.type |= VIRTIO_BLK_T_IN;
|
||||
in += num;
|
||||
}
|
||||
}
|
||||
|
||||
virtblk_add_req(vbr, out, in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void virtblk_bio_send_data_work(struct work_struct *work)
|
||||
{
|
||||
struct virtblk_req *vbr;
|
||||
|
||||
vbr = container_of(work, struct virtblk_req, work);
|
||||
|
||||
virtblk_bio_send_data(vbr);
|
||||
}
|
||||
|
||||
static void virtblk_bio_send_flush_work(struct work_struct *work)
|
||||
{
|
||||
struct virtblk_req *vbr;
|
||||
|
||||
vbr = container_of(work, struct virtblk_req, work);
|
||||
|
||||
virtblk_bio_send_flush(vbr);
|
||||
}
|
||||
|
||||
static inline void virtblk_request_done(struct virtblk_req *vbr)
|
||||
{
|
||||
struct virtio_blk *vblk = vbr->vblk;
|
||||
struct request *req = vbr->req;
|
||||
int error = virtblk_result(vbr);
|
||||
|
||||
if (req->cmd_type == REQ_TYPE_BLOCK_PC) {
|
||||
req->resid_len = vbr->in_hdr.residual;
|
||||
req->sense_len = vbr->in_hdr.sense_len;
|
||||
req->errors = vbr->in_hdr.errors;
|
||||
} else if (req->cmd_type == REQ_TYPE_SPECIAL) {
|
||||
req->errors = (error != 0);
|
||||
}
|
||||
|
||||
__blk_end_request_all(req, error);
|
||||
mempool_free(vbr, vblk->pool);
|
||||
}
|
||||
|
||||
static inline void virtblk_bio_flush_done(struct virtblk_req *vbr)
|
||||
{
|
||||
struct virtio_blk *vblk = vbr->vblk;
|
||||
|
||||
if (vbr->flags & VBLK_REQ_DATA) {
|
||||
/* Send out the actual write data */
|
||||
INIT_WORK(&vbr->work, virtblk_bio_send_data_work);
|
||||
queue_work(virtblk_wq, &vbr->work);
|
||||
} else {
|
||||
bio_endio(vbr->bio, virtblk_result(vbr));
|
||||
mempool_free(vbr, vblk->pool);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void virtblk_bio_data_done(struct virtblk_req *vbr)
|
||||
{
|
||||
struct virtio_blk *vblk = vbr->vblk;
|
||||
|
||||
if (unlikely(vbr->flags & VBLK_REQ_FUA)) {
|
||||
/* Send out a flush before end the bio */
|
||||
vbr->flags &= ~VBLK_REQ_DATA;
|
||||
INIT_WORK(&vbr->work, virtblk_bio_send_flush_work);
|
||||
queue_work(virtblk_wq, &vbr->work);
|
||||
} else {
|
||||
bio_endio(vbr->bio, virtblk_result(vbr));
|
||||
mempool_free(vbr, vblk->pool);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void virtblk_bio_done(struct virtblk_req *vbr)
|
||||
{
|
||||
if (unlikely(vbr->flags & VBLK_IS_FLUSH))
|
||||
virtblk_bio_flush_done(vbr);
|
||||
else
|
||||
virtblk_bio_data_done(vbr);
|
||||
}
|
||||
|
||||
static void virtblk_done(struct virtqueue *vq)
|
||||
{
|
||||
struct virtio_blk *vblk = vq->vdev->priv;
|
||||
bool bio_done = false, req_done = false;
|
||||
struct virtblk_req *vbr;
|
||||
unsigned long flags;
|
||||
unsigned int len;
|
||||
|
||||
spin_lock_irqsave(vblk->disk->queue->queue_lock, flags);
|
||||
do {
|
||||
virtqueue_disable_cb(vq);
|
||||
while ((vbr = virtqueue_get_buf(vblk->vq, &len)) != NULL) {
|
||||
if (vbr->bio) {
|
||||
virtblk_bio_done(vbr);
|
||||
bio_done = true;
|
||||
} else {
|
||||
virtblk_request_done(vbr);
|
||||
req_done = true;
|
||||
}
|
||||
}
|
||||
} while (!virtqueue_enable_cb(vq));
|
||||
/* In case queue is stopped waiting for more buffers. */
|
||||
blk_start_queue(vblk->disk->queue);
|
||||
if (req_done)
|
||||
blk_start_queue(vblk->disk->queue);
|
||||
spin_unlock_irqrestore(vblk->disk->queue->queue_lock, flags);
|
||||
|
||||
if (bio_done)
|
||||
wake_up(&vblk->queue_wait);
|
||||
}
|
||||
|
||||
static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
|
||||
|
|
@ -106,13 +301,13 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
|
|||
unsigned long num, out = 0, in = 0;
|
||||
struct virtblk_req *vbr;
|
||||
|
||||
vbr = mempool_alloc(vblk->pool, GFP_ATOMIC);
|
||||
vbr = virtblk_alloc_req(vblk, GFP_ATOMIC);
|
||||
if (!vbr)
|
||||
/* When another request finishes we'll try again. */
|
||||
return false;
|
||||
|
||||
vbr->req = req;
|
||||
|
||||
vbr->bio = NULL;
|
||||
if (req->cmd_flags & REQ_FLUSH) {
|
||||
vbr->out_hdr.type = VIRTIO_BLK_T_FLUSH;
|
||||
vbr->out_hdr.sector = 0;
|
||||
|
|
@ -172,7 +367,8 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
|
|||
}
|
||||
}
|
||||
|
||||
if (virtqueue_add_buf(vblk->vq, vblk->sg, out, in, vbr, GFP_ATOMIC)<0) {
|
||||
if (virtqueue_add_buf(vblk->vq, vblk->sg, out, in, vbr,
|
||||
GFP_ATOMIC) < 0) {
|
||||
mempool_free(vbr, vblk->pool);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -180,7 +376,7 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
|
|||
return true;
|
||||
}
|
||||
|
||||
static void do_virtblk_request(struct request_queue *q)
|
||||
static void virtblk_request(struct request_queue *q)
|
||||
{
|
||||
struct virtio_blk *vblk = q->queuedata;
|
||||
struct request *req;
|
||||
|
|
@ -203,6 +399,34 @@ static void do_virtblk_request(struct request_queue *q)
|
|||
virtqueue_kick(vblk->vq);
|
||||
}
|
||||
|
||||
static void virtblk_make_request(struct request_queue *q, struct bio *bio)
|
||||
{
|
||||
struct virtio_blk *vblk = q->queuedata;
|
||||
struct virtblk_req *vbr;
|
||||
|
||||
BUG_ON(bio->bi_phys_segments + 2 > vblk->sg_elems);
|
||||
|
||||
vbr = virtblk_alloc_req(vblk, GFP_NOIO);
|
||||
if (!vbr) {
|
||||
bio_endio(bio, -ENOMEM);
|
||||
return;
|
||||
}
|
||||
|
||||
vbr->bio = bio;
|
||||
vbr->flags = 0;
|
||||
if (bio->bi_rw & REQ_FLUSH)
|
||||
vbr->flags |= VBLK_REQ_FLUSH;
|
||||
if (bio->bi_rw & REQ_FUA)
|
||||
vbr->flags |= VBLK_REQ_FUA;
|
||||
if (bio->bi_size)
|
||||
vbr->flags |= VBLK_REQ_DATA;
|
||||
|
||||
if (unlikely(vbr->flags & VBLK_REQ_FLUSH))
|
||||
virtblk_bio_send_flush(vbr);
|
||||
else
|
||||
virtblk_bio_send_data(vbr);
|
||||
}
|
||||
|
||||
/* return id (s/n) string for *disk to *id_str
|
||||
*/
|
||||
static int virtblk_get_id(struct gendisk *disk, char *id_str)
|
||||
|
|
@ -360,7 +584,7 @@ static int init_vq(struct virtio_blk *vblk)
|
|||
int err = 0;
|
||||
|
||||
/* We expect one virtqueue, for output. */
|
||||
vblk->vq = virtio_find_single_vq(vblk->vdev, blk_done, "requests");
|
||||
vblk->vq = virtio_find_single_vq(vblk->vdev, virtblk_done, "requests");
|
||||
if (IS_ERR(vblk->vq))
|
||||
err = PTR_ERR(vblk->vq);
|
||||
|
||||
|
|
@ -477,6 +701,8 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
|
|||
struct virtio_blk *vblk;
|
||||
struct request_queue *q;
|
||||
int err, index;
|
||||
int pool_size;
|
||||
|
||||
u64 cap;
|
||||
u32 v, blk_size, sg_elems, opt_io_size;
|
||||
u16 min_io_size;
|
||||
|
|
@ -506,10 +732,12 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
|
|||
goto out_free_index;
|
||||
}
|
||||
|
||||
init_waitqueue_head(&vblk->queue_wait);
|
||||
vblk->vdev = vdev;
|
||||
vblk->sg_elems = sg_elems;
|
||||
sg_init_table(vblk->sg, vblk->sg_elems);
|
||||
mutex_init(&vblk->config_lock);
|
||||
|
||||
INIT_WORK(&vblk->config_work, virtblk_config_changed_work);
|
||||
vblk->config_enable = true;
|
||||
|
||||
|
|
@ -517,7 +745,10 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
|
|||
if (err)
|
||||
goto out_free_vblk;
|
||||
|
||||
vblk->pool = mempool_create_kmalloc_pool(1,sizeof(struct virtblk_req));
|
||||
pool_size = sizeof(struct virtblk_req);
|
||||
if (use_bio)
|
||||
pool_size += sizeof(struct scatterlist) * sg_elems;
|
||||
vblk->pool = mempool_create_kmalloc_pool(1, pool_size);
|
||||
if (!vblk->pool) {
|
||||
err = -ENOMEM;
|
||||
goto out_free_vq;
|
||||
|
|
@ -530,12 +761,14 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
|
|||
goto out_mempool;
|
||||
}
|
||||
|
||||
q = vblk->disk->queue = blk_init_queue(do_virtblk_request, NULL);
|
||||
q = vblk->disk->queue = blk_init_queue(virtblk_request, NULL);
|
||||
if (!q) {
|
||||
err = -ENOMEM;
|
||||
goto out_put_disk;
|
||||
}
|
||||
|
||||
if (use_bio)
|
||||
blk_queue_make_request(q, virtblk_make_request);
|
||||
q->queuedata = vblk;
|
||||
|
||||
virtblk_name_format("vd", index, vblk->disk->disk_name, DISK_NAME_LEN);
|
||||
|
|
@ -620,7 +853,6 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
|
|||
if (!err && opt_io_size)
|
||||
blk_queue_io_opt(q, blk_size * opt_io_size);
|
||||
|
||||
|
||||
add_disk(vblk->disk);
|
||||
err = device_create_file(disk_to_dev(vblk->disk), &dev_attr_serial);
|
||||
if (err)
|
||||
|
|
|
|||
Loading…
Reference in a new issue