1 /*
2 * s390 storage attributes device -- KVM object
3 *
4 * Copyright 2016 IBM Corp.
5 * Author(s): Claudio Imbrenda <imbrenda@linux.vnet.ibm.com>
6 *
7 * This work is licensed under the terms of the GNU GPL, version 2 or (at
8 * your option) any later version. See the COPYING file in the top-level
9 * directory.
10 */
11
12 #include "qemu/osdep.h"
13 #include "hw/s390x/s390-virtio-ccw.h"
14 #include "migration/qemu-file.h"
15 #include "hw/s390x/storage-attributes.h"
16 #include "qemu/error-report.h"
17 #include "system/kvm.h"
18 #include "system/memory_mapping.h"
19 #include "system/ram_addr.h"
20 #include "kvm/kvm_s390x.h"
21 #include "qapi/error.h"
22
kvm_s390_stattrib_create(void)23 Object *kvm_s390_stattrib_create(void)
24 {
25 if (kvm_enabled() &&
26 kvm_check_extension(kvm_state, KVM_CAP_S390_CMMA_MIGRATION)) {
27 return object_new(TYPE_KVM_S390_STATTRIB);
28 }
29 return NULL;
30 }
31
kvm_s390_stattrib_instance_init(Object * obj)32 static void kvm_s390_stattrib_instance_init(Object *obj)
33 {
34 KVMS390StAttribState *sas = KVM_S390_STATTRIB(obj);
35
36 sas->still_dirty = 0;
37 }
38
kvm_s390_stattrib_read_helper(S390StAttribState * sa,uint64_t * start_gfn,uint32_t count,uint8_t * values,uint32_t flags)39 static int kvm_s390_stattrib_read_helper(S390StAttribState *sa,
40 uint64_t *start_gfn,
41 uint32_t count,
42 uint8_t *values,
43 uint32_t flags)
44 {
45 KVMS390StAttribState *sas = KVM_S390_STATTRIB(sa);
46 int r;
47 struct kvm_s390_cmma_log clog = {
48 .values = (uint64_t)values,
49 .start_gfn = *start_gfn,
50 .count = count,
51 .flags = flags,
52 };
53
54 r = kvm_vm_ioctl(kvm_state, KVM_S390_GET_CMMA_BITS, &clog);
55 if (r < 0) {
56 error_report("KVM_S390_GET_CMMA_BITS failed: %s", strerror(-r));
57 return r;
58 }
59
60 *start_gfn = clog.start_gfn;
61 sas->still_dirty = clog.remaining;
62 return clog.count;
63 }
64
kvm_s390_stattrib_get_stattr(S390StAttribState * sa,uint64_t * start_gfn,uint32_t count,uint8_t * values)65 static int kvm_s390_stattrib_get_stattr(S390StAttribState *sa,
66 uint64_t *start_gfn,
67 uint32_t count,
68 uint8_t *values)
69 {
70 return kvm_s390_stattrib_read_helper(sa, start_gfn, count, values, 0);
71 }
72
kvm_s390_stattrib_peek_stattr(S390StAttribState * sa,uint64_t start_gfn,uint32_t count,uint8_t * values)73 static int kvm_s390_stattrib_peek_stattr(S390StAttribState *sa,
74 uint64_t start_gfn,
75 uint32_t count,
76 uint8_t *values)
77 {
78 return kvm_s390_stattrib_read_helper(sa, &start_gfn, count, values,
79 KVM_S390_CMMA_PEEK);
80 }
81
kvm_s390_stattrib_set_stattr(S390StAttribState * sa,uint64_t start_gfn,uint32_t count,uint8_t * values)82 static int kvm_s390_stattrib_set_stattr(S390StAttribState *sa,
83 uint64_t start_gfn,
84 uint32_t count,
85 uint8_t *values)
86 {
87 KVMS390StAttribState *sas = KVM_S390_STATTRIB(sa);
88 S390CcwMachineState *s390ms = S390_CCW_MACHINE(qdev_get_machine());
89 unsigned long max = s390_get_memory_limit(s390ms) / TARGET_PAGE_SIZE;
90
91 if (start_gfn + count > max) {
92 error_report("Out of memory bounds when setting storage attributes");
93 return -1;
94 }
95 if (!sas->incoming_buffer) {
96 sas->incoming_buffer = g_malloc0(max);
97 }
98
99 memcpy(sas->incoming_buffer + start_gfn, values, count);
100
101 return 0;
102 }
103
kvm_s390_stattrib_synchronize(S390StAttribState * sa)104 static void kvm_s390_stattrib_synchronize(S390StAttribState *sa)
105 {
106 KVMS390StAttribState *sas = KVM_S390_STATTRIB(sa);
107 S390CcwMachineState *s390ms = S390_CCW_MACHINE(qdev_get_machine());
108 unsigned long max = s390_get_memory_limit(s390ms) / TARGET_PAGE_SIZE;
109 unsigned long start_gfn, end_gfn, pages;
110 GuestPhysBlockList guest_phys_blocks;
111 GuestPhysBlock *block;
112 int r;
113 struct kvm_s390_cmma_log clog = {
114 .flags = 0,
115 .mask = ~0ULL,
116 };
117
118 if (!sas->incoming_buffer) {
119 return;
120 }
121 guest_phys_blocks_init(&guest_phys_blocks);
122 guest_phys_blocks_append(&guest_phys_blocks);
123
124 QTAILQ_FOREACH(block, &guest_phys_blocks.head, next) {
125 assert(QEMU_IS_ALIGNED(block->target_start, TARGET_PAGE_SIZE));
126 assert(QEMU_IS_ALIGNED(block->target_end, TARGET_PAGE_SIZE));
127
128 start_gfn = block->target_start / TARGET_PAGE_SIZE;
129 end_gfn = block->target_end / TARGET_PAGE_SIZE;
130
131 while (start_gfn < end_gfn) {
132 /* Don't exceed the maximum buffer size. */
133 pages = MIN(end_gfn - start_gfn, KVM_S390_SKEYS_MAX / 2);
134
135 /*
136 * If we ever get guest physical memory beyond the configured
137 * memory limit, something went very wrong.
138 */
139 assert(start_gfn + pages <= max);
140
141 clog.start_gfn = start_gfn;
142 clog.count = pages;
143 clog.values = (uint64_t)(sas->incoming_buffer + start_gfn);
144 r = kvm_vm_ioctl(kvm_state, KVM_S390_SET_CMMA_BITS, &clog);
145 if (r) {
146 error_report("KVM_S390_SET_CMMA_BITS failed: %s", strerror(-r));
147 goto out;
148 }
149
150 start_gfn += pages;
151 }
152 }
153
154 out:
155 guest_phys_blocks_free(&guest_phys_blocks);
156 g_free(sas->incoming_buffer);
157 sas->incoming_buffer = NULL;
158 }
159
kvm_s390_stattrib_set_migrationmode(S390StAttribState * sa,bool val,Error ** errp)160 static int kvm_s390_stattrib_set_migrationmode(S390StAttribState *sa, bool val,
161 Error **errp)
162 {
163 struct kvm_device_attr attr = {
164 .group = KVM_S390_VM_MIGRATION,
165 .attr = val,
166 .addr = 0,
167 };
168 int r;
169
170 r = kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr);
171 if (r) {
172 error_setg_errno(errp, -r, "setting KVM_S390_VM_MIGRATION failed");
173 }
174 return r;
175 }
176
kvm_s390_stattrib_get_dirtycount(S390StAttribState * sa)177 static long long kvm_s390_stattrib_get_dirtycount(S390StAttribState *sa)
178 {
179 KVMS390StAttribState *sas = KVM_S390_STATTRIB(sa);
180 uint8_t val[8];
181
182 kvm_s390_stattrib_peek_stattr(sa, 0, 1, val);
183 return sas->still_dirty;
184 }
185
kvm_s390_stattrib_get_active(S390StAttribState * sa)186 static int kvm_s390_stattrib_get_active(S390StAttribState *sa)
187 {
188 return kvm_s390_cmma_active();
189 }
190
kvm_s390_stattrib_class_init(ObjectClass * oc,const void * data)191 static void kvm_s390_stattrib_class_init(ObjectClass *oc, const void *data)
192 {
193 S390StAttribClass *sac = S390_STATTRIB_CLASS(oc);
194 DeviceClass *dc = DEVICE_CLASS(oc);
195
196 sac->get_stattr = kvm_s390_stattrib_get_stattr;
197 sac->peek_stattr = kvm_s390_stattrib_peek_stattr;
198 sac->set_stattr = kvm_s390_stattrib_set_stattr;
199 sac->set_migrationmode = kvm_s390_stattrib_set_migrationmode;
200 sac->get_dirtycount = kvm_s390_stattrib_get_dirtycount;
201 sac->synchronize = kvm_s390_stattrib_synchronize;
202 sac->get_active = kvm_s390_stattrib_get_active;
203
204 /* Reason: Can only be instantiated one time (internally) */
205 dc->user_creatable = false;
206 }
207
208 static const TypeInfo kvm_s390_stattrib_info = {
209 .name = TYPE_KVM_S390_STATTRIB,
210 .parent = TYPE_S390_STATTRIB,
211 .instance_init = kvm_s390_stattrib_instance_init,
212 .instance_size = sizeof(KVMS390StAttribState),
213 .class_init = kvm_s390_stattrib_class_init,
214 .class_size = sizeof(S390StAttribClass),
215 };
216
kvm_s390_stattrib_register_types(void)217 static void kvm_s390_stattrib_register_types(void)
218 {
219 type_register_static(&kvm_s390_stattrib_info);
220 }
221
222 type_init(kvm_s390_stattrib_register_types)
223