Home | History | Annotate | Line # | Download | only in isc
quota.c revision 1.8
      1  1.5  christos /*	$NetBSD: quota.c,v 1.8 2022/09/23 12:15:33 christos Exp $	*/
      2  1.1  christos 
      3  1.1  christos /*
      4  1.1  christos  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  1.1  christos  *
      6  1.8  christos  * SPDX-License-Identifier: MPL-2.0
      7  1.8  christos  *
      8  1.1  christos  * This Source Code Form is subject to the terms of the Mozilla Public
      9  1.1  christos  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  1.7  christos  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  1.1  christos  *
     12  1.1  christos  * See the COPYRIGHT file distributed with this work for additional
     13  1.1  christos  * information regarding copyright ownership.
     14  1.1  christos  */
     15  1.1  christos 
     16  1.1  christos /*! \file */
     17  1.1  christos 
     18  1.1  christos #include <stddef.h>
     19  1.1  christos 
     20  1.4  christos #include <isc/atomic.h>
     21  1.1  christos #include <isc/quota.h>
     22  1.1  christos #include <isc/util.h>
     23  1.1  christos 
     24  1.7  christos #define QUOTA_MAGIC    ISC_MAGIC('Q', 'U', 'O', 'T')
     25  1.7  christos #define VALID_QUOTA(p) ISC_MAGIC_VALID(p, QUOTA_MAGIC)
     26  1.7  christos 
     27  1.7  christos #define QUOTA_CB_MAGIC	  ISC_MAGIC('Q', 'T', 'C', 'B')
     28  1.7  christos #define VALID_QUOTA_CB(p) ISC_MAGIC_VALID(p, QUOTA_CB_MAGIC)
     29  1.7  christos 
     30  1.3  christos void
     31  1.4  christos isc_quota_init(isc_quota_t *quota, unsigned int max) {
     32  1.6  christos 	atomic_init(&quota->max, max);
     33  1.6  christos 	atomic_init(&quota->used, 0);
     34  1.6  christos 	atomic_init(&quota->soft, 0);
     35  1.6  christos 	atomic_init(&quota->waiting, 0);
     36  1.6  christos 	ISC_LIST_INIT(quota->cbs);
     37  1.6  christos 	isc_mutex_init(&quota->cblock);
     38  1.7  christos 	quota->magic = QUOTA_MAGIC;
     39  1.1  christos }
     40  1.1  christos 
     41  1.1  christos void
     42  1.1  christos isc_quota_destroy(isc_quota_t *quota) {
     43  1.7  christos 	REQUIRE(VALID_QUOTA(quota));
     44  1.7  christos 	quota->magic = 0;
     45  1.7  christos 
     46  1.4  christos 	INSIST(atomic_load(&quota->used) == 0);
     47  1.6  christos 	INSIST(atomic_load(&quota->waiting) == 0);
     48  1.6  christos 	INSIST(ISC_LIST_EMPTY(quota->cbs));
     49  1.6  christos 	atomic_store_release(&quota->max, 0);
     50  1.6  christos 	atomic_store_release(&quota->used, 0);
     51  1.6  christos 	atomic_store_release(&quota->soft, 0);
     52  1.6  christos 	isc_mutex_destroy(&quota->cblock);
     53  1.1  christos }
     54  1.1  christos 
     55  1.1  christos void
     56  1.4  christos isc_quota_soft(isc_quota_t *quota, unsigned int soft) {
     57  1.7  christos 	REQUIRE(VALID_QUOTA(quota));
     58  1.6  christos 	atomic_store_release(&quota->soft, soft);
     59  1.1  christos }
     60  1.1  christos 
     61  1.1  christos void
     62  1.4  christos isc_quota_max(isc_quota_t *quota, unsigned int max) {
     63  1.7  christos 	REQUIRE(VALID_QUOTA(quota));
     64  1.6  christos 	atomic_store_release(&quota->max, max);
     65  1.4  christos }
     66  1.4  christos 
     67  1.4  christos unsigned int
     68  1.4  christos isc_quota_getmax(isc_quota_t *quota) {
     69  1.7  christos 	REQUIRE(VALID_QUOTA(quota));
     70  1.4  christos 	return (atomic_load_relaxed(&quota->max));
     71  1.4  christos }
     72  1.4  christos 
     73  1.4  christos unsigned int
     74  1.4  christos isc_quota_getsoft(isc_quota_t *quota) {
     75  1.7  christos 	REQUIRE(VALID_QUOTA(quota));
     76  1.4  christos 	return (atomic_load_relaxed(&quota->soft));
     77  1.4  christos }
     78  1.4  christos 
     79  1.4  christos unsigned int
     80  1.4  christos isc_quota_getused(isc_quota_t *quota) {
     81  1.7  christos 	REQUIRE(VALID_QUOTA(quota));
     82  1.4  christos 	return (atomic_load_relaxed(&quota->used));
     83  1.1  christos }
     84  1.1  christos 
     85  1.6  christos static isc_result_t
     86  1.6  christos quota_reserve(isc_quota_t *quota) {
     87  1.1  christos 	isc_result_t result;
     88  1.6  christos 	uint_fast32_t max = atomic_load_acquire(&quota->max);
     89  1.6  christos 	uint_fast32_t soft = atomic_load_acquire(&quota->soft);
     90  1.6  christos 	uint_fast32_t used = atomic_load_acquire(&quota->used);
     91  1.6  christos 	do {
     92  1.6  christos 		if (max != 0 && used >= max) {
     93  1.6  christos 			return (ISC_R_QUOTA);
     94  1.6  christos 		}
     95  1.6  christos 		if (soft != 0 && used >= soft) {
     96  1.6  christos 			result = ISC_R_SOFTQUOTA;
     97  1.6  christos 		} else {
     98  1.1  christos 			result = ISC_R_SUCCESS;
     99  1.4  christos 		}
    100  1.6  christos 	} while (!atomic_compare_exchange_weak_acq_rel(&quota->used, &used,
    101  1.6  christos 						       used + 1));
    102  1.1  christos 	return (result);
    103  1.1  christos }
    104  1.1  christos 
    105  1.6  christos /* Must be quota->cbslock locked */
    106  1.6  christos static void
    107  1.6  christos enqueue(isc_quota_t *quota, isc_quota_cb_t *cb) {
    108  1.6  christos 	REQUIRE(cb != NULL);
    109  1.6  christos 	ISC_LIST_ENQUEUE(quota->cbs, cb, link);
    110  1.6  christos 	atomic_fetch_add_release(&quota->waiting, 1);
    111  1.6  christos }
    112  1.6  christos 
    113  1.6  christos /* Must be quota->cbslock locked */
    114  1.6  christos static isc_quota_cb_t *
    115  1.6  christos dequeue(isc_quota_t *quota) {
    116  1.6  christos 	isc_quota_cb_t *cb = ISC_LIST_HEAD(quota->cbs);
    117  1.6  christos 	INSIST(cb != NULL);
    118  1.6  christos 	ISC_LIST_DEQUEUE(quota->cbs, cb, link);
    119  1.6  christos 	atomic_fetch_sub_relaxed(&quota->waiting, 1);
    120  1.6  christos 	return (cb);
    121  1.6  christos }
    122  1.6  christos 
    123  1.6  christos static void
    124  1.6  christos quota_release(isc_quota_t *quota) {
    125  1.6  christos 	/*
    126  1.6  christos 	 * This is opportunistic - we might race with a failing quota_attach_cb
    127  1.6  christos 	 * and not detect that something is waiting, but eventually someone will
    128  1.6  christos 	 * be releasing quota and will detect it, so we don't need to worry -
    129  1.6  christos 	 * and we're saving a lot by not locking cblock every time.
    130  1.6  christos 	 */
    131  1.6  christos 
    132  1.6  christos 	if (atomic_load_acquire(&quota->waiting) > 0) {
    133  1.6  christos 		isc_quota_cb_t *cb = NULL;
    134  1.6  christos 		LOCK(&quota->cblock);
    135  1.6  christos 		if (atomic_load_relaxed(&quota->waiting) > 0) {
    136  1.6  christos 			cb = dequeue(quota);
    137  1.6  christos 		}
    138  1.6  christos 		UNLOCK(&quota->cblock);
    139  1.6  christos 		if (cb != NULL) {
    140  1.6  christos 			cb->cb_func(quota, cb->data);
    141  1.6  christos 			return;
    142  1.6  christos 		}
    143  1.6  christos 	}
    144  1.6  christos 
    145  1.6  christos 	INSIST(atomic_fetch_sub_release(&quota->used, 1) > 0);
    146  1.1  christos }
    147  1.1  christos 
    148  1.5  christos static isc_result_t
    149  1.6  christos doattach(isc_quota_t *quota, isc_quota_t **p) {
    150  1.1  christos 	isc_result_t result;
    151  1.5  christos 	REQUIRE(p != NULL && *p == NULL);
    152  1.5  christos 
    153  1.6  christos 	result = quota_reserve(quota);
    154  1.5  christos 	if (result == ISC_R_SUCCESS || result == ISC_R_SOFTQUOTA) {
    155  1.5  christos 		*p = quota;
    156  1.5  christos 	}
    157  1.5  christos 
    158  1.1  christos 	return (result);
    159  1.1  christos }
    160  1.1  christos 
    161  1.5  christos isc_result_t
    162  1.7  christos isc_quota_attach(isc_quota_t *quota, isc_quota_t **quotap) {
    163  1.7  christos 	REQUIRE(VALID_QUOTA(quota));
    164  1.7  christos 	REQUIRE(quotap != NULL && *quotap == NULL);
    165  1.7  christos 
    166  1.7  christos 	return (isc_quota_attach_cb(quota, quotap, NULL));
    167  1.5  christos }
    168  1.5  christos 
    169  1.5  christos isc_result_t
    170  1.7  christos isc_quota_attach_cb(isc_quota_t *quota, isc_quota_t **quotap,
    171  1.7  christos 		    isc_quota_cb_t *cb) {
    172  1.7  christos 	REQUIRE(VALID_QUOTA(quota));
    173  1.7  christos 	REQUIRE(cb == NULL || VALID_QUOTA_CB(cb));
    174  1.7  christos 	REQUIRE(quotap != NULL && *quotap == NULL);
    175  1.7  christos 
    176  1.7  christos 	isc_result_t result = doattach(quota, quotap);
    177  1.6  christos 	if (result == ISC_R_QUOTA && cb != NULL) {
    178  1.6  christos 		LOCK(&quota->cblock);
    179  1.6  christos 		enqueue(quota, cb);
    180  1.6  christos 		UNLOCK(&quota->cblock);
    181  1.6  christos 	}
    182  1.6  christos 	return (result);
    183  1.6  christos }
    184  1.6  christos 
    185  1.6  christos void
    186  1.6  christos isc_quota_cb_init(isc_quota_cb_t *cb, isc_quota_cb_func_t cb_func, void *data) {
    187  1.6  christos 	ISC_LINK_INIT(cb, link);
    188  1.6  christos 	cb->cb_func = cb_func;
    189  1.6  christos 	cb->data = data;
    190  1.7  christos 	cb->magic = QUOTA_CB_MAGIC;
    191  1.5  christos }
    192  1.5  christos 
    193  1.1  christos void
    194  1.7  christos isc_quota_detach(isc_quota_t **quotap) {
    195  1.7  christos 	REQUIRE(quotap != NULL && VALID_QUOTA(*quotap));
    196  1.7  christos 	isc_quota_t *quota = *quotap;
    197  1.7  christos 	*quotap = NULL;
    198  1.7  christos 
    199  1.7  christos 	quota_release(quota);
    200  1.1  christos }
    201