@@ -30,9 +30,13 @@ struct blk_device {
uint32_t guest_features;
uint16_t config_vector;
uint8_t status;
+ pthread_t io_thread;
+ pthread_mutex_t io_mutex;
+ pthread_cond_t io_cond;
/* virtio queue */
uint16_t queue_selector;
+ uint64_t virtio_blk_queue_set_flags;
struct virt_queue vqs[NUM_VIRT_QUEUES];
};
@@ -52,6 +56,9 @@ static struct blk_device blk_device = {
* same applies to VIRTIO_BLK_F_BLK_SIZE
*/
.host_features = (1UL << VIRTIO_BLK_F_SEG_MAX),
+
+ .io_mutex = PTHREAD_MUTEX_INITIALIZER,
+ .io_cond = PTHREAD_COND_INITIALIZER
};
static bool virtio_blk_pci_io_device_specific_in(void *data, unsigned long offset, int size, uint32_t count)
@@ -148,15 +155,57 @@ static bool virtio_blk_do_io_request(struct kvm *self, struct virt_queue *queue)
return true;
}
-static void virtio_blk_handle_callback(struct kvm *self, uint16_t queue_index)
+
+
+static int virtio_blk_get_selected_queue(void)
{
- struct virt_queue *vq = &blk_device.vqs[queue_index];
+ int i;
- while (virt_queue__available(vq))
- virtio_blk_do_io_request(self, vq);
+ for (i = 0 ; i < NUM_VIRT_QUEUES ; i++) {
+ if (blk_device.virtio_blk_queue_set_flags & (1 << i)) {
+ blk_device.virtio_blk_queue_set_flags &= ~(1 << i);
+ return i;
+ }
+ }
- kvm__irq_line(self, VIRTIO_BLK_IRQ, 1);
+ return -1;
+}
+static void *virtio_blk_io_thread(void *ptr)
+{
+ struct kvm *self = ptr;
+ int ret;
+ mutex_lock(&blk_device.io_mutex);
+ ret = pthread_cond_wait(&blk_device.io_cond, &blk_device.io_mutex);
+ while (ret == 0) {
+ int queue_index = virtio_blk_get_selected_queue();
+ mutex_unlock(&blk_device.io_mutex);
+ while (queue_index >= 0) {
+ struct virt_queue *vq = &blk_device.vqs[queue_index];
+
+ while (virt_queue__available(vq))
+ virtio_blk_do_io_request(self, vq);
+
+ kvm__irq_line(self, VIRTIO_BLK_IRQ, 1);
+
+ mutex_lock(&blk_device.io_mutex);
+ queue_index = virtio_blk_get_selected_queue();
+ mutex_unlock(&blk_device.io_mutex);
+ }
+ mutex_lock(&blk_device.io_mutex);
+ ret = pthread_cond_wait(&(blk_device.io_cond), &(blk_device.io_mutex));
+ }
+
+ return NULL;
+}
+
+static void virtio_blk_handle_callback(struct kvm *self, uint16_t queue_index)
+{
+ pthread_mutex_lock(&(blk_device.io_mutex));
+ blk_device.virtio_blk_queue_set_flags |= (1 << queue_index);
+ pthread_mutex_unlock(&(blk_device.io_mutex));
+
+ pthread_cond_signal(&(blk_device.io_cond));
}
static bool virtio_blk_pci_io_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
@@ -242,6 +291,8 @@ void virtio_blk__init(struct kvm *self)
if (!self->disk_image)
return;
+ pthread_create(&blk_device.io_thread, NULL, virtio_blk_io_thread, self);
+
blk_device.blk_config.capacity = self->disk_image->size / SECTOR_SIZE;
pci__register(&virtio_blk_pci_device, PCI_VIRTIO_BLK_DEVNUM);
Add I/O thread to handle I/O operations in virtio-blk. There is currently support for multiple virtio queues but the kernel side supports only one virtio queue. It's not too much of a performance impact and the ABI does support multiple queues there - So I've prefered to do it like that to keep it flexible. I/O performance itself doesn't increase much due to the patch, what changes is system responsiveness during I/O operations. On an unthreaded system, The VCPU is frozen up until the I/O request is complete. On the other hand, On a threaded system the VCPU is free to do other work or queue more I/O while waiting for the original I/O request to complete. Signed-off-by: Sasha Levin <levinsasha928@gmail.com> --- tools/kvm/virtio-blk.c | 61 ++++++++++++++++++++++++++++++++++++++++++++---- 1 files changed, 56 insertions(+), 5 deletions(-)