Also copy virtio-dev since this in clearly implementation ...
On Thu, Jan 30, 2014 at 07:34:30PM +1030, Rusty Russell wrote:
> Hi,
>
>
I tried to write a new balloon driver; it's completely untested
> (as I need to write the device). The protocol is basically two vqs, one
> for the guest to send commands, one for the host to send commands.
>
> Some interesting things come out:
> 1) We do need to explicitly tell the host where the page is we want.
>
This is required for compaction, for example.
>
> 2) We need to be able to exceed the balloon target, especially for page
>
migration. Thus there's no mechanism for the device to refuse to
>
give us the pages.
>
> 3) The device can offer multiple page sizes, but the driver can only
>
accept one. I'm not sure if this is useful, as guests are either
>
huge page backed or not, and returning sub-pages isn't useful.
>
> Linux demo code follows.
>
> Cheers,
> Rusty.
More comments:
- for projects like auto-ballooning that Luiz works on,
it's not nice that to swap page 1 for page 2
you have to inflate then deflate
besides overhead this confuses the host:
imagine you tell QEMU to increase target,
meanwhile guest inflates temporarily,
QEMU thinks okay done, now you suddenly deflate.
- what's the status of page returned from balloon?
is it zeroed or can it have old data in there?
I think in practice Linux will sometimes map in a zero page,
so guest can save cycles and avoid zeroing it out.
I think we should tell this to guest when returning
pages.
- I am guessing EXTRA_MEM is for uses like the ones proposed by
Frank Swiderski from google that inflate/deflate balloon
whenever guest wants (look for "Add a page cache-backed balloon
device driver").
this is useful but - we need to distinguish pages
like this from regular inflate.
it's not just counter and host needs a way to know
that it's target is reached
- do we even want to allow guest not telling host when it wants
to reuse the page?
if yes, I think this should be per-page somehow: when balloon
is inflated guest should tell host whether it
expects to use this page.
So I think we should accomodate these uses, and so we want the following flags:
- WEAK_TARGET (that's the EXTRA_MEM but I think done in a better way)
flag that specifies pages do not count against target,
can be taken out of balloon.
EXTRA_MEM suggests there's an upper limit on balloon size
but IMHO that's just extra work for host: host does not care
I think, give it as much as you want.
set by guest, used by host
- TELL_HOST flag that specifies guest will tell host before using pages
(that's VIRTIO_BALLOON_F_MUST_TELL_HOST
at the moment, listed here for completeness)
set by guest, used by host
- ZEROED
flag that specifies that page returned to guest
is zeroed
set by host, used by guest
Each of the flags can be just a feature flag, and then
if we wants a mix of them host can create multiple
balloon devices with differnet flags, and guest looks for best
balloon for its purposes.
Alternatively flags can be set and reported per page.
A couple of other suggestions:
- how to accomodate memory pressure in guest?
Let's add a field telling host how hard do we
want our memory back
- assume you want to over-commit host and start
inflating balloon.
If low on memory it might be better for guest to
wait a bit before inflating.
Also, if host asks for a lot of memory a ton of
allocations will slow guest significantly.
But for guest to do the right thing we need host to tell guest what
are its memory and time contraints.
Let's add a field telling guest how hard do we
want it to give us memory (e.g. time limit)
> diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
> index 9076635697bb..1dd45691b618 100644
> --- a/drivers/virtio/Makefile
> +++ b/drivers/virtio/Makefile
> @@ -1,4 +1,4 @@
> obj-$(CONFIG_VIRTIO) += virtio.o virtio_ring.o
> obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o
> obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o
> -obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o
> +obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o virtio_balloon2.o
> diff --git a/drivers/virtio/virtio_balloon2.c b/drivers/virtio/virtio_balloon2.c
> new file mode 100644
> index 000000000000..93f13e7c561d
> --- /dev/null
> +++ b/drivers/virtio/virtio_balloon2.c
> @@ -0,0 +1,566 @@
> +/*
> + * Virtio balloon implementation, inspired by Dor Laor and Marcelo
> + * Tosatti's implementations.
> + *
> + * Copyright 2008, 2014 Rusty Russell IBM Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor,
Boston, MA 02110-1301 USA
> + */
> +
> +#include <linux/virtio.h>
> +#include <linux/virtio_balloon.h>
> +#include <linux/swap.h>
> +#include <linux/kthread.h>
> +#include <linux/freezer.h>
> +#include <linux/delay.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/balloon_compaction.h>
> +
> +struct gcmd_get_pages {
> + __le64 type; /* VIRTIO_BALLOON_GCMD_GET_PAGES */
> + __le64 pages[256];
> +};
> +
> +struct gcmd_give_pages {
> + __le64 type; /* VIRTIO_BALLOON_GCMD_GIVE_PAGES */
> + __le64 pages[256];
> +};
> +
> +struct gcmd_need_mem {
> + __le64 type; /* VIRTIO_BALLOON_GCMD_NEED_MEM */
> +};
> +
> +struct gcmd_stats_reply {
> + __le64 type; /* VIRTIO_BALLOON_GCMD_STATS_REPLY */
> + struct virtio_balloon_statistic stats[VIRTIO_BALLOON_S_NR];
> +};
> +
> +struct hcmd_set_balloon {
> + __le64 type; /* VIRTIO_BALLOON_HCMD_SET_BALLOON */
> + __le64 target;
> +};
> +
> +struct hcmd_get_stats {
> + __le64 type; /* VIRTIO_BALLOON_HCMD_GET_STATS */
> +};
> +
> +struct virtio_balloon {
> + /* Protects contents of entire structure. */
> + struct mutex lock;
> +
> + struct virtio_device *vdev;
> + struct virtqueue *gcmd_vq, *hcmd_vq;
> +
> + /* The thread servicing the balloon. */
> + struct task_struct *thread;
> +
> + /* For interrupt/suspend to wake balloon thread. */
> + wait_queue_head_t wait;
> +
> + /* How many pages are we supposed to have in balloon? */
> + s64 target;
> +
> + /* How many do we have in the balloon? */
> + u64 num_pages;
> +
> + /* This reminds me of Eeyore. */
> + bool broken;
> +
> + /*
> + * The pages we've told the Host we're not using are enqueued
> + * at vb_dev_info->pages list.
> + */
> + struct balloon_dev_info *vb_dev_info;
> +
> + /* To avoid kmalloc, we use single hcmd and gcmd buffers. */
> + union gcmd {
> + __le64 type;
> + struct gcmd_get_pages get_pages;
> + struct gcmd_give_pages give_pages;
> + struct gcmd_need_mem need_mem;
> + struct gcmd_stats_reply stats_reply;
> + } gcmd;
> +
> + union hcmd {
> + __le64 type;
> + struct hcmd_set_balloon set_balloon;
> + struct hcmd_get_stats get_stats;
> + } hcmd;
> +};
> +
> +static struct virtio_device_id id_table[] = {
> + { VIRTIO_ID_MEMBALLOON, VIRTIO_DEV_ANY_ID },
> + { 0 },
> +};
> +
> +static void wake_balloon(struct virtqueue *vq)
> +{
> + struct virtio_balloon *vb = vq->vdev->priv;
> +
> + wake_up(&vb->wait);
> +}
> +
> +/* Command is in vb->gcmd, lock is held. */
> +static bool send_gcmd(struct virtio_balloon *vb, size_t len)
> +{
> + struct scatterlist sg;
> +
> + BUG_ON(len
> sizeof(vb->gcmd));
> + sg_init_one(&sg, &vb->gcmd, len);
> +
> + /*
> + * We should always be able to add one buffer to an empty queue.
> + * If not, it's a broken device.
> + */
> + if (virtqueue_add_outbuf(vb->gcmd_vq, &sg, 1, vb, GFP_KERNEL) != 0
> +
|| virtqueue_kick(vb->gcmd_vq) != 0) {
> + vb->broken = true;
> + return false;
> + }
> +
> + /* When host has read buffer, this completes via wake_balloon */
> + wait_event(vb->wait,
> +
virtqueue_get_buf(vb->gcmd_vq, &len)
> +
|| (vb->broken = virtqueue_is_broken(vb->gcmd_vq)));
> + return !vb->broken;
> +}
> +
> +static void give_to_balloon(struct virtio_balloon *vb, u64 num)
> +{
> + struct balloon_dev_info *vb_dev_info = vb->vb_dev_info;
> + u64 i;
> +
> + /* We can only do one array worth at a time. */
> + num = min_t(u64, num, ARRAY_SIZE(vb->gcmd.give_pages.pages));
> +
> + vb->gcmd.give_pages.type = cpu_to_le64(VIRTIO_BALLOON_GCMD_GIVE_PAGES);
> +
> + for (i = 0; i < num; i++) {
> + struct page *page = balloon_page_enqueue(vb_dev_info);
> +
> + if (!page) {
> + dev_info_ratelimited(&vb->vdev->dev,
> +
"Out of puff! Can't get page\n");
> + /* Sleep for at least 1/5 of a second before retry. */
> + msleep(200);
> + break;
> + }
> +
> + vb->gcmd.give_pages.pages[i] = page_to_pfn(page) << PAGE_SHIFT;
> + vb->num_pages++;
> + adjust_managed_page_count(page, -1);
> + }
> +
> + /* Did we get any? */
> + if (i)
> + send_gcmd(vb, offsetof(struct gcmd_give_pages, pages[i]));
> +}
> +
> +static void take_from_balloon(struct virtio_balloon *vb, u64 num)
> +{
> + struct balloon_dev_info *vb_dev_info = vb->vb_dev_info;
> + size_t i;
> +
> + /* We can only do one array worth at a time. */
> + num = min_t(u64, num, ARRAY_SIZE(vb->gcmd.get_pages.pages));
> +
> + vb->gcmd.get_pages.type = cpu_to_le64(VIRTIO_BALLOON_GCMD_GET_PAGES);
> +
> + for (i = 0; i < num; i++) {
> + struct page *page = balloon_page_dequeue(vb_dev_info);
> +
> + /* In case we ran out of pages (compaction) */
> + if (!page)
> + break;
> +
> + vb->gcmd.get_pages.pages[i] = page_to_pfn(page) << PAGE_SHIFT;
> + vb->num_pages--;
> + }
> + num = i;
> + if (num)
> + send_gcmd(vb, offsetof(struct gcmd_get_pages, pages[num]));
> +
> + /* Now release those pages. */
> + for (i = 0; i < num; i++) {
> + struct page *page;
> +
> + page = pfn_to_page(vb->gcmd.get_pages.pages[i] >> PAGE_SHIFT);
> + balloon_page_free(page);
> + adjust_managed_page_count(page, 1);
> + }
> + mutex_unlock(&vb->lock);
> +}
> +
> +static inline void set_stat(struct gcmd_stats_reply *stats, int idx,
> +
u64 tag, u64 val)
> +{
> + BUG_ON(idx >= ARRAY_SIZE(stats->stats));
> + stats->stats[idx].tag = cpu_to_le64(tag);
> + stats->stats[idx].val = cpu_to_le64(val);
> +}
> +
> +#define pages_to_bytes(x) ((u64)(x) << PAGE_SHIFT)
> +
> +static void get_stats(struct gcmd_stats_reply *stats)
> +{
> + unsigned long events[NR_VM_EVENT_ITEMS];
> + struct sysinfo i;
> + int idx = 0;
> +
> + all_vm_events(events);
> + si_meminfo(&i);
> +
> + stats->type = cpu_to_le64(VIRTIO_BALLOON_GCMD_STATS_REPLY);
> + set_stat(stats, idx++, VIRTIO_BALLOON_S_SWAP_IN,
> + pages_to_bytes(events[PSWPIN]));
> + set_stat(stats, idx++, VIRTIO_BALLOON_S_SWAP_OUT,
> + pages_to_bytes(events[PSWPOUT]));
> + set_stat(stats, idx++, VIRTIO_BALLOON_S_MAJFLT,
> + events[PGMAJFAULT]);
> + set_stat(stats, idx++, VIRTIO_BALLOON_S_MINFLT,
> + events[PGFAULT]);
> + set_stat(stats, idx++, VIRTIO_BALLOON_S_MEMFREE,
> + pages_to_bytes(i.freeram));
> + set_stat(stats, idx++, VIRTIO_BALLOON_S_MEMTOT,
> + pages_to_bytes(i.totalram));
> +}
> +
> +static bool move_towards_target(struct virtio_balloon *vb)
> +{
> + bool moved = false;
> +
> + if (vb->broken)
> + return false;
> +
> + mutex_lock(&vb->lock);
> + if (vb->num_pages < vb->target) {
> + give_to_balloon(vb, vb->target - vb->num_pages);
> + moved = true;
> + } else if (vb->num_pages
> vb->target) {
> + take_from_balloon(vb, vb->num_pages - vb->target);
> + moved = true;
> + }
> + mutex_unlock(&vb->lock);
> + return moved;
> +}
> +
> +static bool process_hcmd(struct virtio_balloon *vb)
> +{
> + union hcmd *hcmd = NULL;
> + unsigned int cmdlen;
> + struct scatterlist sg;
> +
> + if (vb->broken)
> + return false;
> +
> + mutex_lock(&vb->lock);
> + hcmd = virtqueue_get_buf(vb->hcmd_vq, &cmdlen);
> + if (!hcmd) {
> + mutex_unlock(&vb->lock);
> + return false;
> + }
> +
> + switch (hcmd->type) {
> + case cpu_to_le64(VIRTIO_BALLOON_HCMD_SET_BALLOON):
> + vb->target = le64_to_cpu(hcmd->set_balloon.target);
> + break;
> + case cpu_to_le64(VIRTIO_BALLOON_HCMD_GET_STATS):
> + get_stats(&vb->gcmd.stats_reply);
> + send_gcmd(vb, sizeof(vb->gcmd.stats_reply));
> + break;
> + default:
> + dev_err_ratelimited(&vb->vdev->dev, "Unknown hcmd %llu\n",
> +
le64_to_cpu(hcmd->type));
> + break;
> + }
> +
> + /* Re-queue the hcmd for next time. */
> + sg_init_one(&sg, &vb->hcmd, sizeof(vb->hcmd));
> + virtqueue_add_inbuf(vb->hcmd_vq, &sg, 1, vb, GFP_KERNEL);
> +
> + mutex_unlock(&vb->lock);
> + return true;
> +}
> +
> +static int balloon(void *_vballoon)
> +{
> + struct virtio_balloon *vb = _vballoon;
> +
> + set_freezable();
> + while (!kthread_should_stop()) {
> + try_to_freeze();
> +
> + wait_event_interruptible(vb->wait,
> + kthread_should_stop()
> + || freezing(current)
> + || process_hcmd(vb)
> + || move_towards_target(vb));
> + }
> + return 0;
> +}
> +
> +static int init_vqs(struct virtio_balloon *vb)
> +{
> + struct virtqueue *vqs[2];
> + vq_callback_t *callbacks[] = { wake_balloon, wake_balloon };
> + const char *names[] = { "gcmd", "hcmd" };
> + struct scatterlist sg;
> + int err;
> +
> + err = vb->vdev->config->find_vqs(vb->vdev, 2, vqs, callbacks, names);
> + if (err)
> + return err;
> +
> + vb->gcmd_vq = vqs[0];
> + vb->hcmd_vq = vqs[1];
> +
> + /*
> + * Prime this virtqueue with one buffer so the hypervisor can
> + * use it to signal us later (it can't be broken yet!).
> + */
> + sg_init_one(&sg, &vb->hcmd, sizeof(vb->hcmd));
> + if (virtqueue_add_inbuf(vb->hcmd_vq, &sg, 1, vb, GFP_KERNEL) < 0)
> + BUG();
> + virtqueue_kick(vb->hcmd_vq);
> +
> + return 0;
> +}
> +
> +static const struct address_space_operations virtio_balloon_aops;
> +#ifdef CONFIG_BALLOON_COMPACTION
> +/*
> + * virtballoon_migratepage - perform the balloon page migration on behalf of
> + *
a compation thread.
(called under page lock)
> + * @mapping: the page->mapping which will be assigned to the new migrated page.
> + * @newpage: page that will replace the isolated page after migration finishes.
> + * @page
: the isolated (old) page that is about to be migrated to newpage.
> + * @mode
: compaction mode -- not used for balloon page migration.
> + *
> + * After a ballooned page gets isolated by compaction procedures, this is the
> + * function that performs the page migration on behalf of a compaction thread
> + * The page migration for virtio balloon is done in a simple swap fashion which
> + * follows these two macro steps:
> + * 1) insert newpage into vb->pages list and update the host about it;
> + * 2) update the host about the old page removed from vb->pages list;
> + *
> + * This function preforms the balloon page migration task.
> + * Called through balloon_mapping->a_ops->migratepage
> + */
> +static int virtballoon_migratepage(struct address_space *mapping,
> + struct page *newpage, struct page *page, enum migrate_mode mode)
> +{
> + struct balloon_dev_info *vb_dev_info = balloon_page_device(page);
> + struct virtio_balloon *vb;
> + unsigned long flags;
> + int err;
> +
> + BUG_ON(!vb_dev_info);
> +
> + vb = vb_dev_info->balloon_device;
> +
> + /*
> + * In order to avoid lock contention while migrating pages concurrently
> + * to leak_balloon() or fill_balloon() we just give up the balloon_lock
> + * this turn, as it is easier to retry the page migration later.
> + * This also prevents fill_balloon() getting stuck into a mutex
> + * recursion in the case it ends up triggering memory compaction
> + * while it is attempting to inflate the ballon.
> + */
> + if (!mutex_trylock(&vb->lock))
> + return -EAGAIN;
> +
> + /* Try to get the page out of the balloon. */
> + vb->gcmd.get_pages.type = cpu_to_le64(VIRTIO_BALLOON_GCMD_GET_PAGES);
> + vb->gcmd.get_pages.pages[0] = page_to_pfn(page) << PAGE_SHIFT;
> + if (!send_gcmd(vb, offsetof(struct gcmd_get_pages, pages[1]))) {
> + err = -EIO;
> + goto unlock;
> + }
> +
> + /* Now put newpage into balloon. */
> + vb->gcmd.give_pages.type = cpu_to_le64(VIRTIO_BALLOON_GCMD_GIVE_PAGES);
> + vb->gcmd.give_pages.pages[0] = page_to_pfn(newpage) << PAGE_SHIFT;
> + if (!send_gcmd(vb, offsetof(struct gcmd_give_pages, pages[1]))) {
> + /* We leak a page here, but only happens if balloon broken. */
> + err = -EIO;
> + goto unlock;
> + }
> +
> + spin_lock_irqsave(&vb_dev_info->pages_lock, flags);
> + balloon_page_insert(newpage, mapping, &vb_dev_info->pages);
> + vb_dev_info->isolated_pages--;
> + spin_unlock_irqrestore(&vb_dev_info->pages_lock, flags);
> +
> + /*
> + * It's safe to delete page->lru here because this page is at
> + * an isolated migration list, and this step is expected to happen here
> + */
> + balloon_page_delete(page);
> + err = MIGRATEPAGE_BALLOON_SUCCESS;
> +
> +unlock:
> + mutex_unlock(&vb->lock);
> + return err;
> +}
> +
> +/* define the balloon_mapping->a_ops callback to allow balloon page migration */
> +static const struct address_space_operations virtio_balloon_aops = {
> + .migratepage = virtballoon_migratepage,
> +};
> +#endif /* CONFIG_BALLOON_COMPACTION */
> +
> +static int virtballoon_probe(struct virtio_device *vdev)
> +{
> + struct virtio_balloon *vb;
> + struct address_space *vb_mapping;
> + struct balloon_dev_info *vb_devinfo;
> + __le64 v;
> + int err;
> +
> + virtio_cread(vdev, struct virtio_balloon_config_space, pagesizes, &v);
> + /* FIXME: Support large pages. */
> + if (!(le64_to_cpu(v) & PAGE_SIZE)) {
> + dev_warn(&vdev->dev, "Unacceptable pagesize %llu\n",
> + (long long)le64_to_cpu(v));
> + err = -EINVAL;
> + goto out;
> + }
> + v = cpu_to_le64(PAGE_SIZE);
> + virtio_cwrite(vdev, struct virtio_balloon_config_space, page_size, &v);
> +
> + vdev->priv = vb = kmalloc(sizeof(*vb), GFP_KERNEL);
> + if (!vb) {
> + err = -ENOMEM;
> + goto out;
> + }
> +
> + vb->target = 0;
> + vb->num_pages = 0;
> + mutex_init(&vb->lock);
> + init_waitqueue_head(&vb->wait);
> + vb->vdev = vdev;
> +
> + vb_devinfo = balloon_devinfo_alloc(vb);
> + if (IS_ERR(vb_devinfo)) {
> + err = PTR_ERR(vb_devinfo);
> + goto out_free_vb;
> + }
> +
> + vb_mapping = balloon_mapping_alloc(vb_devinfo,
> +
(balloon_compaction_check()) ?
> +
&virtio_balloon_aops : NULL);
> + if (IS_ERR(vb_mapping)) {
> + /*
> + * IS_ERR(vb_mapping) && PTR_ERR(vb_mapping) == -EOPNOTSUPP
> + * This means !CONFIG_BALLOON_COMPACTION, otherwise we get off.
> + */
> + err = PTR_ERR(vb_mapping);
> + if (err != -EOPNOTSUPP)
> + goto out_free_vb_devinfo;
> + }
> +
> + vb->vb_dev_info = vb_devinfo;
> +
> + err = init_vqs(vb);
> + if (err)
> + goto out_free_vb_mapping;
> +
> + vb->thread = kthread_run(balloon, vb, "vballoon");
> + if (IS_ERR(vb->thread)) {
> + err = PTR_ERR(vb->thread);
> + goto out_del_vqs;
> + }
> +
> + return 0;
> +
> +out_del_vqs:
> + vdev->config->del_vqs(vdev);
> +out_free_vb_mapping:
> + balloon_mapping_free(vb_mapping);
> +out_free_vb_devinfo:
> + balloon_devinfo_free(vb_devinfo);
> +out_free_vb:
> + kfree(vb);
> +out:
> + return err;
> +}
> +
> +/* FIXME: Leave pages alone during suspend, rather than taking them
> + * all back! */
> +static void remove_common(struct virtio_balloon *vb)
> +{
> + /* There might be pages left in the balloon: free them. */
> + while (vb->num_pages)
> + take_from_balloon(vb, vb->num_pages);
> +
> + /* Now we reset the device so we can clean up the queues. */
> + vb->vdev->config->reset(vb->vdev);
> + vb->vdev->config->del_vqs(vb->vdev);
> +}
> +
> +static void virtballoon_remove(struct virtio_device *vdev)
> +{
> + struct virtio_balloon *vb = vdev->priv;
> +
> + kthread_stop(vb->thread);
> + remove_common(vb);
> + balloon_mapping_free(vb->vb_dev_info->mapping);
> + balloon_devinfo_free(vb->vb_dev_info);
> + kfree(vb);
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int virtballoon_freeze(struct virtio_device *vdev)
> +{
> + struct virtio_balloon *vb = vdev->priv;
> +
> + /*
> + * The kthread is already frozen by the PM core before this
> + * function is called.
> + */
> +
> + remove_common(vb);
> + return 0;
> +}
> +
> +static int virtballoon_restore(struct virtio_device *vdev)
> +{
> + return init_vqs(vdev->priv);
> +}
> +#endif
> +
> +static unsigned int features[] = {
> + /* FIXME: Support VIRTIO_BALLOON_F_EXTRA_MEM! */
> +};
> +
> +static struct virtio_driver virtio_balloon_driver = {
> + .feature_table = features,
> + .feature_table_size = ARRAY_SIZE(features),
> + .driver.name = KBUILD_MODNAME,
> + .driver.owner = THIS_MODULE,
> + .id_table = id_table,
> + .probe = virtballoon_probe,
> + .remove = virtballoon_remove,
> +#ifdef CONFIG_PM_SLEEP
> + .freeze = virtballoon_freeze,
> + .restore = virtballoon_restore,
> +#endif
> +};
> +
> +module_virtio_driver(virtio_balloon_driver);
> +MODULE_DEVICE_TABLE(virtio, id_table);
> +MODULE_DESCRIPTION("Virtio balloon driver");
> +MODULE_LICENSE("GPL");
> diff --git a/include/uapi/linux/virtio_balloon.h b/include/uapi/linux/virtio_balloon.h
> index 5e26f61b5df5..cdca2934668a 100644
> --- a/include/uapi/linux/virtio_balloon.h
> +++ b/include/uapi/linux/virtio_balloon.h
> @@ -28,32 +28,45 @@
> #include <linux/virtio_ids.h>
> #include <linux/virtio_config.h>
>
> -/* The feature bitmap for virtio balloon */
> -#define VIRTIO_BALLOON_F_MUST_TELL_HOST 0 /* Tell before reclaiming pages */
> -#define VIRTIO_BALLOON_F_STATS_VQ 1 /* Memory Stats virtqueue */
> -
> -/* Size of a PFN in the balloon interface. */
> -#define VIRTIO_BALLOON_PFN_SHIFT 12
> -
> -struct virtio_balloon_config
> -{
> - /* Number of pages host wants Guest to give up. */
> - __le32 num_pages;
> - /* Number of pages we've actually got in balloon. */
> - __le32 actual;
> +/* This means the balloon can go negative (ie. add memory to system) */
> +#define VIRTIO_BALLOON_F_EXTRA_MEM 0
> +
> +struct virtio_balloon_config_space {
> + /* Set by device: bits indicate what page sizes supported. */
> + __le64 pagesizes;
> + /* Set by driver: only a single bit is set! */
> + __le64 page_size;
> +
> + /* These set by device if VIRTIO_BALLOON_F_EXTRA_MEM. */
> + __le64 extra_mem_start;
> + __le64 extra_mem_end;
> +};
> +
> +struct virtio_balloon_statistic {
> + __le64 tag; /* VIRTIO_BALLOON_S_* */
> + __le64 val;
> };
>
> -#define VIRTIO_BALLOON_S_SWAP_IN 0
/* Amount of memory swapped in */
> -#define VIRTIO_BALLOON_S_SWAP_OUT 1
/* Amount of memory swapped out */
> -#define VIRTIO_BALLOON_S_MAJFLT
2
/* Number of major faults */
> -#define VIRTIO_BALLOON_S_MINFLT
3
/* Number of minor faults */
> -#define VIRTIO_BALLOON_S_MEMFREE 4
/* Total amount of free memory */
> -#define VIRTIO_BALLOON_S_MEMTOT
5
/* Total amount of memory */
> -#define VIRTIO_BALLOON_S_NR
6
> -
> -struct virtio_balloon_stat {
> - __u16 tag;
> - __u64 val;
> -} __attribute__((packed));
> +/* Guest->host command queue. */
> +/* Ask the host for more pages.
> +
Followed by array of 1 or more readable le64 pageaddr's. */
> +#define VIRTIO_BALLOON_GCMD_GET_PAGES ((__le64)0)
> +/* Give the host more pages.
> +
Followed by array of 1 or more readable le64 pageaddr's */
> +#define VIRTIO_BALLOON_GCMD_GIVE_PAGES ((__le64)1)
> +/* Dear host: I need more memory. */
> +#define VIRTIO_BALLOON_GCMD_NEEDMEM ((__le64)2)
> +/* Dear host: here are your stats.
> + * Followed by 0 or more struct virtio_balloon_statistic structs. */
> +#define VIRTIO_BALLOON_GCMD_STATS_REPLY ((__le64)3)
> +
> +/* Host->guest command queue. */
> +/* Followed by s64 of new balloon target size (only negative if
> + * VIRTIO_BALLOON_F_EXTRA_MEM). */
> +#define VIRTIO_BALLOON_HCMD_SET_BALLOON ((__le64)0x8000)
> +/* Ask for statistics */
> +#define VIRTIO_BALLOON_HCMD_GET_STATS ((__le64)0x8001)
> +
> +#include <linux/virtio_balloon_legacy.h>
>
> #endif /* _LINUX_VIRTIO_BALLOON_H */
> diff --git a/include/uapi/linux/virtio_balloon_legacy.h b/include/uapi/linux/virtio_balloon_legacy.h
> new file mode 100644
> index 000000000000..cbf77bc1aee3
> --- /dev/null
> +++ b/include/uapi/linux/virtio_balloon_legacy.h
> @@ -0,0 +1,59 @@
> +#ifndef _LINUX_VIRTIO_BALLOON_LEGACY_H
> +#define _LINUX_VIRTIO_BALLOON_LEGACY_H
> +/* This header is BSD licensed so anyone can use the definitions to implement
> + * compatible drivers/servers.
> + *
> + * 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 IBM 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 IBM 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. */
> +#include <linux/virtio_ids.h>
> +#include <linux/virtio_config.h>
> +
> +/* The feature bitmap for virtio balloon */
> +#define VIRTIO_BALLOON_F_MUST_TELL_HOST 0 /* Tell before reclaiming pages */
> +#define VIRTIO_BALLOON_F_STATS_VQ 1 /* Memory Stats virtqueue */
> +
> +/* Size of a PFN in the balloon interface. */
> +#define VIRTIO_BALLOON_PFN_SHIFT 12
> +
> +struct virtio_balloon_config
> +{
> + /* Number of pages host wants Guest to give up. */
> + __le32 num_pages;
> + /* Number of pages we've actually got in balloon. */
> + __le32 actual;
> +};
> +
> +#define VIRTIO_BALLOON_S_SWAP_IN 0
/* Amount of memory swapped in */
> +#define VIRTIO_BALLOON_S_SWAP_OUT 1
/* Amount of memory swapped out */
> +#define VIRTIO_BALLOON_S_MAJFLT
2
/* Number of major faults */
> +#define VIRTIO_BALLOON_S_MINFLT
3
/* Number of minor faults */
> +#define VIRTIO_BALLOON_S_MEMFREE 4
/* Total amount of free memory */
> +#define VIRTIO_BALLOON_S_MEMTOT
5
/* Total amount of memory */
> +#define VIRTIO_BALLOON_S_NR
6
> +
> +struct virtio_balloon_stat {
> + __u16 tag;
> + __u64 val;
> +} __attribute__((packed));
> +
> +#endif /* _LINUX_VIRTIO_BALLOON_LEGACY_H */
> diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h
> index 284fc3a05f7b..8b5ac0047190 100644
> --- a/include/uapi/linux/virtio_ids.h
> +++ b/include/uapi/linux/virtio_ids.h
> @@ -33,11 +33,12 @@
> #define VIRTIO_ID_BLOCK 2 /* virtio block */
> #define VIRTIO_ID_CONSOLE 3 /* virtio console */
> #define VIRTIO_ID_RNG 4 /* virtio rng */
> -#define VIRTIO_ID_BALLOON 5 /* virtio balloon */
> +#define VIRTIO_ID_BALLOON 5 /* virtio balloon (legacy) */
> #define VIRTIO_ID_RPMSG 7 /* virtio remote processor messaging */
> #define VIRTIO_ID_SCSI 8 /* virtio scsi */
> #define VIRTIO_ID_9P 9 /* 9p virtio console */
> #define VIRTIO_ID_RPROC_SERIAL 11 /* virtio remoteproc serial link */
> #define VIRTIO_ID_CAIF
12 /* Virtio caif */
> +#define VIRTIO_ID_MEMBALLOON
13 /* virtio balloon */
>
> #endif /* _LINUX_VIRTIO_IDS_H */
>
>
>
---------------------------------------------------------------------
> To unsubscribe from this mail list, you must leave the OASIS TC that
> generates this mail. Follow this link to all your TCs in OASIS at:
> https://www.oasis-open.org/apps/org/workgroup/portal/my_workgroups.php