1 /* $OpenBSD: viomb.c,v 1.13 2024/12/20 22:18:27 sf Exp $ */
2 /* $NetBSD: viomb.c,v 1.1 2011/10/30 12:12:21 hannken Exp $ */
3
4 /*
5 * Copyright (c) 2012 Talypov Dinar <dinar@i-nk.ru>
6 * Copyright (c) 2010 Minoura Makoto.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/device.h>
33 #include <sys/task.h>
34 #include <sys/pool.h>
35 #include <sys/sensors.h>
36
37 #include <uvm/uvm_extern.h>
38
39 #include <dev/pv/virtioreg.h>
40 #include <dev/pv/virtiovar.h>
41
42 #if VIRTIO_PAGE_SIZE!=PAGE_SIZE
43 #error non-4K page sizes are not supported yet
44 #endif
45
46 #define DEVNAME(sc) sc->sc_dev.dv_xname
47 #if VIRTIO_DEBUG
48 #define VIOMBDEBUG(sc, format, args...) \
49 do { printf("%s: " format, sc->sc_dev.dv_xname, ##args);} \
50 while (0)
51 #else
52 #define VIOMBDEBUG(...)
53 #endif
54
55 /* flags used to specify kind of operation,
56 * actually should be moved to virtiovar.h
57 */
58 #define VRING_READ 0
59 #define VRING_WRITE 1
60
61 /* notify or don't notify */
62 #define VRING_NO_NOTIFY 0
63 #define VRING_NOTIFY 1
64
65 /* Configuration registers */
66 #define VIRTIO_BALLOON_CONFIG_NUM_PAGES 0 /* 32bit */
67 #define VIRTIO_BALLOON_CONFIG_ACTUAL 4 /* 32bit */
68
69 /* Feature bits */
70 #define VIRTIO_BALLOON_F_MUST_TELL_HOST (1ULL<<0)
71 #define VIRTIO_BALLOON_F_STATS_VQ (1ULL<<1)
72
73 static const struct virtio_feature_name viomb_feature_names[] = {
74 #if VIRTIO_DEBUG
75 {VIRTIO_BALLOON_F_MUST_TELL_HOST, "TellHost"},
76 {VIRTIO_BALLOON_F_STATS_VQ, "StatVQ"},
77 #endif
78 {0, NULL}
79 };
80 #define PGS_PER_REQ 256 /* 1MB, 4KB/page */
81 #define VQ_INFLATE 0
82 #define VQ_DEFLATE 1
83
84 struct balloon_req {
85 bus_dmamap_t bl_dmamap;
86 struct pglist bl_pglist;
87 int bl_nentries;
88 u_int32_t *bl_pages;
89 };
90
91 struct viomb_softc {
92 struct device sc_dev;
93 struct virtio_softc *sc_virtio;
94 struct virtqueue sc_vq[2];
95 u_int32_t sc_npages; /* desired pages */
96 u_int32_t sc_actual; /* current pages */
97 struct balloon_req sc_req;
98 struct taskq *sc_taskq;
99 struct task sc_task;
100 struct pglist sc_balloon_pages;
101 struct ksensor sc_sens[2];
102 struct ksensordev sc_sensdev;
103 };
104
105 int viomb_match(struct device *, void *, void *);
106 void viomb_attach(struct device *, struct device *, void *);
107 void viomb_worker(void *);
108 void viomb_inflate(struct viomb_softc *);
109 void viomb_deflate(struct viomb_softc *);
110 int viomb_config_change(struct virtio_softc *);
111 void viomb_read_config(struct viomb_softc *);
112 int viomb_vq_dequeue(struct virtqueue *);
113 int viomb_inflate_intr(struct virtqueue *);
114 int viomb_deflate_intr(struct virtqueue *);
115
116 const struct cfattach viomb_ca = {
117 sizeof(struct viomb_softc), viomb_match, viomb_attach
118 };
119
120 struct cfdriver viomb_cd = {
121 NULL, "viomb", DV_DULL
122 };
123
124 int
viomb_match(struct device * parent,void * match,void * aux)125 viomb_match(struct device *parent, void *match, void *aux)
126 {
127 struct virtio_attach_args *va = aux;
128 if (va->va_devid == PCI_PRODUCT_VIRTIO_BALLOON)
129 return (1);
130 return (0);
131 }
132
133 void
viomb_attach(struct device * parent,struct device * self,void * aux)134 viomb_attach(struct device *parent, struct device *self, void *aux)
135 {
136 struct viomb_softc *sc = (struct viomb_softc *)self;
137 struct virtio_softc *vsc = (struct virtio_softc *)parent;
138 struct virtio_attach_args *va = aux;
139 int i;
140
141 if (vsc->sc_child != NULL) {
142 printf("child already attached for %s; something wrong...\n",
143 parent->dv_xname);
144 return;
145 }
146
147 /* fail on non-4K page size archs */
148 if (VIRTIO_PAGE_SIZE != PAGE_SIZE){
149 printf("non-4K page size arch found, needs %d, got %d\n",
150 VIRTIO_PAGE_SIZE, PAGE_SIZE);
151 return;
152 }
153
154 sc->sc_virtio = vsc;
155 vsc->sc_vqs = &sc->sc_vq[VQ_INFLATE];
156 vsc->sc_nvqs = 0;
157 vsc->sc_child = self;
158 vsc->sc_ipl = IPL_BIO;
159 vsc->sc_config_change = viomb_config_change;
160
161 vsc->sc_driver_features = VIRTIO_BALLOON_F_MUST_TELL_HOST;
162 if (virtio_negotiate_features(vsc, viomb_feature_names) != 0)
163 goto err;
164
165 if ((virtio_alloc_vq(vsc, &sc->sc_vq[VQ_INFLATE], VQ_INFLATE, 1,
166 "inflate") != 0))
167 goto err;
168 vsc->sc_nvqs++;
169 if ((virtio_alloc_vq(vsc, &sc->sc_vq[VQ_DEFLATE], VQ_DEFLATE, 1,
170 "deflate") != 0))
171 goto err;
172 vsc->sc_nvqs++;
173
174 sc->sc_vq[VQ_INFLATE].vq_done = viomb_inflate_intr;
175 sc->sc_vq[VQ_DEFLATE].vq_done = viomb_deflate_intr;
176 virtio_start_vq_intr(vsc, &sc->sc_vq[VQ_INFLATE]);
177 virtio_start_vq_intr(vsc, &sc->sc_vq[VQ_DEFLATE]);
178
179 viomb_read_config(sc);
180 TAILQ_INIT(&sc->sc_balloon_pages);
181
182 if ((sc->sc_req.bl_pages = dma_alloc(sizeof(u_int32_t) * PGS_PER_REQ,
183 PR_NOWAIT|PR_ZERO)) == NULL) {
184 printf("%s: Can't alloc DMA memory.\n", DEVNAME(sc));
185 goto err;
186 }
187 if (bus_dmamap_create(vsc->sc_dmat, sizeof(u_int32_t) * PGS_PER_REQ,
188 1, sizeof(u_int32_t) * PGS_PER_REQ, 0,
189 BUS_DMA_NOWAIT, &sc->sc_req.bl_dmamap)) {
190 printf("%s: dmamap creation failed.\n", DEVNAME(sc));
191 goto err;
192 }
193 if (bus_dmamap_load(vsc->sc_dmat, sc->sc_req.bl_dmamap,
194 &sc->sc_req.bl_pages[0],
195 sizeof(uint32_t) * PGS_PER_REQ,
196 NULL, BUS_DMA_NOWAIT)) {
197 printf("%s: dmamap load failed.\n", DEVNAME(sc));
198 goto err_dmamap;
199 }
200
201 sc->sc_taskq = taskq_create("viomb", 1, IPL_BIO, 0);
202 if (sc->sc_taskq == NULL)
203 goto err_dmamap;
204 task_set(&sc->sc_task, viomb_worker, sc);
205
206 strlcpy(sc->sc_sensdev.xname, DEVNAME(sc),
207 sizeof(sc->sc_sensdev.xname));
208 strlcpy(sc->sc_sens[0].desc, "desired",
209 sizeof(sc->sc_sens[0].desc));
210 sc->sc_sens[0].type = SENSOR_INTEGER;
211 sensor_attach(&sc->sc_sensdev, &sc->sc_sens[0]);
212 sc->sc_sens[0].value = sc->sc_npages << PAGE_SHIFT;
213
214 strlcpy(sc->sc_sens[1].desc, "current",
215 sizeof(sc->sc_sens[1].desc));
216 sc->sc_sens[1].type = SENSOR_INTEGER;
217 sensor_attach(&sc->sc_sensdev, &sc->sc_sens[1]);
218 sc->sc_sens[1].value = sc->sc_actual << PAGE_SHIFT;
219
220 sensordev_install(&sc->sc_sensdev);
221
222 printf("\n");
223 if (virtio_attach_finish(vsc, va) != 0)
224 goto err_dmamap;
225 return;
226
227 err_dmamap:
228 bus_dmamap_destroy(vsc->sc_dmat, sc->sc_req.bl_dmamap);
229 err:
230 if (sc->sc_req.bl_pages)
231 dma_free(sc->sc_req.bl_pages, sizeof(u_int32_t) * PGS_PER_REQ);
232 for (i = 0; i < vsc->sc_nvqs; i++)
233 virtio_free_vq(vsc, &sc->sc_vq[i]);
234 vsc->sc_nvqs = 0;
235 vsc->sc_child = VIRTIO_CHILD_ERROR;
236 return;
237 }
238
239 /*
240 * Config change
241 */
242 int
viomb_config_change(struct virtio_softc * vsc)243 viomb_config_change(struct virtio_softc *vsc)
244 {
245 struct viomb_softc *sc = (struct viomb_softc *)vsc->sc_child;
246
247 task_add(sc->sc_taskq, &sc->sc_task);
248
249 return (1);
250 }
251
252 void
viomb_worker(void * arg1)253 viomb_worker(void *arg1)
254 {
255 struct viomb_softc *sc = (struct viomb_softc *)arg1;
256 int s;
257
258 s = splbio();
259 viomb_read_config(sc);
260 if (sc->sc_npages > sc->sc_actual){
261 VIOMBDEBUG(sc, "inflating balloon from %u to %u.\n",
262 sc->sc_actual, sc->sc_npages);
263 viomb_inflate(sc);
264 }
265 else if (sc->sc_npages < sc->sc_actual){
266 VIOMBDEBUG(sc, "deflating balloon from %u to %u.\n",
267 sc->sc_actual, sc->sc_npages);
268 viomb_deflate(sc);
269 }
270
271 sc->sc_sens[0].value = sc->sc_npages << PAGE_SHIFT;
272 sc->sc_sens[1].value = sc->sc_actual << PAGE_SHIFT;
273
274 splx(s);
275 }
276
277 void
viomb_inflate(struct viomb_softc * sc)278 viomb_inflate(struct viomb_softc *sc)
279 {
280 struct virtio_softc *vsc = (struct virtio_softc *)sc->sc_virtio;
281 struct balloon_req *b;
282 struct vm_page *p;
283 struct virtqueue *vq = &sc->sc_vq[VQ_INFLATE];
284 u_int32_t nvpages;
285 int slot, error, i = 0;
286
287 nvpages = sc->sc_npages - sc->sc_actual;
288 if (nvpages > PGS_PER_REQ)
289 nvpages = PGS_PER_REQ;
290 b = &sc->sc_req;
291
292 if ((error = uvm_pglistalloc(nvpages * PAGE_SIZE, 0,
293 dma_constraint.ucr_high,
294 0, 0, &b->bl_pglist, nvpages,
295 UVM_PLA_NOWAIT))) {
296 printf("%s unable to allocate %u physmem pages,"
297 "error %d\n", DEVNAME(sc), nvpages, error);
298 return;
299 }
300
301 b->bl_nentries = nvpages;
302 TAILQ_FOREACH(p, &b->bl_pglist, pageq)
303 b->bl_pages[i++] = p->phys_addr / VIRTIO_PAGE_SIZE;
304
305 KASSERT(i == nvpages);
306
307 if ((virtio_enqueue_prep(vq, &slot)) > 0) {
308 printf("%s:virtio_enqueue_prep() vq_num %d\n",
309 DEVNAME(sc), vq->vq_num);
310 goto err;
311 }
312 if (virtio_enqueue_reserve(vq, slot, 1)) {
313 printf("%s:virtio_enqueue_reserve vq_num %d\n",
314 DEVNAME(sc), vq->vq_num);
315 goto err;
316 }
317 bus_dmamap_sync(vsc->sc_dmat, b->bl_dmamap, 0,
318 sizeof(u_int32_t) * nvpages, BUS_DMASYNC_PREWRITE);
319 virtio_enqueue_p(vq, slot, b->bl_dmamap, 0,
320 sizeof(u_int32_t) * nvpages, VRING_READ);
321 virtio_enqueue_commit(vsc, vq, slot, VRING_NOTIFY);
322 return;
323 err:
324 uvm_pglistfree(&b->bl_pglist);
325 return;
326 }
327
328 void
viomb_deflate(struct viomb_softc * sc)329 viomb_deflate(struct viomb_softc *sc)
330 {
331 struct virtio_softc *vsc = (struct virtio_softc *)sc->sc_virtio;
332 struct balloon_req *b;
333 struct vm_page *p;
334 struct virtqueue *vq = &sc->sc_vq[VQ_DEFLATE];
335 u_int64_t nvpages;
336 int i, slot;
337
338 nvpages = sc->sc_actual - sc->sc_npages;
339 if (nvpages > PGS_PER_REQ)
340 nvpages = PGS_PER_REQ;
341 b = &sc->sc_req;
342 b->bl_nentries = nvpages;
343
344 TAILQ_INIT(&b->bl_pglist);
345 for (i = 0; i < nvpages; i++) {
346 p = TAILQ_FIRST(&sc->sc_balloon_pages);
347 if (p == NULL){
348 b->bl_nentries = i - 1;
349 break;
350 }
351 TAILQ_REMOVE(&sc->sc_balloon_pages, p, pageq);
352 TAILQ_INSERT_TAIL(&b->bl_pglist, p, pageq);
353 b->bl_pages[i] = p->phys_addr / VIRTIO_PAGE_SIZE;
354 }
355
356 if (virtio_enqueue_prep(vq, &slot)) {
357 printf("%s:virtio_get_slot(def) vq_num %d\n",
358 DEVNAME(sc), vq->vq_num);
359 goto err;
360 }
361 if (virtio_enqueue_reserve(vq, slot, 1)) {
362 printf("%s:virtio_enqueue_reserve() vq_num %d\n",
363 DEVNAME(sc), vq->vq_num);
364 goto err;
365 }
366 bus_dmamap_sync(vsc->sc_dmat, b->bl_dmamap, 0,
367 sizeof(u_int32_t) * nvpages,
368 BUS_DMASYNC_PREWRITE);
369 virtio_enqueue_p(vq, slot, b->bl_dmamap, 0,
370 sizeof(u_int32_t) * nvpages, VRING_READ);
371
372 if (!virtio_has_feature(vsc, VIRTIO_BALLOON_F_MUST_TELL_HOST))
373 uvm_pglistfree(&b->bl_pglist);
374 virtio_enqueue_commit(vsc, vq, slot, VRING_NOTIFY);
375 return;
376 err:
377 TAILQ_CONCAT(&sc->sc_balloon_pages, &b->bl_pglist, pageq);
378 return;
379 }
380
381 void
viomb_read_config(struct viomb_softc * sc)382 viomb_read_config(struct viomb_softc *sc)
383 {
384 struct virtio_softc *vsc = (struct virtio_softc *)sc->sc_virtio;
385 u_int32_t reg;
386
387 /* these values are explicitly specified as little-endian */
388 reg = virtio_read_device_config_4(vsc, VIRTIO_BALLOON_CONFIG_NUM_PAGES);
389 sc->sc_npages = letoh32(reg);
390 reg = virtio_read_device_config_4(vsc, VIRTIO_BALLOON_CONFIG_ACTUAL);
391 sc->sc_actual = letoh32(reg);
392 VIOMBDEBUG(sc, "sc->sc_npages %u, sc->sc_actual %u\n",
393 sc->sc_npages, sc->sc_actual);
394 }
395
396 int
viomb_vq_dequeue(struct virtqueue * vq)397 viomb_vq_dequeue(struct virtqueue *vq)
398 {
399 struct virtio_softc *vsc = vq->vq_owner;
400 struct viomb_softc *sc = (struct viomb_softc *)vsc->sc_child;
401 int r, slot;
402
403 r = virtio_dequeue(vsc, vq, &slot, NULL);
404 if (r != 0) {
405 printf("%s: dequeue failed, errno %d\n", DEVNAME(sc), r);
406 return(r);
407 }
408 virtio_dequeue_commit(vq, slot);
409 return(0);
410 }
411
412 /*
413 * interrupt handling for vq's
414 */
415 int
viomb_inflate_intr(struct virtqueue * vq)416 viomb_inflate_intr(struct virtqueue *vq)
417 {
418 struct virtio_softc *vsc = vq->vq_owner;
419 struct viomb_softc *sc = (struct viomb_softc *)vsc->sc_child;
420 struct balloon_req *b;
421 u_int64_t nvpages;
422
423 if (viomb_vq_dequeue(vq))
424 return(1);
425
426 b = &sc->sc_req;
427 nvpages = b->bl_nentries;
428 bus_dmamap_sync(vsc->sc_dmat, b->bl_dmamap, 0,
429 sizeof(u_int32_t) * nvpages,
430 BUS_DMASYNC_POSTWRITE);
431 TAILQ_CONCAT(&sc->sc_balloon_pages, &b->bl_pglist, pageq);
432 VIOMBDEBUG(sc, "updating sc->sc_actual from %u to %llu\n",
433 sc->sc_actual, sc->sc_actual + nvpages);
434 virtio_write_device_config_4(vsc, VIRTIO_BALLOON_CONFIG_ACTUAL,
435 sc->sc_actual + nvpages);
436 viomb_read_config(sc);
437
438 /* if we have more work to do, add it to the task list */
439 if (sc->sc_npages > sc->sc_actual)
440 task_add(sc->sc_taskq, &sc->sc_task);
441
442 return (1);
443 }
444
445 int
viomb_deflate_intr(struct virtqueue * vq)446 viomb_deflate_intr(struct virtqueue *vq)
447 {
448 struct virtio_softc *vsc = vq->vq_owner;
449 struct viomb_softc *sc = (struct viomb_softc *)vsc->sc_child;
450 struct balloon_req *b;
451 u_int64_t nvpages;
452
453 if (viomb_vq_dequeue(vq))
454 return(1);
455
456 b = &sc->sc_req;
457 nvpages = b->bl_nentries;
458 bus_dmamap_sync(vsc->sc_dmat, b->bl_dmamap, 0,
459 sizeof(u_int32_t) * nvpages,
460 BUS_DMASYNC_POSTWRITE);
461
462 if (virtio_has_feature(vsc, VIRTIO_BALLOON_F_MUST_TELL_HOST))
463 uvm_pglistfree(&b->bl_pglist);
464
465 VIOMBDEBUG(sc, "updating sc->sc_actual from %u to %llu\n",
466 sc->sc_actual, sc->sc_actual - nvpages);
467 virtio_write_device_config_4(vsc, VIRTIO_BALLOON_CONFIG_ACTUAL,
468 sc->sc_actual - nvpages);
469 viomb_read_config(sc);
470
471 /* if we have more work to do, add it to tasks list */
472 if (sc->sc_npages < sc->sc_actual)
473 task_add(sc->sc_taskq, &sc->sc_task);
474
475 return(1);
476 }
477