#include <linux/sched.h>
#include <litmus/litmus.h>
#include <litmus/reservations/reservation.h>
void reservation_init(struct reservation *res)
{
memset(res, 0, sizeof(*res));
res->state = RESERVATION_INACTIVE;
INIT_LIST_HEAD(&res->clients);
INIT_LIST_HEAD(&res->replenish_list);
budget_notifier_list_init(&res->budget_notifiers);
}
struct task_struct* default_dispatch_client(
struct reservation *res,
lt_t *for_at_most)
{
struct reservation_client *client, *next;
struct task_struct* tsk;
BUG_ON(res->state != RESERVATION_ACTIVE);
*for_at_most = 0;
list_for_each_entry_safe(client, next, &res->clients, list) {
tsk = client->dispatch(client);
if (likely(tsk)) {
/* Primitive form of round-robin scheduling:
* make sure we alternate between multiple clients
* with at least the granularity of the replenishment
* period. Reservations that need more fine-grained
* or more predictable alternation between threads
* within a reservation should provide a custom
* dispatch function. */
list_del(&client->list);
/* move to back of list */
list_add_tail(&client->list, &res->clients);
return tsk;
}
}
return NULL;
}
void common_drain_budget(
struct reservation *res,
lt_t how_much)
{
if (how_much >= res->cur_budget)
res->cur_budget = 0;
else
res->cur_budget -= how_much;
res->budget_consumed += how_much;
res->budget_consumed_total += how_much;
switch (res->state) {
case RESERVATION_DEPLETED:
case RESERVATION_INACTIVE:
BUG();
break;
case RESERVATION_ACTIVE_IDLE:
case RESERVATION_ACTIVE:
if (!res->cur_budget) {
res->env->change_state(res->env, res,
RESERVATION_DEPLETED);
} /* else: stay in current state */
break;
}
}
static struct task_struct * task_client_dispatch(struct reservation_client *client)
{
struct task_client *tc = container_of(client, struct task_client, client);
return tc->task;
}
void task_client_init(struct task_client *tc, struct task_struct *tsk,
struct reservation *res)
{
memset(&tc->client, 0, sizeof(tc->client));
tc->client.dispatch = task_client_dispatch;
tc->client.reservation = res;
tc->task = tsk;
}
static void sup_scheduler_update_at(
struct sup_reservation_environment* sup_env,
lt_t when)
{
if (sup_env->next_scheduler_update > when)
sup_env->next_scheduler_update = when;
}
static void sup_scheduler_update_after(
struct sup_reservation_environment* sup_env,
lt_t timeout)
{
sup_scheduler_update_at(sup_env, sup_env->env.current_time + timeout);
}
static int _sup_queue_depleted(
struct sup_reservation_environment* sup_env,
struct reservation *res)
{
struct list_head *pos;
struct reservation *queued;
int passed_earlier = 0;
BUG_ON(in_list(&res->replenish_list));
list_for_each(pos, &sup_env->depleted_reservations) {
queued = list_entry(pos, struct reservation, replenish_list);
if (queued->next_replenishment > res->next_replenishment) {
list_add(&res->replenish_list, pos->prev);
return passed_earlier;
} else
passed_earlier = 1;
}
list_add_tail(&res->replenish_list, &sup_env->depleted_reservations);
return passed_earlier;
}
static void sup_queue_depleted(
struct sup_reservation_environment* sup_env,
struct reservation *res)
{
int passed_earlier = _sup_queue_depleted(sup_env, res);
/* check for updated replenishment time */
if (!passed_earlier)
sup_scheduler_update_at(sup_env, res->next_replenishment);
}
static int _sup_queue_active(
struct sup_reservation_environment* sup_env,
struct reservation *res)
{
struct list_head *pos;
struct reservation *queued;
int passed_active = 0;
if (likely(res->priority != RESERVATION_BACKGROUND_PRIORITY)) {
/* enqueue in order of priority */
list_for_each(pos, &sup_env->active_reservations) {
queued = list_entry(pos, struct reservation, list);
if (queued->priority > res->priority) {
list_add(&res->list, pos->prev);
return passed_active;
} else if (queued->state == RESERVATION_ACTIVE)
passed_active = 1;
}
} else {
/* don't preempt unless the list happens to be empty */
passed_active = !list_empty(&sup_env->active_reservations);
}
/* Either a background reservation, or we fell off the end of the list.
* In both cases, just add the reservation to the end of the list of
* active reservations. */
list_add_tail(&res->list, &sup_env->active_reservations);
return passed_active;
}
static void sup_queue_active(
struct sup_reservation_environment* sup_env,
struct reservation *res)
{
int passed_active = _sup_queue_active(sup_env, res);
/* check for possible preemption */
if (res->state == RESERVATION_ACTIVE && !passed_active)
sup_env->next_scheduler_update = SUP_RESCHEDULE_NOW;
else if (res == list_first_entry(&sup_env->active_reservations,
struct reservation, list)) {
/* First reservation is draining budget => make sure
* the scheduler is called to notice when the reservation
* budget has been drained completely. */
sup_scheduler_update_after(sup_env, res->cur_budget);
}
}
static void sup_queue_reservation(
struct sup_reservation_environment* sup_env,
struct reservation *res)
{
switch (res->state) {
case RESERVATION_INACTIVE:
list_add(&res->list, &sup_env->inactive_reservations);
break;
case RESERVATION_DEPLETED:
sup_queue_depleted(sup_env, res);
break;
case RESERVATION_ACTIVE_IDLE:
case RESERVATION_ACTIVE:
sup_queue_active(sup_env, res);
break;
}
}
void sup_add_new_reservation(
struct sup_reservation_environment* sup_env,
struct reservation* new_res)
{
new_res->env = &sup_env->env;
list_add(&new_res->all_list, &sup_env->all_reservations);
sup_queue_reservation(sup_env, new_res);
}
struct reservation* sup_find_by_id(struct sup_reservation_environment* sup_env,
unsigned int id)
{
struct reservation *res;
list_for_each_entry(res, &sup_env->all_reservations, all_list) {
if (res->id == id)
return res;
}
return NULL;
}
static void sup_charge_budget(
struct sup_reservation_environment* sup_env,
lt_t delta)
{
struct reservation *res;
/* charge the highest-priority ACTIVE or ACTIVE_IDLE reservation */
res = list_first_entry_or_null(
&sup_env->active_reservations, struct reservation, list);
if (res) {
TRACE("R%d: charging at %llu for %llu execution, budget before: %llu\n",
res->id, res->env->current_time, delta, res->cur_budget);
res->ops->drain_budget(res, delta);
TRACE("R%d: budget now: %llu, priority: %llu\n",
res->id, res->cur_budget, res->priority);
}
/* check when the next budget expires */
res = list_first_entry_or_null(
&sup_env->active_reservations, struct reservation, list);
if (res) {
/* make sure scheduler is invoked when this reservation expires
* its remaining budget */
TRACE("requesting scheduler update for reservation %u "
"in %llu nanoseconds\n",
res->id, res->cur_budget);
sup_scheduler_update_after(sup_env, res->cur_budget);
}
}
static void sup_replenish_budgets(struct sup_reservation_environment* sup_env)
{
struct list_head *pos, *next;
struct reservation *res;
list_for_each_safe(pos, next, &sup_env->depleted_reservations) {
res = list_entry(pos, struct reservation, replenish_list);
if (res->next_replenishment <= sup_env->env.current_time) {
TRACE("R%d: replenishing budget at %llu, "
"priority: %llu\n",
res->id, res->env->current_time, res->priority);
res->ops->replenish(res);
} else {
/* list is ordered by increasing depletion times */
break;
}
}
/* request a scheduler update at the next replenishment instant */
res = list_first_entry_or_null(&sup_env->depleted_reservations,
struct reservation, replenish_list);
if (res)
sup_scheduler_update_at(sup_env, res->next_replenishment);
}
void sup_update_time(
struct sup_reservation_environment* sup_env,
lt_t now)
{
lt_t delta;
/* If the time didn't advance, there is nothing to do.
* This check makes it safe to call sup_advance_time() potentially
* multiple times (e.g., via different code paths. */
if (!list_empty(&sup_env->active_reservations))
TRACE("(sup_update_time) now: %llu, current_time: %llu\n", now,
sup_env->env.current_time);
if (unlikely(now <= sup_env->env.current_time))
return;
delta = now - sup_env->env.current_time;
sup_env->env.current_time = now;
/* check if future updates are required */
if (sup_env->next_scheduler_update <= sup_env->env.current_time)
sup_env->next_scheduler_update = SUP_NO_SCHEDULER_UPDATE;
/* deplete budgets by passage of time */
sup_charge_budget(sup_env, delta);
/* check if any budgets were replenished */
sup_replenish_budgets(sup_env);
}
struct task_struct* sup_dispatch(struct sup_reservation_environment* sup_env)
{
struct reservation *res, *next;
struct task_struct *tsk = NULL;
lt_t time_slice;
list_for_each_entry_safe(res, next, &sup_env->active_reservations, list) {
if (res->state == RESERVATION_ACTIVE) {
tsk = res->ops->dispatch_client(res, &time_slice);
if (likely(tsk)) {
if (time_slice)
sup_scheduler_update_after(sup_env, time_slice);
sup_scheduler_update_after(sup_env, res->cur_budget);
return tsk;
}
}
}
return NULL;
}
static void sup_res_change_state(
struct reservation_environment* env,
struct reservation *res,
reservation_state_t new_state)
{
struct sup_reservation_environment* sup_env;
sup_env = container_of(env, struct sup_reservation_environment, env);
TRACE("reservation R%d state %d->%d at %llu\n",
res->id, res->state, new_state, env->current_time);
if (new_state == RESERVATION_DEPLETED
&& (res->state == RESERVATION_ACTIVE ||
res->state == RESERVATION_ACTIVE_IDLE)) {
budget_notifiers_fire(&res->budget_notifiers, false);
} else if (res->state == RESERVATION_DEPLETED
&& new_state == RESERVATION_ACTIVE) {
budget_notifiers_fire(&res->budget_notifiers, true);
}
/* dequeue prior to re-queuing */
if (res->state == RESERVATION_DEPLETED)
list_del(&res->replenish_list);
else
list_del(&res->list);
/* check if we need to reschedule because we lost an active reservation */
if (res->state == RESERVATION_ACTIVE && !sup_env->will_schedule)
sup_env->next_scheduler_update = SUP_RESCHEDULE_NOW;
res->state = new_state;
sup_queue_reservation(sup_env, res);
}
static void sup_request_replenishment(
struct reservation_environment* env,
struct reservation *res)
{
struct sup_reservation_environment* sup_env;
sup_env = container_of(env, struct sup_reservation_environment, env);
sup_queue_depleted(sup_env, res);
}
void sup_init(struct sup_reservation_environment* sup_env)
{
memset(sup_env, 0, sizeof(*sup_env));
INIT_LIST_HEAD(&sup_env->all_reservations);
INIT_LIST_HEAD(&sup_env->active_reservations);
INIT_LIST_HEAD(&sup_env->depleted_reservations);
INIT_LIST_HEAD(&sup_env->inactive_reservations);
sup_env->env.change_state = sup_res_change_state;
sup_env->env.request_replenishment = sup_request_replenishment;
sup_env->next_scheduler_update = SUP_NO_SCHEDULER_UPDATE;
}