/* * Copyright (c) 2003-2005 Sendmail, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. */ #include "sm/generic.h" SM_RCSID("@(#)$Id: resource.c,v 1.26 2006/03/29 18:37:18 ca Exp $") #include "sm/error.h" #include "sm/assert.h" #include "sm/qmgr.h" #include "sm/qmgr-int.h" #include "qmgr.h" #include "log.h" /* ** QM_COMP_RESOURCE -- compute total resource usage ** result stored in qmgr_ctx->qmgr_total_usage ** ** Parameters: ** qmgr_ctx -- QMGR context ** locktype -- kind of locking ** ** Returns: ** usual sm_error code ** ** Called by: ** qss_control() ** qss_unthrottle() ** ** Locking: ** qmgr_ctx is locked/unlocked as requested by locktype ** check unlocking! XXX ** ** Last code review: 2005-04-21 23:54:01 ** Last code change: */ sm_ret_T qm_comp_resource(qmgr_ctx_P qmgr_ctx, thr_lock_T locktype) { sm_ret_T ret; int r; uint u; uint total, sum; SM_IS_QMGR_CTX(qmgr_ctx); ret = SM_SUCCESS; if (thr_lock_it(locktype)) { r = pthread_mutex_lock(&qmgr_ctx->qmgr_mutex); SM_LOCK_OK(r); if (r != 0) { QM_LEV_DPRINTFC(QDC_RSRC, 0, (QM_DEBFP, "sev=ERROR, func=qm_comp_resource, lock=%d\n", r)); return sm_error_temp(SM_EM_Q, r); } } ret = cdb_fs_getfree(qmgr_ctx->qmgr_cdb_fsctx, &qmgr_ctx->qmgr_cdb_kbfree); QM_LEV_DPRINTFC(QDC_RSRC, 3, (QM_DEBFP, "func=qm_comp_resource, cdb_kbfree=%lu, ret=%r\n", qmgr_ctx->qmgr_cdb_kbfree, ret)); if (sm_is_success(ret)) { qmgr_ctx->qmgr_usage[QMGR_RFL_CDB_I] = DISK_USAGE(qmgr_ctx->qmgr_cdb_kbfree, qmgr_ctx); QMGR_SET_USE_MAX(QMGR_RFL_CDB_I); } ret = edb_fs_getfree(qmgr_ctx->qmgr_edb_fsctx, &qmgr_ctx->qmgr_edb_kbfree); QM_LEV_DPRINTFC(QDC_RSRC, 3, (QM_DEBFP, "func=qm_comp_resource, edb_kbfree=%lu, ret=%r\n", qmgr_ctx->qmgr_edb_kbfree, ret)); if (sm_is_success(ret)) { qmgr_ctx->qmgr_usage[QMGR_RFL_EDB_I] = DISK_USAGE(qmgr_ctx->qmgr_edb_kbfree, qmgr_ctx); QMGR_SET_USE_MAX(QMGR_RFL_EDB_I); } ret = ibdb_fs_getfree(qmgr_ctx->qmgr_ibdb, &qmgr_ctx->qmgr_ibdb_kbfree); QM_LEV_DPRINTFC(QDC_RSRC, 3, (QM_DEBFP, "func=qm_comp_resource, ibdb_kbfree=%lu, ret=%r\n", qmgr_ctx->qmgr_ibdb_kbfree, ret)); if (sm_is_success(ret)) { qmgr_ctx->qmgr_usage[QMGR_RFL_IBD_I] = DISK_USAGE(qmgr_ctx->qmgr_ibdb_kbfree, qmgr_ctx); QMGR_SET_USE_MAX(QMGR_RFL_IBD_I); } total = QMGR_R_USE_NONE; sum = 0; for (u = 0; u <= QMGR_RFL_LAST_I; u++) { if (qmgr_ctx->qmgr_usage[u] >= qmgr_ctx->qmgr_upper[u]) { total = QMGR_R_USE_FULL; break; } if (qmgr_ctx->qmgr_usage[u] > qmgr_ctx->qmgr_lower[u]) { sum += 100 * (qmgr_ctx->qmgr_usage[u] - qmgr_ctx->qmgr_lower[u]) / (qmgr_ctx->qmgr_upper[u] - qmgr_ctx->qmgr_lower[u]); } } if (total == QMGR_R_USE_NONE) total = sum / (QMGR_RFL_LAST_I + 1); qmgr_ctx->qmgr_total_usage = total; if (thr_unl_always(locktype)) { r = pthread_mutex_unlock(&qmgr_ctx->qmgr_mutex); SM_ASSERT(r == 0); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_Q, r); } return ret; } /* ** QM_RESOURCE -- change resource usage ** ** Parameters: ** qmgr_ctx -- QMGR context ** direction -- up/down/any direction ** use -- fullness of cache/DB/resource (0..100) ** resource -- which resource? ** ** Returns: ** usual sm_error code ** ** Locking: ** qmgr_ctx is locked here (additional parameter in case a caller ** has a lock on qmgr_ctx?) ** ** Last code review: 2005-04-23 21:52:26 ** Last code change: */ sm_ret_T qm_resource(qmgr_ctx_P qmgr_ctx, int direction, uint use, uint resource) { sm_ret_T ret; int r; SM_IS_QMGR_CTX(qmgr_ctx); SM_REQUIRE(resource <= QMGR_RFL_LAST_I); ret = SM_SUCCESS; QM_LEV_DPRINTFC(QDC_RSRC, 6, (QM_DEBFP, "func=qm_resource, dir=%d, use=%u, resource=%u, flags=%#x\n", direction, use, resource, qmgr_ctx->qmgr_rflags)); r = pthread_mutex_lock(&qmgr_ctx->qmgr_mutex); SM_LOCK_OK(r); if (r != 0) { sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_CONTROL, QM_LMOD_CONTROL, SM_LOG_CRIT, 4, "sev=CRIT, func=qm_resource, lock=%d", r); return sm_error_temp(SM_EM_Q, r); } /* ** Throttle the system iff ** - asked for and ** - the use of the resource exceeds the upper threshold and ** - the use of the resource exceeds the last recorded usage ** ** else unthrottle the system iff ** - asked for and ** - the use of the resource is below the lower threshold and ** - the use of the resource is below the last recorded usage and ** - the resource is flagged as being exceeded ** ** in that case unthrottle the SMTPS servers iff ** - there is no resource shortage at all */ if (qmgr_is_throttle(direction) && use > qmgr_ctx->qmgr_upper[resource] && use > qmgr_ctx->qmgr_usage[resource]) { QMGR_SET_RFLAG_I(qmgr_ctx, resource); QM_LEV_DPRINTFC(QDC_RSRC, 2, (QM_DEBFP, "func=qm_resource, status=throttle, use=%u, resource=%d, flags=%#x\n", use, resource, qmgr_ctx->qmgr_rflags)); } else if (qmgr_is_un_throttle(direction) && use < qmgr_ctx->qmgr_lower[resource] && use < qmgr_ctx->qmgr_usage[resource] && QMGR_IS_RFLAG_I(qmgr_ctx, resource) ) { QMGR_CLR_RFLAG_I(qmgr_ctx, resource); QM_LEV_DPRINTFC(QDC_RSRC, 2, (QM_DEBFP, "func=qm_resource, status=unthrottle, use=%u,resource=%d, flags=%#x\n", use, resource, qmgr_ctx->qmgr_rflags)); if (QMGR_RFL_IS_NO_RSR(qmgr_ctx)) { sm_log_write(qmgr_ctx->qmgr_lctx, QM_LCAT_CONTROL, QM_LMOD_CONTROL, SM_LOG_INFO, 9, "sev=INFO, func=qm_resource, status=unthrottle, use=%u, resource=%d, flags=%#x" , use, resource, qmgr_ctx->qmgr_rflags); ret = qss_wakeup(qmgr_ctx, THR_NO_LOCK); if (sm_is_err(ret)) QM_LEV_DPRINTFC(QDC_RSRC, 1, (QM_DEBFP, "sev=ERROR, func=qm_resource, qss_wakeup=%r\n", ret)); } } qmgr_ctx->qmgr_usage[resource] = use; QMGR_SET_USE_MAX(resource); r = pthread_mutex_unlock(&qmgr_ctx->qmgr_mutex); SM_ASSERT(r == 0); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_Q, r); return ret; }