/*
 * 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;
}


syntax highlighted by Code2HTML, v. 0.9.1