xref: /dflybsd-src/sys/dev/virtual/virtio/balloon/virtio_balloon.c (revision d147c94391cf5cc415970d2b885fcc931026c34e)
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