xref: /qemu/hw/s390x/s390-stattrib-kvm.c (revision 06b40d250ecfa1633209c2e431a7a38acfd03a98)
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