Project

General

Profile

0004-Adding-virtio-balloon-statistics.patch

ddegroot, 10/04/2018 04:37 PM

View differences:

sys/dev/virtual/virtio/balloon/virtio_balloon.c
72 72
#define VTBALLOON_INFO     	 0x01
73 73
#define VTBALLOON_ERROR    	 0x02
74 74
#define VTBALLOON_TRACE    	 0x04
75

  
76
	struct virtqueue	*vtballoon_stats_vq;
77
	struct vtballoon_stat	 vtballoon_stats[VTBALLOON_S_NR];
78
	bool			 vtballoon_update_stats;
75 79
};
76 80

  
77 81
static struct virtio_feature_desc vtballoon_feature_desc[] = {
......
81 85
	{ 0, NULL }
82 86
};
83 87

  
84
#define vtballoon_dprintf(_sc, _level, _msg, _args ...) do {            \
85
        if ((_sc)->vtballoon_debug & (_level))                          \
86
                device_printf((_sc)->vtballoon_dev, "%s:%d: "_msg,      \
87
                  __FUNCTION__, __LINE__, ##_args);                     \
88
#define vtballoon_dprintf(_sc, _level, _msg, _args ...) do {	    \
89
	if ((_sc)->vtballoon_debug & (_level))			  \
90
		device_printf((_sc)->vtballoon_dev, "%s:%d: "_msg,      \
91
		  __FUNCTION__, __LINE__, ##_args);		     \
88 92
} while (0)
89 93

  
90 94
static int		vtballoon_probe(device_t);
......
96 100
static void		vtballoon_negotiate_features(struct vtballoon_softc *);
97 101
static int		vtballoon_alloc_virtqueues(struct vtballoon_softc *);
98 102

  
99
static void		vtballoon_inflate_vq_intr(void *);
100
static void		vtballoon_deflate_vq_intr(void *);
101 103
static void 		vtballoon_config_change_intr(void *);
102 104

  
105
static void		vtballoon_update_stats(struct vtballoon_softc *sc);
106
static void		vtballoon_stats_vq_intr(void *);
107

  
108
static void		vtballoon_inflate_vq_intr(void *);
109
static void		vtballoon_deflate_vq_intr(void *);
103 110
static void		vtballoon_inflate(struct vtballoon_softc *, int);
104 111
static void		vtballoon_deflate(struct vtballoon_softc *, int);
105 112

  
......
118 125
static void		vtballoon_add_sysctl(struct vtballoon_softc *);
119 126

  
120 127
/* Features desired/implemented by this driver. */
121
#define VTBALLOON_FEATURES		0
128
//#define VTBALLOON_FEATURES		0
129
//#define VTBALLOON_FEATURES		VIRTIO_BALLOON_F_STATS_VQ | VIRTIO_BALLOON_F_MUST_TELL_HOST
130
#define VTBALLOON_FEATURES		VIRTIO_BALLOON_F_STATS_VQ
122 131

  
123 132
/* Timeout between retries when the balloon needs inflating. */
124 133
//#define VTBALLOON_LOWMEM_TIMEOUT	hz
......
137 146

  
138 147
//#define VTBALLOON_DEFAULT_DEBUG_LEVEL   VTBALLOON_ERROR
139 148
#define VTBALLOON_DEFAULT_DEBUG_LEVEL   VTBALLOON_INFO | VTBALLOON_ERROR
149
//#define VTBALLOON_DEFAULT_DEBUG_LEVEL   VTBALLOON_INFO | VTBALLOON_ERROR | VTBALLOON_TRACE
150

  
151
/*
152
 * Maximum number of interrupts to request
153
 */
154
#define VTBALLOON_MAX_INTERRUPTS	4
140 155

  
141 156
/* Must be able to fix all pages frames in one page (segment). */
142 157
CTASSERT(VTBALLOON_PAGES_PER_REQUEST * sizeof(uint32_t) <= PAGE_SIZE);
143 158

  
144
#define VTBALLOON_SLZ(_sc)              &(_sc)->vtballoon_slz
159
#define VTBALLOON_SLZ(_sc)		&(_sc)->vtballoon_slz
145 160
#define VTBALLOON_ENTER_SLZ(_sc)	lwkt_serialize_enter(VTBALLOON_SLZ(sc));
146 161
#define VTBALLOON_EXIT_SLZ(_sc)		lwkt_serialize_exit(VTBALLOON_SLZ(sc));
147 162

  
......
193 208
	int error, i;
194 209

  
195 210
	sc = device_get_softc(dev);
211
	sc->vtballoon_dev = dev;
196 212
	sc->vtballoon_debug = VTBALLOON_DEFAULT_DEBUG_LEVEL;
213

  
197 214
	vtballoon_dprintf(sc, VTBALLOON_TRACE, "\n");
198
	sc->vtballoon_dev = dev;
199 215

  
200 216
	lwkt_serialize_init(VTBALLOON_SLZ(sc));
201 217
	TAILQ_INIT(&sc->vtballoon_pages);
......
225 241
		goto fail;
226 242
	}
227 243

  
228
	struct irqmap info[3];
244
	int nrhandlers = virtio_with_feature(sc->vtballoon_dev, VIRTIO_BALLOON_F_STATS_VQ) ? 4 : 3;
245
	struct irqmap info[4];
229 246

  
230 247
	/* Possible "Virtqueue <-> IRQ" configurations */
231 248
	switch (sc->vtballoon_nintr) {
232 249
	case 1:
250
		info[2] = (struct irqmap){0, -1, vtballoon_config_change_intr, "config"};
233 251
		info[0] = (struct irqmap){0, 0, vtballoon_inflate_vq_intr, "inflate"};
234 252
		info[1] = (struct irqmap){0, 1, vtballoon_deflate_vq_intr, "deflate"};
235
		info[2] = (struct irqmap){0, -1, vtballoon_config_change_intr, "config"};
253
		info[3] = (struct irqmap){0, 2, vtballoon_stats_vq_intr, "stats"};
236 254
		break;
237 255
	case 2:
256
		info[2] = (struct irqmap){1, -1, vtballoon_config_change_intr, "config"};
238 257
		info[0] = (struct irqmap){0, 0, vtballoon_inflate_vq_intr, "inflate"};
239 258
		info[1] = (struct irqmap){0, 1, vtballoon_deflate_vq_intr, "deflate"};
240
		info[2] = (struct irqmap){1, -1, vtballoon_config_change_intr, "config"};
259
		info[3] = (struct irqmap){0, 2, vtballoon_stats_vq_intr, "stats"};
241 260
		break;
242 261
	case 3:
262
		info[2] = (struct irqmap){2, -1, vtballoon_config_change_intr, "config"};
243 263
		info[0] = (struct irqmap){0, 0, vtballoon_inflate_vq_intr, "inflate"};
244 264
		info[1] = (struct irqmap){1, 1, vtballoon_deflate_vq_intr, "deflate"};
245
		info[2] = (struct irqmap){2, -1, vtballoon_config_change_intr, "config"};
265
		info[3] = (struct irqmap){2, 2, vtballoon_stats_vq_intr, "stats"};
266
		break;
267
	case 4:
268
		info[2] = (struct irqmap){3, -1, vtballoon_config_change_intr, "config"};
269
		info[0] = (struct irqmap){0, 0, vtballoon_inflate_vq_intr, "inflate"};
270
		info[1] = (struct irqmap){1, 1, vtballoon_deflate_vq_intr, "deflate"};
271
		info[3] = (struct irqmap){2, 2, vtballoon_stats_vq_intr, "stats"};
246 272
		break;
247 273
	default:
248 274
		vtballoon_dprintf(sc, VTBALLOON_ERROR, "Invalid interrupt vector count: %d\n", sc->vtballoon_nintr);
249 275
		goto fail;
250 276
	}
251
	for (i = 0; i < 3; i++) {
277
	for (i = 0; i < nrhandlers; i++) {
252 278
		error = virtio_bind_intr(sc->vtballoon_dev, info[i].irq, info[i].idx,
253 279
		    info[i].handler, sc);
254 280
		if (error) {
255
			vtballoon_dprintf(sc, VTBALLOON_ERROR, "cannot bind virtqueue '%s' handler to IRQ:%d/%d\n", info[i].handler_name, info[i].irq, sc->vtballoon_nintr);
281
			vtballoon_dprintf(sc, VTBALLOON_ERROR, "cannot bind virtqueue '%s' handler to IRQ:%d/%d\n", 
282
				info[i].handler_name, info[i].irq, sc->vtballoon_nintr);
256 283
			goto fail;
257 284
		}
258 285
	}
......
274 301
	virtqueue_enable_intr(sc->vtballoon_inflate_vq);
275 302
	virtqueue_enable_intr(sc->vtballoon_deflate_vq);
276 303

  
304
	if (virtio_with_feature(sc->vtballoon_dev, VIRTIO_BALLOON_F_STATS_VQ)) {
305
		virtqueue_enable_intr(sc->vtballoon_stats_vq);
306
		/*
307
		 * Prime this stats virtqueue with one buffer so the hypervisor can
308
		 * use it to signal us later.
309
		 */
310
		vtballoon_update_stats(sc);
311
	}
312

  
277 313
fail:
278 314
	if (error)
279 315
		vtballoon_detach(dev);
......
303 339

  
304 340
	lwkt_serialize_handler_disable(VTBALLOON_SLZ(sc));
305 341

  
306
        for (i = 0; i < sc->vtballoon_nintr; i++)
307
                virtio_teardown_intr(dev, i);
342
	for (i = 0; i < sc->vtballoon_nintr; i++)
343
		virtio_teardown_intr(dev, i);
308 344

  
309 345
	if (device_is_attached(dev)) {
310 346
		vtballoon_pop(sc);
......
319 355
	return (0);
320 356
}
321 357

  
322
static void
323
vtballoon_config_change_intr(void *arg)
324
{
325
	struct vtballoon_softc *sc = arg;
326
	vtballoon_dprintf(sc, VTBALLOON_TRACE, "\n");
327
	wakeup_one(sc);
328
}
329

  
330 358
static void
331 359
vtballoon_negotiate_features(struct vtballoon_softc *sc)
332 360
{
......
346 374
	int intrcount = virtio_intr_count(sc->vtballoon_dev);
347 375
	int use_config = 1;
348 376

  
349
        intrcount = imin(intrcount, 3);
377
	intrcount = imin(intrcount, VTBALLOON_MAX_INTERRUPTS);
350 378
	if (intrcount < 1)
351
	        return (ENXIO);
379
		return (ENXIO);
352 380

  
353 381
	cnt = intrcount;
354 382
	error = virtio_intr_alloc(sc->vtballoon_dev, &cnt, use_config, NULL);
355 383
	if (error != 0) {
356
	        virtio_intr_release(sc->vtballoon_dev);
357
	        return (error);
384
		virtio_intr_release(sc->vtballoon_dev);
385
		return (error);
358 386
	}
359 387
	sc->vtballoon_nintr = cnt;
388
	vtballoon_dprintf(sc, VTBALLOON_TRACE, "%d Interrupts Allocated\n", sc->vtballoon_nintr);
360 389
	return (0);
361 390
}
362 391

  
......
364 393
vtballoon_alloc_virtqueues(struct vtballoon_softc *sc)
365 394
{
366 395
	device_t dev;
367
	struct vq_alloc_info vq_info[2];
396
	struct vq_alloc_info vq_info[3];
368 397
	int nvqs;
369 398

  
370 399
	dev = sc->vtballoon_dev;
......
377 406
	VQ_ALLOC_INFO_INIT(&vq_info[1], 0, &sc->vtballoon_deflate_vq,
378 407
		"%s deflate", device_get_nameunit(dev));
379 408

  
409
	if (virtio_with_feature(sc->vtballoon_dev, VIRTIO_BALLOON_F_STATS_VQ)) {
410
		VQ_ALLOC_INFO_INIT(&vq_info[2], 0, &sc->vtballoon_stats_vq,
411
			"%s stats", device_get_nameunit(dev));
412
		nvqs = 3;
413
	}
380 414
	return (virtio_alloc_virtqueues(dev, nvqs, vq_info));
381 415
}
382 416

  
417
static void
418
vtballoon_config_change_intr(void *arg)
419
{
420
	struct vtballoon_softc *sc = arg;
421
	vtballoon_dprintf(sc, VTBALLOON_TRACE, "\n");
422
	ASSERT_SERIALIZED(VTBALLOON_SLZ(sc));
423
	wakeup_one(sc);
424
}
425

  
426
static inline void
427
vtballoon_update_stat(struct vtballoon_softc *sc, int idx,
428
	uint16_t tag, uint64_t val)
429
{
430
	KASSERT(idx >= VTBALLOON_S_NR, "Stats index out of bounds");
431
	//sc->vtballoon_stats[idx].tag = virtio_is_little_endian(sc->vtballoon_dev) ? le16toh(tag) : tag;
432
	//sc->vtballoon_stats[idx].val = virtio_is_little_endian(sc->vtballoon_dev) ? le64toh(val) : val;
433
	sc->vtballoon_stats[idx].tag = le16toh(tag);
434
	sc->vtballoon_stats[idx].val = le64toh(val);
435

  
436
}
437

  
438
static unsigned int update_balloon_stats(struct vtballoon_softc *sc)
439
{
440
	#define pages_to_bytes(x) ((uint64_t)(x) << PAGE_SHIFT)
441
	unsigned int idx = 0;
442
	struct vmtotal total;
443
	struct vmmeter vmm;
444
	struct vmstats vms;
445
	size_t vmt_size = sizeof(total);
446
	size_t vmm_size = sizeof(vmm);
447
	size_t vms_size = sizeof(vms);
448

  
449
	vtballoon_dprintf(sc, VTBALLOON_TRACE, "Updating Stats Buffer\n");
450
	if (!kernel_sysctlbyname("vm.vmtotal", &total, &vmt_size, NULL, 0, NULL)) {
451
		// Total amount of free memory	// ?
452
		vtballoon_update_stat(sc, idx++, VTBALLOON_S_MEMFREE,
453
					pages_to_bytes(total.t_rm - total.t_arm));
454
		// Total amount of memory	// ?
455
		vtballoon_update_stat(sc, idx++, VTBALLOON_S_MEMTOT,
456
					pages_to_bytes(total.t_rm));
457
		// Available memory as in /proc	// ?
458
		vtballoon_update_stat(sc, idx++, VTBALLOON_S_AVAIL,
459
					pages_to_bytes(total.t_arm));
460
	}
461
	if (!kernel_sysctlbyname("vm.vmstats", &vms, &vms_size, NULL, 0, NULL)) {
462
		// Disk caches
463
		vtballoon_update_stat(sc, idx++, VTBALLOON_S_CACHES,
464
					pages_to_bytes(vms.v_cache_count));
465
	}
466
	if (!kernel_sysctlbyname("vm.vmmeter", &vmm, &vmm_size, NULL, 0, NULL)) {
467
		// Amount of memory swapped in
468
		vtballoon_update_stat(sc, idx++, VTBALLOON_S_SWAP_IN,
469
					pages_to_bytes(vmm.v_swappgsin));
470
		// Amount of memory swapped out
471
		vtballoon_update_stat(sc, idx++, VTBALLOON_S_SWAP_OUT,
472
					pages_to_bytes(vmm.v_swappgsout));
473
		// Number of major faults
474
		vtballoon_update_stat(sc, idx++, VTBALLOON_S_MAJFLT,
475
					vmm.v_vm_faults);
476
		// Number of minor faults
477
		vtballoon_update_stat(sc, idx++, VTBALLOON_S_MINFLT,
478
					vmm.v_intrans);		// ?
479
	}
480

  
481
	if (sc->vtballoon_debug & VTBALLOON_TRACE)  {
482
		static const char *vt_balloon_names[]=VTBALLOON_S_NAMES;
483
		int i;
484
		for (i=0; i < idx; i++) {
485
			kprintf("\t%s = %lu\n", vt_balloon_names[sc->vtballoon_stats[i].tag], sc->vtballoon_stats[i].val);
486
		}
487
	}
488

  
489
	return idx;
490
}
491

  
492
static void
493
vtballoon_update_stats(struct vtballoon_softc *sc)
494
{
495
	struct virtqueue *vq = sc->vtballoon_stats_vq;
496

  
497
	ASSERT_SERIALIZED(VTBALLOON_SLZ(sc));
498

  
499
	vtballoon_dprintf(sc, VTBALLOON_TRACE, "Return Requested Stats\n");
500

  
501
	struct sglist sg;
502
	struct sglist_seg segs[1];
503
	unsigned int num_stats;
504
	int error;
505

  
506
	num_stats = update_balloon_stats(sc);
507

  
508
	sglist_init(&sg, 1, segs);
509
	error = sglist_append(&sg, sc->vtballoon_stats, sizeof(sc->vtballoon_stats[0]) * num_stats);
510
	KASSERT(error == 0, ("error adding page frames to sglist"));
511

  
512
	error = virtqueue_enqueue(vq, vq, &sg, 1, 0);
513
	KASSERT(error == 0, ("error enqueuing page frames to virtqueue"));
514
	virtqueue_notify(sc->vtballoon_stats_vq, NULL);
515
}
516

  
517
/*
518
 * While most virtqueues communicate guest-initiated requests to the hypervisor,
519
 * the stats queue operates in reverse.  The driver(host) initializes the virtqueue
520
 * with a single buffer. From that point forward, all conversations consist of
521
 * a hypervisor request (a call to this function) which directs us to refill
522
 * the virtqueue with a fresh stats buffer. Since stats collection can sleep,
523
 * we delegate the job to the vtballoon_thread which will do the actual stats
524
 * collecting work.
525
 */
526
static void
527
vtballoon_stats_vq_intr(void *arg)
528
{
529
	struct vtballoon_softc *sc = arg;
530
	struct virtqueue *vq = sc->vtballoon_stats_vq;
531

  
532
	ASSERT_SERIALIZED(VTBALLOON_SLZ(sc));
533
	if (sc->vtballoon_update_stats || !virtqueue_pending(vq))
534
		return;
535

  
536
	vtballoon_dprintf(sc, VTBALLOON_TRACE, "Ballooon Stats Requested\n");
537
	sc->vtballoon_update_stats = true;
538
	wakeup_one(sc);
539
	virtqueue_dequeue(vq, NULL);
540
}
541

  
383 542
static void
384 543
vtballoon_inflate_vq_intr(void *arg)
385 544
{
386 545
	struct vtballoon_softc *sc = arg;
546
	struct virtqueue *vq = sc->vtballoon_inflate_vq;
387 547
	ASSERT_SERIALIZED(VTBALLOON_SLZ(sc));
548
	if (!virtqueue_pending(vq))
549
		return;
388 550
	wakeup_one(sc);
389 551
}
390 552

  
......
392 554
vtballoon_deflate_vq_intr(void *arg)
393 555
{
394 556
	struct vtballoon_softc *sc = arg;
557
	struct virtqueue *vq = sc->vtballoon_deflate_vq;
395 558
	ASSERT_SERIALIZED(VTBALLOON_SLZ(sc));
559
	if (!virtqueue_pending(vq))
560
		return;
396 561
	wakeup_one(sc);
397 562
}
398 563

  
......
527 692

  
528 693
	virtqueue_disable_intr(sc->vtballoon_inflate_vq);
529 694
	virtqueue_disable_intr(sc->vtballoon_deflate_vq);
530

  
695
/*
696
	if (virtio_with_feature(sc->vtballoon_dev, VIRTIO_BALLOON_F_STATS_VQ)) {
697
		virtqueue_disable_intr(sc->vtballoon_stats_vq);
698
	}
699
*/
531 700
	virtio_stop(sc->vtballoon_dev);
532 701
}
533 702

  
......
538 707

  
539 708
	m = vm_page_alloc(NULL, 0, sc->vtballoon_pagereq);
540 709
	/*m = vm_page_alloc_contig(vm_paddr_t low, vm_paddr_t high,
541
	             unsigned long alignment, unsigned long boundary,
542
	             unsigned long size, vm_memattr_t memattr)*/		// ? Would alloc_contig be a better match for balloon ?
710
		unsigned long alignment, unsigned long boundary,
711
		unsigned long size, vm_memattr_t memattr)*/			// ? Would alloc_contig be a better match for balloon ?
543 712
	if (m != NULL)
544 713
		sc->vtballoon_current_npages++;
545 714

  
......
611 780
			break;
612 781
		else if (current < desired && timeout == 0)
613 782
			break;
783
		else if (sc->vtballoon_update_stats)
784
			break;
614 785
		else if (!timeout)
615 786
			vtballoon_dprintf(sc, VTBALLOON_INFO, "balloon %d (4K pages) reached\n", current);
616 787

  
......
643 814

  
644 815
			vtballoon_update_size(sc);
645 816
		}
817
		if (sc->vtballoon_update_stats) {
818
			vtballoon_update_stats(sc);
819
			sc->vtballoon_update_stats = false;
820
		}
646 821
	}
647 822

  
648 823
	kthread_exit();
sys/dev/virtual/virtio/balloon/virtio_balloon.h
47 47
	uint32_t actual;
48 48
};
49 49

  
50
#define VIRTIO_BALLOON_S_SWAP_IN  0   /* Amount of memory swapped in */
51
#define VIRTIO_BALLOON_S_SWAP_OUT 1   /* Amount of memory swapped out */
52
#define VIRTIO_BALLOON_S_MAJFLT   2   /* Number of major faults */
53
#define VIRTIO_BALLOON_S_MINFLT   3   /* Number of minor faults */
54
#define VIRTIO_BALLOON_S_MEMFREE  4   /* Total amount of free memory */
55
#define VIRTIO_BALLOON_S_MEMTOT   5   /* Total amount of memory */
56
#define VIRTIO_BALLOON_S_NR       6
57

  
58
struct virtio_balloon_stat {
50
#define VTBALLOON_S_SWAP_IN	 0	/* The amount of memory that has been swapped in (in bytes) */
51
#define VTBALLOON_S_SWAP_OUT	 1	/* The amount of memory that has been swapped out to disk (in bytes). */
52
#define VTBALLOON_S_MAJFLT	 2	/* The number of major page faults that have occurred. */
53
#define VTBALLOON_S_MINFLT	 3	/* The number of minor page faults that have occurred. */
54
#define VTBALLOON_S_MEMFREE	 4	/* The amount of memory not being used for any purpose (in bytes). */
55
#define VTBALLOON_S_MEMTOT	 5	/* The total amount of memory available (in bytes). */
56
#define VTBALLOON_S_AVAIL	 6	/* The amount of availabe memory (in bytes) as in linux-proc */
57
#define VTBALLOON_S_CACHES	 7	/* Disk-File caches */
58
#define VTBALLOON_S_HTLB_PGALLOC 8	/* Hugetlb page allocations */
59
#define VTBALLOON_S_HTLB_PGFAIL	 9	/* Hugetlb page allocation failures */
60
#define VTBALLOON_S_NR		10
61

  
62
#define VTBALLOON_S_NAMES_WITH_PREFIX(VTBALLOON_S_NAMES_prefix) { \
63
	VTBALLOON_S_NAMES_prefix "swap-in", \
64
	VTBALLOON_S_NAMES_prefix "swap-out", \
65
	VTBALLOON_S_NAMES_prefix "major-faults", \
66
	VTBALLOON_S_NAMES_prefix "minor-faults", \
67
	VTBALLOON_S_NAMES_prefix "free-memory", \
68
	VTBALLOON_S_NAMES_prefix "total-memory", \
69
	VTBALLOON_S_NAMES_prefix "available-memory", \
70
	VTBALLOON_S_NAMES_prefix "disk-file-caches", \
71
	VTBALLOON_S_NAMES_prefix "hugetlb-allocations", \
72
	VTBALLOON_S_NAMES_prefix "hugetlb-failures" \
73
}
74
#define VTBALLOON_S_NAMES VTBALLOON_S_NAMES_WITH_PREFIX("")
75

  
76
/*
77
 * Memory statistics structure.
78
 * Driver fills an array of these structures and passes to device.
79
 *
80
 * NOTE: fields are laid out in a way that would make compiler add padding
81
 * between and after fields, but the virtio specification does not allow
82
 * for this. So the struct has to be packed.
83
 */
84
struct vtballoon_stat {
59 85
	uint16_t tag;
60 86
	uint64_t val;
61 87
} __packed;
62
-