diff --git a/Makefile b/Makefile index ed72ed36bfe05f2190609a728b737fdf4b860565..f874e7c1803cbba7b5f0c2c18f4632227e31008d 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ # # This Makefile was generated by the command line : -# coq_makefile -f _CoqProject ./util/fixedpoint.v ./util/ssromega.v ./util/bigcat.v ./util/nat.v ./util/seqset.v ./util/notation.v ./util/list.v ./util/powerset.v ./util/all.v ./util/sorting.v ./util/tactics.v ./util/bigord.v ./util/exists.v ./util/induction.v ./util/sum.v ./util/divround.v ./util/counting.v ./implementation/basic/bertogna_edf_example.v ./implementation/basic/task.v ./implementation/basic/schedule.v ./implementation/basic/job.v ./implementation/basic/arrival_sequence.v ./implementation/jitter/bertogna_edf_example.v ./implementation/jitter/task.v ./implementation/jitter/schedule.v ./implementation/jitter/job.v ./implementation/jitter/arrival_sequence.v ./analysis/basic/bertogna_fp_theory.v ./analysis/basic/interference_bound_edf.v ./analysis/basic/interference_bound_fp.v ./analysis/basic/interference_bound.v ./analysis/basic/bertogna_edf_comp.v ./analysis/basic/bertogna_fp_comp.v ./analysis/basic/bertogna_edf_theory.v ./analysis/basic/workload_bound.v ./analysis/parallel/bertogna_fp_theory.v ./analysis/parallel/interference_bound_edf.v ./analysis/parallel/interference_bound_fp.v ./analysis/parallel/interference_bound.v ./analysis/parallel/bertogna_edf_comp.v ./analysis/parallel/bertogna_fp_comp.v ./analysis/parallel/bertogna_edf_theory.v ./analysis/parallel/workload_bound.v ./analysis/jitter/bertogna_fp_theory.v ./analysis/jitter/interference_bound_edf.v ./analysis/jitter/interference_bound_fp.v ./analysis/jitter/interference_bound.v ./analysis/jitter/bertogna_edf_comp.v ./analysis/jitter/bertogna_fp_comp.v ./analysis/jitter/bertogna_edf_theory.v ./analysis/jitter/workload_bound.v ./model/basic/time.v ./model/basic/schedulability.v ./model/basic/task.v ./model/basic/task_arrival.v ./model/basic/platform.v ./model/basic/schedule.v ./model/basic/priority.v ./model/basic/interference_edf.v ./model/basic/interference.v ./model/basic/workload.v ./model/basic/job.v ./model/basic/arrival_sequence.v ./model/basic/response_time.v ./model/basic/platform_fp.v ./model/jitter/time.v ./model/jitter/schedulability.v ./model/jitter/task.v ./model/jitter/task_arrival.v ./model/jitter/platform.v ./model/jitter/schedule.v ./model/jitter/priority.v ./model/jitter/interference_edf.v ./model/jitter/interference.v ./model/jitter/workload.v ./model/jitter/job.v ./model/jitter/arrival_sequence.v ./model/jitter/response_time.v ./model/jitter/platform_fp.v -o Makefile +# coq_makefile -f _CoqProject ./util/fixedpoint.v ./util/ssromega.v ./util/bigcat.v ./util/nat.v ./util/seqset.v ./util/notation.v ./util/list.v ./util/powerset.v ./util/all.v ./util/sorting.v ./util/tactics.v ./util/bigord.v ./util/exists.v ./util/induction.v ./util/sum.v ./util/divround.v ./util/counting.v ./implementation/basic/bertogna_edf_example.v ./implementation/basic/task.v ./implementation/basic/schedule.v ./implementation/basic/bertogna_fp_example.v ./implementation/basic/job.v ./implementation/basic/arrival_sequence.v ./implementation/parallel/bertogna_edf_example.v ./implementation/parallel/task.v ./implementation/parallel/schedule.v ./implementation/parallel/bertogna_fp_example.v ./implementation/parallel/job.v ./implementation/parallel/arrival_sequence.v ./implementation/jitter/bertogna_edf_example.v ./implementation/jitter/task.v ./implementation/jitter/schedule.v ./implementation/jitter/bertogna_fp_example.v ./implementation/jitter/job.v ./implementation/jitter/arrival_sequence.v ./implementation/apa/bertogna_edf_example.v ./implementation/apa/task.v ./implementation/apa/schedule.v ./implementation/apa/bertogna_fp_example.v ./implementation/apa/job.v ./implementation/apa/arrival_sequence.v ./analysis/basic/bertogna_fp_theory.v ./analysis/basic/interference_bound_edf.v ./analysis/basic/interference_bound_fp.v ./analysis/basic/interference_bound.v ./analysis/basic/bertogna_edf_comp.v ./analysis/basic/bertogna_fp_comp.v ./analysis/basic/bertogna_edf_theory.v ./analysis/basic/workload_bound.v ./analysis/parallel/bertogna_fp_theory.v ./analysis/parallel/interference_bound_edf.v ./analysis/parallel/interference_bound_fp.v ./analysis/parallel/interference_bound.v ./analysis/parallel/bertogna_edf_comp.v ./analysis/parallel/bertogna_fp_comp.v ./analysis/parallel/bertogna_edf_theory.v ./analysis/parallel/workload_bound.v ./analysis/jitter/bertogna_fp_theory.v ./analysis/jitter/interference_bound_edf.v ./analysis/jitter/interference_bound_fp.v ./analysis/jitter/interference_bound.v ./analysis/jitter/bertogna_edf_comp.v ./analysis/jitter/bertogna_fp_comp.v ./analysis/jitter/bertogna_edf_theory.v ./analysis/jitter/workload_bound.v ./analysis/apa/bertogna_fp_theory.v ./analysis/apa/interference_bound_edf.v ./analysis/apa/interference_bound_fp.v ./analysis/apa/interference_bound.v ./analysis/apa/bertogna_edf_comp.v ./analysis/apa/bertogna_fp_comp.v ./analysis/apa/bertogna_edf_theory.v ./analysis/apa/workload_bound.v ./model/basic/time.v ./model/basic/schedulability.v ./model/basic/task.v ./model/basic/task_arrival.v ./model/basic/platform.v ./model/basic/schedule.v ./model/basic/priority.v ./model/basic/interference_edf.v ./model/basic/interference.v ./model/basic/constrained_deadlines.v ./model/basic/workload.v ./model/basic/job.v ./model/basic/arrival_sequence.v ./model/basic/response_time.v ./model/jitter/time.v ./model/jitter/schedulability.v ./model/jitter/task.v ./model/jitter/task_arrival.v ./model/jitter/platform.v ./model/jitter/schedule.v ./model/jitter/priority.v ./model/jitter/interference_edf.v ./model/jitter/interference.v ./model/jitter/constrained_deadlines.v ./model/jitter/workload.v ./model/jitter/job.v ./model/jitter/arrival_sequence.v ./model/jitter/response_time.v ./model/apa/time.v ./model/apa/schedulability.v ./model/apa/task.v ./model/apa/task_arrival.v ./model/apa/platform.v ./model/apa/schedule.v ./model/apa/priority.v ./model/apa/affinity.v ./model/apa/interference_edf.v ./model/apa/interference.v ./model/apa/constrained_deadlines.v ./model/apa/workload.v ./model/apa/job.v ./model/apa/arrival_sequence.v ./model/apa/response_time.v -o Makefile # .DEFAULT_GOAL := all @@ -114,13 +114,27 @@ VFILES:=util/fixedpoint.v\ implementation/basic/bertogna_edf_example.v\ implementation/basic/task.v\ implementation/basic/schedule.v\ + implementation/basic/bertogna_fp_example.v\ implementation/basic/job.v\ implementation/basic/arrival_sequence.v\ + implementation/parallel/bertogna_edf_example.v\ + implementation/parallel/task.v\ + implementation/parallel/schedule.v\ + implementation/parallel/bertogna_fp_example.v\ + implementation/parallel/job.v\ + implementation/parallel/arrival_sequence.v\ implementation/jitter/bertogna_edf_example.v\ implementation/jitter/task.v\ implementation/jitter/schedule.v\ + implementation/jitter/bertogna_fp_example.v\ implementation/jitter/job.v\ implementation/jitter/arrival_sequence.v\ + implementation/apa/bertogna_edf_example.v\ + implementation/apa/task.v\ + implementation/apa/schedule.v\ + implementation/apa/bertogna_fp_example.v\ + implementation/apa/job.v\ + implementation/apa/arrival_sequence.v\ analysis/basic/bertogna_fp_theory.v\ analysis/basic/interference_bound_edf.v\ analysis/basic/interference_bound_fp.v\ @@ -145,6 +159,14 @@ VFILES:=util/fixedpoint.v\ analysis/jitter/bertogna_fp_comp.v\ analysis/jitter/bertogna_edf_theory.v\ analysis/jitter/workload_bound.v\ + analysis/apa/bertogna_fp_theory.v\ + analysis/apa/interference_bound_edf.v\ + analysis/apa/interference_bound_fp.v\ + analysis/apa/interference_bound.v\ + analysis/apa/bertogna_edf_comp.v\ + analysis/apa/bertogna_fp_comp.v\ + analysis/apa/bertogna_edf_theory.v\ + analysis/apa/workload_bound.v\ model/basic/time.v\ model/basic/schedulability.v\ model/basic/task.v\ @@ -154,11 +176,11 @@ VFILES:=util/fixedpoint.v\ model/basic/priority.v\ model/basic/interference_edf.v\ model/basic/interference.v\ + model/basic/constrained_deadlines.v\ model/basic/workload.v\ model/basic/job.v\ model/basic/arrival_sequence.v\ model/basic/response_time.v\ - model/basic/platform_fp.v\ model/jitter/time.v\ model/jitter/schedulability.v\ model/jitter/task.v\ @@ -168,11 +190,26 @@ VFILES:=util/fixedpoint.v\ model/jitter/priority.v\ model/jitter/interference_edf.v\ model/jitter/interference.v\ + model/jitter/constrained_deadlines.v\ model/jitter/workload.v\ model/jitter/job.v\ model/jitter/arrival_sequence.v\ model/jitter/response_time.v\ - model/jitter/platform_fp.v + model/apa/time.v\ + model/apa/schedulability.v\ + model/apa/task.v\ + model/apa/task_arrival.v\ + model/apa/platform.v\ + model/apa/schedule.v\ + model/apa/priority.v\ + model/apa/affinity.v\ + model/apa/interference_edf.v\ + model/apa/interference.v\ + model/apa/constrained_deadlines.v\ + model/apa/workload.v\ + model/apa/job.v\ + model/apa/arrival_sequence.v\ + model/apa/response_time.v ifneq ($(filter-out archclean clean cleanall printenv,$(MAKECMDGOALS)),) -include $(addsuffix .d,$(VFILES)) diff --git a/analysis/apa/bertogna_edf_comp.v b/analysis/apa/bertogna_edf_comp.v new file mode 100755 index 0000000000000000000000000000000000000000..f0a777da4672957ec19d4308b527cad47b3dde5c --- /dev/null +++ b/analysis/apa/bertogna_edf_comp.v @@ -0,0 +1,999 @@ +Require Import rt.util.all rt.util.divround. +Require Import rt.analysis.apa.bertogna_edf_theory. +From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop div path. + +Module ResponseTimeIterationEDF. + + Import ResponseTimeAnalysisEDF. + + (* In this section, we define the algorithm corresponding to the APA-reduction + of Bertogna and Cirinei's RTA for EDF scheduling. *) + Section Analysis. + + Context {sporadic_task: eqType}. + Variable task_cost: sporadic_task -> time. + Variable task_period: sporadic_task -> time. + Variable task_deadline: sporadic_task -> time. + + Context {Job: eqType}. + Variable job_cost: Job -> time. + Variable job_deadline: Job -> time. + Variable job_task: Job -> sporadic_task. + + (* As input for each iteration of the algorithm, we consider pairs + of tasks and response-time bounds. *) + Let task_with_response_time := (sporadic_task * time)%type. + + (* Consider a platform with num_cpus processors. *) + Variable num_cpus: nat. + + (* Assume that every task has a processor affinity alpha. *) + Variable alpha: task_affinity sporadic_task num_cpus. + + (* For the reduction to subproblems, consider a subaffinity alpha' for each task. *) + Variable alpha': task_affinity sporadic_task num_cpus. + + (* First, recall the interference bound under EDF. Note that we use + subaffinity (alpha' tsk) when computing the set of interfering tasks. *) + Let I (rt_bounds: seq task_with_response_time) + (tsk: sporadic_task) (delta: time) := + total_interference_bound_edf task_cost task_period task_deadline alpha tsk + (alpha' tsk) (* Subproblem alpha' *) + rt_bounds delta. + + (* Then, we define the response-time recurrence. Note that we divide the + interference by the cardinality of (alpha' tsk). *) + Definition edf_response_time_bound (rt_bounds: seq task_with_response_time) + (tsk: sporadic_task) (delta: time) := + task_cost tsk + div_floor (I rt_bounds tsk delta) + #|alpha' tsk|. + + (* Also note that a response-time is only valid if it is no larger + than the deadline. *) + Definition R_le_deadline (pair: task_with_response_time) := + let (tsk, R) := pair in + R <= task_deadline tsk. + + (* Next, we define the fixed-point iteration for computing the + response-time bound of each task. *) + + (* Given a sequence 'rt_bounds' of task and response-time bounds + from the previous iteration, we compute the response-time + bound of a single task using the RTA for EDF. *) + Definition update_bound (rt_bounds: seq task_with_response_time) pair := + let '(tsk, R) := pair in + (tsk, edf_response_time_bound rt_bounds tsk R). + + (* To compute the response-time bounds of the entire task set, + we start the iteration with a sequence of tasks and costs: + <(task1, cost1), (task2, cost2), ...>. *) + Let initial_state (ts: seq sporadic_task) := + map (fun tsk => (tsk, task_cost tsk)) ts. + + (* Then, we successively update the the response-time bounds based + on the slack computed in the previous iteration. *) + Definition edf_rta_iteration (rt_bounds: seq task_with_response_time) := + map (update_bound rt_bounds) rt_bounds. + + (* To ensure that the procedure converges, we stop the iteration + after a "sufficient" number of times, which corresponds to + the time complexity of the procedure. *) + Let max_steps (ts: seq sporadic_task) := + \sum_(tsk <- ts) (task_deadline tsk - task_cost tsk) + 1. + + (* This yields the following definition for the RTA. At the end + we check if all computed response-time bounds are less than + or equal to the deadline, in which case they are valid. *) + Definition edf_claimed_bounds (ts: seq sporadic_task) := + let R_values := iter (max_steps ts) edf_rta_iteration (initial_state ts) in + if (all R_le_deadline R_values) then + Some R_values + else None. + + (* The schedulability test simply checks if we got a list of + response-time bounds (i.e., if the computation did not fail). *) + Definition edf_schedulable (ts: seq sporadic_task) := + edf_claimed_bounds ts != None. + + (* In the following section, we prove several helper lemmas about the + list of tasks/response-time bounds. *) + Section SimpleLemmas. + + (* Updating a single response-time bound does not modify the task. *) + Lemma edf_claimed_bounds_unzip1_update_bound : + forall l rt_bounds, + unzip1 (map (update_bound rt_bounds) l) = unzip1 l. + Proof. + induction l; first by done. + intros rt_bounds. + simpl; f_equal; last by done. + by unfold update_bound; desf. + Qed. + + (* At any point of the iteration, the tasks are the same. *) + Lemma edf_claimed_bounds_unzip1_iteration : + forall l k, + unzip1 (iter k edf_rta_iteration (initial_state l)) = l. + Proof. + intros l k; clear -k. + induction k; simpl. + { + unfold initial_state. + induction l; first by done. + by simpl; rewrite IHl. + } + { + unfold edf_rta_iteration. + by rewrite edf_claimed_bounds_unzip1_update_bound. + } + Qed. + + (* The iteration preserves the size of the list. *) + Lemma edf_claimed_bounds_size : + forall l k, + size (iter k edf_rta_iteration (initial_state l)) = size l. + Proof. + intros l k; clear -k. + induction k; simpl; first by rewrite size_map. + by rewrite size_map. + Qed. + + (* If the analysis succeeds, the computed response-time bounds are no smaller + than the task cost. *) + Lemma edf_claimed_bounds_ge_cost : + forall l k tsk R, + (tsk, R) \in (iter k edf_rta_iteration (initial_state l)) -> + R >= task_cost tsk. + Proof. + intros l k tsk R IN. + destruct k. + { + move: IN => /mapP IN; destruct IN as [x IN EQ]; inversion EQ. + by apply leqnn. + } + { + rewrite iterS in IN. + move: IN => /mapP IN; destruct IN as [x IN EQ]. + unfold update_bound in EQ; destruct x; inversion EQ. + by unfold edf_response_time_bound; apply leq_addr. + } + Qed. + + (* If the analysis suceeds, the computed response-time bounds are no larger + than the deadline. *) + Lemma edf_claimed_bounds_le_deadline : + forall ts rt_bounds tsk R, + edf_claimed_bounds ts = Some rt_bounds -> + (tsk, R) \in rt_bounds -> + R <= task_deadline tsk. + Proof. + intros ts rt_bounds tsk R SOME PAIR; unfold edf_claimed_bounds in SOME. + destruct (all R_le_deadline (iter (max_steps ts) + edf_rta_iteration (initial_state ts))) eqn:DEADLINE; + last by done. + move: DEADLINE => /allP DEADLINE. + inversion SOME as [EQ]; rewrite -EQ in PAIR. + by specialize (DEADLINE (tsk, R) PAIR). + Qed. + + (* The list contains a response-time bound for every task in the task set. *) + Lemma edf_claimed_bounds_has_R_for_every_task : + forall ts rt_bounds tsk, + edf_claimed_bounds ts = Some rt_bounds -> + tsk \in ts -> + exists R, + (tsk, R) \in rt_bounds. + Proof. + intros ts rt_bounds tsk SOME IN. + unfold edf_claimed_bounds in SOME. + destruct (all R_le_deadline (iter (max_steps ts) edf_rta_iteration (initial_state ts))); + last by done. + inversion SOME as [EQ]; clear SOME EQ. + generalize dependent tsk. + induction (max_steps ts) as [| step]; simpl in *. + { + intros tsk IN; unfold initial_state. + exists (task_cost tsk). + by apply/mapP; exists tsk. + } + { + intros tsk IN. + set prev_state := iter step edf_rta_iteration (initial_state ts). + fold prev_state in IN, IHstep. + specialize (IHstep tsk IN); des. + exists (edf_response_time_bound prev_state tsk R). + by apply/mapP; exists (tsk, R); [by done | by f_equal]. + } + Qed. + + End SimpleLemmas. + + (* In this section, we prove the convergence of the RTA procedure. + Since we define the RTA procedure as the application of a function + a fixed number of times, this translates into proving that the value + of the iteration at (max_steps ts) is equal to the value at (max_steps ts) + 1. *) + Section Convergence. + + (* Consider any sequence of tasks with valid parameters. *) + Variable ts: seq sporadic_task. + Hypothesis H_valid_task_parameters: + valid_sporadic_taskset task_cost task_period task_deadline ts. + + (* To simplify, let f denote the RTA procedure. *) + Let f (k: nat) := iter k edf_rta_iteration (initial_state ts). + + (* Since the iteration is applied directly to a list of tasks and response-times, + we define a corresponding relation "<=" over those lists. *) + + (* Let 'all_le' be a binary relation over lists of tasks/response-time bounds. + It states that every element of list l1 has a response-time bound R that is less + than or equal to the corresponding response-time bound R' in list l2 (point-wise). + In addition, the relation states that the tasks of both lists are unchanged. *) + Let all_le := fun (l1 l2: list task_with_response_time) => + (unzip1 l1 == unzip1 l2) && + all (fun p => (snd (fst p)) <= (snd (snd p))) (zip l1 l2). + + (* Similarly, we define a strict version of 'all_le' called 'one_lt', which states that + there exists at least one element whose response-time bound increases. *) + Let one_lt := fun (l1 l2: list task_with_response_time) => + (unzip1 l1 == unzip1 l2) && + has (fun p => (snd (fst p)) < (snd (snd p))) (zip l1 l2). + + (* Next, we prove some basic properties about the relation all_le. *) + Section RelationProperties. + + (* The relation is reflexive, ... *) + Lemma all_le_reflexive : reflexive all_le. + Proof. + intros l; unfold all_le; rewrite eq_refl andTb. + destruct l; first by done. + by apply/(zipP t (fun x y => snd x <= snd y)). + Qed. + + (* ... and transitive. *) + Lemma all_le_transitive: transitive all_le. + Proof. + unfold transitive, all_le. + move => y x z /andP [/eqP ZIPxy LExy] /andP [/eqP ZIPyz LEyz]. + apply/andP; split; first by rewrite ZIPxy -ZIPyz. + move: LExy => /(zipP _ (fun x y => snd x <= snd y)) LExy. + move: LEyz => /(zipP _ (fun x y => snd x <= snd y)) LEyz. + assert (SIZExy: size (unzip1 x) = size (unzip1 y)). + by rewrite ZIPxy. + assert (SIZEyz: size (unzip1 y) = size (unzip1 z)). + by rewrite ZIPyz. + rewrite 2!size_map in SIZExy; rewrite 2!size_map in SIZEyz. + destruct y. + { + apply size0nil in SIZExy; symmetry in SIZEyz. + by apply size0nil in SIZEyz; subst. + } + apply/(zipP t (fun x y => snd x <= snd y)); first by rewrite SIZExy -SIZEyz. + intros i LTi. + exploit LExy; first by rewrite SIZExy. + { + rewrite size_zip -SIZEyz -SIZExy minnn in LTi. + by rewrite size_zip -SIZExy minnn; apply LTi. + } + instantiate (1 := t); intro LE. + exploit LEyz; first by apply SIZEyz. + { + rewrite size_zip SIZExy SIZEyz minnn in LTi. + by rewrite size_zip SIZEyz minnn; apply LTi. + } + by instantiate (1 := t); intro LE'; apply (leq_trans LE). + Qed. + + (* At any step of the iteration, the corresponding list + is larger than or equal to the initial state. *) + Lemma bertogna_edf_comp_iteration_preserves_minimum : + forall step, all_le (initial_state ts) (f step). + Proof. + unfold f. + intros step; destruct step; first by apply all_le_reflexive. + apply/andP; split. + { + assert (UNZIP0 := edf_claimed_bounds_unzip1_iteration ts 0). + by simpl in UNZIP0; rewrite UNZIP0 edf_claimed_bounds_unzip1_iteration. + } + destruct ts as [| tsk0 ts']. + { + clear -step; induction step; first by done. + by rewrite iterSr IHstep. + } + + apply/(zipP (tsk0,0) (fun x y => snd x <= snd y)); + first by rewrite edf_claimed_bounds_size size_map. + + intros i LTi; rewrite iterS; unfold edf_rta_iteration at 1. + have MAP := @nth_map _ (tsk0,0) _ (tsk0,0). + rewrite size_zip edf_claimed_bounds_size size_map minnn in LTi. + rewrite MAP; clear MAP; last by rewrite edf_claimed_bounds_size. + destruct (nth (tsk0, 0) (initial_state (tsk0 :: ts')) i) as [tsk_i R_i] eqn:SUBST. + rewrite SUBST; unfold update_bound. + unfold initial_state in SUBST. + have MAP := @nth_map _ tsk0 _ (tsk0, 0). + rewrite ?MAP // in SUBST; inversion SUBST; clear MAP. + assert (EQtsk: tsk_i = fst (nth (tsk0, 0) (iter step edf_rta_iteration + (initial_state (tsk0 :: ts'))) i)). + { + have MAP := @nth_map _ (tsk0,0) _ tsk0 (fun x => fst x). + rewrite -MAP; clear MAP; last by rewrite edf_claimed_bounds_size. + have UNZIP := edf_claimed_bounds_unzip1_iteration; unfold unzip1 in UNZIP. + by rewrite UNZIP; symmetry. + } + destruct (nth (tsk0, 0) (iter step edf_rta_iteration (initial_state (tsk0 :: ts')))) as [tsk_i' R_i']. + by simpl in EQtsk; rewrite -EQtsk; subst; apply leq_addr. + Qed. + + (* The application of the function is inductive. *) + Lemma bertogna_edf_comp_iteration_inductive (P : seq task_with_response_time -> Type) : + P (initial_state ts) -> + (forall k, P (f k) -> P (f (k.+1))) -> + P (f (max_steps ts)). + Proof. + by intros P0 Pn; induction (max_steps ts); last by apply Pn. + Qed. + + (* As a last step, we show that edf_rta_iteration preserves order, i.e., for any + list l1 no smaller than the initial state, and list l2 such that + l1 <= l2, we have (edf_rta_iteration l1) <= (edf_rta_iteration l2). *) + Lemma bertogna_edf_comp_iteration_preserves_order : + forall l1 l2, + all_le (initial_state ts) l1 -> + all_le l1 l2 -> + all_le (edf_rta_iteration l1) (edf_rta_iteration l2). + Proof. + rename H_valid_task_parameters into VALID. + intros x1 x2 LEinit LE. + move: LE => /andP [/eqP ZIP LE]; unfold all_le. + + assert (UNZIP': unzip1 (edf_rta_iteration x1) = unzip1 (edf_rta_iteration x2)). + { + by rewrite 2!edf_claimed_bounds_unzip1_update_bound. + } + + apply/andP; split; first by rewrite UNZIP'. + apply f_equal with (B := nat) (f := fun x => size x) in UNZIP'. + rename UNZIP' into SIZE. + rewrite size_map [size (unzip1 _)]size_map in SIZE. + move: LE => /(zipP _ (fun x y => snd x <= snd y)) LE. + destruct x1 as [| p0 x1'], x2 as [| p0' x2']; try (by ins). + apply/(zipP p0 (fun x y => snd x <= snd y)); first by done. + + intros i LTi. + exploit LE; first by rewrite 2!size_map in SIZE. + { + by rewrite size_zip 2!size_map -size_zip in LTi; apply LTi. + } + rewrite 2!size_map in SIZE. + instantiate (1 := p0); intro LEi. + rewrite (nth_map p0); + last by rewrite size_zip 2!size_map -SIZE minnn in LTi. + rewrite (nth_map p0); + last by rewrite size_zip 2!size_map SIZE minnn in LTi. + unfold update_bound, edf_response_time_bound; desf; simpl. + rename s into tsk_i, s0 into tsk_i', t into R_i, t0 into R_i', Heq into EQ, Heq0 into EQ'. + assert (EQtsk: tsk_i = tsk_i'). + { + destruct p0 as [tsk0 R0], p0' as [tsk0' R0']; simpl in H2; subst. + have MAP := @nth_map _ (tsk0',R0) _ tsk0' (fun x => fst x) i ((tsk0', R0) :: x1'). + have MAP' := @nth_map _ (tsk0',R0) _ tsk0' (fun x => fst x) i ((tsk0', R0') :: x2'). + assert (FSTeq: fst (nth (tsk0', R0)((tsk0', R0) :: x1') i) = + fst (nth (tsk0',R0) ((tsk0', R0') :: x2') i)). + { + rewrite -MAP; + last by simpl; rewrite size_zip 2!size_map /= -H0 minnn in LTi. + rewrite -MAP'; + last by simpl; rewrite size_zip 2!size_map /= H0 minnn in LTi. + by f_equal; simpl; f_equal. + } + apply f_equal with (B := sporadic_task) (f := fun x => fst x) in EQ. + apply f_equal with (B := sporadic_task) (f := fun x => fst x) in EQ'. + by rewrite FSTeq EQ' /= in EQ; rewrite EQ. + } + subst tsk_i'; rewrite leq_add2l. + unfold I, total_interference_bound_edf; apply leq_div2r. + rewrite 2!big_cons. + destruct p0 as [tsk0 R0], p0' as [tsk0' R0']. + simpl in H2; subst tsk0'. + rename R_i into delta, R_i' into delta'. + rewrite EQ EQ' in LEi; simpl in LEi. + rename H0 into SIZE, H1 into UNZIP; clear EQ EQ'. + + assert (SUBST: forall l delta, + \sum_(j <- l | let '(tsk_other, _) := j in + different_task_in alpha tsk_i (alpha' tsk_i) tsk_other) + (let '(tsk_other, R_other) := j in + interference_bound_edf task_cost task_period task_deadline tsk_i delta + (tsk_other, R_other)) = + \sum_(j <- l | different_task_in alpha tsk_i (alpha' tsk_i) (fst j)) + interference_bound_edf task_cost task_period task_deadline tsk_i delta j). + { + intros l x; clear -l. + induction l; first by rewrite 2!big_nil. + by rewrite 2!big_cons; rewrite IHl; desf; rewrite /= Heq in Heq0. + } rewrite 2!SUBST; clear SUBST. + + assert (VALID': valid_sporadic_taskset task_cost task_period task_deadline + (unzip1 ((tsk0, R0) :: x1'))). + { + move: LEinit => /andP [/eqP EQinit _]. + rewrite -EQinit; unfold valid_sporadic_taskset. + move => tsk /mapP IN. destruct IN as [p INinit EQ]; subst. + by move: INinit => /mapP INinit; destruct INinit as [tsk INtsk]; subst; apply VALID. + } + + assert (GE_COST: all (fun p => task_cost (fst p) <= snd p) ((tsk0, R0) :: x1')). + { + clear LE; move: LEinit => /andP [/eqP UNZIP' LE]. + move: LE => /(zipP _ (fun x y => snd x <= snd y)) LE. + specialize (LE (tsk0, R0)). + apply/(all_nthP (tsk0,R0)). + intros j LTj; generalize UNZIP'; simpl; intro SIZE'. + have F := @f_equal _ _ size (unzip1 (initial_state ts)). + apply F in SIZE'; clear F; rewrite /= 3!size_map in SIZE'. + exploit LE; [by rewrite size_map /= | |]. + { + rewrite size_zip size_map /= SIZE' minnn. + by simpl in LTj; apply LTj. + } + clear LE; intro LE. + unfold initial_state in LE. + have MAP := @nth_map _ tsk0 _ (tsk0,R0). + rewrite MAP /= in LE; + [clear MAP | by rewrite SIZE'; simpl in LTj]. + apply leq_trans with (n := task_cost (nth tsk0 ts j)); + [apply eq_leq; f_equal | by done]. + have MAP := @nth_map _ (tsk0, R0) _ tsk0 (fun x => fst x). + rewrite -MAP; [clear MAP | by done]. + unfold unzip1 in UNZIP'; rewrite -UNZIP'; f_equal. + clear -ts; induction ts; [by done | by simpl; f_equal]. + } + move: GE_COST => /allP GE_COST. + + assert (LESUM: \sum_(j <- x1' | different_task_in alpha tsk_i (alpha' tsk_i) (fst j)) + interference_bound_edf task_cost task_period task_deadline tsk_i delta j <= \sum_(j <- x2' | different_task_in alpha tsk_i (alpha' tsk_i) (fst j)) + interference_bound_edf task_cost task_period task_deadline tsk_i delta' j). + { + set elem := (tsk0, R0); rewrite 2!(big_nth elem). + rewrite -SIZE. + rewrite big_mkcond [\sum_(_ <- _ | different_task_in _ _ _ _)_]big_mkcond. + rewrite big_seq_cond [\sum_(_ <- _ | true) _]big_seq_cond. + apply leq_sum; intros j; rewrite andbT; intros INj. + rewrite mem_iota add0n subn0 in INj; move: INj => /andP [_ INj]. + assert (FSTeq: fst (nth elem x1' j) = fst (nth elem x2' j)). + { + have MAP := @nth_map _ elem _ tsk0 (fun x => fst x). + by rewrite -2?MAP -?SIZE //; f_equal. + } rewrite -FSTeq. + destruct (different_task_in alpha tsk_i (alpha' tsk_i) (fst (nth elem x1' j))) eqn:INTERF; + last by done. + { + exploit (LE elem); [by rewrite /= SIZE | | intro LEj]. + { + rewrite size_zip 2!size_map /= -SIZE minnn in LTi. + by rewrite size_zip /= -SIZE minnn; apply (leq_ltn_trans INj). + } + simpl in LEj. + exploit (VALID' (fst (nth elem x1' j))); last intro VALIDj. + { + apply/mapP; exists (nth elem x1' j); last by done. + by rewrite in_cons; apply/orP; right; rewrite mem_nth. + } + exploit (GE_COST (nth elem x1' j)); last intro GE_COSTj. + { + by rewrite in_cons; apply/orP; right; rewrite mem_nth. + } + unfold is_valid_sporadic_task in *. + destruct (nth elem x1' j) as [tsk_j R_j] eqn:SUBST1, + (nth elem x2' j) as [tsk_j' R_j'] eqn:SUBST2. + rewrite SUBST1 SUBST2 in LEj; clear SUBST1 SUBST2. + simpl in FSTeq; rewrite -FSTeq; simpl in LEj; simpl in VALIDj; des. + by apply interference_bound_edf_monotonic. + } + } + destruct (different_task_in alpha tsk_i (alpha' tsk_i) tsk0) eqn:INTERFtsk0; last by done. + apply leq_add; last by done. + { + exploit (LE (tsk0, R0)); [by rewrite /= SIZE | | intro LEj]; + first by instantiate (1 := 0); rewrite size_zip /= -SIZE minnn. + exploit (VALID' tsk0); first by rewrite in_cons; apply/orP; left. + exploit (GE_COST (tsk0, R0)); first by rewrite in_cons eq_refl orTb. + unfold is_valid_sporadic_task; intros GE_COST0 VALID0; des; simpl in LEj. + by apply interference_bound_edf_monotonic. + } + Qed. + + (* It follows from the properties above that the iteration is monotonically increasing. *) + Lemma bertogna_edf_comp_iteration_monotonic: forall k, all_le (f k) (f k.+1). + Proof. + unfold f; intros k. + apply fun_mon_iter_mon_generic with (x1 := k) (x2 := k.+1); + try (by done); + [ by apply all_le_reflexive + | by apply all_le_transitive + | by apply bertogna_edf_comp_iteration_preserves_order + | by apply bertogna_edf_comp_iteration_preserves_minimum]. + Qed. + + End RelationProperties. + + (* Knowing that the iteration is monotonically increasing (with respect to all_le), + we show that the RTA procedure converges to a fixed point. *) + + (* First, note that when there are no tasks, the iteration trivially converges. *) + Lemma bertogna_edf_comp_f_converges_with_no_tasks : + size ts = 0 -> + f (max_steps ts) = f (max_steps ts).+1. + Proof. + intro SIZE; destruct ts; last by inversion SIZE. + unfold max_steps; rewrite big_nil /=. + by unfold edf_rta_iteration. + Qed. + + (* Otherwise, if the iteration reached a fixed point before (max_steps ts), then + the value at (max_steps ts) is still at a fixed point. *) + Lemma bertogna_edf_comp_f_converges_early : + (exists k, k <= max_steps ts /\ f k = f k.+1) -> + f (max_steps ts) = f (max_steps ts).+1. + Proof. + by intros EX; des; apply iter_fix with (k := k). + Qed. + + (* Else, we derive a contradiction. *) + Section DerivingContradiction. + + (* Assume that there are tasks. *) + Hypothesis H_at_least_one_task: size ts > 0. + + (* Assume that the iteration continued to diverge. *) + Hypothesis H_keeps_diverging: + forall k, + k <= max_steps ts -> f k != f k.+1. + + (* Since the iteration is monotonically increasing, it must be + strictly increasing. *) + Lemma bertogna_edf_comp_f_increases : + forall k, + k <= max_steps ts -> one_lt (f k) (f k.+1). + Proof. + rename H_at_least_one_task into NONEMPTY. + intros step LEstep; unfold one_lt; apply/andP; split; + first by rewrite 2!edf_claimed_bounds_unzip1_iteration. + rewrite -[has _ _]negbK; apply/negP; unfold not; intro ALL. + rewrite -all_predC in ALL. + move: ALL => /allP ALL. + exploit (H_keeps_diverging step); [by done | intro DIFF]. + assert (DUMMY: exists tsk: sporadic_task, True). + { + destruct ts as [|tsk0]; first by rewrite ltnn in NONEMPTY. + by exists tsk0. + } + des; clear DUMMY. + move: DIFF => /eqP DIFF; apply DIFF. + apply eq_from_nth with (x0 := (tsk, 0)); + first by simpl; rewrite size_map. + { + intros i LTi. + remember (nth (tsk, 0)(f step) i) as p_i;rewrite -Heqp_i. + remember (nth (tsk, 0)(f step.+1) i) as p_i';rewrite -Heqp_i'. + rename Heqp_i into EQ, Heqp_i' into EQ'. + exploit (ALL (p_i, p_i')). + { + rewrite EQ EQ'. + rewrite -nth_zip; last by unfold f; rewrite iterS size_map. + apply mem_nth; rewrite size_zip. + unfold f; rewrite iterS size_map. + by rewrite minnn. + } + unfold predC; simpl; rewrite -ltnNge; intro LTp. + + have GROWS := bertogna_edf_comp_iteration_monotonic step. + move: GROWS => /andP [_ /allP GROWS]. + exploit (GROWS (p_i, p_i')). + { + rewrite EQ EQ'. + rewrite -nth_zip; last by unfold f; rewrite iterS size_map. + apply mem_nth; rewrite size_zip. + unfold f; rewrite iterS size_map. + by rewrite minnn. + } + simpl; intros LE. + destruct p_i as [tsk_i R_i], p_i' as [tsk_i' R_i']. + simpl in *. + assert (EQtsk: tsk_i = tsk_i'). + { + unfold edf_rta_iteration in EQ'. + rewrite (nth_map (tsk, 0)) in EQ'; last by done. + by unfold update_bound in EQ'; desf. + } + rewrite EQtsk; f_equal. + by apply/eqP; rewrite eqn_leq; apply/andP; split. + } + Qed. + + (* In the end, each response-time bound is so high that the sum + of all response-time bounds exceeds the sum of all deadlines. + Contradiction! *) + Lemma bertogna_edf_comp_rt_grows_too_much : + forall k, + k <= max_steps ts -> + \sum_((tsk, R) <- f k) (R - task_cost tsk) + 1 > k. + Proof. + have LT := bertogna_edf_comp_f_increases. + have MONO := bertogna_edf_comp_iteration_monotonic. + rename H_at_least_one_task into NONEMPTY. + unfold valid_sporadic_taskset, is_valid_sporadic_task in *. + rename H_valid_task_parameters into VALID. + intros step LE. + assert (DUMMY: exists tsk: sporadic_task, True). + { + destruct ts as [|tsk0]; first by rewrite ltnn in NONEMPTY. + by exists tsk0. + } destruct DUMMY as [elem _]. + + induction step. + { + by rewrite addn1. + } + { + rewrite -addn1 ltn_add2r. + apply leq_ltn_trans with (n := \sum_(i <- f step) (let '(tsk, R) := i in R - task_cost tsk)). + { + rewrite -ltnS; rewrite addn1 in IHstep. + by apply IHstep, ltnW. + } + rewrite (eq_bigr (fun x => snd x - task_cost (fst x))); + last by ins; destruct i. + rewrite [\sum_(_ <- f step.+1)_](eq_bigr (fun x => snd x - task_cost (fst x))); + last by ins; destruct i. + unfold f at 2; rewrite iterS. + rewrite big_map; fold (f step). + rewrite -(ltn_add2r (\sum_(i <- f step) task_cost (fst i))). + rewrite -2!big_split /=. + rewrite big_seq_cond [\sum_(_ <- _ | true)_]big_seq_cond. + rewrite (eq_bigr (fun i => snd i)); last first. + { + intro i; rewrite andbT; intro IN; + rewrite subh1; first by rewrite -addnBA // subnn addn0. + have GE_COST := edf_claimed_bounds_ge_cost ts step. + by destruct i; apply GE_COST. + } + rewrite [\sum_(_ <- _ | _)(_ - _ + _)](eq_bigr (fun i => snd (update_bound (f step) i))); last first. + { + intro i; rewrite andbT; intro IN. + unfold update_bound; destruct i; simpl. + rewrite subh1; first by rewrite -addnBA // subnn addn0. + apply (edf_claimed_bounds_ge_cost ts step.+1). + by rewrite iterS; apply/mapP; exists (s, t). + } + rewrite -2!big_seq_cond. + + specialize (LT step (ltnW LE)). + specialize (MONO step). + move: LT => /andP [_ LT]; move: LT => /hasP LT. + destruct LT as [[x1 x2] INzip LT]; simpl in *. + move: MONO => /andP [_ /(zipP _ (fun x y => snd x <= snd y)) MONO]. + rewrite 2!(big_nth (elem, 0)). + apply mem_zip_exists with (elem := (elem, 0)) (elem' := (elem, 0)) in INzip; des; + last by rewrite size_map. + rewrite -> big_cat_nat with (m := 0) (n := idx) (p := size (f step)); + [simpl | by done | by apply ltnW]. + rewrite -> big_cat_nat with (m := idx) (n := idx.+1) (p := size (f step)); + [simpl | by done | by done]. + rewrite big_nat_recr /=; last by done. + rewrite -> big_cat_nat with (m := 0) (n := idx) (p := size (f step)); + [simpl | by done | by apply ltnW]. + rewrite -> big_cat_nat with (m := idx) (n := idx.+1) (p := size (f step)); + [simpl | by done | by done]. + rewrite big_nat_recr /=; last by done. + rewrite [\sum_(idx <= i < idx) _]big_geq // add0n. + rewrite [\sum_(idx <= i < idx) _]big_geq // add0n. + rewrite -addn1 -addnA; apply leq_add. + { + rewrite big_nat_cond [\sum_(_ <= _ < _ | true) _]big_nat_cond. + apply leq_sum; move => i /andP [/andP [LT1 LT2] _]. + exploit (MONO (elem,0)); [by rewrite size_map | | intro LEi]. + { + rewrite size_zip; apply (ltn_trans LT2). + by apply leq_trans with (n := size (f step)); + [by done | by rewrite size_map minnn]. + } + unfold edf_rta_iteration in LEi. + by rewrite -> nth_map with (x1 := (elem, 0)) in LEi; + last by apply (ltn_trans LT2). + } + rewrite -addnA [_ + 1]addnC addnA; apply leq_add. + { + unfold edf_rta_iteration in INzip2; rewrite addn1. + rewrite -> nth_map with (x1 := (elem, 0)) in INzip2; last by done. + by rewrite -INzip2 -INzip1. + } + { + rewrite big_nat_cond [\sum_(_ <= _ < _ | true) _]big_nat_cond. + apply leq_sum; move => i /andP [/andP [LT1 LT2] _]. + exploit (MONO (elem,0)); + [ by rewrite size_map + | by rewrite size_zip; apply (leq_trans LT2); rewrite size_map minnn | intro LEi ]. + unfold edf_rta_iteration in LEi. + by rewrite -> nth_map with (x1 := (elem, 0)) in LEi; last by done. + } + } + Qed. + + End DerivingContradiction. + + (* Using the lemmas above, we prove that edf_rta_iteration reaches + a fixed point after (max_steps ts) step, ... *) + Lemma edf_claimed_bounds_finds_fixed_point_of_list : + forall rt_bounds, + edf_claimed_bounds ts = Some rt_bounds -> + valid_sporadic_taskset task_cost task_period task_deadline ts -> + f (max_steps ts) = edf_rta_iteration (f (max_steps ts)). + Proof. + intros rt_bounds SOME VALID. + unfold valid_sporadic_taskset, is_valid_sporadic_task in *. + unfold edf_claimed_bounds in SOME; desf. + rename Heq into LE. + fold (f (max_steps ts)) in *; fold (f (max_steps ts).+1). + + (* Either the task set is empty or not. *) + destruct (size ts == 0) eqn:EMPTY; + first by apply bertogna_edf_comp_f_converges_with_no_tasks; apply/eqP. + apply negbT in EMPTY; rewrite -lt0n in EMPTY. + + (* Either f converges by the deadline or not. *) + destruct ([exists k in 'I_((max_steps ts).+1), f k == f k.+1]) eqn:EX. + { + move: EX => /exists_inP EX; destruct EX as [k _ ITERk]. + destruct k as [k LTk]; simpl in ITERk. + apply bertogna_edf_comp_f_converges_early. + exists k; split; [by apply LTk | by apply/eqP]. + } + + (* If not, then we reach a contradiction *) + apply negbT in EX; rewrite negb_exists_in in EX. + move: EX => /forall_inP EX. + + assert (SAMESUM: \sum_(tsk <- ts) task_cost tsk = \sum_(p <- f (max_steps ts)) task_cost (fst p)). + { + have MAP := @big_map _ 0 addn _ _ (fun x => fst x) (f (max_steps ts)) + (fun x => true) (fun x => task_cost x). + have UNZIP := edf_claimed_bounds_unzip1_iteration ts (max_steps ts). + fold (f (max_steps ts)) in UNZIP; unfold unzip1 in UNZIP. + by rewrite UNZIP in MAP; rewrite MAP. + } + + (* Show that the sum is less than the sum of all deadlines. *) + assert (SUM: \sum_(p <- f (max_steps ts)) (snd p - task_cost (fst p)) + 1 <= max_steps ts). + { + unfold max_steps at 2; rewrite leq_add2r. + rewrite -(leq_add2r (\sum_(tsk <- ts) task_cost tsk)). + rewrite {1}SAMESUM -2!big_split /=. + rewrite big_seq_cond [\sum_(_ <- _ | true)_]big_seq_cond. + rewrite (eq_bigr (fun x => snd x)); last first. + { + intro i; rewrite andbT; intro IN. + rewrite subh1; first by rewrite -addnBA // subnn addn0. + have GE_COST := edf_claimed_bounds_ge_cost ts (max_steps ts). + fold (f (max_steps ts)) in GE_COST. + by destruct i; apply GE_COST. + } + rewrite (eq_bigr (fun x => task_deadline x)); last first. + { + intro i; rewrite andbT; intro IN. + rewrite subh1; first by rewrite -addnBA // subnn addn0. + by specialize (VALID i IN); des. + } + rewrite -2!big_seq_cond. + have MAP := @big_map _ 0 addn _ _ (fun x => fst x) (f (max_steps ts)) + (fun x => true) (fun x => task_deadline x). + have UNZIP := edf_claimed_bounds_unzip1_iteration ts (max_steps ts). + fold (f (max_steps ts)) in UNZIP; unfold unzip1 in UNZIP. + rewrite UNZIP in MAP; rewrite MAP. + rewrite big_seq_cond [\sum_(_ <- _|true)_]big_seq_cond. + apply leq_sum; intro i; rewrite andbT; intro IN. + move: LE => /allP LE; unfold R_le_deadline in LE. + by specialize (LE i IN); destruct i. + } + + have TOOMUCH := + bertogna_edf_comp_rt_grows_too_much EMPTY _ (max_steps ts) (leqnn (max_steps ts)). + exploit TOOMUCH; [| intro BUG]. + { + intros k LEk; rewrite -ltnS in LEk. + by exploit (EX (Ordinal LEk)); [by done | by ins]. + } + rewrite (eq_bigr (fun i => snd i - task_cost (fst i))) in BUG; + last by ins; destruct i. + by apply (leq_ltn_trans SUM) in BUG; rewrite ltnn in BUG. + Qed. + + (* ...and since there cannot be a vector of response-time bounds with values less than + the task costs, this solution is also the least fixed point. *) + Lemma edf_claimed_bounds_finds_least_fixed_point : + forall v, + all_le (initial_state ts) v -> + v = edf_rta_iteration v -> + all_le (f (max_steps ts)) v. + Proof. + intros v GE0 EQ. + apply bertogna_edf_comp_iteration_inductive; first by done. + intros k GEk. + rewrite EQ. + apply bertogna_edf_comp_iteration_preserves_order; last by done. + by apply bertogna_edf_comp_iteration_preserves_minimum. + Qed. + + + (* Therefore, with regard to the response-time bound recurrence, + the individual response-time bounds (elements of the list) are + also fixed points. *) + Theorem edf_claimed_bounds_finds_fixed_point_for_each_bound : + forall tsk R rt_bounds, + edf_claimed_bounds ts = Some rt_bounds -> + (tsk, R) \in rt_bounds -> + R = edf_response_time_bound rt_bounds tsk R. + Proof. + intros tsk R rt_bounds SOME IN. + have CONV := edf_claimed_bounds_finds_fixed_point_of_list rt_bounds. + rewrite -iterS in CONV; fold (f (max_steps ts).+1) in CONV. + unfold edf_claimed_bounds in *; desf. + exploit (CONV); [by done | by done | intro ITER; clear CONV]. + unfold f in ITER. + + cut (update_bound (iter (max_steps ts) + edf_rta_iteration (initial_state ts)) (tsk,R) = (tsk, R)). + { + intros EQ. + have F := @f_equal _ _ (fun x => snd x) _ (tsk, R). + by apply F in EQ; simpl in EQ. + } + set s := iter (max_steps ts) edf_rta_iteration (initial_state ts). + fold s in ITER, IN. + move: IN => /(nthP (tsk,0)) IN; destruct IN as [i LT EQ]. + generalize EQ; rewrite ITER iterS in EQ; intro EQ'. + fold s in EQ. + unfold edf_rta_iteration in EQ. + have MAP := @nth_map _ (tsk,0) _ _ (update_bound s). + by rewrite MAP // EQ' in EQ; rewrite EQ. + Qed. + + End Convergence. + + (* Now we prove the correctness of the response-time bounds. *) + Section MainProof. + + (* Consider a task set ts where... *) + Variable ts: taskset_of sporadic_task. + + (* ... all tasks have valid parameters ... *) + Hypothesis H_valid_task_parameters: + valid_sporadic_taskset task_cost task_period task_deadline ts. + + (* ... and constrained deadlines. *) + Hypothesis H_constrained_deadlines: + forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk. + + (* Assume that alpha' is a non-empty subaffinity of alpha. *) + Hypothesis H_non_empty_affinity: + forall tsk, tsk \in ts -> #|alpha' tsk| > 0. + Hypothesis H_subaffinity: + forall tsk, tsk \in ts -> is_subaffinity (alpha' tsk) (alpha tsk). + + (* Next, consider any arrival sequence such that...*) + Context {arr_seq: arrival_sequence Job}. + + (* ...all jobs come from task set ts, ...*) + Hypothesis H_all_jobs_from_taskset: + forall (j: JobIn arr_seq), job_task j \in ts. + + (* ...they have valid parameters,...*) + Hypothesis H_valid_job_parameters: + forall (j: JobIn arr_seq), + valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j. + + (* ... and satisfy the sporadic task model.*) + Hypothesis H_sporadic_tasks: + sporadic_task_model task_period arr_seq job_task. + + (* Then, consider any schedule with at least one CPU such that...*) + Variable sched: schedule num_cpus arr_seq. + + (* ...jobs only execute after they arrived and no longer + than their execution costs,... *) + Hypothesis H_jobs_must_arrive_to_execute: + jobs_must_arrive_to_execute sched. + Hypothesis H_completed_jobs_dont_execute: + completed_jobs_dont_execute job_cost sched. + + (* ...and jobs are sequential. *) + Hypothesis H_sequential_jobs: sequential_jobs sched. + + (* Assume a work-conserving APA scheduler that enforces EDF policy. *) + Hypothesis H_respects_affinity: respects_affinity job_task sched alpha. + Hypothesis H_work_conserving: apa_work_conserving job_cost job_task sched alpha. + Hypothesis H_edf_policy: enforces_JLDP_policy_under_weak_APA job_cost job_task sched alpha (EDF job_deadline). + + (* To avoid a long list of parameters, we provide some local definitions. *) + Definition no_deadline_missed_by_task (tsk: sporadic_task) := + task_misses_no_deadline job_cost job_deadline job_task sched tsk. + Definition no_deadline_missed_by_job := + job_misses_no_deadline job_cost job_deadline sched. + Let response_time_bounded_by (tsk: sporadic_task) := + is_response_time_bound_of_task job_cost job_task tsk sched. + + (* In the following theorem, we prove that any response-time bound contained + in edf_claimed_bounds is safe. The proof follows by direct application of + the main Theorem from bertogna_edf_theory.v. *) + Theorem edf_analysis_yields_response_time_bounds : + forall tsk R, + (tsk, R) \In edf_claimed_bounds ts -> + response_time_bounded_by tsk R. + Proof. + have BOUND := bertogna_cirinei_response_time_bound_edf. + intros tsk R IN j JOBj. + destruct (edf_claimed_bounds ts) as [rt_bounds |] eqn:SOME; last by done. + unfold edf_rta_iteration in *. + unfold is_response_time_bound_of_task in *. + apply BOUND with (task_cost := task_cost) (task_period := task_period) + (task_deadline := task_deadline) (job_deadline := job_deadline) + (job_task := job_task) (ts := ts) (tsk := tsk) (rt_bounds := rt_bounds) (alpha := alpha) (alpha' := alpha'); try (by ins). + by unfold edf_claimed_bounds in SOME; desf; rewrite edf_claimed_bounds_unzip1_iteration. + by ins; apply edf_claimed_bounds_finds_fixed_point_for_each_bound with (ts := ts). + by ins; rewrite (edf_claimed_bounds_le_deadline ts rt_bounds). + Qed. + + (* Therefore, if the schedulability test suceeds, ...*) + Hypothesis H_test_succeeds: edf_schedulable ts. + + (*... no task misses its deadline. *) + Theorem taskset_schedulable_by_edf_rta : + forall tsk, tsk \in ts -> no_deadline_missed_by_task tsk. + Proof. + have RLIST := (edf_analysis_yields_response_time_bounds). + have DL := (edf_claimed_bounds_le_deadline ts). + have HAS := (edf_claimed_bounds_has_R_for_every_task ts). + unfold no_deadline_missed_by_task, task_misses_no_deadline, + job_misses_no_deadline, completed, + edf_schedulable, + valid_sporadic_job in *. + rename H_valid_job_parameters into JOBPARAMS. + intros tsk INtsk j JOBtsk. + destruct (edf_claimed_bounds ts) as [rt_bounds |] eqn:SOME; last by ins. + exploit (HAS rt_bounds tsk); [by ins | by ins | clear HAS; intro HAS; des]. + have COMPLETED := RLIST tsk R HAS j JOBtsk. + exploit (DL rt_bounds tsk R); + [by ins | by ins | clear DL; intro DL]. + + rewrite eqn_leq; apply/andP; split; first by apply cumulative_service_le_job_cost. + apply leq_trans with (n := service sched j (job_arrival j + R)); last first. + { + unfold valid_sporadic_taskset, is_valid_sporadic_task in *. + apply extend_sum; rewrite // leq_add2l. + specialize (JOBPARAMS j); des; rewrite JOBPARAMS1. + by rewrite JOBtsk. + } + rewrite leq_eqVlt; apply/orP; left; rewrite eq_sym. + by apply COMPLETED. + Qed. + + (* For completeness, since all jobs of the arrival sequence + are spawned by the task set, we conclude that no job misses + its deadline. *) + Theorem jobs_schedulable_by_edf_rta : + forall (j: JobIn arr_seq), no_deadline_missed_by_job j. + Proof. + intros j. + have SCHED := taskset_schedulable_by_edf_rta. + unfold no_deadline_missed_by_task, task_misses_no_deadline in *. + apply SCHED with (tsk := job_task j); last by done. + by apply H_all_jobs_from_taskset. + Qed. + + End MainProof. + + End Analysis. + +End ResponseTimeIterationEDF. \ No newline at end of file diff --git a/analysis/apa/bertogna_edf_theory.v b/analysis/apa/bertogna_edf_theory.v new file mode 100644 index 0000000000000000000000000000000000000000..97485a09c0f93da117bdc49457d5bbe50c75f7da --- /dev/null +++ b/analysis/apa/bertogna_edf_theory.v @@ -0,0 +1,985 @@ +Require Import rt.util.all rt.util.divround. +Require Import rt.model.apa.task rt.model.apa.job rt.model.apa.task_arrival + rt.model.apa.schedule rt.model.apa.platform rt.model.apa.interference + rt.model.apa.workload rt.model.apa.schedulability rt.model.apa.priority + rt.model.apa.platform rt.model.apa.response_time + rt.model.apa.affinity rt.model.apa.constrained_deadlines. +Require Import rt.analysis.apa.workload_bound rt.analysis.apa.interference_bound_edf. +From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop div path. + +Module ResponseTimeAnalysisEDF. + + Export Job SporadicTaskset ScheduleOfSporadicTask Workload Schedulability ResponseTime + Priority SporadicTaskArrival WorkloadBound InterferenceBoundEDF + Interference Platform Affinity ConstrainedDeadlines. + + (* In this section, we prove that any fixed point in the APA-reduction of + Bertogna and Cirinei's RTA for EDF scheduling is a safe response-time bound. + This result corresponds to Lemma 10 in the revised version of the APA paper: + http://www.mpi-sws.org/~bbb/papers/pdf/ecrts13a-rev1.pdf *) + Section ResponseTimeBound. + + Context {sporadic_task: eqType}. + Variable task_cost: sporadic_task -> time. + Variable task_period: sporadic_task -> time. + Variable task_deadline: sporadic_task -> time. + + Context {Job: eqType}. + Variable job_cost: Job -> time. + Variable job_deadline: Job -> time. + Variable job_task: Job -> sporadic_task. + + (* Assume any job arrival sequence... *) + Context {arr_seq: arrival_sequence Job}. + + (* ... in which jobs arrive sporadically and have valid parameters. *) + Hypothesis H_sporadic_tasks: + sporadic_task_model task_period arr_seq job_task. + Hypothesis H_valid_job_parameters: + forall (j: JobIn arr_seq), + valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j. + + (* Consider a task set ts where all tasks have valid parameters + and constrained deadlines, ... *) + Variable ts: taskset_of sporadic_task. + Hypothesis H_valid_task_parameters: + valid_sporadic_taskset task_cost task_period task_deadline ts. + Hypothesis H_constrained_deadlines: + forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk. + + (* ... and assume that all jobs in the arrival sequence come from the task set. *) + Hypothesis H_all_jobs_from_taskset: + forall (j: JobIn arr_seq), job_task j \in ts. + + (* Also assume that every task has a non-empty processor affinity alpha. *) + Context {num_cpus: nat}. + Variable alpha: task_affinity sporadic_task num_cpus. + + (* Next, consider any schedule such that...*) + Variable sched: schedule num_cpus arr_seq. + + (* ...jobs are sequential and do not execute before their + arrival times nor longer than their execution costs. *) + Hypothesis H_sequential_jobs: sequential_jobs sched. + Hypothesis H_jobs_must_arrive_to_execute: + jobs_must_arrive_to_execute sched. + Hypothesis H_completed_jobs_dont_execute: + completed_jobs_dont_execute job_cost sched. + + (* Assume that the schedule is an work-conserving APA schedule that + enforces EDF priorities. *) + Hypothesis H_respects_affinity: respects_affinity job_task sched alpha. + Hypothesis H_work_conserving: apa_work_conserving job_cost job_task sched alpha. + Hypothesis H_edf_policy: + enforces_JLDP_policy_under_weak_APA job_cost job_task sched alpha (EDF job_deadline). + + (* Let's define some local names to avoid passing many parameters. *) + Let no_deadline_is_missed_by_tsk (tsk: sporadic_task) := + task_misses_no_deadline job_cost job_deadline job_task sched tsk. + Let response_time_bounded_by (tsk: sporadic_task) := + is_response_time_bound_of_task job_cost job_task tsk sched. + + (* Now we consider the response-time recurrence. In the computation of + the response-time bound, we assume that each task under analysis has + a non-empty subaffinity alpha'. + Note that the notation #|...| expresses the cardinality of the set. *) + Variable alpha': task_affinity sporadic_task num_cpus. + Hypothesis H_affinity_subset: forall tsk, tsk \in ts -> is_subaffinity (alpha' tsk) (alpha tsk). + Hypothesis H_at_least_one_cpu : forall tsk, tsk \in ts -> #|alpha' tsk| > 0. + + (* Assume that a response-time bound R is known... *) + Let task_with_response_time := (sporadic_task * time)%type. + Variable rt_bounds: seq task_with_response_time. + + (* ...for any task in the task set, ... *) + Hypothesis H_rt_bounds_contains_all_tasks: unzip1 rt_bounds = ts. + + (* ... where R is a fixed-point of the response-time recurrence (with + alpha' as the reduced affinity mask), ... *) + Let I (tsk: sporadic_task) (delta: time) := + total_interference_bound_edf task_cost task_period task_deadline alpha + tsk (alpha' tsk) rt_bounds delta. + Hypothesis H_response_time_is_fixed_point : + forall tsk R, + (tsk, R) \in rt_bounds -> + R = task_cost tsk + div_floor (I tsk R) #|alpha' tsk|. + + (* ..., and R is no larger than the deadline. *) + Hypothesis H_tasks_miss_no_deadlines: + forall tsk R, + (tsk, R) \in rt_bounds -> R <= task_deadline tsk. + + (* In order to prove that R is a response-time bound, we first provide some lemmas. *) + Section Lemmas. + + (* Let (tsk, R) be any task to be analyzed, with its response-time bound R. *) + Variable tsk: sporadic_task. + Variable R: time. + Hypothesis H_tsk_R_in_rt_bounds: (tsk, R) \in rt_bounds. + + (* Consider any job j of tsk ... *) + Variable j: JobIn arr_seq. + Hypothesis H_job_of_tsk: job_task j = tsk. + + (* ... that did not complete on time, ... *) + Hypothesis H_j_not_completed: ~~ completed job_cost sched j (job_arrival j + R). + + (* ... and that is the first job not to satisfy its response-time bound. *) + Hypothesis H_all_previous_jobs_completed_on_time : + forall (j_other: JobIn arr_seq) tsk_other R_other, + job_task j_other = tsk_other -> + (tsk_other, R_other) \in rt_bounds -> + job_arrival j_other + R_other < job_arrival j + R -> + completed job_cost sched j_other (job_arrival j_other + R_other). + + (* Let's call x the interference incurred by job j due to tsk_other, ...*) + Let x (tsk_other: sporadic_task) := + task_interference job_cost job_task sched alpha j + tsk_other (job_arrival j) (job_arrival j + R). + + (* and X the total interference incurred by job j due to any task. *) + Let X := total_interference job_cost sched j (job_arrival j) (job_arrival j + R). + + (* Recall Bertogna and Cirinei's workload bound ... *) + Let workload_bound (tsk_other: sporadic_task) (R_other: time) := + W task_cost task_period tsk_other R_other R. + + (*... and the EDF-specific bound, ... *) + Let edf_specific_bound (tsk_other: sporadic_task) (R_other: time) := + edf_specific_interference_bound task_cost task_period task_deadline tsk tsk_other R_other. + + (* ... which combined form the interference bound. *) + Let interference_bound (tsk_other: sporadic_task) (R_other: time) := + interference_bound_edf task_cost task_period task_deadline tsk R (tsk_other, R_other). + + (* Based on the definition of a different task in subaffinity alpha', ... *) + Let other_task_in alpha' := different_task_in alpha tsk alpha'. + + (* ...let (other_tasks_in alpha') denote the set of tasks that are different from tsk + and that can be scheduled on subaffinity alpha'. *) + Let other_tasks_in alpha' := + [seq tsk_other <- ts | other_task_in (alpha' tsk) tsk_other]. + + (* Now we establish results the interfering tasks. *) + Section LemmasAboutInterferingTasks. + + (* Let (tsk_other, R_other) be any pair of higher-priority task and + response-time bound computed in previous iterations. *) + Variable tsk_other: sporadic_task. + Variable R_other: time. + Hypothesis H_response_time_of_tsk_other: (tsk_other, R_other) \in rt_bounds. + + (* Note that tsk_other is in the task set, ...*) + Lemma bertogna_edf_tsk_other_in_ts: tsk_other \in ts. + Proof. + by rewrite set_mem -H_rt_bounds_contains_all_tasks; apply/mapP; exists (tsk_other, R_other). + Qed. + + (* ... and R_other is larger than the cost of tsk_other. *) + Lemma bertogna_edf_R_other_ge_cost : + R_other >= task_cost tsk_other. + Proof. + by rewrite [R_other](H_response_time_is_fixed_point tsk_other); + first by apply leq_addr. + Qed. + + (* Since tsk_other cannot interfere more than it executes, we show that + the interference caused by tsk_other is no larger than workload bound W. *) + Lemma bertogna_edf_workload_bounds_interference : + x tsk_other <= workload_bound tsk_other R_other. + Proof. + unfold valid_sporadic_job in *. + rename H_all_previous_jobs_completed_on_time into BEFOREok, + H_valid_job_parameters into PARAMS, + H_valid_task_parameters into TASK_PARAMS, + H_constrained_deadlines into RESTR, + H_tasks_miss_no_deadlines into NOMISS. + unfold x, task_interference. + have INts := bertogna_edf_tsk_other_in_ts. + apply leq_trans with (n := workload job_task sched tsk_other + (job_arrival j) (job_arrival j + R)); + first by apply task_interference_le_workload. + by apply workload_bounded_by_W with (task_deadline0 := task_deadline) + (job_cost0 := job_cost) (job_deadline0 := job_deadline); try (by ins); last 2 first; + [ by apply bertogna_edf_R_other_ge_cost + | by ins; apply NOMISS + | by ins; apply TASK_PARAMS + | by ins; apply RESTR + | by ins; apply BEFOREok with (tsk_other := tsk_other)]. + Qed. + + (* Recall that the edf-specific interference bound also holds for tsk_other. *) + Lemma bertogna_edf_specific_bound_holds : + x tsk_other <= edf_specific_bound tsk_other R_other. + Proof. + apply interference_bound_edf_bounds_interference with (job_deadline0 := job_deadline) + (ts0 := ts); try (by done); + [ by apply bertogna_edf_tsk_other_in_ts + | by apply H_tasks_miss_no_deadlines + | by apply H_tasks_miss_no_deadlines | ]. + by ins; apply H_all_previous_jobs_completed_on_time with (tsk_other := tsk_other). + Qed. + + End LemmasAboutInterferingTasks. + + (* Next we prove some lemmas that help to derive a contradiction.*) + Section DerivingContradiction. + + (* 0) Since job j did not complete by its response time bound, it follows that + the total interference X exceeds R - e_k + 1. *) + Lemma bertogna_edf_too_much_interference : X >= R - task_cost tsk + 1. + Proof. + rename H_completed_jobs_dont_execute into COMP, + H_valid_job_parameters into PARAMS, H_response_time_is_fixed_point into REC, + H_job_of_tsk into JOBtsk, H_j_not_completed into NOTCOMP. + unfold completed, valid_sporadic_job in *. + unfold X, total_interference; rewrite addn1. + rewrite -(ltn_add2r (task_cost tsk)). + rewrite subh1; last by rewrite [R](REC tsk) // leq_addr. + rewrite -addnBA // subnn addn0. + move: (NOTCOMP) => /negP NOTCOMP'. + rewrite neq_ltn in NOTCOMP. + move: NOTCOMP => /orP [LT | BUG]; last first. + { + exfalso; rewrite ltnNge in BUG; move: BUG => /negP BUG; apply BUG. + by apply cumulative_service_le_job_cost. + } + apply leq_ltn_trans with (n := (\sum_(job_arrival j <= t < job_arrival j + R) + backlogged job_cost sched j t) + + service sched j (job_arrival j + R)); last first. + { + rewrite -addn1 -addnA leq_add2l addn1. + apply leq_trans with (n := job_cost j); first by done. + by specialize (PARAMS j); des; rewrite -JOBtsk. + } + unfold service; rewrite service_before_arrival_eq_service_during //. + rewrite -big_split /=. + apply leq_trans with (n := \sum_(job_arrival j <= i < job_arrival j + R) 1); + first by rewrite big_const_nat iter_addn mul1n addn0 addKn. + rewrite big_nat_cond [\sum_(_ <= _ < _ | true) _]big_nat_cond. + apply leq_sum; move => i /andP [/andP [GEi LTi] _]. + destruct (backlogged job_cost sched j i) eqn:BACK; + first by rewrite -addn1 addnC; apply leq_add. + apply negbT in BACK. + rewrite add0n lt0n -not_scheduled_no_service negbK. + rewrite /backlogged negb_and negbK in BACK. + move: BACK => /orP [/negP NOTPENDING | SCHED]; last by done. + exfalso; apply NOTPENDING; unfold pending; apply/andP; split; first by done. + apply/negP; red; intro BUG; apply NOTCOMP'. + by apply completion_monotonic with (t := i); try (by done); apply ltnW. + Qed. + + (* 1) Next, we prove that during the scheduling window of j, any job that is + scheduled while j is backlogged comes from a different task. + This follows from the fact that j is the first job not to complete + by its response-time bound, so previous jobs of j's task must have + completed by their periods and cannot be pending. *) + Lemma bertogna_edf_interference_by_different_tasks : + forall t j_other, + job_arrival j <= t < job_arrival j + R -> + backlogged job_cost sched j t -> + scheduled sched j_other t -> + job_task j_other != tsk. + Proof. + rename H_all_jobs_from_taskset into FROMTS, + H_valid_task_parameters into PARAMS, + H_job_of_tsk into JOBtsk, H_sporadic_tasks into SPO, + H_work_conserving into WORK, + H_tsk_R_in_rt_bounds into INbounds, + H_all_previous_jobs_completed_on_time into BEFOREok, + H_tasks_miss_no_deadlines into NOMISS, + H_constrained_deadlines into RESTR. + move => t j_other /andP [LEt GEt] BACK SCHED. + apply/eqP; red; intro SAMEtsk. + move: SCHED => /existsP [cpu SCHED]. + assert (SCHED': scheduled sched j_other t). + by apply/existsP; exists cpu. + clear SCHED; rename SCHED' into SCHED. + move: (SCHED) => PENDING. + apply scheduled_implies_pending with (job_cost0 := job_cost) in PENDING; try (by done). + destruct (ltnP (job_arrival j_other) (job_arrival j)) as [BEFOREother | BEFOREj]. + { + move: (BEFOREother) => LT; rewrite -(ltn_add2r R) in LT. + specialize (BEFOREok j_other tsk R SAMEtsk INbounds LT). + move: PENDING => /andP [_ /negP NOTCOMP]; apply NOTCOMP. + apply completion_monotonic with (t0 := job_arrival j_other + R); try (by done). + apply leq_trans with (n := job_arrival j); last by done. + apply leq_trans with (n := job_arrival j_other + task_deadline tsk); + first by rewrite leq_add2l; apply NOMISS. + apply leq_trans with (n := job_arrival j_other + task_period tsk); + first by rewrite leq_add2l; apply RESTR; rewrite -JOBtsk FROMTS. + rewrite -SAMEtsk; apply SPO; [ | by rewrite JOBtsk | by apply ltnW]. + by red; intro EQ; subst; rewrite ltnn in BEFOREother. + } + { + move: PENDING => /andP [ARRIVED _]. + exploit (SPO j j_other); [ | by rewrite SAMEtsk | by done | ]; last first. + { + apply/negP; rewrite -ltnNge. + apply leq_ltn_trans with (n := t); first by done. + apply leq_trans with (n := job_arrival j + R); first by done. + by rewrite leq_add2l; apply leq_trans with (n := task_deadline tsk); + [by apply NOMISS | by rewrite JOBtsk RESTR // -JOBtsk FROMTS]. + } + by red; intros EQtsk; subst; rewrite /backlogged SCHED andbF in BACK. + } + Qed. + + (* 2) In order to use the lemmas in constrained_deadlines.v, we show that + all jobs released before the end of the interval complete by their + periods. This follows trivially from the hypothesis that all jobs + before (job_arrival j + R) complete by their response-time bounds. + With this lemma, we can conclude that during job j's scheduling + window there cannot be multiple pending jobs of each task.*) + Lemma bertogna_edf_all_previous_jobs_complete_by_their_period: + forall t (j0: JobIn arr_seq), + t < job_arrival j + R -> + job_arrival j0 + task_period (job_task j0) <= t -> + completed job_cost sched j0 + (job_arrival j0 + task_period (job_task j0)). + Proof. + rename H_rt_bounds_contains_all_tasks into UNZIP, + H_constrained_deadlines into CONSTR, + H_tasks_miss_no_deadlines into NOMISS, + H_all_jobs_from_taskset into FROMTS, + H_all_previous_jobs_completed_on_time into BEFOREok. + intros t j0 LEt LE. + cut ((job_task j0) \in unzip1 rt_bounds = true); last by rewrite UNZIP FROMTS. + move => /mapP [p IN EQ]; destruct p as [tsk' R0]; simpl in *; subst tsk'. + apply completion_monotonic with (t0 := job_arrival j0 + R0); first by done. + { + rewrite leq_add2l; apply leq_trans with (n := task_deadline (job_task j0)); + [by apply NOMISS | by apply CONSTR; rewrite FROMTS]. + } + apply BEFOREok with (tsk_other := (job_task j0)); try by done. + apply leq_ltn_trans with (n := t); last by done. + apply leq_trans with (n := job_arrival j0 + task_period (job_task j0)); last by done. + by rewrite leq_add2l; apply leq_trans with (n := task_deadline (job_task j0)); + [by apply NOMISS | apply CONSTR; rewrite FROMTS]. + Qed. + + (* 3) Next, we prove that the sum of the interference of each task is equal to the + total interference multiplied by the number of processors in tsk's *actual* + affinity. This holds because interference only occurs when all processors in + the affinity are busy. + With this lemma we can relate per-task interference with the total interference + incurred by j (backlogged time). *) + Lemma bertogna_edf_all_cpus_in_affinity_busy : + \sum_(tsk_k <- other_tasks_in alpha) x tsk_k = X * #|alpha tsk|. + Proof. + have DIFFTASK := bertogna_edf_interference_by_different_tasks. + rename H_all_jobs_from_taskset into FROMTS, + H_valid_task_parameters into PARAMS, + H_job_of_tsk into JOBtsk, H_sporadic_tasks into SPO, + H_work_conserving into WORK, + H_tsk_R_in_rt_bounds into INbounds, + H_all_previous_jobs_completed_on_time into BEFOREok, + H_tasks_miss_no_deadlines into NOMISS, + H_rt_bounds_contains_all_tasks into UNZIP, + H_constrained_deadlines into RESTR, + H_respects_affinity into APA. + unfold sporadic_task_model in *. + unfold x, X, total_interference, task_interference. + rewrite -big_mkcond -exchange_big big_distrl /= mul1n. + rewrite [\sum_(_ <= _ < _ | backlogged _ _ _ _) _]big_mkcond. + apply eq_big_nat; move => t /andP [GEt LTt]. + destruct (backlogged job_cost sched j t) eqn:BACK; last first. + { + rewrite (eq_bigr (fun i => 0)); + first by rewrite big_const_seq iter_addn mul0n addn0. + by intros i _; rewrite (eq_bigr (fun i => 0)); + first by rewrite big_const_seq iter_addn mul0n addn0. + } + rewrite big_mkcond /=. + rewrite exchange_big /=. + apply eq_trans with (y := \sum_(cpu < num_cpus | cpu \in alpha tsk) 1); + last by rewrite sum1_card. + rewrite [\sum_(_ < _ | _) 1]big_mkcond /=. + apply eq_bigr; intros cpu _. + unfold can_execute_on in *. + destruct (cpu \in alpha (job_task j)) eqn:ALPHA; rewrite -?JOBtsk ALPHA; + last by rewrite big_filter (eq_bigr (fun x => 0)); + [by simpl_sum_const | by ins]. + move: (WORK j t BACK cpu ALPHA) => [j_other /eqP SCHED]; unfold scheduled_on in *. + rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq; destruct ts. + { + rewrite (eq_bigr (fun i => 0)); + last by intros i DIFF; rewrite /task_scheduled_on SCHED;apply/eqP;rewrite eqb0 eq_sym. + rewrite big_const_seq iter_addn mul0n 2!addn0; apply/eqP; rewrite eqb1. + by unfold task_scheduled_on; rewrite SCHED. + } + rewrite mem_filter; apply/andP; split; last by apply FROMTS. + unfold different_task_in, affinity_intersects. + apply/andP; split; last first. + { + apply/existsP; exists cpu; rewrite -JOBtsk ALPHA andTb. + by apply APA with (t := t); apply/eqP. + } + apply DIFFTASK with (t := t); [by auto | by done |]. + by apply/existsP; exists cpu; apply/eqP. + Qed. + + (* 4) Recall that the reduction-based analysis considers only the interfering tasks + within subaffinity (alpha' tsk), as opposed to (alpha tsk). Therefore, we must + reason about the task interference wihin (alpha' tsk). + We begin by showing that the cumulative interference within (alpha' tsk) is at + least the total interference multiplied by the number of processors in (alpha' tsk). *) + Lemma bertogna_edf_all_cpus_in_subaffinity_busy : + \sum_(tsk_k <- other_tasks_in alpha') x tsk_k >= X * #|alpha' tsk|. + Proof. + have DIFFTASK := bertogna_edf_interference_by_different_tasks. + rename H_all_jobs_from_taskset into FROMTS, + H_valid_task_parameters into PARAMS, + H_job_of_tsk into JOBtsk, H_sporadic_tasks into SPO, + H_work_conserving into WORK, + H_tsk_R_in_rt_bounds into INbounds, + H_all_previous_jobs_completed_on_time into BEFOREok, + H_tasks_miss_no_deadlines into NOMISS, + H_rt_bounds_contains_all_tasks into UNZIP, + H_constrained_deadlines into RESTR, + H_respects_affinity into APA, H_affinity_subset into SUB. + unfold sporadic_task_model in *. + unfold x, X, total_interference, task_interference. + rewrite -big_mkcond -exchange_big big_distrl /= mul1n. + rewrite [\sum_(_ <= _ < _ | backlogged _ _ _ _) _]big_mkcond /=. + apply leq_sum_nat; move => t /andP [GEt LTt] _. + destruct (backlogged job_cost sched j t) eqn:BACK; last first. + { + rewrite (eq_bigr (fun i => 0)); + first by rewrite big_const_seq iter_addn mul0n addn0. + by intros i _; rewrite (eq_bigr (fun i => 0)); + first by rewrite big_const_seq iter_addn mul0n addn0. + } + rewrite big_mkcond /=. + rewrite exchange_big /=. + apply leq_trans with (n := \sum_(cpu < num_cpus | cpu \in alpha' tsk) 1); + first by rewrite sum1_card. + rewrite [\sum_(_ < _ | _) 1]big_mkcond /=. + apply leq_sum; intros cpu _. + unfold can_execute_on in *. + destruct (cpu \in alpha' (job_task j)) eqn:ALPHA'; rewrite -?JOBtsk ALPHA'; + last by done. + move: (SUB (job_task j) (FROMTS j) cpu ALPHA') => SUBj. + move: (WORK j t BACK cpu SUBj) => [j_other /eqP SCHED]; unfold scheduled_on in *. + rewrite (bigD1_seq (job_task j_other)) /=; last by apply filter_uniq; destruct ts. + { + by rewrite {1}/task_scheduled_on SUBj SCHED eq_refl andTb. + } + { + rewrite mem_filter; apply/andP; split; last by apply FROMTS. + apply/andP; split; last first. + { + apply/existsP; exists cpu; apply/andP; split; first by rewrite -JOBtsk. + by apply APA with (t := t); apply/eqP. + } + apply DIFFTASK with (t := t); [by auto | by done |]. + by apply/existsP; exists cpu; apply/eqP. + } + Qed. + + (* Let's define a predicate for whether a task is scheduled on (alpha tsk). *) + Let scheduled_on_alpha_tsk := fun t tsk_k => + task_scheduled_on_affinity job_task sched (alpha tsk) tsk_k t. + + (* 5) Now we prove that, at all times that j is backlogged, the number + of tasks whose affinity intersects (alpha' tsk) that are in fact + scheduled on (alpha' tsk) is at least the size of (alpha' tsk). + This is required to prove lemma (6). *) + Lemma bertogna_edf_alpha'_is_full: + forall t, + job_arrival j <= t < job_arrival j + R -> + backlogged job_cost sched j t -> + count (scheduled_on_alpha_tsk t) (other_tasks_in alpha') >= #|alpha' tsk|. + Proof. + have COMP := bertogna_edf_all_previous_jobs_complete_by_their_period. + rename H_work_conserving into WORK, H_respects_affinity into APA, + H_affinity_subset into SUB, H_job_of_tsk into JOBtsk, + H_all_jobs_from_taskset into FROMTS, + H_valid_task_parameters into PARAMS, + H_sequential_jobs into SEQ. + move => t /andP [GEt LTt] BACK. + move: WORK => WORK. + specialize (WORK j t BACK). + rewrite -size_filter. + apply leq_trans with (n := size (alpha' tsk)); + first by apply card_size. + apply leq_trans with (n := size (pmap (fun cpu => sched cpu t) (enum (alpha' tsk)))). + { + rewrite size_pmap -size_filter. + apply uniq_leq_size; first by destruct (alpha' tsk). + intros cpu IN. + rewrite mem_filter; apply/andP; split; last by rewrite mem_enum. + feed (WORK cpu); first by apply SUB; rewrite ?FROMTS ?JOBtsk. + by move: WORK => [j_other /eqP SCHED]; rewrite SCHED. + } + { + apply leq_trans with (n := size (map (fun j: JobIn arr_seq => job_task j) (pmap (fun cpu => sched cpu t) (enum (alpha' tsk))))); + first by rewrite size_map. + apply uniq_leq_size. + { + rewrite map_inj_in_uniq. + { + apply pmap_inj_in_uniq; last by apply enum_uniq. + intros cpu1 cpu2 IN1 IN2 SCHED2. + destruct (sched cpu1 t) eqn:SCHED1; symmetry in SCHED2; + first by apply SEQ with (j := j0) (t := t). + rewrite 2!mem_enum in IN1 IN2. + exploit (WORK cpu1); first by apply SUB; rewrite ?FROMTS ?JOBtsk. + by move => [j_other SCHED]; rewrite /scheduled_on SCHED1 in SCHED. + } + { + intros j1 j2 SCHED1 SCHED2 SAMEtsk. + rewrite 2!mem_pmap in SCHED1 SCHED2. + move: SCHED1 SCHED2 => /mapP [cpu1 IN1 SCHED1] /mapP [cpu2 IN2 SCHED2]. + assert (PENDING1: pending job_cost sched j1 t). + { + apply scheduled_implies_pending; try by done. + by apply/existsP; exists cpu1; rewrite /scheduled_on -SCHED1. + } + assert (SCHED2': pending job_cost sched j2 t). + { + apply scheduled_implies_pending; try by done. + by apply/existsP; exists cpu2; rewrite /scheduled_on -SCHED2. + } + apply platform_at_most_one_pending_job_of_each_task with (task_cost0 := task_cost) + (task_period0 := task_period) (task_deadline0 := task_deadline) (tsk0 := tsk) + (job_cost0 := job_cost) (job_task0 := job_task) (sched0 := sched) (j0 := j) (t0 := t); + rewrite ?JOBtsk ?SAMEtsk //; first by apply PARAMS; rewrite -JOBtsk FROMTS. + intros j0 tsk0 TSK0 LE. + by apply (COMP t); rewrite ?TSK0. + } + } + { + move => tsk' /mapP [j' IN EQtsk']. + rewrite mem_pmap in IN. + move: IN => /mapP [cpu INcpu SCHED']. + rewrite mem_enum in INcpu. + rewrite mem_filter; apply/andP; split. + { + apply/existsP; exists cpu; apply/andP; split; + first by apply SUB; rewrite -?JOBtsk ?FROMTS ?JOBtsk. + by rewrite /task_scheduled_on -SCHED' EQtsk'. + } + rewrite EQtsk' mem_filter; apply/andP; split; last by apply FROMTS. + apply/andP; split; last first. + { + apply/existsP; exists cpu; apply/andP; split; first by done. + by apply APA with (t := t); rewrite /scheduled_on SCHED'. + } + { + apply/eqP; intro SAMEtsk. + move: BACK => /andP [PENDING NOTSCHED]. + assert (SCHEDULED': scheduled sched j' t). + { + by apply/existsP; exists cpu; rewrite /scheduled_on -SCHED'. + } + move: (SCHEDULED') => PENDING'. + apply scheduled_implies_pending with (job_cost0 := job_cost) in PENDING'; + try by done. + assert (BUG: j = j'). + { + apply platform_at_most_one_pending_job_of_each_task with (task_cost0 := task_cost) + (task_period0 := task_period) (task_deadline0 := task_deadline) (tsk0 := tsk) + (job_cost0 := job_cost) (job_task0 := job_task) (sched0 := sched) (j0 := j) (t0 := t); + rewrite ?JOBtsk ?SAMEtsk //; first by apply PARAMS; rewrite -JOBtsk FROMTS. + intros j0 tsk0 TSK0 LE. + by apply (COMP t); rewrite ?TSK0. + } + by rewrite BUG SCHEDULED' in NOTSCHED. + } + } + } + Qed. + + (* Before stating the next lemma, let (num_tasks_exceeding delta) be the + number of interfering tasks that can execute on (alpha' tsk) whose + interference x is larger than delta. *) + Let num_tasks_exceeding delta := count (fun i => x i >= delta) (other_tasks_in alpha'). + + (* 6) Now we prove that, for any delta, if (num_task_exceeding delta > 0), then the + cumulative interference caused by the complementary set of interfering tasks fills + the remaining, not-completely-full (#|alpha' tsk| - num_tasks_exceeding delta) + processors. *) + Lemma bertogna_edf_interference_in_non_full_processors : + forall delta, + 0 < num_tasks_exceeding delta < #|alpha' tsk| -> + \sum_(i <- other_tasks_in alpha' | x i < delta) x i >= delta * (#|alpha' tsk| - num_tasks_exceeding delta). + Proof. + have COMP := bertogna_edf_all_previous_jobs_complete_by_their_period. + have ATMOST := platform_at_most_one_pending_job_of_each_task. + have INV := bertogna_edf_alpha'_is_full. + rename H_all_jobs_from_taskset into FROMTS, + H_valid_task_parameters into PARAMS, + H_job_of_tsk into JOBtsk, + H_sporadic_tasks into SPO, + H_tsk_R_in_rt_bounds into INbounds, + H_all_previous_jobs_completed_on_time into BEFOREok, + H_tasks_miss_no_deadlines into NOMISS, + H_constrained_deadlines into RESTR, + H_sequential_jobs into SEQ. + unfold sporadic_task_model in *. + move => delta /andP [HAS LT]. + rewrite -has_count in HAS. + + set some_interference_A := fun t => + has (fun tsk_k => backlogged job_cost sched j t && + (x tsk_k >= delta) && + scheduled_on_alpha_tsk t tsk_k) + (other_tasks_in alpha'). + set total_interference_B := fun t => + backlogged job_cost sched j t * + count (fun tsk_k => (x tsk_k < delta) && + scheduled_on_alpha_tsk t tsk_k) (other_tasks_in alpha'). + + apply leq_trans with ((\sum_(job_arrival j <= t < job_arrival j + R) + some_interference_A t) * (#|alpha' tsk| - num_tasks_exceeding delta)). + { + rewrite leq_mul2r; apply/orP; right. + move: HAS => /hasP HAS; destruct HAS as [tsk_a INa LEa]. + apply leq_trans with (n := x tsk_a); first by apply LEa. + unfold x, task_interference, some_interference_A. + apply leq_sum_nat; move => t /andP [GEt LTt] _. + destruct (backlogged job_cost sched j t) eqn:BACK; + last by rewrite (eq_bigr (fun x => 0)); [by simpl_sum_const | by ins]. + destruct ([exists cpu, can_execute_on alpha (job_task j) cpu && + task_scheduled_on job_task sched tsk_a cpu t]) eqn:SCHED; + last first. + { + apply negbT in SCHED; rewrite negb_exists in SCHED; move: SCHED => /forallP ALL. + rewrite (eq_bigr (fun x => 0)); first by simpl_sum_const. + by intros cpu _; specialize (ALL cpu); apply negbTE in ALL; rewrite ALL. + } + move: SCHED => /existsP [cpu /andP [CAN SCHED]]. + apply leq_trans with (n := 1); last first. + { + rewrite lt0b; apply/hasP; exists tsk_a; first by done. + rewrite LEa 2!andTb; apply/existsP; exists cpu. + by apply/andP; split; first by rewrite -JOBtsk. + } + rewrite (bigD1 cpu) /= // SCHED. + rewrite (eq_bigr (fun x => 0)); first by simpl_sum_const; rewrite leq_b1. + intros cpu' DIFF. + apply/eqP; rewrite eqb0; apply/negP. + move => /andP [ALPHA' SCHED']. + move: DIFF => /negP DIFF; apply DIFF; apply/eqP. + unfold task_scheduled_on in *. + destruct (sched cpu t) as [j1|] eqn:SCHED1; last by done. + destruct (sched cpu' t) as [j2|] eqn:SCHED2; last by done. + move: SCHED SCHED' => /eqP JOB /eqP JOB'. + subst tsk_a; symmetry in JOB'. + assert (BUG: j1 = j2). + { + assert (PENDING1: pending job_cost sched j1 t). + { + apply scheduled_implies_pending; try by done. + by apply/existsP; exists cpu; rewrite /scheduled_on -SCHED1. + } + assert (SCHED2': pending job_cost sched j2 t). + { + apply scheduled_implies_pending; try by done. + by apply/existsP; exists cpu'; rewrite /scheduled_on -SCHED2. + } + apply platform_at_most_one_pending_job_of_each_task with (task_cost0 := task_cost) + (task_period0 := task_period) (task_deadline0 := task_deadline) (tsk0 := tsk) + (job_cost0 := job_cost) (job_task0 := job_task) (sched0 := sched) (j0 := j) (t0 := t); + rewrite ?JOBtsk ?SAMEtsk //; first by apply PARAMS; rewrite -JOBtsk FROMTS. + intros j0 tsk0 TSK0 LE. + by apply (COMP t); rewrite ?TSK0. + } + by subst j2; apply SEQ with (j := j1) (t := t). + } + + apply leq_trans with (\sum_(job_arrival j <= t < job_arrival j + R) + total_interference_B t). + { + rewrite big_distrl /=. + apply leq_sum_nat; move => t LEt _. + unfold some_interference_A, total_interference_B. + destruct (backlogged job_cost sched j t) eqn:BACK; + [rewrite mul1n /= | by rewrite has_pred0 //]. + + destruct (has (fun tsk_k : sporadic_task => (delta <= x tsk_k) && + scheduled_on_alpha_tsk t tsk_k) (other_tasks_in alpha')) eqn:HAS'; + last by done. + rewrite mul1n; move: HAS => /hasP [tsk_k INk LEk]. + unfold num_tasks_exceeding. + apply leq_trans with (n := #|alpha' tsk| - + count (fun i => (x i >= delta) && + scheduled_on_alpha_tsk t i) (other_tasks_in alpha')). + { + apply leq_sub2l. + rewrite -2!sum1_count big_mkcond /=. + rewrite [\sum_(_ <- _ | _ <= _)_]big_mkcond /=. + apply leq_sum; intros i _. + by destruct (scheduled_on_alpha_tsk t i); + [by rewrite andbT | by rewrite andbF]. + } + rewrite -count_filter -[count _ (other_tasks_in _)]count_filter. + eapply leq_trans with (n := count (predC (fun tsk => delta <= x tsk)) _); + last by apply eq_leq, eq_in_count; red; ins; rewrite ltnNge. + rewrite leq_subLR count_predC size_filter. + move: INV => INV. by apply (INV t). + } + { + unfold x at 2, total_interference_B. + rewrite exchange_big /=; apply leq_sum; intros t _. + destruct (backlogged job_cost sched j t) eqn:BACK; last by ins. + rewrite mul1n -sum1_count. + rewrite big_mkcond [\sum_(i <- other_tasks_in alpha' | _ < _) _]big_mkcond /=. + apply leq_sum_seq; move => tsk_k IN _. + destruct (x tsk_k < delta); [rewrite andTb | by rewrite andFb]. + destruct (scheduled_on_alpha_tsk t tsk_k) eqn:SCHED; last by done. + move: SCHED => /existsP [cpu /andP [ALPHA SCHED]]. + rewrite (bigD1 cpu) /= // JOBtsk SCHED andbT. + by rewrite /can_execute_on ALPHA. + } + Qed. + + (* 7) Based on lemma (6), we prove that, for any interval delta, if the sum of per-task + interference exceeds (delta * |alpha' tsk|), the same applies for the + sum of the minimum of the interference and delta. *) + Lemma bertogna_edf_minimum_exceeds_interference : + forall delta, + \sum_(tsk_k <- other_tasks_in alpha') x tsk_k >= delta * #|alpha' tsk| -> + \sum_(tsk_k <- other_tasks_in alpha') minn (x tsk_k) delta >= + delta * #|alpha' tsk|. + Proof. + intros delta SUMLESS. + set more_interf := fun tsk_k => x tsk_k >= delta. + rewrite [\sum_(_ <- _) minn _ _](bigID more_interf) /=. + unfold more_interf, minn. + rewrite [\sum_(_ <- _ | delta <= _)_](eq_bigr (fun i => delta)); + last by intros i COND; rewrite leqNgt in COND; destruct (delta > x i). + rewrite [\sum_(_ <- _ | ~~_)_](eq_big (fun i => x i < delta) + (fun i => x i)); + [| by red; ins; rewrite ltnNge + | by intros i COND; rewrite -ltnNge in COND; rewrite COND]. + + (* Case 1: num_tasks_exceeding = 0 *) + destruct (~~ has (fun i => delta <= x i) (other_tasks_in alpha')) eqn:HASa. + { + rewrite [\sum_(_ <- _ | _ <= _) _]big_hasC; last by apply HASa. + rewrite big_seq_cond; move: HASa => /hasPn HASa. + rewrite add0n (eq_bigl (fun i => (i \in other_tasks_in alpha') && true)); + last by red; intros tsk_k; destruct (tsk_k \in other_tasks_in alpha') eqn:INk; + [by rewrite andTb ltnNge; apply HASa | by rewrite andFb]. + by rewrite -big_seq_cond. + } apply negbFE in HASa. + + (* Case 2: num_tasks_exceeding >= #|alpha' tsk| *) + destruct (num_tasks_exceeding delta >= #|alpha' tsk|) eqn:CARD. + { + apply leq_trans with (delta * num_tasks_exceeding delta); + first by rewrite leq_mul2l; apply/orP; right. + unfold num_tasks_exceeding; rewrite -sum1_count big_distrr /=. + rewrite -[\sum_(_ <- _ | _) _]addn0. + by apply leq_add; [by apply leq_sum; ins; rewrite muln1|by ins]. + } apply negbT in CARD; rewrite -ltnNge in CARD. + + (* Case 3: num_tasks_exceeding < #|alpha' tsk| *) + rewrite big_const_seq iter_addn addn0; fold num_tasks_exceeding. + apply leq_trans with (n := delta * num_tasks_exceeding delta + + delta * (#|alpha' tsk| - num_tasks_exceeding delta)); + first by rewrite -mulnDr subnKC //; apply ltnW. + rewrite leq_add2l bertogna_edf_interference_in_non_full_processors //. + by apply/andP; split; [by rewrite -has_count | by done]. + Qed. + + (* 8) Note that lemma (7) only refers to interference within subaffinity (alpha' tsk). + To reason about the interference incurred by job j in its complete affinity, + we prove that an exceeding interference on affinity (alpha tsk) also + implies an exceeding interference on the subaffinity (alpha' tsk). *) + Lemma bertogna_edf_interference_on_subaffinity : + forall delta, + \sum_(tsk_k <- other_tasks_in alpha) x tsk_k >= delta * #|alpha tsk| -> + \sum_(tsk_k <- other_tasks_in alpha') x tsk_k >= delta * #|alpha' tsk|. + Proof. + have ALL := bertogna_edf_all_cpus_in_affinity_busy. + have SUB := bertogna_edf_all_cpus_in_subaffinity_busy. + rename H_at_least_one_cpu into NONEMPTY', + H_job_of_tsk into JOBtsk, H_affinity_subset into SUBSET, + H_all_jobs_from_taskset into FROMTS. + intros delta LE. + rewrite ALL in LE. + apply leq_trans with (n := X * #|alpha' tsk|); last by apply SUB. + rewrite leq_mul2r in LE; move: LE => /orP [/eqP EMPTY | LEx]; + last by rewrite leq_mul2r; apply/orP; right. + assert (NONEMPTY: #|alpha tsk| > 0). + { + apply leq_trans with (n := #|alpha' tsk|); + last by apply leq_subaffinity, SUBSET; rewrite -JOBtsk FROMTS. + by apply NONEMPTY'; rewrite -JOBtsk FROMTS. + } + by rewrite EMPTY ltnn in NONEMPTY. + Qed. + + (* 9) Next, using lemmas (0), (3), (7) and (8), we prove that the reduction-based + interference bound is not enough to cover the sum of the minima over all tasks + (artifact of the proof by contradiction). *) + Lemma bertogna_edf_sum_exceeds_total_interference: + \sum_((tsk_other, R_other) <- rt_bounds | other_task_in (alpha' tsk) tsk_other) + minn (x tsk_other) (R - task_cost tsk + 1) > I tsk R. + Proof. + have GE_COST := bertogna_edf_R_other_ge_cost. + have EXCEEDS := bertogna_edf_minimum_exceeds_interference. + have SUB := bertogna_edf_interference_on_subaffinity. + have ALLBUSY := bertogna_edf_all_cpus_in_affinity_busy. + have TOOMUCH := bertogna_edf_too_much_interference. + rename H_rt_bounds_contains_all_tasks into UNZIP, + H_job_of_tsk into JOBtsk, + H_all_jobs_from_taskset into FROMTS, + H_response_time_is_fixed_point into REC. + apply leq_trans with (n := \sum_(tsk_other <- other_tasks_in alpha') + minn (x tsk_other) (R - task_cost tsk + 1)); + last first. + { + rewrite (eq_bigr (fun i => minn (x (fst i)) (R - task_cost tsk + 1))); + last by ins; destruct i. + assert (FILTER: filter (other_task_in (alpha' tsk)) (unzip1 rt_bounds) = + filter (other_task_in (alpha' tsk)) ts). + by f_equal. + unfold other_tasks_in; rewrite -FILTER; clear FILTER. + rewrite -[\sum_(_ <- rt_bounds | _)_]big_filter. + assert (SUBST: [seq i <- rt_bounds + | let '(tsk_other, _) := i in + other_task_in (alpha' tsk) tsk_other] = + [seq i <- rt_bounds + | other_task_in (alpha' tsk) (fst i)]). + { + by apply eq_filter; red; intro i; destruct i. + } rewrite SUBST; clear SUBST. + have MAP := big_map (fun x => fst x) (fun i => true) + (fun i => minn (x i) (R - task_cost tsk + 1)). + by rewrite -MAP; apply eq_leq; f_equal; rewrite filter_map. + } + + apply ltn_div_trunc with (d := #|alpha' tsk|); + first by apply H_at_least_one_cpu; rewrite -JOBtsk FROMTS. + rewrite -(ltn_add2l (task_cost tsk)) -REC; last first. by done. + rewrite -addn1 -leq_subLR. + rewrite -[R + 1 - _]subh1; last by apply GE_COST. + rewrite leq_divRL; + last by apply H_at_least_one_cpu; rewrite -JOBtsk FROMTS. + apply EXCEEDS, SUB. + apply leq_trans with (n := X * #|alpha tsk|); last by rewrite ALLBUSY. + by rewrite leq_mul2r; apply/orP; right; apply TOOMUCH. + Qed. + + (* 10) After concluding that the sum of the minima exceeds (R - e_i + 1), + we prove that there exists a tuple (tsk_k, R_k) that satisfies + min (x_k, R - e_i + 1) > min (W_k, I_edf, R - e_i + 1). + This implies that either x_k > W_k or x_k > I_edf, which is a contradiction, + since both W_k and I_edf are valid task interference bounds. *) + Lemma bertogna_edf_exists_task_that_exceeds_bound : + exists tsk_other R_other, + (tsk_other, R_other) \in rt_bounds /\ + (minn (x tsk_other) (R - task_cost tsk + 1) > interference_bound tsk_other R_other). + Proof. + have SUM := bertogna_edf_sum_exceeds_total_interference. + have BOUND := bertogna_edf_workload_bounds_interference. + have EDFBOUND := bertogna_edf_specific_bound_holds. + rename H_rt_bounds_contains_all_tasks into UNZIP. + assert (HAS: has (fun tup : task_with_response_time => + let (tsk_other, R_other) := tup in + (tsk_other \in ts) && other_task_in (alpha' tsk) tsk_other && + (minn (x tsk_other) (R - task_cost tsk + 1) > interference_bound tsk_other R_other)) + rt_bounds). + { + apply/negP; unfold not; intro NOTHAS. + move: NOTHAS => /negP /hasPn ALL. + rewrite -[_ < _]negbK in SUM. + move: SUM => /negP SUM; apply SUM; rewrite -leqNgt. + unfold I, total_interference_bound_edf. + rewrite big_seq_cond [\sum_(_ <- _ | let _ := _ in _)_]big_seq_cond. + apply leq_sum; move => tsk_k /andP [INBOUNDSk INTERFk]; destruct tsk_k as [tsk_k R_k]. + specialize (ALL (tsk_k, R_k) INBOUNDSk). + unfold interference_bound_edf; simpl in *. + rewrite leq_min; apply/andP; split. + { + unfold interference_bound; rewrite leq_min; apply/andP; split; + last by rewrite geq_minr. + by apply leq_trans with (n := x tsk_k); + [by rewrite geq_minl | by apply BOUND]. + } + { + apply leq_trans with (n := x tsk_k); + [by rewrite geq_minl | by apply EDFBOUND]. + } + } + move: HAS => /hasP HAS; destruct HAS as [[tsk_k R_k] HPk MIN]. + move: MIN => /andP [/andP [INts INTERFk] MINk]. + by exists tsk_k, R_k; repeat split. + Qed. + + End DerivingContradiction. + + End Lemmas. + + (* Using the lemmas above, we now prove that any response-time bound + obtained from the analysis is safe. *) + Section MainProof. + + (* Let (tsk, R) be any task to be analyzed, with its response-time bound R. *) + Variable tsk: sporadic_task. + Variable R: time. + Hypothesis H_tsk_R_in_rt_bounds: (tsk, R) \in rt_bounds. + + (* Then, R bounds the response-time of tsk. *) + Theorem bertogna_cirinei_response_time_bound_edf : + response_time_bounded_by tsk R. + Proof. + intros j JOBtsk. + + (* First, rewrite the claim in terms of the *absolute* response-time bound (arrival + R) *) + remember (job_arrival j + R) as ctime. + + revert H_tsk_R_in_rt_bounds. + generalize dependent R; generalize dependent tsk; generalize dependent j. + + (* Now, we apply strong induction on the absolute response-time bound. *) + induction ctime as [ctime IH] using strong_ind. + + intros j tsk' JOBtsk R' EQc INbounds; subst ctime. + + (* First, let's simplify the induction hypothesis. *) + assert (BEFOREok: forall (j0: JobIn arr_seq) tsk R0, + job_task j0 = tsk -> + (tsk, R0) \in rt_bounds -> + job_arrival j0 + R0 < job_arrival j + R' -> + service sched j0 (job_arrival j0 + R0) == job_cost j0). + { + by ins; apply IH with (tsk := tsk0) (R := R0). + } + clear IH. + + (* The proof follows by contradiction. Assume that job j does not complete by its + response-time bound. By the induction hypothesis, all jobs with absolute + response-time bound t < (job_arrival j + R) have correct response-time bounds. *) + destruct (completed job_cost sched j (job_arrival j + R')) eqn:NOTCOMP; + first by done. + apply negbT in NOTCOMP; exfalso. + + (* Next, we derive a contradiction using the previous lemmas. *) + exploit (bertogna_edf_exists_task_that_exceeds_bound tsk' R' INbounds j JOBtsk NOTCOMP). + { + by ins; apply IH with (tsk := tsk_other) (R := R_other). + } + intro EX; destruct EX as [tsk_other [R_other [HP LTmin]]]. + unfold interference_bound_edf, interference_bound_generic in LTmin. + rewrite minnAC in LTmin; apply min_lt_same in LTmin. + have BASICBOUND := bertogna_edf_workload_bounds_interference R' j BEFOREok tsk_other R_other HP. + have EDFBOUND := (bertogna_edf_specific_bound_holds tsk' R' INbounds j JOBtsk BEFOREok tsk_other R_other HP). + unfold minn in LTmin; clear -LTmin HP BASICBOUND EDFBOUND tsk; desf. + { + by apply (leq_ltn_trans BASICBOUND) in LTmin; rewrite ltnn in LTmin. + } + { + by apply (leq_ltn_trans EDFBOUND) in LTmin; rewrite ltnn in LTmin. + } + Qed. + + End MainProof. + + End ResponseTimeBound. + +End ResponseTimeAnalysisEDF. \ No newline at end of file diff --git a/analysis/apa/bertogna_fp_comp.v b/analysis/apa/bertogna_fp_comp.v new file mode 100644 index 0000000000000000000000000000000000000000..b8a18df38d37f5529c442a9ba2e4eb50d6768264 --- /dev/null +++ b/analysis/apa/bertogna_fp_comp.v @@ -0,0 +1,731 @@ +Require Import rt.util.all rt.util.divround. +Require Import rt.analysis.apa.bertogna_fp_theory. +From mathcomp Require Import ssreflect ssrbool ssrfun eqtype ssrnat seq fintype bigop div path. + +Module ResponseTimeIterationFP. + + Import ResponseTimeAnalysisFP. + + (* In this section, we define the algorithm corresponding to the APA-reduction + of Bertogna and Cirinei's RTA for FP scheduling. *) + Section Analysis. + + Context {sporadic_task: eqType}. + Variable task_cost: sporadic_task -> time. + Variable task_period: sporadic_task -> time. + Variable task_deadline: sporadic_task -> time. + + Context {Job: eqType}. + Variable job_cost: Job -> time. + Variable job_deadline: Job -> time. + Variable job_task: Job -> sporadic_task. + + (* As input for each iteration of the algorithm, we consider pairs + of tasks and computed response-time bounds. *) + Let task_with_response_time := (sporadic_task * time)%type. + + (* Consider a platform with num_cpus processors, ... *) + Variable num_cpus: nat. + + (* ..., and priorities based on an FP policy. *) + Variable higher_priority: FP_policy sporadic_task. + + (* Assume that every task has a processor affinity alpha. *) + Variable alpha: task_affinity sporadic_task num_cpus. + + (* For the reduction to subproblems, consider a subaffinity alpha' for each task. *) + Variable alpha': task_affinity sporadic_task num_cpus. + + (* Next we define the fixed-point iteration for the APA-reduction of + Bertogna and Cirineis's response-time analysis. *) + + (* First, given a sequence of pairs R_prev = <..., (tsk_hp, R_hp)> of + response-time bounds for the higher-priority tasks, we define an + iteration that computes the response-time bound of the current task: + + R_tsk (0) = task_cost tsk + R_tsk (step + 1) = f (R step), + + where f is the response-time recurrence, step is the number of iterations, + and R_tsk (0) is the initial state. *) + Definition per_task_rta (tsk: sporadic_task) + (R_prev: seq task_with_response_time) (step: nat) := + iter step + (fun t => task_cost tsk + + div_floor + (total_interference_bound_fp task_cost task_period alpha tsk + (alpha' tsk) R_prev t higher_priority) + #|alpha' tsk|) + (task_cost tsk). + + (* To ensure that the iteration converges, we will apply per_task_rta + a "sufficient" number of times: task_deadline tsk - task_cost tsk + 1. + This corresponds to the time complexity of the iteration. *) + Definition max_steps (tsk: sporadic_task) := task_deadline tsk - task_cost tsk + 1. + + (* Next we compute the response-time bounds for the entire task set. + Since high-priority tasks may not be schedulable, we allow the + computation to fail. + Thus, given the response-time bound of previous tasks, we either + (a) append the computed response-time bound (tsk, R) of the current task + to the list of pairs, or, + (b) return None if the response-time analysis failed. *) + Definition fp_bound_of_task hp_pairs tsk := + if hp_pairs is Some rt_bounds then + let R := per_task_rta tsk rt_bounds (max_steps tsk) in + if R <= task_deadline tsk then + Some (rcons rt_bounds (tsk, R)) + else None + else None. + + (* The response-time analysis for a given task set is defined + as a left-fold (reduce) based on the function above. + This either returns a list of task and response-time bounds, or None. *) + Definition fp_claimed_bounds (ts: seq sporadic_task) := + foldl fp_bound_of_task (Some [::]) ts. + + (* The schedulability test simply checks if we got a list of + response-time bounds (i.e., if the computation did not fail). *) + Definition fp_schedulable (ts: seq sporadic_task) := + fp_claimed_bounds ts != None. + + (* In the following section, we prove several helper lemmas about the + list of response-time bounds. The results seem trivial, but must be proven + nonetheless since the list of response-time bounds is computed with + a specific algorithm and there are no lemmas in the library for that. *) + Section SimpleLemmas. + + (* First, we show that the first component of the computed list is the set of tasks. *) + Lemma fp_claimed_bounds_unzip : + forall ts hp_bounds, + fp_claimed_bounds ts = Some hp_bounds -> + unzip1 hp_bounds = ts. + Proof. + unfold fp_claimed_bounds in *; intros ts. + induction ts using last_ind; first by destruct hp_bounds. + { + intros hp_bounds SOME. + destruct (lastP hp_bounds) as [| hp_bounds']. + { + rewrite -cats1 foldl_cat /= in SOME. + unfold fp_bound_of_task at 1 in SOME; simpl in *; desf. + by destruct l. + } + rewrite -cats1 foldl_cat /= in SOME. + unfold fp_bound_of_task at 1 in SOME; simpl in *; desf. + move: H0 => /eqP EQSEQ. + rewrite eqseq_rcons in EQSEQ. + move: EQSEQ => /andP [/eqP SUBST /eqP EQSEQ]; subst. + unfold unzip1; rewrite map_rcons; f_equal. + by apply IHts. + } + Qed. + + (* Next, we show that some properties of the analysis are preserved for the + prefixes of the list: (a) the tasks do not change, (b) R <= deadline, + (c) R is computed using the response-time equation, ... *) + Lemma fp_claimed_bounds_rcons : + forall ts' hp_bounds tsk1 tsk2 R, + (fp_claimed_bounds (rcons ts' tsk1) = Some (rcons hp_bounds (tsk2, R)) -> + (fp_claimed_bounds ts' = Some hp_bounds /\ + tsk1 = tsk2 /\ + R = per_task_rta tsk1 hp_bounds (max_steps tsk1) /\ + R <= task_deadline tsk1)). + Proof. + intros ts hp_bounds tsk tsk' R. + rewrite -cats1. + unfold fp_claimed_bounds in *. + rewrite foldl_cat /=. + unfold fp_bound_of_task at 1; simpl; desf. + intros EQ; inversion EQ; move: EQ H0 => _ /eqP EQ. + rewrite eqseq_rcons in EQ. + move: EQ => /andP [/eqP EQ /eqP RESP]. + by inversion RESP; repeat split; subst. + Qed. + + (* ..., which implies that any prefix of the computation is the computation + of the prefix. *) + Lemma fp_claimed_bounds_take : + forall ts hp_bounds i, + fp_claimed_bounds ts = Some hp_bounds -> + i <= size hp_bounds -> + fp_claimed_bounds (take i ts) = Some (take i hp_bounds). + Proof. + intros ts hp_bounds i SOME LTi. + have UNZIP := fp_claimed_bounds_unzip ts hp_bounds SOME. + rewrite <- UNZIP in *. + rewrite -[hp_bounds]take_size /unzip1 map_take in SOME. + fold (unzip1 hp_bounds) in *; clear UNZIP. + rewrite leq_eqVlt in LTi. + move: LTi => /orP [/eqP EQ | LTi]; first by subst. + remember (size hp_bounds) as len; apply eq_leq in Heqlen. + induction len; first by rewrite ltn0 in LTi. + { + assert (TAKElen: fp_claimed_bounds (take len (unzip1 (hp_bounds))) = + Some (take len (hp_bounds))). + { + assert (exists p, p \in hp_bounds). + { + destruct hp_bounds; first by rewrite ltn0 in Heqlen. + by exists t; rewrite in_cons eq_refl orTb. + } destruct H as [[tsk R] _]. + rewrite (take_nth tsk) in SOME; last by rewrite size_map. + rewrite (take_nth (tsk,R)) in SOME; last by done. + destruct (nth (tsk, R) hp_bounds len) as [tsk_len R_len]. + by apply fp_claimed_bounds_rcons in SOME; des. + } + rewrite ltnS leq_eqVlt in LTi. + move: LTi => /orP [/eqP EQ | LESS]; first by subst. + apply ltnW in Heqlen. + by specialize (IHlen Heqlen TAKElen LESS). + } + Qed. + + (* If the analysis suceeds, the computed response-time bounds are no larger + than the deadlines ... *) + Lemma fp_claimed_bounds_le_deadline : + forall ts' rt_bounds tsk R, + fp_claimed_bounds ts' = Some rt_bounds -> + (tsk, R) \in rt_bounds -> + R <= task_deadline tsk. + Proof. + intros ts; induction ts as [| ts' tsk_lst] using last_ind. + { + intros rt_bounds tsk R SOME IN. + by inversion SOME; subst; rewrite in_nil in IN. + } + { + intros rt_bounds tsk_i R SOME IN. + destruct (lastP rt_bounds) as [|rt_bounds (tsk_lst', R_lst)]; + first by rewrite in_nil in IN. + rewrite mem_rcons in_cons in IN; move: IN => /orP IN. + destruct IN as [LAST | FRONT]. + { + move: LAST => /eqP LAST. + rewrite -cats1 in SOME. + unfold fp_claimed_bounds in *. + rewrite foldl_cat /= in SOME. + unfold fp_bound_of_task in SOME. + desf; rename H0 into EQ. + move: EQ => /eqP EQ. + rewrite eqseq_rcons in EQ. + move: EQ => /andP [_ /eqP EQ]. + inversion EQ; subst. + by apply Heq0. + } + { + apply IHts with (rt_bounds := rt_bounds); last by ins. + by apply fp_claimed_bounds_rcons in SOME; des. + } + } + Qed. + + (* ... and no smaller than the task costs. *) + Lemma fp_claimed_bounds_ge_cost : + forall ts' rt_bounds tsk R, + fp_claimed_bounds ts' = Some rt_bounds -> + (tsk, R) \in rt_bounds -> + R >= task_cost tsk. + Proof. + intros ts; induction ts as [| ts' tsk_lst] using last_ind. + { + intros rt_bounds tsk R SOME IN. + by inversion SOME; subst; rewrite in_nil in IN. + } + { + intros rt_bounds tsk_i R SOME IN. + destruct (lastP rt_bounds) as [|rt_bounds (tsk_lst', R_lst)]; + first by rewrite in_nil in IN. + rewrite mem_rcons in_cons in IN; move: IN => /orP IN. + destruct IN as [LAST | FRONT]. + { + move: LAST => /eqP LAST. + rewrite -cats1 in SOME. + unfold fp_claimed_bounds in *. + rewrite foldl_cat /= in SOME. + unfold fp_bound_of_task in SOME. + desf; rename H0 into EQ. + move: EQ => /eqP EQ. + rewrite eqseq_rcons in EQ. + move: EQ => /andP [_ /eqP EQ]. + inversion EQ; subst. + by destruct (max_steps tsk_lst'); + [by apply leqnn | by apply leq_addr]. + } + { + apply IHts with (rt_bounds := rt_bounds); last by ins. + by apply fp_claimed_bounds_rcons in SOME; des. + } + } + Qed. + + (* Short lemma about unfolding the iteration one step. *) + Lemma per_task_rta_fold : + forall tsk rt_bounds, + task_cost tsk + + div_floor (total_interference_bound_fp task_cost task_period alpha tsk (alpha' tsk) rt_bounds + (per_task_rta tsk rt_bounds (max_steps tsk)) higher_priority) #|alpha' tsk| + = per_task_rta tsk rt_bounds (max_steps tsk).+1. + Proof. + by done. + Qed. + + End SimpleLemmas. + + (* In this section, we prove that if the task set is sorted by priority, + the tasks in fp_claimed_bounds are interfering tasks. *) + Section HighPriorityTasks. + + (* Consider a list of previous tasks and a task tsk to be analyzed. *) + Variable ts: taskset_of sporadic_task. + + (* Assume that the task set is sorted by unique priorities, ... *) + Hypothesis H_task_set_is_sorted: sorted higher_priority ts. + Hypothesis H_task_set_has_unique_priorities: + FP_is_antisymmetric_over_task_set higher_priority ts. + + (* ...the priority order is transitive, ...*) + Hypothesis H_priority_transitive: FP_is_transitive higher_priority. + + (* ... and that the response-time analysis succeeds. *) + Variable hp_bounds: seq task_with_response_time. + Variable R: time. + Hypothesis H_analysis_succeeds: fp_claimed_bounds ts = Some hp_bounds. + + (* Let's refer to tasks by index. *) + Variable elem: sporadic_task. + Let TASK := nth elem ts. + + (* We prove that higher-priority tasks have smaller index. *) + Lemma fp_claimed_bounds_hp_tasks_have_smaller_index : + forall hp_idx idx, + hp_idx < size ts -> + idx < size ts -> + hp_idx != idx -> + higher_priority (TASK hp_idx) (TASK idx) -> + hp_idx < idx. + Proof. + unfold TASK; clear TASK. + rename ts into ts'; destruct ts' as [ts UNIQ]; simpl in *. + intros hp_idx idx LThp LT NEQ HP. + rewrite ltn_neqAle; apply/andP; split; first by done. + by apply sorted_rel_implies_le_idx with (leT := higher_priority) (s := ts) (x0 := elem). + Qed. + + End HighPriorityTasks. + + (* In this section, we show that the fixed-point iteration converges. *) + Section Convergence. + + (* Consider any sequence with higher-priority tasks. *) + Variable ts_hp: seq sporadic_task. + + (* Assume that the response-time analysis succeeds for the higher-priority tasks. *) + Variable rt_bounds: seq task_with_response_time. + Hypothesis H_test_succeeds: fp_claimed_bounds ts_hp = Some rt_bounds. + + (* Consider any task tsk to be analyzed, ... *) + Variable tsk: sporadic_task. + + (* ... and assume all tasks have valid parameters. *) + Hypothesis H_valid_task_parameters: + valid_sporadic_taskset task_cost task_period task_deadline (rcons ts_hp tsk). + + (* To simplify, let f denote the fixed-point iteration. *) + Let f := per_task_rta tsk rt_bounds. + + (* Assume that f (max_steps tsk) is no larger than the deadline. *) + Hypothesis H_no_larger_than_deadline: f (max_steps tsk) <= task_deadline tsk. + + (* First, we show that f is monotonically increasing. *) + Lemma bertogna_fp_comp_f_monotonic : + forall x1 x2, x1 <= x2 -> f x1 <= f x2. + Proof. + unfold valid_sporadic_taskset, is_valid_sporadic_task in *. + rename H_test_succeeds into SOME, + H_valid_task_parameters into VALID. + intros x1 x2 LEx; unfold f, per_task_rta. + apply fun_mon_iter_mon; [by ins | by ins; apply leq_addr |]. + clear LEx x1 x2; intros x1 x2 LEx. + unfold div_floor, total_interference_bound_fp. + rewrite big_seq_cond [\sum_(i <- _ | let '(tsk_other, _) := i in + higher_priority_task_in _ _ _ _ _)_]big_seq_cond. + rewrite leq_add2l leq_div2r //. + apply leq_sum; move => i /andP [IN _]. + destruct i as [i R]. + have GE_COST := fp_claimed_bounds_ge_cost ts_hp rt_bounds i R SOME IN. + have UNZIP := fp_claimed_bounds_unzip ts_hp rt_bounds SOME. + unfold interference_bound_generic; simpl. + assert (IN': i \in ts_hp). + { + by rewrite -UNZIP; apply/mapP; exists (i,R). + } + unfold interference_bound_generic; simpl. + rewrite leq_min; apply/andP; split. + { + apply leq_trans with (n := W task_cost task_period i R x1); first by apply geq_minl. + exploit (VALID i); [by rewrite mem_rcons in_cons IN' orbT | ins; des]. + by apply W_monotonic; try (by done); apply GE_COST. + } + { + apply leq_trans with (n := x1 - task_cost tsk + 1); first by apply geq_minr. + by rewrite leq_add2r leq_sub2r //. + } + Qed. + + (* If the iteration converged at an earlier step, it remains as a fixed point. *) + Lemma bertogna_fp_comp_f_converges_early : + (exists k, k <= max_steps tsk /\ f k = f k.+1) -> + f (max_steps tsk) = f (max_steps tsk).+1. + Proof. + by intros EX; des; apply iter_fix with (k := k). + Qed. + + (* Else, we derive a contradiction. *) + Section DerivingContradiction. + + (* Assume instead that the iteration continued to diverge. *) + Hypothesis H_keeps_diverging: + forall k, + k <= max_steps tsk -> f k != f k.+1. + + (* By monotonicity, it follows that the value always increases. *) + Lemma bertogna_fp_comp_f_increases : + forall k, + k <= max_steps tsk -> + f k < f k.+1. + Proof. + intros k LT. + rewrite ltn_neqAle; apply/andP; split. + by apply H_keeps_diverging. + by apply bertogna_fp_comp_f_monotonic, leqnSn. + Qed. + + (* In the end, the response-time bound must exceed the deadline. Contradiction! *) + Lemma bertogna_fp_comp_rt_grows_too_much : + forall k, + k <= max_steps tsk -> + f k > k + task_cost tsk - 1. + Proof. + have INC := bertogna_fp_comp_f_increases. + rename H_valid_task_parameters into TASK_PARAMS. + unfold valid_sporadic_taskset, is_valid_sporadic_task in *; des. + exploit (TASK_PARAMS tsk); + [by rewrite mem_rcons in_cons eq_refl orTb | intro PARAMS; des]. + induction k. + { + intros _; rewrite add0n -addn1 subh1; + first by rewrite -addnBA // subnn addn0 /= leqnn. + by apply PARAMS. + } + { + intros LT. + specialize (IHk (ltnW LT)). + apply leq_ltn_trans with (n := f k);last by apply INC, ltnW. + rewrite -addn1 -addnA [1 + _]addnC addnA -addnBA // subnn addn0. + rewrite -(ltn_add2r 1) in IHk. + rewrite subh1 in IHk; + last by apply leq_trans with (n := task_cost tsk); + [by apply PARAMS | by apply leq_addl]. + by rewrite -addnBA // subnn addn0 addn1 ltnS in IHk. + } + Qed. + + End DerivingContradiction. + + (* Using the lemmas above, we prove the convergence of the iteration after max_steps. *) + Lemma per_task_rta_converges: + f (max_steps tsk) = f (max_steps tsk).+1. + Proof. + have TOOMUCH := bertogna_fp_comp_rt_grows_too_much. + have INC := bertogna_fp_comp_f_increases. + rename H_no_larger_than_deadline into LE, + H_valid_task_parameters into TASK_PARAMS. + unfold valid_sporadic_taskset, is_valid_sporadic_task in *; des. + + (* Either f converges by the deadline or not. *) + destruct ([exists k in 'I_(max_steps tsk).+1, f k == f k.+1]) eqn:EX. + { + move: EX => /exists_inP EX; destruct EX as [k _ ITERk]. + apply bertogna_fp_comp_f_converges_early. + by exists k; split; [by rewrite -ltnS; apply ltn_ord | by apply/eqP]. + } + + (* If not, then we reach a contradiction *) + apply negbT in EX; rewrite negb_exists_in in EX. + move: EX => /forall_inP EX. + rewrite leqNgt in LE; move: LE => /negP LE. + exfalso; apply LE. + + assert (DIFF: forall k : nat, k <= max_steps tsk -> f k != f k.+1). + { + intros k LEk; rewrite -ltnS in LEk. + by exploit (EX (Ordinal LEk)); [by done | intro DIFF; apply DIFF]. + } + exploit TOOMUCH; [by apply DIFF | by apply leq_addr |]. + exploit (TASK_PARAMS tsk); + [by rewrite mem_rcons in_cons eq_refl orTb | intro PARAMS; des]. + rewrite subh1; last by apply PARAMS2. + rewrite -addnBA // subnn addn0 subn1 prednK //. + intros LT; apply (leq_ltn_trans LT). + by rewrite /max_steps [_ - _ + 1]addn1; apply INC, leq_addr. + Qed. + + End Convergence. + + (* Now we prove the correctness of the response-time bounds. *) + Section MainProof. + + (* Consider a task set ts. *) + Variable ts: taskset_of sporadic_task. + + (* Assume that all tasks have valid parameters ... *) + Hypothesis H_valid_task_parameters: + valid_sporadic_taskset task_cost task_period task_deadline ts. + + (* ...and constrained deadlines. *) + Hypothesis H_constrained_deadlines: + forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk. + + (* Assume that alpha' is a non-empty subaffinity of alpha. *) + Hypothesis H_non_empty_affinity: + forall tsk, tsk \in ts -> #|alpha' tsk| > 0. + Hypothesis H_subaffinity: + forall tsk, tsk \in ts -> is_subaffinity (alpha' tsk) (alpha tsk). + + (* Assume that the task set is totally ordered by unique decreasing + priorities and that the priority order is transitive. *) + Hypothesis H_task_set_is_sorted: sorted higher_priority ts. + Hypothesis H_task_set_has_unique_priorities: + FP_is_antisymmetric_over_task_set higher_priority ts. + Hypothesis H_priority_is_total: + FP_is_total_over_task_set higher_priority ts. + Hypothesis H_priority_transitive: FP_is_transitive higher_priority. + + (* Next, consider any arrival sequence such that...*) + Context {arr_seq: arrival_sequence Job}. + + (* ...all jobs come from task set ts, ...*) + Hypothesis H_all_jobs_from_taskset: + forall (j: JobIn arr_seq), job_task j \in ts. + + (* ...jobs have valid parameters,...*) + Hypothesis H_valid_job_parameters: + forall (j: JobIn arr_seq), + valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j. + + (* ... and satisfy the sporadic task model.*) + Hypothesis H_sporadic_tasks: + sporadic_task_model task_period arr_seq job_task. + + (* Then, consider any schedule such that...*) + Variable sched: schedule num_cpus arr_seq. + + (* ...jobs only execute after they arrived and no longer + than their execution costs,... *) + Hypothesis H_jobs_must_arrive_to_execute: + jobs_must_arrive_to_execute sched. + Hypothesis H_completed_jobs_dont_execute: + completed_jobs_dont_execute job_cost sched. + + (* ...and jobs are sequential. *) + Hypothesis H_sequential_jobs: sequential_jobs sched. + + (* Assume a work-conserving APA scheduler that enforces the FP policy. *) + Hypothesis H_respects_affinity: respects_affinity job_task sched alpha. + Hypothesis H_work_conserving: apa_work_conserving job_cost job_task sched alpha. + Hypothesis H_enforces_FP_policy: + enforces_FP_policy_under_weak_APA job_cost job_task sched alpha higher_priority. + + (* To avoid a long list of parameters, we provide some local definitions. *) + Let no_deadline_missed_by_task (tsk: sporadic_task) := + task_misses_no_deadline job_cost job_deadline job_task sched tsk. + Let no_deadline_missed_by_job := + job_misses_no_deadline job_cost job_deadline sched. + Let response_time_bounded_by (tsk: sporadic_task) := + is_response_time_bound_of_task job_cost job_task tsk sched. + + (* In the following theorem, we prove that any response-time bound contained + in fp_claimed_bounds is safe. The proof follows by induction on the task set: + + Induction hypothesis: assume all higher-priority tasks have safe response-time bounds. + Inductive step: we prove that the response-time bound of the current task is safe. + + Note that the inductive step is a direct application of the main Theorem from + bertogna_fp_theory.v. *) + Theorem fp_analysis_yields_response_time_bounds : + forall tsk R, + (tsk, R) \In fp_claimed_bounds ts -> + response_time_bounded_by tsk R. + Proof. + rename H_valid_job_parameters into JOBPARAMS, H_valid_task_parameters into TASKPARAMS. + unfold valid_sporadic_taskset in *. + intros tsk R MATCH. + assert (SOME: exists hp_bounds, fp_claimed_bounds ts = Some hp_bounds /\ + (tsk, R) \in hp_bounds). + { + destruct (fp_claimed_bounds ts); last by done. + by exists l; split. + } clear MATCH; des; rename SOME0 into IN. + + have UNZIP := fp_claimed_bounds_unzip ts hp_bounds SOME. + + set elem := (tsk,R). + move: IN => /(nthP elem) [idx LTidx EQ]. + set NTH := fun k => nth elem hp_bounds k. + set TASK := fun k => (NTH k).1. + set RESP := fun k => (NTH k).2. + cut (response_time_bounded_by (TASK idx) (RESP idx)); + first by unfold TASK, RESP, NTH; rewrite EQ. + clear EQ. + + assert (PAIR: forall idx, (TASK idx, RESP idx) = NTH idx). + { + by intros i; unfold TASK, RESP; destruct (NTH i). + } + + assert (SUBST: forall i, i < size hp_bounds -> TASK i = nth tsk ts i). + { + + by intros i LTi; rewrite /TASK /NTH -UNZIP (nth_map elem) //. + } + + assert (SIZE: size hp_bounds = size ts). + { + by rewrite -UNZIP size_map. + } + + induction idx as [idx IH'] using strong_ind. + + assert (IH: forall tsk_hp R_hp, (tsk_hp, R_hp) \in take idx hp_bounds -> response_time_bounded_by tsk_hp R_hp). + { + intros tsk_hp R_hp INhp. + move: INhp => /(nthP elem) [k LTk EQ]. + rewrite size_take LTidx in LTk. + rewrite nth_take in EQ; last by done. + cut (response_time_bounded_by (TASK k) (RESP k)); + first by unfold TASK, RESP, NTH; rewrite EQ. + by apply IH'; try (by done); apply (ltn_trans LTk). + } clear IH'. + + unfold response_time_bounded_by in *. + + exploit (fp_claimed_bounds_rcons (take idx ts) (take idx hp_bounds) (TASK idx) (TASK idx) (RESP idx)). + { + by rewrite PAIR SUBST // -2?take_nth -?SIZE // (fp_claimed_bounds_take _ hp_bounds). + } + intros [_ [_ [REC DL]]]. + + apply bertogna_cirinei_response_time_bound_fp with + (alpha0 := alpha) (alpha'0 := alpha') + (task_cost0 := task_cost) (task_period0 := task_period) + (task_deadline0 := task_deadline) (job_deadline0 := job_deadline) (tsk0 := (TASK idx)) + (job_task0 := job_task) (ts0 := ts) (hp_bounds0 := take idx hp_bounds) + (higher_eq_priority := higher_priority); try (by ins). + { + cut (NTH idx \in hp_bounds = true); [intros IN | by apply mem_nth]. + by rewrite set_mem -UNZIP; apply/mapP; exists (TASK idx, RESP idx); rewrite PAIR. + } + { + intros hp_tsk IN INTERF. + exists (RESP (index hp_tsk ts)). + move: (IN) => INDEX; apply nth_index with (x0 := tsk) in INDEX. + rewrite -{1}[hp_tsk]INDEX -SUBST; last by rewrite SIZE index_mem. + assert (UNIQ: uniq hp_bounds). + { + apply map_uniq with (f := fst); unfold unzip1 in *; rewrite UNZIP. + by destruct ts. + } + rewrite -filter_idx_lt_take //. + { + rewrite PAIR mem_filter; apply/andP; split; + last by apply mem_nth; rewrite SIZE index_mem. + { + rewrite /NTH index_uniq; [| by rewrite SIZE index_mem | by done ]. + { + move: INTERF => /andP [/andP [HP NEQ] _]. + apply fp_claimed_bounds_hp_tasks_have_smaller_index with + (ts := ts) (elem := tsk) (hp_bounds := hp_bounds); + try (by done); + [by rewrite index_mem | by rewrite -SIZE | | by rewrite INDEX -SUBST]. + apply/eqP; intro BUG; subst idx. + rewrite SUBST -{1}INDEX in NEQ; + first by rewrite eq_refl in NEQ. + by rewrite SIZE index_mem INDEX. + } + } + } + } + { + intros hp_tsk R_hp IN; apply mem_take in IN. + by apply fp_claimed_bounds_ge_cost with (ts' := ts) (rt_bounds := hp_bounds). + } + { + intros hp_tsk R_hp IN; apply mem_take in IN. + by apply fp_claimed_bounds_le_deadline with (ts' := ts) (rt_bounds := hp_bounds). + } + { + rewrite REC. + rewrite per_task_rta_fold. + apply per_task_rta_converges with (ts_hp := take idx ts); + [by apply fp_claimed_bounds_take; try (by apply ltnW) | | by rewrite -REC ]. + rewrite SUBST // -take_nth -?SIZE //. + by intros i IN; eapply TASKPARAMS, mem_take, IN. + } + Qed. + + (* Therefore, if the schedulability test suceeds, ...*) + Hypothesis H_test_succeeds: fp_schedulable ts. + + (*..., no task misses its deadline. *) + Theorem taskset_schedulable_by_fp_rta : + forall tsk, tsk \in ts -> no_deadline_missed_by_task tsk. + Proof. + have RLIST := (fp_analysis_yields_response_time_bounds). + have UNZIP := (fp_claimed_bounds_unzip ts). + have DL := (fp_claimed_bounds_le_deadline ts). + unfold no_deadline_missed_by_task, task_misses_no_deadline, + job_misses_no_deadline, completed, + fp_schedulable, valid_sporadic_job in *. + rename H_valid_job_parameters into JOBPARAMS. + move => tsk INtsk j JOBtsk. + + destruct (fp_claimed_bounds ts) as [rt_bounds |]; last by ins. + feed (UNZIP rt_bounds); first by done. + assert (EX: exists R, (tsk, R) \in rt_bounds). + { + rewrite set_mem -UNZIP in INtsk; move: INtsk => /mapP EX. + by destruct EX as [p]; destruct p as [tsk' R]; simpl in *; subst tsk'; exists R. + } des. + exploit (RLIST tsk R); [by ins | by apply JOBtsk | intro COMPLETED]. + exploit (DL rt_bounds tsk R); [by ins | by ins | clear DL; intro DL]. + rewrite eqn_leq; apply/andP; split; first by apply cumulative_service_le_job_cost. + apply leq_trans with (n := service sched j (job_arrival j + R)); last first. + { + unfold valid_sporadic_taskset, is_valid_sporadic_task in *. + apply extend_sum; rewrite // leq_add2l. + specialize (JOBPARAMS j); des; rewrite JOBPARAMS1. + by rewrite JOBtsk. + } + rewrite leq_eqVlt; apply/orP; left; rewrite eq_sym. + by apply COMPLETED. + Qed. + + (* For completeness, since all jobs of the arrival sequence + are spawned by the task set, we also conclude that no job in + the schedule misses its deadline. *) + Theorem jobs_schedulable_by_fp_rta : + forall (j: JobIn arr_seq), no_deadline_missed_by_job j. + Proof. + intros j. + have SCHED := taskset_schedulable_by_fp_rta. + unfold no_deadline_missed_by_task, task_misses_no_deadline in *. + apply SCHED with (tsk := job_task j); last by done. + by apply H_all_jobs_from_taskset. + Qed. + + End MainProof. + + End Analysis. + +End ResponseTimeIterationFP. \ No newline at end of file diff --git a/analysis/apa/bertogna_fp_theory.v b/analysis/apa/bertogna_fp_theory.v new file mode 100644 index 0000000000000000000000000000000000000000..0dab3f0ffe88f737c08404369e8a1d6a0ddb51b5 --- /dev/null +++ b/analysis/apa/bertogna_fp_theory.v @@ -0,0 +1,1029 @@ +Require Import rt.util.all rt.util.divround. +Require Import rt.model.apa.task rt.model.apa.job rt.model.apa.task_arrival + rt.model.apa.schedule rt.model.apa.platform rt.model.apa.platform_fp + rt.model.apa.workload rt.model.apa.schedulability rt.model.apa.priority + rt.model.apa.response_time rt.model.apa.interference + rt.model.apa.affinity rt.model.apa.constrained_deadlines. +Require Import rt.analysis.apa.workload_bound rt.analysis.apa.interference_bound_fp. +From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop div path. + +Module ResponseTimeAnalysisFP. + + Export Job SporadicTaskset ScheduleOfSporadicTask Workload Interference InterferenceBoundFP + Platform PlatformFP Schedulability ResponseTime Priority + SporadicTaskArrival WorkloadBound Affinity ConstrainedDeadlines. + + + (* In this section, we prove that any fixed point in the APA-reduction of Bertogna + and Cirinei's RTA for FP scheduling with slack updates is a safe response-time + bound. This result corresponds to Lemma 9 in the revised version of the APA paper: + http://www.mpi-sws.org/~bbb/papers/pdf/ecrts13a-rev1.pdf *) + Section ResponseTimeBound. + + Context {sporadic_task: eqType}. + Variable task_cost: sporadic_task -> time. + Variable task_period: sporadic_task -> time. + Variable task_deadline: sporadic_task -> time. + + Context {Job: eqType}. + Variable job_cost: Job -> time. + Variable job_deadline: Job -> time. + Variable job_task: Job -> sporadic_task. + + (* Assume any job arrival sequence... *) + Context {arr_seq: arrival_sequence Job}. + + (* ... in which jobs arrive sporadically and have valid parameters. *) + Hypothesis H_sporadic_tasks: + sporadic_task_model task_period arr_seq job_task. + Hypothesis H_valid_job_parameters: + forall (j: JobIn arr_seq), + valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j. + + (* Assume that we have a task set where all tasks have valid + parameters and constrained deadlines, ... *) + Variable ts: taskset_of sporadic_task. + Hypothesis H_valid_task_parameters: + valid_sporadic_taskset task_cost task_period task_deadline ts. + Hypothesis H_constrained_deadlines: + forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk. + + (* ... and that all jobs in the arrival sequence come from the task set. *) + Hypothesis H_all_jobs_from_taskset: + forall (j: JobIn arr_seq), job_task j \in ts. + + (* Also assume that every task has a non-empty processor affinity alpha. *) + Context {num_cpus: nat}. + Variable alpha: task_affinity sporadic_task num_cpus. + + (* Next, consider any schedule such that...*) + Variable sched: schedule num_cpus arr_seq. + + (* ...jobs are sequential and do not execute before their + arrival times nor longer than their execution costs. *) + Hypothesis H_sequential_jobs: sequential_jobs sched. + Hypothesis H_jobs_must_arrive_to_execute: + jobs_must_arrive_to_execute sched. + Hypothesis H_completed_jobs_dont_execute: + completed_jobs_dont_execute job_cost sched. + + (* Consider a given FP policy, ... *) + Variable higher_eq_priority: FP_policy sporadic_task. + + (* ... and assume that the schedule is an APA work-conserving + schedule that enforces this policy. *) + Hypothesis H_respects_affinity: respects_affinity job_task sched alpha. + Hypothesis H_work_conserving: apa_work_conserving job_cost job_task sched alpha. + Hypothesis H_enforces_FP_policy: + enforces_FP_policy_under_weak_APA job_cost job_task sched alpha higher_eq_priority. + + (* Let's define some local names to avoid passing many parameters. *) + Let no_deadline_is_missed_by_tsk (tsk: sporadic_task) := + task_misses_no_deadline job_cost job_deadline job_task sched tsk. + Let response_time_bounded_by (tsk: sporadic_task) := + is_response_time_bound_of_task job_cost job_task tsk sched. + + (* Next, we consider the response-time recurrence. + Let tsk be a task in ts that is to be analyzed. *) + Variable tsk: sporadic_task. + Hypothesis task_in_ts: tsk \in ts. + + (* When computing the response-time bounds, we assume that each task under + analysis has a non-empty subaffinity alpha'. *) + Variable alpha': task_affinity sporadic_task num_cpus. + Hypothesis H_affinity_subset: forall tsk, tsk \in ts -> is_subaffinity (alpha' tsk) (alpha tsk). + Hypothesis H_at_least_one_cpu : forall tsk, tsk \in ts -> #|alpha' tsk| > 0. + + (* Let (hp_task_in alpha') denote whether a task is a higher-priority task + (with respect to tsk) that can execute on processors in (alpha' tsk). *) + Let hp_task_in alpha' := higher_priority_task_in alpha higher_eq_priority tsk alpha'. + + (* Assume a response-time bound is known... *) + Let task_with_response_time := (sporadic_task * time)%type. + Variable hp_bounds: seq task_with_response_time. + Hypothesis H_response_time_of_interfering_tasks_is_known: + forall hp_tsk R, + (hp_tsk, R) \in hp_bounds -> + is_response_time_bound_of_task job_cost job_task hp_tsk sched R. + + (* ... for every higher-priority task in (alpha tsk). *) + Hypothesis H_hp_bounds_has_interfering_tasks: + forall hp_tsk, + hp_tsk \in ts -> + hp_task_in (alpha tsk) hp_tsk -> + exists R, + (hp_tsk, R) \in hp_bounds. + + (* Assume that the response-time bounds are larger than task costs. *) + Hypothesis H_response_time_bounds_ge_cost: + forall hp_tsk R, + (hp_tsk, R) \in hp_bounds -> R >= task_cost hp_tsk. + + (* Assume that no deadline is missed by any higher-priority task. *) + Hypothesis H_interfering_tasks_miss_no_deadlines: + forall hp_tsk R, + (hp_tsk, R) \in hp_bounds -> R <= task_deadline hp_tsk. + + (* Let R be the fixed point of the APA-reduction of Bertogna and + Cirinei's response-time recurrence (using subaffinity alpha'), ...*) + Variable R: time. + Hypothesis H_response_time_recurrence_holds : + R = task_cost tsk + + div_floor + (total_interference_bound_fp task_cost task_period alpha tsk + (alpha' tsk) hp_bounds R higher_eq_priority) + #|alpha' tsk|. + + (* ... and assume that R is no larger than the deadline of tsk.*) + Hypothesis H_response_time_no_larger_than_deadline: + R <= task_deadline tsk. + + (* In order to prove that R is a response-time bound, we first provide some lemmas. *) + Section Lemmas. + + (* Consider any job j of tsk. *) + Variable j: JobIn arr_seq. + Hypothesis H_job_of_tsk: job_task j = tsk. + + (* Assume that job j is the first job of tsk not to complete by the response time bound. *) + Hypothesis H_j_not_completed: ~~ completed job_cost sched j (job_arrival j + R). + Hypothesis H_previous_jobs_of_tsk_completed : + forall (j0: JobIn arr_seq), + job_task j0 = tsk -> + job_arrival j0 < job_arrival j -> + completed job_cost sched j0 (job_arrival j0 + R). + + (* Let's call x the interference incurred by job j due to tsk_other, ...*) + Let x (tsk_other: sporadic_task) := + task_interference job_cost job_task sched alpha j tsk_other + (job_arrival j) (job_arrival j + R). + + (* and X the total interference incurred by job j due to any task. *) + Let X := total_interference job_cost sched j (job_arrival j) (job_arrival j + R). + + (* Recall Bertogna and Cirinei's workload bound. *) + Let workload_bound (tsk_other: sporadic_task) (R_other: time) := + W task_cost task_period tsk_other R_other R. + + (* Let (hp_tasks_in alpha') denote the set of higher-priority tasks + that can be scheduled on subaffinity alpha'. *) + Let hp_tasks_in alpha' := + [seq tsk_other <- ts | hp_task_in (alpha' tsk) tsk_other]. + + (* Now we establish results the higher-priority tasks. *) + Section LemmasAboutHPTasks. + + (* Let (tsk_other, R_other) be any pair of higher-priority task + and response-time bound computed in previous iterations. *) + Variable tsk_other: sporadic_task. + Variable R_other: time. + Hypothesis H_tsk_other_already_processed: (tsk_other, R_other) \in hp_bounds. + Hypothesis H_tsk_other_has_higher_priority: hp_task_in (alpha tsk) tsk_other. + + (* Since tsk_other cannot interfere more than it executes, we show that + the interference caused by tsk_other is no larger than workload bound W. *) + Lemma bertogna_fp_workload_bounds_interference : + x tsk_other <= workload_bound tsk_other R_other. + Proof. + unfold completed, completed_jobs_dont_execute, valid_sporadic_job in *. + rename H_valid_job_parameters into PARAMS, + H_valid_task_parameters into TASK_PARAMS, + H_constrained_deadlines into RESTR, + H_all_jobs_from_taskset into FROMTS, + H_response_time_of_interfering_tasks_is_known into RESP, + H_interfering_tasks_miss_no_deadlines into NOMISS, + H_response_time_bounds_ge_cost into GE_COST. + unfold x, workload_bound. + destruct ([exists t: 'I_(job_arrival j + R), + task_is_scheduled job_task sched tsk_other t]) eqn: SCHED; + last first. + { + apply negbT in SCHED; rewrite negb_exists in SCHED. + move: SCHED => /forallP SCHED. + apply leq_trans with (n := 0); last by done. + apply leq_trans with (n := \sum_(job_arrival j <= t < job_arrival j + R) 0); + last by rewrite big1. + apply leq_sum_nat; move => i /andP [_ LTi] _. + specialize (SCHED (Ordinal LTi)). + rewrite negb_exists in SCHED; move: SCHED => /forallP SCHED. + rewrite big1 //; intros cpu _. + specialize (SCHED cpu); apply negbTE in SCHED. + by rewrite SCHED andbF. + } + move: SCHED => /existsP [t /existsP [cpu SCHED]]. + unfold task_scheduled_on in SCHED. + destruct (sched cpu t) as [j0 |]; last by done. + assert (INts: tsk_other \in ts). + by move: SCHED => /eqP <-; rewrite FROMTS. + apply leq_trans with (n := workload job_task sched tsk_other + (job_arrival j) (job_arrival j + R)); + first by apply task_interference_le_workload. + by apply workload_bounded_by_W with (task_deadline0 := task_deadline) + (job_cost0 := job_cost) (job_deadline0 := job_deadline); + try (by ins); last 2 first; + [ by ins; apply GE_COST + | by ins; apply NOMISS + | by ins; apply TASK_PARAMS + | by ins; apply RESTR + | by ins; apply RESP with (hp_tsk := tsk_other)]. + Qed. + + End LemmasAboutHPTasks. + + (* Next we prove some lemmas that help to derive a contradiction.*) + Section DerivingContradiction. + + (* 0) Since job j did not complete by its response time bound, it follows that + the total interference X >= R - e_k + 1. *) + Lemma bertogna_fp_too_much_interference : X >= R - task_cost tsk + 1. + Proof. + rename H_completed_jobs_dont_execute into COMP, + H_valid_job_parameters into PARAMS, + H_response_time_recurrence_holds into REC, + H_job_of_tsk into JOBtsk, H_j_not_completed into NOTCOMP. + unfold completed, valid_sporadic_job in *. + unfold X, total_interference; rewrite addn1. + rewrite -(ltn_add2r (task_cost tsk)). + rewrite subh1; last by rewrite [R]REC // leq_addr. + rewrite -addnBA // subnn addn0. + move: (NOTCOMP) => /negP NOTCOMP'. + rewrite neq_ltn in NOTCOMP. + move: NOTCOMP => /orP [LT | BUG]; last first. + { + exfalso; rewrite ltnNge in BUG; move: BUG => /negP BUG; apply BUG. + by apply cumulative_service_le_job_cost. + } + apply leq_ltn_trans with (n := (\sum_(job_arrival j <= t < job_arrival j + R) + backlogged job_cost sched j t) + + service sched j (job_arrival j + R)); last first. + { + rewrite -addn1 -addnA leq_add2l addn1. + apply leq_trans with (n := job_cost j); first by done. + by specialize (PARAMS j); des; rewrite -JOBtsk. + } + unfold service; rewrite service_before_arrival_eq_service_during //. + rewrite -big_split /=. + apply leq_trans with (n := \sum_(job_arrival j <= i < job_arrival j + R) 1); + first by rewrite big_const_nat iter_addn mul1n addn0 addKn. + rewrite big_nat_cond [\sum_(_ <= _ < _ | true) _]big_nat_cond. + apply leq_sum; move => i /andP [/andP [GEi LTi] _]. + destruct (backlogged job_cost sched j i) eqn:BACK; + first by rewrite -addn1 addnC; apply leq_add. + apply negbT in BACK. + rewrite add0n lt0n -not_scheduled_no_service negbK. + rewrite /backlogged negb_and negbK in BACK. + move: BACK => /orP [/negP NOTPENDING | SCHED]; last by done. + exfalso; apply NOTPENDING; unfold pending; apply/andP; split; first by done. + apply/negP; red; intro BUG; apply NOTCOMP'. + by apply completion_monotonic with (t := i); try (by done); apply ltnW. + Qed. + + (* 1) Next, we prove that during the scheduling window of j, any job that is + scheduled while j is backlogged comes from a different task. + This follows from the fact that j is the first job not to complete + by its response-time bound, so previous jobs of j's task must have + completed by their periods and cannot be pending. *) + Lemma bertogna_fp_interference_by_different_tasks : + forall t j_other, + job_arrival j <= t < job_arrival j + R -> + backlogged job_cost sched j t -> + scheduled sched j_other t -> + job_task j_other != tsk. + Proof. + rename H_all_jobs_from_taskset into FROMTS, + H_valid_task_parameters into PARAMS, + H_job_of_tsk into JOBtsk, H_sporadic_tasks into SPO, + H_work_conserving into WORK, + H_previous_jobs_of_tsk_completed into BEFOREok, + H_response_time_no_larger_than_deadline into NOMISS, + H_constrained_deadlines into RESTR. + move => t j_other /andP [LEt GEt] BACK SCHED. + apply/eqP; red; intro SAMEtsk. + move: SCHED => /existsP [cpu SCHED]. + assert (SCHED': scheduled sched j_other t). + by apply/existsP; exists cpu. + clear SCHED; rename SCHED' into SCHED. + move: (SCHED) => PENDING. + apply scheduled_implies_pending with (job_cost0 := job_cost) in PENDING; try (by done). + destruct (ltnP (job_arrival j_other) (job_arrival j)) as [BEFOREother | BEFOREj]. + { + move: (BEFOREother) => LT; rewrite -(ltn_add2r R) in LT. + specialize (BEFOREok j_other SAMEtsk BEFOREother). + move: PENDING => /andP [_ /negP NOTCOMP]; apply NOTCOMP. + apply completion_monotonic with (t0 := job_arrival j_other + R); try (by done). + apply leq_trans with (n := job_arrival j); last by done. + apply leq_trans with (n := job_arrival j_other + task_deadline tsk); + first by rewrite leq_add2l; apply NOMISS. + apply leq_trans with (n := job_arrival j_other + task_period tsk); + first by rewrite leq_add2l; apply RESTR; rewrite -JOBtsk FROMTS. + rewrite -SAMEtsk; apply SPO; [ | by rewrite JOBtsk | by apply ltnW]. + by red; intro EQ; subst j_other; rewrite ltnn in BEFOREother. + } + { + move: PENDING => /andP [ARRIVED _]. + exploit (SPO j j_other); [ | by rewrite SAMEtsk | by done | ]; last first. + { + apply/negP; rewrite -ltnNge. + apply leq_ltn_trans with (n := t); first by done. + apply leq_trans with (n := job_arrival j + R); first by done. + by rewrite leq_add2l; apply leq_trans with (n := task_deadline tsk); + [by apply NOMISS | by rewrite JOBtsk RESTR // -JOBtsk FROMTS]. + } + by red; intros EQtsk; subst j_other; rewrite /backlogged SCHED andbF in BACK. + } + Qed. + + (* 2) In order to use the lemmas in constrained_deadlines.v, we show that + all jobs of higher-priority tasks released before the end of the + interval complete by their periods. This follows trivially from + the fact that all these tasks are known to be schedulable. + With this lemma, we can conclude that during job j's scheduling + window there cannot be multiple pending jobs of higher-priority tasks.*) + Lemma bertogna_fp_previous_interfering_jobs_complete_by_their_period: + forall (j0: JobIn arr_seq), + hp_task_in (alpha tsk) (job_task j0) -> + completed job_cost sched j0 + (job_arrival j0 + task_period (job_task j0)). + Proof. + rename H_hp_bounds_has_interfering_tasks into HAS, + H_constrained_deadlines into CONSTR, + H_response_time_no_larger_than_deadline into NOMISS, + H_all_jobs_from_taskset into FROMTS, + H_interfering_tasks_miss_no_deadlines into NOMISS', + H_previous_jobs_of_tsk_completed into BEFOREok, + H_response_time_of_interfering_tasks_is_known into RESP. + intros j0 INTERF. + exploit (HAS (job_task j0)); + [by rewrite FROMTS | by done | move => [R0 INbounds]]. + apply completion_monotonic with (t := job_arrival j0 + R0); first by done. + { + rewrite leq_add2l; apply leq_trans with (n := task_deadline (job_task j0)); + [by apply NOMISS' | by apply CONSTR; rewrite FROMTS]. + } + { + by apply (RESP (job_task j0)). + } + Qed. + + (* 3) Next, we prove that the sum of the interference of each task is equal to the + total interference multiplied by the number of processors in tsk's *actual* + affinity. This holds because interference only occurs when all processors in + the affinity are busy. + With this lemma we can relate per-task interference with the total interference + incurred by j (backlogged time). *) + Lemma bertogna_fp_all_cpus_in_affinity_busy : + \sum_(tsk_k <- hp_tasks_in alpha) x tsk_k = X * #|alpha tsk|. + Proof. + have DIFFTASK := bertogna_fp_interference_by_different_tasks. + rename H_all_jobs_from_taskset into FROMTS, + H_valid_task_parameters into PARAMS, + H_job_of_tsk into JOBtsk, H_sporadic_tasks into SPO, + H_work_conserving into WORK, + H_previous_jobs_of_tsk_completed into BEFOREok, + H_response_time_no_larger_than_deadline into NOMISS, + H_constrained_deadlines into RESTR, + H_respects_affinity into APA, H_enforces_FP_policy into FP. + unfold sporadic_task_model in *. + unfold x, X, total_interference, task_interference. + rewrite -big_mkcond -exchange_big big_distrl /= mul1n. + rewrite [\sum_(_ <= _ < _ | backlogged _ _ _ _) _]big_mkcond. + apply eq_big_nat; move => t /andP [GEt LTt]. + destruct (backlogged job_cost sched j t) eqn:BACK; last first. + { + rewrite (eq_bigr (fun i => 0)); + first by rewrite big_const_seq iter_addn mul0n addn0. + by intros i _; rewrite (eq_bigr (fun i => 0)); + first by rewrite big_const_seq iter_addn mul0n addn0. + } + rewrite big_mkcond /=. + rewrite exchange_big /=. + apply eq_trans with (y := \sum_(cpu < num_cpus | cpu \in alpha tsk) 1); + last by rewrite sum1_card. + rewrite [\sum_(_ < _ | _) 1]big_mkcond /=. + apply eq_bigr; intros cpu _. + unfold can_execute_on in *. + destruct (cpu \in alpha (job_task j)) eqn:ALPHA; rewrite -?JOBtsk ALPHA; + last by rewrite big_filter (eq_bigr (fun x => 0)); + [by simpl_sum_const | by ins]. + move: (WORK j t BACK cpu ALPHA) => [j_other /eqP SCHED]; unfold scheduled_on in *. + rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq; destruct ts. + { + rewrite (eq_bigr (fun i => 0)); + last by intros i DIFF; rewrite /task_scheduled_on SCHED;apply/eqP;rewrite eqb0 eq_sym. + rewrite big_const_seq iter_addn mul0n 2!addn0; apply/eqP; rewrite eqb1. + by unfold task_scheduled_on; rewrite SCHED. + } + rewrite mem_filter; apply/andP; split; last by apply FROMTS. + unfold higher_priority_task_in, affinity_intersects. + apply/andP; split; last first. + { + apply/existsP; exists cpu; rewrite -JOBtsk ALPHA andTb. + by apply APA with (t := t); apply/eqP. + } + apply/andP; split; last first. + { + apply DIFFTASK with (t := t); [by auto | by done |]. + by apply/existsP; exists cpu; apply/eqP. + } + by rewrite -JOBtsk; apply FP with (cpu := cpu) (t := t); try (by apply/eqP). + Qed. + + (* 4) Recall that the reduction-based analysis considers only the interfering tasks + within subaffinity (alpha' tsk), as opposed to (alpha tsk). Therefore, we must + reason about the task interference wihin (alpha' tsk). + We begin by showing that the cumulative interference within (alpha' tsk) is at + least the total interference multiplied by the number of processors in (alpha' tsk). *) + Lemma bertogna_fp_all_cpus_in_subaffinity_busy : + \sum_(tsk_k <- hp_tasks_in alpha') x tsk_k >= X * #|alpha' tsk|. + Proof. + have DIFFTASK := bertogna_fp_interference_by_different_tasks. + rename H_all_jobs_from_taskset into FROMTS, + H_valid_task_parameters into PARAMS, + H_job_of_tsk into JOBtsk, H_sporadic_tasks into SPO, + H_work_conserving into WORK, + H_response_time_no_larger_than_deadline into NOMISS, + H_constrained_deadlines into RESTR, + H_enforces_FP_policy into FP, + H_respects_affinity into APA, H_affinity_subset into SUB. + unfold sporadic_task_model in *. + unfold x, X, total_interference, task_interference. + rewrite -big_mkcond -exchange_big big_distrl /= mul1n. + rewrite [\sum_(_ <= _ < _ | backlogged _ _ _ _) _]big_mkcond /=. + apply leq_sum_nat; move => t /andP [GEt LTt] _. + destruct (backlogged job_cost sched j t) eqn:BACK; last first. + { + rewrite (eq_bigr (fun i => 0)); + first by rewrite big_const_seq iter_addn mul0n addn0. + by intros i _; rewrite (eq_bigr (fun i => 0)); + first by rewrite big_const_seq iter_addn mul0n addn0. + } + rewrite big_mkcond /=. + rewrite exchange_big /=. + apply leq_trans with (n := \sum_(cpu < num_cpus | cpu \in alpha' tsk) 1); + first by rewrite sum1_card. + rewrite [\sum_(_ < _ | _) 1]big_mkcond /=. + apply leq_sum; intros cpu _. + unfold can_execute_on in *. + destruct (cpu \in alpha' (job_task j)) eqn:ALPHA'; rewrite -?JOBtsk ALPHA'; + last by done. + move: (SUB (job_task j) (FROMTS j) cpu ALPHA') => SUBj. + move: (WORK j t BACK cpu SUBj) => [j_other /eqP SCHED]; unfold scheduled_on in *. + rewrite (bigD1_seq (job_task j_other)) /=; last by apply filter_uniq; destruct ts. + { + by rewrite {1}/task_scheduled_on SUBj SCHED eq_refl andTb. + } + { + rewrite mem_filter; apply/andP; split; last by apply FROMTS. + apply/andP; split; last first. + { + apply/existsP; exists cpu; apply/andP; split; first by rewrite -JOBtsk. + by apply APA with (t := t); apply/eqP. + } + apply/andP; split; last first. + { + apply DIFFTASK with (t := t); [by auto | by done |]. + by apply/existsP; exists cpu; apply/eqP. + } + by rewrite -JOBtsk; apply FP with (cpu := cpu) (t := t); try (by apply/eqP). + } + Qed. + + (* Let's define a predicate for whether a task is scheduled on (alpha tsk). *) + Let scheduled_on_alpha_tsk := fun t tsk_k => + task_scheduled_on_affinity job_task sched (alpha tsk) tsk_k t. + + + (* 5) Now we prove that, at all times that j is backlogged, the number + of tasks whose affinity intersects (alpha' tsk) that are in fact + scheduled on (alpha' tsk) is at least the size of (alpha' tsk). + This is required to prove lemma (6). *) + Lemma bertogna_fp_alpha'_is_full: + forall t, + job_arrival j <= t < job_arrival j + R -> + backlogged job_cost sched j t -> + count (scheduled_on_alpha_tsk t) (hp_tasks_in alpha') >= #|alpha' tsk|. + Proof. + have DIFFTASK := bertogna_fp_interference_by_different_tasks. + have COMP := bertogna_fp_previous_interfering_jobs_complete_by_their_period. + rename H_work_conserving into WORK, H_respects_affinity into APA, + H_affinity_subset into SUB, H_job_of_tsk into JOBtsk, + H_all_jobs_from_taskset into FROMTS, + H_valid_task_parameters into PARAMS, + H_previous_jobs_of_tsk_completed into BEFOREok, + H_sequential_jobs into SEQ, H_constrained_deadlines into CONSTR, + H_enforces_FP_policy into FP. + move => t /andP [GEt LTt] BACK. + move: WORK => WORK. + specialize (WORK j t BACK). + rewrite -size_filter. + apply leq_trans with (n := size (alpha' tsk)); + first by apply card_size. + apply leq_trans with (n := size (pmap (fun cpu => sched cpu t) (enum (alpha' tsk)))). + { + rewrite size_pmap -size_filter. + apply uniq_leq_size; first by destruct (alpha' tsk). + intros cpu IN. + rewrite mem_filter; apply/andP; split; last by rewrite mem_enum. + feed (WORK cpu); first by apply SUB; rewrite JOBtsk. + by move: WORK => [j_other /eqP SCHED]; rewrite SCHED. + } + { + apply leq_trans with (n := size (map (fun j: JobIn arr_seq => job_task j) (pmap (fun cpu => sched cpu t) (enum (alpha' tsk))))); + first by rewrite size_map. + apply uniq_leq_size. + { + rewrite map_inj_in_uniq. + { + apply pmap_inj_in_uniq; last by apply enum_uniq. + intros cpu1 cpu2 IN1 IN2 SCHED2. + destruct (sched cpu1 t) eqn:SCHED1; symmetry in SCHED2; + first by apply SEQ with (j := j0) (t := t). + rewrite 2!mem_enum in IN1 IN2. + exploit (WORK cpu1); first by apply SUB; rewrite JOBtsk. + by move => [j_other SCHED]; rewrite /scheduled_on SCHED1 in SCHED. + } + { + intros j1 j2 SCHED1 SCHED2 SAMEtsk. + rewrite 2!mem_pmap in SCHED1 SCHED2. + move: SCHED1 SCHED2 => /mapP [cpu1 IN1 SCHED1] /mapP [cpu2 IN2 SCHED2]. + assert (PENDING1: pending job_cost sched j1 t). + { + apply scheduled_implies_pending; try by done. + by apply/existsP; exists cpu1; rewrite /scheduled_on -SCHED1. + } + assert (SCHED2': pending job_cost sched j2 t). + { + apply scheduled_implies_pending; try by done. + by apply/existsP; exists cpu2; rewrite /scheduled_on -SCHED2. + } + + apply platform_fp_no_multiple_jobs_of_interfering_tasks with + (task_period0 := task_period) (tsk0 := tsk) (alpha0 := alpha) + (higher_eq_priority0 := higher_eq_priority) (t0 := t) + (job_cost0 := job_cost) (job_task0 := job_task) (sched0 := sched); + rewrite ?JOBtsk ?SAMEtsk //. + { + by intros j0 tsk0 TSK0 LE; subst tsk0; apply COMP. + } + { + apply/andP; split; last first. + { + apply/existsP; exists cpu2; apply/andP; split. + by apply SUB; [rewrite -JOBtsk FROMTS | rewrite -mem_enum]. + by apply APA with (t := t); apply/eqP; rewrite SCHED2. + } + apply/andP; split. + { + rewrite -JOBtsk; apply FP with (cpu := cpu2) (t := t); + [by done | by apply/eqP; rewrite SCHED2 |]. + by apply SUB; rewrite ?FROMTS // -mem_enum JOBtsk. + } + { + apply DIFFTASK with (t := t); [auto | by done |]. + by apply/existsP; exists cpu2; apply/eqP; rewrite SCHED2. + } + } + } + } + { + move => tsk' /mapP [j' IN EQtsk']. + rewrite mem_pmap in IN. + move: IN => /mapP [cpu INcpu SCHED']. + rewrite mem_enum in INcpu. + rewrite mem_filter; apply/andP; split. + { + apply/existsP; exists cpu; apply/andP; split; first by apply SUB. + by rewrite /task_scheduled_on -SCHED' EQtsk'. + } + rewrite EQtsk' mem_filter; apply/andP; split; last by apply FROMTS. + apply/andP; split; last first. + { + apply/existsP; exists cpu; apply/andP; split; first by done. + by apply APA with (t := t); rewrite /scheduled_on SCHED'. + } + apply/andP; split. + { + by rewrite -JOBtsk; apply FP with (cpu := cpu) (t := t); + [| by apply/eqP; rewrite SCHED' | apply SUB; rewrite JOBtsk]. + } + { + apply/eqP; intro SAMEtsk. + move: BACK => /andP [PENDING NOTSCHED]. + assert (SCHEDULED': scheduled sched j' t). + { + by apply/existsP; exists cpu; rewrite /scheduled_on -SCHED'. + } + move: (SCHEDULED') => PENDING'. + apply scheduled_implies_pending with (job_cost0 := job_cost) in PENDING'; + try by done. + assert (BUG: j' = j). + { + apply platform_fp_no_multiple_jobs_of_tsk with (task_cost0 := task_cost) + (task_period0 := task_period) (task_deadline0 := task_deadline) + (job_cost0 := job_cost) (job_task0 := job_task) + (sched0 := sched) (tsk0 := tsk) (t0 := t); + try (by done); + [by apply PARAMS | by apply/andP; split | |]. + { + apply leq_trans with (n := job_arrival j + R); first by done. + by rewrite leq_add2l; apply leq_trans with (n := task_deadline tsk); + last by apply CONSTR; rewrite -JOBtsk FROMTS. + } + { + intros j0 JOB0 LT0. + apply completion_monotonic with (t0 := job_arrival j0 + R); + [by done | | by apply BEFOREok]. + by rewrite leq_add2l; apply leq_trans with (n := task_deadline tsk); + last by apply CONSTR; rewrite -JOBtsk FROMTS. + } + } + by rewrite -BUG SCHEDULED' in NOTSCHED. + } + } + } + Qed. + + (* Before stating the next lemma, let (num_tasks_exceeding delta) be the + number of interfering tasks that can execute on (alpha' tsk) whose + interference x is larger than delta. *) + Let num_tasks_exceeding delta := count (fun i => x i >= delta) (hp_tasks_in alpha'). + + (* 6) Now we prove that, for any delta, if (num_task_exceeding delta > 0), then the + cumulative interference caused by the complementary set of interfering tasks fills + the remaining, not-completely-full (#|alpha' tsk| - num_tasks_exceeding delta) + processors. *) + Lemma bertogna_fp_interference_in_non_full_processors : + forall delta, + 0 < num_tasks_exceeding delta < #|alpha' tsk| -> + \sum_(i <- hp_tasks_in alpha' | x i < delta) x i >= delta * (#|alpha' tsk| - num_tasks_exceeding delta). + Proof. + have ATMOST := platform_at_most_one_pending_job_of_each_task. + have INV := bertogna_fp_alpha'_is_full. + have DIFFTASK := bertogna_fp_interference_by_different_tasks. + have COMP := bertogna_fp_previous_interfering_jobs_complete_by_their_period. + rename H_all_jobs_from_taskset into FROMTS, + H_valid_task_parameters into PARAMS, + H_job_of_tsk into JOBtsk, H_affinity_subset into SUB, + H_sporadic_tasks into SPO, H_respects_affinity into APA, + H_constrained_deadlines into RESTR, + H_sequential_jobs into SEQ, H_enforces_FP_policy into FP. + unfold sporadic_task_model in *. + move => delta /andP [HAS LT]. + rewrite -has_count in HAS. + + set some_interference_A := fun t => + has (fun tsk_k => backlogged job_cost sched j t && + (x tsk_k >= delta) && + scheduled_on_alpha_tsk t tsk_k) + (hp_tasks_in alpha'). + set total_interference_B := fun t => + backlogged job_cost sched j t * + count (fun tsk_k => (x tsk_k < delta) && + scheduled_on_alpha_tsk t tsk_k) (hp_tasks_in alpha'). + + apply leq_trans with ((\sum_(job_arrival j <= t < job_arrival j + R) + some_interference_A t) * (#|alpha' tsk| - num_tasks_exceeding delta)). + { + rewrite leq_mul2r; apply/orP; right. + move: HAS => /hasP HAS; destruct HAS as [tsk_a INa LEa]. + apply leq_trans with (n := x tsk_a); first by apply LEa. + unfold x, task_interference, some_interference_A. + apply leq_sum_nat; move => t /andP [GEt LTt] _. + destruct (backlogged job_cost sched j t) eqn:BACK; + last by rewrite (eq_bigr (fun x => 0)); [by simpl_sum_const | by ins]. + destruct ([exists cpu, can_execute_on alpha (job_task j) cpu && + task_scheduled_on job_task sched tsk_a cpu t]) eqn:SCHED; + last first. + { + apply negbT in SCHED; rewrite negb_exists in SCHED; move: SCHED => /forallP ALL. + rewrite (eq_bigr (fun x => 0)); first by simpl_sum_const. + by intros cpu _; specialize (ALL cpu); apply negbTE in ALL; rewrite ALL. + } + move: SCHED => /existsP [cpu /andP [CAN SCHED]]. + apply leq_trans with (n := 1); last first. + { + rewrite lt0b; apply/hasP; exists tsk_a; first by done. + rewrite LEa 2!andTb; apply/existsP; exists cpu. + by apply/andP; split; first by rewrite -JOBtsk. + } + rewrite (bigD1 cpu) /= // SCHED. + rewrite (eq_bigr (fun x => 0)); first by simpl_sum_const; rewrite leq_b1. + intros cpu' DIFF. + apply/eqP; rewrite eqb0; apply/negP. + move => /andP [ALPHA' SCHED']. + move: DIFF => /negP DIFF; apply DIFF; apply/eqP. + unfold task_scheduled_on in *. + destruct (sched cpu t) as [j1|] eqn:SCHED1; last by done. + destruct (sched cpu' t) as [j2|] eqn:SCHED2; last by done. + move: SCHED SCHED' => /eqP JOB /eqP JOB'. + subst tsk_a; symmetry in JOB'. + assert (BUG: j1 = j2). + { + assert (PENDING1: pending job_cost sched j1 t). + { + apply scheduled_implies_pending; try by done. + by apply/existsP; exists cpu; rewrite /scheduled_on -SCHED1. + } + assert (SCHED2': pending job_cost sched j2 t). + { + apply scheduled_implies_pending; try by done. + by apply/existsP; exists cpu'; rewrite /scheduled_on -SCHED2. + } + apply platform_fp_no_multiple_jobs_of_interfering_tasks with + (task_period0 := task_period) (tsk0 := tsk) (alpha0 := alpha) + (higher_eq_priority0 := higher_eq_priority) (t0 := t) + (job_cost0 := job_cost) (job_task0 := job_task) (sched0 := sched); + rewrite ?JOBtsk ?SAMEtsk //. + { + by intros j0 tsk0 TSK0 LE; subst tsk0; apply COMP. + } + { + apply/andP; split; last first. + { + apply/existsP; exists cpu; apply/andP; split; + first by rewrite -JOBtsk. + by apply APA with (t := t); apply/eqP. + } + apply/andP; split. + { + by rewrite -JOBtsk; apply FP with (cpu := cpu) (t := t); + [by done | by apply/eqP | by done]. + } + { + apply DIFFTASK with (t := t); [auto | by done |]. + by apply/existsP; exists cpu; apply/eqP. + } + } + } + by subst j2; apply SEQ with (j := j1) (t := t). + } + + apply leq_trans with (\sum_(job_arrival j <= t < job_arrival j + R) + total_interference_B t). + { + rewrite big_distrl /=. + apply leq_sum_nat; move => t LEt _. + unfold some_interference_A, total_interference_B. + destruct (backlogged job_cost sched j t) eqn:BACK; + [rewrite mul1n /= | by rewrite has_pred0 //]. + + destruct (has (fun tsk_k : sporadic_task => (delta <= x tsk_k) && + scheduled_on_alpha_tsk t tsk_k) (hp_tasks_in alpha')) eqn:HAS'; + last by done. + rewrite mul1n; move: HAS => /hasP [tsk_k INk LEk]. + unfold num_tasks_exceeding. + apply leq_trans with (n := #|alpha' tsk| - + count (fun i => (x i >= delta) && + scheduled_on_alpha_tsk t i) (hp_tasks_in alpha')). + { + apply leq_sub2l. + rewrite -2!sum1_count big_mkcond /=. + rewrite [\sum_(_ <- _ | _ <= _)_]big_mkcond /=. + apply leq_sum; intros i _. + by destruct (scheduled_on_alpha_tsk t i); + [by rewrite andbT | by rewrite andbF]. + } + rewrite -count_filter -[count _ (hp_tasks_in _)]count_filter. + eapply leq_trans with (n := count (predC (fun tsk => delta <= x tsk)) _); + last by apply eq_leq, eq_in_count; red; ins; rewrite ltnNge. + rewrite leq_subLR count_predC size_filter. + move: INV => INV. by apply (INV t). + } + { + unfold x at 2, total_interference_B. + rewrite exchange_big /=; apply leq_sum; intros t _. + destruct (backlogged job_cost sched j t) eqn:BACK; last by ins. + rewrite mul1n -sum1_count. + rewrite big_mkcond [\sum_(i <- hp_tasks_in alpha' | _ < _) _]big_mkcond /=. + apply leq_sum_seq; move => tsk_k IN _. + destruct (x tsk_k < delta); [rewrite andTb | by rewrite andFb]. + destruct (scheduled_on_alpha_tsk t tsk_k) eqn:SCHED; last by done. + move: SCHED => /existsP [cpu /andP [ALPHA SCHED]]. + rewrite (bigD1 cpu) /= // JOBtsk SCHED andbT. + by rewrite /can_execute_on ALPHA. + } + Qed. + + (* 7) Based on lemma (6), we prove that, for any interval delta, if the sum of per-task + interference exceeds (delta * |alpha' tsk|), the same applies for the + sum of the minimum of the interference and delta. *) + Lemma bertogna_fp_minimum_exceeds_interference : + forall delta, + \sum_(tsk_k <- hp_tasks_in alpha') x tsk_k >= delta * #|alpha' tsk| -> + \sum_(tsk_k <- hp_tasks_in alpha') minn (x tsk_k) delta >= + delta * #|alpha' tsk|. + Proof. + intros delta SUMLESS. + set more_interf := fun tsk_k => x tsk_k >= delta. + rewrite [\sum_(_ <- _) minn _ _](bigID more_interf) /=. + unfold more_interf, minn. + rewrite [\sum_(_ <- _ | delta <= _)_](eq_bigr (fun i => delta)); + last by intros i COND; rewrite leqNgt in COND; destruct (delta > x i). + rewrite [\sum_(_ <- _ | ~~_)_](eq_big (fun i => x i < delta) + (fun i => x i)); + [| by red; ins; rewrite ltnNge + | by intros i COND; rewrite -ltnNge in COND; rewrite COND]. + + (* Case 1: num_tasks_exceeding = 0 *) + destruct (~~ has (fun i => delta <= x i) (hp_tasks_in alpha')) eqn:HASa. + { + rewrite [\sum_(_ <- _ | _ <= _) _]big_hasC; last by apply HASa. + rewrite big_seq_cond; move: HASa => /hasPn HASa. + rewrite add0n (eq_bigl (fun i => (i \in hp_tasks_in alpha') && true)); + last by red; intros tsk_k; destruct (tsk_k \in hp_tasks_in alpha') eqn:INk; + [by rewrite andTb ltnNge; apply HASa | by rewrite andFb]. + by rewrite -big_seq_cond. + } apply negbFE in HASa. + + (* Case 2: num_tasks_exceeding >= |alpha' tsk| *) + destruct (num_tasks_exceeding delta >= #|alpha' tsk|) eqn:CARD. + { + apply leq_trans with (delta * num_tasks_exceeding delta); + first by rewrite leq_mul2l; apply/orP; right. + unfold num_tasks_exceeding; rewrite -sum1_count big_distrr /=. + rewrite -[\sum_(_ <- _ | _) _]addn0. + by apply leq_add; [by apply leq_sum; ins; rewrite muln1|by ins]. + } apply negbT in CARD; rewrite -ltnNge in CARD. + + (* Case 3: num_tasks_exceeding < |alpha' tsk| *) + rewrite big_const_seq iter_addn addn0; fold num_tasks_exceeding. + apply leq_trans with (n := delta * num_tasks_exceeding delta + + delta * (#|alpha' tsk| - num_tasks_exceeding delta)); + first by rewrite -mulnDr subnKC //; apply ltnW. + rewrite leq_add2l bertogna_fp_interference_in_non_full_processors //. + by apply/andP; split; [by rewrite -has_count | by done]. + Qed. + + (* 8) Note that lemma (7) only refers to interference within subaffinity (alpha' tsk). + To reason about the interference incurred by job j in its complete affinity, + we prove that an exceeding interference on affinity (alpha tsk) also + implies an exceeding interference on the subaffinity (alpha' tsk). *) + Lemma bertogna_fp_interference_on_subaffinity : + forall delta, + \sum_(tsk_k <- hp_tasks_in alpha) x tsk_k >= delta * #|alpha tsk| -> + \sum_(tsk_k <- hp_tasks_in alpha') x tsk_k >= delta * #|alpha' tsk|. + Proof. + have ALL := bertogna_fp_all_cpus_in_affinity_busy. + have SUB := bertogna_fp_all_cpus_in_subaffinity_busy. + rename H_at_least_one_cpu into NONEMPTY', + H_job_of_tsk into JOBtsk, H_affinity_subset into SUBSET, + H_all_jobs_from_taskset into FROMTS. + intros delta LE. + rewrite ALL in LE. + apply leq_trans with (n := X * #|alpha' tsk|); last by apply SUB. + rewrite leq_mul2r in LE; move: LE => /orP [/eqP EMPTY | LEx]; + last by rewrite leq_mul2r; apply/orP; right. + assert (NONEMPTY: #|alpha tsk| > 0). + { + apply leq_trans with (n := #|alpha' tsk|); + last by apply leq_subaffinity, SUBSET; rewrite -JOBtsk FROMTS. + by apply NONEMPTY'; rewrite -JOBtsk FROMTS. + } + by rewrite EMPTY ltnn in NONEMPTY. + Qed. + + (* 9) Next, using lemmas (0), (3), (7) and (8), we prove that the reduction-based + interference bound is not enough to cover the sum of the minima over all tasks + (artifact of the proof by contradiction). *) + Lemma bertogna_fp_sum_exceeds_total_interference: + \sum_((tsk_other, R_other) <- hp_bounds | hp_task_in (alpha' tsk) tsk_other) + minn (x tsk_other) (R - task_cost tsk + 1) > + total_interference_bound_fp task_cost task_period alpha tsk + (alpha' tsk) hp_bounds R higher_eq_priority. + Proof. + have EXCEEDS := bertogna_fp_minimum_exceeds_interference. + have SUB := bertogna_fp_interference_on_subaffinity. + have ALLBUSY := bertogna_fp_all_cpus_in_affinity_busy. + have TOOMUCH := bertogna_fp_too_much_interference. + + rename H_hp_bounds_has_interfering_tasks into HAS, + H_response_time_recurrence_holds into REC, + H_at_least_one_cpu into NONEMPTY, + H_affinity_subset into SUBSET. + unfold total_interference_bound_fp. + apply leq_trans with (n := \sum_(tsk_other <- hp_tasks_in alpha') minn (x tsk_other) (R - task_cost tsk + 1)); + last first. + { + rewrite (eq_bigr (fun i => minn (x (fst i)) (R - task_cost tsk + 1))); + last by ins; destruct i. + rewrite (eq_bigl (fun i => hp_task_in (alpha' tsk) (fst i))); + last by intro p; destruct p. + have MAP := @big_map _ 0 addn _ _ (fun x => fst x) hp_bounds _ (fun y => minn (x y) (R - task_cost tsk + 1)). + rewrite -MAP. + rewrite -[\sum_(_ <- [seq fst _ | _ <- _] | _)_]big_filter. + apply leq_sum_sub_uniq; first by apply filter_uniq; destruct ts. + red; move => tsk0 IN0. + rewrite mem_filter in IN0; move: IN0 => /andP [INTERF0 IN0]. + rewrite mem_filter; apply/andP; split; first by done. + apply/mapP. + exploit (HAS tsk0); [by done | | move => [R0 INbounds0]]. + { + move: INTERF0 => /andP [HP INTER]. + apply/andP; split; first by done. + move: INTER => /existsP [cpu /andP [IN1 IN2]]. + by apply/existsP; exists cpu; rewrite IN2 andbT; apply SUBSET. + } + by exists (tsk0, R0). + } + apply ltn_div_trunc with (d := #|alpha' tsk|); first by apply NONEMPTY. + rewrite -(ltn_add2l (task_cost tsk)) -REC. + rewrite -addn1 -leq_subLR. + rewrite -[R + 1 - _]subh1; last by rewrite REC; apply leq_addr. + rewrite leq_divRL; last by apply NONEMPTY. + apply EXCEEDS, SUB. + apply leq_trans with (n := X * #|alpha tsk|); last by rewrite ALLBUSY. + by rewrite leq_mul2r; apply/orP; right; apply TOOMUCH. + Qed. + + (* 10) After concluding that the sum of the minima exceeds (R - e_i + 1), + we prove that there exists a tuple (tsk_k, R_k) that satisfies + min (x_k, R - e_i + 1) > min (W_k, R - e_i + 1). + This implies that x_k > W_k, which is of course a contradiction, + since W_k is a valid task interference bound. *) + Lemma bertogna_fp_exists_task_that_exceeds_bound : + exists tsk_k R_k, + (tsk_k, R_k) \in hp_bounds /\ + (minn (x tsk_k) (R - task_cost tsk + 1) > + minn (workload_bound tsk_k R_k) (R - task_cost tsk + 1)). + Proof. + assert (HAS: has (fun tup : task_with_response_time => + let (tsk_k, R_k) := tup in + (minn (x tsk_k) (R - task_cost tsk + 1) > + minn (workload_bound tsk_k R_k)(R - task_cost tsk + 1))) + hp_bounds). + { + apply/negP; unfold not; intro NOTHAS. + move: NOTHAS => /negP /hasPn ALL. + have SUM := bertogna_fp_sum_exceeds_total_interference. + rewrite -[_ < _]negbK in SUM. + move: SUM => /negP SUM; apply SUM; rewrite -leqNgt. + unfold total_interference_bound_fp. + rewrite big_seq_cond. + rewrite [\sum_(i <- _| let '(tsk_other,_) := i in _)_]big_seq_cond. + apply leq_sum; move => [tsk_k R_k /andP [IN INTERk]]. + specialize (ALL (tsk_k, R_k)); simpl in ALL. + by rewrite leqNgt; apply ALL, IN. + } + move: HAS => /hasP HAS; destruct HAS as [[tsk_k R_k] HPk MIN]. + by exists tsk_k, R_k; repeat split. + Qed. + + End DerivingContradiction. + + End Lemmas. + + (* Using the lemmas above, we prove that R bounds the response time of tsk. *) + Theorem bertogna_cirinei_response_time_bound_fp : + response_time_bounded_by tsk R. + Proof. + have EX := bertogna_fp_exists_task_that_exceeds_bound. + have WORKLOAD := bertogna_fp_workload_bounds_interference. + rename H_response_time_bounds_ge_cost into GE_COST, + H_interfering_tasks_miss_no_deadlines into NOMISS, + H_response_time_recurrence_holds into REC, + H_response_time_of_interfering_tasks_is_known into RESP, + H_response_time_no_larger_than_deadline into LEdl. + intros j JOBtsk. + + (* First, rewrite the claim in terms of the *absolute* response-time bound (arrival + R) *) + remember (job_arrival j + R) 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 JOBtsk EQc; subst ctime. + + (* First, let's simplify the induction hypothesis. *) + assert (BEFOREok: forall (j0: JobIn arr_seq), + job_task j0 = tsk -> + job_arrival j0 < job_arrival j -> + service sched j0 (job_arrival j0 + R) == job_cost j0). + { + by ins; apply IH; try (by done); rewrite ltn_add2r. + } clear IH. + + unfold response_time_bounded_by, is_response_time_bound_of_task, + completed, completed_jobs_dont_execute, valid_sporadic_job in *. + + (* Now we start the proof. Assume by contradiction that job j + is not complete at time (job_arrival j + R). *) + destruct (completed job_cost sched j (job_arrival j + R)) eqn:NOTCOMP; + first by done. + apply negbT in NOTCOMP; exfalso. + + (* We derive a contradiction using the previous lemmas. *) + specialize (EX j JOBtsk NOTCOMP BEFOREok). + destruct EX as [tsk_k [R_k [HPk LTmin]]]. + unfold minn at 1 in LTmin. + specialize (WORKLOAD j tsk_k R_k HPk). + destruct (W task_cost task_period tsk_k R_k R < R - task_cost tsk + 1); rewrite leq_min in LTmin; + last by move: LTmin => /andP [_ BUG]; rewrite ltnn in BUG. + move: LTmin => /andP [BUG _]; des. + apply leq_trans with (p := W task_cost task_period tsk_k R_k R) in BUG; last by done. + by rewrite ltnn in BUG. + Qed. + + End ResponseTimeBound. + +End ResponseTimeAnalysisFP. \ No newline at end of file diff --git a/analysis/apa/interference_bound.v b/analysis/apa/interference_bound.v new file mode 100644 index 0000000000000000000000000000000000000000..8814c15fd5e5d9dca4d056c30f5488799e982c9c --- /dev/null +++ b/analysis/apa/interference_bound.v @@ -0,0 +1,45 @@ +Require Import rt.util.all. +Require Import rt.model.apa.schedule. +Require Import rt.analysis.apa.workload_bound. +From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop. + +Module InterferenceBoundGeneric. + + Section Definitions. + + Import Schedule WorkloadBound. + + Context {sporadic_task: eqType}. + Variable task_cost: sporadic_task -> time. + Variable task_period: sporadic_task -> time. + Variable task_deadline: sporadic_task -> time. + + Variable num_cpus: nat. + + (* Let tsk be the task to be analyzed. *) + Variable tsk: sporadic_task. + + Let task_with_response_time := (sporadic_task * time)%type. + + (* Assume a known response-time bound for each interfering task ... *) + Variable R_prev: seq task_with_response_time. + + (* ... and an interval length delta. *) + Variable delta: time. + + Section PerTask. + + Variable tsk_R: task_with_response_time. + Let tsk_other := fst tsk_R. + Let R_other := snd tsk_R. + + (* Based on the workload bound, Bertogna and Cirinei define the + following interference bound for a task. *) + Definition interference_bound_generic := + minn (W task_cost task_period tsk_other R_other delta) (delta - (task_cost tsk) + 1). + + End PerTask. + + End Definitions. + +End InterferenceBoundGeneric. \ No newline at end of file diff --git a/analysis/apa/interference_bound_edf.v b/analysis/apa/interference_bound_edf.v new file mode 100644 index 0000000000000000000000000000000000000000..ef241c8cca672c9b6c2af616e541b3ce1f87fe01 --- /dev/null +++ b/analysis/apa/interference_bound_edf.v @@ -0,0 +1,1189 @@ +Require Import rt.util.all rt.util.divround. +Require Import rt.model.apa.task rt.model.apa.job rt.model.apa.schedule + rt.model.apa.task_arrival rt.model.apa.platform rt.model.apa.response_time + rt.model.apa.workload rt.model.apa.priority rt.model.apa.schedulability + rt.model.apa.interference rt.model.apa.interference_edf + rt.model.apa.affinity. +Require Import rt.analysis.apa.workload_bound rt.analysis.apa.interference_bound. +From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop div path. + +(* In this file, we define the reduction-based interference bound for APA + scheduling with EDF, based on Bertogna and Cirinei's interference bound. *) +Module InterferenceBoundEDF. + + Import Job SporadicTaskset Schedule ScheduleOfSporadicTask Schedulability + WorkloadBound ResponseTime Priority Affinity + SporadicTaskArrival Interference InterferenceEDF. + Export InterferenceBoundGeneric. + + (* First we define Bertogna and Cirinei's EDF-specific interference bound. *) + Section SpecificBoundDef. + + Context {sporadic_task: eqType}. + Variable task_cost: sporadic_task -> time. + Variable task_period: sporadic_task -> time. + Variable task_deadline: sporadic_task -> time. + + (* Let tsk be the task to be analyzed. *) + Variable tsk: sporadic_task. + + (* Consider the interference incurred by tsk in a window of length delta... *) + Variable delta: time. + + (* ... due to a different task tsk_other, with response-time bound R_other. *) + Variable tsk_other: sporadic_task. + Variable R_other: time. + + (* Bertogna and Cirinei define the following bound for task interference + under EDF scheduling. *) + Definition edf_specific_interference_bound := + let d_tsk := task_deadline tsk in + let e_other := task_cost tsk_other in + let p_other := task_period tsk_other in + let d_other := task_deadline tsk_other in + (div_floor d_tsk p_other) * e_other + + minn e_other ((d_tsk %% p_other) - (d_other - R_other)). + + End SpecificBoundDef. + + (* Next, we define the total interference bound for EDF, which combines the generic + and the EDF-specific bounds. *) + Section TotalInterferenceBoundEDF. + + Context {sporadic_task: eqType}. + Variable task_cost: sporadic_task -> time. + Variable task_period: sporadic_task -> time. + Variable task_deadline: sporadic_task -> time. + + Context {num_cpus: nat}. + + (* Assume that each task has a processor affinity alpha. *) + Variable alpha: task_affinity sporadic_task num_cpus. + + Let task_with_response_time := (sporadic_task * time)%type. + + (* Let tsk be the task to be analyzed, ... *) + Variable tsk: sporadic_task. + + (* ... and let alpha' be any subaffinity of tsk. *) + Variable alpha': affinity num_cpus. + + (* Assume a known response-time bound for each interfering task ... *) + Variable R_prev: seq task_with_response_time. + + (* ... and an interval length delta. *) + Variable delta: time. + + (* First we recall the interference bounds. *) + Section RecallInterferenceBounds. + + Variable tsk_R: task_with_response_time. + Let tsk_other := fst tsk_R. + Let R_other := snd tsk_R. + + (* By combining Bertogna's interference bound for a work-conserving + scheduler ... *) + Let basic_interference_bound := interference_bound_generic task_cost task_period tsk delta tsk_R. + + (* ... with and EDF-specific interference bound, ... *) + Let edf_specific_bound := edf_specific_interference_bound task_cost task_period task_deadline tsk tsk_other R_other. + + (* ... Bertogna and Cirinei define the following interference bound + under EDF scheduling. *) + Definition interference_bound_edf := + minn basic_interference_bound edf_specific_bound. + + End RecallInterferenceBounds. + + (* Next we define the computation of the total interference for APA scheduling. *) + Section TotalInterference. + + (* Let (other_task_in alpha') denote the other tasks that can execute on alpha'. *) + Let other_task_in alpha' := different_task_in alpha tsk alpha'. + + (* The total interference incurred by tsk is bounded by the sum of individual + interferences of the other tasks that can be scheduled on alpha'. *) + Definition total_interference_bound_edf := + \sum_((tsk_other, R_other) <- R_prev | other_task_in alpha' tsk_other) + interference_bound_edf (tsk_other, R_other). + + End TotalInterference. + + End TotalInterferenceBoundEDF. + + (* In this section, we show that the EDF-specific interference bound is safe. *) + Section ProofSpecificBound. + + Import Schedule Interference Platform SporadicTaskset Affinity. + + Context {sporadic_task: eqType}. + Variable task_cost: sporadic_task -> time. + Variable task_period: sporadic_task -> time. + Variable task_deadline: sporadic_task -> time. + + Context {Job: eqType}. + Variable job_cost: Job -> time. + Variable job_deadline: Job -> time. + Variable job_task: Job -> sporadic_task. + + (* Assume any job arrival sequence... *) + Context {arr_seq: arrival_sequence Job}. + + (* ... in which jobs arrive sporadically and have valid parameters. *) + Hypothesis H_sporadic_tasks: + sporadic_task_model task_period arr_seq job_task. + Hypothesis H_valid_job_parameters: + forall (j: JobIn arr_seq), + valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j. + + (* Consider any schedule such that...*) + Variable num_cpus: nat. + Variable sched: schedule num_cpus arr_seq. + + (* ...jobs do not execute before their arrival times nor longer + than their execution costs. *) + Hypothesis H_jobs_must_arrive_to_execute: + jobs_must_arrive_to_execute sched. + Hypothesis H_completed_jobs_dont_execute: + completed_jobs_dont_execute job_cost sched. + + (* Also assume that jobs are sequential and that + there exists at least one processor. *) + Hypothesis H_sequential_jobs: sequential_jobs sched. + Hypothesis H_at_least_one_cpu: num_cpus > 0. + + (* Assume that every job at any time has a processor affinity alpha. *) + Variable alpha: task_affinity sporadic_task num_cpus. + + (* Consider a task set ts where all jobs come from the task set + and tasks have valid parameters and constrained deadlines. *) + Variable ts: taskset_of sporadic_task. + Hypothesis all_jobs_from_taskset: + forall (j: JobIn arr_seq), job_task j \in ts. + Hypothesis H_valid_task_parameters: + valid_sporadic_taskset task_cost task_period task_deadline ts. + Hypothesis H_constrained_deadlines: + forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk. + + Let no_deadline_is_missed_by_tsk (tsk: sporadic_task) := + task_misses_no_deadline job_cost job_deadline job_task sched tsk. + Let response_time_bounded_by (tsk: sporadic_task) := + is_response_time_bound_of_task job_cost job_task tsk sched. + + (* Assume that the scheduler is a work-conserving EDF scheduler. *) + Hypothesis H_work_conserving: apa_work_conserving job_cost job_task sched alpha. + Hypothesis H_edf_weak_APA_scheduler: + enforces_JLDP_policy_under_weak_APA job_cost job_task sched alpha (EDF job_deadline). + + (* Let tsk_i be the task to be analyzed, ...*) + Variable tsk_i: sporadic_task. + Hypothesis H_tsk_i_in_task_set: tsk_i \in ts. + + (* ... and j_i one of its jobs. *) + Variable j_i: JobIn arr_seq. + Hypothesis H_job_of_tsk_i: job_task j_i = tsk_i. + + (* Let tsk_k denote any interfering task, ... *) + Variable tsk_k: sporadic_task. + Hypothesis H_tsk_k_in_task_set: tsk_k \in ts. + + (* ...and R_k its response-time bound. *) + Variable R_k: time. + Hypothesis H_R_k_le_deadline: R_k <= task_deadline tsk_k. + + (* Consider a time window of length delta <= D_i, starting with j_i's arrival time. *) + Variable delta: time. + Hypothesis H_delta_le_deadline: delta <= task_deadline tsk_i. + + (* Assume that the jobs of tsk_k satisfy the response-time bound before the end of the interval *) + Hypothesis H_all_previous_jobs_completed_on_time : + forall (j_k: JobIn arr_seq), + job_task j_k = tsk_k -> + job_arrival j_k + R_k < job_arrival j_i + delta -> + completed job_cost sched j_k (job_arrival j_k + R_k). + + (* In this section, we prove that Bertogna and Cirinei's EDF interference bound + indeed bounds the interference caused by task tsk_k in the interval [t1, t1 + delta). *) + Section MainProof. + + (* Let's call x the task interference incurred by job j due to tsk_k. *) + Let x := + task_interference job_cost job_task sched alpha j_i + tsk_k (job_arrival j_i) (job_arrival j_i + delta). + + (* Also, recall the EDF-specific interference bound for EDF. *) + Let interference_bound := + edf_specific_interference_bound task_cost task_period task_deadline tsk_i tsk_k R_k. + + (* Let's simplify the names a bit. *) + Let t1 := job_arrival j_i. + Let t2 := job_arrival j_i + delta. + Let D_i := task_deadline tsk_i. + Let D_k := task_deadline tsk_k. + Let p_k := task_period tsk_k. + + Let n_k := div_floor D_i p_k. + + (* Let's give a simpler name to job interference. *) + Let interference_caused_by := job_interference job_cost job_task sched alpha j_i. + + (* Identify the subset of jobs that actually cause interference *) + Let interfering_jobs := + filter (fun (x: JobIn arr_seq) => + (job_task x == tsk_k) && (interference_caused_by x t1 t2 != 0)) + (jobs_scheduled_between sched t1 t2). + + (* Now, consider the list of interfering jobs sorted by arrival time. *) + Let earlier_arrival := fun (x y: JobIn arr_seq) => job_arrival x <= job_arrival y. + Let sorted_jobs := (sort earlier_arrival interfering_jobs). + + (* Now we proceed with the proof. The first step consists in simplifying the sum corresponding to the workload. *) + Section SimplifyJobSequence. + + (* Use the alternative definition of task interference, based on + individual job interference. *) + Lemma interference_bound_edf_use_another_definition : + x <= \sum_(j <- jobs_scheduled_between sched t1 t2 | job_task j == tsk_k) + interference_caused_by j t1 t2. + Proof. + by apply interference_le_interference_joblist. + Qed. + + (* Remove the elements that we don't care about from the sum *) + Lemma interference_bound_edf_simpl_by_filtering_interfering_jobs : + \sum_(j <- jobs_scheduled_between sched t1 t2 | job_task j == tsk_k) + interference_caused_by j t1 t2 = + \sum_(j <- interfering_jobs) interference_caused_by j t1 t2. + Proof. + unfold interfering_jobs; rewrite big_filter. + rewrite big_mkcond; rewrite [\sum_(_ <- _ | _) _]big_mkcond /=. + apply eq_bigr; intros i _; clear -i. + destruct (job_task i == tsk_k); rewrite ?andTb ?andFb; last by done. + destruct (interference_caused_by i t1 t2 != 0) eqn:DIFF; first by done. + by apply negbT in DIFF; rewrite negbK in DIFF; apply/eqP. + Qed. + + (* Then, we consider the sum over the sorted sequence of jobs. *) + Lemma interference_bound_edf_simpl_by_sorting_interfering_jobs : + \sum_(j <- interfering_jobs) interference_caused_by j t1 t2 = + \sum_(j <- sorted_jobs) interference_caused_by j t1 t2. + Proof. + by rewrite (eq_big_perm sorted_jobs) /=; last by rewrite -(perm_sort earlier_arrival). + Qed. + + (* Note that both sequences have the same set of elements. *) + Lemma interference_bound_edf_job_in_same_sequence : + forall j, + (j \in interfering_jobs) = (j \in sorted_jobs). + Proof. + by apply perm_eq_mem; rewrite -(perm_sort earlier_arrival). + Qed. + + (* Also recall that all jobs in the sorted sequence is an interfering job of tsk_k, ... *) + Lemma interference_bound_edf_all_jobs_from_tsk_k : + forall j, + j \in sorted_jobs -> + job_task j = tsk_k /\ + interference_caused_by j t1 t2 != 0 /\ + j \in jobs_scheduled_between sched t1 t2. + Proof. + intros j LT. + rewrite -interference_bound_edf_job_in_same_sequence mem_filter in LT. + by move: LT => /andP [/andP [/eqP JOBi SERVi] INi]; repeat split. + Qed. + + (* ...and consecutive jobs are ordered by arrival. *) + Lemma interference_bound_edf_jobs_ordered_by_arrival : + forall i elem, + i < (size sorted_jobs).-1 -> + earlier_arrival (nth elem sorted_jobs i) (nth elem sorted_jobs i.+1). + Proof. + intros i elem LT. + assert (SORT: sorted earlier_arrival sorted_jobs). + by apply sort_sorted; unfold total, earlier_arrival; ins; apply leq_total. + by destruct sorted_jobs; simpl in *; [by rewrite ltn0 in LT | by apply/pathP]. + Qed. + + (* Finally, for any job of task tsk_k, the interference is bounded by the task cost. *) + Lemma interference_bound_edf_interference_le_task_cost : + forall j, + j \in interfering_jobs -> + interference_caused_by j t1 t2 <= task_cost tsk_k. + Proof. + rename H_valid_job_parameters into PARAMS. + intros j; rewrite mem_filter; move => /andP [/andP [/eqP JOBj _] _]. + specialize (PARAMS j); des. + apply leq_trans with (n := service_during sched j t1 t2); + first by apply job_interference_le_service. + by apply cumulative_service_le_task_cost with (job_task0 := job_task) + (task_deadline0 := task_deadline) (job_cost0 := job_cost) + (job_deadline0 := job_deadline). + Qed. + + End SimplifyJobSequence. + + (* Next, we show that if the number of jobs is no larger than n_k, + the workload bound trivially holds. *) + Section InterferenceFewJobs. + + Hypothesis H_few_jobs: size sorted_jobs <= n_k. + + Lemma interference_bound_edf_holds_for_at_most_n_k_jobs : + \sum_(j <- sorted_jobs) interference_caused_by j t1 t2 <= + interference_bound. + Proof. + rewrite -[\sum_(_ <- _ | _) _]addn0 leq_add //. + apply leq_trans with (n := \sum_(x <- sorted_jobs) task_cost tsk_k); + last by rewrite big_const_seq iter_addn addn0 mulnC leq_mul2r; apply/orP; right. + { + rewrite [\sum_(_ <- _) interference_caused_by _ _ _]big_seq_cond. + rewrite [\sum_(_ <- _) task_cost _]big_seq_cond. + apply leq_sum; intros i; move/andP => [INi _]. + rewrite -interference_bound_edf_job_in_same_sequence in INi. + by apply interference_bound_edf_interference_le_task_cost. + } + Qed. + + End InterferenceFewJobs. + + (* Otherwise, assume that the number of jobs is larger than n_k >= 0. *) + Section InterferenceManyJobs. + + Hypothesis H_many_jobs: n_k < size sorted_jobs. + + (* This trivially implies that there's at least one job. *) + Lemma interference_bound_edf_at_least_one_job: size sorted_jobs > 0. + Proof. + by apply leq_ltn_trans with (n := n_k). + Qed. + + (* Let j_fst be the first job, and a_fst its arrival time. *) + Variable elem: JobIn arr_seq. + Let j_fst := nth elem sorted_jobs 0. + Let a_fst := job_arrival j_fst. + + (* In this section, we prove some basic lemmas about j_fst. *) + Section FactsAboutFirstJob. + + (* The first job is an interfering job of task tsk_k. *) + Lemma interference_bound_edf_j_fst_is_job_of_tsk_k : + job_task j_fst = tsk_k /\ + interference_caused_by j_fst t1 t2 != 0 /\ + j_fst \in jobs_scheduled_between sched t1 t2. + Proof. + by apply interference_bound_edf_all_jobs_from_tsk_k, mem_nth, + interference_bound_edf_at_least_one_job. + Qed. + + (* The deadline of j_fst is the deadline of tsk_k. *) + Lemma interference_bound_edf_j_fst_deadline : + job_deadline j_fst = task_deadline tsk_k. + Proof. + unfold valid_sporadic_job in *. + rename H_valid_job_parameters into PARAMS. + have FST := interference_bound_edf_j_fst_is_job_of_tsk_k. + destruct FST as [FSTtask _]. + by specialize (PARAMS j_fst); des; rewrite PARAMS1 FSTtask. + Qed. + + (* The deadline of j_i is the deadline of tsk_i. *) + Lemma interference_bound_edf_j_i_deadline : + job_deadline j_i = task_deadline tsk_i. + Proof. + unfold valid_sporadic_job in *. + rename H_valid_job_parameters into PARAMS, + H_job_of_tsk_i into JOBtsk. + by specialize (PARAMS j_i); des; rewrite PARAMS1 JOBtsk. + Qed. + + (* If j_fst completes by its response-time bound, then t1 <= a_fst + R_k, + where t1 is the beginning of the time window (arrival of j_i). *) + Lemma interference_bound_edf_j_fst_completion_implies_rt_bound_inside_interval : + completed job_cost sched j_fst (a_fst + R_k) -> + t1 <= a_fst + R_k. + Proof. + intros RBOUND. + rewrite leqNgt; apply/negP; unfold not; intro BUG. + have FST := interference_bound_edf_j_fst_is_job_of_tsk_k. + destruct FST as [_ [ FSTserv _]]. + move: FSTserv => /negP FSTserv; apply FSTserv. + rewrite -leqn0; apply leq_trans with (n := service_during sched j_fst t1 t2); + first by apply job_interference_le_service. + rewrite leqn0; apply/eqP. + by apply cumulative_service_after_job_rt_zero with (job_cost0 := job_cost) (R := R_k); + try (by done); apply ltnW. + Qed. + + End FactsAboutFirstJob. + + (* Now, let's prove the interference bound for the particular case of a single job. + This case must be solved separately because the single job can simultaneously + be carry-in and carry-out job, so its response time is not necessarily + bounded by R_k (from the hypothesis H_all_previous_jobs_completed_on_time). *) + Section InterferenceSingleJob. + + (* Assume that there's at least one job in the sorted list. *) + Hypothesis H_only_one_job: size sorted_jobs = 1. + + (* Since there's only one job, we simplify the terms in the interference bound. *) + Lemma interference_bound_edf_simpl_when_there's_one_job : + D_i %% p_k - (D_k - R_k) = D_i - (D_k - R_k). + Proof. + rename H_many_jobs into NUM, + H_valid_task_parameters into TASK_PARAMS, + H_tsk_k_in_task_set into INk. + unfold valid_sporadic_taskset, is_valid_sporadic_task, + interference_bound, edf_specific_interference_bound in *. + rewrite H_only_one_job in NUM. + rewrite ltnS leqn0 in NUM; move: NUM => /eqP EQnk. + move: EQnk => /eqP EQnk; unfold n_k, div_floor in EQnk. + rewrite -leqn0 leqNgt divn_gt0 in EQnk; + last by specialize (TASK_PARAMS tsk_k INk); des. + by rewrite -ltnNge in EQnk; rewrite modn_small //. + Qed. + + (* Next, we show that if j_fst completes by its response-time bound R_k, + then then interference bound holds. *) + Section ResponseTimeOfSingleJobBounded. + + Hypothesis H_j_fst_completed_by_rt_bound : + completed job_cost sched j_fst (a_fst + R_k). + + Lemma interference_bound_edf_holds_for_single_job_that_completes_on_time : + job_interference job_cost job_task sched alpha j_i j_fst t1 t2 <= D_i - (D_k - R_k). + Proof. + rename H_j_fst_completed_by_rt_bound into RBOUND. + have AFTERt1 := + interference_bound_edf_j_fst_completion_implies_rt_bound_inside_interval RBOUND. + have FST := interference_bound_edf_j_fst_is_job_of_tsk_k. + destruct FST as [_ [ LEdl _]]. + apply interference_under_edf_implies_shorter_deadlines with + (job_deadline0 := job_deadline) in LEdl; try (by done). + destruct (D_k - R_k <= D_i) eqn:LEdk; last first. + { + apply negbT in LEdk; rewrite -ltnNge in LEdk. + apply leq_trans with (n := 0); last by done. + apply leq_trans with (n := job_interference job_cost job_task sched alpha j_i j_fst + (a_fst + R_k) t2). + { + apply extend_sum; last by apply leqnn. + rewrite -(leq_add2r D_i). + rewrite interference_bound_edf_j_fst_deadline + interference_bound_edf_j_i_deadline in LEdl. + apply leq_trans with (n := a_fst + D_k); last by done. + rewrite -addnA leq_add2l. + by apply ltnW; rewrite -ltn_subRL. + } + apply leq_trans with (n := service_during sched j_fst (a_fst + R_k) t2); + first by apply job_interference_le_service. + unfold service_during; rewrite leqn0; apply/eqP. + by apply cumulative_service_after_job_rt_zero with (job_cost0 := job_cost) (R := R_k); + try (by done); apply leqnn. + } + { + rewrite -(leq_add2r (D_k - R_k)) subh1 // -addnBA // subnn addn0. + assert (SUBST: D_k - R_k = \sum_(a_fst + R_k <= i < a_fst + D_k) 1). + { + rewrite big_const_nat iter_addn mul1n addn0. + rewrite addnC -subnBA; last by apply leq_addr. + by rewrite addnC -addnBA // subnn addn0. + } + apply leq_trans with (n := job_interference job_cost job_task sched alpha j_i j_fst t1 + (a_fst + D_k) + (D_k - R_k)). + { + rewrite leq_add2r. + destruct (t2 <= a_fst + R_k) eqn:LEt2. + { + apply extend_sum; first by apply leqnn. + apply leq_trans with (n := a_fst + R_k); first by done. + by rewrite leq_add2l; apply H_R_k_le_deadline. + } + { + unfold job_interference. + apply negbT in LEt2; rewrite -ltnNge in LEt2. + rewrite -> big_cat_nat with (n := a_fst + R_k); + [simpl | by apply AFTERt1 | by apply ltnW]. + apply leq_trans with (n := job_interference job_cost job_task sched alpha j_i j_fst t1 + (a_fst + R_k) + service_during sched j_fst (a_fst + R_k) t2). + { + rewrite leq_add2l. + by apply job_interference_le_service. + } + unfold service_during. + rewrite (cumulative_service_after_job_rt_zero job_cost _ _ _ R_k) //. + rewrite addn0; apply extend_sum; first by apply leqnn. + by rewrite leq_add2l; apply H_R_k_le_deadline. + } + } + + unfold job_interference. + rewrite -> big_cat_nat with (n := a_fst + R_k); + [simpl| by apply AFTERt1 | by rewrite leq_add2l; apply H_R_k_le_deadline]. + apply leq_trans with (n := job_interference job_cost job_task sched alpha j_i j_fst t1 + (a_fst+R_k) + service_during sched j_fst (a_fst+R_k) (a_fst+D_k) + (D_k-R_k)); + first by rewrite leq_add2r leq_add2l job_interference_le_service. + unfold service_during. + rewrite (cumulative_service_after_job_rt_zero job_cost _ _ _ R_k) // addn0. + apply leq_trans with (n := (\sum_(t1 <= t < a_fst + R_k) 1) + + \sum_(a_fst + R_k <= t < a_fst + D_k) 1). + { + apply leq_add; last by rewrite SUBST. + rewrite big_const_nat iter_addn mul1n addn0. + rewrite -{1}[a_fst + R_k](addKn t1) -addnBA //. + by apply job_interference_le_delta. + } + rewrite -big_cat_nat; + [simpl | by apply AFTERt1 | by rewrite leq_add2l; apply H_R_k_le_deadline ]. + rewrite big_const_nat iter_addn mul1n addn0 leq_subLR. + by unfold D_i, D_k, t1, a_fst; rewrite -interference_bound_edf_j_fst_deadline + -interference_bound_edf_j_i_deadline. + } + Qed. + + End ResponseTimeOfSingleJobBounded. + + (* Else, if j_fst did not complete by its response-time bound, then + we need a separate proof. *) + Section ResponseTimeOfSingleJobNotBounded. + + Hypothesis H_j_fst_not_complete_by_rt_bound : + ~~ completed job_cost sched j_fst (a_fst + R_k). + + (* This trivially implies that a_fst + R_k lies after the end of the interval, + otherwise j_fst would have completed by its response-time bound. *) + Lemma interference_bound_edf_response_time_bound_of_j_fst_after_interval : + job_arrival j_fst + R_k >= job_arrival j_i + delta. + Proof. + have FST := interference_bound_edf_j_fst_is_job_of_tsk_k. + destruct FST as [FSTtask _]. + rewrite leqNgt; apply/negP; intro LT. + move: H_j_fst_not_complete_by_rt_bound => /negP BUG; apply BUG. + by apply H_all_previous_jobs_completed_on_time. + Qed. + + (* If the slack is too big (D_i < D_k - R_k), j_fst causes no interference. *) + Lemma interference_bound_edf_holds_for_single_job_with_big_slack : + D_i < D_k - R_k -> + interference_caused_by j_fst t1 t2 = 0. + Proof. + intro LTdk. + rewrite ltn_subRL in LTdk. + rewrite -(ltn_add2l a_fst) addnA in LTdk. + apply leq_ltn_trans with (m := t1 + D_i) in LTdk; last first. + { + rewrite leq_add2r. + apply leq_trans with (n := t1 + delta); first by apply leq_addr. + by apply interference_bound_edf_response_time_bound_of_j_fst_after_interval. + } + apply/eqP; rewrite -[_ _ _ _ == 0]negbK; apply/negP; red; intro BUG. + apply interference_under_edf_implies_shorter_deadlines with + (job_deadline0 := job_deadline) in BUG; try (by done). + rewrite interference_bound_edf_j_fst_deadline + interference_bound_edf_j_i_deadline in BUG. + by apply (leq_trans LTdk) in BUG; rewrite ltnn in BUG. + Qed. + + (* Else, if the slack is small, j_fst causes interference for no longer than + D_i - (D_k - R_k). *) + Lemma interference_bound_edf_holds_for_single_job_with_small_slack : + D_i >= D_k - R_k -> + interference_caused_by j_fst t1 t2 <= D_i - (D_k - R_k). + Proof. + intro LEdk. + have FST := interference_bound_edf_j_fst_is_job_of_tsk_k. + destruct FST as [FSTtask [LEdl _]]. + have LTr := interference_bound_edf_response_time_bound_of_j_fst_after_interval. + apply subh3; last by apply LEdk. + apply leq_trans with (n := job_interference job_cost job_task sched alpha j_i j_fst t1 + (job_arrival j_fst + R_k) + (D_k - R_k)); + first by rewrite leq_add2r; apply extend_sum; [by apply leqnn|]. + apply leq_trans with (n := \sum_(t1 <= t < a_fst + R_k) 1 + + \sum_(a_fst + R_k <= t < a_fst + D_k)1). + { + apply leq_add. + { + rewrite big_const_nat iter_addn mul1n addn0. + rewrite -{1}[job_arrival j_fst + R_k](addKn t1) -addnBA; + first by apply job_interference_le_delta. + by apply leq_trans with (n := t1 + delta); first by apply leq_addr. + } + rewrite big_const_nat iter_addn mul1n addn0 addnC. + rewrite -subnBA; last by apply leq_addr. + by rewrite addnC -addnBA // subnn addn0. + } + rewrite -big_cat_nat; simpl; last 2 first. + { + apply leq_trans with (n := t1 + delta); first by apply leq_addr. + by apply interference_bound_edf_response_time_bound_of_j_fst_after_interval. + } + by rewrite leq_add2l; apply H_R_k_le_deadline. + rewrite big_const_nat iter_addn mul1n addn0 leq_subLR. + unfold D_i, D_k, t1, a_fst; rewrite -interference_bound_edf_j_fst_deadline + -interference_bound_edf_j_i_deadline. + by apply interference_under_edf_implies_shorter_deadlines with + (job_deadline0 := job_deadline) in LEdl. + Qed. + + End ResponseTimeOfSingleJobNotBounded. + + (* By combining the results above, we prove that the interference caused by the single job + is bounded by D_i - (D_k - R_k), ... *) + Lemma interference_bound_edf_interference_of_j_fst_limited_by_slack : + interference_caused_by j_fst t1 t2 <= D_i - (D_k - R_k). + Proof. + destruct (completed job_cost sched j_fst (a_fst + R_k)) eqn:COMP; + first by apply interference_bound_edf_holds_for_single_job_that_completes_on_time. + apply negbT in COMP. + destruct (ltnP D_i (D_k - R_k)) as [LEdk | LTdk]. + by rewrite interference_bound_edf_holds_for_single_job_with_big_slack. + by apply interference_bound_edf_holds_for_single_job_with_small_slack. + Qed. + + (* ... and thus the interference bound holds. *) + Lemma interference_bound_edf_holds_for_a_single_job : + interference_caused_by j_fst t1 t2 <= interference_bound. + Proof. + have ONE := interference_bound_edf_simpl_when_there's_one_job. + have SLACK := interference_bound_edf_interference_of_j_fst_limited_by_slack. + rename H_many_jobs into NUM, H_only_one_job into SIZE. + unfold interference_caused_by, interference_bound, edf_specific_interference_bound. + fold D_i D_k p_k n_k. + rewrite SIZE ltnS leqn0 in NUM; move: NUM => /eqP EQnk. + rewrite EQnk mul0n add0n. + rewrite leq_min; apply/andP; split. + { + apply interference_bound_edf_interference_le_task_cost. + rewrite interference_bound_edf_job_in_same_sequence. + by apply mem_nth; rewrite SIZE. + } + by rewrite ONE; apply SLACK. + Qed. + + End InterferenceSingleJob. + + (* Next, consider the other case where there are at least two jobs: + the first job j_fst, and the last job j_lst. *) + Section InterferenceTwoOrMoreJobs. + + (* Assume there are at least two jobs. *) + Variable num_mid_jobs: nat. + Hypothesis H_at_least_two_jobs : size sorted_jobs = num_mid_jobs.+2. + + (* Let j_lst be the last job of the sequence and a_lst its arrival time. *) + Let j_lst := nth elem sorted_jobs num_mid_jobs.+1. + Let a_lst := job_arrival j_lst. + + (* In this section, we prove some basic lemmas about the first and last jobs. *) + Section FactsAboutFirstAndLastJobs. + + (* The last job is an interfering job of task tsk_k. *) + Lemma interference_bound_edf_j_lst_is_job_of_tsk_k : + job_task j_lst = tsk_k /\ + interference_caused_by j_lst t1 t2 != 0 /\ + j_lst \in jobs_scheduled_between sched t1 t2. + Proof. + apply interference_bound_edf_all_jobs_from_tsk_k, mem_nth. + by rewrite H_at_least_two_jobs. + Qed. + + (* The deadline of j_lst is the deadline of tsk_k. *) + Lemma interference_bound_edf_j_lst_deadline : + job_deadline j_lst = task_deadline tsk_k. + Proof. + unfold valid_sporadic_job in *. + rename H_valid_job_parameters into PARAMS. + have LST := interference_bound_edf_j_lst_is_job_of_tsk_k. + destruct LST as [LSTtask _]. + by specialize (PARAMS j_lst); des; rewrite PARAMS1 LSTtask. + Qed. + + (* The first job arrives before the last job. *) + Lemma interference_bound_edf_j_fst_before_j_lst : + job_arrival j_fst <= job_arrival j_lst. + Proof. + rename H_at_least_two_jobs into SIZE. + unfold j_fst, j_lst; rewrite -[num_mid_jobs.+1]add0n. + apply prev_le_next; last by rewrite SIZE leqnn. + by intros i LT; apply interference_bound_edf_jobs_ordered_by_arrival. + Qed. + + (* The last job arrives before the end of the interval. *) + Lemma interference_bound_edf_last_job_arrives_before_end_of_interval : + job_arrival j_lst < t2. + Proof. + rewrite leqNgt; apply/negP; unfold not; intro LT2. + exploit interference_bound_edf_all_jobs_from_tsk_k. + { + apply mem_nth; instantiate (1 := num_mid_jobs.+1). + by rewrite -(ltn_add2r 1) addn1 H_at_least_two_jobs addn1. + } + instantiate (1 := elem); move => [LSTtsk [/eqP LSTserv LSTin]]. + apply LSTserv; apply/eqP; rewrite -leqn0. + apply leq_trans with (n := service_during sched j_lst t1 t2); + first by apply job_interference_le_service. + rewrite leqn0; apply/eqP; unfold service_during. + by apply cumulative_service_before_job_arrival_zero. + Qed. + + (* Since there are multiple jobs, j_fst is far enough from the end of + the interval that its response-time bound is valid + (by the assumption H_all_previous_jobs_completed_on_time). *) + Lemma interference_bound_edf_j_fst_completed_on_time : + completed job_cost sched j_fst (a_fst + R_k). + Proof. + have FST := interference_bound_edf_j_fst_is_job_of_tsk_k; des. + set j_snd := nth elem sorted_jobs 1. + exploit interference_bound_edf_all_jobs_from_tsk_k. + { + by apply mem_nth; instantiate (1 := 1); rewrite H_at_least_two_jobs. + } + instantiate (1 := elem); move => [SNDtsk [/eqP SNDserv _]]. + apply H_all_previous_jobs_completed_on_time; try (by done). + apply leq_ltn_trans with (n := job_arrival j_snd); last first. + { + rewrite ltnNge; apply/negP; red; intro BUG; apply SNDserv. + apply/eqP; rewrite -leqn0; apply leq_trans with (n := service_during + sched j_snd t1 t2); + first by apply job_interference_le_service. + rewrite leqn0; apply/eqP. + by apply cumulative_service_before_job_arrival_zero. + } + apply leq_trans with (n := a_fst + p_k). + { + by rewrite leq_add2l; apply leq_trans with (n := D_k); + [by apply H_R_k_le_deadline | by apply H_constrained_deadlines]. + } + + (* Since jobs are sporadic, we know that the first job arrives + at least p_k units before the second. *) + unfold p_k; rewrite -FST. + apply H_sporadic_tasks; [| by rewrite SNDtsk | ]; last first. + { + apply interference_bound_edf_jobs_ordered_by_arrival. + by rewrite H_at_least_two_jobs. + } + red; move => /eqP BUG. + by rewrite nth_uniq in BUG; rewrite ?SIZE //; + [ by apply interference_bound_edf_at_least_one_job + | by rewrite H_at_least_two_jobs + | by rewrite sort_uniq; apply filter_uniq, undup_uniq]. + Qed. + + End FactsAboutFirstAndLastJobs. + + (* Next, we prove that the distance between the first and last jobs is at least + num_mid_jobs + 1 periods. *) + Lemma interference_bound_edf_many_periods_in_between : + a_lst - a_fst >= num_mid_jobs.+1 * p_k. + Proof. + unfold a_fst, a_lst, j_fst, j_lst. + assert (EQnk: num_mid_jobs.+1=(size sorted_jobs).-1). + by rewrite H_at_least_two_jobs. + rewrite EQnk telescoping_sum; + last by ins; apply interference_bound_edf_jobs_ordered_by_arrival. + rewrite -[_ * _ tsk_k]addn0 mulnC -iter_addn -{1}[_.-1]subn0 -big_const_nat. + rewrite big_nat_cond [\sum_(0 <= i < _)(_-_)]big_nat_cond. + apply leq_sum; intros i; rewrite andbT; move => /andP LT; des. + + (* To simplify, call the jobs 'cur' and 'next' *) + set cur := nth elem sorted_jobs i. + set next := nth elem sorted_jobs i.+1. + + (* Show that cur arrives earlier than next *) + assert (ARRle: job_arrival cur <= job_arrival next). + by unfold cur, next; apply interference_bound_edf_jobs_ordered_by_arrival. + + (* Show that both cur and next are in the arrival sequence *) + assert (INnth: cur \in interfering_jobs /\ next \in interfering_jobs). + { + rewrite 2!interference_bound_edf_job_in_same_sequence; split. + by apply mem_nth, (ltn_trans LT0); destruct sorted_jobs; ins. + by apply mem_nth; destruct sorted_jobs; ins. + } + rewrite 2?mem_filter in INnth; des. + + (* Use the sporadic task model to conclude that cur and next are separated + by at least (task_period tsk) units. Of course this only holds if cur != next. + Since we don't know much about the list (except that it's sorted), we must + also prove that it doesn't contain duplicates. *) + assert (CUR_LE_NEXT: job_arrival cur + task_period (job_task cur) <= job_arrival next). + { + apply H_sporadic_tasks; last by ins. + unfold cur, next, not; intro EQ; move: EQ => /eqP EQ. + rewrite nth_uniq in EQ; first by move: EQ => /eqP EQ; intuition. + by apply ltn_trans with (n := (size sorted_jobs).-1); destruct sorted_jobs; ins. + by destruct sorted_jobs; ins. + by rewrite sort_uniq -/interfering_jobs filter_uniq // undup_uniq. + by rewrite INnth INnth0. + } + by rewrite subh3 // addnC /p_k -INnth. + Qed. + + (* Using the lemma above, we prove that the ratio n_k is at least the number of + middle jobs + 1, ... *) + Lemma interference_bound_edf_n_k_covers_middle_jobs_plus_one : + n_k >= num_mid_jobs.+1. + Proof. + have DIST := interference_bound_edf_many_periods_in_between. + have AFTERt1 := + interference_bound_edf_j_fst_completion_implies_rt_bound_inside_interval + interference_bound_edf_j_fst_completed_on_time. + rename H_valid_task_parameters into TASK_PARAMS, + H_tsk_k_in_task_set into INk. + unfold valid_sporadic_taskset, is_valid_sporadic_task, + interference_bound, edf_specific_interference_bound in *. + rewrite leqNgt; apply/negP; unfold not; intro LTnk; unfold n_k in LTnk. + rewrite ltn_divLR in LTnk; last by specialize (TASK_PARAMS tsk_k INk); des. + apply (leq_trans LTnk) in DIST; rewrite ltn_subRL in DIST. + rewrite -(ltn_add2r D_k) -addnA [D_i + _]addnC addnA in DIST. + apply leq_ltn_trans with (m := job_arrival j_i + D_i) in DIST; last first. + { + rewrite leq_add2r; apply (leq_trans AFTERt1). + by rewrite leq_add2l; apply H_R_k_le_deadline. + } + have LST := interference_bound_edf_j_lst_is_job_of_tsk_k. + destruct LST as [_ [ LEdl _]]. + apply interference_under_edf_implies_shorter_deadlines with + (job_deadline0 := job_deadline) in LEdl; try (by done). + unfold D_i, D_k in DIST; rewrite interference_bound_edf_j_lst_deadline + interference_bound_edf_j_i_deadline in LEdl. + by rewrite ltnNge LEdl in DIST. + Qed. + + (* ... which allows bounding the interference of the middle and last jobs + using n_k multiplied by the cost. *) + Lemma interference_bound_edf_holds_for_middle_and_last_jobs : + interference_caused_by j_lst t1 t2 + + \sum_(0 <= i < num_mid_jobs) + interference_caused_by (nth elem sorted_jobs i.+1) t1 t2 + <= n_k * task_cost tsk_k. + Proof. + apply leq_trans with (n := num_mid_jobs.+1 * task_cost tsk_k); last first. + { + rewrite leq_mul2r; apply/orP; right. + by apply interference_bound_edf_n_k_covers_middle_jobs_plus_one. + } + rewrite mulSn; apply leq_add. + { + apply interference_bound_edf_interference_le_task_cost. + rewrite interference_bound_edf_job_in_same_sequence. + by apply mem_nth; rewrite H_at_least_two_jobs. + } + { + apply leq_trans with (n := \sum_(0 <= i < num_mid_jobs) task_cost tsk_k); + last by rewrite big_const_nat iter_addn addn0 mulnC subn0. + rewrite big_nat_cond [\sum_(0 <= i < num_mid_jobs) task_cost _]big_nat_cond. + apply leq_sum; intros i; rewrite andbT; move => /andP LT; des. + apply interference_bound_edf_interference_le_task_cost. + rewrite interference_bound_edf_job_in_same_sequence. + apply mem_nth; rewrite H_at_least_two_jobs. + by rewrite ltnS; apply leq_trans with (n := num_mid_jobs). + } + Qed. + + (* Now, since n_k < sorted_jobs = num_mid_jobs + 2, it follows that + n_k = num_mid_jobs + 1. *) + Lemma interference_bound_edf_n_k_equals_num_mid_jobs_plus_one : + n_k = num_mid_jobs.+1. + Proof. + have NK := interference_bound_edf_n_k_covers_middle_jobs_plus_one. + rename H_many_jobs into NUM, H_at_least_two_jobs into SIZE. + by move: NK; rewrite leq_eqVlt orbC; move => /orP NK; des; + [by rewrite SIZE ltnS leqNgt NK in NUM | by rewrite NK]. + Qed. + + (* After proving the bounds of the middle and last jobs, we do the same for + the first job. This requires a different proof in order to exploit the slack. *) + Section InterferenceOfFirstJob. + + (* As required by the next lemma, in order to move (D_i %% p_k) to the left of + the inequality (<=), we must show that it is no smaller than the slack. *) + Lemma interference_bound_edf_remainder_ge_slack : + D_k - R_k <= D_i %% p_k. + Proof. + have AFTERt1 := + interference_bound_edf_j_fst_completion_implies_rt_bound_inside_interval + interference_bound_edf_j_fst_completed_on_time. + have NK := interference_bound_edf_n_k_equals_num_mid_jobs_plus_one. + have DIST := interference_bound_edf_many_periods_in_between. + rewrite -NK in DIST. + rewrite -subndiv_eq_mod leq_subLR. + fold (div_floor D_i p_k) n_k. + rewrite addnBA; last by apply leq_trunc_div. + apply leq_trans with (n := R_k + D_i - (a_lst - a_fst)); last by apply leq_sub2l. + rewrite subnBA; last by apply interference_bound_edf_j_fst_before_j_lst. + rewrite -(leq_add2r a_lst) subh1; last first. + { + apply leq_trans with (n := t2); + [by apply ltnW, interference_bound_edf_last_job_arrives_before_end_of_interval|]. + rewrite addnC addnA. + apply leq_trans with (n := t1 + D_i). + unfold t2; rewrite leq_add2l; apply H_delta_le_deadline. + by rewrite leq_add2r; apply AFTERt1. + } + rewrite -addnBA // subnn addn0 [D_k + _]addnC. + apply leq_trans with (n := t1 + D_i); + last by rewrite -addnA [D_i + _]addnC addnA leq_add2r addnC AFTERt1. + have LST := interference_bound_edf_j_lst_is_job_of_tsk_k. + destruct LST as [_ [ LSTserv _]]. + unfold D_i, D_k, a_lst, t1; rewrite -interference_bound_edf_j_lst_deadline + -interference_bound_edf_j_i_deadline. + by apply interference_under_edf_implies_shorter_deadlines with + (job_deadline0 := job_deadline) in LSTserv. + Qed. + + (* To conclude that the interference bound holds, it suffices to show that + this reordered inequality holds. *) + Lemma interference_bound_edf_simpl_by_moving_to_left_side : + interference_caused_by j_fst t1 t2 + (D_k - R_k) + D_i %/ p_k * p_k <= D_i -> + interference_caused_by j_fst t1 t2 <= D_i %% p_k - (D_k - R_k). + Proof. + intro LE. + apply subh3; last by apply interference_bound_edf_remainder_ge_slack. + by rewrite -subndiv_eq_mod; apply subh3; last by apply leq_trunc_div. + Qed. + + (* Next, we prove that interference caused by j_fst is bounded by the length + of the interval [t1, a_fst + R_k), ... *) + Lemma interference_bound_edf_interference_of_j_fst_bounded_by_response_time : + interference_caused_by j_fst t1 t2 <= \sum_(t1 <= t < a_fst + R_k) 1. + Proof. + assert (AFTERt1: t1 <= a_fst + R_k). + { + apply interference_bound_edf_j_fst_completion_implies_rt_bound_inside_interval. + by apply interference_bound_edf_j_fst_completed_on_time. + } + destruct (leqP t2 (a_fst + R_k)) as [LEt2 | GTt2]. + { + apply leq_trans with (n := job_interference job_cost job_task sched alpha j_i j_fst t1 + (a_fst + R_k)); + first by apply extend_sum; rewrite ?leqnn. + simpl_sum_const; rewrite -{1}[_ + R_k](addKn t1) -addnBA //. + by apply job_interference_le_delta. + } + { + unfold interference_caused_by, job_interference. + rewrite -> big_cat_nat with (n := a_fst + R_k); + [simpl | by apply AFTERt1 | by apply ltnW]. + rewrite -[\sum_(_ <= _ < _) 1]addn0; apply leq_add. + { + simpl_sum_const; rewrite -{1}[_ + R_k](addKn t1) -addnBA //. + by apply job_interference_le_delta. + } + apply leq_trans with (n := service_during sched j_fst (a_fst + R_k) t2); + first by apply job_interference_le_service. + rewrite leqn0; apply/eqP. + apply cumulative_service_after_job_rt_zero with (job_cost0 := job_cost) (R := R_k); + [ by done | | by apply leqnn]. + by apply interference_bound_edf_j_fst_completed_on_time. + } + Qed. + + (* ..., which leads to the following bounds based on interval lengths. *) + Lemma interference_bound_edf_bounding_interference_with_interval_lengths : + interference_caused_by j_fst t1 t2 + (D_k - R_k) + D_i %/ p_k * p_k <= + \sum_(t1 <= t < a_fst + R_k) 1 + + \sum_(a_fst + R_k <= t < a_fst + D_k) 1 + + \sum_(a_fst + D_k <= t < a_lst + D_k) 1. + Proof. + apply leq_trans with (n := \sum_(t1 <= t < a_fst + R_k) 1 + (D_k - R_k) + + D_i %/ p_k * p_k). + { + rewrite 2!leq_add2r. + apply interference_bound_edf_interference_of_j_fst_bounded_by_response_time. + } + apply leq_trans with (n := \sum_(t1 <= t < a_fst + R_k) 1 + (D_k - R_k) + + (a_lst - a_fst)). + { + rewrite leq_add2l; fold (div_floor D_i p_k) n_k. + rewrite interference_bound_edf_n_k_equals_num_mid_jobs_plus_one. + by apply interference_bound_edf_many_periods_in_between. + } + apply leq_trans with (n := \sum_(t1 <= t < a_fst + R_k) 1 + + \sum_(a_fst + R_k <= t < a_fst + D_k) 1 + \sum_(a_fst + D_k <= t < a_lst + D_k) 1). + { + by rewrite -2!addnA leq_add2l; apply leq_add; + rewrite big_const_nat iter_addn mul1n addn0; + rewrite ?subnDl ?subnDr leqnn. + } + by apply leqnn. + Qed. + + (* To conclude, we show that the concatenation of these interval lengths equals + (a_lst + D_k) - 1, ... *) + Lemma interference_bound_edf_simpl_by_concatenation_of_intervals : + \sum_(t1 <= t < a_fst + R_k) 1 + + \sum_(a_fst + R_k <= t < a_fst + D_k) 1 + + \sum_(a_fst + D_k <= t < a_lst + D_k) 1 = (a_lst + D_k) - t1. + Proof. + assert (AFTERt1: t1 <= a_fst + R_k). + { + apply interference_bound_edf_j_fst_completion_implies_rt_bound_inside_interval. + by apply interference_bound_edf_j_fst_completed_on_time. + } + rewrite -big_cat_nat; + [simpl | by apply AFTERt1 | by rewrite leq_add2l; apply H_R_k_le_deadline]. + rewrite -big_cat_nat; simpl; last 2 first. + { + apply leq_trans with (n := a_fst + R_k); first by apply AFTERt1. + by rewrite leq_add2l; apply H_R_k_le_deadline. + } + { + rewrite leq_add2r; unfold a_fst, a_lst, j_fst, j_lst. + rewrite -[num_mid_jobs.+1]add0n; apply prev_le_next; + last by rewrite add0n H_at_least_two_jobs ltnSn. + by ins; apply interference_bound_edf_jobs_ordered_by_arrival. + } + by rewrite big_const_nat iter_addn mul1n addn0. + Qed. + + (* ... which results in proving that (a_lst + D_k) - t1 <= D_i. + This holds because high-priority jobs have earlier deadlines. Therefore, + the interference caused by the first job is bounded by D_i %% p_k - (D_k - R_k). *) + Lemma interference_bound_edf_interference_of_j_fst_limited_by_remainder_and_slack : + interference_caused_by j_fst t1 t2 <= D_i %% p_k - (D_k - R_k). + Proof. + apply interference_bound_edf_simpl_by_moving_to_left_side. + apply (leq_trans interference_bound_edf_bounding_interference_with_interval_lengths). + rewrite interference_bound_edf_simpl_by_concatenation_of_intervals leq_subLR. + have LST := interference_bound_edf_j_lst_is_job_of_tsk_k. + destruct LST as [_ [ LSTserv _]]. + unfold D_i, D_k, a_lst, t1; rewrite -interference_bound_edf_j_lst_deadline + -interference_bound_edf_j_i_deadline. + by apply interference_under_edf_implies_shorter_deadlines + with (job_deadline0 := job_deadline) in LSTserv. + Qed. + + End InterferenceOfFirstJob. + + (* Using the lemmas above we show that the interference bound works in the + case of two or more jobs. *) + Lemma interference_bound_edf_holds_for_multiple_jobs : + \sum_(0 <= i < num_mid_jobs.+2) + interference_caused_by (nth elem sorted_jobs i) t1 t2 <= interference_bound. + Proof. + (* Knowing that we have at least two elements, we take first and last out of the sum *) + rewrite [nth]lock big_nat_recl // big_nat_recr // /= -lock. + rewrite addnA addnC addnA. + + have NK := interference_bound_edf_n_k_equals_num_mid_jobs_plus_one. + + (* We use the lemmas we proved to show that the interference bound holds. *) + unfold interference_bound, edf_specific_interference_bound. + fold D_i D_k p_k n_k. + rewrite addnC addnA; apply leq_add; + first by rewrite addnC interference_bound_edf_holds_for_middle_and_last_jobs. + rewrite leq_min; apply/andP; split. + { + apply interference_bound_edf_interference_le_task_cost. + rewrite interference_bound_edf_job_in_same_sequence. + by apply mem_nth; rewrite H_at_least_two_jobs. + } + by apply interference_bound_edf_interference_of_j_fst_limited_by_remainder_and_slack. + Qed. + + End InterferenceTwoOrMoreJobs. + + End InterferenceManyJobs. + + Theorem interference_bound_edf_bounds_interference : + x <= interference_bound. + Proof. + (* Use the definition of workload based on list of jobs. *) + apply (leq_trans interference_bound_edf_use_another_definition). + + (* We only care about the jobs that cause interference. *) + rewrite interference_bound_edf_simpl_by_filtering_interfering_jobs. + + (* Now we order the list by job arrival time. *) + rewrite interference_bound_edf_simpl_by_sorting_interfering_jobs. + + (* Next, we show that the workload bound holds if n_k + is no larger than the number of interferings jobs. *) + destruct (size sorted_jobs <= n_k) eqn:NUM; + first by apply interference_bound_edf_holds_for_at_most_n_k_jobs. + apply negbT in NUM; rewrite -ltnNge in NUM. + + (* Find some dummy element to use in the nth function *) + assert (EX: exists elem: JobIn arr_seq, True). + destruct sorted_jobs as [| j]; [by rewrite ltn0 in NUM | by exists j]. + destruct EX as [elem _]. + + (* Now we index the sum to access the first and last elements. *) + rewrite (big_nth elem). + + (* First, we show that the bound holds for an empty list of jobs. *) + destruct (size sorted_jobs) as [| n] eqn:SIZE; + first by rewrite big_geq. + + (* Then, we show the same for a single job, or for multiple jobs. *) + rewrite SIZE; destruct n as [| num_mid_jobs]. + { + rewrite big_nat_recr // big_geq //. + rewrite [nth]lock /= -lock add0n. + by apply interference_bound_edf_holds_for_a_single_job; rewrite SIZE. + } + { + by apply interference_bound_edf_holds_for_multiple_jobs; first by rewrite SIZE. + } + Qed. + + End MainProof. + + End ProofSpecificBound. + + (* As required by the proof of convergence of EDF RTA, we show that the + EDF-specific bound is monotonically increasing with both the size + of the interval and the value of the previous response-time bounds. *) + Section MonotonicitySpecificBound. + + Context {sporadic_task: eqType}. + Variable task_cost: sporadic_task -> time. + Variable task_period: sporadic_task -> time. + Variable task_deadline: sporadic_task -> time. + + Variable tsk tsk_other: sporadic_task. + Hypothesis H_period_positive: task_period tsk_other > 0. + + Variable delta delta' R R': time. + Hypothesis H_delta_monotonic: delta <= delta'. + Hypothesis H_response_time_monotonic: R <= R'. + Hypothesis H_cost_le_rt_bound: task_cost tsk_other <= R. + + Lemma interference_bound_edf_monotonic : + interference_bound_edf task_cost task_period task_deadline tsk delta (tsk_other, R) <= + interference_bound_edf task_cost task_period task_deadline tsk delta' (tsk_other, R'). + Proof. + rename H_response_time_monotonic into LEr, H_delta_monotonic into LEx, + H_cost_le_rt_bound into LEcost, H_period_positive into GEperiod. + unfold interference_bound_edf, interference_bound_generic. + rewrite leq_min; apply/andP; split. + { + rewrite leq_min; apply/andP; split. + apply leq_trans with (n := (minn (W task_cost task_period (fst (tsk_other, R)) + (snd (tsk_other, R)) delta) (delta - task_cost tsk + 1))); + first by apply geq_minl. + apply leq_trans with (n := W task_cost task_period (fst (tsk_other, R)) + (snd (tsk_other, R)) delta); + [by apply geq_minl | by apply W_monotonic]. + apply leq_trans with (n := minn (W task_cost task_period (fst (tsk_other, R)) (snd (tsk_other, R)) delta) (delta - task_cost tsk + 1)); + first by apply geq_minl. + apply leq_trans with (n := delta - task_cost tsk + 1); + first by apply geq_minr. + by rewrite leq_add2r leq_sub2r. + } + { + apply leq_trans with (n := edf_specific_interference_bound task_cost task_period + task_deadline tsk tsk_other R); + first by apply geq_minr. + unfold edf_specific_interference_bound; simpl. + rewrite leq_add2l leq_min; apply/andP; split; first by apply geq_minl. + apply leq_trans with (n := task_deadline tsk %% task_period tsk_other - + (task_deadline tsk_other - R)); + [by apply geq_minr | by rewrite 2?leq_sub2l 2?leq_sub2r // leq_sub2l]. + } + Qed. + + End MonotonicitySpecificBound. + +End InterferenceBoundEDF. \ No newline at end of file diff --git a/analysis/apa/interference_bound_fp.v b/analysis/apa/interference_bound_fp.v new file mode 100644 index 0000000000000000000000000000000000000000..54230846186d9fd11aaf14029edb2501f362bea9 --- /dev/null +++ b/analysis/apa/interference_bound_fp.v @@ -0,0 +1,54 @@ +Require Import rt.util.all. +Require Import rt.model.apa.schedule rt.model.apa.priority rt.model.apa.workload + rt.model.apa.interference rt.model.apa.affinity. +Require Import rt.analysis.apa.workload_bound rt.analysis.apa.interference_bound. +From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop. + +Module InterferenceBoundFP. + + Import Schedule WorkloadBound Priority Interference Affinity. + Export InterferenceBoundGeneric. + + Section Definitions. + + Context {sporadic_task: eqType}. + Variable task_cost: sporadic_task -> time. + Variable task_period: sporadic_task -> time. + Variable task_deadline: sporadic_task -> time. + + (* Assume that each task has a processor affinity alpha. *) + Context {num_cpus: nat}. + Variable alpha: task_affinity sporadic_task num_cpus. + + (* Let tsk be the task to be analyzed ... *) + Variable tsk: sporadic_task. + + (* ... under subaffinity alpha'. *) + Variable alpha': affinity num_cpus. + + Let task_with_response_time := (sporadic_task * time)%type. + + (* Assume a known response-time bound for each interfering task ... *) + Variable R_prev: seq task_with_response_time. + + (* ... and an interval length delta. *) + Variable delta: time. + + (* Assume an FP policy. *) + Variable higher_eq_priority: FP_policy sporadic_task. + + (* Recall the generic interference bound. *) + Let total_interference_bound := interference_bound_generic task_cost task_period tsk delta. + + (* Let (hp_task_in alpha') denote the higher-priority tasks that can execute on alpha'. *) + Let hp_task_in alpha' := higher_priority_task_in alpha higher_eq_priority tsk alpha'. + + (* The total interference incurred by tsk is bounded by the sum + of individual task interferences of tasks in (hp_task_in alpha'). *) + Definition total_interference_bound_fp := + \sum_((tsk_other, R_other) <- R_prev | hp_task_in alpha' tsk_other) + total_interference_bound (tsk_other, R_other). + + End Definitions. + +End InterferenceBoundFP. \ No newline at end of file diff --git a/analysis/apa/workload_bound.v b/analysis/apa/workload_bound.v new file mode 100644 index 0000000000000000000000000000000000000000..ce0b848479f04163405b6e5751e1c1f922ac86d2 --- /dev/null +++ b/analysis/apa/workload_bound.v @@ -0,0 +1,729 @@ +Require Import rt.util.all rt.util.divround. +Require Import rt.model.apa.task rt.model.apa.job rt.model.apa.schedule + rt.model.apa.task_arrival rt.model.apa.response_time + rt.model.apa.workload rt.model.apa.schedulability. +From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq div fintype bigop path. + +Module WorkloadBound. + + Import Job SporadicTaskset Schedule ScheduleOfSporadicTask SporadicTaskArrival ResponseTime Schedulability Workload. + + Section WorkloadBoundDef. + + Context {sporadic_task: eqType}. + Variable task_cost: sporadic_task -> time. + Variable task_period: sporadic_task -> time. + + (* Consider any task tsk with response-time bound R_tsk, + that is scheduled in an interval of length delta. *) + Variable tsk: sporadic_task. + Variable R_tsk: time. + Variable delta: time. + + (* Based on the number of jobs that execute completely in the interval, ... *) + Definition max_jobs := + div_floor (delta + R_tsk - task_cost tsk) (task_period tsk). + + (* ... Bertogna and Cirinei's workload bound is defined as follows. *) + Definition W := + let e_k := (task_cost tsk) in + let p_k := (task_period tsk) in + minn e_k (delta + R_tsk - e_k - max_jobs * p_k) + max_jobs * e_k. + + End WorkloadBoundDef. + + Section BasicLemmas. + + Context {sporadic_task: eqType}. + Variable task_cost: sporadic_task -> time. + Variable task_period: sporadic_task -> time. + + (* Let tsk be any task...*) + Variable tsk: sporadic_task. + + (* ... with period > 0. *) + Hypothesis H_period_positive: task_period tsk > 0. + + (* Let R1 <= R2 be two response-time bounds that + are larger than the cost of the tsk. *) + Variable R1 R2: time. + Hypothesis H_R_lower_bound: R1 >= task_cost tsk. + Hypothesis H_R1_le_R2: R1 <= R2. + + Let workload_bound := W task_cost task_period tsk. + + (* Then, Bertogna and Cirinei's workload bound is monotonically increasing. *) + Lemma W_monotonic : + forall t1 t2, + t1 <= t2 -> + workload_bound R1 t1 <= workload_bound R2 t2. + Proof. + intros t1 t2 LEt. + unfold workload_bound, W, max_jobs, div_floor; rewrite 2!subndiv_eq_mod. + set e := task_cost tsk; set p := task_period tsk. + set x1 := t1 + R1. + set x2 := t2 + R2. + set delta := x2 - x1. + rewrite -[x2](addKn x1) -addnBA; fold delta; + last by apply leq_add. + + induction delta; first by rewrite addn0 leqnn. + { + apply (leq_trans IHdelta). + + (* Prove special case for p <= 1. *) + destruct (leqP p 1) as [LTp | GTp]. + { + rewrite leq_eqVlt in LTp; move: LTp => /orP LTp; des; + last by rewrite ltnS in LTp; apply (leq_trans H_period_positive) in LTp. + { + rewrite LTp 2!modn1 2!divn1. + rewrite leq_add2l leq_mul2r; apply/orP; right. + by rewrite leq_sub2r // leq_add2l. + } + } + (* Harder case: p > 1. *) + { + assert (EQ: (x1 + delta.+1 - e) = (x1 + delta - e).+1). + { + rewrite -[(x1 + delta - e).+1]addn1. + rewrite [_+1]addnC addnBA; last first. + { + apply (leq_trans H_R_lower_bound). + by rewrite -addnA addnC -addnA leq_addr. + } + by rewrite [1 + _]addnC -addnA addn1. + } rewrite -> EQ in *; clear EQ. + + have DIV := divSn_cases (x1 + delta - e) p GTp; des. + { + rewrite DIV leq_add2r leq_min; apply/andP; split; + first by rewrite geq_minl. + by apply leq_trans with (n := (x1 + delta - e) %% p); + [by rewrite geq_minr | by rewrite -DIV0 addn1 leqnSn]. + } + { + rewrite -[minn e _]add0n -addnA; apply leq_add; first by ins. + rewrite -DIV mulnDl mul1n [_ + e]addnC. + by apply leq_add; [by rewrite geq_minl | by ins]. + } + } + } + Qed. + + End BasicLemmas. + + Section ProofWorkloadBound. + + Context {sporadic_task: eqType}. + Variable task_cost: sporadic_task -> time. + Variable task_period: sporadic_task -> time. + Variable task_deadline: sporadic_task -> time. + + Context {Job: eqType}. + Variable job_cost: Job -> time. + Variable job_task: Job -> sporadic_task. + Variable job_deadline: Job -> time. + + Variable arr_seq: arrival_sequence Job. + + (* Assume that all jobs have valid parameters *) + Hypothesis H_jobs_have_valid_parameters : + forall (j: JobIn arr_seq), + valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j. + + (* Consider any schedule. *) + Context {num_cpus: nat}. + Variable sched: schedule num_cpus arr_seq. + + (* Assumption: jobs only execute if they arrived. + This is used to eliminate jobs that arrive after end of the interval t1 + delta. *) + Hypothesis H_jobs_must_arrive_to_execute: + jobs_must_arrive_to_execute sched. + + (* Assumption: jobs do not execute after they completed. + This is used to eliminate jobs that complete before the start of the interval t1. *) + Hypothesis H_completed_jobs_dont_execute: + completed_jobs_dont_execute job_cost sched. + + (* Assumption: Jobs are sequential. + This is required to use interval lengths as a measure of service. *) + Hypothesis H_sequential_jobs: sequential_jobs sched. + + (* Assumption: sporadic task model. + This is necessary to conclude that consecutive jobs ordered by arrival times + are separated by at least 'period' times units. *) + Hypothesis H_sporadic_tasks: sporadic_task_model task_period arr_seq job_task. + + (* Before starting the proof, let's give simpler names to the definitions. *) + Let job_has_completed_by := completed job_cost sched. + + Let workload_of (tsk: sporadic_task) (t1 t2: time) := + workload job_task sched tsk t1 t2. + + (* Now we define the theorem. Let tsk be any task in the taskset. *) + Variable tsk: sporadic_task. + + (* Assumption: the task must have valid parameters: + a) period > 0 (used in divisions) + b) deadline of the jobs = deadline of the task + c) cost <= period + (used to prove that the distance between the first and last + jobs is at least (cost + n*period), where n is the number + of middle jobs. If cost >> period, the claim does not hold + for every task set. *) + Hypothesis H_valid_task_parameters: + is_valid_sporadic_task task_cost task_period task_deadline tsk. + + (* Assumption: the task must have a constrained deadline. + This is required to prove that n_k (max_jobs) from Bertogna + and Cirinei's formula accounts for at least the number of + middle jobs (i.e., number of jobs - 2 in the worst case). *) + Hypothesis H_constrained_deadline: task_deadline tsk <= task_period tsk. + + (* Consider an interval [t1, t1 + delta). *) + Variable t1 delta: time. + + (* Assume that a response-time bound R_tsk for that task in any + schedule of this processor platform is also given, ... *) + Variable R_tsk: time. + + Hypothesis H_response_time_bound : + forall (j: JobIn arr_seq), + job_task j = tsk -> + job_arrival j + R_tsk < t1 + delta -> + job_has_completed_by j (job_arrival j + R_tsk). + + (* ... such that R_tsk >= task_cost tsk and R_tsk <= task_deadline tsk. *) + Hypothesis H_response_time_ge_cost: R_tsk >= task_cost tsk. + Hypothesis H_no_deadline_miss: R_tsk <= task_deadline tsk. + + Section MainProof. + + (* In this section, we prove that the workload of a task in the + interval [t1, t1 + delta) is bounded by W. *) + + (* Let's simplify the names a bit. *) + Let t2 := t1 + delta. + Let n_k := max_jobs task_cost task_period tsk R_tsk delta. + Let workload_bound := W task_cost task_period tsk R_tsk delta. + + (* Since we only care about the workload of tsk, we restrict + our view to the set of jobs of tsk scheduled in [t1, t2). *) + Let scheduled_jobs := + jobs_of_task_scheduled_between job_task sched tsk t1 t2. + + (* Now, let's consider the list of interfering jobs sorted by arrival time. *) + Let earlier_arrival := fun (x y: JobIn arr_seq) => job_arrival x <= job_arrival y. + Let sorted_jobs := (sort earlier_arrival scheduled_jobs). + + (* The first step consists in simplifying the sum corresponding + to the workload. *) + Section SimplifyJobSequence. + + (* After switching to the definition of workload based on a list + of jobs, we show that sorting the list preserves the sum. *) + Lemma workload_bound_simpl_by_sorting_scheduled_jobs : + workload_joblist job_task sched tsk t1 t2 = + \sum_(i <- sorted_jobs) service_during sched i t1 t2. + Proof. + unfold workload_joblist; fold scheduled_jobs. + rewrite (eq_big_perm sorted_jobs) /= //. + by rewrite -(perm_sort earlier_arrival). + Qed. + + (* Remember that both sequences have the same set of elements *) + Lemma workload_bound_job_in_same_sequence : + forall j, + (j \in scheduled_jobs) = (j \in sorted_jobs). + Proof. + by apply perm_eq_mem; rewrite -(perm_sort earlier_arrival). + Qed. + + (* Remember that all jobs in the sorted sequence is an + interfering job of task tsk. *) + Lemma workload_bound_all_jobs_from_tsk : + forall j_i, + j_i \in sorted_jobs -> + job_task j_i = tsk /\ + service_during sched j_i t1 t2 != 0 /\ + j_i \in jobs_scheduled_between sched t1 t2. + Proof. + intros j_i LTi. + rewrite -workload_bound_job_in_same_sequence mem_filter in LTi; des. + repeat split; [by done | | by done]. + unfold jobs_scheduled_between in *; rewrite mem_undup in LTi0. + apply mem_bigcat_nat_exists in LTi0; des. + rewrite mem_scheduled_jobs_eq_scheduled in LTi0. + apply service_implies_cumulative_service with (t := i); + first by apply/andP; split. + by rewrite -not_scheduled_no_service negbK. + Qed. + + (* Remember that consecutive jobs are ordered by arrival. *) + Lemma workload_bound_jobs_ordered_by_arrival : + forall i elem, + i < (size sorted_jobs).-1 -> + earlier_arrival (nth elem sorted_jobs i) (nth elem sorted_jobs i.+1). + Proof. + intros i elem LT. + assert (SORT: sorted earlier_arrival sorted_jobs). + by apply sort_sorted; unfold total, earlier_arrival; ins; apply leq_total. + by destruct sorted_jobs; simpl in *; [by rewrite ltn0 in LT | by apply/pathP]. + Qed. + + End SimplifyJobSequence. + + (* Next, we show that if the number of jobs is no larger than n_k, + the workload bound trivially holds. *) + Section WorkloadNotManyJobs. + + Lemma workload_bound_holds_for_at_most_n_k_jobs : + size sorted_jobs <= n_k -> + \sum_(i <- sorted_jobs) service_during sched i t1 t2 <= + workload_bound. + Proof. + intros LEnk. + rewrite -[\sum_(_ <- _ | _) _]add0n leq_add //. + apply leq_trans with (n := \sum_(x <- sorted_jobs) task_cost tsk); + last by rewrite big_const_seq iter_addn addn0 mulnC leq_mul2r; apply/orP; right. + { + rewrite [\sum_(_ <- _) service_during _ _ _ _]big_seq_cond. + rewrite [\sum_(_ <- _) task_cost _]big_seq_cond. + apply leq_sum; intros j_i; move/andP => [INi _]. + apply workload_bound_all_jobs_from_tsk in INi; des. + eapply cumulative_service_le_task_cost; + [by apply H_completed_jobs_dont_execute | by apply INi |]. + by apply H_jobs_have_valid_parameters. + } + Qed. + + End WorkloadNotManyJobs. + + (* Otherwise, assume that the number of jobs is larger than n_k >= 0. + First, consider the simple case with only one job. *) + Section WorkloadSingleJob. + + (* Assume that there's at least one job in the sorted list. *) + Hypothesis H_at_least_one_job: size sorted_jobs > 0. + + Variable elem: JobIn arr_seq. + Let j_fst := nth elem sorted_jobs 0. + + (* The first job is an interfering job of task tsk. *) + Lemma workload_bound_j_fst_is_job_of_tsk : + job_task j_fst = tsk /\ + service_during sched j_fst t1 t2 != 0 /\ + j_fst \in jobs_scheduled_between sched t1 t2. + Proof. + by apply workload_bound_all_jobs_from_tsk, mem_nth. + Qed. + + (* The workload bound holds for the single job. *) + Lemma workload_bound_holds_for_a_single_job : + \sum_(0 <= i < 1) service_during sched (nth elem sorted_jobs i) t1 t2 <= + workload_bound. + Proof. + unfold workload_bound, W; fold n_k. + have INfst := workload_bound_j_fst_is_job_of_tsk; des. + rewrite big_nat_recr // big_geq // [nth]lock /= -lock add0n. + destruct n_k; last first. + { + rewrite -[service_during _ _ _ _]add0n; rewrite leq_add //. + rewrite -[service_during _ _ _ _]add0n [_* task_cost tsk]mulSnr. + apply leq_add; first by done. + by eapply cumulative_service_le_task_cost; + [| by apply INfst + | by apply H_jobs_have_valid_parameters]. + } + { + rewrite 2!mul0n addn0 subn0 leq_min; apply/andP; split. + { + by eapply cumulative_service_le_task_cost; + [| by apply INfst + | by apply H_jobs_have_valid_parameters]. + } + { + rewrite -addnBA // -[service_during _ _ _ _]addn0. + apply leq_add; last by done. + by apply cumulative_service_le_delta. + } + } + Qed. + + End WorkloadSingleJob. + + (* Next, consider the last case where there are at least two jobs: + the first job j_fst, and the last job j_lst. *) + Section WorkloadTwoOrMoreJobs. + + (* There are at least two jobs. *) + Variable num_mid_jobs: nat. + Hypothesis H_at_least_two_jobs : size sorted_jobs = num_mid_jobs.+2. + + Variable elem: JobIn arr_seq. + Let j_fst := nth elem sorted_jobs 0. + Let j_lst := nth elem sorted_jobs num_mid_jobs.+1. + + (* The last job is an interfering job of task tsk. *) + Lemma workload_bound_j_lst_is_job_of_tsk : + job_task j_lst = tsk /\ + service_during sched j_lst t1 t2 != 0 /\ + j_lst \in jobs_scheduled_between sched t1 t2. + Proof. + apply workload_bound_all_jobs_from_tsk, mem_nth. + by rewrite H_at_least_two_jobs. + Qed. + + (* The response time of the first job must fall inside the interval. *) + Lemma workload_bound_response_time_of_first_job_inside_interval : + t1 <= job_arrival j_fst + R_tsk. + Proof. + rewrite leqNgt; apply /negP; unfold not; intro LTt1. + exploit workload_bound_all_jobs_from_tsk. + { + apply mem_nth; instantiate (1 := 0). + apply ltn_trans with (n := 1); [by done | by rewrite H_at_least_two_jobs]. + } + instantiate (1 := elem); move => [FSTtsk [/eqP FSTserv FSTin]]. + apply FSTserv. + apply (cumulative_service_after_job_rt_zero job_cost) with (R := R_tsk); + try (by done); last by apply ltnW. + apply H_response_time_bound; first by done. + by apply leq_trans with (n := t1); last by apply leq_addr. + Qed. + + (* The arrival of the last job must also fall inside the interval. *) + Lemma workload_bound_last_job_arrives_before_end_of_interval : + job_arrival j_lst < t2. + Proof. + rewrite leqNgt; apply/negP; unfold not; intro LT2. + exploit workload_bound_all_jobs_from_tsk. + { + apply mem_nth; instantiate (1 := num_mid_jobs.+1). + by rewrite -(ltn_add2r 1) addn1 H_at_least_two_jobs addn1. + } + instantiate (1 := elem); move => [LSTtsk [/eqP LSTserv LSTin]]. + by unfold service_during; apply LSTserv, cumulative_service_before_job_arrival_zero. + Qed. + + (* Next, we upper-bound the service of the first and last jobs using their arrival times. *) + Lemma workload_bound_service_of_first_and_last_jobs : + service_during sched j_fst t1 t2 + + service_during sched j_lst t1 t2 <= + (job_arrival j_fst + R_tsk - t1) + (t2 - job_arrival j_lst). + Proof. + apply leq_add; unfold service_during. + { + rewrite -[_ + _ - _]mul1n -[1*_]addn0 -iter_addn -big_const_nat. + apply leq_trans with (n := \sum_(t1 <= t < job_arrival j_fst + R_tsk) + service_at sched j_fst t); + last by apply leq_sum; ins; apply service_at_most_one. + destruct (job_arrival j_fst + R_tsk < t2) eqn:LEt2; last first. + { + unfold t2; apply negbT in LEt2; rewrite -ltnNge in LEt2. + rewrite -> big_cat_nat with (n := t1 + delta) (p := job_arrival j_fst + R_tsk); + [by apply leq_addr | by apply leq_addr | by done]. + } + { + rewrite -> big_cat_nat with (n := job_arrival j_fst + R_tsk); + [| by apply workload_bound_response_time_of_first_job_inside_interval + | by apply ltnW]. + rewrite -{2}[\sum_(_ <= _ < _) _]addn0 /= leq_add2l leqn0; apply/eqP. + apply (cumulative_service_after_job_rt_zero job_cost) with (R := R_tsk); try (by done). + apply H_response_time_bound; last by done. + exploit workload_bound_all_jobs_from_tsk. + by apply mem_nth; instantiate (1 := 0); rewrite H_at_least_two_jobs. + by instantiate (1 := elem); move => [FSTtsk _]. + } + } + { + rewrite -[_ - _]mul1n -[1 * _]addn0 -iter_addn -big_const_nat. + destruct (job_arrival j_lst <= t1) eqn:LT. + { + apply leq_trans with (n := \sum_(job_arrival j_lst <= t < t2) + service_at sched j_lst t); + first by rewrite -> big_cat_nat with (m := job_arrival j_lst) (n := t1); + [by apply leq_addl | by ins | by apply leq_addr]. + by apply leq_sum; ins; apply service_at_most_one. + } + { + apply negbT in LT; rewrite -ltnNge in LT. + rewrite -> big_cat_nat with (n := job_arrival j_lst); + [| by apply ltnW + | by apply ltnW, workload_bound_last_job_arrives_before_end_of_interval]. + rewrite /= -[\sum_(_ <= _ < _) 1]add0n; apply leq_add. + rewrite cumulative_service_before_job_arrival_zero; + [by apply leqnn | by ins | by apply leqnn]. + by apply leq_sum; ins; apply service_at_most_one. + } + } + Qed. + + (* Simplify the expression from the previous lemma. *) + Lemma workload_bound_simpl_expression_with_first_and_last : + job_arrival j_fst + R_tsk - t1 + (t2 - job_arrival j_lst) = + delta + R_tsk - (job_arrival j_lst - job_arrival j_fst). + Proof. + have lemma1 := workload_bound_last_job_arrives_before_end_of_interval. + have lemma2 := workload_bound_response_time_of_first_job_inside_interval. + rewrite addnBA; last by apply ltnW. + rewrite subh1 // -addnBA; last by apply leq_addr. + rewrite addnC [job_arrival _ + _]addnC. + unfold t2; rewrite [t1 + _]addnC -[delta + t1 - _]subnBA // subnn subn0. + rewrite addnA -subnBA; first by ins. + unfold j_fst, j_lst. rewrite -[_.+1]add0n. + apply prev_le_next; last by rewrite H_at_least_two_jobs add0n leqnn. + by ins; apply workload_bound_jobs_ordered_by_arrival. + Qed. + + (* Bound the service of the middle jobs. *) + Lemma workload_bound_service_of_middle_jobs : + \sum_(0 <= i < num_mid_jobs) + service_during sched (nth elem sorted_jobs i.+1) t1 t2 <= + num_mid_jobs * task_cost tsk. + Proof. + apply leq_trans with (n := num_mid_jobs * task_cost tsk); + last by rewrite leq_mul2l; apply/orP; right. + apply leq_trans with (n := \sum_(0 <= i < num_mid_jobs) task_cost tsk); + last by rewrite big_const_nat iter_addn addn0 mulnC subn0. + rewrite big_nat_cond [\sum_(0 <= i < num_mid_jobs) task_cost _]big_nat_cond. + apply leq_sum; intros i; rewrite andbT; move => /andP LT; des. + eapply cumulative_service_le_task_cost; + [by apply H_completed_jobs_dont_execute | | by apply H_jobs_have_valid_parameters]. + exploit workload_bound_all_jobs_from_tsk. + { + instantiate (1 := nth elem sorted_jobs i.+1). + apply mem_nth; rewrite H_at_least_two_jobs. + by rewrite ltnS; apply leq_trans with (n := num_mid_jobs). + } + by ins; des. + Qed. + + (* Conclude that the distance between first and last is at least num_mid_jobs + 1 periods. *) + Lemma workload_bound_many_periods_in_between : + job_arrival j_lst - job_arrival j_fst >= num_mid_jobs.+1 * (task_period tsk). + Proof. + assert (EQnk: num_mid_jobs.+1=(size sorted_jobs).-1). + by rewrite H_at_least_two_jobs. + unfold j_fst, j_lst; rewrite EQnk telescoping_sum; + last by ins; apply workload_bound_jobs_ordered_by_arrival. + rewrite -[_ * _ tsk]addn0 mulnC -iter_addn -{1}[_.-1]subn0 -big_const_nat. + rewrite big_nat_cond [\sum_(0 <= i < _)(_-_)]big_nat_cond. + apply leq_sum; intros i; rewrite andbT; move => /andP LT; des. + + (* To simplify, call the jobs 'cur' and 'next' *) + set cur := nth elem sorted_jobs i. + set next := nth elem sorted_jobs i.+1. + + (* Show that cur arrives earlier than next *) + assert (ARRle: job_arrival cur <= job_arrival next). + by unfold cur, next; apply workload_bound_jobs_ordered_by_arrival. + + (* Show that both cur and next are in the arrival sequence *) + assert (INnth: cur \in scheduled_jobs /\ next \in scheduled_jobs). + { + rewrite 2!workload_bound_job_in_same_sequence; split. + by apply mem_nth, (ltn_trans LT0); destruct sorted_jobs; ins. + by apply mem_nth; destruct sorted_jobs; ins. + } + rewrite 2?mem_filter in INnth; des. + + (* Use the sporadic task model to conclude that cur and next are separated + by at least (task_period tsk) units. Of course this only holds if cur != next. + Since we don't know much about the list (except that it's sorted), we must + also prove that it doesn't contain duplicates. *) + assert (CUR_LE_NEXT: job_arrival cur + task_period (job_task cur) <= job_arrival next). + { + apply H_sporadic_tasks; last by ins. + unfold cur, next, not; intro EQ; move: EQ => /eqP EQ. + rewrite nth_uniq in EQ; first by move: EQ => /eqP EQ; intuition. + by apply ltn_trans with (n := (size sorted_jobs).-1); destruct sorted_jobs; ins. + by destruct sorted_jobs; ins. + by rewrite sort_uniq -/scheduled_jobs filter_uniq // undup_uniq. + by rewrite INnth INnth0. + } + by rewrite subh3 // addnC -INnth. + Qed. + + (* Prove that n_k is at least the number of the middle jobs *) + Lemma workload_bound_n_k_covers_middle_jobs : + n_k >= num_mid_jobs. + Proof. + rename H_valid_task_parameters into PARAMS. + unfold is_valid_sporadic_task in *; des. + rewrite leqNgt; apply/negP; unfold not; intro LTnk. + assert (DISTmax: job_arrival j_lst - job_arrival j_fst >= delta + task_period tsk). + { + apply leq_trans with (n := n_k.+2 * task_period tsk). + { + rewrite -addn1 mulnDl mul1n leq_add2r. + apply leq_trans with (n := delta + R_tsk - task_cost tsk); + first by rewrite -addnBA //; apply leq_addr. + by apply ltnW, ltn_ceil, PARAMS0. + } + apply leq_trans with (num_mid_jobs.+1 * task_period tsk); + first by rewrite leq_mul2r; apply/orP; right. + by apply workload_bound_many_periods_in_between. + } + rewrite <- leq_add2r with (p := job_arrival j_fst) in DISTmax. + rewrite addnC subh1 in DISTmax; last first. + { + unfold j_fst, j_lst; rewrite -[_.+1]add0n. + apply prev_le_next; last by rewrite H_at_least_two_jobs add0n leqnn. + by ins; apply workload_bound_jobs_ordered_by_arrival. + } + rewrite -subnBA // subnn subn0 in DISTmax. + rewrite [delta + task_period tsk]addnC addnA in DISTmax. + have BEFOREt2 := workload_bound_last_job_arrives_before_end_of_interval. + generalize BEFOREt2; move: BEFOREt2; rewrite {1}ltnNge; move => /negP BEFOREt2'. + intros BEFOREt2; apply BEFOREt2'; clear BEFOREt2'. + apply leq_trans with (n := job_arrival j_fst + task_deadline tsk + delta); + last by apply leq_trans with (n := job_arrival j_fst + task_period tsk + delta); + [rewrite leq_add2r leq_add2l; apply H_constrained_deadline | apply DISTmax]. + unfold t2; rewrite leq_add2r. + apply leq_trans with (n := job_arrival j_fst + R_tsk); + last by rewrite leq_add2l. + by apply workload_bound_response_time_of_first_job_inside_interval. + Qed. + + (* If n_k = num_mid_jobs, then the workload bound holds. *) + Lemma workload_bound_n_k_equals_num_mid_jobs : + num_mid_jobs = n_k -> + service_during sched j_lst t1 t2 + + service_during sched j_fst t1 t2 + + \sum_(0 <= i < num_mid_jobs) + service_during sched (nth elem sorted_jobs i.+1) t1 t2 + <= workload_bound. + Proof. + rename H_valid_task_parameters into PARAMS. + unfold is_valid_sporadic_task in *; des. + unfold workload_bound, W; fold n_k. + move => NK; rewrite -NK. + apply leq_add; + last by apply workload_bound_service_of_middle_jobs. + apply leq_trans with (delta + R_tsk - (job_arrival j_lst - job_arrival j_fst)). + { + rewrite addnC -workload_bound_simpl_expression_with_first_and_last. + by apply workload_bound_service_of_first_and_last_jobs. + } + rewrite leq_min; apply/andP; split. + { + rewrite leq_subLR [_ + task_cost _]addnC -leq_subLR. + apply leq_trans with (num_mid_jobs.+1 * task_period tsk); + last by apply workload_bound_many_periods_in_between. + rewrite NK ltnW // -ltn_divLR; + last by apply PARAMS0. + by unfold n_k, max_jobs, div_floor. + } + { + rewrite -subnDA; apply leq_sub2l. + apply leq_trans with (n := num_mid_jobs.+1 * task_period tsk); + last by apply workload_bound_many_periods_in_between. + rewrite -addn1 addnC mulnDl mul1n. + by rewrite leq_add2l; last by apply PARAMS3. + } + Qed. + + (* If n_k = num_mid_jobs + 1, then the workload bound holds. *) + Lemma workload_bound_n_k_equals_num_mid_jobs_plus_1 : + num_mid_jobs.+1 = n_k -> + service_during sched j_lst t1 t2 + + service_during sched j_fst t1 t2 + + \sum_(0 <= i < num_mid_jobs) + service_during sched (nth elem sorted_jobs i.+1) t1 t2 + <= workload_bound. + Proof. + unfold workload_bound, W; fold n_k. + move => NK; rewrite -NK. + rewrite -{2}addn1 mulnDl mul1n [_* _ + _]addnC addnA addn_minl. + apply leq_add; last by apply workload_bound_service_of_middle_jobs. + rewrite leq_min; apply/andP; split. + { + assert (SIZE: 0 < size sorted_jobs). + by rewrite H_at_least_two_jobs. + have INfst := workload_bound_j_fst_is_job_of_tsk SIZE elem; + have INlst := workload_bound_j_lst_is_job_of_tsk; des. + by apply leq_add; apply cumulative_service_le_task_cost with (task_deadline0 := task_deadline) + (job_cost0 := job_cost) (job_deadline0 := job_deadline) (job_task0 := job_task). + } + { + rewrite subnAC subnK; last first. + { + assert (TMP: delta + R_tsk = task_cost tsk + (delta + R_tsk - task_cost tsk)); + first by rewrite subnKC; [by ins | by rewrite -[task_cost _]add0n; apply leq_add]. + rewrite TMP; clear TMP. + rewrite -{1}[task_cost _]addn0 -addnBA NK; [by apply leq_add | by apply leq_trunc_div]. + } + apply leq_trans with (delta + R_tsk - (job_arrival j_lst - job_arrival j_fst)). + { + rewrite addnC -workload_bound_simpl_expression_with_first_and_last. + by apply workload_bound_service_of_first_and_last_jobs. + } + { + by apply leq_sub2l, workload_bound_many_periods_in_between. + } + } + Qed. + + End WorkloadTwoOrMoreJobs. + + (* Using the lemmas above, we prove the main theorem about the workload bound. *) + Theorem workload_bounded_by_W : + workload_of tsk t1 (t1 + delta) <= workload_bound. + Proof. + unfold workload_of, workload_bound, W in *; ins; des. + fold n_k. + + (* Use the definition of workload based on list of jobs. *) + rewrite workload_eq_workload_joblist. + + (* Now we order the list by job arrival time. *) + rewrite workload_bound_simpl_by_sorting_scheduled_jobs. + + (* Next, we show that the workload bound holds if n_k + is no larger than the number of interferings jobs. *) + destruct (size sorted_jobs <= n_k) eqn:NUM; + first by apply workload_bound_holds_for_at_most_n_k_jobs. + apply negbT in NUM; rewrite -ltnNge in NUM. + + (* Find some dummy element to use in the nth function *) + assert (EX: exists elem: JobIn arr_seq, True). + destruct sorted_jobs; [ by rewrite ltn0 in NUM | by exists j]. + destruct EX as [elem _]. + + (* Now we index the sum to access the first and last elements. *) + rewrite (big_nth elem). + + (* First, we show that the bound holds for an empty list of jobs. *) + destruct (size sorted_jobs) as [| n] eqn:SIZE; + first by rewrite big_geq. + + (* Then, we show the same for a singleton set of jobs. *) + destruct n as [| num_mid_jobs]; + first by apply workload_bound_holds_for_a_single_job; rewrite SIZE. + + (* Knowing that we have at least two elements, we take first and last out of the sum *) + rewrite [nth]lock big_nat_recl // big_nat_recr // /= -lock. + rewrite addnA addnC addnA. + + (* There are two cases to be analyze since n <= n_k < n + 2, + where n is the number of middle jobs. *) + have NK := workload_bound_n_k_covers_middle_jobs num_mid_jobs SIZE elem. + move: NK; rewrite leq_eqVlt orbC leq_eqVlt; move => /orP [NK | /eqP NK]. + move: NK => /orP [/eqP NK | NK]; last by rewrite ltnS leqNgt NK in NUM. + { + (* Case 1: n_k = n + 1, where n is the number of middle jobs. *) + by apply (workload_bound_n_k_equals_num_mid_jobs_plus_1 num_mid_jobs). + } + { + (* Case 2: n_k = n, where n is the number of middle jobs. *) + by apply (workload_bound_n_k_equals_num_mid_jobs num_mid_jobs). + } + Qed. + + End MainProof. + + End ProofWorkloadBound. + +End WorkloadBound. \ No newline at end of file diff --git a/analysis/basic/bertogna_edf_comp.v b/analysis/basic/bertogna_edf_comp.v index d9493644e86d3a567ec61e0984c3510492750c81..3bc47989a57b84f8797162319c04d987e83d07b0 100755 --- a/analysis/basic/bertogna_edf_comp.v +++ b/analysis/basic/bertogna_edf_comp.v @@ -57,7 +57,7 @@ Module ResponseTimeIterationEDF. (* To compute the response-time bounds of the entire task set, We start the iteration with a sequence of tasks and costs: <(task1, cost1), (task2, cost2), ...>. *) - Let initial_state (ts: taskset_of sporadic_task) := + Let initial_state (ts: seq sporadic_task) := map (fun t => (t, task_cost t)) ts. (* Then, we successively update the the response-time bounds based @@ -68,13 +68,13 @@ Module ResponseTimeIterationEDF. (* To ensure that the procedure converges, we stop the iteration after a "sufficient" number of times, which corresponds to the time complexity of the procedure. *) - Let max_steps (ts: taskset_of sporadic_task) := + Let max_steps (ts: seq sporadic_task) := \sum_(tsk <- ts) (task_deadline tsk - task_cost tsk) + 1. (* This yields the following definition for the RTA. At the end we check if all computed response-time bounds are less than or equal to the deadline, in which case they are valid. *) - Definition edf_claimed_bounds (ts: taskset_of sporadic_task) := + Definition edf_claimed_bounds (ts: seq sporadic_task) := let R_values := iter (max_steps ts) edf_rta_iteration (initial_state ts) in if (all R_le_deadline R_values) then Some R_values @@ -82,7 +82,7 @@ Module ResponseTimeIterationEDF. (* The schedulability test simply checks if we got a list of response-time bounds (i.e., if the computation did not fail). *) - Definition edf_schedulable (ts: taskset_of sporadic_task) := + Definition edf_schedulable (ts: seq sporadic_task) := edf_claimed_bounds ts != None. (* In the following section, we prove several helper lemmas about the @@ -205,7 +205,7 @@ Module ResponseTimeIterationEDF. Section Convergence. (* Consider any valid task set. *) - Variable ts: taskset_of sporadic_task. + Variable ts: seq sporadic_task. Hypothesis H_valid_task_parameters: valid_sporadic_taskset task_cost task_period task_deadline ts. @@ -395,11 +395,11 @@ Module ResponseTimeIterationEDF. assert (SUBST: forall l delta, \sum_(j <- l | let '(tsk_other, _) := j in - jldp_can_interfere_with tsk_i tsk_other) + different_task tsk_i tsk_other) (let '(tsk_other, R_other) := j in interference_bound_edf task_cost task_period task_deadline tsk_i delta (tsk_other, R_other)) = - \sum_(j <- l | jldp_can_interfere_with tsk_i (fst j)) + \sum_(j <- l | different_task tsk_i (fst j)) interference_bound_edf task_cost task_period task_deadline tsk_i delta j). { intros l x; clear -l. @@ -444,13 +444,14 @@ Module ResponseTimeIterationEDF. } move: GE_COST => /allP GE_COST. - assert (LESUM: \sum_(j <- x1' | jldp_can_interfere_with tsk_i (fst j)) - interference_bound_edf task_cost task_period task_deadline tsk_i delta j <= \sum_(j <- x2' | jldp_can_interfere_with tsk_i (fst j)) + assert (LESUM: \sum_(j <- x1' | different_task tsk_i (fst j)) + interference_bound_edf task_cost task_period task_deadline tsk_i delta j <= + \sum_(j <- x2' | different_task tsk_i (fst j)) interference_bound_edf task_cost task_period task_deadline tsk_i delta' j). { set elem := (tsk0, R0); rewrite 2!(big_nth elem). rewrite -SIZE. - rewrite big_mkcond [\sum_(_ <- _ | jldp_can_interfere_with _ _)_]big_mkcond. + rewrite big_mkcond [\sum_(_ <- _ | different_task _ _)_]big_mkcond. rewrite big_seq_cond [\sum_(_ <- _ | true) _]big_seq_cond. apply leq_sum; intros j; rewrite andbT; intros INj. rewrite mem_iota add0n subn0 in INj; move: INj => /andP [_ INj]. @@ -459,7 +460,7 @@ Module ResponseTimeIterationEDF. have MAP := @nth_map _ elem _ tsk0 (fun x => fst x). by rewrite -2?MAP -?SIZE //; f_equal. } rewrite -FSTeq. - destruct (jldp_can_interfere_with tsk_i (fst (nth elem x1' j))) eqn:INTERF; + destruct (different_task tsk_i (fst (nth elem x1' j))) eqn:INTERF; last by done. { exploit (LE elem); [by rewrite /= SIZE | | intro LEj]. @@ -485,7 +486,7 @@ Module ResponseTimeIterationEDF. by apply interference_bound_edf_monotonic. } } - destruct (jldp_can_interfere_with tsk_i tsk0) eqn:INTERFtsk0; last by done. + destruct (different_task tsk_i tsk0) eqn:INTERFtsk0; last by done. apply leq_add; last by done. { exploit (LE (tsk0, R0)); [by rewrite /= SIZE | | intro LEj]; @@ -820,15 +821,13 @@ Module ResponseTimeIterationEDF. Qed. (* Therefore, with regard to the response-time bound recurrence, ...*) - Let rt_recurrence (tsk: sporadic_task) (rt_bounds: seq task_with_response_time) (R: time) := - task_cost tsk + div_floor (I rt_bounds tsk R) num_cpus. (* ..., the individual response-time bounds (elements of the list) are also fixed points. *) Theorem edf_claimed_bounds_finds_fixed_point_for_each_bound : forall tsk R rt_bounds, edf_claimed_bounds ts = Some rt_bounds -> (tsk, R) \in rt_bounds -> - R = rt_recurrence tsk rt_bounds R. + R = edf_response_time_bound rt_bounds tsk R. Proof. intros tsk R rt_bounds SOME IN. have CONV := edf_claimed_bounds_finds_fixed_point_of_list rt_bounds. @@ -858,17 +857,14 @@ Module ResponseTimeIterationEDF. Section MainProof. - (* Consider a task set ts. *) + (* Consider a task set ts where... *) Variable ts: taskset_of sporadic_task. - (* Assume the task set has no duplicates, ... *) - Hypothesis H_ts_is_a_set: uniq ts. - - (* ...all tasks have valid parameters, ... *) + (* ...all tasks have valid parameters ... *) Hypothesis H_valid_task_parameters: valid_sporadic_taskset task_cost task_period task_deadline ts. - (* ...constrained deadlines, ...*) + (* ...and constrained deadlines. *) Hypothesis H_constrained_deadlines: forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk. diff --git a/analysis/basic/bertogna_edf_theory.v b/analysis/basic/bertogna_edf_theory.v index b76bc4784d570fc6e6c1487efbe411aa755fd732..b35dc603940d33bdebdcc6f9bec7e8859002c4d9 100644 --- a/analysis/basic/bertogna_edf_theory.v +++ b/analysis/basic/bertogna_edf_theory.v @@ -2,7 +2,8 @@ Require Import rt.util.all. Require Import rt.model.basic.task rt.model.basic.job rt.model.basic.task_arrival rt.model.basic.schedule rt.model.basic.platform rt.model.basic.interference rt.model.basic.workload rt.model.basic.schedulability rt.model.basic.priority - rt.model.basic.platform rt.model.basic.response_time. + rt.model.basic.platform rt.model.basic.response_time + rt.model.basic.constrained_deadlines. Require Import rt.analysis.basic.workload_bound rt.analysis.basic.interference_bound_edf. From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop div path. @@ -10,10 +11,12 @@ Module ResponseTimeAnalysisEDF. Export Job SporadicTaskset Schedule ScheduleOfSporadicTask Workload Schedulability ResponseTime Priority SporadicTaskArrival WorkloadBound InterferenceBoundEDF - Interference Platform. + Interference Platform ConstrainedDeadlines. - (* In this section, we prove that Bertogna and Cirinei's RTA yields - safe response-time bounds. *) + (* In this section, we prove that any fixed point in Bertogna and + Cirinei's RTA for EDF scheduling is a safe response-time bound. + This analysis can be found in Chapter 17.1.2 of Baruah et al.'s + book Multiprocessor Scheduling for Real-time Systems. *) Section ResponseTimeBound. Context {sporadic_task: eqType}. @@ -36,48 +39,52 @@ Module ResponseTimeAnalysisEDF. forall (j: JobIn arr_seq), valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j. - (* Consider any schedule such that...*) - Variable num_cpus: nat. - Variable sched: schedule num_cpus arr_seq. - - (* ...jobs do not execute before their arrival times nor longer - than their execution costs. *) - Hypothesis H_jobs_must_arrive_to_execute: - jobs_must_arrive_to_execute sched. - Hypothesis H_completed_jobs_dont_execute: - completed_jobs_dont_execute job_cost sched. - - (* Also assume that jobs are sequential and that - there exists at least one processor. *) - Hypothesis H_sequential_jobs: sequential_jobs sched. - Hypothesis H_at_least_one_cpu : - num_cpus > 0. - - (* Assume that we have a task set where all tasks have valid - parameters and constrained deadlines, ... *) + (* Consider a task set ts where all tasks have valid parameters + and constrained deadlines, ... *) Variable ts: taskset_of sporadic_task. Hypothesis H_valid_task_parameters: valid_sporadic_taskset task_cost task_period task_deadline ts. Hypothesis H_constrained_deadlines: forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk. - (* ... and that all jobs in the arrival sequence come from the task set. *) + (* ... and assume that all jobs in the arrival sequence come from the task set. *) Hypothesis H_all_jobs_from_taskset: forall (j: JobIn arr_seq), job_task j \in ts. + + (* Next, consider any schedule such that...*) + Variable num_cpus: nat. + Variable sched: schedule num_cpus arr_seq. + + (* ...jobs are sequential and do not execute before their + arrival times nor longer than their execution costs. *) + Hypothesis H_sequential_jobs: sequential_jobs sched. + Hypothesis H_jobs_must_arrive_to_execute: + jobs_must_arrive_to_execute sched. + Hypothesis H_completed_jobs_dont_execute: + completed_jobs_dont_execute job_cost sched. + + (* Assume that there exists at least one processor. *) + Hypothesis H_at_least_one_cpu: num_cpus > 0. + + (* Assume that the schedule is a work-conserving EDF schedule. *) + Hypothesis H_work_conserving: work_conserving job_cost sched. + Hypothesis H_edf_policy: enforces_JLDP_policy job_cost sched (EDF job_deadline). + (* Let's define some local names to avoid passing many parameters. *) Let no_deadline_is_missed_by_tsk (tsk: sporadic_task) := task_misses_no_deadline job_cost job_deadline job_task sched tsk. Let response_time_bounded_by (tsk: sporadic_task) := is_response_time_bound_of_task job_cost job_task tsk sched. - (* Assume that a response-time bound R is known... *) + (* Next we consider the response-time recurrence. + Assume that a response-time bound R is known... *) Let task_with_response_time := (sporadic_task * time)%type. Variable rt_bounds: seq task_with_response_time. (* ...for any task in the task set, ... *) Hypothesis H_rt_bounds_contains_all_tasks: unzip1 rt_bounds = ts. - (* ... such that R is a fixed-point of the response-time recurrence, ... *) + (* ... where R is a fixed-point of the response-time recurrence, ... *) Let I (tsk: sporadic_task) (delta: time) := total_interference_bound_edf task_cost task_period task_deadline tsk rt_bounds delta. Hypothesis H_response_time_is_fixed_point : @@ -90,16 +97,7 @@ Module ResponseTimeAnalysisEDF. forall tsk_other R, (tsk_other, R) \in rt_bounds -> R <= task_deadline tsk_other. - (* Assume that we have a work-conserving EDF scheduler. *) - Hypothesis H_work_conserving: work_conserving job_cost sched. - Hypothesis H_edf_policy: enforces_JLDP_policy job_cost sched (EDF job_deadline). - - (* Assume that the task set has no duplicates. This is required to - avoid problems when counting tasks (for example, when stating - that the number of interfering tasks is at most num_cpus). *) - Hypothesis H_ts_is_a_set : uniq ts. - - (* In order to prove that R is a response-time bound, we first present some lemmas. *) + (* In order to prove that R is a response-time bound, we first provide some lemmas. *) Section Lemmas. (* Let (tsk, R) be any task to be analyzed, with its response-time bound R. *) @@ -127,7 +125,7 @@ Module ResponseTimeAnalysisEDF. task_interference job_cost job_task sched j tsk_other (job_arrival j) (job_arrival j + R). - (* and X the total interference incurred by job j due to any task. *) + (* ...and X the total interference incurred by job j due to any task. *) Let X := total_interference job_cost sched j (job_arrival j) (job_arrival j + R). (* Recall Bertogna and Cirinei's workload bound ... *) @@ -142,9 +140,14 @@ Module ResponseTimeAnalysisEDF. Let interference_bound (tsk_other: sporadic_task) (R_other: time) := interference_bound_edf task_cost task_period task_deadline tsk R (tsk_other, R_other). - (* Also, let ts_interf be the subset of tasks that interfere with tsk. *) - Let ts_interf := [seq tsk_other <- ts | jldp_can_interfere_with tsk tsk_other]. + (* Based on the definition of a different task, ... *) + Let other_task := different_task tsk. + (* ...let other_tasks denote the set of tasks that are different from tsk. *) + Let other_tasks := + [seq tsk_other <- ts | other_task tsk_other]. + + (* Now we establish results the interfering tasks. *) Section LemmasAboutInterferingTasks. (* Let (tsk_other, R_other) be any pair of higher-priority task and @@ -156,7 +159,8 @@ Module ResponseTimeAnalysisEDF. (* Note that tsk_other is in the task set, ...*) Lemma bertogna_edf_tsk_other_in_ts: tsk_other \in ts. Proof. - by rewrite -H_rt_bounds_contains_all_tasks; apply/mapP; exists (tsk_other, R_other). + rewrite set_mem -H_rt_bounds_contains_all_tasks. + by apply/mapP; exists (tsk_other, R_other). Qed. (* ... and R_other is larger than the cost of tsk_other. *) @@ -253,56 +257,11 @@ Module ResponseTimeAnalysisEDF. by apply completion_monotonic with (t := i); try (by done); apply ltnW. Qed. - Let scheduled_task_other_than_tsk (t: time) (tsk_other: sporadic_task) := - task_is_scheduled job_task sched tsk_other t && - jldp_can_interfere_with tsk tsk_other. - - (* 1) At all times that j is backlogged, all processors are busy with other tasks. *) - Lemma bertogna_edf_scheduling_invariant: - forall t, - job_arrival j <= t < job_arrival j + R -> - backlogged job_cost sched j t -> - count (scheduled_task_other_than_tsk t) ts = num_cpus. - Proof. - rename H_all_jobs_from_taskset into FROMTS, - H_valid_task_parameters into PARAMS, - H_job_of_tsk into JOBtsk, - H_sporadic_tasks into SPO, - H_tsk_R_in_rt_bounds into INbounds, - H_all_previous_jobs_completed_on_time into BEFOREok, - H_tasks_miss_no_deadlines into NOMISS, - H_rt_bounds_contains_all_tasks into UNZIP, - H_constrained_deadlines into RESTR, - H_work_conserving into WORK. - unfold x, X, total_interference, task_interference. - move => t /andP [LEt LTt] BACK. - unfold scheduled_task_other_than_tsk in *. - eapply platform_cpus_busy_with_interfering_tasks; try (by done); - [ by apply WORK | by apply SPO - | apply PARAMS; rewrite -JOBtsk; apply FROMTS - | by apply JOBtsk | by apply BACK | ]. - { - intros j0 tsk0 TSK0 LE. - cut (tsk0 \in unzip1 rt_bounds = true); last by rewrite UNZIP -TSK0 FROMTS. - move => /mapP [p IN EQ]; destruct p as [tsk' R0]; simpl in *; subst tsk'. - apply completion_monotonic with (t0 := job_arrival j0 + R0); try (by done). - { - rewrite leq_add2l TSK0. - apply leq_trans with (n := task_deadline tsk0); first by apply NOMISS. - by apply RESTR; rewrite -TSK0 FROMTS. - } - { - apply BEFOREok with (tsk_other := tsk0); try (by done). - apply leq_ltn_trans with (n := t); last by done. - apply leq_trans with (n := job_arrival j0 + task_period tsk0); last by done. - rewrite leq_add2l; apply leq_trans with (n := task_deadline tsk0); first by apply NOMISS. - by apply RESTR; rewrite -TSK0 FROMTS. - } - } - Qed. - - (* 2) Prove that during the scheduling window of j, any job that is scheduled while j is - backlogged comes from a different task. *) + (* 1) Next, we prove that during the scheduling window of j, any job that is + scheduled while j is backlogged comes from a different task. + This follows from the fact that j is the first job not to complete + by its response-time bound, so previous jobs of j's task must have + completed by their periods and cannot be pending. *) Lemma bertogna_edf_interference_by_different_tasks : forall t j_other, job_arrival j <= t < job_arrival j + R -> @@ -354,12 +313,96 @@ Module ResponseTimeAnalysisEDF. } Qed. + (* 2) In order to use the lemmas in constrained_deadlines.v, we show that + all jobs released before the end of the interval complete by their + periods. This follows trivially from the hypothesis that all jobs + before (job_arrival j + R) complete by their response-time bounds. + With this lemma, we can conclude that during job j's scheduling + window there cannot be multiple pending jobs of each task.*) + Lemma bertogna_edf_all_previous_jobs_complete_by_their_period: + forall t (j0: JobIn arr_seq), + t < job_arrival j + R -> + job_arrival j0 + task_period (job_task j0) <= t -> + completed job_cost sched j0 + (job_arrival j0 + task_period (job_task j0)). + Proof. + rename H_rt_bounds_contains_all_tasks into UNZIP, + H_constrained_deadlines into CONSTR, + H_tasks_miss_no_deadlines into NOMISS, + H_all_jobs_from_taskset into FROMTS, + H_all_previous_jobs_completed_on_time into BEFOREok. + intros t j0 LEt LE. + cut ((job_task j0) \in unzip1 rt_bounds = true); last by rewrite UNZIP FROMTS. + move => /mapP [p IN EQ]; destruct p as [tsk' R0]; simpl in *; subst tsk'. + apply completion_monotonic with (t0 := job_arrival j0 + R0); first by done. + { + rewrite leq_add2l; apply leq_trans with (n := task_deadline (job_task j0)); + [by apply NOMISS | by apply CONSTR; rewrite FROMTS]. + } + apply BEFOREok with (tsk_other := (job_task j0)); try by done. + apply leq_ltn_trans with (n := t); last by done. + apply leq_trans with (n := job_arrival j0 + task_period (job_task j0)); last by done. + by rewrite leq_add2l; apply leq_trans with (n := task_deadline (job_task j0)); + [by apply NOMISS | apply CONSTR; rewrite FROMTS]. + Qed. + + (* Let's define a predicate to identify the other tasks that are scheduled. *) + Let other_scheduled_task (t: time) (tsk_other: sporadic_task) := + task_is_scheduled job_task sched tsk_other t && + other_task tsk_other. - (* 3) Next, we prove that the sum of the interference of each task is equal + (* 3) Now we prove that, at all times that j is backlogged, the number + of tasks other than tsk that are scheduled is exactly the number + of processors in the system. This is required to prove lemma (4). *) + Lemma bertogna_edf_all_cpus_are_busy: + forall t, + job_arrival j <= t < job_arrival j + R -> + backlogged job_cost sched j t -> + count (other_scheduled_task t) ts = num_cpus. + Proof. + rename H_all_jobs_from_taskset into FROMTS, + H_valid_task_parameters into PARAMS, + H_job_of_tsk into JOBtsk, + H_sporadic_tasks into SPO, + H_tsk_R_in_rt_bounds into INbounds, + H_all_previous_jobs_completed_on_time into BEFOREok, + H_tasks_miss_no_deadlines into NOMISS, + H_rt_bounds_contains_all_tasks into UNZIP, + H_constrained_deadlines into RESTR, + H_work_conserving into WORK. + unfold x, X, total_interference, task_interference. + move => t /andP [LEt LTt] BACK. + eapply platform_cpus_busy_with_interfering_tasks; try (by done); + [ by apply WORK | by apply SPO + | apply PARAMS; rewrite -JOBtsk; apply FROMTS + | by apply JOBtsk | by apply BACK | ]. + { + intros j0 tsk0 TSK0 LE. + cut (tsk0 \in unzip1 rt_bounds = true); last by rewrite UNZIP -TSK0 FROMTS. + move => /mapP [p IN EQ]; destruct p as [tsk' R0]; simpl in *; subst tsk'. + apply completion_monotonic with (t0 := job_arrival j0 + R0); try (by done). + { + rewrite leq_add2l TSK0. + apply leq_trans with (n := task_deadline tsk0); first by apply NOMISS. + by apply RESTR; rewrite -TSK0 FROMTS. + } + { + apply BEFOREok with (tsk_other := tsk0); try (by done). + apply leq_ltn_trans with (n := t); last by done. + apply leq_trans with (n := job_arrival j0 + task_period tsk0); last by done. + rewrite leq_add2l; apply leq_trans with (n := task_deadline tsk0); first by apply NOMISS. + by apply RESTR; rewrite -TSK0 FROMTS. + } + } + Qed. + + (* 4) Next, we prove that the sum of the interference of each task is equal to the total interference multiplied by the number of processors. This - holds because interference only occurs when all processors are busy. *) - Lemma bertogna_edf_all_cpus_busy : - \sum_(tsk_k <- ts_interf) x tsk_k = X * num_cpus. + holds because interference only occurs when all processors are busy. + With this lemma we can relate per-task interference with the total + interference incurred by j (backlogged time). *) + Lemma bertogna_edf_interference_on_all_cpus : + \sum_(tsk_k <- other_tasks) x tsk_k = X * num_cpus. Proof. have DIFFTASK := bertogna_edf_interference_by_different_tasks. rename H_all_jobs_from_taskset into FROMTS, @@ -388,7 +431,7 @@ Module ResponseTimeAnalysisEDF. apply eq_trans with (y := \sum_(cpu < num_cpus) 1); last by simpl_sum_const. apply eq_bigr; intros cpu _. move: (WORK j t BACK cpu) => [j_other /eqP SCHED]; unfold scheduled_on in *. - rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq. + rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq; destruct ts. { rewrite (eq_bigr (fun i => 0)); last by intros i DIFF; rewrite /task_scheduled_on SCHED;apply/eqP;rewrite eqb0 eq_sym. @@ -396,54 +439,25 @@ Module ResponseTimeAnalysisEDF. by unfold task_scheduled_on; rewrite SCHED. } rewrite mem_filter; apply/andP; split; last by apply FROMTS. - unfold jldp_can_interfere_with. apply DIFFTASK with (t := t); [by auto | by done |]. by apply/existsP; exists cpu; apply/eqP. Qed. - (* 4) Show that by the induction hypothesis, all jobs released - before the end of the interval complete by their periods. *) - Lemma bertogna_edf_all_previous_jobs_complete_by_their_period: - forall t (j0: JobIn arr_seq), - t < job_arrival j + R -> - job_arrival j0 + task_period (job_task j0) <= t -> - completed job_cost sched j0 - (job_arrival j0 + task_period (job_task j0)). - Proof. - rename H_rt_bounds_contains_all_tasks into UNZIP, - H_constrained_deadlines into CONSTR, - H_tasks_miss_no_deadlines into NOMISS, - H_all_jobs_from_taskset into FROMTS, - H_all_previous_jobs_completed_on_time into BEFOREok. - intros t j0 LEt LE. - cut ((job_task j0) \in unzip1 rt_bounds = true); last by rewrite UNZIP FROMTS. - move => /mapP [p IN EQ]; destruct p as [tsk' R0]; simpl in *; subst tsk'. - apply completion_monotonic with (t0 := job_arrival j0 + R0); first by done. - { - rewrite leq_add2l; apply leq_trans with (n := task_deadline (job_task j0)); - [by apply NOMISS | by apply CONSTR; rewrite FROMTS]. - } - apply BEFOREok with (tsk_other := (job_task j0)); try by done. - apply leq_ltn_trans with (n := t); last by done. - apply leq_trans with (n := job_arrival j0 + task_period (job_task j0)); last by done. - by rewrite leq_add2l; apply leq_trans with (n := task_deadline (job_task j0)); - [by apply NOMISS | apply CONSTR; rewrite FROMTS]. - Qed. - - (* Let (cardGE delta) be the number of interfering tasks whose interference - is larger than delta. *) - Let cardGE (delta: time) := count (fun i => x i >= delta) ts_interf. + (* Before stating the next lemma, let (num_tasks_exceeding delta) be the + number of interfering tasks whose interference x is larger than delta. *) + Let num_tasks_exceeding delta := count (fun i => x i >= delta) (other_tasks). - (* 5) If there is at least one of such tasks (e.g., cardGE > 0), then the - cumulative interference caused by the complementary set of interfering - tasks fills at least (num_cpus - cardGE) processors. *) - Lemma bertogna_edf_helper_lemma : + (* 5) Now we prove that, for any delta, if (num_task_exceeding delta > 0), then the + cumulative interference caused by the complementary set of interfering tasks fills + the remaining, not-completely-full (num_cpus - num_tasks_exceeding delta) + processors. *) + Lemma bertogna_edf_interference_in_non_full_processors : forall delta, - 0 < cardGE delta < num_cpus -> - \sum_(i <- ts_interf | x i < delta) x i >= delta * (num_cpus - cardGE delta). + 0 < num_tasks_exceeding delta < num_cpus -> + \sum_(i <- other_tasks | x i < delta) x i >= delta * (num_cpus - num_tasks_exceeding delta). Proof. have COMP := bertogna_edf_all_previous_jobs_complete_by_their_period. - have INV := bertogna_edf_scheduling_invariant. + have INV := bertogna_edf_all_cpus_are_busy. rename H_all_jobs_from_taskset into FROMTS, H_valid_task_parameters into PARAMS, H_job_of_tsk into JOBtsk, @@ -460,14 +474,14 @@ Module ResponseTimeAnalysisEDF. set some_interference_A := fun t => has (fun tsk_k => backlogged job_cost sched j t && (x tsk_k >= delta) && - task_is_scheduled job_task sched tsk_k t) ts_interf. + task_is_scheduled job_task sched tsk_k t) other_tasks. set total_interference_B := fun t => backlogged job_cost sched j t * count (fun tsk_k => (x tsk_k < delta) && - task_is_scheduled job_task sched tsk_k t) ts_interf. + task_is_scheduled job_task sched tsk_k t) other_tasks. apply leq_trans with ((\sum_(job_arrival j <= t < job_arrival j + R) - some_interference_A t) * (num_cpus - cardGE delta)). + some_interference_A t) * (num_cpus - num_tasks_exceeding delta)). { rewrite leq_mul2r; apply/orP; right. move: HAS => /hasP HAS; destruct HAS as [tsk_a INa LEa]. @@ -532,13 +546,13 @@ Module ResponseTimeAnalysisEDF. [rewrite mul1n /= | by rewrite has_pred0 //]. destruct (has (fun tsk_k : sporadic_task => (delta <= x tsk_k) && - task_is_scheduled job_task sched tsk_k t) ts_interf) eqn:HAS'; + task_is_scheduled job_task sched tsk_k t) other_tasks) eqn:HAS'; last by done. rewrite mul1n; move: HAS => /hasP [tsk_k INk LEk]. - unfold cardGE. + unfold num_tasks_exceeding. apply leq_trans with (n := num_cpus - count (fun i => (x i >= delta) && - task_is_scheduled job_task sched i t) ts_interf). + task_is_scheduled job_task sched i t) other_tasks). { apply leq_sub2l. rewrite -2!sum1_count big_mkcond /=. @@ -547,11 +561,11 @@ Module ResponseTimeAnalysisEDF. by destruct (task_is_scheduled job_task sched i t); [by rewrite andbT | by rewrite andbF]. } - rewrite -count_filter -[count _ ts_interf]count_filter. + rewrite -count_filter -[count _ other_tasks]count_filter. eapply leq_trans with (n := count (predC (fun tsk => delta <= x tsk)) _); last by apply eq_leq, eq_in_count; red; ins; rewrite ltnNge. rewrite leq_subLR count_predC size_filter. - by apply leq_trans with (n := count (scheduled_task_other_than_tsk t) ts); + by apply leq_trans with (n := count (other_scheduled_task t) ts); [by rewrite INV | by rewrite count_filter]. } { @@ -559,7 +573,7 @@ Module ResponseTimeAnalysisEDF. rewrite exchange_big /=; apply leq_sum; intros t _. destruct (backlogged job_cost sched j t) eqn:BACK; last by ins. rewrite mul1n -sum1_count. - rewrite big_mkcond [\sum_(i <- ts_interf | _ < _) _]big_mkcond /=. + rewrite big_mkcond [\sum_(i <- other_tasks | _ < _) _]big_mkcond /=. apply leq_sum_seq; move => tsk_k IN _. destruct (x tsk_k < delta); [rewrite andTb | by rewrite andFb]. destruct (task_is_scheduled job_task sched tsk_k t) eqn:SCHED; last by done. @@ -568,13 +582,13 @@ Module ResponseTimeAnalysisEDF. } Qed. - (* 6) Next, we prove that, for any interval delta, if the sum of per-task - interference exceeds delta * num_cpus, the same applies for the - sum of the minimum between the interference and delta. *) + (* 6) Based on lemma (5), we prove that, for any interval delta, if the sum of per-task + interference exceeds (delta * num_cpus), the same applies for the + sum of the minimum of the interference and delta. *) Lemma bertogna_edf_minimum_exceeds_interference : forall delta, - \sum_(tsk_k <- ts_interf) x tsk_k >= delta * num_cpus -> - \sum_(tsk_k <- ts_interf) minn (x tsk_k) delta >= + \sum_(tsk_k <- other_tasks) x tsk_k >= delta * num_cpus -> + \sum_(tsk_k <- other_tasks) minn (x tsk_k) delta >= delta * num_cpus. Proof. intros delta SUMLESS. @@ -588,64 +602,63 @@ Module ResponseTimeAnalysisEDF. [| by red; ins; rewrite ltnNge | by intros i COND; rewrite -ltnNge in COND; rewrite COND]. - (* Case 1: cardGE = 0 *) - destruct (~~ has (fun i => delta <= x i) ts_interf) eqn:HASa. + (* Case 1: num_tasks_exceeding = 0 *) + destruct (~~ has (fun i => delta <= x i) other_tasks) eqn:HASa. { rewrite [\sum_(_ <- _ | _ <= _) _]big_hasC; last by apply HASa. rewrite big_seq_cond; move: HASa => /hasPn HASa. - rewrite add0n (eq_bigl (fun i => (i \in ts_interf) && true)); - last by red; intros tsk_k; destruct (tsk_k \in ts_interf) eqn:INk; + rewrite add0n (eq_bigl (fun i => (i \in other_tasks) && true)); + last by red; intros tsk_k; destruct (tsk_k \in other_tasks) eqn:INk; [by rewrite andTb ltnNge; apply HASa | by rewrite andFb]. by rewrite -big_seq_cond. } apply negbFE in HASa. - (* Case 2: cardGE >= num_cpus *) - destruct (cardGE delta >= num_cpus) eqn:CARD. + (* Case 2: num_tasks_exceeding >= num_cpus *) + destruct (num_tasks_exceeding delta >= num_cpus) eqn:CARD. { - apply leq_trans with (delta * cardGE delta); + apply leq_trans with (delta * num_tasks_exceeding delta); first by rewrite leq_mul2l; apply/orP; right. - unfold cardGE; rewrite -sum1_count big_distrr /=. + unfold num_tasks_exceeding; rewrite -sum1_count big_distrr /=. rewrite -[\sum_(_ <- _ | _) _]addn0. by apply leq_add; [by apply leq_sum; ins; rewrite muln1|by ins]. } apply negbT in CARD; rewrite -ltnNge in CARD. - (* Case 3: cardGE < num_cpus *) - rewrite big_const_seq iter_addn addn0; fold cardGE. - apply leq_trans with (n := delta * cardGE delta + - delta * (num_cpus - cardGE delta)); + (* Case 3: num_tasks_exceeding < num_cpus *) + rewrite big_const_seq iter_addn addn0; fold num_tasks_exceeding. + apply leq_trans with (n := delta * num_tasks_exceeding delta + + delta * (num_cpus - num_tasks_exceeding delta)); first by rewrite -mulnDr subnKC //; apply ltnW. - by rewrite leq_add2l; apply bertogna_edf_helper_lemma; apply/andP; split; - first by rewrite -has_count. + rewrite leq_add2l; apply bertogna_edf_interference_in_non_full_processors. + by apply/andP; split; first by rewrite -has_count. Qed. - (* 7) Now, we prove that the Bertogna's interference bound - is not enough to cover the sum of the "minimum" term over - all tasks (artifact of the proof by contradiction). *) + (* 7) Next, using lemmas (0), (4) and (6) we prove that the reduction-based + interference bound is not enough to cover the sum of the minima over all tasks + (artifact of the proof by contradiction). *) Lemma bertogna_edf_sum_exceeds_total_interference: - \sum_((tsk_other, R_other) <- rt_bounds | jldp_can_interfere_with tsk tsk_other) + \sum_((tsk_other, R_other) <- rt_bounds | other_task tsk_other) minn (x tsk_other) (R - task_cost tsk + 1) > I tsk R. Proof. have GE_COST := bertogna_edf_R_other_ge_cost. have EXCEEDS := bertogna_edf_minimum_exceeds_interference. - have ALLBUSY := bertogna_edf_all_cpus_busy. + have ALLBUSY := bertogna_edf_interference_on_all_cpus. have TOOMUCH := bertogna_edf_too_much_interference. rename H_rt_bounds_contains_all_tasks into UNZIP, H_response_time_is_fixed_point into REC. - apply leq_trans with (n := \sum_(tsk_other <- ts_interf) minn (x tsk_other) (R - task_cost tsk + 1)); + apply leq_trans with (n := \sum_(tsk_other <- other_tasks) minn (x tsk_other) (R - task_cost tsk + 1)); last first. { rewrite (eq_bigr (fun i => minn (x (fst i)) (R - task_cost tsk + 1))); last by ins; destruct i. move: UNZIP => UNZIP. - assert (FILTER: filter (jldp_can_interfere_with tsk) (unzip1 rt_bounds) = - filter (jldp_can_interfere_with tsk) ts). + assert (FILTER: filter other_task (unzip1 rt_bounds) = + filter other_task ts). by f_equal. - unfold ts_interf; rewrite -FILTER; clear FILTER. + unfold other_tasks; rewrite -FILTER; clear FILTER. rewrite -[\sum_(_ <- rt_bounds | _)_]big_filter. assert (SUBST: [seq i <- rt_bounds - | let '(tsk_other, _) := i in - jldp_can_interfere_with tsk tsk_other] = [seq i <- rt_bounds - | jldp_can_interfere_with tsk (fst i)]). + | let '(tsk_other, _) := i in other_task tsk_other] = + [seq i <- rt_bounds | other_task (fst i)]). { by apply eq_filter; red; intro i; destruct i. } rewrite SUBST; clear SUBST. @@ -664,9 +677,11 @@ Module ResponseTimeAnalysisEDF. by rewrite leq_mul2r; apply/orP; right; apply TOOMUCH. Qed. - (* 8) After concluding that the sum of the minimum exceeds (R - e_i + 1), - we prove that there exists a tuple (tsk_k, R_k) such that - min (x_k, R - e_i + 1) > min (W_k, edf_bound, R - e_i + 1). *) + (* 8) After concluding that the sum of the minima exceeds (R - e_i + 1), + we prove that there exists a tuple (tsk_k, R_k) that satisfies + min (x_k, R - e_i + 1) > min (W_k, I_edf, R - e_i + 1). + This implies that either x_k > W_k or x_k > I_edf, which is a contradiction, + since both W_k and I_edf are valid task interference bounds. *) Lemma bertogna_edf_exists_task_that_exceeds_bound : exists tsk_other R_other, (tsk_other, R_other) \in rt_bounds /\ @@ -678,7 +693,7 @@ Module ResponseTimeAnalysisEDF. rename H_rt_bounds_contains_all_tasks into UNZIP. assert (HAS: has (fun tup : task_with_response_time => let (tsk_other, R_other) := tup in - (tsk_other \in ts) && jldp_can_interfere_with tsk tsk_other && + (tsk_other \in ts) && other_task tsk_other && (minn (x tsk_other) (R - task_cost tsk + 1) > interference_bound tsk_other R_other)) rt_bounds). diff --git a/analysis/basic/bertogna_fp_comp.v b/analysis/basic/bertogna_fp_comp.v index dfa823f813dfcdf1e1ed3fd1a5abd4ba5a3c62e6..7208a9ee6eb20055852dc3077187d9a6f66444c1 100644 --- a/analysis/basic/bertogna_fp_comp.v +++ b/analysis/basic/bertogna_fp_comp.v @@ -48,7 +48,7 @@ Module ResponseTimeIterationFP. (fun t => task_cost tsk + div_floor (total_interference_bound_fp task_cost task_period tsk - R_prev t higher_priority) + R_prev t) num_cpus) (task_cost tsk). @@ -75,12 +75,12 @@ Module ResponseTimeIterationFP. (* The response-time analysis for a given task set is defined as a left-fold (reduce) based on the function above. This either returns a list of task and response-time bounds, or None. *) - Definition fp_claimed_bounds (ts: taskset_of sporadic_task) := + Definition fp_claimed_bounds (ts: seq sporadic_task) := foldl fp_bound_of_task (Some [::]) ts. (* The schedulability test simply checks if we got a list of response-time bounds (i.e., if the computation did not fail). *) - Definition fp_schedulable (ts: taskset_of sporadic_task) := + Definition fp_schedulable (ts: seq sporadic_task) := fp_claimed_bounds ts != None. (* In the following section, we prove several helper lemmas about the @@ -258,7 +258,7 @@ Module ResponseTimeIterationFP. forall tsk rt_bounds, task_cost tsk + div_floor (total_interference_bound_fp task_cost task_period tsk rt_bounds - (per_task_rta tsk rt_bounds (max_steps tsk)) higher_priority) num_cpus + (per_task_rta tsk rt_bounds (max_steps tsk))) num_cpus = per_task_rta tsk rt_bounds (max_steps tsk).+1. Proof. by done. @@ -273,14 +273,13 @@ Module ResponseTimeIterationFP. (* Consider a list of previous tasks and a task tsk to be analyzed. *) Variable ts: taskset_of sporadic_task. - (* Assume that the task set doesn't contain duplicates and is sorted by priority, ... *) - Hypothesis H_task_set_is_a_set: uniq ts. + (* Assume that the task set is sorted by unique priorities, ... *) Hypothesis H_task_set_is_sorted: sorted higher_priority ts. + Hypothesis H_task_set_has_unique_priorities: + FP_is_antisymmetric_over_task_set higher_priority ts. - (* ...the priority order is strict (<), ...*) - Hypothesis H_priority_irreflexive: irreflexive higher_priority. - Hypothesis H_priority_transitive: transitive higher_priority. - Hypothesis H_priority_antissymetric: antisymmetric higher_priority. + (* ...the priority order is transitive, ...*) + Hypothesis H_priority_transitive: FP_is_transitive higher_priority. (* ... and that the response-time analysis succeeds. *) Variable hp_bounds: seq task_with_response_time. @@ -290,71 +289,30 @@ Module ResponseTimeIterationFP. (* Let's refer to tasks by index. *) Variable elem: sporadic_task. Let TASK := nth elem ts. - - (* Then, the tasks in the prefix of fp_claimed_bounds are exactly interfering tasks - under FP scheduling.*) - Lemma fp_claimed_bounds_interf: - forall idx, + + (* We prove that higher-priority tasks have smaller index. *) + Lemma fp_claimed_bounds_hp_tasks_have_smaller_index : + forall hp_idx idx, + hp_idx < size ts -> idx < size ts -> - [seq tsk_hp <- ts | fp_can_interfere_with higher_priority (TASK idx) tsk_hp] = take idx ts. + hp_idx != idx -> + higher_priority (TASK hp_idx) (TASK idx) -> + hp_idx < idx. Proof. - rename H_task_set_is_sorted into SORT, - H_task_set_is_a_set into UNIQ, - H_priority_antissymetric into ANTI, - H_priority_irreflexive into IRR. - induction idx. - { - intros LT. - destruct ts as [| tsk0 ts']; [by done | simpl in SORT]. - unfold fp_can_interfere_with; rewrite /= eq_refl andbF. - apply eq_trans with (y := filter pred0 ts'); - last by apply filter_pred0. - apply eq_in_filter; red; intros x INx; rewrite /TASK /=. - destruct (x != tsk0) eqn:SAME; rewrite ?andbT ?andbF //. - apply negbTE; apply/negP; unfold not; intro HP. - move: SAME => /eqP SAME; apply SAME; clear SAME. - apply ANTI; apply/andP; split; first by done. - apply order_path_min in SORT; last by done. - by move: SORT => /allP SORT; apply SORT. - } - { - intros LT. - generalize LT; intro LT'; apply ltSnm in LT. - feed IHidx; first by done. - rewrite -filter_idx_le_takeS //. - apply eq_in_filter; red; intros x INx. - unfold fp_can_interfere_with. - generalize INx; intro SUBST; apply nth_index with (x0 := elem) in SUBST. - rewrite -SUBST; clear SUBST. - rewrite index_uniq; [ | by rewrite index_mem | by done]. - apply/idP/idP. - { - move => /andP [HP DIFF]. - unfold TASK in *. - apply sorted_uniq_rel_implies_le_idx in HP; try (by done); - last by rewrite index_mem. - by rewrite leq_eqVlt in HP; move: HP => /orP [/eqP SAME | LESS]; - first by rewrite SAME eq_refl in DIFF. - } - { - intros LEidx; apply/andP; split; - first by apply sorted_lt_idx_implies_rel. - apply/eqP; red; intro BUG. - eapply f_equal with (f := fun x => index x ts) in BUG. - rewrite nth_index in BUG; last by done. - rewrite BUG in LEidx. - by rewrite index_uniq // ltnn in LEidx. - } - } + unfold TASK; clear TASK. + rename ts into ts'; destruct ts' as [ts UNIQ]; simpl in *. + intros hp_idx idx LThp LT NEQ HP. + rewrite ltn_neqAle; apply/andP; split; first by done. + by apply sorted_rel_implies_le_idx with (leT := higher_priority) (s := ts) (x0 := elem). Qed. - + End HighPriorityTasks. (* In this section, we show that the fixed-point iteration converges. *) Section Convergence. - (* Consider any set of higher-priority tasks. *) - Variable ts_hp: taskset_of sporadic_task. + (* Consider any list of higher-priority tasks. *) + Variable ts_hp: list sporadic_task. (* Assume that the response-time analysis succeeds for the higher-priority tasks. *) Variable rt_bounds: seq task_with_response_time. @@ -384,15 +342,14 @@ Module ResponseTimeIterationFP. apply fun_mon_iter_mon; [by ins | by ins; apply leq_addr |]. clear LEx x1 x2; intros x1 x2 LEx. unfold div_floor, total_interference_bound_fp. - rewrite big_seq_cond [\sum_(i <- _ | let '(tsk_other, _) := i in - _ && (tsk_other != tsk))_]big_seq_cond. - rewrite leq_add2l leq_div2r // leq_sum //. - - intros i; destruct (i \in rt_bounds) eqn:HP; last by rewrite andFb. - destruct i as [i R]; intros _. - have GE_COST := fp_claimed_bounds_ge_cost ts_hp rt_bounds i R SOME. + rewrite big_seq_cond. + rewrite [\sum_(_ <- _ | true) _]big_seq_cond. + rewrite leq_add2l leq_div2r //. + apply leq_sum; move => i /andP [IN _]. + destruct i as [i R]. + have GE_COST := fp_claimed_bounds_ge_cost ts_hp rt_bounds i R SOME IN. have UNZIP := fp_claimed_bounds_unzip ts_hp rt_bounds SOME. - assert (IN: i \in ts_hp). + assert (IN': i \in ts_hp). { by rewrite -UNZIP; apply/mapP; exists (i,R). } @@ -400,8 +357,8 @@ Module ResponseTimeIterationFP. rewrite leq_min; apply/andP; split. { apply leq_trans with (n := W task_cost task_period i R x1); first by apply geq_minl. - exploit (VALID i); [by rewrite mem_rcons in_cons IN orbT | ins; des]. - by apply W_monotonic; try by ins; apply GE_COST. + exploit (VALID i); [by rewrite mem_rcons in_cons IN' orbT | ins; des]. + by apply W_monotonic; try (by done); apply GE_COST. } { apply leq_trans with (n := x1 - task_cost tsk + 1); first by apply geq_minr. @@ -514,28 +471,22 @@ Module ResponseTimeIterationFP. (* Consider a task set ts. *) Variable ts: taskset_of sporadic_task. - (* Assume that higher_priority is a total strict order (<). - TODO: it doesn't have to be total over the universe of tasks, - but only within the task set. However, to weaken this hypothesis - we need to re-prove some lemmas from ssreflect. *) - Hypothesis H_irreflexive: irreflexive higher_priority. - Hypothesis H_transitive: transitive higher_priority. - Hypothesis H_unique_priorities: antisymmetric higher_priority. - Hypothesis H_total: total higher_priority. - - (* Assume the task set has no duplicates, ... *) - Hypothesis H_ts_is_a_set: uniq ts. - - (* ...all tasks have valid parameters, ... *) + (* Assume that all tasks have valid parameters, ... *) Hypothesis H_valid_task_parameters: valid_sporadic_taskset task_cost task_period task_deadline ts. - (* ...constrained deadlines, ...*) + (* ...constrained deadlines.*) Hypothesis H_constrained_deadlines: forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk. - (* ...and tasks are ordered by increasing priorities. *) - Hypothesis H_sorted_ts: sorted higher_priority ts. + (* Assume that the task set is totally ordered by unique priorities, + and that the priority order is transitive. *) + Hypothesis H_task_set_is_sorted: sorted higher_priority ts. + Hypothesis H_task_set_has_unique_priorities: + FP_is_antisymmetric_over_task_set higher_priority ts. + Hypothesis H_priority_is_total: + FP_is_total_over_task_set higher_priority ts. + Hypothesis H_priority_transitive: FP_is_transitive higher_priority. (* Next, consider any arrival sequence such that...*) Context {arr_seq: arrival_sequence Job}. @@ -552,11 +503,10 @@ Module ResponseTimeIterationFP. (* ... and satisfy the sporadic task model.*) Hypothesis H_sporadic_tasks: sporadic_task_model task_period arr_seq job_task. - + (* Then, consider any platform with at least one CPU such that...*) Variable sched: schedule num_cpus arr_seq. - Hypothesis H_at_least_one_cpu : - num_cpus > 0. + Hypothesis H_at_least_one_cpu: num_cpus > 0. (* ...jobs only execute after they arrived and no longer than their execution costs. *) @@ -658,11 +608,37 @@ Module ResponseTimeIterationFP. (higher_eq_priority := higher_priority); try (by done). { cut (NTH idx \in hp_bounds = true); [intros IN | by apply mem_nth]. - by rewrite -UNZIP; apply/mapP; exists (TASK idx, RESP idx); rewrite PAIR. + by rewrite set_mem -UNZIP; apply/mapP; exists (TASK idx, RESP idx); rewrite PAIR. } { - unfold unzip1 in *; rewrite map_take UNZIP SUBST //. - by apply fp_claimed_bounds_interf with (hp_bounds := hp_bounds); rewrite -?SIZE. + intros hp_tsk IN INTERF. + exists (RESP (index hp_tsk ts)). + move: (IN) => INDEX; apply nth_index with (x0 := tsk) in INDEX. + rewrite -{1}[hp_tsk]INDEX -SUBST; last by rewrite SIZE index_mem. + assert (UNIQ: uniq hp_bounds). + { + apply map_uniq with (f := fst); unfold unzip1 in *; rewrite UNZIP. + by destruct ts. + } + rewrite -filter_idx_lt_take //. + { + rewrite PAIR mem_filter; apply/andP; split; + last by apply mem_nth; rewrite SIZE index_mem. + { + rewrite /NTH index_uniq; [| by rewrite SIZE index_mem | by done ]. + { + move: INTERF => /andP [HP NEQ]. + apply fp_claimed_bounds_hp_tasks_have_smaller_index with + (ts := ts) (elem := tsk) (hp_bounds := hp_bounds); + try (by done); + [by rewrite index_mem | by rewrite -SIZE | | by rewrite INDEX -SUBST]. + apply/eqP; intro BUG; subst idx. + rewrite SUBST -{1}INDEX in NEQ; + first by rewrite eq_refl in NEQ. + by rewrite SIZE index_mem INDEX. + } + } + } } { intros hp_tsk R_hp IN; apply mem_take in IN. @@ -701,7 +677,7 @@ Module ResponseTimeIterationFP. feed (UNZIP rt_bounds); first by done. assert (EX: exists R, (tsk, R) \in rt_bounds). { - rewrite -UNZIP in INtsk; move: INtsk => /mapP EX. + rewrite set_mem -UNZIP in INtsk; move: INtsk => /mapP EX. by destruct EX as [p]; destruct p as [tsk' R]; simpl in *; subst tsk'; exists R. } des. exploit (RLIST tsk R); [by ins | by apply JOBtsk | intro COMPLETED]. diff --git a/analysis/basic/bertogna_fp_theory.v b/analysis/basic/bertogna_fp_theory.v index 250e35f2efb9053d3e0b9d49a694c2b3156c1f4f..637f1f5f439908a43e8cdfa9150dc698fc5022d9 100644 --- a/analysis/basic/bertogna_fp_theory.v +++ b/analysis/basic/bertogna_fp_theory.v @@ -1,6 +1,6 @@ Require Import rt.util.all. Require Import rt.model.basic.task rt.model.basic.job rt.model.basic.task_arrival - rt.model.basic.schedule rt.model.basic.platform rt.model.basic.platform_fp + rt.model.basic.schedule rt.model.basic.platform rt.model.basic.constrained_deadlines rt.model.basic.workload rt.model.basic.schedulability rt.model.basic.priority rt.model.basic.response_time rt.model.basic.interference. Require Import rt.analysis.basic.workload_bound rt.analysis.basic.interference_bound_fp. @@ -8,9 +8,14 @@ From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop d Module ResponseTimeAnalysisFP. - Export Job SporadicTaskset ScheduleOfSporadicTask Workload Interference InterferenceBoundFP - Platform PlatformFP Schedulability ResponseTime Priority SporadicTaskArrival WorkloadBound. + Export Job SporadicTaskset ScheduleOfSporadicTask Workload Interference + InterferenceBoundFP Platform Schedulability ResponseTime + Priority SporadicTaskArrival WorkloadBound ConstrainedDeadlines. + (* In this section, we prove that any fixed point in Bertogna and + Cirinei's RTA for FP scheduling is a safe response-time bound. + This analysis can be found in Chapter 18.2 of Baruah et al.'s + book Multiprocessor Scheduling for Real-time Systems. *) Section ResponseTimeBound. Context {sporadic_task: eqType}. @@ -33,46 +38,56 @@ Module ResponseTimeAnalysisFP. forall (j: JobIn arr_seq), valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j. - (* Consider any schedule such that...*) + (* Assume that we have a task set where all tasks have valid + parameters and constrained deadlines, ... *) + Variable ts: taskset_of sporadic_task. + Hypothesis H_valid_task_parameters: + valid_sporadic_taskset task_cost task_period task_deadline ts. + Hypothesis H_constrained_deadlines: + forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk. + + (* ... and that all jobs in the arrival sequence come from the task set. *) + Hypothesis H_all_jobs_from_taskset: + forall (j: JobIn arr_seq), job_task j \in ts. + + (* Next, consider any schedule such that...*) Variable num_cpus: nat. Variable sched: schedule num_cpus arr_seq. - (* ...jobs do not execute before their arrival times nor longer - than their execution costs. *) + (* ...jobs are sequential and do not execute before their + arrival times nor longer than their execution costs. *) + Hypothesis H_sequential_jobs: sequential_jobs sched. Hypothesis H_jobs_must_arrive_to_execute: jobs_must_arrive_to_execute sched. Hypothesis H_completed_jobs_dont_execute: completed_jobs_dont_execute job_cost sched. - (* Also assume that jobs are sequential and that there exists - at least one processor. *) - Hypothesis H_sequential_jobs: sequential_jobs sched. + (* Assume that there exists at least one processor. *) Hypothesis H_at_least_one_cpu: num_cpus > 0. - (* Consider a task set ts with no duplicates where all jobs - come from the task set and all tasks have valid parameters - and constrained deadlines. *) - Variable ts: taskset_of sporadic_task. - Hypothesis H_ts_is_a_set: uniq ts. - Hypothesis H_all_jobs_from_taskset: - forall (j: JobIn arr_seq), job_task j \in ts. - Hypothesis H_valid_task_parameters: - valid_sporadic_taskset task_cost task_period task_deadline ts. - Hypothesis H_constrained_deadlines: - forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk. + (* Consider a given FP policy, ... *) + Variable higher_eq_priority: FP_policy sporadic_task. - (* Next, consider a task tsk that is to be analyzed. *) - Variable tsk: sporadic_task. - Hypothesis task_in_ts: tsk \in ts. + (* ... and assume that the schedule is a work-conserving + schedule that enforces this policy. *) + Hypothesis H_work_conserving: work_conserving job_cost sched. + Hypothesis H_enforces_FP_policy: + enforces_FP_policy job_cost job_task sched higher_eq_priority. + (* Let's define some local names to avoid passing many parameters. *) Let no_deadline_is_missed_by_tsk (tsk: sporadic_task) := task_misses_no_deadline job_cost job_deadline job_task sched tsk. Let response_time_bounded_by (tsk: sporadic_task) := is_response_time_bound_of_task job_cost job_task tsk sched. - (* Assume a given FP policy. *) - Variable higher_eq_priority: FP_policy sporadic_task. - Let can_interfere_with_tsk := fp_can_interfere_with higher_eq_priority tsk. + (* Next, we consider the response-time recurrence. + Let tsk be a task in ts that is to be analyzed. *) + Variable tsk: sporadic_task. + Hypothesis task_in_ts: tsk \in ts. + + (* Let is_hp_task denote whether a task is a higher-priority task + (with respect to tsk). *) + Let is_hp_task := higher_priority_task higher_eq_priority tsk. (* Assume a response-time bound is known... *) Let task_with_response_time := (sporadic_task * time)%type. @@ -82,39 +97,36 @@ Module ResponseTimeAnalysisFP. (hp_tsk, R) \in hp_bounds -> response_time_bounded_by hp_tsk R. - (* ... for any task in ts that interferes with tsk. *) + (* ... for every higher-priority task. *) Hypothesis H_hp_bounds_has_interfering_tasks: - [seq tsk_hp <- ts | can_interfere_with_tsk tsk_hp] = unzip1 hp_bounds. + forall hp_tsk, + hp_tsk \in ts -> + is_hp_task hp_tsk -> + exists R, (hp_tsk, R) \in hp_bounds. (* Assume that the response-time bounds are larger than task costs. *) Hypothesis H_response_time_bounds_ge_cost: forall hp_tsk R, (hp_tsk, R) \in hp_bounds -> R >= task_cost hp_tsk. - (* Assume that no deadline is missed by any interfering task, i.e., - response-time bound R_other <= deadline. *) + (* Assume that no deadline is missed by any higher-priority task. *) Hypothesis H_interfering_tasks_miss_no_deadlines: forall hp_tsk R, (hp_tsk, R) \in hp_bounds -> R <= task_deadline hp_tsk. - (* Assume that the scheduler is work-conserving and enforces the FP policy. *) - Hypothesis H_work_conserving: work_conserving job_cost sched. - Hypothesis H_enforces_FP_policy: - enforces_FP_policy job_cost job_task sched higher_eq_priority. - (* Let R be the fixed point of Bertogna and Cirinei's recurrence, ...*) Variable R: time. Hypothesis H_response_time_recurrence_holds : R = task_cost tsk + div_floor - (total_interference_bound_fp task_cost task_period tsk hp_bounds R higher_eq_priority) + (total_interference_bound_fp task_cost task_period tsk hp_bounds R) num_cpus. (* ... and assume that R is no larger than the deadline of tsk.*) Hypothesis H_response_time_no_larger_than_deadline: R <= task_deadline tsk. - (* In order to prove that R is a response-time bound, we first present some lemmas. *) + (* In order to prove that R is a response-time bound, we first provide some lemmas. *) Section Lemmas. (* Consider any job j of tsk. *) @@ -134,17 +146,18 @@ Module ResponseTimeAnalysisFP. task_interference job_cost job_task sched j tsk_other (job_arrival j) (job_arrival j + R). - (* and X the total interference incurred by job j due to any task. *) + (* ...and X the total interference incurred by job j due to any task. *) Let X := total_interference job_cost sched j (job_arrival j) (job_arrival j + R). (* Recall Bertogna and Cirinei's workload bound. *) Let workload_bound (tsk_other: sporadic_task) (R_other: time) := W task_cost task_period tsk_other R_other R. - (* Also, let ts_interf be the subset of tasks that interfere with tsk. *) - Let ts_interf := [seq tsk_other <- ts | can_interfere_with_tsk tsk_other]. + (* Let hp_tasks denote the set of higher-priority tasks. *) + Let hp_tasks := [seq tsk_other <- ts | is_hp_task tsk_other]. - Section LemmasAboutInterferingTasks. + (* Now we establish results about the higher-priority tasks. *) + Section LemmasAboutHPTasks. (* Let (tsk_other, R_other) be any pair of higher-priority task and response-time bound computed in previous iterations. *) @@ -152,30 +165,6 @@ Module ResponseTimeAnalysisFP. Variable R_other: time. Hypothesis H_response_time_of_tsk_other: (tsk_other, R_other) \in hp_bounds. - (* Note that tsk_other is in task set ts ...*) - Lemma bertogna_fp_tsk_other_in_ts: tsk_other \in ts. - Proof. - rename H_hp_bounds_has_interfering_tasks into UNZIP, - H_response_time_of_tsk_other into INbounds. - move: UNZIP => UNZIP. - cut (tsk_other \in ts_interf = true); - first by rewrite mem_filter; move => /andP [_ IN]. - unfold ts_interf; rewrite UNZIP. - by apply/mapP; exists (tsk_other, R_other). - Qed. - - (*... and interferes with tsk. *) - Lemma bertogna_fp_tsk_other_interferes: can_interfere_with_tsk tsk_other. - Proof. - rename H_hp_bounds_has_interfering_tasks into UNZIP, - H_response_time_of_tsk_other into INbounds. - move: UNZIP => UNZIP. - cut (tsk_other \in ts_interf = true); - first by rewrite mem_filter; move => /andP [INTERF _]. - unfold ts_interf; rewrite UNZIP. - by apply/mapP; exists (tsk_other, R_other). - Qed. - (* Since tsk_other cannot interfere more than it executes, we show that the interference caused by tsk_other is no larger than workload bound W. *) Lemma bertogna_fp_workload_bounds_interference : @@ -184,13 +173,34 @@ Module ResponseTimeAnalysisFP. unfold response_time_bounded_by, is_response_time_bound_of_task, completed, completed_jobs_dont_execute, valid_sporadic_job in *. rename H_valid_job_parameters into PARAMS, + H_all_jobs_from_taskset into FROMTS, H_valid_task_parameters into TASK_PARAMS, H_constrained_deadlines into RESTR, H_response_time_of_interfering_tasks_is_known into RESP, H_interfering_tasks_miss_no_deadlines into NOMISS, H_response_time_bounds_ge_cost into GE_COST. unfold x, workload_bound. - have INts := bertogna_fp_tsk_other_in_ts. + destruct ([exists t: 'I_(job_arrival j + R), + task_is_scheduled job_task sched tsk_other t]) eqn: SCHED; + last first. + { + apply negbT in SCHED; rewrite negb_exists in SCHED. + move: SCHED => /forallP SCHED. + apply leq_trans with (n := 0); last by done. + apply leq_trans with (n := \sum_(job_arrival j <= t < job_arrival j + R) 0); + last by rewrite big1. + apply leq_sum_nat; move => i /andP [_ LTi] _. + specialize (SCHED (Ordinal LTi)). + rewrite negb_exists in SCHED; move: SCHED => /forallP SCHED. + rewrite big1 //; intros cpu _. + specialize (SCHED cpu); apply negbTE in SCHED. + by rewrite SCHED andbF. + } + move: SCHED => /existsP [t /existsP [cpu SCHED]]. + unfold task_scheduled_on in SCHED. + destruct (sched cpu t) as [j0 |]; last by done. + assert (INts: tsk_other \in ts). + by move: SCHED => /eqP <-; rewrite FROMTS. apply leq_trans with (n := workload job_task sched tsk_other (job_arrival j) (job_arrival j + R)); first by apply task_interference_le_workload. @@ -204,7 +214,7 @@ Module ResponseTimeAnalysisFP. | by ins; apply RESP with (hp_tsk := tsk_other)]. Qed. - End LemmasAboutInterferingTasks. + End LemmasAboutHPTasks. (* Next we prove some lemmas that help to derive a contradiction.*) Section DerivingContradiction. @@ -254,68 +264,11 @@ Module ResponseTimeAnalysisFP. by apply completion_monotonic with (t := i); try (by done); apply ltnW. Qed. - Let scheduled_task_other_than_tsk (t: time) (tsk_other: sporadic_task) := - task_is_scheduled job_task sched tsk_other t && - can_interfere_with_tsk tsk_other. - - (* 1) At all times that j is backlogged, all processors are busy with other tasks. *) - Lemma bertogna_fp_scheduling_invariant: - forall t, - job_arrival j <= t < job_arrival j + R -> - backlogged job_cost sched j t -> - count (scheduled_task_other_than_tsk t) ts = num_cpus. - Proof. - rename H_valid_task_parameters into PARAMS, - H_all_jobs_from_taskset into FROMTS, - H_job_of_tsk into JOBtsk, - H_sporadic_tasks into SPO, - H_valid_job_parameters into JOBPARAMS, - H_constrained_deadlines into RESTR, - H_hp_bounds_has_interfering_tasks into UNZIP, - H_interfering_tasks_miss_no_deadlines into NOMISS, - H_response_time_of_interfering_tasks_is_known into PREV. - unfold sporadic_task_model, is_response_time_bound_of_task in *. - move => t /andP [LEt LTt] BACK. - apply platform_fp_cpus_busy_with_interfering_tasks with (task_cost0 := task_cost) - (task_period0 := task_period) (task_deadline0 := task_deadline) (job_task0 := job_task) - (ts0 := ts) (tsk0 := tsk) (higher_eq_priority0 := higher_eq_priority) in BACK; - try (by done); first by apply PARAMS. - { - apply leq_trans with (n := job_arrival j + R); first by done. - rewrite leq_add2l. - by apply leq_trans with (n := task_deadline tsk); last by apply RESTR. - } - { - intros j_other tsk_other JOBother INTERF. - move: UNZIP => UNZIP. - cut (tsk_other \in unzip1 hp_bounds = true); last first. - { - rewrite -UNZIP mem_filter; apply/andP; split; first by done. - by rewrite -JOBother; apply FROMTS. - } intro IN. - move: IN => /mapP [p IN EQ]; destruct p as [tsk' R']; simpl in *; subst tsk'. - apply completion_monotonic with (t0 := job_arrival j_other + R'); try (by done); - last by apply PREV with (hp_tsk := tsk_other). - { - rewrite leq_add2l. - apply leq_trans with (n := task_deadline tsk_other); first by apply NOMISS. - apply RESTR. - cut (tsk_other \in unzip1 hp_bounds = true); - last by apply/mapP; exists (tsk_other, R'). - by rewrite -UNZIP mem_filter; move => /andP [_ IN']. - } - } - { - ins; apply completion_monotonic with (t0 := job_arrival j0 + R); try (by done); - last by apply H_previous_jobs_of_tsk_completed. - rewrite leq_add2l. - by apply leq_trans with (n := task_deadline tsk); last by apply RESTR. - } - Qed. - - - (* 2) Prove that during the scheduling window of j, any job that is scheduled while j is - backlogged comes from a different task. *) + (* 1) Next, we prove that during the scheduling window of j, any job that is + scheduled while j is backlogged comes from a different task. + This follows from the fact that j is the first job not to complete + by its response-time bound, so previous jobs of j's task must have + completed by their periods and cannot be pending. *) Lemma bertogna_fp_interference_by_different_tasks : forall t j_other, job_arrival j <= t < job_arrival j + R -> @@ -365,12 +318,67 @@ Module ResponseTimeAnalysisFP. by red; intros EQtsk; subst j_other; rewrite /backlogged SCHED andbF in BACK. } Qed. + + (* Let's define a predicate to identify the other tasks that are scheduled. *) + Let other_scheduled_task (t: time) (tsk_other: sporadic_task) := + task_is_scheduled job_task sched tsk_other t && + is_hp_task tsk_other. + + (* 2) Now we prove that, at all times that j is backlogged, the number + of tasks other than tsk that are scheduled is exactly the number + of processors in the system. This is required to prove lemma (3). *) + Lemma bertogna_fp_all_cpus_are_busy: + forall t, + job_arrival j <= t < job_arrival j + R -> + backlogged job_cost sched j t -> + count (other_scheduled_task t) ts = num_cpus. + Proof. + rename H_valid_task_parameters into PARAMS, + H_all_jobs_from_taskset into FROMTS, + H_job_of_tsk into JOBtsk, + H_sporadic_tasks into SPO, + H_valid_job_parameters into JOBPARAMS, + H_constrained_deadlines into RESTR, + H_hp_bounds_has_interfering_tasks into HAS, + H_interfering_tasks_miss_no_deadlines into NOMISS, + H_response_time_of_interfering_tasks_is_known into PREV. + unfold sporadic_task_model, is_response_time_bound_of_task in *. + move => t /andP [LEt LTt] BACK. + apply platform_fp_cpus_busy_with_interfering_tasks with (task_cost0 := task_cost) + (task_period0 := task_period) (task_deadline0 := task_deadline) (job_task0 := job_task) + (ts0 := ts) (tsk0 := tsk) (higher_eq_priority0 := higher_eq_priority) in BACK; + try (by done); first by apply PARAMS. + { + apply leq_trans with (n := job_arrival j + R); first by done. + rewrite leq_add2l. + by apply leq_trans with (n := task_deadline tsk); last by apply RESTR. + } + { + intros j_other tsk_other JOBother INTERF. + move: HAS => HAS. + feed (HAS tsk_other); first by rewrite -JOBother FROMTS. + move: (HAS INTERF) => [R' IN]. + apply completion_monotonic with (t0 := job_arrival j_other + R'); try (by done); + last by apply PREV with (hp_tsk := tsk_other). + { + rewrite leq_add2l. + apply leq_trans with (n := task_deadline tsk_other); first by apply NOMISS. + by apply RESTR; rewrite -JOBother FROMTS. + } + } + { + ins; apply completion_monotonic with (t0 := job_arrival j0 + R); try (by done); + last by apply H_previous_jobs_of_tsk_completed. + rewrite leq_add2l. + by apply leq_trans with (n := task_deadline tsk); last by apply RESTR. + } + Qed. - (* 3) Next, we prove that the sum of the interference of each task is equal - to the total interference multiplied by the number of processors. This - holds because interference only occurs when all processors are busy. *) - Lemma bertogna_fp_all_cpus_busy : - \sum_(tsk_k <- ts_interf) x tsk_k = X * num_cpus. + (* 3) Now we prove that, at all times that j is backlogged, the number + of tasks other than tsk that are scheduled is exactly the number + of processors in the system. This is required to prove lemma (4). *) + Lemma bertogna_fp_interference_on_all_cpus : + \sum_(tsk_k <- hp_tasks) x tsk_k = X * num_cpus. Proof. have DIFFTASK := bertogna_fp_interference_by_different_tasks. rename H_work_conserving into WORK, H_enforces_FP_policy into FP, @@ -387,7 +395,7 @@ Module ResponseTimeAnalysisFP. apply eq_trans with (y := \sum_(cpu < num_cpus) 1); last by simpl_sum_const. apply eq_bigr; intros cpu _. move: (WORK j t BACK cpu) => [j_other /eqP SCHED]; unfold scheduled_on in *. - rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq. + rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq; destruct ts. { rewrite (eq_bigr (fun i => 0)); last by intros i DIFF; rewrite /task_scheduled_on SCHED;apply/eqP;rewrite eqb0 eq_sym. @@ -395,7 +403,7 @@ Module ResponseTimeAnalysisFP. by unfold task_scheduled_on; rewrite SCHED. } rewrite mem_filter; apply/andP; split; last by apply FROMTS. - unfold can_interfere_with_tsk, fp_can_interfere_with; apply/andP; split. + apply/andP; split. { rewrite -JOBtsk; apply FP with (t := t); try by done. by apply/existsP; exists cpu; apply/eqP. @@ -404,19 +412,20 @@ Module ResponseTimeAnalysisFP. by apply/existsP; exists cpu; apply/eqP. Qed. - (* Let (cardGE delta) be the number of interfering tasks whose interference - is larger than delta. *) - Let cardGE (delta: time) := count (fun i => x i >= delta) ts_interf. + (* Before stating the next lemma, let (num_tasks_exceeding delta) be the + number of interfering tasks whose interference x is larger than delta. *) + Let num_tasks_exceeding delta := count (fun i => x i >= delta) (hp_tasks). - (* 4) If there is at least one of such tasks (e.g., cardGE > 0), then the - cumulative interference caused by the complementary set of interfering - tasks fills at least (num_cpus - cardGE) processors. *) - Lemma bertogna_fp_helper_lemma : + (* 4) Now we prove that, for any delta, if (num_task_exceeding delta > 0), then the + cumulative interference caused by the complementary set of interfering tasks fills + the remaining, not-completely-full (num_cpus - num_tasks_exceeding delta) + processors. *) + Lemma bertogna_fp_interference_in_non_full_processors : forall delta, - 0 < cardGE delta < num_cpus -> - \sum_(i <- ts_interf | x i < delta) x i >= delta * (num_cpus - cardGE delta). + 0 < num_tasks_exceeding delta < num_cpus -> + \sum_(i <- hp_tasks | x i < delta) x i >= delta * (num_cpus - num_tasks_exceeding delta). Proof. - have INV := bertogna_fp_scheduling_invariant. + have INV := bertogna_fp_all_cpus_are_busy. rename H_all_jobs_from_taskset into FROMTS, H_valid_task_parameters into PARAMS, H_job_of_tsk into JOBtsk, @@ -426,7 +435,7 @@ Module ResponseTimeAnalysisFP. H_constrained_deadlines into CONSTR, H_sequential_jobs into SEQ, H_enforces_FP_policy into FP, - H_hp_bounds_has_interfering_tasks into UNZIP, + H_hp_bounds_has_interfering_tasks into HASHP, H_interfering_tasks_miss_no_deadlines into NOMISSHP. unfold sporadic_task_model in *. move => delta /andP [HAS LT]. @@ -435,14 +444,14 @@ Module ResponseTimeAnalysisFP. set some_interference_A := fun t => has (fun tsk_k => backlogged job_cost sched j t && (x tsk_k >= delta) && - task_is_scheduled job_task sched tsk_k t) ts_interf. + task_is_scheduled job_task sched tsk_k t) hp_tasks. set total_interference_B := fun t => backlogged job_cost sched j t * count (fun tsk_k => (x tsk_k < delta) && - task_is_scheduled job_task sched tsk_k t) ts_interf. + task_is_scheduled job_task sched tsk_k t) hp_tasks. apply leq_trans with ((\sum_(job_arrival j <= t < job_arrival j + R) - some_interference_A t) * (num_cpus - cardGE delta)). + some_interference_A t) * (num_cpus - num_tasks_exceeding delta)). { rewrite leq_mul2r; apply/orP; right. move: HAS => /hasP HAS; destruct HAS as [tsk_a INa LEa]. @@ -510,7 +519,7 @@ Module ResponseTimeAnalysisFP. by rewrite -SAMEjob; apply/existsP; exists cpu; apply/eqP. } { - assert (INTERF: fp_can_interfere_with higher_eq_priority tsk (job_task j1)). + assert (INTERF: is_hp_task (job_task j1)). { apply/andP; split; last by rewrite SAMEtsk. rewrite -JOBtsk; apply FP with (t := t); first by done. @@ -522,11 +531,10 @@ Module ResponseTimeAnalysisFP. rewrite ?JOBtsk ?SAMEtsk //. { intros j0 tsk0 JOB0 INTERF0. - assert (IN: tsk0 \in (unzip1 hp_bounds)). - by rewrite -UNZIP mem_filter; apply/andP; split; last by rewrite -JOB0 FROMTS. - move: IN => /mapP [p IN EQ]; destruct p as [tsk0' R0]; simpl in *; subst tsk0'. + feed (HASHP tsk0); first by rewrite -JOB0 FROMTS. + move: (HASHP INTERF0) => [R0 IN0]. apply completion_monotonic with (t0 := job_arrival j0 + R0); try (by done); - last by eapply H_response_time_of_interfering_tasks_is_known; first by apply IN. + last by eapply H_response_time_of_interfering_tasks_is_known; first by apply IN0. rewrite leq_add2l. by apply leq_trans with (n := task_deadline tsk0); [by apply NOMISSHP | by apply CONSTR; rewrite -JOB0 FROMTS]. @@ -546,13 +554,13 @@ Module ResponseTimeAnalysisFP. [rewrite mul1n /= | by rewrite has_pred0 //]. destruct (has (fun tsk_k : sporadic_task => (delta <= x tsk_k) && - task_is_scheduled job_task sched tsk_k t) ts_interf) eqn:HAS'; + task_is_scheduled job_task sched tsk_k t) hp_tasks) eqn:HAS'; last by done. rewrite mul1n; move: HAS => /hasP [tsk_k INk LEk]. - unfold cardGE. + unfold num_tasks_exceeding. apply leq_trans with (n := num_cpus - count (fun i => (x i >= delta) && - task_is_scheduled job_task sched i t) ts_interf). + task_is_scheduled job_task sched i t) hp_tasks). { apply leq_sub2l. rewrite -2!sum1_count big_mkcond /=. @@ -561,11 +569,11 @@ Module ResponseTimeAnalysisFP. by destruct (task_is_scheduled job_task sched i t); [by rewrite andbT | by rewrite andbF]. } - rewrite -count_filter -[count _ ts_interf]count_filter. + rewrite -count_filter -[count _ hp_tasks]count_filter. eapply leq_trans with (n := count (predC (fun tsk => delta <= x tsk)) _); last by apply eq_leq, eq_in_count; red; ins; rewrite ltnNge. rewrite leq_subLR count_predC size_filter. - by apply leq_trans with (n := count (scheduled_task_other_than_tsk t) ts); + apply leq_trans with (n := count (other_scheduled_task t) ts); [by rewrite INV | by rewrite count_filter]. } { @@ -573,7 +581,7 @@ Module ResponseTimeAnalysisFP. rewrite exchange_big /=; apply leq_sum; intros t _. destruct (backlogged job_cost sched j t) eqn:BACK; last by ins. rewrite mul1n -sum1_count. - rewrite big_mkcond [\sum_(i <- ts_interf | _ < _) _]big_mkcond /=. + rewrite big_mkcond [\sum_(i <- hp_tasks | _ < _) _]big_mkcond /=. apply leq_sum_seq; move => tsk_k IN _. destruct (x tsk_k < delta); [rewrite andTb | by rewrite andFb]. destruct (task_is_scheduled job_task sched tsk_k t) eqn:SCHED; last by done. @@ -582,13 +590,13 @@ Module ResponseTimeAnalysisFP. } Qed. - (* 4) Next, we prove that, for any interval delta, if the sum of per-task - interference exceeds delta * num_cpus, the same applies for the - sum of the minimum between the interference and delta. *) + (* 5) Based on lemma (4), we prove that, for any interval delta, if the sum of per-task + interference exceeds (delta * num_cpus), the same applies for the + sum of the minimum of the interference and delta. *) Lemma bertogna_fp_minimum_exceeds_interference : forall delta, - \sum_(tsk_k <- ts_interf) x tsk_k >= delta * num_cpus -> - \sum_(tsk_k <- ts_interf) minn (x tsk_k) delta >= + \sum_(tsk_k <- hp_tasks) x tsk_k >= delta * num_cpus -> + \sum_(tsk_k <- hp_tasks) minn (x tsk_k) delta >= delta * num_cpus. Proof. intros delta SUMLESS. @@ -602,58 +610,63 @@ Module ResponseTimeAnalysisFP. [| by red; ins; rewrite ltnNge | by intros i COND; rewrite -ltnNge in COND; rewrite COND]. - (* Case 1: cardGE = 0 *) - destruct (~~ has (fun i => delta <= x i) ts_interf) eqn:HASa. + (* Case 1: num_tasks_exceeding = 0 *) + destruct (~~ has (fun i => delta <= x i) hp_tasks) eqn:HASa. { rewrite [\sum_(_ <- _ | _ <= _) _]big_hasC; last by apply HASa. rewrite big_seq_cond; move: HASa => /hasPn HASa. - rewrite add0n (eq_bigl (fun i => (i \in ts_interf) && true)); - last by red; intros tsk_k; destruct (tsk_k \in ts_interf) eqn:INk; + rewrite add0n (eq_bigl (fun i => (i \in hp_tasks) && true)); + last by red; intros tsk_k; destruct (tsk_k \in hp_tasks) eqn:INk; [by rewrite andTb ltnNge; apply HASa | by rewrite andFb]. by rewrite -big_seq_cond. } apply negbFE in HASa. - (* Case 2: cardGE >= num_cpus *) - destruct (cardGE delta >= num_cpus) eqn:CARD. + (* Case 2: num_tasks_exceeding >= num_cpus *) + destruct (num_tasks_exceeding delta >= num_cpus) eqn:CARD. { - apply leq_trans with (delta * cardGE delta); + apply leq_trans with (delta * num_tasks_exceeding delta); first by rewrite leq_mul2l; apply/orP; right. - unfold cardGE; rewrite -sum1_count big_distrr /=. + unfold num_tasks_exceeding; rewrite -sum1_count big_distrr /=. rewrite -[\sum_(_ <- _ | _) _]addn0. by apply leq_add; [by apply leq_sum; ins; rewrite muln1|by ins]. } apply negbT in CARD; rewrite -ltnNge in CARD. - (* Case 3: cardGE < num_cpus *) - rewrite big_const_seq iter_addn addn0; fold cardGE. - apply leq_trans with (n := delta * cardGE delta + - delta * (num_cpus - cardGE delta)); + (* Case 3: num_tasks_exceeding < num_cpus *) + rewrite big_const_seq iter_addn addn0; fold num_tasks_exceeding. + apply leq_trans with (n := delta * num_tasks_exceeding delta + + delta * (num_cpus - num_tasks_exceeding delta)); first by rewrite -mulnDr subnKC //; apply ltnW. - rewrite leq_add2l; apply bertogna_fp_helper_lemma. + rewrite leq_add2l; apply bertogna_fp_interference_in_non_full_processors. by apply/andP; split; first by rewrite -has_count. Qed. - (* 5) Now, we prove that the Bertogna's interference bound - is not enough to cover the sum of the "minimum" term over - all tasks (artifact of the proof by contradiction). *) + (* 6) Next, using lemmas (0), (3) and (5) we prove that the reduction-based + interference bound is not enough to cover the sum of the minima over all tasks + (artifact of the proof by contradiction). *) Lemma bertogna_fp_sum_exceeds_total_interference: \sum_((tsk_k, R_k) <- hp_bounds) minn (x tsk_k) (R - task_cost tsk + 1) > - total_interference_bound_fp task_cost task_period tsk hp_bounds - R higher_eq_priority. + total_interference_bound_fp task_cost task_period tsk hp_bounds R. Proof. have EXCEEDS := bertogna_fp_minimum_exceeds_interference. - have ALLBUSY := bertogna_fp_all_cpus_busy. + have ALLBUSY := bertogna_fp_interference_on_all_cpus. have TOOMUCH := bertogna_fp_too_much_interference. - rename H_hp_bounds_has_interfering_tasks into UNZIP, + rename H_hp_bounds_has_interfering_tasks into HAS, H_response_time_recurrence_holds into REC. - apply leq_trans with (n := \sum_(tsk_k <- ts_interf) minn (x tsk_k) (R - task_cost tsk + 1)); + apply leq_trans with (n := \sum_(tsk_k <- hp_tasks) minn (x tsk_k) (R - task_cost tsk + 1)); last first. { rewrite (eq_bigr (fun i => minn (x (fst i)) (R - task_cost tsk + 1))); last by ins; destruct i. - rewrite big_filter. - have MAP := big_map (fun x => fst x) (fun i => true) (fun i => minn (x i) (R - task_cost tsk + 1)). - by unfold unzip1 in *; rewrite -MAP -UNZIP -big_filter. + have MAP := @big_map _ 0 addn _ _ (fun x => fst x) hp_bounds (fun x => true) (fun y => minn (x y) (R - task_cost tsk + 1)). + rewrite -MAP. + apply leq_sum_sub_uniq; first by apply filter_uniq; destruct ts. + red; move => tsk0 IN0. + rewrite mem_filter in IN0; move: IN0 => /andP [INTERF0 IN0]. + apply/mapP. + feed (HAS tsk0); first by done. + move: (HAS INTERF0) => [R0 IN]. + by exists (tsk0, R0). } apply ltn_div_trunc with (d := num_cpus); first by apply H_at_least_one_cpu. @@ -666,9 +679,11 @@ Module ResponseTimeAnalysisFP. by rewrite leq_mul2r; apply/orP; right; apply TOOMUCH. Qed. - (* 6) After concluding that the sum of the minimum exceeds (R - e_i + 1), - we prove that there exists a tuple (tsk_k, R_k) such that - min (x_k, R - e_i + 1) > min (W_k, R - e_i + 1). *) + (* 7) After concluding that the sum of the minima exceeds (R - e_i + 1), + we prove that there exists a tuple (tsk_k, R_k) that satisfies + min (x_k, R - e_i + 1) > min (W_k, R - e_i + 1). + This implies that x_k > W_k, which is of course a contradiction, + since W_k is a valid task interference bound. *) Lemma bertogna_fp_exists_task_that_exceeds_bound : exists tsk_k R_k, (tsk_k, R_k) \in hp_bounds /\ @@ -676,27 +691,25 @@ Module ResponseTimeAnalysisFP. minn (workload_bound tsk_k R_k) (R - task_cost tsk + 1)). Proof. have SUM := bertogna_fp_sum_exceeds_total_interference. - have INTERFk := bertogna_fp_tsk_other_interferes. - rename H_hp_bounds_has_interfering_tasks into UNZIP. + rename H_hp_bounds_has_interfering_tasks into HASHP. assert (HAS: has (fun tup : task_with_response_time => - let (tsk_k, R_k) := tup in + let (tsk_k, R_k) := tup in (minn (x tsk_k) (R - task_cost tsk + 1) > minn (workload_bound tsk_k R_k)(R - task_cost tsk + 1))) hp_bounds). { - apply/negP; unfold not; intro NOTHAS. - move: NOTHAS => /negP /hasPn ALL. - rewrite -[_ < _]negbK in SUM. - move: SUM => /negP SUM; apply SUM; rewrite -leqNgt. - unfold total_interference_bound_fp. - rewrite [\sum_(i <- _ | let '(tsk_other, _) := i in _)_]big_mkcond. - rewrite big_seq_cond [\sum_(i <- _ | true) _]big_seq_cond. - apply leq_sum; move => tsk_k /andP [HPk _]; destruct tsk_k as [tsk_k R_k]. - specialize (ALL (tsk_k, R_k) HPk). - rewrite -leqNgt in ALL. - specialize (INTERFk tsk_k R_k HPk). - fold (can_interfere_with_tsk); rewrite INTERFk. - by apply ALL. + apply/negP; unfold not; intro NOTHAS. + move: NOTHAS => /negP /hasPn ALL. + rewrite -[_ < _]negbK in SUM. + move: SUM => /negP SUM; apply SUM; rewrite -leqNgt. + rewrite (eq_bigr (fun i => minn (x (fst i)) (R - task_cost tsk + 1))); + last by ins; destruct i. + unfold total_interference_bound_fp. + rewrite big_seq_cond. + rewrite [\sum_(_ <- _ | true)_]big_seq_cond. + apply leq_sum. + intros p; rewrite andbT; intros IN. + by specialize (ALL p IN); destruct p; rewrite leqNgt. } move: HAS => /hasP HAS; destruct HAS as [[tsk_k R_k] HPk MINk]; exists tsk_k, R_k. by repeat split. @@ -716,7 +729,7 @@ Module ResponseTimeAnalysisFP. H_interfering_tasks_miss_no_deadlines into NOMISS, H_response_time_recurrence_holds into REC, H_response_time_of_interfering_tasks_is_known into RESP, - H_hp_bounds_has_interfering_tasks into UNZIP, + H_hp_bounds_has_interfering_tasks into HAS, H_response_time_no_larger_than_deadline into LEdl. intros j JOBtsk. diff --git a/analysis/basic/interference_bound.v b/analysis/basic/interference_bound.v index 63229dee756439782224ada2545ffea7a6c154cc..c5ecc342dd9afda68f7867f69427e01361fe2b7a 100644 --- a/analysis/basic/interference_bound.v +++ b/analysis/basic/interference_bound.v @@ -34,7 +34,7 @@ Module InterferenceBoundGeneric. (* Based on the workload bound, Bertogna and Cirinei define the following interference bound for a task. *) Definition interference_bound_generic := - minn (W task_cost task_period tsk_other R_other delta) (delta - (task_cost tsk) + 1). + minn (W task_cost task_period tsk_other R_other delta) (delta - task_cost tsk + 1). End PerTask. diff --git a/analysis/basic/interference_bound_edf.v b/analysis/basic/interference_bound_edf.v index bf569094fe92c4d5e7aa0739082d1c804cf431df..592123f1bd3cd522916db50e814be9bada0dd6d6 100644 --- a/analysis/basic/interference_bound_edf.v +++ b/analysis/basic/interference_bound_edf.v @@ -64,7 +64,7 @@ Module InterferenceBoundEDF. (* ... and an interval length delta. *) Variable delta: time. - Section PerTask. + Section RecallInterferenceBounds. Variable tsk_R: task_with_response_time. Let tsk_other := fst tsk_R. @@ -82,19 +82,21 @@ Module InterferenceBoundEDF. Definition interference_bound_edf := minn basic_interference_bound edf_specific_bound. - End PerTask. + End RecallInterferenceBounds. - Section AllTasks. + (* Next we define the computation of the total interference for APA scheduling. *) + Section TotalInterference. - Let interferes_with_tsk := jldp_can_interfere_with tsk. + (* Let other_task denote tasks different from tsk. *) + Let other_task := different_task tsk. (* The total interference incurred by tsk is bounded by the sum - of individual task interferences. *) + of individual task interferences of the other tasks. *) Definition total_interference_bound_edf := - \sum_((tsk_other, R_other) <- R_prev | interferes_with_tsk tsk_other) + \sum_((tsk_other, R_other) <- R_prev | other_task tsk_other) interference_bound_edf (tsk_other, R_other). - End AllTasks. + End TotalInterference. End TotalInterferenceBoundEDF. diff --git a/analysis/basic/interference_bound_fp.v b/analysis/basic/interference_bound_fp.v index 35052c7e523e468f7b721b04179d16da3c2f60c1..da6ad7ca34fc537a69ef91aee025f042dd375dfd 100644 --- a/analysis/basic/interference_bound_fp.v +++ b/analysis/basic/interference_bound_fp.v @@ -21,22 +21,22 @@ Module InterferenceBoundFP. Let task_with_response_time := (sporadic_task * time)%type. - (* Assume a known response-time bound for each interfering task ... *) + (* Assume a known response-time bound for each higher-priority task ... *) Variable R_prev: seq task_with_response_time. - (* ... and an interval length delta. *) + (* ... in any interval of length delta. *) Variable delta: time. (* Assume an FP policy. *) Variable higher_eq_priority: FP_policy sporadic_task. - Let can_interfere_with_tsk := fp_can_interfere_with higher_eq_priority tsk. + (* Recall the generic interference bound. *) Let total_interference_bound := interference_bound_generic task_cost task_period tsk delta. (* The total interference incurred by tsk is bounded by the sum of individual task interferences. *) Definition total_interference_bound_fp := - \sum_((tsk_other, R_other) <- R_prev | can_interfere_with_tsk tsk_other) + \sum_((tsk_other, R_other) <- R_prev) total_interference_bound (tsk_other, R_other). End Definitions. diff --git a/analysis/jitter/bertogna_edf_comp.v b/analysis/jitter/bertogna_edf_comp.v index cf5a0572b4a53b25ad4b394a815fd3daa3a9815b..980b598fff516ba93779aaad4352e4660d6ac5b6 100755 --- a/analysis/jitter/bertogna_edf_comp.v +++ b/analysis/jitter/bertogna_edf_comp.v @@ -7,7 +7,7 @@ Module ResponseTimeIterationEDF. Import ResponseTimeAnalysisEDFJitter. (* In this section, we define the algorithm for Bertogna and Cirinei's - response-time analysis for EDF scheduling. *) + response-time analysis for EDF scheduling with release jitter. *) Section Analysis. Context {sporadic_task: eqType}. @@ -59,7 +59,7 @@ Module ResponseTimeIterationEDF. (* To compute the response-time bounds of the entire task set, We start the iteration with a sequence of tasks and costs: <(task1, cost1), (task2, cost2), ...>. *) - Let initial_state (ts: taskset_of sporadic_task) := + Let initial_state (ts: seq sporadic_task) := map (fun t => (t, task_cost t)) ts. (* Then, we successively update the the response-time bounds based @@ -70,14 +70,14 @@ Module ResponseTimeIterationEDF. (* To ensure that the procedure converges, we run the iteration a "sufficient" number of times: task_deadline tsk - task_cost tsk + 1. This corresponds to the time complexity of the procedure. *) - Let max_steps (ts: taskset_of sporadic_task) := + Let max_steps (ts: seq sporadic_task) := \sum_(tsk <- ts) (task_deadline tsk - task_cost tsk) + 1. (* This yields the following definition for the RTA. At the end of the iteration, we check if all computed response-time bounds are less than or equal to the deadline, in which case they are valid. *) - Definition edf_claimed_bounds (ts: taskset_of sporadic_task) := + Definition edf_claimed_bounds (ts: seq sporadic_task) := let R_values := iter (max_steps ts) edf_rta_iteration (initial_state ts) in if (all jitter_plus_R_le_deadline R_values) then Some R_values @@ -85,7 +85,7 @@ Module ResponseTimeIterationEDF. (* The schedulability test simply checks if we got a list of response-time bounds (i.e., if the computation did not fail). *) - Definition edf_schedulable (ts: taskset_of sporadic_task) := + Definition edf_schedulable (ts: seq sporadic_task) := edf_claimed_bounds ts != None. (* In the following section, we prove several helper lemmas about the @@ -258,8 +258,8 @@ Module ResponseTimeIterationEDF. of the iteration at (max_steps ts) is equal to the value at (max_steps ts) + 1. *) Section Convergence. - (* Consider any valid task set. *) - Variable ts: taskset_of sporadic_task. + (* Consider any list of tasks with valid parameters. *) + Variable ts: list sporadic_task. Hypothesis H_valid_task_parameters: valid_sporadic_taskset task_cost task_period task_deadline ts. @@ -341,7 +341,7 @@ Module ResponseTimeIterationEDF. { assert (UNZIP0 := edf_claimed_bounds_unzip1_iteration ts 0). by simpl in UNZIP0; rewrite UNZIP0 edf_claimed_bounds_unzip1_iteration. - } + } destruct ts as [| tsk0 ts']. { clear -step; induction step; first by done. @@ -449,11 +449,11 @@ Module ResponseTimeIterationEDF. assert (SUBST: forall l delta, \sum_(j <- l | let '(tsk_other, _) := j in - jldp_can_interfere_with tsk_i tsk_other) + different_task tsk_i tsk_other) (let '(tsk_other, R_other) := j in interference_bound_edf task_cost task_period task_deadline task_jitter tsk_i delta (tsk_other, R_other)) = - \sum_(j <- l | jldp_can_interfere_with tsk_i (fst j)) + \sum_(j <- l | different_task tsk_i (fst j)) interference_bound_edf task_cost task_period task_deadline task_jitter tsk_i delta j). { intros l x; clear -l. @@ -498,14 +498,14 @@ Module ResponseTimeIterationEDF. } move: GE_COST => /allP GE_COST. - assert (LESUM: \sum_(j <- x1' | jldp_can_interfere_with tsk_i (fst j)) + assert (LESUM: \sum_(j <- x1' | different_task tsk_i (fst j)) interference_bound_edf task_cost task_period task_deadline task_jitter tsk_i delta j <= - \sum_(j <- x2' | jldp_can_interfere_with tsk_i (fst j)) + \sum_(j <- x2' | different_task tsk_i (fst j)) interference_bound_edf task_cost task_period task_deadline task_jitter tsk_i delta' j). { set elem := (tsk0, R0); rewrite 2!(big_nth elem). rewrite -SIZE. - rewrite big_mkcond [\sum_(_ <- _ | jldp_can_interfere_with _ _)_]big_mkcond. + rewrite big_mkcond [\sum_(_ <- _ | different_task _ _)_]big_mkcond. rewrite big_seq_cond [\sum_(_ <- _ | true) _]big_seq_cond. apply leq_sum; intros j; rewrite andbT; intros INj. rewrite mem_iota add0n subn0 in INj; move: INj => /andP [_ INj]. @@ -514,7 +514,7 @@ Module ResponseTimeIterationEDF. have MAP := @nth_map _ elem _ tsk0 (fun x => fst x). by rewrite -2?MAP -?SIZE //; f_equal. } rewrite -FSTeq. - destruct (jldp_can_interfere_with tsk_i (fst (nth elem x1' j))) eqn:INTERF; + destruct (different_task tsk_i (fst (nth elem x1' j))) eqn:INTERF; last by done. { exploit (LE elem); [by rewrite /= SIZE | | intro LEj]. @@ -540,7 +540,7 @@ Module ResponseTimeIterationEDF. by apply interference_bound_edf_monotonic. } } - destruct (jldp_can_interfere_with tsk_i tsk0) eqn:INTERFtsk0; last by done. + destruct (different_task tsk_i tsk0) eqn:INTERFtsk0; last by done. apply leq_add; last by done. { exploit (LE (tsk0, R0)); [by rewrite /= SIZE | | intro LEj]; @@ -876,15 +876,13 @@ Module ResponseTimeIterationEDF. Qed. (* Therefore, with regard to the response-time bound recurrence, ...*) - Let rt_recurrence (tsk: sporadic_task) (rt_bounds: seq task_with_response_time) (R: time) := - task_cost tsk + div_floor (I rt_bounds tsk R) num_cpus. (* ..., the individual response-time bounds (elements of the list) are also fixed points. *) Theorem edf_claimed_bounds_finds_fixed_point_for_each_bound : forall tsk R rt_bounds, edf_claimed_bounds ts = Some rt_bounds -> (tsk, R) \in rt_bounds -> - R = rt_recurrence tsk rt_bounds R. + R = edf_response_time_bound rt_bounds tsk R. Proof. intros tsk R rt_bounds SOME IN. have CONV := edf_claimed_bounds_finds_fixed_point_of_list rt_bounds. @@ -946,17 +944,14 @@ Module ResponseTimeIterationEDF. Section MainProof. - (* Consider a task set ts. *) + (* Consider a task set ts where... *) Variable ts: taskset_of sporadic_task. - (* Assume the task set has no duplicates, ... *) - Hypothesis H_ts_is_a_set: uniq ts. - - (* ...all tasks have valid parameters, ... *) + (* ...all tasks have valid parameters... *) Hypothesis H_valid_task_parameters: valid_sporadic_taskset task_cost task_period task_deadline ts. - (* ...constrained deadlines, ...*) + (* ...and constrained deadlines.*) Hypothesis H_constrained_deadlines: forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk. @@ -979,8 +974,7 @@ Module ResponseTimeIterationEDF. (* Then, consider any platform with at least one CPU such that...*) Variable sched: schedule num_cpus arr_seq. - Hypothesis H_at_least_one_cpu : - num_cpus > 0. + Hypothesis H_at_least_one_cpu: num_cpus > 0. (* ...jobs only execute after jitter and no longer than their execution costs. *) Hypothesis H_jobs_execute_after_jitter: diff --git a/analysis/jitter/bertogna_edf_theory.v b/analysis/jitter/bertogna_edf_theory.v index 3ea4ad152dadba7bda035b648a224d03303bdadd..71cd4dcba4cbb3a05da10d4da714c77c51518cb1 100644 --- a/analysis/jitter/bertogna_edf_theory.v +++ b/analysis/jitter/bertogna_edf_theory.v @@ -2,7 +2,8 @@ Require Import rt.util.all. Require Import rt.model.jitter.job rt.model.jitter.task rt.model.jitter.task_arrival rt.model.jitter.schedule rt.model.jitter.platform rt.model.jitter.interference rt.model.jitter.workload rt.model.jitter.schedulability - rt.model.jitter.priority rt.model.jitter.platform rt.model.jitter.response_time. + rt.model.jitter.priority rt.model.jitter.constrained_deadlines + rt.model.jitter.response_time. Require Import rt.analysis.jitter.workload_bound rt.analysis.jitter.interference_bound_edf. From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop div path. @@ -11,10 +12,13 @@ Module ResponseTimeAnalysisEDFJitter. Export JobWithJitter SporadicTaskset ScheduleOfSporadicTaskWithJitter Workload Schedulability ResponseTime Priority SporadicTaskArrival WorkloadBoundJitter - InterferenceBoundEDFJitter Platform Interference. + InterferenceBoundEDFJitter Platform Interference ConstrainedDeadlines. - (* In this section, we prove that Bertogna and Cirinei's RTA yields - safe response-time bounds. *) + (* In this section, we prove that any fixed point in Bertogna and + Cirinei's RTA for EDF scheduling modified to account for jitter + yields a safe response-time bound. This is an extension of the + analysis found in Chapter 17.1.2 of Baruah et al.'s book + Multiprocessor Scheduling for Real-time Systems. *) Section ResponseTimeBound. Context {sporadic_task: eqType}. @@ -41,42 +45,49 @@ Module ResponseTimeAnalysisEDFJitter. valid_sporadic_job_with_jitter task_cost task_deadline task_jitter job_cost job_deadline job_task job_jitter j. - (* Consider any schedule such that...*) + (* Assume that we have a task set where all tasks have valid + parameters and constrained deadlines, ... *) + Variable ts: taskset_of sporadic_task. + Hypothesis H_valid_task_parameters: + valid_sporadic_taskset task_cost task_period task_deadline ts. + Hypothesis H_constrained_deadlines: + forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk. + + (* ... and that all jobs in the arrival sequence come from the task set. *) + Hypothesis H_all_jobs_from_taskset: + forall (j: JobIn arr_seq), job_task j \in ts. + + (* Next, consider any schedule such that...*) Variable num_cpus: nat. Variable sched: schedule num_cpus arr_seq. - (* ...jobs execute after jitter and no longer than their execution costs. *) - Hypothesis H_jobs_execute_after_jitter: + (* ...jobs are sequential, do not execute before the + jitter has passed and nor longer than their execution costs. *) + Hypothesis H_sequential_jobs: sequential_jobs sched. + Hypothesis H_execute_after_jitter: jobs_execute_after_jitter job_jitter sched. Hypothesis H_completed_jobs_dont_execute: completed_jobs_dont_execute job_cost sched. - (* Also assume that jobs are sequential and that there exists - at least one processor. *) - Hypothesis H_sequential_jobs: sequential_jobs sched. + (* Assume that there exists at least one processor. *) Hypothesis H_at_least_one_cpu: num_cpus > 0. - (* Assume that we have a task set ts, such that all jobs come - from the task set, and all tasks have valid parameters and - constrained deadlines. *) - Variable ts: taskset_of sporadic_task. - Hypothesis H_all_jobs_from_taskset: - forall (j: JobIn arr_seq), job_task j \in ts. - Hypothesis H_valid_task_parameters: - valid_sporadic_taskset task_cost task_period task_deadline ts. - Hypothesis H_constrained_deadlines: - forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk. + (* Assume that the schedule is a work-conserving EDF schedule. *) + Hypothesis H_work_conserving: work_conserving job_cost job_jitter sched. + Hypothesis H_edf_policy: enforces_JLDP_policy job_cost job_jitter sched (EDF job_deadline). + (* Let's define some local names to avoid passing many parameters. *) Let no_deadline_is_missed_by_tsk (tsk: sporadic_task) := task_misses_no_deadline job_cost job_deadline job_task sched tsk. Let response_time_bounded_by (tsk: sporadic_task) := is_response_time_bound_of_task job_cost job_task tsk sched. - (* Assume a known response-time bound R is known... *) + (* Next we consider the response-time recurrence. + Assume that a response-time bound R is known... *) Let task_with_response_time := (sporadic_task * time)%type. Variable rt_bounds: seq task_with_response_time. - (* ...for any task in the task set. *) + (* ...for any task in the task set, ... *) Hypothesis H_rt_bounds_contains_all_tasks: unzip1 rt_bounds = ts. (* Also, assume that R is a fixed-point of the response-time recurrence, ... *) @@ -93,16 +104,7 @@ Module ResponseTimeAnalysisEDFJitter. (tsk, R) \in rt_bounds -> task_jitter tsk + R <= task_deadline tsk. - (* Assume that we have a work-conserving EDF scheduler. *) - Hypothesis H_work_conserving: work_conserving job_cost job_jitter sched. - Hypothesis H_edf_policy: enforces_JLDP_policy job_cost job_jitter sched (EDF job_deadline). - - (* Assume that the task set has no duplicates. This is required to - avoid problems when counting tasks (for example, when stating - that the number of interfering tasks is at most num_cpus). *) - Hypothesis H_ts_is_a_set : uniq ts. - - (* In order to prove that R is a response-time bound, we first present some lemmas. *) + (* In order to prove that R is a response-time bound, we first provide some lemmas. *) Section Lemmas. (* Let (tsk, R) be any task to be analyzed, with its response-time bound R. *) @@ -120,7 +122,7 @@ Module ResponseTimeAnalysisEDFJitter. (* Assume that job j did not complete on time, ... *) Hypothesis H_j_not_completed: ~~ completed job_cost sched j (t1 + R). - (* and that it is the first job not to satisfy its response-time bound. *) + (* ...and that it is the first job not to satisfy its response-time bound. *) Hypothesis H_all_previous_jobs_completed_on_time : forall (j_other: JobIn arr_seq) tsk_other R_other, job_task j_other = tsk_other -> @@ -147,9 +149,14 @@ Module ResponseTimeAnalysisEDFJitter. Let interference_bound (tsk_other: sporadic_task) (R_other: time) := interference_bound_edf task_cost task_period task_deadline task_jitter tsk R (tsk_other, R_other). - (* Also, let ts_interf be the subset of tasks that interfere with tsk. *) - Let ts_interf := [seq tsk_other <- ts | jldp_can_interfere_with tsk tsk_other]. + (* Based on the definition of a different task, ... *) + Let other_task := different_task tsk. + + (* ...let other_tasks denote the set of tasks that are different from tsk. *) + Let other_tasks := + [seq tsk_other <- ts | other_task tsk_other]. + (* Now we establish results the interfering tasks. *) Section LemmasAboutInterferingTasks. (* Let (tsk_other, R_other) be any pair of higher-priority task and @@ -161,7 +168,7 @@ Module ResponseTimeAnalysisEDFJitter. (* Note that tsk_other is in task set ts ...*) Lemma bertogna_edf_tsk_other_in_ts: tsk_other \in ts. Proof. - by rewrite -H_rt_bounds_contains_all_tasks; apply/mapP; exists (tsk_other, R_other). + by rewrite set_mem -H_rt_bounds_contains_all_tasks; apply/mapP; exists (tsk_other, R_other). Qed. (* Also, R_other is larger than the cost of tsk_other. *) @@ -267,65 +274,11 @@ Module ResponseTimeAnalysisEDFJitter. by apply completion_monotonic with (t := i); try (by done); apply ltnW. Qed. - Let scheduled_task_other_than_tsk (t: time) (tsk_other: sporadic_task) := - task_is_scheduled job_task sched tsk_other t && - jldp_can_interfere_with tsk tsk_other. - - (* 1) At all times that j is backlogged, all processors are busy with other tasks. *) - Lemma bertogna_edf_scheduling_invariant: - forall t, - t1 <= t < t1 + R -> - backlogged job_cost job_jitter sched j t -> - count (scheduled_task_other_than_tsk t) ts = num_cpus. - Proof. - rename H_all_jobs_from_taskset into FROMTS, - H_valid_task_parameters into PARAMS, - H_valid_job_parameters into JOBPARAMS, - H_job_of_tsk into JOBtsk, - H_sporadic_tasks into SPO, - H_tsk_R_in_rt_bounds into INbounds, - H_all_previous_jobs_completed_on_time into BEFOREok, - H_tasks_miss_no_deadlines into NOMISS, - H_rt_bounds_contains_all_tasks into UNZIP, - H_constrained_deadlines into RESTR, - H_work_conserving into WORK. - unfold x, X, total_interference, task_interference, - valid_sporadic_job_with_jitter, valid_sporadic_job in *. - move => t /andP [LEt LTt] BACK. - unfold scheduled_task_other_than_tsk in *. - eapply platform_cpus_busy_with_interfering_tasks; try (by done); - [ by apply WORK | by done | by apply SPO - | apply PARAMS; rewrite -JOBtsk; apply FROMTS - | by apply JOBtsk | by apply BACK | ]. - { - intros j0 tsk0 TSK0 LE. - cut (tsk0 \in unzip1 rt_bounds = true); last by rewrite UNZIP -TSK0 FROMTS. - move => /mapP [p IN EQ]; destruct p as [tsk' R0]; simpl in *; subst tsk'. - apply completion_monotonic with (t0 := job_arrival j0 + task_jitter tsk0 + R0); try (by done). - { - rewrite -addnA leq_add2l TSK0. - apply leq_trans with (n := task_deadline tsk0); first by apply NOMISS. - by apply RESTR; rewrite -TSK0 FROMTS. - } - { - apply BEFOREok with (tsk_other := tsk0); try (by done). - apply leq_trans with (n := t1 + R); last first. - { - rewrite leq_add2r leq_add2l -JOBtsk. - by specialize (JOBPARAMS j); des. - } - apply leq_ltn_trans with (n := t); last by done. - apply leq_trans with (n := job_arrival j0 + task_period tsk0); - last by done. - rewrite -addnA leq_add2l. - apply leq_trans with (n := task_deadline tsk0); first by apply NOMISS. - by apply RESTR; rewrite -TSK0; apply FROMTS. - } - } - Qed. - - (* 2) Prove that during the scheduling window of j, any job that is scheduled while j is - backlogged comes from a different task. *) + (* 1) Next, we prove that during the scheduling window of j, any job that is + scheduled while j is backlogged comes from a different task. + This follows from the fact that j is the first job not to complete + by its response-time bound, so previous jobs of j's task must have + completed by their periods and cannot be pending. *) Lemma bertogna_edf_interference_by_different_tasks : forall t j_other, t1 <= t < t1 + R -> @@ -397,49 +350,12 @@ Module ResponseTimeAnalysisEDFJitter. } Qed. - (* 3) Next, we prove that the sum of the interference of each task is equal - to the total interference multiplied by the number of processors. This - holds because interference only occurs when all processors are busy. *) - Lemma bertogna_edf_all_cpus_busy : - \sum_(tsk_k <- ts_interf) x tsk_k = X * num_cpus. - Proof. - have DIFFTASK := bertogna_edf_interference_by_different_tasks. - rename H_all_jobs_from_taskset into FROMTS, - H_valid_task_parameters into PARAMS, - H_job_of_tsk into JOBtsk, H_sporadic_tasks into SPO, - H_work_conserving into WORK, - H_tsk_R_in_rt_bounds into INbounds, - H_all_previous_jobs_completed_on_time into BEFOREok, - H_tasks_miss_no_deadlines into NOMISS, - H_rt_bounds_contains_all_tasks into UNZIP, - H_constrained_deadlines into CONSTR. - unfold sporadic_task_model in *. - unfold x, X, total_interference, task_interference. - rewrite -big_mkcond -exchange_big big_distrl /= mul1n. - rewrite [\sum_(_ <= _ < _ | backlogged _ _ _ _ _) _]big_mkcond. - apply eq_big_nat; move => t /andP [GEt LTt]. - destruct (backlogged job_cost job_jitter sched j t) eqn:BACK; - last by rewrite big1 //; ins; rewrite big1. - rewrite big_mkcond /=. - rewrite exchange_big /=. - apply eq_trans with (y := \sum_(cpu < num_cpus) 1); last by simpl_sum_const. - apply eq_bigr; intros cpu _. - move: (WORK j t BACK cpu) => [j_other /eqP SCHED]; unfold scheduled_on in *. - rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq. - { - rewrite (eq_bigr (fun i => 0)); - last by intros i DIFF; rewrite /task_scheduled_on SCHED;apply/eqP;rewrite eqb0 eq_sym. - simpl_sum_const; apply/eqP; rewrite eqb1. - by unfold task_scheduled_on; rewrite SCHED. - } - rewrite mem_filter; apply/andP; split; last by apply FROMTS. - unfold jldp_can_interfere_with. - apply DIFFTASK with (t := t); [by auto | by done |]. - by apply/existsP; exists cpu; apply/eqP. - Qed. - - (* 4) Show that by the induction hypothesis, all jobs released - before the end of the interval complete by their periods. *) + (* 2) In order to use the lemmas in constrained_deadlines.v, we show that + all jobs released before the end of the interval complete by their + periods. This follows trivially from the hypothesis that all jobs + before (t1 + R) complete by their response-time bounds. + With this lemma, we can conclude that during job j's scheduling + window there cannot be multiple pending jobs of each task.*) Lemma bertogna_edf_all_previous_jobs_complete_by_their_period: forall t (j0: JobIn arr_seq), t < t1 + R -> @@ -476,21 +392,125 @@ Module ResponseTimeAnalysisEDFJitter. by rewrite -addnA leq_add2l; apply leq_trans with (n := task_deadline (job_task j0)); [by apply NOMISS | apply CONSTR; rewrite FROMTS]. Qed. - - (* Let (cardGE delta) be the number of interfering tasks whose interference - is larger than delta. *) - Let cardGE (delta: time) := count (fun i => x i >= delta) ts_interf. - - (* 5) If there is at least one of such tasks (e.g., cardGE > 0), then the - cumulative interference caused by the complementary set of interfering - tasks fills at least (num_cpus - cardGE) processors. *) - Lemma bertogna_edf_helper_lemma : + + + (* Let's define a predicate to identify the other tasks that are scheduled. *) + Let other_scheduled_task (t: time) (tsk_other: sporadic_task) := + task_is_scheduled job_task sched tsk_other t && + other_task tsk_other. + + (* 3) Now we prove that, at all times that j is backlogged, the number + of tasks other than tsk that are scheduled is exactly the number + of processors in the system. This is required to prove lemma (4). *) + Lemma bertogna_edf_all_cpus_are_busy: + forall t, + t1 <= t < t1 + R -> + backlogged job_cost job_jitter sched j t -> + count (other_scheduled_task t) ts = num_cpus. + Proof. + rename H_all_jobs_from_taskset into FROMTS, + H_valid_task_parameters into PARAMS, + H_valid_job_parameters into JOBPARAMS, + H_job_of_tsk into JOBtsk, + H_sporadic_tasks into SPO, + H_tsk_R_in_rt_bounds into INbounds, + H_all_previous_jobs_completed_on_time into BEFOREok, + H_tasks_miss_no_deadlines into NOMISS, + H_rt_bounds_contains_all_tasks into UNZIP, + H_constrained_deadlines into RESTR, + H_work_conserving into WORK. + unfold x, X, total_interference, task_interference, + valid_sporadic_job_with_jitter, valid_sporadic_job in *. + move => t /andP [LEt LTt] BACK. + eapply platform_cpus_busy_with_interfering_tasks; try (by done); + [ by apply WORK | by done | by apply SPO + | apply PARAMS; rewrite -JOBtsk; apply FROMTS + | by apply JOBtsk | by apply BACK | ]. + { + intros j0 tsk0 TSK0 LE. + cut (tsk0 \in unzip1 rt_bounds = true); last by rewrite UNZIP -TSK0 FROMTS. + move => /mapP [p IN EQ]; destruct p as [tsk' R0]; simpl in *; subst tsk'. + apply completion_monotonic with (t0 := job_arrival j0 + task_jitter tsk0 + R0); try (by done). + { + rewrite -addnA leq_add2l TSK0. + apply leq_trans with (n := task_deadline tsk0); first by apply NOMISS. + by apply RESTR; rewrite -TSK0 FROMTS. + } + { + apply BEFOREok with (tsk_other := tsk0); try (by done). + apply leq_trans with (n := t1 + R); last first. + { + rewrite leq_add2r leq_add2l -JOBtsk. + by specialize (JOBPARAMS j); des. + } + apply leq_ltn_trans with (n := t); last by done. + apply leq_trans with (n := job_arrival j0 + task_period tsk0); + last by done. + rewrite -addnA leq_add2l. + apply leq_trans with (n := task_deadline tsk0); first by apply NOMISS. + by apply RESTR; rewrite -TSK0; apply FROMTS. + } + } + Qed. + + + (* 4) Next, we prove that the sum of the interference of each task is equal + to the total interference multiplied by the number of processors. This + holds because interference only occurs when all processors are busy. + With this lemma we can relate per-task interference with the total + interference incurred by j (backlogged time). *) + Lemma bertogna_edf_interference_on_all_cpus : + \sum_(tsk_k <- other_tasks) x tsk_k = X * num_cpus. + Proof. + have DIFFTASK := bertogna_edf_interference_by_different_tasks. + rename H_all_jobs_from_taskset into FROMTS, + H_valid_task_parameters into PARAMS, + H_job_of_tsk into JOBtsk, H_sporadic_tasks into SPO, + H_work_conserving into WORK, + H_tsk_R_in_rt_bounds into INbounds, + H_all_previous_jobs_completed_on_time into BEFOREok, + H_tasks_miss_no_deadlines into NOMISS, + H_rt_bounds_contains_all_tasks into UNZIP, + H_constrained_deadlines into CONSTR. + unfold sporadic_task_model in *. + unfold x, X, total_interference, task_interference. + rewrite -big_mkcond -exchange_big big_distrl /= mul1n. + rewrite [\sum_(_ <= _ < _ | backlogged _ _ _ _ _) _]big_mkcond. + apply eq_big_nat; move => t /andP [GEt LTt]. + destruct (backlogged job_cost job_jitter sched j t) eqn:BACK; + last by rewrite big1 //; ins; rewrite big1. + rewrite big_mkcond /=. + rewrite exchange_big /=. + apply eq_trans with (y := \sum_(cpu < num_cpus) 1); last by simpl_sum_const. + apply eq_bigr; intros cpu _. + move: (WORK j t BACK cpu) => [j_other /eqP SCHED]; unfold scheduled_on in *. + rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq; destruct ts. + { + rewrite (eq_bigr (fun i => 0)); + last by intros i DIFF; rewrite /task_scheduled_on SCHED;apply/eqP;rewrite eqb0 eq_sym. + simpl_sum_const; apply/eqP; rewrite eqb1. + by unfold task_scheduled_on; rewrite SCHED. + } + rewrite mem_filter; apply/andP; split; last by apply FROMTS. + apply DIFFTASK with (t := t); [by auto | by done |]. + by apply/existsP; exists cpu; apply/eqP. + Qed. + + (* Before stating the next lemma, let (num_tasks_exceeding delta) be the + number of interfering tasks whose interference x is larger than delta. *) + Let num_tasks_exceeding delta := count (fun i => x i >= delta) (other_tasks). + + (* 5) Now we prove that, for any delta, if (num_task_exceeding delta > 0), then the + cumulative interference caused by the complementary set of interfering tasks fills + the remaining, not-completely-full (num_cpus - num_tasks_exceeding delta) + processors. *) + Lemma bertogna_edf_interference_in_non_full_processors : forall delta, - 0 < cardGE delta < num_cpus -> - \sum_(i <- ts_interf | x i < delta) x i >= delta * (num_cpus - cardGE delta). + 0 < num_tasks_exceeding delta < num_cpus -> + \sum_(i <- other_tasks | x i < delta) x i >= delta * (num_cpus - num_tasks_exceeding delta). Proof. have COMP := bertogna_edf_all_previous_jobs_complete_by_their_period. - have INV := bertogna_edf_scheduling_invariant. + have INV := bertogna_edf_all_cpus_are_busy. rename H_all_jobs_from_taskset into FROMTS, H_valid_task_parameters into PARAMS, H_job_of_tsk into JOBtsk, H_sporadic_tasks into SPO, @@ -505,14 +525,14 @@ Module ResponseTimeAnalysisEDFJitter. set some_interference_A := fun t => has (fun tsk_k => backlogged job_cost job_jitter sched j t && (x tsk_k >= delta) && - task_is_scheduled job_task sched tsk_k t) ts_interf. + task_is_scheduled job_task sched tsk_k t) other_tasks. set total_interference_B := fun t => backlogged job_cost job_jitter sched j t * count (fun tsk_k => (x tsk_k < delta) && - task_is_scheduled job_task sched tsk_k t) ts_interf. + task_is_scheduled job_task sched tsk_k t) other_tasks. apply leq_trans with ((\sum_(t1 <= t < t1 + R) - some_interference_A t) * (num_cpus - cardGE delta)). + some_interference_A t) * (num_cpus - num_tasks_exceeding delta)). { rewrite leq_mul2r; apply/orP; right. move: HAS => /hasP HAS; destruct HAS as [tsk_a INa LEa]. @@ -576,13 +596,13 @@ Module ResponseTimeAnalysisEDFJitter. [rewrite mul1n /= | by rewrite has_pred0 //]. destruct (has (fun tsk_k : sporadic_task => (delta <= x tsk_k) && - task_is_scheduled job_task sched tsk_k t) ts_interf) eqn:HAS'; + task_is_scheduled job_task sched tsk_k t) other_tasks) eqn:HAS'; last by done. rewrite mul1n; move: HAS => /hasP [tsk_k INk LEk]. - unfold cardGE. + unfold num_tasks_exceeding. apply leq_trans with (n := num_cpus - count (fun i => (x i >= delta) && - task_is_scheduled job_task sched i t) ts_interf). + task_is_scheduled job_task sched i t) other_tasks). { apply leq_sub2l. rewrite -2!sum1_count big_mkcond /=. @@ -591,11 +611,11 @@ Module ResponseTimeAnalysisEDFJitter. by destruct (task_is_scheduled job_task sched i t); [by rewrite andbT | by rewrite andbF]. } - rewrite -count_filter -[count _ ts_interf]count_filter. + rewrite -count_filter -[count _ other_tasks]count_filter. eapply leq_trans with (n := count (predC (fun tsk => delta <= x tsk)) _); last by apply eq_leq, eq_in_count; red; ins; rewrite ltnNge. rewrite leq_subLR count_predC size_filter. - by apply leq_trans with (n := count (scheduled_task_other_than_tsk t) ts); + by apply leq_trans with (n := count (other_scheduled_task t) ts); [by rewrite INV | by rewrite count_filter]. } { @@ -603,7 +623,7 @@ Module ResponseTimeAnalysisEDFJitter. rewrite exchange_big /=; apply leq_sum; intros t _. destruct (backlogged job_cost job_jitter sched j t) eqn:BACK; last by ins. rewrite mul1n -sum1_count. - rewrite big_mkcond [\sum_(i <- ts_interf | _ < _) _]big_mkcond /=. + rewrite big_mkcond [\sum_(i <- other_tasks | _ < _) _]big_mkcond /=. apply leq_sum_seq; move => tsk_k IN _. destruct (x tsk_k < delta); [rewrite andTb | by rewrite andFb]. destruct (task_is_scheduled job_task sched tsk_k t) eqn:SCHED; last by done. @@ -612,13 +632,13 @@ Module ResponseTimeAnalysisEDFJitter. } Qed. - (* 6) Next, we prove that, for any interval delta, if the sum of per-task - interference exceeds delta * num_cpus, the same applies for the - sum of the minimum between the interference and delta. *) + (* 6) Based on lemma (5), we prove that, for any interval delta, if the sum of per-task + interference exceeds (delta * num_cpus), the same applies for the + sum of the minimum of the interference and delta. *) Lemma bertogna_edf_minimum_exceeds_interference : forall delta, - \sum_(tsk_k <- ts_interf) x tsk_k >= delta * num_cpus -> - \sum_(tsk_k <- ts_interf) minn (x tsk_k) delta >= + \sum_(tsk_k <- other_tasks) x tsk_k >= delta * num_cpus -> + \sum_(tsk_k <- other_tasks) minn (x tsk_k) delta >= delta * num_cpus. Proof. intros delta SUMLESS. @@ -632,64 +652,63 @@ Module ResponseTimeAnalysisEDFJitter. [| by red; ins; rewrite ltnNge | by intros i COND; rewrite -ltnNge in COND; rewrite COND]. - (* Case 1: cardGE = 0 *) - destruct (~~ has (fun i => delta <= x i) ts_interf) eqn:HASa. + (* Case 1: num_tasks_exceeding = 0 *) + destruct (~~ has (fun i => delta <= x i) other_tasks) eqn:HASa. { rewrite [\sum_(_ <- _ | _ <= _) _]big_hasC; last by apply HASa. rewrite big_seq_cond; move: HASa => /hasPn HASa. - rewrite add0n (eq_bigl (fun i => (i \in ts_interf) && true)); - last by red; intros tsk_k; destruct (tsk_k \in ts_interf) eqn:INk; + rewrite add0n (eq_bigl (fun i => (i \in other_tasks) && true)); + last by red; intros tsk_k; destruct (tsk_k \in other_tasks) eqn:INk; [by rewrite andTb ltnNge; apply HASa | by rewrite andFb]. by rewrite -big_seq_cond. } apply negbFE in HASa. - (* Case 2: cardGE >= num_cpus *) - destruct (cardGE delta >= num_cpus) eqn:CARD. + (* Case 2: num_tasks_exceeding >= num_cpus *) + destruct (num_tasks_exceeding delta >= num_cpus) eqn:CARD. { - apply leq_trans with (delta * cardGE delta); + apply leq_trans with (delta * num_tasks_exceeding delta); first by rewrite leq_mul2l; apply/orP; right. - unfold cardGE; rewrite -sum1_count big_distrr /=. + unfold num_tasks_exceeding; rewrite -sum1_count big_distrr /=. rewrite -[\sum_(_ <- _ | _) _]addn0. by apply leq_add; [by apply leq_sum; ins; rewrite muln1|by ins]. } apply negbT in CARD; rewrite -ltnNge in CARD. - (* Case 3: cardGE < num_cpus *) - rewrite big_const_seq iter_addn addn0; fold cardGE. - apply leq_trans with (n := delta * cardGE delta + - delta * (num_cpus - cardGE delta)); + (* Case 3: num_tasks_exceeding < num_cpus *) + rewrite big_const_seq iter_addn addn0; fold num_tasks_exceeding. + apply leq_trans with (n := delta * num_tasks_exceeding delta + + delta * (num_cpus - num_tasks_exceeding delta)); first by rewrite -mulnDr subnKC //; apply ltnW. - rewrite leq_add2l; apply bertogna_edf_helper_lemma. + rewrite leq_add2l; apply bertogna_edf_interference_in_non_full_processors. by apply/andP; split; first by rewrite -has_count. Qed. - (* 7) Now, we prove that the Bertogna's interference bound - is not enough to cover the sum of the "minimum" term over - all tasks (artifact of the proof by contradiction). *) + (* 7) Next, using lemmas (0), (4) and (6) we prove that the reduction-based + interference bound is not enough to cover the sum of the minima over all tasks + (artifact of the proof by contradiction). *) Lemma bertogna_edf_sum_exceeds_total_interference: - \sum_((tsk_other, R_other) <- rt_bounds | jldp_can_interfere_with tsk tsk_other) + \sum_((tsk_other, R_other) <- rt_bounds | other_task tsk_other) minn (x tsk_other) (R - task_cost tsk + 1) > I tsk R. Proof. have GE_COST := bertogna_edf_R_other_ge_cost. have EXCEEDS := bertogna_edf_minimum_exceeds_interference. - have ALLBUSY := bertogna_edf_all_cpus_busy. + have ALLBUSY := bertogna_edf_interference_on_all_cpus. have TOOMUCH := bertogna_edf_too_much_interference. rename H_rt_bounds_contains_all_tasks into UNZIP, H_response_time_is_fixed_point into REC. - apply leq_trans with (n := \sum_(tsk_other <- ts_interf) minn (x tsk_other) (R - task_cost tsk + 1)); + apply leq_trans with (n := \sum_(tsk_other <- other_tasks) minn (x tsk_other) (R - task_cost tsk + 1)); last first. { rewrite (eq_bigr (fun i => minn (x (fst i)) (R - task_cost tsk + 1))); last by ins; destruct i. move: UNZIP => UNZIP. - assert (FILTER: filter (jldp_can_interfere_with tsk) (unzip1 rt_bounds) = - filter (jldp_can_interfere_with tsk) ts). + assert (FILTER: filter other_task (unzip1 rt_bounds) = + filter other_task ts). by f_equal. - unfold ts_interf; rewrite -FILTER; clear FILTER. + unfold other_tasks; rewrite -FILTER; clear FILTER. rewrite -[\sum_(_ <- rt_bounds | _)_]big_filter. assert (SUBST: [seq i <- rt_bounds - | let '(tsk_other, _) := i in - jldp_can_interfere_with tsk tsk_other] = [seq i <- rt_bounds - | jldp_can_interfere_with tsk (fst i)]). + | let '(tsk_other, _) := i in other_task tsk_other] = + [seq i <- rt_bounds | other_task (fst i)]). { by apply eq_filter; red; intro i; destruct i. } rewrite SUBST; clear SUBST. @@ -707,9 +726,11 @@ Module ResponseTimeAnalysisEDFJitter. by rewrite leq_mul2r; apply/orP; right; apply TOOMUCH. Qed. - (* 8) After concluding that the sum of the minimum exceeds (R - e_i + 1), - we prove that there exists a tuple (tsk_k, R_k) such that - min (x_k, R - e_i + 1) > min (W_k, edf_bound, R - e_i + 1). *) + (* 8) After concluding that the sum of the minima exceeds (R - e_i + 1), + we prove that there exists a tuple (tsk_k, R_k) that satisfies + min (x_k, R - e_i + 1) > min (W_k, I_edf, R - e_i + 1). + This implies that either x_k > W_k or x_k > I_edf, which is a contradiction, + since both W_k and I_edf are valid task interference bounds. *) Lemma bertogna_edf_exists_task_that_exceeds_bound : exists tsk_other R_other, (tsk_other, R_other) \in rt_bounds /\ @@ -721,7 +742,7 @@ Module ResponseTimeAnalysisEDFJitter. rename H_rt_bounds_contains_all_tasks into UNZIP. assert (HAS: has (fun tup : task_with_response_time => let (tsk_other, R_other) := tup in - (tsk_other \in ts) && jldp_can_interfere_with tsk tsk_other && + (tsk_other \in ts) && other_task tsk_other && (minn (x tsk_other) (R - task_cost tsk + 1) > interference_bound tsk_other R_other)) rt_bounds). diff --git a/analysis/jitter/bertogna_fp_comp.v b/analysis/jitter/bertogna_fp_comp.v index 5a60ea7f15cb7d53993fd7d48476f49e6ce78550..5e9f582142411085eaf419f82baa151677be2504 100644 --- a/analysis/jitter/bertogna_fp_comp.v +++ b/analysis/jitter/bertogna_fp_comp.v @@ -7,7 +7,7 @@ Module ResponseTimeIterationFP. Import ResponseTimeAnalysisFP. (* In this section, we define the algorithm of Bertogna and Cirinei's - response-time analysis for FP scheduling. *) + response-time analysis for FP scheduling with release jitter. *) Section Analysis. Context {sporadic_task: eqType}. @@ -49,8 +49,7 @@ Module ResponseTimeIterationFP. iter step (fun t => task_cost tsk + div_floor - (total_interference_bound_fp task_cost task_period task_jitter tsk - R_prev t higher_priority) + (total_interference_bound_fp task_cost task_period task_jitter tsk R_prev t) num_cpus) (task_cost tsk). @@ -77,12 +76,12 @@ Module ResponseTimeIterationFP. (* The response-time analysis for a given task set is defined as a left-fold (reduce) based on the function above. This either returns a list of task and response-time bounds, or None. *) - Definition fp_claimed_bounds (ts: taskset_of sporadic_task) := + Definition fp_claimed_bounds (ts: seq sporadic_task) := foldl fp_bound_of_task (Some [::]) ts. (* The schedulability test simply checks if we got a list of response-time bounds (i.e., if the computation did not fail). *) - Definition fp_schedulable (ts: taskset_of sporadic_task) := + Definition fp_schedulable (ts: seq sporadic_task) := fp_claimed_bounds ts != None. (* In the following section, we prove several helper lemmas about the @@ -261,7 +260,7 @@ Module ResponseTimeIterationFP. forall tsk rt_bounds, task_cost tsk + div_floor (total_interference_bound_fp task_cost task_period task_jitter tsk rt_bounds - (per_task_rta tsk rt_bounds (max_steps tsk)) higher_priority) num_cpus + (per_task_rta tsk rt_bounds (max_steps tsk))) num_cpus = per_task_rta tsk rt_bounds (max_steps tsk).+1. Proof. by done. @@ -276,14 +275,13 @@ Module ResponseTimeIterationFP. (* Consider a list of previous tasks and a task tsk to be analyzed. *) Variable ts: taskset_of sporadic_task. - (* Assume that the task set doesn't contain duplicates and is sorted by priority, ... *) - Hypothesis H_task_set_is_a_set: uniq ts. + (* Assume that the task set is sorted by unique priorities, ... *) Hypothesis H_task_set_is_sorted: sorted higher_priority ts. + Hypothesis H_task_set_has_unique_priorities: + FP_is_antisymmetric_over_task_set higher_priority ts. - (* ...the priority order is strict (<), ...*) - Hypothesis H_priority_irreflexive: irreflexive higher_priority. - Hypothesis H_priority_transitive: transitive higher_priority. - Hypothesis H_priority_antissymetric: antisymmetric higher_priority. + (* ...the priority order is transitive, ...*) + Hypothesis H_priority_transitive: FP_is_transitive higher_priority. (* ... and that the response-time analysis succeeds. *) Variable hp_bounds: seq task_with_response_time. @@ -294,61 +292,20 @@ Module ResponseTimeIterationFP. Variable elem: sporadic_task. Let TASK := nth elem ts. - (* Then, the tasks in the prefix of fp_claimed_bounds are exactly interfering tasks - under FP scheduling.*) - Lemma fp_claimed_bounds_interf: - forall idx, + (* We prove that higher-priority tasks have smaller index. *) + Lemma fp_claimed_bounds_hp_tasks_have_smaller_index : + forall hp_idx idx, + hp_idx < size ts -> idx < size ts -> - [seq tsk_hp <- ts | fp_can_interfere_with higher_priority (TASK idx) tsk_hp] = take idx ts. + hp_idx != idx -> + higher_priority (TASK hp_idx) (TASK idx) -> + hp_idx < idx. Proof. - rename H_task_set_is_sorted into SORT, - H_task_set_is_a_set into UNIQ, - H_priority_antissymetric into ANTI, - H_priority_irreflexive into IRR. - induction idx. - { - intros LT. - destruct ts as [| tsk0 ts']; [by done | simpl in SORT]. - unfold fp_can_interfere_with; rewrite /= eq_refl andbF. - apply eq_trans with (y := filter pred0 ts'); - last by apply filter_pred0. - apply eq_in_filter; red; intros x INx; rewrite /TASK /=. - destruct (x != tsk0) eqn:SAME; rewrite ?andbT ?andbF //. - apply negbTE; apply/negP; unfold not; intro HP. - move: SAME => /eqP SAME; apply SAME; clear SAME. - apply ANTI; apply/andP; split; first by done. - apply order_path_min in SORT; last by done. - by move: SORT => /allP SORT; apply SORT. - } - { - intros LT. - generalize LT; intro LT'; apply ltSnm in LT. - feed IHidx; first by done. - rewrite -filter_idx_le_takeS //. - apply eq_in_filter; red; intros x INx. - unfold fp_can_interfere_with. - generalize INx; intro SUBST; apply nth_index with (x0 := elem) in SUBST. - rewrite -SUBST; clear SUBST. - rewrite index_uniq; [ | by rewrite index_mem | by done]. - apply/idP/idP. - { - move => /andP [HP DIFF]. - unfold TASK in *. - apply sorted_uniq_rel_implies_le_idx in HP; try (by done); - last by rewrite index_mem. - by rewrite leq_eqVlt in HP; move: HP => /orP [/eqP SAME | LESS]; - first by rewrite SAME eq_refl in DIFF. - } - { - intros LEidx; apply/andP; split; - first by apply sorted_lt_idx_implies_rel. - apply/eqP; red; intro BUG. - eapply f_equal with (f := fun x => index x ts) in BUG. - rewrite nth_index in BUG; last by done. - rewrite BUG in LEidx. - by rewrite index_uniq // ltnn in LEidx. - } - } + unfold TASK; clear TASK. + rename ts into ts'; destruct ts' as [ts UNIQ]; simpl in *. + intros hp_idx idx LThp LT NEQ HP. + rewrite ltn_neqAle; apply/andP; split; first by done. + by apply sorted_rel_implies_le_idx with (leT := higher_priority) (s := ts) (x0 := elem). Qed. End HighPriorityTasks. @@ -357,7 +314,7 @@ Module ResponseTimeIterationFP. Section Convergence. (* Consider any set of higher-priority tasks. *) - Variable ts_hp: taskset_of sporadic_task. + Variable ts_hp: seq sporadic_task. (* Assume that the response-time analysis succeeds for the higher-priority tasks. *) Variable rt_bounds: seq task_with_response_time. @@ -387,27 +344,22 @@ Module ResponseTimeIterationFP. apply fun_mon_iter_mon; [by ins | by ins; apply leq_addr |]. clear LEx x1 x2; intros x1 x2 LEx. unfold div_floor, total_interference_bound_fp. - rewrite big_seq_cond [\sum_(i <- _ | let '(tsk_other, _) := i in - _ && (tsk_other != tsk))_]big_seq_cond. - rewrite leq_add2l leq_div2r // leq_sum //. - - intros i; destruct (i \in rt_bounds) eqn:HP; last by rewrite andFb. - destruct i as [i R]; intros _. - unfold fp_claimed_bounds in SOME. - have GE_COST := (fp_claimed_bounds_ge_cost ts_hp rt_bounds i R SOME). + rewrite leq_add2l leq_div2r //. + rewrite big_seq_cond. + rewrite [\sum_(_ <- _ | true) _]big_seq_cond. + apply leq_sum; move => i /andP [IN _]. + destruct i as [i R]. + have GE_COST := fp_claimed_bounds_ge_cost ts_hp rt_bounds i R SOME IN. have UNZIP := fp_claimed_bounds_unzip ts_hp rt_bounds SOME. - assert (IN: i \in ts_hp). - { - by rewrite -UNZIP; apply/mapP; exists (i,R). - } unfold interference_bound_generic; simpl. rewrite leq_min; apply/andP; split. { - apply leq_trans with (n := W_jitter task_cost task_period task_jitter i R x1); - first by apply geq_minl. - exploit (VALID i); first by rewrite mem_rcons in_cons; apply/orP; right. - by ins; des; apply W_monotonic; try (by ins); apply GE_COST. - } + apply leq_trans with (n := W_jitter task_cost task_period task_jitter i R x1); first by apply geq_minl. + apply W_monotonic; try (by done). + have INts: i \in ts_hp by rewrite -UNZIP; apply/mapP; exists (i, R). + by exploit (VALID i); + [by rewrite mem_rcons in_cons INts orbT | by ins; des]. + } { apply leq_trans with (n := x1 - task_cost tsk + 1); first by apply geq_minr. by rewrite leq_add2r leq_sub2r //. @@ -520,29 +472,22 @@ Module ResponseTimeIterationFP. (* Consider a task set ts. *) Variable ts: taskset_of sporadic_task. - (* Assume that higher_eq_priority is a total strict order (<). - TODO: it doesn't have to be total over the entire domain, but - only within the task set. - But to weaken the hypothesis, we need to re-prove some lemmas - from ssreflect. *) - Hypothesis H_irreflexive: irreflexive higher_priority. - Hypothesis H_transitive: transitive higher_priority. - Hypothesis H_unique_priorities: antisymmetric higher_priority. - Hypothesis H_total: total higher_priority. - - (* Assume the task set has no duplicates, ... *) - Hypothesis H_ts_is_a_set: uniq ts. - - (* ...all tasks have valid parameters, ... *) + (* Assume that all tasks have valid parameters, ... *) Hypothesis H_valid_task_parameters: valid_sporadic_taskset task_cost task_period task_deadline ts. - (* ...constrained deadlines, ...*) + (* ...and constrained deadlines.*) Hypothesis H_constrained_deadlines: forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk. - (* ...and tasks are ordered by increasing priorities. *) - Hypothesis H_sorted_ts: sorted higher_priority ts. + (* Assume that the task set is totally ordered by unique priorities, + and that the priority order is transitive. *) + Hypothesis H_task_set_is_sorted: sorted higher_priority ts. + Hypothesis H_task_set_has_unique_priorities: + FP_is_antisymmetric_over_task_set higher_priority ts. + Hypothesis H_priority_is_total: + FP_is_total_over_task_set higher_priority ts. + Hypothesis H_priority_transitive: FP_is_transitive higher_priority. (* Next, consider any arrival sequence such that...*) Context {arr_seq: arrival_sequence Job}. @@ -563,8 +508,7 @@ Module ResponseTimeIterationFP. (* Then, consider any platform with at least one CPU such that...*) Variable sched: schedule num_cpus arr_seq. - Hypothesis H_at_least_one_cpu : - num_cpus > 0. + Hypothesis H_at_least_one_cpu: num_cpus > 0. (* ...jobs only execute after jitter and no longer than their execution costs. *) Hypothesis H_jobs_must_arrive_to_execute: @@ -602,8 +546,7 @@ Module ResponseTimeIterationFP. Proof. rename H_valid_job_parameters into JOBPARAMS, H_valid_task_parameters into TASKPARAMS, H_constrained_deadlines into RESTR, H_completed_jobs_dont_execute into COMP, - H_jobs_must_arrive_to_execute into MUSTARRIVE, H_sorted_ts into SORT, - H_unique_priorities into UNIQ, + H_jobs_must_arrive_to_execute into MUSTARRIVE, H_all_jobs_from_taskset into ALLJOBS. intros tsk R MATCH. assert (SOME: exists hp_bounds, fp_claimed_bounds ts = Some hp_bounds /\ @@ -668,11 +611,37 @@ Module ResponseTimeIterationFP. { cut (NTH idx \in hp_bounds = true); [intros IN | by apply mem_nth]. - by rewrite -UNZIP; apply/mapP; exists (TASK idx, RESP idx); rewrite PAIR. + by rewrite set_mem -UNZIP; apply/mapP; exists (TASK idx, RESP idx); rewrite PAIR. } { - unfold unzip1 in *; rewrite map_take UNZIP SUBST //. - by apply fp_claimed_bounds_interf with (hp_bounds := hp_bounds); rewrite -?SIZE. + intros hp_tsk IN INTERF. + exists (RESP (index hp_tsk ts)). + move: (IN) => INDEX; apply nth_index with (x0 := tsk) in INDEX. + rewrite -{1}[hp_tsk]INDEX -SUBST; last by rewrite SIZE index_mem. + assert (UNIQ: uniq hp_bounds). + { + apply map_uniq with (f := fst); unfold unzip1 in *; rewrite UNZIP. + by destruct ts. + } + rewrite -filter_idx_lt_take //. + { + rewrite PAIR mem_filter; apply/andP; split; + last by apply mem_nth; rewrite SIZE index_mem. + { + rewrite /NTH index_uniq; [| by rewrite SIZE index_mem | by done ]. + { + move: INTERF => /andP [HP NEQ]. + apply fp_claimed_bounds_hp_tasks_have_smaller_index with + (ts := ts) (elem := tsk) (hp_bounds := hp_bounds); + try (by done); + [by rewrite index_mem | by rewrite -SIZE | | by rewrite INDEX -SUBST]. + apply/eqP; intro BUG; subst idx. + rewrite SUBST -{1}INDEX in NEQ; + first by rewrite eq_refl in NEQ. + by rewrite SIZE index_mem INDEX. + } + } + } } { intros hp_tsk R_hp IN; apply mem_take in IN. @@ -717,7 +686,7 @@ Module ResponseTimeIterationFP. feed (UNZIP rt_bounds); first by done. assert (EX: exists R, (tsk, R) \in rt_bounds). { - rewrite -UNZIP in INtsk; move: INtsk => /mapP EX. + rewrite set_mem -UNZIP in INtsk; move: INtsk => /mapP EX. by destruct EX as [p]; destruct p as [tsk' R]; simpl in *; subst tsk'; exists R. } des. exploit (RLIST tsk R); [by ins | by apply JOBtsk | intro COMPLETED]. diff --git a/analysis/jitter/bertogna_fp_theory.v b/analysis/jitter/bertogna_fp_theory.v index 1f78d2a488f176aad2d8e6be18c1ed6f8e3a1666..c4abdd9bc1d3909530b85f9cd3a8c881f90c7bb0 100644 --- a/analysis/jitter/bertogna_fp_theory.v +++ b/analysis/jitter/bertogna_fp_theory.v @@ -1,6 +1,6 @@ Require Import rt.util.all. Require Import rt.model.jitter.task rt.model.jitter.job rt.model.jitter.task_arrival - rt.model.jitter.schedule rt.model.jitter.platform rt.model.jitter.platform_fp + rt.model.jitter.schedule rt.model.jitter.platform rt.model.jitter.constrained_deadlines rt.model.jitter.workload rt.model.jitter.schedulability rt.model.jitter.priority rt.model.jitter.response_time rt.model.jitter.interference. Require Import rt.analysis.jitter.workload_bound rt.analysis.jitter.interference_bound_fp. @@ -8,10 +8,16 @@ From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop d Module ResponseTimeAnalysisFP. - Export JobWithJitter SporadicTaskset ScheduleOfSporadicTaskWithJitter Workload Interference - Platform PlatformFP Schedulability ResponseTime Priority SporadicTaskArrival - WorkloadBoundJitter Interference InterferenceBoundFP. + Export JobWithJitter SporadicTaskset ScheduleOfSporadicTaskWithJitter + Workload Interference Platform ConstrainedDeadlines Schedulability + ResponseTime Priority SporadicTaskArrival WorkloadBoundJitter + Interference InterferenceBoundFP. + (* In this section, we prove that any fixed point in Bertogna and + Cirinei's RTA for FP scheduling modified to account for jitter + yields a safe response-time bound. This is an extension of the + analysis found in Chapter 18.2 of Baruah et al.'s book + Multiprocessor Scheduling for Real-time Systems. *) Section ResponseTimeBound. Context {sporadic_task: eqType}. @@ -37,76 +43,82 @@ Module ResponseTimeAnalysisFP. valid_sporadic_job_with_jitter task_cost task_deadline task_jitter job_cost job_deadline job_task job_jitter j. - (* Consider any schedule such that...*) + (* Assume that we have a task set where all tasks have valid + parameters and constrained deadlines, ... *) + Variable ts: taskset_of sporadic_task. + Hypothesis H_valid_task_parameters: + valid_sporadic_taskset task_cost task_period task_deadline ts. + Hypothesis H_constrained_deadlines: + forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk. + + (* ... and that all jobs in the arrival sequence come from the task set. *) + Hypothesis H_all_jobs_from_taskset: + forall (j: JobIn arr_seq), job_task j \in ts. + + (* Next, consider any schedule such that...*) Variable num_cpus: nat. Variable sched: schedule num_cpus arr_seq. - (* ...jobs do not execute before their arrival times nor longer - than their execution costs. *) + (* ...jobs are sequential, do not execute before the jitter + has passed and nor longer than their execution costs. *) + Hypothesis H_sequential_jobs: sequential_jobs sched. Hypothesis H_jobs_execute_after_jitter: jobs_execute_after_jitter job_jitter sched. Hypothesis H_completed_jobs_dont_execute: completed_jobs_dont_execute job_cost sched. - (* Also assume that jobs are sequential and that there exists - at least one processor. *) - Hypothesis H_sequential_jobs: sequential_jobs sched. + (* Assume that there exists at least one processor. *) Hypothesis H_at_least_one_cpu: num_cpus > 0. - (* Assume that we have a task set (with no duplicates) where all jobs - come from the task set and all tasks have valid parameters and constrained deadlines. *) - Variable ts: taskset_of sporadic_task. - Hypothesis H_ts_is_a_set: uniq ts. - Hypothesis H_all_jobs_from_taskset: - forall (j: JobIn arr_seq), job_task j \in ts. - Hypothesis H_valid_task_parameters: - valid_sporadic_taskset task_cost task_period task_deadline ts. - Hypothesis H_constrained_deadlines: - forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk. + (* Consider a given FP policy, ... *) + Variable higher_eq_priority: FP_policy sporadic_task. - (* Next, consider a task tsk that is to be analyzed. *) - Variable tsk: sporadic_task. - Hypothesis task_in_ts: tsk \in ts. + (* ...and assume that the schedule is work-conserving and enforces this policy. *) + Hypothesis H_work_conserving: work_conserving job_cost job_jitter sched. + Hypothesis H_enforces_priority: + enforces_FP_policy job_cost job_task job_jitter sched higher_eq_priority. + (* Let's define some local names to avoid passing many parameters. *) Let no_deadline_is_missed_by_tsk (tsk: sporadic_task) := task_misses_no_deadline job_cost job_deadline job_task sched tsk. Let response_time_bounded_by (tsk: sporadic_task) := is_response_time_bound_of_task job_cost job_task tsk sched. - (* Assume a known response-time bound for any interfering task *) - Let task_with_response_time := (sporadic_task * time)%type. - Variable hp_bounds: seq task_with_response_time. - - (* For FP scheduling, assume there exists a fixed task priority. *) - Variable higher_eq_priority: FP_policy sporadic_task. + (* Next, we consider the response-time recurrence. + Let tsk be a task in ts that is to be analyzed. *) + Variable tsk: sporadic_task. + Hypothesis task_in_ts: tsk \in ts. - Let can_interfere_with_tsk := fp_can_interfere_with higher_eq_priority tsk. - - (* Assume that hp_bounds has exactly the tasks that interfere with tsk,... *) - Hypothesis H_hp_bounds_has_interfering_tasks: - [seq tsk_hp <- ts | can_interfere_with_tsk tsk_hp] = unzip1 hp_bounds. + (* Let is_hp_task denote whether a task is a higher-priority task + (with respect to tsk). *) + Let is_hp_task := higher_priority_task higher_eq_priority tsk. - (* ...and that all values in the pairs contain valid response-time bounds *) + (* Assume a response-time bound is known... *) + Let task_with_response_time := (sporadic_task * time)%type. + Variable hp_bounds: seq task_with_response_time. Hypothesis H_response_time_of_interfering_tasks_is_known: forall hp_tsk R, (hp_tsk, R) \in hp_bounds -> response_time_bounded_by hp_tsk (task_jitter hp_tsk + R). + + (* ... for every higher-priority task. *) + Hypothesis H_hp_bounds_has_interfering_tasks: + forall hp_tsk, + hp_tsk \in ts -> + is_hp_task hp_tsk -> + exists R, + (hp_tsk, R) \in hp_bounds. (* Assume that the response-time bounds are larger than task costs. *) Hypothesis H_response_time_bounds_ge_cost: forall hp_tsk R, (hp_tsk, R) \in hp_bounds -> R >= task_cost hp_tsk. - (* Assume that no deadline is missed by any interfering task, i.e., - response-time bound R_other <= deadline. *) + (* Assume that no deadline is missed by any higher-priority task. *) Hypothesis H_interfering_tasks_miss_no_deadlines: forall hp_tsk R, - (hp_tsk, R) \in hp_bounds -> task_jitter hp_tsk + R <= task_deadline hp_tsk. - - (* Assume that the scheduler is work-conserving and enforces priorities. *) - Hypothesis H_work_conserving: work_conserving job_cost job_jitter sched. - Hypothesis H_enforces_priority: - enforces_FP_policy job_cost job_task job_jitter sched higher_eq_priority. + (hp_tsk, R) \in hp_bounds -> + task_jitter hp_tsk + R <= task_deadline hp_tsk. (* Let R be the fixed point of Bertogna and Cirinei's recurrence, ...*) Variable R: time. @@ -114,14 +126,14 @@ Module ResponseTimeAnalysisFP. R = task_cost tsk + div_floor (total_interference_bound_fp task_cost task_period task_jitter - tsk hp_bounds R higher_eq_priority) + tsk hp_bounds R) num_cpus. (* ... and assume that jitter + R is no larger than the deadline of tsk.*) Hypothesis H_response_time_no_larger_than_deadline: task_jitter tsk + R <= task_deadline tsk. - (* In order to prove that R is a response-time bound, we first present some lemmas. *) + (* In order to prove that R is a response-time bound, we first provide some lemmas. *) Section Lemmas. (* Consider any job j of tsk. *) @@ -143,17 +155,18 @@ Module ResponseTimeAnalysisFP. Let x (tsk_other: sporadic_task) := task_interference job_cost job_task job_jitter sched j tsk_other t1 (t1 + R). - (* and X the total interference incurred by job j due to any task. *) + (* ...and X the total interference incurred by job j due to any task. *) Let X := total_interference job_cost job_jitter sched j t1 (t1 + R). (* Recall Bertogna and Cirinei's workload bound. *) Let workload_bound (tsk_other: sporadic_task) (R_other: time) := W_jitter task_cost task_period task_jitter tsk_other R_other R. - (* Also, let ts_interf be the subset of tasks that interfere with tsk. *) - Let ts_interf := [seq tsk_other <- ts | can_interfere_with_tsk tsk_other]. + (* Let hp_tasks denote the set of higher-priority tasks. *) + Let hp_tasks := [seq tsk_other <- ts | is_hp_task tsk_other]. - Section LemmasAboutInterferingTasks. + (* Now we establish results the higher-priority tasks. *) + Section LemmasAboutHPTasks. (* Let (tsk_other, R_other) be any pair of higher-priority task and response-time bound computed in previous iterations. *) @@ -161,30 +174,6 @@ Module ResponseTimeAnalysisFP. Variable R_other: time. Hypothesis H_response_time_of_tsk_other: (tsk_other, R_other) \in hp_bounds. - (* Note that tsk_other is in task set ts ...*) - Lemma bertogna_fp_tsk_other_in_ts: tsk_other \in ts. - Proof. - rename H_hp_bounds_has_interfering_tasks into UNZIP, - H_response_time_of_tsk_other into INbounds. - move: UNZIP => UNZIP. - cut (tsk_other \in ts_interf = true); - first by rewrite mem_filter; move => /andP [_ IN]. - unfold ts_interf; rewrite UNZIP. - by apply/mapP; exists (tsk_other, R_other). - Qed. - - (*... and interferes with task tsk. *) - Lemma bertogna_fp_tsk_other_interferes: can_interfere_with_tsk tsk_other. - Proof. - rename H_hp_bounds_has_interfering_tasks into UNZIP, - H_response_time_of_tsk_other into INbounds. - move: UNZIP => UNZIP. - cut (tsk_other \in ts_interf = true); - first by rewrite mem_filter; move => /andP [INTERF _]. - unfold ts_interf; rewrite UNZIP. - by apply/mapP; exists (tsk_other, R_other). - Qed. - (* Since tsk_other cannot interfere more than it executes, we show that the interference caused by tsk_other is no larger than workload bound W. *) Lemma bertogna_fp_workload_bounds_interference : @@ -193,15 +182,37 @@ Module ResponseTimeAnalysisFP. unfold response_time_bounded_by, is_response_time_bound_of_task, completed, completed_jobs_dont_execute, valid_sporadic_job in *. rename H_valid_job_parameters into PARAMS, + H_all_jobs_from_taskset into FROMTS, H_valid_task_parameters into TASK_PARAMS, H_constrained_deadlines into RESTR, H_response_time_of_interfering_tasks_is_known into RESP, H_interfering_tasks_miss_no_deadlines into NOMISS, H_response_time_bounds_ge_cost into GE_COST. unfold x, workload_bound. - have INts := bertogna_fp_tsk_other_in_ts. + destruct ([exists t: 'I_(t1 + R), + task_is_scheduled job_task sched tsk_other t]) eqn: SCHED; + last first. + { + apply negbT in SCHED; rewrite negb_exists in SCHED. + move: SCHED => /forallP SCHED. + apply leq_trans with (n := 0); last by done. + apply leq_trans with (n := \sum_(t1 <= t < t1 + R) 0); + last by rewrite big1. + apply leq_sum_nat; move => t /andP [_ LTt] _. + unfold t1 in LTt. + specialize (SCHED (Ordinal LTt)). + rewrite negb_exists in SCHED; move: SCHED => /forallP SCHED. + rewrite big1 //; intros cpu _. + specialize (SCHED cpu); apply negbTE in SCHED. + by rewrite SCHED andbF. + } + move: SCHED => /existsP [t /existsP [cpu SCHED]]. + unfold task_scheduled_on in SCHED. + destruct (sched cpu t) as [j0 |]; last by done. + assert (INts: tsk_other \in ts). + by move: SCHED => /eqP <-; rewrite FROMTS. apply leq_trans with (n := workload job_task sched tsk_other t1 (t1 + R)); - first by apply task_interference_le_workload. + first by apply task_interference_le_workload. by apply workload_bounded_by_W with (task_deadline0 := task_deadline) (job_jitter0 := job_jitter) (job_cost0 := job_cost) (job_deadline0 := job_deadline); try (by ins); last 2 first; @@ -212,7 +223,7 @@ Module ResponseTimeAnalysisFP. | by ins; apply GE_COST]. Qed. - End LemmasAboutInterferingTasks. + End LemmasAboutHPTasks. (* Next we prove some lemmas that help to derive a contradiction.*) Section DerivingContradiction. @@ -263,66 +274,11 @@ Module ResponseTimeAnalysisFP. by apply completion_monotonic with (t := i); try (by done); apply ltnW. Qed. - Let scheduled_task_other_than_tsk (t: time) (tsk_other: sporadic_task) := - task_is_scheduled job_task sched tsk_other t && - can_interfere_with_tsk tsk_other. - - (* 1) At all times that j is backlogged, all processors are busy with other tasks. *) - Lemma bertogna_fp_scheduling_invariant: - forall t, - t1 <= t < t1 + R -> - backlogged job_cost job_jitter sched j t -> - count (scheduled_task_other_than_tsk t) ts = num_cpus. - Proof. - rename H_valid_task_parameters into PARAMS, - H_job_of_tsk into JOBtsk, - H_all_jobs_from_taskset into FROMTS, - H_sporadic_tasks into SPO, - H_valid_job_parameters into JOBPARAMS, - H_constrained_deadlines into RESTR, - H_hp_bounds_has_interfering_tasks into UNZIP, - H_interfering_tasks_miss_no_deadlines into NOMISS, - H_response_time_of_interfering_tasks_is_known into PREV, - H_previous_jobs_of_tsk_completed into PREVtsk. - unfold sporadic_task_model, is_response_time_bound_of_task, - valid_sporadic_job_with_jitter in *. - move => t /andP [LEt LTt] BACK. - - apply platform_fp_cpus_busy_with_interfering_tasks with (task_cost0 := task_cost) - (task_period0 := task_period) (task_deadline0 := task_deadline) (job_task0 := job_task) - (ts0 := ts) (tsk0 := tsk) (higher_eq_priority0 := higher_eq_priority) in BACK; - try (by done); first by apply PARAMS; rewrite -JOBtsk FROMTS. - { - apply leq_trans with (n := job_arrival j + job_jitter j + R); first by done. - rewrite -addnA leq_add2l. - apply leq_trans with (n := task_deadline tsk); last by apply RESTR. - apply leq_trans with (n := task_jitter tsk + R); last by done. - by rewrite leq_add2r -JOBtsk; specialize (JOBPARAMS j); des. - } - { - intros j_other tsk_other JOBother INTERF. - move: UNZIP => UNZIP. - cut (tsk_other \in unzip1 hp_bounds = true); last first. - { - rewrite -UNZIP mem_filter; apply/andP; split; first by done. - by rewrite -JOBother; apply FROMTS. - } intro IN. - move: IN => /mapP [p IN EQ]; destruct p as [tsk' R']; simpl in *; subst tsk'. - apply completion_monotonic with (t0 := job_arrival j_other + task_jitter tsk_other + R'); - try (by done); last by rewrite -addnA; apply PREV. - by rewrite -addnA leq_add2l; apply leq_trans with (n := task_deadline tsk_other); - [by apply NOMISS | by apply RESTR; rewrite -JOBother]. - } - { - ins; apply completion_monotonic with (t0 := job_arrival j0 + task_jitter tsk + R); - try (by done); last by apply PREVtsk. - rewrite -addnA leq_add2l. - by apply leq_trans with (n := task_deadline tsk); [by done | by apply RESTR]. - } - Qed. - - (* 2) Prove that during the scheduling window of j, any job that is scheduled while j is - backlogged comes from a different task. *) + (* 1) Next, we prove that during the scheduling window of j, any job that is + scheduled while j is backlogged comes from a different task. + This follows from the fact that j is the first job not to complete + by its response-time bound, so previous jobs of j's task must have + completed by their periods and cannot be pending. *) Lemma bertogna_fp_interference_by_different_tasks : forall t j_other, t1 <= t < t1 + R -> @@ -387,11 +343,68 @@ Module ResponseTimeAnalysisFP. } Qed. - (* 3) Next, we prove that the sum of the interference of each task is equal - to the total interference multiplied by the number of processors. This - holds because interference only occurs when all processors are busy. *) - Lemma bertogna_fp_all_cpus_busy : - \sum_(tsk_k <- ts_interf) x tsk_k = X * num_cpus. + (* Let's define a predicate to identify the other tasks that are scheduled. *) + Let other_scheduled_task (t: time) (tsk_other: sporadic_task) := + task_is_scheduled job_task sched tsk_other t && + is_hp_task tsk_other. + + (* 2) Now we prove that, at all times that j is backlogged, the number + of tasks other than tsk that are scheduled is exactly the number + of processors in the system. This is required to prove lemma (3). *) + Lemma bertogna_fp_all_cpus_are_busy: + forall t, + t1 <= t < t1 + R -> + backlogged job_cost job_jitter sched j t -> + count (other_scheduled_task t) ts = num_cpus. + Proof. + rename H_valid_task_parameters into PARAMS, + H_job_of_tsk into JOBtsk, + H_all_jobs_from_taskset into FROMTS, + H_sporadic_tasks into SPO, + H_valid_job_parameters into JOBPARAMS, + H_constrained_deadlines into RESTR, + H_hp_bounds_has_interfering_tasks into HAS, + H_interfering_tasks_miss_no_deadlines into NOMISS, + H_response_time_of_interfering_tasks_is_known into PREV, + H_previous_jobs_of_tsk_completed into PREVtsk. + unfold sporadic_task_model, is_response_time_bound_of_task, + valid_sporadic_job_with_jitter in *. + move => t /andP [LEt LTt] BACK. + + apply platform_fp_cpus_busy_with_interfering_tasks with (task_cost0 := task_cost) + (task_period0 := task_period) (task_deadline0 := task_deadline) (job_task0 := job_task) + (ts0 := ts) (tsk0 := tsk) (higher_eq_priority0 := higher_eq_priority) in BACK; + try (by done); first by apply PARAMS; rewrite -JOBtsk FROMTS. + { + apply leq_trans with (n := job_arrival j + job_jitter j + R); first by done. + rewrite -addnA leq_add2l. + apply leq_trans with (n := task_deadline tsk); last by apply RESTR. + apply leq_trans with (n := task_jitter tsk + R); last by done. + by rewrite leq_add2r -JOBtsk; specialize (JOBPARAMS j); des. + } + { + intros j_other tsk_other JOBother INTERF. + feed (HAS tsk_other); first by rewrite -JOBother FROMTS. + move: (HAS INTERF) => [R' IN]. + apply completion_monotonic with (t0 := job_arrival j_other + task_jitter tsk_other + R'); + try (by done); last by rewrite -addnA; apply PREV. + by rewrite -addnA leq_add2l; apply leq_trans with (n := task_deadline tsk_other); + [by apply NOMISS | by apply RESTR; rewrite -JOBother]. + } + { + ins; apply completion_monotonic with (t0 := job_arrival j0 + task_jitter tsk + R); + try (by done); last by apply PREVtsk. + rewrite -addnA leq_add2l. + by apply leq_trans with (n := task_deadline tsk); [by done | by apply RESTR]. + } + Qed. + + + (* 3) Now we prove that, at all times that j is backlogged, the number + of tasks other than tsk that are scheduled is exactly the number + of processors in the system. This is required to prove lemma (4). *) + Lemma bertogna_fp_interference_on_all_cpus: + \sum_(tsk_k <- hp_tasks) x tsk_k = X * num_cpus. Proof. have DIFFTASK := bertogna_fp_interference_by_different_tasks. rename H_all_jobs_from_taskset into FROMTS, @@ -414,7 +427,7 @@ Module ResponseTimeAnalysisFP. apply eq_trans with (y := \sum_(cpu < num_cpus) 1); last by simpl_sum_const. apply eq_bigr; intros cpu _. move: (WORK j t BACK cpu) => [j_other /eqP SCHED]; unfold scheduled_on in *. - rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq. + rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq; destruct ts. { rewrite (eq_bigr (fun i => 0)); last by intros i DIFF; rewrite /task_scheduled_on SCHED;apply/eqP;rewrite eqb0 eq_sym. @@ -422,7 +435,7 @@ Module ResponseTimeAnalysisFP. by unfold task_scheduled_on; rewrite SCHED. } rewrite mem_filter; apply/andP; split; last by apply FROMTS. - unfold can_interfere_with_tsk, fp_can_interfere_with; apply/andP; split. + unfold is_hp_task, higher_priority_task; apply/andP; split. { rewrite -JOBtsk; apply FP with (t := t); try by done. by apply/existsP; exists cpu; apply/eqP. @@ -431,19 +444,20 @@ Module ResponseTimeAnalysisFP. by apply/existsP; exists cpu; apply/eqP. Qed. - (* Let (cardGE delta) be the number of interfering tasks whose interference - is larger than delta. *) - Let cardGE (delta: time) := count (fun i => x i >= delta) ts_interf. + (* Before stating the next lemma, let (num_tasks_exceeding delta) be the + number of interfering tasks whose interference x is larger than delta. *) + Let num_tasks_exceeding delta := count (fun i => x i >= delta) (hp_tasks). - (* 4) If there is at least one of such tasks (e.g., cardGE > 0), then the - cumulative interference caused by the complementary set of interfering - tasks fills at least (num_cpus - cardGE) processors. *) - Lemma bertogna_fp_helper_lemma : + (* 4) Now we prove that, for any delta, if (num_task_exceeding delta > 0), then the + cumulative interference caused by the complementary set of interfering tasks fills + the remaining, not-completely-full (num_cpus - num_tasks_exceeding delta) + processors. *) + Lemma bertogna_fp_interference_in_non_full_processors : forall delta, - 0 < cardGE delta < num_cpus -> - \sum_(i <- ts_interf | x i < delta) x i >= delta * (num_cpus - cardGE delta). + 0 < num_tasks_exceeding delta < num_cpus -> + \sum_(i <- hp_tasks | x i < delta) x i >= delta * (num_cpus - num_tasks_exceeding delta). Proof. - have INV := bertogna_fp_scheduling_invariant. + have INV := bertogna_fp_all_cpus_are_busy. rename H_all_jobs_from_taskset into FROMTS, H_valid_task_parameters into PARAMS, H_valid_job_parameters into JOBPARAMS, @@ -454,7 +468,7 @@ Module ResponseTimeAnalysisFP. H_constrained_deadlines into CONSTR, H_sequential_jobs into SEQ, H_enforces_priority into FP, - H_hp_bounds_has_interfering_tasks into UNZIP, + H_hp_bounds_has_interfering_tasks into HASHP, H_interfering_tasks_miss_no_deadlines into NOMISSHP. unfold sporadic_task_model, valid_sporadic_job_with_jitter, valid_sporadic_job in *. move => delta /andP [HAS LT]. @@ -463,14 +477,14 @@ Module ResponseTimeAnalysisFP. set some_interference_A := fun t => has (fun tsk_k => backlogged job_cost job_jitter sched j t && (x tsk_k >= delta) && - task_is_scheduled job_task sched tsk_k t) ts_interf. + task_is_scheduled job_task sched tsk_k t) hp_tasks. set total_interference_B := fun t => backlogged job_cost job_jitter sched j t * count (fun tsk_k => (x tsk_k < delta) && - task_is_scheduled job_task sched tsk_k t) ts_interf. + task_is_scheduled job_task sched tsk_k t) hp_tasks. apply leq_trans with ((\sum_(t1 <= t < t1 + R) - some_interference_A t) * (num_cpus - cardGE delta)). + some_interference_A t) * (num_cpus - num_tasks_exceeding delta)). { rewrite leq_mul2r; apply/orP; right. move: HAS => /hasP HAS; destruct HAS as [tsk_a INa LEa]. @@ -540,7 +554,7 @@ Module ResponseTimeAnalysisFP. by rewrite -SAMEjob; apply/existsP; exists cpu; apply/eqP. } { - assert (INTERF: fp_can_interfere_with higher_eq_priority tsk (job_task j1)). + assert (INTERF: is_hp_task (job_task j1)). { apply/andP; split; last by rewrite SAMEtsk. rewrite -JOBtsk; apply FP with (t := t); first by done. @@ -553,9 +567,8 @@ Module ResponseTimeAnalysisFP. rewrite ?JOBtsk ?SAMEtsk //. { intros j0 tsk0 JOB0 INTERF0. - assert (IN: tsk0 \in (unzip1 hp_bounds)). - by rewrite -UNZIP mem_filter; apply/andP; split; last by rewrite -JOB0 FROMTS. - move: IN => /mapP [p IN EQ]; destruct p as [tsk0' R0]; simpl in *; subst tsk0'. + feed (HASHP tsk0); first by rewrite -JOB0 FROMTS. + move: (HASHP INTERF0) => [R0 IN0]. apply completion_monotonic with (t0 := job_arrival j0 + task_jitter tsk0 + R0); try (by done). { @@ -564,7 +577,7 @@ Module ResponseTimeAnalysisFP. [by apply NOMISSHP | by apply CONSTR; rewrite -JOB0 FROMTS]. } rewrite -addnA. - by eapply H_response_time_of_interfering_tasks_is_known; first by apply IN. + by eapply H_response_time_of_interfering_tasks_is_known; first by apply IN0. } } } @@ -581,13 +594,13 @@ Module ResponseTimeAnalysisFP. [rewrite mul1n /= | by rewrite has_pred0 //]. destruct (has (fun tsk_k : sporadic_task => (delta <= x tsk_k) && - task_is_scheduled job_task sched tsk_k t) ts_interf) eqn:HAS'; + task_is_scheduled job_task sched tsk_k t) hp_tasks) eqn:HAS'; last by done. rewrite mul1n; move: HAS => /hasP [tsk_k INk LEk]. - unfold cardGE. + unfold num_tasks_exceeding. apply leq_trans with (n := num_cpus - count (fun i => (x i >= delta) && - task_is_scheduled job_task sched i t) ts_interf). + task_is_scheduled job_task sched i t) hp_tasks). { apply leq_sub2l. rewrite -2!sum1_count big_mkcond /=. @@ -596,11 +609,11 @@ Module ResponseTimeAnalysisFP. by destruct (task_is_scheduled job_task sched i t); [by rewrite andbT | by rewrite andbF]. } - rewrite -count_filter -[count _ ts_interf]count_filter. + rewrite -count_filter -[count _ hp_tasks]count_filter. eapply leq_trans with (n := count (predC (fun tsk => delta <= x tsk)) _); last by apply eq_leq, eq_in_count; red; ins; rewrite ltnNge. rewrite leq_subLR count_predC size_filter. - by apply leq_trans with (n := count (scheduled_task_other_than_tsk t) ts); + by apply leq_trans with (n := count (other_scheduled_task t) ts); [by rewrite INV | by rewrite count_filter]. } { @@ -608,7 +621,7 @@ Module ResponseTimeAnalysisFP. rewrite exchange_big /=; apply leq_sum; intros t _. destruct (backlogged job_cost job_jitter sched j t) eqn:BACK; last by ins. rewrite mul1n -sum1_count. - rewrite big_mkcond [\sum_(i <- ts_interf | _ < _) _]big_mkcond /=. + rewrite big_mkcond [\sum_(i <- hp_tasks | _ < _) _]big_mkcond /=. apply leq_sum_seq; move => tsk_k IN _. destruct (x tsk_k < delta); [rewrite andTb | by rewrite andFb]. destruct (task_is_scheduled job_task sched tsk_k t) eqn:SCHED; last by done. @@ -617,13 +630,13 @@ Module ResponseTimeAnalysisFP. } Qed. - (* 5) Next, we prove that, for any interval delta, if the sum of per-task - interference exceeds delta * num_cpus, the same applies for the - sum of the minimum between the interference and delta. *) + (* 5) Based on lemma (4), we prove that, for any interval delta, if the sum of per-task + interference exceeds (delta * num_cpus), the same applies for the + sum of the minimum of the interference and delta. *) Lemma bertogna_fp_minimum_exceeds_interference : forall delta, - \sum_(tsk_k <- ts_interf) x tsk_k >= delta * num_cpus -> - \sum_(tsk_k <- ts_interf) minn (x tsk_k) delta >= + \sum_(tsk_k <- hp_tasks) x tsk_k >= delta * num_cpus -> + \sum_(tsk_k <- hp_tasks) minn (x tsk_k) delta >= delta * num_cpus. Proof. intros delta SUMLESS. @@ -637,59 +650,63 @@ Module ResponseTimeAnalysisFP. [| by red; ins; rewrite ltnNge | by intros i COND; rewrite -ltnNge in COND; rewrite COND]. - (* Case 1: cardGE = 0 *) - destruct (~~ has (fun i => delta <= x i) ts_interf) eqn:HASa. + (* Case 1: num_tasks_exceeding = 0 *) + destruct (~~ has (fun i => delta <= x i) hp_tasks) eqn:HASa. { rewrite [\sum_(_ <- _ | _ <= _) _]big_hasC; last by apply HASa. rewrite big_seq_cond; move: HASa => /hasPn HASa. - rewrite add0n (eq_bigl (fun i => (i \in ts_interf) && true)); - last by red; intros tsk_k; destruct (tsk_k \in ts_interf) eqn:INk; + rewrite add0n (eq_bigl (fun i => (i \in hp_tasks) && true)); + last by red; intros tsk_k; destruct (tsk_k \in hp_tasks) eqn:INk; [by rewrite andTb ltnNge; apply HASa | by rewrite andFb]. by rewrite -big_seq_cond. } apply negbFE in HASa. - (* Case 2: cardGE >= num_cpus *) - destruct (cardGE delta >= num_cpus) eqn:CARD. + (* Case 2: num_tasks_exceeding >= num_cpus *) + destruct (num_tasks_exceeding delta >= num_cpus) eqn:CARD. { - apply leq_trans with (delta * cardGE delta); + apply leq_trans with (delta * num_tasks_exceeding delta); first by rewrite leq_mul2l; apply/orP; right. - unfold cardGE; rewrite -sum1_count big_distrr /=. + unfold num_tasks_exceeding; rewrite -sum1_count big_distrr /=. rewrite -[\sum_(_ <- _ | _) _]addn0. by apply leq_add; [by apply leq_sum; ins; rewrite muln1|by ins]. } apply negbT in CARD; rewrite -ltnNge in CARD. - (* Case 3: cardGE < num_cpus *) - rewrite big_const_seq iter_addn addn0; fold cardGE. - apply leq_trans with (n := delta * cardGE delta + - delta * (num_cpus - cardGE delta)); + (* Case 3: num_tasks_exceeding < num_cpus *) + rewrite big_const_seq iter_addn addn0; fold num_tasks_exceeding. + apply leq_trans with (n := delta * num_tasks_exceeding delta + + delta * (num_cpus - num_tasks_exceeding delta)); first by rewrite -mulnDr subnKC //; apply ltnW. - rewrite leq_add2l; apply bertogna_fp_helper_lemma. + rewrite leq_add2l; apply bertogna_fp_interference_in_non_full_processors. by apply/andP; split; first by rewrite -has_count. Qed. - (* 6) Now, we prove that the Bertogna's interference bound - is not enough to cover the sum of the "minimum" term over - all tasks (artifact of the proof by contradiction). *) + (* 6) Next, using lemmas (0), (3) and (5) we prove that the reduction-based + interference bound is not enough to cover the sum of the minima over all tasks + (artifact of the proof by contradiction). *) Lemma bertogna_fp_sum_exceeds_total_interference: \sum_((tsk_k, R_k) <- hp_bounds) minn (x tsk_k) (R - task_cost tsk + 1) > - total_interference_bound_fp task_cost task_period task_jitter tsk hp_bounds - R higher_eq_priority. + total_interference_bound_fp task_cost task_period task_jitter tsk hp_bounds R. Proof. have EXCEEDS := bertogna_fp_minimum_exceeds_interference. - have ALLBUSY := bertogna_fp_all_cpus_busy. + have ALLBUSY := bertogna_fp_interference_on_all_cpus. have TOOMUCH := bertogna_fp_too_much_interference. - rename H_hp_bounds_has_interfering_tasks into UNZIP, + rename H_hp_bounds_has_interfering_tasks into HAS, H_response_time_recurrence_holds into REC. - apply leq_trans with (n := \sum_(tsk_k <- ts_interf) minn (x tsk_k) (R - task_cost tsk + 1)); + apply leq_trans with (n := \sum_(tsk_k <- hp_tasks) minn (x tsk_k) (R - task_cost tsk + 1)); last first. { rewrite (eq_bigr (fun i => minn (x (fst i)) (R - task_cost tsk + 1))); last by ins; destruct i. - rewrite big_filter. - have MAP := big_map (fun x => fst x) (fun i => true) (fun i => minn (x i) - (R - task_cost tsk + 1)). - by unfold unzip1 in *; rewrite -MAP -UNZIP -big_filter. + have MAP := @big_map _ 0 addn _ _ (fun x => fst x) hp_bounds (fun x => true) (fun y => minn (x y) (R - task_cost tsk + 1)). + rewrite -MAP. + apply leq_sum_sub_uniq; first by apply filter_uniq; destruct ts. + red; move => tsk0 IN0. + rewrite mem_filter in IN0; move: IN0 => /andP [INTERF0 IN0]. + apply/mapP. + feed (HAS tsk0); first by done. + move: (HAS INTERF0) => [R0 IN]. + by exists (tsk0, R0). } apply ltn_div_trunc with (d := num_cpus); first by apply H_at_least_one_cpu. @@ -703,9 +720,11 @@ Module ResponseTimeAnalysisFP. by rewrite leq_mul2r; apply/orP; right; apply TOOMUCH. Qed. - (* 7) After concluding that the sum of the minimum exceeds (R' - e_i + 1), - we prove that there exists a tuple (tsk_k, R_k) such that - min (x_k, R' - e_i + 1) > min (W_k, R' - e_i + 1). *) + (* 7) After concluding that the sum of the minima exceeds (R - e_i + 1), + we prove that there exists a tuple (tsk_k, R_k) that satisfies + min (x_k, R - e_i + 1) > min (W_k', R - e_i + 1). + This implies that x_k > W_k', which is of course a contradiction, + since W_k is a valid task interference bound. *) Lemma bertogna_fp_exists_task_that_exceeds_bound : exists tsk_k R_k, (tsk_k, R_k) \in hp_bounds /\ @@ -713,27 +732,25 @@ Module ResponseTimeAnalysisFP. minn (workload_bound tsk_k R_k) (R - task_cost tsk + 1)). Proof. have SUM := bertogna_fp_sum_exceeds_total_interference. - have INTERFk := bertogna_fp_tsk_other_interferes. - rename H_hp_bounds_has_interfering_tasks into UNZIP. + rename H_hp_bounds_has_interfering_tasks into HASHP. assert (HAS: has (fun tup : task_with_response_time => let (tsk_k, R_k) := tup in (minn (x tsk_k) (R - task_cost tsk + 1) > minn (workload_bound tsk_k R_k)(R - task_cost tsk + 1))) hp_bounds). { - apply/negP; unfold not; intro NOTHAS. - move: NOTHAS => /negP /hasPn ALL. - rewrite -[_ < _]negbK in SUM. - move: SUM => /negP SUM; apply SUM; rewrite -leqNgt. - unfold total_interference_bound_fp. - rewrite [\sum_(i <- _ | let '(tsk_other, _) := i in _)_]big_mkcond. - rewrite big_seq_cond [\sum_(i <- _ | true) _]big_seq_cond. - apply leq_sum; move => tsk_k /andP [HPk _]; destruct tsk_k as [tsk_k R_k]. - specialize (ALL (tsk_k, R_k) HPk). - rewrite -leqNgt in ALL. - specialize (INTERFk tsk_k R_k HPk). - fold (can_interfere_with_tsk); rewrite INTERFk. - by apply ALL. + apply/negP; unfold not; intro NOTHAS. + move: NOTHAS => /negP /hasPn ALL. + rewrite -[_ < _]negbK in SUM. + move: SUM => /negP SUM; apply SUM; rewrite -leqNgt. + rewrite (eq_bigr (fun i => minn (x (fst i)) (R - task_cost tsk + 1))); + last by ins; destruct i. + unfold total_interference_bound_fp. + rewrite big_seq_cond. + rewrite [\sum_(_ <- _ | true)_]big_seq_cond. + apply leq_sum. + intros p; rewrite andbT; intros IN. + by specialize (ALL p IN); destruct p; rewrite leqNgt. } move: HAS => /hasP HAS; destruct HAS as [[tsk_k R_k] HPk MINk]; exists tsk_k, R_k. by repeat split. diff --git a/analysis/jitter/interference_bound.v b/analysis/jitter/interference_bound.v index 41df7e005d041cffd16bcd3742acf153e3005d6a..27b9de55059802e7fd10eea81bf09e074bcffe88 100644 --- a/analysis/jitter/interference_bound.v +++ b/analysis/jitter/interference_bound.v @@ -36,25 +36,11 @@ Module InterferenceBoundJitter. (* Based on the workload bound, Bertogna and Cirinei define the following interference bound for a task. *) Definition interference_bound_generic := - minn (W_jitter task_cost task_period task_jitter tsk_other R_other delta) (delta - (task_cost tsk) + 1). + minn (W_jitter task_cost task_period task_jitter tsk_other R_other delta) + (delta - task_cost tsk + 1). End PerTask. - Section AllTasks. - - (* Assume an FP policy. *) - Variable higher_eq_priority: FP_policy sporadic_task. - - Let can_interfere_with_tsk := fp_can_interfere_with higher_eq_priority tsk. - - (* The total interference incurred by tsk is bounded by the sum - of individual task interferences. *) - Definition total_interference_bound_fp := - \sum_((tsk_other, R_other) <- R_prev | can_interfere_with_tsk tsk_other) - interference_bound_generic (tsk_other, R_other). - - End AllTasks. - End Definitions. End InterferenceBoundJitter. \ No newline at end of file diff --git a/analysis/jitter/interference_bound_edf.v b/analysis/jitter/interference_bound_edf.v index 124063581b81453cde8ae327821e3b29d4fa4dca..48d2fad71370a668c0d5f934aabd1de925dc3eb7 100644 --- a/analysis/jitter/interference_bound_edf.v +++ b/analysis/jitter/interference_bound_edf.v @@ -66,7 +66,7 @@ Module InterferenceBoundEDFJitter. (* ... and an interval length delta. *) Variable delta: time. - Section PerTask. + Section RecallInterferenceBounds. Variable tsk_R: task_with_response_time. Let tsk_other := fst tsk_R. @@ -84,19 +84,21 @@ Module InterferenceBoundEDFJitter. Definition interference_bound_edf := minn basic_interference_bound edf_specific_bound. - End PerTask. + End RecallInterferenceBounds. - Section AllTasks. + (* Next we define the computation of the total interference for APA scheduling. *) + Section TotalInterference. - Let can_interfere_with_tsk := jldp_can_interfere_with tsk. + (* Recall the definition of a different task (with respect to tsk). *) + Let other_task := different_task tsk. (* The total interference incurred by tsk is bounded by the sum - of individual task interferences. *) + of individual task interferences of the other tasks. *) Definition total_interference_bound_edf := - \sum_((tsk_other, R_other) <- R_prev | can_interfere_with_tsk tsk_other) + \sum_((tsk_other, R_other) <- R_prev | other_task tsk_other) interference_bound_edf (tsk_other, R_other). - End AllTasks. + End TotalInterference. End TotalInterferenceBoundEDF. diff --git a/analysis/jitter/interference_bound_fp.v b/analysis/jitter/interference_bound_fp.v index b60d89c3d6651a5d73b827d6ce42ff9690fa8a21..4bc8c267e84e228ea1d77d49a915c0f3f39fea19 100644 --- a/analysis/jitter/interference_bound_fp.v +++ b/analysis/jitter/interference_bound_fp.v @@ -31,13 +31,13 @@ Module InterferenceBoundFP. (* Assume an FP policy. *) Variable higher_eq_priority: FP_policy sporadic_task. - Let can_interfere_with_tsk := fp_can_interfere_with higher_eq_priority tsk. + (* Recall the interference bound. *) Let total_interference_bound := interference_bound_generic task_cost task_period task_jitter tsk delta. (* The total interference incurred by tsk is bounded by the sum of individual task interferences. *) Definition total_interference_bound_fp := - \sum_((tsk_other, R_other) <- R_prev | can_interfere_with_tsk tsk_other) + \sum_((tsk_other, R_other) <- R_prev) total_interference_bound (tsk_other, R_other). End Definitions. diff --git a/analysis/parallel/bertogna_edf_comp.v b/analysis/parallel/bertogna_edf_comp.v index a0b40e4ecee8823be52b2558f08ff24a75e90bd7..5ec5b9b7093024e9150231ac0ed5cb1ef6532e0b 100755 --- a/analysis/parallel/bertogna_edf_comp.v +++ b/analysis/parallel/bertogna_edf_comp.v @@ -7,7 +7,7 @@ Module ResponseTimeIterationEDF. Import ResponseTimeAnalysisEDF. (* In this section, we define the algorithm for Bertogna and Cirinei's - response-time analysis for EDF scheduling. *) + response-time analysis for EDF scheduling with parallel jobs. *) Section Analysis. Context {sporadic_task: eqType}. @@ -27,7 +27,7 @@ Module ResponseTimeIterationEDF. (* Consider a platform with num_cpus processors. *) Variable num_cpus: nat. - (* First, recall the interference bound under EDF, ... *) + (* First, recall the jitter-aware interference bound for EDF, ... *) Let I (rt_bounds: seq task_with_response_time) (tsk: sporadic_task) (delta: time) := total_interference_bound_edf task_cost task_period task_deadline tsk rt_bounds delta. @@ -57,7 +57,7 @@ Module ResponseTimeIterationEDF. (* To compute the response-time bounds of the entire task set, We start the iteration with a sequence of tasks and costs: <(task1, cost1), (task2, cost2), ...>. *) - Let initial_state (ts: taskset_of sporadic_task) := + Let initial_state (ts: seq sporadic_task) := map (fun t => (t, task_cost t)) ts. (* Then, we successively update the the response-time bounds based @@ -68,14 +68,14 @@ Module ResponseTimeIterationEDF. (* To ensure that the procedure converges, we run the iteration a "sufficient" number of times: task_deadline tsk - task_cost tsk + 1. This corresponds to the time complexity of the procedure. *) - Let max_steps (ts: taskset_of sporadic_task) := + Let max_steps (ts: seq sporadic_task) := \sum_(tsk <- ts) (task_deadline tsk - task_cost tsk) + 1. (* This yields the following definition for the RTA. At the end of the iteration, we check if all computed response-time bounds are less than or equal to the deadline, in which case they are valid. *) - Definition edf_claimed_bounds (ts: taskset_of sporadic_task) := + Definition edf_claimed_bounds (ts: seq sporadic_task) := let R_values := iter (max_steps ts) edf_rta_iteration (initial_state ts) in if (all R_le_deadline R_values) then Some R_values @@ -83,7 +83,7 @@ Module ResponseTimeIterationEDF. (* The schedulability test simply checks if we got a list of response-time bounds (i.e., if the computation did not fail). *) - Definition edf_schedulable (ts: taskset_of sporadic_task) := + Definition edf_schedulable (ts: seq sporadic_task) := edf_claimed_bounds ts != None. (* In the following section, we prove several helper lemmas about the @@ -205,8 +205,8 @@ Module ResponseTimeIterationEDF. of the iteration at (max_steps ts) is equal to the value at (max_steps ts) + 1. *) Section Convergence. - (* Consider any valid task set. *) - Variable ts: taskset_of sporadic_task. + (* Consider any sequence of tasks with valid parameters. *) + Variable ts: seq sporadic_task. Hypothesis H_valid_task_parameters: valid_sporadic_taskset task_cost task_period task_deadline ts. @@ -395,11 +395,11 @@ Module ResponseTimeIterationEDF. assert (SUBST: forall l delta, \sum_(j <- l | let '(tsk_other, _) := j in - jldp_can_interfere_with tsk_i tsk_other) + different_task tsk_i tsk_other) (let '(tsk_other, R_other) := j in interference_bound_edf task_cost task_period task_deadline tsk_i delta (tsk_other, R_other)) = - \sum_(j <- l | jldp_can_interfere_with tsk_i (fst j)) + \sum_(j <- l | different_task tsk_i (fst j)) interference_bound_edf task_cost task_period task_deadline tsk_i delta j). { intros l x; clear -l. @@ -444,13 +444,13 @@ Module ResponseTimeIterationEDF. } move: GE_COST => /allP GE_COST. - assert (LESUM: \sum_(j <- x1' | jldp_can_interfere_with tsk_i (fst j)) - interference_bound_edf task_cost task_period task_deadline tsk_i delta j <= \sum_(j <- x2' | jldp_can_interfere_with tsk_i (fst j)) + assert (LESUM: \sum_(j <- x1' | different_task tsk_i (fst j)) + interference_bound_edf task_cost task_period task_deadline tsk_i delta j <= \sum_(j <- x2' | different_task tsk_i (fst j)) interference_bound_edf task_cost task_period task_deadline tsk_i delta' j). { set elem := (tsk0, R0); rewrite 2!(big_nth elem). rewrite -SIZE. - rewrite big_mkcond [\sum_(_ <- _ | jldp_can_interfere_with _ _)_]big_mkcond. + rewrite big_mkcond [\sum_(_ <- _ | different_task _ _)_]big_mkcond. rewrite big_seq_cond [\sum_(_ <- _ | true) _]big_seq_cond. apply leq_sum; intros j; rewrite andbT; intros INj. rewrite mem_iota add0n subn0 in INj; move: INj => /andP [_ INj]. @@ -459,7 +459,7 @@ Module ResponseTimeIterationEDF. have MAP := @nth_map _ elem _ tsk0 (fun x => fst x). by rewrite -2?MAP -?SIZE //; f_equal. } rewrite -FSTeq. - destruct (jldp_can_interfere_with tsk_i (fst (nth elem x1' j))) eqn:INTERF; + destruct (different_task tsk_i (fst (nth elem x1' j))) eqn:INTERF; last by done. { exploit (LE elem); [by rewrite /= SIZE | | intro LEj]. @@ -485,7 +485,7 @@ Module ResponseTimeIterationEDF. by apply interference_bound_edf_monotonic. } } - destruct (jldp_can_interfere_with tsk_i tsk0) eqn:INTERFtsk0; last by done. + destruct (different_task tsk_i tsk0) eqn:INTERFtsk0; last by done. apply leq_add; last by done. { exploit (LE (tsk0, R0)); [by rewrite /= SIZE | | intro LEj]; @@ -816,15 +816,13 @@ Module ResponseTimeIterationEDF. Qed. (* Therefore, with regard to the response-time bound recurrence, ...*) - Let rt_recurrence (tsk: sporadic_task) (rt_bounds: seq task_with_response_time) (R: time) := - task_cost tsk + div_floor (I rt_bounds tsk R) num_cpus. (* ..., the individual response-time bounds (elements of the list) are also fixed points. *) Theorem edf_claimed_bounds_finds_fixed_point_for_each_bound : forall tsk R rt_bounds, edf_claimed_bounds ts = Some rt_bounds -> (tsk, R) \in rt_bounds -> - R = rt_recurrence tsk rt_bounds R. + R = edf_response_time_bound rt_bounds tsk R. Proof. intros tsk R rt_bounds SOME IN. have CONV := edf_claimed_bounds_finds_fixed_point_of_list rt_bounds. @@ -854,17 +852,14 @@ Module ResponseTimeIterationEDF. Section MainProof. - (* Consider a task set ts. *) + (* Consider a task set ts where... *) Variable ts: taskset_of sporadic_task. - (* Assume the task set has no duplicates, ... *) - Hypothesis H_ts_is_a_set: uniq ts. - - (* ...all tasks have valid parameters, ... *) + (* ...all tasks have valid parameters ... *) Hypothesis H_valid_task_parameters: valid_sporadic_taskset task_cost task_period task_deadline ts. - (* ...constrained deadlines, ...*) + (* ...and constrained deadlines.*) Hypothesis H_constrained_deadlines: forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk. diff --git a/analysis/parallel/bertogna_edf_theory.v b/analysis/parallel/bertogna_edf_theory.v index 8448eaf546f939352f1c84c1c972f12e63e2a21c..3a7fafdff6f5523cc9b2b722d580ea1579bfb7e1 100644 --- a/analysis/parallel/bertogna_edf_theory.v +++ b/analysis/parallel/bertogna_edf_theory.v @@ -12,8 +12,11 @@ Module ResponseTimeAnalysisEDF. Priority SporadicTaskArrival WorkloadBound InterferenceBoundEDF Interference Platform. - (* In this section, we prove that Bertogna and Cirinei's RTA yields - safe response-time bounds. *) + (* In this section, we prove that any fixed point in Bertogna and + Cirinei's RTA for EDF scheduling modified to consider (potentially) + parallel jobs yields a safe response-time bound. This is an extension + of the analysis found in Chapter 17.1.2 of Baruah et al.'s book + Multiprocessor Scheduling for Real-time Systems. *) Section ResponseTimeBound. Context {sporadic_task: eqType}. @@ -36,7 +39,19 @@ Module ResponseTimeAnalysisEDF. forall (j: JobIn arr_seq), valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j. - (* Consider any schedule such that...*) + (* Assume that we have a task set where all tasks have valid + parameters and constrained deadlines, ... *) + Variable ts: taskset_of sporadic_task. + Hypothesis H_valid_task_parameters: + valid_sporadic_taskset task_cost task_period task_deadline ts. + Hypothesis H_constrained_deadlines: + forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk. + + (* ... and that all jobs in the arrival sequence come from the task set. *) + Hypothesis H_all_jobs_from_taskset: + forall (j: JobIn arr_seq), job_task j \in ts. + + (* Next, consider any schedule such that...*) Variable num_cpus: nat. Variable sched: schedule num_cpus arr_seq. @@ -48,33 +63,27 @@ Module ResponseTimeAnalysisEDF. completed_jobs_dont_execute job_cost sched. (* Assume that there exists at least one processor. *) - Hypothesis H_at_least_one_cpu : - num_cpus > 0. - - (* Assume that we have a task set ts such that all jobs come from - the task set, and all tasks have valid parameters and - constrained deadlines. *) - Variable ts: taskset_of sporadic_task. - Hypothesis H_all_jobs_from_taskset: - forall (j: JobIn arr_seq), job_task j \in ts. - Hypothesis H_valid_task_parameters: - valid_sporadic_taskset task_cost task_period task_deadline ts. - Hypothesis H_constrained_deadlines: - forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk. + Hypothesis H_at_least_one_cpu: num_cpus > 0. + (* Assume that the schedule is a work-conserving EDF schedule. *) + Hypothesis H_work_conserving: work_conserving job_cost sched. + Hypothesis H_edf_policy: enforces_JLDP_policy job_cost sched (EDF job_deadline). + + (* Let's define some local names to avoid passing many parameters. *) Let no_deadline_is_missed_by_tsk (tsk: sporadic_task) := task_misses_no_deadline job_cost job_deadline job_task sched tsk. Let response_time_bounded_by (tsk: sporadic_task) := is_response_time_bound_of_task job_cost job_task tsk sched. - (* Assume a known response-time bound R is known... *) + (* Next we consider the response-time recurrence. + Assume that a response-time bound R is known... *) Let task_with_response_time := (sporadic_task * time)%type. Variable rt_bounds: seq task_with_response_time. - (* ...for any task in the task set. *) + (* ...for any task in the task set, ... *) Hypothesis H_rt_bounds_contains_all_tasks: unzip1 rt_bounds = ts. - (* Also, assume that R is a fixed-point of the response-time recurrence, ... *) + (* ...where R is a fixed-point of the response-time recurrence, ... *) Let I (tsk: sporadic_task) (delta: time) := total_interference_bound_edf task_cost task_period task_deadline tsk rt_bounds delta. Hypothesis H_response_time_is_fixed_point : @@ -87,16 +96,7 @@ Module ResponseTimeAnalysisEDF. forall tsk_other R, (tsk_other, R) \in rt_bounds -> R <= task_deadline tsk_other. - (* Assume that we have a work-conserving EDF scheduler. *) - Hypothesis H_work_conserving: work_conserving job_cost sched. - Hypothesis H_edf_policy: enforces_JLDP_policy job_cost sched (EDF job_deadline). - - (* Assume that the task set has no duplicates. This is required to - avoid problems when counting tasks (for example, when stating - that the number of interfering tasks is at most num_cpus). *) - Hypothesis H_ts_is_a_set : uniq ts. - - (* In order to prove that R is a response-time bound, we first present some lemmas. *) + (* In order to prove that R is a response-time bound, we first provide some lemmas. *) Section Lemmas. (* Let (tsk, R) be any task to be analyzed, with its response-time bound R. *) @@ -111,7 +111,7 @@ Module ResponseTimeAnalysisEDF. (* Assume that job j did not complete on time, ... *) Hypothesis H_j_not_completed: ~~ completed job_cost sched j (job_arrival j + R). - (* and that it is the first job not to satisfy its response-time bound. *) + (* ...and that it is the first job not to satisfy its response-time bound. *) Hypothesis H_all_previous_jobs_completed_on_time : forall (j_other: JobIn arr_seq) tsk_other R_other, job_task j_other = tsk_other -> @@ -124,7 +124,7 @@ Module ResponseTimeAnalysisEDF. task_interference job_cost job_task sched j tsk_other (job_arrival j) (job_arrival j + R). - (* and X the total interference incurred by job j due to any task. *) + (* ...and X the total interference incurred by job j due to any task. *) Let X := total_interference job_cost sched j (job_arrival j) (job_arrival j + R). (* Recall Bertogna and Cirinei's workload bound ... *) @@ -139,9 +139,14 @@ Module ResponseTimeAnalysisEDF. Let interference_bound (tsk_other: sporadic_task) (R_other: time) := interference_bound_edf task_cost task_period task_deadline tsk R (tsk_other, R_other). - (* Also, let ts_interf be the subset of tasks that interfere with tsk. *) - Let ts_interf := [seq tsk_other <- ts | jldp_can_interfere_with tsk tsk_other]. + (* Based on the definition of a different task, ... *) + Let other_task := different_task tsk. + + (* ...let other_tasks denote the set of tasks that are different from tsk. *) + Let other_tasks := + [seq tsk_other <- ts | other_task tsk_other]. + (* Now we establish results the interfering tasks. *) Section LemmasAboutInterferingTasks. (* Let (tsk_other, R_other) be any pair of higher-priority task and @@ -153,7 +158,7 @@ Module ResponseTimeAnalysisEDF. (* Note that tsk_other is in task set ts ...*) Lemma bertogna_edf_tsk_other_in_ts: tsk_other \in ts. Proof. - by rewrite -H_rt_bounds_contains_all_tasks; apply/mapP; exists (tsk_other, R_other). + by rewrite set_mem -H_rt_bounds_contains_all_tasks; apply/mapP; exists (tsk_other, R_other). Qed. (* Also, R_other is larger than the cost of tsk_other. *) @@ -253,9 +258,11 @@ Module ResponseTimeAnalysisEDF. (* 1) Next, we prove that the sum of the interference of each task is equal to the total interference multiplied by the number of processors. This - holds because interference only occurs when all processors are busy. *) - Lemma bertogna_edf_all_cpus_busy : - \sum_(tsk_k <- ts_interf) x tsk_k = X * num_cpus. + holds because interference only occurs when all processors are busy. + With this lemma we can relate per-task interference with the total + interference incurred by j (backlogged time). *) + Lemma bertogna_edf_interference_on_all_cpus : + \sum_(tsk_k <- other_tasks) x tsk_k = X * num_cpus. Proof. rename H_all_jobs_from_taskset into FROMTS, H_valid_task_parameters into PARAMS, @@ -286,7 +293,7 @@ Module ResponseTimeAnalysisEDF. apply eq_bigr; intros cpu _. specialize (WORK j t BACK cpu); des. move: WORK => /eqP SCHED. - rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq. + rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq; destruct ts. { rewrite (eq_bigr (fun i => 0)); last by intros i DIFF; rewrite /task_scheduled_on SCHED;apply/eqP;rewrite eqb0 eq_sym. @@ -294,7 +301,7 @@ Module ResponseTimeAnalysisEDF. by unfold task_scheduled_on; rewrite SCHED. } rewrite mem_filter; apply/andP; split; last by apply FROMTS. - unfold jldp_can_interfere_with. + unfold different_task. apply/eqP; red; intro SAMEtsk. assert (SCHED': scheduled sched j_other t). { @@ -332,31 +339,30 @@ apply scheduled_implies_pending with (job_cost0 := job_cost) in PENDING; try (by } Qed. - (* 2) Now, we prove that the Bertogna's interference bound - is not enough to cover the sum of the "minimum" term over - all tasks (artifact of the proof by contradiction). *) + (* 2) Next, using lemmas (0) and (1) we prove that the reduction-based + interference bound is not enough to cover the sum of the minima over all tasks + (artifact of the proof by contradiction). *) Lemma bertogna_edf_sum_exceeds_total_interference: - \sum_((tsk_other, R_other) <- rt_bounds | jldp_can_interfere_with tsk tsk_other) + \sum_((tsk_other, R_other) <- rt_bounds | other_task tsk_other) x tsk_other > I tsk R. Proof. have GE_COST := bertogna_edf_R_other_ge_cost. - have ALLBUSY := bertogna_edf_all_cpus_busy. + have ALLBUSY := bertogna_edf_interference_on_all_cpus. have TOOMUCH := bertogna_edf_too_much_interference. rename H_rt_bounds_contains_all_tasks into UNZIP, H_response_time_is_fixed_point into REC. - apply leq_trans with (n := \sum_(tsk_other <- ts_interf) x tsk_other); + apply leq_trans with (n := \sum_(tsk_other <- other_tasks) x tsk_other); last first. { rewrite (eq_bigr (fun i => x (fst i))); last by ins; destruct i. - assert (FILTER: filter (jldp_can_interfere_with tsk) (unzip1 rt_bounds) = - filter (jldp_can_interfere_with tsk) ts). + assert (FILTER: filter (different_task tsk) (unzip1 rt_bounds) = + filter (different_task tsk) ts). by f_equal. - unfold ts_interf; rewrite -FILTER; clear FILTER. + unfold other_tasks; rewrite -FILTER; clear FILTER. rewrite -[\sum_(_ <- rt_bounds | _)_]big_filter. assert (SUBST: [seq i <- rt_bounds - | let '(tsk_other, _) := i in - jldp_can_interfere_with tsk tsk_other] = [seq i <- rt_bounds - | jldp_can_interfere_with tsk (fst i)]). + | let '(tsk_other, _) := i in other_task tsk_other] = + [seq i <- rt_bounds| other_task (fst i)]). { by apply eq_filter; red; intro i; destruct i. } rewrite SUBST; clear SUBST. @@ -373,9 +379,11 @@ apply scheduled_implies_pending with (job_cost0 := job_cost) in PENDING; try (by by rewrite leq_mul2r; apply/orP; right; apply TOOMUCH. Qed. - (* 3) After concluding that the sum of the minimum exceeds (R - e_i + 1), - we prove that there exists a tuple (tsk_k, R_k) such that - min (x_k, R - e_i + 1) > min (W_k, edf_bound, R - e_i + 1). *) + (* 3) After concluding that the sum of the minima exceeds (R - e_i + 1), + we prove that there exists a tuple (tsk_k, R_k) that satisfies + min (x_k, R - e_i + 1) > min (W_k, I_edf, R - e_i + 1). + This implies that either x_k > W_k or x_k > I_edf, which is a contradiction, + since both W_k and I_edf are valid task interference bounds. *) Lemma bertogna_edf_exists_task_that_exceeds_bound : exists tsk_other R_other, (tsk_other, R_other) \in rt_bounds /\ @@ -387,7 +395,7 @@ apply scheduled_implies_pending with (job_cost0 := job_cost) in PENDING; try (by rename H_rt_bounds_contains_all_tasks into UNZIP. assert (HAS: has (fun tup : task_with_response_time => let (tsk_other, R_other) := tup in - (tsk_other \in ts) && jldp_can_interfere_with tsk tsk_other && + (tsk_other \in ts) && other_task tsk_other && (x tsk_other > interference_bound tsk_other R_other)) rt_bounds). { diff --git a/analysis/parallel/bertogna_fp_comp.v b/analysis/parallel/bertogna_fp_comp.v index 58beffe3dfbbd92ffbdc246df921df30255d25a6..0656ea907532feef14f5728cf3742e3670f9dfec 100644 --- a/analysis/parallel/bertogna_fp_comp.v +++ b/analysis/parallel/bertogna_fp_comp.v @@ -7,7 +7,7 @@ Module ResponseTimeIterationFP. Import ResponseTimeAnalysisFP. (* In this section, we define the algorithm of Bertogna and Cirinei's - response-time analysis for FP scheduling. *) + response-time analysis for FP scheduling with parallel jobs. *) Section Analysis. Context {sporadic_task: eqType}. @@ -47,8 +47,7 @@ Module ResponseTimeIterationFP. iter step (fun t => task_cost tsk + div_floor - (total_interference_bound_fp task_cost task_period tsk - R_prev t higher_priority) + (total_interference_bound_fp task_cost task_period R_prev t) num_cpus) (task_cost tsk). @@ -75,12 +74,12 @@ Module ResponseTimeIterationFP. (* The response-time analysis for a given task set is defined as a left-fold (reduce) based on the function above. This either returns a list of task and response-time bounds, or None. *) - Definition fp_claimed_bounds (ts: taskset_of sporadic_task) := + Definition fp_claimed_bounds (ts: seq sporadic_task) := foldl fp_bound_of_task (Some [::]) ts. (* The schedulability test simply checks if we got a list of response-time bounds (i.e., if the computation did not fail). *) - Definition fp_schedulable (ts: taskset_of sporadic_task) := + Definition fp_schedulable (ts: seq sporadic_task) := fp_claimed_bounds ts != None. (* In the following section, we prove several helper lemmas about the @@ -258,8 +257,8 @@ Module ResponseTimeIterationFP. Lemma per_task_rta_fold : forall tsk rt_bounds, task_cost tsk + - div_floor (total_interference_bound_fp task_cost task_period tsk rt_bounds - (per_task_rta tsk rt_bounds (max_steps tsk)) higher_priority) num_cpus + div_floor (total_interference_bound_fp task_cost task_period rt_bounds + (per_task_rta tsk rt_bounds (max_steps tsk))) num_cpus = per_task_rta tsk rt_bounds (max_steps tsk).+1. Proof. by done. @@ -274,14 +273,13 @@ Module ResponseTimeIterationFP. (* Consider a list of previous tasks and a task tsk to be analyzed. *) Variable ts: taskset_of sporadic_task. - (* Assume that the task set doesn't contain duplicates and is sorted by priority, ... *) - Hypothesis H_task_set_is_a_set: uniq ts. + (* Assume that the task set is sorted by unique priorities, ... *) Hypothesis H_task_set_is_sorted: sorted higher_priority ts. + Hypothesis H_task_set_has_unique_priorities: + FP_is_antisymmetric_over_task_set higher_priority ts. - (* ...the priority order is strict (<), ...*) - Hypothesis H_priority_irreflexive: irreflexive higher_priority. - Hypothesis H_priority_transitive: transitive higher_priority. - Hypothesis H_priority_antissymetric: antisymmetric higher_priority. + (* ...the priority order is transitive, ...*) + Hypothesis H_priority_transitive: FP_is_transitive higher_priority. (* ... and that the response-time analysis succeeds. *) Variable hp_bounds: seq task_with_response_time. @@ -292,61 +290,20 @@ Module ResponseTimeIterationFP. Variable elem: sporadic_task. Let TASK := nth elem ts. - (* Then, the tasks in the prefix of fp_claimed_bounds are exactly interfering tasks - under FP scheduling.*) - Lemma fp_claimed_bounds_interf: - forall idx, + (* We prove that higher-priority tasks have smaller index. *) + Lemma fp_claimed_bounds_hp_tasks_have_smaller_index : + forall hp_idx idx, + hp_idx < size ts -> idx < size ts -> - [seq tsk_hp <- ts | fp_can_interfere_with higher_priority (TASK idx) tsk_hp] = take idx ts. + hp_idx != idx -> + higher_priority (TASK hp_idx) (TASK idx) -> + hp_idx < idx. Proof. - rename H_task_set_is_sorted into SORT, - H_task_set_is_a_set into UNIQ, - H_priority_antissymetric into ANTI, - H_priority_irreflexive into IRR. - induction idx. - { - intros LT. - destruct ts as [| tsk0 ts']; [by done | simpl in SORT]. - unfold fp_can_interfere_with; rewrite /= eq_refl andbF. - apply eq_trans with (y := filter pred0 ts'); - last by apply filter_pred0. - apply eq_in_filter; red; intros x INx; rewrite /TASK /=. - destruct (x != tsk0) eqn:SAME; rewrite ?andbT ?andbF //. - apply negbTE; apply/negP; unfold not; intro HP. - move: SAME => /eqP SAME; apply SAME; clear SAME. - apply ANTI; apply/andP; split; first by done. - apply order_path_min in SORT; last by done. - by move: SORT => /allP SORT; apply SORT. - } - { - intros LT. - generalize LT; intro LT'; apply ltSnm in LT. - feed IHidx; first by done. - rewrite -filter_idx_le_takeS //. - apply eq_in_filter; red; intros x INx. - unfold fp_can_interfere_with. - generalize INx; intro SUBST; apply nth_index with (x0 := elem) in SUBST. - rewrite -SUBST; clear SUBST. - rewrite index_uniq; [ | by rewrite index_mem | by done]. - apply/idP/idP. - { - move => /andP [HP DIFF]. - unfold TASK in *. - apply sorted_uniq_rel_implies_le_idx in HP; try (by done); - last by rewrite index_mem. - by rewrite leq_eqVlt in HP; move: HP => /orP [/eqP SAME | LESS]; - first by rewrite SAME eq_refl in DIFF. - } - { - intros LEidx; apply/andP; split; - first by apply sorted_lt_idx_implies_rel. - apply/eqP; red; intro BUG. - eapply f_equal with (f := fun x => index x ts) in BUG. - rewrite nth_index in BUG; last by done. - rewrite BUG in LEidx. - by rewrite index_uniq // ltnn in LEidx. - } - } + unfold TASK; clear TASK. + rename ts into ts'; destruct ts' as [ts UNIQ]; simpl in *. + intros hp_idx idx LThp LT NEQ HP. + rewrite ltn_neqAle; apply/andP; split; first by done. + by apply sorted_rel_implies_le_idx with (leT := higher_priority) (s := ts) (x0 := elem). Qed. End HighPriorityTasks. @@ -355,7 +312,7 @@ Module ResponseTimeIterationFP. Section Convergence. (* Consider any set of higher-priority tasks. *) - Variable ts_hp: taskset_of sporadic_task. + Variable ts_hp: seq sporadic_task. (* Assume that the response-time analysis succeeds for the higher-priority tasks. *) Variable rt_bounds: seq task_with_response_time. @@ -384,22 +341,19 @@ Module ResponseTimeIterationFP. intros x1 x2 LEx; unfold f, per_task_rta. apply fun_mon_iter_mon; [by ins | by ins; apply leq_addr |]. clear LEx x1 x2; intros x1 x2 LEx. - unfold div_floor, total_interference_bound_fp. - rewrite big_seq_cond [\sum_(i <- _ | let '(tsk_other, _) := i in - _ && (tsk_other != tsk))_]big_seq_cond. - rewrite leq_add2l leq_div2r // leq_sum //. - - intros i; destruct (i \in rt_bounds) eqn:HP; last by rewrite andFb. - destruct i as [i R]; intros _. - have GE_COST := fp_claimed_bounds_ge_cost ts_hp rt_bounds i R SOME. + rewrite leq_add2l leq_div2r //. + unfold total_interference_bound_fp. + rewrite big_seq_cond. + rewrite [\sum_(_ <- _ | true) _]big_seq_cond. + apply leq_sum; move => i /andP [IN _]. + destruct i as [i R]. + have GE_COST := fp_claimed_bounds_ge_cost ts_hp rt_bounds i R SOME IN. have UNZIP := fp_claimed_bounds_unzip ts_hp rt_bounds SOME. - assert (IN: i \in ts_hp). - { - by rewrite -UNZIP; apply/mapP; exists (i,R). - } - unfold interference_bound_generic; simpl. - exploit (VALID i); [by rewrite mem_rcons in_cons IN orbT | ins; des]. - by apply W_monotonic; try (by ins); apply leqnn. +unfold interference_bound_generic; simpl. + apply W_monotonic; try (by done). + have INts: i \in ts_hp by rewrite -UNZIP; apply/mapP; exists (i, R). + by exploit (VALID i); + [by rewrite mem_rcons in_cons INts orbT | by ins; des]. Qed. (* If the iteration converged at an earlier step, then it remains stable. *) @@ -507,29 +461,22 @@ Module ResponseTimeIterationFP. (* Consider a task set ts. *) Variable ts: taskset_of sporadic_task. - (* Assume that higher_priority is a total strict order (<). - TODO: it doesn't have to be total over the entire domain, but - only within the task set. - But to weaken the hypothesis, we need to re-prove some lemmas - from ssreflect. *) - Hypothesis H_irreflexive: irreflexive higher_priority. - Hypothesis H_transitive: transitive higher_priority. - Hypothesis H_unique_priorities: antisymmetric higher_priority. - Hypothesis H_total: total higher_priority. - - (* Assume the task set has no duplicates, ... *) - Hypothesis H_ts_is_a_set: uniq ts. - - (* ...all tasks have valid parameters, ... *) + (* Assume that all tasks have valid parameters, ... *) Hypothesis H_valid_task_parameters: valid_sporadic_taskset task_cost task_period task_deadline ts. - (* ...constrained deadlines, ...*) + (* ...and constrained deadlines.*) Hypothesis H_constrained_deadlines: forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk. - (* ...and tasks are ordered by increasing priorities. *) - Hypothesis H_sorted_ts: sorted higher_priority ts. + (* Assume that the task set is totally ordered by unique priorities, + and that the priority order is transitive. *) + Hypothesis H_task_set_is_sorted: sorted higher_priority ts. + Hypothesis H_task_set_has_unique_priorities: + FP_is_antisymmetric_over_task_set higher_priority ts. + Hypothesis H_priority_is_total: + FP_is_total_over_task_set higher_priority ts. + Hypothesis H_priority_transitive: FP_is_transitive higher_priority. (* Next, consider any arrival sequence such that...*) Context {arr_seq: arrival_sequence Job}. @@ -549,8 +496,7 @@ Module ResponseTimeIterationFP. (* Then, consider any platform with at least one CPU such that...*) Variable sched: schedule num_cpus arr_seq. - Hypothesis H_at_least_one_cpu : - num_cpus > 0. + Hypothesis H_at_least_one_cpu: num_cpus > 0. (* ...jobs only execute after they arrived and no longer than their execution costs,... *) @@ -650,11 +596,37 @@ Module ResponseTimeIterationFP. { cut (NTH idx \in hp_bounds = true); [intros IN | by apply mem_nth]. - by rewrite -UNZIP; apply/mapP; exists (TASK idx, RESP idx); rewrite PAIR. + by rewrite set_mem -UNZIP; apply/mapP; exists (TASK idx, RESP idx); rewrite PAIR. } { - unfold unzip1 in *; rewrite map_take UNZIP SUBST //. - by apply fp_claimed_bounds_interf with (hp_bounds := hp_bounds); rewrite -?SIZE. + intros hp_tsk IN INTERF. + exists (RESP (index hp_tsk ts)). + move: (IN) => INDEX; apply nth_index with (x0 := tsk) in INDEX. + rewrite -{1}[hp_tsk]INDEX -SUBST; last by rewrite SIZE index_mem. + assert (UNIQ: uniq hp_bounds). + { + apply map_uniq with (f := fst); unfold unzip1 in *; rewrite UNZIP. + by destruct ts. + } + rewrite -filter_idx_lt_take //. + { + rewrite PAIR mem_filter; apply/andP; split; + last by apply mem_nth; rewrite SIZE index_mem. + { + rewrite /NTH index_uniq; [| by rewrite SIZE index_mem | by done ]. + { + move: INTERF => /andP [HP NEQ]. + apply fp_claimed_bounds_hp_tasks_have_smaller_index with + (ts := ts) (elem := tsk) (hp_bounds := hp_bounds); + try (by done); + [by rewrite index_mem | by rewrite -SIZE | | by rewrite INDEX -SUBST]. + apply/eqP; intro BUG; subst idx. + rewrite SUBST -{1}INDEX in NEQ; + first by rewrite eq_refl in NEQ. + by rewrite SIZE index_mem INDEX. + } + } + } } { rewrite REC per_task_rta_fold. @@ -685,7 +657,7 @@ Module ResponseTimeIterationFP. feed (UNZIP rt_bounds); first by done. assert (EX: exists R, (tsk, R) \in rt_bounds). { - rewrite -UNZIP in INtsk; move: INtsk => /mapP EX. + rewrite set_mem -UNZIP in INtsk; move: INtsk => /mapP EX. by destruct EX as [p]; destruct p as [tsk' R]; simpl in *; subst tsk'; exists R. } des. exploit (RLIST tsk R); [by ins | by apply JOBtsk | intro COMPLETED]. diff --git a/analysis/parallel/bertogna_fp_theory.v b/analysis/parallel/bertogna_fp_theory.v index 243cb9b33168511d13ef067c1cfe90ff06a2ffe9..64db0dab1d0555c677e850f6d24fce68472312ea 100644 --- a/analysis/parallel/bertogna_fp_theory.v +++ b/analysis/parallel/bertogna_fp_theory.v @@ -1,16 +1,24 @@ Require Import rt.util.all. Require Import rt.model.basic.task rt.model.basic.job rt.model.basic.task_arrival - rt.model.basic.schedule rt.model.basic.platform rt.model.basic.platform_fp - rt.model.basic.workload rt.model.basic.schedulability rt.model.basic.priority - rt.model.basic.response_time rt.model.basic.interference. + rt.model.basic.schedule rt.model.basic.platform + rt.model.basic.constrained_deadlines + rt.model.basic.workload rt.model.basic.schedulability + rt.model.basic.priority rt.model.basic.response_time + rt.model.basic.interference. Require Import rt.analysis.parallel.workload_bound rt.analysis.parallel.interference_bound_fp. From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop div path. Module ResponseTimeAnalysisFP. - Export Job SporadicTaskset ScheduleOfSporadicTask Workload Interference InterferenceBoundFP - Platform PlatformFP Schedulability ResponseTime Priority SporadicTaskArrival WorkloadBound. + Export Job SporadicTaskset ScheduleOfSporadicTask Workload Interference + InterferenceBoundFP Platform Schedulability ResponseTime + Priority SporadicTaskArrival WorkloadBound ConstrainedDeadlines. + (* In this section, we prove that any fixed point in Bertogna and + Cirinei's RTA for FP scheduling modified to consider (potentially) + parallel jobs yields a safe response-time bound. This is an extension + of the analysis found in Chapter 18.2 of Baruah et al.'s book + Multiprocessor Scheduling for Real-time Systems. *) Section ResponseTimeBound. Context {sporadic_task: eqType}. @@ -33,7 +41,19 @@ Module ResponseTimeAnalysisFP. forall (j: JobIn arr_seq), valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j. - (* Consider any schedule such that...*) + (* Assume that we have a task set where all tasks have valid + parameters and constrained deadlines, ... *) + Variable ts: taskset_of sporadic_task. + Hypothesis H_valid_task_parameters: + valid_sporadic_taskset task_cost task_period task_deadline ts. + Hypothesis H_constrained_deadlines: + forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk. + + (* ... and that all jobs in the arrival sequence come from the task set. *) + Hypothesis H_all_jobs_from_taskset: + forall (j: JobIn arr_seq), job_task j \in ts. + + (* Next, consider any schedule such that...*) Variable num_cpus: nat. Variable sched: schedule num_cpus arr_seq. @@ -44,67 +64,61 @@ Module ResponseTimeAnalysisFP. Hypothesis H_completed_jobs_dont_execute: completed_jobs_dont_execute job_cost sched. + (* Consider a given FP policy, ... *) + Variable higher_eq_priority: FP_policy sporadic_task. + + (* ... and assume that the schedule is an APA work-conserving + schedule that enforces this policy. *) + Hypothesis H_work_conserving: work_conserving job_cost sched. + Hypothesis H_enforces_FP_policy: + enforces_FP_policy job_cost job_task sched higher_eq_priority. + (* Assume that there exists at least one processor. *) - Hypothesis H_at_least_one_cpu : - num_cpus > 0. - - (* Assume that we have a task set (with no duplicates) where all jobs - come from the task set and all tasks have valid parameters and constrained deadlines. *) - Variable ts: taskset_of sporadic_task. - Hypothesis H_ts_is_a_set: uniq ts. - Hypothesis H_all_jobs_from_taskset: - forall (j: JobIn arr_seq), job_task j \in ts. - Hypothesis H_valid_task_parameters: - valid_sporadic_taskset task_cost task_period task_deadline ts. - Hypothesis H_constrained_deadlines: - forall tsk, tsk \in ts -> task_deadline tsk <= task_period tsk. - - (* Next, consider a task tsk that is to be analyzed. *) - Variable tsk: sporadic_task. - Hypothesis task_in_ts: tsk \in ts. + Hypothesis H_at_least_one_cpu: num_cpus > 0. + (* Let's define some local names to avoid passing many parameters. *) Let no_deadline_is_missed_by_tsk (tsk: sporadic_task) := task_misses_no_deadline job_cost job_deadline job_task sched tsk. Let response_time_bounded_by (tsk: sporadic_task) := is_response_time_bound_of_task job_cost job_task tsk sched. - (* Assume a known response-time bound for any interfering task *) - Let task_with_response_time := (sporadic_task * time)%type. - Variable hp_bounds: seq task_with_response_time. - - (* For FP scheduling, assume there exists a fixed task priority. *) - Variable higher_eq_priority: FP_policy sporadic_task. + (* Next, we consider the response-time recurrence. + Let tsk be a task in ts that is to be analyzed. *) + Variable tsk: sporadic_task. + Hypothesis task_in_ts: tsk \in ts. - Let can_interfere_with_tsk := fp_can_interfere_with higher_eq_priority tsk. - - (* Assume that hp_bounds has exactly the tasks that interfere with tsk,... *) - Hypothesis H_hp_bounds_has_interfering_tasks: - [seq tsk_hp <- ts | can_interfere_with_tsk tsk_hp] = unzip1 hp_bounds. + (* Let is_hp_task denote whether a task is a higher-priority task + (with respect to tsk). *) + Let is_hp_task := higher_priority_task higher_eq_priority tsk. - (* ...and that all values in the pairs contain valid response-time bounds *) + (* Assume a response-time bound is known... *) + Let task_with_response_time := (sporadic_task * time)%type. + Variable hp_bounds: seq task_with_response_time. Hypothesis H_response_time_of_interfering_tasks_is_known: forall hp_tsk R, (hp_tsk, R) \in hp_bounds -> response_time_bounded_by hp_tsk R. - - (* Assume that the scheduler is work-conserving and enforces the FP policy. *) - Hypothesis H_work_conserving: work_conserving job_cost sched. - Hypothesis H_enforces_FP_policy: - enforces_FP_policy job_cost job_task sched higher_eq_priority. + (* ... for every higher-priority task. *) + Hypothesis H_hp_bounds_has_interfering_tasks: + forall hp_tsk, + hp_tsk \in ts -> + is_hp_task hp_tsk -> + exists R, (hp_tsk, R) \in hp_bounds. + (* Let R be the fixed point of Bertogna and Cirinei's recurrence, ...*) Variable R: time. Hypothesis H_response_time_recurrence_holds : R = task_cost tsk + div_floor - (total_interference_bound_fp task_cost task_period tsk hp_bounds R higher_eq_priority) + (total_interference_bound_fp task_cost task_period hp_bounds R) num_cpus. (* ... and assume that R is no larger than the deadline of tsk.*) Hypothesis H_response_time_no_larger_than_deadline: R <= task_deadline tsk. - (* In order to prove that R is a response-time bound, we first present some lemmas. *) + (* In order to prove that R is a response-time bound, we first provide some lemmas. *) Section Lemmas. (* Consider any job j of tsk. *) @@ -124,17 +138,18 @@ Module ResponseTimeAnalysisFP. task_interference job_cost job_task sched j tsk_other (job_arrival j) (job_arrival j + R). - (* and X the total interference incurred by job j due to any task. *) + (* ...and X the total interference incurred by job j due to any task. *) Let X := total_interference job_cost sched j (job_arrival j) (job_arrival j + R). (* Recall Bertogna and Cirinei's workload bound. *) Let workload_bound (tsk_other: sporadic_task) (R_other: time) := W task_cost task_period tsk_other R_other R. - (* Also, let ts_interf be the subset of tasks that interfere with tsk. *) - Let ts_interf := [seq tsk_other <- ts | can_interfere_with_tsk tsk_other]. + (* Let hp_tasks denote the set of higher-priority tasks. *) + Let hp_tasks := [seq tsk_other <- ts | is_hp_task tsk_other]. - Section LemmasAboutInterferingTasks. + (* Now we establish results about the higher-priority tasks. *) + Section LemmasAboutHPTasks. (* Let (tsk_other, R_other) be any pair of higher-priority task and response-time bound computed in previous iterations. *) @@ -142,30 +157,6 @@ Module ResponseTimeAnalysisFP. Variable R_other: time. Hypothesis H_response_time_of_tsk_other: (tsk_other, R_other) \in hp_bounds. - (* Note that tsk_other is in task set ts ...*) - Lemma bertogna_fp_tsk_other_in_ts: tsk_other \in ts. - Proof. - rename H_hp_bounds_has_interfering_tasks into UNZIP, - H_response_time_of_tsk_other into INbounds. - move: UNZIP => UNZIP. - cut (tsk_other \in ts_interf = true); - first by rewrite mem_filter; move => /andP [_ IN]. - unfold ts_interf; rewrite UNZIP. - by apply/mapP; exists (tsk_other, R_other). - Qed. - - (*... and interferes with task tsk. *) - Lemma bertogna_fp_tsk_other_interferes: can_interfere_with_tsk tsk_other. - Proof. - rename H_hp_bounds_has_interfering_tasks into UNZIP, - H_response_time_of_tsk_other into INbounds. - move: UNZIP => UNZIP. - cut (tsk_other \in ts_interf = true); - first by rewrite mem_filter; move => /andP [INTERF _]. - unfold ts_interf; rewrite UNZIP. - by apply/mapP; exists (tsk_other, R_other). - Qed. - (* Since tsk_other cannot interfere more than it executes, we show that the interference caused by tsk_other is no larger than workload bound W. *) Lemma bertogna_fp_workload_bounds_interference : @@ -174,9 +165,30 @@ Module ResponseTimeAnalysisFP. unfold response_time_bounded_by, is_response_time_bound_of_task, completed, completed_jobs_dont_execute, valid_sporadic_job in *. rename H_valid_task_parameters into TASK_PARAMS, + H_all_jobs_from_taskset into FROMTS, H_response_time_of_interfering_tasks_is_known into RESP. unfold x, workload_bound. - have INts := bertogna_fp_tsk_other_in_ts. + destruct ([exists t: 'I_(job_arrival j + R), + task_is_scheduled job_task sched tsk_other t]) eqn: SCHED; + last first. + { + apply negbT in SCHED; rewrite negb_exists in SCHED. + move: SCHED => /forallP SCHED. + apply leq_trans with (n := 0); last by done. + apply leq_trans with (n := \sum_(job_arrival j <= t < job_arrival j + R) 0); + last by rewrite big1. + apply leq_sum_nat; move => i /andP [_ LTi] _. + specialize (SCHED (Ordinal LTi)). + rewrite negb_exists in SCHED; move: SCHED => /forallP SCHED. + rewrite big1 //; intros cpu _. + specialize (SCHED cpu); apply negbTE in SCHED. + by rewrite SCHED andbF. + } + move: SCHED => /existsP [t /existsP [cpu SCHED]]. + unfold task_scheduled_on in SCHED. + destruct (sched cpu t) as [j0 |]; last by done. + assert (INts: tsk_other \in ts). + by move: SCHED => /eqP <-; rewrite FROMTS. apply leq_trans with (n := workload job_task sched tsk_other (job_arrival j) (job_arrival j + R)); first by apply task_interference_le_workload. @@ -187,7 +199,7 @@ Module ResponseTimeAnalysisFP. | by ins; apply RESP with (hp_tsk := tsk_other)]. Qed. - End LemmasAboutInterferingTasks. + End LemmasAboutHPTasks. (* Next we prove some lemmas that help to derive a contradiction.*) Section DerivingContradiction. @@ -238,15 +250,16 @@ Module ResponseTimeAnalysisFP. by apply completion_monotonic with (t := i); try (by done); apply ltnW. Qed. - Let scheduled_task_other_than_tsk (t: time) (tsk_other: sporadic_task) := + (* Let's define a predicate to identify the other tasks that are scheduled. *) + Let other_scheduled_task (t: time) (tsk_other: sporadic_task) := task_is_scheduled job_task sched tsk_other t && - can_interfere_with_tsk tsk_other. - - (* 1) Next, we prove that the sum of the interference of each task is equal - to the total interference multiplied by the number of processors. This - holds because interference only occurs when all processors are busy. *) - Lemma bertogna_fp_all_cpus_busy : - \sum_(tsk_k <- ts_interf) x tsk_k = X * num_cpus. + is_hp_task tsk_other. + + (* 1) Now we prove that, at all times that j is backlogged, the number + of tasks other than tsk that are scheduled is exactly the number + of processors in the system. This is required to prove lemma (2). *) + Lemma bertogna_fp_all_cpus_are_busy: + \sum_(tsk_k <- hp_tasks) x tsk_k = X * num_cpus. Proof. rename H_all_jobs_from_taskset into FROMTS, H_valid_task_parameters into PARAMS, @@ -277,7 +290,7 @@ Module ResponseTimeAnalysisFP. apply eq_bigr; intros cpu _. specialize (WORK j t BACK cpu); des. move: WORK => /eqP SCHED. - rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq. + rewrite (bigD1_seq (job_task j_other)) /=; last by rewrite filter_uniq; destruct ts. { rewrite (eq_bigr (fun i => 0)); last by intros i DIFF; rewrite /task_scheduled_on SCHED;apply/eqP;rewrite eqb0 eq_sym. @@ -285,7 +298,6 @@ Module ResponseTimeAnalysisFP. by unfold task_scheduled_on; rewrite SCHED. } rewrite mem_filter; apply/andP; split; last by apply FROMTS. - unfold can_interfere_with_tsk, fp_can_interfere_with. apply/andP; split. { rewrite -JOBtsk; apply FP with (t := t); first by done. @@ -328,25 +340,30 @@ Module ResponseTimeAnalysisFP. } Qed. - (* 2) Now, we prove that the Bertogna's interference bound - is not enough to cover the sum of the "minimum" term over - all tasks (artifact of the proof by contradiction). *) + (* 2) Next, using lemmas (0) and (1) we prove that the reduction-based + interference bound is not enough to cover the sum of the minima over all tasks + (artifact of the proof by contradiction). *) Lemma bertogna_fp_sum_exceeds_total_interference: \sum_((tsk_k, R_k) <- hp_bounds) - x tsk_k > total_interference_bound_fp task_cost task_period tsk - hp_bounds R higher_eq_priority. + x tsk_k > total_interference_bound_fp task_cost task_period hp_bounds R. Proof. have TOOMUCH := bertogna_fp_too_much_interference. - rename H_hp_bounds_has_interfering_tasks into UNZIP, + have ALLBUSY := bertogna_fp_all_cpus_are_busy. + rename H_hp_bounds_has_interfering_tasks into HAS, H_response_time_recurrence_holds into REC. - apply leq_trans with (n := \sum_(tsk_k <- ts_interf) x tsk_k); + apply leq_trans with (n := \sum_(tsk_k <- hp_tasks) x tsk_k); last first. { - rewrite (eq_bigr (fun i => x (fst i))); - last by ins; destruct i. - rewrite big_filter. - have MAP := big_map (fun x => fst x) (fun i => true) (fun i => x i). - by unfold unzip1 in *; rewrite -MAP -UNZIP -big_filter. + rewrite (eq_bigr (fun i => x (fst i))); last by ins; destruct i. + have MAP := @big_map _ 0 addn _ _ (fun x => fst x) hp_bounds (fun x => true) (fun y => (x y)). + rewrite -MAP. + apply leq_sum_sub_uniq; first by apply filter_uniq; destruct ts. + red; move => tsk0 IN0. + rewrite mem_filter in IN0; move: IN0 => /andP [INTERF0 IN0]. + apply/mapP. + feed (HAS tsk0); first by done. + move: (HAS INTERF0) => [R0 IN]. + by exists (tsk0, R0). } apply ltn_div_trunc with (d := num_cpus); first by apply H_at_least_one_cpu. @@ -354,20 +371,22 @@ Module ResponseTimeAnalysisFP. rewrite -addn1 -leq_subLR. rewrite -[R + 1 - _]subh1; last by rewrite REC; apply leq_addr. rewrite leq_divRL; last by apply H_at_least_one_cpu. - rewrite bertogna_fp_all_cpus_busy. + rewrite ALLBUSY. by rewrite leq_mul2r; apply/orP; right; apply TOOMUCH. Qed. - (* 3) After concluding that the sum of the minimum exceeds (R - e_i + 1), - we prove that there exists a tuple (tsk_k, R_k) such that x_k > W_k. *) + (* 3) After concluding that the sum of the minima exceeds (R - e_i + 1), + we prove that there exists a tuple (tsk_k, R_k) that satisfies + min (x_k, R - e_i + 1) > min (W_k, R - e_i + 1). + This implies that x_k > W_k, which is of course a contradiction, + since W_k is a valid task interference bound. *) Lemma bertogna_fp_exists_task_that_exceeds_bound : exists tsk_k R_k, (tsk_k, R_k) \in hp_bounds /\ x tsk_k > workload_bound tsk_k R_k. Proof. - have INTERFk := bertogna_fp_tsk_other_interferes. have SUM := bertogna_fp_sum_exceeds_total_interference. - rename H_hp_bounds_has_interfering_tasks into UNZIP. + rename H_hp_bounds_has_interfering_tasks into HASHP. assert (HAS: has (fun tup : task_with_response_time => let (tsk_k, R_k) := tup in x tsk_k > workload_bound tsk_k R_k) @@ -377,15 +396,13 @@ Module ResponseTimeAnalysisFP. move: NOTHAS => /negP /hasPn ALL. rewrite -[_ < _]negbK in SUM. move: SUM => /negP SUM; apply SUM; rewrite -leqNgt. + rewrite (eq_bigr (fun i => x (fst i))); last by ins; destruct i. unfold total_interference_bound_fp. - rewrite [\sum_(i <- _ | let '(tsk_other, _) := i in _)_]big_mkcond. - rewrite big_seq_cond [\sum_(i <- _ | true) _]big_seq_cond. - apply leq_sum; move => tsk_k /andP [HPk _]; destruct tsk_k as [tsk_k R_k]. - specialize (ALL (tsk_k, R_k) HPk). - rewrite -leqNgt in ALL. - specialize (INTERFk tsk_k R_k HPk). - fold (can_interfere_with_tsk); rewrite INTERFk. - unfold interference_bound_generic. by apply ALL. + rewrite big_seq_cond. + rewrite [\sum_(_ <- _ | true)_]big_seq_cond. + apply leq_sum. + intros p; rewrite andbT; intros IN. + by specialize (ALL p IN); destruct p; rewrite leqNgt. } move: HAS => /hasP HAS; destruct HAS as [[tsk_k R_k] HPk MINk]; exists tsk_k, R_k. by repeat split. @@ -403,7 +420,7 @@ Module ResponseTimeAnalysisFP. have BOUND := bertogna_fp_workload_bounds_interference. rename H_response_time_recurrence_holds into REC, H_response_time_of_interfering_tasks_is_known into RESP, - H_hp_bounds_has_interfering_tasks into UNZIP, + H_hp_bounds_has_interfering_tasks into HAS, H_response_time_no_larger_than_deadline into LEdl. intros j JOBtsk. diff --git a/analysis/parallel/interference_bound_edf.v b/analysis/parallel/interference_bound_edf.v index a06f5532dcb1a53c0bb5badbda1b264da8b81820..f0f2c6a33ef959c913e9e2efde66b0d6385869f1 100644 --- a/analysis/parallel/interference_bound_edf.v +++ b/analysis/parallel/interference_bound_edf.v @@ -63,7 +63,7 @@ Module InterferenceBoundEDF. (* ... and an interval length delta. *) Variable delta: time. - Section PerTask. + Section RecallInterferenceBounds. Variable tsk_R: task_with_response_time. Let tsk_other := fst tsk_R. @@ -81,19 +81,21 @@ Module InterferenceBoundEDF. Definition interference_bound_edf := minn basic_interference_bound edf_specific_bound. - End PerTask. + End RecallInterferenceBounds. - Section AllTasks. + (* Next we define the computation of the total interference for APA scheduling. *) + Section TotalInterference. - Let interferes_with_tsk := jldp_can_interfere_with tsk. + (* Let other_task denote tasks different from tsk. *) + Let other_task := different_task tsk. (* The total interference incurred by tsk is bounded by the sum - of individual task interferences. *) + of individual task interferences of the other tasks. *) Definition total_interference_bound_edf := - \sum_((tsk_other, R_other) <- R_prev | interferes_with_tsk tsk_other) + \sum_((tsk_other, R_other) <- R_prev | other_task tsk_other) interference_bound_edf (tsk_other, R_other). - End AllTasks. + End TotalInterference. End TotalInterferenceBoundEDF. @@ -134,8 +136,7 @@ Module InterferenceBoundEDF. completed_jobs_dont_execute job_cost sched. (* Assume there exists at least one processor. *) - Hypothesis H_at_least_one_cpu : - num_cpus > 0. + Hypothesis H_at_least_one_cpu: num_cpus > 0. (* Assume that we have a task set where all tasks have valid parameters and constrained deadlines. *) diff --git a/analysis/parallel/interference_bound_fp.v b/analysis/parallel/interference_bound_fp.v index 49ed78f3decee2a4f91708322b586af34c0b854f..e6aa91d6f47fb2a8697b2f49c1ef12103d4509ac 100644 --- a/analysis/parallel/interference_bound_fp.v +++ b/analysis/parallel/interference_bound_fp.v @@ -30,13 +30,13 @@ Module InterferenceBoundFP. (* Assume an FP policy. *) Variable higher_eq_priority: FP_policy sporadic_task. - Let can_interfere_with_tsk := fp_can_interfere_with higher_eq_priority tsk. + (* Recall the interference bound. *) Let total_interference_bound := interference_bound_generic task_cost task_period delta. (* The total interference incurred by tsk is bounded by the sum of individual task interferences. *) Definition total_interference_bound_fp := - \sum_((tsk_other, R_other) <- R_prev | can_interfere_with_tsk tsk_other) + \sum_((tsk_other, R_other) <- R_prev) total_interference_bound (tsk_other, R_other). End Definitions. diff --git a/implementation/apa/arrival_sequence.v b/implementation/apa/arrival_sequence.v new file mode 100644 index 0000000000000000000000000000000000000000..21adb133c02d24c6355c75e79bf0f1899d8f11e8 --- /dev/null +++ b/implementation/apa/arrival_sequence.v @@ -0,0 +1,114 @@ +(* We can reuse the.apa definition of periodic arrival sequence. *) +Require Import rt.util.all. +Require Import rt.model.apa.arrival_sequence rt.model.apa.job + rt.model.apa.task rt.model.apa.task_arrival. +Require Import rt.implementation.apa.task rt.implementation.apa.job. +From mathcomp Require Import ssreflect ssrbool ssrfun ssrnat eqtype seq div. + +Module ConcreteArrivalSequence. + + Import Job ArrivalSequence ConcreteTask ConcreteJob SporadicTaskset SporadicTaskArrival. + + Section PeriodicArrivals. + + Context {num_cpus: nat}. + Variable ts: concrete_taskset num_cpus. + + (* At any time t, we release Some job of tsk if t is a multiple of the period, + otherwise we release None. *) + Definition add_job (t: time) (tsk: @concrete_task num_cpus) : option (@concrete_job _) := + if task_period tsk %| t then + Some (Build_concrete_job (t %/ task_period tsk) (task_cost tsk) (task_deadline tsk) tsk) + else + None. + + (* The arrival sequence at any time t is simply the partial map of add_job. *) + Definition periodic_arrival_sequence (t: time) := pmap (add_job t) ts. + + End PeriodicArrivals. + + Section Proofs. + + Context {num_cpus: nat}. + + (* Let ts be any concrete task set with valid parameters. *) + Variable ts: concrete_taskset num_cpus. + 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. + + (* ... every job comes from the task set, ... *) + Theorem periodic_arrivals_all_jobs_from_taskset: + forall (j: JobIn arr_seq), + job_task (_job_in arr_seq j) \in ts. (* TODO: fix coercion. *) + Proof. + intros j. + destruct j as [j arr ARRj]; simpl. + unfold arr_seq, arrives_at, periodic_arrival_sequence in *. + rewrite mem_pmap in ARRj. + move: ARRj => /mapP ARRj; destruct ARRj as [tsk IN SOME]. + by unfold add_job in *; desf. + Qed. + + (* ..., jobs have valid parameters, ... *) + Theorem periodic_arrivals_valid_job_parameters: + forall (j: JobIn arr_seq), + 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 *. + intros j; destruct j as [j arr ARRj]; simpl. + unfold arrives_at, arr_seq, periodic_arrival_sequence in 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 arr_seq job_task. + Proof. + unfold sporadic_task_model; move => j j' /eqP DIFF SAMEtsk LE. + destruct j as [j arr ARR], j' as [j' arr' ARR']; simpl in *. + rewrite eqE /= /jobin_eqdef negb_and /= in DIFF. + unfold arrives_at, arr_seq, periodic_arrival_sequence in *. + rewrite 2!mem_pmap in ARR ARR'. + move: ARR ARR' => /mapP [tsk_j INj SOMEj] /mapP [tsk_j' INj' SOMEj']. + unfold add_job in SOMEj, SOMEj'; desf; simpl in *; + move: Heq0 Heq => /dvdnP [k DIV] /dvdnP [k' DIV']. + { + rewrite DIV DIV' -mulSnr. + rewrite leq_eqVlt in LE; move: LE => /orP [/eqP EQ | LESS]. + { + exfalso; move: DIFF => /negP DIFF; apply DIFF. + by subst; rewrite EQ. + } + subst; rewrite leq_mul2r; apply/orP; right. + by rewrite ltn_mul2r in LESS; move: LESS => /andP [_ LT]. + } + { + assert (LT: arr < arr'). by rewrite ltn_neqAle; apply/andP. + clear LE DIFF; subst tsk_j' arr arr'. + rewrite ltn_mul2r in LT; move: LT => /andP [_ LT]. + by apply leq_trans with (n := k.+1 * task_period tsk_j); + [by rewrite mulSnr | by rewrite leq_mul2r; apply/orP; right]. + } + Qed. + + (* ... and the arrival sequence has no duplicate jobs. *) + Theorem periodic_arrivals_is_a_set: + arrival_sequence_is_a_set arr_seq. + Proof. + intros t. + unfold arr_seq, periodic_arrival_sequence. + apply (pmap_uniq) with (g := job_task); last by destruct ts. + by unfold add_job, ocancel; intro tsk; desf. + Qed. + + End Proofs. + +End ConcreteArrivalSequence. \ No newline at end of file diff --git a/implementation/apa/bertogna_edf_example.v b/implementation/apa/bertogna_edf_example.v new file mode 100644 index 0000000000000000000000000000000000000000..d8fdf6fe5eb33a539cd6ec21dd43ce93acfa312a --- /dev/null +++ b/implementation/apa/bertogna_edf_example.v @@ -0,0 +1,207 @@ +Require Import rt.util.all. +Require Import rt.model.apa.job rt.model.apa.task rt.model.apa.affinity + rt.model.apa.schedule rt.model.apa.interference + rt.model.apa.schedulability + rt.model.apa.priority rt.model.apa.platform. +Require Import rt.analysis.apa.workload_bound + rt.analysis.apa.interference_bound_edf + rt.analysis.apa.bertogna_edf_comp. +Require Import rt.implementation.apa.job + rt.implementation.apa.task + rt.implementation.apa.schedule + rt.implementation.apa.arrival_sequence. +From mathcomp Require Import ssreflect ssrbool ssrnat eqtype seq fintype bigop div. + +Module ResponseTimeAnalysisEDF. + + Import Job Schedule SporadicTaskset Priority Schedulability + Affinity Platform InterferenceBoundEDF WorkloadBound + Interference ResponseTimeIterationEDF. + Import ConcreteJob ConcreteTask ConcreteArrivalSequence ConcreteScheduler. + + (* In this section, we instantiate a simple example to show that the theorems + contain no contradictory assumptions. *) + Section ExampleRTA. + + (* Assume there are two processors. *) + Let num_cpus := 2. + + (* Let (cpu j) denote the j-th processor *) + Let cpu j := @Ordinal num_cpus j. + + (* Define alpha1 := {cpu 0, cpu 1} with the two processors. *) + Program Let alpha1 : affinity num_cpus := + (Build_set [:: cpu 0 _; cpu 1 _] _). + + (* Define the singleton affinity alpha2 := {cpu 0}. *) + Program Let alpha2 : affinity num_cpus := + (Build_set [:: cpu 0 _] _). + + (* Define the singleton affinity alpha3 := {cpu 1}. *) + Program Let alpha3 : affinity num_cpus := + (Build_set [:: cpu 1 _] _). + + (* Now we create three tasks using the affinities above ... *) + Let tsk1 := {| task_id := 1; task_cost := 3; task_period := 5; + task_deadline := 3; task_affinity := alpha1|}. + Let tsk2 := {| task_id := 2; task_cost := 2; task_period := 6; + task_deadline := 5; task_affinity := alpha2|}. + Let tsk3 := {| task_id := 3; task_cost := 2; task_period := 12; + task_deadline := 11; task_affinity := alpha3|}. + + (* ... and group these tasks into task set ts. *) + Program Let ts := Build_set [:: tsk1; tsk2; tsk3] _. + + (* In this section, we let Coq compute a few properties about ts. *) + Section FactsAboutTaskset. + + (* There are no empty affinities. *) + Fact ts_non_empty_affinities: + forall tsk, + tsk \in ts -> + #|task_affinity tsk| > 0. + Proof. + intros tsk IN. + by repeat (move: IN => /orP [/eqP EQ | IN]; subst; rewrite ?set_card; compute). + Qed. + + (* The tasks have valid parameters (e.g., cost > 0). *) + Fact ts_has_valid_parameters: + valid_sporadic_taskset task_cost task_period task_deadline ts. + Proof. + intros tsk IN. + by repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute). + Qed. + + (* The task set has constrained deadlines. *) + Fact ts_has_constrained_deadlines: + forall tsk, + tsk \in ts -> + task_deadline tsk <= task_period tsk. + Proof. + intros tsk IN. + by repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute). + Qed. + + End FactsAboutTaskset. + + (* Next, recall the EDF RTA schedulability test for APA scheduling. + Note that the task functions (from implementation/apa/task.v) + require num_cpus as a parameter, so we leave a blank space so that + can be inferred automatically. *) + Let schedulability_test := + edf_schedulable (@task_cost _) (@task_period _) + (@task_deadline _) num_cpus task_affinity + task_affinity. (* For simplicity, we use subaffinity alpha' = alpha. *) + + (* Now, we guide Coq to compute the schedulability test function + and show it returns true. *) + Fact schedulability_test_succeeds : + schedulability_test ts = true. + Proof. + unfold schedulability_test, edf_schedulable, edf_claimed_bounds; desf. + apply negbT in Heq; move: Heq => /negP ALL. + exfalso; apply ALL; clear ALL. + assert (STEPS: \sum_(tsk <- ts) (task_deadline tsk - task_cost tsk) + 1 = 13). + { + by rewrite unlock; compute. + } rewrite STEPS; clear STEPS. + + have cpu0P: 0 < 2 by done. + have cpu1P: 1 < 2 by done. + + have cpu_compute : + forall (P: processor num_cpus -> bool), + [exists x, P x] = + if (P (cpu 0 cpu0P)) then true else + if P (cpu 1 cpu1P) then true else false. + { + unfold num_cpus in *; intros P. + destruct (P (cpu 0 cpu0P)) eqn:P0; + first by apply/existsP; exists (cpu 0 cpu0P). + destruct (P (cpu 1 cpu1P)) eqn:P1; + first by apply/existsP; exists (cpu 1 cpu1P). + apply negbTE; rewrite negb_exists; apply/forallP. + intros x; destruct x as [x LE]; apply negbT. + have GE0 := leq0n x. + rewrite leq_eqVlt in GE0; move: GE0 => /orP [/eqP EQ0 | GE1]; + first by rewrite -P0; f_equal; apply ord_inj. + rewrite leq_eqVlt in GE1; move: GE1 => /orP [/eqP EQ1 | GE2]; + first by rewrite -P1; f_equal; apply ord_inj. + have LE' := LE. + apply leq_ltn_trans with (m := 2) in LE'; last by done. + by rewrite ltnn in LE'. + } + + Ltac f := + unfold edf_rta_iteration; simpl; + unfold edf_response_time_bound, div_floor, total_interference_bound_edf, interference_bound_edf, interference_bound_generic, W, edf_specific_interference_bound, different_task_in, affinity_intersects; simpl; + rewrite !addnE !set_card !big_cons ?big_nil /=. + + + rewrite [edf_rta_iteration]lock; simpl. + unfold locked at 13; destruct master_key; f; rewrite !cpu_compute /=. + unfold locked at 12; destruct master_key; f; rewrite !cpu_compute /=. + unfold locked at 11; destruct master_key; f; rewrite !cpu_compute /=. + unfold locked at 10; destruct master_key; f; rewrite !cpu_compute /=. + unfold locked at 9; destruct master_key; f; rewrite !cpu_compute /=. + unfold locked at 8; destruct master_key; f; rewrite !cpu_compute /=. + unfold locked at 7; destruct master_key; f; rewrite !cpu_compute /=. + unfold locked at 6; destruct master_key; f; rewrite !cpu_compute /=. + unfold locked at 5; destruct master_key; f; rewrite !cpu_compute /=. + unfold locked at 4; destruct master_key; f; rewrite !cpu_compute /=. + unfold locked at 3; destruct master_key; f; rewrite !cpu_compute /=. + unfold locked at 2; destruct master_key; f; rewrite !cpu_compute /=. + by unfold locked at 1; destruct master_key; f; rewrite !cpu_compute /=. + Qed. + + (* Let arr_seq be the periodic arrival sequence from ts. *) + Let arr_seq := periodic_arrival_sequence ts. + + (* Let sched be the weak APA EDF scheduler. *) + Let sched := scheduler job_cost job_task num_cpus arr_seq task_affinity (EDF job_deadline). + + (* Recall the definition of deadline miss. *) + Let no_deadline_missed_by := + task_misses_no_deadline job_cost job_deadline job_task sched. + + (* To show that the RTA works, we infer the schedulability of the task + set from the result of the RTA procedure. *) + Corollary ts_is_schedulable: + forall tsk, + tsk \in ts -> + no_deadline_missed_by tsk. + Proof. + intros tsk IN. + have VALID := periodic_arrivals_valid_job_parameters ts ts_has_valid_parameters. + have VALIDTS := ts_has_valid_parameters. + unfold valid_sporadic_job, valid_realtime_job in *; des. + apply taskset_schedulable_by_edf_rta with (task_cost := task_cost) (task_period := task_period) (task_deadline := task_deadline) (alpha := task_affinity) (alpha' := task_affinity) (ts0 := ts). + - by apply ts_has_valid_parameters. + - by apply ts_has_constrained_deadlines. + - by apply ts_non_empty_affinities. + - by ins. + - by apply periodic_arrivals_all_jobs_from_taskset. + - by apply periodic_arrivals_valid_job_parameters, ts_has_valid_parameters. + - by apply periodic_arrivals_are_sporadic. + - by apply scheduler_jobs_must_arrive_to_execute. + - apply scheduler_completed_jobs_dont_execute; intro j'. + -- by specialize (VALID j'); des. + -- by apply periodic_arrivals_is_a_set. + - by apply scheduler_sequential_jobs, periodic_arrivals_is_a_set. + - by apply scheduler_respects_affinity. + - apply scheduler_apa_work_conserving; try (by done). + -- by apply periodic_arrivals_is_a_set. + -- by apply EDF_is_transitive. + -- by apply EDF_is_total. + - apply scheduler_enforces_policy. + -- by apply periodic_arrivals_is_a_set. + -- by apply EDF_is_transitive. + -- by apply EDF_is_total. + - by apply schedulability_test_succeeds. + - by apply IN. + Qed. + + End ExampleRTA. + +End ResponseTimeAnalysisEDF. \ No newline at end of file diff --git a/implementation/apa/bertogna_fp_example.v b/implementation/apa/bertogna_fp_example.v new file mode 100644 index 0000000000000000000000000000000000000000..1a1452a6785f15ebd94939ca2a94c106fb6df8fe --- /dev/null +++ b/implementation/apa/bertogna_fp_example.v @@ -0,0 +1,297 @@ +Require Import rt.util.all. +Require Import rt.model.apa.job rt.model.apa.task rt.model.apa.affinity + rt.model.apa.schedule rt.model.apa.schedulability + rt.model.apa.priority rt.model.apa.platform + rt.model.apa.interference. +Require Import rt.analysis.apa.workload_bound + rt.analysis.apa.interference_bound_fp + rt.analysis.apa.bertogna_fp_comp. +Require Import rt.implementation.apa.job + rt.implementation.apa.task + rt.implementation.apa.schedule + rt.implementation.apa.arrival_sequence. +From mathcomp Require Import ssreflect ssrbool ssrnat eqtype fintype seq bigop div. + +Module ResponseTimeAnalysisFP. + + Import Job Schedule SporadicTaskset Priority Schedulability Platform + Interference InterferenceBoundFP WorkloadBound + ResponseTimeIterationFP Affinity. + Import ConcreteJob ConcreteTask ConcreteArrivalSequence ConcreteScheduler. + + (* In this section, we instantiate a simple example to show that the theorems + contain no contradictory assumptions. *) + Section ExampleRTA. + + (* Assume there are two processors. *) + Let num_cpus := 2. + + (* Let (cpu j) denote the j-th processor *) + Let cpu j := @Ordinal num_cpus j. + + (* Define alpha1 := {cpu 0, cpu 1} with the two processors. *) + Program Let alpha1 : affinity num_cpus := + (Build_set [:: cpu 0 _; cpu 1 _] _). + + (* Define the singleton affinity alpha2 := {cpu 0}. *) + Program Let alpha2 : affinity num_cpus := + (Build_set [:: cpu 0 _] _). + + (* Define the singleton affinity alpha3 := {cpu 1}. *) + Program Let alpha3 : affinity num_cpus := + (Build_set [:: cpu 1 _] _). + + (* Now we create three tasks using the affinities above ... *) + Let tsk1 := {| task_id := 1; task_cost := 3; task_period := 5; + task_deadline := 3; task_affinity := alpha1|}. + Let tsk2 := {| task_id := 2; task_cost := 2; task_period := 6; + task_deadline := 5; task_affinity := alpha2|}. + Let tsk3 := {| task_id := 3; task_cost := 2; task_period := 12; + task_deadline := 11; task_affinity := alpha3|}. + + (* ... and group these tasks into task set ts. *) + Program Let ts := Build_set [:: tsk1; tsk2; tsk3] _. + + (* In this section, we let Coq compute a few properties about ts. *) + Section FactsAboutTaskset. + + (* There are no empty affinities. *) + Fact ts_non_empty_affinities: + forall tsk, + tsk \in ts -> #|task_affinity tsk| > 0. + Proof. + intros tsk IN. + by repeat (move: IN => /orP [/eqP EQ | IN]; subst; rewrite ?set_card; compute). + Qed. + + (* The tasks have valid parameters (e.g., cost > 0). *) + Fact ts_has_valid_parameters: + valid_sporadic_taskset task_cost task_period task_deadline ts. + Proof. + intros tsk IN. + by repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute). + Qed. + + (* The task set has constrained deadlines. *) + Fact ts_has_constrained_deadlines: + forall tsk, + tsk \in ts -> + task_deadline tsk <= task_period tsk. + Proof. + intros tsk IN. + by repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute). + Qed. + + End FactsAboutTaskset. + + (* Then, let arr_seq be the periodic arrival sequence from ts. *) + Let arr_seq := periodic_arrival_sequence ts. + + (* Assume rate-monotonic priorities. *) + Let higher_priority : JLDP_policy arr_seq := + (FP_to_JLDP job_task (RM task_period)). + + Section FactsAboutPriorityOrder. + + Lemma ts_has_unique_priorities : + FP_is_antisymmetric_over_task_set (RM task_period) ts. + Proof. + unfold RM; intros tsk tsk' IN IN' HP HP'. + have EQ: task_period tsk = task_period tsk' by apply/eqP; rewrite eqn_leq HP HP'. + clear HP HP'. + rewrite !in_cons 2!in_nil 2!orbF in IN IN'; des; rewrite IN IN'; try (by done); + subst tsk tsk'; simpl in *; by done. + Qed. + + Lemma priority_is_total : + FP_is_total_over_task_set (RM task_period) ts. + Proof. + unfold RM; intros tsk tsk' IN IN'. + destruct (leqP (task_period tsk) (task_period tsk')); + [by left | by right; apply ltnW]. + Qed. + + End FactsAboutPriorityOrder. + + (* Next, recall the FP RTA schedulability test for APA scheduling. + Note that the task functions (from implementation/apa/task.v) + require num_cpus as a parameter, so we leave a blank space so that + can be inferred automatically. *) + Let schedulability_test := + fp_schedulable (@task_cost _) (@task_period _) (@task_deadline _) + num_cpus (RM task_period) task_affinity + task_affinity. (* For simplicity, we use subaffinity alpha' = alpha. *) + + (* Now we show that the schedulability test returns true. *) + Fact schedulability_test_succeeds : + schedulability_test ts = true. + Proof. + unfold schedulability_test, fp_schedulable, fp_claimed_bounds; simpl. + unfold total_interference_bound_fp, div_floor. + rewrite big_nil div0n addn0 /=. + unfold div_floor; rewrite !set_card /=. + + have cpu0P: 0 < 2 by done. + have cpu1P: 1 < 2 by done. + + have cpu_compute : + forall (P: processor num_cpus -> bool), + [exists x, P x] = + if (P (cpu 0 cpu0P)) then true else + if P (cpu 1 cpu1P) then true else false. + { + unfold num_cpus in *; intros P. + destruct (P (cpu 0 cpu0P)) eqn:P0; + first by apply/existsP; exists (cpu 0 cpu0P). + destruct (P (cpu 1 cpu1P)) eqn:P1; + first by apply/existsP; exists (cpu 1 cpu1P). + apply negbTE; rewrite negb_exists; apply/forallP. + intros x; destruct x as [x LE]; apply negbT. + have GE0 := leq0n x. + rewrite leq_eqVlt in GE0; move: GE0 => /orP [/eqP EQ0 | GE1]; + first by rewrite -P0; f_equal; apply ord_inj. + rewrite leq_eqVlt in GE1; move: GE1 => /orP [/eqP EQ1 | GE2]; + first by rewrite -P1; f_equal; apply ord_inj. + have LE' := LE. + apply leq_ltn_trans with (m := 2) in LE'; last by done. + by rewrite ltnn in LE'. + } + + set I2 := total_interference_bound_fp task_cost task_period + task_affinity tsk2 alpha2 [:: (tsk1, 3)]. + + assert (H1: I2 2 (RM task_period) = 1). + { + unfold I2, total_interference_bound_fp; rewrite big_cons big_nil. + unfold higher_priority_task_in; simpl. + by rewrite /affinity_intersects cpu_compute /= addn0; compute. + } + rewrite H1 !divn1 !addn1; clear H1. + assert (H1: I2 3 (RM task_period) = 2). + { + unfold I2, total_interference_bound_fp; rewrite big_cons big_nil. + unfold higher_priority_task_in; simpl. + by rewrite /affinity_intersects cpu_compute /= addn0; compute. + } + rewrite H1 !addn2; clear H1. + assert (H1: I2 4 (RM task_period) = 3). + { + unfold I2, total_interference_bound_fp; rewrite big_cons big_nil. + unfold higher_priority_task_in; simpl. + by rewrite /affinity_intersects cpu_compute /= addn0; compute. + } + rewrite H1 !addn3; clear H1. + assert (H1: I2 5 (RM task_period) = 3). + { + unfold I2, total_interference_bound_fp; rewrite big_cons big_nil. + unfold higher_priority_task_in; simpl. + by rewrite /affinity_intersects cpu_compute /= addn0; compute. + } + rewrite H1 !addn3; clear H1. + have H2: 4 < 5 by compute. + rewrite H2; clear H2 I2. + unfold fp_bound_of_task. + + Ltac f := unfold div_floor; + rewrite !big_cons big_nil /= /higher_priority_task_in /= + /affinity_intersects !addn0 /= ?set_card ?divn1 ?addn0; + unfold interference_bound_generic, W, max_jobs, div_floor; + rewrite addn1 ?addn0. + + have H4: per_task_rta task_cost task_period num_cpus (RM task_period) task_affinity task_affinity tsk3 [:: (tsk1, 3); (tsk2, 5)] + (max_steps task_cost task_deadline tsk3) = 5. + { + rewrite /per_task_rta iterSr /div_floor set_card. + Ltac g := + rewrite /total_interference_bound_fp /interference_bound_generic + /W /max_jobs /div_floor !big_cons big_nil /= /higher_priority_task_in /= + /affinity_intersects. + set x9 := total_interference_bound_fp _ _ _ _ _ _ _ _. + have I9: x9 = 1 by unfold x9; g; rewrite ?cpu_compute /=; compute. + rewrite I9 iterSr. + set x8 := total_interference_bound_fp _ _ _ _ _ _ _ _. + have I8: x8 = 2 by unfold x8; g; rewrite ?cpu_compute /=; compute. + rewrite I8 iterSr. + set x7 := total_interference_bound_fp _ _ _ _ _ _ _ _. + have I7: x7 = 3 by unfold x7; g; rewrite ?cpu_compute /=; compute. + rewrite I7 iterSr. + set x6 := total_interference_bound_fp _ _ _ _ _ _ _ _. + have I6: x6 = 3 by unfold x6; g; rewrite ?cpu_compute /=; compute. + rewrite I6 iterSr. + set x5 := total_interference_bound_fp _ _ _ _ _ _ _ _. + have I5: x5 = 3 by unfold x5; g; rewrite ?cpu_compute /=; compute. + rewrite I5 iterSr. + set x4 := total_interference_bound_fp _ _ _ _ _ _ _ _. + have I4: x4 = 3 by unfold x4; g; rewrite ?cpu_compute /=; compute. + rewrite I4 iterSr. + set x3 := total_interference_bound_fp _ _ _ _ _ _ _ _. + have I3: x3 = 3 by unfold x3; g; rewrite ?cpu_compute /=; compute. + rewrite I3 iterSr. + set x2 := total_interference_bound_fp _ _ _ _ _ _ _ _. + have I2: x2 = 3 by unfold x2; g; rewrite ?cpu_compute /=; compute. + rewrite I2 iterSr. + set x1 := total_interference_bound_fp _ _ _ _ _ _ _ _. + have I1: x1 = 3 by unfold x1; g; rewrite ?cpu_compute /=; compute. + rewrite I1 iterSr /=. + set x0 := total_interference_bound_fp _ _ _ _ _ _ _ _. + have I0: x0 = 3 by unfold x0; g; rewrite ?cpu_compute /=; compute. + by rewrite I0; compute. + } + by rewrite H4. + Qed. + + (* Let sched be the work-conserving RM APA scheduler. *) + Let sched := scheduler job_cost job_task num_cpus arr_seq task_affinity higher_priority. + + (* Recall the definition of deadline miss. *) + Let no_deadline_missed_by := + task_misses_no_deadline job_cost job_deadline job_task sched. + + (* Next, we prove that ts is schedulable with the result of the test. *) + Corollary ts_is_schedulable: + forall tsk, + tsk \in ts -> + no_deadline_missed_by tsk. + Proof. + 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_priority0 := RM task_period) + (alpha := task_affinity) (alpha' := task_affinity); try (by done). + - by apply ts_has_constrained_deadlines. + - by apply ts_non_empty_affinities. + - by ins. + - by apply ts_has_unique_priorities. + - by apply priority_is_total. + - by apply RM_is_transitive. + - by apply periodic_arrivals_all_jobs_from_taskset. + - by apply periodic_arrivals_are_sporadic. + - by apply scheduler_jobs_must_arrive_to_execute. + - apply scheduler_completed_jobs_dont_execute; intro j'. + -- by specialize (VALID j'); des. + -- by apply periodic_arrivals_is_a_set. + - by apply scheduler_sequential_jobs, periodic_arrivals_is_a_set. + - by apply scheduler_respects_affinity. + - apply scheduler_apa_work_conserving. + -- by apply periodic_arrivals_is_a_set. + -- by ins; apply RM_is_transitive. + { + unfold FP_to_JLDP; intros t x y; apply/orP. + by apply priority_is_total; rewrite periodic_arrivals_all_jobs_from_taskset. + } + - apply scheduler_enforces_policy. + -- by apply periodic_arrivals_is_a_set. + -- by ins; apply RM_is_transitive. + { + unfold FP_to_JLDP; intros t x y; apply/orP. + by apply priority_is_total; rewrite periodic_arrivals_all_jobs_from_taskset. + } + - by apply schedulability_test_succeeds. + Qed. + + End ExampleRTA. + +End ResponseTimeAnalysisFP. \ No newline at end of file diff --git a/implementation/apa/job.v b/implementation/apa/job.v new file mode 100644 index 0000000000000000000000000000000000000000..bf22a52aca85ee2489c0b591eb14e871252defd6 --- /dev/null +++ b/implementation/apa/job.v @@ -0,0 +1,63 @@ +Require Import rt.model.basic.time rt.util.all. +Require Import rt.implementation.apa.task. +From mathcomp Require Import ssreflect ssrbool ssrnat eqtype seq. + +Module ConcreteJob. + + Import Time. + Import ConcreteTask. + + Section Defs. + + Context {num_cpus: nat}. + + (* Definition of a concrete task. *) + Record concrete_job := + { + job_id: nat; + job_cost: time; + job_deadline: time; + job_task: @concrete_task num_cpus + }. + + (* To make it compatible with ssreflect, we define a decidable + equality for concrete jobs. *) + Definition job_eqdef (j1 j2: concrete_job) := + (job_id j1 == job_id j2) && + (job_cost j1 == job_cost j2) && + (job_deadline j1 == job_deadline j2) && + (job_task j1 == job_task j2). + + (* Next, we prove that job_eqdef is indeed an equality, ... *) + Lemma eqn_job : Equality.axiom job_eqdef. + Proof. + unfold Equality.axiom; intros x y. + destruct (job_eqdef x y) eqn:EQ. + { + apply ReflectT; unfold job_eqdef in *. + move: EQ => /andP [/andP [/andP [/eqP ID /eqP COST] /eqP DL] /eqP TASK]. + by destruct x, y; simpl in *; subst. + } + { + apply ReflectF. + unfold job_eqdef, not in *; intro BUG. + apply negbT in EQ; rewrite negb_and in EQ. + destruct x, y. + rewrite negb_and in EQ. + move: EQ => /orP [EQ | /eqP TASK]; last by apply TASK; inversion BUG. + move: EQ => /orP [EQ | /eqP DL]. + rewrite negb_and in EQ. + move: EQ => /orP [/eqP ID | /eqP COST]. + by apply ID; inversion BUG. + by apply COST; inversion BUG. + by apply DL; inversion BUG. + } + Qed. + + (* ..., which allows instantiating the canonical structure. *) + Canonical concrete_job_eqMixin := EqMixin eqn_job. + Canonical concrete_job_eqType := Eval hnf in EqType concrete_job concrete_job_eqMixin. + + End Defs. + +End ConcreteJob. \ No newline at end of file diff --git a/implementation/apa/schedule.v b/implementation/apa/schedule.v new file mode 100644 index 0000000000000000000000000000000000000000..80014bdb26b6f64c3c677a86fcd5256ca7fab092 --- /dev/null +++ b/implementation/apa/schedule.v @@ -0,0 +1,748 @@ +Require Import rt.util.all. +Require Import rt.model.apa.job rt.model.apa.task rt.model.apa.affinity + rt.model.apa.arrival_sequence rt.model.apa.schedule + rt.model.apa.platform rt.model.apa.priority. +From mathcomp Require Import ssreflect ssrbool ssrfun eqtype ssrnat fintype bigop seq path. + +Module ConcreteScheduler. + + Import Job SporadicTaskset ArrivalSequence Schedule Platform + Priority Affinity. + + (* In this section, we implement a concrete weak APA scheduler. *) + Section Implementation. + + Context {Job: eqType}. + Context {sporadic_task: eqType}. + + Variable job_cost: Job -> time. + Variable job_task: Job -> sporadic_task. + + (* Let num_cpus denote the number of processors, ...*) + Variable num_cpus: nat. + + (* ... and let arr_seq be any arrival sequence.*) + Variable arr_seq: arrival_sequence Job. + + (* Let alpha be an affinity associated with each task. *) + Variable alpha: task_affinity sporadic_task num_cpus. + + (* Assume a JLDP policy is given. *) + Variable higher_eq_priority: JLDP_policy arr_seq. + + (* Consider the list of pending jobs at time t, ... *) + Definition jobs_pending_at (sched: schedule num_cpus arr_seq) (t: time) := + [seq j <- jobs_arrived_up_to arr_seq t | pending job_cost sched j t]. + + (* ... which we sort by decreasing priority. *) + Definition sorted_pending_jobs (sched: schedule num_cpus arr_seq) (t: time) := + sort (higher_eq_priority t) (jobs_pending_at sched t). + + (* Now we implement the algorithm that generates the APA schedule. *) + + (* Given a job j at time t, we first define a predicate that states + whether j should preempt a mapping (cpu, x), where x is either Some j' + that is currently mapped to cpu or None. *) + Definition should_be_scheduled (j: JobIn arr_seq) (t: time) p := + let '(cpu, mapped_job) := p in + if mapped_job is Some j' then (* If there is a job j', check the priority and affinity. *) + (can_execute_on alpha (job_task j) cpu) && + ~~ (higher_eq_priority t j' j) + else (* Else, if cpu is idle, check only the affinity. *) + (can_execute_on alpha (job_task j) cpu). + + (* Next, using the "should_be_scheduled" predicate, we define a function + that tries to schedule job j by updating a list of mappings. + It does so by replacing the first pair (cpu, x) where j can be + scheduled (if it exists). *) + Definition update_available_cpu t allocation j := + replace_first (should_be_scheduled j t) (* search for processors that j can preempt *) + (set_pair_2nd (Some j)) (* replace the mapping in that processor with j *) + allocation. (* list of mappings *) + + (* Using the fuction "update_available_cpu", we now define an iteration + that tries to successively schedule each job in a list job_list. + Starting with an empty mapping, + <(cpu0, None), (cpu1, None), (cpu2, None), ...>, + it tries to schedule each job in job_list and yields some updated list: + <(cpu0, None), (cpu1, Some j5), (cpu2, j9), ...>. *) + Definition try_to_schedule_every_job job_list t := + foldl (update_available_cpu t) + (zip (enum (processor num_cpus)) (nseq num_cpus None)) (* empty mapping*) + job_list. + + (* The iteration we just defined is then applied to the list of pending jobs + at any time t. *) + Definition schedule_every_pending_job prev_sched t := + try_to_schedule_every_job (sorted_pending_jobs prev_sched t) t. + + (* The schedule can now be constructed iteratively. Starting from the empty schedule, ... *) + Definition empty_schedule : schedule num_cpus arr_seq := + fun cpu t => None. + + (* ..., we update the schedule at time t by calling schedule_every_pending_job with + the list of pending jobs at time t, and then converting the result to a function. *) + Definition update_schedule (prev_sched: schedule num_cpus arr_seq) + (t_next: time) : schedule num_cpus arr_seq := + fun cpu t => + if t == t_next then + pairs_to_function None (schedule_every_pending_job prev_sched t) cpu + else prev_sched cpu t. + + (* This allows us to iteratively construct the schedule up to some time t_max. *) + Fixpoint schedule_prefix (t_max: time) : schedule num_cpus arr_seq := + if t_max is t_prev.+1 then + (* At time t_prev + 1, schedule jobs that have not completed by time t_prev. *) + update_schedule (schedule_prefix t_prev) t_prev.+1 + else + (* At time 0, schedule any jobs that arrive. *) + update_schedule empty_schedule 0. + + (* Finally, the prefixes are used to build the complete schedule. *) + Definition scheduler (cpu: processor num_cpus) (t: time) := (schedule_prefix t) cpu t. + + End Implementation. + + (* In this section, we prove several properties about the scheduling algorithm we + implemented. For example, we show that it is APA work conserving. *) + Section Proofs. + + Context {Job: eqType}. + Context {sporadic_task: eqType}. + + Variable job_cost: Job -> time. + Variable job_task: Job -> sporadic_task. + + (* Assume a positive number of processors. *) + Variable num_cpus: nat. + Hypothesis H_at_least_one_cpu: num_cpus > 0. + + (* Let alpha be an affinity associated with each task. *) + Variable alpha: task_affinity sporadic_task num_cpus. + + (* Let arr_seq be any arrival sequence of jobs where ...*) + Variable arr_seq: arrival_sequence Job. + (* ...jobs have positive cost and...*) + Hypothesis H_job_cost_positive: + forall (j: JobIn arr_seq), job_cost_positive job_cost j. + (* ... at any time, there are no duplicates of the same job. *) + Hypothesis H_arrival_sequence_is_a_set : + arrival_sequence_is_a_set arr_seq. + + (* Consider any JLDP policy higher_eq_priority that is transitive and total. *) + Variable higher_eq_priority: JLDP_policy arr_seq. + Hypothesis H_priority_transitive: forall t, transitive (higher_eq_priority t). + Hypothesis H_priority_total: forall t, total (higher_eq_priority t). + + (* Let sched denote our concrete scheduler implementation. *) + Let sched := scheduler job_cost job_task num_cpus arr_seq alpha higher_eq_priority. + + (* Next, we provide some helper lemmas about the scheduler construction. *) + Section HelperLemmas. + + (* Let sched_prefix denote the prefix construction of our scheduler. *) + Let sched_prefix := schedule_prefix job_cost job_task num_cpus arr_seq alpha higher_eq_priority. + + (* We begin by showing that the scheduler preserves its prefixes. *) + Lemma scheduler_same_prefix : + forall t t_max cpu, + t <= t_max -> + sched_prefix t_max cpu t = sched cpu t. + Proof. + intros t t_max cpu LEt. + induction t_max; first by rewrite leqn0 in LEt; move: LEt => /eqP EQ; subst. + { + rewrite leq_eqVlt in LEt. + move: LEt => /orP [/eqP EQ | LESS]; first by subst. + { + feed IHt_max; first by done. + unfold sched_prefix, schedule_prefix, update_schedule at 1. + assert (FALSE: t == t_max.+1 = false). + { + by apply negbTE; rewrite neq_ltn LESS orTb. + } rewrite FALSE. + by rewrite -IHt_max. + } + } + Qed. + + (* To avoid many parameters, let's also rename the scheduling function. + We make a generic version (for any list of jobs l), ... *) + Let schedule_jobs t l := try_to_schedule_every_job job_task num_cpus arr_seq alpha higher_eq_priority t l. + (* ... and a specific version (for the pending jobs in sched). *) + Let schedule_pending_jobs t := schedule_jobs (sorted_pending_jobs job_cost num_cpus arr_seq higher_eq_priority sched t) t. + + (* Next, we show that there are no duplicate cpus in the mapping. *) + Lemma scheduler_uniq_cpus : + forall t l, + uniq (unzip1 (schedule_jobs l t)). + Proof. + intros t l. + induction l as [| l' j_last] using last_ind. + { + by rewrite unzip1_zip; [rewrite enum_uniq | rewrite size_enum_ord size_nseq]. + } + { + rewrite /schedule_jobs /try_to_schedule_every_job -cats1 foldl_cat /=. + assert (EQ: forall l j, unzip1 (update_available_cpu job_task num_cpus arr_seq + alpha higher_eq_priority t l j) = unzip1 l). + { + intros l j; clear -l j. + induction l; first by done. + unfold update_available_cpu; simpl. + by case SHOULD: should_be_scheduled; simpl; f_equal. + } + by rewrite EQ. + } + Qed. + + (* Next, we show that if a job j is in the mapping, then j must come from the list + of jobs l used in the construction. *) + Lemma scheduler_job_in_mapping : + forall l j t cpu, + (cpu, Some j) \in schedule_jobs l t -> j \in l. + Proof. + intros l j t cpu SOME; clear -SOME. + unfold schedule_every_pending_job in *. + induction l as [| l' j'] using last_ind; simpl in *. + { + apply mem_zip in SOME; last by rewrite size_enum_ord size_nseq. + by move: SOME => [_ /nseqP [SOME _]]. + } + { + rewrite /schedule_jobs /try_to_schedule_every_job -cats1 foldl_cat /= in SOME. + unfold update_available_cpu in SOME. + elim (replace_first_cases SOME) => [IN | [y [NEW IN]]]. + { + by rewrite mem_rcons in_cons; apply/orP; right; apply IHl. + } + { + case: NEW => EQ1 EQ2; subst. + by rewrite mem_rcons in_cons eq_refl orTb. + } + } + Qed. + + (* Next, we prove that if a pair (cpu, j) is in the mapping, then + cpu must be part of j's affinity. *) + Lemma scheduler_mapping_respects_affinity : + forall j t cpu, + (cpu, Some j) \in schedule_pending_jobs t -> + can_execute_on alpha (job_task j) cpu. + Proof. + intros j t cpu SOME. + unfold schedule_pending_jobs, schedule_every_pending_job in SOME. + set l := sorted_pending_jobs _ _ _ _ _ _ in SOME. + induction l as [| l' j'] using last_ind; simpl in *. + { + apply mem_zip in SOME; last by rewrite size_enum_ord size_nseq. + by move: SOME => [_ /nseqP [BUG _]]. + } + { + unfold schedule_jobs, try_to_schedule_every_job in SOME. + rewrite -cats1 foldl_cat /= in SOME. + elim (replace_first_cases SOME) => [IN | [y [NEW [SHOULD _]]]]; + first by apply IHl. + case: NEW => EQ1 EQ2; subst. + by unfold should_be_scheduled in SHOULD; desf. + } + Qed. + + (* Next, we show that the mapping does not schedule the same job j in two + different cpus. *) + Lemma scheduler_has_no_duplicate_jobs : + forall j t cpu1 cpu2, + (cpu1, Some j) \in schedule_pending_jobs t -> + (cpu2, Some j) \in schedule_pending_jobs t -> + cpu1 = cpu2. + Proof. + intros j t cpu1 cpu2 SOME1 SOME2. + unfold schedule_pending_jobs, schedule_every_pending_job in *. + set l := sorted_pending_jobs _ _ _ _ _ _ in SOME1 SOME2. + assert (UNIQ: uniq l). + by rewrite sort_uniq; apply filter_uniq, JobIn_uniq. + + induction l as [| l' j'] using last_ind; simpl in *. + { + apply mem_zip in SOME1; last by rewrite size_enum_ord size_nseq. + by move: SOME1 => [_ /nseqP [BUG _]]. + } + { + rewrite rcons_uniq in UNIQ; move: UNIQ => /andP [NOTIN UNIQ]. + rewrite /schedule_jobs /try_to_schedule_every_job -cats1 foldl_cat /= in SOME1 SOME2. + destruct (replace_first_cases SOME1) as [SAME1 | [[cpu1' p1] [EQ1 [SHOULD1 PREV1]]]]; + destruct (replace_first_cases SOME2) as [SAME2 | [[cpu2' p2] [EQ2 [SHOULD2 PREV2]]]]. + { + by apply IHl. + } + { + move: EQ2; case => EQ1 EQ2; subst cpu2' j'. + destruct p2 as [j2 |]. + { + apply scheduler_job_in_mapping in SAME1. + by rewrite SAME1 in NOTIN. + } + by apply (replace_first_new _ _ _ (cpu2, Some j)) in SOME1; + [by move: SOME1; case => -> | | | by done]; + apply/negP; intro BUG; + apply scheduler_job_in_mapping in BUG; + by rewrite BUG in NOTIN. + } + { + move: EQ1; case => EQ1 EQ2; subst cpu1' j'. + destruct p1 as [j1 |]. + { + apply scheduler_job_in_mapping in SAME2. + by rewrite SAME2 in NOTIN. + } + by apply (replace_first_new _ _ _ (cpu2, Some j)) in SOME1; + [by move: SOME1; case => -> | | | by done]; + apply/negP; intro BUG; + apply scheduler_job_in_mapping in BUG; + by rewrite BUG in NOTIN. + } + { + move: EQ1; case => /= EQ1' /= EQ2'; subst cpu1' j'. + move: EQ2; case => EQ1'; subst cpu2'. + by apply (replace_first_new _ _ _ (cpu2, Some j)) in SOME1; + [by move: SOME1; case => -> | | | by done]; + apply/negP; intro BUG; + apply scheduler_job_in_mapping in BUG; + by rewrite BUG in NOTIN. + } + } + Qed. + + (* Based on the definition of the schedule, a job j is scheduled on cpu + iff (cpu, Some j) is the final mapping. *) + Lemma scheduler_scheduled_on : + forall j cpu t, + scheduled_on sched j cpu t = ((cpu, Some j) \in schedule_pending_jobs t). + Proof. + unfold schedule_pending_jobs, schedule_jobs in *. + intros j cpu t. + induction t. + { + apply/idP/idP. + { + intros SCHED. + unfold scheduled_on, sched, scheduler, schedule_prefix in SCHED. + rewrite /update_schedule eq_refl in SCHED; move: SCHED => /eqP SCHED. + apply pairs_to_function_neq_default in SCHED; last by done. + unfold schedule_every_pending_job in SCHED. + set l := sorted_pending_jobs _ _ _ _ _ _ in SCHED. + set l' := sorted_pending_jobs _ _ _ _ _ _. + assert (EQ: l' = l). + { + unfold l, l', sorted_pending_jobs; f_equal. + unfold jobs_pending_at; apply eq_filter. + unfold pending; red; intro j0; do 2 f_equal. + unfold completed; f_equal. + by unfold service; rewrite big_geq // big_geq //. + } + by rewrite EQ. + } + { + intros SCHED. + have MEM := pairs_to_function_mem None. + apply MEM in SCHED; clear MEM; last by apply (scheduler_uniq_cpus 0). + unfold scheduled_on, sched, scheduler, schedule_prefix. + rewrite /update_schedule eq_refl. + rewrite -SCHED; apply/eqP; f_equal. + unfold schedule_pending_jobs, schedule_jobs, schedule_every_pending_job; f_equal. + unfold sorted_pending_jobs, jobs_pending_at; f_equal. + apply eq_filter; intros j0. + unfold pending; do 2 f_equal. + unfold completed; f_equal. + by unfold service; rewrite big_geq // big_geq //. + } + } + { + apply/idP/idP. + { + intros SCHED. + unfold scheduled_on, sched, scheduler, schedule_prefix in SCHED. + rewrite /update_schedule eq_refl in SCHED; move: SCHED => /eqP SCHED. + apply pairs_to_function_neq_default in SCHED; last by done. + unfold schedule_every_pending_job in SCHED. + set l := try_to_schedule_every_job _ _ _ _ _ _ _ in SCHED. + set l' := try_to_schedule_every_job _ _ _ _ _ _ _. + assert (EQ: l' = l). + { + unfold l', l, schedule_every_pending_job; f_equal. + unfold sorted_pending_jobs, jobs_pending_at; f_equal. + apply eq_filter; intros j0. + unfold pending; do 2 f_equal. + unfold completed; f_equal. + unfold service. rewrite big_nat_recr // big_nat_recr //=. + f_equal; apply eq_big_nat; intros t0 LE. + unfold service_at; apply eq_bigl; intros cpu0. + unfold scheduled_on; f_equal. + unfold sched; move: LE => /andP [_ LE]. + by rewrite <- scheduler_same_prefix with (t_max := t); last by apply ltnW. + } + by rewrite EQ. + } + { + intros SCHED. + have MEM := pairs_to_function_mem None. + apply MEM in SCHED; clear MEM; last by apply (scheduler_uniq_cpus t.+1). + unfold scheduled_on, sched, scheduler, schedule_prefix. + rewrite /update_schedule eq_refl. + rewrite -SCHED; apply/eqP; f_equal. + unfold schedule_every_pending_job; f_equal. + unfold sorted_pending_jobs, jobs_pending_at; f_equal. + apply eq_filter; intros j0. + unfold pending; do 2 f_equal. + unfold completed; f_equal. + unfold service. rewrite big_nat_recr // big_nat_recr //=. + f_equal; apply eq_big_nat; intros t0 LE. + unfold service_at; apply eq_bigl; intros cpu0. + unfold scheduled_on; f_equal. + unfold sched; move: LE => /andP [_ LE]. + by rewrite <- scheduler_same_prefix with (t_max := t); last by apply ltnW. + } + } + Qed. + + (* Now we show that for every cpu, there is always a pair in the mapping. *) + Lemma scheduler_has_cpus : + forall cpu t l, + exists x, + (cpu, x) \in schedule_jobs l t. + Proof. + intros cpu t l. + induction l as [| l' j_last] using last_ind; simpl in *. + { + by exists None; rewrite mem_zip_nseq_r; + [by rewrite mem_enum | by rewrite size_enum_ord]. + } + { + rewrite /schedule_jobs /try_to_schedule_every_job -cats1 foldl_cat /=. + move: IHl => [x IN]. + eapply replace_first_previous in IN; des; + first by exists x; apply IN. + rewrite /set_pair_2nd in IN. + by exists (Some j_last); apply IN0. + } + Qed. + + (* Next, consider a list of jobs l that is sorted by priority and does not have + duplicates. + We prove that for any job j in l, if j is not scheduled at time t, + then every cpu in j's affinity has some job mapped at time t. *) + Lemma scheduler_mapping_is_work_conserving : + forall j cpu t l, + j \in l -> + sorted (higher_eq_priority t) l -> + uniq l -> + (forall cpu, (cpu, Some j) \notin schedule_jobs l t) -> + can_execute_on alpha (job_task j) cpu -> + exists j_other, + (cpu, Some j_other) \in schedule_jobs l t. + Proof. + intros j cpu t l IN SORT UNIQ NOTSCHED CAN. + + generalize dependent cpu. + induction l as [| l' j_last] using last_ind; simpl in *; + first by rewrite in_nil in IN. + { + intros cpu CAN. + rewrite rcons_uniq in UNIQ; move: UNIQ => /andP [NOTIN UNIQ]. + rewrite /schedule_jobs /try_to_schedule_every_job -cats1 foldl_cat /= in NOTSCHED *. + set prev := foldl (update_available_cpu _ _ _ _ _ _) (zip _ _) l' in NOTSCHED *. + rewrite mem_rcons in_cons in IN. + move: IN => /orP [/eqP IN | LAST]; subst. + { + clear IHl. + assert (ALL: forall x, x \in prev -> ~~ (should_be_scheduled job_task num_cpus arr_seq + alpha higher_eq_priority j_last t) x). + { + apply replace_first_failed with (f := set_pair_2nd (Some j_last)). + by intros [cpu' j'] IN; apply NOTSCHED. + } + have [x IN] := scheduler_has_cpus cpu t l'; fold prev in IN. + specialize (ALL (cpu, x) IN). + simpl in ALL. + destruct x as [j' |]; last by rewrite CAN in ALL. + eapply replace_first_previous in IN; des; + first by exists j'; apply IN. + by exists j_last; apply IN0. + } + { + specialize (IHl LAST). + feed_n 2 IHl; + [by apply sorted_rcons_prefix in SORT | by done | ]. + feed IHl. + { + clear IHl; intros cpu'; specialize (NOTSCHED cpu'). + apply/negP; intro BUG. + apply replace_first_previous with (f := set_pair_2nd (Some j_last)) + (P := should_be_scheduled job_task num_cpus arr_seq alpha higher_eq_priority j_last t) + in BUG; des; + first by rewrite BUG in NOTSCHED. + move: BUG => /andP [_ /negP HP]. + by apply HP, order_sorted_rcons with (s := l'); try by done. + } + move: (IHl cpu CAN) => [j_old IN]; clear IHl LAST. + by eapply replace_first_previous in IN; des; + [exists j_old; apply IN | exists j_last; apply IN0]. + } + } + Qed. + + (* Next, we prove that the mapping enforces priority. *) + Lemma scheduler_priority : + forall j j_hp cpu t, + backlogged job_cost sched j t -> + can_execute_on alpha (job_task j) cpu -> + scheduled_on sched j_hp cpu t -> + higher_eq_priority t j_hp j. + Proof. + have SCHED_ON := scheduler_scheduled_on. + intros j j_hp cpu t BACK CAN SCHED. + move: BACK => /andP [PENDING NOTSCHED']. + assert (NOTSCHED: forall cpu, (cpu, Some j) \notin schedule_pending_jobs t). + { + intros cpu'; rewrite -SCHED_ON. + by rewrite negb_exists in NOTSCHED'; move: NOTSCHED' => /forallP ALL; apply ALL. + } + rewrite SCHED_ON in SCHED. + clear NOTSCHED' SCHED_ON. + + unfold schedule_pending_jobs, schedule_jobs, schedule_every_pending_job in *. + set l := sorted_pending_jobs _ _ _ _ _ _ in SCHED NOTSCHED. + + have IN: j \in l. + { + rewrite mem_sort mem_filter PENDING andTb JobIn_has_arrived. + by move: PENDING => /andP [H _]. + } + have INhp: j_hp \in l by apply scheduler_job_in_mapping in SCHED. + have SORT : sorted (higher_eq_priority t) l by apply sort_sorted. + have UNIQ: uniq l by rewrite sort_uniq filter_uniq // JobIn_uniq //. + + induction l as [| l' j_last] using last_ind; + first by rewrite in_nil in IN. + { + rewrite /try_to_schedule_every_job -cats1 foldl_cat /= in SCHED. + rewrite /try_to_schedule_every_job -cats1 foldl_cat /= in NOTSCHED. + set prev := foldl (update_available_cpu job_task num_cpus arr_seq alpha higher_eq_priority t) (zip (enum (processor num_cpus)) (nseq num_cpus None)) l' in SCHED NOTSCHED. + rewrite rcons_uniq in UNIQ; move: UNIQ => /andP [NOTIN UNIQ]. + rewrite 2!mem_rcons 2!in_cons in IN INhp. + move: IN => /orP [/eqP EQ | IN]; + move: INhp => /orP [/eqP EQhp | INhp]. + { + subst j_last j_hp. + by specialize (NOTSCHED cpu); rewrite SCHED in NOTSCHED. + } + { + subst j_last. + by apply order_sorted_rcons with (s := l'). + } + { + subst j_last; clear IHl. + destruct (replace_first_cases SCHED) as [PREV | [[cpu' y] [EQ [SHOULD IN']]]]. + { + unfold prev in PREV. + apply scheduler_job_in_mapping in PREV. + by rewrite PREV in NOTIN. + } + { + move: EQ; case; intro EQ1; subst cpu'. + destruct y as [j'|]. + { + unfold should_be_scheduled in SHOULD. + move: SHOULD => /andP [CAN' /negP HP]. + unfold prev in IN'. + apply scheduler_job_in_mapping in IN'. + by exfalso; apply HP, order_sorted_rcons with (s := l'). + } + { + destruct [exists cpu, ((cpu, Some j) \in prev)] eqn:EX. + { + move: EX => /existsP [cpu' IN'']. + unfold prev in IN''. + apply replace_first_previous with (f := set_pair_2nd (Some j_hp)) (P := should_be_scheduled job_task num_cpus arr_seq alpha higher_eq_priority j_hp t) in IN''; des; + first by specialize (NOTSCHED cpu'); rewrite IN'' in NOTSCHED. + move: IN'' => /andP [_ /negP HP]. + by exfalso; apply HP, order_sorted_rcons with (s := l'). + } + { + apply negbT in EX; rewrite negb_exists in EX. + move: EX => /forallP EX. + apply sorted_rcons_prefix in SORT. + move: (scheduler_mapping_is_work_conserving j cpu t l' IN SORT UNIQ EX CAN) => [j' BUG]. + have MEM := pairs_to_function_mem None. + apply MEM in BUG; last by apply scheduler_uniq_cpus. + have UNIQ' := scheduler_uniq_cpus t l'. + apply MEM in IN'; last by apply UNIQ'. + by rewrite IN' in BUG. + } + } + } + } + { + apply IHl; try (by done); last first. + { + by apply sorted_rcons_prefix in SORT. + } + { + intros cpu0; apply/negP; intro BUG. + unfold update_available_cpu in NOTSCHED. + apply replace_first_previous with (f := set_pair_2nd (Some j_last)) (P := should_be_scheduled job_task num_cpus arr_seq alpha higher_eq_priority j_last t) in BUG; des. + { + by specialize (NOTSCHED cpu0); rewrite BUG in NOTSCHED. + } + { + move: BUG => /andP [_ /negP HP]. + by apply HP, order_sorted_rcons with (s := l'); try by done. + } + } + { + destruct (replace_first_cases SCHED) as [| [[cpu' y][EQ [SHOULD IN']]]]; first by done. + move: EQ; case; intros EQ1 EQ2; subst cpu' j_last. + by rewrite INhp in NOTIN. + } + } + } + Qed. + + End HelperLemmas. + + (* Now, we prove the important properties about the implementation. *) + + (* Jobs do not execute before they arrive, ...*) + Theorem scheduler_jobs_must_arrive_to_execute: + jobs_must_arrive_to_execute sched. + Proof. + unfold jobs_must_arrive_to_execute. + intros j t SCHED. + move: SCHED => /existsP [cpu /eqP SCHED]. + unfold sched, scheduler, schedule_prefix in SCHED. + destruct t. + { + rewrite /update_schedule eq_refl in SCHED. + apply pairs_to_function_neq_default in SCHED; last by done. + unfold schedule_every_pending_job in SCHED. + apply scheduler_job_in_mapping in SCHED. + rewrite mem_sort mem_filter in SCHED. + move: SCHED => /andP [_ ARR]. + by apply JobIn_has_arrived in ARR. + } + { + unfold update_schedule at 1 in SCHED; rewrite eq_refl /= in SCHED. + apply pairs_to_function_neq_default in SCHED; last by done. + unfold schedule_every_pending_job in SCHED. + apply scheduler_job_in_mapping in SCHED. + rewrite mem_sort mem_filter in SCHED. + move: SCHED => /andP [_ ARR]. + by apply JobIn_has_arrived in ARR. + } + Qed. + + (* ..., jobs are sequential, ... *) + Theorem scheduler_sequential_jobs: sequential_jobs sched. + Proof. + intros j t cpu1 cpu2 SCHED1 SCHED2. + by apply scheduler_has_no_duplicate_jobs with (j := j) (t := t); + rewrite -scheduler_scheduled_on; apply/eqP. + Qed. + + (* ... and jobs do not execute after completion. *) + Theorem scheduler_completed_jobs_dont_execute: + completed_jobs_dont_execute job_cost sched. + Proof. + have SEQ := scheduler_sequential_jobs. + rename H_job_cost_positive into GT0. + unfold completed_jobs_dont_execute, service. + intros j t. + induction t; first by rewrite big_geq. + { + rewrite big_nat_recr // /=. + rewrite leq_eqVlt in IHt; move: IHt => /orP [/eqP EQ | LESS]; last first. + { + destruct (job_cost j); first by rewrite ltn0 in LESS. + rewrite -addn1; rewrite ltnS in LESS. + by apply leq_add; last by apply service_at_most_one, SEQ. + } + rewrite EQ -{2}[job_cost j]addn0; apply leq_add; first by done. + destruct t. + { + rewrite big_geq // in EQ. + specialize (GT0 j); unfold job_cost_positive in *. + by rewrite -EQ ltn0 in GT0. + } + { + unfold service_at; rewrite big_mkcond. + apply leq_trans with (n := \sum_(cpu < num_cpus) 0); + last by rewrite big_const_ord iter_addn mul0n addn0. + apply leq_sum; intros cpu _; desf. + move: Heq => /eqP SCHED. + unfold scheduler, schedule_prefix in SCHED. + unfold sched, scheduler, schedule_prefix, update_schedule at 1 in SCHED. + rewrite eq_refl in SCHED. + apply pairs_to_function_neq_default in SCHED; last by done. + unfold schedule_every_pending_job in SCHED. + apply scheduler_job_in_mapping in SCHED. + rewrite mem_sort mem_filter in SCHED. + move: SCHED => /andP [/andP [_ /negP NOTCOMP] _]. + exfalso; apply NOTCOMP; clear NOTCOMP. + unfold completed; apply/eqP. + unfold service; rewrite -EQ. + rewrite big_nat_cond [\sum_(_ <= _ < _ | true)_]big_nat_cond. + apply eq_bigr; move => i /andP [/andP [_ LT] _]. + apply eq_bigl; red; ins. + unfold scheduled_on; f_equal. + fold (schedule_prefix job_cost job_task num_cpus arr_seq alpha higher_eq_priority). + by rewrite scheduler_same_prefix. + } + } + Qed. + + (* In addition, the scheduler is APA work conserving, ... *) + Theorem scheduler_apa_work_conserving: + apa_work_conserving job_cost job_task sched alpha. + Proof. + intros j t BACK cpu CAN. + set l := (sorted_pending_jobs job_cost num_cpus arr_seq higher_eq_priority sched t). + have SORT : sorted (higher_eq_priority t) l by apply sort_sorted. + have UNIQ: uniq l by rewrite sort_uniq filter_uniq // JobIn_uniq //. + move: BACK => /andP [PENDING NOTSCHED]. + have IN: j \in l. + { + rewrite mem_sort mem_filter PENDING andTb JobIn_has_arrived. + by move: PENDING => /andP [H _]. + } + have WORK := scheduler_mapping_is_work_conserving j cpu t l IN SORT UNIQ. + exploit WORK; try by done. + { + rewrite negb_exists in NOTSCHED; move: NOTSCHED => /forallP NOTSCHED. + by intros cpu0; specialize (NOTSCHED cpu0); rewrite -scheduler_scheduled_on. + } + by move => [j_other IN']; exists j_other; rewrite scheduler_scheduled_on. + Qed. + + (* ..., respects affinities, ... *) + Theorem scheduler_respects_affinity: + respects_affinity job_task sched alpha. + Proof. + unfold respects_affinity; intros j cpu t SCHED. + apply scheduler_mapping_respects_affinity with (t := t). + by rewrite -scheduler_scheduled_on. + Qed. + + (* ... and enforces the JLDP policy under weak APA scheduling. *) + Theorem scheduler_enforces_policy : + enforces_JLDP_policy_under_weak_APA job_cost job_task sched alpha higher_eq_priority. + Proof. + unfold enforces_JLDP_policy_under_weak_APA. + intros j j_hp cpu t BACK SCHED ALPHA. + rewrite scheduler_scheduled_on in SCHED. + apply scheduler_priority with (cpu := cpu); try by done. + by rewrite scheduler_scheduled_on. + Qed. + + End Proofs. + +End ConcreteScheduler. \ No newline at end of file diff --git a/implementation/apa/task.v b/implementation/apa/task.v new file mode 100644 index 0000000000000000000000000000000000000000..6281667a82afba5ce8fc63344a14a821cdf349eb --- /dev/null +++ b/implementation/apa/task.v @@ -0,0 +1,76 @@ +Require Import rt.model.apa.time rt.util.all. +Require Import rt.model.apa.task rt.model.apa.affinity. +From mathcomp Require Import ssreflect ssrbool ssrnat eqtype seq. + +Module ConcreteTask. + + Import Time SporadicTaskset Affinity. + + Section Defs. + + (* Let num_cpus be the number of processors. *) + Context {num_cpus: nat}. + + (* Definition of a concrete task. *) + Record concrete_task := + { + task_id: nat; (* for uniqueness *) + task_cost: time; + task_period: time; + task_deadline: time; + task_affinity: affinity num_cpus + }. + + (* To make it compatible with ssreflect, we define a decidable + equality for concrete tasks. *) + Definition task_eqdef (t1 t2: concrete_task) := + (task_id t1 == task_id t2) && + (task_cost t1 == task_cost t2) && + (task_period t1 == task_period t2) && + (task_deadline t1 == task_deadline t2) && + (task_affinity t1 == task_affinity t2). + + (* Next, we prove that task_eqdef is indeed an equality, ... *) + Lemma eqn_task : Equality.axiom task_eqdef. + Proof. + unfold Equality.axiom; intros x y. + destruct (task_eqdef x y) eqn:EQ. + { + apply ReflectT. + unfold task_eqdef in *. + move: EQ => /andP [/andP [/andP [/andP [/eqP ID /eqP COST]] /eqP PERIOD] /eqP DL] /eqP ALPHA. + by destruct x, y; simpl in *; subst. + } + { + apply ReflectF. + unfold task_eqdef, not in *; intro BUG. + apply negbT in EQ; rewrite negb_and in EQ. + destruct x, y. + rewrite negb_and in EQ. + move: EQ => /orP [EQ | /eqP DL]; last by apply DL; inversion BUG. + move: EQ => /orP [EQ | /eqP PERIOD]; last by apply PERIOD; inversion BUG. + rewrite negb_and in EQ. + move: EQ => /orP [EQ | /eqP COST]; last by apply COST; inversion BUG. + rewrite negb_and in EQ. + move: EQ => /orP [/eqP ID | /eqP ALPHA]; last by apply ALPHA; inversion BUG. + by apply ID; inversion BUG. + } + Qed. + + (* ..., which allows instantiating the canonical structure. *) + Canonical concrete_task_eqMixin := EqMixin eqn_task. + Canonical concrete_task_eqType := Eval hnf in EqType concrete_task concrete_task_eqMixin. + + End Defs. + + Section ConcreteTaskset. + + (* Let num_cpus be the number of processors. *) + Variable num_cpus: nat. + + Definition concrete_taskset := + taskset_of (@concrete_task_eqType num_cpus). + + End ConcreteTaskset. + +End ConcreteTask. \ No newline at end of file diff --git a/implementation/basic/arrival_sequence.v b/implementation/basic/arrival_sequence.v index 96089dc1546e8460cd92de5ebd0e643b98c7ec1c..17920ae1ecc32b11b02c920a4af618c5db48159c 100644 --- a/implementation/basic/arrival_sequence.v +++ b/implementation/basic/arrival_sequence.v @@ -95,14 +95,13 @@ Module ConcreteArrivalSequence. } Qed. - (* ... and if the task set has no duplicates, the same applies to - the arrival sequence. *) + (* ... and the arrival sequence has no duplicate jobs. *) Theorem periodic_arrivals_is_a_set: - uniq ts -> arrival_sequence_is_a_set arr_seq. + arrival_sequence_is_a_set arr_seq. Proof. - intros UNIQ t. + intros t. unfold arr_seq, periodic_arrival_sequence. - apply (pmap_uniq) with (g := job_task); last by done. + apply (pmap_uniq) with (g := job_task); last by destruct ts. by unfold add_job, ocancel; intro tsk; desf. Qed. diff --git a/implementation/basic/bertogna_edf_example.v b/implementation/basic/bertogna_edf_example.v index 661197da575fd0778e82987f936aa91761d16bac..dfd5e8f997dc692e3435e86cf05d931fa21caa32 100644 --- a/implementation/basic/bertogna_edf_example.v +++ b/implementation/basic/bertogna_edf_example.v @@ -23,15 +23,10 @@ Module ResponseTimeAnalysisEDF. Let tsk3 := {| task_id := 3; task_cost := 3; task_period := 12; task_deadline := 11|}. (* Let ts be a task set containing these three tasks. *) - Let ts := [:: tsk1; tsk2; tsk3]. + Program Let ts := Build_set [:: tsk1; tsk2; tsk3] _. Section FactsAboutTaskset. - Fact ts_is_a_set: uniq ts. - Proof. - by compute. - Qed. - Fact ts_has_valid_parameters: valid_sporadic_taskset task_cost task_period task_deadline ts. Proof. @@ -109,10 +104,9 @@ Module ResponseTimeAnalysisEDF. Proof. intros tsk IN. have VALID := periodic_arrivals_valid_job_parameters ts ts_has_valid_parameters. - have EDFVALID := @edf_valid_policy _ arr_seq job_deadline. - unfold valid_JLDP_policy, valid_sporadic_job, valid_realtime_job in *; des. + have TSVALID := ts_has_valid_parameters. + unfold valid_sporadic_job, valid_realtime_job in *; des. apply taskset_schedulable_by_edf_rta with (task_cost := task_cost) (task_period := task_period) (task_deadline := task_deadline) (ts0 := ts). - - by apply ts_is_a_set. - by apply ts_has_valid_parameters. - by apply ts_has_constrained_deadlines. - by apply periodic_arrivals_all_jobs_from_taskset. @@ -125,7 +119,9 @@ Module ResponseTimeAnalysisEDF. -- by apply periodic_arrivals_is_a_set. - by apply scheduler_sequential_jobs, periodic_arrivals_is_a_set. - by apply scheduler_work_conserving. - - by apply scheduler_enforces_policy; ins. + - apply scheduler_enforces_policy. + -- by apply EDF_is_transitive. + -- by apply EDF_is_total. - by apply schedulability_test_succeeds. - by apply IN. Qed. diff --git a/implementation/basic/bertogna_fp_example.v b/implementation/basic/bertogna_fp_example.v new file mode 100644 index 0000000000000000000000000000000000000000..e6f766bb79d318a4869a61bf3a2e0d6f073ff1d3 --- /dev/null +++ b/implementation/basic/bertogna_fp_example.v @@ -0,0 +1,175 @@ +Require Import rt.util.all. +Require Import rt.model.basic.job rt.model.basic.task + rt.model.basic.schedule rt.model.basic.schedulability + rt.model.basic.priority rt.model.basic.platform. +Require Import rt.analysis.basic.workload_bound + rt.analysis.basic.interference_bound_fp + rt.analysis.basic.bertogna_fp_comp. +Require Import rt.implementation.basic.job + rt.implementation.basic.task + rt.implementation.basic.schedule + rt.implementation.basic.arrival_sequence. +From mathcomp Require Import ssreflect ssrbool ssrnat eqtype seq bigop div. + +Module ResponseTimeAnalysisFP. + + Import Job Schedule SporadicTaskset Priority Schedulability Platform InterferenceBoundFP WorkloadBound ResponseTimeIterationFP. + Import ConcreteJob ConcreteTask ConcreteArrivalSequence ConcreteScheduler. + + (* In this section, we instantiate a simple example to show that the theorems + contain no contradictory assumptions. *) + Section ExampleRTA. + + Let tsk1 := {| task_id := 1; task_cost := 2; task_period := 5; task_deadline := 3|}. + Let tsk2 := {| task_id := 2; task_cost := 4; task_period := 6; task_deadline := 5|}. + Let tsk3 := {| task_id := 3; task_cost := 3; task_period := 12; task_deadline := 11|}. + + (* Let ts be a task set containing these three tasks (sorted by rate-monotonic priority). *) + Program Let ts := Build_set [:: tsk1; tsk2; tsk3] _. + + Section FactsAboutTaskset. + + Fact ts_has_valid_parameters: + valid_sporadic_taskset task_cost task_period task_deadline ts. + Proof. + intros tsk IN. + repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute); by done. + Qed. + + Fact ts_has_constrained_deadlines: + forall tsk, + tsk \in ts -> + task_deadline tsk <= task_period tsk. + Proof. + intros tsk IN. + repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute); by done. + Qed. + + End FactsAboutTaskset. + + (* Assume there are two processors. *) + Let num_cpus := 2. + + (* Recall the FP RTA schedulability test. *) + Let schedulability_test := + fp_schedulable task_cost task_period task_deadline num_cpus. + + (* Now we show that the schedulability test returns true. *) + Fact schedulability_test_succeeds : + schedulability_test ts = true. + Proof. + unfold schedulability_test, fp_schedulable, fp_claimed_bounds; simpl. + unfold total_interference_bound_fp, div_floor. + rewrite big_nil div0n addn0 /=. + unfold div_floor; simpl. + set I2 := total_interference_bound_fp task_cost task_period tsk2 + [:: (tsk1, 2)]. + assert (H1: I2 4 = 1). + { + by unfold I2, total_interference_bound_fp; rewrite big_cons big_nil; compute. + } + rewrite H1. + have H2: 4 + 1 %/ num_cpus = 4 by compute. + rewrite H2 H1 H2. + have H3: 3 < 5 by compute. + rewrite H3. + unfold fp_bound_of_task. + have H4: per_task_rta task_cost task_period num_cpus tsk3 [:: (tsk1, 2); (tsk2, 4)] + (max_steps task_cost task_deadline tsk3) = 5. + { + unfold per_task_rta; simpl. + unfold total_interference_bound_fp at 9. + rewrite !big_cons big_nil /=. + unfold total_interference_bound_fp at 8. + rewrite !big_cons big_nil /=. + unfold total_interference_bound_fp at 7. + rewrite !big_cons big_nil /=. + unfold total_interference_bound_fp at 6. + rewrite !big_cons big_nil /=. + unfold total_interference_bound_fp at 5. + rewrite !big_cons big_nil /=. + unfold total_interference_bound_fp at 4. + rewrite !big_cons big_nil /=. + unfold total_interference_bound_fp at 3. + rewrite !big_cons big_nil /=. + unfold total_interference_bound_fp at 2. + rewrite !big_cons big_nil /=. + unfold total_interference_bound_fp at 1. + by rewrite !big_cons big_nil /=; compute. + } + by rewrite H4. + Qed. + + (* Let arr_seq be the periodic arrival sequence from ts. *) + Let arr_seq := periodic_arrival_sequence ts. + + (* Assume rate-monotonic priorities. *) + Let higher_priority : JLDP_policy arr_seq := + (FP_to_JLDP job_task (RM task_period)). + + Section FactsAboutPriorityOrder. + + Lemma ts_has_unique_priorities : + FP_is_antisymmetric_over_task_set (RM task_period) ts. + Proof. + unfold RM; intros tsk tsk' IN IN' HP HP'. + have EQ: task_period tsk = task_period tsk' by apply/eqP; rewrite eqn_leq HP HP'. + clear HP HP'. + rewrite !in_cons 2!in_nil 2!orbF in IN IN'; des; rewrite IN IN'; try (by done); + subst tsk tsk'; simpl in *; by done. + Qed. + + Lemma priority_is_total : + FP_is_total_over_task_set (RM task_period) ts. + Proof. + unfold RM; intros tsk tsk' IN IN'. + destruct (leqP (task_period tsk) (task_period tsk')); + [by left | by right; apply ltnW]. + Qed. + + End FactsAboutPriorityOrder. + + (* Let sched be the work-conserving RM scheduler. *) + Let sched := scheduler job_cost num_cpus arr_seq higher_priority. + + (* Recall the definition of deadline miss. *) + Let no_deadline_missed_by := + task_misses_no_deadline job_cost job_deadline job_task sched. + + (* Next, we prove that ts is schedulable with the result of the test. *) + Corollary ts_is_schedulable: + forall tsk, + tsk \in ts -> + no_deadline_missed_by tsk. + Proof. + 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_priority0 := RM task_period); try (by done). + - by apply ts_has_constrained_deadlines. + - by apply ts_has_unique_priorities. + - by apply priority_is_total. + - by apply RM_is_transitive. + - by apply periodic_arrivals_all_jobs_from_taskset. + - by apply periodic_arrivals_are_sporadic. + - by apply scheduler_jobs_must_arrive_to_execute. + - apply scheduler_completed_jobs_dont_execute; intro j'. + -- by specialize (VALID j'); des. + -- by apply periodic_arrivals_is_a_set. + - by apply scheduler_sequential_jobs, periodic_arrivals_is_a_set. + - by apply scheduler_work_conserving. + - apply scheduler_enforces_policy. + -- by intros t; apply RM_is_transitive. + { + unfold FP_to_JLDP; intros t x y; apply/orP. + by apply priority_is_total; rewrite periodic_arrivals_all_jobs_from_taskset. + } + - by apply schedulability_test_succeeds. + Qed. + + End ExampleRTA. + +End ResponseTimeAnalysisFP. \ No newline at end of file diff --git a/implementation/basic/schedule.v b/implementation/basic/schedule.v index c4ba99cc8467fe63b174ce1a5cb21710dbf71415..99adb9705ae30763e257aa572e2edc0812d9fd09 100644 --- a/implementation/basic/schedule.v +++ b/implementation/basic/schedule.v @@ -84,13 +84,15 @@ Module ConcreteScheduler. (* Next, we provide some helper lemmas about the scheduler construction. *) Section HelperLemmas. + + (* Let's use a shorter name for the schedule prefix function. *) + Let sched_prefix := schedule_prefix job_cost num_cpus arr_seq higher_eq_priority. (* First, we show that the scheduler preserves its prefixes. *) Lemma scheduler_same_prefix : forall t t_max cpu, t <= t_max -> - schedule_prefix job_cost num_cpus arr_seq higher_eq_priority t_max cpu t = - scheduler job_cost num_cpus arr_seq higher_eq_priority cpu t. + sched_prefix t_max cpu t = sched cpu t. Proof. intros t t_max cpu LEt. induction t_max. @@ -102,7 +104,7 @@ Module ConcreteScheduler. move: LEt => /orP [/eqP EQ | LESS]; first by subst. { feed IHt_max; first by done. - unfold schedule_prefix, update_schedule at 1. + unfold sched_prefix, schedule_prefix, update_schedule at 1. assert (FALSE: t == t_max.+1 = false). { by apply negbTE; rewrite neq_ltn LESS orTb. @@ -144,7 +146,9 @@ Module ConcreteScheduler. apply eq_big_nat; move => t0 /andP [_ LT]. unfold service_at; apply eq_bigl; red; intros cpu'. fold (schedule_prefix job_cost num_cpus arr_seq higher_eq_priority). - by rewrite /scheduled_on 2?scheduler_same_prefix ?leqnn //. + have SAME := scheduler_same_prefix; unfold sched_prefix, sched in *. + rewrite /scheduled_on; f_equal; unfold schedule_prefix. + by rewrite SAME // ?leqnn. } Qed. @@ -168,6 +172,7 @@ Module ConcreteScheduler. exists i, nth_or_none (sorted_jobs t) i = Some j /\ i >= num_cpus. Proof. + have SAME := scheduler_same_prefix. intros j t BACK. move: BACK => /andP [PENDING /negP NOTCOMP]. assert (IN: j \in sorted_jobs t). @@ -205,7 +210,8 @@ Module ConcreteScheduler. unfold service_at; apply eq_bigl; red; intro cpu. unfold scheduled_on; f_equal. fold (schedule_prefix job_cost num_cpus arr_seq higher_eq_priority). - by rewrite scheduler_same_prefix. + unfold sched_prefix in *. + by rewrite /schedule_prefix SAME. } Qed. diff --git a/implementation/jitter/arrival_sequence.v b/implementation/jitter/arrival_sequence.v index 4a66266ea2b77ddf4a2239e775cacb0357091761..b8b7f4e3554cd4cccdeef2841f46f1d33f6fdaa2 100644 --- a/implementation/jitter/arrival_sequence.v +++ b/implementation/jitter/arrival_sequence.v @@ -95,14 +95,13 @@ Module ConcreteArrivalSequence. } Qed. - (* ... and if the task set has no duplicates, the same applies to - the arrival sequence. *) + (* ... and the arrival sequence has no duplicate jobs. *) Theorem periodic_arrivals_is_a_set: - uniq ts -> arrival_sequence_is_a_set arr_seq. + arrival_sequence_is_a_set arr_seq. Proof. - intros UNIQ t. + intros t. unfold arr_seq, periodic_arrival_sequence. - apply (pmap_uniq) with (g := job_task); last by done. + apply (pmap_uniq) with (g := job_task); last by destruct ts. by unfold add_job, ocancel; intro tsk; desf. Qed. diff --git a/implementation/jitter/bertogna_edf_example.v b/implementation/jitter/bertogna_edf_example.v index 6cfdd6ac69d61c8a4135037adcf1ab2aa7dc8b14..4763d22d9d62250964e0b04dfd872600b2decdb8 100644 --- a/implementation/jitter/bertogna_edf_example.v +++ b/implementation/jitter/bertogna_edf_example.v @@ -23,15 +23,10 @@ Module ResponseTimeAnalysisEDF. Let tsk3 := {| task_id := 3; task_cost := 2; task_period := 12; task_deadline := 11; task_jitter := 2|}. (* Let ts be a task set containing these three tasks. *) - Let ts := [:: tsk1; tsk2; tsk3]. + Program Let ts := Build_set [:: tsk1; tsk2; tsk3] _. Section FactsAboutTaskset. - Fact ts_is_a_set: uniq ts. - Proof. - by compute. - Qed. - Fact ts_has_valid_parameters: valid_sporadic_taskset task_cost task_period task_deadline ts. Proof. @@ -110,11 +105,10 @@ Module ResponseTimeAnalysisEDF. Proof. intros tsk IN. have VALID := periodic_arrivals_valid_job_parameters ts ts_has_valid_parameters. - have EDFVALID := @edf_valid_policy _ arr_seq job_deadline. - unfold valid_JLDP_policy, valid_sporadic_job_with_jitter, + have VALIDTS := ts_has_valid_parameters. + unfold valid_sporadic_job_with_jitter, valid_sporadic_job, valid_realtime_job in *; des. apply taskset_schedulable_by_edf_rta with (task_cost := task_cost) (task_period := task_period) (task_deadline := task_deadline) (ts0 := ts) (task_jitter := task_jitter) (job_jitter := job_jitter). - - by apply ts_is_a_set. - by apply ts_has_valid_parameters. - by apply ts_has_constrained_deadlines. - by apply periodic_arrivals_all_jobs_from_taskset. @@ -127,7 +121,9 @@ Module ResponseTimeAnalysisEDF. -- by apply periodic_arrivals_is_a_set. - by apply scheduler_sequential_jobs, periodic_arrivals_is_a_set. - by apply scheduler_work_conserving. - - by apply scheduler_enforces_policy; ins. + - apply scheduler_enforces_policy. + -- by apply EDF_is_transitive. + -- by apply EDF_is_total. - by apply schedulability_test_succeeds. - by apply IN. Qed. diff --git a/implementation/jitter/bertogna_fp_example.v b/implementation/jitter/bertogna_fp_example.v new file mode 100644 index 0000000000000000000000000000000000000000..da15c4d31850879ac7f42a42dd3f6f275443b807 --- /dev/null +++ b/implementation/jitter/bertogna_fp_example.v @@ -0,0 +1,177 @@ +Require Import rt.util.all. +Require Import rt.model.jitter.job rt.model.jitter.task + rt.model.jitter.schedule rt.model.jitter.schedulability + rt.model.jitter.priority rt.model.jitter.platform. +Require Import rt.analysis.jitter.workload_bound + rt.analysis.jitter.interference_bound_fp + rt.analysis.jitter.bertogna_fp_comp. +Require Import rt.implementation.jitter.job + rt.implementation.jitter.task + rt.implementation.jitter.schedule + rt.implementation.jitter.arrival_sequence. +From mathcomp Require Import ssreflect ssrbool ssrnat eqtype seq bigop div. + +Module ResponseTimeAnalysisFP. + + Import JobWithJitter ScheduleWithJitter SporadicTaskset Priority Schedulability Platform InterferenceBoundFP WorkloadBoundJitter ResponseTimeIterationFP. + Import ConcreteJob ConcreteTask ConcreteArrivalSequence ConcreteScheduler. + + (* In this section, we instantiate a simple example to show that the theorems + contain no contradictory assumptions. *) + Section ExampleRTA. + + Let tsk1 := {| task_id := 1; task_cost := 2; task_period := 5; task_deadline := 3; task_jitter := 0|}. + Let tsk2 := {| task_id := 2; task_cost := 4; task_period := 6; task_deadline := 5; task_jitter := 1|}. + Let tsk3 := {| task_id := 3; task_cost := 3; task_period := 12; task_deadline := 11; task_jitter := 2|}. + + (* Let ts be a task set containing these three tasks (sorted by rate-monotonic priority). *) + Program Let ts := Build_set [:: tsk1; tsk2; tsk3] _. + + Section FactsAboutTaskset. + + Fact ts_has_valid_parameters: + valid_sporadic_taskset task_cost task_period task_deadline ts. + Proof. + intros tsk IN. + repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute); by done. + Qed. + + Fact ts_has_constrained_deadlines: + forall tsk, + tsk \in ts -> + task_deadline tsk <= task_period tsk. + Proof. + intros tsk IN. + repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute); by done. + Qed. + + End FactsAboutTaskset. + + (* Assume there are two processors. *) + Let num_cpus := 2. + + (* Recall the FP RTA schedulability test. *) + Let schedulability_test := + fp_schedulable task_cost task_period task_deadline task_jitter num_cpus. + + (* Now we show that the schedulability test returns true. *) + Fact schedulability_test_succeeds : + schedulability_test ts = true. + Proof. + unfold schedulability_test, fp_schedulable, fp_claimed_bounds; simpl. + unfold total_interference_bound_fp, div_floor. + rewrite big_nil div0n addn0 /=. + unfold div_floor; simpl. + set I2 := total_interference_bound_fp task_cost task_period + task_jitter tsk2 [:: (tsk1, 2)]. + assert (H1: I2 4 = 1). + { + by unfold I2, total_interference_bound_fp; rewrite big_cons big_nil; compute. + } + rewrite H1. + have H2: 4 + 1 %/ num_cpus = 4 by compute. + rewrite H2 H1 H2. + have H3: (1 + 4 <= 5) by compute. + rewrite H3. + unfold fp_bound_of_task. + have H4: per_task_rta task_cost task_period task_jitter num_cpus + tsk3 [:: (tsk1, 2); (tsk2, 4)] + (max_steps task_cost task_deadline tsk3) = 5. + { + unfold per_task_rta; simpl. + unfold total_interference_bound_fp at 9. + rewrite !big_cons big_nil /=. + unfold total_interference_bound_fp at 8. + rewrite !big_cons big_nil /=. + unfold total_interference_bound_fp at 7. + rewrite !big_cons big_nil /=. + unfold total_interference_bound_fp at 6. + rewrite !big_cons big_nil /=. + unfold total_interference_bound_fp at 5. + rewrite !big_cons big_nil /=. + unfold total_interference_bound_fp at 4. + rewrite !big_cons big_nil /=. + unfold total_interference_bound_fp at 3. + rewrite !big_cons big_nil /=. + unfold total_interference_bound_fp at 2. + rewrite !big_cons big_nil /=. + unfold total_interference_bound_fp at 1. + by rewrite !big_cons big_nil /=; compute. + } + by rewrite H4. + Qed. + + (* Let arr_seq be the periodic arrival sequence from ts. *) + Let arr_seq := periodic_arrival_sequence ts. + + (* Assume rate-monotonic priorities. *) + Let higher_priority : JLDP_policy arr_seq := + (FP_to_JLDP job_task (RM task_period)). + + Section FactsAboutPriorityOrder. + + Lemma ts_has_unique_priorities : + FP_is_antisymmetric_over_task_set (RM task_period) ts. + Proof. + unfold RM; intros tsk tsk' IN IN' HP HP'. + have EQ: task_period tsk = task_period tsk' by apply/eqP; rewrite eqn_leq HP HP'. + clear HP HP'. + rewrite !in_cons 2!in_nil 2!orbF in IN IN'; des; rewrite IN IN'; try (by done); + subst tsk tsk'; simpl in *; by done. + Qed. + + Lemma priority_is_total : + FP_is_total_over_task_set (RM task_period) ts. + Proof. + unfold RM; intros tsk tsk' IN IN'. + destruct (leqP (task_period tsk) (task_period tsk')); + [by left | by right; apply ltnW]. + Qed. + + End FactsAboutPriorityOrder. + + (* Let sched be the work-conserving RM scheduler. *) + Let sched := scheduler job_cost job_jitter num_cpus arr_seq higher_priority. + + (* Recall the definition of deadline miss. *) + Let no_deadline_missed_by := + task_misses_no_deadline job_cost job_deadline job_task sched. + + (* Next, we prove that ts is schedulable with the result of the test. *) + Corollary ts_is_schedulable: + forall tsk, + tsk \in ts -> + no_deadline_missed_by tsk. + Proof. + 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_with_jitter, 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) + (task_jitter := task_jitter) (job_jitter := job_jitter) + (ts0 := ts) (higher_priority0 := RM task_period); try (by done). + - by apply ts_has_constrained_deadlines. + - by apply ts_has_unique_priorities. + - by apply priority_is_total. + - by apply RM_is_transitive. + - by apply periodic_arrivals_all_jobs_from_taskset. + - by apply periodic_arrivals_are_sporadic. + - by apply scheduler_jobs_execute_after_jitter. + - apply scheduler_completed_jobs_dont_execute; intro j'. + -- by specialize (VALID j'); des. + -- by apply periodic_arrivals_is_a_set. + - by apply scheduler_sequential_jobs, periodic_arrivals_is_a_set. + - by apply scheduler_work_conserving. + - apply scheduler_enforces_policy. + -- by intros t; apply RM_is_transitive. + { + unfold FP_to_JLDP; intros t x y; apply/orP. + by apply priority_is_total; rewrite periodic_arrivals_all_jobs_from_taskset. + } + - by apply schedulability_test_succeeds. + Qed. + + End ExampleRTA. + +End ResponseTimeAnalysisFP. \ No newline at end of file diff --git a/implementation/jitter/schedule.v b/implementation/jitter/schedule.v index d4fc05aa6b11a9d22f76aa0edcd485ed6265cbbc..58268053bcb3214be63db02c1fed4f24b1f7f721 100644 --- a/implementation/jitter/schedule.v +++ b/implementation/jitter/schedule.v @@ -86,13 +86,15 @@ Module ConcreteScheduler. (* Next, we provide some helper lemmas about the scheduler construction. *) Section HelperLemmas. + + (* Let's use a shorter name for the schedule prefix function. *) + Let sched_prefix := schedule_prefix job_cost job_jitter num_cpus arr_seq higher_eq_priority. (* First, we show that the scheduler preserves its prefixes. *) Lemma scheduler_same_prefix : forall t t_max cpu, t <= t_max -> - schedule_prefix job_cost job_jitter num_cpus arr_seq higher_eq_priority t_max cpu t = - scheduler job_cost job_jitter num_cpus arr_seq higher_eq_priority cpu t. + sched_prefix t_max cpu t = sched cpu t. Proof. intros t t_max cpu LEt. induction t_max. @@ -104,7 +106,7 @@ Module ConcreteScheduler. move: LEt => /orP [/eqP EQ | LESS]; first by subst. { feed IHt_max; first by done. - unfold schedule_prefix, update_schedule at 1. + unfold sched_prefix, schedule_prefix, update_schedule at 1. assert (FALSE: t == t_max.+1 = false). { by apply negbTE; rewrite neq_ltn LESS orTb. @@ -147,7 +149,8 @@ Module ConcreteScheduler. unfold service_at; apply eq_bigl; red; intros cpu'. unfold scheduled_on; f_equal. fold (schedule_prefix job_cost job_jitter num_cpus arr_seq higher_eq_priority). - by rewrite 2?scheduler_same_prefix ?leqnn //. + have SAME := scheduler_same_prefix; unfold sched_prefix, sched in *. + by rewrite /schedule_prefix SAME. } Qed. @@ -170,6 +173,7 @@ Module ConcreteScheduler. exists i, nth_or_none (sorted_jobs t) i = Some j /\ i >= num_cpus. Proof. + have SAME := scheduler_same_prefix. intros j t BACK. move: BACK => /andP [PENDING /negP NOTCOMP]. assert (IN: j \in sorted_jobs t). @@ -209,7 +213,7 @@ Module ConcreteScheduler. unfold service_at; apply eq_bigl; red; intro cpu. unfold scheduled_on; f_equal. fold (schedule_prefix job_cost job_jitter num_cpus arr_seq higher_eq_priority). - by rewrite scheduler_same_prefix //. + by unfold sched_prefix in *; rewrite /schedule_prefix SAME. } Qed. diff --git a/implementation/parallel/arrival_sequence.v b/implementation/parallel/arrival_sequence.v new file mode 100644 index 0000000000000000000000000000000000000000..4f6f2213a1a2cd21ca461cb2a122425adea7f1a3 --- /dev/null +++ b/implementation/parallel/arrival_sequence.v @@ -0,0 +1,2 @@ +(* We reuse the arrival sequence from the basic model. *) +Require Export rt.implementation.basic.arrival_sequence. \ No newline at end of file diff --git a/implementation/parallel/bertogna_edf_example.v b/implementation/parallel/bertogna_edf_example.v new file mode 100644 index 0000000000000000000000000000000000000000..d2398d72dfb56eee18c09e904ca63322e1567b50 --- /dev/null +++ b/implementation/parallel/bertogna_edf_example.v @@ -0,0 +1,137 @@ +Require Import rt.util.all. +Require Import rt.model.basic.job rt.model.basic.task + rt.model.basic.schedule rt.model.basic.schedulability + rt.model.basic.priority rt.model.basic.platform. +Require Import rt.analysis.parallel.workload_bound + rt.analysis.parallel.interference_bound_edf + rt.analysis.parallel.bertogna_edf_comp. +Require Import rt.implementation.parallel.job + rt.implementation.parallel.task + rt.implementation.parallel.schedule + rt.implementation.parallel.arrival_sequence. +From mathcomp Require Import ssreflect ssrbool ssrnat eqtype seq bigop div. + +Module ResponseTimeAnalysisEDF. + + Import Job Schedule SporadicTaskset Priority Schedulability Platform InterferenceBoundEDF WorkloadBound ResponseTimeIterationEDF. + Import ConcreteJob ConcreteTask ConcreteArrivalSequence ConcreteScheduler. + + Section ExampleRTA. + + Let tsk1 := {| task_id := 1; task_cost := 2; task_period := 6; task_deadline := 6|}. + Let tsk2 := {| task_id := 2; task_cost := 3; task_period := 8; task_deadline := 6|}. + Let tsk3 := {| task_id := 3; task_cost := 2; task_period := 12; task_deadline := 12|}. + + (* Let ts be a task set containing these three tasks. *) + Program Let ts := Build_set [:: tsk1; tsk2; tsk3] _. + + Section FactsAboutTaskset. + + Fact ts_has_valid_parameters: + valid_sporadic_taskset task_cost task_period task_deadline ts. + Proof. + intros tsk IN. + repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute); by done. + Qed. + + Fact ts_has_constrained_deadlines: + forall tsk, + tsk \in ts -> + task_deadline tsk <= task_period tsk. + Proof. + intros tsk IN. + repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute); by done. + Qed. + + End FactsAboutTaskset. + + (* Assume there are two processors. *) + Let num_cpus := 2. + + (* Recall the EDF RTA schedulability test. *) + Let schedulability_test := + edf_schedulable task_cost task_period task_deadline num_cpus. + + Fact schedulability_test_succeeds : + schedulability_test ts = true. + Proof. + unfold schedulability_test, edf_schedulable, edf_claimed_bounds; desf. + apply negbT in Heq; move: Heq => /negP ALL. + exfalso; apply ALL; clear ALL. + assert (STEPS: \sum_(tsk <- ts) (task_deadline tsk - task_cost tsk) + 1 = 18). + { + by rewrite unlock; compute. + } rewrite STEPS; clear STEPS. + + Ltac f := + unfold edf_rta_iteration; simpl; + unfold edf_response_time_bound, div_floor, total_interference_bound_edf, interference_bound_edf, interference_bound_generic, W; simpl; + repeat rewrite addnE; + repeat rewrite big_cons; repeat rewrite big_nil; + repeat rewrite addnE; simpl; + unfold num_cpus, divn; simpl. + + rewrite [edf_rta_iteration]lock; simpl. + + unfold locked at 18; destruct master_key; f. + unfold locked at 17; destruct master_key; f. + unfold locked at 16; destruct master_key; f. + unfold locked at 15; destruct master_key; f. + unfold locked at 14; destruct master_key; f. + unfold locked at 13; destruct master_key; f. + unfold locked at 12; destruct master_key; f. + unfold locked at 11; destruct master_key; f. + unfold locked at 10; destruct master_key; f. + unfold locked at 9; destruct master_key; f. + unfold locked at 8; destruct master_key; f. + unfold locked at 7; destruct master_key; f. + unfold locked at 6; destruct master_key; f. + unfold locked at 5; destruct master_key; f. + unfold locked at 4; destruct master_key; f. + unfold locked at 3; destruct master_key; f. + unfold locked at 2; destruct master_key; f. + by unfold locked at 1; destruct master_key; f. + Qed. + + (* Let arr_seq be the periodic arrival sequence from ts. *) + Let arr_seq := periodic_arrival_sequence ts. + + (* Let sched be the work-conserving EDF scheduler. *) + Let sched := scheduler job_cost num_cpus arr_seq (EDF job_deadline). + + (* Recall the definition of deadline miss. *) + Let no_deadline_missed_by := + task_misses_no_deadline job_cost job_deadline job_task sched. + + (* Next, we prove that ts is schedulable with the result of the test. *) + Corollary ts_is_schedulable: + forall tsk, + tsk \in ts -> + no_deadline_missed_by tsk. + Proof. + 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_edf_rta with (task_cost := task_cost) (task_period := task_period) (task_deadline := task_deadline) (ts0 := ts). + - by apply ts_has_valid_parameters. + - by apply ts_has_constrained_deadlines. + - by apply periodic_arrivals_all_jobs_from_taskset. + - by apply periodic_arrivals_valid_job_parameters, ts_has_valid_parameters. + - by apply periodic_arrivals_are_sporadic. + - by compute. + - by apply scheduler_jobs_must_arrive_to_execute. + - apply scheduler_completed_jobs_dont_execute; intro j'. + -- by specialize (VALID j'); des. + -- by apply periodic_arrivals_is_a_set. + - by apply scheduler_work_conserving. + - apply scheduler_enforces_policy. + -- by apply EDF_is_transitive. + -- by apply EDF_is_total. + - by apply schedulability_test_succeeds. + - by apply IN. + Qed. + + End ExampleRTA. + +End ResponseTimeAnalysisEDF. \ No newline at end of file diff --git a/implementation/parallel/bertogna_fp_example.v b/implementation/parallel/bertogna_fp_example.v new file mode 100644 index 0000000000000000000000000000000000000000..aef7d69a421cfa65b501ffb7ef62d2de9e15e17a --- /dev/null +++ b/implementation/parallel/bertogna_fp_example.v @@ -0,0 +1,196 @@ +Require Import rt.util.all. +Require Import rt.model.basic.job rt.model.basic.task + rt.model.basic.schedule rt.model.basic.schedulability + rt.model.basic.priority rt.model.basic.platform + rt.model.basic.interference. +Require Import rt.analysis.parallel.workload_bound + rt.analysis.parallel.interference_bound_fp + rt.analysis.parallel.bertogna_fp_comp. +Require Import rt.implementation.parallel.job + rt.implementation.parallel.task + rt.implementation.parallel.schedule + rt.implementation.parallel.arrival_sequence. +From mathcomp Require Import ssreflect ssrbool ssrnat eqtype seq bigop div. + +Module ResponseTimeAnalysisFP. + + Import Job Schedule SporadicTaskset Priority Schedulability Platform + Interference InterferenceBoundFP WorkloadBound + ResponseTimeIterationFP. + Import ConcreteJob ConcreteTask ConcreteArrivalSequence ConcreteScheduler. + + (* In this section, we instantiate a simple example to show that the theorems + contain no contradictory assumptions. *) + Section ExampleRTA. + + Let tsk1 := {| task_id := 1; task_cost := 2; task_period := 6; task_deadline := 6|}. + Let tsk2 := {| task_id := 2; task_cost := 3; task_period := 8; task_deadline := 6|}. + Let tsk3 := {| task_id := 3; task_cost := 2; task_period := 12; task_deadline := 12|}. + + (* Let ts be a task set containing these three tasks (sorted by rate-monotonic priority). *) + Program Let ts := Build_set [:: tsk1; tsk2; tsk3] _. + + Section FactsAboutTaskset. + + Fact ts_has_valid_parameters: + valid_sporadic_taskset task_cost task_period task_deadline ts. + Proof. + intros tsk IN. + repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute); by done. + Qed. + + Fact ts_has_constrained_deadlines: + forall tsk, + tsk \in ts -> + task_deadline tsk <= task_period tsk. + Proof. + intros tsk IN. + repeat (move: IN => /orP [/eqP EQ | IN]; subst; compute); by done. + Qed. + + End FactsAboutTaskset. + + (* Assume there are two processors. *) + Let num_cpus := 2. + + (* Recall the FP RTA schedulability test. *) + Let schedulability_test := + fp_schedulable task_cost task_period task_deadline num_cpus. + + (* Now we show that the schedulability test returns true. *) + Fact schedulability_test_succeeds : + schedulability_test ts = true. + Proof. + unfold schedulability_test, fp_schedulable, fp_claimed_bounds; simpl. + unfold total_interference_bound_fp, div_floor. + rewrite big_nil div0n addn0 /=. + unfold div_floor; simpl. + set I2 := total_interference_bound_fp task_cost task_period [:: (tsk1, 2)]. + assert (H1: I2 3 = 2). + { + by rewrite /I2 /total_interference_bound_fp big_cons big_nil; compute. + } + assert (H2: I2 4 = 2). + { + by rewrite /I2 /total_interference_bound_fp big_cons big_nil; compute. + } + rewrite H1 H2. + have H3: 3 + 2 %/ num_cpus = 4 by compute. + rewrite H3 H2 H3 H2 H3. + have H4: 3 < 6 by compute. + rewrite H4; clear H4 H2 H1 I2. + unfold fp_bound_of_task. + have H5: per_task_rta task_cost task_period num_cpus tsk3 [:: (tsk1, 2); (tsk2, 4)] (max_steps task_cost task_deadline tsk3) = 4. + { + Ltac g := rewrite /total_interference_bound_fp /interference_bound_generic + /W /max_jobs /div_floor /div_ceil !big_cons big_nil /= /higher_priority_task /= ?addn0; compute. + rewrite /per_task_rta iterSr /div_floor. + set x10 := total_interference_bound_fp _ _ _ _. + have I10: x10 = 5 by unfold x10; g. + rewrite I10 iterSr. + set x9 := total_interference_bound_fp _ _ _ _. + have I9: x9 = 5 by unfold x9; g. + rewrite I9 iterSr. + set x8 := total_interference_bound_fp _ _ _ _. + have I8: x8 = 5 by unfold x8; g. + rewrite I8 iterSr. + set x7 := total_interference_bound_fp _ _ _ _. + have I7: x7 = 5 by unfold x7; g. + rewrite I7 iterSr. + set x6 := total_interference_bound_fp _ _ _ _. + have I6: x6 = 5 by unfold x6; g. + rewrite I6 iterSr. + set x5 := total_interference_bound_fp _ _ _ _. + have I5: x5 = 5 by unfold x5; g. + rewrite I5 iterSr. + set x4 := total_interference_bound_fp _ _ _ _. + have I4: x4 = 5 by unfold x4; g. + rewrite I4 iterSr. + set x3 := total_interference_bound_fp _ _ _ _. + have I3: x3 = 5 by unfold x3; g. + rewrite I3 iterSr. + set x2 := total_interference_bound_fp _ _ _ _. + have I2: x2 = 5 by unfold x2; g. + rewrite I2 iterSr. + set x1 := total_interference_bound_fp _ _ _ _. + have I1: x1 = 5 by unfold x1; g. + rewrite I1 iterSr. + set x0 := total_interference_bound_fp _ _ _ _. + have I0: x0 = 5 by unfold x0; g. + by rewrite /= I0; compute. + } + by rewrite H5. + Qed. + + (* Let arr_seq be the periodic arrival sequence from ts. *) + Let arr_seq := periodic_arrival_sequence ts. + + (* Assume rate-monotonic priorities. *) + Let higher_priority : JLDP_policy arr_seq := + (FP_to_JLDP job_task (RM task_period)). + + Section FactsAboutPriorityOrder. + + Lemma ts_has_unique_priorities : + FP_is_antisymmetric_over_task_set (RM task_period) ts. + Proof. + unfold RM; intros tsk tsk' IN IN' HP HP'. + have EQ: task_period tsk = task_period tsk' by apply/eqP; rewrite eqn_leq HP HP'. + clear HP HP'. + rewrite !in_cons 2!in_nil 2!orbF in IN IN'; des; rewrite IN IN'; try (by done); + subst tsk tsk'; simpl in *; by done. + Qed. + + Lemma priority_is_total : + FP_is_total_over_task_set (RM task_period) ts. + Proof. + unfold RM; intros tsk tsk' IN IN'. + destruct (leqP (task_period tsk) (task_period tsk')); + [by left | by right; apply ltnW]. + Qed. + + End FactsAboutPriorityOrder. + + (* Let sched be the work-conserving RM scheduler. *) + Let sched := scheduler job_cost num_cpus arr_seq higher_priority. + + (* Recall the definition of deadline miss. *) + Let no_deadline_missed_by := + task_misses_no_deadline job_cost job_deadline job_task sched. + + (* Next, we prove that ts is schedulable with the result of the test. *) + Corollary ts_is_schedulable: + forall tsk, + tsk \in ts -> + no_deadline_missed_by tsk. + Proof. + 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_priority0 := RM task_period); try (by done). + - by apply ts_has_constrained_deadlines. + - by apply ts_has_unique_priorities. + - by apply priority_is_total. + - by apply RM_is_transitive. + - by apply periodic_arrivals_all_jobs_from_taskset. + - by apply periodic_arrivals_are_sporadic. + - by apply scheduler_jobs_must_arrive_to_execute. + - apply scheduler_completed_jobs_dont_execute; intro j'. + -- by specialize (VALID j'); des. + -- by apply periodic_arrivals_is_a_set. + - by apply scheduler_work_conserving. + - apply scheduler_enforces_policy. + -- by intros t; apply RM_is_transitive. + { + unfold FP_to_JLDP; intros t x y; apply/orP. + by apply priority_is_total; rewrite periodic_arrivals_all_jobs_from_taskset. + } + - by apply schedulability_test_succeeds. + Qed. + + End ExampleRTA. + +End ResponseTimeAnalysisFP. \ No newline at end of file diff --git a/implementation/parallel/job.v b/implementation/parallel/job.v new file mode 100644 index 0000000000000000000000000000000000000000..78b0bd655db83854403535830cdf828d6329ce74 --- /dev/null +++ b/implementation/parallel/job.v @@ -0,0 +1,2 @@ +(* We reuse the job from the basic model. *) +Require Export rt.implementation.basic.job. \ No newline at end of file diff --git a/implementation/parallel/schedule.v b/implementation/parallel/schedule.v new file mode 100644 index 0000000000000000000000000000000000000000..fdfac70394bc838fd58d19c473fd11d69a513052 --- /dev/null +++ b/implementation/parallel/schedule.v @@ -0,0 +1,4 @@ +(* For simplicity, we reuse the scheduler from the basic model. + Although it does not exploit parallelism, it is a restricted case + of a parallel scheduler. *) +Require Export rt.implementation.basic.schedule. \ No newline at end of file diff --git a/implementation/parallel/task.v b/implementation/parallel/task.v new file mode 100644 index 0000000000000000000000000000000000000000..d4eb8e27de636a10883885260ff913d298a0d176 --- /dev/null +++ b/implementation/parallel/task.v @@ -0,0 +1,2 @@ +(* We reuse the task from the basic model. *) +Require Export rt.implementation.basic.task. \ No newline at end of file diff --git a/model/apa/affinity.v b/model/apa/affinity.v new file mode 100644 index 0000000000000000000000000000000000000000..f819d86a54368335431d9f00c1c9df3e96a52568 --- /dev/null +++ b/model/apa/affinity.v @@ -0,0 +1,103 @@ +Require Import rt.util.all. +Require Import rt.model.apa.job rt.model.apa.task + rt.model.apa.arrival_sequence rt.model.apa.schedule. +From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop. + +(* Definition and properties about processor affinities. *) +Module Affinity. + + Import ArrivalSequence ScheduleOfSporadicTask. + + Section AffinityDefs. + + Variable sporadic_task: eqType. + + (* Given the number of processors, ... *) + Variable num_cpus: nat. + + (* ... let a processor affinity be a subset of these processors. + Note: notation {set T} is just a list of type T with no duplicates. *) + Definition affinity := {set (processor num_cpus)}. + + (* Next, we define a task affinity as the mapping from tasks to affinities. *) + Definition task_affinity := sporadic_task -> affinity. + + End AffinityDefs. + + Section Properties. + + Context {sporadic_task: eqType}. + Context {num_cpus: nat}. + + Section JobProperties. + + (* Assume that each task has a processor affinity alpha. *) + Variable alpha: task_affinity sporadic_task num_cpus. + + (* Then, we say that task tsk ...*) + Variable tsk: sporadic_task. + + (* ... can execute on processor cpu ...*) + Variable cpu: processor num_cpus. + + (* ... iff cpu is part of j's affinity. *) + Definition can_execute_on := cpu \in alpha tsk. + + End JobProperties. + + Section ScheduleProperties. + + Context {Job: eqType}. + Variable job_task: Job -> sporadic_task. + + (* Consider any schedule, ... *) + Context {arr_seq: arrival_sequence Job}. + Variable sched: schedule num_cpus arr_seq. + + (* ... and some affinity alpha. *) + Variable alpha: affinity num_cpus. + + (* We say that a task is executing on alpha at time t iff it is + scheduled on some processor in that affinity. *) + Definition task_scheduled_on_affinity (tsk: sporadic_task) (t: time) := + [exists cpu, (cpu \in alpha) && task_scheduled_on job_task sched tsk cpu t]. + + End ScheduleProperties. + + Section Subset. + + (* Given two affinities alpha' and alpha, ...*) + Variable alpha' alpha: affinity num_cpus. + + (* ... we say that alpha' is subaffinity of alpha iff + alpha' is contained in alpha. *) + Definition is_subaffinity := {subset alpha' <= alpha}. + + Section Lemmas. + + Hypothesis H_subaffinity: is_subaffinity. + + Lemma leq_subaffinity : #|alpha'| <= #|alpha|. + Proof. + assert (UNIQ: uniq (alpha)). by destruct (alpha). + assert (UNIQ': uniq (alpha')). by destruct (alpha'). + move: (UNIQ) (UNIQ') => /card_uniqP -> /card_uniqP ->. + by apply uniq_leq_size. + Qed. + + End Lemmas. + + End Subset. + + Section IntersectingAffinities. + + (* We say that alpha and alpha' intersect iff there exists a processor + that belongs to both affinities. *) + Definition affinity_intersects (alpha alpha': affinity num_cpus) := + [exists cpu, (cpu \in alpha) && (cpu \in alpha')]. + + End IntersectingAffinities. + + End Properties. + +End Affinity. \ No newline at end of file diff --git a/model/apa/arrival_sequence.v b/model/apa/arrival_sequence.v new file mode 100644 index 0000000000000000000000000000000000000000..0ab7d9296238e824a51fce508cb97454946058c9 --- /dev/null +++ b/model/apa/arrival_sequence.v @@ -0,0 +1,2 @@ +(* The arrival sequence remains the same under APA scheduling. *) +Require Export rt.model.basic.arrival_sequence. \ No newline at end of file diff --git a/model/apa/constrained_deadlines.v b/model/apa/constrained_deadlines.v new file mode 100644 index 0000000000000000000000000000000000000000..55b1abc631378c74b5becd5dc033e357a2d6d69d --- /dev/null +++ b/model/apa/constrained_deadlines.v @@ -0,0 +1,274 @@ +Require Import rt.util.all. +Require Import rt.model.apa.task rt.model.apa.job rt.model.apa.schedule + rt.model.apa.priority rt.model.apa.task_arrival + rt.model.apa.interference rt.model.apa.affinity + rt.model.apa.platform. +From mathcomp Require Import ssreflect ssrbool ssrfun eqtype ssrnat seq fintype bigop. + +Module ConstrainedDeadlines. + + Import Job SporadicTaskset ScheduleOfSporadicTask SporadicTaskset + SporadicTaskArrival Interference Priority Affinity Platform. + + Section Lemmas. + + Context {sporadic_task: eqType}. + Variable task_cost: sporadic_task -> time. + Variable task_period: sporadic_task -> time. + Variable task_deadline: sporadic_task -> time. + + Context {Job: eqType}. + Variable job_cost: Job -> time. + Variable job_deadline: Job -> time. + Variable job_task: Job -> sporadic_task. + + (* Assume any job arrival sequence ... *) + Context {arr_seq: arrival_sequence Job}. + + (* ... and any schedule of this arrival sequence. *) + Context {num_cpus: nat}. + Variable sched: schedule num_cpus arr_seq. + + (* Assume that every task has a processor affinity alpha. *) + Variable alpha: task_affinity sporadic_task num_cpus. + + (* Assume all jobs have valid parameters, ...*) + Hypothesis H_valid_job_parameters: + forall (j: JobIn arr_seq), + valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j. + + (* In this section we prove the absence of multiple jobs of the same + task when constrained deadlines are assumed. *) + Section NoMultipleJobs. + + (* Assume any work-conserving priority-based scheduler. *) + Variable higher_eq_priority: JLDP_policy arr_seq. + Hypothesis H_work_conserving: + apa_work_conserving job_cost job_task sched alpha. + Hypothesis H_enforces_JLDP_policy: + enforces_JLDP_policy_under_weak_APA job_cost job_task sched alpha higher_eq_priority. + + (* Consider task set ts. *) + Variable ts: taskset_of sporadic_task. + + (* Assume that all jobs come from the taskset. *) + Hypothesis H_all_jobs_from_taskset: + forall (j: JobIn arr_seq), job_task j \in ts. + + (* Suppose that jobs are sequential, ...*) + Hypothesis H_sequential_jobs: sequential_jobs sched. + (* ... jobs only execute after they arrive, ... *) + Hypothesis H_jobs_must_arrive_to_execute: + jobs_must_arrive_to_execute sched. + (* ... and jobs do not execute after completion. *) + Hypothesis H_completed_jobs_dont_execute: + completed_jobs_dont_execute job_cost sched. + + (* Assume that the schedule satisfies the sporadic task model ...*) + Hypothesis H_sporadic_tasks: + sporadic_task_model task_period arr_seq job_task. + + (* Consider a valid task tsk, ...*) + Variable tsk: sporadic_task. + Hypothesis H_valid_task: is_valid_sporadic_task task_cost task_period task_deadline tsk. + + (*... whose job j ... *) + Variable j: JobIn arr_seq. + Variable H_job_of_tsk: job_task j = tsk. + + (*... is backlogged at time t. *) + Variable t: time. + Hypothesis H_j_backlogged: backlogged job_cost sched j t. + + (* Assume that any previous jobs of tsk have completed by the period. *) + Hypothesis H_all_previous_jobs_completed : + forall (j_other: JobIn arr_seq) tsk_other, + job_task j_other = tsk_other -> + job_arrival j_other + task_period tsk_other <= t -> + completed job_cost sched j_other (job_arrival j_other + task_period (job_task j_other)). + + Let scheduled_task_other_than (tsk tsk_other: sporadic_task) := + task_is_scheduled job_task sched tsk_other t && (tsk_other != tsk). + + (* Then, there can be at most one pending job of each task at time t. *) + Lemma platform_at_most_one_pending_job_of_each_task : + forall j1 j2, + pending job_cost sched j1 t -> + pending job_cost sched j2 t -> + job_task j1 = job_task j2 -> + j1 = j2. + Proof. + rename H_sporadic_tasks into SPO, H_all_previous_jobs_completed into PREV. + intros j1 j2 PENDING1 PENDING2 SAMEtsk. + apply/eqP; rewrite -[_ == _]negbK; apply/negP; red; move => /eqP DIFF. + move: PENDING1 PENDING2 => /andP [ARRIVED1 /negP NOTCOMP1] /andP [ARRIVED2 /negP NOTCOMP2]. + destruct (leqP (job_arrival j1) (job_arrival j2)) as [BEFORE1 | BEFORE2]. + { + specialize (SPO j1 j2 DIFF SAMEtsk BEFORE1). + assert (LEt: job_arrival j1 + task_period (job_task j1) <= t). + { + by apply leq_trans with (n := job_arrival j2); first by done. + } + exploit (PREV j1 (job_task j1)); try (by done). + intros COMP1; apply NOTCOMP1. + by apply completion_monotonic with (t0 := job_arrival j1 + task_period (job_task j1)). + } + { + apply ltnW in BEFORE2. + exploit (SPO j2 j1); [by red; ins; subst | by rewrite SAMEtsk | by done | intro SPO']. + assert (LEt: job_arrival j2 + task_period (job_task j2) <= t). + { + by apply leq_trans with (n := job_arrival j1); first by done. + } + exploit (PREV j2 (job_task j2)); try (by done). + intros COMP2; apply NOTCOMP2. + by apply completion_monotonic with (t0 := job_arrival j2 + task_period (job_task j2)). + } + Qed. + + End NoMultipleJobs. + + (* In this section we also prove the absence of multiple jobs of the same + task when constrained deadlines are assumed, but in the specific case + of fixed-priority scheduling. *) + Section NoMultipleJobsFP. + + (* Assume any work-conserving priority-based scheduler. *) + Variable higher_eq_priority: FP_policy sporadic_task. + Hypothesis H_work_conserving: apa_work_conserving job_cost job_task sched alpha. + Hypothesis H_enforces_JLDP_policy: + enforces_FP_policy_under_weak_APA job_cost job_task sched alpha higher_eq_priority. + + (* Consider any task set ts. *) + Variable ts: taskset_of sporadic_task. + + (* Assume that all jobs come from the taskset. *) + Hypothesis H_all_jobs_from_taskset: + forall (j: JobIn arr_seq), job_task j \in ts. + + (* Suppose that jobs are sequential, ...*) + Hypothesis H_sequential_jobs: sequential_jobs sched. + (* ... jobs only execute after the jitter, ... *) + Hypothesis H_jobs_must_arrive_to_execute: + jobs_must_arrive_to_execute sched. + (* ... and jobs do not execute after completion. *) + Hypothesis H_completed_jobs_dont_execute: + completed_jobs_dont_execute job_cost sched. + + (* Assume that the schedule satisfies the sporadic task model ...*) + Hypothesis H_sporadic_tasks: + sporadic_task_model task_period arr_seq job_task. + + (* Consider a valid task tsk, ...*) + Variable tsk: sporadic_task. + Hypothesis H_valid_task: is_valid_sporadic_task task_cost task_period task_deadline tsk. + + (*... whose job j ... *) + Variable j: JobIn arr_seq. + Variable H_job_of_tsk: job_task j = tsk. + + (*... is backlogged at time t <= job_arrival j + task_period tsk. *) + Variable t: time. + Hypothesis H_j_backlogged: backlogged job_cost sched j t. + Hypothesis H_t_before_period: t < job_arrival j + task_period tsk. + + (* Recall the definition of a higher-priority task in affinity (alpha' tsk). *) + Let hp_task_in alpha' := higher_priority_task_in alpha higher_eq_priority tsk alpha'. + + (* Assume that any jobs of higher-priority tasks complete by their period. *) + Hypothesis H_all_previous_jobs_completed : + forall (j_other: JobIn arr_seq) tsk_other, + job_task j_other = tsk_other -> + hp_task_in (alpha tsk) tsk_other -> + completed job_cost sched j_other (job_arrival j_other + task_period tsk_other). + + (* Assume that any jobs of tsk prior to j complete by their period. *) + Hypothesis H_all_previous_jobs_of_tsk_completed : + forall j0 : JobIn arr_seq, + job_task j0 = tsk -> + job_arrival j0 < job_arrival j -> + completed job_cost sched j0 (job_arrival j0 + task_period tsk). + + Definition scheduled_task_with_higher_eq_priority (tsk tsk_other: sporadic_task) := + task_is_scheduled job_task sched tsk_other t && + hp_task_in (alpha tsk) tsk_other. + + (* Then, there can be at most one pending job of higher-priority tasks at time t. *) + Lemma platform_fp_no_multiple_jobs_of_interfering_tasks : + forall j1 j2, + pending job_cost sched j1 t -> + pending job_cost sched j2 t -> + job_task j1 = job_task j2 -> + hp_task_in (alpha tsk) (job_task j1) -> + j1 = j2. + Proof. + unfold sporadic_task_model in *. + rename H_sporadic_tasks into SPO, H_all_previous_jobs_of_tsk_completed into PREVtsk, + H_all_previous_jobs_completed into PREV. + intros j1 j2 PENDING1 PENDING2 SAMEtsk INTERF. + apply/eqP; rewrite -[_ == _]negbK; apply/negP; red; move => /eqP DIFF. + move: PENDING1 PENDING2 => /andP [ARRIVED1 /negP NOTCOMP1] /andP [ARRIVED2 /negP NOTCOMP2]. + destruct (leqP (job_arrival j1) (job_arrival j2)) as [BEFORE1 | BEFORE2]. + { + specialize (SPO j1 j2 DIFF SAMEtsk BEFORE1). + assert (LEt: job_arrival j1 + task_period (job_task j1) <= t). + by apply leq_trans with (n := job_arrival j2). + exploit (PREV j1 (job_task j1)); [by done | by apply INTERF | intros COMP1]. + apply NOTCOMP1. + by apply completion_monotonic with (t0 := job_arrival j1 + task_period (job_task j1)). + } + { + apply ltnW in BEFORE2. + exploit (SPO j2 j1); [by red; ins; subst j2 | by rewrite SAMEtsk | by done | intro SPO']. + assert (LEt: job_arrival j2 + task_period (job_task j2) <= t). + by apply leq_trans with (n := job_arrival j1). + exploit (PREV j2 (job_task j2)); + [by done | by rewrite -SAMEtsk | intro COMP2 ]. + apply NOTCOMP2. + by apply completion_monotonic with (t0 := job_arrival j2 + task_period (job_task j2)). + } + Qed. + + (* Also, there can be at most one pending job of tsk at time t. *) + Lemma platform_fp_no_multiple_jobs_of_tsk : + forall j', + pending job_cost sched j' t -> + job_task j' = tsk -> + j' = j. + Proof. + unfold sporadic_task_model in *. + rename H_sporadic_tasks into SPO, + H_valid_task into PARAMS, + H_all_previous_jobs_of_tsk_completed into PREVtsk, + H_all_previous_jobs_completed into PREV, + H_j_backlogged into BACK, H_job_of_tsk into JOBtsk. + intros j' PENDING' SAMEtsk. + apply/eqP; rewrite -[_ == _]negbK; apply/negP; red; move => /eqP DIFF. + move: BACK PENDING' => /andP [/andP [ARRIVED /negP NOTCOMP] NOTSCHED] + /andP [ARRIVED' /negP NOTCOMP']. + destruct (leqP (job_arrival j') (job_arrival j)) as [BEFORE | BEFORE']. + { + exploit (SPO j' j DIFF); [by rewrite JOBtsk | by done | intro SPO']. + assert (LEt: job_arrival j' + task_period tsk <= t). + by apply leq_trans with (n := job_arrival j); first by rewrite -SAMEtsk. + apply NOTCOMP'. + apply completion_monotonic with (t0 := job_arrival j' + task_period tsk); [by done | by done |]. + apply PREVtsk; first by done. + apply leq_trans with (n := job_arrival j' + task_period tsk); last by rewrite -SAMEtsk. + rewrite -addn1; apply leq_add; first by done. + by unfold is_valid_sporadic_task in *; des. + } + { + unfold has_arrived in *. + rewrite leqNgt in ARRIVED'; move: ARRIVED' => /negP BUG; apply BUG. + apply leq_trans with (n := job_arrival j + task_period tsk); first by done. + by rewrite -JOBtsk; apply SPO; + [by red; ins; subst j' | by rewrite SAMEtsk | by apply ltnW]. + } + Qed. + + End NoMultipleJobsFP. + + End Lemmas. + +End ConstrainedDeadlines. \ No newline at end of file diff --git a/model/apa/interference.v b/model/apa/interference.v new file mode 100644 index 0000000000000000000000000000000000000000..f0a2fe842157a8bb5b8006fa23e7e8fe2a7bac30 --- /dev/null +++ b/model/apa/interference.v @@ -0,0 +1,269 @@ +Require Import rt.util.all rt.util.divround. +Require Import rt.model.apa.task rt.model.apa.job rt.model.apa.schedule + rt.model.apa.priority rt.model.apa.workload rt.model.apa.affinity. +From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop. + +Module Interference. + + Import Schedule ScheduleOfSporadicTask Priority Workload Affinity. + + (* In this section, we define the notion of a possible interfering task. *) + Section PossibleInterferingTasks. + + Context {sporadic_task: eqType}. + Variable task_cost: sporadic_task -> time. + Variable task_period: sporadic_task -> time. + Variable task_deadline: sporadic_task -> time. + + (* Consider an APA platform with affinity alpha. *) + Context {num_cpus: nat}. + Variable alpha: task_affinity sporadic_task num_cpus. + + Section FP. + + (* Assume an FP policy. *) + Variable higher_eq_priority: FP_policy sporadic_task. + + (* Let tsk be the task to be analyzed ... *) + Variable tsk: sporadic_task. + + (* ...assuming a subaffinity alpha'. *) + Variable alpha': affinity num_cpus. + + (* Let tsk_other be another task. *) + Variable tsk_other: sporadic_task. + + (* Under FP scheduling with constrained deadlines, tsk_other can only interfere + with tsk if it is a different task with higher or equal priority and + intersecting affinity. *) + Definition higher_priority_task_in := + higher_eq_priority tsk_other tsk && + (tsk_other != tsk) && + affinity_intersects alpha' (alpha tsk_other). + + End FP. + + Section JLFP. + + (* Let tsk be the task to be analyzed ... *) + Variable tsk: sporadic_task. + + (* ...assuming a subaffinity alpha'. *) + Variable alpha': affinity num_cpus. + + (* Let tsk_other be another task. *) + Variable tsk_other: sporadic_task. + + (* Under JLFP/JLDP scheduling with constrained deadlines, tsk_other can only interfere + with tsk if it is a different task with intersecting affinity. *) + Definition different_task_in := + (tsk_other != tsk) && + affinity_intersects alpha' (alpha tsk_other). + + End JLFP. + + End PossibleInterferingTasks. + + Section InterferenceDefs. + + Context {sporadic_task: eqType}. + Context {Job: eqType}. + Variable job_cost: Job -> time. + Variable job_task: Job -> sporadic_task. + + (* Assume any job arrival sequence...*) + Context {arr_seq: arrival_sequence Job}. + + (* ... and any schedule. *) + Context {num_cpus: nat}. + Variable sched: schedule num_cpus arr_seq. + + (* Assume that every job at any time has a processor affinity alpha. *) + Variable alpha: task_affinity sporadic_task num_cpus. + + (* Consider any job j that incurs interference. *) + Variable j: JobIn arr_seq. + + (* Recall the definition of backlogged (pending and not scheduled). *) + Let job_is_backlogged (t: time) := backlogged job_cost sched j t. + + (* First, we define total interference. *) + Section TotalInterference. + + (* The total interference incurred by job j during [t1, t2) is the + cumulative time in which j is backlogged in this interval. *) + Definition total_interference (t1 t2: time) := + \sum_(t1 <= t < t2) job_is_backlogged t. + + End TotalInterference. + + (* Next, we define job interference. *) + Section JobInterference. + + (* Let job_other be a job that interferes with j. *) + Variable job_other: JobIn arr_seq. + + (* The interference caused by job_other during [t1, t2) is the cumulative + time in which j is backlogged while job_other is scheduled. *) + Definition job_interference (t1 t2: time) := + \sum_(t1 <= t < t2) + \sum_(cpu < num_cpus) + (job_is_backlogged t && + can_execute_on alpha (job_task j) cpu && + scheduled_on sched job_other cpu t). + + End JobInterference. + + (* Next, we define task interference. *) + Section TaskInterference. + + (* Consider any interfering task tsk_other. *) + Variable tsk_other: sporadic_task. + + (* The interference caused by tsk during [t1, t2) is the cumulative time + in which j is backlogged while tsk is scheduled. *) + Definition task_interference (t1 t2: time) := + \sum_(t1 <= t < t2) + \sum_(cpu < num_cpus) + (job_is_backlogged t && + can_execute_on alpha (job_task j) cpu && + task_scheduled_on job_task sched tsk_other cpu t). + + End TaskInterference. + + (* Next, we define an approximation of the total interference based on + each per-task interference. *) + Section TaskInterferenceJobList. + + Variable tsk_other: sporadic_task. + + Definition task_interference_joblist (t1 t2: time) := + \sum_(j <- jobs_scheduled_between sched t1 t2 | job_task j == tsk_other) + job_interference j t1 t2. + + End TaskInterferenceJobList. + + (* Now we prove some basic lemmas about interference. *) + Section BasicLemmas. + + (* Total interference cannot be larger than the considered time window. *) + Lemma total_interference_le_delta : + forall t1 t2, + total_interference t1 t2 <= t2 - t1. + Proof. + unfold total_interference; intros t1 t2. + apply leq_trans with (n := \sum_(t1 <= t < t2) 1); + first by apply leq_sum; ins; apply leq_b1. + by rewrite big_const_nat iter_addn mul1n addn0 leqnn. + Qed. + + (* Job interference is bounded by the service of the interfering job. *) + Lemma job_interference_le_service : + forall j_other t1 t2, + job_interference j_other t1 t2 <= service_during sched j_other t1 t2. + Proof. + intros j_other t1 t2; unfold job_interference, service_during. + apply leq_sum; intros t _. + unfold service_at; rewrite [\sum_(_ < _ | scheduled_on _ _ _ _)_]big_mkcond. + apply leq_sum; intros cpu _. + destruct (job_is_backlogged t); [rewrite andTb | by rewrite andFb]. + destruct (can_execute_on alpha (job_task j) cpu); [rewrite andTb | by rewrite andFb]. + by destruct (scheduled_on sched j_other cpu t). + Qed. + + (* Task interference is bounded by the workload of the interfering task. *) + Lemma task_interference_le_workload : + forall tsk t1 t2, + task_interference tsk t1 t2 <= workload job_task sched tsk t1 t2. + Proof. + unfold task_interference, workload; intros tsk t1 t2. + apply leq_sum; intros t _. + apply leq_sum; intros cpu _. + destruct (job_is_backlogged t); [rewrite andTb | by rewrite andFb]. + destruct (can_execute_on alpha (job_task j) cpu); [rewrite andTb | by rewrite andFb]. + unfold task_scheduled_on, service_of_task. + by destruct (sched cpu t). + Qed. + + End BasicLemmas. + + (* Now we prove some bounds on interference for sequential jobs. *) + Section InterferenceNoParallelism. + + (* If jobs are sequential, ... *) + Hypothesis H_sequential_jobs: sequential_jobs sched. + + (* ... then the interference incurred by a job in an interval + of length delta is at most delta. *) + Lemma job_interference_le_delta : + forall j_other t1 delta, + job_interference j_other t1 (t1 + delta) <= delta. + Proof. + rename H_sequential_jobs into SEQ. + unfold job_interference, sequential_jobs in *. + intros j_other t1 delta. + apply leq_trans with (n := \sum_(t1 <= t < t1 + delta) 1); + last by rewrite big_const_nat iter_addn mul1n addn0 addKn leqnn. + apply leq_sum; intros t _. + destruct ([exists cpu, scheduled_on sched j_other cpu t]) eqn:EX. + { + move: EX => /existsP [cpu SCHED]. + rewrite (bigD1 cpu) // /=. + rewrite big_mkcond (eq_bigr (fun x => 0)) /=; + first by simpl_sum_const; rewrite leq_b1. + intros cpu' _; des_if_goal; last by done. + destruct (scheduled_on sched j_other cpu' t) eqn:SCHED'; last by rewrite andbF. + move: SCHED SCHED' => /eqP SCHED /eqP SCHED'. + by specialize (SEQ j_other t cpu cpu' SCHED SCHED'); rewrite SEQ in Heq. + } + { + apply negbT in EX; rewrite negb_exists in EX. + move: EX => /forallP EX. + rewrite (eq_bigr (fun x => 0)); first by simpl_sum_const. + by intros cpu _; specialize (EX cpu); apply negbTE in EX; rewrite EX andbF. + } + Qed. + + End InterferenceNoParallelism. + + (* Next, we show that the cumulative per-task interference bounds the total + interference. *) + Section BoundUsingPerTaskInterference. + + Lemma interference_le_interference_joblist : + forall tsk t1 t2, + task_interference tsk t1 t2 <= task_interference_joblist tsk t1 t2. + Proof. + intros tsk t1 t2. + unfold task_interference, task_interference_joblist, job_interference, job_is_backlogged. + rewrite [\sum_(_ <- _ sched _ _ | _) _]exchange_big /=. + rewrite big_nat_cond [\sum_(_ <= _ < _ | true) _]big_nat_cond. + apply leq_sum; move => t /andP [LEt _]. + rewrite exchange_big /=. + apply leq_sum; intros cpu _. + destruct (backlogged job_cost sched j t) eqn:BACK; + last by rewrite andFb (eq_bigr (fun x => 0)); + first by rewrite big_const_seq iter_addn mul0n addn0. + rewrite andTb. + destruct (task_scheduled_on job_task sched tsk cpu t) eqn:SCHED; + [rewrite andbT | by rewrite andbF]. + unfold scheduled_on, task_scheduled_on in *. + destruct (sched cpu t) as [j' |] eqn:SOME; last by done. + rewrite big_mkcond /= (bigD1_seq j') /=; last by apply undup_uniq. + { + by rewrite SCHED eq_refl andbT leq_addr. + } + { + unfold jobs_scheduled_between. + rewrite mem_undup; apply mem_bigcat_nat with (j := t); + first by done. + apply mem_bigcat_ord with (j := cpu); first by apply ltn_ord. + by unfold make_sequence; rewrite SOME mem_seq1 eq_refl. + } + Qed. + + End BoundUsingPerTaskInterference. + + End InterferenceDefs. + +End Interference. \ No newline at end of file diff --git a/model/apa/interference_edf.v b/model/apa/interference_edf.v new file mode 100644 index 0000000000000000000000000000000000000000..c1bb429ec65ae6116bf906f6750c1e82940665c4 --- /dev/null +++ b/model/apa/interference_edf.v @@ -0,0 +1,76 @@ +Require Import rt.util.all. +Require Import rt.model.apa.task rt.model.apa.job rt.model.apa.schedule + rt.model.apa.priority rt.model.apa.task_arrival rt.model.apa.interference + rt.model.apa.arrival_sequence rt.model.apa.platform + rt.model.apa.affinity. +From mathcomp Require Import ssreflect ssrbool ssrfun eqtype ssrnat seq fintype bigop. + +Module InterferenceEDF. + + Import Schedule Priority Platform Interference Priority Affinity. + + Section Lemmas. + + Context {sporadic_task: eqType}. + Context {Job: eqType}. + Variable job_cost: Job -> time. + Variable job_deadline: Job -> time. + Variable job_task: Job -> sporadic_task. + + (* Assume any job arrival sequence... *) + Context {arr_seq: arrival_sequence Job}. + + (* Consider any schedule. *) + Variable num_cpus: nat. + Variable sched: schedule num_cpus arr_seq. + + (* Assume that every job at any time has a processor affinity alpha. *) + Variable alpha: task_affinity sporadic_task num_cpus. + + (* Assume that the schedule satisfies the global scheduling invariant + for EDF, i.e., if any job of tsk is backlogged, every processor + must be busy with jobs with no larger absolute deadline. *) + Hypothesis H_scheduler_uses_EDF: + enforces_JLDP_policy_under_weak_APA job_cost job_task sched alpha (EDF job_deadline). + + (* Under EDF scheduling, a job only causes interference if its deadline + is not larger than the deadline of the analyzed job. *) + Lemma interference_under_edf_implies_shorter_deadlines : + forall (j j': JobIn arr_seq) t1 t2, + job_interference job_cost job_task sched alpha j' j t1 t2 != 0 -> + job_arrival j + job_deadline j <= job_arrival j' + job_deadline j'. + Proof. + rename H_scheduler_uses_EDF into PRIO. + unfold enforces_JLDP_policy_under_weak_APA in *. + intros j j' t1 t2 INTERF. + unfold job_interference in INTERF. + destruct ([exists t': 'I_t2, + [exists cpu: processor num_cpus, + (t' >= t1) && + backlogged job_cost sched j' t' && + can_execute_on alpha (job_task j') cpu && + scheduled_on sched j cpu t']]) eqn:EX. + { + move: EX => /existsP [t' /existsP [cpu /andP [/andP [/andP [LE BACK] CAN] SCHED]]]. + by eapply PRIO in SCHED; last by apply CAN. + } + { + apply negbT in EX; rewrite negb_exists in EX; move: EX => /forallP ALL. + rewrite big_nat_cond (eq_bigr (fun x => 0)) in INTERF; + first by rewrite -big_nat_cond big_const_nat iter_addn mul0n addn0 eq_refl in INTERF. + move => i /andP [/andP [GEi LTi] _]. + specialize (ALL (Ordinal LTi)). + rewrite negb_exists in ALL. + move: ALL => /forallP ALL. + rewrite (eq_bigr (fun x => 0)); + first by rewrite big_const_ord iter_addn mul0n addn0. + intros cpu _; specialize (ALL cpu); simpl in ALL. + destruct (backlogged job_cost sched j' i); last by rewrite andFb. + rewrite GEi 2!andTb in ALL; rewrite andTb. + by apply negbTE in ALL; rewrite ALL. + } + Qed. + + End Lemmas. + +End InterferenceEDF. \ No newline at end of file diff --git a/model/apa/job.v b/model/apa/job.v new file mode 100644 index 0000000000000000000000000000000000000000..3e6878902b7da1994119dd724869a2e3baa0d599 --- /dev/null +++ b/model/apa/job.v @@ -0,0 +1,2 @@ +(* The notion of a valid job remains the same under APA scheduling. *) +Require Export rt.model.basic.job. \ No newline at end of file diff --git a/model/apa/platform.v b/model/apa/platform.v new file mode 100644 index 0000000000000000000000000000000000000000..d734339292813ac5390e93d4770c3e184a6dedd0 --- /dev/null +++ b/model/apa/platform.v @@ -0,0 +1,87 @@ +Require Import rt.util.all. +Require Import rt.model.apa.task rt.model.apa.job rt.model.apa.schedule + rt.model.apa.priority rt.model.apa.task_arrival + rt.model.apa.interference rt.model.apa.affinity. +From mathcomp Require Import ssreflect ssrbool ssrfun eqtype ssrnat seq fintype bigop. + +Module Platform. + + Import Job SporadicTaskset ScheduleOfSporadicTask SporadicTaskset + SporadicTaskArrival Interference Priority Affinity. + + Section Properties. + + Context {sporadic_task: eqType}. + Variable task_cost: sporadic_task -> time. + Variable task_period: sporadic_task -> time. + Variable task_deadline: sporadic_task -> time. + + Context {Job: eqType}. + Variable job_cost: Job -> time. + Variable job_deadline: Job -> time. + Variable job_task: Job -> sporadic_task. + + (* Assume any job arrival sequence ... *) + Context {arr_seq: arrival_sequence Job}. + + (* ... and any schedule of this arrival sequence. *) + Context {num_cpus: nat}. + Variable sched: schedule num_cpus arr_seq. + + (* Assume that every task has a processor affinity alpha. *) + Variable alpha: task_affinity sporadic_task num_cpus. + + Section Execution. + + (* A schedule is work-conserving iff when a job j is backlogged, all + processors *on which j can be scheduled* are busy with other jobs. *) + Definition apa_work_conserving := + forall j t, + backlogged job_cost sched j t -> + forall cpu, + can_execute_on alpha (job_task j) cpu -> + exists j_other, + scheduled_on sched j_other cpu t. + + (* In a schedule that enforces affinities, a job is scheduled + only if its affinity allows it. *) + Definition respects_affinity := + forall j cpu t, + scheduled_on sched j cpu t -> + can_execute_on alpha (job_task j) cpu. + + End Execution. + + Section JLDP. + + (* A JLFP/JLDP policy ...*) + Variable higher_eq_priority: JLDP_policy arr_seq. + + (* ... is enforced by a weak APA scheduler iff at any time t, + for any backlogged job j, if there is another job j_hp + executing on j's affinity, then j_hp's priority must be + as high as j's priority. *) + Definition enforces_JLDP_policy_under_weak_APA := + forall (j j_hp: JobIn arr_seq) cpu t, + backlogged job_cost sched j t -> + scheduled_on sched j_hp cpu t -> + can_execute_on alpha (job_task j) cpu -> + higher_eq_priority t j_hp j. + + End JLDP. + + Section FP. + + (* Given an FP policy, ...*) + Variable higher_eq_priority: FP_policy sporadic_task. + + (* ... this policy is enforced by a weak APA scheduler iff + the corresponding JLDP policy is enforced by the scheduler. *) + Definition enforces_FP_policy_under_weak_APA := + enforces_JLDP_policy_under_weak_APA (FP_to_JLDP job_task higher_eq_priority). + + End FP. + + End Properties. + +End Platform. \ No newline at end of file diff --git a/model/apa/priority.v b/model/apa/priority.v new file mode 100644 index 0000000000000000000000000000000000000000..ee2fe4187ad5f9e099450d57026e0a05ede7c0cf --- /dev/null +++ b/model/apa/priority.v @@ -0,0 +1,2 @@ +(* The definition of priority remains the same under APA scheduling. *) +Require Export rt.model.basic.priority. \ No newline at end of file diff --git a/model/apa/response_time.v b/model/apa/response_time.v new file mode 100644 index 0000000000000000000000000000000000000000..06c3c32c6294099338ab2e33f00382a08a44487c --- /dev/null +++ b/model/apa/response_time.v @@ -0,0 +1,2 @@ +(* The definition of response time remains the same under APA scheduling. *) +Require Export rt.model.basic.response_time. \ No newline at end of file diff --git a/model/apa/schedulability.v b/model/apa/schedulability.v new file mode 100644 index 0000000000000000000000000000000000000000..db9b150a58cc5222236c5269952218bb7abe79c4 --- /dev/null +++ b/model/apa/schedulability.v @@ -0,0 +1,2 @@ +(* The notion of schedulability remains the same under APA scheduling. *) +Require Export rt.model.basic.schedulability. \ No newline at end of file diff --git a/model/apa/schedule.v b/model/apa/schedule.v new file mode 100644 index 0000000000000000000000000000000000000000..80aa78b0d6d4d096740ddce3a839fdf3b14a7d2e --- /dev/null +++ b/model/apa/schedule.v @@ -0,0 +1,2 @@ +(* The definition of schedule remains the same under APA scheduling. *) +Require Export rt.model.basic.schedule. \ No newline at end of file diff --git a/model/apa/task.v b/model/apa/task.v new file mode 100644 index 0000000000000000000000000000000000000000..12257fa0840d540106f3908d33a0190d24ec37fa --- /dev/null +++ b/model/apa/task.v @@ -0,0 +1,2 @@ +(* The notion of a valid task remains the same under APA scheduling. *) +Require Export rt.model.basic.task. \ No newline at end of file diff --git a/model/apa/task_arrival.v b/model/apa/task_arrival.v new file mode 100644 index 0000000000000000000000000000000000000000..d7299e0141593fe1e59ad6a3ebc964267bde4af5 --- /dev/null +++ b/model/apa/task_arrival.v @@ -0,0 +1,2 @@ +(* The notion of interarrival times does not change under APA scheduling. *) +Require Export rt.model.basic.task_arrival. \ No newline at end of file diff --git a/model/apa/time.v b/model/apa/time.v new file mode 100644 index 0000000000000000000000000000000000000000..394817a0f5a1deef15d9e34ac23dea748feec9ec --- /dev/null +++ b/model/apa/time.v @@ -0,0 +1,2 @@ +(* The definition of time is the same. *) +Require Export rt.model.basic.time. \ No newline at end of file diff --git a/model/apa/workload.v b/model/apa/workload.v new file mode 100644 index 0000000000000000000000000000000000000000..b3482234eedcb5ac8bd3676ff5a7156d51b60d5d --- /dev/null +++ b/model/apa/workload.v @@ -0,0 +1,2 @@ +(* The definition of workload does not change under APA scheduling. *) +Require Export rt.model.basic.workload. \ No newline at end of file diff --git a/model/basic/platform_fp.v b/model/basic/constrained_deadlines.v similarity index 57% rename from model/basic/platform_fp.v rename to model/basic/constrained_deadlines.v index 6befe703964baad39a7873e65c7fd76589d6a6df..04d6105d588574118f9915440a1becc7fbe5a2c1 100644 --- a/model/basic/platform_fp.v +++ b/model/basic/constrained_deadlines.v @@ -1,15 +1,16 @@ Require Import rt.util.all. Require Import rt.model.basic.task rt.model.basic.job rt.model.basic.schedule - rt.model.basic.task_arrival rt.model.basic.interference - rt.model.basic.priority rt.model.basic.platform. + rt.model.basic.priority rt.model.basic.task_arrival + rt.model.basic.interference rt.model.basic.platform. From mathcomp Require Import ssreflect ssrbool ssrfun eqtype ssrnat seq fintype bigop. -Module PlatformFP. - - Import Job SporadicTaskset Schedule ScheduleOfSporadicTask SporadicTaskset SporadicTaskArrival Interference Priority Platform. +Module ConstrainedDeadlines. + Import Job SporadicTaskset ScheduleOfSporadicTask SporadicTaskset + SporadicTaskArrival Interference Priority Platform. + Section Lemmas. - + Context {sporadic_task: eqType}. Variable task_cost: sporadic_task -> time. Variable task_period: sporadic_task -> time. @@ -20,19 +21,181 @@ Module PlatformFP. Variable job_deadline: Job -> time. Variable job_task: Job -> sporadic_task. - (* Consider any job arrival sequence ... *) + (* Assume any job arrival sequence ... *) Context {arr_seq: arrival_sequence Job}. (* ... and any schedule of this arrival sequence. *) Context {num_cpus: nat}. Variable sched: schedule num_cpus arr_seq. - - (* Assume that all jobs have valid parameters. *) + + (* Assume all jobs have valid parameters, ...*) Hypothesis H_valid_job_parameters: forall (j: JobIn arr_seq), valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j. - Section JobInvariantAsTaskInvariant. + (* In this section we prove the absence of multiple jobs of the same + task when constrained deadlines are assumed. *) + Section NoMultipleJobs. + + (* Assume any work-conserving priority-based scheduler. *) + Variable higher_eq_priority: JLDP_policy arr_seq. + Hypothesis H_work_conserving: work_conserving job_cost sched. + Hypothesis H_enforces_JLDP_policy: + enforces_JLDP_policy job_cost sched higher_eq_priority. + + (* Consider task set ts. *) + Variable ts: taskset_of sporadic_task. + + (* Assume that all jobs come from the taskset. *) + Hypothesis H_all_jobs_from_taskset: + forall (j: JobIn arr_seq), job_task j \in ts. + + (* Suppose that jobs are sequential, ...*) + Hypothesis H_sequential_jobs: sequential_jobs sched. + (* ... jobs must arrive to execute, ... *) + Hypothesis H_completed_jobs_dont_execute: + completed_jobs_dont_execute job_cost sched. + (* ... and jobs do not execute after completion. *) + Hypothesis H_jobs_must_arrive_to_execute: + jobs_must_arrive_to_execute sched. + + (* Assume that the schedule satisfies the sporadic task model ...*) + Hypothesis H_sporadic_tasks: + sporadic_task_model task_period arr_seq job_task. + + (* Consider a valid task tsk, ...*) + Variable tsk: sporadic_task. + Hypothesis H_valid_task: is_valid_sporadic_task task_cost task_period task_deadline tsk. + + (*... whose job j ... *) + Variable j: JobIn arr_seq. + Variable H_job_of_tsk: job_task j = tsk. + + (*... is backlogged at time t. *) + Variable t: time. + Hypothesis H_j_backlogged: backlogged job_cost sched j t. + + (* Assume that any previous jobs of tsk have completed by the period. *) + Hypothesis H_all_previous_jobs_completed : + forall (j_other: JobIn arr_seq) tsk_other, + job_task j_other = tsk_other -> + job_arrival j_other + task_period tsk_other <= t -> + completed job_cost sched j_other (job_arrival j_other + task_period (job_task j_other)). + + Let scheduled_task_other_than (tsk tsk_other: sporadic_task) := + task_is_scheduled job_task sched tsk_other t && (tsk_other != tsk). + + (* Then, there can be at most one pending job of each task at time t. *) + Lemma platform_at_most_one_pending_job_of_each_task : + forall j1 j2, + pending job_cost sched j1 t -> + pending job_cost sched j2 t -> + job_task j1 = job_task j2 -> + j1 = j2. + Proof. + rename H_sporadic_tasks into SPO, H_all_previous_jobs_completed into PREV. + intros j1 j2 PENDING1 PENDING2 SAMEtsk. + apply/eqP; rewrite -[_ == _]negbK; apply/negP; red; move => /eqP DIFF. + move: PENDING1 PENDING2 => /andP [ARRIVED1 /negP NOTCOMP1] /andP [ARRIVED2 /negP NOTCOMP2]. + destruct (leqP (job_arrival j1) (job_arrival j2)) as [BEFORE1 | BEFORE2]. + { + specialize (SPO j1 j2 DIFF SAMEtsk BEFORE1). + exploit (PREV j1 (job_task j1)); + [by done | by apply leq_trans with (n := job_arrival j2) | intros COMP1]. + apply NOTCOMP1. + apply completion_monotonic with (t0 := job_arrival j1 + task_period (job_task j1)); + try (by done). + by apply leq_trans with (n := job_arrival j2). + } + { + apply ltnW in BEFORE2. + exploit (SPO j2 j1); [by red; ins; subst | by rewrite SAMEtsk | by done | intro SPO']. + exploit (PREV j2 (job_task j2)); + [by done | by apply leq_trans with (n := job_arrival j1) | intros COMP2]. + apply NOTCOMP2. + apply completion_monotonic with (t0 := job_arrival j2 + task_period (job_task j2)); + try (by done). + by apply leq_trans with (n := job_arrival j1). + } + Qed. + + (* Therefore, all processors are busy with tasks other than tsk. *) + Lemma platform_cpus_busy_with_interfering_tasks : + count (scheduled_task_other_than tsk) ts = num_cpus. + Proof. + have UNIQ := platform_at_most_one_pending_job_of_each_task. + rename H_all_jobs_from_taskset into FROMTS, + H_sequential_jobs into SEQUENTIAL, + H_work_conserving into WORK, + H_enforces_JLDP_policy into PRIO, + H_j_backlogged into BACK, + H_job_of_tsk into JOBtsk, + H_valid_job_parameters into JOBPARAMS, + H_valid_task into TASKPARAMS, + H_all_previous_jobs_completed into PREV, + H_completed_jobs_dont_execute into COMP, + H_jobs_must_arrive_to_execute into ARRIVE. + apply work_conserving_eq_work_conserving_count in WORK. + unfold valid_sporadic_job, valid_realtime_job, + enforces_JLDP_policy, completed_jobs_dont_execute, + sporadic_task_model, is_valid_sporadic_task, + jobs_of_same_task_dont_execute_in_parallel, + sequential_jobs in *. + apply/eqP; rewrite eqn_leq; apply/andP; split. + { + apply leq_trans with (n := count (fun x => task_is_scheduled job_task sched x t) ts); + first by apply sub_count; first by red; move => x /andP [SCHED _]. + unfold task_is_scheduled. + apply count_exists; first by destruct ts. + { + intros cpu x1 x2 SCHED1 SCHED2. + unfold task_scheduled_on in *. + destruct (sched cpu t); last by done. + move: SCHED1 SCHED2 => /eqP SCHED1 /eqP SCHED2. + by rewrite -SCHED1 -SCHED2. + } + } + { + rewrite -(WORK j t) // -count_predT. + apply leq_trans with (n := count (fun j: JobIn arr_seq => scheduled_task_other_than tsk (job_task j)) (jobs_scheduled_at sched t)); + last first. + { + rewrite -count_map. + apply count_sub_uniqr; + last by red; move => tsk' /mapP [j' _ JOBtsk']; subst; apply FROMTS. + rewrite map_inj_in_uniq; first by apply scheduled_jobs_uniq. + red; intros j1 j2 SCHED1 SCHED2 SAMEtsk. + rewrite 2!mem_scheduled_jobs_eq_scheduled in SCHED1 SCHED2. + apply scheduled_implies_pending with (job_cost0 := job_cost) in SCHED1; try (by done). + apply scheduled_implies_pending with (job_cost0 := job_cost) in SCHED2; try (by done). + by apply UNIQ. + } + { + apply sub_in_count; intros j' SCHED' _. + rewrite mem_scheduled_jobs_eq_scheduled in SCHED'. + unfold scheduled_task_other_than; apply/andP; split. + { + move: SCHED' => /existsP [cpu /eqP SCHED']. + by apply/existsP; exists cpu; rewrite /task_scheduled_on SCHED' eq_refl. + } + { + apply/eqP; red; intro SAMEtsk; symmetry in SAMEtsk. + move: BACK => /andP [PENDING NOTSCHED]. + generalize SCHED'; intro PENDING'. + apply scheduled_implies_pending with (job_cost0 := job_cost) in PENDING'; try (by done). + exploit (UNIQ j j' PENDING PENDING'); [by rewrite -SAMEtsk | intro EQjob; subst]. + by rewrite SCHED' in NOTSCHED. + } + } + } + Qed. + + End NoMultipleJobs. + + (* In this section we also prove the absence of multiple jobs of the same + task when constrained deadlines are assumed, but in the specific case + of fixed-priority scheduling. *) + Section NoMultipleJobsFP. (* Assume any work-conserving priority-based scheduler. *) Variable higher_eq_priority: FP_policy sporadic_task. @@ -40,12 +203,10 @@ Module PlatformFP. Hypothesis H_enforces_JLDP_policy: enforces_FP_policy job_cost job_task sched higher_eq_priority. - (* Consider any task set ts ... *) + (* Consider any task set ts. *) Variable ts: taskset_of sporadic_task. - (* ... that has no duplicate tasks ... *) - Hypothesis H_ts_is_a_set: uniq ts. - (* ... and such that all jobs come from the taskset. *) + (* Assume that all jobs come from the taskset. *) Hypothesis H_all_jobs_from_taskset: forall (j: JobIn arr_seq), job_task j \in ts. @@ -75,13 +236,14 @@ Module PlatformFP. Hypothesis H_j_backlogged: backlogged job_cost sched j t. Hypothesis H_t_before_period: t < job_arrival j + task_period tsk. - Let can_interfere_with_tsk := fp_can_interfere_with higher_eq_priority tsk. + (* Recall the definition of a higher-priority task (with respect to tsk). *) + Let is_hp_task := higher_priority_task higher_eq_priority tsk. (* Assume that any jobs of higher-priority tasks complete by their period. *) Hypothesis H_all_previous_jobs_completed : forall (j_other: JobIn arr_seq) tsk_other, job_task j_other = tsk_other -> - can_interfere_with_tsk tsk_other -> + is_hp_task tsk_other -> completed job_cost sched j_other (job_arrival j_other + task_period tsk_other). (* Assume that any jobs of tsk prior to j complete by their period. *) @@ -93,7 +255,7 @@ Module PlatformFP. Definition scheduled_task_with_higher_eq_priority (tsk tsk_other: sporadic_task) := task_is_scheduled job_task sched tsk_other t && - can_interfere_with_tsk tsk_other. + is_hp_task tsk_other. (* Then, there can be at most one pending job of higher-priority tasks at time t. *) Lemma platform_fp_no_multiple_jobs_of_interfering_tasks : @@ -101,7 +263,7 @@ Module PlatformFP. pending job_cost sched j1 t -> pending job_cost sched j2 t -> job_task j1 = job_task j2 -> - can_interfere_with_tsk (job_task j1) -> + is_hp_task (job_task j1) -> j1 = j2. Proof. unfold sporadic_task_model in *. @@ -192,17 +354,16 @@ Module PlatformFP. apply work_conserving_eq_work_conserving_count in WORK. unfold valid_sporadic_job, valid_realtime_job, enforces_FP_policy, enforces_JLDP_policy, FP_to_JLDP, - task_precedence_constraints, completed_jobs_dont_execute, + completed_jobs_dont_execute, sequential_jobs, sporadic_task_model, is_valid_sporadic_task, jobs_of_same_task_dont_execute_in_parallel, - sequential_jobs, - can_interfere_with_tsk in *. + is_hp_task in *. apply/eqP; rewrite eqn_leq; apply/andP; split. { apply leq_trans with (n := count (fun x => task_is_scheduled job_task sched x t) ts); first by apply sub_count; red; move => x /andP [SCHED _]. unfold task_is_scheduled. - apply count_exists; first by done. + apply count_exists; first by destruct ts. { intros cpu x1 x2 SCHED1 SCHED2. unfold task_scheduled_on in *. @@ -261,9 +422,9 @@ Module PlatformFP. } } Qed. - - End JobInvariantAsTaskInvariant. - + + End NoMultipleJobsFP. + End Lemmas. - -End PlatformFP. \ No newline at end of file + +End ConstrainedDeadlines. \ No newline at end of file diff --git a/model/basic/interference.v b/model/basic/interference.v index 9cf6985e57ad2de5d835d72b9ce3e130f3f7def3..b1d1031be6dcf2125c03b11d3cbad1d344f592fd 100644 --- a/model/basic/interference.v +++ b/model/basic/interference.v @@ -7,6 +7,7 @@ Module Interference. Import Schedule ScheduleOfSporadicTask Priority Workload. + (* In this section, we define the notion of a possible interfering task. *) Section PossibleInterferingTasks. Context {sporadic_task: eqType}. @@ -19,19 +20,31 @@ Module Interference. (* Assume an FP policy. *) Variable higher_eq_priority: FP_policy sporadic_task. - (* Under constrained deadlines, tsk_other can only interfere with tsk - if it's a different task with higher or equal priority. *) - Definition fp_can_interfere_with (tsk tsk_other: sporadic_task) := - higher_eq_priority tsk_other tsk && (tsk_other != tsk). + (* Let tsk be the task to be analyzed ... *) + Variable tsk: sporadic_task. + + (* ...and let tsk_other be another task. *) + Variable tsk_other: sporadic_task. + + (* Under FP scheduling with constrained deadlines, tsk_other can only interfere + with tsk if it is a different task with higher priority. *) + Definition higher_priority_task := + higher_eq_priority tsk_other tsk && + (tsk_other != tsk). End FP. Section JLFP. - (* Under JLFP/JLDP policies, any two different tasks can interfere with - each other. *) - Definition jldp_can_interfere_with (tsk tsk_other: sporadic_task) := - tsk_other != tsk. + (* Let tsk be the task to be analyzed ... *) + Variable tsk: sporadic_task. + + (* ...and let tsk_other be another task. *) + Variable tsk_other: sporadic_task. + + (* Under JLFP/JLDP scheduling with constrained deadlines, tsk_other can only interfere + with tsk if it is a different task. *) + Definition different_task := tsk_other != tsk. End JLFP. @@ -58,22 +71,24 @@ Module Interference. Let job_is_backlogged (t: time) := backlogged job_cost sched j t. + (* First, we define total interference. *) Section TotalInterference. - (* First, we define the total interference incurred by job j during [t1, t2) - as the cumulative time in which j is backlogged in this interval. *) + (* The total interference incurred by job j during [t1, t2) is the + cumulative time in which j is backlogged in this interval. *) Definition total_interference (t1 t2: time) := \sum_(t1 <= t < t2) job_is_backlogged t. End TotalInterference. - + + (* Next, we define job interference. *) Section JobInterference. (* Let job_other be a job that interferes with j. *) Variable job_other: JobIn arr_seq. - (* We define the total interference caused by job_other during [t1, t2) as the - cumulative service received by job_other while j is backlogged. *) + (* The interference caused by job_other during [t1, t2) is the cumulative + time in which j is backlogged while job_other is scheduled. *) Definition job_interference (t1 t2: time) := \sum_(t1 <= t < t2) \sum_(cpu < num_cpus) @@ -81,13 +96,14 @@ Module Interference. End JobInterference. + (* Next, we define task interference. *) Section TaskInterference. (* In order to define task interference, consider any interfering task tsk_other. *) Variable tsk_other: sporadic_task. - (* We define the total interference caused by tsk during [t1, t2) as - the cumulative service received by tsk while j is backlogged. *) + (* The interference caused by tsk during [t1, t2) is the cumulative time + in which j is backlogged while tsk is scheduled. *) Definition task_interference (t1 t2: time) := \sum_(t1 <= t < t2) \sum_(cpu < num_cpus) @@ -96,6 +112,8 @@ Module Interference. End TaskInterference. + (* Next, we define an approximation of the total interference based on + each per-task interference. *) Section TaskInterferenceJobList. Variable tsk_other: sporadic_task. @@ -106,6 +124,7 @@ Module Interference. End TaskInterferenceJobList. + (* Now we prove some basic lemmas about interference. *) Section BasicLemmas. (* Interference cannot be larger than the considered time window. *) @@ -119,6 +138,7 @@ Module Interference. by rewrite big_const_nat iter_addn mul1n addn0 leqnn. Qed. + (* Job interference is bounded by the service of the interfering job. *) Lemma job_interference_le_service : forall j_other t1 t2, job_interference j_other t1 t2 <= service_during sched j_other t1 t2. @@ -131,6 +151,7 @@ Module Interference. by destruct (scheduled_on sched j_other cpu t). Qed. + (* Task interference is bounded by the workload of the interfering task. *) Lemma task_interference_le_workload : forall tsk t1 t2, task_interference tsk t1 t2 <= workload job_task sched tsk t1 t2. @@ -145,6 +166,7 @@ Module Interference. End BasicLemmas. + (* Now we prove some bounds on interference for sequential jobs. *) Section InterferenceSequentialJobs. (* If jobs are sequential, ... *) @@ -183,7 +205,8 @@ Module Interference. End InterferenceSequentialJobs. - (* The sequential per-job interference bounds the actual interference. *) + (* Next, we show that the cumulative per-task interference bounds the total + interference. *) Section BoundUsingPerJobInterference. Lemma interference_le_interference_joblist : diff --git a/model/basic/platform.v b/model/basic/platform.v index f89ff7697319f22f16b75e399f151216fb51334d..d7e31764f77f5bf1f974b71113cff6bc0c7f1386 100644 --- a/model/basic/platform.v +++ b/model/basic/platform.v @@ -7,7 +7,7 @@ Module Platform. Import Job SporadicTaskset Schedule ScheduleOfSporadicTask SporadicTaskset SporadicTaskArrival Interference Priority. - Section SchedulingInvariants. + Section Properties. Context {sporadic_task: eqType}. Variable task_cost: sporadic_task -> time. @@ -26,7 +26,7 @@ Module Platform. Context {num_cpus: nat}. Variable sched: schedule num_cpus arr_seq. - Section WorkConserving. + Section Execution. (* A scheduler is work-conserving iff when a job j is backlogged, all processors are busy with other jobs. *) @@ -43,7 +43,7 @@ Module Platform. backlogged job_cost sched j t -> size (jobs_scheduled_at sched t) = num_cpus. - End WorkConserving. + End Execution. Section JLDP. @@ -154,170 +154,11 @@ Module Platform. } } Qed. - + End EquivalentDefinitions. - Section JobInvariantAsTaskInvariant. - - (* Assume any work-conserving priority-based scheduler. *) - Variable higher_eq_priority: JLDP_policy arr_seq. - Hypothesis H_work_conserving: work_conserving. - Hypothesis H_enforces_JLDP_policy: enforces_JLDP_policy higher_eq_priority. - - (* Consider task set ts. *) - Variable ts: taskset_of sporadic_task. - - (* Assume the task set has no duplicates, ... *) - Hypothesis H_ts_is_a_set: uniq ts. - (* ... and all jobs come from the taskset. *) - Hypothesis H_all_jobs_from_taskset: - forall (j: JobIn arr_seq), job_task j \in ts. - - (* Suppose that jobs are sequential, ...*) - Hypothesis H_sequential_jobs: sequential_jobs sched. - (* ... jobs must arrive to execute, ... *) - Hypothesis H_completed_jobs_dont_execute: - completed_jobs_dont_execute job_cost sched. - (* ... and jobs do not execute after completion. *) - Hypothesis H_jobs_must_arrive_to_execute: - jobs_must_arrive_to_execute sched. - - (* Assume that the schedule satisfies the sporadic task model ...*) - Hypothesis H_sporadic_tasks: - sporadic_task_model task_period arr_seq job_task. - - (* Consider a valid task tsk, ...*) - Variable tsk: sporadic_task. - Hypothesis H_valid_task: is_valid_sporadic_task task_cost task_period task_deadline tsk. - - (*... whose job j ... *) - Variable j: JobIn arr_seq. - Variable H_job_of_tsk: job_task j = tsk. - - (*... is backlogged at time t. *) - Variable t: time. - Hypothesis H_j_backlogged: backlogged job_cost sched j t. - - (* Assume that any previous jobs of tsk have completed by the period. *) - Hypothesis H_all_previous_jobs_completed : - forall (j_other: JobIn arr_seq) tsk_other, - job_task j_other = tsk_other -> - job_arrival j_other + task_period tsk_other <= t -> - completed job_cost sched j_other (job_arrival j_other + task_period (job_task j_other)). - - Let scheduled_task_other_than (tsk tsk_other: sporadic_task) := - task_is_scheduled job_task sched tsk_other t && (tsk_other != tsk). - - (* Then, there can be at most one pending job of each task at time t. *) - Lemma platform_at_most_one_pending_job_of_each_task : - forall j1 j2, - pending job_cost sched j1 t -> - pending job_cost sched j2 t -> - job_task j1 = job_task j2 -> - j1 = j2. - Proof. - rename H_sporadic_tasks into SPO, H_all_previous_jobs_completed into PREV. - intros j1 j2 PENDING1 PENDING2 SAMEtsk. - apply/eqP; rewrite -[_ == _]negbK; apply/negP; red; move => /eqP DIFF. - move: PENDING1 PENDING2 => /andP [ARRIVED1 /negP NOTCOMP1] /andP [ARRIVED2 /negP NOTCOMP2]. - destruct (leqP (job_arrival j1) (job_arrival j2)) as [BEFORE1 | BEFORE2]. - { - specialize (SPO j1 j2 DIFF SAMEtsk BEFORE1). - exploit (PREV j1 (job_task j1)); - [by done | by apply leq_trans with (n := job_arrival j2) | intros COMP1]. - apply NOTCOMP1. - apply completion_monotonic with (t0 := job_arrival j1 + task_period (job_task j1)); - try (by done). - by apply leq_trans with (n := job_arrival j2). - } - { - apply ltnW in BEFORE2. - exploit (SPO j2 j1); [by red; ins; subst | by rewrite SAMEtsk | by done | intro SPO']. - exploit (PREV j2 (job_task j2)); - [by done | by apply leq_trans with (n := job_arrival j1) | intros COMP2]. - apply NOTCOMP2. - apply completion_monotonic with (t0 := job_arrival j2 + task_period (job_task j2)); - try (by done). - by apply leq_trans with (n := job_arrival j1). - } - Qed. - - (* Therefore, all processors are busy with tasks other than tsk. *) - Lemma platform_cpus_busy_with_interfering_tasks : - count (scheduled_task_other_than tsk) ts = num_cpus. - Proof. - have UNIQ := platform_at_most_one_pending_job_of_each_task. - rename H_all_jobs_from_taskset into FROMTS, - H_sequential_jobs into SEQUENTIAL, - H_work_conserving into WORK, - H_enforces_JLDP_policy into PRIO, - H_j_backlogged into BACK, - H_job_of_tsk into JOBtsk, - H_valid_job_parameters into JOBPARAMS, - H_valid_task into TASKPARAMS, - H_all_previous_jobs_completed into PREV, - H_completed_jobs_dont_execute into COMP, - H_jobs_must_arrive_to_execute into ARRIVE. - apply work_conserving_eq_work_conserving_count in WORK. - unfold valid_sporadic_job, valid_realtime_job, - enforces_JLDP_policy, - task_precedence_constraints, completed_jobs_dont_execute, - sporadic_task_model, is_valid_sporadic_task, - jobs_of_same_task_dont_execute_in_parallel, - sequential_jobs in *. - apply/eqP; rewrite eqn_leq; apply/andP; split. - { - apply leq_trans with (n := count (fun x => task_is_scheduled job_task sched x t) ts); - first by apply sub_count; first by red; move => x /andP [SCHED _]. - unfold task_is_scheduled. - apply count_exists; first by done. - { - intros cpu x1 x2 SCHED1 SCHED2. - unfold task_scheduled_on in *. - destruct (sched cpu t); last by done. - move: SCHED1 SCHED2 => /eqP SCHED1 /eqP SCHED2. - by rewrite -SCHED1 -SCHED2. - } - } - { - rewrite -(WORK j t) // -count_predT. - apply leq_trans with (n := count (fun j: JobIn arr_seq => scheduled_task_other_than tsk (job_task j)) (jobs_scheduled_at sched t)); - last first. - { - rewrite -count_map. - apply count_sub_uniqr; - last by red; move => tsk' /mapP [j' _ JOBtsk']; subst; apply FROMTS. - rewrite map_inj_in_uniq; first by apply scheduled_jobs_uniq. - red; intros j1 j2 SCHED1 SCHED2 SAMEtsk. - rewrite 2!mem_scheduled_jobs_eq_scheduled in SCHED1 SCHED2. - apply scheduled_implies_pending with (job_cost0 := job_cost) in SCHED1; try (by done). - apply scheduled_implies_pending with (job_cost0 := job_cost) in SCHED2; try (by done). - by apply UNIQ. - } - { - apply sub_in_count; intros j' SCHED' _. - rewrite mem_scheduled_jobs_eq_scheduled in SCHED'. - unfold scheduled_task_other_than; apply/andP; split. - { - move: SCHED' => /existsP [cpu /eqP SCHED']. - by apply/existsP; exists cpu; rewrite /task_scheduled_on SCHED' eq_refl. - } - { - apply/eqP; red; intro SAMEtsk; symmetry in SAMEtsk. - move: BACK => /andP [PENDING NOTSCHED]. - generalize SCHED'; intro PENDING'. - apply scheduled_implies_pending with (job_cost0 := job_cost) in PENDING'; try (by done). - exploit (UNIQ j j' PENDING PENDING'); [by rewrite -SAMEtsk | intro EQjob; subst]. - by rewrite SCHED' in NOTSCHED. - } - } - } - Qed. - - End JobInvariantAsTaskInvariant. - End Lemmas. - End SchedulingInvariants. + End Properties. End Platform. \ No newline at end of file diff --git a/model/basic/priority.v b/model/basic/priority.v index b96753c05474719c6d5b4354753c4c827ee9bda6..f2898b5181c5e023ea646d48cb8a5ac37cc7062e 100644 --- a/model/basic/priority.v +++ b/model/basic/priority.v @@ -6,151 +6,185 @@ Set Implicit Arguments. (* Definitions of FP and JLFP/JLDP priority relations. *) Module Priority. - Import SporadicTask Schedule. + Import SporadicTaskset Schedule. Section PriorityDefs. - (* Assume a given job arrival sequence. *) - Context {Job: eqType}. - Variable arr_seq: arrival_sequence Job. + Section JLDP. - Section Definitions. + (* Consider any job arrival sequence. *) + Context {Job: eqType}. + Variable arr_seq: arrival_sequence Job. - (* In the following, we define all priority relations as non-strict, i.e., they specify that - "job_high has higher priority than (or the same priority as) job_low". *) - - (* A JLDP policy is a generic relation between two jobs of an arrival sequence - that can vary with time. *) + (* We define a JLDP policy as a relation between jobs in the + arrival sequence at each point in time. + Although this definition doesn't specify how the policy was + constructed (e.g., whether it depends on the schedule, on + job parameters, etc.), it is as general as possible. + Knowing the priority of the jobs is sufficient to make + scheduling decisions. *) Definition JLDP_policy := time -> JobIn arr_seq -> JobIn arr_seq -> bool. - (* FP policy is simply a relation between tasks. - Because our model of processor platform is based on a generic JLDP policy, - we generate a JLDP policy from an FP policy whenever required. *) - Variable sporadic_task: eqType. - Definition FP_policy := sporadic_task -> sporadic_task -> bool. + End JLDP. - End Definitions. - - Section ValidJLFPPolicy. + Section FP. - Variable is_higher_priority: JLDP_policy. + (* Let Task denote any type of task. *) + Variable Task: eqType. - (* A policy is reflexive, since a job has the same priority as itself. *) - Definition JLFP_is_reflexive := - forall t, reflexive (is_higher_priority t). + (* We define an FP policy as a relation between tasks. *) + Definition FP_policy := Task -> Task -> bool. - (* A policy is transitive. *) - Definition JLFP_is_transitive := - forall t, transitive (is_higher_priority t). - - (* A policy is total, since it must know the priority of any two jobs at any time. *) - Definition JLFP_is_total := - forall t, total (is_higher_priority t). - - (* A JLDP policy is valid iff it satisfies the three properties. - Note that, for generality, we don't enforce antisymmetry and allow multiple - jobs with same priority. *) - Definition valid_JLDP_policy := - JLFP_is_reflexive /\ JLFP_is_transitive /\ JLFP_is_total. - - End ValidJLFPPolicy. - - Section MonotonicPriorities. - - Context {sporadic_task: eqType}. - Variable job_task: Job -> sporadic_task. - Variable higher_eq_priority: JLDP_policy. - - Definition monotonic_priorities := - forall (j j': JobIn arr_seq) t, - job_task j = job_task j' -> - job_arrival j < job_arrival j' -> - higher_eq_priority t j j'. - - End MonotonicPriorities. + End FP. - Section ValidFPPolicy. + End PriorityDefs. - Context {sporadic_task: eqType}. - - Variable is_higher_priority: FP_policy sporadic_task. + (* To avoid repeating definitions for JLDP and FP policies, + we state everything in terms of JLDP policies. + For that, we define a function that converts the relation + between tasks to a relation between jobs. *) + Section GeneralizeFP. - Definition FP_is_reflexive := reflexive is_higher_priority. + (* Consider any arrival sequence of jobs spawned by tasks. *) + Context {Task: eqType}. + Context {Job: eqType}. + Variable job_task: Job -> Task. + Variable arr_seq: arrival_sequence Job. - Definition FP_is_transitive := transitive is_higher_priority. - - Definition FP_is_total := total is_higher_priority. + (* We convert FP to JLDP policies using the following function. *) + Definition FP_to_JLDP (task_hp: FP_policy Task) : JLDP_policy arr_seq := + fun (t: time) (jhigh jlow: JobIn arr_seq) => + task_hp (job_task jhigh) (job_task jlow). - (* We enforce the same restrictions for FP policy: reflexive, transitive, total. *) - Definition valid_FP_policy := - FP_is_reflexive /\ FP_is_transitive /\ FP_is_total. + End GeneralizeFP. - End ValidFPPolicy. + (* Next, we define properties of a JLDP policy. *) + Section PropertiesJLDP. - End PriorityDefs. - - Section RateDeadlineMonotonic. + (* Consider any JLDP policy. *) + Context {Job: eqType}. + Context {arr_seq: arrival_sequence Job}. + Variable job_priority: JLDP_policy arr_seq. - Context {sporadic_task: eqType}. - Variable task_period: sporadic_task -> time. - Variable task_deadline: sporadic_task -> time. + (* Now we define the properties. *) - (* Rate-Monotonic and Deadline-Monotonic as priority order *) - Definition RM (tsk1 tsk2: sporadic_task) := task_period tsk1 <= task_period tsk2. - - Definition DM (tsk1 tsk2: sporadic_task) := task_deadline tsk1 <= task_deadline tsk2. - - (* Rate-Montonic is a valid FP policy. *) - Lemma rm_is_valid : valid_FP_policy RM. - Proof. - unfold valid_FP_policy, FP_is_reflexive, FP_is_transitive, RM; - repeat (split; try red); first by ins. - by intros x y z XY YZ; apply leq_trans with (n := task_period x). - by red; intros tsk1 tsk2; apply/orP; destruct (leqP (task_period tsk1) (task_period tsk2)); - [by left | by right; apply ltnW]. - Qed. - - (* Deadline-Monotonic is a valid FP policy. *) - Lemma dm_is_valid : valid_FP_policy DM. - Proof. - unfold valid_FP_policy, FP_is_reflexive, FP_is_transitive, DM; - repeat (split; try red); first by ins. - by intros x y z; apply leq_trans. - by red; intros tsk1 tsk2; apply/orP; destruct (leqP (task_deadline tsk1) (task_deadline tsk2)); - [by left | by right; apply ltnW]. - Qed. - - End RateDeadlineMonotonic. + (* Whether the JLDP policy is reflexive. *) + Definition JLDP_is_reflexive := + forall t, reflexive (job_priority t). - Section GeneralizeFP. + (* Whether the JLDP policy is irreflexive. *) + Definition JLDP_is_irreflexive := + forall t, irreflexive (job_priority t). - Context {sporadic_task: eqType}. + (* Whether the JLDP policy is transitive. *) + Definition JLDP_is_transitive := + forall t, transitive (job_priority t). + + (* Whether the JLDP policy is total. *) + Definition JLDP_is_total := + forall t, total (job_priority t). + + End PropertiesJLDP. + + (* Next we define properties of an FP policy. *) + Section PropertiesFP. + + (* Assume that jobs are spawned by tasks. *) Context {Job: eqType}. - Variable job_task: Job -> sporadic_task. + Context {Task: eqType}. + Variable job_task: Job -> Task. + + (* Consider any job arrival sequence... *) Variable arr_seq: arrival_sequence Job. - Variable num_cpus: nat. - (* We define a function to get from FP to JLDP policy. *) - Definition FP_to_JLDP (task_hp: FP_policy sporadic_task) : JLDP_policy arr_seq := - fun (t: time) (jhigh jlow: JobIn arr_seq) => - task_hp (job_task jhigh) (job_task jlow). + (* ...and let task_priority be any FP policy. *) + Variable task_priority: FP_policy Task. - (* With this function, from a valid FP policy comes a valid JLDP policy. *) - Lemma valid_FP_is_valid_JLDP : - forall task_hp (FP: valid_FP_policy task_hp), - valid_JLDP_policy (FP_to_JLDP task_hp). - Proof. - unfold FP_to_JLDP, valid_FP_policy, valid_JLDP_policy, - FP_is_reflexive, FP_is_transitive, FP_is_total, - JLFP_is_reflexive, JLFP_is_transitive, JLFP_is_total, reflexive, transitive. - ins; destruct FP as [REFL [TRANS TOTAL]]; repeat (split; try red); des; last by ins. - by ins; rewrite REFL. - by intros sched t y x z; apply TRANS. - Qed. + (* Now we define the properties. *) + + (* Whether the FP policy is reflexive. *) + Definition FP_is_reflexive := reflexive task_priority. - End GeneralizeFP. + (* Whether the FP policy is irreflexive. *) + Definition FP_is_irreflexive := irreflexive task_priority. + + (* Whether the FP policy is transitive. *) + Definition FP_is_transitive := transitive task_priority. + + Section Antisymmetry. + + (* Consider any task set ts. *) + Variable ts: taskset_of Task. + + (* First we define whether task set ts is totally ordered with + the priority. *) + Definition FP_is_total_over_task_set := + total_over_list task_priority ts. + + (* Then we define whether an FP policy is antisymmetric over task set ts, i.e., + whether the task set has unique priorities. *) + Definition FP_is_antisymmetric_over_task_set := + antisymmetric_over_list task_priority ts. + + End Antisymmetry. + + End PropertiesFP. + + (* Next we define some known FP policies. *) + Section KnownFPPolicies. + + Context {Job: eqType}. + Context {Task: eqType}. + Variable task_period: Task -> time. + Variable task_deadline: Task -> time. + Variable job_task: Job -> Task. + + (* Rate-monotonic orders tasks by smaller periods. *) + Definition RM (tsk1 tsk2: Task) := + task_period tsk1 <= task_period tsk2. - Section JLFPDefs. + (* Deadline-monotonic orders tasks by smaller relative deadlines. *) + Definition DM (tsk1 tsk2: Task) := + task_deadline tsk1 <= task_deadline tsk2. + + Section Properties. + + Variable arr_seq: arrival_sequence Job. + + (* RM is reflexive. *) + Lemma RM_is_reflexive : FP_is_reflexive RM. + Proof. + unfold FP_is_reflexive, reflexive, RM. + by intros tsk; apply leqnn. + Qed. + + (* RM is transitive. *) + Lemma RM_is_transitive : FP_is_transitive RM. + Proof. + unfold FP_is_transitive, transitive, RM. + by intros y x z; apply leq_trans. + Qed. + + (* DM is reflexive. *) + Lemma DM_is_reflexive : FP_is_reflexive DM. + Proof. + unfold FP_is_reflexive, reflexive, DM. + by intros tsk; apply leqnn. + Qed. + + (* DM is transitive. *) + Lemma DM_is_transitive : FP_is_transitive DM. + Proof. + unfold FP_is_transitive, transitive, DM. + by intros y x z; apply leq_trans. + Qed. + + End Properties. + + End KnownFPPolicies. + + (* Next, we define the notion of JLFP policies. *) + Section JLFP. Context {sporadic_task: eqType}. Context {Job: eqType}. @@ -159,48 +193,58 @@ Module Priority. Variable is_higher_priority: JLDP_policy arr_seq. - (* JLFP policy is a JLDP policy where the priorities do not vary with time. *) - Definition is_JLFP_policy (is_higher_priority: JLDP_policy arr_seq) := + (* We call a policy JLFP iff job priorities do not vary with time. *) + Definition is_JLFP_policy := forall j1 j2 t t', is_higher_priority t j1 j2 -> is_higher_priority t' j1 j2. - (* Lemma: every FP policy is a JLFP policy. *) - Variable job_task: Job -> sporadic_task. - Lemma FP_is_JLFP : - forall FP_higher_priority, - is_JLFP_policy (FP_to_JLDP job_task FP_higher_priority). - Proof. - by unfold is_JLFP_policy, valid_FP_policy. - Qed. - - End JLFPDefs. + End JLFP. - Section EDFDefs. + (* In this section, we define known JLFP policies. *) + Section KnownJLFPPolicies. Context {Job: eqType}. Variable arr_seq: arrival_sequence Job. Variable job_deadline: Job -> time. - + + (* Earliest deadline first (EDF) orders jobs by absolute deadlines. *) Definition EDF (t: time) (j1 j2: JobIn arr_seq) := job_arrival j1 + job_deadline j1 <= job_arrival j2 + job_deadline j2. - (* Lemma: EDF is a JLFP policy. *) - Lemma edf_JLFP : is_JLFP_policy EDF. - Proof. - by unfold is_JLFP_policy, EDF. - Qed. - - Lemma edf_valid_policy : valid_JLDP_policy EDF. - Proof. - unfold valid_JLDP_policy, EDF, JLFP_is_reflexive, JLFP_is_transitive, reflexive, transitive. - repeat (split; try red). - by ins; apply leqnn. - by intros sched t y x z; apply leq_trans. - by intros _; red; intros j1 j2; apply/orP; - destruct (leqP (job_arrival j1 + job_deadline j1) (job_arrival j2 + job_deadline j2)); - [by left | by right; apply ltnW]. - Qed. - - End EDFDefs. + Section Properties. + + (* EDF is a JLFP policy. *) + Lemma edf_JLFP : is_JLFP_policy EDF. + Proof. + by unfold is_JLFP_policy, EDF. + Qed. + + (* EDF is reflexive. *) + Lemma EDF_is_reflexive : JLDP_is_reflexive EDF. + Proof. + unfold JLDP_is_reflexive, reflexive. + by intros t j; apply leqnn. + Qed. + + (* EDF is transitive. *) + Lemma EDF_is_transitive : JLDP_is_transitive EDF. + Proof. + unfold JLDP_is_transitive, transitive. + by intros t y x z; apply leq_trans. + Qed. + + (* EDF is total. *) + Lemma EDF_is_total : JLDP_is_total EDF. + Proof. + unfold JLDP_is_total, total, EDF. + intros _ x y. + by case (leqP (job_arrival x + job_deadline x) + (job_arrival y + job_deadline y)); + [by rewrite orTb | by move/ltnW => ->]. + Qed. + + End Properties. + + End KnownJLFPPolicies. End Priority. \ No newline at end of file diff --git a/model/basic/schedule.v b/model/basic/schedule.v index 47cfa1baa78ea77d0324bebb0074efb3650547f7..ec938aab7da97b2e05f6df984b5e7ab427c07262 100644 --- a/model/basic/schedule.v +++ b/model/basic/schedule.v @@ -561,18 +561,11 @@ Module ScheduleOfSporadicTask. Context {num_cpus: nat}. Variable sched: schedule num_cpus arr_seq. - (* Next we define intra-task parallelism, ... *) + (* Next we define intra-task parallelism. *) Definition jobs_of_same_task_dont_execute_in_parallel := forall (j j': JobIn arr_seq) t, job_task j = job_task j' -> scheduled sched j t -> scheduled sched j' t -> j = j'. - - (* ... and task precedence constraints. *) - Definition task_precedence_constraints := - forall (j j': JobIn arr_seq) t, - job_task j = job_task j' -> - job_arrival j < job_arrival j' -> - pending job_cost sched j t -> ~~ scheduled sched j' t. End ScheduleProperties. diff --git a/model/basic/task.v b/model/basic/task.v index df65a6d58a6d3af7539050109ff51b0781064369..f6c3f0d85476e414753236f43d63f5b3cc6cf934 100644 --- a/model/basic/task.v +++ b/model/basic/task.v @@ -41,10 +41,11 @@ Module SporadicTaskset. Section TasksetDefs. - (* A task set is just a sequence of tasks. *) - Definition taskset_of (Task: eqType) := seq Task. + (* A task set is defined as a {set ...}, which denotes a sequence + of tasks with no duplicates. *) + Definition taskset_of (Task: eqType) := {set Task}. - (* Next, we define some properties of a task set. *) + (* Next, we define some properties abouts sequences of tasks. *) Section TasksetProperties. Context {Task: eqType}. @@ -55,7 +56,7 @@ Module SporadicTaskset. Let is_valid_task := is_valid_sporadic_task task_cost task_period task_deadline. - Variable ts: taskset_of Task. + Variable ts: seq Task. (* A valid sporadic taskset only contains valid tasks. *) Definition valid_sporadic_taskset := diff --git a/model/jitter/platform_fp.v b/model/jitter/constrained_deadlines.v similarity index 55% rename from model/jitter/platform_fp.v rename to model/jitter/constrained_deadlines.v index e330c42bc3a9f46ca6f73feb18cedfe4891317f1..807164d5f742e4380e40abeb5831c037a691ee20 100644 --- a/model/jitter/platform_fp.v +++ b/model/jitter/constrained_deadlines.v @@ -1,16 +1,16 @@ Require Import rt.util.all. Require Import rt.model.jitter.task rt.model.jitter.job rt.model.jitter.schedule - rt.model.jitter.task_arrival rt.model.jitter.interference - rt.model.jitter.priority rt.model.jitter.platform. + rt.model.jitter.priority rt.model.jitter.task_arrival + rt.model.jitter.interference rt.model.jitter.platform. From mathcomp Require Import ssreflect ssrbool ssrfun eqtype ssrnat seq fintype bigop. -Module PlatformFP. +Module ConstrainedDeadlines. Import Job SporadicTaskset ScheduleOfSporadicTaskWithJitter SporadicTaskset SporadicTaskArrival Interference Priority Platform. - + Section Lemmas. - + Context {sporadic_task: eqType}. Variable task_cost: sporadic_task -> time. Variable task_period: sporadic_task -> time. @@ -22,19 +22,189 @@ Module PlatformFP. Variable job_task: Job -> sporadic_task. Variable job_jitter: Job -> time. - (* Assume any job arrival sequence... *) + (* Assume any job arrival sequence ... *) Context {arr_seq: arrival_sequence Job}. - (* Consider any schedule. *) + (* ... and any schedule of this arrival sequence. *) Context {num_cpus: nat}. Variable sched: schedule num_cpus arr_seq. - + (* Assume all jobs have valid parameters, ...*) Hypothesis H_valid_job_parameters: forall (j: JobIn arr_seq), valid_sporadic_job task_cost task_deadline job_cost job_deadline job_task j. - Section JobInvariantAsTaskInvariant. + (* In this section we prove the absence of multiple jobs of the same + task when constrained deadlines are assumed. *) + Section NoMultipleJobs. + + (* Assume any work-conserving priority-based scheduler. *) + Variable higher_eq_priority: JLDP_policy arr_seq. + Hypothesis H_work_conserving: + work_conserving job_cost job_jitter sched. + Hypothesis H_enforces_JLDP_policy: + enforces_JLDP_policy job_cost job_jitter sched higher_eq_priority. + + (* Consider task set ts. *) + Variable ts: taskset_of sporadic_task. + + (* Assume that all jobs come from the taskset. *) + Hypothesis H_all_jobs_from_taskset: + forall (j: JobIn arr_seq), job_task j \in ts. + + (* Suppose that jobs are sequential, ...*) + Hypothesis H_sequential_jobs: sequential_jobs sched. + (* ... jobs only execute after the jitter, ... *) + Hypothesis H_jobs_execute_after_jitter: + jobs_execute_after_jitter job_jitter sched. + (* ... and jobs do not execute after completion. *) + Hypothesis H_completed_jobs_dont_execute: + completed_jobs_dont_execute job_cost sched. + + (* Assume that the schedule satisfies the sporadic task model ...*) + Hypothesis H_sporadic_tasks: + sporadic_task_model task_period arr_seq job_task. + + (* Consider a valid task tsk, ...*) + Variable tsk: sporadic_task. + Hypothesis H_valid_task: is_valid_sporadic_task task_cost task_period task_deadline tsk. + + (*... whose job j ... *) + Variable j: JobIn arr_seq. + Variable H_job_of_tsk: job_task j = tsk. + + (*... is backlogged at time t. *) + Variable t: time. + Hypothesis H_j_backlogged: backlogged job_cost job_jitter sched j t. + + (* Assume that any previous jobs of tsk have completed by the period. *) + Hypothesis H_all_previous_jobs_completed : + forall (j_other: JobIn arr_seq) tsk_other, + job_task j_other = tsk_other -> + job_arrival j_other + task_period tsk_other <= t -> + completed job_cost sched j_other (job_arrival j_other + task_period (job_task j_other)). + + Let scheduled_task_other_than (tsk tsk_other: sporadic_task) := + task_is_scheduled job_task sched tsk_other t && (tsk_other != tsk). + + (* Then, there can be at most one pending job of each task at time t. *) + Lemma platform_at_most_one_pending_job_of_each_task : + forall j1 j2, + pending job_cost job_jitter sched j1 t -> + pending job_cost job_jitter sched j2 t -> + job_task j1 = job_task j2 -> + j1 = j2. + Proof. + rename H_sporadic_tasks into SPO, H_all_previous_jobs_completed into PREV. + intros j1 j2 PENDING1 PENDING2 SAMEtsk. + apply/eqP; rewrite -[_ == _]negbK; apply/negP; red; move => /eqP DIFF. + move: PENDING1 PENDING2 => /andP [ARRIVED1 /negP NOTCOMP1] /andP [ARRIVED2 /negP NOTCOMP2]. + unfold jitter_has_passed, actual_arrival in *. + destruct (leqP (job_arrival j1) (job_arrival j2)) as [BEFORE1 | BEFORE2]. + { + specialize (SPO j1 j2 DIFF SAMEtsk BEFORE1). + assert (LEt: job_arrival j1 + task_period (job_task j1) <= t). + { + apply leq_trans with (n := job_arrival j2); first by done. + by apply leq_trans with (n := job_arrival j2 + job_jitter j2); first by apply leq_addr. + } + exploit (PREV j1 (job_task j1)); try (by done). + intros COMP1; apply NOTCOMP1. + by apply completion_monotonic with (t0 := job_arrival j1 + task_period (job_task j1)). + } + { + apply ltnW in BEFORE2. + exploit (SPO j2 j1); [by red; ins; subst | by rewrite SAMEtsk | by done | intro SPO']. + assert (LEt: job_arrival j2 + task_period (job_task j2) <= t). + { + apply leq_trans with (n := job_arrival j1); first by done. + by apply leq_trans with (n := job_arrival j1 + job_jitter j1); first by apply leq_addr. + } + exploit (PREV j2 (job_task j2)); try (by done). + intros COMP2; apply NOTCOMP2. + by apply completion_monotonic with (t0 := job_arrival j2 + task_period (job_task j2)). + } + Qed. + + (* Therefore, all processors are busy with tasks other than tsk. *) + Lemma platform_cpus_busy_with_interfering_tasks : + count (scheduled_task_other_than tsk) ts = num_cpus. + Proof. + have UNIQ := platform_at_most_one_pending_job_of_each_task. + rename H_all_jobs_from_taskset into FROMTS, + H_sequential_jobs into SEQUENTIAL, + H_work_conserving into WORK, + H_enforces_JLDP_policy into PRIO, + H_j_backlogged into BACK, + H_job_of_tsk into JOBtsk, + H_valid_job_parameters into JOBPARAMS, + H_valid_task into TASKPARAMS, + H_all_previous_jobs_completed into PREV, + H_completed_jobs_dont_execute into COMP, + H_jobs_execute_after_jitter into JITTER. + apply work_conserving_eq_work_conserving_count in WORK. + unfold valid_sporadic_job, valid_realtime_job, + enforces_JLDP_policy, completed_jobs_dont_execute, + sporadic_task_model, is_valid_sporadic_task, + jobs_of_same_task_dont_execute_in_parallel, + sequential_jobs in *. + apply/eqP; rewrite eqn_leq; apply/andP; split. + { + apply leq_trans with (n := count (fun x => task_is_scheduled job_task sched x t) ts); + first by apply sub_count; first by red; move => x /andP [SCHED _]. + unfold task_is_scheduled. + apply count_exists; first by destruct ts. + { + intros cpu x1 x2 SCHED1 SCHED2. + destruct (sched cpu t); last by done. + move: SCHED1 SCHED2 => /eqP SCHED1 /eqP SCHED2. + by rewrite -SCHED1 -SCHED2. + } + } + { + rewrite -(WORK j t) // -count_predT. + apply leq_trans with (n := count (fun j: JobIn arr_seq => scheduled_task_other_than tsk (job_task j)) (jobs_scheduled_at sched t)); + last first. + { + rewrite -count_map. + apply count_sub_uniqr; + last by red; move => tsk' /mapP [j' _ JOBtsk']; subst; apply FROMTS. + rewrite map_inj_in_uniq; first by apply scheduled_jobs_uniq. + red; intros j1 j2 SCHED1 SCHED2 SAMEtsk. + rewrite 2!mem_scheduled_jobs_eq_scheduled in SCHED1 SCHED2. + apply scheduled_implies_pending with (job_cost0 := job_cost) + (job_jitter0 := job_jitter) in SCHED1; try (by done). + apply scheduled_implies_pending with (job_cost0 := job_cost) + (job_jitter0 := job_jitter) in SCHED2; try (by done). + by apply UNIQ. + } + { + apply sub_in_count; intros j' SCHED' _. + rewrite mem_scheduled_jobs_eq_scheduled in SCHED'. + unfold scheduled_task_other_than; apply/andP; split. + { + move: SCHED' => /existsP [cpu /eqP SCHED']. + by apply/existsP; exists cpu; rewrite /task_scheduled_on SCHED' eq_refl. + } + { + apply/eqP; red; intro SAMEtsk; symmetry in SAMEtsk. + move: BACK => /andP [PENDING NOTSCHED]. + generalize SCHED'; intro PENDING'. + apply scheduled_implies_pending with (job_cost0 := job_cost) + (job_jitter0 := job_jitter) in PENDING'; try (by done). + exploit (UNIQ j j' PENDING PENDING'); [by rewrite -SAMEtsk | intro EQjob; subst]. + by rewrite SCHED' in NOTSCHED. + } + } + } + Qed. + + End NoMultipleJobs. + + (* In this section we also prove the absence of multiple jobs of the same + task when constrained deadlines are assumed, but in the specific case + of fixed-priority scheduling. *) + Section NoMultipleJobsFP. (* Assume any work-conserving priority-based scheduler. *) Variable higher_eq_priority: FP_policy sporadic_task. @@ -42,12 +212,10 @@ Module PlatformFP. Hypothesis H_enforces_JLDP_policy: enforces_FP_policy job_cost job_task job_jitter sched higher_eq_priority. - (* Consider task set ts. *) + (* Consider any task set ts. *) Variable ts: taskset_of sporadic_task. - (* Assume the task set has no duplicates, ... *) - Hypothesis H_ts_is_a_set: uniq ts. - (* ... and all jobs come from the taskset. *) + (* Assume that all jobs come from the taskset. *) Hypothesis H_all_jobs_from_taskset: forall (j: JobIn arr_seq), job_task j \in ts. @@ -77,13 +245,14 @@ Module PlatformFP. Hypothesis H_j_backlogged: backlogged job_cost job_jitter sched j t. Hypothesis H_t_before_period: t < job_arrival j + task_period tsk. - Let can_interfere_with_tsk := fp_can_interfere_with higher_eq_priority tsk. + (* Recall the definition of a higher-priority task (with respect to tsk). *) + Let is_hp_task := higher_priority_task higher_eq_priority tsk. (* Assume that any jobs of higher-priority tasks complete by their period. *) Hypothesis H_all_previous_jobs_completed : forall (j_other: JobIn arr_seq) tsk_other, job_task j_other = tsk_other -> - can_interfere_with_tsk tsk_other -> + is_hp_task tsk_other -> completed job_cost sched j_other (job_arrival j_other + task_period tsk_other). (* Assume that any jobs of tsk prior to j complete by their period. *) @@ -93,9 +262,9 @@ Module PlatformFP. job_arrival j0 < job_arrival j -> completed job_cost sched j0 (job_arrival j0 + task_period tsk). - Definition scheduled_task_with_higher_eq_priority (tsk tsk_other: sporadic_task) := + Definition scheduled_task_with_higher_eq_priority (tsk_other: sporadic_task) := task_is_scheduled job_task sched tsk_other t && - can_interfere_with_tsk tsk_other. + is_hp_task tsk_other. (* Then, there can be at most one pending job of higher-priority tasks at time t. *) Lemma platform_fp_no_multiple_jobs_of_interfering_tasks : @@ -103,7 +272,7 @@ Module PlatformFP. pending job_cost job_jitter sched j1 t -> pending job_cost job_jitter sched j2 t -> job_task j1 = job_task j2 -> - can_interfere_with_tsk (job_task j1) -> + is_hp_task (job_task j1) -> j1 = j2. Proof. unfold sporadic_task_model in *. @@ -183,7 +352,7 @@ Module PlatformFP. (* Therefore, all processors are busy with tasks other than tsk. *) Lemma platform_fp_cpus_busy_with_interfering_tasks : - count (scheduled_task_with_higher_eq_priority tsk) ts = num_cpus. + count scheduled_task_with_higher_eq_priority ts = num_cpus. Proof. have UNIQ := platform_fp_no_multiple_jobs_of_interfering_tasks. have UNIQ' := platform_fp_no_multiple_jobs_of_tsk. @@ -203,17 +372,16 @@ Module PlatformFP. apply work_conserving_eq_work_conserving_count in WORK. unfold valid_sporadic_job, valid_realtime_job, enforces_FP_policy, enforces_JLDP_policy, FP_to_JLDP, - task_precedence_constraints, completed_jobs_dont_execute, + completed_jobs_dont_execute, sequential_jobs, sporadic_task_model, is_valid_sporadic_task, jobs_of_same_task_dont_execute_in_parallel, - sequential_jobs, - can_interfere_with_tsk in *. + is_hp_task in *. apply/eqP; rewrite eqn_leq; apply/andP; split. { apply leq_trans with (n := count (fun x => task_is_scheduled job_task sched x t) ts); first by apply sub_count; red; move => x /andP [SCHED _]. unfold task_is_scheduled. - apply count_exists; first by done. + apply count_exists; first by destruct ts. { intros cpu x1 x2 SCHED1 SCHED2. destruct (sched cpu t); last by done. @@ -224,13 +392,13 @@ Module PlatformFP. { rewrite -(WORK j t) // -count_predT. apply leq_trans with (n := count (fun j: JobIn arr_seq => - scheduled_task_with_higher_eq_priority tsk (job_task j)) (jobs_scheduled_at sched t)); + scheduled_task_with_higher_eq_priority (job_task j)) (jobs_scheduled_at sched t)); last first. { rewrite -count_map. - apply leq_trans with (n := count predT [seq x <- (map (fun (j: JobIn arr_seq) => job_task j) (jobs_scheduled_at sched t)) | scheduled_task_with_higher_eq_priority tsk x]); + apply leq_trans with (n := count predT [seq x <- (map (fun (j: JobIn arr_seq) => job_task j) (jobs_scheduled_at sched t)) | scheduled_task_with_higher_eq_priority x]); first by rewrite count_filter; apply sub_count; red; ins. - apply leq_trans with (n := count predT [seq x <- ts | scheduled_task_with_higher_eq_priority tsk x]); + apply leq_trans with (n := count predT [seq x <- ts | scheduled_task_with_higher_eq_priority x]); last by rewrite count_predT size_filter. apply count_sub_uniqr; last first. @@ -274,9 +442,9 @@ Module PlatformFP. } } Qed. - - End JobInvariantAsTaskInvariant. - + + End NoMultipleJobsFP. + End Lemmas. - -End PlatformFP. \ No newline at end of file + +End ConstrainedDeadlines. \ No newline at end of file diff --git a/model/jitter/interference.v b/model/jitter/interference.v index 63dca9f75da1e18564d34a73523a09f0a3274e4b..f6ee8eed753bbd64ce44462c9a5e6388345a13e6 100644 --- a/model/jitter/interference.v +++ b/model/jitter/interference.v @@ -36,22 +36,24 @@ Module Interference. Let job_is_backlogged (t: time) := backlogged job_cost job_jitter sched j t. + (* First, we define total interference. *) Section TotalInterference. - (* First, we define the total interference incurred by job j during [t1, t2) - as the cumulative time in which j is backlogged in this interval. *) + (* The total interference incurred by job j during [t1, t2) is the + cumulative time in which j is backlogged in this interval. *) Definition total_interference (t1 t2: time) := \sum_(t1 <= t < t2) job_is_backlogged t. End TotalInterference. + (* Next, we define job interference. *) Section JobInterference. (* Let job_other be a job that interferes with j. *) Variable job_other: JobIn arr_seq. - (* We define the total interference caused by job_other during [t1, t2) as the - cumulative service received by job_other while j is backlogged. *) + (* The interference caused by job_other during [t1, t2) is the cumulative + time in which j is backlogged while job_other is scheduled. *) Definition job_interference (t1 t2: time) := \sum_(t1 <= t < t2) \sum_(cpu < num_cpus) @@ -60,13 +62,14 @@ Module Interference. End JobInterference. + (* Next, we define task interference. *) Section TaskInterference. (* In order to define task interference, consider any interfering task tsk_other. *) Variable tsk_other: sporadic_task. - (* We define the total interference caused by tsk during [t1, t2) as - the cumulative service received by tsk while j is backlogged. *) + (* The interference caused by tsk during [t1, t2) is the cumulative time + in which j is backlogged while tsk is scheduled. *) Definition task_interference (t1 t2: time) := \sum_(t1 <= t < t2) \sum_(cpu < num_cpus) @@ -75,6 +78,8 @@ Module Interference. End TaskInterference. + (* Next, we define an approximation of the total interference based on + each per-task interference. *) Section TaskInterferenceJobList. Variable tsk_other: sporadic_task. @@ -85,6 +90,7 @@ Module Interference. End TaskInterferenceJobList. + (* Now we prove some basic lemmas about interference. *) Section BasicLemmas. (* Interference cannot be larger than the considered time window. *) @@ -98,6 +104,7 @@ Module Interference. by rewrite big_const_nat iter_addn mul1n addn0 leqnn. Qed. + (* Job interference is bounded by the service of the interfering job. *) Lemma job_interference_le_service : forall j_other t1 t2, job_interference j_other t1 t2 <= service_during sched j_other t1 t2. @@ -110,6 +117,7 @@ Module Interference. by destruct (scheduled_on sched j_other cpu t). Qed. + (* Task interference is bounded by the workload of the interfering task. *) Lemma task_interference_le_workload : forall tsk t1 t2, task_interference tsk t1 t2 <= workload job_task sched tsk t1 t2. @@ -124,6 +132,7 @@ Module Interference. End BasicLemmas. + (* Now we prove some bounds on interference for sequential jobs. *) Section InterferenceSequentialJobs. (* If jobs are sequential, ... *) @@ -162,7 +171,8 @@ Module Interference. End InterferenceSequentialJobs. - (* The sequential per-job interference bounds the actual interference. *) + (* Next, we show that the cumulative per-task interference bounds the total + interference. *) Section BoundUsingPerJobInterference. Lemma interference_le_interference_joblist : diff --git a/model/jitter/platform.v b/model/jitter/platform.v index ad283f800cb28ee2bf9cabd7a450bbe7046675c3..cb916f90809d4cb0f54a88461af86936c0bf55bc 100644 --- a/model/jitter/platform.v +++ b/model/jitter/platform.v @@ -7,7 +7,7 @@ Module Platform. Import Job SporadicTaskset ScheduleOfSporadicTaskWithJitter SporadicTaskset SporadicTaskArrival Interference Priority. - Section SchedulingInvariants. + Section Properties. Context {sporadic_task: eqType}. Variable task_cost: sporadic_task -> time. @@ -27,11 +27,11 @@ Module Platform. Context {num_cpus: nat}. Variable sched: schedule num_cpus arr_seq. - Section WorkConserving. + Section Execution. (* A scheduler is work-conserving iff when a job j is backlogged, all processors are busy with other jobs. - NOTE: backlogged means that jitter has already passed. *) + Note: backlogged means that jitter has already passed. *) Definition work_conserving := forall j t, backlogged job_cost job_jitter sched j t -> @@ -45,7 +45,7 @@ Module Platform. backlogged job_cost job_jitter sched j t -> size (jobs_scheduled_at sched t) = num_cpus. - End WorkConserving. + End Execution. Section JLDP. @@ -159,174 +159,8 @@ Module Platform. End EquivalentDefinitions. - Section JobInvariantAsTaskInvariant. - - (* Assume any work-conserving priority-based scheduler. *) - Variable higher_eq_priority: JLDP_policy arr_seq. - Hypothesis H_work_conserving: work_conserving. - Hypothesis H_enforces_JLDP_policy: enforces_JLDP_policy higher_eq_priority. - - (* Consider task set ts. *) - Variable ts: taskset_of sporadic_task. - - (* Assume the task set has no duplicates, ... *) - Hypothesis H_ts_is_a_set: uniq ts. - (* ... and all jobs come from the taskset. *) - Hypothesis H_all_jobs_from_taskset: - forall (j: JobIn arr_seq), job_task j \in ts. - - (* Suppose that jobs are sequential, ...*) - Hypothesis H_sequential_jobs: sequential_jobs sched. - (* ... jobs only execute after the jitter, ... *) - Hypothesis H_jobs_execute_after_jitter: - jobs_execute_after_jitter job_jitter sched. - (* ... and jobs do not execute after completion. *) - Hypothesis H_completed_jobs_dont_execute: - completed_jobs_dont_execute job_cost sched. - - (* Assume that the schedule satisfies the sporadic task model ...*) - Hypothesis H_sporadic_tasks: - sporadic_task_model task_period arr_seq job_task. - - (* Consider a valid task tsk, ...*) - Variable tsk: sporadic_task. - Hypothesis H_valid_task: is_valid_sporadic_task task_cost task_period task_deadline tsk. - - (*... whose job j ... *) - Variable j: JobIn arr_seq. - Variable H_job_of_tsk: job_task j = tsk. - - (*... is backlogged at time t. *) - Variable t: time. - Hypothesis H_j_backlogged: backlogged job_cost job_jitter sched j t. - - (* Assume that any previous jobs of tsk have completed by the period. *) - Hypothesis H_all_previous_jobs_completed : - forall (j_other: JobIn arr_seq) tsk_other, - job_task j_other = tsk_other -> - job_arrival j_other + task_period tsk_other <= t -> - completed job_cost sched j_other (job_arrival j_other + task_period (job_task j_other)). - - Let scheduled_task_other_than (tsk tsk_other: sporadic_task) := - task_is_scheduled job_task sched tsk_other t && (tsk_other != tsk). - - (* Then, there can be at most one pending job of each task at time t. *) - Lemma platform_at_most_one_pending_job_of_each_task : - forall j1 j2, - pending job_cost job_jitter sched j1 t -> - pending job_cost job_jitter sched j2 t -> - job_task j1 = job_task j2 -> - j1 = j2. - Proof. - rename H_sporadic_tasks into SPO, H_all_previous_jobs_completed into PREV. - intros j1 j2 PENDING1 PENDING2 SAMEtsk. - apply/eqP; rewrite -[_ == _]negbK; apply/negP; red; move => /eqP DIFF. - move: PENDING1 PENDING2 => /andP [ARRIVED1 /negP NOTCOMP1] /andP [ARRIVED2 /negP NOTCOMP2]. - unfold jitter_has_passed, actual_arrival in *. - destruct (leqP (job_arrival j1) (job_arrival j2)) as [BEFORE1 | BEFORE2]. - { - specialize (SPO j1 j2 DIFF SAMEtsk BEFORE1). - assert (LEt: job_arrival j1 + task_period (job_task j1) <= t). - { - apply leq_trans with (n := job_arrival j2); first by done. - by apply leq_trans with (n := job_arrival j2 + job_jitter j2); first by apply leq_addr. - } - exploit (PREV j1 (job_task j1)); try (by done). - intros COMP1; apply NOTCOMP1. - by apply completion_monotonic with (t0 := job_arrival j1 + task_period (job_task j1)). - } - { - apply ltnW in BEFORE2. - exploit (SPO j2 j1); [by red; ins; subst | by rewrite SAMEtsk | by done | intro SPO']. - assert (LEt: job_arrival j2 + task_period (job_task j2) <= t). - { - apply leq_trans with (n := job_arrival j1); first by done. - by apply leq_trans with (n := job_arrival j1 + job_jitter j1); first by apply leq_addr. - } - exploit (PREV j2 (job_task j2)); try (by done). - intros COMP2; apply NOTCOMP2. - by apply completion_monotonic with (t0 := job_arrival j2 + task_period (job_task j2)). - } - Qed. - - (* Therefore, all processors are busy with tasks other than tsk. *) - Lemma platform_cpus_busy_with_interfering_tasks : - count (scheduled_task_other_than tsk) ts = num_cpus. - Proof. - have UNIQ := platform_at_most_one_pending_job_of_each_task. - rename H_all_jobs_from_taskset into FROMTS, - H_sequential_jobs into SEQUENTIAL, - H_work_conserving into WORK, - H_enforces_JLDP_policy into PRIO, - H_j_backlogged into BACK, - H_job_of_tsk into JOBtsk, - H_valid_job_parameters into JOBPARAMS, - H_valid_task into TASKPARAMS, - H_all_previous_jobs_completed into PREV, - H_completed_jobs_dont_execute into COMP, - H_jobs_execute_after_jitter into JITTER. - apply work_conserving_eq_work_conserving_count in WORK. - unfold valid_sporadic_job, valid_realtime_job, - enforces_JLDP_policy, - task_precedence_constraints, completed_jobs_dont_execute, - sporadic_task_model, is_valid_sporadic_task, - jobs_of_same_task_dont_execute_in_parallel, - sequential_jobs in *. - apply/eqP; rewrite eqn_leq; apply/andP; split. - { - apply leq_trans with (n := count (fun x => task_is_scheduled job_task sched x t) ts); - first by apply sub_count; first by red; move => x /andP [SCHED _]. - unfold task_is_scheduled. - apply count_exists; first by done. - { - intros cpu x1 x2 SCHED1 SCHED2. - destruct (sched cpu t); last by done. - move: SCHED1 SCHED2 => /eqP SCHED1 /eqP SCHED2. - by rewrite -SCHED1 -SCHED2. - } - } - { - rewrite -(WORK j t) // -count_predT. - apply leq_trans with (n := count (fun j: JobIn arr_seq => scheduled_task_other_than tsk (job_task j)) (jobs_scheduled_at sched t)); - last first. - { - rewrite -count_map. - apply count_sub_uniqr; - last by red; move => tsk' /mapP [j' _ JOBtsk']; subst; apply FROMTS. - rewrite map_inj_in_uniq; first by apply scheduled_jobs_uniq. - red; intros j1 j2 SCHED1 SCHED2 SAMEtsk. - rewrite 2!mem_scheduled_jobs_eq_scheduled in SCHED1 SCHED2. - apply scheduled_implies_pending with (job_cost0 := job_cost) - (job_jitter0 := job_jitter) in SCHED1; try (by done). - apply scheduled_implies_pending with (job_cost0 := job_cost) - (job_jitter0 := job_jitter) in SCHED2; try (by done). - by apply UNIQ. - } - { - apply sub_in_count; intros j' SCHED' _. - rewrite mem_scheduled_jobs_eq_scheduled in SCHED'. - unfold scheduled_task_other_than; apply/andP; split. - { - move: SCHED' => /existsP [cpu /eqP SCHED']. - by apply/existsP; exists cpu; rewrite /task_scheduled_on SCHED' eq_refl. - } - { - apply/eqP; red; intro SAMEtsk; symmetry in SAMEtsk. - move: BACK => /andP [PENDING NOTSCHED]. - generalize SCHED'; intro PENDING'. - apply scheduled_implies_pending with (job_cost0 := job_cost) - (job_jitter0 := job_jitter) in PENDING'; try (by done). - exploit (UNIQ j j' PENDING PENDING'); [by rewrite -SAMEtsk | intro EQjob; subst]. - by rewrite SCHED' in NOTSCHED. - } - } - } - Qed. - - End JobInvariantAsTaskInvariant. - End Lemmas. - End SchedulingInvariants. + End Properties. End Platform. \ No newline at end of file diff --git a/model/jitter/schedule.v b/model/jitter/schedule.v index a78068ba480014d8ebe99f9019d99bc54eb23d90..fdb9016714391c3cb6d65981d98d4cb1d0c78139 100644 --- a/model/jitter/schedule.v +++ b/model/jitter/schedule.v @@ -195,18 +195,11 @@ Module ScheduleOfSporadicTaskWithJitter. Context {num_cpus: nat}. Variable sched: schedule num_cpus arr_seq. - (* Next we define intra-task parallelism, ... *) + (* Next we define intra-task parallelism. *) Definition jobs_of_same_task_dont_execute_in_parallel := forall (j j': JobIn arr_seq) t, job_task j = job_task j' -> scheduled sched j t -> scheduled sched j' t -> j = j'. - - (* ... and task precedence constraints. *) - Definition task_precedence_constraints := - forall (j j': JobIn arr_seq) t, - job_task j = job_task j' -> - job_arrival j < job_arrival j' -> - pending job_cost job_jitter sched j t -> ~~ scheduled sched j' t. End ScheduleProperties. diff --git a/util/exists.v b/util/exists.v index 7dd564da3724ad6cd7a3d5d3c9ae8fdcdf1a5857..1376b4dfb1714ea766e572bc199a2d854c8861f7 100644 --- a/util/exists.v +++ b/util/exists.v @@ -51,3 +51,22 @@ Section OrdExists. End OrdExists. +(*Section Computation. + + Variable m: nat. + Hypothesis H_m_positive: m > 0. + + Variable P: 'I_m -> bool. + + Program Fixpoint compute_exists n (Q: n < m) := + match n with + | 0 => P (@Ordinal m 0 _) + | S n => P (@Ordinal m (S n) _) || compute_exists n _ + end. + + Program Definition compute_exists_ordinal := compute_exists (m.-1) _. + Next Obligation. + by rewrite -(ltn_add2r 1) 2!addn1 prednK //. + Qed. + +End Computation.*) \ No newline at end of file diff --git a/util/seqset.v b/util/seqset.v index fb734c841989a720f968347ea170812c64e67ff8..af3a5ffe58cdce34d40a023835ec1decf499d8a3 100644 --- a/util/seqset.v +++ b/util/seqset.v @@ -33,6 +33,11 @@ Section Lemmas. by destruct s. Qed. + Lemma set_mem : forall x, (x \in s) = (x \in _set_seq s). + Proof. + by intros x; destruct s. + Qed. + End Lemmas. Section LemmasFinType. diff --git a/util/sorting.v b/util/sorting.v index e28d8003bcaff85a1ac8e26216945d627003a734..30d2f7d89c6190d9fbd9721d3521de5bda5f7413 100644 --- a/util/sorting.v +++ b/util/sorting.v @@ -1,4 +1,4 @@ -Require Import rt.util.tactics rt.util.induction. +Require Import rt.util.tactics rt.util.induction rt.util.list. From mathcomp Require Import ssreflect ssrbool eqtype ssrnat seq fintype bigop path. (* Lemmas about sorted lists. *) @@ -72,9 +72,10 @@ Section Sorting. } Qed. - Lemma sorted_uniq_rel_implies_le_idx : + Lemma sorted_rel_implies_le_idx : forall {T: eqType} (leT: rel T) (s: seq T) x0 i1 i2, - irreflexive leT -> + uniq s -> + antisymmetric_over_list leT s -> transitive leT -> sorted leT s -> leT (nth x0 s i1) (nth x0 s i2) -> @@ -82,7 +83,7 @@ Section Sorting. i2 < size s -> i1 <= i2. Proof. - intros T leT s x0 i1 i2 IRR TRANS SORT REL SIZE1 SIZE2. + intros T leT s x0 i1 i2 UNIQ ANTI TRANS SORT REL SIZE1 SIZE2. generalize dependent i2. induction i1; first by done. { @@ -92,12 +93,12 @@ Section Sorting. rewrite ltn_neqAle; apply/andP; split. { apply/eqP; red; intro BUG; subst. - assert (REL': leT (nth x0 s i2) (nth x0 s i2)). - { - rewrite /transitive (TRANS (nth x0 s i2.+1)) //. + assert (REL': leT (nth x0 s i2) (nth x0 s i2.+1)). by apply sorted_lt_idx_implies_rel; rewrite // ltnSn. - } - by rewrite /irreflexive IRR in REL'. + rewrite /antisymmetric_over_list in ANTI. + exploit (ANTI (nth x0 s i2) (nth x0 s i2.+1)); rewrite ?mem_nth //. + move => /eqP EQ; rewrite nth_uniq in EQ; try (by done). + by rewrite -[_ == _]negbK in EQ; move: EQ => /negP EQ; apply EQ; apply/eqP. } { apply IHi1; last by done.