#include #include #include #include static lt_t td_cur_major_cycle_start(struct table_driven_reservation *tdres) { lt_t x, tmp; tmp = tdres->res.env->current_time - tdres->res.env->time_zero; x = div64_u64(tmp, tdres->major_cycle); x *= tdres->major_cycle; return x; } static lt_t td_next_major_cycle_start(struct table_driven_reservation *tdres) { lt_t x, tmp; tmp = tdres->res.env->current_time - tdres->res.env->time_zero; x = div64_u64(tmp, tdres->major_cycle) + 1; x *= tdres->major_cycle; return x; } static void td_client_arrives( struct reservation* res, struct reservation_client *client ) { struct table_driven_reservation *tdres = container_of(res, struct table_driven_reservation, res); list_add_tail(&client->list, &res->clients); switch (res->state) { case RESERVATION_INACTIVE: /* Figure out first replenishment time. */ tdres->major_cycle_start = td_next_major_cycle_start(tdres); res->next_replenishment = tdres->major_cycle_start; res->next_replenishment += tdres->intervals[0].start; tdres->next_interval = 0; res->env->change_state(res->env, res, RESERVATION_DEPLETED); break; case RESERVATION_ACTIVE: case RESERVATION_DEPLETED: /* do nothing */ break; case RESERVATION_ACTIVE_IDLE: res->env->change_state(res->env, res, RESERVATION_ACTIVE); break; } } static void td_client_departs( struct reservation *res, struct reservation_client *client, int did_signal_job_completion ) { list_del(&client->list); switch (res->state) { case RESERVATION_INACTIVE: case RESERVATION_ACTIVE_IDLE: BUG(); /* INACTIVE or IDLE <=> no client */ break; case RESERVATION_ACTIVE: if (list_empty(&res->clients)) { res->env->change_state(res->env, res, RESERVATION_ACTIVE_IDLE); } /* else: nothing to do, more clients ready */ break; case RESERVATION_DEPLETED: /* do nothing */ break; } } static lt_t td_time_remaining_until_end(struct table_driven_reservation *tdres) { lt_t now = tdres->res.env->current_time; lt_t end = tdres->cur_interval.end; TRACE("td_remaining(%u): start=%llu now=%llu end=%llu state=%d\n", tdres->res.id, tdres->cur_interval.start, now, end, tdres->res.state); if (now >= end) return 0; else return end - now; } static void td_replenish( struct reservation *res) { struct table_driven_reservation *tdres = container_of(res, struct table_driven_reservation, res); TRACE("td_replenish(%u): expected_replenishment=%llu\n", res->id, res->next_replenishment); /* figure out current interval */ tdres->cur_interval.start = tdres->major_cycle_start + tdres->intervals[tdres->next_interval].start; tdres->cur_interval.end = tdres->major_cycle_start + tdres->intervals[tdres->next_interval].end; TRACE("major_cycle_start=%llu => [%llu, %llu]\n", tdres->major_cycle_start, tdres->cur_interval.start, tdres->cur_interval.end); /* reset budget */ res->cur_budget = td_time_remaining_until_end(tdres); res->budget_consumed = 0; TRACE("td_replenish(%u): %s budget=%llu\n", res->id, res->cur_budget ? "" : "WARNING", res->cur_budget); /* prepare next slot */ tdres->next_interval = (tdres->next_interval + 1) % tdres->num_intervals; if (!tdres->next_interval) /* wrap to next major cycle */ tdres->major_cycle_start += tdres->major_cycle; /* determine next time this reservation becomes eligible to execute */ res->next_replenishment = tdres->major_cycle_start; res->next_replenishment += tdres->intervals[tdres->next_interval].start; TRACE("td_replenish(%u): next_replenishment=%llu\n", res->id, res->next_replenishment); switch (res->state) { case RESERVATION_DEPLETED: case RESERVATION_ACTIVE: case RESERVATION_ACTIVE_IDLE: if (list_empty(&res->clients)) res->env->change_state(res->env, res, RESERVATION_ACTIVE_IDLE); else /* we have clients & budget => ACTIVE */ res->env->change_state(res->env, res, RESERVATION_ACTIVE); break; case RESERVATION_INACTIVE: BUG(); break; } } static void td_drain_budget( struct reservation *res, lt_t how_much) { struct table_driven_reservation *tdres = container_of(res, struct table_driven_reservation, res); res->budget_consumed += how_much; res->budget_consumed_total += how_much; /* Table-driven scheduling: instead of tracking the budget, we compute * how much time is left in this allocation interval. */ /* sanity check: we should never try to drain from future slots */ BUG_ON(tdres->cur_interval.start > res->env->current_time); switch (res->state) { case RESERVATION_DEPLETED: case RESERVATION_INACTIVE: BUG(); break; case RESERVATION_ACTIVE_IDLE: case RESERVATION_ACTIVE: res->cur_budget = td_time_remaining_until_end(tdres); TRACE("td_drain_budget(%u): drained to budget=%llu\n", res->id, res->cur_budget); if (!res->cur_budget) { res->env->change_state(res->env, res, RESERVATION_DEPLETED); } else { /* sanity check budget calculation */ BUG_ON(res->env->current_time >= tdres->cur_interval.end); BUG_ON(res->env->current_time < tdres->cur_interval.start); } break; } } static struct task_struct* td_dispatch_client( struct reservation *res, lt_t *for_at_most) { struct task_struct *t; struct table_driven_reservation *tdres = container_of(res, struct table_driven_reservation, res); /* usual logic for selecting a client */ t = default_dispatch_client(res, for_at_most); TRACE_TASK(t, "td_dispatch_client(%u): selected, budget=%llu\n", res->id, res->cur_budget); /* check how much budget we have left in this time slot */ res->cur_budget = td_time_remaining_until_end(tdres); TRACE_TASK(t, "td_dispatch_client(%u): updated to budget=%llu next=%d\n", res->id, res->cur_budget, tdres->next_interval); if (unlikely(!res->cur_budget)) { /* Unlikely case: if we ran out of budget, the user configured * a broken scheduling table (overlapping table slots). * Not much we can do about this, but we can't dispatch a job * now without causing overload. So let's register this reservation * as depleted and wait for the next allocation. */ TRACE("td_dispatch_client(%u): budget unexpectedly depleted " "(check scheduling table for unintended overlap)\n", res->id); res->env->change_state(res->env, res, RESERVATION_DEPLETED); return NULL; } else return t; } static struct reservation_ops td_ops = { .dispatch_client = td_dispatch_client, .client_arrives = td_client_arrives, .client_departs = td_client_departs, .replenish = td_replenish, .drain_budget = td_drain_budget, }; void table_driven_reservation_init( struct table_driven_reservation *tdres, lt_t major_cycle, struct lt_interval *intervals, unsigned int num_intervals) { unsigned int i; /* sanity checking */ BUG_ON(!num_intervals); for (i = 0; i < num_intervals; i++) BUG_ON(intervals[i].end <= intervals[i].start); for (i = 0; i + 1 < num_intervals; i++) BUG_ON(intervals[i + 1].start <= intervals[i].end); BUG_ON(intervals[num_intervals - 1].end > major_cycle); reservation_init(&tdres->res); tdres->res.kind = TABLE_DRIVEN; tdres->major_cycle = major_cycle; tdres->intervals = intervals; tdres->cur_interval.start = 0; tdres->cur_interval.end = 0; tdres->num_intervals = num_intervals; tdres->res.ops = &td_ops; }