From 3f39fe20c14ffbb7ed3de1e5fc513d3beb5e4517 Mon Sep 17 00:00:00 2001 From: Felipe Cerqueira <felipec@mpi-sws.org> Date: Tue, 10 Jan 2017 11:29:49 +0100 Subject: [PATCH] Major Commit: Suspension-aware Scheduling 1) Definition of a generic model for job suspensions based on received service (e.g., job j_1 should suspend for 4ms as soon as service reaches 5ms). 2) Definition of the dynamic suspension model (i.e., cumulative suspension of job j_1 <= X). 3) Analysis of suspension-aware scheduling by inflation of job costs (via schedule reduction). In the literature, this is called suspension-oblivious analysis. 4) Analysis of suspension-aware scheduling by adjusting job jitter (via schedule reduction). 5) Proof of (weak) sustainability of job costs under suspension-aware scheduling. We show that if we increase the costs of all jobs while reducing their suspension times in a certain way, the response times of all jobs do not decrease. This has an important implication regarding worst-case schedules: if some schedulability analysis already accounts for the fact that job suspension times can vary from 0 to the task suspension bound, then it's perfectly safe to assume that jobs execute for their WCET. 6) Proof of sustainability of the cost of a single job under suspension-aware scheduling. That is, we show that increasing the cost of a single job does not reduce its own response time. (Note that this is a very basic result that applies to many work-conserving, JLFP schedulers. We don't claim anything about the response time of other jobs.) --- analysis/uni/basic/fp_rta_comp.v | 2 +- analysis/uni/basic/fp_rta_theory.v | 2 +- analysis/uni/basic/workload_bound_fp.v | 2 +- analysis/uni/jitter/fp_rta_comp.v | 52 +- analysis/uni/jitter/fp_rta_theory.v | 42 +- analysis/uni/jitter/workload_bound_fp.v | 44 +- .../uni/susp/dynamic/jitter/jitter_schedule.v | 162 +++ .../jitter/jitter_schedule_properties.v | 459 ++++++ .../dynamic/jitter/jitter_schedule_service.v | 1283 +++++++++++++++++ .../jitter/jitter_taskset_generation.v | 61 + .../susp/dynamic/jitter/rta_by_reduction.v | 202 +++ .../susp/dynamic/jitter/taskset_membership.v | 350 +++++ .../uni/susp/dynamic/jitter/taskset_rta.v | 233 +++ analysis/uni/susp/dynamic/oblivious/fp_rta.v | 2 +- .../susp/sustainability/allcosts/reduction.v | 165 +++ .../allcosts/reduction_properties.v | 891 ++++++++++++ .../sustainability/singlecost/reduction.v | 102 ++ .../singlecost/reduction_properties.v | 741 ++++++++++ implementation/apa/schedule.v | 2 +- implementation/global/basic/schedule.v | 2 +- implementation/global/jitter/schedule.v | 2 +- implementation/uni/basic/schedule.v | 2 +- implementation/uni/jitter/arrival_sequence.v | 46 +- implementation/uni/jitter/fp_rta_example.v | 19 +- implementation/uni/jitter/schedule.v | 2 +- implementation/uni/susp/schedule.v | 2 +- model/arrival/basic/arrival_bounds.v | 2 +- model/arrival/basic/task_arrival.v | 2 +- model/arrival/jitter/arrival_bounds.v | 2 +- model/arrival/jitter/task_arrival.v | 2 +- model/schedule/uni/jitter/valid_schedule.v | 66 + model/schedule/uni/response_time.v | 52 +- .../uni/susp/build_suspension_table.v | 264 ++++ model/schedule/uni/susp/last_execution.v | 47 + .../schedule/uni/susp/suspension_intervals.v | 74 +- model/schedule/uni/susp/valid_schedule.v | 75 + 36 files changed, 5320 insertions(+), 138 deletions(-) create mode 100644 analysis/uni/susp/dynamic/jitter/jitter_schedule.v create mode 100644 analysis/uni/susp/dynamic/jitter/jitter_schedule_properties.v create mode 100644 analysis/uni/susp/dynamic/jitter/jitter_schedule_service.v create mode 100644 analysis/uni/susp/dynamic/jitter/jitter_taskset_generation.v create mode 100644 analysis/uni/susp/dynamic/jitter/rta_by_reduction.v create mode 100644 analysis/uni/susp/dynamic/jitter/taskset_membership.v create mode 100644 analysis/uni/susp/dynamic/jitter/taskset_rta.v create mode 100644 analysis/uni/susp/sustainability/allcosts/reduction.v create mode 100644 analysis/uni/susp/sustainability/allcosts/reduction_properties.v create mode 100644 analysis/uni/susp/sustainability/singlecost/reduction.v create mode 100644 analysis/uni/susp/sustainability/singlecost/reduction_properties.v create mode 100644 model/schedule/uni/jitter/valid_schedule.v create mode 100644 model/schedule/uni/susp/build_suspension_table.v create mode 100644 model/schedule/uni/susp/valid_schedule.v diff --git a/analysis/uni/basic/fp_rta_comp.v b/analysis/uni/basic/fp_rta_comp.v index f3740944e..a74f4ef95 100644 --- a/analysis/uni/basic/fp_rta_comp.v +++ b/analysis/uni/basic/fp_rta_comp.v @@ -255,7 +255,7 @@ Module ResponseTimeIterationFP. Hypothesis H_valid_task_parameters: valid_sporadic_taskset task_cost task_period task_deadline ts. - (* Assume any job arrival sequence with consistent, non-duplicate arrivals... *) + (* Assume any job arrival sequence with consistent, duplicate-free arrivals... *) Variable arr_seq: arrival_sequence Job. Hypothesis H_arrival_times_are_consistent: arrival_times_are_consistent job_arrival arr_seq. Hypothesis H_no_duplicate_arrivals: arrival_sequence_is_a_set arr_seq. diff --git a/analysis/uni/basic/fp_rta_theory.v b/analysis/uni/basic/fp_rta_theory.v index 7f4e04176..9b5fae2e3 100644 --- a/analysis/uni/basic/fp_rta_theory.v +++ b/analysis/uni/basic/fp_rta_theory.v @@ -29,7 +29,7 @@ Module ResponseTimeAnalysisFP. Variable job_deadline: Job -> time. Variable job_task: Job -> SporadicTask. - (* Assume any job arrival sequence with consistent, non-duplicate arrivals... *) + (* Assume any job arrival sequence with consistent, duplicate-free arrivals... *) Variable arr_seq: arrival_sequence Job. Hypothesis H_arrival_times_are_consistent: arrival_times_are_consistent job_arrival arr_seq. Hypothesis H_no_duplicate_arrivals: arrival_sequence_is_a_set arr_seq. diff --git a/analysis/uni/basic/workload_bound_fp.v b/analysis/uni/basic/workload_bound_fp.v index 26bede2c4..de92fb980 100644 --- a/analysis/uni/basic/workload_bound_fp.v +++ b/analysis/uni/basic/workload_bound_fp.v @@ -160,7 +160,7 @@ Module WorkloadBoundFP. Hypothesis H_valid_task_parameters: valid_sporadic_taskset task_cost task_period task_deadline ts. - (* Consider any job arrival sequence with consistent, non-duplicate arrivals. *) + (* Consider any job arrival sequence with consistent, duplicate-free arrivals. *) Variable arr_seq: arrival_sequence Job. Hypothesis H_arrival_times_are_consistent: arrival_times_are_consistent job_arrival arr_seq. Hypothesis H_arr_seq_is_a_set: arrival_sequence_is_a_set arr_seq. diff --git a/analysis/uni/jitter/fp_rta_comp.v b/analysis/uni/jitter/fp_rta_comp.v index 7b54d24d7..3368541bc 100644 --- a/analysis/uni/jitter/fp_rta_comp.v +++ b/analysis/uni/jitter/fp_rta_comp.v @@ -260,13 +260,13 @@ Module ResponseTimeIterationFP. Variable job_task: Job -> SporadicTask. (* Consider a task set ts... *) - Variable ts: taskset_of SporadicTask. + Variable ts: seq SporadicTask. - (* ...where tasks have valid parameters. *) - Hypothesis H_valid_task_parameters: - valid_sporadic_taskset task_cost task_period task_deadline ts. + (* ...with positive task costs and periods. *) + Hypothesis H_positive_costs: forall tsk, tsk \in ts -> task_cost tsk > 0. + Hypothesis H_positive_periods: forall tsk, tsk \in ts -> task_period tsk > 0. - (* Next, consider any job arrival sequence with consistent, non-duplicate arrivals, ... *) + (* Next, consider any job arrival sequence with consistent, duplicate-free arrivals, ... *) Variable arr_seq: arrival_sequence Job. Hypothesis H_arrival_times_are_consistent: arrival_times_are_consistent job_arrival arr_seq. Hypothesis H_arr_seq_is_a_set: arrival_sequence_is_a_set arr_seq. @@ -276,18 +276,29 @@ Module ResponseTimeIterationFP. forall j, arrives_in arr_seq j -> job_task j \in ts. + + (* ... and satisfy the sporadic task model.*) + Hypothesis H_sporadic_tasks: + sporadic_task_model task_period job_arrival job_task arr_seq. - (* ...have valid parameters,...*) - Hypothesis H_valid_job_parameters: + (* Assume that the cost of each job is bounded by the cost of its task,... *) + Hypothesis H_job_cost_le_task_cost: forall j, arrives_in arr_seq j -> - valid_sporadic_job_with_jitter task_cost task_deadline task_jitter - job_cost job_deadline job_jitter job_task j. + job_cost j <= task_cost (job_task j). - (* ... and satisfy the sporadic task model.*) - Hypothesis H_sporadic_tasks: - sporadic_task_model task_period job_arrival job_task arr_seq. + (* ...the jitter of each job is bounded by the jitter of its task,... *) + Hypothesis H_job_jitter_le_task_jitter: + forall j, + arrives_in arr_seq j -> + job_jitter j <= task_jitter (job_task j). + (* ...and that job deadlines equal task deadlines. *) + Hypothesis H_job_deadline_eq_task_deadline: + forall j, + arrives_in arr_seq j -> + job_deadline j = task_deadline (job_task j). + (* Assume any fixed-priority policy... *) Variable higher_eq_priority: FP_policy SporadicTask. @@ -329,26 +340,21 @@ Module ResponseTimeIterationFP. (tsk, R) \In RTA_claimed_bounds -> response_time_bounded_by tsk (task_jitter tsk + R). Proof. - rename H_valid_task_parameters into PARAMS, - H_valid_job_parameters into JOBPARAMS. unfold valid_sporadic_job, valid_realtime_job, valid_sporadic_taskset, is_valid_sporadic_task in *. unfold RTA_claimed_bounds; intros tsk R. case SOME: fp_claimed_bounds => [rt_bounds|] IN; last by done. - move: (PARAMS) => PARAMStsk. - feed (PARAMStsk tsk); - [by apply fp_claimed_bounds_from_taskset with (tsk0 := tsk) (R0 := R) in SOME | des]. apply uniprocessor_response_time_bound_fp with (task_cost0 := task_cost) (task_period0 := task_period) - (ts0 := ts) (task_deadline0 := task_deadline) - (job_deadline0 := job_deadline) (job_jitter0 := job_jitter) + (ts0 := ts) (job_jitter0 := job_jitter) (higher_eq_priority0 := higher_eq_priority); try (by done). { apply fp_claimed_bounds_gt_zero with (task_cost0 := task_cost) (task_period0 := task_period) (task_deadline0 := task_deadline) (higher_eq_priority0 := higher_eq_priority) (ts0 := ts) (task_jitter0 := task_jitter) (rt_bounds0 := rt_bounds) (tsk0 := tsk); try (by done). - by intros tsk0 IN0; specialize (PARAMS tsk0 IN0); des. + apply H_positive_costs. + by eapply fp_claimed_bounds_from_taskset; eauto 1. } by apply fp_claimed_bounds_yields_fixed_point with (task_deadline0 := task_deadline) (rt_bounds0 := rt_bounds). @@ -370,17 +376,13 @@ Module ResponseTimeIterationFP. have DL := fp_claimed_bounds_le_deadline task_cost task_period task_deadline task_jitter higher_eq_priority ts. have BOUND := fp_analysis_yields_response_time_bounds. - rename H_test_succeeds into TEST, H_valid_job_parameters into JOBPARAMS. + rename H_test_succeeds into TEST. move:TEST; case TEST:(fp_claimed_bounds _ _ _ _ _) => [rt_bounds|] _//. intros tsk IN. move: (RESP rt_bounds TEST tsk IN) => [R INbounds]. specialize (DL rt_bounds TEST tsk R INbounds). apply task_completes_before_deadline with (task_deadline0 := task_deadline) (R0 := task_jitter tsk + R); try (by done). - { - intros j ARRj; unfold valid_sporadic_job_with_jitter in *. - by specialize (JOBPARAMS j ARRj); move: JOBPARAMS => [[_ [_ EQ]] _]. - } by apply BOUND; rewrite /RTA_claimed_bounds TEST. Qed. diff --git a/analysis/uni/jitter/fp_rta_theory.v b/analysis/uni/jitter/fp_rta_theory.v index 645ad7ca3..73069b6f6 100644 --- a/analysis/uni/jitter/fp_rta_theory.v +++ b/analysis/uni/jitter/fp_rta_theory.v @@ -30,34 +30,39 @@ Module ResponseTimeAnalysisFP. Context {Job: eqType}. Variable job_arrival: Job -> time. Variable job_cost: Job -> time. - Variable job_deadline: Job -> time. Variable job_jitter: Job -> time. Variable job_task: Job -> SporadicTask. - (* Consider any job arrival sequence with consistent, non-duplicate arrivals... *) + (* Consider any task set ts... *) + Variable ts: seq SporadicTask. + + (* ...with positive task periods. *) + Hypothesis H_positive_periods: forall tsk, tsk \in ts -> task_period tsk > 0. + + (* Consider any job arrival sequence with consistent, duplicate-free arrivals... *) Variable arr_seq: arrival_sequence Job. Hypothesis H_arrival_times_are_consistent: arrival_times_are_consistent job_arrival arr_seq. Hypothesis H_arr_seq_is_a_set: arrival_sequence_is_a_set arr_seq. - (* ... in which jobs arrive sporadically and have valid parameters. *) + (* ... in which jobs arrive sporadically,... *) Hypothesis H_sporadic_tasks: sporadic_task_model task_period job_arrival job_task arr_seq. - Hypothesis H_valid_job_parameters: + + (* ...the cost of each job is bounded by the cost of its task, ... *) + Hypothesis H_job_cost_le_task_cost: forall j, arrives_in arr_seq j -> - valid_sporadic_job_with_jitter task_cost task_deadline task_jitter - job_cost job_deadline job_jitter job_task j. + job_cost j <= task_cost (job_task j). - (* Consider a task set ts where all tasks have valid parameters... *) - Variable ts: seq SporadicTask. - Hypothesis H_valid_task_parameters: - valid_sporadic_taskset task_cost task_period task_deadline ts. - - (* ... and assume that all jobs in the arrival sequence come from the task set. *) - Hypothesis H_all_jobs_from_taskset: + (* ...and the jitter of each job is bounded by the jitter of its task. *) + Hypothesis H_job_jitter_le_task_jitter: forall j, arrives_in arr_seq j -> - job_task j \in ts. + job_jitter j <= task_jitter (job_task j). + + (* Assume that all jobs in the arrival sequence come from the task set. *) + Hypothesis H_all_jobs_from_taskset: + forall j, arrives_in arr_seq j -> job_task j \in ts. (* Next, consider any uniprocessor schedule of this arrival sequence... *) Variable sched: schedule Job. @@ -105,15 +110,13 @@ Module ResponseTimeAnalysisFP. Proof. unfold valid_sporadic_job_with_jitter, valid_sporadic_job, valid_sporadic_taskset, is_valid_sporadic_task in *. - rename H_response_time_is_fixed_point into FIX, - H_valid_task_parameters into PARAMS, - H_valid_job_parameters into JOBPARAMS. + rename H_response_time_is_fixed_point into FIX. intros j IN JOBtsk. set arr := actual_arrival job_arrival job_jitter. apply completion_monotonic with (t := arr j + R); try (by done). { rewrite -addnA leq_add2l leq_add2r. - by rewrite -JOBtsk; specialize (JOBPARAMS j IN); des. + by rewrite -JOBtsk; apply H_job_jitter_le_task_jitter. } set prio := FP_to_JLFP job_task higher_eq_priority. apply busy_interval_bounds_response_time with (arr_seq0 := arr_seq) @@ -122,8 +125,7 @@ Module ResponseTimeAnalysisFP. - by intros x z y; apply H_priority_is_transitive. intros t. apply fp_workload_bound_holds with (task_cost0 := task_cost) - (task_period0 := task_period) (task_deadline0 := task_deadline) - (job_deadline0 := job_deadline) (task_jitter0 := task_jitter) (ts0 := ts); try (by done). + (task_period0 := task_period) (task_jitter0 := task_jitter) (ts0 := ts); try (by done). by rewrite JOBtsk. Qed. diff --git a/analysis/uni/jitter/workload_bound_fp.v b/analysis/uni/jitter/workload_bound_fp.v index f7379e31b..1e5c93580 100644 --- a/analysis/uni/jitter/workload_bound_fp.v +++ b/analysis/uni/jitter/workload_bound_fp.v @@ -155,22 +155,22 @@ Module WorkloadBoundFP. Context {Task: eqType}. Variable task_cost: Task -> time. Variable task_period: Task -> time. - Variable task_deadline: Task -> time. Variable task_jitter: Task -> time. Context {Job: eqType}. Variable job_arrival: Job -> time. Variable job_cost: Job -> time. - Variable job_deadline: Job -> time. Variable job_jitter: Job -> time. Variable job_task: Job -> Task. - (* Let ts be any task set with valid task parameters. *) + (* Let ts be any task set... *) Variable ts: seq Task. - Hypothesis H_valid_task_parameters: - valid_sporadic_taskset task_cost task_period task_deadline ts. + + (* ...with positive task periods. *) + Hypothesis H_positive_periods: + forall tsk, tsk \in ts -> task_period tsk > 0. - (* Consider any job arrival sequence with consistent, non-duplicate arrivals. *) + (* Consider any job arrival sequence with consistent, duplicate-free arrivals. *) Variable arr_seq: arrival_sequence Job. Hypothesis H_arrival_times_are_consistent: arrival_times_are_consistent job_arrival arr_seq. Hypothesis H_arrival_sequence_is_a_set: arrival_sequence_is_a_set arr_seq. @@ -178,18 +178,21 @@ Module WorkloadBoundFP. (* First, let's define some local names for clarity. *) Let actual_arrivals := actual_arrivals_between job_arrival job_jitter arr_seq. - (* Next, assume that all jobs come from the task set ...*) + (* Next, assume that all jobs come from the task set, ...*) Hypothesis H_all_jobs_from_taskset: + forall j, arrives_in arr_seq j -> job_task j \in ts. + + (* ...the cost of each job is bounded by the cost of its task, ... *) + Hypothesis H_job_cost_le_task_cost: forall j, arrives_in arr_seq j -> - job_task j \in ts. + job_cost j <= task_cost (job_task j). - (* ...and have valid parameters. *) - Hypothesis H_valid_job_parameters: + (* ...and the jitter of each job is bounded by the jitter of its task. *) + Hypothesis H_job_jitter_le_task_jitter: forall j, arrives_in arr_seq j -> - valid_sporadic_job_with_jitter task_cost task_deadline task_jitter - job_cost job_deadline job_jitter job_task j. + job_jitter j <= task_jitter (job_task j). (* Assume that jobs arrived sporadically. *) Hypothesis H_sporadic_arrivals: @@ -221,16 +224,13 @@ Module WorkloadBoundFP. actual_hp_workload t (t + R) <= R. Proof. rename H_fixed_point into FIX, H_all_jobs_from_taskset into FROMTS, - H_valid_job_parameters into JOBPARAMS, - H_valid_task_parameters into PARAMS, H_arrival_times_are_consistent into CONS, H_arrival_sequence_is_a_set into SET. unfold actual_hp_workload, workload_of_higher_or_equal_priority_tasks, valid_sporadic_job_with_jitter, valid_sporadic_job, valid_realtime_job, valid_sporadic_taskset, is_valid_sporadic_task in *. have BOUND := sporadic_task_with_jitter_arrival_bound task_period task_jitter job_arrival job_jitter job_task arr_seq CONS SET. - feed_n 2 BOUND; (try by done); - first by intros j ARRj; specialize (JOBPARAMS j ARRj); des. + feed_n 2 BOUND; (try by done). intro t. rewrite {2}FIX /workload_bound /total_workload_bound_fp. set l := actual_arrivals_between job_arrival job_jitter arr_seq t (t + R). @@ -253,16 +253,12 @@ Module WorkloadBoundFP. { rewrite /num_actual_arrivals_of_task -sum1_size big_distrl /= big_filter. apply leq_sum_seq; move => j1 IN1 /eqP EQ. - rewrite -EQ mul1n. - feed (JOBPARAMS j1). - { - rewrite mem_filter in IN1; move: IN1 => /andP [_ ARR1]. - by apply in_arrivals_implies_arrived in ARR1. - } - by move: JOBPARAMS => [[_ [LE _]] _]. + rewrite -EQ mul1n; apply H_job_cost_le_task_cost. + rewrite mem_filter in IN1; move: IN1 => /andP [_ ARR1]. + by apply in_arrivals_implies_arrived in ARR1. } rewrite /task_workload_bound_FP leq_mul2r; apply/orP; right. - feed (BOUND t (t + R) tsk0); first by feed (PARAMS tsk0); last by des. + feed (BOUND t (t + R) tsk0); first by apply H_positive_periods. by rewrite -addnA addKn in BOUND. Qed. diff --git a/analysis/uni/susp/dynamic/jitter/jitter_schedule.v b/analysis/uni/susp/dynamic/jitter/jitter_schedule.v new file mode 100644 index 000000000..00494ad4c --- /dev/null +++ b/analysis/uni/susp/dynamic/jitter/jitter_schedule.v @@ -0,0 +1,162 @@ +Require Import rt.util.all. +Require Import rt.model.priority rt.model.suspension. +Require Import rt.model.arrival.basic.arrival_sequence. +Require Import rt.model.schedule.uni.jitter.schedule. +Require Import rt.model.schedule.uni.transformation.construction. +From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq. + +(* In this file, we formalize a reduction from a suspension-aware schedule + to a jitter-aware schedule. *) +Module JitterScheduleConstruction. + + Import UniprocessorScheduleWithJitter Suspension Priority ScheduleConstruction. + + Section ConstructingJitterSchedule. + + Context {Task: eqType}. + Context {Job: eqType}. + Variable job_arrival: Job -> time. + Variable job_task: Job -> Task. + + (** Basic Setup & Setting*) + + (* Consider any job arrival sequence. *) + Variable arr_seq: arrival_sequence Job. + + (* Assume any FP policy and the corresponding job-level priority relation. *) + Variable higher_eq_priority: FP_policy Task. + Let job_higher_eq_priority := FP_to_JLFP job_task higher_eq_priority. + + (* Consider the original job and task costs from the suspension-aware schedule... *) + Variable job_cost: Job -> time. + Variable task_cost: Task -> time. + + (* ...and assume that jobs have associated suspension times. *) + Variable job_suspension_duration: job_suspension Job. + + (* Next, consider any suspension-aware schedule of the arrival sequence. *) + Variable sched_susp: schedule Job. + + (** Definition of the Reduction *) + + (* Now we proceed with the reduction. Let j be the job to be analyzed. *) + Variable j: Job. + + (* For simplicity, we give some local names for the parameters of this job... *) + Let arr_j := job_arrival j. + Let task_of_j := job_task j. + + (* ...and recall the definition of other higher-or-equal-priority tasks. *) + Let other_hep_task tsk_other := + higher_eq_priority tsk_other task_of_j && (tsk_other != task_of_j). + + (* Moreover, assume that jobs from higher-priority tasks are associated a response-time bound R. *) + Variable R: Job -> time. + + (* Now we are going to redefine the job parameters for the new schedule. *) + Section DefiningJobParameters. + + (* First, we inflate job costs with suspension time. *) + Section CostInflation. + + (* Recall the total suspension time of a job in the original schedule. *) + Let job_total_suspension := + total_suspension job_cost job_suspension_duration. + + (* We inflate job costs as follows. + (a) The cost of job j is inflated with its total suspension time. + (b) The cost of all other jobs remains unchanged. *) + Definition inflated_job_cost (any_j: Job) := + if any_j == j then + job_cost any_j + job_total_suspension any_j + else + job_cost any_j. + + End CostInflation. + + (* Next, we show how to set the job jitter in the new schedule + to compensate for the suspension times. *) + Section ConvertingSuspensionToJitter. + + (* Let any_j be any job in the new schedule. *) + Variable any_j: Job. + + (* First, recall the distance between any_j and job j that is to be analyzed. + Note that since we use natural numbers, this distance saturates to 0 if + any_j arrives later than j. *) + Let distance_to_j := job_arrival j - job_arrival any_j. + + (* Then, we define the actual job jitter in the new schedule as follows. + a) For every job of higher-priority tasks (with respect to job j), the jitter is set to + the minimum between the distance to j and the term (R any_j - job_cost any_j). + b) The remaining jobs have no jitter. + + The intuition behind this formula is that any_j is to be released as close to job j as + possible, while not "trespassing" the response-time bound (R any_j) from sched_susp, + which is only assumed to be valid for higher-priority tasks. *) + Definition job_jitter := + if other_hep_task (job_task any_j) then + minn distance_to_j (R any_j - job_cost any_j) + else 0. + + End ConvertingSuspensionToJitter. + + End DefiningJobParameters. + + (** Schedule Construction *) + + (* Next we generate a jitter-aware schedule using the job parameters above. + For that, we always pick the highest-priority pending job (after jitter) in + the new schedule. However, if there are multiple highest-priority jobs, we + try not to schedule job j in order to maximize interference. *) + Section ScheduleConstruction. + + (* First, we define the schedule construction function. *) + Section ConstructionStep. + + (* For any time t, suppose that we have generated the schedule prefix in the + interval [0, t). Then, we must define what should be scheduled at time t. *) + Variable sched_prefix: schedule Job. + Variable t: time. + + (* For simplicity, let's define some local names. *) + Let job_is_pending := pending job_arrival inflated_job_cost job_jitter sched_prefix. + Let actual_job_arrivals_up_to := actual_arrivals_up_to job_arrival job_jitter arr_seq. + Let lower_priority j1 j2 := ~~ job_higher_eq_priority j1 j2. + + (* Next, consider the list of pending jobs at time t that are different from j + and whose jitter has passed, in the new schedule. *) + Definition pending_jobs_other_than_j := + [seq j_other <- actual_job_arrivals_up_to t | job_is_pending j_other t & j_other != j]. + + (* From the list of pending jobs, we can return one of the (possibly many) + highest-priority jobs, or None, in case there are no pending jobs. *) + Definition highest_priority_job_other_than_j := + seq_min job_higher_eq_priority pending_jobs_other_than_j. + + (* Then, we construct the new schedule at time t as follows. + a) If job j is pending and the highest-priority job (other than j) has + lower priority than j, we have to schedule j. + b) Else, if job j is not pending, we pick one of the highest priority pending jobs. *) + Definition build_schedule : option Job := + if job_is_pending j t then + if highest_priority_job_other_than_j is Some j_hp then + if lower_priority j_hp j then + Some j + else Some j_hp + else Some j + else highest_priority_job_other_than_j. + + End ConstructionStep. + + (* Finally, starting from the empty schedule, ...*) + Let empty_schedule : schedule Job := fun t => None. + + (* ...we use the recursive definition above to construct the jitter-aware schedule. *) + Definition sched_jitter := build_schedule_from_prefixes build_schedule empty_schedule. + + End ScheduleConstruction. + + End ConstructingJitterSchedule. + +End JitterScheduleConstruction. \ No newline at end of file diff --git a/analysis/uni/susp/dynamic/jitter/jitter_schedule_properties.v b/analysis/uni/susp/dynamic/jitter/jitter_schedule_properties.v new file mode 100644 index 000000000..67e68044b --- /dev/null +++ b/analysis/uni/susp/dynamic/jitter/jitter_schedule_properties.v @@ -0,0 +1,459 @@ +Require Import rt.util.all. +Require Import rt.model.priority rt.model.suspension. +Require Import rt.model.arrival.basic.job rt.model.arrival.basic.task + rt.model.arrival.basic.arrival_sequence. +Require Import rt.model.arrival.jitter.job. +Require Import rt.model.schedule.uni.schedulability rt.model.schedule.uni.service + rt.model.schedule.uni.workload + rt.model.schedule.uni.response_time. +Require Import rt.model.schedule.uni.jitter.schedule + rt.model.schedule.uni.jitter.valid_schedule + rt.model.schedule.uni.jitter.platform. +Require Import rt.model.schedule.uni.susp.suspension_intervals + rt.model.schedule.uni.susp.schedule + rt.model.schedule.uni.susp.valid_schedule + rt.model.schedule.uni.susp.platform. +Require Import rt.analysis.uni.susp.dynamic.jitter.jitter_schedule. +Require Import rt.model.schedule.uni.transformation.construction. +From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop path. + +(* In this file, we prove several properties about the constructed jitter-aware schedule. *) +Module JitterScheduleProperties. + + Import Job SporadicTaskset Suspension Priority SuspensionIntervals Workload Service + UniprocessorScheduleWithJitter Schedulability ResponseTime + ScheduleConstruction ValidSuspensionAwareSchedule ValidJitterAwareSchedule. + + Module basic := schedule.UniprocessorSchedule. + Module susp := ScheduleWithSuspensions. + Module jitter_aware := Platform. + Module susp_aware := PlatformWithSuspensions. + Module job_jitter := JobWithJitter. + Module reduction := JitterScheduleConstruction. + + Section ProvingScheduleProperties. + + Context {Task: eqType}. + Context {Job: eqType}. + Variable job_arrival: Job -> time. + Variable job_task: Job -> Task. + + (** Basic Setup & Setting *) + + (* Let ts be any task set to be analyzed. *) + Variable ts: seq Task. + + (* Next, consider any consistent job arrival sequence... *) + Variable arr_seq: arrival_sequence Job. + Hypothesis H_arrival_times_are_consistent: + arrival_times_are_consistent job_arrival arr_seq. + + (* ...whose jobs come from task set ts. *) + Hypothesis H_jobs_from_taskset: + forall j, arrives_in arr_seq j -> job_task j \in ts. + + (* Consider any FP policy that is reflexive, transitive and total. *) + Variable higher_eq_priority: FP_policy Task. + Hypothesis H_priority_is_reflexive: FP_is_reflexive higher_eq_priority. + Hypothesis H_priority_is_transitive: FP_is_transitive higher_eq_priority. + Hypothesis H_priority_is_total: FP_is_total_over_task_set higher_eq_priority ts. + Let job_higher_eq_priority := FP_to_JLDP job_task higher_eq_priority. + + (* Consider the original job and task costs from the suspension-aware schedule. *) + Variable job_cost: Job -> time. + Variable task_cost: Task -> time. + + (* Assume that jobs have associated suspension times. *) + Variable job_suspension_duration: job_suspension Job. + + (* Next, consider any valid suspension-aware schedule of this arrival sequence. + (Note: see rt.model.schedule.uni.susp.valid_schedule.v for details) *) + Variable sched_susp: schedule Job. + Hypothesis H_valid_schedule: + valid_suspension_aware_schedule job_arrival arr_seq job_higher_eq_priority + job_suspension_duration job_cost sched_susp. + + (* Finally, recall the definition of a job response-time bound in sched_susp. *) + Let job_response_time_in_sched_susp_bounded_by := + is_response_time_bound_of_job job_arrival job_cost sched_susp. + + (** Analysis Setup *) + + (* Now we proceed with the reduction. Let j be the job to be analyzed. *) + Variable j: Job. + Hypothesis H_from_arrival_sequence: arrives_in arr_seq j. + + (* For simplicity, let's give some local names for the parameters of this job... *) + Let arr_j := job_arrival j. + Let task_of_j := job_task j. + + (* ...and recall the definition of other higher-or-equal-priority tasks. *) + Let other_hep_task tsk_other := + higher_eq_priority tsk_other task_of_j && (tsk_other != task_of_j). + + (* Moreover, assume that each job is associated a response-time bound R. *) + Variable R: Job -> time. + + (** Instantiation of the Reduction *) + + (* Next, recall the jitter-aware schedule from the reduction. *) + Let sched_jitter := reduction.sched_jitter job_arrival job_task arr_seq higher_eq_priority + job_cost job_suspension_duration j R. + Let inflated_job_cost := reduction.inflated_job_cost job_cost job_suspension_duration j. + Let job_jitter := reduction.job_jitter job_arrival job_task higher_eq_priority job_cost j R. + + (** Schedule Construction *) + + (* In this section, we prove that the jitter-aware schedule uses its construction function. *) + Section PropertiesOfScheduleConstruction. + + Let build_schedule := reduction.build_schedule job_arrival job_task arr_seq higher_eq_priority + job_cost job_suspension_duration j R. + + (* Then, by showing that the construction function depends only on the previous service, ... *) + Lemma sched_jitter_depends_only_on_service: + forall sched1 sched2 t, + (forall j, service sched1 j t = service sched2 j t) -> + build_schedule sched1 t = build_schedule sched2 t. + Proof. + intros sched1 sched2 t ALL. + rewrite /build_schedule /reduction.build_schedule /reduction.highest_priority_job_other_than_j. + set pend1 := pending _ _ _ sched1. + set pend2 := pending _ _ _ sched2. + have SAME': forall j, pend1 j t = pend2 j t. + { + intros j0; rewrite /pend1 /pend2 /pending. + case: jitter_has_passed => //=. + by rewrite /completed_by ALL. + } + set pendjobs := reduction.pending_jobs_other_than_j _ _ _ _ _ _ _ _. + have SAME: pendjobs sched1 t = pendjobs sched2 t. + { + apply eq_in_filter; intros j0 IN. + rewrite /pending; case: jitter_has_passed => //=. + by rewrite /completed_by ALL. + } + rewrite SAME SAME'; by done. + Qed. + + (* ...we infer that the generated schedule is indeed based on the construction function. *) + Corollary sched_jitter_uses_construction_function: + forall t, + sched_jitter t = build_schedule sched_jitter t. + Proof. + by ins; apply service_dependent_schedule_construction, sched_jitter_depends_only_on_service. + Qed. + + End PropertiesOfScheduleConstruction. + + (** Valid Schedule Properties *) + + (* In this section, we prove that sched_jitter is a valid jitter-aware schedule. *) + Section ScheduleIsValid. + + (* For simplicity, let's recall some definitions from the schedule construction. *) + Let pending_jobs_other_than_j := + reduction.pending_jobs_other_than_j job_arrival job_task arr_seq higher_eq_priority + job_cost job_suspension_duration j R sched_jitter. + Let hp_job_other_than_j := + reduction.highest_priority_job_other_than_j job_arrival job_task arr_seq higher_eq_priority + job_cost job_suspension_duration j R sched_jitter. + + (* Also recall the definition of a valid jitter-aware schedule. *) + Let is_valid_jitter_aware_schedule := + valid_jitter_aware_schedule job_arrival arr_seq job_higher_eq_priority + inflated_job_cost job_jitter. + + (* First, we show that scheduled jobs come from the arrival sequence. *) + Lemma sched_jitter_jobs_come_from_arrival_sequence: + jobs_come_from_arrival_sequence sched_jitter arr_seq. + Proof. + move: H_valid_schedule => [FROM _]. + move => j0 t /eqP SCHED. + rewrite sched_jitter_uses_construction_function /reduction.build_schedule + -/hp_job_other_than_j in SCHED. + set pending_in_jitter := pending _ _ _ _ in SCHED. + destruct (hp_job_other_than_j t) as [j_hp|] eqn:HP; last first. + { + case PENDj: (pending_in_jitter j t); rewrite PENDj in SCHED; last by done. + by case: SCHED => SAME; subst j0. + } + { + have IN: arrives_in arr_seq j_hp. + { + have IN: j_hp \in pending_jobs_other_than_j t. + { + rewrite /hp_job_other_than_j /reduction.highest_priority_job_other_than_j in HP. + by apply seq_min_in_seq in HP. + } + rewrite mem_filter in IN; move: IN => /andP [/andP _ IN]. + rewrite /actual_arrivals_up_to in IN. + by apply in_actual_arrivals_between_implies_arrived in IN. + } + case PENDj: (pending_in_jitter j t); rewrite PENDj in SCHED; + first by move: SCHED; case: (~~ _); case => SAME; subst. + by case: SCHED => SAME; subst j0. + } + Qed. + + (* Next, we show that jobs do not execute before their arrival times... *) + Lemma sched_jitter_jobs_execute_after_jitter: + jobs_execute_after_jitter job_arrival job_jitter sched_jitter. + Proof. + move => j0 t /eqP SCHED. + rewrite sched_jitter_uses_construction_function /reduction.build_schedule + -/hp_job_other_than_j in SCHED. + set pending_in_jitter := pending _ _ _ _ in SCHED. + destruct (hp_job_other_than_j t) as [j_hp|] eqn:HP; last first. + { + case PENDj: (pending_in_jitter j t); rewrite PENDj in SCHED; last by done. + by case: SCHED => SAME; subst j0; move: PENDj => /andP [ARR _]. + } + { + have ARR: jitter_has_passed job_arrival job_jitter j_hp t. + { + have IN: j_hp \in pending_jobs_other_than_j t. + { + rewrite /hp_job_other_than_j /reduction.highest_priority_job_other_than_j in HP. + by apply seq_min_in_seq in HP. + } + by rewrite mem_filter in IN; move: IN => /andP [/andP [/andP [ARR _] _] _]. + } + case PENDj: (pending_in_jitter j t); rewrite PENDj in SCHED; + last by case: SCHED => SAME; subst j0. + move: SCHED; case: (~~ _); case => SAME; subst; last by done. + by move: PENDj => /andP [ARRj _]. + } + Qed. + + (* ...nor longer than their execution costs. *) + Lemma sched_jitter_completed_jobs_dont_execute: + completed_jobs_dont_execute inflated_job_cost sched_jitter. + Proof. + intros j0 t. + induction t; + first by rewrite /service /service_during big_geq //. + rewrite /service /service_during big_nat_recr //=. + rewrite leq_eqVlt in IHt; move: IHt => /orP [/eqP EQ | LT]; last first. + { + apply: leq_trans LT; rewrite -addn1. + by apply leq_add; last by apply leq_b1. + } + rewrite -[inflated_job_cost _]addn0; apply leq_add; first by rewrite -EQ. + rewrite leqn0 eqb0 /scheduled_at. + rewrite sched_jitter_uses_construction_function /reduction.build_schedule + -/hp_job_other_than_j. + destruct (hp_job_other_than_j t) as [j_hp|] eqn:HP; last first. + { + case PENDj: pending; last by done. + apply/eqP; case => SAME; subst j0; move: PENDj => /andP [_ NOTCOMPj]. + by rewrite /completed_by EQ eq_refl in NOTCOMPj. + } + rewrite /hp_job_other_than_j /reduction.highest_priority_job_other_than_j in HP. + apply seq_min_in_seq in HP; rewrite mem_filter /pending /completed_by in HP. + move: HP => /andP [/andP [/andP [_ NOTCOMPhp] _] _]. + case PENDj: pending. + { + move: PENDj => /andP [_ NOTCOMPj]. + case: (~~ higher_eq_priority _ _); apply/eqP; case => SAME; subst j0; + first by rewrite /completed_by EQ eq_refl in NOTCOMPj. + by rewrite EQ eq_refl in NOTCOMPhp. + } + { + apply/eqP; case => SAME; subst j0. + by rewrite EQ eq_refl in NOTCOMPhp. + } + Qed. + + (* In addition, we prove that the schedule is (jitter-aware) work-conserving... *) + Lemma sched_jitter_work_conserving: + jitter_aware.work_conserving job_arrival inflated_job_cost job_jitter arr_seq sched_jitter. + Proof. + intros j0 t IN BACK. + move: BACK => /andP [PEND NOTSCHED]. + rewrite /scheduled_at sched_jitter_uses_construction_function + /reduction.build_schedule -/hp_job_other_than_j in NOTSCHED *. + destruct (hp_job_other_than_j t) as [j_hp|] eqn:HP. + { + case PENDj: pending; rewrite PENDj in NOTSCHED; last by exists j_hp. + by case: (~~ _); [by exists j | by exists j_hp]. + } + { + case PENDj: pending; rewrite PENDj in NOTSCHED; first by exists j. + rewrite /hp_job_other_than_j /reduction.highest_priority_job_other_than_j in HP. + case: (boolP (j0 == j)) => [EQ | NEQ]; + first by move: EQ => /eqP EQ; subst j0; rewrite PEND in PENDj. + have IN0: j0 \in pending_jobs_other_than_j t. + { + rewrite mem_filter PEND NEQ /=. + apply arrived_between_implies_in_actual_arrivals; try (by done). + by move: PEND => /andP [ARR _]. + } + move: HP => /eqP HP; rewrite -[_ == _]negbK in HP. + exfalso; move: HP => /negP BUG; apply: BUG. + by apply seq_min_exists with (x := j0). + } + Qed. + + (* ...and respects task priorities. *) + Lemma sched_jitter_respects_policy: + jitter_aware.respects_FP_policy job_arrival inflated_job_cost job_jitter + job_task arr_seq sched_jitter higher_eq_priority. + Proof. + rename H_priority_is_transitive into TRANS, H_priority_is_total into TOTAL, + H_priority_is_reflexive into REFL, H_jobs_from_taskset into FROMTS. + move => j1 j2 t IN BACK /eqP SCHED. + move: BACK => /andP [PEND NOTSCHED]. + rewrite /scheduled_at sched_jitter_uses_construction_function /reduction.build_schedule + -/hp_job_other_than_j in SCHED NOTSCHED *. + set pend := pending _ _ _ _ in SCHED NOTSCHED. + have ALL: forall j_hi j_lo, hp_job_other_than_j t = Some j_hi -> + j_lo \in pending_jobs_other_than_j t -> + higher_eq_priority (job_task j_hi) (job_task j_lo). + { + intros j_hi j_lo SOME INlo; move: SCHED => MIN. + rewrite /hp_job_other_than_j /reduction.highest_priority_job_other_than_j in SOME. + apply seq_min_computes_min with (y := j_lo) in SOME; try (by done); + first by intros x y z; apply TRANS. + intros x y; rewrite mem_filter [y \in _]mem_filter /actual_arrivals_up_to. + move => /andP [_ INx] /andP [_ INy]. + rewrite /FP_is_total_over_task_set /total_over_list in TOTAL. + by apply/orP; apply TOTAL; apply FROMTS; + eapply in_actual_arrivals_between_implies_arrived; eauto 1. + } + case PENDj: (pend j t); rewrite PENDj in SCHED NOTSCHED. + { + destruct (hp_job_other_than_j t) as [j_hp|] eqn:HP. + { + rewrite /FP_to_JLFP /= in SCHED NOTSCHED. + case LP: (~~ higher_eq_priority (job_task j_hp) (job_task j)); + rewrite LP in SCHED NOTSCHED. + { + case: SCHED => SAME; subst j2. + case: (boolP (j1 == j)) => [EQ | NEQ]; first by move: EQ => /eqP EQ; subst j1. + apply (TRANS (job_task j_hp)). + { + have INhp: arrives_in arr_seq j_hp. + { + rewrite /hp_job_other_than_j /reduction.highest_priority_job_other_than_j in HP. + apply seq_min_in_seq in HP. + rewrite mem_filter in HP; move: HP => /andP [_ INhp]. + rewrite /actual_arrivals_up_to in INhp. + by apply in_actual_arrivals_before_implies_arrived in INhp. + } + by exploit (TOTAL (job_task j) (job_task j_hp)); try (by apply FROMTS); + move => [HPj | HPhp]; last by rewrite HPhp in LP. + } + apply ALL; try (by done). + move: PEND => /andP [ARR NOTCOMP]. + rewrite mem_filter /pending ARR NOTCOMP 2!andTb. + by apply/andP; split; last by apply arrived_between_implies_in_actual_arrivals. + } + { + case: SCHED => SAME; subst j2. + case: (boolP (j1 == j)) => [EQ | NEQ]; + first by move: EQ => /eqP EQ; subst j1; apply negbT in LP; rewrite negbK in LP. + apply ALL; try (by done). + move: PEND => /andP [ARR NOTCOMP]. + rewrite mem_filter /pending ARR NOTCOMP 2!andTb. + by apply/andP; split; last by apply arrived_between_implies_in_actual_arrivals. + } + } + { + case: SCHED => SAME; subst j2. + case: (boolP (j1 == j)) => [EQ | NEQ]; first by move: EQ => /eqP EQ; subst j1. + suff NOTNONE: hp_job_other_than_j t != None by rewrite HP in NOTNONE. + apply seq_min_exists with (x := j1). + move: PEND => /andP [ARR NOTCOMP]. + rewrite mem_filter /pending ARR NOTCOMP 2!andTb. + by apply/andP; split; last by apply arrived_between_implies_in_actual_arrivals. + } + } + { + case: (boolP (j1 == j)) => [EQ | NEQ]; + first by move: EQ => /eqP EQ; subst j1; rewrite -/pend PENDj in PEND. + apply ALL; first by done. + move: PEND => /andP [ARR NOTCOMP]. + rewrite mem_filter /pending ARR NOTCOMP 2!andTb. + by apply/andP; split; last by apply arrived_between_implies_in_actual_arrivals. + } + Qed. + + (* From the properties above, we conclude that the generated schedule is valid. *) + Corollary sched_jitter_is_valid: is_valid_jitter_aware_schedule sched_jitter. + Proof. + repeat split. + - by apply sched_jitter_jobs_come_from_arrival_sequence. + - by apply sched_jitter_jobs_execute_after_jitter. + - by apply sched_jitter_completed_jobs_dont_execute. + - by apply sched_jitter_work_conserving. + - by apply sched_jitter_respects_policy. + Qed. + + (* Finally, we show that the generated schedule does not pick job j if + there are other pending higher-or-equal-priority jobs. *) + Lemma sched_jitter_does_not_pick_j: + forall j_hp t, + arrives_in arr_seq j_hp -> + j_hp != j -> + pending job_arrival inflated_job_cost job_jitter sched_jitter j_hp t -> + higher_eq_priority (job_task j_hp) (job_task j) -> + ~~ scheduled_at sched_jitter j t. + Proof. + rename H_priority_is_transitive into TRANS, H_priority_is_total into TOTAL, + H_jobs_from_taskset into FROMTS. + move => j_hp t ARRinhp NEQ PENDhp HP; apply/negP; move => /eqP SCHEDj. + rewrite sched_jitter_uses_construction_function /reduction.build_schedule + -/hp_job_other_than_j in SCHEDj. + set pend := pending _ _ _ _ in SCHEDj. + have INhp: j_hp \in pending_jobs_other_than_j t. + { + rewrite mem_filter PENDhp NEQ /=. + apply arrived_between_implies_in_actual_arrivals; try (by done). + rewrite /actual_arrival_between /=. + by move: PENDhp => /andP [ARRhp _]. + } + case PENDj: (pend j t); rewrite PENDj in SCHEDj. + { + destruct (hp_job_other_than_j t) as [j_hp'|] eqn:HP'. + { + have ALL: forall j_lo, j_lo \in pending_jobs_other_than_j t -> + higher_eq_priority (job_task j_hp') (job_task j_lo). + { + intros j_lo INlo; move: HP' => MIN. + rewrite /hp_job_other_than_j /reduction.highest_priority_job_other_than_j in MIN. + apply seq_min_computes_min with (y := j_lo) in MIN; try (by done); + first by intros x y z; apply TRANS. + intros x y; rewrite mem_filter [y \in _]mem_filter /actual_arrivals_up_to. + move => /andP [_ INx] /andP [_ INy]. + by apply/orP; apply TOTAL; apply FROMTS; + eapply in_actual_arrivals_between_implies_arrived; eauto 1. + } + case LP: (~~ higher_eq_priority (job_task j_hp') (job_task j)); rewrite LP in SCHEDj. + { + move: LP => /negP LP; apply: LP. + by apply (TRANS (job_task j_hp)); first by apply ALL. + } + { + case: SCHEDj => SAME; subst j_hp'. + rewrite /hp_job_other_than_j /reduction.highest_priority_job_other_than_j in HP'. + by apply seq_min_in_seq in HP'; rewrite mem_filter eq_refl andbF /= in HP'. + } + } + { + suff NOTNONE: hp_job_other_than_j t != None by rewrite HP' in NOTNONE. + by apply seq_min_exists with (x := j_hp). + } + } + { + rewrite /hp_job_other_than_j /reduction.highest_priority_job_other_than_j in SCHEDj. + apply seq_min_in_seq in SCHEDj. + by rewrite mem_filter eq_refl andbF in SCHEDj. + } + Qed. + + End ScheduleIsValid. + + End ProvingScheduleProperties. + +End JitterScheduleProperties. \ No newline at end of file diff --git a/analysis/uni/susp/dynamic/jitter/jitter_schedule_service.v b/analysis/uni/susp/dynamic/jitter/jitter_schedule_service.v new file mode 100644 index 000000000..c5d88d837 --- /dev/null +++ b/analysis/uni/susp/dynamic/jitter/jitter_schedule_service.v @@ -0,0 +1,1283 @@ +Require Import rt.util.all. +Require Import rt.model.priority rt.model.suspension. +Require Import rt.model.arrival.basic.job rt.model.arrival.basic.task + rt.model.arrival.basic.task_arrival + rt.model.arrival.basic.arrival_sequence. +Require Import rt.model.arrival.jitter.job. +Require Import rt.model.schedule.uni.schedulability rt.model.schedule.uni.service + rt.model.schedule.uni.workload + rt.model.schedule.uni.response_time. +Require Import rt.model.schedule.uni.jitter.schedule + rt.model.schedule.uni.jitter.platform. +Require Import rt.model.schedule.uni.susp.suspension_intervals + rt.model.schedule.uni.susp.schedule + rt.model.schedule.uni.susp.platform + rt.model.schedule.uni.susp.valid_schedule. +Require Import rt.analysis.uni.susp.dynamic.jitter.jitter_schedule + rt.analysis.uni.susp.dynamic.jitter.jitter_schedule_properties. +Require Import rt.model.schedule.uni.transformation.construction. +From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop path. + +(* In this file, we compare the service received by the analyzed job j after + reducing the suspension-aware schedule to a jitter-aware schedule. *) +Module JitterScheduleService. + + Import Job SporadicTaskset Suspension Priority SuspensionIntervals Workload Service + UniprocessorScheduleWithJitter Schedulability ResponseTime TaskArrival + ScheduleConstruction ValidSuspensionAwareSchedule. + + Module basic := schedule.UniprocessorSchedule. + Module susp := ScheduleWithSuspensions. + Module jitter_aware := Platform. + Module susp_aware := PlatformWithSuspensions. + Module job_jitter := JobWithJitter. + Module reduction := JitterScheduleConstruction. + Module reduction_prop := JitterScheduleProperties. + + (* We begin by providing the initial setup and definitions in Sections 1 to 5. + The main results are proven later in Sections 6-(A) to 6-(C). *) + Section ProvingScheduleProperties. + + Context {Task: eqType}. + Variable task_cost: Task -> time. + Variable task_period: Task -> time. + Variable task_deadline: Task -> time. + Context {Job: eqType}. + Variable job_arrival: Job -> time. + Variable job_cost: Job -> time. + Variable job_deadline: Job -> time. + Variable job_task: Job -> Task. + + (** 1) Basic Setup & Setting *) + + (* Let ts be any task set. *) + Variable ts: seq Task. + + (* Next, consider any consistent, duplicate-free job arrival sequence... *) + Variable arr_seq: arrival_sequence Job. + Hypothesis H_arrival_times_are_consistent: + arrival_times_are_consistent job_arrival arr_seq. + Hypothesis H_arrival_sequence_is_a_set: arrival_sequence_is_a_set arr_seq. + + (* ...where all jobs come from task set ts. *) + Hypothesis H_jobs_from_taskset: + forall j, arrives_in arr_seq j -> job_task j \in ts. + + (* Since we consider real-time tasks, assume that job deadlines are equal to task deadlines. *) + Hypothesis H_job_deadlines_equal_task_deadlines: + forall j, arrives_in arr_seq j -> job_deadline j = task_deadline (job_task j). + + (* Also assume that tasks have constrained deadlines and that jobs arrive sporadically. + (Note: this is required to bound the interference of previous jobs of the analyzed task.) *) + Hypothesis H_constrained_deadlines: + constrained_deadline_model task_period task_deadline ts. + Hypothesis H_sporadic_arrivals: + sporadic_task_model task_period job_arrival job_task arr_seq. + + (* Consider any FP policy that is reflexive, transitive and total. *) + Variable higher_eq_priority: FP_policy Task. + Hypothesis H_priority_is_reflexive: FP_is_reflexive higher_eq_priority. + Hypothesis H_priority_is_transitive: FP_is_transitive higher_eq_priority. + Hypothesis H_priority_is_total: FP_is_total_over_task_set higher_eq_priority ts. + Let job_higher_eq_priority := FP_to_JLDP job_task higher_eq_priority. + + (* Assume that jobs have associated suspension times. *) + Variable job_suspension_duration: job_suspension Job. + + (* Next, consider any valid suspension-aware schedule of this arrival sequence. + (Note: see definition in rt.model.schedule.uni.susp.valid_schedule.v) *) + Variable sched_susp: schedule Job. + Hypothesis H_valid_schedule: + valid_suspension_aware_schedule job_arrival arr_seq job_higher_eq_priority + job_suspension_duration job_cost sched_susp. + + (* Finally, recall the notion of job response-time bound and deadline miss in sched_susp. *) + Let job_response_time_in_sched_susp_bounded_by := + is_response_time_bound_of_job job_arrival job_cost sched_susp. + Let job_misses_no_deadline_in_sched_susp := + job_misses_no_deadline job_arrival job_cost job_deadline sched_susp. + + (** 2) Analysis Setup *) + + (* Recall that we are going to analyze the response time of some job after + applying the reduction to the jitter-aware schedule as defined in + rt.analysis.uni.susp.dynamic.jitter.jitter_schedule. *) + + (* Let j be the job to be analyzed. *) + Variable j: Job. + Hypothesis H_from_arrival_sequence: arrives_in arr_seq j. + Let arr_j := job_arrival j. + + (* Let R_j be any value that we want to prove to be a response-time bound for job j in sched_susp. + Note that in the context of this proof, R_j also delimits the length of the schedules + that we are going to analyze, i.e., we only care about the interval [0, arr_j + R_j). *) + Variable R_j: time. + + (* Next, recall the definition of higher-or-equal-priority tasks (other than j's task). *) + Let other_hep_task tsk_other := + higher_eq_priority tsk_other (job_task j) && (tsk_other != job_task j). + + (* Assume that each job of a higher-or-equal-priority task (other than j's task) is + associated a response-time bound R_hp. + (Note: this follows from analyzing the higher-priority tasks in a previous step.) *) + Variable R_hp: Job -> time. + Hypothesis H_bounded_response_time_of_hp_jobs: + forall j_hp, + arrives_in arr_seq j_hp -> + other_hep_task (job_task j_hp) -> + job_response_time_in_sched_susp_bounded_by j_hp (R_hp j_hp). + + (* Also assume that all the previous jobs of same task as j do not miss any + deadlines in sched_susp. + (Note: this is an induction hypothesis that is easily obtained when analyzing the + sequence of jobs of the same task.) *) + Hypothesis H_no_deadline_misses_for_previous_jobs: + forall j0, + arrives_in arr_seq j0 -> + job_arrival j0 < job_arrival j -> + job_task j0 = job_task j -> + job_misses_no_deadline_in_sched_susp j0. + + (** 3) Instantiation of the Reduction *) + + (* Having stated all the properties of the suspension-aware schedule, now we recall + the construction of the jitter-aware schedule and the corresponding job parameters. *) + Let sched_jitter := reduction.sched_jitter job_arrival job_task arr_seq higher_eq_priority + job_cost job_suspension_duration j R_hp. + Let inflated_job_cost := reduction.inflated_job_cost job_cost job_suspension_duration j. + Let job_jitter := reduction.job_jitter job_arrival job_task higher_eq_priority job_cost j R_hp. + + (* By the end of this file, we are going to show that if job j completes by time (arr_j + R_j) + in sched_jitter, then it also completes by time (arr_j + R_j) in sched_susp. + The argument is based on the fact that the service of higher-or-equal-priority jobs is moved + from the interval [0, arr_j) to the interval [arr_j, arr_j + R_j), when we introduce jitter. + + The proofs are structured in the three final sections. In Section 6-A, we prove that + sched_jitter provides provides less service for higher-or-equal-priority jobs during [0, arr_j). + In Section B, we prove that sched_jitter provides more service for higher-or-equal-priority + jobs during [arr_j, arr_j + R). In Section 6-C, we conclude with the main theorem that compares + the response time of job j in both schedules. *) + + (** 4) Setup for Next Sections *) + + (* For simplicity, let's define some local names... *) + Let actual_job_arrival := actual_arrival job_arrival job_jitter. + Let job_arrived_before := arrived_before job_arrival. + Let job_has_arrived := has_arrived job_arrival. + Let job_has_actually_arrived := jitter_has_passed job_arrival job_jitter. + Let job_completed_in_sched_jitter := completed_by inflated_job_cost sched_jitter. + + (* ...and also recall definitions related to the suspension-aware schedule. *) + Let job_suspended_at := + suspended_at job_arrival job_cost job_suspension_duration sched_susp. + Let job_cumulative_suspension := + cumulative_suspension_during job_arrival job_cost job_suspension_duration sched_susp. + Let job_completed_in_sched_susp := completed_by job_cost sched_susp. + Let backlogged_in_sched_susp := susp.backlogged job_arrival job_cost + job_suspension_duration sched_susp. + + (* Since we'll have to reason about sets of arriving jobs with and without jitter, + let's use simpler names for those as well. *) + Let arrivals := jobs_arrived_between arr_seq. + Let actual_arrivals := actual_arrivals_between job_arrival job_jitter arr_seq. + + (* Because we are dealing with a bounded scheduling window, we also identify all + job arrivals (with and without jitter) in the interval [0, arr_j + R_j). *) + Let arrivals_before_end_of_interval := arrivals 0 (arr_j + R_j). + Let actual_arrivals_before_end_of_interval := actual_arrivals 0 (arr_j + R_j). + + (* Next, by checking jobs priorities, ... *) + Let other_higher_eq_priority_job j_hp := + higher_eq_priority (job_task j_hp) (job_task j) && (j_hp != j). + + (* ...we identify the workload of higher-or-equal-priority jobs (other than j) + that arrive in any interval [t1, t2), in the original schedule,... *) + Definition workload_of_other_hep_jobs_in_sched_susp t1 t2 := + workload_of_jobs job_cost (arrivals t1 t2) other_higher_eq_priority_job. + + (* ... and also the workload of higher-or-equal priority jobs (other than j) + with actual arrival time in the interval [t1, t2), in the jitter-aware schedule. *) + Definition workload_of_other_hep_jobs_in_sched_jitter t1 t2 := + workload_of_jobs inflated_job_cost (actual_arrivals t1 t2) other_higher_eq_priority_job. + + (* Next, we recall the cumulative service of all higher-or-equal-priority + jobs (other than j) that arrived in the interval [0, arr_j + R_j), + received in a given interval [t1, t2) in the original schedule. *) + Definition service_of_other_hep_jobs_in_sched_susp t1 t2 := + service_of_jobs sched_susp arrivals_before_end_of_interval other_higher_eq_priority_job t1 t2. + + (* Similarly, we recall the cumulative service of all higher-or-equal-priority + jobs (other than j) with actual arrival time in the interval [0, arr_j + R_j), + received in a given interval [t1, t2) in the jitter-aware schedule. *) + Definition service_of_other_hep_jobs_in_sched_jitter t1 t2 := + service_of_jobs sched_jitter actual_arrivals_before_end_of_interval + other_higher_eq_priority_job t1 t2. + + (** 5) Auxiliary Lemmas *) + + (* Before moving to the main results, let's prove some auxiliary lemmas about service/workload. *) + Section AuxiliaryLemmas. + + (* First, we prove that if all higher-or-equal-priority jobs have completed by + some time t in the jitter-aware schedule, then the service received + by those jobs up to time t amounts to the requested workload. *) + Section ServiceEqualsWorkload. + + (* Let t be any time no later than (arr_j + R_j)... *) + Variable t: time. + Hypothesis H_before_end_of_interval: t <= arr_j + R_j. + + (* ...in which all higher-or-equal-priority jobs (other than j) have completed. *) + Hypothesis H_workload_has_finished: + forall j_hp, + arrives_in arr_seq j_hp -> + actual_arrival_before job_arrival job_jitter j_hp t -> + other_higher_eq_priority_job j_hp -> + job_completed_in_sched_jitter j_hp t. + + (* Then, the service received by all those jobs in the interval [0, t) amounts to + the workload requested by them in the interval [0, t). *) + Lemma jitter_reduction_service_equals_workload_in_jitter: + service_of_other_hep_jobs_in_sched_jitter 0 t >= + workload_of_other_hep_jobs_in_sched_jitter 0 t. + Proof. + rename H_workload_has_finished into WORK. + rewrite /workload_of_other_hep_jobs_in_sched_jitter + /workload_of_jobs /service_of_other_hep_jobs_in_sched_jitter + /actual_arrivals_before_end_of_interval /actual_arrivals_before. + set act := actual_arrivals. + set t1 := arr_j; set t2 := arr_j + R_j. + set hep := other_higher_eq_priority_job. + set Sj := service_during sched_jitter. + apply leq_trans with (n := \sum_(j0 <- act 0 t | hep j0) Sj j0 0 t); last first. + { + rewrite big_mkcond [X in _ <= X]big_mkcond. + apply leq_sum_sub_uniq; first by apply actual_arrivals_uniq. + intros j0 IN0. + by apply actual_arrivals_between_sub with (t3 := 0) (t4 := t). + } + apply leq_sum_seq; rewrite /act /actual_arrivals; intros j0 IN0 HP0. + apply eq_leq; symmetry; apply/eqP. + have ARRin: arrives_in arr_seq j0. + by apply in_actual_arrivals_between_implies_arrived in IN0. + apply in_actual_arrivals_implies_arrived_before in IN0. + by apply WORK. + Qed. + + End ServiceEqualsWorkload. + + (* Next, we prove a lemma showing that service in the suspension-aware schedule + is bounded by the workload. *) + Section ServiceBoundedByWorkload. + + (* Consider any time t <= arr_j + R_j. *) + Variable t: time. + Hypothesis H_before_end_of_interval: t <= arr_j + R_j. + + (* Then, the service of all jobs with higher-or-equal-priority that arrive in + the interval [0, arr_j + R_j), received in the interval [0, t), is no + larger than the workload of all jobs with higher-or-equal-priority that + are released in the interval [0, t), in the suspension-aware schedule. *) + Lemma jitter_reduction_service_in_sched_susp_le_workload: + service_of_other_hep_jobs_in_sched_susp 0 t <= + workload_of_other_hep_jobs_in_sched_susp 0 t. + Proof. + move: (H_valid_schedule) => [FROMarr [MUSTARRs [COMPs _]]]. + rename H_before_end_of_interval into LTt. + rewrite /workload_of_other_hep_jobs_in_sched_susp /workload_of_jobs + /service_of_other_hep_jobs_in_sched_susp /service_of_jobs + /arrivals_before_end_of_interval /jobs_arrived_before. + set all := arrivals. + set t1 := arr_j; set t2 := arr_j + R_j. + set hep := other_higher_eq_priority_job. + set Ss := service_during sched_susp. + apply leq_trans with (n := \sum_(j0 <- all 0 t | hep j0) Ss j0 0 t); + last by apply leq_sum; intros j0 _; apply cumulative_service_le_job_cost. + rewrite exchange_big [X in _ <= X]exchange_big /=. + apply leq_sum_nat; move => t' /= LT' _. + apply leq_trans with (n := \sum_(j0 <- all 0 t2 | hep j0 && + (scheduled_at sched_susp j0 t')) 1). + { + rewrite big_mkcond [X in _ <= X]big_mkcond. + rewrite /service_at; apply leq_sum; intros j0 _. + by case: hep; case SCHED': scheduled_at. + } + apply leq_trans with (n := \sum_(j0 <- all 0 t | hep j0 && + (scheduled_at sched_susp j0 t')) 1); last first. + { + rewrite big_mkcond [X in _ <= X]big_mkcond. + rewrite /service_at; apply leq_sum; intros j0 _. + by case: hep; case SCHED': scheduled_at. + } + rewrite -big_filter -[X in _ <= X]big_filter. + apply leq_sum_sub_uniq; first by rewrite filter_uniq //; eapply arrivals_uniq; eauto 1. + intros j0; rewrite 2!mem_filter; move => /andP [/andP [HP0 SCHED0] IN0]. + rewrite HP0 SCHED0 /=. + have ARRin0: arrives_in arr_seq j0 by apply FROMarr in SCHED0. + have ARR0: job_arrival j0 <= t' by apply MUSTARRs. + apply arrived_between_implies_in_arrivals with (job_arrival0 := job_arrival); + try (by done). + by apply: (leq_ltn_trans _ LT'). + Qed. + + End ServiceBoundedByWorkload. + + End AuxiliaryLemmas. + + (** ** 6-(A) Less High-Priority Service Before the Arrival of Job j in sched_jitter *) + + (* In this section we prove that before the arrival of job j, the cumulative service + received by other higher-or-equal-priority is no larger in the jitter-aware schedule + than in the suspension-aware schedule. *) + Section LessServiceBeforeArrival. + + (* In fact, we can prove that the service is not larger for each higher-or-equal-priority + job in isolation. *) + Section LessServiceForEachJob. + + (* Let j_hp be any higher-or-equal-priority job (different from j). *) + Variable j_hp: Job. + Hypothesis H_arrives: arrives_in arr_seq j_hp. + Hypothesis H_higher_or_equal_priority: other_higher_eq_priority_job j_hp. + + (* For simplicity, let's define some local names. *) + Let arr_hp := job_arrival j_hp. + Let cost_hp := job_cost j_hp. + Let Rhp := R_hp j_hp. + + (* Using a series of case analyses, we are going to prove that + service sched_jitter j_hp t <= service sched_susp j_hp t. *) + Section Case1. + + (* Case 1. Assume that j_hp is a job from the same task as j. *) + Hypothesis H_same_task: job_task j_hp = job_task j. + + (* Due to constrained deadlines, we can infer that previous jobs of the same task + complete in sched_susp before j arrives. Because these jobs do not have inflated + costs, they cannot receive more service in sched_jitter before the arrival of j. *) + Lemma jitter_reduction_less_job_service_before_interval_case1: + service sched_jitter j_hp arr_j <= service sched_susp j_hp arr_j. + Proof. + move: (H_valid_schedule) => [_ [MUSTARRs [COMPs _]]]. + rename H_no_deadline_misses_for_previous_jobs into NOMISS, + H_constrained_deadlines into DL, H_jobs_from_taskset into FROM, + H_sporadic_arrivals into SPO. + move: H_higher_or_equal_priority => /andP [HP NEQ]. + case (ltnP arr_hp arr_j) => [BEFORE | AFTER]; last first. + { + rewrite /service /service_during. + rewrite (cumulative_service_before_jitter_is_zero job_arrival job_jitter) //; + first by eapply reduction_prop.sched_jitter_jobs_execute_after_jitter; eauto 1. + move: H_same_task => /eqP SAMEtsk; apply negbF in SAMEtsk. + by rewrite /actual_arrival /job_jitter /reduction.job_jitter HP SAMEtsk /= addn0. + } + apply leq_trans with (n := inflated_job_cost j_hp). + { + apply cumulative_service_le_job_cost. + by apply reduction_prop.sched_jitter_completed_jobs_dont_execute. + } + rewrite /inflated_job_cost /reduction.inflated_job_cost. + apply negbTE in NEQ; rewrite NEQ. + apply eq_leq; symmetry; apply/eqP. + apply completion_monotonic with (t := arr_hp + job_deadline j_hp); + [by done | | by apply NOMISS]. + rewrite H_job_deadlines_equal_task_deadlines //. + apply leq_trans with (n := arr_hp + task_period (job_task j_hp)); + first by rewrite leq_add2l DL // FROM. + apply SPO; try (by done); last by apply ltnW. + by intros SAME; subst; rewrite eq_refl in NEQ. + Qed. + + End Case1. + + Section Case2. + + (* Case 2. Assume that j_hp is a job from another task,... *) + Hypothesis H_different_task: job_task j_hp != job_task j. + + (* ...that is released (with jitter) no earlier than the arrival of j. *) + Hypothesis H_released_no_earlier: arr_j <= actual_job_arrival j_hp. + + (* Since j_hp cannot execute in sched_jitter, the claim trivially holds. *) + Lemma jitter_reduction_less_job_service_before_interval_case2: + service sched_jitter j_hp arr_j <= service sched_susp j_hp arr_j. + Proof. + rename H_different_task into DIFFtask. + move: H_higher_or_equal_priority => /andP [HP NEQ]. + rewrite /service /service_during. + by rewrite (cumulative_service_before_jitter_is_zero job_arrival job_jitter) //; + first by eapply reduction_prop.sched_jitter_jobs_execute_after_jitter; eauto 1. + Qed. + + End Case2. + + Section Case3. + + (* Case 3. Assume that j_hp is a job from another task,... *) + Hypothesis H_different_task: job_task j_hp != job_task j. + + (* ...and that (arr_j - arr_hp < arr_j - cost_hp). *) + Hypothesis H_distance_is_smaller: + arr_j - arr_hp < Rhp - cost_hp. + + (* By definition, the jitter of j_hp is set so that it arrives after j. + Since j_hp cannot execute in sched_jitter, the claim follows trivially. *) + Lemma jitter_reduction_less_job_service_before_interval_case3: + service sched_jitter j_hp arr_j <= service sched_susp j_hp arr_j. + Proof. + rename H_higher_or_equal_priority into HEP, H_distance_is_smaller into MIN. + move: (HEP) => /andP [HP NEQ]. + rewrite /service /service_during. + rewrite (cumulative_service_before_jitter_is_zero job_arrival job_jitter) //; + first by eapply reduction_prop.sched_jitter_jobs_execute_after_jitter; eauto 1. + rewrite /actual_arrival /job_jitter /reduction.job_jitter HP H_different_task /=. + rewrite /minn MIN. + case (leqP (job_arrival j) (job_arrival j_hp)) => [AFTER | BEFORE]; + first by apply: (leq_trans AFTER); apply leq_addr. + by rewrite subnKC; last by apply ltnW. + Qed. + + End Case3. + + Section Case4. + + (* Case 4. Assume that j_hp is a job from another task... *) + Hypothesis H_different_task: job_task j_hp != job_task j. + + (* ...and that j_hp completes in sched_susp before j arrives. *) + Hypothesis H_completes_before_j_arrives: arr_hp + Rhp <= arr_j. + + (* Since j_hp completes early in sched_susp and receives maximum service, it cannot + receive more service in sched_jitter before j arrives, thus the claim holds. *) + Lemma jitter_reduction_less_job_service_before_interval_case4: + service sched_jitter j_hp arr_j <= service sched_susp j_hp arr_j. + Proof. + rename H_higher_or_equal_priority into HEP, + H_bounded_response_time_of_hp_jobs into RESPhp. + move: (HEP) => /andP [HP NEQ]. + apply leq_trans with (n := service sched_susp j_hp (arr_hp + Rhp)); + last by apply extend_sum. + apply leq_trans with (n := cost_hp); + last by apply eq_leq; symmetry; apply/eqP; apply RESPhp; last by apply/andP; split. + apply leq_trans with (n := inflated_job_cost j_hp); + last by rewrite /inflated_job_cost /reduction.inflated_job_cost -[_==_]negbK NEQ. + apply cumulative_service_le_job_cost. + by apply reduction_prop.sched_jitter_completed_jobs_dont_execute. + Qed. + + End Case4. + + Section Case5. + + (* Case 5. Assume that j_hp is a job from another task,... *) + Hypothesis H_different_task: job_task j_hp != job_task j. + + (* ...that is released (with jitter) before the arrival of j. *) + Hypothesis H_released_before: actual_job_arrival j_hp < arr_j. + + (* Also assume that (arr_j < arr_hp + Rhp) and (Rhp - costhp <= arr_j - arr_hp). *) + Hypothesis H_j_hp_completes_after_j_arrives: arr_j < arr_hp + Rhp. + Hypothesis H_distance_is_not_smaller: Rhp - cost_hp <= arr_j - arr_hp. + + (* Note that in this case the jitter of job j_hp is set to (Rhp - cost_hp). *) + Remark jitter_reduction_jitter_equals_R_minus_cost: + job_jitter j_hp = Rhp - cost_hp. + Proof. + rename H_higher_or_equal_priority into HEP, H_different_task into DIFFtask. + move: (HEP) => /andP [HP NEQ]. + rewrite /job_jitter /reduction.job_jitter HP DIFFtask /= /minn. + by rewrite ltnNge H_distance_is_not_smaller /=. + Qed. + + (* Since j_hp is released late in sched_jitter with "slack" (Rhp - cost_hp), even + if it executes at full speed, it cannot beat sched_susp in terms of service. + Therefore, the claim also holds. *) + Lemma jitter_reduction_less_job_service_before_interval_case5: + service sched_jitter j_hp arr_j <= service sched_susp j_hp arr_j. + Proof. + move: (H_valid_schedule) => [_ [MUSTARRs [COMPs _]]]. + have JITdef := jitter_reduction_jitter_equals_R_minus_cost. + rename H_higher_or_equal_priority into HEP, + H_bounded_response_time_of_hp_jobs into RESPhp. + move: (HEP) => /andP [HP NEQ]. + set arr_hp' := actual_job_arrival j_hp. + set cost_hp' := inflated_job_cost j_hp. + have JIT: job_jitter j_hp = Rhp - cost_hp by apply JITdef. + apply leq_trans with (n := cost_hp - (arr_hp + Rhp - arr_j)); last first. + { + rewrite /cost_hp leq_subLR. + apply leq_trans with (n := service sched_susp j_hp (arr_hp + Rhp)); + first by apply eq_leq;symmetry;apply/eqP; apply RESPhp; last by apply/andP; split. + rewrite /service /service_during. + apply leq_trans with (n := \sum_(arr_j <= t' < arr_hp+Rhp) + 1 + service sched_susp j_hp arr_j); + last by apply leq_add; first by simpl_sum_const. + rewrite -> big_cat_nat with (n := arr_j); [simpl | by done | by apply ltnW]. + by rewrite addnC; apply leq_add; first by apply leq_sum; intros t0 _; apply leq_b1. + } + { + have AFTERj := reduction_prop.sched_jitter_jobs_execute_after_jitter job_arrival + job_task arr_seq higher_eq_priority job_cost job_suspension_duration. + rewrite /service /service_during. + rewrite (ignore_service_before_jitter job_arrival job_jitter) //; + [| by eapply AFTERj; eauto 1 | by apply ltnW]. + apply leq_trans with (n := \sum_(arr_hp' <= t0 < arr_j) 1); + first by apply leq_sum; intros t0 _; apply leq_b1. + simpl_sum_const; rewrite /arr_hp' /actual_job_arrival /actual_arrival JIT. + have LEcost: cost_hp <= Rhp. + { + apply leq_trans with (n := service sched_susp j_hp (arr_hp + Rhp)); + first by apply eq_leq;symmetry;apply/eqP;apply RESPhp; last by apply/andP;split. + apply leq_trans with (n := \sum_(arr_hp <= t' < arr_hp + Rhp) 1); + last by simpl_sum_const; rewrite addKn. + rewrite /service /service_during. + rewrite (ignore_service_before_arrival job_arrival) //; last by apply leq_addr. + by apply leq_sum; intros t0 _; apply leq_b1. + } + rewrite addnBA; last by done. + rewrite subnBA; last by apply: (leq_trans LEcost); apply leq_addl. + rewrite subnBA; last by apply ltnW. + by apply leq_sub2r; rewrite addnC. + } + Qed. + + End Case5. + + (** **** Main Claim of Section (A) *) + + (* Using the case analysis above, we conclude that before the arrival of job j, + any higher-or-equal-priority job receives no more service in the jitter-aware + schedule than in the suspension-aware schedule. *) + Lemma jitter_reduction_less_job_service_before_interval: + service sched_jitter j_hp arr_j <= service sched_susp j_hp arr_j. + Proof. + have CASE1 := jitter_reduction_less_job_service_before_interval_case1. + have CASE2 := jitter_reduction_less_job_service_before_interval_case2. + have CASE3 := jitter_reduction_less_job_service_before_interval_case3. + have CASE4 := jitter_reduction_less_job_service_before_interval_case4. + have CASE5 := jitter_reduction_less_job_service_before_interval_case5. + have AFTERj := reduction_prop.sched_jitter_jobs_execute_after_jitter job_arrival job_task + arr_seq higher_eq_priority job_cost job_suspension_duration j _ R_hp. + feed AFTERj; try (by done). + case (boolP (job_task j_hp == job_task j)) => [/eqP SAME | DIFFtsk]; first by apply CASE1. + case (leqP arr_j (actual_job_arrival j_hp)) => [LEarr | GTarr]; first by apply CASE2. + case (ltnP (arr_j - arr_hp) (Rhp - cost_hp)) => [LTdiff | GEdiff]; first by apply CASE3. + case (leqP (arr_hp + Rhp) arr_j) => [LEarrj | GTarrj]; first by apply CASE4. + by apply CASE5. + Qed. + + End LessServiceForEachJob. + + (* Since the result about service applies to each individual job, we can prove that + it also holds for the cumulative service of all higher-or-equal-priority jobs. *) + Corollary jitter_reduction_less_service_before_the_interval: + service_of_other_hep_jobs_in_sched_jitter 0 arr_j <= + service_of_other_hep_jobs_in_sched_susp 0 arr_j. + Proof. + rewrite /service_of_other_hep_jobs_in_sched_jitter + /service_of_other_hep_jobs_in_sched_susp + /actual_arrivals_before_end_of_interval /arrivals_before_end_of_interval + /actual_arrivals_before /jobs_arrived_before. + set hep := other_higher_eq_priority_job. + set Ss := service_during sched_susp. + set t2 := arr_j + R_j. + apply leq_trans with (n := \sum_(j_hp <- actual_arrivals 0 t2 | hep j_hp) Ss j_hp 0 arr_j). + { + apply leq_sum_seq; rewrite /actual_arrivals; intros j0 IN0 HEP0. + apply jitter_reduction_less_job_service_before_interval; try (by done). + by apply in_actual_arrivals_between_implies_arrived in IN0. + } + { + rewrite big_mkcond [X in _ <= X]big_mkcond /actual_arrivals /=. + apply leq_sum_sub_uniq; first by apply actual_arrivals_uniq. + by intros j0; rewrite !mem_filter /=; move => /andP [_ IN0]. + } + Qed. + + End LessServiceBeforeArrival. + + (** ** 6-(B) More High-Priority Service After the Arrival of Job j in sched_jitter *) + + (* So far, we have shown that the higher-or-equal-priority jobs receives less service + in the jitter-aware schedule during [0, arr_j). Recall that our final goal is to show + that all this service is actually moved into the interval [arr_j, arr_j + R_j) and + converted into interference for the job j being analyzed. + In this section, we reason about what happens to high-priority jobs after job j arrives. *) + Section MoreServiceAfterArrival. + + (* First, we show that the workload is conserved at every point in the interval + [arr_j, arr_j + R_j). *) + Section Conservation. + + (* Consider any time t >= arr_j (no earlier than the arrival of j). *) + Variable t: time. + Hypothesis H_no_earlier_than_j: t >= arr_j. + + (* Then, we prove that every job that arrives up to time t is also released + in the jitter-aware schedule up to time t. *) + Lemma jitter_reduction_actual_arrival_before_end_of_interval: + forall j_hp, + other_higher_eq_priority_job j_hp -> + job_arrival j_hp <= t -> + actual_job_arrival j_hp <= t. + Proof. + move => j_hp /andP [HP NEQ] ARRhp. + set arr_hp := job_arrival j_hp. + set cost_hp := job_cost j_hp. + rewrite /actual_job_arrival /actual_arrival /job_jitter /reduction.job_jitter HP /=. + case: ifP => [NEQtsk | /eqP SAMEtsk]; last by rewrite addn0. + case (ltnP (arr_j - arr_hp) (R_hp j_hp - cost_hp)) => [MINl | MINr]. + { + rewrite /minn MINl. + case (leqP arr_hp arr_j) => [BEFORE | AFTER]; first by rewrite subnKC //. + by apply leq_trans with (n := arr_hp + (arr_hp - arr_hp)); + [by rewrite leq_add2l leq_sub2r // ltnW | by rewrite subnn addn0]. + } + { + rewrite /minn ltnNge MINr /=. + apply leq_trans with (n := arr_hp + (arr_j - arr_hp)); first by rewrite leq_add2l. + case (leqP arr_hp arr_j) => [BEFORE | AFTER]; first by rewrite subnKC //. + by apply leq_trans with (n := arr_hp + (arr_hp - arr_hp)); + [by rewrite leq_add2l leq_sub2r // ltnW | by rewrite subnn addn0]. + } + Qed. + + (* This implies that the workload is conserved up to time t (inclusive). That is, + in the jitter-aware schedule, there's always as much (high-priority) work to be + executed as in the original schedule. *) + Lemma jitter_reduction_workload_conservation_inside_interval: + workload_of_other_hep_jobs_in_sched_susp 0 t.+1 <= + workload_of_other_hep_jobs_in_sched_jitter 0 t.+1. + Proof. + rewrite /workload_of_other_hep_jobs_in_sched_susp + /workload_of_other_hep_jobs_in_sched_jitter /workload_of_jobs. + set all := arrivals; set act := actual_arrivals. + set hep := other_higher_eq_priority_job. + apply leq_trans with (n := \sum_(j0 <- all 0 t.+1 | hep j0) inflated_job_cost j0). + { + apply leq_sum_seq; rewrite /all /arrivals; move => j0 IN0 /andP [_ NEQ]. + by apply negbTE in NEQ; rewrite /inflated_job_cost /reduction.inflated_job_cost NEQ //. + } + apply leq_trans with (n := \sum_(j0 <- all 0 t.+1 | hep j0 && + (actual_job_arrival j0 < t.+1)) inflated_job_cost j0); last first. + { + rewrite -big_filter -[X in _ <= X]big_filter. + apply leq_sum_sub_uniq; + first by rewrite filter_uniq //; eapply arrivals_uniq; eauto 1. + intros j0; rewrite !mem_filter /=. + by move => /andP [/andP [HP LT] IN]; rewrite HP LT IN. + } + rewrite big_mkcond [X in _ <= X]big_mkcond /=. + apply leq_sum_seq; intros j0 IN0 _. + case HP: hep; simpl; last by done. + case: (leqP _ _); last by done. + intros BUG; exfalso; rewrite leqNgt in BUG; move: BUG => /negP BUG; apply: BUG. + apply jitter_reduction_actual_arrival_before_end_of_interval; try (by done). + by eapply in_arrivals_implies_arrived_before in IN0; eauto 1. + Qed. + + End Conservation. + + (* Since the higher-or-equal-priority jobs receive no more service in the jitter-aware + schedule during [0, arr_j), and because the workload is conserved, we prove next + that those jobs receive no less service in the jitter-aware schedule in the interval + [arr_j, arr_j + R_j). *) + Section MoreServiceInsideTheInterval. + + (* The claim we need to show is presented next. The proof follows by induction on + the interval length d: + forall d, + d <= R_j -> + service_of_other_hep_jobs_in_sched_susp arr_j (arr_j + d) <= + service_of_other_hep_jobs_in_sched_jitter arr_j (arr_j + d). *) + + (* Since the base case of the induction is trivial, we focus on the inductive step. *) + Section InductiveStep. + + (* By strong induction, assume that for a given interval length d < R_j, ... *) + Variable d: time. + Hypothesis H_d_lt_R: d < R_j. + + (* ...the higher-or-equal-priority jobs (other than j) received as much service in + the jitter-aware schedule during [arr_j, arr_j + d) as in the suspension-aware + schedule. *) + Hypothesis H_induction_hypothesis: + service_of_other_hep_jobs_in_sched_susp arr_j (arr_j + d) <= + service_of_other_hep_jobs_in_sched_jitter arr_j (arr_j + d). + + (* Now we must prove that the claim continues to hold for interval [arr_j, arr_j + d + 1): + + service_of_other_hep_jobs_in_sched_susp arr_j (arr_j + d + 1) <= + service_of_other_hep_jobs_in_sched_jitter arr_j (arr_j + d + 1). *) + + (* The proof begins with a case analysis on whether there are pending + higher-or-equal-priority jobs at time (arr_j + d) in the jitter-aware schedule. *) + Section NoPendingJobs. + + (* Case 1. Assume that all higher-or-equal-priority jobs (other than j) whose jitter + has passed by time (arr_j + d) are already complete at time (arr_j + d) + in the jitter-aware schedule. *) + Hypothesis H_all_jobs_completed_in_sched_jitter: + forall j_hp, + arrives_in arr_seq j_hp -> + other_higher_eq_priority_job j_hp -> + job_has_actually_arrived j_hp (arr_j + d) -> + job_completed_in_sched_jitter j_hp (arr_j + d). + + (* First, we show that the service received in the suspension-aware schedule + during [arr_j, arr_j + d + 1) is bounded by the difference between the + requested workload and the service received prior to the arrival of job j. *) + Lemma jitter_reduction_convert_service_to_workload: + service_of_other_hep_jobs_in_sched_susp arr_j (arr_j + d + 1) <= + workload_of_other_hep_jobs_in_sched_susp 0 (arr_j + d + 1) + - service_of_other_hep_jobs_in_sched_susp 0 arr_j. + Proof. + have LEWORKs := jitter_reduction_service_in_sched_susp_le_workload. + rewrite /service_of_other_hep_jobs_in_sched_jitter + /actual_arrivals_before_end_of_interval /actual_arrivals_before. + rewrite /service_of_other_hep_jobs_in_sched_susp /service_of_jobs + /arrivals_before_end_of_interval /jobs_arrived_before. + set all := arrivals; set act := actual_arrivals. + set hep := other_higher_eq_priority_job. + set Ss := service_during sched_susp. + set Sj := service_during sched_jitter. + set SCHs := scheduled_at sched_susp. + set SCHj := scheduled_at sched_jitter. + set SUSP := job_cumulative_suspension. + set Wj := workload_of_other_hep_jobs_in_sched_jitter. + set Ws := workload_of_other_hep_jobs_in_sched_susp. + set t1 := arr_j. + set t2 := arr_j + R_j. + rewrite exchange_big [X in _ <= _ - X]exchange_big /= /service_at. + rewrite -/SCHs -/SCHj addnS addn0. + set TSs := fun a b => \sum_(a <= t0 < b) + \sum_(j_hp <- all 0 t2 | hep j_hp) SCHs j_hp t0. + set TSj := fun a b => \sum_(a <= t0 < b) + \sum_(j_hp <- act 0 t2 | hep j_hp) SCHj j_hp t0. + rewrite -/(TSs t1 (t1 + d).+1) -/(TSs 0 t1). + rewrite subh3 //; last first. + { + apply leq_trans with (n := TSs 0 (t1 + d).+1). + { + apply extend_sum; try (by done). + by apply leq_trans with (n := t1 + d); first by apply leq_addr. + } + rewrite /TSs exchange_big /=. + by apply LEWORKs. + } + rewrite addnC -big_cat_nat //=; + last by apply leq_trans with (n := t1 + d); first by apply leq_addr. + by rewrite exchange_big; apply LEWORKs; rewrite ltn_add2l. + Qed. + + (* Because of workload conservation, we show that the workload in the suspension-aware + schedule is bounded by the workload in the jitter-aware schedule. *) + Lemma jitter_reduction_compare_workload: + workload_of_other_hep_jobs_in_sched_susp 0 (arr_j + d + 1) + - service_of_other_hep_jobs_in_sched_susp 0 arr_j + <= workload_of_other_hep_jobs_in_sched_jitter 0 (arr_j + d + 1) + - service_of_other_hep_jobs_in_sched_susp 0 arr_j. + Proof. + have CONS := jitter_reduction_workload_conservation_inside_interval. + rewrite /service_of_other_hep_jobs_in_sched_jitter + /actual_arrivals_before_end_of_interval /actual_arrivals_before. + rewrite /service_of_other_hep_jobs_in_sched_susp /service_of_jobs + /arrivals_before_end_of_interval /jobs_arrived_before. + set all := arrivals; set act := actual_arrivals. + set hep := other_higher_eq_priority_job. + set Ss := service_during sched_susp. + set Sj := service_during sched_jitter. + set SCHs := scheduled_at sched_susp. + set SCHj := scheduled_at sched_jitter. + set SUSP := job_cumulative_suspension. + set Wj := workload_of_other_hep_jobs_in_sched_jitter. + set Ws := workload_of_other_hep_jobs_in_sched_susp. + set t1 := arr_j. + set t2 := arr_j + R_j. + rewrite exchange_big /= /service_at. + rewrite -/SCHs -/SCHj addnS addn0. + set TSs := fun a b => \sum_(a <= t0 < b) + \sum_(j_hp <- all 0 t2 | hep j_hp) SCHs j_hp t0. + set TSj := fun a b => \sum_(a <= t0 < b) + \sum_(j_hp <- act 0 t2 | hep j_hp) SCHj j_hp t0. + rewrite -/(TSs t1 (t1 + d).+1) -/(TSs 0 t1). + apply leq_trans with (n := Ws 0 (t1 + d).+1 - TSs 0 t1); first by done. + by rewrite leq_sub2r //; apply CONS, leq_addr. + Qed. + + (* Since the higher-or-equal-priority jobs received less service in the + jitter-aware schedule in the interval [0, arr_j), we can compare the + service in the two schedules. *) + Lemma jitter_reduction_compare_service: + workload_of_other_hep_jobs_in_sched_jitter 0 (arr_j + d + 1) + - service_of_other_hep_jobs_in_sched_susp 0 arr_j + <= workload_of_other_hep_jobs_in_sched_jitter 0 (arr_j + d + 1) + - service_of_other_hep_jobs_in_sched_jitter 0 arr_j. + Proof. + have LEserv := jitter_reduction_less_service_before_the_interval. + rewrite /service_of_other_hep_jobs_in_sched_jitter + /actual_arrivals_before_end_of_interval /actual_arrivals_before. + rewrite /service_of_other_hep_jobs_in_sched_susp /service_of_jobs + /arrivals_before_end_of_interval /jobs_arrived_before. + set all := arrivals; set act := actual_arrivals. + set hep := other_higher_eq_priority_job. + set Ss := service_during sched_susp. + set Sj := service_during sched_jitter. + set SCHs := scheduled_at sched_susp. + set SCHj := scheduled_at sched_jitter. + set SUSP := job_cumulative_suspension. + set Wj := workload_of_other_hep_jobs_in_sched_jitter. + set Ws := workload_of_other_hep_jobs_in_sched_susp. + set t1 := arr_j. + set t2 := arr_j + R_j. + rewrite exchange_big [X in _ <= _ - X]exchange_big /= /service_at. + rewrite -/SCHs -/SCHj addnS addn0. + set TSs := fun a b => \sum_(a <= t0 < b) + \sum_(j_hp <- all 0 t2 | hep j_hp) SCHs j_hp t0. + set TSj := fun a b => \sum_(a <= t0 < b) + \sum_(j_hp <- act 0 t2 | hep j_hp) SCHj j_hp t0. + rewrite -/(TSs t1 (t1 + d).+1) -/(TSs 0 t1). + rewrite leq_sub2l //. + rewrite /TSj /TSs exchange_big [X in _ <= X]exchange_big /=. + by apply LEserv. + Qed. + + (* Having inferred that the difference between the workload and service is that + large in the jitter-aware schedule, we can convert this difference back to + service received in the interval [arr_j, arr_j + d + 1] in sched_jitter. *) + Lemma jitter_reduction_convert_workload_to_service: + workload_of_other_hep_jobs_in_sched_jitter 0 (arr_j + d + 1) - + service_of_other_hep_jobs_in_sched_jitter 0 arr_j <= + service_of_other_hep_jobs_in_sched_jitter arr_j (arr_j + d + 1). + Proof. + have EQWORKj := jitter_reduction_service_equals_workload_in_jitter. + rename H_all_jobs_completed_in_sched_jitter into ALL. + rewrite /service_of_other_hep_jobs_in_sched_jitter + /actual_arrivals_before_end_of_interval /actual_arrivals_before. + rewrite /service_of_other_hep_jobs_in_sched_susp /service_of_jobs + /arrivals_before_end_of_interval /jobs_arrived_before. + set all := arrivals; set act := actual_arrivals. + set hep := other_higher_eq_priority_job. + set Ss := service_during sched_susp. + set Sj := service_during sched_jitter. + set SCHs := scheduled_at sched_susp. + set SCHj := scheduled_at sched_jitter. + set SUSP := job_cumulative_suspension. + set Wj := workload_of_other_hep_jobs_in_sched_jitter. + set Ws := workload_of_other_hep_jobs_in_sched_susp. + set t1 := arr_j. + set t2 := arr_j + R_j. + rewrite exchange_big [X in _ <= X]exchange_big /= /service_at. + rewrite -/SCHs -/SCHj addnS addn0. + set TSs := fun a b => \sum_(a <= t0 < b) + \sum_(j_hp <- all 0 t2 | hep j_hp) SCHs j_hp t0. + set TSj := fun a b => \sum_(a <= t0 < b) + \sum_(j_hp <- act 0 t2 | hep j_hp) SCHj j_hp t0. + rewrite -/(TSj t1 (t1 + d).+1) -/(TSj 0 t1). + rewrite leq_subLR -big_cat_nat //=; + last by apply leq_trans with (n := t1 + d); first by apply leq_addr. + rewrite exchange_big /=. + feed (EQWORKj (t1 + d).+1); first by rewrite ltn_add2l. + apply EQWORKj. + intros j0 ARRin0 ARR0 HEP0; specialize (ALL j0 ARRin0 HEP0 ARR0). + by apply completion_monotonic with (t := t1 + d); + first by apply reduction_prop.sched_jitter_completed_jobs_dont_execute. + Qed. + + (* By combining each inequality above in sequence, we complete the induction + step for Case 1. *) + Lemma jitter_reduction_inductive_step_case1: + service_of_other_hep_jobs_in_sched_susp arr_j (arr_j + d + 1) <= + service_of_other_hep_jobs_in_sched_jitter arr_j (arr_j + d + 1). + Proof. + apply: (leq_trans jitter_reduction_convert_service_to_workload). + apply: (leq_trans jitter_reduction_compare_workload). + apply: (leq_trans jitter_reduction_compare_service). + by apply: (leq_trans jitter_reduction_convert_workload_to_service). + Qed. + + End NoPendingJobs. + + Section ThereArePendingJobs. + + (* Case 2. Assume that there are higher-or-equal-priority jobs (other than j) whose + jitter has passed by time (arr_j + d) that are still pending at time + (arr_j + d) in the jitter-aware schedule. *) + Hypothesis H_there_are_pending_jobs_in_sched_jitter: + exists j_hp, + arrives_in arr_seq j_hp /\ + other_higher_eq_priority_job j_hp /\ + job_has_actually_arrived j_hp (arr_j + d) /\ + ~~ job_completed_in_sched_jitter j_hp (arr_j + d). + + (* By the induction hypothesis, the higher-or-equal-priority jobs received + as much service in the jitter-aware schedule as in the suspension-aware + schedule in the interval [arr_j, arr_j + d). Therefore, it only remains + to show that: + + service_of_other_hep_jobs_in_sched_susp (arr_j + d) (arr_j + d + 1) <= + service_of_other_hep_jobs_in_sched_jitter (arr_j + d) (arr_j + d + 1). *) + + (* Because the LHS in the inequality above cannot be larger than 1 (single point), + it suffices to show that there is a higher-or-equal-priority job (different from j) + scheduled at time (arr_j + d) in the jitter-aware schedule. That follows + from two facts: + (a) The jitter-aware schedule is work-conserving and enforces priorities. + Therefore, even if the job j_hp in the hypothesis above is not scheduled, + there will always be a job with higher-or-equal-priority being scheduled. + (b) If there is at least one higher-or-equal-priority pending job, by the + additional property we embedded in the schedule construction, we avoid + scheduling job j (see lemma sched_jitter_does_not_pick_j). *) + Lemma jitter_reduction_inductive_step_case2: + service_of_other_hep_jobs_in_sched_susp arr_j (arr_j + d + 1) <= + service_of_other_hep_jobs_in_sched_jitter arr_j (arr_j + d + 1). + Proof. + have RESPj := reduction_prop.sched_jitter_respects_policy job_arrival job_task ts + arr_seq _ _ higher_eq_priority _ _ _ job_cost job_suspension_duration j _ R_hp. + feed_n 6 RESPj; try (by done). + unfold reduction_prop.jitter_aware.respects_FP_policy in RESPj. + have NOTj := reduction_prop.sched_jitter_does_not_pick_j job_arrival job_task ts arr_seq + _ _ higher_eq_priority _ _ job_cost job_suspension_duration j R_hp. + feed_n 4 NOTj; try (by done). + have WORKj := reduction_prop.sched_jitter_work_conserving job_arrival job_task arr_seq _ + higher_eq_priority job_cost job_suspension_duration j R_hp. + feed WORKj; first by done. + have AFTERj := reduction_prop.sched_jitter_jobs_execute_after_jitter job_arrival job_task + arr_seq higher_eq_priority job_cost job_suspension_duration j _ R_hp. + feed AFTERj; try (by done). + set sched_j := reduction_prop.reduction.sched_jitter _ _ _ _ _ _ _ _ in AFTERj NOTj + WORKj RESPj. + set inf_cost := reduction_prop.reduction.inflated_job_cost _ _ _ in NOTj WORKj + AFTERj RESPj. + set job_jit := reduction_prop.reduction.job_jitter _ _ _ _ _ _ in AFTERj NOTj + WORKj RESPj. + rename H_priority_is_transitive into TRANS, H_induction_hypothesis into IH, + H_there_are_pending_jobs_in_sched_jitter into HASj. + rewrite /service_of_other_hep_jobs_in_sched_jitter + /actual_arrivals_before_end_of_interval /actual_arrivals_before. + rewrite /service_of_other_hep_jobs_in_sched_susp /service_of_jobs + /arrivals_before_end_of_interval /jobs_arrived_before. + set all := arrivals; set act := actual_arrivals. + set hep := other_higher_eq_priority_job. + set Ss := service_during sched_susp. + set Sj := service_during sched_jitter. + set SCHs := scheduled_at sched_susp. + set SCHj := scheduled_at sched_jitter. + set SUSP := job_cumulative_suspension. + set Wj := workload_of_other_hep_jobs_in_sched_jitter. + set Ws := workload_of_other_hep_jobs_in_sched_susp. + set t1 := arr_j. + set t2 := arr_j + R_j. + rewrite exchange_big [X in _ <= X]exchange_big /=. + rewrite addnS /service_at addn0. + rewrite big_nat_recr ?leq_addr // big_nat_recr ?leq_addr //=. + apply leq_add; + first by rewrite exchange_big [X in _ <= X]exchange_big; apply IH. + case (boolP (has (fun j0 => hep j0 && scheduled_at sched_susp j0 (t1 + d)) + (all 0 t2))) => [HASs | ALLs]; last first. + { + rewrite -all_predC in ALLs; move: ALLs => /allP ALLs. + rewrite big_seq_cond big1 //. + move => j0 /andP [IN0 HP0]; apply/eqP; rewrite eqb0. + by specialize (ALLs j0 IN0); rewrite /= HP0 /= in ALLs. + } + move: HASs => /hasP [j0 IN0 /andP [HP0 SCHED0]]. + rewrite big_mkcond (bigD1_seq j0) /=; [| by done | by eapply arrivals_uniq; eauto 1]. + rewrite HP0 SCHED0 big1 //; last first. + { + intros j1 NEQ; case: (hep j1); last by done. + apply/eqP; rewrite eqb0; apply/negP; intro SCHED1. + apply (only_one_job_scheduled _ j1) in SCHED0; last by done. + by rewrite SCHED0 eq_refl in NEQ. + } + rewrite addn0. + move: HASj => [j1 [ARRin1 [HEP1 [IN1 NOTCOMP1]]]]. + move: (HEP1) => /andP [HP1 NEQ1]. + rewrite /act /actual_arrivals in IN1. + case (boolP (scheduled_at sched_jitter j1 (t1+d))) => [SCHED1 | NOTSCHED1]. + { + rewrite (big_rem j1) /=; first by rewrite /hep HEP1 SCHED1. + apply arrived_between_implies_in_actual_arrivals; try (by done). + rewrite /actual_arrival_between /=. + by apply leq_ltn_trans with (n := arr_j + d); last by rewrite ltn_add2l. + } + have BACK1: backlogged job_arrival inflated_job_cost job_jitter sched_jitter j1 (t1+d). + by repeat (apply/andP; split); try (by done). + move: (BACK1) (BACK1) => SCHED2 PRIO2. + apply WORKj in SCHED2; try (by done). + move: SCHED2 => [j2 SCHED2]. + apply RESPj with (j_hp := j2) in PRIO2; try (by done). + have ARRin2: arrives_in arr_seq j2. + { + rewrite /sched_j in SCHED2. + by apply reduction_prop.sched_jitter_jobs_come_from_arrival_sequence with + (sched_susp0 := sched_susp) in SCHED2. + } + have HP2: hep j2. + { + apply/andP; split; first by apply (TRANS (job_task j1)). + apply/eqP; intro SAME; subst j2. + move: BACK1 => /andP [PEND1 _]. + by specialize (NOTj j1 (t1+d) ARRin1 NEQ1 PEND1 HP1); rewrite SCHED2 in NOTj. + } + have IN2: j2 \in act 0 t2. + { + apply arrived_between_implies_in_actual_arrivals; try (by done). + rewrite /actual_arrival_between /=. + apply leq_ltn_trans with (n := t1+d); last by rewrite ltn_add2l. + by apply AFTERj. + } + by rewrite (big_rem j2) //= HP2 SCHED2. + Qed. + + End ThereArePendingJobs. + + End InductiveStep. + + (** **** Main Claim of Section (B) *) + + (* Using the proof by induction above, we conclude that, for any interval length + d <= R_j, the service received by higher-or-equal-priority jobs (other than j) + in the interval [arr_j, arr_j + d) in the jitter-aware schedule is as large as + the corresponding service in the suspension-aware schedule. *) + Lemma jitter_reduction_more_service_inside_the_interval: + forall d, + d <= R_j -> + service_of_other_hep_jobs_in_sched_susp arr_j (arr_j + d) <= + service_of_other_hep_jobs_in_sched_jitter arr_j (arr_j + d). + Proof. + have CASE1 := jitter_reduction_inductive_step_case1. + have CASE2 := jitter_reduction_inductive_step_case2. + set all := arrivals; set act := actual_arrivals. + set hep := other_higher_eq_priority_job. + rename H_priority_is_transitive into TRANS. + induction d. + { + rewrite /service_of_other_hep_jobs_in_sched_susp /service_of_jobs. + by intros _; rewrite exchange_big /= addn0 big_geq. + } + intros LTR; feed (IHd); first by apply ltnW. + rewrite -addn1 addnA. + case (boolP (has (fun j0 => hep j0 && job_has_actually_arrived j0 (arr_j + d) + && ~~ completed_by inflated_job_cost sched_jitter j0 (arr_j + d)) + (act 0 (arr_j + R_j)))) => [HASj | ALLj]; last first. + { + apply CASE1; try (by done). + rewrite -all_predC in ALLj; move: ALLj => /allP ALLj. + intros j0 ARRin0 HEP0 ARR0. + feed (ALLj j0). + { + apply arrived_between_implies_in_actual_arrivals; try (by done). + rewrite /actual_arrival_between /=. + by apply leq_ltn_trans with (n := arr_j + d); last by rewrite ltn_add2l. + } + by rewrite /= /hep HEP0 ARR0 /= negbK in ALLj. + } + { + apply (CASE2 _ LTR IHd). + move: HASj => /hasP [j0 IN0 /andP [/andP [HP0 ARR0] NOTCOMP0]]. + exists j0; repeat (split); try (by done). + rewrite /act /actual_arrivals in IN0. + by apply in_actual_arrivals_between_implies_arrived in IN0. + } + Qed. + + End MoreServiceInsideTheInterval. + + End MoreServiceAfterArrival. + + (** ** 6-(C) Conclusion: Comparing Response Times of Job j *) + + (* In this section, we prove that the generated schedule is "worse" for job j. + More precisely, job j receives no more service in the jitter-aware schedule + than the cumulative service and suspension time in the original schedule. *) + Section JitterAwareScheduleIsWorse. + + (* Recall the definition of job response-time bound in sched_jitter. *) + Let job_response_time_in_sched_jitter_bounded_by := + is_response_time_bound_of_job job_arrival inflated_job_cost sched_jitter. + + (* From this point, we are going to analyze both schedules up to time (arr_j + R_j) and + compare the service received by job j. At the end, we are going to prove that R_j is + also a response-time bound for job j in the suspension-aware schedule sched_susp. *) + + (* First, we show that the service received by job j in the interval [arr_j, arr_j + R_j) + is always bounded by the difference between the interval length R_j and the service + received by the other higher-or-equal-priority jobs in the same interval. *) + Lemma jitter_reduction_service_jitter: + service_during sched_jitter j arr_j (arr_j + R_j) <= + R_j - service_of_other_hep_jobs_in_sched_jitter arr_j (arr_j + R_j). + Proof. + have ARRj := reduction_prop.sched_jitter_jobs_come_from_arrival_sequence job_arrival job_task + arr_seq higher_eq_priority job_cost job_suspension_duration sched_susp _ j _ R_hp. + feed_n 2 ARRj; try done. + have AFTERj := reduction_prop.sched_jitter_jobs_execute_after_jitter job_arrival job_task + arr_seq higher_eq_priority job_cost job_suspension_duration j _ R_hp. + feed AFTERj; try done. + set Sj := service_during sched_jitter j arr_j. + set Shp := service_of_other_hep_jobs_in_sched_jitter arr_j. + rewrite subh3 //; last first. + { + rewrite /Shp /service_of_other_hep_jobs_in_sched_jitter. + rewrite -[X in _ <= X](addKn arr_j). + by apply service_of_jobs_le_delta, actual_arrivals_uniq. + } + apply leq_trans with (n := \sum_(arr_j <= t < arr_j + R_j) 1); + last by simpl_sum_const; rewrite addKn. + rewrite /Sj /Shp /service_of_other_hep_jobs_in_sched_jitter /service_of_jobs + /service_during. + rewrite exchange_big -big_split /=. + apply leq_sum_nat; move => i /andP [GEi LTi] _. + destruct (sched_jitter i) as [j'|] eqn:SCHED; + last by rewrite /service_at /scheduled_at SCHED /= add0n; simpl_sum_const. + case (boolP ((j' == j) || ~~ higher_eq_priority (job_task j') (job_task j))). + { + intros OR; rewrite big1; first by rewrite addn0 leq_b1. + intros j_hp HP; rewrite /other_higher_eq_priority_job in HP. + apply/eqP; rewrite eqb0; apply contraT; rewrite negbK; move => /eqP SCHED'. + rewrite SCHED in SCHED'; case: SCHED' => SAME; subst j_hp. + move: OR => /orP [/eqP EQ | NOTHP]; subst; first by rewrite eq_refl andbF in HP. + by apply negbTE in NOTHP; rewrite NOTHP /= in HP. + } + { + rewrite negb_or negbK; move => /andP [NEQ HP]. + rewrite -[1]add0n; apply leq_add. + { + rewrite leqn0 eqb0; apply/negP; intro SCHED'. + apply only_one_job_scheduled with (j1 := j') in SCHED'; [subst | by apply/eqP]. + by rewrite eq_refl in NEQ. + } + { + move: SCHED => /eqP SCHED. + have IN: arrives_in arr_seq j' by apply ARRj in SCHED. + have ARR: actual_arrival_before job_arrival job_jitter j' (arr_j + R_j). + by apply AFTERj in SCHED; apply: (leq_ltn_trans _ LTi). + rewrite big_mkcond (bigD1_seq j') /=; first last. + - by eapply actual_arrivals_uniq; eauto 1. + - by eapply arrived_between_implies_in_actual_arrivals. + rewrite /other_higher_eq_priority_job HP NEQ /=. + move: SCHED => /eqP SCHED. + rewrite /service_at /scheduled_at SCHED eq_refl. + rewrite big1 //; intros j_other NEQother. + case: ifP => HPother; last by done. + apply/eqP; rewrite eqb0; apply/eqP; case => SAME; subst j_other. + by rewrite eq_refl in NEQother. + } + } + Qed. + + (* Next, since we want to infer that job j is schedulable in the suspension-aware + schedule if it is schedulable in the jitter-aware schedule, we can assume by + contrapositive that job j has not completed by time (arr_j + R_j) in + the suspension-aware schedule. *) + Section JobNotCompleted. + + (* Assume that j has not completed by (arr_j + R_j) in the suspension-aware schedule. *) + Hypothesis H_j_not_completed: + ~~ job_completed_in_sched_susp j (arr_j + R_j). + + (* Then, we can prove that the difference between the interval length and + the service received by the other higher-or-equal-priority jobs during + [arr_j, arr_j + R_j) in the suspension-aware schedule is bounded by + the cumulative service and suspension time of job j. *) + Lemma jitter_reduction_service_susp: + R_j - service_of_other_hep_jobs_in_sched_susp arr_j (arr_j + R_j) <= + service_during sched_susp j arr_j (arr_j + R_j) + + job_cumulative_suspension j arr_j (arr_j + R_j). + Proof. + move: (H_valid_schedule) => [FROM [ARRIVE [COMPs [WORK [PRIO _]]]]]. + rename H_j_not_completed into NOTCOMP. + rewrite leq_subLR -big_split /=. + rewrite /service_of_other_hep_jobs_in_sched_susp /service_of_jobs. + rewrite exchange_big -big_split /=. + apply leq_trans with (n := \sum_(arr_j <= t < arr_j + R_j) 1); + first by simpl_sum_const; rewrite addKn. + apply leq_sum_nat; move => i /andP [GEi LTi] _. + rewrite -/job_suspended_at /service_at. + case: (boolP (job_suspended_at _ _)) => [SUSP | NOTSUSP]; + [by rewrite addnA leq_addl | rewrite addn0]. + case: (boolP (scheduled_at _ _ _)) => [SCHED | NOTSCHED]; + [by rewrite leq_addl | rewrite addn0]. + have BACK: susp.backlogged job_arrival job_cost job_suspension_duration sched_susp j i. + { + repeat (apply/andP; split); try (by done). + apply/negP; intro COMP. + move: NOTCOMP => /negP NOTCOMP; apply: NOTCOMP. + by apply completion_monotonic with (t := i); try (by done); apply ltnW. + } + move: (BACK) => SCHED; apply WORK in SCHED; last by done. + move: SCHED => [j_hp SCHEDhp]. + have NEQ: j_hp != j by apply/eqP => SAME; subst; rewrite SCHEDhp in NOTSCHED. + have HP: higher_eq_priority (job_task j_hp) (job_task j) by apply PRIO with (t := i). + rewrite (big_rem j_hp) /other_higher_eq_priority_job /=; last first. + { + have IN: arrives_in arr_seq j_hp by apply FROM in SCHEDhp. + apply arrived_between_implies_in_arrivals with (job_arrival0 := job_arrival); + try (by done). + by apply: (leq_trans _ LTi); apply ARRIVE. + } + by rewrite HP NEQ SCHEDhp /=. + Qed. + + (* Since the higher-or-equal-priority jobs receive more service during + [arr_j, arr_j + R_j) in the jitter-aware schedule and produce more + interference, it follows that job j cannot receive as much service + in the jitter-aware schedule as in the suspension-aware schedule. *) + Lemma jitter_reduction_less_service_for_job_j: + service_during sched_jitter j arr_j (arr_j + R_j) <= + service_during sched_susp j arr_j (arr_j + R_j) + + job_cumulative_suspension j arr_j (arr_j + R_j). + Proof. + apply: (leq_trans jitter_reduction_service_jitter). + apply: (leq_trans _ jitter_reduction_service_susp). + by apply leq_sub2l, jitter_reduction_more_service_inside_the_interval. + Qed. + + End JobNotCompleted. + + (** **** Main Claim of Section (C) *) + + (* Suppose that the response time of job j is bounded by R_j in sched_jitter. *) + Hypothesis H_response_time_of_j_in_sched_jitter: + job_response_time_in_sched_jitter_bounded_by j R_j. + + (* Then, using the lemmas above, we conclude that the response time of job j in sched_susp + is also bounded by R_j. *) + Corollary jitter_reduction_job_j_completes_no_later: + job_response_time_in_sched_susp_bounded_by j R_j. + Proof. + move: (H_valid_schedule) => [_ [MUSTARRs [COMPs [WORK [PRIO SELF]]]]]. + rename H_response_time_of_j_in_sched_jitter into COMPj. + apply contraT; intro NOTCOMPs. + suff NOTCOMPj: ~~ job_response_time_in_sched_jitter_bounded_by j R_j; + [by rewrite COMPj in NOTCOMPj | clear COMPj]. + have LESS := jitter_reduction_less_service_for_job_j NOTCOMPs. + rewrite neq_ltn; apply/orP; left. + rewrite /inflated_job_cost /reduction.inflated_job_cost eq_refl. + apply leq_ltn_trans with (n := service_during sched_jitter j arr_j (arr_j + R_j)). + { + rewrite /service /service_during. + rewrite (ignore_service_before_arrival job_arrival) ?leq_addr //. + apply jobs_with_jitter_must_arrive_to_execute with (job_jitter0 := job_jitter). + by apply reduction_prop.sched_jitter_jobs_execute_after_jitter. + } + apply: (leq_ltn_trans LESS). + rewrite -addn1 -addnA [_ + 1]addnC addnA; apply leq_add. + { + rewrite addn1; apply contraT; rewrite -leqNgt; intro LE. + exfalso; move: NOTCOMPs => /negP NOTCOMPs; apply: NOTCOMPs. + rewrite /job_response_time_in_sched_susp_bounded_by /is_response_time_bound_of_job. + rewrite /completed_by eqn_leq; apply/andP; split; + first by apply cumulative_service_le_job_cost. + apply: (leq_trans LE). + rewrite /service /service_during. + by rewrite [X in _ <= X](ignore_service_before_arrival job_arrival) ?leq_addr. + } + by apply cumulative_suspension_le_total_suspension. + Qed. + + End JitterAwareScheduleIsWorse. + + End ProvingScheduleProperties. + +End JitterScheduleService. \ No newline at end of file diff --git a/analysis/uni/susp/dynamic/jitter/jitter_taskset_generation.v b/analysis/uni/susp/dynamic/jitter/jitter_taskset_generation.v new file mode 100644 index 000000000..4500ed1c1 --- /dev/null +++ b/analysis/uni/susp/dynamic/jitter/jitter_taskset_generation.v @@ -0,0 +1,61 @@ +Require Import rt.util.all. +Require Import rt.model.priority rt.model.suspension. +Require Import rt.model.arrival.basic.arrival_sequence. +Require Import rt.model.schedule.uni.jitter.schedule. +Require Import rt.analysis.uni.susp.dynamic.jitter.jitter_schedule. +From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq. + +(* In this file we construct a jitter-aware task set that contains the + jitter-aware schedule generated in the reduction. *) +Module JitterTaskSetGeneration. + + Import UniprocessorScheduleWithJitter Suspension Priority + JitterScheduleConstruction. + + Section GeneratingTaskset. + + Context {Task: eqType}. + + (** Analysis Setup *) + + (* Let ts be the original, suspension-aware task set. *) + Variable ts: seq Task. + + (* Assume that tasks have cost and suspension bound. *) + Variable original_task_cost: Task -> time. + Variable task_suspension_bound: Task -> time. + + (* Consider any FP policy that is reflexive, transitive and total, i.e., that + indicates "higher-or-equal priority". *) + Variable higher_eq_priority: FP_policy Task. + + (* Let tsk_i be any task to be analyzed... *) + Variable tsk_i: Task. + + (* ...and recall the definition of higher-or-equal-priority tasks (other than tsk_i). *) + Let other_hep_task tsk_other := higher_eq_priority tsk_other tsk_i && (tsk_other != tsk_i). + + (** Definition of Jitter-Aware Task Parameters *) + + (* We are going to define next a jitter-aware task set that models the jitter-aware + schedule that we constructed in the reduction. *) + + (* First, using the task suspension bounds, we inflate the cost of the analyzed task + in a suspension-oblivious manner. *) + Definition inflated_task_cost (tsk: Task) := + if tsk == tsk_i then + original_task_cost tsk + task_suspension_bound tsk + else original_task_cost tsk. + + (* Next, assuming that higher-priority tasks have a valid response-time bound R,... *) + Variable R: Task -> time. + + (* ...we define the task jitter as follows. *) + Definition task_jitter (tsk: Task) := + if other_hep_task tsk then + R tsk - original_task_cost tsk + else 0. + + End GeneratingTaskset. + +End JitterTaskSetGeneration. \ No newline at end of file diff --git a/analysis/uni/susp/dynamic/jitter/rta_by_reduction.v b/analysis/uni/susp/dynamic/jitter/rta_by_reduction.v new file mode 100644 index 000000000..21ebe100f --- /dev/null +++ b/analysis/uni/susp/dynamic/jitter/rta_by_reduction.v @@ -0,0 +1,202 @@ +Require Import rt.util.all. +Require Import rt.model.priority rt.model.suspension. +Require Import rt.model.arrival.basic.job rt.model.arrival.basic.task + rt.model.arrival.basic.arrival_sequence rt.model.arrival.basic.task_arrival. +Require Import rt.model.arrival.jitter.job. +Require Import rt.model.schedule.uni.schedulability rt.model.schedule.uni.service + rt.model.schedule.uni.workload + rt.model.schedule.uni.response_time. +Require Import rt.model.schedule.uni.jitter.schedule + rt.model.schedule.uni.jitter.platform. +Require Import rt.model.schedule.uni.susp.suspension_intervals + rt.model.schedule.uni.susp.schedule + rt.model.schedule.uni.susp.valid_schedule + rt.model.schedule.uni.susp.platform. +Require Import rt.analysis.uni.susp.dynamic.jitter.jitter_schedule + rt.analysis.uni.susp.dynamic.jitter.jitter_schedule_properties + rt.analysis.uni.susp.dynamic.jitter.jitter_schedule_service + rt.analysis.uni.susp.dynamic.jitter.jitter_taskset_generation. +From mathcomp Require Import ssreflect ssrbool ssrfun eqtype ssrnat seq fintype bigop. + +(* In this file, we determine task response-time bounds in suspension-aware + schedules using a reduction to jitter-aware schedules. *) +Module RTAByReduction. + + Import TaskArrival SporadicTaskset Suspension Priority Workload Service Schedulability + UniprocessorScheduleWithJitter ResponseTime SuspensionIntervals ValidSuspensionAwareSchedule. + + Module susp_aware := PlatformWithSuspensions. + Module reduction := JitterScheduleConstruction. + Module reduction_prop := JitterScheduleProperties. + Module reduction_serv := JitterScheduleService. + Module ts_gen := JitterTaskSetGeneration. + + Section ComparingResponseTimeBounds. + + Context {Task: eqType}. + Variable task_period: Task -> time. + Variable task_deadline: Task -> time. + Context {Job: eqType}. + Variable job_arrival: Job -> time. + Variable job_deadline: Job -> time. + Variable job_task: Job -> Task. + + (** Basic Setup & Setting *) + + (* Let ts be any task set with constrained deadlines. *) + Variable ts: seq Task. + Hypothesis H_constrained_deadlines: + constrained_deadline_model task_period task_deadline ts. + + (* Consider any consistent, duplicate-free job arrival sequence... *) + Variable arr_seq: arrival_sequence Job. + Hypothesis H_arrival_times_are_consistent: + arrival_times_are_consistent job_arrival arr_seq. + Hypothesis H_arrival_sequence_is_a_set: arrival_sequence_is_a_set arr_seq. + + (* ...with sporadic arrivals... *) + Hypothesis H_sporadic_arrivals: + sporadic_task_model task_period job_arrival job_task arr_seq. + + (* ...and in which all jobs come from task set ts. *) + Hypothesis H_jobs_from_taskset: + forall j, arrives_in arr_seq j -> job_task j \in ts. + + (* Since we consider real-time tasks, assume that job deadlines are equal to task deadlines. *) + Hypothesis H_job_deadlines_equal_task_deadlines: + forall j, arrives_in arr_seq j -> job_deadline j = task_deadline (job_task j). + + (* Consider any FP policy that is reflexive, transitive and total. + Note that the policy does not depend on the schedule. *) + Variable higher_eq_priority: FP_policy Task. + Hypothesis H_priority_is_reflexive: FP_is_reflexive higher_eq_priority. + Hypothesis H_priority_is_transitive: FP_is_transitive higher_eq_priority. + Hypothesis H_priority_is_total: FP_is_total_over_task_set higher_eq_priority ts. + Let job_higher_eq_priority := FP_to_JLDP job_task higher_eq_priority. + + (* Assume that jobs and tasks have associated costs... *) + Variable job_cost: Job -> time. + Variable task_cost: Task -> time. + + (* ...and suspension times. *) + Variable job_suspension_duration: job_suspension Job. + Variable task_suspension_bound: Task -> time. + + (* Assume that jobs have positive cost. *) + Hypothesis H_positive_costs: + forall j, arrives_in arr_seq j -> job_cost j > 0. + + (* Next, consider any valid suspension-aware schedule of this arrival sequence. + (Note: see rt.model.schedule.uni.susp.valid_schedule.v for details) *) + Variable sched_susp: schedule Job. + Hypothesis H_valid_schedule: + valid_suspension_aware_schedule job_arrival arr_seq job_higher_eq_priority + job_suspension_duration job_cost sched_susp. + + (** Analysis Setup *) + + (* Now we proceed with the proof. Let tsk be the task to be analyzed. *) + Variable tsk: Task. + Hypothesis H_tsk_in_ts: tsk \in ts. + + (* For simplicity, let's define some local names. *) + Let other_hep_task tsk_other := + higher_eq_priority tsk_other tsk && (tsk_other != tsk). + Let task_response_time_in_sched_susp_bounded_by := + is_response_time_bound_of_task job_arrival job_cost job_task arr_seq sched_susp. + Let job_response_time_in_sched_susp_bounded_by := + is_response_time_bound_of_job job_arrival job_cost sched_susp. + Let completed_in_sched_susp_by := completed_by job_cost sched_susp. + Let job_misses_no_deadline_in_sched_susp := + job_misses_no_deadline job_arrival job_cost job_deadline sched_susp. + + (* Assume that each task is associated a value R... *) + Variable R: Task -> time. + + (* ...that bounds the response-time of all tasks with higher-or-equal priority + (other than tsk) in the suspension-aware schedule sched_susp. *) + Hypothesis H_valid_response_time_bound_of_hp_tasks: + forall tsk_hp, + tsk_hp \in ts -> + other_hep_task tsk_hp -> + task_response_time_in_sched_susp_bounded_by tsk_hp (R tsk_hp). + + (* The existence of those response-time bounds implies that we can compute the actual + response times of the higher-priority jobs in sched_susp. *) + Definition actual_response_time (j_hp: Job) : time := + [pick-min r <= R (job_task j_hp) | + job_response_time_in_sched_susp_bounded_by j_hp r]. + + (* Next, let j be any job of tsk... *) + Variable j: Job. + Hypothesis H_j_arrives: arrives_in arr_seq j. + Hypothesis H_job_of_tsk: job_task j = tsk. + + (* ...and assume that all the previous jobs of same task do not miss any + deadlines in sched_susp. *) + Hypothesis H_no_deadline_misses_for_previous_jobs: + forall j0, + arrives_in arr_seq j0 -> + job_arrival j0 < job_arrival j -> + job_task j0 = job_task j -> + job_misses_no_deadline_in_sched_susp j0. + + (** Instantiation of the Reduction *) + + (* First, recall the parameters of the jitter-aware task set. *) + Let inflated_task_cost := ts_gen.inflated_task_cost task_cost task_suspension_bound tsk. + Let task_jitter := ts_gen.task_jitter task_cost higher_eq_priority tsk. + + (* Then, using the actual response times of higher-priority jobs as parameters, we construct + the jitter-aware schedule from sched_susp. *) + Let sched_jitter := reduction.sched_jitter job_arrival job_task arr_seq higher_eq_priority + job_cost job_suspension_duration j actual_response_time. + + (* Next, recall the corresponding job parameters... *) + Let inflated_job_cost := reduction.inflated_job_cost job_cost job_suspension_duration j. + Let job_jitter := reduction.job_jitter job_arrival job_task higher_eq_priority job_cost j + actual_response_time. + + (* ...and the definition of job response-time bound in sched_jitter. *) + Let job_response_time_in_sched_jitter_bounded_by := + is_response_time_bound_of_job job_arrival inflated_job_cost sched_jitter. + + (** Central Hypothesis *) + + (* Assume that using some jitter-aware RTA, we determine that + (R tsk) is a response-time bound for tsk in sched_jitter. *) + Hypothesis H_valid_response_time_bound_in_sched_jitter: + job_response_time_in_sched_jitter_bounded_by j (R tsk). + + (** **** Main Claim *) + + (* Then, we use the properties of the reduction to prove that (R tsk) is also a + response-time bound for tsk in the original schedule sched_susp. *) + Theorem valid_response_time_bound_in_sched_susp: + job_response_time_in_sched_susp_bounded_by j (R tsk). + Proof. + rename H_priority_is_reflexive into REFL, H_priority_is_transitive into TRANS, + H_priority_is_total into TOT, H_jobs_from_taskset into FROM, + H_valid_response_time_bound_of_hp_tasks into RESPhp, + H_valid_response_time_bound_in_sched_jitter into RESPj. + rewrite -H_job_of_tsk /job_response_time_in_sched_susp_bounded_by. + apply reduction_serv.jitter_reduction_job_j_completes_no_later with (job_task0 := job_task) + (ts0 := ts) (arr_seq0 := arr_seq) (higher_eq_priority0 := higher_eq_priority) + (task_period0 := task_period) (task_deadline0 := task_deadline) (job_deadline0 := job_deadline) + (job_suspension_duration0 := job_suspension_duration) (R_hp := actual_response_time); + try (by done). + { + intros j_hp ARRhp OTHERhp. + rewrite /actual_response_time. + apply pick_min_holds; last by intros r RESP _. + exists (Ordinal (ltnSn (R (job_task j_hp)))). + by apply RESPhp; try (by done); [by apply FROM | rewrite /other_hep_task -H_job_of_tsk]. + } + { + by rewrite /is_response_time_bound_of_job H_job_of_tsk; apply RESPj. + } + Qed. + + End ComparingResponseTimeBounds. + +End RTAByReduction. \ No newline at end of file diff --git a/analysis/uni/susp/dynamic/jitter/taskset_membership.v b/analysis/uni/susp/dynamic/jitter/taskset_membership.v new file mode 100644 index 000000000..40a2fb460 --- /dev/null +++ b/analysis/uni/susp/dynamic/jitter/taskset_membership.v @@ -0,0 +1,350 @@ +Require Import rt.util.all. +Require Import rt.model.priority rt.model.suspension. +Require Import rt.model.arrival.basic.task rt.model.arrival.basic.job + rt.model.arrival.basic.arrival_sequence. +Require Import rt.model.arrival.jitter.job. +Require Import rt.model.schedule.uni.response_time. +Require Import rt.model.schedule.uni.susp.schedule + rt.model.schedule.uni.susp.platform + rt.model.schedule.uni.susp.valid_schedule. +Require Import rt.analysis.uni.susp.dynamic.jitter.jitter_schedule + rt.analysis.uni.susp.dynamic.jitter.jitter_taskset_generation. +Require Import rt.analysis.uni.susp.sustainability.singlecost.reduction + rt.analysis.uni.susp.sustainability.singlecost.reduction_properties. +From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop. + +(* In this file we prove that the jitter-aware schedule sched_jitter used in the + reduction is an instance of the jitter-aware task set that we analyze. *) +Module TaskSetMembership. + + Import SporadicTaskset Suspension Priority ValidSuspensionAwareSchedule + ScheduleWithSuspensions ResponseTime PlatformWithSuspensions. + + Module reduction := JitterScheduleConstruction. + Module ts_gen := JitterTaskSetGeneration. + Module sust := SustainabilitySingleCost. + Module sust_prop := SustainabilitySingleCostProperties. + Module valid_sched := ValidSuspensionAwareSchedule. + Module job_susp := Job. + Module job_jitter := JobWithJitter. + + Section ProvingMembership. + + Context {Task: eqType}. + Variable task_period: Task -> time. + Variable task_deadline: Task -> time. + Context {Job: eqType}. + Variable job_arrival: Job -> time. + Variable job_deadline: Job -> time. + Variable job_task: Job -> Task. + + (** Basic Setup & Setting*) + + (* Let ts be any suspension-aware task set. *) + Variable ts: seq Task. + + (* Consider any job arrival sequence with consistent, duplicate-free arrivals... *) + Variable arr_seq: arrival_sequence Job. + Hypothesis H_arrival_times_are_consistent: + arrival_times_are_consistent job_arrival arr_seq. + Hypothesis H_arrival_sequence_is_a_set: arrival_sequence_is_a_set arr_seq. + + (* ...where jobs come from the task set. *) + Hypothesis H_jobs_come_from_taskset: + forall j, arrives_in arr_seq j -> job_task j \in ts. + + (* ...and the associated job and task costs. *) + Variable job_cost: Job -> time. + Variable task_cost: Task -> time. + + (* Assume that jobs and tasks have associated suspension times. *) + Variable job_suspension_duration: job_suspension Job. + Variable task_suspension_bound: Task -> time. + + (* Assume any FP policy that is reflexive, transitive and total... *) + Variable higher_eq_priority: FP_policy Task. + Hypothesis H_priority_is_reflexive: FP_is_reflexive higher_eq_priority. + Hypothesis H_priority_is_transitive: FP_is_transitive higher_eq_priority. + Hypothesis H_priority_is_total: FP_is_total_over_task_set higher_eq_priority ts. + Let job_higher_eq_priority := FP_to_JLDP job_task higher_eq_priority. + + (* Recall the definition of a valid suspension-aware schedule. *) + Let is_valid_suspension_aware_schedule := + valid_suspension_aware_schedule job_arrival arr_seq job_higher_eq_priority + job_suspension_duration. + + (* Next, consider any valid suspension-aware schedule of this arrival sequence. + (Note: see rt.model.schedule.uni.susp.valid_schedule.v for details) *) + Variable sched_susp: schedule Job. + Hypothesis H_valid_schedule: + valid_suspension_aware_schedule job_arrival arr_seq job_higher_eq_priority + job_suspension_duration job_cost sched_susp. + + (* Recall the definition of response-time bounds in sched_susp. *) + Let task_response_time_in_sched_susp_bounded_by := + is_response_time_bound_of_task job_arrival job_cost job_task arr_seq sched_susp. + Let job_response_time_in_sched_susp_bounded_by := + is_response_time_bound_of_job job_arrival job_cost sched_susp. + + (** Analysis Setup *) + + (* Let tsk_i be any task to be analyzed... *) + Variable tsk_i: Task. + Hypothesis H_tsk_in_ts: tsk_i \in ts. + + (* ...and let j be any job of this task. *) + Variable j: Job. + Hypothesis H_j_arrives: arrives_in arr_seq j. + Hypothesis H_job_of_tsk_i: job_task j = tsk_i. + + (* Also recall the definition of task response-time bound with any job cost and schedule... *) + Let is_task_response_time_bound_with job_cost sched := + is_response_time_bound_of_task job_arrival job_cost job_task arr_seq sched. + + (* ...and the definition of higher-or-equal-priority tasks (other than tsk_i). *) + Let other_hep_task tsk_other := higher_eq_priority tsk_other tsk_i && (tsk_other != tsk_i). + + (* Next, assume that for each of those higher-or-equal-priority tasks (other than tsk_i), + we know a response-time bound R that is valid across all suspension-aware schedules of ts. *) + Variable R: Task -> time. + Hypothesis H_valid_response_time_bound_of_hp_tasks_in_all_schedules: + forall job_cost sched, + is_valid_suspension_aware_schedule job_cost sched -> + forall tsk_hp, + tsk_hp \in ts -> + other_hep_task tsk_hp -> + is_task_response_time_bound_with job_cost sched tsk_hp (R tsk_hp). + + (* The existence of response-time bounds across all schedules implies that we can find + actual response times of the higher-priority jobs in sched_susp... *) + Definition actual_response_time (j_hp: Job) : time := + [pick-min r <= R (job_task j_hp) | + job_response_time_in_sched_susp_bounded_by j_hp r]. + + (* ...and show that they are valid... *) + Corollary actual_response_time_is_valid: + forall j_hp, + arrives_in arr_seq j_hp -> + other_hep_task (job_task j_hp) -> + job_response_time_in_sched_susp_bounded_by j_hp (actual_response_time j_hp). + Proof. + rename H_valid_response_time_bound_of_hp_tasks_in_all_schedules into RESPhp, + H_jobs_come_from_taskset into FROM. + intros j_hp ARRhp HP. + rewrite /actual_response_time. + apply pick_min_holds; last by done. + exists (Ordinal (ltnSn (R (job_task j_hp)))). simpl. + by apply RESPhp; try (by done); first by apply FROM. + Qed. + + (* ...and tight. *) + Corollary actual_response_time_is_minimum: + forall j_hp r_hp, + arrives_in arr_seq j_hp -> + other_hep_task (job_task j_hp) -> + job_response_time_in_sched_susp_bounded_by j_hp r_hp -> + actual_response_time j_hp <= r_hp. + Proof. + rename H_valid_response_time_bound_of_hp_tasks_in_all_schedules into RESPhp, + H_jobs_come_from_taskset into FROM. + intros j_hp r_hp ARRhp HP RESP. + case (leqP r_hp (R (job_task j_hp))) => [LT | GE]. + { + rewrite /actual_response_time. + apply pick_min_holds; + last by intros x RESPx _ MINx; rewrite -ltnS in LT; apply (MINx (Ordinal LT)). + exists (Ordinal (ltnSn (R (job_task j_hp)))). + by apply RESPhp; try (by done); first by apply FROM. + } + { + apply leq_trans with (n := (R (job_task j_hp))); last by apply ltnW. + rewrite -ltnS /actual_response_time. + apply pick_min_ltn. + exists (Ordinal (ltnSn (R (job_task j_hp)))). simpl. + by apply RESPhp; try (by done); first by apply FROM. + } + Qed. + + (** Instantiation of the Reduction *) + + (* Using the actual response time of higher-priority jobs as a parameter, we construct + the jitter-aware schedule from sched_susp. *) + Let inflated_job_cost := reduction.inflated_job_cost job_cost job_suspension_duration j. + Let job_jitter := reduction.job_jitter job_arrival job_task higher_eq_priority job_cost j + actual_response_time. + + (* We also recall the parameters of the generated jitter-aware task set. *) + Let inflated_task_cost := ts_gen.inflated_task_cost task_cost task_suspension_bound tsk_i. + Let task_jitter := ts_gen.task_jitter task_cost higher_eq_priority tsk_i R. + + (** Proof of Task Set Membership *) + + (* Now we proceed with the main claim. We are going to show that the job parameters in the + jitter-aware schedule sched_susp are an instance of the task set parameters. *) + + (* Assume that the original costs are positive... *) + Hypothesis H_positive_costs: + forall j, arrives_in arr_seq j -> job_cost j > 0. + + (* ...and no larger than the task costs. *) + Hypothesis H_job_cost_le_task_cost: + forall j, + arrives_in arr_seq j -> + job_cost j <= task_cost (job_task j). + + (* Also assume that job suspension times are bounded by the task suspension bounds. *) + Hypothesis H_dynamic_suspensions: + dynamic_suspension_model job_cost job_task job_suspension_duration task_suspension_bound. + + (* We begin by showing that the inflated job costs remain positive... *) + Section JobCostPositive. + + Lemma ts_membership_inflated_job_cost_positive: + forall j, arrives_in arr_seq j -> inflated_job_cost j > 0. + Proof. + intros j0 ARR0. + apply leq_trans with (n := job_cost j0); first by apply H_positive_costs. + rewrite /inflated_job_cost /reduction.inflated_job_cost. + by case: ifP => _; first by apply leq_addr. + Qed. + + End JobCostPositive. + + (* ...and no larger than the inflated task costs. *) + Section JobCostBoundedByTaskCost. + + Lemma ts_membership_inflated_job_cost_le_inflated_task_cost: + forall j, + arrives_in arr_seq j -> + inflated_job_cost j <= inflated_task_cost (job_task j). + Proof. + intros j' ARR'. + rewrite /inflated_job_cost /inflated_task_cost /reduction.inflated_job_cost + /ts_gen.inflated_task_cost. + case: ifP => [/eqP SAME | NEQ]; subst. + { + rewrite eq_refl; apply leq_add; last by apply H_dynamic_suspensions. + by apply H_job_cost_le_task_cost. + } + case: ifP => [SAMEtsk | DIFFtsk]; last by apply H_job_cost_le_task_cost. + apply leq_trans with (n := task_cost (job_task j')); last by apply leq_addr. + by apply H_job_cost_le_task_cost. + Qed. + + End JobCostBoundedByTaskCost. + + (* Finally, we show that the job jitter in sched_susp is upper-bounded by the task jitter. + This only concerns higher-priority jobs, which are assigned non-zero jitter to + compensate suspension times. *) + Section JobJitterBoundedByTaskJitter. + + (* Let any_j be any job from the arrival sequence. *) + Variable any_j: Job. + Hypothesis H_any_j_arrives: arrives_in arr_seq any_j. + + (* Since most parts of the proof are trivial, we focus on the more complicated case + of higher-priority jobs. *) + Section JitterOfHigherPriorityJobs. + + (* Suppose that any_j is a higher-or-equal-priority job from some task other than tsk_i. *) + Hypothesis H_higher_priority: higher_eq_priority (job_task any_j) tsk_i. + Hypothesis H_different_task: job_task any_j != tsk_i. + + (* Recall that we want to prove that job_jitter any_j <= task_jitter (job_task any_j). + By definition, this amounts to showing that: + actual_response_time any_j - job_cost any_j <= + R (job_task any_j) - task_cost (job_task any_j). *) + + (* The proof follows by a sustainability argument based on the following reduction. *) + + (* By inflating the cost of any_j to its worst-case execution time...*) + Let higher_cost_wcet j' := + if j' == any_j then task_cost (job_task any_j) else job_cost j'. + + (* ...we construct a new suspension-aware schedule sched_susp_highercost where the response + time of any_j is as large as in the original schedule sched_susp. + (For more details, see analysis/uni/susp/sustainability/cost. ) *) + Let sched_susp_highercost := + sust.sched_susp_highercost job_arrival arr_seq job_higher_eq_priority + sched_susp job_suspension_duration higher_cost_wcet. + + (* Next, recall the definition of task response-time bounds in sched_susp_highercost. *) + Let task_response_time_in_sched_susp_highercost_bounded_by := + is_response_time_bound_of_task job_arrival higher_cost_wcet job_task arr_seq + sched_susp_highercost. + + (* Since the response-time bounds R are valid across all suspension-aware schedules + of task set ts, they are also valid in sched_susp_higher_cost. *) + Remark response_time_bound_in_sched_susp_highercost: + forall tsk_hp, + tsk_hp \in ts -> + other_hep_task tsk_hp -> + task_response_time_in_sched_susp_highercost_bounded_by tsk_hp (R tsk_hp). + Proof. + rename H_valid_response_time_bound_of_hp_tasks_in_all_schedules into RESPhp, + H_jobs_come_from_taskset into FROM, H_valid_schedule into VALID. + split_conj VALID. + feed (RESPhp higher_cost_wcet sched_susp_highercost). + { + repeat split. + - by apply sust_prop.sched_susp_highercost_jobs_come_from_arrival_sequence. + - by apply sust_prop.sched_susp_highercost_jobs_must_arrive_to_execute. + - by apply sust_prop.sched_susp_highercost_completed_jobs_dont_execute. + - by apply sust_prop.sched_susp_highercost_work_conserving. + - apply sust_prop.sched_susp_highercost_respects_policy; try (by done). + -- by intros t j1 j2 j3; apply H_priority_is_transitive. + -- by intros j1 j2 t ARR1 ARR2; apply/orP; apply H_priority_is_total; apply FROM. + - by apply sust_prop.sched_susp_highercost_respects_self_suspensions. + } + by intros tsk_hp IN Ohp; by apply RESPhp. + Qed. + + (* Finally, by comparing the two schedules, we prove that the difference between the + actual response time and job cost is bounded by the difference between the + response-time bound and the task cost. *) + Lemma ts_membership_difference_in_response_times: + actual_response_time any_j - job_cost any_j <= + R (job_task any_j) - task_cost (job_task any_j). + Proof. + have VALIDr := actual_response_time_is_valid. + have MINr := actual_response_time_is_minimum. + have RESPhp := response_time_bound_in_sched_susp_highercost. + rename H_jobs_come_from_taskset into FROM, H_valid_schedule into VALIDSCHED. + split_conj VALIDSCHED. + apply leq_trans with (n := R (job_task any_j) - higher_cost_wcet any_j); + last by apply leq_sub2l; rewrite /higher_cost_wcet eq_refl. + apply sust_prop.sched_susp_highercost_incurs_more_interference with + (job_arrival0 := job_arrival) (arr_seq0 := arr_seq) (sched_susp0 := sched_susp) + (higher_eq_priority0:=job_higher_eq_priority) + (job_suspension_duration0 := job_suspension_duration); try (by done). + - by intros t j1; apply H_priority_is_reflexive. + - by rewrite /higher_cost_wcet eq_refl; apply H_job_cost_le_task_cost. + - by move => j' NEQ; apply negbTE in NEQ; rewrite /higher_cost_wcet NEQ. + - by apply H_positive_costs. + - by apply VALIDr; try (by done); apply/andP; split. + - by intros r' RESP; apply MINr; try (by done); first by apply/andP; split. + - by apply RESPhp; try (by done); [apply FROM | apply/andP; split]. + Qed. + + End JitterOfHigherPriorityJobs. + + (* Using the lemmas above, we conclude that the job jitter parameter is + upper-bounded by the task jitter for any job in the arrival sequence. *) + Lemma ts_membership_job_jitter_le_task_jitter: + job_jitter any_j <= task_jitter (job_task any_j). + Proof. + have DIFF := ts_membership_difference_in_response_times. + rewrite /job_jitter /task_jitter /reduction.job_jitter /ts_gen.task_jitter H_job_of_tsk_i. + case: ifP => // /andP [HP' NEQ]. + rewrite /minn; case: ifP => [LTdist | GEdist]; last by apply DIFF. + case (leqP (job_arrival j) (job_arrival any_j)) => [AFTER | BEFORE]; + first by apply leq_trans with (n := 0); first rewrite leqn0 subn_eq0. + apply leq_trans with (n := actual_response_time any_j - job_cost any_j); first by apply ltnW. + by apply DIFF. + Qed. + + End JobJitterBoundedByTaskJitter. + + End ProvingMembership. + +End TaskSetMembership. \ No newline at end of file diff --git a/analysis/uni/susp/dynamic/jitter/taskset_rta.v b/analysis/uni/susp/dynamic/jitter/taskset_rta.v new file mode 100644 index 000000000..400d139f5 --- /dev/null +++ b/analysis/uni/susp/dynamic/jitter/taskset_rta.v @@ -0,0 +1,233 @@ +Require Import rt.util.all. +Require Import rt.model.priority rt.model.suspension. +Require Import rt.model.arrival.basic.task rt.model.arrival.basic.job + rt.model.arrival.basic.task_arrival rt.model.arrival.basic.arrival_sequence. +Require Import rt.model.arrival.jitter.job. +Require Import rt.model.schedule.uni.response_time. +Require Import rt.model.schedule.uni.susp.schedule rt.model.schedule.uni.susp.platform + rt.model.schedule.uni.susp.valid_schedule. +Require Import rt.model.schedule.uni.jitter.valid_schedule. +Require Import rt.analysis.uni.susp.dynamic.jitter.rta_by_reduction + rt.analysis.uni.susp.dynamic.jitter.jitter_taskset_generation + rt.analysis.uni.susp.dynamic.jitter.taskset_membership. +From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop. + +(* In this file we use the reduction to jitter-aware schedule to analyze + individual tasks using RTA. *) +Module TaskSetRTA. + + Import SporadicTaskset Suspension Priority ValidSuspensionAwareSchedule + ScheduleWithSuspensions ResponseTime PlatformWithSuspensions + TaskArrival ValidJitterAwareSchedule RTAByReduction TaskSetMembership. + + Module ts_gen := JitterTaskSetGeneration. + Module job_susp := Job. + Module job_jitter := JobWithJitter. + + (* In this section, we are going to assume we have obtained response-time + bounds for high-priority tasks and then use the reduction to infer a + response-time bound for a particular task. *) + Section PerTaskAnalysis. + + Context {Task: eqType}. + Variable task_cost: Task -> time. + Variable task_period: Task -> time. + Variable task_deadline: Task -> time. + Context {Job: eqType}. + Variable job_arrival: Job -> time. + Variable job_deadline: Job -> time. + Variable job_task: Job -> Task. + + (** Basic Setup & Setting*) + + (* Let ts be any task set with constrained deadlines. *) + Variable ts: seq Task. + Hypothesis H_constrained_deadlines: + constrained_deadline_model task_period task_deadline ts. + + (* Consider any consistent, duplicate-free job arrival sequence... *) + Variable arr_seq: arrival_sequence Job. + Hypothesis H_arrival_times_are_consistent: + arrival_times_are_consistent job_arrival arr_seq. + Hypothesis H_arrival_sequence_is_a_set: arrival_sequence_is_a_set arr_seq. + + (* ...with sporadic arrivals... *) + Hypothesis H_sporadic_arrivals: + sporadic_task_model task_period job_arrival job_task arr_seq. + + (* ...and in which all jobs come from the task set. *) + Hypothesis H_jobs_come_from_taskset: + forall j, arrives_in arr_seq j -> job_task j \in ts. + + (* Assume that job deadlines equal task deadlines. *) + Hypothesis H_job_deadline_eq_task_deadline: + forall j, arrives_in arr_seq j -> job_deadline j = task_deadline (job_task j). + + (* Consider any job suspension times and task suspension bound. *) + Variable job_suspension_duration: job_suspension Job. + Variable task_suspension_bound: Task -> time. + + (* Assume any FP policy that is reflexive, transitive and total. *) + Variable higher_eq_priority: FP_policy Task. + Hypothesis H_priority_is_reflexive: FP_is_reflexive higher_eq_priority. + Hypothesis H_priority_is_transitive: FP_is_transitive higher_eq_priority. + Hypothesis H_priority_is_total: FP_is_total_over_task_set higher_eq_priority ts. + Let job_higher_eq_priority := FP_to_JLDP job_task higher_eq_priority. + + (* Recall the definition of a valid suspension-aware and jitter-aware schedules. *) + Let is_valid_suspension_aware_schedule := + valid_suspension_aware_schedule job_arrival arr_seq job_higher_eq_priority + job_suspension_duration. + Let is_valid_jitter_aware_schedule := + valid_jitter_aware_schedule job_arrival arr_seq job_higher_eq_priority. + + (* Also recall the definition of task response-time bound for given job cost and schedule. *) + Let is_task_response_time_bound_with job_cost sched := + is_response_time_bound_of_task job_arrival job_cost job_task arr_seq sched. + + (** Analysis Setup *) + + (* Let tsk_i be any task to be analyzed. *) + Variable tsk_i: Task. + Hypothesis H_tsk_in_ts: tsk_i \in ts. + + (* Recall the definition of higher-or-equal-priority tasks (other than tsk_i). *) + Let other_hep_task tsk_other := higher_eq_priority tsk_other tsk_i && (tsk_other != tsk_i). + + (* Next, assume that for each of those higher-or-equal-priority tasks (other than tsk_i), + we know a response-time bound R that is valid across all suspension-aware schedules of ts. *) + Variable R: Task -> time. + Hypothesis H_valid_response_time_bound_of_hp_tasks_in_all_schedules: + forall job_cost sched, + is_valid_suspension_aware_schedule job_cost sched -> + forall tsk_hp, + tsk_hp \in ts -> + other_hep_task tsk_hp -> + is_task_response_time_bound_with job_cost sched tsk_hp (R tsk_hp). + + (* Since this analysis is for constrained deadlines, also assume that + (R tsk_i) is no larger than the deadline of tsk_i. *) + Hypothesis H_R_le_deadline: R tsk_i <= task_deadline tsk_i. + + (** Recall: Properties of Valid Jitter-Aware Jobs *) + + (* Recall that a valid jitter-aware schedule must have positive job costs,... *) + Let job_cost_positive job_cost := + forall j, arrives_in arr_seq j -> job_cost j > 0. + + (* ...job costs that are no larger than the task costs... *) + Let job_cost_le_task_cost job_cost task_cost := + forall j, + arrives_in arr_seq j -> + job_cost j <= task_cost (job_task j). + + (* ...and job jitter no larger than the task jitter. *) + Let job_jitter_le_task_jitter job_jitter task_jitter := + forall j, + arrives_in arr_seq j -> + job_jitter j <= task_jitter (job_task j). + + (* This is summarized in the following predicate. *) + Definition valid_jobs_with_jitter job_cost job_jitter task_cost task_jitter := + job_cost_positive job_cost /\ + job_cost_le_task_cost job_cost task_cost /\ + job_jitter_le_task_jitter job_jitter task_jitter. + + (** Conclusion: Response-time Bound for Task tsk_i *) + + (* Recall the parameters of the generated jitter-aware task set. *) + Let inflated_task_cost := ts_gen.inflated_task_cost task_cost task_suspension_bound tsk_i. + Let task_jitter := ts_gen.task_jitter task_cost higher_eq_priority tsk_i R. + + (* By using a jitter-aware RTA, assume that we proved that (R tsk_i) is a valid + response-time bound for task tsk_i in any jitter-aware schedule of the same + arrival sequence. *) + Hypothesis H_valid_response_time_bound_of_tsk_i: + forall job_cost job_jitter sched, + valid_jobs_with_jitter job_cost job_jitter inflated_task_cost task_jitter -> + is_valid_jitter_aware_schedule job_cost job_jitter sched -> + is_task_response_time_bound_with job_cost sched tsk_i (R tsk_i). + + (* Next, consider any job cost function... *) + Variable job_cost: Job -> time. + + (* ...and let sched_susp be any valid suspension-aware schedule with those job costs. *) + Variable sched_susp: schedule Job. + Hypothesis H_valid_schedule: is_valid_suspension_aware_schedule job_cost sched_susp. + + (* Assume that the job costs are positive... *) + Hypothesis H_job_cost_positive: + forall j, arrives_in arr_seq j -> job_cost j > 0. + + (* ...and no larger than the task costs. *) + Hypothesis H_job_cost_le_task_cost: + forall j, + arrives_in arr_seq j -> + job_cost j <= task_cost (job_task j). + + (* Also assume that job suspension times are bounded by the task suspension bounds. *) + Hypothesis H_dynamic_suspensions: + dynamic_suspension_model job_cost job_task job_suspension_duration task_suspension_bound. + + (* Using the reduction to a jitter-aware schedule, we conclude that (R tsk_i) must + also be a response-time bound for task tsk_i in the suspension-aware schedule. *) + Theorem valid_response_time_bound_of_tsk_i: + is_task_response_time_bound_with job_cost sched_susp tsk_i (R tsk_i). + Proof. + unfold is_task_response_time_bound_with, + is_response_time_bound_of_task, is_response_time_bound_of_job in *. + rename H_valid_response_time_bound_of_hp_tasks_in_all_schedules into RESPhp, + H_valid_response_time_bound_of_tsk_i into RESPi. + + move: (H_valid_schedule) => [_ [_ [COMP _]]]. + intros j ARRj JOBtsk. + + (* First, rewrite the claim in terms of the *absolute* response-time bound (arrival + R) *) + remember (job_arrival j + R tsk_i) as ctime. + + (* Now, we apply strong induction on the absolute response-time bound. *) + generalize dependent j. + induction ctime as [ctime IH] using strong_ind. + + intros j ARRj JOBtsk EQc; subst ctime. + + (* First, let's simplify the induction hypothesis. *) + have BEFOREok: + forall j0, + arrives_in arr_seq j0 -> + job_task j0 = tsk_i -> + job_arrival j0 < job_arrival j -> + completed_by job_cost sched_susp j0 (job_arrival j0 + R tsk_i); + [by ins; apply IH; try (by done); rewrite ltn_add2r | clear IH]. + set actual_response_time := + actual_response_time job_arrival job_task job_cost sched_susp R. + set inflated_job_cost := + reduction.inflated_job_cost job_cost job_suspension_duration. + set job_jitter := reduction.job_jitter job_arrival job_task + higher_eq_priority job_cost j actual_response_time. + apply valid_response_time_bound_in_sched_susp with + (task_period0 := task_period) (task_deadline0 := task_deadline) + (job_deadline0 := job_deadline) (job_task0 := job_task) (ts0 := ts) + (arr_seq0 := arr_seq) (higher_eq_priority0 := higher_eq_priority) + (job_suspension_duration0:=job_suspension_duration); try (by done). + - by intros tsk_hp IN OHEP j'; apply RESPhp. + { + intros j0 ARR0 LT0 JOB0. + apply completion_monotonic with (t := job_arrival j0 + R tsk_i); + [by done | | by apply BEFOREok; rewrite // -JOBtsk]. + by rewrite leq_add2l H_job_deadline_eq_task_deadline // JOB0 JOBtsk. + } + { + apply RESPi with (job_jitter := job_jitter); try (by done); + last by eapply reduction_prop.sched_jitter_is_valid; eauto 1. + repeat split; intros j' ARR. + - by eapply ts_membership_inflated_job_cost_positive; eauto 1. + - by eapply ts_membership_inflated_job_cost_le_inflated_task_cost; + eauto 1. + - by eapply ts_membership_job_jitter_le_task_jitter; eauto 1. + } + Qed. + + End PerTaskAnalysis. + +End TaskSetRTA. \ No newline at end of file diff --git a/analysis/uni/susp/dynamic/oblivious/fp_rta.v b/analysis/uni/susp/dynamic/oblivious/fp_rta.v index 6aba2b662..d6c9c4943 100644 --- a/analysis/uni/susp/dynamic/oblivious/fp_rta.v +++ b/analysis/uni/susp/dynamic/oblivious/fp_rta.v @@ -39,7 +39,7 @@ Module SuspensionObliviousFP. Hypothesis H_valid_task_parameters: valid_sporadic_taskset task_cost task_period task_deadline ts. - (* Next, consider any job arrival sequence with consistent, non-duplicate arrivals,... *) + (* Next, consider any job arrival sequence with consistent, duplicate-free arrivals,... *) Variable arr_seq: arrival_sequence Job. Hypothesis H_arrival_times_are_consistent: arrival_times_are_consistent job_arrival arr_seq. Hypothesis H_arrival_sequence_is_a_set: arrival_sequence_is_a_set arr_seq. diff --git a/analysis/uni/susp/sustainability/allcosts/reduction.v b/analysis/uni/susp/sustainability/allcosts/reduction.v new file mode 100644 index 000000000..220ee451a --- /dev/null +++ b/analysis/uni/susp/sustainability/allcosts/reduction.v @@ -0,0 +1,165 @@ +Require Import rt.util.all. +Require Import rt.model.priority rt.model.suspension. +Require Import rt.model.schedule.uni.susp.schedule + rt.model.schedule.uni.susp.platform + rt.model.schedule.uni.susp.build_suspension_table. +Require Import rt.model.schedule.uni.transformation.construction. +From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq bigop fintype. + +(* In this file, we prove that uniprocessor suspension-aware scheduling is + sustainable with job costs under the dynamic suspension model. *) +Module SustainabilityAllCosts. + + Import ScheduleWithSuspensions Suspension Priority PlatformWithSuspensions + ScheduleConstruction SuspensionTableConstruction. + + Section Reduction. + + Context {Job: eqType}. + Variable job_arrival: Job -> time. + Variable job_cost: Job -> time. + + (** Basic Setup & Setting *) + + (* Consider any job arrival sequence... *) + Variable arr_seq: arrival_sequence Job. + + (* ...and assume any (schedule-independent) job-level priorities. *) + Variable higher_eq_priority: JLDP_policy Job. + + (* Next, consider any suspension-aware schedule of the arrival sequence... *) + Variable sched_susp: schedule Job. + + (* ...and the associated job suspension durations. *) + Variable job_suspension_duration: job_suspension Job. + + (** Definition of the Reduction *) + + (* Now we proceed with the reduction. + Let inflated_job_cost denote any job cost inflation, i.e., the cost of + every job is as large as in the original schedule. *) + Variable inflated_job_cost: Job -> time. + + (* To simplify the analysis, instead of observing an infinite schedule, we + focus on the finite window delimited by the job we want to analyze. + Let's call this job j, with arrival time arr_j, ... *) + Variable j: Job. + Let arr_j := job_arrival j. + + (* ...and suppose that we want to prove that the response time of j in sched_susp + is upper-bounded by some value R. + In the analysis, we only need to focus on the schedule prefix [0, arr_j + R). *) + Variable R: time. + + (* In this sustainability proof, we must construct a new schedule with the inflated + job costs that is no "better" for job j than the original schedule. + Since we also modify the suspension durations, the construction is divided in + two parts: (A) Schedule Construction, and (B) Definition of Suspension Times. *) + + (** (A) Schedule Construction *) + Section ScheduleConstruction. + + (* Let's begin by defining the schedule construction function. *) + Section ConstructionStep. + + (* For any time t, suppose that we have generated the schedule prefix [0, t). + Then, we must define what should be scheduled at time t. *) + Variable sched_prefix: schedule Job. + Variable t: time. + + (* Consider the set of jobs that arrived up to time t. *) + Let arrivals := jobs_arrived_up_to arr_seq t. + + (* Recall whether a job is pending in the new schedule. *) + Let job_is_pending := pending job_arrival inflated_job_cost sched_prefix. + + (* Also, let's denote as late any job whose service in the new schedule is strictly + less than the service in sched_susp plus the difference between job costs. *) + Definition job_is_late any_j t := + service sched_prefix any_j t + < service sched_susp any_j t + (inflated_job_cost any_j - job_cost any_j). + + (* In order not to have to prove complex properties about the entire schedule, + we split the construction for the schedule prefix [0, arr_j + R) and for the + suffix [arr_j + R, ...). *) + + (** (A.1) The prefix is built in a way that prevents jobs from getting late. *) + Section Prefix. + + (* Consider the list of pending jobs in the new schedule that are either late + or scheduled in sched_susp. + (Note that when there are no late jobs, we pick the jobs that are scheduled + in sched_susp so that the suspension times remain consistent.) *) + Definition jobs_that_are_late_or_scheduled_in_sched_susp := + [seq any_j <- arrivals | job_is_pending any_j t && + (job_is_late any_j t || scheduled_at sched_susp any_j t)]. + + (* From that list, we take one of the (possibly many) highest-priority jobs, + or None, in case there are no late jobs and sched_susp is idle. *) + Definition highest_priority_late_job := + seq_min (higher_eq_priority t) jobs_that_are_late_or_scheduled_in_sched_susp. + + End Prefix. + + (** (A.2) In the suffix, we just pick the highest-priority pending job + so that the schedule constraints are satisfied. *) + Section Suffix. + + (* From the list of pending jobs in the new schedule, ... *) + Definition pending_jobs := + [seq any_j <- arrivals | job_is_pending any_j t ]. + + (* ...let's take one of the (possibly many) highest-priority jobs. *) + Definition highest_priority_job := + seq_min (higher_eq_priority t) pending_jobs. + + End Suffix. + + (** (A.3) In the end, we just combine the prefix and suffix schedules. *) + Definition build_schedule := + if t < arr_j + R then + highest_priority_late_job (* PREFIX (see A.1) *) + else + highest_priority_job. (* SUFFIX (see A.2) *) + + End ConstructionStep. + + (* To conclude, starting from the empty schedule, ...*) + Let empty_schedule : schedule Job := fun t => None. + + (* ...we use the recursive definition above to construct the new schedule. *) + Definition sched_new := + build_schedule_from_prefixes build_schedule empty_schedule. + + End ScheduleConstruction. + + (** (B) Definition of Suspension Times *) + + (* Having decided when jobs should be scheduled, we now define when they should suspend + so that the schedule properties remain consistent. *) + Section DefiningSuspension. + + (* Recall the definition of a suspended job in sched_susp. *) + Let job_is_suspended_in_sched_susp := + suspended_at job_arrival job_cost job_suspension_duration sched_susp. + + (* Based on the notion of a "late" job from the schedule construction, we say that a + job is suspended at time t in sched_new iff: + (a) time t precedes arr_j + R (i.e., suspensions only occur in the prefix), + (b) the job is suspended in sched_susp at time t, and + (c) the job is not late in sched_new at time t. *) + Definition suspended_in_sched_new (any_j: Job) (t: time) := + (t < arr_j + R) && job_is_suspended_in_sched_susp any_j t + && ~~ job_is_late sched_new any_j t. + + (* Next, using this suspension predicate, we build a table of suspension durations + that is valid up to time (arr_j + R). + For more details, see model/schedule/uni/susp/build_suspension_table. *) + Definition reduced_suspension_duration := + build_suspension_duration sched_new (arr_j + R) suspended_in_sched_new. + + End DefiningSuspension. + + End Reduction. + +End SustainabilityAllCosts. \ No newline at end of file diff --git a/analysis/uni/susp/sustainability/allcosts/reduction_properties.v b/analysis/uni/susp/sustainability/allcosts/reduction_properties.v new file mode 100644 index 000000000..506e5b834 --- /dev/null +++ b/analysis/uni/susp/sustainability/allcosts/reduction_properties.v @@ -0,0 +1,891 @@ +Require Import rt.util.all. +Require Import rt.model.priority rt.model.suspension. +Require Import rt.model.arrival.basic.arrival_sequence. +Require Import rt.model.schedule.uni.response_time. +Require Import rt.model.schedule.uni.susp.suspension_intervals + rt.model.schedule.uni.susp.schedule + rt.model.schedule.uni.susp.valid_schedule + rt.model.schedule.uni.susp.build_suspension_table + rt.model.schedule.uni.susp.platform. +Require Import rt.analysis.uni.susp.sustainability.allcosts.reduction. +Require Import rt.model.schedule.uni.transformation.construction. + +From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop. + +(* In this file, we prove several properties about the schedule we constructed + in the sustainability proof. *) +Module SustainabilityAllCostsProperties. + + Import ScheduleWithSuspensions Suspension Priority SuspensionIntervals + PlatformWithSuspensions ResponseTime ScheduleConstruction + SuspensionTableConstruction ValidSuspensionAwareSchedule. + + Module reduction := SustainabilityAllCosts. + + Section ReductionProperties. + + Context {Task: eqType}. + Context {Job: eqType}. + Variable job_arrival: Job -> time. + Variable job_cost: Job -> time. + + (** Basic Setup & Setting*) + + (* Consider any job arrival sequence with consistent job arrivals. *) + Variable arr_seq: arrival_sequence Job. + Hypothesis H_arrival_times_are_consistent: + arrival_times_are_consistent job_arrival arr_seq. + + (* Assume any (schedule-independent) JLDP policy that is reflexive, transitive and total, + i.e., that indicates "higher-or-equal priority". *) + Variable higher_eq_priority: JLDP_policy Job. + Hypothesis H_priority_is_reflexive: JLDP_is_reflexive higher_eq_priority. + Hypothesis H_priority_is_transitive: JLDP_is_transitive higher_eq_priority. + Hypothesis H_priority_is_total: JLDP_is_total arr_seq higher_eq_priority. + + (* Next, consider any suspension-aware schedule of the arrival sequence... *) + Variable sched_susp: schedule Job. + Hypothesis H_jobs_come_from_arrival_sequence: + jobs_come_from_arrival_sequence sched_susp arr_seq. + + (* ...and the associated job suspension times. *) + Variable job_suspension_duration: job_suspension Job. + + (* Assume that, in this schedule, jobs only execute after they arrive... *) + Hypothesis H_jobs_must_arrive_to_execute: + jobs_must_arrive_to_execute job_arrival sched_susp. + + (* ...and no longer than their execution costs. *) + Hypothesis H_completed_jobs_dont_execute: + completed_jobs_dont_execute job_cost sched_susp. + + (* Also assume that the schedule is work-conserving if there are non-suspended jobs, ... *) + Hypothesis H_work_conserving: + work_conserving job_arrival job_cost job_suspension_duration arr_seq sched_susp. + + (* ...that the schedule respects job priorities... *) + Hypothesis H_respects_priority: + respects_JLDP_policy job_arrival job_cost job_suspension_duration arr_seq + sched_susp higher_eq_priority. + + (* ...and that suspended jobs are not allowed to be scheduled. *) + Hypothesis H_respects_self_suspensions: + respects_self_suspensions job_arrival job_cost job_suspension_duration sched_susp. + + (** Reduction Setup *) + + (* Now we prove properties about the reduction. + Let j be the job to be analyzed with arrival time arr_j. *) + Variable j: Job. + Let arr_j := job_arrival j. + + (* Suppose that we want to prove that the response time of job j in sched_susp + is upper-bounded by some value R. This allows us to restrict our analysis + to the finite scheduling window [0, arr_j + R) during the reduction. *) + Variable R: time. + + (* Next, consider any job cost inflation... *) + Variable inflated_job_cost: Job -> time. + + (* ...in which the cost of every job is no less than in the original schedule. *) + Hypothesis H_job_costs_do_not_decrease: + forall any_j, inflated_job_cost any_j >= job_cost any_j. + + (* Recall the schedule we constructed from sched_susp using these inflated costs. *) + Let sched_new := reduction.sched_new job_arrival job_cost arr_seq higher_eq_priority + sched_susp inflated_job_cost j R. + + (* Also recall the predicate we defined for a suspended job in the new schedule... *) + Let suspended_in_sched_new := + reduction.suspended_in_sched_new job_arrival job_cost arr_seq higher_eq_priority + sched_susp job_suspension_duration inflated_job_cost j R. + + (* ...and the corresponding suspension table. *) + Let reduced_suspension_duration := + reduction.reduced_suspension_duration job_arrival job_cost arr_seq higher_eq_priority + sched_susp job_suspension_duration inflated_job_cost j R. + + (* For simplicity, let's define some local names. *) + Let job_response_time_in_sched_susp_bounded_by := + is_response_time_bound_of_job job_arrival job_cost sched_susp. + Let job_response_time_in_sched_new_bounded_by := + is_response_time_bound_of_job job_arrival inflated_job_cost sched_new. + Let suspended_in_sched_susp := + suspended_at job_arrival job_cost job_suspension_duration sched_susp. + Let job_is_late := reduction.job_is_late job_cost sched_susp inflated_job_cost sched_new. + Let build_schedule := reduction.build_schedule job_arrival job_cost arr_seq higher_eq_priority + sched_susp inflated_job_cost j R. + Let late_or_sched_jobs := reduction.jobs_that_are_late_or_scheduled_in_sched_susp + job_arrival job_cost arr_seq sched_susp inflated_job_cost sched_new. + Let hp_job := reduction.highest_priority_job job_arrival arr_seq higher_eq_priority + inflated_job_cost sched_new. + Let hp_late_job := reduction.highest_priority_late_job job_arrival job_cost arr_seq + higher_eq_priority sched_susp inflated_job_cost sched_new. + Let completed_in_sched_susp := completed_by job_cost sched_susp. + Let completed_in_sched_new := completed_by inflated_job_cost sched_new. + + (** Properties of the Schedule Construction *) + + (* In this section, we prove that the new schedule is equivalent to its construction function. *) + Section PropertiesOfScheduleConstruction. + + (* By showing that the construction function depends only on the service, ... *) + Lemma sched_new_depends_only_on_service: + forall sched1 sched2 t, + (forall j, service sched1 j t = service sched2 j t) -> + build_schedule sched1 t = build_schedule sched2 t. + Proof. + intros sched1 sched2 t SAME. + rewrite /build_schedule /reduction.build_schedule. + case: (_ < _). + { + rewrite /reduction.highest_priority_late_job; f_equal. + apply eq_in_filter; intros j0 IN0; f_equal; + first by rewrite /pending /completed_by SAME. + by f_equal; rewrite /reduction.job_is_late SAME. + } + { + rewrite /reduction.highest_priority_job; f_equal. + apply eq_in_filter; intros j0 IN0. + by rewrite /pending /completed_by SAME. + } + Qed. + + (* ...we infer that the new schedule is equivalent to the construction function. *) + Corollary sched_new_uses_construction_function: + forall t, + sched_new t = build_schedule sched_new t. + Proof. + by ins; apply service_dependent_schedule_construction, + sched_new_depends_only_on_service. + Qed. + + End PropertiesOfScheduleConstruction. + + (** Basic Properties of the Generated Schedule *) + + (* In this section, we prove some properties about the generated schedule that + only depend on the construction function but not on suspension times. *) + Section BasicScheduleProperties. + + (* First, we show that scheduled jobs come from the arrival sequence. *) + Lemma sched_new_jobs_come_from_arrival_sequence: + jobs_come_from_arrival_sequence sched_new arr_seq. + Proof. + move => j0 t /eqP SCHEDn. + rewrite /scheduled_at sched_new_uses_construction_function in SCHEDn. + rewrite /build_schedule /reduction.build_schedule in SCHEDn. + case (ltnP t (arr_j + R)) => [LT | GE]. + { + rewrite LT /reduction.highest_priority_late_job in SCHEDn; + apply seq_min_in_seq in SCHEDn. + rewrite mem_filter in SCHEDn. + move: SCHEDn => /andP [_ IN]. + by eapply in_arrivals_implies_arrived; eauto 1. + } + { + rewrite ltnNge GE /= in SCHEDn. + rewrite /reduction.highest_priority_job in SCHEDn; apply seq_min_in_seq in SCHEDn. + rewrite mem_filter in SCHEDn. + move: SCHEDn => /andP [_ IN]. + by eapply in_arrivals_implies_arrived; eauto 1. + } + Qed. + + (* Next, we show that jobs do not execute before their arrival times... *) + Lemma sched_new_jobs_must_arrive_to_execute: + jobs_must_arrive_to_execute job_arrival sched_new. + Proof. + move => j0 t /eqP SCHEDn. + rewrite /scheduled_at sched_new_uses_construction_function in SCHEDn. + rewrite /build_schedule /reduction.build_schedule in SCHEDn. + case (ltnP t (arr_j + R)) => [LT | GE]. + { + rewrite LT /reduction.highest_priority_late_job in SCHEDn; + apply seq_min_in_seq in SCHEDn. + rewrite mem_filter in SCHEDn. + by move: SCHEDn => /andP [/andP [/andP [ARR _] _] _]. + } + { + rewrite ltnNge GE /= in SCHEDn. + rewrite /reduction.highest_priority_job in SCHEDn; apply seq_min_in_seq in SCHEDn. + rewrite mem_filter in SCHEDn. + by move: SCHEDn => /andP [/andP [ARR _] _]. + } + Qed. + + (* ...nor longer than their execution costs. *) + Lemma sched_new_completed_jobs_dont_execute: + completed_jobs_dont_execute inflated_job_cost sched_new. + Proof. + intros j0 t. + induction t; + first by rewrite /service /service_during big_geq //. + rewrite /service /service_during big_nat_recr //=. + rewrite leq_eqVlt in IHt; move: IHt => /orP [/eqP EQ | LT]; last first. + { + apply: leq_trans LT; rewrite -addn1. + by apply leq_add; last by apply leq_b1. + } + rewrite -[inflated_job_cost _]addn0; apply leq_add; first by rewrite -EQ. + rewrite leqn0 eqb0 /scheduled_at. + rewrite sched_new_uses_construction_function /build_schedule + /reduction.build_schedule. + case: (_ < _); rewrite /reduction.highest_priority_job + /reduction.highest_priority_late_job. + { + apply/eqP; intro SCHEDn. + apply seq_min_in_seq in SCHEDn. + rewrite mem_filter in SCHEDn. + move: SCHEDn => /andP [/andP [/andP [_ NOTCOMP] _] _]. + by rewrite /completed_by EQ eq_refl in NOTCOMP. + } + { + apply/eqP; intro SCHEDn. + apply seq_min_in_seq in SCHEDn. + rewrite mem_filter in SCHEDn. + move: SCHEDn => /andP [/andP [_ NOTCOMP] _]. + by rewrite /completed_by EQ eq_refl in NOTCOMP. + } + Qed. + + End BasicScheduleProperties. + + (** Service Invariants *) + + (* In this section, we prove some service invariants guaranteed by the new schedule + up to time (arr_j + R). + Note that these properties follow directly from the schedule construction and + do not depend on suspension times. *) + Section ServiceInvariant. + + (* Let t be any time in the interval [0, arr_j + R]. *) + Variable t: time. + Hypothesis H_before_R: t <= arr_j + R. + + (* By induction on time, we prove that for any job, the service received up to + time t in the new schedule is no more than the service received up to time t in + the original schedule, plus the difference between job costs due to inflation. *) + Lemma sched_new_service_invariant: + forall any_j, + service sched_new any_j t + <= service sched_susp any_j t + (inflated_job_cost any_j - job_cost any_j). + Proof. + rename H_priority_is_total into TOTAL, H_priority_is_transitive into TRANS, + H_respects_priority into PRIO, H_work_conserving into WORK. + rename t into t', H_before_R into BEFORE. + move: t' BEFORE. + induction t'; + first by intros _ j0; rewrite /service /service_during big_geq. + intros LTr; feed IHt'; first by apply ltnW. + intros j0. + case (boolP (scheduled_at sched_new j0 t')) => [SCHEDn| NOTSCHEDn]; last first. + { + apply negbTE in NOTSCHEDn. + rewrite /service /service_during big_nat_recr //=. + rewrite /service_at NOTSCHEDn addn0. + apply: (leq_trans (IHt' j0)). + by rewrite leq_add2r extend_sum. + } + move: (SCHEDn) => IN. + rewrite /scheduled_at sched_new_uses_construction_function in IN. + rewrite /build_schedule /reduction.build_schedule LTr in IN; move: IN => /eqP IN. + rewrite /reduction.highest_priority_late_job in IN. + apply seq_min_in_seq in IN; rewrite mem_filter in IN. + move: IN => /andP [/andP [PEND OR] IN]. + move: OR => /orP [LT | SCHEDs]. + { + apply leq_trans with (n := service sched_new j0 t' + 1); + first by rewrite /service/service_during big_nat_recr // leq_add ?leq_b1. + rewrite addn1. + by apply: (leq_trans LT); rewrite leq_add2r extend_sum. + } + { + rewrite /service /service_during big_nat_recr // big_nat_recr //=. + rewrite /service_at SCHEDn SCHEDs -addnA [1 + _]addnC addnA leq_add2r. + by apply IHt'. + } + Qed. + + (* From the previous lemma, we conclude that any job that completes in the new + schedule up to time t must have completed in the original schedule as well. *) + Corollary sched_new_jobs_complete_later: + forall any_j, + completed_by inflated_job_cost sched_new any_j t -> + completed_by job_cost sched_susp any_j t. + Proof. + intros j0 COMPn. + rewrite /completed_by eqn_leq; apply/andP; split; + first by apply cumulative_service_le_job_cost. + rewrite -(leq_add2r (inflated_job_cost j0 - job_cost j0)). + rewrite subnKC; last by eauto 1. + move: COMPn => /eqP {1}<-. + by apply sched_new_service_invariant. + Qed. + + End ServiceInvariant. + + (** Properties of the Suspension Predicate *) + + (* In order to prove schedule properties that depend on suspension times, we first + prove some facts about the suspension predicate we defined. *) + Section SuspensionPredicate. + + (* Let any_j be any job. *) + Variable any_j: Job. + + (* First, we show that if the suspension predicate holds for any_j at time t, + then any_j must have arrived... *) + Lemma suspended_in_sched_new_implies_arrived: + forall t, + suspended_in_sched_new any_j t -> has_arrived job_arrival any_j t. + Proof. + rename any_j into j0. + move => t /andP [/andP [LT SUSPs] _]. + by eapply suspended_implies_arrived; eauto 1. + Qed. + + (* ...and cannot have completed. *) + Lemma suspended_in_sched_new_implies_not_completed: + forall t, + suspended_in_sched_new any_j t -> ~~ completed_in_sched_new any_j t. + Proof. + rename any_j into j0. + move => t /andP [/andP [LT SUSPs] _]. + apply/negP; intro COMPn. + apply suspended_implies_not_completed in SUSPs. + apply sched_new_jobs_complete_later in COMPn; last by apply ltnW. + by rewrite COMPn in SUSPs. + Qed. + + (* Next, we show that if the suspension predicate changes from false at time t + to true at time (t + 1), then any_j must be scheduled at time t. *) + Lemma executes_before_suspension_in_sched_new: + forall t, + t < arr_j + R -> + has_arrived job_arrival any_j t -> + ~~ suspended_in_sched_new any_j t -> + suspended_in_sched_new any_j t.+1 -> + scheduled_at sched_new any_j t. + Proof. + rename any_j into j0. + intros t LTr ARR NOTSUSPn SUSPn'. + rewrite negb_and LTr /= negbK -/suspended_in_sched_susp -/job_is_late in NOTSUSPn. + move: NOTSUSPn => /orP [NOTSUSPs | LATE]; last first. + { + apply contraT; intro NOTSCHEDn. + move: SUSPn' => /andP [_ NOTLATE]. + rewrite /job_is_late /reduction.job_is_late -leqNgt -/sched_new in LATE NOTLATE. + have GT: service sched_new j0 t < service sched_new j0 t.+1. + { + apply: (leq_trans LATE); apply: (leq_trans _ NOTLATE). + by rewrite leq_add2r extend_sum. + } + rewrite /service/service_during big_nat_recr// -addn1 leq_add2l in GT. + rewrite lt0n eqb0 negbK in GT. + by rewrite GT in NOTSCHEDn. + } + move: (SUSPn') => /andP [/andP [_ SUSPs'] NOTLATE']. + rewrite -/suspended_in_sched_susp in SUSPs'. + rewrite /reduction.job_is_late -/sched_new -leqNgt in NOTLATE'. + apply contraT; intro NOTSCHEDn. + have SAME: service sched_new j0 t.+1 = service sched_new j0 t. + { + apply negbTE in NOTSCHEDn. + by rewrite /service /service_during big_nat_recr //= /service_at NOTSCHEDn. + } + rewrite SAME {SAME} in NOTLATE'. + have INV := sched_new_service_invariant t (ltnW LTr) j0. + have SCHEDs: ~~ scheduled_at sched_susp j0 t. + { + apply contraT; rewrite negbK; intro SCHEDs. + have BUG: service sched_susp j0 t + 1 <= service sched_susp j0 t. + { + rewrite -(leq_add2r (inflated_job_cost j0 - job_cost j0)). + apply: (leq_trans _ INV); apply: (leq_trans _ NOTLATE'). + rewrite leq_add2r. + by rewrite /service /service_during big_nat_recr //= /service_at SCHEDs. + } + by rewrite addn1 ltnn in BUG. + } + suff BUG: scheduled_at sched_susp j0 t by rewrite BUG in SCHEDs. + by eapply executes_before_suspension; eauto 1. + Qed. + + (* For simplicity, let's call suspension_start the time following the last + execution of a job. *) + Let suspension_start := time_after_last_execution job_arrival. + + (* Then, we prove that if any_j is suspended at time t, it does not receive + any service between time t and the previous beginning of suspension. *) + Lemma suspended_in_sched_new_no_service_since_execution: + forall t t_mid, + suspended_in_sched_new any_j t -> + suspension_start sched_new any_j t <= t_mid < t -> + service sched_new any_j t <= service sched_new any_j t_mid. + Proof. + have BEFORE := executes_before_suspension_in_sched_new. + rename any_j into j0. + induction t; first by intros t_susp _; rewrite ltn0 andbF. + move => t_mid SUSPn' /andP [GE LT]. + move: (SUSPn') => /andP [/andP [LTr' SUSPs'] _]. + have LTr: t < arr_j + R by apply: (ltn_trans _ LTr'). + have ARR: has_arrived job_arrival j0 t.+1. + by apply suspended_implies_arrived in SUSPs'. + rewrite /has_arrived leq_eqVlt in ARR; move: ARR => /orP [/eqP EQ | ARR]. + { + rewrite /service /service_during. + rewrite (cumulative_service_before_job_arrival_zero job_arrival) ?EQ //. + by apply sched_new_jobs_must_arrive_to_execute. + } + case (boolP (scheduled_at sched_new j0 t)) => [SCHEDn | NOTSCHEDn]; last first. + { + apply leq_trans with (n := service sched_new j0 t). + { + apply negbTE in NOTSCHEDn; rewrite /service/service_during. + by rewrite big_nat_recr //= /service_at NOTSCHEDn addn0. + } + case (boolP (t_mid == t)) => [/eqP EQ | DIFF]; first by subst. + apply IHt. + { + apply contraT; intro NOTSUSPn. + suff SCHEDn: scheduled_at sched_new j0 t by rewrite SCHEDn in NOTSCHEDn. + by apply BEFORE. + } + apply/andP; split. + { + apply: (leq_trans _ GE); apply last_execution_monotonic; last by done. + by apply sched_new_jobs_must_arrive_to_execute. + } + by rewrite ltn_neqAle -ltnS LT andbT. + } + set start := suspension_start sched_new. + apply leq_trans with (n := service sched_new j0 (start j0 t.+1)); + last by apply extend_sum. + apply extend_sum; first by done. + rewrite /start /suspension_start /time_after_last_execution. + have EX: [exists t0:'I_t.+1, scheduled_at sched_new j0 t0]. + by apply/existsP; exists (Ordinal (ltnSn t)). + rewrite EX -addn1 leq_add2r addn1. + have GEt := @leq_bigmax_cond _ (fun x:'I_t.+1 => scheduled_at sched_new j0 x) + id (Ordinal (ltnSn t)). + by apply GEt. + Qed. + + (* Next, we prove that if the suspension predicate holds for any_j at time t, + then the latest execution of any_j in the new schedule is no earlier than + its latest execution in the original schedule. *) + Lemma suspended_in_sched_new_suspension_starts_no_earlier: + forall t, + has_arrived job_arrival any_j t -> + suspended_in_sched_new any_j t -> + suspension_start sched_susp any_j t <= suspension_start sched_new any_j t. + Proof. + have BEFORE := executes_before_suspension_in_sched_new. + rename any_j into j0. + induction t. + { + intros ARR SUSPs; apply leq_trans with (n := 0); last by done. + rewrite /suspension_start /time_after_last_execution. + have NOTEX: [exists t0: 'I_0, scheduled_at sched_susp j0 t0] = false. + by apply negbTE; rewrite negb_exists; apply/forallP; intros [x LT0]. + by rewrite NOTEX {NOTEX}. + } + { + intros ARR SUSPn'. + move: (SUSPn') => /andP [/andP [LTr _] _]. + have LTr': t < arr_j + R by apply: (ltn_trans _ LTr). + rewrite /has_arrived leq_eqVlt in ARR; move: ARR => /orP [/eqP EQ | ARR]. + { + have LAST := last_execution_after_arrival. + rewrite /suspension_start /has_arrived in LAST *. + apply leq_trans with (n := job_arrival j0); + last by apply LAST, sched_new_jobs_must_arrive_to_execute. + rewrite /time_after_last_execution. + suff NOTEX: [exists t0: 'I_t.+1, scheduled_at sched_susp j0 t0] = false + by rewrite NOTEX. + apply negbTE; rewrite negb_exists; apply/forallP; intros t0. + apply/negP; intro SCHED0. + apply H_jobs_must_arrive_to_execute in SCHED0. + suff BUG: t0 < t0 by rewrite ltnn in BUG. + by apply: (leq_trans _ SCHED0); rewrite EQ. + } + rewrite ltnS in ARR. + case (boolP (suspended_in_sched_new j0 t)) => [SUSPn | NOTSUSPn]. + { + apply leq_trans with (n := suspension_start sched_new j0 t); last first. + { + apply last_execution_monotonic; last by done. + by apply sched_new_jobs_must_arrive_to_execute. + } + apply leq_trans with (n := suspension_start sched_susp j0 t); [|by apply IHt]. + apply eq_leq, same_service_implies_same_last_execution. + rewrite /service /service_during big_nat_recr //= /service_at. + case (boolP (scheduled_at sched_susp j0 t)); last by done. + intros SCHEDs; apply H_respects_self_suspensions in SCHEDs. + by move: SUSPn => /andP [/andP [_ SUSPs] _]. + } + move: (SUSPn') => /andP [/andP [_ SUSPs']] _. + have SCHEDn := BEFORE t LTr' ARR NOTSUSPn SUSPn'. + apply leq_trans with (n := t.+1); + first by move: SUSPs' => /andP [_ /andP [LE _]]. + rewrite /suspension_start /time_after_last_execution. + have EX: [exists t0:'I_t.+1, scheduled_at sched_new j0 t0]. + by apply/existsP; exists (Ordinal (ltnSn t)). + rewrite EX -addn1 leq_add2r addn1. + have GE := @leq_bigmax_cond _ (fun x:'I_t.+1 => scheduled_at sched_new j0 x) + id (Ordinal (ltnSn t)). + by apply GE. + } + Qed. + + (* using the previous lemmas, we conclude that the suspension predicate is continuous + between any suspension point and the last execution of the job. *) + Lemma suspended_in_sched_new_is_continuous: + forall t t_mid, + suspended_in_sched_new any_j t -> + suspension_start sched_new any_j t <= t_mid < t -> + suspended_in_sched_new any_j t_mid. + Proof. + have NOSERV := suspended_in_sched_new_no_service_since_execution. + have NOEARLIER := suspended_in_sched_new_suspension_starts_no_earlier. + rename any_j into j0; intros t. + induction t_mid as [k IH] using strong_ind. + have INV := sched_new_service_invariant. + move => SUSPn /andP [GE LT]. + move: (SUSPn) => /andP [/andP [LTr SUSPt] NOTLATEt]. + rewrite -/job_is_late in NOTLATEt. + apply/andP; split; last first. + { + rewrite /reduction.job_is_late -/sched_new -2!leqNgt in NOTLATEt *. + feed (INV t); [by apply ltnW | specialize (INV j0)]. + set Sn := service sched_new in INV NOTLATEt *. + set Ss := service sched_susp in INV NOTLATEt *. + set cost0 := job_cost j0 in INV NOTLATEt *. + set cost0' := inflated_job_cost j0 in INV NOTLATEt *. + have SAME: Sn j0 t = Ss j0 t + (cost0' - cost0). + by apply/eqP; rewrite eqn_leq; apply/andP. + clear INV. + apply leq_trans with (n := Ss j0 t + (cost0' - cost0)); + first by rewrite leq_add2r; apply extend_sum; last by apply ltnW. + rewrite -SAME {SAME}. + by apply NOSERV; rewrite ?SUSPn ?LTr ?SUSPt ?GE ?LT. + } + apply/andP; split; first by apply: (ltn_trans _ LTr). + apply suspended_in_suspension_interval with (t0 := t); try done. + { + apply/negP; intro COMPmid. + apply suspended_implies_not_completed in SUSPt. + suff BUG: completed_by job_cost sched_susp j0 t by rewrite BUG in SUSPt. + by apply completion_monotonic with (t0 := k); [| by apply ltnW |]. + } + apply/andP; split; + last by apply: (ltn_trans LT); move: SUSPt => /andP [_ /andP [_ GTt]]. + apply: (leq_trans _ GE). + apply NOEARLIER; try done. + by apply suspended_implies_arrived in SUSPt. + Qed. + + End SuspensionPredicate. + + (** Properties of the Suspension Table *) + + (* In this section, we prove some properties about the suspension table. *) + Section SuspensionTable. + + (* First, we show that no job ever suspends after (arr_j + R). *) + Lemma suspended_in_sched_new_only_inside_window: + forall any_j t, + arr_j + R <= t -> + ~~ suspended_at job_arrival inflated_job_cost reduced_suspension_duration + sched_new any_j t. + Proof. + intros any_j t LE. + case (boolP (has_arrived job_arrival any_j t)) => [ARR | NOTARR]; last first. + { + apply/negP; intro SUSP. + apply suspended_implies_arrived in SUSP; + last by apply sched_new_jobs_must_arrive_to_execute. + by rewrite SUSP in NOTARR. + } + apply suspension_duration_no_suspension_after_t_max; try done; + first by apply sched_new_jobs_must_arrive_to_execute. + intros j0 t0 LT. + rewrite /reduction.suspended_in_sched_new LT /=. + move => /andP [SUSPs _]. + by apply suspended_implies_arrived in SUSPs. + Qed. + + (* Next, using the lemmas about the suspension predicate, we show that the suspension + predicate for the new schedule matches the generated suspension table. + (see model/schedule/uni/susp/build_suspension_table.v) *) + Lemma sched_new_suspension_matches: + forall any_j t, + t < arr_j + R -> + suspended_in_sched_new any_j t = + suspended_at job_arrival inflated_job_cost reduced_suspension_duration sched_new any_j t. + Proof. + apply suspension_duration_matches_predicate_up_to_t_max; + first by apply sched_new_jobs_must_arrive_to_execute. + - by intros j0 t LT SUSP; apply suspended_in_sched_new_implies_arrived. + - by intros j0 t LT SUSP; apply suspended_in_sched_new_implies_not_completed. + - by intros j0 t LT SUSP; apply suspended_in_sched_new_is_continuous. + Qed. + + (* Recall the definition of cumulative suspension in both schedules. *) + Let cumulative_suspension_in_sched_susp := + cumulative_suspension job_arrival job_cost job_suspension_duration sched_susp. + Let cumulative_suspension_in_sched_new := + cumulative_suspension job_arrival inflated_job_cost reduced_suspension_duration sched_new. + + (* To conclude, we prove that the suspension durations in the new schedule are no + longer than in the original schedule. *) + Lemma sched_new_has_shorter_suspensions: + forall any_j t, + cumulative_suspension_in_sched_new any_j t + <= cumulative_suspension_in_sched_susp any_j t. + Proof. + intros j0 t. + apply leq_sum; intros i _. + case (ltnP i (arr_j + R)) => [LTr | GEr]. + { + rewrite -sched_new_suspension_matches //. + rewrite /suspended_in_sched_new /reduction.suspended_in_sched_new. + rewrite LTr /=. + by case: (X in (_ && X)); rewrite ?andbT ?andbF. + } + { + apply leq_trans with (n := 0); last by done. + rewrite leqn0 eqb0. + case (boolP (has_arrived job_arrival j0 i)) => [ARR | NOTARR]; + first by apply suspended_in_sched_new_only_inside_window. + apply/negP; intro SUSPn. + apply suspended_implies_arrived in SUSPn; first by rewrite SUSPn in NOTARR. + by apply sched_new_jobs_must_arrive_to_execute. + } + Qed. + + End SuspensionTable. + + (** Suspension-Related Schedule Properties *) + + (* Having shown that the suspension table matches the suspension predicate, + we now analyze the suspension predicate and prove that the generated + schedule satisfies all the remaining properties. *) + Section AdditionalScheduleProperties. + + (* First, we show that the new schedule respects self-suspensions. *) + Lemma sched_new_respects_self_suspensions: + respects_self_suspensions job_arrival inflated_job_cost reduced_suspension_duration sched_new. + Proof. + move => j0 t /eqP SCHEDn; apply/negP. + case (ltnP t (arr_j + R)) => [LTr | GEr]; + last by apply suspended_in_sched_new_only_inside_window. + rewrite -sched_new_suspension_matches /suspended_in_sched_new //. + rewrite negb_and negbK. + rewrite /scheduled_at sched_new_uses_construction_function in SCHEDn. + rewrite /build_schedule /reduction.build_schedule in SCHEDn. + case (ltnP t (arr_j + R)) => [LT | GE]; last by rewrite ltnNge GE. + rewrite LT /reduction.highest_priority_late_job in SCHEDn. + apply seq_min_in_seq in SCHEDn. + rewrite mem_filter in SCHEDn. + move: SCHEDn => /andP [/andP [PEND OR] _]. + move: OR => /orP [SERV | SCHED]; first by rewrite SERV orbT. + apply/orP; left. + rewrite negb_and; apply/orP; right. + by apply/negP; apply H_respects_self_suspensions. + Qed. + + (* Next, we prove that the new schedule is (suspension-aware) work-conserving... *) + Lemma sched_new_work_conserving: + work_conserving job_arrival inflated_job_cost reduced_suspension_duration + arr_seq sched_new. + Proof. + have COMPnew := sched_new_completed_jobs_dont_execute. + have MATCH := sched_new_suspension_matches. + rename H_work_conserving into WORKs, H_respects_priority into PRIOs. + intros j0 t IN BACK. + move: BACK => /andP [/andP[PEND NOTSCHED] NOTSUSP]. + rewrite /scheduled_at sched_new_uses_construction_function in NOTSCHED *. + rewrite /build_schedule /reduction.build_schedule in NOTSCHED *. + case: (ltnP t (arr_j + R)) => [BEFORE | AFTER]; last first. + { + clear NOTSUSP; rewrite ltnNge AFTER /= in NOTSCHED. + rewrite -/hp_job in NOTSCHED *. + destruct (hp_job t) eqn:SCHEDn; first by exists s. + exfalso; clear NOTSCHED. + suff NOTNONE: hp_job t != None by rewrite SCHEDn in NOTNONE. + move: (PEND) => /andP [ARR NOTCOMPn]. + have IN0: j0 \in jobs_arrived_up_to arr_seq t. + by eapply arrived_between_implies_in_arrivals; eauto 1. + apply seq_min_exists with (x := j0). + by rewrite mem_filter PEND IN0. + } + rewrite -MATCH // in NOTSUSP. + rewrite /suspended_in_sched_new negb_and negbK in NOTSUSP. + rewrite BEFORE /= in NOTSUSP NOTSCHED. + rewrite -/hp_late_job in NOTSCHED *. + destruct (hp_late_job t) eqn:SCHEDn; first by exists s. + exfalso; clear NOTSCHED. + suff NOTNONE: hp_late_job t != None by rewrite SCHEDn in NOTNONE. + move: (PEND) => /andP [ARR NOTCOMPn]. + have IN0: j0 \in jobs_arrived_up_to arr_seq t. + by eapply arrived_between_implies_in_arrivals; eauto 1. + move: NOTSUSP => /orP [NOTSUSPs | LT]; last first. + { + clear SCHEDn; apply seq_min_exists with (x := j0). + by rewrite mem_filter PEND IN0 andbT LT. + } + case (boolP (completed_by job_cost sched_susp j0 t)) => [COMPs | NOTCOMPs]. + { + clear SCHEDn; apply seq_min_exists with (x := j0). + rewrite mem_filter PEND IN0 andbT /=. + apply/orP; left. + apply leq_trans with (n := job_cost j0 + (inflated_job_cost j0 - job_cost j0)); + last by move: COMPs => /eqP <-; rewrite leq_add2r. + rewrite subnKC; last by apply H_job_costs_do_not_decrease. + by rewrite ltn_neqAle; apply/andP; split; + last by apply sched_new_completed_jobs_dont_execute. + } + case (boolP (scheduled_at sched_susp j0 t)) => [SCHEDs | NOTSCHEDs]. + { + clear SCHEDn; apply seq_min_exists with (x := j0). + by rewrite mem_filter PEND IN0 andbT SCHEDs orbT. + } + feed (WORKs j0 t IN); first by repeat (apply/andP; split). + move: WORKs => [j_hp SCHEDhp]. + clear SCHEDn; apply seq_min_exists with (x := j_hp). + have ARRhp: has_arrived job_arrival j_hp t. + by apply H_jobs_must_arrive_to_execute. + have ARRINhp: arrives_in arr_seq j_hp. + by eapply H_jobs_come_from_arrival_sequence; eauto 1. + have INhp: j_hp \in jobs_arrived_up_to arr_seq t. + by eapply arrived_between_implies_in_arrivals; eauto 1. + rewrite mem_filter INhp andbT SCHEDhp orbT andbT. + apply/andP; split; first by done. + apply contraT; rewrite negbK; intro COMPhpN. + have COMPhpS: completed_by job_cost sched_susp j_hp t. + by apply sched_new_jobs_complete_later; first by apply ltnW. + by apply completed_implies_not_scheduled in COMPhpS; + first by rewrite SCHEDhp in COMPhpS. + Qed. + + (* ...and respects job priorities. *) + Lemma sched_new_respects_policy: + respects_JLDP_policy job_arrival inflated_job_cost reduced_suspension_duration + arr_seq sched_new higher_eq_priority. + Proof. + have MATCH := sched_new_suspension_matches. + rename H_priority_is_transitive into TRANS, H_priority_is_total into TOTAL, + H_priority_is_reflexive into REFL, H_work_conserving into WORKs, + H_respects_priority into PRIOs. + move => j1 j2 t ARRin BACK /eqP SCHED. + move: BACK => /andP [/andP [PEND NOTSCHED] NOTSUSPn]. + rewrite /scheduled_at sched_new_uses_construction_function /build_schedule + /reduction.build_schedule in SCHED NOTSCHED *. + case: (ltnP t (arr_j + R)) => [BEFORE | AFTER]; last first. + { + rewrite ltnNge AFTER /= in SCHED NOTSCHED. + rewrite /reduction.highest_priority_job in SCHED NOTSCHED. + set jobs := reduction.pending_jobs _ _ _ _ _ in SCHED NOTSCHED. + have IN: j1 \in jobs. + { + rewrite mem_filter PEND /=. + move: PEND => /andP [ARR _]. + by eapply arrived_between_implies_in_arrivals; eauto 1. + } + apply seq_min_computes_min with (y := j1) in SCHED; try (by done). + by intros x y; rewrite 2!mem_filter; move => /andP [_ INx] /andP [_ INy]; + apply TOTAL; eapply in_arrivals_implies_arrived; eauto 2. + } + rewrite BEFORE in SCHED NOTSCHED. + rewrite /reduction.highest_priority_late_job in SCHED NOTSCHED. + set jobs := _ sched_new t in SCHED NOTSCHED. + rewrite -MATCH // negb_and negbK in NOTSUSPn. + have TOT: forall x y, x \in jobs -> y \in jobs -> + higher_eq_priority t x y || higher_eq_priority t y x. + { + intros x y INx INy; rewrite 2!mem_filter in INx INy. + move: INx INy => /andP [_ INx] /andP [_ INy]. + by apply TOTAL; eapply in_arrivals_implies_arrived; eauto 1. + } + have ARR1: has_arrived job_arrival j1 t by move: PEND => /andP [ARR _]. + have IN1: j1 \in jobs_arrived_up_to arr_seq t. + by eapply arrived_between_implies_in_arrivals; eauto 1. + move: NOTSUSPn => /orP [NOTSUSPs | LT]; last first. + { + apply seq_min_computes_min with (l := jobs); try done. + by rewrite mem_filter IN1 andbT PEND LT. + } + rewrite BEFORE /= in NOTSUSPs. + case (boolP (scheduled_at sched_susp j1 t)) => [SCHEDs | NOTSCHEDs]. + { + apply seq_min_computes_min with (l := jobs); try done. + by rewrite mem_filter IN1 andbT PEND SCHEDs orbT. + } + case (boolP (completed_by job_cost sched_susp j1 t)) => [COMPs | NOTCOMPs]. + { + apply seq_min_computes_min with (l := jobs); try (by done). + rewrite mem_filter PEND /= IN1 andbT. + apply/orP; left. + rewrite /reduction.job_is_late. + move: COMPs => /eqP ->; rewrite subnKC; last by eauto 1. + move: PEND => /andP [_ NOTCOMP]. + rewrite ltn_neqAle; apply/andP; split; first by done. + apply cumulative_service_le_job_cost. + by apply sched_new_completed_jobs_dont_execute. + } + feed (WORKs j1 t ARRin); first by repeat (apply/andP; split). + move: WORKs => [j_hp SCHEDhp]. + apply: (TRANS _ j_hp); + last by apply PRIOs; try done; repeat (apply/andP; split). + have ARRhp: has_arrived job_arrival j_hp t. + by apply H_jobs_must_arrive_to_execute. + have ARRINhp: arrives_in arr_seq j_hp. + by eapply H_jobs_come_from_arrival_sequence; eauto 1. + have INhp: j_hp \in jobs_arrived_up_to arr_seq t. + by eapply arrived_between_implies_in_arrivals; eauto 1. + apply seq_min_computes_min with (l := jobs); try done. + rewrite mem_filter INhp andbT SCHEDhp orbT andbT. + apply/andP; split; first by done. + apply contraT; rewrite negbK; intro COMPhpN. + have COMPhpS: completed_by job_cost sched_susp j_hp t. + by apply sched_new_jobs_complete_later; first by apply ltnW. + by apply completed_implies_not_scheduled in COMPhpS; + first by rewrite SCHEDhp in COMPhpS. + Qed. + + End AdditionalScheduleProperties. + + (** Final Remarks *) + + Section FinalRemarks. + + (* To summarize, we conclude that the new schedule is a valid suspension-aware schedule ... *) + Remark sched_new_is_valid: + valid_suspension_aware_schedule job_arrival arr_seq higher_eq_priority + reduced_suspension_duration inflated_job_cost sched_new. + Proof. + repeat split. + - by apply sched_new_jobs_come_from_arrival_sequence. + - by apply sched_new_jobs_must_arrive_to_execute. + - by apply sched_new_completed_jobs_dont_execute. + - by apply sched_new_work_conserving. + - by apply sched_new_respects_policy. + - by apply sched_new_respects_self_suspensions. + Qed. + + (* ...and that if the analyzed job j has response-time bound R in the schedule, + then it also has response-time bound R in the original schedule. *) + Remark sched_new_response_time_of_job_j: + job_response_time_in_sched_new_bounded_by j R -> + job_response_time_in_sched_susp_bounded_by j R. + Proof. + by apply sched_new_jobs_complete_later. + Qed. + + End FinalRemarks. + + End ReductionProperties. + +End SustainabilityAllCostsProperties. \ No newline at end of file diff --git a/analysis/uni/susp/sustainability/singlecost/reduction.v b/analysis/uni/susp/sustainability/singlecost/reduction.v new file mode 100644 index 000000000..ace08f94e --- /dev/null +++ b/analysis/uni/susp/sustainability/singlecost/reduction.v @@ -0,0 +1,102 @@ +Require Import rt.util.all. +Require Import rt.model.priority rt.model.suspension. +Require Import rt.model.schedule.uni.susp.schedule. +Require Import rt.model.schedule.uni.transformation.construction. +From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq. + +(* In this file, we propose a reduction that converts a suspension-aware + schedule to another suspension-aware schedule where the cost of a certain + job is inflated and its response time does not decrease. *) +Module SustainabilitySingleCost. + + Import ScheduleWithSuspensions Suspension Priority ScheduleConstruction. + + Section Reduction. + + Context {Job: eqType}. + Variable job_arrival: Job -> time. + Variable job_cost: Job -> time. + + (** Basic Setup & Setting*) + + (* Consider any job arrival sequence... *) + Variable arr_seq: arrival_sequence Job. + + (* ...and assume any (schedule-independent) JLDP policy. *) + Variable higher_eq_priority: JLDP_policy Job. + + (* Next, consider any suspension-aware schedule of the arrival sequence... *) + Variable sched_susp: schedule Job. + + (* ...and the associated job suspension times. *) + Variable job_suspension_duration: job_suspension Job. + + (** Definition of the Reduction *) + + (* Now we proceed with the reduction. Let j be the job to be analyzed. *) + Variable j: Job. + + (* Next, consider any job cost inflation that does not decrease the cost of job j + and that preserves the cost of the remaining jobs. *) + Variable inflated_job_cost: Job -> time. + + (** Schedule Construction *) + + (* We are going to construct a new schedule that copies the original suspension-aware + schedule and tries to preserve the service received by job j, until the time when + it completes in the original schedule. *) + Section ScheduleConstruction. + + (* First, we define the schedule construction function. *) + Section ConstructionStep. + + (* For any time t, suppose that we have generated the schedule prefix in the + interval [0, t). Then, we must define what should be scheduled at time t. *) + Variable sched_prefix: schedule Job. + Variable t: time. + + (* For simplicity, let's define some local names. *) + Let job_is_pending := pending job_arrival inflated_job_cost sched_prefix. + Let job_is_suspended := + suspended_at job_arrival inflated_job_cost job_suspension_duration sched_prefix. + Let actual_job_arrivals_up_to := jobs_arrived_up_to arr_seq. + + (* Recall that a job is ready in the new schedule iff it is pending and not suspended. *) + Let job_is_ready j t := job_is_pending j t && ~~ job_is_suspended j t. + + (* Then, consider the list of ready jobs at time t in the new schedule. *) + Definition ready_jobs := + [seq j_other <- actual_job_arrivals_up_to t | job_is_ready j_other t]. + + (* From the list of ready jobs, we take one of the (possibly many) + highest-priority jobs, or None, in case there are no ready jobs. *) + Definition highest_priority_job := seq_min (higher_eq_priority t) ready_jobs. + + (* Then, we construct the new schedule at time t as follows. + a) If the currently scheduled job in sched_susp is ready to execute in the new schedule + and has highest priority, pick that job. + b) Else, pick one of the highest priority ready jobs in the new schedule. *) + Definition build_schedule : option Job := + if highest_priority_job is Some j_hp then + if sched_susp t is Some j_in_susp then + if job_is_ready j_in_susp t && higher_eq_priority t j_in_susp j_hp then + Some j_in_susp + else + Some j_hp + else Some j_hp + else None. + + End ConstructionStep. + + (* To conclude, starting from the empty schedule, ...*) + Let empty_schedule : schedule Job := fun t => None. + + (* ...we use the recursive definition above to construct the new schedule. *) + Definition sched_susp_highercost := + build_schedule_from_prefixes build_schedule empty_schedule. + + End ScheduleConstruction. + + End Reduction. + +End SustainabilitySingleCost. \ No newline at end of file diff --git a/analysis/uni/susp/sustainability/singlecost/reduction_properties.v b/analysis/uni/susp/sustainability/singlecost/reduction_properties.v new file mode 100644 index 000000000..1389856cc --- /dev/null +++ b/analysis/uni/susp/sustainability/singlecost/reduction_properties.v @@ -0,0 +1,741 @@ +Require Import rt.util.all. +Require Import rt.model.priority rt.model.suspension. +Require Import rt.model.arrival.basic.arrival_sequence. +Require Import rt.model.schedule.uni.response_time. +Require Import rt.model.schedule.uni.susp.suspension_intervals + rt.model.schedule.uni.susp.schedule + rt.model.schedule.uni.susp.platform. +Require Import rt.analysis.uni.susp.sustainability.singlecost.reduction. +Require Import rt.model.schedule.uni.transformation.construction. + +From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop. + +(* In this file, we prove that the in the generated suspension-aware schedule, + the response time of the job whose cost is inflated does not decrease. *) +Module SustainabilitySingleCostProperties. + + Import ScheduleWithSuspensions Suspension Priority SuspensionIntervals + PlatformWithSuspensions ResponseTime ScheduleConstruction. + + Module reduction := SustainabilitySingleCost. + + Section ReductionProperties. + + Context {Job: eqType}. + Variable job_arrival: Job -> time. + Variable job_cost: Job -> time. + + (** Basic Setup & Setting*) + + (* Consider any job arrival sequence with consistent job arrivals. *) + Variable arr_seq: arrival_sequence Job. + Hypothesis H_arrival_times_are_consistent: + arrival_times_are_consistent job_arrival arr_seq. + + (* Assume any (schedule-independent) JLDP policy that is reflexive, transitive and total, + i.e., that indicates "higher-or-equal priority". *) + Variable higher_eq_priority: JLDP_policy Job. + Hypothesis H_priority_is_reflexive: JLDP_is_reflexive higher_eq_priority. + Hypothesis H_priority_is_transitive: JLDP_is_transitive higher_eq_priority. + Hypothesis H_priority_is_total: JLDP_is_total arr_seq higher_eq_priority. + + (* Next, consider any suspension-aware schedule of the arrival sequence... *) + Variable sched_susp: schedule Job. + Hypothesis H_jobs_come_from_arrival_sequence: + jobs_come_from_arrival_sequence sched_susp arr_seq. + + (* ...and the associated job suspension times. *) + Variable job_suspension_duration: job_suspension Job. + + (* Assume that, in this schedule, jobs only execute after they arrive... *) + Hypothesis H_jobs_must_arrive_to_execute: + jobs_must_arrive_to_execute job_arrival sched_susp. + + (* ...and no longer than their execution costs. *) + Hypothesis H_completed_jobs_dont_execute: + completed_jobs_dont_execute job_cost sched_susp. + + (* Also assume that the schedule is work-conserving if there are non-suspended jobs, ... *) + Hypothesis H_work_conserving: + work_conserving job_arrival job_cost job_suspension_duration arr_seq sched_susp. + + (* ...that the schedule respects job priorities... *) + Hypothesis H_respects_priority: + respects_JLDP_policy job_arrival job_cost job_suspension_duration arr_seq + sched_susp higher_eq_priority. + + (* ...and that suspended jobs are not allowed to be scheduled. *) + Hypothesis H_respects_self_suspensions: + respects_self_suspensions job_arrival job_cost job_suspension_duration sched_susp. + + (** Reduction Setup *) + + (* Now we prove properties about the reduction. Let j be any job. *) + Variable j: Job. + Let arr_j := job_arrival j. + + (* Next, consider any job cost inflation... *) + Variable inflated_job_cost: Job -> time. + + (* ...that does not decrease the cost of job j... *) + Hypothesis H_cost_of_j_does_not_decrease: inflated_job_cost j >= job_cost j. + + (* ...and that preserves the cost of the remaining jobs. *) + Hypothesis H_inflation_only_for_job_j: + forall any_j, + any_j != j -> + inflated_job_cost any_j = job_cost any_j. + + (* Recall the schedule we constructed from sched_susp by inflating the cost of job j. *) + Let sched_susp_highercost := reduction.sched_susp_highercost job_arrival arr_seq higher_eq_priority + sched_susp job_suspension_duration inflated_job_cost. + + (* For simplicity, we define some local names for the definitions related to both schedules. *) + Let job_response_time_in_sched_susp_bounded_by := + is_response_time_bound_of_job job_arrival job_cost sched_susp. + Let job_response_time_in_sched_susp_highercost_bounded_by := + is_response_time_bound_of_job job_arrival inflated_job_cost sched_susp_highercost. + Let ready_jobs := reduction.ready_jobs job_arrival arr_seq job_suspension_duration + inflated_job_cost sched_susp_highercost. + Let hp_job := reduction.highest_priority_job job_arrival arr_seq higher_eq_priority + job_suspension_duration inflated_job_cost sched_susp_highercost. + Let completed_in_sched_susp := completed_by job_cost sched_susp. + Let completed_in_sched_susp_highercost := completed_by inflated_job_cost sched_susp_highercost. + Let suspended_in_sched_susp := + suspended_at job_arrival job_cost job_suspension_duration sched_susp. + Let suspended_in_sched_susp_highercost := + suspended_at job_arrival inflated_job_cost job_suspension_duration sched_susp_highercost. + Let service_in_sched_susp := service sched_susp. + Let service_in_sched_susp_highercost := service sched_susp_highercost. + + (** Properties of the Schedule Construction *) + + (* In this section, we prove that the new schedule uses its construction function. *) + Section PropertiesOfScheduleConstruction. + + (* Recall the construction function of the new schedule. *) + Let build_schedule := reduction.build_schedule job_arrival arr_seq higher_eq_priority + sched_susp job_suspension_duration inflated_job_cost. + + (* By showing that the construction function depends only on the schedule prefix, ... *) + Lemma sched_susp_highercost_depends_only_on_prefix: + forall sched1 sched2 t, + (forall t0, t0 < t -> sched1 t0 = sched2 t0) -> + build_schedule sched1 t = build_schedule sched2 t. + Proof. + intros sched1 sched2 t ALL. + rewrite /build_schedule /reduction.build_schedule /reduction.highest_priority_job. + have COMP: forall j, completed_by inflated_job_cost sched1 j t = + completed_by inflated_job_cost sched2 j t. + { + intros j0; rewrite /completed_by; f_equal. + apply eq_big_nat; move => i /= LTi. + by rewrite /service_at /scheduled_at ALL. + } + set pend1 := pending _ _ sched1. + set pend2 := pending _ _ sched2. + set susp1 := suspended_at _ _ _ sched1. + set susp2 := suspended_at _ _ _ sched2. + have SAME: forall j, pend1 j t = pend2 j t. + { + intros j0; rewrite /pend1 /pend2 /pending. + by case: has_arrived => //=; rewrite COMP. + } + set readyjobs := reduction.ready_jobs _ _ _ _. + have EX: forall j0, [exists t0: 'I_t, scheduled_at sched1 j0 t0] = + [exists t0: 'I_t, scheduled_at sched2 j0 t0]. + { + intros j0; apply/idP/idP. + - by move => /existsP [t0 SCHED]; apply/existsP; exists t0; rewrite /scheduled_at -ALL. + - by move => /existsP [t0 SCHED]; apply/existsP; exists t0; rewrite /scheduled_at ALL. + } + have BEG: forall j0, time_after_last_execution job_arrival sched1 j0 t = + time_after_last_execution job_arrival sched2 j0 t. + { + intros j0; rewrite /time_after_last_execution EX. + case: ifP => _; last by done. + by f_equal; apply eq_bigl; intros t0; rewrite /scheduled_at ALL. + } + have SUSP: forall j0, has_arrived job_arrival j0 t -> + suspension_duration job_arrival job_suspension_duration sched1 j0 t = + suspension_duration job_arrival job_suspension_duration sched2 j0 t. + { + intros j0 ARR0; rewrite /suspension_duration BEG; f_equal. + rewrite /service /service_during big_nat_cond [in RHS]big_nat_cond. + apply congr_big; try (by done). + move => i /= /andP [LTi _]. + rewrite /service_at /scheduled_at ALL //. + apply: (leq_trans LTi). + by apply last_execution_bounded_by_identity. + } + have SAMEsusp: forall j0, has_arrived job_arrival j0 t -> susp1 j0 t = susp2 j0 t. + by intros j0 ARR0; rewrite /susp1 /susp2 /suspended_at COMP BEG SUSP. + have SAMEready: readyjobs sched1 t = readyjobs sched2 t. + { + apply eq_in_filter; intros j0 IN. + rewrite -/pend1 -/pend2 SAME; f_equal. + rewrite /suspended_at COMP BEG SUSP; first by done. + by rewrite /has_arrived -ltnS; eapply in_arrivals_implies_arrived_before; eauto 1. + } + rewrite SAMEready; desf; try (by done); rewrite SAME SAMEsusp in Heq1; try (by done); + by apply H_jobs_must_arrive_to_execute; apply/eqP. + Qed. + + (* ...we infer that the new schedule is indeed based on the construction function. *) + Corollary sched_susp_highercost_uses_construction_function: + forall t, + sched_susp_highercost t = build_schedule sched_susp_highercost t. + Proof. + by ins; apply prefix_dependent_schedule_construction, + sched_susp_highercost_depends_only_on_prefix. + Qed. + + End PropertiesOfScheduleConstruction. + + (** Basic Properties of the Generated Schedule *) + + (* In this section, we prove that sched_susp_highercost is a valid suspension-aware schedule. *) + Section ScheduleIsValid. + + (* First, we show that scheduled jobs come from the arrival sequence. *) + Lemma sched_susp_highercost_jobs_come_from_arrival_sequence: + jobs_come_from_arrival_sequence sched_susp_highercost arr_seq. + Proof. + rename H_jobs_come_from_arrival_sequence into FROM. + move => j0 t /eqP SCHED. + rewrite sched_susp_highercost_uses_construction_function /reduction.build_schedule + -/hp_job in SCHED. + destruct (hp_job t) as [j_hp|] eqn:HP; last by done. + have IN: j_hp \in ready_jobs t. + by rewrite /hp_job /reduction.highest_priority_job in HP; apply seq_min_in_seq in HP. + have ARRhp: arrives_in arr_seq j_hp. + { + rewrite mem_filter in IN; move: IN => /andP [/andP _ IN]. + by apply in_arrivals_implies_arrived in IN. + } + destruct (sched_susp t) eqn:SCHEDs; last by case: SCHED => SAME; subst. + move: SCHEDs => /eqP SCHEDs; apply FROM in SCHEDs. + case: SCHED; case: ifP; first by move => /andP [PEND _]; case => <-. + by move => NOTPEND; case => <-. + Qed. + + (* Next, we show that jobs do not execute before their arrival times... *) + Lemma sched_susp_highercost_jobs_must_arrive_to_execute: + jobs_must_arrive_to_execute job_arrival sched_susp_highercost. + Proof. + have FUNC := sched_susp_highercost_uses_construction_function. + rename H_jobs_must_arrive_to_execute into MUST. + move => j0 t /eqP SCHED. + rewrite FUNC /reduction.build_schedule -/hp_job in SCHED. + destruct (hp_job t) as [j_hp|] eqn:HP; last by done. + have IN: j_hp \in ready_jobs t. + by rewrite /hp_job /reduction.highest_priority_job in HP; apply seq_min_in_seq in HP. + have ARRhp: has_arrived job_arrival j_hp t. + { + rewrite mem_filter in IN; move: IN => /andP [/andP _ IN]. + by eapply in_arrivals_implies_arrived_before in IN; eauto. + } + destruct (sched_susp t) eqn:SCHEDs; last by case: SCHED => SAME; subst. + case: SCHED; case: ifP; + first by move => /andP [PEND _]; case => <-; apply MUST; apply/eqP. + by move => NOTPEND; case => <-. + Qed. + + (* ...nor longer than their execution costs. *) + Lemma sched_susp_highercost_completed_jobs_dont_execute: + completed_jobs_dont_execute inflated_job_cost sched_susp_highercost. + Proof. + intros j0 t. + induction t; + first by rewrite /service /service_during big_geq //. + rewrite /service /service_during big_nat_recr //=. + rewrite leq_eqVlt in IHt; move: IHt => /orP [/eqP EQ | LT]; last first. + { + apply: leq_trans LT; rewrite -addn1. + by apply leq_add; last by apply leq_b1. + } + rewrite -[inflated_job_cost _]addn0; apply leq_add; first by rewrite -EQ. + rewrite leqn0 eqb0 /scheduled_at. + rewrite sched_susp_highercost_uses_construction_function /reduction.build_schedule + -/hp_job. + destruct (hp_job t) as [j_hp|] eqn:HP; last by done. + have IN: j_hp \in ready_jobs t. + by rewrite /hp_job /reduction.highest_priority_job in HP; apply seq_min_in_seq in HP. + have PENDhp: pending job_arrival inflated_job_cost sched_susp_highercost j_hp t. + { + rewrite mem_filter in IN; move: IN => /andP [/andP [PEND _] IN]. + by eapply in_arrivals_implies_arrived_before in IN; eauto. + } + destruct (sched_susp t) eqn:SCHEDs; last first. + { + apply/eqP; case => SAME; subst j0. + suff NOTPEND: ~~ pending job_arrival inflated_job_cost sched_susp_highercost j_hp t. + by rewrite PENDhp in NOTPEND. + by rewrite /pending negb_and; apply/orP; right; rewrite negbK; apply/eqP. + } + { + case: ifP => [PEND | NOTPEND]; apply/eqP; case => SAME; subst j0; + last by move: PENDhp => /andP [_ NC]; rewrite /completed_by EQ eq_refl in NC. + move: PEND => /andP [/andP [/andP [_ NC] _] _]. + by rewrite /completed_by EQ eq_refl in NC. + } + Qed. + + (* In addition, we prove that the new schedule is work-conserving,... *) + Lemma sched_susp_highercost_work_conserving: + work_conserving job_arrival inflated_job_cost job_suspension_duration + arr_seq sched_susp_highercost. + Proof. + intros j0 t IN BACK. + move: BACK => /andP [/andP[PEND NOTSCHED] NOTSUSP]. + rewrite /scheduled_at sched_susp_highercost_uses_construction_function + /reduction.build_schedule -/hp_job in NOTSCHED *. + destruct (hp_job) as [j_hp|] eqn:HP. + { + destruct (sched_susp t) eqn:SCHEDs; last by exists j_hp. + by case: ifP; intros _; [exists s | exists j_hp]. + } + { + have IN0: j0 \in ready_jobs t. + { + rewrite mem_filter PEND NOTSUSP /=. + eapply arrived_between_implies_in_arrivals; eauto 1. + by move: PEND => /andP [ARR _]. + } + suff NOTNONE: hp_job t != None by rewrite HP in NOTNONE. + by apply seq_min_exists with (x := j0). + } + Qed. + + (* ...respects job priorities, ... *) + Lemma sched_susp_highercost_respects_policy: + respects_JLDP_policy job_arrival inflated_job_cost job_suspension_duration + arr_seq sched_susp_highercost higher_eq_priority. + Proof. + rename H_priority_is_transitive into TRANS, H_priority_is_total into TOTAL, + H_priority_is_reflexive into REFL. + move => j1 j2 t IN BACK /eqP SCHED. + move: BACK => /andP [/andP [PEND NOTSCHED] NOTSUSP]. + rewrite /scheduled_at sched_susp_highercost_uses_construction_function /reduction.build_schedule + -/hp_job in SCHED NOTSCHED *. + set pend := pending _ _ _ in SCHED NOTSCHED. + have ALL: forall j_hi j_lo, hp_job t = Some j_hi -> + j_lo \in ready_jobs t -> + higher_eq_priority t j_hi j_lo. + { + intros j_hi j_lo SOME INlo; move: SCHED => MIN. + rewrite /hp_job /reduction.highest_priority_job in SOME. + apply seq_min_computes_min with (y := j_lo) in SOME; try (by done). + intros x y; rewrite mem_filter [y \in _]mem_filter /jobs_arrived_up_to. + move => /andP [_ INx] /andP [_ INy]. + by apply TOTAL; eapply in_arrivals_implies_arrived; eauto 1. + } + destruct (hp_job t) as [j_hp |] eqn:HP; last by done. + have HIGHER: higher_eq_priority t j_hp j1. + { + apply ALL; first by done. + move: PEND => /andP [ARR NOTCOMP]. + rewrite mem_filter /pending ARR NOTCOMP NOTSUSP /=. + by eapply arrived_between_implies_in_arrivals; eauto 1. + } + destruct (sched_susp t) eqn:SCHEDs; last by case: SCHED => SAME; subst j2. + move: SCHED; case: ifP => [/andP[ PENDs HPs] | NOTPENDs]; case => SAME; subst; + first by apply (TRANS t (j_hp)). + apply ALL; first by done. + move: PEND => /andP [ARR NOTCOMP]. + rewrite mem_filter /pending ARR NOTCOMP NOTSUSP /=. + by eapply arrived_between_implies_in_arrivals; eauto 1. + Qed. + + (* ...and does not allow suspended jobs to be scheduled. *) + Lemma sched_susp_highercost_respects_self_suspensions: + respects_self_suspensions job_arrival inflated_job_cost + job_suspension_duration sched_susp_highercost. + Proof. + rename H_respects_self_suspensions into SELF. + move => j0 t /eqP SCHED SUSP. + set suspended := suspended_at _ _ _ _ in SUSP. + rewrite sched_susp_highercost_uses_construction_function /reduction.build_schedule + -/hp_job in SCHED. + destruct (hp_job t) as [j_hp|] eqn:HP; last by done. + have IN: j_hp \in ready_jobs t. + by rewrite /hp_job /reduction.highest_priority_job in HP; apply seq_min_in_seq in HP. + have NOTSUSP: ~~ suspended j_hp t. + { + by rewrite mem_filter in IN; move: IN => /andP [/andP [_ NOTs] _]. + } + destruct (sched_susp t) eqn:SCHEDs; + last by case: SCHED => SAME; subst; rewrite SUSP in NOTSUSP. + case: SCHED; case: ifP; last by move => _; case => SAME; subst; rewrite SUSP in NOTSUSP. + move: SCHEDs; move => /eqP SCHEDs /andP [/andP [PEND NOTSUSPs] _]; case => SAME; subst. + by rewrite -/suspended SUSP in NOTSUSPs. + Qed. + + End ScheduleIsValid. + + (** Scheduling Invariant *) + + (* In this section, we compare the two schedules to determine they are the same + while job j has not completed in sched_susp. *) + Section SchedulingInvariant. + + (* To prove that the schedules are the same, we use induction over time. *) + Section InductiveStep. + + (* Consider any time t by which job j has not completed in sched_susp. *) + Variable t: time. + Hypothesis H_j_has_not_completed: ~~ completed_in_sched_susp j t. + + (* Induction Hypothesis: + Assume that at every time prior to time t, any job that is scheduled + in sched_susp is also scheduled in sched_susp_highercost. *) + Hypothesis H_schedules_are_the_same: + forall k any_j, + k < t -> + scheduled_at sched_susp any_j k = scheduled_at sched_susp_highercost any_j k. + + (* Then, let k be any time no later than t. *) + Variable k: time. + Hypothesis H_k_before_t: k <= t. + + (* First, we prove that jobs complete at time k in sched_susp iff they + complete in the new schedule. *) + Lemma sched_susp_highercost_same_completion: + forall any_j, + completed_in_sched_susp any_j k = completed_in_sched_susp_highercost any_j k. + Proof. + rename H_j_has_not_completed into NOTCOMPj, + H_schedules_are_the_same into IH, H_cost_of_j_does_not_decrease into COSTj, + H_inflation_only_for_job_j into COSTother. + have COMPLETIONw := sched_susp_highercost_completed_jobs_dont_execute. + rewrite /completed_in_sched_susp/completed_in_sched_susp_highercost + /completed_by in NOTCOMPj *. + intros any_j; apply/idP/idP. + { + intros COMPs. + case (boolP (any_j == j)) => [/eqP EQ | NEQ]; subst. + { + suff BUG: service sched_susp j t == job_cost j by rewrite BUG in NOTCOMPj. + by apply completion_monotonic with (t0 := k); try (by done); apply ltnW. + } + rewrite COSTother //; move: COMPs => /eqP <-. + apply/eqP; rewrite /service /service_during big_nat_cond [X in _=X]big_nat_cond. + apply eq_bigr; move => i /= /andP [LT _]. + by rewrite /service_at IH //; apply: (leq_trans LT). + } + { + intros COMPw. + rewrite eqn_leq; apply/andP; split; + first by apply cumulative_service_le_job_cost. + apply leq_trans with (n := inflated_job_cost any_j); + first by case (boolP (any_j==j)) => [/eqP EQ | NEQ]; subst; rewrite ?COSTj ?COSTother. + move: COMPw => /eqP <-. + apply leq_sum_nat; move => i /= LT _. + by rewrite /service_at IH //; apply: (leq_trans LT). + } + Qed. + + (* We also prove the execution patterns of the jobs coincide... *) + Lemma sched_susp_highercost_same_time_after_last_exec: + forall any_j, + time_after_last_execution job_arrival sched_susp any_j k = + time_after_last_execution job_arrival sched_susp_highercost any_j k. + Proof. + rename H_schedules_are_the_same into IH. + intros any_j; rewrite /time_after_last_execution. + have EXsame: [exists t0:'I_k, scheduled_at sched_susp any_j t0] = + [exists t0:'I_k, scheduled_at sched_susp_highercost any_j t0]. + { + by apply/idP/idP; move => /existsP [t0 LT0]; + apply/existsP; exists t0; rewrite ?IH//-?IH//; apply leq_trans with (n := k). + } + rewrite EXsame {EXsame}; case: ifP => [EX | _]; last by done. + f_equal; apply eq_bigl; intros i; rewrite IH //. + by apply leq_trans with (n := k). + Qed. + + (* ...and the jobs have the same suspension intervals, ... *) + Lemma sched_susp_highercost_same_suspension_duration: + forall any_j, + has_arrived job_arrival any_j k -> + suspension_duration job_arrival job_suspension_duration sched_susp any_j k = + suspension_duration job_arrival job_suspension_duration sched_susp_highercost any_j k. + Proof. + intros any_j ARR. + rewrite /suspension_duration /service /service_during; f_equal. + rewrite sched_susp_highercost_same_time_after_last_exec. + rewrite big_nat_cond [X in _ = X]big_nat_cond; apply eq_bigr. + move => i /= /andP [LT _]. + rewrite /service_at H_schedules_are_the_same //. + apply leq_trans with (n := k); last by done. + apply: (leq_trans LT). + by apply last_execution_bounded_by_identity. + Qed. + + (* ...which implies that jobs suspend at time k in sched_susp iff they suspend + in the new schedule as well. *) + Lemma sched_susp_highercost_same_suspension: + forall any_j, + has_arrived job_arrival any_j k -> + suspended_in_sched_susp any_j k = suspended_in_sched_susp_highercost any_j k. + Proof. + intros any_j ARR. + rewrite /suspended_in_sched_susp /suspended_in_sched_susp_highercost /suspended_at. + rewrite -/completed_in_sched_susp_highercost -sched_susp_highercost_same_completion. + rewrite sched_susp_highercost_same_time_after_last_exec. + by rewrite sched_susp_highercost_same_suspension_duration. + Qed. + + (* Using the lemmas above, we conclude the inductive step by showing that the + two schedules are the same at time k. *) + Lemma sched_susp_highercost_same_schedule: + forall any_j, + scheduled_at sched_susp any_j k = scheduled_at sched_susp_highercost any_j k. + Proof. + have FUNC := sched_susp_highercost_uses_construction_function. + have SELFw := sched_susp_highercost_respects_self_suspensions. + have COMPLETIONw := sched_susp_highercost_completed_jobs_dont_execute. + have LEMMAcomp := sched_susp_highercost_same_completion. + have LEMMAsusp := sched_susp_highercost_same_suspension. + rename H_jobs_must_arrive_to_execute into MUSTARR, + H_cost_of_j_does_not_decrease into HIGHER, + H_inflation_only_for_job_j into SAME, + H_respects_self_suspensions into SELFs, + H_work_conserving into WORKs, H_priority_is_reflexive into REFL, + H_respects_priority into PRIOs. + rewrite /service_in_sched_susp /service_in_sched_susp_highercost /service /service_during. + suff EQsched: sched_susp k = sched_susp_highercost k by rewrite /scheduled_at EQsched. + rewrite /scheduled_at FUNC /reduction.build_schedule -/hp_job. + destruct (hp_job k) as [j_hp|] eqn:HP; last first. + { + destruct (sched_susp k) as [j_s|] eqn:SCHEDs; last by done. + suff NOTNONE: hp_job k != None by rewrite HP in NOTNONE. + apply seq_min_exists with (x := j_s). + have NOTCOMPs: ~~ completed_in_sched_susp j_s k. + { + apply/negP; intros COMP'. + apply completed_implies_not_scheduled in COMP'; try (by done). + by rewrite /scheduled_at SCHEDs eq_refl in COMP'. + } + have ARR: has_arrived job_arrival j_s k by apply MUSTARR; apply/eqP. + have NOTCOMPw: ~~ completed_by inflated_job_cost sched_susp_highercost j_s k. + by rewrite -/completed_in_sched_susp_highercost -LEMMAcomp //. + have NOTSUSPs: ~~ suspended_in_sched_susp j_s k by apply/negP; apply SELFs; apply/eqP. + have NOTSUSPw: ~~ suspended_in_sched_susp_highercost j_s k by rewrite -LEMMAsusp //. + have ARRw: j_s \in jobs_arrived_up_to arr_seq k. + { + eapply arrived_between_implies_in_arrivals; eauto 1. + by apply H_jobs_come_from_arrival_sequence with (t := k); apply/eqP. + } + by rewrite mem_filter; repeat (apply/andP; split). + } + { + have IN: j_hp \in ready_jobs k. + by apply seq_min_in_seq with (rel := higher_eq_priority k). + rewrite mem_filter in IN; move: IN => /andP [/andP [/andP [ARRhp NOTCOMPhp] NOTSUSP] IN]. + have ARRINhp: arrives_in arr_seq j_hp by apply in_arrivals_implies_arrived in IN. + destruct (sched_susp k) as [j_s|] eqn:SCHEDs. + { + case: ifP => // NOTPEND. + have PENDw: pending job_arrival inflated_job_cost sched_susp_highercost j_s k. + { + apply/andP; split; first by apply MUSTARR; apply/eqP. + rewrite -/completed_in_sched_susp_highercost -LEMMAcomp //. + apply/negP; intros COMPs. + apply completed_implies_not_scheduled in COMPs; try (by done). + by rewrite /scheduled_at SCHEDs eq_refl in COMPs. + } + have ARRs: has_arrived job_arrival j_s k by apply MUSTARR; apply/eqP. + have NOTSUSPs: ~~ suspended_in_sched_susp j_s k by apply/negP; apply SELFs; apply/eqP. + have NOTSUSPw: ~~ suspended_in_sched_susp_highercost j_s k by rewrite -LEMMAsusp. + rewrite -/suspended_in_sched_susp_highercost PENDw NOTSUSPw /= in NOTPEND. + suff PRIOINV: higher_eq_priority k j_s j_hp by rewrite PRIOINV in NOTPEND. + apply PRIOs; try (by done); last by apply/eqP. + have NOTCOMPhp': ~~ completed_by job_cost sched_susp j_hp k. + by rewrite -/completed_in_sched_susp LEMMAcomp. + have NOTSCHEDhp: ~~ scheduled_at sched_susp j_hp k. + { + apply/negP; intro SCHEDs'. + apply only_one_job_scheduled with (j1 := j_s) in SCHEDs'; subst; last by apply/eqP. + suff BUG: higher_eq_priority k j_hp j_hp by rewrite BUG in NOTPEND. + by apply REFL. + } + have NOTSUSPhp: ~~ suspended_in_sched_susp j_hp k by rewrite LEMMAsusp. + by repeat (apply/andP; split). + } + { + have NOTCOMPhp': ~~ completed_by job_cost sched_susp j_hp k. + by rewrite -/completed_in_sched_susp LEMMAcomp. + have NOTSCHEDhp: ~~ scheduled_at sched_susp j_hp k by rewrite /scheduled_at SCHEDs. + have NOTSUSPhp: ~~ suspended_in_sched_susp j_hp k by rewrite LEMMAsusp. + feed (WORKs j_hp k ARRINhp); first by repeat (apply/andP; split). + move: WORKs => [j_x SCHEDx]. + by rewrite /scheduled_at SCHEDs in SCHEDx. + } + } + Qed. + + End InductiveStep. + + (* Using the inductive step above, we prove that before the completion of job j + in sched_susp, the two schedules are exactly the same. *) + Lemma scheduled_in_susp_iff_scheduled_in_wcet: + forall t any_j, + ~~ completed_in_sched_susp j t -> + scheduled_at sched_susp any_j t = scheduled_at sched_susp_highercost any_j t. + Proof. + have LEMMAsched := sched_susp_highercost_same_schedule. + induction t as [t IHtmp] using strong_ind. + intros j' NOTCOMP. + suff IH: forall k any_j, k < t -> + scheduled_at sched_susp any_j k = scheduled_at sched_susp_highercost any_j k. + by apply LEMMAsched with (t := t). + intros k any_j LT; apply IHtmp; first by done. + apply/negP; intro COMPk. + suff COMPt: completed_in_sched_susp j t by rewrite COMPt in NOTCOMP. + by apply completion_monotonic with (t0 := k); [| apply ltnW|]. + Qed. + + End SchedulingInvariant. + + (** Comparison of Response-time Bounds *) + + (* In this section, we use the scheduling invariant above to compare response-time bounds + for job j in both schedules. *) + Section ComparingResponseTimes. + + (* Assume that job j has positive cost. *) + Hypothesis H_cost_j_positive: job_cost j > 0. + + (* Also assume that the response time of job j in sched_susp is equal to some value r... *) + Variable r: time. + Hypothesis H_response_time_bound_in_sched_susp: + job_response_time_in_sched_susp_bounded_by j r. + Hypothesis H_response_time_bound_is_tight: + forall r', job_response_time_in_sched_susp_bounded_by j r' -> r <= r'. + + (* ...and that the response time of j in the new schedule is upper-bounded by some value R. *) + Variable R: time. + Hypothesis H_response_time_bound_in_sched_susp_highercost: + job_response_time_in_sched_susp_highercost_bounded_by j R. + + (* Using the scheduling invariant, we show that job j receives the same service + in both schedules up to time (arr_j + r). *) + Lemma sched_susp_highercost_same_service_for_j: + forall t, + t <= arr_j + r -> + service_in_sched_susp j t = service_in_sched_susp_highercost j t. + Proof. + rename H_response_time_bound_is_tight into TIGHT. + have IFF := scheduled_in_susp_iff_scheduled_in_wcet. + rewrite /service_in_sched_susp /service_in_sched_susp_highercost /service /service_during. + induction t; first by intros _; rewrite big_geq // big_geq. + intros LT. + feed IHt; first by apply ltnW. + rewrite big_nat_recr // big_nat_recr //=. + f_equal; first by done. + rewrite /service_at IFF; first by done. + apply/negP; intro COMPt. + suff BUG: t >= arr_j + r by rewrite leqNgt LT in BUG. + have AFTER: arr_j <= t. + { + apply contraT; rewrite -ltnNge; intro BEFORE. + suff BUG: ~~ completed_in_sched_susp j t by rewrite COMPt in BUG. + rewrite /completed_in_sched_susp /completed_by /service /service_during. + rewrite (cumulative_service_before_job_arrival_zero job_arrival) //; last by apply ltnW. + by rewrite eq_sym -lt0n. + } + rewrite -[t](addKn arr_j) -addnBA //. + rewrite leq_add2l; apply TIGHT. + rewrite /job_response_time_in_sched_susp_bounded_by /is_response_time_bound_of_job. + by rewrite subnKC. + Qed. + + (* Next, since r is an exact response time bound, we show that r <= R... *) + Lemma sched_susp_highercost_r_le_R: r <= R. + Proof. + have SAME := sched_susp_highercost_same_service_for_j. + rename H_response_time_bound_in_sched_susp_highercost into RESPw, + H_response_time_bound_in_sched_susp into RESPs, + H_response_time_bound_is_tight into TIGHT, + H_cost_of_j_does_not_decrease into COSTj. + unfold job_response_time_in_sched_susp_bounded_by, service_in_sched_susp_highercost, + job_response_time_in_sched_susp_highercost_bounded_by, service_in_sched_susp, + is_response_time_bound_of_job, completed_by, service in *. + set Sw := service_during sched_susp_highercost in RESPw RESPs TIGHT SAME. + set Ss := service_during sched_susp in RESPs TIGHT SAME. + apply contraT; rewrite -ltnNge; intros LT. + suff BUG: job_cost j > inflated_job_cost j by rewrite ltnNge COSTj in BUG. + move: RESPw RESPs => /eqP RESPw /eqP RESPs; rewrite -RESPw -RESPs. + rewrite /Ss /service_during. + rewrite -> big_cat_nat with (n := arr_j + R); + [simpl | by done | by rewrite leq_add2l ltnW]. + rewrite -addn1; apply leq_add; + first by rewrite -SAME; [apply leqnn | rewrite leq_add2l ltnW]. + rewrite lt0n; apply/eqP; intro ZERO. + suff BUG: R >= r by rewrite leqNgt LT in BUG. + apply TIGHT. + rewrite -(eqn_add2r 0) -{2}ZERO addn0. + rewrite -big_cat_nat //=; last by rewrite leq_add2l ltnW. + by rewrite -RESPs. + Qed. + + (* ...and also prove that R must be as large as the inflated job cost. *) + Lemma R_bounds_inflated_cost: R >= inflated_job_cost j. + Proof. + apply leq_trans with (n := service sched_susp_highercost j (arr_j + R)); + first by apply eq_leq; symmetry; apply/eqP. + rewrite /service /service_during. + rewrite (ignore_service_before_arrival job_arrival) //; first last. + - by apply leq_addr. + - by apply sched_susp_highercost_jobs_must_arrive_to_execute. + by apply cumulative_service_le_delta. + Qed. + + (* To conclude, we prove that the difference between the response-time bound and the job cost + is larger in the new schedule. *) + Theorem sched_susp_highercost_incurs_more_interference: + r - job_cost j <= R - inflated_job_cost j. + Proof. + have GECOST := R_bounds_inflated_cost. + have LEQ := sched_susp_highercost_r_le_R. + have SAME := sched_susp_highercost_same_service_for_j. + rename H_response_time_bound_in_sched_susp into RESPs, + H_response_time_bound_in_sched_susp_highercost into RESPw. + rewrite leq_subLR. + rewrite addnBA; last by apply GECOST. + apply leq_trans with (n := service_in_sched_susp j (arr_j + r) + R + - service_in_sched_susp_highercost j (arr_j + R)); + last by apply leq_sub; [rewrite leq_add2r|]; apply eq_leq; [|symmetry]; apply/eqP. + rewrite addnC. + rewrite /service_in_sched_susp /service_in_sched_susp_highercost /service + /service_during in SAME *. + rewrite -> big_cat_nat with (n := arr_j+r) (p := arr_j+R); + [simpl | by done | by rewrite leq_add2l]. + feed (SAME (arr_j + r)); first by apply leqnn. + rewrite -/(service_during sched_susp_highercost _ _ _) + -/(service_during sched_susp_highercost _ _ _) + -/(service_during sched_susp _ _ _) in SAME *. + set Ss := service_during sched_susp in SAME *; set Sw := service_during sched_susp_highercost. + rewrite -subnBA; + last by apply leq_trans with (n := Sw j 0 (arr_j + r)); [rewrite SAME | apply leq_addr]. + rewrite addnC -addnBA; last by rewrite SAME. + apply leq_trans with (n := R - (Sw j (arr_j + r) (arr_j + R) + 0)); + last by rewrite leq_sub2l // leq_add2l; apply eq_leq; apply/eqP; rewrite subn_eq0 SAME. + rewrite addn0 subh3 //; last first. + { + apply leq_trans with (n := Sw j arr_j (arr_j+R)); last by apply cumulative_service_le_delta. + by apply extend_sum; first by apply leq_addr. + } + { + apply leq_trans with (n := r + \sum_(arr_j+r<=t<arr_j+R) 1); + first by rewrite leq_add2l; apply leq_sum; intros t _; apply leq_b1. + by simpl_sum_const; rewrite subnDl subnKC. + } + Qed. + + End ComparingResponseTimes. + + End ReductionProperties. + +End SustainabilitySingleCostProperties. \ No newline at end of file diff --git a/implementation/apa/schedule.v b/implementation/apa/schedule.v index 263da8fd8..486e6f026 100644 --- a/implementation/apa/schedule.v +++ b/implementation/apa/schedule.v @@ -146,7 +146,7 @@ Module ConcreteScheduler. (* Let alpha be an affinity associated with each task. *) Variable alpha: task_affinity sporadic_task num_cpus. - (* Let arr_seq be any job arrival sequence with consistent, non-duplicate arrivals. *) + (* Let arr_seq be any job arrival sequence with consistent, duplicate-free arrivals. *) Variable arr_seq: arrival_sequence Job. Hypothesis H_arrival_times_are_consistent: arrival_times_are_consistent job_arrival arr_seq. Hypothesis H_arrival_sequence_is_a_set: arrival_sequence_is_a_set arr_seq. diff --git a/implementation/global/basic/schedule.v b/implementation/global/basic/schedule.v index ab0715841..9e8eded3f 100644 --- a/implementation/global/basic/schedule.v +++ b/implementation/global/basic/schedule.v @@ -95,7 +95,7 @@ Module ConcreteScheduler. Variable num_cpus: nat. Hypothesis H_at_least_one_cpu: num_cpus > 0. - (* Let arr_seq be any job arrival sequence with consistent, non-duplicate arrivals. *) + (* Let arr_seq be any job arrival sequence with consistent, duplicate-free arrivals. *) Variable arr_seq: arrival_sequence Job. Hypothesis H_arrival_times_are_consistent: arrival_times_are_consistent job_arrival arr_seq. Hypothesis H_arrival_sequence_is_a_set: arrival_sequence_is_a_set arr_seq. diff --git a/implementation/global/jitter/schedule.v b/implementation/global/jitter/schedule.v index eb8c0cc29..47ab16a7c 100644 --- a/implementation/global/jitter/schedule.v +++ b/implementation/global/jitter/schedule.v @@ -98,7 +98,7 @@ Module ConcreteScheduler. Variable num_cpus: nat. Hypothesis H_at_least_one_cpu: num_cpus > 0. - (* Let arr_seq be any job arrival sequence with consistent, non-duplicate arrivals. *) + (* Let arr_seq be any job arrival sequence with consistent, duplicate-free arrivals. *) Variable arr_seq: arrival_sequence Job. Hypothesis H_arrival_times_are_consistent: arrival_times_are_consistent job_arrival arr_seq. Hypothesis H_arrival_sequence_is_a_set: arrival_sequence_is_a_set arr_seq. diff --git a/implementation/uni/basic/schedule.v b/implementation/uni/basic/schedule.v index 8b77cdd23..742c5d25d 100644 --- a/implementation/uni/basic/schedule.v +++ b/implementation/uni/basic/schedule.v @@ -89,7 +89,7 @@ Module ConcreteScheduler. Variable job_arrival: Job -> time. Variable job_cost: Job -> time. - (* Let arr_seq be any job arrival sequence with consistent, non-duplicate arrivals. *) + (* Let arr_seq be any job arrival sequence with consistent, duplicate-free arrivals. *) Variable arr_seq: arrival_sequence Job. Hypothesis H_arrival_times_are_consistent: arrival_times_are_consistent job_arrival arr_seq. Hypothesis H_arrival_sequence_is_a_set: arrival_sequence_is_a_set arr_seq. diff --git a/implementation/uni/jitter/arrival_sequence.v b/implementation/uni/jitter/arrival_sequence.v index 3f3eb9db4..b88bf013f 100644 --- a/implementation/uni/jitter/arrival_sequence.v +++ b/implementation/uni/jitter/arrival_sequence.v @@ -30,9 +30,7 @@ Module ConcreteArrivalSequence. (* Let ts be any concrete task set with valid parameters. *) Variable ts: concrete_taskset. - Hypothesis H_valid_task_parameters: - valid_sporadic_taskset task_cost task_period task_deadline ts. - + (* Regarding the periodic arrival sequence built from ts, we prove that...*) Let arr_seq := periodic_arrival_sequence ts. @@ -58,22 +56,6 @@ Module ConcreteArrivalSequence. by unfold add_job in *; desf. Qed. - (* ..., jobs have valid parameters, ... *) - Theorem periodic_arrivals_valid_job_parameters: - forall j, - arrives_in arr_seq j -> - valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j. - Proof. - rename H_valid_task_parameters into PARAMS. - unfold valid_sporadic_taskset, is_valid_sporadic_task in *. - move => j [t ARRj]. - rewrite mem_pmap in ARRj; move: ARRj => /mapP [tsk IN SOME]. - unfold add_job in SOME; desf. - specialize (PARAMS tsk IN); des. - unfold valid_sporadic_job, valid_realtime_job, job_cost_positive. - by repeat split; try (by done); apply leqnn. - Qed. - (* ... job arrivals satisfy the sporadic task model, ... *) Theorem periodic_arrivals_are_sporadic: sporadic_task_model task_period job_arrival job_task arr_seq. @@ -105,7 +87,31 @@ Module ConcreteArrivalSequence. apply (pmap_uniq) with (g := job_task); last by destruct ts. by unfold add_job, ocancel; intro tsk; desf. Qed. - + + (* We also show that job costs are bounded by task costs... *) + Theorem periodic_arrivals_job_cost_le_task_cost: + forall j, + arrives_in arr_seq j -> + job_cost j <= task_cost (job_task j). + Proof. + intros j [t ARRj]. + rewrite mem_pmap in ARRj. + move: ARRj => /mapP [tsk_j INj SOMEj]. + by unfold add_job in SOMEj; desf. + Qed. + + (* ...and that job deadlines equal task deadlines. *) + Theorem periodic_arrivals_job_deadline_eq_task_deadline: + forall j, + arrives_in arr_seq j -> + job_deadline j = task_deadline (job_task j). + Proof. + intros j [t ARRj]. + rewrite mem_pmap in ARRj. + move: ARRj => /mapP [tsk_j INj SOMEj]. + by unfold add_job in SOMEj; desf. + Qed. + End Proofs. End ConcreteArrivalSequence. \ No newline at end of file diff --git a/implementation/uni/jitter/fp_rta_example.v b/implementation/uni/jitter/fp_rta_example.v index 1482a0c17..161f6f891 100644 --- a/implementation/uni/jitter/fp_rta_example.v +++ b/implementation/uni/jitter/fp_rta_example.v @@ -33,9 +33,15 @@ Module ResponseTimeAnalysisFP. Program Let ts := Build_set [:: tsk1; tsk2; tsk3] _. (* ...which can be shown to have valid parameters. *) - Fact ts_has_valid_parameters: - valid_sporadic_taskset task_cost task_period task_deadline ts. + Remark ts_has_positive_costs: + forall tsk, tsk \in ts -> task_cost tsk > 0. Proof. + intros tsk IN. + by repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute); by done. + Qed. + Remark ts_has_positive_periods: + forall tsk, tsk \in ts -> task_period tsk > 0. + Proof. intros tsk IN. repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute); by done. Qed. @@ -146,18 +152,19 @@ Module ResponseTimeAnalysisFP. Proof. rename H_jitter_is_bounded into JIT. intros tsk IN. - have VALID := periodic_arrivals_valid_job_parameters ts ts_has_valid_parameters. - have TSVALID := ts_has_valid_parameters. unfold valid_sporadic_job, valid_realtime_job in *; des. apply taskset_schedulable_by_fp_rta with (task_cost := task_cost) (task_period := task_period) (task_deadline := task_deadline) (ts0 := ts) (higher_eq_priority0 := RM task_period) (job_jitter0 := job_jitter) (task_jitter := task_jitter); try (by done). + - by apply ts_has_positive_costs. + - by apply ts_has_positive_periods. - by apply periodic_arrivals_are_consistent. - - by apply periodic_arrivals_is_a_set. + - - apply periodic_arrivals_is_a_set. - by apply periodic_arrivals_all_jobs_from_taskset. - - by intros j ARRj; specialize (VALID j ARRj); des; repeat split; try (apply JIT). - by apply periodic_arrivals_are_sporadic. + - by apply periodic_arrivals_job_cost_le_task_cost. + - by apply periodic_arrivals_job_deadline_eq_task_deadline. - by apply RM_is_reflexive. - by apply RM_is_transitive. - by apply scheduler_jobs_come_from_arrival_sequence. diff --git a/implementation/uni/jitter/schedule.v b/implementation/uni/jitter/schedule.v index 273b2f067..d4581e2c0 100644 --- a/implementation/uni/jitter/schedule.v +++ b/implementation/uni/jitter/schedule.v @@ -90,7 +90,7 @@ Module ConcreteScheduler. Variable job_cost: Job -> time. Variable job_jitter: Job -> time. - (* Assume any job arrival sequence with consistent, non-duplicate arrivals. *) + (* Assume any job arrival sequence with consistent, duplicate-free arrivals. *) Variable arr_seq: arrival_sequence Job. Hypothesis H_arrival_times_are_consistent: arrival_times_are_consistent job_arrival arr_seq. Hypothesis H_no_duplicate_arrivals: arrival_sequence_is_a_set arr_seq. diff --git a/implementation/uni/susp/schedule.v b/implementation/uni/susp/schedule.v index ae9088610..bdcdc0eef 100644 --- a/implementation/uni/susp/schedule.v +++ b/implementation/uni/susp/schedule.v @@ -135,7 +135,7 @@ Module ConcreteScheduler. Variable job_arrival: Job -> time. Variable job_cost: Job -> time. - (* Assume any job arrival sequence with consistent, non-duplicate arrivals... *) + (* Assume any job arrival sequence with consistent, duplicate-free arrivals... *) Variable arr_seq: arrival_sequence Job. Hypothesis H_arrival_times_are_consistent: arrival_times_are_consistent job_arrival arr_seq. Hypothesis H_no_duplicate_arrivals: arrival_sequence_is_a_set arr_seq. diff --git a/model/arrival/basic/arrival_bounds.v b/model/arrival/basic/arrival_bounds.v index 0cfbb40c8..1f7797bbc 100644 --- a/model/arrival/basic/arrival_bounds.v +++ b/model/arrival/basic/arrival_bounds.v @@ -19,7 +19,7 @@ Module ArrivalBounds. Variable job_cost: Job -> time. Variable job_task: Job -> Task. - (* Consider any job arrival sequence with consistent, non-duplicate arrivals. *) + (* Consider any job arrival sequence with consistent, duplicate-free arrivals. *) Variable arr_seq: arrival_sequence Job. Hypothesis H_arrival_times_are_consistent: arrival_times_are_consistent job_arrival arr_seq. Hypothesis H_arrival_sequence_is_a_set: arrival_sequence_is_a_set arr_seq. diff --git a/model/arrival/basic/task_arrival.v b/model/arrival/basic/task_arrival.v index a55fa51a6..d9cb85399 100644 --- a/model/arrival/basic/task_arrival.v +++ b/model/arrival/basic/task_arrival.v @@ -72,7 +72,7 @@ Module TaskArrival. Variable job_arrival: Job -> time. Variable job_task: Job -> Task. - (* Consider any arrival sequence with consistent, non-duplicate arrivals, ... *) + (* Consider any arrival sequence with consistent, duplicate-free arrivals, ... *) Variable arr_seq: arrival_sequence Job. Hypothesis H_consistent_arrivals: arrival_times_are_consistent job_arrival arr_seq. Hypothesis H_no_duplicate_arrivals: arrival_sequence_is_a_set arr_seq. diff --git a/model/arrival/jitter/arrival_bounds.v b/model/arrival/jitter/arrival_bounds.v index 6828eb9dd..65de91276 100644 --- a/model/arrival/jitter/arrival_bounds.v +++ b/model/arrival/jitter/arrival_bounds.v @@ -24,7 +24,7 @@ Module ArrivalBounds. Variable job_jitter: Job -> time. Variable job_task: Job -> Task. - (* Consider any job arrival sequence with consistent, non-duplicate arrivals... *) + (* Consider any job arrival sequence with consistent, duplicate-free arrivals... *) Variable arr_seq: arrival_sequence Job. Hypothesis H_arrival_times_are_consistent: arrival_times_are_consistent job_arrival arr_seq. Hypothesis H_arrival_sequence_is_a_set: arrival_sequence_is_a_set arr_seq. diff --git a/model/arrival/jitter/task_arrival.v b/model/arrival/jitter/task_arrival.v index f92439414..cc27dc598 100644 --- a/model/arrival/jitter/task_arrival.v +++ b/model/arrival/jitter/task_arrival.v @@ -54,7 +54,7 @@ Module TaskArrivalWithJitter. Variable job_jitter: Job -> time. Variable job_task: Job -> Task. - (* Consider any arrival sequence with consistent, non-duplicate arrivals, ... *) + (* Consider any arrival sequence with consistent, duplicate-free arrivals, ... *) Variable arr_seq: arrival_sequence Job. Hypothesis H_consistent_arrivals: arrival_times_are_consistent job_arrival arr_seq. Hypothesis H_no_duplicate_arrivals: arrival_sequence_is_a_set arr_seq. diff --git a/model/schedule/uni/jitter/valid_schedule.v b/model/schedule/uni/jitter/valid_schedule.v new file mode 100644 index 000000000..46c8be09d --- /dev/null +++ b/model/schedule/uni/jitter/valid_schedule.v @@ -0,0 +1,66 @@ +Require Import rt.util.all. +Require Import rt.model.priority. +Require Import rt.model.arrival.basic.arrival_sequence. +Require Import rt.model.schedule.uni.jitter.schedule + rt.model.schedule.uni.jitter.platform. + +From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop. + +(* In this file, we construct a predicate that defines a valid jitter-aware schedule + of a given task set. *) +Module ValidJitterAwareSchedule. + + Import UniprocessorScheduleWithJitter Priority Platform. + + (** Basic Setup & Setting*) + Section DefiningValidSchedule. + + Context {Task: eqType}. + Context {Job: eqType}. + Variable job_arrival: Job -> time. + Variable job_task: Job -> Task. + + (* Consider any job arrival sequence. *) + Variable arr_seq: arrival_sequence Job. + + (* Assume any given job-level policy. *) + Variable higher_eq_priority: JLDP_policy Job. + + (** Definition of the Jitter-Aware Schedule *) + + (* Consider any job cost and job jitter functions. *) + Variable job_cost: Job -> time. + Variable job_jitter: Job -> time. + + (* Let sched be any schedule. *) + Variable sched: schedule Job. + + (* For sched to denote a valid jitter-aware schedule of ts, the following properties must hold. *) + + (* 1) All scheduled jobs must come from the arrival sequence. *) + Let H1_jobs_come_from_arrival_sequence := jobs_come_from_arrival_sequence sched arr_seq. + + (* 2) Jobs only execute after the jitter has passed. *) + Let H2_jobs_execute_after_jitter := jobs_execute_after_jitter job_arrival job_jitter sched. + + (* 3) Jobs do not execute for longer than their costs. *) + Let H3_completed_jobs_dont_execute := completed_jobs_dont_execute job_cost sched. + + (* 4) The schedule is work-conserving. *) + Let H4_work_conserving := work_conserving job_arrival job_cost job_jitter arr_seq sched. + + (* 5) The schedule respects task priorities. *) + Let H5_respects_priority := + respects_JLDP_policy job_arrival job_cost job_jitter arr_seq sched higher_eq_priority. + + (* All these properties can be combined into the following predicate. *) + Definition valid_jitter_aware_schedule := + H1_jobs_come_from_arrival_sequence /\ + H2_jobs_execute_after_jitter /\ + H3_completed_jobs_dont_execute /\ + H4_work_conserving /\ + H5_respects_priority. + + End DefiningValidSchedule. + +End ValidJitterAwareSchedule. \ No newline at end of file diff --git a/model/schedule/uni/response_time.v b/model/schedule/uni/response_time.v index 49799fa77..4e18a8a92 100644 --- a/model/schedule/uni/response_time.v +++ b/model/schedule/uni/response_time.v @@ -7,7 +7,7 @@ Module ResponseTime. Import UniprocessorSchedule SporadicTaskset TaskArrival. - (* In this section, we define the notion of a response-time bound. *) + (* In this section, we define the notion of response-time bound. *) Section ResponseTimeBound. Context {sporadic_task: eqType}. @@ -22,23 +22,40 @@ Module ResponseTime. (* ...and any uniprocessor schedule of these jobs. *) Variable sched: schedule Job. - (* Let tsk be any task that is to be analyzed. *) - Variable tsk: sporadic_task. - (* For simplicity, let's define some local names. *) Let job_has_completed_by := completed_by job_cost sched. - (* Then, we say that R is a response-time bound of tsk in this schedule ... *) - Variable R: time. - - (* ... iff any job j of tsk in this arrival sequence has - completed by (job_arrival j + R). *) - Definition is_response_time_bound_of_task := - forall j, - arrives_in arr_seq j -> - job_task j = tsk -> - job_has_completed_by j (job_arrival j + R). - + Section Job. + + (* Given any job j, ... *) + Variable j: Job. + + (* ...we say that R is a response-time bound of j in this schedule ... *) + Variable R: time. + + (* ... iff j completes by (job_arrival j + R). *) + Definition is_response_time_bound_of_job := job_has_completed_by j (job_arrival j + R). + + End Job. + + Section Task. + + (* Let tsk be any task that is to be analyzed. *) + Variable tsk: sporadic_task. + + (* Then, we say that R is a response-time bound of tsk in this schedule ... *) + Variable R: time. + + (* ... iff any job j of tsk in this arrival sequence has + completed by (job_arrival j + R). *) + Definition is_response_time_bound_of_task := + forall j, + arrives_in arr_seq j -> + job_task j = tsk -> + is_response_time_bound_of_job j R. + + End Task. + End ResponseTimeBound. (* In this section, we prove some basic lemmas about response-time bounds. *) @@ -61,7 +78,7 @@ Module ResponseTime. completed_jobs_dont_execute job_cost sched. (* For simplicity, let's define some local names. *) - Let job_has_completed_by := completed_by job_cost sched. + Let response_time_bounded_by := is_response_time_bound_of_job job_arrival job_cost sched. (* We begin by proving lemmas about job response-time bounds. *) Section SpecificJob. @@ -71,8 +88,7 @@ Module ResponseTime. (* ...with response-time bound R. *) Variable R: time. - Hypothesis response_time_bound: - job_has_completed_by j (job_arrival j + R). + Hypothesis response_time_bound: response_time_bounded_by j R. (* Then, the service received by j at any time t' after its response time is 0. *) Lemma service_after_job_rt_zero : diff --git a/model/schedule/uni/susp/build_suspension_table.v b/model/schedule/uni/susp/build_suspension_table.v new file mode 100644 index 000000000..0e7a1411c --- /dev/null +++ b/model/schedule/uni/susp/build_suspension_table.v @@ -0,0 +1,264 @@ +Require Import rt.util.all. +Require Import rt.model.suspension. +Require Import rt.model.schedule.uni.susp.schedule. +From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq bigop fintype. + +(* In this file, we take any predicate that defines whether a job + is suspended at time t and build a table of suspension durations + that is valid up to time t. *) +Module SuspensionTableConstruction. + + Import ScheduleWithSuspensions Suspension. + + Section BuildingSuspensionTable. + + Context {Job: eqType}. + Variable job_arrival: Job -> time. + Variable job_cost: Job -> time. + + (** Basic Setup & Setting *) + + (* Consider any job arrival sequence... *) + Variable arr_seq: arrival_sequence Job. + + (* ...and any schedule of this arrival sequence... *) + Variable sched: schedule Job. + + (* ...in which jobs must arrive to execute. *) + Hypothesis H_jobs_must_arrive_to_execute: + jobs_must_arrive_to_execute job_arrival sched. + + (* Recall the instant following the last execution of a job, which + indicates the start of the latest suspension interval. *) + Let start_of_latest_suspension := + time_after_last_execution job_arrival sched. + + (* For simplicity, let's also define some local names. *) + Let job_completed_by := completed_by job_cost sched. + + (** Construction of Suspension Table *) + + (* We are going to construct a suspension table that is valid up to time t_max. *) + Variable t_max: time. + + (* First, consider any predicate that indicates whether a job is suspended at time t. *) + Variable job_suspended_at: Job -> time -> bool. + + (* Assume that this predicate only holds for jobs that have arrived... *) + Hypothesis H_arrived: + forall j t, + t < t_max -> + job_suspended_at j t -> + has_arrived job_arrival j t. + + (* ...and that have not yet completed. *) + Hypothesis H_not_completed: + forall j t, + t < t_max -> + job_suspended_at j t -> + ~~ job_completed_by j t. + + (* Assume that this predicate divides the timeline into continuous + suspension intervals, for any given amount of received service. *) + Hypothesis H_continuous_suspension: + forall j t t_susp, + t < t_max -> + job_suspended_at j t -> + start_of_latest_suspension j t <= t_susp < t -> + job_suspended_at j t_susp. + + (* Then, we can construct a suspension table for the given suspension + predicate as follows. *) + Definition build_suspension_duration (j: Job) (s: time) := + \sum_(0 <= t < t_max | service sched j t == s) job_suspended_at j t. + + (* In order to prove that the suspension table matches the given predicate, + let's first prove some helper lemmas. *) + Section HelperLemmas. + + (* First, we show that for any job j suspended at time t, the cumulative suspension + time before the beginning of the suspension is zero. *) + Lemma not_suspended_before_suspension_start: + forall j t, + t < t_max -> + job_suspended_at j t -> + let susp_start := start_of_latest_suspension j t in + let S := service sched j in + \sum_(0 <= i < susp_start | S i == S susp_start) job_suspended_at j i = 0. + Proof. + rename H_arrived into ARR, H_not_completed into COMPLETED, + H_continuous_suspension into CONT. + intros j t LTmax SUSPt X1 X2; rewrite /X1 /X2; clear X1 X2. + set ex := start_of_latest_suspension. + set S := service sched. + rewrite big_nat_cond big1 ?add0n //. + move => i /= /andP [LTex /eqP SAME]. + apply/eqP; rewrite eqb0; apply/negP; intro SUSPi. + suff BUG: S j i != S j (ex j t) by rewrite SAME eq_refl in BUG. + rewrite neq_ltn; apply/orP; left. + rewrite /S/ex (same_service_since_last_execution job_arrival) //. + eapply less_service_before_start_of_suspension; last by apply LTex. + apply ARR; last by done. + apply ltn_trans with (n := ex j t); first by done. + apply leq_ltn_trans with (n := t); last by done. + by apply last_execution_bounded_by_identity, ARR. + Qed. + + (* Next, we prove that after time t_max, no job suspends according to the table. *) + Lemma suspension_duration_no_suspension_after_t_max: + forall j t, + has_arrived job_arrival j t -> + t_max <= t -> + ~~ suspended_at job_arrival job_cost build_suspension_duration sched j t. + Proof. + have ZERO := not_suspended_before_suspension_start. + rename H_arrived into ARR. + intros j t ARRt GEmax. + rewrite /suspended_at negb_and; apply/orP; right. + rewrite negb_and -leqNgt; apply/orP; right. + rewrite /suspension_duration /build_suspension_duration. + set ex := _ job_arrival _. + set S := service sched. + set susp_at := job_suspended_at. + case (ltnP (ex j t) t_max) => [LT | GE]. + { + apply leq_trans with (n := t_max); last by done. + rewrite big_mkcond /=. + rewrite -> big_cat_nat with (n := ex j t); [simpl | by done | by apply ltnW]. + rewrite big_nat_cond big1 ?add0n. + { + apply leq_trans with (n := ex j t + \sum_(ex j t <= i < t_max) 1); + last by simpl_sum_const; rewrite subnKC // ltnW. + by rewrite leq_add2l; apply leq_sum; intros i _; case: ifP => //_; apply leq_b1. + } + move => /= i /andP [LTex _]; case: ifP => /eqP SERV; last by done. + apply/eqP; rewrite eqb0; apply/negP; intro SUSPi. + suff BUG: S j i != S j (ex j t) by rewrite SERV eq_refl in BUG. + rewrite neq_ltn; apply/orP; left. + rewrite /S/ex same_service_since_last_execution //. + eapply less_service_before_start_of_suspension; last by apply LTex. + by apply ARR; first by apply:(ltn_trans LTex). + } + { + rewrite big_nat_cond big1 ?addn0; + first by apply last_execution_bounded_by_identity. + move => /= i /andP [LTmax /eqP SERV]. + apply/eqP; rewrite eqb0; apply/negP; intro SUSPi. + suff BUG: S j i != S j (ex j t) by rewrite SERV eq_refl in BUG. + rewrite neq_ltn; apply/orP; left. + rewrite /S/ex same_service_since_last_execution //. + eapply less_service_before_start_of_suspension; first by apply ARR. + by apply: (leq_trans LTmax); apply GE. + } + Qed. + + End HelperLemmas. + + (* Using the lemmas above, we prove that up to time t_max, the constructed suspension + table matches the given suspension predicate. *) + Lemma suspension_duration_matches_predicate_up_to_t_max: + forall j t, + t < t_max -> + job_suspended_at j t = + suspended_at job_arrival job_cost build_suspension_duration sched j t. + Proof. + have ZERO := not_suspended_before_suspension_start. + rename H_arrived into ARR, H_not_completed into COMPLETED, + H_continuous_suspension into CONT. + intros j t LEmax. + apply/idP/idP. + { + intros SUSPt. + set ex := time_after_last_execution job_arrival sched. + set S := service sched. + set susp_at := job_suspended_at. + have LEt: ex j t <= t. + by apply last_execution_bounded_by_identity, ARR. + apply/andP; split; first by apply COMPLETED. + apply/andP; split; first by done. + rewrite /suspension_duration /build_suspension_duration. + rewrite -/ex -/S -/susp_at. + apply leq_trans with (n := ex j t + \sum_(ex j t <= t0 < t.+1) 1); + first by simpl_sum_const; rewrite subnKC // ltnW // ltnS. + rewrite leq_add2l. + rewrite -> big_cat_nat with (m := 0) (n := ex j t); rewrite //=; + last by apply leq_trans with (n := t); last by apply ltnW. + rewrite ZERO // add0n. + apply leq_trans with (n := \sum_(ex j t<= i <t.+1|S j i==S j (ex j t)) susp_at j i); + last by rewrite big_mkcond [X in _ <= X]big_mkcond /= extend_sum. + rewrite [X in _ <= X]big_mkcond /=. + apply leq_sum_nat; move => i /andP [GE LT] _. + have SAMEserv: S j i == S j (ex j t). + { + rewrite ltnS in LT. + rewrite eqn_leq; apply/andP; split; last by apply extend_sum. + by rewrite /S/ex same_service_since_last_execution ?extend_sum. + } + rewrite SAMEserv lt0n eqb0 negbK. + rewrite ltnS leq_eqVlt in LT. + move: LT => /orP [/eqP EQ | LT]; subst; first by done. + by apply CONT with (t := t); try (apply/andP; split). + } + { + move => /andP [NOTCOMP /andP [GE LT]]. + rewrite /suspension_duration /build_suspension_duration in LT. + set S := service sched in LT. + set susp_at := job_suspended_at in LT *. + set ex := _ job_arrival _ in GE LT. + rewrite -> big_cat_nat with (m := 0) (n := ex j t) in LT; rewrite //= in LT; + last by apply leq_trans with (n := t); last by apply ltnW. + rewrite big_nat_cond big1 ?add0n in LT; last first. + { + move => i /= /andP [LTex /eqP SAME]. + apply/eqP; rewrite eqb0; apply/negP; intro SUSPi. + suff BUG: S j i != S j (ex j t) by rewrite SAME eq_refl in BUG. + rewrite neq_ltn; apply/orP; left. + rewrite /S/ex same_service_since_last_execution //. + eapply less_service_before_start_of_suspension; last by apply LTex. + by apply ARR; first by apply:(ltn_trans LTex); apply:(leq_ltn_trans _ LEmax). + } + case (boolP ([exists t0:'I_t_max,(S j t0==S j (ex j t))&&susp_at j t0]));last first. + { + rewrite negb_exists; move => /forallP ALL. + rewrite big_nat_cond big1 in LT; first by rewrite addn0 ltnNge GE in LT. + move => i /andP [/andP [_ LTmax] EQ]. + specialize (ALL (Ordinal LTmax)). + by rewrite EQ /= in ALL; apply/eqP; rewrite eqb0. + } + move => /existsP [t0 /andP [/eqP EQ SUSP0]]. + have MAX := @arg_maxP _ t0 (fun x=>(S j x == S j (ex j t)) && susp_at j x) id. + feed MAX; simpl in MAX; first by rewrite EQ eq_refl SUSP0. + move: MAX => [m /andP [/eqP EQserv SUSPm] ALL]; clear EQ SUSP0 t0. + case (ltnP t m) => [LTm | GEm]. + { + apply CONT with (t := m); try done; apply/andP; split; last by done. + rewrite /start_of_latest_suspension. + rewrite (same_service_implies_same_last_execution _ _ _ _ t); first by done. + rewrite -/S EQserv /S /ex /start_of_latest_suspension. + by rewrite same_service_since_last_execution. + } + rewrite leq_eqVlt in GEm; move: GEm => /orP [/eqP EQm | GTm]; subst; first by done. + apply contraT; intro NOTSUSP. + set SUM := (X in _ < _ + X) in LT. + suff BUG: t >= ex j t + SUM by rewrite leqNgt LT in BUG. + rewrite /SUM in LT *; clear SUM LT. + apply leq_trans with (n := ex j t + (t - ex j t)); last by rewrite subnKC. + rewrite leq_add2l. + rewrite -> big_cat_nat with (n := t); rewrite //=; last by apply ltnW. + rewrite [X in _ + X <= _]big_nat_cond [X in _ + X <= _]big1 ?addn0. + { + apply leq_trans with (n := \sum_(ex j t <= i < t) 1); last by simpl_sum_const. + by rewrite big_mkcond; apply leq_sum; intros i _; case: ifP => // _; apply leq_b1. + } + move => i /andP [/andP [GEi LTi] /eqP SERVi]. + apply/eqP; rewrite eqb0; apply/negP; intro SUSPi. + specialize (ALL (Ordinal LTi)); rewrite /= in ALL. + feed ALL; first by rewrite SERVi eq_refl SUSPi. + suff BUG: m >= t by rewrite leqNgt GTm in BUG. + by apply: (leq_trans GEi). + } + Qed. + + End BuildingSuspensionTable. + +End SuspensionTableConstruction. \ No newline at end of file diff --git a/model/schedule/uni/susp/last_execution.v b/model/schedule/uni/susp/last_execution.v index bf1c65f82..5c3f4979b 100644 --- a/model/schedule/uni/susp/last_execution.v +++ b/model/schedule/uni/susp/last_execution.v @@ -356,6 +356,53 @@ Module LastExecution. Qed. End ExistsIntermediateExecution. + + (* In this section we prove that before the last execution the job + must have received strictly less service. *) + Section LessServiceBeforeLastExecution. + + (* Let t be any time... *) + Variable t: time. + + (* ...and consider any earlier time t0 no earlier than the arrival of job j... *) + Variable t0: time. + Hypothesis H_no_earlier_than_arrival: has_arrived job_arrival j t0. + + (* ...and before the last execution of job j (with respect to time t). *) + Hypothesis H_before_last_execution: t0 < time_after_last_execution j t. + + (* Then, we can prove that the service received by j before time t0 + is strictly less than the service received by j before time t. *) + Lemma less_service_before_start_of_suspension: + service sched j t0 < service sched j t. + Proof. + rename H_no_earlier_than_arrival into ARR, H_before_last_execution into LT. + set ex := time_after_last_execution in LT. + set S := service sched. + case EX:([exists t0:'I_t, scheduled_at sched j t0]); last first. + { + rewrite /ex /time_after_last_execution EX in LT. + apply leq_trans with (p := t0) in LT; last by done. + by rewrite ltnn in LT. + } + { + rewrite /ex /time_after_last_execution EX in LT. + set m := (X in _ < X + 1) in LT. + apply leq_ltn_trans with (n := S j m); + first by rewrite -/m addn1 ltnS in LT; apply extend_sum. + move: EX => /existsP [t' SCHED']. + have LTt: m < t by apply bigmax_ltn_ord with (i0 := t'). + rewrite /S /service /service_during. + rewrite -> big_cat_nat with (p := t) (n := m); [simpl | by done | by apply ltnW]. + rewrite -addn1 leq_add2l; destruct t; first by done. + rewrite big_nat_recl //. + apply leq_trans with (n := scheduled_at sched j m); last by apply leq_addr. + rewrite lt0n eqb0 negbK. + by apply bigmax_pred with (i0 := t'). + } + Qed. + + End LessServiceBeforeLastExecution. End Lemmas. diff --git a/model/schedule/uni/susp/suspension_intervals.v b/model/schedule/uni/susp/suspension_intervals.v index 1222b486b..6177442ac 100644 --- a/model/schedule/uni/susp/suspension_intervals.v +++ b/model/schedule/uni/susp/suspension_intervals.v @@ -44,7 +44,7 @@ Module SuspensionIntervals. (Note that suspension_start can return time t itself.) *) Let suspension_start := time_after_last_execution job_arrival sched j t. - (* Next, using the service received by j in the interval [0, suspension_start), ... *) + (* Next, using the service received by j in the interval [0, suspension_start),...*) Let current_service := service sched j suspension_start. (* ... we recall the duration of the suspension expected for job j after having @@ -151,7 +151,8 @@ Module SuspensionIntervals. case: (boolP (completed_by _ _ _ _)) => [COMP | NOTCOMP]; first by apply completed_implies_not_scheduled in COMP; first by rewrite SCHED' in COMP. - rewrite andTb (same_service_implies_same_last_execution _ _ _ _ suspension_start) //. + rewrite andTb (same_service_implies_same_last_execution _ _ _ _ + suspension_start) //. rewrite /suspension_start last_execution_idempotent //. apply/andP; split; first by apply leq_addr. by rewrite ltn_add2l. @@ -289,19 +290,20 @@ Module SuspensionIntervals. total_suspension_of_j, total_suspension. intros t1 t2. apply leq_trans with (n := \sum_(0 <= s < job_cost j) - \sum_(t1 <= t < t2 | service sched j t == s) suspended_at j t). + \sum_(t1 <= t < t2 | service sched j t == s) suspended_at j t). { rewrite (exchange_big_dep_nat (fun x => true)) //=. apply leq_sum; intros s _. destruct (boolP (suspended_at j s)) as [SUSP | NOTSUSP]; last by done. rewrite (big_rem (service sched j s)); first by rewrite eq_refl. rewrite mem_index_iota; apply/andP; split; first by done. - rewrite ltn_neqAle; apply/andP; split; last by apply cumulative_service_le_job_cost. + rewrite ltn_neqAle; apply/andP; split; + last by apply cumulative_service_le_job_cost. by apply suspended_implies_not_completed in SUSP. } { apply leq_sum_nat; move => s /andP [_ LT] _. - destruct (boolP [exists t: 'I_t2, (t >= t1) && (service sched j t == s)]) as [EX | ALL]; + destruct (boolP [exists t:'I_t2, (t>=t1)&& (service sched j t==s)]) as [EX|ALL]; last first. { rewrite negb_exists in ALL; move: ALL => /forallP ALL. @@ -374,13 +376,13 @@ Module SuspensionIntervals. Proof. rename H_j_has_completed into COMP, H_jobs_must_arrive_to_execute into ARR. have EARLIER := exists_last_execution_with_smaller_service job_arrival - job_cost sched ARR j t COMP. + job_cost sched ARR j t COMP. apply/eqP; rewrite eqn_leq; apply/andP; split; first by apply cumulative_suspension_le_total_suspension. rewrite /total_suspension /cumulative_suspension /cumulative_suspension_during. move: COMP => /eqP COMP. apply leq_trans with (n := \sum_(0 <= s < job_cost j) - \sum_(0 <= t0 < t | service sched j t0 == s) suspended_at j t0); + \sum_(0 <= t0 < t | service sched j t0 == s) suspended_at j t0); last first. { rewrite (exchange_big_dep_nat (fun x => true)) //=. @@ -409,7 +411,7 @@ Module SuspensionIntervals. move: (EARLIER s LTs) => [t' EQ']. apply leq_trans with (n := \sum_(0 <= t0 < t | (service sched j t0 == s) && - (b t' <= t0 < b t' + n (service sched j (b t')))) 1); last first. + (b t' <= t0 < b t' + n (service sched j (b t')))) 1); last first. { rewrite big_mkcond [\sum_(_ <= _ < _ | _ == s)_]big_mkcond. apply leq_sum_nat; move => i /andP [_ LTi] _. @@ -421,10 +423,11 @@ Module SuspensionIntervals. by apply: (leq_ltn_trans _ LTs); apply eq_leq; apply/eqP. } { - apply leq_trans with (n := \sum_(b t' <= t0 < b t' + n (service sched j (b t')) | - (0 <= t0 < t) && (service sched j t0 == s)) 1). + apply leq_trans with (n := \sum_(b t'<= t0< b t'+ n (service sched j (b t')) | + (0 <= t0 < t) && (service sched j t0 == s)) 1). { - apply leq_trans with (n := \sum_(b t' <= t0 < b t' + n (service sched j (b t'))) 1); + apply leq_trans with (n := \sum_(b t' <= t0 < b t' + + n (service sched j (b t'))) 1); first by simpl_sum_const; rewrite addKn -EQ'. rewrite [in X in _ <= X]big_mkcond /=. apply leq_sum_nat; move => i /andP [LEi GTi] _. @@ -463,6 +466,55 @@ Module SuspensionIntervals. End SuspendsForTotalSuspension. + (* In this section, we prove that a job executes just before it starts suspending. *) + Section ExecutionBeforeSuspension. + + (* Assume that jobs do not execute before they arrive... *) + Hypothesis H_jobs_must_arrive_to_execute: + jobs_must_arrive_to_execute job_arrival sched. + + (* ...and nor after completion. *) + Hypothesis H_completed_jobs_dont_execute: + completed_jobs_dont_execute job_cost sched. + + (* Assume that the schedule respects self-suspensions. *) + Hypothesis H_respects_self_suspensions: respects_self_suspensions. + + (* Let j be any job... *) + Variable j: Job. + + (* ...that has arrived by some time t. *) + Variable t: time. + Hypothesis H_arrived: has_arrived job_arrival j t. + + (* If job j is not suspended at time t, but starts to suspend at time t + 1, ... *) + Hypothesis H_not_suspended_at_t: ~~ suspended_at j t. + Hypothesis H_begins_suspension: suspended_at j t.+1. + + (* ...then j must be scheduled at time t. *) + Lemma executes_before_suspension: + scheduled_at sched j t. + Proof. + rename H_not_suspended_at_t into NOTSUSPs, H_begins_suspension into SUSPs'. + move: SUSPs' => /andP [NOTCOMP' /andP [GE LT]]. + apply contraT; intro NOTSCHED. + suff BUG: suspended_at j t by rewrite BUG in NOTSUSPs. + apply suspended_in_suspension_interval with (t := t.+1); try done. + { + apply contraT; rewrite negbK; intro COMP. + suff COMP': completed_by job_cost sched j t.+1 by rewrite COMP' in NOTCOMP'. + by apply completion_monotonic with (t0 := t). + } + apply/andP; split; last by apply: (leq_ltn_trans _ LT). + apply leq_trans with (n := time_after_last_execution job_arrival sched j t); + last by apply last_execution_bounded_by_identity. + apply eq_leq, same_service_implies_same_last_execution. + rewrite /service /service_during big_nat_recr //= /service_at. + by apply negbTE in NOTSCHED; rewrite NOTSCHED. + Qed. + + End ExecutionBeforeSuspension. + End Lemmas. End DefiningSuspensionIntervals. diff --git a/model/schedule/uni/susp/valid_schedule.v b/model/schedule/uni/susp/valid_schedule.v new file mode 100644 index 000000000..2ce70a272 --- /dev/null +++ b/model/schedule/uni/susp/valid_schedule.v @@ -0,0 +1,75 @@ +Require Import rt.util.all. +Require Import rt.model.priority rt.model.suspension. +Require Import rt.model.arrival.basic.arrival_sequence. +Require Import rt.model.schedule.uni.susp.schedule + rt.model.schedule.uni.susp.platform. + +From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop. + +(* In this file, we construct a predicate that defines a valid suspension-aware schedule. *) +Module ValidSuspensionAwareSchedule. + + Import ScheduleWithSuspensions Suspension Priority PlatformWithSuspensions. + + (** Basic Setup & Setting*) + Section DefiningValidSchedule. + + Context {Task: eqType}. + Context {Job: eqType}. + Variable job_arrival: Job -> time. + Variable job_task: Job -> Task. + + (* Consider any job arrival sequence. *) + Variable arr_seq: arrival_sequence Job. + + (* Assume any given job-level policy... *) + Variable higher_eq_priority: JLDP_policy Job. + + (* ...and job suspension times. *) + Variable job_suspension_duration: job_suspension Job. + + (** Definition of the Suspension-Aware Schedule *) + + (* Let job_cost denote any job cost function... *) + Variable job_cost: Job -> time. + + (* ...and let sched_susp be any schedule. *) + Variable sched_susp: schedule Job. + + (* For sched_susp to denote a valid suspension-aware schedule, + the following properties must be satisfied. *) + + (* 1) All scheduled jobs must come from the arrival sequence. *) + Let H1_jobs_come_from_arrival_sequence := jobs_come_from_arrival_sequence sched_susp arr_seq. + + (* 2) Jobs only execute after they arrive. *) + Let H2_jobs_must_arrive_to_execute := jobs_must_arrive_to_execute job_arrival sched_susp. + + (* 3) Jobs do not execute for longer than their costs. *) + Let H3_completed_jobs_dont_execute := completed_jobs_dont_execute job_cost sched_susp. + + (* 4) The schedule is work-conserving if there are non-suspended jobs. *) + Let H4_work_conserving := + work_conserving job_arrival job_cost job_suspension_duration arr_seq sched_susp. + + (* 5) The schedule respects task priorities. *) + Let H5_respects_priority := + respects_JLDP_policy job_arrival job_cost job_suspension_duration arr_seq + sched_susp higher_eq_priority. + + (* 6) Suspended jobs are not allowed to be schedule. *) + Let H6_respects_self_suspensions := + respects_self_suspensions job_arrival job_cost job_suspension_duration sched_susp. + + (* All these properties can be combined into the following predicate. *) + Definition valid_suspension_aware_schedule := + H1_jobs_come_from_arrival_sequence /\ + H2_jobs_must_arrive_to_execute /\ + H3_completed_jobs_dont_execute /\ + H4_work_conserving /\ + H5_respects_priority /\ + H6_respects_self_suspensions. + + End DefiningValidSchedule. + +End ValidSuspensionAwareSchedule. \ No newline at end of file -- GitLab