Statistics
| Branch: | Revision:

root / hw / virtio-balloon.c @ 9b32d5a5

History | View | Annotate | Download (4.9 kB)

1
/*
2
 * Virtio Block Device
3
 *
4
 * Copyright IBM, Corp. 2008
5
 *
6
 * Authors:
7
 *  Anthony Liguori   <aliguori@us.ibm.com>
8
 *
9
 * This work is licensed under the terms of the GNU GPL, version 2.  See
10
 * the COPYING file in the top-level directory.
11
 *
12
 */
13

    
14
#include "qemu-common.h"
15
#include "virtio.h"
16
#include "pc.h"
17
#include "sysemu.h"
18
#include "cpu.h"
19
#include "balloon.h"
20
#include "virtio-balloon.h"
21
#include "kvm.h"
22

    
23
#if defined(__linux__)
24
#include <sys/mman.h>
25
#endif
26

    
27
typedef struct VirtIOBalloon
28
{
29
    VirtIODevice vdev;
30
    VirtQueue *ivq, *dvq;
31
    uint32_t num_pages;
32
    uint32_t actual;
33
} VirtIOBalloon;
34

    
35
static VirtIOBalloon *to_virtio_balloon(VirtIODevice *vdev)
36
{
37
    return (VirtIOBalloon *)vdev;
38
}
39

    
40
static void balloon_page(void *addr, int deflate)
41
{
42
#if defined(__linux__)
43
    if (!kvm_enabled() || kvm_has_sync_mmu())
44
        madvise(addr, TARGET_PAGE_SIZE,
45
                deflate ? MADV_WILLNEED : MADV_DONTNEED);
46
#endif
47
}
48

    
49
/* FIXME: once we do a virtio refactoring, this will get subsumed into common
50
 * code */
51
static size_t memcpy_from_iovector(void *data, size_t offset, size_t size,
52
                                   struct iovec *iov, int iovlen)
53
{
54
    int i;
55
    uint8_t *ptr = data;
56
    size_t iov_off = 0;
57
    size_t data_off = 0;
58

    
59
    for (i = 0; i < iovlen && size; i++) {
60
        if (offset < (iov_off + iov[i].iov_len)) {
61
            size_t len = MIN((iov_off + iov[i].iov_len) - offset , size);
62

    
63
            memcpy(ptr + data_off, iov[i].iov_base + (offset - iov_off), len);
64

    
65
            data_off += len;
66
            offset += len;
67
            size -= len;
68
        }
69

    
70
        iov_off += iov[i].iov_len;
71
    }
72

    
73
    return data_off;
74
}
75

    
76
static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq)
77
{
78
    VirtIOBalloon *s = to_virtio_balloon(vdev);
79
    VirtQueueElement elem;
80

    
81
    while (virtqueue_pop(vq, &elem)) {
82
        size_t offset = 0;
83
        uint32_t pfn;
84

    
85
        while (memcpy_from_iovector(&pfn, offset, 4,
86
                                    elem.out_sg, elem.out_num) == 4) {
87
            ram_addr_t pa;
88
            ram_addr_t addr;
89

    
90
            pa = (ram_addr_t)ldl_p(&pfn) << VIRTIO_BALLOON_PFN_SHIFT;
91
            offset += 4;
92

    
93
            addr = cpu_get_physical_page_desc(pa);
94
            if ((addr & ~TARGET_PAGE_MASK) != IO_MEM_RAM)
95
                continue;
96

    
97
            balloon_page(phys_ram_base + addr, !!(vq == s->dvq));
98
        }
99

    
100
        virtqueue_push(vq, &elem, offset);
101
        virtio_notify(vdev, vq);
102
    }
103
}
104

    
105
static void virtio_balloon_get_config(VirtIODevice *vdev, uint8_t *config_data)
106
{
107
    VirtIOBalloon *dev = to_virtio_balloon(vdev);
108
    struct virtio_balloon_config config;
109

    
110
    config.num_pages = cpu_to_le32(dev->num_pages);
111
    config.actual = cpu_to_le32(dev->actual);
112

    
113
    memcpy(config_data, &config, 8);
114
}
115

    
116
static void virtio_balloon_set_config(VirtIODevice *vdev,
117
                                      const uint8_t *config_data)
118
{
119
    VirtIOBalloon *dev = to_virtio_balloon(vdev);
120
    struct virtio_balloon_config config;
121
    memcpy(&config, config_data, 8);
122
    dev->actual = config.actual;
123
}
124

    
125
static uint32_t virtio_balloon_get_features(VirtIODevice *vdev)
126
{
127
    return 0;
128
}
129

    
130
static ram_addr_t virtio_balloon_to_target(void *opaque, ram_addr_t target)
131
{
132
    VirtIOBalloon *dev = opaque;
133

    
134
    if (target > ram_size)
135
        target = ram_size;
136

    
137
    if (target) {
138
        dev->num_pages = (ram_size - target) >> VIRTIO_BALLOON_PFN_SHIFT;
139
        virtio_notify_config(&dev->vdev);
140
    }
141

    
142
    return ram_size - (dev->actual << VIRTIO_BALLOON_PFN_SHIFT);
143
}
144

    
145
static void virtio_balloon_save(QEMUFile *f, void *opaque)
146
{
147
    VirtIOBalloon *s = opaque;
148

    
149
    virtio_save(&s->vdev, f);
150

    
151
    qemu_put_be32(f, s->num_pages);
152
    qemu_put_be32(f, s->actual);
153
}
154

    
155
static int virtio_balloon_load(QEMUFile *f, void *opaque, int version_id)
156
{
157
    VirtIOBalloon *s = opaque;
158

    
159
    if (version_id != 1)
160
        return -EINVAL;
161

    
162
    virtio_load(&s->vdev, f);
163

    
164
    s->num_pages = qemu_get_be32(f);
165
    s->actual = qemu_get_be32(f);
166

    
167
    return 0;
168
}
169

    
170
void *virtio_balloon_init(PCIBus *bus)
171
{
172
    VirtIOBalloon *s;
173

    
174
    s = (VirtIOBalloon *)virtio_init_pci(bus, "virtio-balloon",
175
                                         PCI_VENDOR_ID_REDHAT_QUMRANET,
176
                                         PCI_DEVICE_ID_VIRTIO_BALLOON,
177
                                         0, VIRTIO_ID_BALLOON,
178
                                         0x05, 0x00, 0x00,
179
                                         8, sizeof(VirtIOBalloon));
180
    if (s == NULL)
181
        return NULL;
182

    
183
    s->vdev.get_config = virtio_balloon_get_config;
184
    s->vdev.set_config = virtio_balloon_set_config;
185
    s->vdev.get_features = virtio_balloon_get_features;
186

    
187
    s->ivq = virtio_add_queue(&s->vdev, 128, virtio_balloon_handle_output);
188
    s->dvq = virtio_add_queue(&s->vdev, 128, virtio_balloon_handle_output);
189

    
190
    qemu_add_balloon_handler(virtio_balloon_to_target, s);
191

    
192
    register_savevm("virtio-balloon", -1, 1, virtio_balloon_save, virtio_balloon_load, s);
193

    
194
    return &s->vdev;
195
}