1737c9cd8SNick Mathewson /*
2e49e2891SNick Mathewson  * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
3737c9cd8SNick Mathewson  * Copyright (c) 2002-2006 Niels Provos <[email protected]>
4737c9cd8SNick Mathewson  * All rights reserved.
5737c9cd8SNick Mathewson  *
6737c9cd8SNick Mathewson  * Redistribution and use in source and binary forms, with or without
7737c9cd8SNick Mathewson  * modification, are permitted provided that the following conditions
8737c9cd8SNick Mathewson  * are met:
9737c9cd8SNick Mathewson  * 1. Redistributions of source code must retain the above copyright
10737c9cd8SNick Mathewson  *    notice, this list of conditions and the following disclaimer.
11737c9cd8SNick Mathewson  * 2. Redistributions in binary form must reproduce the above copyright
12737c9cd8SNick Mathewson  *    notice, this list of conditions and the following disclaimer in the
13737c9cd8SNick Mathewson  *    documentation and/or other materials provided with the distribution.
14737c9cd8SNick Mathewson  * 3. The name of the author may not be used to endorse or promote products
15737c9cd8SNick Mathewson  *    derived from this software without specific prior written permission.
16737c9cd8SNick Mathewson  *
17737c9cd8SNick Mathewson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18737c9cd8SNick Mathewson  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19737c9cd8SNick Mathewson  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20737c9cd8SNick Mathewson  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21737c9cd8SNick Mathewson  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22737c9cd8SNick Mathewson  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23737c9cd8SNick Mathewson  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24737c9cd8SNick Mathewson  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25737c9cd8SNick Mathewson  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26737c9cd8SNick Mathewson  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27737c9cd8SNick Mathewson  */
28ded0a090SKevin Bowling #include "evconfig-private.h"
29737c9cd8SNick Mathewson 
30737c9cd8SNick Mathewson #include <sys/types.h>
31737c9cd8SNick Mathewson #include <limits.h>
32737c9cd8SNick Mathewson #include <string.h>
33737c9cd8SNick Mathewson #include <stdlib.h>
34737c9cd8SNick Mathewson 
35737c9cd8SNick Mathewson #include "event2/event.h"
36737c9cd8SNick Mathewson #include "event2/event_struct.h"
37737c9cd8SNick Mathewson #include "event2/util.h"
38737c9cd8SNick Mathewson #include "event2/bufferevent.h"
39737c9cd8SNick Mathewson #include "event2/bufferevent_struct.h"
40737c9cd8SNick Mathewson #include "event2/buffer.h"
41737c9cd8SNick Mathewson 
42737c9cd8SNick Mathewson #include "ratelim-internal.h"
43737c9cd8SNick Mathewson 
44737c9cd8SNick Mathewson #include "bufferevent-internal.h"
45737c9cd8SNick Mathewson #include "mm-internal.h"
46737c9cd8SNick Mathewson #include "util-internal.h"
475b18f130SNick Mathewson #include "event-internal.h"
48737c9cd8SNick Mathewson 
49737c9cd8SNick Mathewson int
ev_token_bucket_init_(struct ev_token_bucket * bucket,const struct ev_token_bucket_cfg * cfg,ev_uint32_t current_tick,int reinitialize)508ac3c4c2SNick Mathewson ev_token_bucket_init_(struct ev_token_bucket *bucket,
51737c9cd8SNick Mathewson     const struct ev_token_bucket_cfg *cfg,
52737c9cd8SNick Mathewson     ev_uint32_t current_tick,
53737c9cd8SNick Mathewson     int reinitialize)
54737c9cd8SNick Mathewson {
55737c9cd8SNick Mathewson 	if (reinitialize) {
56737c9cd8SNick Mathewson 		/* on reinitialization, we only clip downwards, since we've
57737c9cd8SNick Mathewson 		   already used who-knows-how-much bandwidth this tick.  We
58737c9cd8SNick Mathewson 		   leave "last_updated" as it is; the next update will add the
59737c9cd8SNick Mathewson 		   appropriate amount of bandwidth to the bucket.
60737c9cd8SNick Mathewson 		*/
619c8db0f8SNick Mathewson 		if (bucket->read_limit > (ev_int64_t) cfg->read_maximum)
62737c9cd8SNick Mathewson 			bucket->read_limit = cfg->read_maximum;
639c8db0f8SNick Mathewson 		if (bucket->write_limit > (ev_int64_t) cfg->write_maximum)
64737c9cd8SNick Mathewson 			bucket->write_limit = cfg->write_maximum;
65737c9cd8SNick Mathewson 	} else {
66737c9cd8SNick Mathewson 		bucket->read_limit = cfg->read_rate;
67737c9cd8SNick Mathewson 		bucket->write_limit = cfg->write_rate;
68737c9cd8SNick Mathewson 		bucket->last_updated = current_tick;
69737c9cd8SNick Mathewson 	}
70737c9cd8SNick Mathewson 	return 0;
71737c9cd8SNick Mathewson }
72737c9cd8SNick Mathewson 
73737c9cd8SNick Mathewson int
ev_token_bucket_update_(struct ev_token_bucket * bucket,const struct ev_token_bucket_cfg * cfg,ev_uint32_t current_tick)748ac3c4c2SNick Mathewson ev_token_bucket_update_(struct ev_token_bucket *bucket,
75737c9cd8SNick Mathewson     const struct ev_token_bucket_cfg *cfg,
76737c9cd8SNick Mathewson     ev_uint32_t current_tick)
77737c9cd8SNick Mathewson {
78737c9cd8SNick Mathewson 	/* It's okay if the tick number overflows, since we'll just
79737c9cd8SNick Mathewson 	 * wrap around when we do the unsigned substraction. */
80737c9cd8SNick Mathewson 	unsigned n_ticks = current_tick - bucket->last_updated;
81737c9cd8SNick Mathewson 
82737c9cd8SNick Mathewson 	/* Make sure some ticks actually happened, and that time didn't
83737c9cd8SNick Mathewson 	 * roll back. */
84737c9cd8SNick Mathewson 	if (n_ticks == 0 || n_ticks > INT_MAX)
85737c9cd8SNick Mathewson 		return 0;
86737c9cd8SNick Mathewson 
87737c9cd8SNick Mathewson 	/* Naively, we would say
88737c9cd8SNick Mathewson 		bucket->limit += n_ticks * cfg->rate;
89737c9cd8SNick Mathewson 
90737c9cd8SNick Mathewson 		if (bucket->limit > cfg->maximum)
91737c9cd8SNick Mathewson 			bucket->limit = cfg->maximum;
92737c9cd8SNick Mathewson 
93737c9cd8SNick Mathewson 	   But we're worried about overflow, so we do it like this:
94737c9cd8SNick Mathewson 	*/
95737c9cd8SNick Mathewson 
96737c9cd8SNick Mathewson 	if ((cfg->read_maximum - bucket->read_limit) / n_ticks < cfg->read_rate)
97737c9cd8SNick Mathewson 		bucket->read_limit = cfg->read_maximum;
98737c9cd8SNick Mathewson 	else
99737c9cd8SNick Mathewson 		bucket->read_limit += n_ticks * cfg->read_rate;
100737c9cd8SNick Mathewson 
101737c9cd8SNick Mathewson 
102737c9cd8SNick Mathewson 	if ((cfg->write_maximum - bucket->write_limit) / n_ticks < cfg->write_rate)
103737c9cd8SNick Mathewson 		bucket->write_limit = cfg->write_maximum;
104737c9cd8SNick Mathewson 	else
105737c9cd8SNick Mathewson 		bucket->write_limit += n_ticks * cfg->write_rate;
106737c9cd8SNick Mathewson 
107737c9cd8SNick Mathewson 
108737c9cd8SNick Mathewson 	bucket->last_updated = current_tick;
109737c9cd8SNick Mathewson 
110737c9cd8SNick Mathewson 	return 1;
111737c9cd8SNick Mathewson }
112737c9cd8SNick Mathewson 
11385047a69SNick Mathewson static inline void
bufferevent_update_buckets(struct bufferevent_private * bev)11485047a69SNick Mathewson bufferevent_update_buckets(struct bufferevent_private *bev)
11585047a69SNick Mathewson {
11685047a69SNick Mathewson 	/* Must hold lock on bev. */
11785047a69SNick Mathewson 	struct timeval now;
11885047a69SNick Mathewson 	unsigned tick;
11985047a69SNick Mathewson 	event_base_gettimeofday_cached(bev->bev.ev_base, &now);
1208ac3c4c2SNick Mathewson 	tick = ev_token_bucket_get_tick_(&now, bev->rate_limiting->cfg);
12185047a69SNick Mathewson 	if (tick != bev->rate_limiting->limit.last_updated)
1228ac3c4c2SNick Mathewson 		ev_token_bucket_update_(&bev->rate_limiting->limit,
12385047a69SNick Mathewson 		    bev->rate_limiting->cfg, tick);
12485047a69SNick Mathewson }
12585047a69SNick Mathewson 
126737c9cd8SNick Mathewson ev_uint32_t
ev_token_bucket_get_tick_(const struct timeval * tv,const struct ev_token_bucket_cfg * cfg)1278ac3c4c2SNick Mathewson ev_token_bucket_get_tick_(const struct timeval *tv,
128737c9cd8SNick Mathewson     const struct ev_token_bucket_cfg *cfg)
129737c9cd8SNick Mathewson {
130737c9cd8SNick Mathewson 	/* This computation uses two multiplies and a divide.  We could do
131737c9cd8SNick Mathewson 	 * fewer if we knew that the tick length was an integer number of
132737c9cd8SNick Mathewson 	 * seconds, or if we knew it divided evenly into a second.  We should
133737c9cd8SNick Mathewson 	 * investigate that more.
134737c9cd8SNick Mathewson 	 */
135737c9cd8SNick Mathewson 
136737c9cd8SNick Mathewson 	/* We cast to an ev_uint64_t first, since we don't want to overflow
137737c9cd8SNick Mathewson 	 * before we do the final divide. */
138737c9cd8SNick Mathewson 	ev_uint64_t msec = (ev_uint64_t)tv->tv_sec * 1000 + tv->tv_usec / 1000;
139737c9cd8SNick Mathewson 	return (unsigned)(msec / cfg->msec_per_tick);
140737c9cd8SNick Mathewson }
141737c9cd8SNick Mathewson 
142737c9cd8SNick Mathewson struct ev_token_bucket_cfg *
ev_token_bucket_cfg_new(size_t read_rate,size_t read_burst,size_t write_rate,size_t write_burst,const struct timeval * tick_len)1432cbb1a16SNick Mathewson ev_token_bucket_cfg_new(size_t read_rate, size_t read_burst,
1442cbb1a16SNick Mathewson     size_t write_rate, size_t write_burst,
145737c9cd8SNick Mathewson     const struct timeval *tick_len)
146737c9cd8SNick Mathewson {
147737c9cd8SNick Mathewson 	struct ev_token_bucket_cfg *r;
148737c9cd8SNick Mathewson 	struct timeval g;
149737c9cd8SNick Mathewson 	if (! tick_len) {
150737c9cd8SNick Mathewson 		g.tv_sec = 1;
151737c9cd8SNick Mathewson 		g.tv_usec = 0;
152737c9cd8SNick Mathewson 		tick_len = &g;
153737c9cd8SNick Mathewson 	}
154737c9cd8SNick Mathewson 	if (read_rate > read_burst || write_rate > write_burst ||
155737c9cd8SNick Mathewson 	    read_rate < 1 || write_rate < 1)
156737c9cd8SNick Mathewson 		return NULL;
1572cbb1a16SNick Mathewson 	if (read_rate > EV_RATE_LIMIT_MAX ||
1582cbb1a16SNick Mathewson 	    write_rate > EV_RATE_LIMIT_MAX ||
1592cbb1a16SNick Mathewson 	    read_burst > EV_RATE_LIMIT_MAX ||
1602cbb1a16SNick Mathewson 	    write_burst > EV_RATE_LIMIT_MAX)
1612cbb1a16SNick Mathewson 		return NULL;
162737c9cd8SNick Mathewson 	r = mm_calloc(1, sizeof(struct ev_token_bucket_cfg));
163737c9cd8SNick Mathewson 	if (!r)
164737c9cd8SNick Mathewson 		return NULL;
165737c9cd8SNick Mathewson 	r->read_rate = read_rate;
166737c9cd8SNick Mathewson 	r->write_rate = write_rate;
167737c9cd8SNick Mathewson 	r->read_maximum = read_burst;
168737c9cd8SNick Mathewson 	r->write_maximum = write_burst;
169737c9cd8SNick Mathewson 	memcpy(&r->tick_timeout, tick_len, sizeof(struct timeval));
1705b18f130SNick Mathewson 	r->msec_per_tick = (tick_len->tv_sec * 1000) +
1715b18f130SNick Mathewson 	    (tick_len->tv_usec & COMMON_TIMEOUT_MICROSECONDS_MASK)/1000;
172737c9cd8SNick Mathewson 	return r;
173737c9cd8SNick Mathewson }
174737c9cd8SNick Mathewson 
175737c9cd8SNick Mathewson void
ev_token_bucket_cfg_free(struct ev_token_bucket_cfg * cfg)176737c9cd8SNick Mathewson ev_token_bucket_cfg_free(struct ev_token_bucket_cfg *cfg)
177737c9cd8SNick Mathewson {
178737c9cd8SNick Mathewson 	mm_free(cfg);
179737c9cd8SNick Mathewson }
180737c9cd8SNick Mathewson 
181998c8138SAlexander Drozdov /* Default values for max_single_read & max_single_write variables. */
182998c8138SAlexander Drozdov #define MAX_SINGLE_READ_DEFAULT 16384
183998c8138SAlexander Drozdov #define MAX_SINGLE_WRITE_DEFAULT 16384
184737c9cd8SNick Mathewson 
185737c9cd8SNick Mathewson #define LOCK_GROUP(g) EVLOCK_LOCK((g)->lock, 0)
186737c9cd8SNick Mathewson #define UNLOCK_GROUP(g) EVLOCK_UNLOCK((g)->lock, 0)
187737c9cd8SNick Mathewson 
188cb9da0bfSNick Mathewson static int bev_group_suspend_reading_(struct bufferevent_rate_limit_group *g);
189cb9da0bfSNick Mathewson static int bev_group_suspend_writing_(struct bufferevent_rate_limit_group *g);
190cb9da0bfSNick Mathewson static void bev_group_unsuspend_reading_(struct bufferevent_rate_limit_group *g);
191cb9da0bfSNick Mathewson static void bev_group_unsuspend_writing_(struct bufferevent_rate_limit_group *g);
192737c9cd8SNick Mathewson 
193737c9cd8SNick Mathewson /** Helper: figure out the maximum amount we should write if is_write, or
194737c9cd8SNick Mathewson     the maximum amount we should read if is_read.  Return that maximum, or
195737c9cd8SNick Mathewson     0 if our bucket is wholly exhausted.
196737c9cd8SNick Mathewson  */
197598d1336SNick Mathewson static inline ev_ssize_t
bufferevent_get_rlim_max_(struct bufferevent_private * bev,int is_write)198cb9da0bfSNick Mathewson bufferevent_get_rlim_max_(struct bufferevent_private *bev, int is_write)
199737c9cd8SNick Mathewson {
200737c9cd8SNick Mathewson 	/* needs lock on bev. */
201998c8138SAlexander Drozdov 	ev_ssize_t max_so_far = is_write?bev->max_single_write:bev->max_single_read;
202737c9cd8SNick Mathewson 
203737c9cd8SNick Mathewson #define LIM(x)						\
204737c9cd8SNick Mathewson 	(is_write ? (x).write_limit : (x).read_limit)
205737c9cd8SNick Mathewson 
206737c9cd8SNick Mathewson #define GROUP_SUSPENDED(g)			\
207737c9cd8SNick Mathewson 	(is_write ? (g)->write_suspended : (g)->read_suspended)
208737c9cd8SNick Mathewson 
209737c9cd8SNick Mathewson 	/* Sets max_so_far to MIN(x, max_so_far) */
210737c9cd8SNick Mathewson #define CLAMPTO(x)				\
211737c9cd8SNick Mathewson 	do {					\
212737c9cd8SNick Mathewson 		if (max_so_far > (x))		\
213737c9cd8SNick Mathewson 			max_so_far = (x);	\
214737c9cd8SNick Mathewson 	} while (0);
215737c9cd8SNick Mathewson 
216737c9cd8SNick Mathewson 	if (!bev->rate_limiting)
217737c9cd8SNick Mathewson 		return max_so_far;
218737c9cd8SNick Mathewson 
219737c9cd8SNick Mathewson 	/* If rate-limiting is enabled at all, update the appropriate
220737c9cd8SNick Mathewson 	   bucket, and take the smaller of our rate limit and the group
221737c9cd8SNick Mathewson 	   rate limit.
222737c9cd8SNick Mathewson 	 */
223737c9cd8SNick Mathewson 
224737c9cd8SNick Mathewson 	if (bev->rate_limiting->cfg) {
22585047a69SNick Mathewson 		bufferevent_update_buckets(bev);
226737c9cd8SNick Mathewson 		max_so_far = LIM(bev->rate_limiting->limit);
227737c9cd8SNick Mathewson 	}
228737c9cd8SNick Mathewson 	if (bev->rate_limiting->group) {
229737c9cd8SNick Mathewson 		struct bufferevent_rate_limit_group *g =
230737c9cd8SNick Mathewson 		    bev->rate_limiting->group;
2312cbb1a16SNick Mathewson 		ev_ssize_t share;
232737c9cd8SNick Mathewson 		LOCK_GROUP(g);
233737c9cd8SNick Mathewson 		if (GROUP_SUSPENDED(g)) {
234737c9cd8SNick Mathewson 			/* We can get here if we failed to lock this
235737c9cd8SNick Mathewson 			 * particular bufferevent while suspending the whole
236737c9cd8SNick Mathewson 			 * group. */
237737c9cd8SNick Mathewson 			if (is_write)
2388ac3c4c2SNick Mathewson 				bufferevent_suspend_write_(&bev->bev,
239737c9cd8SNick Mathewson 				    BEV_SUSPEND_BW_GROUP);
240737c9cd8SNick Mathewson 			else
2418ac3c4c2SNick Mathewson 				bufferevent_suspend_read_(&bev->bev,
242737c9cd8SNick Mathewson 				    BEV_SUSPEND_BW_GROUP);
243737c9cd8SNick Mathewson 			share = 0;
244737c9cd8SNick Mathewson 		} else {
245737c9cd8SNick Mathewson 			/* XXXX probably we should divide among the active
246737c9cd8SNick Mathewson 			 * members, not the total members. */
247737c9cd8SNick Mathewson 			share = LIM(g->rate_limit) / g->n_members;
248737c9cd8SNick Mathewson 			if (share < g->min_share)
249737c9cd8SNick Mathewson 				share = g->min_share;
250737c9cd8SNick Mathewson 		}
251737c9cd8SNick Mathewson 		UNLOCK_GROUP(g);
252737c9cd8SNick Mathewson 		CLAMPTO(share);
253737c9cd8SNick Mathewson 	}
254737c9cd8SNick Mathewson 
25508598709SNick Mathewson 	if (max_so_far < 0)
25608598709SNick Mathewson 		max_so_far = 0;
257737c9cd8SNick Mathewson 	return max_so_far;
258737c9cd8SNick Mathewson }
259737c9cd8SNick Mathewson 
260598d1336SNick Mathewson ev_ssize_t
bufferevent_get_read_max_(struct bufferevent_private * bev)261cb9da0bfSNick Mathewson bufferevent_get_read_max_(struct bufferevent_private *bev)
262737c9cd8SNick Mathewson {
263cb9da0bfSNick Mathewson 	return bufferevent_get_rlim_max_(bev, 0);
264737c9cd8SNick Mathewson }
265737c9cd8SNick Mathewson 
266598d1336SNick Mathewson ev_ssize_t
bufferevent_get_write_max_(struct bufferevent_private * bev)267cb9da0bfSNick Mathewson bufferevent_get_write_max_(struct bufferevent_private *bev)
268737c9cd8SNick Mathewson {
269cb9da0bfSNick Mathewson 	return bufferevent_get_rlim_max_(bev, 1);
270737c9cd8SNick Mathewson }
271737c9cd8SNick Mathewson 
272737c9cd8SNick Mathewson int
bufferevent_decrement_read_buckets_(struct bufferevent_private * bev,ev_ssize_t bytes)273cb9da0bfSNick Mathewson bufferevent_decrement_read_buckets_(struct bufferevent_private *bev, ev_ssize_t bytes)
274737c9cd8SNick Mathewson {
275ff3f6cd4SNick Mathewson 	/* XXXXX Make sure all users of this function check its return value */
276ff3f6cd4SNick Mathewson 	int r = 0;
277737c9cd8SNick Mathewson 	/* need to hold lock on bev */
278737c9cd8SNick Mathewson 	if (!bev->rate_limiting)
279737c9cd8SNick Mathewson 		return 0;
280737c9cd8SNick Mathewson 
281737c9cd8SNick Mathewson 	if (bev->rate_limiting->cfg) {
282737c9cd8SNick Mathewson 		bev->rate_limiting->limit.read_limit -= bytes;
283737c9cd8SNick Mathewson 		if (bev->rate_limiting->limit.read_limit <= 0) {
2848ac3c4c2SNick Mathewson 			bufferevent_suspend_read_(&bev->bev, BEV_SUSPEND_BW);
285ff3f6cd4SNick Mathewson 			if (event_add(&bev->rate_limiting->refill_bucket_event,
286ff3f6cd4SNick Mathewson 				&bev->rate_limiting->cfg->tick_timeout) < 0)
287ff3f6cd4SNick Mathewson 				r = -1;
288c75341b0SNick Mathewson 		} else if (bev->read_suspended & BEV_SUSPEND_BW) {
289c75341b0SNick Mathewson 			if (!(bev->write_suspended & BEV_SUSPEND_BW))
290c75341b0SNick Mathewson 				event_del(&bev->rate_limiting->refill_bucket_event);
2918ac3c4c2SNick Mathewson 			bufferevent_unsuspend_read_(&bev->bev, BEV_SUSPEND_BW);
292737c9cd8SNick Mathewson 		}
293737c9cd8SNick Mathewson 	}
294737c9cd8SNick Mathewson 
295737c9cd8SNick Mathewson 	if (bev->rate_limiting->group) {
296737c9cd8SNick Mathewson 		LOCK_GROUP(bev->rate_limiting->group);
297737c9cd8SNick Mathewson 		bev->rate_limiting->group->rate_limit.read_limit -= bytes;
298fb366c1dSNick Mathewson 		bev->rate_limiting->group->total_read += bytes;
299737c9cd8SNick Mathewson 		if (bev->rate_limiting->group->rate_limit.read_limit <= 0) {
300cb9da0bfSNick Mathewson 			bev_group_suspend_reading_(bev->rate_limiting->group);
301c75341b0SNick Mathewson 		} else if (bev->rate_limiting->group->read_suspended) {
302cb9da0bfSNick Mathewson 			bev_group_unsuspend_reading_(bev->rate_limiting->group);
303737c9cd8SNick Mathewson 		}
304737c9cd8SNick Mathewson 		UNLOCK_GROUP(bev->rate_limiting->group);
305737c9cd8SNick Mathewson 	}
306737c9cd8SNick Mathewson 
307ff3f6cd4SNick Mathewson 	return r;
308737c9cd8SNick Mathewson }
309737c9cd8SNick Mathewson 
310737c9cd8SNick Mathewson int
bufferevent_decrement_write_buckets_(struct bufferevent_private * bev,ev_ssize_t bytes)311cb9da0bfSNick Mathewson bufferevent_decrement_write_buckets_(struct bufferevent_private *bev, ev_ssize_t bytes)
312737c9cd8SNick Mathewson {
313ff3f6cd4SNick Mathewson 	/* XXXXX Make sure all users of this function check its return value */
314ff3f6cd4SNick Mathewson 	int r = 0;
315737c9cd8SNick Mathewson 	/* need to hold lock */
316737c9cd8SNick Mathewson 	if (!bev->rate_limiting)
317737c9cd8SNick Mathewson 		return 0;
318737c9cd8SNick Mathewson 
319737c9cd8SNick Mathewson 	if (bev->rate_limiting->cfg) {
320737c9cd8SNick Mathewson 		bev->rate_limiting->limit.write_limit -= bytes;
321737c9cd8SNick Mathewson 		if (bev->rate_limiting->limit.write_limit <= 0) {
3228ac3c4c2SNick Mathewson 			bufferevent_suspend_write_(&bev->bev, BEV_SUSPEND_BW);
323ff3f6cd4SNick Mathewson 			if (event_add(&bev->rate_limiting->refill_bucket_event,
324ff3f6cd4SNick Mathewson 				&bev->rate_limiting->cfg->tick_timeout) < 0)
325ff3f6cd4SNick Mathewson 				r = -1;
326c75341b0SNick Mathewson 		} else if (bev->write_suspended & BEV_SUSPEND_BW) {
327c75341b0SNick Mathewson 			if (!(bev->read_suspended & BEV_SUSPEND_BW))
328c75341b0SNick Mathewson 				event_del(&bev->rate_limiting->refill_bucket_event);
3298ac3c4c2SNick Mathewson 			bufferevent_unsuspend_write_(&bev->bev, BEV_SUSPEND_BW);
330737c9cd8SNick Mathewson 		}
331737c9cd8SNick Mathewson 	}
332737c9cd8SNick Mathewson 
333737c9cd8SNick Mathewson 	if (bev->rate_limiting->group) {
334737c9cd8SNick Mathewson 		LOCK_GROUP(bev->rate_limiting->group);
335737c9cd8SNick Mathewson 		bev->rate_limiting->group->rate_limit.write_limit -= bytes;
336fb366c1dSNick Mathewson 		bev->rate_limiting->group->total_written += bytes;
337737c9cd8SNick Mathewson 		if (bev->rate_limiting->group->rate_limit.write_limit <= 0) {
338cb9da0bfSNick Mathewson 			bev_group_suspend_writing_(bev->rate_limiting->group);
339c75341b0SNick Mathewson 		} else if (bev->rate_limiting->group->write_suspended) {
340cb9da0bfSNick Mathewson 			bev_group_unsuspend_writing_(bev->rate_limiting->group);
341737c9cd8SNick Mathewson 		}
342737c9cd8SNick Mathewson 		UNLOCK_GROUP(bev->rate_limiting->group);
343737c9cd8SNick Mathewson 	}
344737c9cd8SNick Mathewson 
345ff3f6cd4SNick Mathewson 	return r;
346737c9cd8SNick Mathewson }
347737c9cd8SNick Mathewson 
348737c9cd8SNick Mathewson /** Stop reading on every bufferevent in <b>g</b> */
349737c9cd8SNick Mathewson static int
bev_group_suspend_reading_(struct bufferevent_rate_limit_group * g)350cb9da0bfSNick Mathewson bev_group_suspend_reading_(struct bufferevent_rate_limit_group *g)
351737c9cd8SNick Mathewson {
352737c9cd8SNick Mathewson 	/* Needs group lock */
353737c9cd8SNick Mathewson 	struct bufferevent_private *bev;
354737c9cd8SNick Mathewson 	g->read_suspended = 1;
355737c9cd8SNick Mathewson 	g->pending_unsuspend_read = 0;
356737c9cd8SNick Mathewson 
3578ac3c4c2SNick Mathewson 	/* Note that in this loop we call EVLOCK_TRY_LOCK_ instead of BEV_LOCK,
358737c9cd8SNick Mathewson 	   to prevent a deadlock.  (Ordinarily, the group lock nests inside
359737c9cd8SNick Mathewson 	   the bufferevent locks.  If we are unable to lock any individual
360737c9cd8SNick Mathewson 	   bufferevent, it will find out later when it looks at its limit
36178d67b29SNick Mathewson 	   and sees that its group is suspended.)
362737c9cd8SNick Mathewson 	*/
363974d004eSNick Mathewson 	LIST_FOREACH(bev, &g->members, rate_limiting->next_in_group) {
3648ac3c4c2SNick Mathewson 		if (EVLOCK_TRY_LOCK_(bev->lock)) {
3658ac3c4c2SNick Mathewson 			bufferevent_suspend_read_(&bev->bev,
366737c9cd8SNick Mathewson 			    BEV_SUSPEND_BW_GROUP);
367737c9cd8SNick Mathewson 			EVLOCK_UNLOCK(bev->lock, 0);
368737c9cd8SNick Mathewson 		}
369737c9cd8SNick Mathewson 	}
370737c9cd8SNick Mathewson 	return 0;
371737c9cd8SNick Mathewson }
372737c9cd8SNick Mathewson 
373737c9cd8SNick Mathewson /** Stop writing on every bufferevent in <b>g</b> */
374737c9cd8SNick Mathewson static int
bev_group_suspend_writing_(struct bufferevent_rate_limit_group * g)375cb9da0bfSNick Mathewson bev_group_suspend_writing_(struct bufferevent_rate_limit_group *g)
376737c9cd8SNick Mathewson {
377737c9cd8SNick Mathewson 	/* Needs group lock */
378737c9cd8SNick Mathewson 	struct bufferevent_private *bev;
379737c9cd8SNick Mathewson 	g->write_suspended = 1;
380737c9cd8SNick Mathewson 	g->pending_unsuspend_write = 0;
381974d004eSNick Mathewson 	LIST_FOREACH(bev, &g->members, rate_limiting->next_in_group) {
3828ac3c4c2SNick Mathewson 		if (EVLOCK_TRY_LOCK_(bev->lock)) {
3838ac3c4c2SNick Mathewson 			bufferevent_suspend_write_(&bev->bev,
384737c9cd8SNick Mathewson 			    BEV_SUSPEND_BW_GROUP);
385737c9cd8SNick Mathewson 			EVLOCK_UNLOCK(bev->lock, 0);
386737c9cd8SNick Mathewson 		}
387737c9cd8SNick Mathewson 	}
388737c9cd8SNick Mathewson 	return 0;
389737c9cd8SNick Mathewson }
390737c9cd8SNick Mathewson 
391737c9cd8SNick Mathewson /** Timer callback invoked on a single bufferevent with one or more exhausted
392737c9cd8SNick Mathewson     buckets when they are ready to refill. */
393737c9cd8SNick Mathewson static void
bev_refill_callback_(evutil_socket_t fd,short what,void * arg)394cb9da0bfSNick Mathewson bev_refill_callback_(evutil_socket_t fd, short what, void *arg)
395737c9cd8SNick Mathewson {
396737c9cd8SNick Mathewson 	unsigned tick;
397737c9cd8SNick Mathewson 	struct timeval now;
398737c9cd8SNick Mathewson 	struct bufferevent_private *bev = arg;
399737c9cd8SNick Mathewson 	int again = 0;
400737c9cd8SNick Mathewson 	BEV_LOCK(&bev->bev);
401737c9cd8SNick Mathewson 	if (!bev->rate_limiting || !bev->rate_limiting->cfg) {
402737c9cd8SNick Mathewson 		BEV_UNLOCK(&bev->bev);
403737c9cd8SNick Mathewson 		return;
404737c9cd8SNick Mathewson 	}
405737c9cd8SNick Mathewson 
406737c9cd8SNick Mathewson 	/* First, update the bucket */
407737c9cd8SNick Mathewson 	event_base_gettimeofday_cached(bev->bev.ev_base, &now);
4088ac3c4c2SNick Mathewson 	tick = ev_token_bucket_get_tick_(&now,
409737c9cd8SNick Mathewson 	    bev->rate_limiting->cfg);
4108ac3c4c2SNick Mathewson 	ev_token_bucket_update_(&bev->rate_limiting->limit,
411737c9cd8SNick Mathewson 	    bev->rate_limiting->cfg,
412737c9cd8SNick Mathewson 	    tick);
413737c9cd8SNick Mathewson 
414737c9cd8SNick Mathewson 	/* Now unsuspend any read/write operations as appropriate. */
415737c9cd8SNick Mathewson 	if ((bev->read_suspended & BEV_SUSPEND_BW)) {
416737c9cd8SNick Mathewson 		if (bev->rate_limiting->limit.read_limit > 0)
4178ac3c4c2SNick Mathewson 			bufferevent_unsuspend_read_(&bev->bev, BEV_SUSPEND_BW);
418737c9cd8SNick Mathewson 		else
419737c9cd8SNick Mathewson 			again = 1;
420737c9cd8SNick Mathewson 	}
421737c9cd8SNick Mathewson 	if ((bev->write_suspended & BEV_SUSPEND_BW)) {
422737c9cd8SNick Mathewson 		if (bev->rate_limiting->limit.write_limit > 0)
4238ac3c4c2SNick Mathewson 			bufferevent_unsuspend_write_(&bev->bev, BEV_SUSPEND_BW);
424737c9cd8SNick Mathewson 		else
425737c9cd8SNick Mathewson 			again = 1;
426737c9cd8SNick Mathewson 	}
427737c9cd8SNick Mathewson 	if (again) {
428737c9cd8SNick Mathewson 		/* One or more of the buckets may need another refill if they
429737c9cd8SNick Mathewson 		   started negative.
430737c9cd8SNick Mathewson 
431737c9cd8SNick Mathewson 		   XXXX if we need to be quiet for more ticks, we should
432737c9cd8SNick Mathewson 		   maybe figure out what timeout we really want.
433737c9cd8SNick Mathewson 		*/
434ff3f6cd4SNick Mathewson 		/* XXXX Handle event_add failure somehow */
435737c9cd8SNick Mathewson 		event_add(&bev->rate_limiting->refill_bucket_event,
436737c9cd8SNick Mathewson 		    &bev->rate_limiting->cfg->tick_timeout);
437737c9cd8SNick Mathewson 	}
438737c9cd8SNick Mathewson 	BEV_UNLOCK(&bev->bev);
439737c9cd8SNick Mathewson }
440737c9cd8SNick Mathewson 
4413aa44159SNick Mathewson /** Helper: grab a random element from a bufferevent group.
4423aa44159SNick Mathewson  *
4433aa44159SNick Mathewson  * Requires that we hold the lock on the group.
4443aa44159SNick Mathewson  */
445737c9cd8SNick Mathewson static struct bufferevent_private *
bev_group_random_element_(struct bufferevent_rate_limit_group * group)446cb9da0bfSNick Mathewson bev_group_random_element_(struct bufferevent_rate_limit_group *group)
447737c9cd8SNick Mathewson {
448737c9cd8SNick Mathewson 	int which;
449737c9cd8SNick Mathewson 	struct bufferevent_private *bev;
450737c9cd8SNick Mathewson 
451737c9cd8SNick Mathewson 	/* requires group lock */
452737c9cd8SNick Mathewson 
453737c9cd8SNick Mathewson 	if (!group->n_members)
454737c9cd8SNick Mathewson 		return NULL;
455737c9cd8SNick Mathewson 
456974d004eSNick Mathewson 	EVUTIL_ASSERT(! LIST_EMPTY(&group->members));
457737c9cd8SNick Mathewson 
458e86af4b7SNicholas Marriott 	which = evutil_weakrand_range_(&group->weakrand_seed, group->n_members);
459737c9cd8SNick Mathewson 
460974d004eSNick Mathewson 	bev = LIST_FIRST(&group->members);
461737c9cd8SNick Mathewson 	while (which--)
462974d004eSNick Mathewson 		bev = LIST_NEXT(bev, rate_limiting->next_in_group);
463737c9cd8SNick Mathewson 
464737c9cd8SNick Mathewson 	return bev;
465737c9cd8SNick Mathewson }
466737c9cd8SNick Mathewson 
467737c9cd8SNick Mathewson /** Iterate over the elements of a rate-limiting group 'g' with a random
468737c9cd8SNick Mathewson     starting point, assigning each to the variable 'bev', and executing the
469737c9cd8SNick Mathewson     block 'block'.
470737c9cd8SNick Mathewson 
471737c9cd8SNick Mathewson     We do this in a half-baked effort to get fairness among group members.
472737c9cd8SNick Mathewson     XXX Round-robin or some kind of priority queue would be even more fair.
473737c9cd8SNick Mathewson  */
474737c9cd8SNick Mathewson #define FOREACH_RANDOM_ORDER(block)			\
475737c9cd8SNick Mathewson 	do {						\
476cb9da0bfSNick Mathewson 		first = bev_group_random_element_(g);	\
477974d004eSNick Mathewson 		for (bev = first; bev != LIST_END(&g->members); \
478974d004eSNick Mathewson 		    bev = LIST_NEXT(bev, rate_limiting->next_in_group)) { \
479737c9cd8SNick Mathewson 			block ;					 \
480737c9cd8SNick Mathewson 		}						 \
481974d004eSNick Mathewson 		for (bev = LIST_FIRST(&g->members); bev && bev != first; \
482974d004eSNick Mathewson 		    bev = LIST_NEXT(bev, rate_limiting->next_in_group)) { \
483737c9cd8SNick Mathewson 			block ;						\
484737c9cd8SNick Mathewson 		}							\
485737c9cd8SNick Mathewson 	} while (0)
486737c9cd8SNick Mathewson 
487737c9cd8SNick Mathewson static void
bev_group_unsuspend_reading_(struct bufferevent_rate_limit_group * g)488cb9da0bfSNick Mathewson bev_group_unsuspend_reading_(struct bufferevent_rate_limit_group *g)
489737c9cd8SNick Mathewson {
490737c9cd8SNick Mathewson 	int again = 0;
491737c9cd8SNick Mathewson 	struct bufferevent_private *bev, *first;
492737c9cd8SNick Mathewson 
493737c9cd8SNick Mathewson 	g->read_suspended = 0;
494737c9cd8SNick Mathewson 	FOREACH_RANDOM_ORDER({
4958ac3c4c2SNick Mathewson 		if (EVLOCK_TRY_LOCK_(bev->lock)) {
4968ac3c4c2SNick Mathewson 			bufferevent_unsuspend_read_(&bev->bev,
497737c9cd8SNick Mathewson 			    BEV_SUSPEND_BW_GROUP);
498737c9cd8SNick Mathewson 			EVLOCK_UNLOCK(bev->lock, 0);
499737c9cd8SNick Mathewson 		} else {
500737c9cd8SNick Mathewson 			again = 1;
501737c9cd8SNick Mathewson 		}
502737c9cd8SNick Mathewson 	});
503737c9cd8SNick Mathewson 	g->pending_unsuspend_read = again;
504737c9cd8SNick Mathewson }
50585047a69SNick Mathewson 
50685047a69SNick Mathewson static void
bev_group_unsuspend_writing_(struct bufferevent_rate_limit_group * g)507cb9da0bfSNick Mathewson bev_group_unsuspend_writing_(struct bufferevent_rate_limit_group *g)
50885047a69SNick Mathewson {
50985047a69SNick Mathewson 	int again = 0;
51085047a69SNick Mathewson 	struct bufferevent_private *bev, *first;
511737c9cd8SNick Mathewson 	g->write_suspended = 0;
51285047a69SNick Mathewson 
513737c9cd8SNick Mathewson 	FOREACH_RANDOM_ORDER({
5148ac3c4c2SNick Mathewson 		if (EVLOCK_TRY_LOCK_(bev->lock)) {
5158ac3c4c2SNick Mathewson 			bufferevent_unsuspend_write_(&bev->bev,
516737c9cd8SNick Mathewson 			    BEV_SUSPEND_BW_GROUP);
517737c9cd8SNick Mathewson 			EVLOCK_UNLOCK(bev->lock, 0);
518737c9cd8SNick Mathewson 		} else {
519737c9cd8SNick Mathewson 			again = 1;
520737c9cd8SNick Mathewson 		}
521737c9cd8SNick Mathewson 	});
522737c9cd8SNick Mathewson 	g->pending_unsuspend_write = again;
523737c9cd8SNick Mathewson }
524737c9cd8SNick Mathewson 
52585047a69SNick Mathewson /** Callback invoked every tick to add more elements to the group bucket
52685047a69SNick Mathewson     and unsuspend group members as needed.
52785047a69SNick Mathewson  */
52885047a69SNick Mathewson static void
bev_group_refill_callback_(evutil_socket_t fd,short what,void * arg)529cb9da0bfSNick Mathewson bev_group_refill_callback_(evutil_socket_t fd, short what, void *arg)
53085047a69SNick Mathewson {
53185047a69SNick Mathewson 	struct bufferevent_rate_limit_group *g = arg;
53285047a69SNick Mathewson 	unsigned tick;
53385047a69SNick Mathewson 	struct timeval now;
53485047a69SNick Mathewson 
53585047a69SNick Mathewson 	event_base_gettimeofday_cached(event_get_base(&g->master_refill_event), &now);
53685047a69SNick Mathewson 
53785047a69SNick Mathewson 	LOCK_GROUP(g);
5385b18f130SNick Mathewson 
5398ac3c4c2SNick Mathewson 	tick = ev_token_bucket_get_tick_(&now, &g->rate_limit_cfg);
5408ac3c4c2SNick Mathewson 	ev_token_bucket_update_(&g->rate_limit, &g->rate_limit_cfg, tick);
54185047a69SNick Mathewson 
54285047a69SNick Mathewson 	if (g->pending_unsuspend_read ||
54385047a69SNick Mathewson 	    (g->read_suspended && (g->rate_limit.read_limit >= g->min_share))) {
544cb9da0bfSNick Mathewson 		bev_group_unsuspend_reading_(g);
54585047a69SNick Mathewson 	}
54685047a69SNick Mathewson 	if (g->pending_unsuspend_write ||
54785047a69SNick Mathewson 	    (g->write_suspended && (g->rate_limit.write_limit >= g->min_share))){
548cb9da0bfSNick Mathewson 		bev_group_unsuspend_writing_(g);
54985047a69SNick Mathewson 	}
55085047a69SNick Mathewson 
551737c9cd8SNick Mathewson 	/* XXXX Rather than waiting to the next tick to unsuspend stuff
552737c9cd8SNick Mathewson 	 * with pending_unsuspend_write/read, we should do it on the
553737c9cd8SNick Mathewson 	 * next iteration of the mainloop.
554737c9cd8SNick Mathewson 	 */
555737c9cd8SNick Mathewson 
556737c9cd8SNick Mathewson 	UNLOCK_GROUP(g);
557737c9cd8SNick Mathewson }
558737c9cd8SNick Mathewson 
559737c9cd8SNick Mathewson int
bufferevent_set_rate_limit(struct bufferevent * bev,struct ev_token_bucket_cfg * cfg)560737c9cd8SNick Mathewson bufferevent_set_rate_limit(struct bufferevent *bev,
561737c9cd8SNick Mathewson     struct ev_token_bucket_cfg *cfg)
562737c9cd8SNick Mathewson {
563*74517b2aSAzat Khuzhin 	struct bufferevent_private *bevp = BEV_UPCAST(bev);
564737c9cd8SNick Mathewson 	int r = -1;
565737c9cd8SNick Mathewson 	struct bufferevent_rate_limit *rlim;
566737c9cd8SNick Mathewson 	struct timeval now;
567737c9cd8SNick Mathewson 	ev_uint32_t tick;
56834d64f8aSNick Mathewson 	int reinit = 0, suspended = 0;
569737c9cd8SNick Mathewson 	/* XXX reference-count cfg */
570737c9cd8SNick Mathewson 
571737c9cd8SNick Mathewson 	BEV_LOCK(bev);
572737c9cd8SNick Mathewson 
573737c9cd8SNick Mathewson 	if (cfg == NULL) {
574737c9cd8SNick Mathewson 		if (bevp->rate_limiting) {
57534d64f8aSNick Mathewson 			rlim = bevp->rate_limiting;
57634d64f8aSNick Mathewson 			rlim->cfg = NULL;
5778ac3c4c2SNick Mathewson 			bufferevent_unsuspend_read_(bev, BEV_SUSPEND_BW);
5788ac3c4c2SNick Mathewson 			bufferevent_unsuspend_write_(bev, BEV_SUSPEND_BW);
57934d64f8aSNick Mathewson 			if (event_initialized(&rlim->refill_bucket_event))
58034d64f8aSNick Mathewson 				event_del(&rlim->refill_bucket_event);
581737c9cd8SNick Mathewson 		}
582737c9cd8SNick Mathewson 		r = 0;
583737c9cd8SNick Mathewson 		goto done;
584737c9cd8SNick Mathewson 	}
585737c9cd8SNick Mathewson 
586737c9cd8SNick Mathewson 	event_base_gettimeofday_cached(bev->ev_base, &now);
5878ac3c4c2SNick Mathewson 	tick = ev_token_bucket_get_tick_(&now, cfg);
588737c9cd8SNick Mathewson 
589737c9cd8SNick Mathewson 	if (bevp->rate_limiting && bevp->rate_limiting->cfg == cfg) {
59034d64f8aSNick Mathewson 		/* no-op */
59134d64f8aSNick Mathewson 		r = 0;
59234d64f8aSNick Mathewson 		goto done;
59334d64f8aSNick Mathewson 	}
59434d64f8aSNick Mathewson 	if (bevp->rate_limiting == NULL) {
595737c9cd8SNick Mathewson 		rlim = mm_calloc(1, sizeof(struct bufferevent_rate_limit));
596737c9cd8SNick Mathewson 		if (!rlim)
597737c9cd8SNick Mathewson 			goto done;
59834d64f8aSNick Mathewson 		bevp->rate_limiting = rlim;
59934d64f8aSNick Mathewson 	} else {
60034d64f8aSNick Mathewson 		rlim = bevp->rate_limiting;
60134d64f8aSNick Mathewson 	}
60234d64f8aSNick Mathewson 	reinit = rlim->cfg != NULL;
60334d64f8aSNick Mathewson 
604737c9cd8SNick Mathewson 	rlim->cfg = cfg;
6058ac3c4c2SNick Mathewson 	ev_token_bucket_init_(&rlim->limit, cfg, tick, reinit);
60634d64f8aSNick Mathewson 
60734d64f8aSNick Mathewson 	if (reinit) {
60834d64f8aSNick Mathewson 		EVUTIL_ASSERT(event_initialized(&rlim->refill_bucket_event));
60934d64f8aSNick Mathewson 		event_del(&rlim->refill_bucket_event);
61034d64f8aSNick Mathewson 	}
61102fbf687SNick Mathewson 	event_assign(&rlim->refill_bucket_event, bev->ev_base,
61202fbf687SNick Mathewson 	    -1, EV_FINALIZE, bev_refill_callback_, bevp);
61334d64f8aSNick Mathewson 
61434d64f8aSNick Mathewson 	if (rlim->limit.read_limit > 0) {
6158ac3c4c2SNick Mathewson 		bufferevent_unsuspend_read_(bev, BEV_SUSPEND_BW);
61634d64f8aSNick Mathewson 	} else {
6178ac3c4c2SNick Mathewson 		bufferevent_suspend_read_(bev, BEV_SUSPEND_BW);
61834d64f8aSNick Mathewson 		suspended=1;
619737c9cd8SNick Mathewson 	}
62034d64f8aSNick Mathewson 	if (rlim->limit.write_limit > 0) {
6218ac3c4c2SNick Mathewson 		bufferevent_unsuspend_write_(bev, BEV_SUSPEND_BW);
62234d64f8aSNick Mathewson 	} else {
6238ac3c4c2SNick Mathewson 		bufferevent_suspend_write_(bev, BEV_SUSPEND_BW);
62434d64f8aSNick Mathewson 		suspended = 1;
62534d64f8aSNick Mathewson 	}
62634d64f8aSNick Mathewson 
62734d64f8aSNick Mathewson 	if (suspended)
62834d64f8aSNick Mathewson 		event_add(&rlim->refill_bucket_event, &cfg->tick_timeout);
62934d64f8aSNick Mathewson 
630737c9cd8SNick Mathewson 	r = 0;
63134d64f8aSNick Mathewson 
632737c9cd8SNick Mathewson done:
633737c9cd8SNick Mathewson 	BEV_UNLOCK(bev);
634737c9cd8SNick Mathewson 	return r;
635737c9cd8SNick Mathewson }
636737c9cd8SNick Mathewson 
637737c9cd8SNick Mathewson struct bufferevent_rate_limit_group *
bufferevent_rate_limit_group_new(struct event_base * base,const struct ev_token_bucket_cfg * cfg)638737c9cd8SNick Mathewson bufferevent_rate_limit_group_new(struct event_base *base,
639737c9cd8SNick Mathewson     const struct ev_token_bucket_cfg *cfg)
640737c9cd8SNick Mathewson {
641737c9cd8SNick Mathewson 	struct bufferevent_rate_limit_group *g;
642737c9cd8SNick Mathewson 	struct timeval now;
643737c9cd8SNick Mathewson 	ev_uint32_t tick;
644737c9cd8SNick Mathewson 
645737c9cd8SNick Mathewson 	event_base_gettimeofday_cached(base, &now);
6468ac3c4c2SNick Mathewson 	tick = ev_token_bucket_get_tick_(&now, cfg);
647737c9cd8SNick Mathewson 
648737c9cd8SNick Mathewson 	g = mm_calloc(1, sizeof(struct bufferevent_rate_limit_group));
649737c9cd8SNick Mathewson 	if (!g)
650737c9cd8SNick Mathewson 		return NULL;
651737c9cd8SNick Mathewson 	memcpy(&g->rate_limit_cfg, cfg, sizeof(g->rate_limit_cfg));
652974d004eSNick Mathewson 	LIST_INIT(&g->members);
653737c9cd8SNick Mathewson 
6548ac3c4c2SNick Mathewson 	ev_token_bucket_init_(&g->rate_limit, cfg, tick, 0);
655737c9cd8SNick Mathewson 
65602fbf687SNick Mathewson 	event_assign(&g->master_refill_event, base, -1, EV_PERSIST|EV_FINALIZE,
657cb9da0bfSNick Mathewson 	    bev_group_refill_callback_, g);
658ff3f6cd4SNick Mathewson 	/*XXXX handle event_add failure */
659737c9cd8SNick Mathewson 	event_add(&g->master_refill_event, &cfg->tick_timeout);
660737c9cd8SNick Mathewson 
661737c9cd8SNick Mathewson 	EVTHREAD_ALLOC_LOCK(g->lock, EVTHREAD_LOCKTYPE_RECURSIVE);
6626d5440e8SNick Mathewson 
6636d5440e8SNick Mathewson 	bufferevent_rate_limit_group_set_min_share(g, 64);
6646d5440e8SNick Mathewson 
6653aa44159SNick Mathewson 	evutil_weakrand_seed_(&g->weakrand_seed,
6663aa44159SNick Mathewson 	    (ev_uint32_t) ((now.tv_sec + now.tv_usec) + (ev_intptr_t)g));
6673aa44159SNick Mathewson 
668737c9cd8SNick Mathewson 	return g;
669737c9cd8SNick Mathewson }
670737c9cd8SNick Mathewson 
671737c9cd8SNick Mathewson int
bufferevent_rate_limit_group_set_cfg(struct bufferevent_rate_limit_group * g,const struct ev_token_bucket_cfg * cfg)672ee41aca6SNick Mathewson bufferevent_rate_limit_group_set_cfg(
673ee41aca6SNick Mathewson 	struct bufferevent_rate_limit_group *g,
674ee41aca6SNick Mathewson 	const struct ev_token_bucket_cfg *cfg)
675ee41aca6SNick Mathewson {
676ee41aca6SNick Mathewson 	int same_tick;
677ee41aca6SNick Mathewson 	if (!g || !cfg)
678ee41aca6SNick Mathewson 		return -1;
679ee41aca6SNick Mathewson 
680ee41aca6SNick Mathewson 	LOCK_GROUP(g);
681ee41aca6SNick Mathewson 	same_tick = evutil_timercmp(
682ee41aca6SNick Mathewson 		&g->rate_limit_cfg.tick_timeout, &cfg->tick_timeout, ==);
683ee41aca6SNick Mathewson 	memcpy(&g->rate_limit_cfg, cfg, sizeof(g->rate_limit_cfg));
684ee41aca6SNick Mathewson 
6852cbb1a16SNick Mathewson 	if (g->rate_limit.read_limit > (ev_ssize_t)cfg->read_maximum)
686ee41aca6SNick Mathewson 		g->rate_limit.read_limit = cfg->read_maximum;
6872cbb1a16SNick Mathewson 	if (g->rate_limit.write_limit > (ev_ssize_t)cfg->write_maximum)
688ee41aca6SNick Mathewson 		g->rate_limit.write_limit = cfg->write_maximum;
689ee41aca6SNick Mathewson 
690ee41aca6SNick Mathewson 	if (!same_tick) {
691ee41aca6SNick Mathewson 		/* This can cause a hiccup in the schedule */
692ee41aca6SNick Mathewson 		event_add(&g->master_refill_event, &cfg->tick_timeout);
693ee41aca6SNick Mathewson 	}
694ee41aca6SNick Mathewson 
6956d5440e8SNick Mathewson 	/* The new limits might force us to adjust min_share differently. */
6966d5440e8SNick Mathewson 	bufferevent_rate_limit_group_set_min_share(g, g->configured_min_share);
6976d5440e8SNick Mathewson 
698ee41aca6SNick Mathewson 	UNLOCK_GROUP(g);
699ee41aca6SNick Mathewson 	return 0;
700ee41aca6SNick Mathewson }
701ee41aca6SNick Mathewson 
7026ae53d67SNick Mathewson int
bufferevent_rate_limit_group_set_min_share(struct bufferevent_rate_limit_group * g,size_t share)7036ae53d67SNick Mathewson bufferevent_rate_limit_group_set_min_share(
7046ae53d67SNick Mathewson 	struct bufferevent_rate_limit_group *g,
7056ae53d67SNick Mathewson 	size_t share)
7066ae53d67SNick Mathewson {
7072cbb1a16SNick Mathewson 	if (share > EV_SSIZE_MAX)
7089c8db0f8SNick Mathewson 		return -1;
7099c8db0f8SNick Mathewson 
7106d5440e8SNick Mathewson 	g->configured_min_share = share;
7116d5440e8SNick Mathewson 
7126d5440e8SNick Mathewson 	/* Can't set share to less than the one-tick maximum.  IOW, at steady
7136d5440e8SNick Mathewson 	 * state, at least one connection can go per tick. */
7146d5440e8SNick Mathewson 	if (share > g->rate_limit_cfg.read_rate)
7156d5440e8SNick Mathewson 		share = g->rate_limit_cfg.read_rate;
7166d5440e8SNick Mathewson 	if (share > g->rate_limit_cfg.write_rate)
7176d5440e8SNick Mathewson 		share = g->rate_limit_cfg.write_rate;
7186d5440e8SNick Mathewson 
7196ae53d67SNick Mathewson 	g->min_share = share;
7206ae53d67SNick Mathewson 	return 0;
7216ae53d67SNick Mathewson }
722ee41aca6SNick Mathewson 
723ee41aca6SNick Mathewson void
bufferevent_rate_limit_group_free(struct bufferevent_rate_limit_group * g)724ee41aca6SNick Mathewson bufferevent_rate_limit_group_free(struct bufferevent_rate_limit_group *g)
725ee41aca6SNick Mathewson {
726ee41aca6SNick Mathewson 	LOCK_GROUP(g);
727ee41aca6SNick Mathewson 	EVUTIL_ASSERT(0 == g->n_members);
728ee41aca6SNick Mathewson 	event_del(&g->master_refill_event);
729ee41aca6SNick Mathewson 	UNLOCK_GROUP(g);
730ee41aca6SNick Mathewson 	EVTHREAD_FREE_LOCK(g->lock, EVTHREAD_LOCKTYPE_RECURSIVE);
731ee41aca6SNick Mathewson 	mm_free(g);
732ee41aca6SNick Mathewson }
733ee41aca6SNick Mathewson 
734ee41aca6SNick Mathewson int
bufferevent_add_to_rate_limit_group(struct bufferevent * bev,struct bufferevent_rate_limit_group * g)735737c9cd8SNick Mathewson bufferevent_add_to_rate_limit_group(struct bufferevent *bev,
736737c9cd8SNick Mathewson     struct bufferevent_rate_limit_group *g)
737737c9cd8SNick Mathewson {
738737c9cd8SNick Mathewson 	int wsuspend, rsuspend;
739*74517b2aSAzat Khuzhin 	struct bufferevent_private *bevp = BEV_UPCAST(bev);
740737c9cd8SNick Mathewson 	BEV_LOCK(bev);
741737c9cd8SNick Mathewson 
742737c9cd8SNick Mathewson 	if (!bevp->rate_limiting) {
743737c9cd8SNick Mathewson 		struct bufferevent_rate_limit *rlim;
744737c9cd8SNick Mathewson 		rlim = mm_calloc(1, sizeof(struct bufferevent_rate_limit));
745737c9cd8SNick Mathewson 		if (!rlim) {
746737c9cd8SNick Mathewson 			BEV_UNLOCK(bev);
747737c9cd8SNick Mathewson 			return -1;
748737c9cd8SNick Mathewson 		}
74902fbf687SNick Mathewson 		event_assign(&rlim->refill_bucket_event, bev->ev_base,
75002fbf687SNick Mathewson 		    -1, EV_FINALIZE, bev_refill_callback_, bevp);
751737c9cd8SNick Mathewson 		bevp->rate_limiting = rlim;
752737c9cd8SNick Mathewson 	}
753737c9cd8SNick Mathewson 
754737c9cd8SNick Mathewson 	if (bevp->rate_limiting->group == g) {
755737c9cd8SNick Mathewson 		BEV_UNLOCK(bev);
756737c9cd8SNick Mathewson 		return 0;
757737c9cd8SNick Mathewson 	}
758737c9cd8SNick Mathewson 	if (bevp->rate_limiting->group)
759737c9cd8SNick Mathewson 		bufferevent_remove_from_rate_limit_group(bev);
760737c9cd8SNick Mathewson 
761737c9cd8SNick Mathewson 	LOCK_GROUP(g);
762737c9cd8SNick Mathewson 	bevp->rate_limiting->group = g;
763737c9cd8SNick Mathewson 	++g->n_members;
764974d004eSNick Mathewson 	LIST_INSERT_HEAD(&g->members, bevp, rate_limiting->next_in_group);
765737c9cd8SNick Mathewson 
766737c9cd8SNick Mathewson 	rsuspend = g->read_suspended;
767737c9cd8SNick Mathewson 	wsuspend = g->write_suspended;
768737c9cd8SNick Mathewson 
769737c9cd8SNick Mathewson 	UNLOCK_GROUP(g);
770737c9cd8SNick Mathewson 
771737c9cd8SNick Mathewson 	if (rsuspend)
7728ac3c4c2SNick Mathewson 		bufferevent_suspend_read_(bev, BEV_SUSPEND_BW_GROUP);
773737c9cd8SNick Mathewson 	if (wsuspend)
7748ac3c4c2SNick Mathewson 		bufferevent_suspend_write_(bev, BEV_SUSPEND_BW_GROUP);
775737c9cd8SNick Mathewson 
776737c9cd8SNick Mathewson 	BEV_UNLOCK(bev);
777737c9cd8SNick Mathewson 	return 0;
778737c9cd8SNick Mathewson }
779737c9cd8SNick Mathewson 
780737c9cd8SNick Mathewson int
bufferevent_remove_from_rate_limit_group(struct bufferevent * bev)781737c9cd8SNick Mathewson bufferevent_remove_from_rate_limit_group(struct bufferevent *bev)
782737c9cd8SNick Mathewson {
7838ac3c4c2SNick Mathewson 	return bufferevent_remove_from_rate_limit_group_internal_(bev, 1);
7840bffe43aSNick Mathewson }
7850bffe43aSNick Mathewson 
7860bffe43aSNick Mathewson int
bufferevent_remove_from_rate_limit_group_internal_(struct bufferevent * bev,int unsuspend)7878ac3c4c2SNick Mathewson bufferevent_remove_from_rate_limit_group_internal_(struct bufferevent *bev,
7880bffe43aSNick Mathewson     int unsuspend)
7890bffe43aSNick Mathewson {
790*74517b2aSAzat Khuzhin 	struct bufferevent_private *bevp = BEV_UPCAST(bev);
791737c9cd8SNick Mathewson 	BEV_LOCK(bev);
792737c9cd8SNick Mathewson 	if (bevp->rate_limiting && bevp->rate_limiting->group) {
793737c9cd8SNick Mathewson 		struct bufferevent_rate_limit_group *g =
794737c9cd8SNick Mathewson 		    bevp->rate_limiting->group;
795737c9cd8SNick Mathewson 		LOCK_GROUP(g);
796737c9cd8SNick Mathewson 		bevp->rate_limiting->group = NULL;
797737c9cd8SNick Mathewson 		--g->n_members;
798974d004eSNick Mathewson 		LIST_REMOVE(bevp, rate_limiting->next_in_group);
799737c9cd8SNick Mathewson 		UNLOCK_GROUP(g);
800737c9cd8SNick Mathewson 	}
8010bffe43aSNick Mathewson 	if (unsuspend) {
8028ac3c4c2SNick Mathewson 		bufferevent_unsuspend_read_(bev, BEV_SUSPEND_BW_GROUP);
8038ac3c4c2SNick Mathewson 		bufferevent_unsuspend_write_(bev, BEV_SUSPEND_BW_GROUP);
8040bffe43aSNick Mathewson 	}
805737c9cd8SNick Mathewson 	BEV_UNLOCK(bev);
806737c9cd8SNick Mathewson 	return 0;
807737c9cd8SNick Mathewson }
80885047a69SNick Mathewson 
80985047a69SNick Mathewson /* ===
81085047a69SNick Mathewson  * API functions to expose rate limits.
81185047a69SNick Mathewson  *
81285047a69SNick Mathewson  * Don't use these from inside Libevent; they're meant to be for use by
81385047a69SNick Mathewson  * the program.
81485047a69SNick Mathewson  * === */
81585047a69SNick Mathewson 
81685047a69SNick Mathewson /* Mostly you don't want to use this function from inside libevent;
817cb9da0bfSNick Mathewson  * bufferevent_get_read_max_() is more likely what you want*/
81885047a69SNick Mathewson ev_ssize_t
bufferevent_get_read_limit(struct bufferevent * bev)81985047a69SNick Mathewson bufferevent_get_read_limit(struct bufferevent *bev)
82085047a69SNick Mathewson {
82185047a69SNick Mathewson 	ev_ssize_t r;
82285047a69SNick Mathewson 	struct bufferevent_private *bevp;
82385047a69SNick Mathewson 	BEV_LOCK(bev);
82485047a69SNick Mathewson 	bevp = BEV_UPCAST(bev);
82585047a69SNick Mathewson 	if (bevp->rate_limiting && bevp->rate_limiting->cfg) {
82685047a69SNick Mathewson 		bufferevent_update_buckets(bevp);
82785047a69SNick Mathewson 		r = bevp->rate_limiting->limit.read_limit;
82885047a69SNick Mathewson 	} else {
82985047a69SNick Mathewson 		r = EV_SSIZE_MAX;
83085047a69SNick Mathewson 	}
83185047a69SNick Mathewson 	BEV_UNLOCK(bev);
83285047a69SNick Mathewson 	return r;
83385047a69SNick Mathewson }
83485047a69SNick Mathewson 
83585047a69SNick Mathewson /* Mostly you don't want to use this function from inside libevent;
836cb9da0bfSNick Mathewson  * bufferevent_get_write_max_() is more likely what you want*/
83785047a69SNick Mathewson ev_ssize_t
bufferevent_get_write_limit(struct bufferevent * bev)83885047a69SNick Mathewson bufferevent_get_write_limit(struct bufferevent *bev)
83985047a69SNick Mathewson {
84085047a69SNick Mathewson 	ev_ssize_t r;
84185047a69SNick Mathewson 	struct bufferevent_private *bevp;
84285047a69SNick Mathewson 	BEV_LOCK(bev);
84385047a69SNick Mathewson 	bevp = BEV_UPCAST(bev);
84485047a69SNick Mathewson 	if (bevp->rate_limiting && bevp->rate_limiting->cfg) {
84585047a69SNick Mathewson 		bufferevent_update_buckets(bevp);
84685047a69SNick Mathewson 		r = bevp->rate_limiting->limit.write_limit;
84785047a69SNick Mathewson 	} else {
84885047a69SNick Mathewson 		r = EV_SSIZE_MAX;
84985047a69SNick Mathewson 	}
85085047a69SNick Mathewson 	BEV_UNLOCK(bev);
85185047a69SNick Mathewson 	return r;
85285047a69SNick Mathewson }
85385047a69SNick Mathewson 
854998c8138SAlexander Drozdov int
bufferevent_set_max_single_read(struct bufferevent * bev,size_t size)855998c8138SAlexander Drozdov bufferevent_set_max_single_read(struct bufferevent *bev, size_t size)
856998c8138SAlexander Drozdov {
857998c8138SAlexander Drozdov 	struct bufferevent_private *bevp;
858998c8138SAlexander Drozdov 	BEV_LOCK(bev);
859998c8138SAlexander Drozdov 	bevp = BEV_UPCAST(bev);
860998c8138SAlexander Drozdov 	if (size == 0 || size > EV_SSIZE_MAX)
861998c8138SAlexander Drozdov 		bevp->max_single_read = MAX_SINGLE_READ_DEFAULT;
862998c8138SAlexander Drozdov 	else
863998c8138SAlexander Drozdov 		bevp->max_single_read = size;
864998c8138SAlexander Drozdov 	BEV_UNLOCK(bev);
865998c8138SAlexander Drozdov 	return 0;
866998c8138SAlexander Drozdov }
867998c8138SAlexander Drozdov 
868998c8138SAlexander Drozdov int
bufferevent_set_max_single_write(struct bufferevent * bev,size_t size)869998c8138SAlexander Drozdov bufferevent_set_max_single_write(struct bufferevent *bev, size_t size)
870998c8138SAlexander Drozdov {
871998c8138SAlexander Drozdov 	struct bufferevent_private *bevp;
872998c8138SAlexander Drozdov 	BEV_LOCK(bev);
873998c8138SAlexander Drozdov 	bevp = BEV_UPCAST(bev);
874998c8138SAlexander Drozdov 	if (size == 0 || size > EV_SSIZE_MAX)
875998c8138SAlexander Drozdov 		bevp->max_single_write = MAX_SINGLE_WRITE_DEFAULT;
876998c8138SAlexander Drozdov 	else
877998c8138SAlexander Drozdov 		bevp->max_single_write = size;
878998c8138SAlexander Drozdov 	BEV_UNLOCK(bev);
879998c8138SAlexander Drozdov 	return 0;
880998c8138SAlexander Drozdov }
881998c8138SAlexander Drozdov 
882998c8138SAlexander Drozdov ev_ssize_t
bufferevent_get_max_single_read(struct bufferevent * bev)883998c8138SAlexander Drozdov bufferevent_get_max_single_read(struct bufferevent *bev)
884998c8138SAlexander Drozdov {
885998c8138SAlexander Drozdov 	ev_ssize_t r;
886998c8138SAlexander Drozdov 
887998c8138SAlexander Drozdov 	BEV_LOCK(bev);
888998c8138SAlexander Drozdov 	r = BEV_UPCAST(bev)->max_single_read;
889998c8138SAlexander Drozdov 	BEV_UNLOCK(bev);
890998c8138SAlexander Drozdov 	return r;
891998c8138SAlexander Drozdov }
892998c8138SAlexander Drozdov 
893998c8138SAlexander Drozdov ev_ssize_t
bufferevent_get_max_single_write(struct bufferevent * bev)894998c8138SAlexander Drozdov bufferevent_get_max_single_write(struct bufferevent *bev)
895998c8138SAlexander Drozdov {
896998c8138SAlexander Drozdov 	ev_ssize_t r;
897998c8138SAlexander Drozdov 
898998c8138SAlexander Drozdov 	BEV_LOCK(bev);
899998c8138SAlexander Drozdov 	r = BEV_UPCAST(bev)->max_single_write;
900998c8138SAlexander Drozdov 	BEV_UNLOCK(bev);
901998c8138SAlexander Drozdov 	return r;
902998c8138SAlexander Drozdov }
903998c8138SAlexander Drozdov 
904162ce8a8SNick Mathewson ev_ssize_t
bufferevent_get_max_to_read(struct bufferevent * bev)905162ce8a8SNick Mathewson bufferevent_get_max_to_read(struct bufferevent *bev)
906162ce8a8SNick Mathewson {
907162ce8a8SNick Mathewson 	ev_ssize_t r;
908162ce8a8SNick Mathewson 	BEV_LOCK(bev);
909cb9da0bfSNick Mathewson 	r = bufferevent_get_read_max_(BEV_UPCAST(bev));
910162ce8a8SNick Mathewson 	BEV_UNLOCK(bev);
911162ce8a8SNick Mathewson 	return r;
912162ce8a8SNick Mathewson }
913162ce8a8SNick Mathewson 
914162ce8a8SNick Mathewson ev_ssize_t
bufferevent_get_max_to_write(struct bufferevent * bev)915162ce8a8SNick Mathewson bufferevent_get_max_to_write(struct bufferevent *bev)
916162ce8a8SNick Mathewson {
917162ce8a8SNick Mathewson 	ev_ssize_t r;
918162ce8a8SNick Mathewson 	BEV_LOCK(bev);
919cb9da0bfSNick Mathewson 	r = bufferevent_get_write_max_(BEV_UPCAST(bev));
920162ce8a8SNick Mathewson 	BEV_UNLOCK(bev);
921162ce8a8SNick Mathewson 	return r;
922162ce8a8SNick Mathewson }
923162ce8a8SNick Mathewson 
9241c77fbb0SMark Ellzey const struct ev_token_bucket_cfg *
bufferevent_get_token_bucket_cfg(const struct bufferevent * bev)9251c77fbb0SMark Ellzey bufferevent_get_token_bucket_cfg(const struct bufferevent *bev) {
9264b3d5af8SMark Ellzey 	struct bufferevent_private *bufev_private = BEV_UPCAST(bev);
9274b3d5af8SMark Ellzey 	struct ev_token_bucket_cfg *cfg;
9284b3d5af8SMark Ellzey 
9294b3d5af8SMark Ellzey 	BEV_LOCK(bev);
9304b3d5af8SMark Ellzey 
9314b3d5af8SMark Ellzey 	if (bufev_private->rate_limiting) {
9324b3d5af8SMark Ellzey 		cfg = bufev_private->rate_limiting->cfg;
9334b3d5af8SMark Ellzey 	} else {
9344b3d5af8SMark Ellzey 		cfg = NULL;
9354b3d5af8SMark Ellzey 	}
9364b3d5af8SMark Ellzey 
9374b3d5af8SMark Ellzey 	BEV_UNLOCK(bev);
9384b3d5af8SMark Ellzey 
9394b3d5af8SMark Ellzey 	return cfg;
9404b3d5af8SMark Ellzey }
941162ce8a8SNick Mathewson 
94285047a69SNick Mathewson /* Mostly you don't want to use this function from inside libevent;
943cb9da0bfSNick Mathewson  * bufferevent_get_read_max_() is more likely what you want*/
94485047a69SNick Mathewson ev_ssize_t
bufferevent_rate_limit_group_get_read_limit(struct bufferevent_rate_limit_group * grp)94585047a69SNick Mathewson bufferevent_rate_limit_group_get_read_limit(
94685047a69SNick Mathewson 	struct bufferevent_rate_limit_group *grp)
94785047a69SNick Mathewson {
94885047a69SNick Mathewson 	ev_ssize_t r;
94985047a69SNick Mathewson 	LOCK_GROUP(grp);
95085047a69SNick Mathewson 	r = grp->rate_limit.read_limit;
95185047a69SNick Mathewson 	UNLOCK_GROUP(grp);
95285047a69SNick Mathewson 	return r;
95385047a69SNick Mathewson }
95485047a69SNick Mathewson 
95585047a69SNick Mathewson /* Mostly you don't want to use this function from inside libevent;
956cb9da0bfSNick Mathewson  * bufferevent_get_write_max_() is more likely what you want. */
95785047a69SNick Mathewson ev_ssize_t
bufferevent_rate_limit_group_get_write_limit(struct bufferevent_rate_limit_group * grp)95885047a69SNick Mathewson bufferevent_rate_limit_group_get_write_limit(
95985047a69SNick Mathewson 	struct bufferevent_rate_limit_group *grp)
96085047a69SNick Mathewson {
96185047a69SNick Mathewson 	ev_ssize_t r;
96285047a69SNick Mathewson 	LOCK_GROUP(grp);
96385047a69SNick Mathewson 	r = grp->rate_limit.write_limit;
96485047a69SNick Mathewson 	UNLOCK_GROUP(grp);
96585047a69SNick Mathewson 	return r;
96685047a69SNick Mathewson }
96785047a69SNick Mathewson 
96885047a69SNick Mathewson int
bufferevent_decrement_read_limit(struct bufferevent * bev,ev_ssize_t decr)96985047a69SNick Mathewson bufferevent_decrement_read_limit(struct bufferevent *bev, ev_ssize_t decr)
97085047a69SNick Mathewson {
97185047a69SNick Mathewson 	int r = 0;
9722cbb1a16SNick Mathewson 	ev_ssize_t old_limit, new_limit;
97385047a69SNick Mathewson 	struct bufferevent_private *bevp;
97485047a69SNick Mathewson 	BEV_LOCK(bev);
97585047a69SNick Mathewson 	bevp = BEV_UPCAST(bev);
97685047a69SNick Mathewson 	EVUTIL_ASSERT(bevp->rate_limiting && bevp->rate_limiting->cfg);
97785047a69SNick Mathewson 	old_limit = bevp->rate_limiting->limit.read_limit;
97885047a69SNick Mathewson 
97985047a69SNick Mathewson 	new_limit = (bevp->rate_limiting->limit.read_limit -= decr);
98085047a69SNick Mathewson 	if (old_limit > 0 && new_limit <= 0) {
9818ac3c4c2SNick Mathewson 		bufferevent_suspend_read_(bev, BEV_SUSPEND_BW);
98285047a69SNick Mathewson 		if (event_add(&bevp->rate_limiting->refill_bucket_event,
98385047a69SNick Mathewson 			&bevp->rate_limiting->cfg->tick_timeout) < 0)
98485047a69SNick Mathewson 			r = -1;
98585047a69SNick Mathewson 	} else if (old_limit <= 0 && new_limit > 0) {
98634d64f8aSNick Mathewson 		if (!(bevp->write_suspended & BEV_SUSPEND_BW))
98785047a69SNick Mathewson 			event_del(&bevp->rate_limiting->refill_bucket_event);
9888ac3c4c2SNick Mathewson 		bufferevent_unsuspend_read_(bev, BEV_SUSPEND_BW);
98985047a69SNick Mathewson 	}
99085047a69SNick Mathewson 
99185047a69SNick Mathewson 	BEV_UNLOCK(bev);
99285047a69SNick Mathewson 	return r;
99385047a69SNick Mathewson }
99485047a69SNick Mathewson 
99585047a69SNick Mathewson int
bufferevent_decrement_write_limit(struct bufferevent * bev,ev_ssize_t decr)99685047a69SNick Mathewson bufferevent_decrement_write_limit(struct bufferevent *bev, ev_ssize_t decr)
99785047a69SNick Mathewson {
99885047a69SNick Mathewson 	/* XXXX this is mostly copy-and-paste from
99985047a69SNick Mathewson 	 * bufferevent_decrement_read_limit */
100085047a69SNick Mathewson 	int r = 0;
10012cbb1a16SNick Mathewson 	ev_ssize_t old_limit, new_limit;
100285047a69SNick Mathewson 	struct bufferevent_private *bevp;
100385047a69SNick Mathewson 	BEV_LOCK(bev);
100485047a69SNick Mathewson 	bevp = BEV_UPCAST(bev);
100585047a69SNick Mathewson 	EVUTIL_ASSERT(bevp->rate_limiting && bevp->rate_limiting->cfg);
100685047a69SNick Mathewson 	old_limit = bevp->rate_limiting->limit.write_limit;
100785047a69SNick Mathewson 
100885047a69SNick Mathewson 	new_limit = (bevp->rate_limiting->limit.write_limit -= decr);
100985047a69SNick Mathewson 	if (old_limit > 0 && new_limit <= 0) {
10108ac3c4c2SNick Mathewson 		bufferevent_suspend_write_(bev, BEV_SUSPEND_BW);
101185047a69SNick Mathewson 		if (event_add(&bevp->rate_limiting->refill_bucket_event,
101285047a69SNick Mathewson 			&bevp->rate_limiting->cfg->tick_timeout) < 0)
101385047a69SNick Mathewson 			r = -1;
101485047a69SNick Mathewson 	} else if (old_limit <= 0 && new_limit > 0) {
101534d64f8aSNick Mathewson 		if (!(bevp->read_suspended & BEV_SUSPEND_BW))
101685047a69SNick Mathewson 			event_del(&bevp->rate_limiting->refill_bucket_event);
10178ac3c4c2SNick Mathewson 		bufferevent_unsuspend_write_(bev, BEV_SUSPEND_BW);
101885047a69SNick Mathewson 	}
101985047a69SNick Mathewson 
102085047a69SNick Mathewson 	BEV_UNLOCK(bev);
102185047a69SNick Mathewson 	return r;
102285047a69SNick Mathewson }
102385047a69SNick Mathewson 
102485047a69SNick Mathewson int
bufferevent_rate_limit_group_decrement_read(struct bufferevent_rate_limit_group * grp,ev_ssize_t decr)102585047a69SNick Mathewson bufferevent_rate_limit_group_decrement_read(
102685047a69SNick Mathewson 	struct bufferevent_rate_limit_group *grp, ev_ssize_t decr)
102785047a69SNick Mathewson {
102885047a69SNick Mathewson 	int r = 0;
10292cbb1a16SNick Mathewson 	ev_ssize_t old_limit, new_limit;
103085047a69SNick Mathewson 	LOCK_GROUP(grp);
103185047a69SNick Mathewson 	old_limit = grp->rate_limit.read_limit;
103285047a69SNick Mathewson 	new_limit = (grp->rate_limit.read_limit -= decr);
103385047a69SNick Mathewson 
103485047a69SNick Mathewson 	if (old_limit > 0 && new_limit <= 0) {
1035cb9da0bfSNick Mathewson 		bev_group_suspend_reading_(grp);
103685047a69SNick Mathewson 	} else if (old_limit <= 0 && new_limit > 0) {
1037cb9da0bfSNick Mathewson 		bev_group_unsuspend_reading_(grp);
103885047a69SNick Mathewson 	}
103985047a69SNick Mathewson 
104085047a69SNick Mathewson 	UNLOCK_GROUP(grp);
104185047a69SNick Mathewson 	return r;
104285047a69SNick Mathewson }
104385047a69SNick Mathewson 
104485047a69SNick Mathewson int
bufferevent_rate_limit_group_decrement_write(struct bufferevent_rate_limit_group * grp,ev_ssize_t decr)104585047a69SNick Mathewson bufferevent_rate_limit_group_decrement_write(
104685047a69SNick Mathewson 	struct bufferevent_rate_limit_group *grp, ev_ssize_t decr)
104785047a69SNick Mathewson {
104885047a69SNick Mathewson 	int r = 0;
10492cbb1a16SNick Mathewson 	ev_ssize_t old_limit, new_limit;
105085047a69SNick Mathewson 	LOCK_GROUP(grp);
105185047a69SNick Mathewson 	old_limit = grp->rate_limit.write_limit;
105285047a69SNick Mathewson 	new_limit = (grp->rate_limit.write_limit -= decr);
105385047a69SNick Mathewson 
105485047a69SNick Mathewson 	if (old_limit > 0 && new_limit <= 0) {
1055cb9da0bfSNick Mathewson 		bev_group_suspend_writing_(grp);
105685047a69SNick Mathewson 	} else if (old_limit <= 0 && new_limit > 0) {
1057cb9da0bfSNick Mathewson 		bev_group_unsuspend_writing_(grp);
105885047a69SNick Mathewson 	}
105985047a69SNick Mathewson 
106085047a69SNick Mathewson 	UNLOCK_GROUP(grp);
106185047a69SNick Mathewson 	return r;
106285047a69SNick Mathewson }
106385047a69SNick Mathewson 
1064fb366c1dSNick Mathewson void
bufferevent_rate_limit_group_get_totals(struct bufferevent_rate_limit_group * grp,ev_uint64_t * total_read_out,ev_uint64_t * total_written_out)1065fb366c1dSNick Mathewson bufferevent_rate_limit_group_get_totals(struct bufferevent_rate_limit_group *grp,
1066fb366c1dSNick Mathewson     ev_uint64_t *total_read_out, ev_uint64_t *total_written_out)
1067fb366c1dSNick Mathewson {
1068fb366c1dSNick Mathewson 	EVUTIL_ASSERT(grp != NULL);
1069fb366c1dSNick Mathewson 	if (total_read_out)
1070fb366c1dSNick Mathewson 		*total_read_out = grp->total_read;
1071fb366c1dSNick Mathewson 	if (total_written_out)
1072fb366c1dSNick Mathewson 		*total_written_out = grp->total_written;
1073fb366c1dSNick Mathewson }
1074fb366c1dSNick Mathewson 
1075fb366c1dSNick Mathewson void
bufferevent_rate_limit_group_reset_totals(struct bufferevent_rate_limit_group * grp)1076fb366c1dSNick Mathewson bufferevent_rate_limit_group_reset_totals(struct bufferevent_rate_limit_group *grp)
1077fb366c1dSNick Mathewson {
1078fb366c1dSNick Mathewson 	grp->total_read = grp->total_written = 0;
1079fb366c1dSNick Mathewson }
1080998c8138SAlexander Drozdov 
1081998c8138SAlexander Drozdov int
bufferevent_ratelim_init_(struct bufferevent_private * bev)1082cb9da0bfSNick Mathewson bufferevent_ratelim_init_(struct bufferevent_private *bev)
1083998c8138SAlexander Drozdov {
1084998c8138SAlexander Drozdov 	bev->rate_limiting = NULL;
1085998c8138SAlexander Drozdov 	bev->max_single_read = MAX_SINGLE_READ_DEFAULT;
1086998c8138SAlexander Drozdov 	bev->max_single_write = MAX_SINGLE_WRITE_DEFAULT;
1087998c8138SAlexander Drozdov 
1088998c8138SAlexander Drozdov 	return 0;
1089998c8138SAlexander Drozdov }
1090