~ubuntu-branches/ubuntu/wily/qemu-kvm-spice/wily

« back to all changes in this revision

Viewing changes to hw/virtio-balloon.c

  • Committer: Bazaar Package Importer
  • Author(s): Serge Hallyn
  • Date: 2011-10-19 10:44:56 UTC
  • Revision ID: james.westby@ubuntu.com-20111019104456-xgvskumk3sxi97f4
Tags: upstream-0.15.0+noroms
ImportĀ upstreamĀ versionĀ 0.15.0+noroms

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Virtio Balloon Device
 
3
 *
 
4
 * Copyright IBM, Corp. 2008
 
5
 * Copyright (C) 2011 Red Hat, Inc.
 
6
 * Copyright (C) 2011 Amit Shah <amit.shah@redhat.com>
 
7
 *
 
8
 * Authors:
 
9
 *  Anthony Liguori   <aliguori@us.ibm.com>
 
10
 *
 
11
 * This work is licensed under the terms of the GNU GPL, version 2.  See
 
12
 * the COPYING file in the top-level directory.
 
13
 *
 
14
 */
 
15
 
 
16
#include "iov.h"
 
17
#include "qemu-common.h"
 
18
#include "virtio.h"
 
19
#include "pc.h"
 
20
#include "cpu.h"
 
21
#include "monitor.h"
 
22
#include "balloon.h"
 
23
#include "virtio-balloon.h"
 
24
#include "kvm.h"
 
25
#include "qlist.h"
 
26
#include "qint.h"
 
27
#include "qstring.h"
 
28
 
 
29
#if defined(__linux__)
 
30
#include <sys/mman.h>
 
31
#endif
 
32
 
 
33
/* Disable guest-provided stats by now (https://bugzilla.redhat.com/show_bug.cgi?id=623903) */
 
34
#define ENABLE_GUEST_STATS   0
 
35
 
 
36
 
 
37
typedef struct VirtIOBalloon
 
38
{
 
39
    VirtIODevice vdev;
 
40
    VirtQueue *ivq, *dvq, *svq;
 
41
    uint32_t num_pages;
 
42
    uint32_t actual;
 
43
    uint64_t stats[VIRTIO_BALLOON_S_NR];
 
44
    VirtQueueElement stats_vq_elem;
 
45
    size_t stats_vq_offset;
 
46
    MonitorCompletion *stats_callback;
 
47
    void *stats_opaque_callback_data;
 
48
    DeviceState *qdev;
 
49
} VirtIOBalloon;
 
50
 
 
51
static VirtIOBalloon *to_virtio_balloon(VirtIODevice *vdev)
 
52
{
 
53
    return (VirtIOBalloon *)vdev;
 
54
}
 
55
 
 
56
static void balloon_page(void *addr, int deflate)
 
57
{
 
58
#if defined(__linux__)
 
59
    if (!kvm_enabled() || kvm_has_sync_mmu())
 
60
        qemu_madvise(addr, TARGET_PAGE_SIZE,
 
61
                deflate ? QEMU_MADV_WILLNEED : QEMU_MADV_DONTNEED);
 
62
#endif
 
63
}
 
64
 
 
65
/*
 
66
 * reset_stats - Mark all items in the stats array as unset
 
67
 *
 
68
 * This function needs to be called at device intialization and before
 
69
 * before updating to a set of newly-generated stats.  This will ensure that no
 
70
 * stale values stick around in case the guest reports a subset of the supported
 
71
 * statistics.
 
72
 */
 
73
static inline void reset_stats(VirtIOBalloon *dev)
 
74
{
 
75
    int i;
 
76
    for (i = 0; i < VIRTIO_BALLOON_S_NR; dev->stats[i++] = -1);
 
77
}
 
78
 
 
79
static void stat_put(QDict *dict, const char *label, uint64_t val)
 
80
{
 
81
    if (val != -1)
 
82
        qdict_put(dict, label, qint_from_int(val));
 
83
}
 
84
 
 
85
static QObject *get_stats_qobject(VirtIOBalloon *dev)
 
86
{
 
87
    QDict *dict = qdict_new();
 
88
    uint64_t actual = ram_size - ((uint64_t) dev->actual <<
 
89
                                  VIRTIO_BALLOON_PFN_SHIFT);
 
90
 
 
91
    stat_put(dict, "actual", actual);
 
92
#if ENABLE_GUEST_STATS
 
93
    stat_put(dict, "mem_swapped_in", dev->stats[VIRTIO_BALLOON_S_SWAP_IN]);
 
94
    stat_put(dict, "mem_swapped_out", dev->stats[VIRTIO_BALLOON_S_SWAP_OUT]);
 
95
    stat_put(dict, "major_page_faults", dev->stats[VIRTIO_BALLOON_S_MAJFLT]);
 
96
    stat_put(dict, "minor_page_faults", dev->stats[VIRTIO_BALLOON_S_MINFLT]);
 
97
    stat_put(dict, "free_mem", dev->stats[VIRTIO_BALLOON_S_MEMFREE]);
 
98
    stat_put(dict, "total_mem", dev->stats[VIRTIO_BALLOON_S_MEMTOT]);
 
99
#endif
 
100
 
 
101
    return QOBJECT(dict);
 
102
}
 
103
 
 
104
static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq)
 
105
{
 
106
    VirtIOBalloon *s = to_virtio_balloon(vdev);
 
107
    VirtQueueElement elem;
 
108
 
 
109
    while (virtqueue_pop(vq, &elem)) {
 
110
        size_t offset = 0;
 
111
        uint32_t pfn;
 
112
 
 
113
        while (iov_to_buf(elem.out_sg, elem.out_num, &pfn, offset, 4) == 4) {
 
114
            ram_addr_t pa;
 
115
            ram_addr_t addr;
 
116
 
 
117
            pa = (ram_addr_t)ldl_p(&pfn) << VIRTIO_BALLOON_PFN_SHIFT;
 
118
            offset += 4;
 
119
 
 
120
            addr = cpu_get_physical_page_desc(pa);
 
121
            if ((addr & ~TARGET_PAGE_MASK) != IO_MEM_RAM)
 
122
                continue;
 
123
 
 
124
            /* Using qemu_get_ram_ptr is bending the rules a bit, but
 
125
               should be OK because we only want a single page.  */
 
126
            balloon_page(qemu_get_ram_ptr(addr), !!(vq == s->dvq));
 
127
        }
 
128
 
 
129
        virtqueue_push(vq, &elem, offset);
 
130
        virtio_notify(vdev, vq);
 
131
    }
 
132
}
 
133
 
 
134
static void complete_stats_request(VirtIOBalloon *vb)
 
135
{
 
136
    QObject *stats;
 
137
 
 
138
    if (!vb->stats_opaque_callback_data)
 
139
        return;
 
140
 
 
141
    stats = get_stats_qobject(vb);
 
142
    vb->stats_callback(vb->stats_opaque_callback_data, stats);
 
143
    qobject_decref(stats);
 
144
    vb->stats_opaque_callback_data = NULL;
 
145
    vb->stats_callback = NULL;
 
146
}
 
147
 
 
148
static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq)
 
149
{
 
150
    VirtIOBalloon *s = DO_UPCAST(VirtIOBalloon, vdev, vdev);
 
151
    VirtQueueElement *elem = &s->stats_vq_elem;
 
152
    VirtIOBalloonStat stat;
 
153
    size_t offset = 0;
 
154
 
 
155
    if (!virtqueue_pop(vq, elem)) {
 
156
        return;
 
157
    }
 
158
 
 
159
    /* Initialize the stats to get rid of any stale values.  This is only
 
160
     * needed to handle the case where a guest supports fewer stats than it
 
161
     * used to (ie. it has booted into an old kernel).
 
162
     */
 
163
    reset_stats(s);
 
164
 
 
165
    while (iov_to_buf(elem->out_sg, elem->out_num, &stat, offset, sizeof(stat))
 
166
           == sizeof(stat)) {
 
167
        uint16_t tag = tswap16(stat.tag);
 
168
        uint64_t val = tswap64(stat.val);
 
169
 
 
170
        offset += sizeof(stat);
 
171
        if (tag < VIRTIO_BALLOON_S_NR)
 
172
            s->stats[tag] = val;
 
173
    }
 
174
    s->stats_vq_offset = offset;
 
175
 
 
176
    complete_stats_request(s);
 
177
}
 
178
 
 
179
static void virtio_balloon_get_config(VirtIODevice *vdev, uint8_t *config_data)
 
180
{
 
181
    VirtIOBalloon *dev = to_virtio_balloon(vdev);
 
182
    struct virtio_balloon_config config;
 
183
 
 
184
    config.num_pages = cpu_to_le32(dev->num_pages);
 
185
    config.actual = cpu_to_le32(dev->actual);
 
186
 
 
187
    memcpy(config_data, &config, 8);
 
188
}
 
189
 
 
190
static void virtio_balloon_set_config(VirtIODevice *vdev,
 
191
                                      const uint8_t *config_data)
 
192
{
 
193
    VirtIOBalloon *dev = to_virtio_balloon(vdev);
 
194
    struct virtio_balloon_config config;
 
195
    memcpy(&config, config_data, 8);
 
196
    dev->actual = le32_to_cpu(config.actual);
 
197
}
 
198
 
 
199
static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f)
 
200
{
 
201
    f |= (1 << VIRTIO_BALLOON_F_STATS_VQ);
 
202
    return f;
 
203
}
 
204
 
 
205
static void virtio_balloon_stat(void *opaque, MonitorCompletion cb,
 
206
                                void *cb_data)
 
207
{
 
208
    VirtIOBalloon *dev = opaque;
 
209
 
 
210
    /* For now, only allow one request at a time.  This restriction can be
 
211
     * removed later by queueing callback and data pairs.
 
212
     */
 
213
    if (dev->stats_callback != NULL) {
 
214
        return;
 
215
    }
 
216
    dev->stats_callback = cb;
 
217
    dev->stats_opaque_callback_data = cb_data;
 
218
 
 
219
    if (ENABLE_GUEST_STATS
 
220
        && (dev->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ))) {
 
221
        virtqueue_push(dev->svq, &dev->stats_vq_elem, dev->stats_vq_offset);
 
222
        virtio_notify(&dev->vdev, dev->svq);
 
223
        return;
 
224
    }
 
225
 
 
226
    /* Stats are not supported.  Clear out any stale values that might
 
227
     * have been set by a more featureful guest kernel.
 
228
     */
 
229
    reset_stats(dev);
 
230
    complete_stats_request(dev);
 
231
}
 
232
 
 
233
static void virtio_balloon_to_target(void *opaque, ram_addr_t target)
 
234
{
 
235
    VirtIOBalloon *dev = opaque;
 
236
 
 
237
    if (target > ram_size) {
 
238
        target = ram_size;
 
239
    }
 
240
    if (target) {
 
241
        dev->num_pages = (ram_size - target) >> VIRTIO_BALLOON_PFN_SHIFT;
 
242
        virtio_notify_config(&dev->vdev);
 
243
    }
 
244
}
 
245
 
 
246
static void virtio_balloon_save(QEMUFile *f, void *opaque)
 
247
{
 
248
    VirtIOBalloon *s = opaque;
 
249
 
 
250
    virtio_save(&s->vdev, f);
 
251
 
 
252
    qemu_put_be32(f, s->num_pages);
 
253
    qemu_put_be32(f, s->actual);
 
254
}
 
255
 
 
256
static int virtio_balloon_load(QEMUFile *f, void *opaque, int version_id)
 
257
{
 
258
    VirtIOBalloon *s = opaque;
 
259
 
 
260
    if (version_id != 1)
 
261
        return -EINVAL;
 
262
 
 
263
    virtio_load(&s->vdev, f);
 
264
 
 
265
    s->num_pages = qemu_get_be32(f);
 
266
    s->actual = qemu_get_be32(f);
 
267
    return 0;
 
268
}
 
269
 
 
270
VirtIODevice *virtio_balloon_init(DeviceState *dev)
 
271
{
 
272
    VirtIOBalloon *s;
 
273
    int ret;
 
274
 
 
275
    s = (VirtIOBalloon *)virtio_common_init("virtio-balloon",
 
276
                                            VIRTIO_ID_BALLOON,
 
277
                                            8, sizeof(VirtIOBalloon));
 
278
 
 
279
    s->vdev.get_config = virtio_balloon_get_config;
 
280
    s->vdev.set_config = virtio_balloon_set_config;
 
281
    s->vdev.get_features = virtio_balloon_get_features;
 
282
 
 
283
    ret = qemu_add_balloon_handler(virtio_balloon_to_target,
 
284
                                   virtio_balloon_stat, s);
 
285
    if (ret < 0) {
 
286
        virtio_cleanup(&s->vdev);
 
287
        return NULL;
 
288
    }
 
289
 
 
290
    s->ivq = virtio_add_queue(&s->vdev, 128, virtio_balloon_handle_output);
 
291
    s->dvq = virtio_add_queue(&s->vdev, 128, virtio_balloon_handle_output);
 
292
    s->svq = virtio_add_queue(&s->vdev, 128, virtio_balloon_receive_stats);
 
293
 
 
294
    reset_stats(s);
 
295
 
 
296
    s->qdev = dev;
 
297
    register_savevm(dev, "virtio-balloon", -1, 1,
 
298
                    virtio_balloon_save, virtio_balloon_load, s);
 
299
 
 
300
    return &s->vdev;
 
301
}
 
302
 
 
303
void virtio_balloon_exit(VirtIODevice *vdev)
 
304
{
 
305
    VirtIOBalloon *s = DO_UPCAST(VirtIOBalloon, vdev, vdev);
 
306
    unregister_savevm(s->qdev, "virtio-balloon", s);
 
307
    virtio_cleanup(vdev);
 
308
}