1 | /* |
2 | * Ratelimiting calculations |
3 | * |
4 | * Copyright IBM, Corp. 2011 |
5 | * |
6 | * Authors: |
7 | * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> |
8 | * |
9 | * This work is licensed under the terms of the GNU LGPL, version 2 or later. |
10 | * See the COPYING.LIB file in the top-level directory. |
11 | * |
12 | */ |
13 | |
14 | #ifndef QEMU_RATELIMIT_H |
15 | #define QEMU_RATELIMIT_H |
16 | |
17 | #include "qemu/timer.h" |
18 | |
19 | typedef struct { |
20 | int64_t slice_start_time; |
21 | int64_t slice_end_time; |
22 | uint64_t slice_quota; |
23 | uint64_t slice_ns; |
24 | uint64_t dispatched; |
25 | } RateLimit; |
26 | |
27 | /** Calculate and return delay for next request in ns |
28 | * |
29 | * Record that we sent @n data units (where @n matches the scale chosen |
30 | * during ratelimit_set_speed). If we may send more data units |
31 | * in the current time slice, return 0 (i.e. no delay). Otherwise |
32 | * return the amount of time (in ns) until the start of the next time |
33 | * slice that will permit sending the next chunk of data. |
34 | * |
35 | * Recording sent data units even after exceeding the quota is |
36 | * permitted; the time slice will be extended accordingly. |
37 | */ |
38 | static inline int64_t ratelimit_calculate_delay(RateLimit *limit, uint64_t n) |
39 | { |
40 | int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); |
41 | double delay_slices; |
42 | |
43 | assert(limit->slice_quota && limit->slice_ns); |
44 | |
45 | if (limit->slice_end_time < now) { |
46 | /* Previous, possibly extended, time slice finished; reset the |
47 | * accounting. */ |
48 | limit->slice_start_time = now; |
49 | limit->slice_end_time = now + limit->slice_ns; |
50 | limit->dispatched = 0; |
51 | } |
52 | |
53 | limit->dispatched += n; |
54 | if (limit->dispatched < limit->slice_quota) { |
55 | /* We may send further data within the current time slice, no |
56 | * need to delay the next request. */ |
57 | return 0; |
58 | } |
59 | |
60 | /* Quota exceeded. Wait based on the excess amount and then start a new |
61 | * slice. */ |
62 | delay_slices = (double)limit->dispatched / limit->slice_quota; |
63 | limit->slice_end_time = limit->slice_start_time + |
64 | (uint64_t)(delay_slices * limit->slice_ns); |
65 | return limit->slice_end_time - now; |
66 | } |
67 | |
68 | static inline void ratelimit_set_speed(RateLimit *limit, uint64_t speed, |
69 | uint64_t slice_ns) |
70 | { |
71 | limit->slice_ns = slice_ns; |
72 | limit->slice_quota = MAX(((double)speed * slice_ns) / 1000000000ULL, 1); |
73 | } |
74 | |
75 | #endif |
76 | |