Hi,

Here is a updated version:
- fixes panic when host requests more memory to deflate than guest has
- some cleanup

Index: files.pci
===================================================================
RCS file: /cvs/src/sys/dev/pci/files.pci,v
retrieving revision 1.289
diff -u -r1.289 files.pci
--- files.pci   29 Oct 2012 18:36:42 -0000      1.289
+++ files.pci   6 Nov 2012 10:34:22 -0000
@@ -837,3 +837,7 @@
 device vioblk: scsi
 attach vioblk at virtio
 file   dev/pci/vioblk.c                vioblk
+
+device viomb
+attach viomb at virtio
+file   dev/pci/viomb.c                 viomb
Index: GENERIC
===================================================================
RCS file: /cvs/src/sys/arch/i386/conf/GENERIC,v
retrieving revision 1.741
diff -u -r1.741 GENERIC
--- GENERIC     8 Oct 2012 17:26:02 -0000       1.741
+++ GENERIC     6 Nov 2012 10:36:25 -0000
@@ -799,3 +799,4 @@
 virtio*                at pci?         # Virtio PCI device
 vioblk*                at virtio?      # Virtio block device
 vio*           at virtio?      # Virtio network device
+viomb*         at virtio?      # Virtio memory ballooning device
--- /dev/null   Thu Nov  8 11:03:03 2012
+++ sys/dev/pci/viomb.c Thu Nov  8 11:02:31 2012
@@ -0,0 +1,447 @@
+/* $OpenBSD$    */
+/* $NetBSD: viomb.c,v 1.1 2011/10/30 12:12:21 hannken Exp $     */
+/*
+ * Copyright (c) 2012 Dinar Talypov <di...@i-nk.ru>
+ * Copyright (c) 2010 Minoura Makoto.
+ * 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.
+ *
+ * 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.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/workq.h>
+#include <sys/stdint.h>
+#include <uvm/uvm.h>
+#include <dev/pci/pcidevs.h>
+#include <dev/pci/pcivar.h>
+
+#include <dev/pci/virtioreg.h>
+#include <dev/pci/virtiovar.h>
+
+#define        DEVNAME(sc)     sc->sc_dev.dv_xname
+
+#if VIRTIO_DEBUG
+#define VIOMBDEBUG(sc, format, args...)        \
+               do { printf("%s: " format, sc->sc_dev.dv_xname, ##args);} \
+               while (0)
+#else
+#define VIOMBDEBUG(...)
+#endif
+
+/* flags used to specify kind of operation,
+ * actually should be moved to virtiovar.h
+ */
+#define VRING_READ             0
+#define VRING_WRITE            1
+
+/* notify or don't notify */
+#define VRING_NO_NOTIFY                0
+#define VRING_NOTIFY           1
+
+/* Configuration registers */
+#define VIRTIO_BALLOON_CONFIG_NUM_PAGES        0       /* 32bit */
+#define VIRTIO_BALLOON_CONFIG_ACTUAL   4       /* 32bit */
+
+/* Feature bits */
+#define VIRTIO_BALLOON_F_MUST_TELL_HOST (1<<0)
+#define VIRTIO_BALLOON_F_STATS_VQ      (1<<1)
+
+static const struct virtio_feature_name viomb_feature_names[] = {
+       {VIRTIO_BALLOON_F_MUST_TELL_HOST, "TellHost"},
+       {VIRTIO_BALLOON_F_STATS_VQ, "StatVQ"},
+       {0, NULL}
+};
+#define PGS_PER_REQ            256     /* 1MB, 4KB/page */
+#define VQ_INFLATE     0
+#define VQ_DEFLATE     1
+
+struct balloon_req {
+       bus_dmamap_t    bl_dmamap;
+       struct pglist   bl_pglist;
+       int             bl_nentries;
+       uint32_t        bl_pages[PGS_PER_REQ];
+};
+
+struct viomb_softc {
+       struct device           sc_dev;
+       struct virtio_softc     *sc_virtio;
+
+       struct virtqueue        sc_vq[2];
+       u_int32_t               sc_npages;
+       u_int32_t               sc_actual;
+       u_int32_t               sc_balloon_size;
+       int                     sc_inflight;
+       struct balloon_req      sc_req;
+       struct pglist           sc_balloon_pages;
+       int                     sc_flags;
+#define INFALTE_DONE   0x01
+#define DEFLATE_DONE   0x02
+};
+
+int             viomb_match(struct device *, void *, void *);
+void            viomb_attach(struct device *, struct device *, void *);
+int             viomb_intr(void *);
+void            viomb_inflate(void *, void *);
+void            viomb_deflate(void *, void *);
+int             viomb_config_change(struct virtio_softc *);
+void            viomb_read_config(struct viomb_softc *);
+int             inflate_intr(struct virtqueue *);
+int             deflate_intr(struct virtqueue *);
+
+struct cfattach viomb_ca = {
+       sizeof(struct viomb_softc),
+       viomb_match,
+       viomb_attach
+};
+
+struct cfdriver viomb_cd = {
+       NULL,
+       "viomb",
+       DV_DULL
+};
+
+struct workq   *viomb_workq;
+
+int
+viomb_match(struct device * parent, void *match, void *aux)
+{
+       struct virtio_softc *va = aux;
+       if (va->sc_childdevid == PCI_PRODUCT_VIRTIO_BALLOON)
+               return 1;
+       return 0;
+}
+
+void
+viomb_attach(struct device * parent, struct device * self, void *aux)
+{
+       struct viomb_softc *sc = (struct viomb_softc *)self;
+       struct virtio_softc *vsc = (struct virtio_softc *)parent;
+       u_int32_t       features;
+       int             i;
+
+       if (vsc->sc_child != NULL) {
+               printf("child already attached for %s; something wrong...\n",
+                      parent->dv_xname);
+               return;
+       }
+       sc->sc_virtio = vsc;
+       vsc->sc_vqs = &sc->sc_vq[VQ_INFLATE];
+       vsc->sc_nvqs = 0;
+       vsc->sc_child = self;
+       vsc->sc_ipl = IPL_VM;
+       vsc->sc_config_change = viomb_config_change;
+       vsc->sc_intrhand = virtio_vq_intr;
+
+       /* negotiate features */
+       features = VIRTIO_F_RING_INDIRECT_DESC;
+       features = virtio_negotiate_features(vsc, features,
+                   viomb_feature_names);
+
+       if ((virtio_alloc_vq(vsc, &sc->sc_vq[VQ_INFLATE], VQ_INFLATE,
+                   sizeof(u_int32_t) * PGS_PER_REQ, 1, "inflate") != 0))
+               goto err;
+       vsc->sc_nvqs++;
+       if ((virtio_alloc_vq(vsc, &sc->sc_vq[VQ_DEFLATE], VQ_DEFLATE,
+                   sizeof(u_int32_t) * PGS_PER_REQ, 1, "deflate") != 0))
+               goto err;
+       vsc->sc_nvqs++;
+
+       sc->sc_vq[VQ_INFLATE].vq_done = inflate_intr;
+       sc->sc_vq[VQ_DEFLATE].vq_done = deflate_intr;
+       virtio_start_vq_intr(vsc, &sc->sc_vq[VQ_INFLATE]);
+       virtio_start_vq_intr(vsc, &sc->sc_vq[VQ_DEFLATE]);
+
+       viomb_read_config(sc);
+       virtio_write_device_config_4(vsc, VIRTIO_BALLOON_CONFIG_ACTUAL, 1);
+       sc->sc_inflight = 0;
+       TAILQ_INIT(&sc->sc_balloon_pages);
+
+       if (bus_dmamap_create(vsc->sc_dmat, sizeof(u_int32_t) * PGS_PER_REQ,
+                             1, sizeof(u_int32_t) * PGS_PER_REQ, 0,
+                             BUS_DMA_NOWAIT, &sc->sc_req.bl_dmamap)) {
+               printf("%s: dmamap creation failed.\n", DEVNAME(sc));
+               goto err;
+       }
+       if (bus_dmamap_load(vsc->sc_dmat, sc->sc_req.bl_dmamap,
+                           &sc->sc_req.bl_pages[0],
+                           sizeof(uint32_t) * PGS_PER_REQ,
+                           NULL, BUS_DMA_NOWAIT)) {
+               printf("%s: dmamap load failed.\n", DEVNAME(sc));
+               goto err_dmamap;
+       }
+
+       viomb_workq = workq_create("viomb", 1, IPL_VM);
+       if (viomb_workq == NULL)
+               goto err_dmamap;
+       printf("\n");
+       return;
+
+err_dmamap:
+       bus_dmamap_destroy(vsc->sc_dmat, sc->sc_req.bl_dmamap);
+err:
+       for (i = 0; i < vsc->sc_nvqs; i++)
+               virtio_free_vq(vsc, &sc->sc_vq[i]);
+       vsc->sc_nvqs = 0;
+       vsc->sc_child = VIRTIO_CHILD_ERROR;
+       return;
+}
+
+/*
+ * Config change
+ */
+int
+viomb_config_change(struct virtio_softc * vsc)
+{
+       struct viomb_softc *sc = (struct viomb_softc *)vsc->sc_child;
+       u_int32_t       old;
+
+       old = sc->sc_npages;
+       viomb_read_config(sc);
+       if (sc->sc_npages > old) {
+               workq_add_task(viomb_workq, 0, viomb_inflate, sc, NULL);
+               VIOMBDEBUG(sc, "inflating balloon from %u to %u.\n",
+                        old, sc->sc_npages);
+       } else if (sc->sc_npages < old) {
+               workq_add_task(viomb_workq, 0, viomb_deflate, sc, NULL);
+               VIOMBDEBUG(sc, "deflating balloon from %u to %u.\n",
+                        old, sc->sc_npages);
+       }
+       return 0;
+}
+
+/* inflate task */
+void
+viomb_inflate(void *arg1, void *arg2)
+{
+       struct viomb_softc *sc = (struct viomb_softc *) arg1;
+       struct virtio_softc *vsc = (struct virtio_softc *) sc->sc_virtio;
+       int             i, slot;
+       u_int32_t       nvpages, nhpages;
+       struct balloon_req *b;
+       struct vm_page *p;
+       struct virtqueue *vq = &sc->sc_vq[0];
+       int             error, j, count;
+
+       nvpages = sc->sc_npages - sc->sc_actual;
+       count = nvpages / PGS_PER_REQ;
+
+       for (j = 0; j < count; j++) {
+               nvpages = PGS_PER_REQ;
+               nhpages = nvpages * VIRTIO_PAGE_SIZE / PAGE_SIZE;
+
+               b = &sc->sc_req;
+               if ((error = uvm_pglistalloc(nhpages * PAGE_SIZE, 0,
+                                            dma_constraint.ucr_high,
+                                            0, 0, &b->bl_pglist, nhpages,
+                                            UVM_PLA_NOWAIT))) {
+                       printf("%s unable to allocate %lu physmem pages,"
+                        "error %d\n", DEVNAME(sc), nhpages, error);
+                       return; /* just return, do nothing */
+               }
+               b->bl_nentries = nvpages;
+               i = 0;
+               TAILQ_FOREACH(p, &b->bl_pglist, pageq)
+                       b->bl_pages[i++] = p->phys_addr / VIRTIO_PAGE_SIZE;
+
+               KASSERT(i == nvpages);
+
+               if ((virtio_enqueue_prep(vq, &slot)) > 0) {
+                       printf("%s:virtio_enqueue_prep() vq_num %d\n",
+                              DEVNAME(sc), vq->vq_num);
+                       goto err;
+               }
+               if (virtio_enqueue_reserve(vq, slot, 1)) {
+                       printf("%s:virtio_alloc(inf) vq_num %d\n",
+                              DEVNAME(sc), vq->vq_num);
+                       goto err;
+               }
+               bus_dmamap_sync(vsc->sc_dmat, b->bl_dmamap, 0,
+                        sizeof(u_int32_t) * nvpages, BUS_DMASYNC_PREWRITE);
+               virtio_enqueue(vq, slot, b->bl_dmamap, VRING_READ);
+               virtio_enqueue_commit(vsc, vq, slot, VRING_NOTIFY);
+               sc->sc_inflight += nvpages;
+       }
+       return;
+err:
+       uvm_pglistfree(&b->bl_pglist);
+       return;
+}
+
+/* deflate task */
+void
+viomb_deflate(void *arg1, void *arg2)
+{
+       struct viomb_softc *sc = (struct viomb_softc *) arg1;
+       struct virtio_softc *vsc = (struct virtio_softc *) sc->sc_virtio;
+       int             i, slot;
+       u_int64_t       nvpages, nhpages;
+       struct balloon_req *b;
+       struct vm_page *p;
+       struct virtqueue *vq = &sc->sc_vq[1];
+       int             count, j;
+
+       nvpages = (sc->sc_actual + sc->sc_inflight) - sc->sc_npages;
+       b = &sc->sc_req;
+       count = nvpages / PGS_PER_REQ;
+
+       for (j = 0; j < count; j++) {
+               nvpages = PGS_PER_REQ;
+               nhpages = nvpages * VIRTIO_PAGE_SIZE / PAGE_SIZE;
+               b->bl_nentries = nvpages;
+               TAILQ_INIT(&b->bl_pglist);
+               for (i = 0; i < nhpages; i++) {
+                       p = TAILQ_FIRST(&sc->sc_balloon_pages);
+                       if (p == NULL){
+                           b->bl_nentries = (i - 1)*PAGE_SIZE/VIRTIO_PAGE_SIZE;
+                           break;
+                       }
+                       TAILQ_REMOVE(&sc->sc_balloon_pages, p, pageq);
+                       TAILQ_INSERT_TAIL(&b->bl_pglist, p, pageq);
+                       b->bl_pages[i] = p->phys_addr / VIRTIO_PAGE_SIZE;
+               }
+
+               if (virtio_enqueue_prep(vq, &slot)) {
+                       printf("%s:virtio_get_slot(def) vq_num %d\n",
+                              DEVNAME(sc), vq->vq_num);
+                       goto err;
+               }
+               if (virtio_enqueue_reserve(vq, slot, 1)) {
+                       printf("%s:virtio_alloc(def) vq_num %d\n",
+                              DEVNAME(sc), vq->vq_num);
+                       goto err;
+               }
+               bus_dmamap_sync(vsc->sc_dmat, b->bl_dmamap, 0,
+                               sizeof(u_int32_t) * nvpages,
+                               BUS_DMASYNC_PREWRITE);
+               virtio_enqueue(vq, slot, b->bl_dmamap, VRING_READ);
+               virtio_enqueue_commit(vsc, vq, slot, VRING_NOTIFY);
+               sc->sc_inflight -= nvpages;
+
+               if (!(vsc->sc_features & VIRTIO_BALLOON_F_MUST_TELL_HOST))
+                       uvm_pglistfree(&b->bl_pglist);
+       }
+       return;
+err:
+       TAILQ_FOREACH_REVERSE(p, &b->bl_pglist, pglist, pageq) {
+               TAILQ_REMOVE(&b->bl_pglist, p, pageq);
+               TAILQ_INSERT_HEAD(&sc->sc_balloon_pages, p, pageq);
+       }
+       return;
+}
+
+void
+viomb_read_config(struct viomb_softc * sc)
+{
+       u_int32_t       reg;
+       struct virtio_softc *vsc = (struct virtio_softc *) sc->sc_virtio;
+
+       /* these values are explicitly specified as little-endian */
+       reg = virtio_read_device_config_4(vsc,
+                                         VIRTIO_BALLOON_CONFIG_NUM_PAGES);
+       sc->sc_npages = letoh32(reg);
+       reg = virtio_read_device_config_4(vsc,
+                                         VIRTIO_BALLOON_CONFIG_ACTUAL);
+       sc->sc_actual = letoh32(reg);
+       VIOMBDEBUG(sc, "sc->sc_npages %u, sc->sc_actual %u\n",
+                sc->sc_npages, sc->sc_actual);
+}
+
+/*
+ * interrupt handling for vq's
+ */
+
+int
+inflate_intr(struct virtqueue * vq)
+{
+       struct virtio_softc *vsc = vq->vq_owner;
+       struct viomb_softc *sc = (struct viomb_softc *) vsc->sc_child;
+       struct balloon_req *b;
+       int             r, slot;
+       u_int64_t       nvpages;
+       struct vm_page *p;
+
+       r = virtio_dequeue(vsc, vq, &slot, NULL);
+       if (r != 0) {
+               printf("%s: inflate dequeue failed, errno %d.\n",
+                      DEVNAME(sc), r);
+               return 1;
+       }
+       virtio_dequeue_commit(vq, slot);
+
+       b = &sc->sc_req;
+       nvpages = b->bl_nentries;
+       bus_dmamap_sync(vsc->sc_dmat, b->bl_dmamap,
+                       offsetof(struct balloon_req, bl_pages),
+                       sizeof(u_int32_t) * nvpages,
+                       BUS_DMASYNC_POSTWRITE);
+       while (!TAILQ_EMPTY(&b->bl_pglist)) {
+               p = TAILQ_FIRST(&b->bl_pglist);
+               TAILQ_REMOVE(&b->bl_pglist, p, pageq);
+               TAILQ_INSERT_TAIL(&sc->sc_balloon_pages, p, pageq);
+       }
+       sc->sc_inflight -= nvpages;
+       VIOMBDEBUG(sc, "updating sc->sc_actual from %lu to %lu\n",
+                sc->sc_actual, sc->sc_actual + nvpages);
+       virtio_write_device_config_4(vsc, VIRTIO_BALLOON_CONFIG_ACTUAL,
+                                    sc->sc_actual + nvpages);
+       viomb_read_config(sc);
+       return 1;
+}
+
+int
+deflate_intr(struct virtqueue * vq)
+{
+       struct virtio_softc *vsc = vq->vq_owner;
+       struct viomb_softc *sc = (struct viomb_softc *) vsc->sc_child;
+       struct balloon_req *b;
+       int             r, slot;
+       u_int64_t       nvpages, nhpages;
+
+       r = virtio_dequeue(vsc, vq, &slot, NULL);
+       if (r != 0) {
+               printf("%s: deflate dequeue failed, errno %d\n",
+                      DEVNAME(sc), r);
+               return 1;
+       }
+       virtio_dequeue_commit(vq, slot);
+       b = &sc->sc_req;
+       nvpages = b->bl_nentries;
+       nhpages = nvpages * VIRTIO_PAGE_SIZE / PAGE_SIZE;
+       bus_dmamap_sync(vsc->sc_dmat, b->bl_dmamap,
+                       offsetof(struct balloon_req, bl_pages),
+                       PAGE_SIZE * nvpages,
+                       BUS_DMASYNC_POSTWRITE);
+
+       if (vsc->sc_features & VIRTIO_BALLOON_F_MUST_TELL_HOST)
+               uvm_pglistfree(&b->bl_pglist);
+
+       sc->sc_inflight += nvpages;
+       sc->sc_actual = nvpages;
+       VIOMBDEBUG(sc, "updating sc->sc_actual from %ul to %ul\n",
+                sc->sc_actual, nvpages);
+       virtio_write_device_config_4(vsc, VIRTIO_BALLOON_CONFIG_ACTUAL,
+                                    nvpages);
+       viomb_read_config(sc);
+       return 1;
+}

Reply via email to