195fbc42eSDiederik de Groot /*-
295fbc42eSDiederik de Groot * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
395fbc42eSDiederik de Groot *
495fbc42eSDiederik de Groot * Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org>
595fbc42eSDiederik de Groot * All rights reserved.
695fbc42eSDiederik de Groot *
795fbc42eSDiederik de Groot * Redistribution and use in source and binary forms, with or without
895fbc42eSDiederik de Groot * modification, are permitted provided that the following conditions
995fbc42eSDiederik de Groot * are met:
1095fbc42eSDiederik de Groot * 1. Redistributions of source code must retain the above copyright
1195fbc42eSDiederik de Groot * notice unmodified, this list of conditions, and the following
1295fbc42eSDiederik de Groot * disclaimer.
1395fbc42eSDiederik de Groot * 2. Redistributions in binary form must reproduce the above copyright
1495fbc42eSDiederik de Groot * notice, this list of conditions and the following disclaimer in the
1595fbc42eSDiederik de Groot * documentation and/or other materials provided with the distribution.
1695fbc42eSDiederik de Groot *
1795fbc42eSDiederik de Groot * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1895fbc42eSDiederik de Groot * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1995fbc42eSDiederik de Groot * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2095fbc42eSDiederik de Groot * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2195fbc42eSDiederik de Groot * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2295fbc42eSDiederik de Groot * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2395fbc42eSDiederik de Groot * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2495fbc42eSDiederik de Groot * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2595fbc42eSDiederik de Groot * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2695fbc42eSDiederik de Groot * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2795fbc42eSDiederik de Groot *
2895fbc42eSDiederik de Groot * $FreeBSD: head/sys/dev/virtio/balloon/virtio_balloon.c 326255 2017-11-27 14:52:40Z pfg $
2995fbc42eSDiederik de Groot */
3095fbc42eSDiederik de Groot
3195fbc42eSDiederik de Groot /*
3295fbc42eSDiederik de Groot * Copyright (c) 2018 The DragonFly Project. All rights reserved.
3395fbc42eSDiederik de Groot *
3495fbc42eSDiederik de Groot * This code is derived from software contributed to The DragonFly Project
3595fbc42eSDiederik de Groot * by Diederik de Groot <info@talon.nl>
3695fbc42eSDiederik de Groot *
3795fbc42eSDiederik de Groot * Redistribution and use in source and binary forms, with or without
3895fbc42eSDiederik de Groot * modification, are permitted provided that the following conditions
3995fbc42eSDiederik de Groot * are met:
4095fbc42eSDiederik de Groot *
4195fbc42eSDiederik de Groot * 1. Redistributions of source code must retain the above copyright
4295fbc42eSDiederik de Groot * notice, this list of conditions and the following disclaimer.
4395fbc42eSDiederik de Groot * 2. Redistributions in binary form must reproduce the above copyright
4495fbc42eSDiederik de Groot * notice, this list of conditions and the following disclaimer in
4595fbc42eSDiederik de Groot * the documentation and/or other materials provided with the
4695fbc42eSDiederik de Groot * distribution.
4795fbc42eSDiederik de Groot * 3. Neither the name of The DragonFly Project nor the names of its
4895fbc42eSDiederik de Groot * contributors may be used to endorse or promote products derived
4995fbc42eSDiederik de Groot * from this software without specific, prior written permission.
5095fbc42eSDiederik de Groot *
5195fbc42eSDiederik de Groot * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
5295fbc42eSDiederik de Groot * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5395fbc42eSDiederik de Groot * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
5495fbc42eSDiederik de Groot * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
5595fbc42eSDiederik de Groot * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
5695fbc42eSDiederik de Groot * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
5795fbc42eSDiederik de Groot * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
5895fbc42eSDiederik de Groot * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
5995fbc42eSDiederik de Groot * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
6095fbc42eSDiederik de Groot * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
6195fbc42eSDiederik de Groot * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6295fbc42eSDiederik de Groot * SUCH DAMAGE.
6395fbc42eSDiederik de Groot */
6495fbc42eSDiederik de Groot
6595fbc42eSDiederik de Groot /* Driver for VirtIO memory balloon devices. */
6695fbc42eSDiederik de Groot
6795fbc42eSDiederik de Groot #include <sys/cdefs.h>
6895fbc42eSDiederik de Groot #include <sys/param.h>
6995fbc42eSDiederik de Groot #include <sys/systm.h>
7095fbc42eSDiederik de Groot #include <sys/kernel.h>
7195fbc42eSDiederik de Groot #include <sys/endian.h>
7295fbc42eSDiederik de Groot #include <sys/kthread.h>
7395fbc42eSDiederik de Groot #include <sys/malloc.h>
7495fbc42eSDiederik de Groot #include <sys/module.h>
7595fbc42eSDiederik de Groot #include <sys/sglist.h>
7695fbc42eSDiederik de Groot #include <sys/sysctl.h>
7795fbc42eSDiederik de Groot #include <sys/lock.h>
7895fbc42eSDiederik de Groot #include <sys/queue.h>
7995fbc42eSDiederik de Groot
8095fbc42eSDiederik de Groot #include <vm/vm.h>
8195fbc42eSDiederik de Groot #include <vm/vm_page.h>
8295fbc42eSDiederik de Groot #include <sys/bus.h>
8395fbc42eSDiederik de Groot #include <sys/rman.h>
8495fbc42eSDiederik de Groot
8595fbc42eSDiederik de Groot #include <dev/virtual/virtio/virtio/virtio.h>
8695fbc42eSDiederik de Groot #include <dev/virtual/virtio/virtio/virtqueue.h>
8795fbc42eSDiederik de Groot #include <dev/virtual/virtio/balloon/virtio_balloon.h>
8895fbc42eSDiederik de Groot
8995fbc42eSDiederik de Groot struct vtballoon_softc {
9095fbc42eSDiederik de Groot device_t vtballoon_dev;
9195fbc42eSDiederik de Groot struct lwkt_serialize vtballoon_slz;
9295fbc42eSDiederik de Groot uint64_t vtballoon_features;
9395fbc42eSDiederik de Groot uint32_t vtballoon_flags;
9495fbc42eSDiederik de Groot #define VTBALLOON_FLAG_DETACH 0x01
9595fbc42eSDiederik de Groot
9695fbc42eSDiederik de Groot struct virtqueue *vtballoon_inflate_vq;
9795fbc42eSDiederik de Groot struct virtqueue *vtballoon_deflate_vq;
9895fbc42eSDiederik de Groot
9995fbc42eSDiederik de Groot uint32_t vtballoon_desired_npages;
10095fbc42eSDiederik de Groot uint32_t vtballoon_current_npages;
10195fbc42eSDiederik de Groot TAILQ_HEAD(,vm_page) vtballoon_pages;
10295fbc42eSDiederik de Groot
10395fbc42eSDiederik de Groot struct thread *vtballoon_td;
10495fbc42eSDiederik de Groot uint32_t *vtballoon_page_frames;
10595fbc42eSDiederik de Groot int vtballoon_pagereq;
10695fbc42eSDiederik de Groot int vtballoon_timeout;
10795fbc42eSDiederik de Groot int vtballoon_nintr;
10895fbc42eSDiederik de Groot int vtballoon_debug;
10995fbc42eSDiederik de Groot #define VTBALLOON_INFO 0x01
11095fbc42eSDiederik de Groot #define VTBALLOON_ERROR 0x02
11195fbc42eSDiederik de Groot #define VTBALLOON_DEBUG 0x04
11295fbc42eSDiederik de Groot #define VTBALLOON_TRACE 0x08
11395fbc42eSDiederik de Groot
11495fbc42eSDiederik de Groot struct virtqueue *vtballoon_stats_vq;
11595fbc42eSDiederik de Groot struct vtballoon_stat vtballoon_stats[VTBALLOON_S_NR];
11695fbc42eSDiederik de Groot bool vtballoon_update_stats;
11795fbc42eSDiederik de Groot };
11895fbc42eSDiederik de Groot
11995fbc42eSDiederik de Groot static struct virtio_feature_desc vtballoon_feature_desc[] = {
12095fbc42eSDiederik de Groot { VIRTIO_BALLOON_F_MUST_TELL_HOST, "MustTellHost" },
12195fbc42eSDiederik de Groot { VIRTIO_BALLOON_F_STATS_VQ, "StatsVq" },
12295fbc42eSDiederik de Groot { VIRTIO_BALLOON_F_DEFLATE_ON_OOM, "DeflateOnOutOfMemory" },
12395fbc42eSDiederik de Groot { 0, NULL }
12495fbc42eSDiederik de Groot };
12595fbc42eSDiederik de Groot
12695fbc42eSDiederik de Groot #define vtballoon_dprintf(_sc, _level, _msg, _args ...) do { \
12795fbc42eSDiederik de Groot if ((_sc)->vtballoon_debug & (_level)) \
12895fbc42eSDiederik de Groot device_printf((_sc)->vtballoon_dev, "%s:%d: "_msg, \
12995fbc42eSDiederik de Groot __FUNCTION__, __LINE__, ##_args); \
13095fbc42eSDiederik de Groot } while (0)
13195fbc42eSDiederik de Groot
13295fbc42eSDiederik de Groot static int vtballoon_probe(device_t);
13395fbc42eSDiederik de Groot static int vtballoon_attach(device_t);
13495fbc42eSDiederik de Groot static int vtballoon_detach(device_t);
13595fbc42eSDiederik de Groot
13695fbc42eSDiederik de Groot static int vtballoon_alloc_intrs(struct vtballoon_softc *sc);
13795fbc42eSDiederik de Groot
13895fbc42eSDiederik de Groot static void vtballoon_negotiate_features(struct vtballoon_softc *);
13995fbc42eSDiederik de Groot static int vtballoon_alloc_virtqueues(struct vtballoon_softc *);
14095fbc42eSDiederik de Groot
14195fbc42eSDiederik de Groot static void vtballoon_config_change_intr(void *);
14295fbc42eSDiederik de Groot
14395fbc42eSDiederik de Groot static void vtballoon_update_stats(struct vtballoon_softc *sc);
14495fbc42eSDiederik de Groot static void vtballoon_stats_vq_intr(void *);
14595fbc42eSDiederik de Groot
14695fbc42eSDiederik de Groot static void vtballoon_inflate_vq_intr(void *);
14795fbc42eSDiederik de Groot static void vtballoon_deflate_vq_intr(void *);
14895fbc42eSDiederik de Groot static void vtballoon_inflate(struct vtballoon_softc *, int);
14995fbc42eSDiederik de Groot static void vtballoon_deflate(struct vtballoon_softc *, int);
15095fbc42eSDiederik de Groot
15195fbc42eSDiederik de Groot static void vtballoon_send_page_frames(struct vtballoon_softc *,
15295fbc42eSDiederik de Groot struct virtqueue *, int);
15395fbc42eSDiederik de Groot
15495fbc42eSDiederik de Groot static void vtballoon_pop(struct vtballoon_softc *);
15595fbc42eSDiederik de Groot static void vtballoon_stop(struct vtballoon_softc *);
15695fbc42eSDiederik de Groot
15795fbc42eSDiederik de Groot static vm_page_t vtballoon_alloc_page(struct vtballoon_softc *);
15895fbc42eSDiederik de Groot static void vtballoon_free_page(struct vtballoon_softc *, vm_page_t);
15995fbc42eSDiederik de Groot
16095fbc42eSDiederik de Groot static int vtballoon_sleep(struct vtballoon_softc *);
16195fbc42eSDiederik de Groot static void vtballoon_thread(void *);
16295fbc42eSDiederik de Groot static void vtballoon_get_tunables(struct vtballoon_softc *);
16395fbc42eSDiederik de Groot static void vtballoon_add_sysctl(struct vtballoon_softc *);
16495fbc42eSDiederik de Groot
16595fbc42eSDiederik de Groot /*
16695fbc42eSDiederik de Groot * Features desired/implemented by this driver.
16795fbc42eSDiederik de Groot * VIRTIO_BALLOON_F_STATS_VQ | VIRTIO_BALLOON_F_MUST_TELL_HOST
16895fbc42eSDiederik de Groot */
16995fbc42eSDiederik de Groot #define VTBALLOON_FEATURES VIRTIO_BALLOON_F_STATS_VQ
17095fbc42eSDiederik de Groot
17195fbc42eSDiederik de Groot /* Timeout between retries when the balloon needs inflating. */
17295fbc42eSDiederik de Groot #define VTBALLOON_LOWMEM_TIMEOUT hz * 100
17395fbc42eSDiederik de Groot
17495fbc42eSDiederik de Groot /* vm_page_alloc flags */
17595fbc42eSDiederik de Groot #define VTBALLOON_REGULAR_ALLOC VM_ALLOC_NORMAL
17695fbc42eSDiederik de Groot #define VTBALLOON_LOWMEM_ALLOC VM_ALLOC_SYSTEM
17795fbc42eSDiederik de Groot
17895fbc42eSDiederik de Groot /*
17995fbc42eSDiederik de Groot * Maximum number of pages we'll request to inflate or deflate
18095fbc42eSDiederik de Groot * the balloon in one virtqueue request. Both Linux and NetBSD
18195fbc42eSDiederik de Groot * have settled on 256, doing up to 1MB at a time.
18295fbc42eSDiederik de Groot */
18395fbc42eSDiederik de Groot #define VTBALLOON_PAGES_PER_REQUEST 256
18495fbc42eSDiederik de Groot
18595fbc42eSDiederik de Groot /*
18695fbc42eSDiederik de Groot * Default Debug Level
18795fbc42eSDiederik de Groot * VTBALLOON_INFO | VTBALLOON_ERROR | VTBALLOON_DEBUG | VTBALLOON_TRACE
18895fbc42eSDiederik de Groot */
18995fbc42eSDiederik de Groot #define VTBALLOON_DEFAULT_DEBUG_LEVEL VTBALLOON_INFO | VTBALLOON_ERROR
19095fbc42eSDiederik de Groot
19195fbc42eSDiederik de Groot /*
19295fbc42eSDiederik de Groot * Maximum number of interrupts to request
19395fbc42eSDiederik de Groot */
19495fbc42eSDiederik de Groot #define VTBALLOON_MAX_INTERRUPTS 4
19595fbc42eSDiederik de Groot
19695fbc42eSDiederik de Groot /* Must be able to fix all pages frames in one page (segment). */
19795fbc42eSDiederik de Groot CTASSERT(VTBALLOON_PAGES_PER_REQUEST * sizeof(uint32_t) <= PAGE_SIZE);
19895fbc42eSDiederik de Groot
19995fbc42eSDiederik de Groot #define VTBALLOON_SLZ(_sc) &(_sc)->vtballoon_slz
20095fbc42eSDiederik de Groot #define VTBALLOON_ENTER_SLZ(_sc) lwkt_serialize_enter(VTBALLOON_SLZ(sc));
20195fbc42eSDiederik de Groot #define VTBALLOON_EXIT_SLZ(_sc) lwkt_serialize_exit(VTBALLOON_SLZ(sc));
20295fbc42eSDiederik de Groot
20395fbc42eSDiederik de Groot static device_method_t vtballoon_methods[] = {
20495fbc42eSDiederik de Groot /* Device methods. */
20595fbc42eSDiederik de Groot DEVMETHOD(device_probe, vtballoon_probe),
20695fbc42eSDiederik de Groot DEVMETHOD(device_attach, vtballoon_attach),
20795fbc42eSDiederik de Groot DEVMETHOD(device_detach, vtballoon_detach),
20895fbc42eSDiederik de Groot
20995fbc42eSDiederik de Groot DEVMETHOD_END
21095fbc42eSDiederik de Groot };
21195fbc42eSDiederik de Groot
21295fbc42eSDiederik de Groot static driver_t vtballoon_driver = {
21395fbc42eSDiederik de Groot "vtballoon",
21495fbc42eSDiederik de Groot vtballoon_methods,
21595fbc42eSDiederik de Groot sizeof(struct vtballoon_softc)
21695fbc42eSDiederik de Groot };
21795fbc42eSDiederik de Groot static devclass_t vtballoon_devclass;
21895fbc42eSDiederik de Groot
21995fbc42eSDiederik de Groot DRIVER_MODULE(virtio_balloon, virtio_pci, vtballoon_driver,
220*17975de1SSascha Wildner vtballoon_devclass, NULL, NULL);
22195fbc42eSDiederik de Groot MODULE_VERSION(virtio_balloon, 1);
22295fbc42eSDiederik de Groot MODULE_DEPEND(virtio_balloon, virtio, 1, 1, 1);
22395fbc42eSDiederik de Groot
22495fbc42eSDiederik de Groot static int
vtballoon_probe(device_t dev)22595fbc42eSDiederik de Groot vtballoon_probe(device_t dev)
22695fbc42eSDiederik de Groot {
22795fbc42eSDiederik de Groot struct vtballoon_softc *sc = device_get_softc(dev);
22895fbc42eSDiederik de Groot vtballoon_dprintf(sc, VTBALLOON_TRACE, "\n");
22995fbc42eSDiederik de Groot if (virtio_get_device_type(dev) != VIRTIO_ID_BALLOON)
23095fbc42eSDiederik de Groot return (ENXIO);
23195fbc42eSDiederik de Groot
23295fbc42eSDiederik de Groot device_set_desc(dev, "VirtIO Balloon Adapter");
23395fbc42eSDiederik de Groot
23495fbc42eSDiederik de Groot return (BUS_PROBE_DEFAULT);
23595fbc42eSDiederik de Groot }
23695fbc42eSDiederik de Groot
23795fbc42eSDiederik de Groot struct irqmap {
23895fbc42eSDiederik de Groot int irq;
23995fbc42eSDiederik de Groot int idx;
24095fbc42eSDiederik de Groot driver_intr_t *handler;
24195fbc42eSDiederik de Groot const char * handler_name;
24295fbc42eSDiederik de Groot };
24395fbc42eSDiederik de Groot
24495fbc42eSDiederik de Groot static int
vtballoon_attach(device_t dev)24595fbc42eSDiederik de Groot vtballoon_attach(device_t dev)
24695fbc42eSDiederik de Groot {
24795fbc42eSDiederik de Groot struct vtballoon_softc *sc;
24895fbc42eSDiederik de Groot int error, i;
24995fbc42eSDiederik de Groot
25095fbc42eSDiederik de Groot sc = device_get_softc(dev);
25195fbc42eSDiederik de Groot sc->vtballoon_dev = dev;
25295fbc42eSDiederik de Groot sc->vtballoon_debug = VTBALLOON_DEFAULT_DEBUG_LEVEL;
25395fbc42eSDiederik de Groot
25495fbc42eSDiederik de Groot vtballoon_dprintf(sc, VTBALLOON_TRACE, "\n");
25595fbc42eSDiederik de Groot
25695fbc42eSDiederik de Groot lwkt_serialize_init(VTBALLOON_SLZ(sc));
25795fbc42eSDiederik de Groot TAILQ_INIT(&sc->vtballoon_pages);
25895fbc42eSDiederik de Groot
25995fbc42eSDiederik de Groot vtballoon_get_tunables(sc);
26095fbc42eSDiederik de Groot vtballoon_add_sysctl(sc);
26195fbc42eSDiederik de Groot
26295fbc42eSDiederik de Groot virtio_set_feature_desc(dev, vtballoon_feature_desc);
26395fbc42eSDiederik de Groot vtballoon_negotiate_features(sc);
26495fbc42eSDiederik de Groot
26595fbc42eSDiederik de Groot sc->vtballoon_page_frames = contigmalloc(VTBALLOON_PAGES_PER_REQUEST *
26695fbc42eSDiederik de Groot sizeof(uint32_t), M_DEVBUF, M_NOWAIT | M_ZERO, 0, BUS_SPACE_MAXADDR, 16, 0);
26795fbc42eSDiederik de Groot if (sc->vtballoon_page_frames == NULL) {
26895fbc42eSDiederik de Groot error = ENOMEM;
26995fbc42eSDiederik de Groot vtballoon_dprintf(sc, VTBALLOON_ERROR, "cannot allocate page frame request array (error:%d)\n", error);
27095fbc42eSDiederik de Groot goto fail;
27195fbc42eSDiederik de Groot }
27295fbc42eSDiederik de Groot error = vtballoon_alloc_intrs(sc);
27395fbc42eSDiederik de Groot if (error) {
27495fbc42eSDiederik de Groot vtballoon_dprintf(sc, VTBALLOON_ERROR, "cannot allocate interrupts (error:%d)\n", error);
27595fbc42eSDiederik de Groot goto fail;
27695fbc42eSDiederik de Groot }
27795fbc42eSDiederik de Groot
27895fbc42eSDiederik de Groot error = vtballoon_alloc_virtqueues(sc);
27995fbc42eSDiederik de Groot if (error) {
28095fbc42eSDiederik de Groot vtballoon_dprintf(sc, VTBALLOON_ERROR, "cannot allocate virtqueues (error:%d)\n", error);
28195fbc42eSDiederik de Groot goto fail;
28295fbc42eSDiederik de Groot }
28395fbc42eSDiederik de Groot
28495fbc42eSDiederik de Groot int nrhandlers = virtio_with_feature(sc->vtballoon_dev, VIRTIO_BALLOON_F_STATS_VQ) ? 4 : 3;
28595fbc42eSDiederik de Groot struct irqmap info[4];
28695fbc42eSDiederik de Groot
28795fbc42eSDiederik de Groot /* Possible "Virtqueue <-> IRQ" configurations */
28895fbc42eSDiederik de Groot switch (sc->vtballoon_nintr) {
28995fbc42eSDiederik de Groot case 1:
29095fbc42eSDiederik de Groot info[2] = (struct irqmap){0, -1, vtballoon_config_change_intr, "config"};
29195fbc42eSDiederik de Groot info[0] = (struct irqmap){0, 0, vtballoon_inflate_vq_intr, "inflate"};
29295fbc42eSDiederik de Groot info[1] = (struct irqmap){0, 1, vtballoon_deflate_vq_intr, "deflate"};
29395fbc42eSDiederik de Groot info[3] = (struct irqmap){0, 2, vtballoon_stats_vq_intr, "stats"};
29495fbc42eSDiederik de Groot break;
29595fbc42eSDiederik de Groot case 2:
29695fbc42eSDiederik de Groot info[2] = (struct irqmap){1, -1, vtballoon_config_change_intr, "config"};
29795fbc42eSDiederik de Groot info[0] = (struct irqmap){0, 0, vtballoon_inflate_vq_intr, "inflate"};
29895fbc42eSDiederik de Groot info[1] = (struct irqmap){0, 1, vtballoon_deflate_vq_intr, "deflate"};
29995fbc42eSDiederik de Groot info[3] = (struct irqmap){0, 2, vtballoon_stats_vq_intr, "stats"};
30095fbc42eSDiederik de Groot break;
30195fbc42eSDiederik de Groot case 3:
30295fbc42eSDiederik de Groot info[2] = (struct irqmap){2, -1, vtballoon_config_change_intr, "config"};
30395fbc42eSDiederik de Groot info[0] = (struct irqmap){0, 0, vtballoon_inflate_vq_intr, "inflate"};
30495fbc42eSDiederik de Groot info[1] = (struct irqmap){1, 1, vtballoon_deflate_vq_intr, "deflate"};
30595fbc42eSDiederik de Groot info[3] = (struct irqmap){2, 2, vtballoon_stats_vq_intr, "stats"};
30695fbc42eSDiederik de Groot break;
30795fbc42eSDiederik de Groot case 4:
30895fbc42eSDiederik de Groot info[2] = (struct irqmap){3, -1, vtballoon_config_change_intr, "config"};
30995fbc42eSDiederik de Groot info[0] = (struct irqmap){0, 0, vtballoon_inflate_vq_intr, "inflate"};
31095fbc42eSDiederik de Groot info[1] = (struct irqmap){1, 1, vtballoon_deflate_vq_intr, "deflate"};
31195fbc42eSDiederik de Groot info[3] = (struct irqmap){2, 2, vtballoon_stats_vq_intr, "stats"};
31295fbc42eSDiederik de Groot break;
31395fbc42eSDiederik de Groot default:
31495fbc42eSDiederik de Groot vtballoon_dprintf(sc, VTBALLOON_ERROR, "Invalid interrupt vector count: %d\n", sc->vtballoon_nintr);
31595fbc42eSDiederik de Groot goto fail;
31695fbc42eSDiederik de Groot }
31795fbc42eSDiederik de Groot for (i = 0; i < nrhandlers; i++) {
31895fbc42eSDiederik de Groot error = virtio_bind_intr(sc->vtballoon_dev, info[i].irq, info[i].idx,
31995fbc42eSDiederik de Groot info[i].handler, sc);
32095fbc42eSDiederik de Groot if (error) {
32195fbc42eSDiederik de Groot vtballoon_dprintf(sc, VTBALLOON_ERROR, "cannot bind virtqueue '%s' handler to IRQ:%d/%d\n",
32295fbc42eSDiederik de Groot info[i].handler_name, info[i].irq, sc->vtballoon_nintr);
32395fbc42eSDiederik de Groot goto fail;
32495fbc42eSDiederik de Groot }
32595fbc42eSDiederik de Groot }
32695fbc42eSDiederik de Groot
32795fbc42eSDiederik de Groot for (i = 0; i < sc->vtballoon_nintr; i++) {
32895fbc42eSDiederik de Groot error = virtio_setup_intr(dev, i, VTBALLOON_SLZ(sc));
32995fbc42eSDiederik de Groot if (error) {
33095fbc42eSDiederik de Groot vtballoon_dprintf(sc, VTBALLOON_ERROR, "cannot setup virtqueue interrupt:%d (error:%d)\n", i, error);
33195fbc42eSDiederik de Groot goto fail;
33295fbc42eSDiederik de Groot }
33395fbc42eSDiederik de Groot }
33495fbc42eSDiederik de Groot
33595fbc42eSDiederik de Groot error = kthread_create(vtballoon_thread, sc, &sc->vtballoon_td, "virtio_balloon");
33695fbc42eSDiederik de Groot if (error) {
33795fbc42eSDiederik de Groot vtballoon_dprintf(sc, VTBALLOON_ERROR, "cannot create balloon kthread (error:%d)\n", error);
33895fbc42eSDiederik de Groot goto fail;
33995fbc42eSDiederik de Groot }
34095fbc42eSDiederik de Groot
34195fbc42eSDiederik de Groot virtqueue_enable_intr(sc->vtballoon_inflate_vq);
34295fbc42eSDiederik de Groot virtqueue_enable_intr(sc->vtballoon_deflate_vq);
34395fbc42eSDiederik de Groot
34495fbc42eSDiederik de Groot if (virtio_with_feature(sc->vtballoon_dev, VIRTIO_BALLOON_F_STATS_VQ)) {
34595fbc42eSDiederik de Groot virtqueue_enable_intr(sc->vtballoon_stats_vq);
34695fbc42eSDiederik de Groot #if 0 /* enabling this causes a panic, on asserting ASSERT_SERIALIZED(sc) in vtballoon_update_stats */
34795fbc42eSDiederik de Groot /*
34895fbc42eSDiederik de Groot * Prime this stats virtqueue with one buffer so the hypervisor can
34995fbc42eSDiederik de Groot * use it to signal us later.
35095fbc42eSDiederik de Groot */
35195fbc42eSDiederik de Groot VTBALLOON_ENTER_SLZ(sc);
35295fbc42eSDiederik de Groot vtballoon_update_stats(sc);
35395fbc42eSDiederik de Groot VTBALLOON_EXIT_SLZ(sc);
35495fbc42eSDiederik de Groot #endif
35595fbc42eSDiederik de Groot }
35695fbc42eSDiederik de Groot
35795fbc42eSDiederik de Groot fail:
35895fbc42eSDiederik de Groot if (error)
35995fbc42eSDiederik de Groot vtballoon_detach(dev);
36095fbc42eSDiederik de Groot
36195fbc42eSDiederik de Groot return (error);
36295fbc42eSDiederik de Groot }
36395fbc42eSDiederik de Groot
36495fbc42eSDiederik de Groot static int
vtballoon_detach(device_t dev)36595fbc42eSDiederik de Groot vtballoon_detach(device_t dev)
36695fbc42eSDiederik de Groot {
36795fbc42eSDiederik de Groot struct vtballoon_softc *sc;
36895fbc42eSDiederik de Groot int i;
36995fbc42eSDiederik de Groot
37095fbc42eSDiederik de Groot sc = device_get_softc(dev);
37195fbc42eSDiederik de Groot vtballoon_dprintf(sc, VTBALLOON_TRACE, "\n");
37295fbc42eSDiederik de Groot
37395fbc42eSDiederik de Groot if (sc->vtballoon_td != NULL) {
37495fbc42eSDiederik de Groot VTBALLOON_ENTER_SLZ(sc);
37595fbc42eSDiederik de Groot sc->vtballoon_flags |= VTBALLOON_FLAG_DETACH;
37695fbc42eSDiederik de Groot
37795fbc42eSDiederik de Groot /* drain */
37895fbc42eSDiederik de Groot wakeup_one(sc);
37995fbc42eSDiederik de Groot zsleep(sc->vtballoon_td, VTBALLOON_SLZ(sc), 0, "vtbdth", 0);
38095fbc42eSDiederik de Groot VTBALLOON_EXIT_SLZ(sc);
38195fbc42eSDiederik de Groot sc->vtballoon_td = NULL;
38295fbc42eSDiederik de Groot }
38395fbc42eSDiederik de Groot
38495fbc42eSDiederik de Groot lwkt_serialize_handler_disable(VTBALLOON_SLZ(sc));
38595fbc42eSDiederik de Groot
38695fbc42eSDiederik de Groot for (i = 0; i < sc->vtballoon_nintr; i++)
38795fbc42eSDiederik de Groot virtio_teardown_intr(dev, i);
38895fbc42eSDiederik de Groot
38995fbc42eSDiederik de Groot if (device_is_attached(dev)) {
39095fbc42eSDiederik de Groot vtballoon_pop(sc);
39195fbc42eSDiederik de Groot vtballoon_stop(sc);
39295fbc42eSDiederik de Groot }
39395fbc42eSDiederik de Groot
39495fbc42eSDiederik de Groot if (sc->vtballoon_page_frames != NULL) {
39595fbc42eSDiederik de Groot contigfree(sc->vtballoon_page_frames, VTBALLOON_PAGES_PER_REQUEST *
39695fbc42eSDiederik de Groot sizeof(uint32_t), M_DEVBUF);
39795fbc42eSDiederik de Groot sc->vtballoon_page_frames = NULL;
39895fbc42eSDiederik de Groot }
39995fbc42eSDiederik de Groot return (0);
40095fbc42eSDiederik de Groot }
40195fbc42eSDiederik de Groot
40295fbc42eSDiederik de Groot static void
vtballoon_negotiate_features(struct vtballoon_softc * sc)40395fbc42eSDiederik de Groot vtballoon_negotiate_features(struct vtballoon_softc *sc)
40495fbc42eSDiederik de Groot {
40595fbc42eSDiederik de Groot device_t dev;
40695fbc42eSDiederik de Groot uint64_t features;
40795fbc42eSDiederik de Groot
40895fbc42eSDiederik de Groot dev = sc->vtballoon_dev;
40995fbc42eSDiederik de Groot vtballoon_dprintf(sc, VTBALLOON_TRACE, "\n");
41095fbc42eSDiederik de Groot features = virtio_negotiate_features(dev, VTBALLOON_FEATURES);
41195fbc42eSDiederik de Groot sc->vtballoon_features = features;
41295fbc42eSDiederik de Groot }
41395fbc42eSDiederik de Groot
vtballoon_alloc_intrs(struct vtballoon_softc * sc)41495fbc42eSDiederik de Groot static int vtballoon_alloc_intrs(struct vtballoon_softc *sc)
41595fbc42eSDiederik de Groot {
41695fbc42eSDiederik de Groot vtballoon_dprintf(sc, VTBALLOON_TRACE, "\n");
41795fbc42eSDiederik de Groot int cnt, error;
41895fbc42eSDiederik de Groot int intrcount = virtio_intr_count(sc->vtballoon_dev);
41995fbc42eSDiederik de Groot int use_config = 1;
42095fbc42eSDiederik de Groot
42195fbc42eSDiederik de Groot intrcount = imin(intrcount, VTBALLOON_MAX_INTERRUPTS);
42295fbc42eSDiederik de Groot if (intrcount < 1)
42395fbc42eSDiederik de Groot return (ENXIO);
42495fbc42eSDiederik de Groot
42595fbc42eSDiederik de Groot cnt = intrcount;
42695fbc42eSDiederik de Groot error = virtio_intr_alloc(sc->vtballoon_dev, &cnt, use_config, NULL);
42795fbc42eSDiederik de Groot if (error != 0) {
42895fbc42eSDiederik de Groot virtio_intr_release(sc->vtballoon_dev);
42995fbc42eSDiederik de Groot return (error);
43095fbc42eSDiederik de Groot }
43195fbc42eSDiederik de Groot sc->vtballoon_nintr = cnt;
43295fbc42eSDiederik de Groot vtballoon_dprintf(sc, VTBALLOON_TRACE, "%d Interrupts Allocated\n", sc->vtballoon_nintr);
43395fbc42eSDiederik de Groot return (0);
43495fbc42eSDiederik de Groot }
43595fbc42eSDiederik de Groot
43695fbc42eSDiederik de Groot static int
vtballoon_alloc_virtqueues(struct vtballoon_softc * sc)43795fbc42eSDiederik de Groot vtballoon_alloc_virtqueues(struct vtballoon_softc *sc)
43895fbc42eSDiederik de Groot {
43995fbc42eSDiederik de Groot device_t dev;
44095fbc42eSDiederik de Groot struct vq_alloc_info vq_info[3];
44195fbc42eSDiederik de Groot int nvqs;
44295fbc42eSDiederik de Groot
44395fbc42eSDiederik de Groot dev = sc->vtballoon_dev;
44495fbc42eSDiederik de Groot vtballoon_dprintf(sc, VTBALLOON_TRACE, "\n");
44595fbc42eSDiederik de Groot nvqs = 2;
44695fbc42eSDiederik de Groot
44795fbc42eSDiederik de Groot VQ_ALLOC_INFO_INIT(&vq_info[0], 0, &sc->vtballoon_inflate_vq,
44895fbc42eSDiederik de Groot "%s inflate", device_get_nameunit(dev));
44995fbc42eSDiederik de Groot
45095fbc42eSDiederik de Groot VQ_ALLOC_INFO_INIT(&vq_info[1], 0, &sc->vtballoon_deflate_vq,
45195fbc42eSDiederik de Groot "%s deflate", device_get_nameunit(dev));
45295fbc42eSDiederik de Groot
45395fbc42eSDiederik de Groot if (virtio_with_feature(sc->vtballoon_dev, VIRTIO_BALLOON_F_STATS_VQ)) {
45495fbc42eSDiederik de Groot VQ_ALLOC_INFO_INIT(&vq_info[2], 0, &sc->vtballoon_stats_vq,
45595fbc42eSDiederik de Groot "%s stats", device_get_nameunit(dev));
45695fbc42eSDiederik de Groot nvqs = 3;
45795fbc42eSDiederik de Groot }
45895fbc42eSDiederik de Groot return (virtio_alloc_virtqueues(dev, nvqs, vq_info));
45995fbc42eSDiederik de Groot }
46095fbc42eSDiederik de Groot
46195fbc42eSDiederik de Groot static void
vtballoon_config_change_intr(void * arg)46295fbc42eSDiederik de Groot vtballoon_config_change_intr(void *arg)
46395fbc42eSDiederik de Groot {
46495fbc42eSDiederik de Groot struct vtballoon_softc *sc = arg;
46595fbc42eSDiederik de Groot vtballoon_dprintf(sc, VTBALLOON_TRACE, "\n");
46695fbc42eSDiederik de Groot ASSERT_SERIALIZED(VTBALLOON_SLZ(sc));
46795fbc42eSDiederik de Groot wakeup_one(sc);
46895fbc42eSDiederik de Groot }
46995fbc42eSDiederik de Groot
47095fbc42eSDiederik de Groot static inline void
vtballoon_update_stat(struct vtballoon_softc * sc,int idx,uint16_t tag,uint64_t val)47195fbc42eSDiederik de Groot vtballoon_update_stat(struct vtballoon_softc *sc, int idx,
47295fbc42eSDiederik de Groot uint16_t tag, uint64_t val)
47395fbc42eSDiederik de Groot {
47495fbc42eSDiederik de Groot KASSERT(idx >= VTBALLOON_S_NR, ("Stats index out of bounds"));
47595fbc42eSDiederik de Groot /*
47695fbc42eSDiederik de Groot * XXX: Required for endianess in the future
47795fbc42eSDiederik de Groot * sc->vtballoon_stats[idx].tag = virtio_is_little_endian(sc->vtballoon_dev) ? le16toh(tag) : tag;
47895fbc42eSDiederik de Groot * sc->vtballoon_stats[idx].val = virtio_is_little_endian(sc->vtballoon_dev) ? le64toh(val) : val;
47995fbc42eSDiederik de Groot * at the moment virtio balloon is always little endian.
48095fbc42eSDiederik de Groot *
48195fbc42eSDiederik de Groot */
48295fbc42eSDiederik de Groot sc->vtballoon_stats[idx].tag = le16toh(tag);
48395fbc42eSDiederik de Groot sc->vtballoon_stats[idx].val = le64toh(val);
48495fbc42eSDiederik de Groot
48595fbc42eSDiederik de Groot }
48695fbc42eSDiederik de Groot
48795fbc42eSDiederik de Groot /*
48895fbc42eSDiederik de Groot * collect guest side statistics
48995fbc42eSDiederik de Groot *
49095fbc42eSDiederik de Groot * XXX: am i using the correct memory and pagefault values
49195fbc42eSDiederik de Groot */
collect_balloon_stats(struct vtballoon_softc * sc)49295fbc42eSDiederik de Groot static unsigned int collect_balloon_stats(struct vtballoon_softc *sc)
49395fbc42eSDiederik de Groot {
49495fbc42eSDiederik de Groot #define pages_to_bytes(x) ((uint64_t)(x) << PAGE_SHIFT)
49595fbc42eSDiederik de Groot unsigned int idx = 0;
49695fbc42eSDiederik de Groot struct vmtotal total;
49795fbc42eSDiederik de Groot struct vmmeter vmm;
49895fbc42eSDiederik de Groot struct vmstats vms;
49995fbc42eSDiederik de Groot size_t vmt_size = sizeof(total);
50095fbc42eSDiederik de Groot size_t vmm_size = sizeof(vmm);
50195fbc42eSDiederik de Groot size_t vms_size = sizeof(vms);
50295fbc42eSDiederik de Groot
50395fbc42eSDiederik de Groot vtballoon_dprintf(sc, VTBALLOON_TRACE, "Updating Stats Buffer\n");
50495fbc42eSDiederik de Groot if (!kernel_sysctlbyname("vm.vmtotal", &total, &vmt_size, NULL, 0, NULL)) {
50595fbc42eSDiederik de Groot /* Total amount of free memory )*/
50695fbc42eSDiederik de Groot vtballoon_update_stat(sc, idx++, VTBALLOON_S_MEMFREE,
50795fbc42eSDiederik de Groot pages_to_bytes(total.t_rm - total.t_arm));
50895fbc42eSDiederik de Groot /* Total amount of memory */
50995fbc42eSDiederik de Groot vtballoon_update_stat(sc, idx++, VTBALLOON_S_MEMTOT,
51095fbc42eSDiederik de Groot pages_to_bytes(total.t_rm));
51195fbc42eSDiederik de Groot /* Available memory as in /proc */
51295fbc42eSDiederik de Groot vtballoon_update_stat(sc, idx++, VTBALLOON_S_AVAIL,
51395fbc42eSDiederik de Groot pages_to_bytes(total.t_arm));
51495fbc42eSDiederik de Groot }
51595fbc42eSDiederik de Groot if (!kernel_sysctlbyname("vm.vmstats", &vms, &vms_size, NULL, 0, NULL)) {
51695fbc42eSDiederik de Groot /* Disk caches */
51795fbc42eSDiederik de Groot vtballoon_update_stat(sc, idx++, VTBALLOON_S_CACHES,
51895fbc42eSDiederik de Groot pages_to_bytes(vms.v_cache_count));
51995fbc42eSDiederik de Groot }
52095fbc42eSDiederik de Groot if (!kernel_sysctlbyname("vm.vmmeter", &vmm, &vmm_size, NULL, 0, NULL)) {
52195fbc42eSDiederik de Groot /* Amount of memory swapped in */
52295fbc42eSDiederik de Groot vtballoon_update_stat(sc, idx++, VTBALLOON_S_SWAP_IN,
52395fbc42eSDiederik de Groot pages_to_bytes(vmm.v_swappgsin));
52495fbc42eSDiederik de Groot /* Amount of memory swapped out */
52595fbc42eSDiederik de Groot vtballoon_update_stat(sc, idx++, VTBALLOON_S_SWAP_OUT,
52695fbc42eSDiederik de Groot pages_to_bytes(vmm.v_swappgsout));
52795fbc42eSDiederik de Groot /* Number of major faults */
52895fbc42eSDiederik de Groot vtballoon_update_stat(sc, idx++, VTBALLOON_S_MAJFLT,
52995fbc42eSDiederik de Groot vmm.v_vm_faults);
53095fbc42eSDiederik de Groot /* Number of minor faults */
53195fbc42eSDiederik de Groot vtballoon_update_stat(sc, idx++, VTBALLOON_S_MINFLT,
53295fbc42eSDiederik de Groot vmm.v_intrans);
53395fbc42eSDiederik de Groot }
53495fbc42eSDiederik de Groot
53595fbc42eSDiederik de Groot if (sc->vtballoon_debug & VTBALLOON_TRACE) {
53695fbc42eSDiederik de Groot static const char *vt_balloon_names[]=VTBALLOON_S_NAMES;
53795fbc42eSDiederik de Groot int i;
53895fbc42eSDiederik de Groot for (i=0; i < idx; i++) {
53995fbc42eSDiederik de Groot kprintf("\t%s = %lu\n", vt_balloon_names[sc->vtballoon_stats[i].tag], sc->vtballoon_stats[i].val);
54095fbc42eSDiederik de Groot }
54195fbc42eSDiederik de Groot }
54295fbc42eSDiederik de Groot
54395fbc42eSDiederik de Groot return idx;
54495fbc42eSDiederik de Groot }
54595fbc42eSDiederik de Groot
54695fbc42eSDiederik de Groot static void
vtballoon_update_stats(struct vtballoon_softc * sc)54795fbc42eSDiederik de Groot vtballoon_update_stats(struct vtballoon_softc *sc)
54895fbc42eSDiederik de Groot {
54995fbc42eSDiederik de Groot struct virtqueue *vq = sc->vtballoon_stats_vq;
55095fbc42eSDiederik de Groot
55195fbc42eSDiederik de Groot ASSERT_SERIALIZED(VTBALLOON_SLZ(sc));
55295fbc42eSDiederik de Groot
55395fbc42eSDiederik de Groot vtballoon_dprintf(sc, VTBALLOON_TRACE, "Stats Requested\n");
55495fbc42eSDiederik de Groot
55595fbc42eSDiederik de Groot struct sglist sg;
55695fbc42eSDiederik de Groot struct sglist_seg segs[1];
55795fbc42eSDiederik de Groot unsigned int num_stats;
55895fbc42eSDiederik de Groot int error;
55995fbc42eSDiederik de Groot
56095fbc42eSDiederik de Groot num_stats = collect_balloon_stats(sc);
56195fbc42eSDiederik de Groot
56295fbc42eSDiederik de Groot sglist_init(&sg, 1, segs);
56395fbc42eSDiederik de Groot error = sglist_append(&sg, sc->vtballoon_stats, sizeof(sc->vtballoon_stats[0]) * num_stats);
56495fbc42eSDiederik de Groot KASSERT(error == 0, ("error adding page frames to sglist"));
56595fbc42eSDiederik de Groot
56695fbc42eSDiederik de Groot error = virtqueue_enqueue(vq, vq, &sg, 1, 0);
56795fbc42eSDiederik de Groot KASSERT(error == 0, ("error enqueuing page frames to virtqueue"));
56895fbc42eSDiederik de Groot virtqueue_notify(sc->vtballoon_stats_vq, NULL);
56995fbc42eSDiederik de Groot }
57095fbc42eSDiederik de Groot
57195fbc42eSDiederik de Groot /*
57295fbc42eSDiederik de Groot * While most virtqueues communicate guest-initiated requests to the hypervisor,
57395fbc42eSDiederik de Groot * the stats queue operates in reverse. The driver(host) initializes the virtqueue
57495fbc42eSDiederik de Groot * with a single buffer. From that point forward, all conversations consist of
57595fbc42eSDiederik de Groot * a hypervisor request (a call to this function) which directs us to refill
57695fbc42eSDiederik de Groot * the virtqueue with a fresh stats buffer. Since stats collection can sleep,
57795fbc42eSDiederik de Groot * we delegate the job to the vtballoon_thread which will do the actual stats
57895fbc42eSDiederik de Groot * collecting work.
57995fbc42eSDiederik de Groot */
58095fbc42eSDiederik de Groot static void
vtballoon_stats_vq_intr(void * arg)58195fbc42eSDiederik de Groot vtballoon_stats_vq_intr(void *arg)
58295fbc42eSDiederik de Groot {
58395fbc42eSDiederik de Groot struct vtballoon_softc *sc = arg;
58495fbc42eSDiederik de Groot struct virtqueue *vq = sc->vtballoon_stats_vq;
58595fbc42eSDiederik de Groot
58695fbc42eSDiederik de Groot ASSERT_SERIALIZED(VTBALLOON_SLZ(sc));
58795fbc42eSDiederik de Groot if (sc->vtballoon_update_stats || !virtqueue_pending(vq))
58895fbc42eSDiederik de Groot return;
58995fbc42eSDiederik de Groot
59095fbc42eSDiederik de Groot vtballoon_dprintf(sc, VTBALLOON_TRACE, "Ballooon Stats Requested\n");
59195fbc42eSDiederik de Groot sc->vtballoon_update_stats = true;
59295fbc42eSDiederik de Groot wakeup_one(sc);
59395fbc42eSDiederik de Groot virtqueue_dequeue(vq, NULL);
59495fbc42eSDiederik de Groot }
59595fbc42eSDiederik de Groot
59695fbc42eSDiederik de Groot static void
vtballoon_inflate_vq_intr(void * arg)59795fbc42eSDiederik de Groot vtballoon_inflate_vq_intr(void *arg)
59895fbc42eSDiederik de Groot {
59995fbc42eSDiederik de Groot struct vtballoon_softc *sc = arg;
60095fbc42eSDiederik de Groot struct virtqueue *vq = sc->vtballoon_inflate_vq;
60195fbc42eSDiederik de Groot ASSERT_SERIALIZED(VTBALLOON_SLZ(sc));
60295fbc42eSDiederik de Groot if (!virtqueue_pending(vq))
60395fbc42eSDiederik de Groot return;
60495fbc42eSDiederik de Groot wakeup_one(sc);
60595fbc42eSDiederik de Groot }
60695fbc42eSDiederik de Groot
60795fbc42eSDiederik de Groot static void
vtballoon_deflate_vq_intr(void * arg)60895fbc42eSDiederik de Groot vtballoon_deflate_vq_intr(void *arg)
60995fbc42eSDiederik de Groot {
61095fbc42eSDiederik de Groot struct vtballoon_softc *sc = arg;
61195fbc42eSDiederik de Groot struct virtqueue *vq = sc->vtballoon_deflate_vq;
61295fbc42eSDiederik de Groot ASSERT_SERIALIZED(VTBALLOON_SLZ(sc));
61395fbc42eSDiederik de Groot if (!virtqueue_pending(vq))
61495fbc42eSDiederik de Groot return;
61595fbc42eSDiederik de Groot wakeup_one(sc);
61695fbc42eSDiederik de Groot }
61795fbc42eSDiederik de Groot
61895fbc42eSDiederik de Groot static void
vtballoon_inflate(struct vtballoon_softc * sc,int npages)61995fbc42eSDiederik de Groot vtballoon_inflate(struct vtballoon_softc *sc, int npages)
62095fbc42eSDiederik de Groot {
62195fbc42eSDiederik de Groot struct virtqueue *vq;
62295fbc42eSDiederik de Groot
62395fbc42eSDiederik de Groot vm_page_t m;
62495fbc42eSDiederik de Groot int i;
62595fbc42eSDiederik de Groot
62695fbc42eSDiederik de Groot vq = sc->vtballoon_inflate_vq;
62795fbc42eSDiederik de Groot
62895fbc42eSDiederik de Groot if (npages > VTBALLOON_PAGES_PER_REQUEST)
62995fbc42eSDiederik de Groot npages = VTBALLOON_PAGES_PER_REQUEST;
63095fbc42eSDiederik de Groot
63195fbc42eSDiederik de Groot for (i = 0; i < npages; i++) {
63295fbc42eSDiederik de Groot if ((m = vtballoon_alloc_page(sc)) == NULL) {
63395fbc42eSDiederik de Groot /* First allocate usign VTBALLOON_REGULAR_ALLOC and fall back to VTBALLOON_LOWMEM_ALLOC
63495fbc42eSDiederik de Groot * when the guest is under severe memory pressure. Quickly decrease the
63595fbc42eSDiederik de Groot * allocation rate, allowing the system to swap out pages.
63695fbc42eSDiederik de Groot */
63795fbc42eSDiederik de Groot sc->vtballoon_pagereq = VM_ALLOC_SYSTEM | VM_ALLOC_INTERRUPT;
63895fbc42eSDiederik de Groot sc->vtballoon_timeout = VTBALLOON_LOWMEM_TIMEOUT;
63995fbc42eSDiederik de Groot break;
64095fbc42eSDiederik de Groot }
64195fbc42eSDiederik de Groot
64295fbc42eSDiederik de Groot sc->vtballoon_page_frames[i] =
64395fbc42eSDiederik de Groot VM_PAGE_TO_PHYS(m) >> VIRTIO_BALLOON_PFN_SHIFT;
64495fbc42eSDiederik de Groot
64595fbc42eSDiederik de Groot KASSERT(m->queue == PQ_NONE,
64695fbc42eSDiederik de Groot ("%s: allocated page %p on queue", __func__, m));
64795fbc42eSDiederik de Groot TAILQ_INSERT_TAIL(&sc->vtballoon_pages, m, pageq);
64895fbc42eSDiederik de Groot }
64995fbc42eSDiederik de Groot
65095fbc42eSDiederik de Groot if (i > 0)
65195fbc42eSDiederik de Groot vtballoon_send_page_frames(sc, vq, i);
65295fbc42eSDiederik de Groot }
65395fbc42eSDiederik de Groot
65495fbc42eSDiederik de Groot static void
vtballoon_deflate(struct vtballoon_softc * sc,int npages)65595fbc42eSDiederik de Groot vtballoon_deflate(struct vtballoon_softc *sc, int npages)
65695fbc42eSDiederik de Groot {
65795fbc42eSDiederik de Groot TAILQ_HEAD(, vm_page) free_pages;
65895fbc42eSDiederik de Groot struct virtqueue *vq;
65995fbc42eSDiederik de Groot vm_page_t m;
66095fbc42eSDiederik de Groot int i;
66195fbc42eSDiederik de Groot
66295fbc42eSDiederik de Groot vq = sc->vtballoon_deflate_vq;
66395fbc42eSDiederik de Groot TAILQ_INIT(&free_pages);
66495fbc42eSDiederik de Groot
66595fbc42eSDiederik de Groot if (npages > VTBALLOON_PAGES_PER_REQUEST)
66695fbc42eSDiederik de Groot npages = VTBALLOON_PAGES_PER_REQUEST;
66795fbc42eSDiederik de Groot
66895fbc42eSDiederik de Groot for (i = 0; i < npages; i++) {
66995fbc42eSDiederik de Groot m = TAILQ_FIRST(&sc->vtballoon_pages);
67095fbc42eSDiederik de Groot KASSERT(m != NULL, ("%s: no more pages to deflate", __func__));
67195fbc42eSDiederik de Groot
67295fbc42eSDiederik de Groot sc->vtballoon_page_frames[i] =
67395fbc42eSDiederik de Groot VM_PAGE_TO_PHYS(m) >> VIRTIO_BALLOON_PFN_SHIFT;
67495fbc42eSDiederik de Groot
67595fbc42eSDiederik de Groot TAILQ_REMOVE(&sc->vtballoon_pages, m, pageq);
67695fbc42eSDiederik de Groot TAILQ_INSERT_TAIL(&free_pages, m, pageq);
67795fbc42eSDiederik de Groot }
67895fbc42eSDiederik de Groot
67995fbc42eSDiederik de Groot if (i > 0) {
68095fbc42eSDiederik de Groot /*
68195fbc42eSDiederik de Groot * Note that if virtio VIRTIO_BALLOON_F_MUST_TELL_HOST
68295fbc42eSDiederik de Groot * feature is true, we *have* to tell host first
68395fbc42eSDiederik de Groot * before freeing the pages.
68495fbc42eSDiederik de Groot */
68595fbc42eSDiederik de Groot vtballoon_send_page_frames(sc, vq, i);
68695fbc42eSDiederik de Groot
68795fbc42eSDiederik de Groot while ((m = TAILQ_FIRST(&free_pages)) != NULL) {
68895fbc42eSDiederik de Groot TAILQ_REMOVE(&free_pages, m, pageq);
68995fbc42eSDiederik de Groot vtballoon_free_page(sc, m);
69095fbc42eSDiederik de Groot }
69195fbc42eSDiederik de Groot }
69295fbc42eSDiederik de Groot
69395fbc42eSDiederik de Groot KASSERT((TAILQ_EMPTY(&sc->vtballoon_pages) &&
69495fbc42eSDiederik de Groot sc->vtballoon_current_npages == 0) ||
69595fbc42eSDiederik de Groot (!TAILQ_EMPTY(&sc->vtballoon_pages) &&
69695fbc42eSDiederik de Groot sc->vtballoon_current_npages != 0),
69795fbc42eSDiederik de Groot ("%s: bogus page count %d", __func__,
69895fbc42eSDiederik de Groot sc->vtballoon_current_npages));
69995fbc42eSDiederik de Groot }
70095fbc42eSDiederik de Groot
70195fbc42eSDiederik de Groot static void
vtballoon_send_page_frames(struct vtballoon_softc * sc,struct virtqueue * vq,int npages)70295fbc42eSDiederik de Groot vtballoon_send_page_frames(struct vtballoon_softc *sc, struct virtqueue *vq,
70395fbc42eSDiederik de Groot int npages)
70495fbc42eSDiederik de Groot {
70595fbc42eSDiederik de Groot struct sglist sg;
70695fbc42eSDiederik de Groot struct sglist_seg segs[1];
70795fbc42eSDiederik de Groot void *c;
70895fbc42eSDiederik de Groot int error;
70995fbc42eSDiederik de Groot
71095fbc42eSDiederik de Groot sglist_init(&sg, 1, segs);
71195fbc42eSDiederik de Groot
71295fbc42eSDiederik de Groot error = sglist_append(&sg, sc->vtballoon_page_frames,
71395fbc42eSDiederik de Groot npages * sizeof(uint32_t));
71495fbc42eSDiederik de Groot KASSERT(error == 0, ("error adding page frames to sglist"));
71595fbc42eSDiederik de Groot
71695fbc42eSDiederik de Groot error = virtqueue_enqueue(vq, vq, &sg, 1, 0);
71795fbc42eSDiederik de Groot KASSERT(error == 0, ("error enqueuing page frames to virtqueue"));
71895fbc42eSDiederik de Groot virtqueue_notify(vq, NULL);
71995fbc42eSDiederik de Groot
72095fbc42eSDiederik de Groot /*
72195fbc42eSDiederik de Groot * Inflate and deflate operations are done synchronously. The
72295fbc42eSDiederik de Groot * interrupt handler will wake us up.
72395fbc42eSDiederik de Groot */
72495fbc42eSDiederik de Groot VTBALLOON_ENTER_SLZ(sc);
72595fbc42eSDiederik de Groot while ((c = virtqueue_dequeue(vq, NULL)) == NULL) {
72695fbc42eSDiederik de Groot zsleep(sc, VTBALLOON_SLZ(sc), 0, "vtbspf", 0);
72795fbc42eSDiederik de Groot }
72895fbc42eSDiederik de Groot VTBALLOON_EXIT_SLZ(sc);
72995fbc42eSDiederik de Groot
73095fbc42eSDiederik de Groot KASSERT(c == vq, ("unexpected balloon operation response"));
73195fbc42eSDiederik de Groot }
73295fbc42eSDiederik de Groot
73395fbc42eSDiederik de Groot static void
vtballoon_pop(struct vtballoon_softc * sc)73495fbc42eSDiederik de Groot vtballoon_pop(struct vtballoon_softc *sc)
73595fbc42eSDiederik de Groot {
73695fbc42eSDiederik de Groot vtballoon_dprintf(sc, VTBALLOON_TRACE, "Popping\n");
73795fbc42eSDiederik de Groot
73895fbc42eSDiederik de Groot while (!TAILQ_EMPTY(&sc->vtballoon_pages))
73995fbc42eSDiederik de Groot vtballoon_deflate(sc, sc->vtballoon_current_npages);
74095fbc42eSDiederik de Groot }
74195fbc42eSDiederik de Groot
74295fbc42eSDiederik de Groot static void
vtballoon_stop(struct vtballoon_softc * sc)74395fbc42eSDiederik de Groot vtballoon_stop(struct vtballoon_softc *sc)
74495fbc42eSDiederik de Groot {
74595fbc42eSDiederik de Groot vtballoon_dprintf(sc, VTBALLOON_TRACE, "Stopping\n");
74695fbc42eSDiederik de Groot
74795fbc42eSDiederik de Groot virtqueue_disable_intr(sc->vtballoon_inflate_vq);
74895fbc42eSDiederik de Groot virtqueue_disable_intr(sc->vtballoon_deflate_vq);
74995fbc42eSDiederik de Groot /*
75095fbc42eSDiederik de Groot if (virtio_with_feature(sc->vtballoon_dev, VIRTIO_BALLOON_F_STATS_VQ)) {
75195fbc42eSDiederik de Groot virtqueue_disable_intr(sc->vtballoon_stats_vq);
75295fbc42eSDiederik de Groot }
75395fbc42eSDiederik de Groot */
75495fbc42eSDiederik de Groot virtio_stop(sc->vtballoon_dev);
75595fbc42eSDiederik de Groot }
75695fbc42eSDiederik de Groot
75795fbc42eSDiederik de Groot static vm_page_t
vtballoon_alloc_page(struct vtballoon_softc * sc)75895fbc42eSDiederik de Groot vtballoon_alloc_page(struct vtballoon_softc *sc)
75995fbc42eSDiederik de Groot {
76095fbc42eSDiederik de Groot vm_page_t m;
76195fbc42eSDiederik de Groot
76295fbc42eSDiederik de Groot m = vm_page_alloc(NULL, 0, sc->vtballoon_pagereq);
76395fbc42eSDiederik de Groot if (m != NULL)
76495fbc42eSDiederik de Groot sc->vtballoon_current_npages++;
76595fbc42eSDiederik de Groot
76695fbc42eSDiederik de Groot return (m);
76795fbc42eSDiederik de Groot }
76895fbc42eSDiederik de Groot
76995fbc42eSDiederik de Groot static void
vtballoon_free_page(struct vtballoon_softc * sc,vm_page_t m)77095fbc42eSDiederik de Groot vtballoon_free_page(struct vtballoon_softc *sc, vm_page_t m)
77195fbc42eSDiederik de Groot {
77295fbc42eSDiederik de Groot vm_page_free_toq(m);
77395fbc42eSDiederik de Groot sc->vtballoon_current_npages--;
77495fbc42eSDiederik de Groot }
77595fbc42eSDiederik de Groot
77695fbc42eSDiederik de Groot static uint32_t
vtballoon_desired_size(struct vtballoon_softc * sc)77795fbc42eSDiederik de Groot vtballoon_desired_size(struct vtballoon_softc *sc)
77895fbc42eSDiederik de Groot {
77995fbc42eSDiederik de Groot uint32_t desired;
78095fbc42eSDiederik de Groot
78195fbc42eSDiederik de Groot desired = virtio_read_dev_config_4(sc->vtballoon_dev,
78295fbc42eSDiederik de Groot offsetof(struct virtio_balloon_config, num_pages));
78395fbc42eSDiederik de Groot
78495fbc42eSDiederik de Groot return (le32toh(desired));
78595fbc42eSDiederik de Groot }
78695fbc42eSDiederik de Groot
78795fbc42eSDiederik de Groot static void
vtballoon_update_size(struct vtballoon_softc * sc)78895fbc42eSDiederik de Groot vtballoon_update_size(struct vtballoon_softc *sc)
78995fbc42eSDiederik de Groot {
79095fbc42eSDiederik de Groot virtio_write_dev_config_4(sc->vtballoon_dev,
79195fbc42eSDiederik de Groot offsetof(struct virtio_balloon_config, actual),
79295fbc42eSDiederik de Groot htole32(sc->vtballoon_current_npages));
79395fbc42eSDiederik de Groot }
79495fbc42eSDiederik de Groot
79595fbc42eSDiederik de Groot static int
vtballoon_sleep(struct vtballoon_softc * sc)79695fbc42eSDiederik de Groot vtballoon_sleep(struct vtballoon_softc *sc)
79795fbc42eSDiederik de Groot {
79895fbc42eSDiederik de Groot int rc, timeout;
79995fbc42eSDiederik de Groot uint32_t current, desired;
80095fbc42eSDiederik de Groot
80195fbc42eSDiederik de Groot rc = 0;
80295fbc42eSDiederik de Groot current = sc->vtballoon_current_npages;
80395fbc42eSDiederik de Groot sc->vtballoon_pagereq = VM_ALLOC_NORMAL | VM_ALLOC_INTERRUPT;
80495fbc42eSDiederik de Groot
80595fbc42eSDiederik de Groot VTBALLOON_ENTER_SLZ(sc);
80695fbc42eSDiederik de Groot for (;;) {
80795fbc42eSDiederik de Groot if (sc->vtballoon_flags & VTBALLOON_FLAG_DETACH) {
80895fbc42eSDiederik de Groot rc = 1;
80995fbc42eSDiederik de Groot break;
81095fbc42eSDiederik de Groot }
81195fbc42eSDiederik de Groot
81295fbc42eSDiederik de Groot desired = vtballoon_desired_size(sc);
81395fbc42eSDiederik de Groot if (desired != sc->vtballoon_desired_npages)
81495fbc42eSDiederik de Groot vtballoon_dprintf(sc, VTBALLOON_DEBUG, "balloon %s %d -> %d (4K pages)\n",
81595fbc42eSDiederik de Groot desired < sc->vtballoon_desired_npages ? "deflating" : "inflating",
81695fbc42eSDiederik de Groot current, desired);
81795fbc42eSDiederik de Groot
81895fbc42eSDiederik de Groot sc->vtballoon_desired_npages = desired;
81995fbc42eSDiederik de Groot
82095fbc42eSDiederik de Groot /*
82195fbc42eSDiederik de Groot * If given, use non-zero timeout on the first time through
82295fbc42eSDiederik de Groot * the loop. On subsequent times, timeout will be zero so
82395fbc42eSDiederik de Groot * we will reevaluate the desired size of the balloon and
82495fbc42eSDiederik de Groot * break out to retry if needed.
82595fbc42eSDiederik de Groot */
82695fbc42eSDiederik de Groot timeout = sc->vtballoon_timeout;
82795fbc42eSDiederik de Groot sc->vtballoon_timeout = 0;
82895fbc42eSDiederik de Groot
82995fbc42eSDiederik de Groot if (current > desired)
83095fbc42eSDiederik de Groot break;
83195fbc42eSDiederik de Groot else if (current < desired && timeout == 0)
83295fbc42eSDiederik de Groot break;
83395fbc42eSDiederik de Groot else if (sc->vtballoon_update_stats)
83495fbc42eSDiederik de Groot break;
83595fbc42eSDiederik de Groot else if (!timeout)
83695fbc42eSDiederik de Groot vtballoon_dprintf(sc, VTBALLOON_TRACE, "balloon %d (4K pages) reached\n", current);
83795fbc42eSDiederik de Groot
83895fbc42eSDiederik de Groot zsleep(sc, VTBALLOON_SLZ(sc), 0, "vtbslp", timeout);
83995fbc42eSDiederik de Groot }
84095fbc42eSDiederik de Groot VTBALLOON_EXIT_SLZ(sc);
84195fbc42eSDiederik de Groot
84295fbc42eSDiederik de Groot return (rc);
84395fbc42eSDiederik de Groot }
84495fbc42eSDiederik de Groot
84595fbc42eSDiederik de Groot static void
vtballoon_thread(void * arg)84695fbc42eSDiederik de Groot vtballoon_thread(void *arg)
84795fbc42eSDiederik de Groot {
84895fbc42eSDiederik de Groot struct vtballoon_softc *sc = arg;
84995fbc42eSDiederik de Groot vtballoon_dprintf(sc, VTBALLOON_TRACE, "Thread started.\n");
85095fbc42eSDiederik de Groot
85195fbc42eSDiederik de Groot uint32_t current, desired;
85295fbc42eSDiederik de Groot for (;;) {
85395fbc42eSDiederik de Groot if (vtballoon_sleep(sc) != 0)
85495fbc42eSDiederik de Groot break;
85595fbc42eSDiederik de Groot
85695fbc42eSDiederik de Groot current = sc->vtballoon_current_npages;
85795fbc42eSDiederik de Groot desired = sc->vtballoon_desired_npages;
85895fbc42eSDiederik de Groot
85995fbc42eSDiederik de Groot if (desired != current) {
86095fbc42eSDiederik de Groot if (desired > current)
86195fbc42eSDiederik de Groot vtballoon_inflate(sc, desired - current);
86295fbc42eSDiederik de Groot else
86395fbc42eSDiederik de Groot vtballoon_deflate(sc, current - desired);
86495fbc42eSDiederik de Groot
86595fbc42eSDiederik de Groot vtballoon_update_size(sc);
86695fbc42eSDiederik de Groot }
86795fbc42eSDiederik de Groot if (sc->vtballoon_update_stats) {
86895fbc42eSDiederik de Groot vtballoon_update_stats(sc);
86995fbc42eSDiederik de Groot sc->vtballoon_update_stats = false;
87095fbc42eSDiederik de Groot }
87195fbc42eSDiederik de Groot }
87295fbc42eSDiederik de Groot
87395fbc42eSDiederik de Groot kthread_exit();
87495fbc42eSDiederik de Groot }
87595fbc42eSDiederik de Groot
87695fbc42eSDiederik de Groot static void
vtballoon_get_tunables(struct vtballoon_softc * sc)87795fbc42eSDiederik de Groot vtballoon_get_tunables(struct vtballoon_softc *sc)
87895fbc42eSDiederik de Groot {
87995fbc42eSDiederik de Groot char tmpstr[64];
88095fbc42eSDiederik de Groot vtballoon_dprintf(sc, VTBALLOON_TRACE, "\n");
88195fbc42eSDiederik de Groot
88295fbc42eSDiederik de Groot TUNABLE_INT_FETCH("hw.vtballoon.debug_level", &sc->vtballoon_debug);
88395fbc42eSDiederik de Groot
88495fbc42eSDiederik de Groot ksnprintf(tmpstr, sizeof(tmpstr), "dev.vtballoon.%d.debug_level",
88595fbc42eSDiederik de Groot device_get_unit(sc->vtballoon_dev));
88695fbc42eSDiederik de Groot TUNABLE_INT_FETCH(tmpstr, &sc->vtballoon_debug);
88795fbc42eSDiederik de Groot }
88895fbc42eSDiederik de Groot
88995fbc42eSDiederik de Groot static void
vtballoon_add_sysctl(struct vtballoon_softc * sc)89095fbc42eSDiederik de Groot vtballoon_add_sysctl(struct vtballoon_softc *sc)
89195fbc42eSDiederik de Groot {
89295fbc42eSDiederik de Groot device_t dev;
89395fbc42eSDiederik de Groot struct sysctl_ctx_list *ctx;
89495fbc42eSDiederik de Groot struct sysctl_oid *tree;
89595fbc42eSDiederik de Groot struct sysctl_oid_list *child;
89695fbc42eSDiederik de Groot
89795fbc42eSDiederik de Groot dev = sc->vtballoon_dev;
89895fbc42eSDiederik de Groot vtballoon_dprintf(sc, VTBALLOON_TRACE, "\n");
89995fbc42eSDiederik de Groot
90095fbc42eSDiederik de Groot ctx = device_get_sysctl_ctx(dev);
90195fbc42eSDiederik de Groot tree = device_get_sysctl_tree(dev);
90295fbc42eSDiederik de Groot child = SYSCTL_CHILDREN(tree);
90395fbc42eSDiederik de Groot
90495fbc42eSDiederik de Groot SYSCTL_ADD_INT(ctx, child, OID_AUTO, "debug_level",
90595fbc42eSDiederik de Groot CTLFLAG_RW, &sc->vtballoon_debug, 0,
90695fbc42eSDiederik de Groot "Debug level");
90795fbc42eSDiederik de Groot
90895fbc42eSDiederik de Groot SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "desired",
90995fbc42eSDiederik de Groot CTLFLAG_RD, &sc->vtballoon_desired_npages, sizeof(uint32_t),
91095fbc42eSDiederik de Groot "Desired balloon size in pages");
91195fbc42eSDiederik de Groot
91295fbc42eSDiederik de Groot SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "current",
91395fbc42eSDiederik de Groot CTLFLAG_RD, &sc->vtballoon_current_npages, sizeof(uint32_t),
91495fbc42eSDiederik de Groot "Current balloon size in pages");
91595fbc42eSDiederik de Groot }
916