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