1135446Strhodes/* 2254402Serwin * Copyright (C) 2004, 2005, 2007, 2012 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 1999-2002 Internet Software Consortium. 4135446Strhodes * 5193149Sdougb * Permission to use, copy, modify, and/or distribute this software for any 6135446Strhodes * purpose with or without fee is hereby granted, provided that the above 7135446Strhodes * copyright notice and this permission notice appear in all copies. 8135446Strhodes * 9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11135446Strhodes * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15135446Strhodes * PERFORMANCE OF THIS SOFTWARE. 16135446Strhodes */ 17135446Strhodes 18234010Sdougb/* $Id: ratelimiter.c,v 1.25 2007/06/19 23:47:17 tbox Exp $ */ 19135446Strhodes 20170222Sdougb/*! \file */ 21170222Sdougb 22135446Strhodes#include <config.h> 23135446Strhodes 24135446Strhodes#include <isc/mem.h> 25135446Strhodes#include <isc/ratelimiter.h> 26135446Strhodes#include <isc/task.h> 27135446Strhodes#include <isc/time.h> 28135446Strhodes#include <isc/timer.h> 29135446Strhodes#include <isc/util.h> 30135446Strhodes 31135446Strhodestypedef enum { 32135446Strhodes isc_ratelimiter_stalled = 0, 33135446Strhodes isc_ratelimiter_ratelimited = 1, 34135446Strhodes isc_ratelimiter_idle = 2, 35135446Strhodes isc_ratelimiter_shuttingdown = 3 36135446Strhodes} isc_ratelimiter_state_t; 37135446Strhodes 38135446Strhodesstruct isc_ratelimiter { 39135446Strhodes isc_mem_t * mctx; 40135446Strhodes isc_mutex_t lock; 41135446Strhodes int refs; 42135446Strhodes isc_task_t * task; 43135446Strhodes isc_timer_t * timer; 44135446Strhodes isc_interval_t interval; 45135446Strhodes isc_uint32_t pertic; 46135446Strhodes isc_ratelimiter_state_t state; 47135446Strhodes isc_event_t shutdownevent; 48135446Strhodes ISC_LIST(isc_event_t) pending; 49135446Strhodes}; 50135446Strhodes 51135446Strhodes#define ISC_RATELIMITEREVENT_SHUTDOWN (ISC_EVENTCLASS_RATELIMITER + 1) 52135446Strhodes 53135446Strhodesstatic void 54135446Strhodesratelimiter_tick(isc_task_t *task, isc_event_t *event); 55135446Strhodes 56135446Strhodesstatic void 57135446Strhodesratelimiter_shutdowncomplete(isc_task_t *task, isc_event_t *event); 58135446Strhodes 59135446Strhodesisc_result_t 60135446Strhodesisc_ratelimiter_create(isc_mem_t *mctx, isc_timermgr_t *timermgr, 61135446Strhodes isc_task_t *task, isc_ratelimiter_t **ratelimiterp) 62135446Strhodes{ 63135446Strhodes isc_result_t result; 64135446Strhodes isc_ratelimiter_t *rl; 65135446Strhodes INSIST(ratelimiterp != NULL && *ratelimiterp == NULL); 66135446Strhodes 67135446Strhodes rl = isc_mem_get(mctx, sizeof(*rl)); 68135446Strhodes if (rl == NULL) 69135446Strhodes return ISC_R_NOMEMORY; 70135446Strhodes rl->mctx = mctx; 71135446Strhodes rl->refs = 1; 72135446Strhodes rl->task = task; 73135446Strhodes isc_interval_set(&rl->interval, 0, 0); 74135446Strhodes rl->timer = NULL; 75135446Strhodes rl->pertic = 1; 76135446Strhodes rl->state = isc_ratelimiter_idle; 77135446Strhodes ISC_LIST_INIT(rl->pending); 78135446Strhodes 79135446Strhodes result = isc_mutex_init(&rl->lock); 80135446Strhodes if (result != ISC_R_SUCCESS) 81135446Strhodes goto free_mem; 82135446Strhodes result = isc_timer_create(timermgr, isc_timertype_inactive, 83135446Strhodes NULL, NULL, rl->task, ratelimiter_tick, 84135446Strhodes rl, &rl->timer); 85135446Strhodes if (result != ISC_R_SUCCESS) 86135446Strhodes goto free_mutex; 87135446Strhodes 88135446Strhodes /* 89135446Strhodes * Increment the reference count to indicate that we may 90135446Strhodes * (soon) have events outstanding. 91135446Strhodes */ 92135446Strhodes rl->refs++; 93135446Strhodes 94135446Strhodes ISC_EVENT_INIT(&rl->shutdownevent, 95135446Strhodes sizeof(isc_event_t), 96135446Strhodes 0, NULL, ISC_RATELIMITEREVENT_SHUTDOWN, 97135446Strhodes ratelimiter_shutdowncomplete, rl, rl, NULL, NULL); 98135446Strhodes 99135446Strhodes *ratelimiterp = rl; 100135446Strhodes return (ISC_R_SUCCESS); 101135446Strhodes 102135446Strhodesfree_mutex: 103135446Strhodes DESTROYLOCK(&rl->lock); 104135446Strhodesfree_mem: 105135446Strhodes isc_mem_put(mctx, rl, sizeof(*rl)); 106135446Strhodes return (result); 107135446Strhodes} 108135446Strhodes 109135446Strhodesisc_result_t 110135446Strhodesisc_ratelimiter_setinterval(isc_ratelimiter_t *rl, isc_interval_t *interval) { 111135446Strhodes isc_result_t result = ISC_R_SUCCESS; 112135446Strhodes LOCK(&rl->lock); 113135446Strhodes rl->interval = *interval; 114135446Strhodes /* 115135446Strhodes * If the timer is currently running, change its rate. 116135446Strhodes */ 117254402Serwin if (rl->state == isc_ratelimiter_ratelimited) { 118135446Strhodes result = isc_timer_reset(rl->timer, isc_timertype_ticker, NULL, 119135446Strhodes &rl->interval, ISC_FALSE); 120135446Strhodes } 121135446Strhodes UNLOCK(&rl->lock); 122135446Strhodes return (result); 123135446Strhodes} 124135446Strhodes 125135446Strhodesvoid 126135446Strhodesisc_ratelimiter_setpertic(isc_ratelimiter_t *rl, isc_uint32_t pertic) { 127135446Strhodes if (pertic == 0) 128135446Strhodes pertic = 1; 129135446Strhodes rl->pertic = pertic; 130135446Strhodes} 131135446Strhodes 132135446Strhodesisc_result_t 133135446Strhodesisc_ratelimiter_enqueue(isc_ratelimiter_t *rl, isc_task_t *task, 134135446Strhodes isc_event_t **eventp) 135135446Strhodes{ 136135446Strhodes isc_result_t result = ISC_R_SUCCESS; 137135446Strhodes isc_event_t *ev; 138135446Strhodes 139135446Strhodes REQUIRE(eventp != NULL && *eventp != NULL); 140135446Strhodes REQUIRE(task != NULL); 141135446Strhodes ev = *eventp; 142135446Strhodes REQUIRE(ev->ev_sender == NULL); 143135446Strhodes 144135446Strhodes LOCK(&rl->lock); 145254402Serwin if (rl->state == isc_ratelimiter_ratelimited || 146135446Strhodes rl->state == isc_ratelimiter_stalled) { 147135446Strhodes isc_event_t *ev = *eventp; 148135446Strhodes ev->ev_sender = task; 149254402Serwin ISC_LIST_APPEND(rl->pending, ev, ev_link); 150135446Strhodes *eventp = NULL; 151254402Serwin } else if (rl->state == isc_ratelimiter_idle) { 152135446Strhodes result = isc_timer_reset(rl->timer, isc_timertype_ticker, NULL, 153135446Strhodes &rl->interval, ISC_FALSE); 154135446Strhodes if (result == ISC_R_SUCCESS) { 155135446Strhodes ev->ev_sender = task; 156135446Strhodes rl->state = isc_ratelimiter_ratelimited; 157135446Strhodes } 158135446Strhodes } else { 159135446Strhodes INSIST(rl->state == isc_ratelimiter_shuttingdown); 160135446Strhodes result = ISC_R_SHUTTINGDOWN; 161135446Strhodes } 162135446Strhodes UNLOCK(&rl->lock); 163135446Strhodes if (*eventp != NULL && result == ISC_R_SUCCESS) 164135446Strhodes isc_task_send(task, eventp); 165135446Strhodes return (result); 166135446Strhodes} 167135446Strhodes 168135446Strhodesstatic void 169135446Strhodesratelimiter_tick(isc_task_t *task, isc_event_t *event) { 170135446Strhodes isc_result_t result = ISC_R_SUCCESS; 171135446Strhodes isc_ratelimiter_t *rl = (isc_ratelimiter_t *)event->ev_arg; 172135446Strhodes isc_event_t *p; 173135446Strhodes isc_uint32_t pertic; 174135446Strhodes 175135446Strhodes UNUSED(task); 176135446Strhodes 177135446Strhodes isc_event_free(&event); 178135446Strhodes 179135446Strhodes pertic = rl->pertic; 180254402Serwin while (pertic != 0) { 181135446Strhodes pertic--; 182135446Strhodes LOCK(&rl->lock); 183135446Strhodes p = ISC_LIST_HEAD(rl->pending); 184135446Strhodes if (p != NULL) { 185135446Strhodes /* 186135446Strhodes * There is work to do. Let's do it after unlocking. 187135446Strhodes */ 188135446Strhodes ISC_LIST_UNLINK(rl->pending, p, ev_link); 189135446Strhodes } else { 190135446Strhodes /* 191135446Strhodes * No work left to do. Stop the timer so that we don't 192135446Strhodes * waste resources by having it fire periodically. 193135446Strhodes */ 194135446Strhodes result = isc_timer_reset(rl->timer, 195135446Strhodes isc_timertype_inactive, 196135446Strhodes NULL, NULL, ISC_FALSE); 197135446Strhodes RUNTIME_CHECK(result == ISC_R_SUCCESS); 198135446Strhodes rl->state = isc_ratelimiter_idle; 199135446Strhodes pertic = 0; /* Force the loop to exit. */ 200135446Strhodes } 201135446Strhodes UNLOCK(&rl->lock); 202135446Strhodes if (p != NULL) { 203135446Strhodes isc_task_t *evtask = p->ev_sender; 204135446Strhodes isc_task_send(evtask, &p); 205135446Strhodes } 206135446Strhodes INSIST(p == NULL); 207135446Strhodes } 208135446Strhodes} 209135446Strhodes 210135446Strhodesvoid 211135446Strhodesisc_ratelimiter_shutdown(isc_ratelimiter_t *rl) { 212135446Strhodes isc_event_t *ev; 213135446Strhodes isc_task_t *task; 214135446Strhodes LOCK(&rl->lock); 215135446Strhodes rl->state = isc_ratelimiter_shuttingdown; 216135446Strhodes (void)isc_timer_reset(rl->timer, isc_timertype_inactive, 217135446Strhodes NULL, NULL, ISC_FALSE); 218135446Strhodes while ((ev = ISC_LIST_HEAD(rl->pending)) != NULL) { 219135446Strhodes ISC_LIST_UNLINK(rl->pending, ev, ev_link); 220135446Strhodes ev->ev_attributes |= ISC_EVENTATTR_CANCELED; 221135446Strhodes task = ev->ev_sender; 222135446Strhodes isc_task_send(task, &ev); 223135446Strhodes } 224135446Strhodes isc_timer_detach(&rl->timer); 225135446Strhodes /* 226135446Strhodes * Send an event to our task. The delivery of this event 227135446Strhodes * indicates that no more timer events will be delivered. 228135446Strhodes */ 229135446Strhodes ev = &rl->shutdownevent; 230135446Strhodes isc_task_send(rl->task, &ev); 231135446Strhodes 232135446Strhodes UNLOCK(&rl->lock); 233135446Strhodes} 234135446Strhodes 235135446Strhodesstatic void 236135446Strhodesratelimiter_shutdowncomplete(isc_task_t *task, isc_event_t *event) { 237135446Strhodes isc_ratelimiter_t *rl = (isc_ratelimiter_t *)event->ev_arg; 238135446Strhodes 239135446Strhodes UNUSED(task); 240135446Strhodes 241135446Strhodes isc_ratelimiter_detach(&rl); 242135446Strhodes} 243135446Strhodes 244135446Strhodesstatic void 245135446Strhodesratelimiter_free(isc_ratelimiter_t *rl) { 246135446Strhodes DESTROYLOCK(&rl->lock); 247135446Strhodes isc_mem_put(rl->mctx, rl, sizeof(*rl)); 248135446Strhodes} 249135446Strhodes 250135446Strhodesvoid 251135446Strhodesisc_ratelimiter_attach(isc_ratelimiter_t *source, isc_ratelimiter_t **target) { 252135446Strhodes REQUIRE(source != NULL); 253135446Strhodes REQUIRE(target != NULL && *target == NULL); 254135446Strhodes 255135446Strhodes LOCK(&source->lock); 256135446Strhodes REQUIRE(source->refs > 0); 257135446Strhodes source->refs++; 258135446Strhodes INSIST(source->refs > 0); 259135446Strhodes UNLOCK(&source->lock); 260135446Strhodes *target = source; 261135446Strhodes} 262135446Strhodes 263135446Strhodesvoid 264135446Strhodesisc_ratelimiter_detach(isc_ratelimiter_t **rlp) { 265135446Strhodes isc_ratelimiter_t *rl = *rlp; 266135446Strhodes isc_boolean_t free_now = ISC_FALSE; 267135446Strhodes 268135446Strhodes LOCK(&rl->lock); 269135446Strhodes REQUIRE(rl->refs > 0); 270135446Strhodes rl->refs--; 271135446Strhodes if (rl->refs == 0) 272135446Strhodes free_now = ISC_TRUE; 273135446Strhodes UNLOCK(&rl->lock); 274135446Strhodes 275135446Strhodes if (free_now) 276135446Strhodes ratelimiter_free(rl); 277135446Strhodes 278135446Strhodes *rlp = NULL; 279135446Strhodes} 280135446Strhodes 281135446Strhodesisc_result_t 282135446Strhodesisc_ratelimiter_stall(isc_ratelimiter_t *rl) { 283135446Strhodes isc_result_t result = ISC_R_SUCCESS; 284135446Strhodes 285135446Strhodes LOCK(&rl->lock); 286135446Strhodes switch (rl->state) { 287135446Strhodes case isc_ratelimiter_shuttingdown: 288135446Strhodes result = ISC_R_SHUTTINGDOWN; 289135446Strhodes break; 290135446Strhodes case isc_ratelimiter_ratelimited: 291135446Strhodes result = isc_timer_reset(rl->timer, isc_timertype_inactive, 292254402Serwin NULL, NULL, ISC_FALSE); 293135446Strhodes RUNTIME_CHECK(result == ISC_R_SUCCESS); 294254402Serwin /* FALLTHROUGH */ 295135446Strhodes case isc_ratelimiter_idle: 296135446Strhodes case isc_ratelimiter_stalled: 297135446Strhodes rl->state = isc_ratelimiter_stalled; 298135446Strhodes break; 299135446Strhodes } 300135446Strhodes UNLOCK(&rl->lock); 301135446Strhodes return (result); 302135446Strhodes} 303135446Strhodes 304135446Strhodesisc_result_t 305135446Strhodesisc_ratelimiter_release(isc_ratelimiter_t *rl) { 306135446Strhodes isc_result_t result = ISC_R_SUCCESS; 307135446Strhodes 308135446Strhodes LOCK(&rl->lock); 309135446Strhodes switch (rl->state) { 310135446Strhodes case isc_ratelimiter_shuttingdown: 311135446Strhodes result = ISC_R_SHUTTINGDOWN; 312135446Strhodes break; 313135446Strhodes case isc_ratelimiter_stalled: 314135446Strhodes if (!ISC_LIST_EMPTY(rl->pending)) { 315135446Strhodes result = isc_timer_reset(rl->timer, 316135446Strhodes isc_timertype_ticker, NULL, 317135446Strhodes &rl->interval, ISC_FALSE); 318135446Strhodes if (result == ISC_R_SUCCESS) 319135446Strhodes rl->state = isc_ratelimiter_ratelimited; 320254402Serwin } else 321135446Strhodes rl->state = isc_ratelimiter_idle; 322135446Strhodes break; 323135446Strhodes case isc_ratelimiter_ratelimited: 324135446Strhodes case isc_ratelimiter_idle: 325135446Strhodes break; 326135446Strhodes } 327135446Strhodes UNLOCK(&rl->lock); 328135446Strhodes return (result); 329135446Strhodes} 330