[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [UNIKRAFT/BALLOON v2 3/7] plat/drivers/balloon: KVM Virtio balloon driver implementation
From: Cezar Craciunoiu <cezar.craciunoiu@xxxxxxxxx> The implementation is based on different Virtio drivers in the project. It follows a similar structure to 9p/blk/etc. After adding the device, the driver uses virtqueues to send/take memory to/from KVM. Other functions will interact with the driver through the inflate_balloon and deflate_balloon calls. Signed-off-by: Cezar Craciunoiu <cezar.craciunoiu@xxxxxxxxx> --- plat/drivers/balloon/balloon_drv.c | 371 +++++++++++++++++++++++++++++ 1 file changed, 371 insertions(+) create mode 100644 plat/drivers/balloon/balloon_drv.c diff --git a/plat/drivers/balloon/balloon_drv.c b/plat/drivers/balloon/balloon_drv.c new file mode 100644 index 0000000..832eff8 --- /dev/null +++ b/plat/drivers/balloon/balloon_drv.c @@ -0,0 +1,371 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Authors: Cason Schindler & Jack Raney <cason.j.schindler@xxxxxxxxx> + * Cezar Craciunoiu <cezar.craciunoiu@xxxxxxxxx> + * + * Copyright (c) 2019, The University of Texas at Austin. All rights reserved. + * 2020, University Politehnica of Bucharest. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + */ +#include <uk/plat/common/sections.h> +#include <sys/types.h> +#include <uk/assert.h> +#include <kvm/config.h> +#include <inttypes.h> +#include <uk/alloc.h> +#include <uk/sglist.h> +#include <uk/list.h> +#include <uk/assert.h> +#include <uk/mutex.h> +#include <virtio/virtio_ids.h> +#include <virtio/virtio_bus.h> +#include <virtio/virtqueue.h> +#include <balloon/balloon.h> + +#define DRIVER_NAME "virtio-balloon" +#define VTBALLOON_PAGES_PER_REQUEST 8192 + +static struct uk_alloc *a; + +static struct virtio_balloon_device *global_vb; + +/* pages given to hypervisor (in the balloon) */ +struct balloon_pages { + + uint32_t num_pages; + +}; + +/* temporary storage for pages with which we are either + * inflating or deflating the balloon + */ +struct transport_pages { + + uint32_t num_pages; + uint32_t *pages; + +}; + +/* wrapper for virtio device */ +struct virtio_balloon_device { + + struct virtio_dev *vdev; + + struct virtqueue *inflate_vq, *deflate_vq; + + __u16 infvq_id; + __u16 defvq_id; + + char *tag; + + struct balloon_pages *balloon; + + struct transport_pages *transport; + + uint64_t features; + uint32_t flags; + + struct uk_mutex lock; +}; + +static void clear_transport(struct virtio_balloon_device *vb) +{ + int num = vb->transport->num_pages; + int i; + + for (i = 0; i < num; i++) { + (vb->transport->pages)[i] = 0; + vb->transport->num_pages -= 1; + } +} + +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2011, Bryan Venteicher <bryanv@xxxxxxxxxxx> + * All rights reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* The above copyright notice applies only to the below function, + * vtballoon_send_page_frames, which is based on FreeBSD's function + * of the same name. + */ + +static void vtballoon_send_page_frames(struct virtio_balloon_device *vb, + struct virtqueue *vq, int npages) +{ + struct uk_sglist sg; + struct uk_sglist_seg segs[1]; + int c; + void *vq_cookie; + __u32 len = 0; + + uk_sglist_init(&sg, 1, segs); + + uk_sglist_append(&sg, vb->transport->pages, npages * sizeof(uint32_t)); + + virtqueue_buffer_enqueue(vq, &vq_cookie, &sg, 1, 0); + + virtqueue_host_notify(vq); + + /* wait on KVM to respond. Need a safer method for this */ + while ((c = virtqueue_buffer_dequeue(vq, &vq_cookie, &len)) < 0) + ; + +} + +/** + * This is equivalent to leaking from the balloon and + * increasing memory reservation for guest. + */ +int deflate_balloon(uintptr_t *pages_to_guest, uint32_t num) +{ + struct virtio_balloon_device *vb = global_vb; + int num_pages_taken; + uint32_t i; + + /* check if device is ready */ + if (!global_vb) + return -ENXIO; + + uk_mutex_lock(&vb->lock); + + clear_transport(vb); + + if (vb->balloon->num_pages < num) + num = vb->balloon->num_pages; + + for (i = 0; i < num; i++) { + vb->transport->pages[i] = pages_to_guest[i]; + vb->balloon->num_pages -= 1; + vb->transport->num_pages += 1; + } + + num_pages_taken = vb->transport->num_pages; + + if (vb->transport->num_pages != 0) { + vtballoon_send_page_frames(vb, vb->deflate_vq, + vb->transport->num_pages); + } + + uk_mutex_unlock(&vb->lock); + + return num_pages_taken; +} + +/** + * This is equivalent to filling the balloon and + * decreasing memory reservation for guest. + */ +int inflate_balloon(uintptr_t *pages_to_host, uint32_t num) +{ + struct virtio_balloon_device *vb = global_vb; + int num_pages_given; + uint32_t i; + + /* check if device is ready */ + if (!global_vb) + return -ENXIO; + + uk_mutex_lock(&vb->lock); + + clear_transport(vb); + + for (i = 0; i < num; i++) { + vb->transport->pages[i] = pages_to_host[i] / __PAGE_SIZE; + vb->balloon->num_pages += 1; + vb->transport->num_pages += 1; + } + + num_pages_given = vb->transport->num_pages; + + if (vb->transport->num_pages != 0) { + vtballoon_send_page_frames(vb, vb->inflate_vq, + vb->transport->num_pages); + } + + uk_mutex_unlock(&vb->lock); + + return num_pages_given; +} + + +static inline void virtio_balloon_feature_set(struct virtio_balloon_device *vb) +{ + vb->features = 0; + vb->flags = 0; + vb->vdev->features = 0; +} + +static int virtio_balloon_vq_alloc(struct virtio_balloon_device *vb) +{ + int vq_avail = 0; + int rc = 0; + __u16 qdesc_size[2]; + + vq_avail = virtio_find_vqs(vb->vdev, 2, &(qdesc_size[0])); + if (unlikely(vq_avail != 2)) { + uk_pr_err(DRIVER_NAME": Expected: %d queues, found %d\n", + 2, vq_avail); + rc = -ENOMEM; + goto exit; + } + + vb->infvq_id = 0; + vb->defvq_id = 1; + + vb->inflate_vq = virtio_vqueue_setup(vb->vdev, vb->infvq_id, + qdesc_size[0], NULL, a); + vb->inflate_vq->priv = vb; + + if (unlikely(PTRISERR(vb->inflate_vq))) { + uk_pr_err(DRIVER_NAME": Failed to set up virtqueue %"PRIu16"\n", + vb->infvq_id); + rc = PTR2ERR(vb->inflate_vq); + } + + vb->deflate_vq = virtio_vqueue_setup(vb->vdev, vb->defvq_id, + qdesc_size[1], NULL, a); + vb->deflate_vq->priv = vb; + + if (unlikely(PTRISERR(vb->deflate_vq))) { + uk_pr_err(DRIVER_NAME": Failed to set up virtqueue %"PRIu16"\n", + vb->defvq_id); + rc = PTR2ERR(vb->deflate_vq); + } + +exit: + return rc; +} + +static int virtio_balloon_start(struct virtio_balloon_device *vb) +{ + /* Disable interrupts for queues to suppress error messages */ + virtqueue_intr_disable(vb->inflate_vq); + virtqueue_intr_disable(vb->deflate_vq); + virtio_dev_drv_up(vb->vdev); + uk_pr_info(DRIVER_NAME": %s started\n", vb->tag); + + return 0; +} + +static int virtio_balloon_add_dev(struct virtio_dev *vdev) +{ + + struct virtio_balloon_device *vbdev; + int rc = 0; + void *alc; + + UK_ASSERT(vdev != NULL); + + vbdev = uk_calloc(a, 1, sizeof(*vbdev)); + + if (!vbdev) { + rc = -ENOMEM; + goto err_out; + } + + vbdev->tag = "VIRTIO_BALLOON_DRV_DEV"; + + uk_mutex_init(&vbdev->lock); + + vbdev->vdev = vdev; + virtio_balloon_feature_set(vbdev); + rc = virtio_balloon_vq_alloc(vbdev); + if (rc) + goto err_out; + + vbdev->transport = uk_calloc(a, 1, sizeof(struct transport_pages)); + if (!(vbdev->transport)) { + rc = -ENOMEM; + goto err_out; + } + vbdev->transport->pages = uk_calloc(a, 1, + VTBALLOON_PAGES_PER_REQUEST * sizeof(uint32_t)); + if (!(vbdev->transport->pages)) { + rc = -ENOMEM; + goto err_out; + } + vbdev->balloon = uk_calloc(a, 1, sizeof(struct balloon_pages)); + if (!(vbdev->balloon)) { + rc = -ENOMEM; + goto err_out; + } + + rc = virtio_balloon_start(vbdev); + if (rc) + goto err_out; + +exit: + global_vb = vbdev; + /* initial alloc and free to trigger ballon init */ + alc = uk_palloc(a, 1); + uk_pfree(a, alc, 1); + return rc; +err_out: + uk_free(a, vbdev->transport->pages); + uk_free(a, vbdev->transport); + uk_free(a, vbdev->balloon); + uk_free(a, vbdev); + goto exit; + +} + +static int virtio_balloon_drv_init(struct uk_alloc *drv_allocator) +{ + if (!drv_allocator) + return -EINVAL; + + a = drv_allocator; + return 0; + +} + +static const struct virtio_dev_id vballoon_dev_id[] = { + {VIRTIO_ID_BALLOON}, + {VIRTIO_ID_INVALID} /* List Terminator */ +}; + +static struct virtio_driver virtio_balloon_driver = { + .dev_ids = vballoon_dev_id, + .init = virtio_balloon_drv_init, + .add_dev = virtio_balloon_add_dev +}; +VIRTIO_BUS_REGISTER_DRIVER(&virtio_balloon_driver); -- 2.20.1
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |