/* * Copyright (c) 2020 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * $Id: //eng/vdo-releases/aluminum/src/c++/vdo/kernel/limiter.c#2 $ */ #include "limiter.h" #include /**********************************************************************/ void getLimiterValuesAtomically(Limiter *limiter, uint32_t *active, uint32_t *maximum) { spin_lock(&limiter->lock); *active = limiter->active; *maximum = limiter->maximum; spin_unlock(&limiter->lock); } /**********************************************************************/ void initializeLimiter(Limiter *limiter, uint32_t limit) { limiter->active = 0; limiter->limit = limit; limiter->maximum = 0; init_waitqueue_head(&limiter->waiterQueue); spin_lock_init(&limiter->lock); } /**********************************************************************/ bool limiterIsIdle(Limiter *limiter) { spin_lock(&limiter->lock); bool idle = limiter->active == 0; spin_unlock(&limiter->lock); return idle; } /**********************************************************************/ void limiterReleaseMany(Limiter *limiter, uint32_t count) { spin_lock(&limiter->lock); limiter->active -= count; spin_unlock(&limiter->lock); if (waitqueue_active(&limiter->waiterQueue)) { wake_up_nr(&limiter->waiterQueue, count); } } /**********************************************************************/ void limiterWaitForIdle(Limiter *limiter) { spin_lock(&limiter->lock); while (limiter->active > 0) { DEFINE_WAIT(wait); prepare_to_wait_exclusive(&limiter->waiterQueue, &wait, TASK_UNINTERRUPTIBLE); spin_unlock(&limiter->lock); io_schedule(); spin_lock(&limiter->lock); finish_wait(&limiter->waiterQueue, &wait); }; spin_unlock(&limiter->lock); } /** * Take one permit from the limiter, if one is available, and update * the maximum active count if appropriate. * * The limiter's lock must already be locked. * * @param limiter The limiter to update * * @return true iff the permit was acquired **/ static bool takePermitLocked(Limiter *limiter) { if (limiter->active >= limiter->limit) { return false; } limiter->active += 1; if (limiter->active > limiter->maximum) { limiter->maximum = limiter->active; } return true; } /**********************************************************************/ void limiterWaitForOneFree(Limiter *limiter) { spin_lock(&limiter->lock); while (!takePermitLocked(limiter)) { DEFINE_WAIT(wait); prepare_to_wait_exclusive(&limiter->waiterQueue, &wait, TASK_UNINTERRUPTIBLE); spin_unlock(&limiter->lock); io_schedule(); spin_lock(&limiter->lock); finish_wait(&limiter->waiterQueue, &wait); }; spin_unlock(&limiter->lock); } /**********************************************************************/ bool limiterPoll(Limiter *limiter) { spin_lock(&limiter->lock); bool acquired = takePermitLocked(limiter); spin_unlock(&limiter->lock); return acquired; }