qemu/hw/virtio-balloon.c
<<
>>
Prefs
   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 "qemu/iov.h"
  17#include "qemu/timer.h"
  18#include "qemu-common.h"
  19#include "virtio.h"
  20#include "pc.h"
  21#include "cpu.h"
  22#include "sysemu/balloon.h"
  23#include "virtio-balloon.h"
  24#include "sysemu/kvm.h"
  25#include "exec/address-spaces.h"
  26#include "qapi/visitor.h"
  27
  28#if defined(__linux__)
  29#include <sys/mman.h>
  30#endif
  31
  32typedef struct VirtIOBalloon
  33{
  34    VirtIODevice vdev;
  35    VirtQueue *ivq, *dvq, *svq;
  36    uint32_t num_pages;
  37    uint32_t actual;
  38    uint64_t stats[VIRTIO_BALLOON_S_NR];
  39    VirtQueueElement stats_vq_elem;
  40    size_t stats_vq_offset;
  41    QEMUTimer *stats_timer;
  42    int64_t stats_last_update;
  43    int64_t stats_poll_interval;
  44    DeviceState *qdev;
  45} VirtIOBalloon;
  46
  47static VirtIOBalloon *to_virtio_balloon(VirtIODevice *vdev)
  48{
  49    return (VirtIOBalloon *)vdev;
  50}
  51
  52static void balloon_page(void *addr, int deflate)
  53{
  54#if defined(__linux__)
  55    if (!kvm_enabled() || kvm_has_sync_mmu())
  56        qemu_madvise(addr, TARGET_PAGE_SIZE,
  57                deflate ? QEMU_MADV_WILLNEED : QEMU_MADV_DONTNEED);
  58#endif
  59}
  60
  61static const char *balloon_stat_names[] = {
  62   [VIRTIO_BALLOON_S_SWAP_IN] = "stat-swap-in",
  63   [VIRTIO_BALLOON_S_SWAP_OUT] = "stat-swap-out",
  64   [VIRTIO_BALLOON_S_MAJFLT] = "stat-major-faults",
  65   [VIRTIO_BALLOON_S_MINFLT] = "stat-minor-faults",
  66   [VIRTIO_BALLOON_S_MEMFREE] = "stat-free-memory",
  67   [VIRTIO_BALLOON_S_MEMTOT] = "stat-total-memory",
  68   [VIRTIO_BALLOON_S_NR] = NULL
  69};
  70
  71/*
  72 * reset_stats - Mark all items in the stats array as unset
  73 *
  74 * This function needs to be called at device intialization and before
  75 * before updating to a set of newly-generated stats.  This will ensure that no
  76 * stale values stick around in case the guest reports a subset of the supported
  77 * statistics.
  78 */
  79static inline void reset_stats(VirtIOBalloon *dev)
  80{
  81    int i;
  82    for (i = 0; i < VIRTIO_BALLOON_S_NR; dev->stats[i++] = -1);
  83}
  84
  85static bool balloon_stats_supported(const VirtIOBalloon *s)
  86{
  87    return s->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ);
  88}
  89
  90static bool balloon_stats_enabled(const VirtIOBalloon *s)
  91{
  92    return s->stats_poll_interval > 0;
  93}
  94
  95static void balloon_stats_destroy_timer(VirtIOBalloon *s)
  96{
  97    if (balloon_stats_enabled(s)) {
  98        qemu_del_timer(s->stats_timer);
  99        qemu_free_timer(s->stats_timer);
 100        s->stats_timer = NULL;
 101        s->stats_poll_interval = 0;
 102    }
 103}
 104
 105static void balloon_stats_change_timer(VirtIOBalloon *s, int secs)
 106{
 107    qemu_mod_timer(s->stats_timer, qemu_get_clock_ms(vm_clock) + secs * 1000);
 108}
 109
 110static void balloon_stats_poll_cb(void *opaque)
 111{
 112    VirtIOBalloon *s = opaque;
 113
 114    if (!balloon_stats_supported(s)) {
 115        /* re-schedule */
 116        balloon_stats_change_timer(s, s->stats_poll_interval);
 117        return;
 118    }
 119
 120    virtqueue_push(s->svq, &s->stats_vq_elem, s->stats_vq_offset);
 121    virtio_notify(&s->vdev, s->svq);
 122}
 123
 124static void balloon_stats_get_all(Object *obj, struct Visitor *v,
 125                                  void *opaque, const char *name, Error **errp)
 126{
 127    VirtIOBalloon *s = opaque;
 128    int i;
 129
 130    if (!s->stats_last_update) {
 131        error_setg(errp, "guest hasn't updated any stats yet");
 132        return;
 133    }
 134
 135    visit_start_struct(v, NULL, "guest-stats", name, 0, errp);
 136    visit_type_int(v, &s->stats_last_update, "last-update", errp);
 137
 138    visit_start_struct(v, NULL, NULL, "stats", 0, errp);
 139    for (i = 0; i < VIRTIO_BALLOON_S_NR; i++) {
 140        visit_type_int64(v, (int64_t *) &s->stats[i], balloon_stat_names[i],
 141                         errp);
 142    }
 143    visit_end_struct(v, errp);
 144
 145    visit_end_struct(v, errp);
 146}
 147
 148static void balloon_stats_get_poll_interval(Object *obj, struct Visitor *v,
 149                                            void *opaque, const char *name,
 150                                            Error **errp)
 151{
 152    VirtIOBalloon *s = opaque;
 153    visit_type_int(v, &s->stats_poll_interval, name, errp);
 154}
 155
 156static void balloon_stats_set_poll_interval(Object *obj, struct Visitor *v,
 157                                            void *opaque, const char *name,
 158                                            Error **errp)
 159{
 160    VirtIOBalloon *s = opaque;
 161    int64_t value;
 162
 163    visit_type_int(v, &value, name, errp);
 164    if (error_is_set(errp)) {
 165        return;
 166    }
 167
 168    if (value < 0) {
 169        error_setg(errp, "timer value must be greater than zero");
 170        return;
 171    }
 172
 173    if (value == s->stats_poll_interval) {
 174        return;
 175    }
 176
 177    if (value == 0) {
 178        /* timer=0 disables the timer */
 179        balloon_stats_destroy_timer(s);
 180        return;
 181    }
 182
 183    if (balloon_stats_enabled(s)) {
 184        /* timer interval change */
 185        s->stats_poll_interval = value;
 186        balloon_stats_change_timer(s, value);
 187        return;
 188    }
 189
 190    /* create a new timer */
 191    g_assert(s->stats_timer == NULL);
 192    s->stats_timer = qemu_new_timer_ms(vm_clock, balloon_stats_poll_cb, s);
 193    s->stats_poll_interval = value;
 194    balloon_stats_change_timer(s, 0);
 195}
 196
 197static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq)
 198{
 199    VirtIOBalloon *s = to_virtio_balloon(vdev);
 200    VirtQueueElement elem;
 201    MemoryRegionSection section;
 202
 203    while (virtqueue_pop(vq, &elem)) {
 204        size_t offset = 0;
 205        uint32_t pfn;
 206
 207        while (iov_to_buf(elem.out_sg, elem.out_num, offset, &pfn, 4) == 4) {
 208            ram_addr_t pa;
 209            ram_addr_t addr;
 210
 211            pa = (ram_addr_t)ldl_p(&pfn) << VIRTIO_BALLOON_PFN_SHIFT;
 212            offset += 4;
 213
 214            /* FIXME: remove get_system_memory(), but how? */
 215            section = memory_region_find(get_system_memory(), pa, 1);
 216            if (!section.size || !memory_region_is_ram(section.mr))
 217                continue;
 218
 219            /* Using memory_region_get_ram_ptr is bending the rules a bit, but
 220               should be OK because we only want a single page.  */
 221            addr = section.offset_within_region;
 222            balloon_page(memory_region_get_ram_ptr(section.mr) + addr,
 223                         !!(vq == s->dvq));
 224        }
 225
 226        virtqueue_push(vq, &elem, offset);
 227        virtio_notify(vdev, vq);
 228    }
 229}
 230
 231static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq)
 232{
 233    VirtIOBalloon *s = DO_UPCAST(VirtIOBalloon, vdev, vdev);
 234    VirtQueueElement *elem = &s->stats_vq_elem;
 235    VirtIOBalloonStat stat;
 236    size_t offset = 0;
 237    qemu_timeval tv;
 238
 239    if (!virtqueue_pop(vq, elem)) {
 240        goto out;
 241    }
 242
 243    /* Initialize the stats to get rid of any stale values.  This is only
 244     * needed to handle the case where a guest supports fewer stats than it
 245     * used to (ie. it has booted into an old kernel).
 246     */
 247    reset_stats(s);
 248
 249    while (iov_to_buf(elem->out_sg, elem->out_num, offset, &stat, sizeof(stat))
 250           == sizeof(stat)) {
 251        uint16_t tag = tswap16(stat.tag);
 252        uint64_t val = tswap64(stat.val);
 253
 254        offset += sizeof(stat);
 255        if (tag < VIRTIO_BALLOON_S_NR)
 256            s->stats[tag] = val;
 257    }
 258    s->stats_vq_offset = offset;
 259
 260    if (qemu_gettimeofday(&tv) < 0) {
 261        fprintf(stderr, "warning: %s: failed to get time of day\n", __func__);
 262        goto out;
 263    }
 264
 265    s->stats_last_update = tv.tv_sec;
 266
 267out:
 268    if (balloon_stats_enabled(s)) {
 269        balloon_stats_change_timer(s, s->stats_poll_interval);
 270    }
 271}
 272
 273static void virtio_balloon_get_config(VirtIODevice *vdev, uint8_t *config_data)
 274{
 275    VirtIOBalloon *dev = to_virtio_balloon(vdev);
 276    struct virtio_balloon_config config;
 277
 278    config.num_pages = cpu_to_le32(dev->num_pages);
 279    config.actual = cpu_to_le32(dev->actual);
 280
 281    memcpy(config_data, &config, 8);
 282}
 283
 284static void virtio_balloon_set_config(VirtIODevice *vdev,
 285                                      const uint8_t *config_data)
 286{
 287    VirtIOBalloon *dev = to_virtio_balloon(vdev);
 288    struct virtio_balloon_config config;
 289    uint32_t oldactual = dev->actual;
 290    memcpy(&config, config_data, 8);
 291    dev->actual = le32_to_cpu(config.actual);
 292    if (dev->actual != oldactual) {
 293        qemu_balloon_changed(ram_size -
 294                             (dev->actual << VIRTIO_BALLOON_PFN_SHIFT));
 295    }
 296}
 297
 298static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f)
 299{
 300    f |= (1 << VIRTIO_BALLOON_F_STATS_VQ);
 301    return f;
 302}
 303
 304static void virtio_balloon_stat(void *opaque, BalloonInfo *info)
 305{
 306    VirtIOBalloon *dev = opaque;
 307    info->actual = ram_size - ((uint64_t) dev->actual <<
 308                               VIRTIO_BALLOON_PFN_SHIFT);
 309}
 310
 311static void virtio_balloon_to_target(void *opaque, ram_addr_t target)
 312{
 313    VirtIOBalloon *dev = opaque;
 314
 315    if (target > ram_size) {
 316        target = ram_size;
 317    }
 318    if (target) {
 319        dev->num_pages = (ram_size - target) >> VIRTIO_BALLOON_PFN_SHIFT;
 320        virtio_notify_config(&dev->vdev);
 321    }
 322}
 323
 324static void virtio_balloon_save(QEMUFile *f, void *opaque)
 325{
 326    VirtIOBalloon *s = opaque;
 327
 328    virtio_save(&s->vdev, f);
 329
 330    qemu_put_be32(f, s->num_pages);
 331    qemu_put_be32(f, s->actual);
 332}
 333
 334static int virtio_balloon_load(QEMUFile *f, void *opaque, int version_id)
 335{
 336    VirtIOBalloon *s = opaque;
 337    int ret;
 338
 339    if (version_id != 1)
 340        return -EINVAL;
 341
 342    ret = virtio_load(&s->vdev, f);
 343    if (ret) {
 344        return ret;
 345    }
 346
 347    s->num_pages = qemu_get_be32(f);
 348    s->actual = qemu_get_be32(f);
 349    return 0;
 350}
 351
 352VirtIODevice *virtio_balloon_init(DeviceState *dev)
 353{
 354    VirtIOBalloon *s;
 355    int ret;
 356
 357    s = (VirtIOBalloon *)virtio_common_init("virtio-balloon",
 358                                            VIRTIO_ID_BALLOON,
 359                                            8, sizeof(VirtIOBalloon));
 360
 361    s->vdev.get_config = virtio_balloon_get_config;
 362    s->vdev.set_config = virtio_balloon_set_config;
 363    s->vdev.get_features = virtio_balloon_get_features;
 364
 365    ret = qemu_add_balloon_handler(virtio_balloon_to_target,
 366                                   virtio_balloon_stat, s);
 367    if (ret < 0) {
 368        virtio_cleanup(&s->vdev);
 369        return NULL;
 370    }
 371
 372    s->ivq = virtio_add_queue(&s->vdev, 128, virtio_balloon_handle_output);
 373    s->dvq = virtio_add_queue(&s->vdev, 128, virtio_balloon_handle_output);
 374    s->svq = virtio_add_queue(&s->vdev, 128, virtio_balloon_receive_stats);
 375
 376    s->qdev = dev;
 377    register_savevm(dev, "virtio-balloon", -1, 1,
 378                    virtio_balloon_save, virtio_balloon_load, s);
 379
 380    object_property_add(OBJECT(dev), "guest-stats", "guest statistics",
 381                        balloon_stats_get_all, NULL, NULL, s, NULL);
 382
 383    object_property_add(OBJECT(dev), "guest-stats-polling-interval", "int",
 384                        balloon_stats_get_poll_interval,
 385                        balloon_stats_set_poll_interval,
 386                        NULL, s, NULL);
 387
 388    return &s->vdev;
 389}
 390
 391void virtio_balloon_exit(VirtIODevice *vdev)
 392{
 393    VirtIOBalloon *s = DO_UPCAST(VirtIOBalloon, vdev, vdev);
 394
 395    balloon_stats_destroy_timer(s);
 396    qemu_remove_balloon_handler(s);
 397    unregister_savevm(s->qdev, "virtio-balloon", s);
 398    virtio_cleanup(vdev);
 399}
 400