1649Sjoehw/* $NetBSD: ratelimiter.c,v 1.6 2020/05/25 20:47:20 christos Exp $ */ 2963Sjoehw 3649Sjoehw/* 4281SN/A * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC") 5635Sjoehw * Copyright (C) 1999-2002 Internet Software Consortium. 6635Sjoehw * 7635Sjoehw * Permission to use, copy, modify, and/or distribute this software for any 8635Sjoehw * purpose with or without fee is hereby granted, provided that the above 9635Sjoehw * copyright notice and this permission notice appear in all copies. 10635Sjoehw * 11281SN/A * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 12281SN/A * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 13281SN/A * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 14281SN/A * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 15281SN/A * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 16281SN/A * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17281SN/A * PERFORMANCE OF THIS SOFTWARE. 18281SN/A */ 19281SN/A 20281SN/A/* Id: ratelimiter.c,v 1.25 2007/06/19 23:47:17 tbox Exp */ 21281SN/A 22281SN/A/*! \file */ 23281SN/A 24281SN/A#include <config.h> 25281SN/A 26281SN/A#include <isc/mem.h> 27281SN/A#include <isc/ratelimiter.h> 28281SN/A#include <isc/task.h> 29281SN/A#include <isc/time.h> 30281SN/A#include <isc/timer.h> 31649Sjoehw#include <isc/util.h> 32635Sjoehw 33281SN/Atypedef enum { 34281SN/A isc_ratelimiter_stalled = 0, 35281SN/A isc_ratelimiter_ratelimited = 1, 36281SN/A isc_ratelimiter_idle = 2, 37281SN/A isc_ratelimiter_shuttingdown = 3 38281SN/A} isc_ratelimiter_state_t; 39281SN/A 40281SN/Astruct isc_ratelimiter { 41281SN/A isc_mem_t * mctx; 42281SN/A isc_mutex_t lock; 43281SN/A int refs; 44281SN/A isc_task_t * task; 45281SN/A isc_timer_t * timer; 46649Sjoehw isc_interval_t interval; 47798Sjoehw isc_uint32_t pertic; 48281SN/A isc_ratelimiter_state_t state; 49281SN/A isc_event_t shutdownevent; 50281SN/A ISC_LIST(isc_event_t) pending; 51798Sjoehw}; 52281SN/A 53281SN/A#define ISC_RATELIMITEREVENT_SHUTDOWN (ISC_EVENTCLASS_RATELIMITER + 1) 54281SN/A 55281SN/Astatic void 56281SN/Aratelimiter_tick(isc_task_t *task, isc_event_t *event); 57281SN/A 58281SN/Astatic void 59281SN/Aratelimiter_shutdowncomplete(isc_task_t *task, isc_event_t *event); 60281SN/A 61281SN/Aisc_result_t 62281SN/Aisc_ratelimiter_create(isc_mem_t *mctx, isc_timermgr_t *timermgr, 63281SN/A isc_task_t *task, isc_ratelimiter_t **ratelimiterp) 64281SN/A{ 65281SN/A isc_result_t result; 66281SN/A isc_ratelimiter_t *rl; 67281SN/A INSIST(ratelimiterp != NULL && *ratelimiterp == NULL); 68281SN/A 69281SN/A rl = isc_mem_get(mctx, sizeof(*rl)); 70281SN/A if (rl == NULL) 71281SN/A return ISC_R_NOMEMORY; 72281SN/A rl->mctx = mctx; 73281SN/A rl->refs = 1; 74281SN/A rl->task = task; 75281SN/A isc_interval_set(&rl->interval, 0, 0); 76281SN/A rl->timer = NULL; 77281SN/A rl->pertic = 1; 78281SN/A rl->state = isc_ratelimiter_idle; 79281SN/A ISC_LIST_INIT(rl->pending); 80798Sjoehw 81798Sjoehw result = isc_mutex_init(&rl->lock); 82798Sjoehw if (result != ISC_R_SUCCESS) 83798Sjoehw goto free_mem; 84798Sjoehw result = isc_timer_create(timermgr, isc_timertype_inactive, 85798Sjoehw NULL, NULL, rl->task, ratelimiter_tick, 86798Sjoehw rl, &rl->timer); 87798Sjoehw if (result != ISC_R_SUCCESS) 88798Sjoehw goto free_mutex; 89963Sjoehw 90963Sjoehw /* 91281SN/A * Increment the reference count to indicate that we may 92281SN/A * (soon) have events outstanding. 93281SN/A */ 94281SN/A rl->refs++; 95281SN/A 96281SN/A ISC_EVENT_INIT(&rl->shutdownevent, 97281SN/A sizeof(isc_event_t), 98281SN/A 0, NULL, ISC_RATELIMITEREVENT_SHUTDOWN, 99281SN/A ratelimiter_shutdowncomplete, rl, rl, NULL, NULL); 100281SN/A 101281SN/A *ratelimiterp = rl; 102281SN/A return (ISC_R_SUCCESS); 103281SN/A 104281SN/Afree_mutex: 105281SN/A DESTROYLOCK(&rl->lock); 106281SN/Afree_mem: 107281SN/A isc_mem_put(mctx, rl, sizeof(*rl)); 108281SN/A return (result); 109281SN/A} 110281SN/A 111281SN/Aisc_result_t 112281SN/Aisc_ratelimiter_setinterval(isc_ratelimiter_t *rl, isc_interval_t *interval) { 113281SN/A isc_result_t result = ISC_R_SUCCESS; 114281SN/A LOCK(&rl->lock); 115281SN/A rl->interval = *interval; 116649Sjoehw /* 117281SN/A * If the timer is currently running, change its rate. 118281SN/A */ 119281SN/A if (rl->state == isc_ratelimiter_ratelimited) { 120281SN/A result = isc_timer_reset(rl->timer, isc_timertype_ticker, NULL, 121281SN/A &rl->interval, ISC_FALSE); 122281SN/A } 123281SN/A UNLOCK(&rl->lock); 124281SN/A return (result); 125281SN/A} 126281SN/A 127281SN/Avoid 128281SN/Aisc_ratelimiter_setpertic(isc_ratelimiter_t *rl, isc_uint32_t pertic) { 129281SN/A if (pertic == 0) 130281SN/A pertic = 1; 131281SN/A rl->pertic = pertic; 132281SN/A} 133281SN/A 134281SN/Aisc_result_t 135281SN/Aisc_ratelimiter_enqueue(isc_ratelimiter_t *rl, isc_task_t *task, 136281SN/A isc_event_t **eventp) 137281SN/A{ 138281SN/A isc_result_t result = ISC_R_SUCCESS; 139281SN/A isc_event_t *ev; 140281SN/A 141281SN/A REQUIRE(eventp != NULL && *eventp != NULL); 142281SN/A REQUIRE(task != NULL); 143281SN/A ev = *eventp; 144281SN/A REQUIRE(ev->ev_sender == NULL); 145281SN/A 146281SN/A LOCK(&rl->lock); 147281SN/A if (rl->state == isc_ratelimiter_ratelimited || 148281SN/A rl->state == isc_ratelimiter_stalled) { 149281SN/A isc_event_t *ev = *eventp; 150281SN/A ev->ev_sender = task; 151281SN/A ISC_LIST_APPEND(rl->pending, ev, ev_link); 152281SN/A *eventp = NULL; 153281SN/A } else if (rl->state == isc_ratelimiter_idle) { 154281SN/A result = isc_timer_reset(rl->timer, isc_timertype_ticker, NULL, 155281SN/A &rl->interval, ISC_FALSE); 156281SN/A if (result == ISC_R_SUCCESS) { 157281SN/A ev->ev_sender = task; 158281SN/A rl->state = isc_ratelimiter_ratelimited; 159281SN/A } 160281SN/A } else { 161281SN/A INSIST(rl->state == isc_ratelimiter_shuttingdown); 162281SN/A result = ISC_R_SHUTTINGDOWN; 163281SN/A } 164281SN/A UNLOCK(&rl->lock); 165281SN/A if (*eventp != NULL && result == ISC_R_SUCCESS) 166281SN/A isc_task_send(task, eventp); 167281SN/A return (result); 168281SN/A} 169281SN/A 170281SN/Astatic void 171281SN/Aratelimiter_tick(isc_task_t *task, isc_event_t *event) { 172281SN/A isc_result_t result = ISC_R_SUCCESS; 173281SN/A isc_ratelimiter_t *rl = (isc_ratelimiter_t *)event->ev_arg; 174281SN/A isc_event_t *p; 175281SN/A isc_uint32_t pertic; 176281SN/A 177281SN/A UNUSED(task); 178281SN/A 179281SN/A isc_event_free(&event); 180281SN/A 181281SN/A pertic = rl->pertic; 182281SN/A while (pertic != 0) { 183281SN/A pertic--; 184281SN/A LOCK(&rl->lock); 185281SN/A p = ISC_LIST_HEAD(rl->pending); 186281SN/A if (p != NULL) { 187281SN/A /* 188281SN/A * There is work to do. Let's do it after unlocking. 189281SN/A */ 190281SN/A ISC_LIST_UNLINK(rl->pending, p, ev_link); 191281SN/A } else { 192635Sjoehw /* 193635Sjoehw * No work left to do. Stop the timer so that we don't 194635Sjoehw * waste resources by having it fire periodically. 195635Sjoehw */ 196635Sjoehw result = isc_timer_reset(rl->timer, 197635Sjoehw isc_timertype_inactive, 198635Sjoehw NULL, NULL, ISC_FALSE); 199635Sjoehw RUNTIME_CHECK(result == ISC_R_SUCCESS); 200635Sjoehw rl->state = isc_ratelimiter_idle; 201635Sjoehw pertic = 0; /* Force the loop to exit. */ 202635Sjoehw } 203635Sjoehw UNLOCK(&rl->lock); 204635Sjoehw if (p != NULL) { 205635Sjoehw isc_task_t *evtask = p->ev_sender; 206635Sjoehw isc_task_send(evtask, &p); 207635Sjoehw } 208281SN/A INSIST(p == NULL); 209281SN/A } 210281SN/A} 211281SN/A 212281SN/Avoid 213281SN/Aisc_ratelimiter_shutdown(isc_ratelimiter_t *rl) { 214281SN/A isc_event_t *ev; 215281SN/A isc_task_t *task; 216281SN/A LOCK(&rl->lock); 217281SN/A rl->state = isc_ratelimiter_shuttingdown; 218281SN/A (void)isc_timer_reset(rl->timer, isc_timertype_inactive, 219281SN/A NULL, NULL, ISC_FALSE); 220281SN/A while ((ev = ISC_LIST_HEAD(rl->pending)) != NULL) { 221281SN/A ISC_LIST_UNLINK(rl->pending, ev, ev_link); 222281SN/A ev->ev_attributes |= ISC_EVENTATTR_CANCELED; 223281SN/A task = ev->ev_sender; 224281SN/A isc_task_send(task, &ev); 225281SN/A } 226281SN/A isc_timer_detach(&rl->timer); 227281SN/A /* 228281SN/A * Send an event to our task. The delivery of this event 229281SN/A * indicates that no more timer events will be delivered. 230281SN/A */ 231281SN/A ev = &rl->shutdownevent; 232281SN/A isc_task_send(rl->task, &ev); 233281SN/A 234281SN/A UNLOCK(&rl->lock); 235281SN/A} 236281SN/A 237281SN/Astatic void 238281SN/Aratelimiter_shutdowncomplete(isc_task_t *task, isc_event_t *event) { 239281SN/A isc_ratelimiter_t *rl = (isc_ratelimiter_t *)event->ev_arg; 240281SN/A 241281SN/A UNUSED(task); 242281SN/A 243281SN/A isc_ratelimiter_detach(&rl); 244281SN/A} 245281SN/A 246281SN/Astatic void 247281SN/Aratelimiter_free(isc_ratelimiter_t *rl) { 248281SN/A DESTROYLOCK(&rl->lock); 249281SN/A isc_mem_put(rl->mctx, rl, sizeof(*rl)); 250281SN/A} 251635Sjoehw 252635Sjoehwvoid 253635Sjoehwisc_ratelimiter_attach(isc_ratelimiter_t *source, isc_ratelimiter_t **target) { 254635Sjoehw REQUIRE(source != NULL); 255649Sjoehw REQUIRE(target != NULL && *target == NULL); 256649Sjoehw 257649Sjoehw LOCK(&source->lock); 258649Sjoehw REQUIRE(source->refs > 0); 259281SN/A source->refs++; 260281SN/A INSIST(source->refs > 0); 261281SN/A UNLOCK(&source->lock); 262281SN/A *target = source; 263464SN/A} 264464SN/A 265464SN/Avoid 266418SN/Aisc_ratelimiter_detach(isc_ratelimiter_t **rlp) { 267293SN/A isc_ratelimiter_t *rl = *rlp; 268293SN/A isc_boolean_t free_now = ISC_FALSE; 269963Sjoehw 270963Sjoehw LOCK(&rl->lock); 271281SN/A REQUIRE(rl->refs > 0); 272281SN/A rl->refs--; 273281SN/A if (rl->refs == 0) 274281SN/A free_now = ISC_TRUE; 275281SN/A UNLOCK(&rl->lock); 276281SN/A 277281SN/A if (free_now) 278281SN/A ratelimiter_free(rl); 279281SN/A 280281SN/A *rlp = NULL; 281281SN/A} 282281SN/A 283281SN/Aisc_result_t 284281SN/Aisc_ratelimiter_stall(isc_ratelimiter_t *rl) { 285281SN/A isc_result_t result = ISC_R_SUCCESS; 286281SN/A 287635Sjoehw LOCK(&rl->lock); 288635Sjoehw switch (rl->state) { 289635Sjoehw case isc_ratelimiter_shuttingdown: 290635Sjoehw result = ISC_R_SHUTTINGDOWN; 291281SN/A break; 292293SN/A case isc_ratelimiter_ratelimited: 293963Sjoehw result = isc_timer_reset(rl->timer, isc_timertype_inactive, 294963Sjoehw NULL, NULL, ISC_FALSE); 295649Sjoehw RUNTIME_CHECK(result == ISC_R_SUCCESS); 296281SN/A case isc_ratelimiter_idle: 297281SN/A case isc_ratelimiter_stalled: 298281SN/A rl->state = isc_ratelimiter_stalled; 299281SN/A break; 300281SN/A } 301281SN/A UNLOCK(&rl->lock); 302281SN/A return (result); 303281SN/A} 304281SN/A 305635Sjoehwisc_result_t 306635Sjoehwisc_ratelimiter_release(isc_ratelimiter_t *rl) { 307635Sjoehw isc_result_t result = ISC_R_SUCCESS; 308635Sjoehw 309281SN/A LOCK(&rl->lock); 310281SN/A switch (rl->state) { 311281SN/A case isc_ratelimiter_shuttingdown: 312281SN/A result = ISC_R_SHUTTINGDOWN; 313281SN/A break; 314281SN/A case isc_ratelimiter_stalled: 315281SN/A if (!ISC_LIST_EMPTY(rl->pending)) { 316281SN/A result = isc_timer_reset(rl->timer, 317281SN/A isc_timertype_ticker, NULL, 318281SN/A &rl->interval, ISC_FALSE); 319281SN/A if (result == ISC_R_SUCCESS) 320293SN/A rl->state = isc_ratelimiter_ratelimited; 321635Sjoehw } else 322635Sjoehw rl->state = isc_ratelimiter_idle; 323635Sjoehw break; 324635Sjoehw case isc_ratelimiter_ratelimited: 325963Sjoehw case isc_ratelimiter_idle: 326963Sjoehw break; 327281SN/A } 328281SN/A UNLOCK(&rl->lock); 329281SN/A return (result); 330281SN/A} 331281SN/A