OSDN Git Service

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