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