Reviewed-by: Alok Kataria <akat...@vmware.com>
Reviewed-by: Shaileshkumar Jain <saj...@vmware.com>
Signed-off-by: Dmitry Torokhov <dt...@vmware.com>
---
drivers/virtio/virtio_balloon.c | 68 +++++++++++++++++++++++++++++++++++----
include/linux/virtio_balloon.h | 8 +++--
2 files changed, 67 insertions(+), 9 deletions(-)
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index 369f2ee..444c929 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -25,6 +25,8 @@
#include <linux/freezer.h>
#include <linux/delay.h>
+#define VIRTIO_BALLOON_CHUNK_SIZE 256
+
struct virtio_balloon
{
struct virtio_device *vdev;
@@ -46,9 +48,13 @@ struct virtio_balloon
unsigned int num_pages;
struct list_head pages;
+ /* Pages that host refused to lock for one reason or another. */
+ struct list_head refused_pages;
+
/* The array of pfns we tell the Host about. */
unsigned int num_pfns;
- u32 pfns[256];
+ u32 pfns[VIRTIO_BALLOON_CHUNK_SIZE];
+ u8 pfns_status[VIRTIO_BALLOON_CHUNK_SIZE];
/* Memory statistics */
int need_stats_update;
@@ -81,14 +87,21 @@ static void balloon_ack(struct virtqueue *vq)
static void tell_host(struct virtio_balloon *vb, struct virtqueue *vq)
{
- struct scatterlist sg;
+ struct scatterlist sg[2];
+ int n_out = 0;
- sg_init_one(&sg, vb->pfns, sizeof(vb->pfns[0]) * vb->num_pfns);
+ sg_init_table(sg, 2);
+ sg_set_buf(&sg[0], vb->pfns, sizeof(vb->pfns[0]) * vb->num_pfns);
+ sg_set_buf(&sg[1], vb->pfns_status,
+ sizeof(vb->pfns_status[0]) * vb->num_pfns);
init_completion(&vb->acked);
+ if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_HOST_MAY_REFUSE))
+ n_out = 1;
+
/* We should always be able to add one buffer to an empty queue. */
- if (vq->vq_ops->add_buf(vq, &sg, 1, 0, vb) < 0)
+ if (vq->vq_ops->add_buf(vq, sg, 1, n_out, vb) < 0)
BUG();
vq->vq_ops->kick(vq);
@@ -98,6 +111,8 @@ static void tell_host(struct virtio_balloon *vb, struct virtqueue *vq)
static void fill_balloon(struct virtio_balloon *vb, size_t num)
{
+ int i;
+
/* We can only do one array worth at a time. */
num = min(num, ARRAY_SIZE(vb->pfns));
@@ -113,9 +128,7 @@ static void fill_balloon(struct virtio_balloon *vb, size_t num)
break;
}
vb->pfns[vb->num_pfns] = page_to_balloon_pfn(page);
- totalram_pages--;
- vb->num_pages++;
- list_add(&page->lru, &vb->pages);
+ vb->pfns_status[vb->num_pfns] = VIRTIO_BALLOON_PFN_OK;
}
/* Didn't get any? Oh well. */
@@ -123,6 +136,32 @@ static void fill_balloon(struct virtio_balloon *vb, size_t num)
return;
tell_host(vb, vb->inflate_vq);
+
+ for (i = 0; i < vb->num_pfns; i++) {
+ struct page *page = pfn_to_page(vb->pfns[i]);
+ if (vb->pfns_status[i] == VIRTIO_BALLOON_PFN_OK) {
+ list_add(&page->lru, &vb->pages);
+ vb->num_pages++;
+ totalram_pages--;
+ } else {
+ dev_dbg(&vb->vdev->dev,
+ "adding page %p (pfn %x) to refused pages list\n",
+ page, vb->pfns[i]);
+ list_add(&page->lru, &vb->refused_pages);
+ }
+ }
+}
+
+static void release_refused_pages(struct virtio_balloon *vb)
+{
+ struct page *page, *next;
+
+ list_for_each_entry_safe(page, next, &vb->refused_pages, lru) {
+ dev_dbg(&vb->vdev->dev,
+ "releasing previously refused page %p", page);
+ list_del(&page->lru);
+ __free_page(page);
+ }
}
static void release_pages_by_pfn(const u32 pfns[], unsigned int num)
@@ -263,13 +302,26 @@ static int balloon(void *_vballoon)
|| vb->need_stats_update
|| kthread_should_stop()
|| freezing(current));
+
if (vb->need_stats_update)
stats_handle_request(vb);
+
if (diff > 0)
fill_balloon(vb, diff);
else if (diff < 0)
leak_balloon(vb, -diff);
+
update_balloon_size(vb);
+
+ if (diff > 0 &&
+ !list_empty(&vb->refused_pages) &&
+ towards_target(vb) <= 0) {
+ /*
+ * We reached our goal so we now can release
+ * pages that host refused to lock for us.
+ */
+ release_refused_pages(vb);
+ }
}
return 0;
}
@@ -289,6 +341,7 @@ static int virtballoon_probe(struct virtio_device *vdev)
}
INIT_LIST_HEAD(&vb->pages);
+ INIT_LIST_HEAD(&vb->refused_pages);
vb->num_pages = 0;
init_waitqueue_head(&vb->config_change);
vb->vdev = vdev;
@@ -357,6 +410,7 @@ static void __devexit virtballoon_remove(struct virtio_device *vdev)
static unsigned int features[] = {
VIRTIO_BALLOON_F_MUST_TELL_HOST,
VIRTIO_BALLOON_F_STATS_VQ,
+ VIRTIO_BALLOON_F_HOST_MAY_REFUSE,
};
static struct virtio_driver virtio_balloon_driver = {
diff --git a/include/linux/virtio_balloon.h b/include/linux/virtio_balloon.h
index a50ecd1..4baad1c 100644
--- a/include/linux/virtio_balloon.h
+++ b/include/linux/virtio_balloon.h
@@ -6,12 +6,16 @@
#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 */
+#define VIRTIO_BALLOON_F_MUST_TELL_HOST 0 /* Tell before reclaiming pages */
+#define VIRTIO_BALLOON_F_STATS_VQ 1 /* Memory Stats virtqueue */
+#define VIRTIO_BALLOON_F_HOST_MAY_REFUSE 2 /* Host may refuse some pages */
/* Size of a PFN in the balloon interface. */
#define VIRTIO_BALLOON_PFN_SHIFT 12
+#define VIRTIO_BALLOON_PFN_OK 0
+#define VIRTIO_BALLOON_PFN_FAIL 1
+
struct virtio_balloon_config
{
/* Number of pages host wants Guest to give up. */
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majo...@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Signed-off-by: Dmitry Torokhov <dt...@vmware.com>
---
arch/x86/kernel/cpu/vmware.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/arch/x86/kernel/cpu/vmware.c b/arch/x86/kernel/cpu/vmware.c
index 1cbed97..dfdb4db 100644
--- a/arch/x86/kernel/cpu/vmware.c
+++ b/arch/x86/kernel/cpu/vmware.c
@@ -22,6 +22,7 @@
*/
#include <linux/dmi.h>
+#include <linux/module.h>
#include <asm/div64.h>
#include <asm/vmware.h>
#include <asm/x86_init.h>
@@ -101,6 +102,7 @@ int vmware_platform(void)
return 0;
}
+EXPORT_SYMBOL(vmware_platform);
/*
* VMware hypervisor takes care of exporting a reliable TSC to the guest.
Reviewed-by: Alok Kataria <akat...@vmware.com>
Signed-off-by: Dmitry Torokhov <dt...@vmware.com>
---
drivers/misc/Kconfig | 13 +
drivers/misc/Makefile | 1
drivers/misc/vmware_balloon.c | 626 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 640 insertions(+), 0 deletions(-)
create mode 100644 drivers/misc/vmware_balloon.c
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 2191c8d..de7e97e 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -311,6 +311,19 @@ config TI_DAC7512
This driver can also be built as a module. If so, the module
will be calles ti_dac7512.
+config VMWARE_BALLOON
+ tristate "VMware Balloon Driver"
+ depends on VIRTIO && X86
+ help
+ This option enables virtio based transport between VMware hypervisor
+ and virtio_balloon and allows host control memory ballooning within
+ the guest.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vmware_balloon.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 27c4843..7b6f7ee 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -29,3 +29,4 @@ obj-$(CONFIG_C2PORT) += c2port/
obj-$(CONFIG_IWMC3200TOP) += iwmc3200top/
obj-y += eeprom/
obj-y += cb710/
+obj-$(CONFIG_VMWARE_BALLOON) += vmware_balloon.o
diff --git a/drivers/misc/vmware_balloon.c b/drivers/misc/vmware_balloon.c
new file mode 100644
index 0000000..4f56e46
--- /dev/null
+++ b/drivers/misc/vmware_balloon.c
@@ -0,0 +1,626 @@
+/*
+ * VMware Balloon driver (virtio-based).
+ *
+ * Copyright (C) 2008-2010, VMware, Inc. All Rights Reserved.
+ *
+ * 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; version 2 of the License and no 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, GOOD TITLE or
+ * NON INFRINGEMENT. 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.
+ *
+ * Maintained by: Dmitry Torokhov <dt...@vmware.com>
+ *
+ */
+
+//#define DEBUG
+//#define VERBOSE_DEBUG
+
+#include <linux/module.h>
+#include <linux/virtio.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_ids.h>
+#include <linux/virtio_balloon.h>
+#include <asm/vmware.h>
+
+MODULE_AUTHOR("VMware, Inc.");
+MODULE_DESCRIPTION("VMware balloon over virtio driver");
+MODULE_ALIAS("dmi:*:svnVMware*:*");
+MODULE_ALIAS("vmware_balloon");
+MODULE_LICENSE("GPL");
+
+/* ================================================================= */
+
+#define VMW_BALLOON_HV_PORT 0x5670
+#define VMW_BALLOON_HV_MAGIC 0x456c6d6f
+#define VMW_BALLOON_PROTOCOL_VERSION 2
+#define VMW_BALLOON_GUEST_ID 1 /* Linux */
+
+#define VMW_BALLOON_CMD_START 0
+#define VMW_BALLOON_CMD_GET_TARGET 1
+#define VMW_BALLOON_CMD_LOCK 2
+#define VMW_BALLOON_CMD_UNLOCK 3
+#define VMW_BALLOON_CMD_GUEST_ID 4
+
+/* use config value for max balloon size */
+#define BALLOON_MAX_SIZE_USE_CONFIG 0
+
+/* error codes */
+#define VMW_BALLOON_SUCCESS 0
+#define VMW_BALLOON_FAILURE -1
+#define VMW_BALLOON_ERROR_CMD_INVALID 1
+#define VMW_BALLOON_ERROR_PPN_INVALID 2
+#define VMW_BALLOON_ERROR_PPN_LOCKED 3
+#define VMW_BALLOON_ERROR_PPN_UNLOCKED 4
+#define VMW_BALLOON_ERROR_PPN_PINNED 5
+#define VMW_BALLOON_ERROR_PPN_NOTNEEDED 6
+#define VMW_BALLOON_ERROR_RESET 7
+#define VMW_BALLOON_ERROR_BUSY 8
+
+#define VMWARE_BALLOON_CMD(cmd, data, result) \
+({ \
+ unsigned long __stat, __dummy1, __dummy2; \
+ __asm__ __volatile__ ("inl (%%dx)" : \
+ "=a"(__stat), \
+ "=c"(__dummy1), \
+ "=d"(__dummy2), \
+ "=b"(result) : \
+ "0"(VMW_BALLOON_HV_MAGIC), \
+ "1"(VMW_BALLOON_CMD_##cmd), \
+ "2"(VMW_BALLOON_HV_PORT), \
+ "3"(data) : \
+ "memory"); \
+ result &= -1UL; \
+ __stat & -1UL; \
+})
+
+/* ================================================================= */
+
+#define STATS_INC(x)
+
+struct vmballoon_dev {
+ struct virtio_device vdev;
+ struct virtqueue vqs[2];
+ struct virtio_balloon_config config;
+ struct timer_list poll_timer; /* for polling host for target size */
+ struct sysinfo sysinfo;
+ u8 status;
+ void *vb;
+ bool target_current;
+};
+
+static struct vmballoon_dev *to_vmballoon_dev(struct virtio_device *vdev)
+{
+ return container_of(vdev, struct vmballoon_dev, vdev);
+}
+
+static void vmballoon_release_device(struct device *dev)
+{
+ struct virtio_device *vdev =
+ container_of(dev, struct virtio_device, dev);
+ struct vmballoon_dev *bdev = to_vmballoon_dev(vdev);
+
+ dev_vdbg(&bdev->vdev.dev, "executing %s\n", __func__);
+ kfree(bdev);
+}
+
+static void vmballoon_signal_reset(struct vmballoon_dev *bdev)
+{
+ struct virtio_driver *drv = container_of(bdev->vdev.dev.driver,
+ struct virtio_driver, driver);
+ if (!bdev->config.reset_pending) {
+ bdev->config.reset_completed = false;
+ bdev->config.reset_pending = true;
+ /*
+ * Make sure config is written before we signal that
+ * it is changed. We are running on x86 so simple
+ * barrier() is enough.
+ */
+ barrier();
+ if (drv->config_changed)
+ drv->config_changed(&bdev->vdev);
+ }
+}
+
+static bool vmballoon_send_start(struct vmballoon_dev *bdev)
+{
+ unsigned long status, dummy;
+
+ STATS_INC(bdev->stats.start);
+
+ status = VMWARE_BALLOON_CMD(START, VMW_BALLOON_PROTOCOL_VERSION, dummy);
+ if (status == VMW_BALLOON_SUCCESS)
+ return true;
+
+ dev_vdbg(&bdev->vdev.dev, "%s - failed, hv returns %ld\n",
+ __func__, status);
+ STATS_INC(bdev->stats.start_fail);
+ return false;
+}
+
+static bool vmballoon_check_status(struct vmballoon_dev *bdev,
+ unsigned long status)
+{
+ switch (status) {
+ case VMW_BALLOON_SUCCESS:
+ return true;
+
+ case VMW_BALLOON_ERROR_RESET:
+ vmballoon_signal_reset(bdev);
+ /* fall through */
+
+ default:
+ return false;
+ }
+}
+
+static bool vmballoon_send_guest_id(struct vmballoon_dev *bdev)
+{
+ unsigned long status, dummy;
+
+ status = VMWARE_BALLOON_CMD(GUEST_ID, VMW_BALLOON_GUEST_ID, dummy);
+
+ STATS_INC(bdev->stats.guestType);
+
+ if (vmballoon_check_status(bdev, status))
+ return true;
+
+ dev_vdbg(&bdev->vdev.dev, "%s - failed, hv returns %ld\n",
+ __func__, status);
+ STATS_INC(bdev->stats.guest_type_fail);
+ return false;
+}
+
+static bool vmballoon_send_get_target(struct vmballoon_dev *bdev,
+ u32 *new_target)
+{
+ unsigned long status;
+ unsigned long target;
+ unsigned long limit;
+ u32 limit32;
+
+ if (bdev->config.reset_pending)
+ return false;
+
+ /*
+ * si_meminfo() is cheap. Moreover, we want to provide dynamic
+ * max balloon size later. So let us call si_meminfo() every
+ * iteration.
+ */
+ si_meminfo(&bdev->sysinfo);
+ limit = bdev->sysinfo.totalram;
+
+ /* Ensure limit fits in 32-bits */
+ limit32 = (u32)limit;
+ if (limit != limit32)
+ return false;
+
+ /* update stats */
+ STATS_INC(bdev->stats.target);
+
+ status = VMWARE_BALLOON_CMD(GET_TARGET, limit, target);
+ if (vmballoon_check_status(bdev, status)) {
+ *new_target = target;
+ return true;
+ }
+
+ dev_vdbg(&bdev->vdev.dev, "%s - failed, hv returns %ld\n",
+ __func__, status);
+ STATS_INC(bdev->stats.target_fail);
+ return false;
+}
+
+static bool vmballoon_send_lock_page(struct vmballoon_dev *bdev,
+ unsigned long pfn)
+{
+ unsigned long status, dummy;
+ u32 pfn32;
+
+ pfn32 = (u32)pfn;
+ if (pfn32 != pfn)
+ return false;
+
+ if (bdev->config.reset_pending)
+ return false;
+
+ STATS_INC(bdev->stats.lock);
+
+ status = VMWARE_BALLOON_CMD(LOCK, pfn, dummy);
+ if (vmballoon_check_status(bdev, status))
+ return true;
+
+ dev_vdbg(&bdev->vdev.dev, "%s - ppn %lx, hv returns %ld\n",
+ __func__, pfn, status);
+ STATS_INC(bdev->stats.lock_fail);
+ return false;
+}
+
+static bool vmballoon_send_unlock_page(struct vmballoon_dev *bdev,
+ unsigned long pfn)
+{
+ unsigned long status, dummy;
+ u32 pfn32;
+
+ pfn32 = (u32)pfn;
+ if (pfn32 != pfn)
+ return false;
+
+ if (bdev->config.reset_pending)
+ return false;
+
+ STATS_INC(bdev->stats.unlock);
+
+ status = VMWARE_BALLOON_CMD(UNLOCK, pfn, dummy);
+ if (vmballoon_check_status(bdev, status))
+ return true;
+
+ dev_vdbg(&bdev->vdev.dev, "%s - ppn %lx, hv returns %ld\n",
+ __func__, pfn, status);
+ STATS_INC(bdev->stats.unlock_fail);
+ return false;
+}
+
+static void vmballoon_do_reset(struct vmballoon_dev *bdev)
+{
+ dev_vdbg(&bdev->vdev.dev, "executing %s\n", __func__);
+
+ /* send start command */
+ if (vmballoon_send_start(bdev)) {
+ bdev->config.reset_pending = false;
+ vmballoon_send_guest_id(bdev);
+ }
+}
+
+static void vmballoon_poll_host(unsigned long data)
+{
+ struct vmballoon_dev *bdev = (struct vmballoon_dev *) data;
+ struct virtio_driver *drv = container_of(bdev->vdev.dev.driver,
+ struct virtio_driver, driver);
+ u32 new_target;
+
+ STATS_INC(bdev->stats.timer);
+
+ if (bdev->config.reset_pending) {
+ if (!bdev->config.reset_completed)
+ goto out;
+
+ vmballoon_do_reset(bdev);
+ }
+
+ if (vmballoon_send_get_target(bdev, &new_target)) {
+ bdev->target_current = true;
+ if (new_target != le32_to_cpu(bdev->config.num_pages)) {
+ dev_dbg(&bdev->vdev.dev,
+ "%s: target changed (was %d, now %d pages)\n",
+ __func__,
+ le32_to_cpu(bdev->config.num_pages),
+ new_target);
+ bdev->config.num_pages = cpu_to_le32(new_target);
+ /*
+ * Make sure new target is written before we signal
+ * that it is changed. We are running on x86 so simple
+ * barrier() is enough.
+ */
+ barrier();
+ if (drv->config_changed)
+ drv->config_changed(&bdev->vdev);
+ }
+ }
+
+out:
+ mod_timer(&bdev->poll_timer, round_jiffies(jiffies + HZ));
+}
+
+/* Virtqueue config operations */
+
+static int vmballoon_vq_add_buf(struct virtqueue *vq,
+ struct scatterlist sg[],
+ unsigned int out_num,
+ unsigned int in_num,
+ void *data)
+{
+ struct vmballoon_dev *bdev = to_vmballoon_dev(vq->vdev);
+ bool success;
+ struct scatterlist *sg_tmp;
+ struct scatterlist *sg_out;
+ u32 *pfns;
+ unsigned int npfns;
+ struct scatterlist *sg_in = NULL;
+ unsigned int sg_in_idx, sg_in_size;
+ u8 *pfns_state;
+ unsigned int total_pfns = 0, refused_pfns = 0;
+ unsigned int i, j;
+
+ dev_dbg(&bdev->vdev.dev, "executing %s for %s\n", __func__, vq->name);
+
+ if (out_num == 0 || in_num == 0)
+ return -EINVAL;
+
+ for_each_sg(sg, sg_tmp, out_num + 1, i)
+ sg_in = sg_tmp;
+ BUG_ON(sg_in == NULL);
+
+ pfns_state = sg_virt(sg_in);
+ sg_in_idx = 0;
+ sg_in_size = sg_in->length / sizeof(pfns_state[0]);
+
+ for_each_sg(sg, sg_out, out_num, i) {
+
+ pfns = sg_virt(sg_out);
+ npfns = sg_out->length / sizeof(pfns[0]);
+ total_pfns += npfns;
+
+ for (j = 0; j < npfns; j++) {
+
+ success = (unsigned long)vq->priv == 0 ?
+ vmballoon_send_lock_page(bdev, pfns[j]) :
+ vmballoon_send_unlock_page(bdev, pfns[j]);
+
+ if (sg_in_idx >= sg_in_size) {
+ sg_in = sg_next(sg_in);
+ BUG_ON(sg_in == NULL);
+
+ pfns_state = sg_virt(sg_in);
+ sg_in_idx = 0;
+ sg_in_size = sg_in->length /
+ sizeof(pfns_state[0]);
+ }
+
+ if (success) {
+ pfns_state[sg_in_idx++] = VIRTIO_BALLOON_PFN_OK;
+ } else {
+ pfns_state[sg_in_idx++] = VIRTIO_BALLOON_PFN_FAIL;
+ refused_pfns++;
+ }
+ }
+ }
+
+ bdev->vb = data;
+ bdev->target_current = false;
+
+ dev_dbg(&vq->vdev->dev, "done %s (%d requested, %d refused)\n",
+ __func__, total_pfns, refused_pfns);
+ return 0;
+}
+
+static void *vmballoon_vq_get_buf(struct virtqueue *vq, unsigned int *len)
+{
+ struct vmballoon_dev *bdev = to_vmballoon_dev(vq->vdev);
+
+ dev_vdbg(&vq->vdev->dev, "executing %s\n", __func__);
+
+ return bdev->vb;
+}
+
+static void vmballoon_vq_kick(struct virtqueue *vq)
+{
+ dev_vdbg(&vq->vdev->dev, "executing %s\n", __func__);
+
+ /*
+ * We contact hypervisor in vmballoon_vq_add_buf() so here
+ * we just signal the completion.
+ */
+ vq->callback(vq);
+}
+
+static void vmballoon_vq_disable_cb(struct virtqueue *vq)
+{
+ dev_vdbg(&vq->vdev->dev, "executing %s\n", __func__);
+}
+
+static bool vmballoon_vq_enable_cb(struct virtqueue *vq)
+{
+ dev_vdbg(&vq->vdev->dev, "executing %s\n", __func__);
+ return false;
+}
+
+static struct virtqueue_ops vmballoon_vq_ops = {
+ .add_buf = vmballoon_vq_add_buf,
+ .get_buf = vmballoon_vq_get_buf,
+ .kick = vmballoon_vq_kick,
+ .disable_cb = vmballoon_vq_disable_cb,
+ .enable_cb = vmballoon_vq_enable_cb,
+};
+
+/* Virtdevice config operations */
+
+static u32 vmballoon_get_features(struct virtio_device *vdev)
+{
+ unsigned long features;
+
+ dev_vdbg(&vdev->dev, "executing %s\n", __func__);
+
+ __set_bit(VIRTIO_BALLOON_F_MUST_TELL_HOST, &features);
+ __set_bit(VIRTIO_BALLOON_F_HOST_MAY_REFUSE, &features);
+
+ return features;
+}
+
+static void vmballoon_finalize_features(struct virtio_device *vdev)
+{
+ dev_vdbg(&vdev->dev, "executing %s\n", __func__);
+}
+
+static void vmballoon_get_target(struct vmballoon_dev *bdev)
+{
+ u32 new_target;
+
+ if (bdev->target_current) {
+ dev_vdbg(&bdev->vdev.dev,
+ "%s: target is current, skipping host query\n",
+ __func__);
+ } else if (vmballoon_send_get_target(bdev, &new_target)) {
+ bdev->target_current = true;
+ if (le32_to_cpu(bdev->config.num_pages) != new_target) {
+ dev_dbg(&bdev->vdev.dev,
+ "%s: target now is %d pages (%dM)\n",
+ __func__, new_target, new_target >> 8);
+ bdev->config.num_pages = cpu_to_le32(new_target);
+ }
+ }
+}
+
+static void vmballoon_get(struct virtio_device *vdev, unsigned offset,
+ void *buf, unsigned len)
+{
+ struct vmballoon_dev *bdev = to_vmballoon_dev(vdev);
+
+ dev_vdbg(&vdev->dev, "%s: reading %d bytes at offset %d\n",
+ __func__, len, offset);
+
+ if (offset == offsetof(struct virtio_balloon_config, num_pages))
+ vmballoon_get_target(bdev);
+
+ memcpy(buf, (u8 *)&bdev->config + offset, len);
+}
+
+static void vmballoon_set(struct virtio_device *vdev, unsigned offset,
+ const void *buf, unsigned len)
+{
+ struct vmballoon_dev *bdev = to_vmballoon_dev(vdev);
+
+ dev_dbg(&vdev->dev, "%s: writing %d bytes at offset %d\n",
+ __func__, len, offset);
+
+ memcpy((u8 *)&bdev->config + offset, buf, len);
+}
+
+static u8 vmballoon_get_status(struct virtio_device *vdev)
+{
+ struct vmballoon_dev *bdev = to_vmballoon_dev(vdev);
+
+ dev_vdbg(&bdev->vdev.dev, "executing %s\n", __func__);
+ return bdev->status;
+}
+
+static void vmballoon_set_status(struct virtio_device *vdev, u8 status)
+{
+ struct vmballoon_dev *bdev = to_vmballoon_dev(vdev);
+
+ dev_vdbg(&bdev->vdev.dev, "executing %s\n", __func__);
+ bdev->status = status;
+}
+
+static int vmballoon_find_vqs(struct virtio_device *vdev, unsigned nvqs,
+ struct virtqueue *vqs[],
+ vq_callback_t *callbacks[],
+ const char *names[])
+{
+ struct vmballoon_dev *bdev = to_vmballoon_dev(vdev);
+ struct virtqueue *vq;
+ long i;
+
+ dev_vdbg(&bdev->vdev.dev, "executing %s\n", __func__);
+
+ if (nvqs != 2)
+ return -EINVAL;
+
+ for (i = 0; i < nvqs; i++) {
+ vq = &bdev->vqs[i];
+
+ memset(vq, 0, sizeof(*vq));
+ INIT_LIST_HEAD(&vq->list);
+ vq->callback = callbacks[i];
+ vq->name = names[i];
+ vq->vdev = vdev;
+ vq->vq_ops = &vmballoon_vq_ops;
+ vq->priv = (void *) i;
+
+ vqs[i] = vq;
+ }
+
+ /*
+ * Reset and start balloon interface
+ */
+ vmballoon_do_reset(bdev);
+
+ /*
+ * Start polling host for the target balloon size
+ */
+ mod_timer(&bdev->poll_timer, round_jiffies(jiffies + HZ));
+
+ return 0;
+}
+
+static void vmballoon_del_vqs(struct virtio_device *vdev)
+{
+ /*
+ * We are using statically allocated virtqueues, no need to do
+ * anything here.
+ */
+}
+
+static void vmballoon_reset(struct virtio_device *vdev)
+{
+ struct vmballoon_dev *bdev = to_vmballoon_dev(vdev);
+
+ dev_vdbg(&bdev->vdev.dev, "executing %s", __func__);
+
+ /* Stop host polling */
+ del_timer_sync(&bdev->poll_timer);
+
+ /* Virtio core expects us to reset the status */
+ bdev->status = 0;
+}
+
+static struct virtio_config_ops vmballoon_config_ops = {
+ .get_features = vmballoon_get_features,
+ .finalize_features = vmballoon_finalize_features,
+ .get = vmballoon_get,
+ .set = vmballoon_set,
+ .get_status = vmballoon_get_status,
+ .set_status = vmballoon_set_status,
+ .reset = vmballoon_reset,
+ .find_vqs = vmballoon_find_vqs,
+ .del_vqs = vmballoon_del_vqs,
+};
+
+static struct vmballoon_dev *vmballoon_dev;
+
+static int __init vmballoon_init(void)
+{
+ struct virtio_device *vdev;
+ int err;
+
+ /*
+ * Check if we are running on VMware's hypervisor and bail out
+ * if we are not.
+ */
+ if (!vmware_platform())
+ return -ENODEV;
+
+ vmballoon_dev = kzalloc(sizeof(struct vmballoon_dev), GFP_KERNEL);
+ if (!vmballoon_dev)
+ return -ENOMEM;
+
+ setup_timer(&vmballoon_dev->poll_timer,
+ vmballoon_poll_host, (unsigned long)vmballoon_dev);
+
+ vdev = &vmballoon_dev->vdev;
+ vdev->dev.release = vmballoon_release_device;
+ vdev->id.device = VIRTIO_ID_BALLOON;
+ vdev->config = &vmballoon_config_ops;
+
+ err = register_virtio_device(&vmballoon_dev->vdev);
+ if (err) {
+ kfree(&vmballoon_dev->vdev);
+ return err;
+ }
+
+ return 0;
+}
+module_init(vmballoon_init)
+
+static void __exit vmballoon_exit(void)
+{
+ unregister_virtio_device(&vmballoon_dev->vdev);
+}
+module_exit(vmballoon_exit)
Please update the virtio pci spec (can't find the URL - Rusty?) to
reflect the protocol change.
--
Do not meddle in the internals of kernels, for they are subtle and quick to panic.
This is a little wierd since it's specific to the balloon driver. The
virtio design is to make transport and device orthogonal so we could
mix'n'match any driver with any transport, but this will only work with
the balloon driver. It's also 640 lines while the original balloon
driver is ~300. Some of that is because you're emulating a queue that
doesn't really exist, IIUC.
So while I'd love to see other hypervisors adopt virtio, I think in this
case it makes more sense to have your own balloon driver.
--
Do not meddle in the internals of kernels, for they are subtle and quick to panic.
--
On Sat, Mar 20, 2010 at 06:10:57AM -0700, Avi Kivity wrote:
> On 03/19/2010 10:47 PM, Dmitry Torokhov wrote:
> > This is a virtio-based transport between VMware's hypervisor
> > and virtio_ballon driver that allows host control guest memory
> > ballooning.
> >
> >
>
> This is a little wierd since it's specific to the balloon driver. The
> virtio design is to make transport and device orthogonal so we could
> mix'n'match any driver with any transport, but this will only work with
> the balloon driver. It's also 640 lines while the original balloon
> driver is ~300. Some of that is because you're emulating a queue that
> doesn't really exist, IIUC.
Yes, this is corect. We have a dedicated balloon interface, so, while
trying to work with existing kernel infrastructure, we had to emulate
a virtual queue.
>
> So while I'd love to see other hypervisors adopt virtio, I think in this
> case it makes more sense to have your own balloon driver.
>
Fair enough. If this is a common sentiment we'll gladly prepare our
standalone driver for submission.
Thanks.
--
Dmitry
It's also possible to split out a "balloon core" that can be shared. In
this case it isn't worth it however as there any user visible interfaces
and the shared code would amount to couple dozen lines or so.
--
error compiling committee.c: too many arguments to function