| 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/lockable.h" |
| 18 | #include "qemu/timer.h" |
| 19 | |
| 20 | typedef struct { |
| 21 | QemuMutex lock; |
| 22 | int64_t slice_start_time; |
| 23 | int64_t slice_end_time; |
| 24 | uint64_t slice_quota; |
| 25 | uint64_t slice_ns; |
| 26 | uint64_t dispatched; |
| 27 | } RateLimit; |
| 28 | |
| 29 | /** Calculate and return delay for next request in ns |
| 30 | * |
| 31 | * Record that we sent @n data units (where @n matches the scale chosen |
| 32 | * during ratelimit_set_speed). If we may send more data units |
| 33 | * in the current time slice, return 0 (i.e. no delay). Otherwise |
| 34 | * return the amount of time (in ns) until the start of the next time |
| 35 | * slice that will permit sending the next chunk of data. |
| 36 | * |
| 37 | * Recording sent data units even after exceeding the quota is |
| 38 | * permitted; the time slice will be extended accordingly. |
| 39 | */ |
| 40 | static inline int64_t ratelimit_calculate_delay(RateLimit *limit, uint64_t n) |
| 41 | { |
| 42 | int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); |
| 43 | double delay_slices; |
| 44 | |
| 45 | QEMU_LOCK_GUARD(&limit->lock); |
| 46 | if (!limit->slice_quota) { |
| 47 | /* Throttling disabled. */ |
| 48 | return 0; |
| 49 | } |
| 50 | assert(limit->slice_ns); |
| 51 | |
| 52 | if (limit->slice_end_time < now) { |
| 53 | /* Previous, possibly extended, time slice finished; reset the |
| 54 | * accounting. */ |
| 55 | limit->slice_start_time = now; |
| 56 | limit->slice_end_time = now + limit->slice_ns; |
| 57 | limit->dispatched = 0; |
| 58 | } |
| 59 | |
| 60 | limit->dispatched += n; |
| 61 | if (limit->dispatched < limit->slice_quota) { |
| 62 | /* We may send further data within the current time slice, no |
| 63 | * need to delay the next request. */ |
| 64 | return 0; |
| 65 | } |
| 66 | |
| 67 | /* Quota exceeded. Wait based on the excess amount and then start a new |
| 68 | * slice. */ |
| 69 | delay_slices = (double)limit->dispatched / limit->slice_quota; |
| 70 | limit->slice_end_time = limit->slice_start_time + |
| 71 | (uint64_t)(delay_slices * limit->slice_ns); |
| 72 | return limit->slice_end_time - now; |
| 73 | } |
| 74 | |
| 75 | static inline void ratelimit_init(RateLimit *limit) |
| 76 | { |
| 77 | qemu_mutex_init(&limit->lock); |
| 78 | } |
| 79 | |
| 80 | static inline void ratelimit_destroy(RateLimit *limit) |
| 81 | { |
| 82 | qemu_mutex_destroy(&limit->lock); |
| 83 | } |
| 84 | |
| 85 | static inline void ratelimit_set_speed(RateLimit *limit, uint64_t speed, |
| 86 | uint64_t slice_ns) |
| 87 | { |
| 88 | QEMU_LOCK_GUARD(&limit->lock); |
| 89 | limit->slice_ns = slice_ns; |
| 90 | if (speed == 0) { |
| 91 | limit->slice_quota = 0; |
| 92 | } else { |
| 93 | limit->slice_quota = MAX(((double)speed * slice_ns) / 1000000000ULL, 1); |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | #endif |