1 #include "kvm/virtio-balloon.h"
3 #include "kvm/virtio-pci-dev.h"
5 #include "kvm/virtio.h"
9 #include "kvm/threadpool.h"
10 #include "kvm/guest_compat.h"
11 #include "kvm/virtio-pci.h"
13 #include <linux/virtio_ring.h>
14 #include <linux/virtio_balloon.h>
16 #include <linux/kernel.h>
17 #include <linux/list.h>
19 #include <sys/types.h>
23 #include <sys/eventfd.h>
25 #define NUM_VIRT_QUEUES 3
26 #define VIRTIO_BLN_QUEUE_SIZE 128
27 #define VIRTIO_BLN_INFLATE 0
28 #define VIRTIO_BLN_DEFLATE 1
29 #define VIRTIO_BLN_STATS 2
32 struct list_head list
;
33 struct virtio_pci vpci
;
38 struct virt_queue vqs
[NUM_VIRT_QUEUES
];
39 struct thread_pool__job jobs
[NUM_VIRT_QUEUES
];
41 struct virtio_balloon_stat stats
[VIRTIO_BALLOON_S_NR
];
42 struct virtio_balloon_stat
*cur_stat
;
48 struct virtio_balloon_config config
;
51 static struct bln_dev bdev
;
52 extern struct kvm
*kvm
;
54 static bool virtio_bln_do_io_request(struct kvm
*kvm
, struct bln_dev
*bdev
, struct virt_queue
*queue
)
56 struct iovec iov
[VIRTIO_BLN_QUEUE_SIZE
];
61 head
= virt_queue__get_iov(queue
, iov
, &out
, &in
, kvm
);
62 ptrs
= iov
[0].iov_base
;
63 len
= iov
[0].iov_len
/ sizeof(u32
);
65 for (i
= 0 ; i
< len
; i
++) {
68 guest_ptr
= guest_flat_to_host(kvm
, ptrs
[i
] << VIRTIO_BALLOON_PFN_SHIFT
);
69 if (queue
== &bdev
->vqs
[VIRTIO_BLN_INFLATE
]) {
70 madvise(guest_ptr
, 1 << VIRTIO_BALLOON_PFN_SHIFT
, MADV_DONTNEED
);
71 bdev
->config
.actual
++;
72 } else if (queue
== &bdev
->vqs
[VIRTIO_BLN_DEFLATE
]) {
73 bdev
->config
.actual
--;
77 virt_queue__set_used_elem(queue
, head
, len
);
82 static bool virtio_bln_do_stat_request(struct kvm
*kvm
, struct bln_dev
*bdev
, struct virt_queue
*queue
)
84 struct iovec iov
[VIRTIO_BLN_QUEUE_SIZE
];
86 struct virtio_balloon_stat
*stat
;
89 head
= virt_queue__get_iov(queue
, iov
, &out
, &in
, kvm
);
90 stat
= iov
[0].iov_base
;
92 /* Initial empty stat buffer */
93 if (bdev
->cur_stat
== NULL
) {
94 bdev
->cur_stat
= stat
;
95 bdev
->cur_stat_head
= head
;
100 memcpy(bdev
->stats
, stat
, iov
[0].iov_len
);
102 bdev
->stat_count
= iov
[0].iov_len
/ sizeof(struct virtio_balloon_stat
);
103 bdev
->cur_stat
= stat
;
104 bdev
->cur_stat_head
= head
;
106 if (write(bdev
->stat_waitfd
, &wait_val
, sizeof(wait_val
)) <= 0)
112 static void virtio_bln_do_io(struct kvm
*kvm
, void *param
)
114 struct virt_queue
*vq
= param
;
116 if (vq
== &bdev
.vqs
[VIRTIO_BLN_STATS
]) {
117 virtio_bln_do_stat_request(kvm
, &bdev
, vq
);
118 virtio_pci__signal_vq(kvm
, &bdev
.vpci
, VIRTIO_BLN_STATS
);
122 while (virt_queue__available(vq
)) {
123 virtio_bln_do_io_request(kvm
, &bdev
, vq
);
124 virtio_pci__signal_vq(kvm
, &bdev
.vpci
, vq
- bdev
.vqs
);
128 static int virtio_bln__collect_stats(void)
132 virt_queue__set_used_elem(&bdev
.vqs
[VIRTIO_BLN_STATS
], bdev
.cur_stat_head
,
133 sizeof(struct virtio_balloon_stat
));
134 virtio_pci__signal_vq(kvm
, &bdev
.vpci
, VIRTIO_BLN_STATS
);
136 if (read(bdev
.stat_waitfd
, &tmp
, sizeof(tmp
)) <= 0)
142 static int virtio_bln__print_stats(void)
146 if (virtio_bln__collect_stats() < 0)
149 printf("\n\n\t*** Guest memory statistics ***\n\n");
150 for (i
= 0; i
< bdev
.stat_count
; i
++) {
151 switch (bdev
.stats
[i
].tag
) {
152 case VIRTIO_BALLOON_S_SWAP_IN
:
153 printf("The amount of memory that has been swapped in (in bytes):");
155 case VIRTIO_BALLOON_S_SWAP_OUT
:
156 printf("The amount of memory that has been swapped out to disk (in bytes):");
158 case VIRTIO_BALLOON_S_MAJFLT
:
159 printf("The number of major page faults that have occurred:");
161 case VIRTIO_BALLOON_S_MINFLT
:
162 printf("The number of minor page faults that have occurred:");
164 case VIRTIO_BALLOON_S_MEMFREE
:
165 printf("The amount of memory not being used for any purpose (in bytes):");
167 case VIRTIO_BALLOON_S_MEMTOT
:
168 printf("The total amount of memory available (in bytes):");
171 printf("%llu\n", bdev
.stats
[i
].val
);
178 static void handle_sigmem(int sig
)
180 if (sig
== SIGKVMADDMEM
) {
181 bdev
.config
.num_pages
+= 256;
182 } else if (sig
== SIGKVMDELMEM
) {
183 if (bdev
.config
.num_pages
< 256)
186 bdev
.config
.num_pages
-= 256;
187 } else if (sig
== SIGKVMMEMSTAT
) {
188 virtio_bln__print_stats();
193 /* Notify that the configuration space has changed */
194 virtio_pci__signal_config(kvm
, &bdev
.vpci
);
197 static void set_config(struct kvm
*kvm
, void *dev
, u8 data
, u32 offset
)
199 struct bln_dev
*bdev
= dev
;
201 ((u8
*)(&bdev
->config
))[offset
] = data
;
204 static u8
get_config(struct kvm
*kvm
, void *dev
, u32 offset
)
206 struct bln_dev
*bdev
= dev
;
208 return ((u8
*)(&bdev
->config
))[offset
];
211 static u32
get_host_features(struct kvm
*kvm
, void *dev
)
213 return 1 << VIRTIO_BALLOON_F_STATS_VQ
;
216 static void set_guest_features(struct kvm
*kvm
, void *dev
, u32 features
)
218 struct bln_dev
*bdev
= dev
;
220 bdev
->features
= features
;
223 static int init_vq(struct kvm
*kvm
, void *dev
, u32 vq
, u32 pfn
)
225 struct bln_dev
*bdev
= dev
;
226 struct virt_queue
*queue
;
229 compat__remove_message(bdev
->compat_id
);
231 queue
= &bdev
->vqs
[vq
];
233 p
= guest_pfn_to_host(kvm
, queue
->pfn
);
235 thread_pool__init_job(&bdev
->jobs
[vq
], kvm
, virtio_bln_do_io
, queue
);
236 vring_init(&queue
->vring
, VIRTIO_BLN_QUEUE_SIZE
, p
, VIRTIO_PCI_VRING_ALIGN
);
241 static int notify_vq(struct kvm
*kvm
, void *dev
, u32 vq
)
243 struct bln_dev
*bdev
= dev
;
245 thread_pool__do_job(&bdev
->jobs
[vq
]);
250 static int get_pfn_vq(struct kvm
*kvm
, void *dev
, u32 vq
)
252 struct bln_dev
*bdev
= dev
;
254 return bdev
->vqs
[vq
].pfn
;
257 static int get_size_vq(struct kvm
*kvm
, void *dev
, u32 vq
)
259 return VIRTIO_BLN_QUEUE_SIZE
;
262 void virtio_bln__init(struct kvm
*kvm
)
264 signal(SIGKVMADDMEM
, handle_sigmem
);
265 signal(SIGKVMDELMEM
, handle_sigmem
);
266 signal(SIGKVMMEMSTAT
, handle_sigmem
);
268 bdev
.stat_waitfd
= eventfd(0, 0);
269 memset(&bdev
.config
, 0, sizeof(struct virtio_balloon_config
));
271 virtio_pci__init(kvm
, &bdev
.vpci
, &bdev
, PCI_DEVICE_ID_VIRTIO_BLN
, VIRTIO_ID_BALLOON
);
272 bdev
.vpci
.ops
= (struct virtio_pci_ops
) {
273 .set_config
= set_config
,
274 .get_config
= get_config
,
275 .get_host_features
= get_host_features
,
276 .set_guest_features
= set_guest_features
,
278 .notify_vq
= notify_vq
,
279 .get_pfn_vq
= get_pfn_vq
,
280 .get_size_vq
= get_size_vq
,
283 bdev
.compat_id
= compat__add_message("virtio-balloon device was not detected",
284 "While you have requested a virtio-balloon device, "
285 "the guest kernel didn't seem to detect it.\n"
286 "Please make sure that the kernel was compiled"
287 "with CONFIG_VIRTIO_BALLOON.");