xref: /openbmc/qemu/system/dirtylimit.c (revision ec6f3fc3)
1 /*
2  * Dirty page rate limit implementation code
3  *
4  * Copyright (c) 2022 CHINA TELECOM CO.,LTD.
5  *
6  * Authors:
7  *  Hyman Huang(黄勇) <huangy81@chinatelecom.cn>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10  * See the COPYING file in the top-level directory.
11  */
12 
13 #include "qemu/osdep.h"
14 #include "qemu/main-loop.h"
15 #include "qapi/qapi-commands-migration.h"
16 #include "qapi/qmp/qdict.h"
17 #include "qapi/error.h"
18 #include "sysemu/dirtyrate.h"
19 #include "sysemu/dirtylimit.h"
20 #include "monitor/hmp.h"
21 #include "monitor/monitor.h"
22 #include "exec/memory.h"
23 #include "exec/target_page.h"
24 #include "hw/boards.h"
25 #include "sysemu/kvm.h"
26 #include "trace.h"
27 #include "migration/misc.h"
28 #include "migration/migration.h"
29 #include "migration/options.h"
30 
31 /*
32  * Dirtylimit stop working if dirty page rate error
33  * value less than DIRTYLIMIT_TOLERANCE_RANGE
34  */
35 #define DIRTYLIMIT_TOLERANCE_RANGE  25  /* MB/s */
36 /*
37  * Plus or minus vcpu sleep time linearly if dirty
38  * page rate error value percentage over
39  * DIRTYLIMIT_LINEAR_ADJUSTMENT_PCT.
40  * Otherwise, plus or minus a fixed vcpu sleep time.
41  */
42 #define DIRTYLIMIT_LINEAR_ADJUSTMENT_PCT     50
43 /*
44  * Max vcpu sleep time percentage during a cycle
45  * composed of dirty ring full and sleep time.
46  */
47 #define DIRTYLIMIT_THROTTLE_PCT_MAX 99
48 
49 struct {
50     VcpuStat stat;
51     bool running;
52     QemuThread thread;
53 } *vcpu_dirty_rate_stat;
54 
55 typedef struct VcpuDirtyLimitState {
56     int cpu_index;
57     bool enabled;
58     /*
59      * Quota dirty page rate, unit is MB/s
60      * zero if not enabled.
61      */
62     uint64_t quota;
63 } VcpuDirtyLimitState;
64 
65 struct {
66     VcpuDirtyLimitState *states;
67     /* Max cpus number configured by user */
68     int max_cpus;
69     /* Number of vcpu under dirtylimit */
70     int limited_nvcpu;
71 } *dirtylimit_state;
72 
73 /* protect dirtylimit_state */
74 static QemuMutex dirtylimit_mutex;
75 
76 /* dirtylimit thread quit if dirtylimit_quit is true */
77 static bool dirtylimit_quit;
78 
79 static void vcpu_dirty_rate_stat_collect(void)
80 {
81     MigrationState *s = migrate_get_current();
82     VcpuStat stat;
83     int i = 0;
84     int64_t period = DIRTYLIMIT_CALC_TIME_MS;
85 
86     if (migrate_dirty_limit() &&
87         migration_is_active(s)) {
88         period = s->parameters.x_vcpu_dirty_limit_period;
89     }
90 
91     /* calculate vcpu dirtyrate */
92     vcpu_calculate_dirtyrate(period,
93                               &stat,
94                               GLOBAL_DIRTY_LIMIT,
95                               false);
96 
97     for (i = 0; i < stat.nvcpu; i++) {
98         vcpu_dirty_rate_stat->stat.rates[i].id = i;
99         vcpu_dirty_rate_stat->stat.rates[i].dirty_rate =
100             stat.rates[i].dirty_rate;
101     }
102 
103     g_free(stat.rates);
104 }
105 
106 static void *vcpu_dirty_rate_stat_thread(void *opaque)
107 {
108     rcu_register_thread();
109 
110     /* start log sync */
111     global_dirty_log_change(GLOBAL_DIRTY_LIMIT, true);
112 
113     while (qatomic_read(&vcpu_dirty_rate_stat->running)) {
114         vcpu_dirty_rate_stat_collect();
115         if (dirtylimit_in_service()) {
116             dirtylimit_process();
117         }
118     }
119 
120     /* stop log sync */
121     global_dirty_log_change(GLOBAL_DIRTY_LIMIT, false);
122 
123     rcu_unregister_thread();
124     return NULL;
125 }
126 
127 int64_t vcpu_dirty_rate_get(int cpu_index)
128 {
129     DirtyRateVcpu *rates = vcpu_dirty_rate_stat->stat.rates;
130     return qatomic_read_i64(&rates[cpu_index].dirty_rate);
131 }
132 
133 void vcpu_dirty_rate_stat_start(void)
134 {
135     if (qatomic_read(&vcpu_dirty_rate_stat->running)) {
136         return;
137     }
138 
139     qatomic_set(&vcpu_dirty_rate_stat->running, 1);
140     qemu_thread_create(&vcpu_dirty_rate_stat->thread,
141                        "dirtyrate-stat",
142                        vcpu_dirty_rate_stat_thread,
143                        NULL,
144                        QEMU_THREAD_JOINABLE);
145 }
146 
147 void vcpu_dirty_rate_stat_stop(void)
148 {
149     qatomic_set(&vcpu_dirty_rate_stat->running, 0);
150     dirtylimit_state_unlock();
151     qemu_mutex_unlock_iothread();
152     qemu_thread_join(&vcpu_dirty_rate_stat->thread);
153     qemu_mutex_lock_iothread();
154     dirtylimit_state_lock();
155 }
156 
157 void vcpu_dirty_rate_stat_initialize(void)
158 {
159     MachineState *ms = MACHINE(qdev_get_machine());
160     int max_cpus = ms->smp.max_cpus;
161 
162     vcpu_dirty_rate_stat =
163         g_malloc0(sizeof(*vcpu_dirty_rate_stat));
164 
165     vcpu_dirty_rate_stat->stat.nvcpu = max_cpus;
166     vcpu_dirty_rate_stat->stat.rates =
167         g_new0(DirtyRateVcpu, max_cpus);
168 
169     vcpu_dirty_rate_stat->running = false;
170 }
171 
172 void vcpu_dirty_rate_stat_finalize(void)
173 {
174     g_free(vcpu_dirty_rate_stat->stat.rates);
175     vcpu_dirty_rate_stat->stat.rates = NULL;
176 
177     g_free(vcpu_dirty_rate_stat);
178     vcpu_dirty_rate_stat = NULL;
179 }
180 
181 void dirtylimit_state_lock(void)
182 {
183     qemu_mutex_lock(&dirtylimit_mutex);
184 }
185 
186 void dirtylimit_state_unlock(void)
187 {
188     qemu_mutex_unlock(&dirtylimit_mutex);
189 }
190 
191 static void
192 __attribute__((__constructor__)) dirtylimit_mutex_init(void)
193 {
194     qemu_mutex_init(&dirtylimit_mutex);
195 }
196 
197 static inline VcpuDirtyLimitState *dirtylimit_vcpu_get_state(int cpu_index)
198 {
199     return &dirtylimit_state->states[cpu_index];
200 }
201 
202 void dirtylimit_state_initialize(void)
203 {
204     MachineState *ms = MACHINE(qdev_get_machine());
205     int max_cpus = ms->smp.max_cpus;
206     int i;
207 
208     dirtylimit_state = g_malloc0(sizeof(*dirtylimit_state));
209 
210     dirtylimit_state->states =
211             g_new0(VcpuDirtyLimitState, max_cpus);
212 
213     for (i = 0; i < max_cpus; i++) {
214         dirtylimit_state->states[i].cpu_index = i;
215     }
216 
217     dirtylimit_state->max_cpus = max_cpus;
218     trace_dirtylimit_state_initialize(max_cpus);
219 }
220 
221 void dirtylimit_state_finalize(void)
222 {
223     g_free(dirtylimit_state->states);
224     dirtylimit_state->states = NULL;
225 
226     g_free(dirtylimit_state);
227     dirtylimit_state = NULL;
228 
229     trace_dirtylimit_state_finalize();
230 }
231 
232 bool dirtylimit_in_service(void)
233 {
234     return !!dirtylimit_state;
235 }
236 
237 bool dirtylimit_vcpu_index_valid(int cpu_index)
238 {
239     MachineState *ms = MACHINE(qdev_get_machine());
240 
241     return !(cpu_index < 0 ||
242              cpu_index >= ms->smp.max_cpus);
243 }
244 
245 static uint64_t dirtylimit_dirty_ring_full_time(uint64_t dirtyrate)
246 {
247     static uint64_t max_dirtyrate;
248     uint64_t dirty_ring_size_MiB;
249 
250     dirty_ring_size_MiB = qemu_target_pages_to_MiB(kvm_dirty_ring_size());
251 
252     if (max_dirtyrate < dirtyrate) {
253         max_dirtyrate = dirtyrate;
254     }
255 
256     return dirty_ring_size_MiB * 1000000 / max_dirtyrate;
257 }
258 
259 static inline bool dirtylimit_done(uint64_t quota,
260                                    uint64_t current)
261 {
262     uint64_t min, max;
263 
264     min = MIN(quota, current);
265     max = MAX(quota, current);
266 
267     return ((max - min) <= DIRTYLIMIT_TOLERANCE_RANGE) ? true : false;
268 }
269 
270 static inline bool
271 dirtylimit_need_linear_adjustment(uint64_t quota,
272                                   uint64_t current)
273 {
274     uint64_t min, max;
275 
276     min = MIN(quota, current);
277     max = MAX(quota, current);
278 
279     return ((max - min) * 100 / max) > DIRTYLIMIT_LINEAR_ADJUSTMENT_PCT;
280 }
281 
282 static void dirtylimit_set_throttle(CPUState *cpu,
283                                     uint64_t quota,
284                                     uint64_t current)
285 {
286     int64_t ring_full_time_us = 0;
287     uint64_t sleep_pct = 0;
288     uint64_t throttle_us = 0;
289 
290     if (current == 0) {
291         cpu->throttle_us_per_full = 0;
292         return;
293     }
294 
295     ring_full_time_us = dirtylimit_dirty_ring_full_time(current);
296 
297     if (dirtylimit_need_linear_adjustment(quota, current)) {
298         if (quota < current) {
299             sleep_pct = (current - quota) * 100 / current;
300             throttle_us =
301                 ring_full_time_us * sleep_pct / (double)(100 - sleep_pct);
302             cpu->throttle_us_per_full += throttle_us;
303         } else {
304             sleep_pct = (quota - current) * 100 / quota;
305             throttle_us =
306                 ring_full_time_us * sleep_pct / (double)(100 - sleep_pct);
307             cpu->throttle_us_per_full -= throttle_us;
308         }
309 
310         trace_dirtylimit_throttle_pct(cpu->cpu_index,
311                                       sleep_pct,
312                                       throttle_us);
313     } else {
314         if (quota < current) {
315             cpu->throttle_us_per_full += ring_full_time_us / 10;
316         } else {
317             cpu->throttle_us_per_full -= ring_full_time_us / 10;
318         }
319     }
320 
321     /*
322      * TODO: in the big kvm_dirty_ring_size case (eg: 65536, or other scenario),
323      *       current dirty page rate may never reach the quota, we should stop
324      *       increasing sleep time?
325      */
326     cpu->throttle_us_per_full = MIN(cpu->throttle_us_per_full,
327         ring_full_time_us * DIRTYLIMIT_THROTTLE_PCT_MAX);
328 
329     cpu->throttle_us_per_full = MAX(cpu->throttle_us_per_full, 0);
330 }
331 
332 static void dirtylimit_adjust_throttle(CPUState *cpu)
333 {
334     uint64_t quota = 0;
335     uint64_t current = 0;
336     int cpu_index = cpu->cpu_index;
337 
338     quota = dirtylimit_vcpu_get_state(cpu_index)->quota;
339     current = vcpu_dirty_rate_get(cpu_index);
340 
341     if (!dirtylimit_done(quota, current)) {
342         dirtylimit_set_throttle(cpu, quota, current);
343     }
344 
345     return;
346 }
347 
348 void dirtylimit_process(void)
349 {
350     CPUState *cpu;
351 
352     if (!qatomic_read(&dirtylimit_quit)) {
353         dirtylimit_state_lock();
354 
355         if (!dirtylimit_in_service()) {
356             dirtylimit_state_unlock();
357             return;
358         }
359 
360         CPU_FOREACH(cpu) {
361             if (!dirtylimit_vcpu_get_state(cpu->cpu_index)->enabled) {
362                 continue;
363             }
364             dirtylimit_adjust_throttle(cpu);
365         }
366         dirtylimit_state_unlock();
367     }
368 }
369 
370 void dirtylimit_change(bool start)
371 {
372     if (start) {
373         qatomic_set(&dirtylimit_quit, 0);
374     } else {
375         qatomic_set(&dirtylimit_quit, 1);
376     }
377 }
378 
379 void dirtylimit_set_vcpu(int cpu_index,
380                          uint64_t quota,
381                          bool enable)
382 {
383     trace_dirtylimit_set_vcpu(cpu_index, quota);
384 
385     if (enable) {
386         dirtylimit_state->states[cpu_index].quota = quota;
387         if (!dirtylimit_vcpu_get_state(cpu_index)->enabled) {
388             dirtylimit_state->limited_nvcpu++;
389         }
390     } else {
391         dirtylimit_state->states[cpu_index].quota = 0;
392         if (dirtylimit_state->states[cpu_index].enabled) {
393             dirtylimit_state->limited_nvcpu--;
394         }
395     }
396 
397     dirtylimit_state->states[cpu_index].enabled = enable;
398 }
399 
400 void dirtylimit_set_all(uint64_t quota,
401                         bool enable)
402 {
403     MachineState *ms = MACHINE(qdev_get_machine());
404     int max_cpus = ms->smp.max_cpus;
405     int i;
406 
407     for (i = 0; i < max_cpus; i++) {
408         dirtylimit_set_vcpu(i, quota, enable);
409     }
410 }
411 
412 void dirtylimit_vcpu_execute(CPUState *cpu)
413 {
414     if (cpu->throttle_us_per_full) {
415         dirtylimit_state_lock();
416 
417         if (dirtylimit_in_service() &&
418             dirtylimit_vcpu_get_state(cpu->cpu_index)->enabled) {
419             dirtylimit_state_unlock();
420             trace_dirtylimit_vcpu_execute(cpu->cpu_index,
421                     cpu->throttle_us_per_full);
422 
423             g_usleep(cpu->throttle_us_per_full);
424             return;
425         }
426 
427         dirtylimit_state_unlock();
428     }
429 }
430 
431 static void dirtylimit_init(void)
432 {
433     dirtylimit_state_initialize();
434     dirtylimit_change(true);
435     vcpu_dirty_rate_stat_initialize();
436     vcpu_dirty_rate_stat_start();
437 }
438 
439 static void dirtylimit_cleanup(void)
440 {
441     vcpu_dirty_rate_stat_stop();
442     vcpu_dirty_rate_stat_finalize();
443     dirtylimit_change(false);
444     dirtylimit_state_finalize();
445 }
446 
447 /*
448  * dirty page rate limit is not allowed to set if migration
449  * is running with dirty-limit capability enabled.
450  */
451 static bool dirtylimit_is_allowed(void)
452 {
453     MigrationState *ms = migrate_get_current();
454 
455     if (migration_is_running(ms->state) &&
456         (!qemu_thread_is_self(&ms->thread)) &&
457         migrate_dirty_limit() &&
458         dirtylimit_in_service()) {
459         return false;
460     }
461     return true;
462 }
463 
464 void qmp_cancel_vcpu_dirty_limit(bool has_cpu_index,
465                                  int64_t cpu_index,
466                                  Error **errp)
467 {
468     if (!kvm_enabled() || !kvm_dirty_ring_enabled()) {
469         return;
470     }
471 
472     if (has_cpu_index && !dirtylimit_vcpu_index_valid(cpu_index)) {
473         error_setg(errp, "incorrect cpu index specified");
474         return;
475     }
476 
477     if (!dirtylimit_is_allowed()) {
478         error_setg(errp, "can't cancel dirty page rate limit while"
479                    " migration is running");
480         return;
481     }
482 
483     if (!dirtylimit_in_service()) {
484         return;
485     }
486 
487     dirtylimit_state_lock();
488 
489     if (has_cpu_index) {
490         dirtylimit_set_vcpu(cpu_index, 0, false);
491     } else {
492         dirtylimit_set_all(0, false);
493     }
494 
495     if (!dirtylimit_state->limited_nvcpu) {
496         dirtylimit_cleanup();
497     }
498 
499     dirtylimit_state_unlock();
500 }
501 
502 void hmp_cancel_vcpu_dirty_limit(Monitor *mon, const QDict *qdict)
503 {
504     int64_t cpu_index = qdict_get_try_int(qdict, "cpu_index", -1);
505     Error *err = NULL;
506 
507     qmp_cancel_vcpu_dirty_limit(!!(cpu_index != -1), cpu_index, &err);
508     if (err) {
509         hmp_handle_error(mon, err);
510         return;
511     }
512 
513     monitor_printf(mon, "[Please use 'info vcpu_dirty_limit' to query "
514                    "dirty limit for virtual CPU]\n");
515 }
516 
517 void qmp_set_vcpu_dirty_limit(bool has_cpu_index,
518                               int64_t cpu_index,
519                               uint64_t dirty_rate,
520                               Error **errp)
521 {
522     if (!kvm_enabled() || !kvm_dirty_ring_enabled()) {
523         error_setg(errp, "dirty page limit feature requires KVM with"
524                    " accelerator property 'dirty-ring-size' set'");
525         return;
526     }
527 
528     if (has_cpu_index && !dirtylimit_vcpu_index_valid(cpu_index)) {
529         error_setg(errp, "incorrect cpu index specified");
530         return;
531     }
532 
533     if (!dirtylimit_is_allowed()) {
534         error_setg(errp, "can't set dirty page rate limit while"
535                    " migration is running");
536         return;
537     }
538 
539     if (!dirty_rate) {
540         qmp_cancel_vcpu_dirty_limit(has_cpu_index, cpu_index, errp);
541         return;
542     }
543 
544     dirtylimit_state_lock();
545 
546     if (!dirtylimit_in_service()) {
547         dirtylimit_init();
548     }
549 
550     if (has_cpu_index) {
551         dirtylimit_set_vcpu(cpu_index, dirty_rate, true);
552     } else {
553         dirtylimit_set_all(dirty_rate, true);
554     }
555 
556     dirtylimit_state_unlock();
557 }
558 
559 void hmp_set_vcpu_dirty_limit(Monitor *mon, const QDict *qdict)
560 {
561     int64_t dirty_rate = qdict_get_int(qdict, "dirty_rate");
562     int64_t cpu_index = qdict_get_try_int(qdict, "cpu_index", -1);
563     Error *err = NULL;
564 
565     if (dirty_rate < 0) {
566         error_setg(&err, "invalid dirty page limit %" PRId64, dirty_rate);
567         goto out;
568     }
569 
570     qmp_set_vcpu_dirty_limit(!!(cpu_index != -1), cpu_index, dirty_rate, &err);
571 
572 out:
573     hmp_handle_error(mon, err);
574 }
575 
576 /* Return the max throttle time of each virtual CPU */
577 uint64_t dirtylimit_throttle_time_per_round(void)
578 {
579     CPUState *cpu;
580     int64_t max = 0;
581 
582     CPU_FOREACH(cpu) {
583         if (cpu->throttle_us_per_full > max) {
584             max = cpu->throttle_us_per_full;
585         }
586     }
587 
588     return max;
589 }
590 
591 /*
592  * Estimate average dirty ring full time of each virtaul CPU.
593  * Return 0 if guest doesn't dirty memory.
594  */
595 uint64_t dirtylimit_ring_full_time(void)
596 {
597     CPUState *cpu;
598     uint64_t curr_rate = 0;
599     int nvcpus = 0;
600 
601     CPU_FOREACH(cpu) {
602         if (cpu->running) {
603             nvcpus++;
604             curr_rate += vcpu_dirty_rate_get(cpu->cpu_index);
605         }
606     }
607 
608     if (!curr_rate || !nvcpus) {
609         return 0;
610     }
611 
612     return dirtylimit_dirty_ring_full_time(curr_rate / nvcpus);
613 }
614 
615 static struct DirtyLimitInfo *dirtylimit_query_vcpu(int cpu_index)
616 {
617     DirtyLimitInfo *info = NULL;
618 
619     info = g_malloc0(sizeof(*info));
620     info->cpu_index = cpu_index;
621     info->limit_rate = dirtylimit_vcpu_get_state(cpu_index)->quota;
622     info->current_rate = vcpu_dirty_rate_get(cpu_index);
623 
624     return info;
625 }
626 
627 static struct DirtyLimitInfoList *dirtylimit_query_all(void)
628 {
629     int i, index;
630     DirtyLimitInfo *info = NULL;
631     DirtyLimitInfoList *head = NULL, **tail = &head;
632 
633     dirtylimit_state_lock();
634 
635     if (!dirtylimit_in_service()) {
636         dirtylimit_state_unlock();
637         return NULL;
638     }
639 
640     for (i = 0; i < dirtylimit_state->max_cpus; i++) {
641         index = dirtylimit_state->states[i].cpu_index;
642         if (dirtylimit_vcpu_get_state(index)->enabled) {
643             info = dirtylimit_query_vcpu(index);
644             QAPI_LIST_APPEND(tail, info);
645         }
646     }
647 
648     dirtylimit_state_unlock();
649 
650     return head;
651 }
652 
653 struct DirtyLimitInfoList *qmp_query_vcpu_dirty_limit(Error **errp)
654 {
655     return dirtylimit_query_all();
656 }
657 
658 void hmp_info_vcpu_dirty_limit(Monitor *mon, const QDict *qdict)
659 {
660     DirtyLimitInfoList *info;
661     g_autoptr(DirtyLimitInfoList) head = NULL;
662     Error *err = NULL;
663 
664     if (!dirtylimit_in_service()) {
665         monitor_printf(mon, "Dirty page limit not enabled!\n");
666         return;
667     }
668 
669     head = qmp_query_vcpu_dirty_limit(&err);
670     if (err) {
671         hmp_handle_error(mon, err);
672         return;
673     }
674 
675     for (info = head; info != NULL; info = info->next) {
676         monitor_printf(mon, "vcpu[%"PRIi64"], limit rate %"PRIi64 " (MB/s),"
677                             " current rate %"PRIi64 " (MB/s)\n",
678                             info->value->cpu_index,
679                             info->value->limit_rate,
680                             info->value->current_rate);
681     }
682 }
683