Library rt.analysis.jitter.bertogna_fp_theory

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.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.

Module ResponseTimeAnalysisFP.

  Export JobWithJitter SporadicTaskset ScheduleOfSporadicTaskWithJitter Workload Interference
         Platform PlatformFP Schedulability ResponseTime Priority SporadicTaskArrival
         WorkloadBoundJitter Interference InterferenceBoundFP.

  Section ResponseTimeBound.

    Context {sporadic_task: eqType}.
    Variable task_cost: sporadic_task time.
    Variable task_period: sporadic_task time.
    Variable task_deadline: sporadic_task time.
    Variable task_jitter: sporadic_task time.

    Context {Job: eqType}.
    Variable job_cost: Job time.
    Variable job_deadline: Job time.
    Variable job_task: Job sporadic_task.
    Variable job_jitter: Job time.

    (* 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:
       (j: JobIn arr_seq),
        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...*)
    Variable num_cpus: nat.
    Variable sched: schedule num_cpus arr_seq.

    (* do not execute before their arrival times nor longer
       than their execution costs. *)

    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.
    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:
       (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:
       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.

    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.

    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.

    (* ...and that all values in the pairs contain valid response-time bounds *)
    Hypothesis H_response_time_of_interfering_tasks_is_known:
       hp_tsk R,
        (hp_tsk, R) \in hp_bounds
        response_time_bounded_by hp_tsk (task_jitter hp_tsk + R).

    (* Assume that the response-time bounds are larger than task costs. *)
    Hypothesis H_response_time_bounds_ge_cost:
       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. *)

    Hypothesis H_interfering_tasks_miss_no_deadlines:
       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.

    (* 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 +
            (total_interference_bound_fp task_cost task_period task_jitter
                                         tsk hp_bounds R higher_eq_priority)

    (* ... 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. *)
    Section Lemmas.

      (* Consider any job j of tsk. *)
      Variable j: JobIn arr_seq.
      Hypothesis H_job_of_tsk: job_task j = tsk.

      (* Let t1 be the first point in time where j can actually be scheduled. *)
      Let t1 := job_arrival j + job_jitter j.

      (* 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 (t1 + R).
      Hypothesis H_previous_jobs_of_tsk_completed :
         (j0: JobIn arr_seq),
          job_task j0 = tsk
          job_arrival j0 < job_arrival j
          completed job_cost sched j0 (job_arrival j0 + task_jitter tsk + 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 job_jitter sched j tsk_other t1 (t1 + R).

      (* 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].

      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 hp_bounds.

        (* Note that tsk_other is in task set ts ...*)
        Lemma bertogna_fp_tsk_other_in_ts: tsk_other \in ts.

        (*... and interferes with task tsk. *)
        Lemma bertogna_fp_tsk_other_interferes: can_interfere_with_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.

      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 >= R - e_k + 1. *)

        Lemma bertogna_fp_too_much_interference : X R - task_cost tsk + 1.

        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:
            t1 t < t1 + R
            backlogged job_cost job_jitter sched j t
            count (scheduled_task_other_than_tsk t) ts = num_cpus.

        (* 2) Prove that during the scheduling window of j, any job that is scheduled while j is
           backlogged comes from a different task. *)

        Lemma bertogna_fp_interference_by_different_tasks :
           t j_other,
            t1 t < t1 + R
            backlogged job_cost job_jitter sched j t
            scheduled sched j_other t
            job_task j_other != tsk.

        (* 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 (cardGE delta) be the number of interfering tasks whose interference
           is larger than delta. *)

        Let cardGE (delta: time) := count (fun ix i delta) ts_interf.

        (* 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 :
            0 < cardGE delta < num_cpus
            \sum_(i <- ts_interf | x i < delta) x i delta × (num_cpus - cardGE delta).

        (* 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. *)

        Lemma bertogna_fp_minimum_exceeds_interference :
            \sum_(tsk_k <- ts_interf) x tsk_k delta × num_cpus
               \sum_(tsk_k <- ts_interf) minn (x tsk_k) delta
               delta × num_cpus.

        (* 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). *)

        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.

        (* 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). *)

        Lemma bertogna_fp_exists_task_that_exceeds_bound :
           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)).

      End DerivingContradiction.

    End Lemmas.

    (* Using the lemmas above, we prove that R' bounds the response time of task tsk. *)
    Theorem bertogna_cirinei_response_time_bound_fp :
      response_time_bounded_by tsk (task_jitter tsk + R).

  End ResponseTimeBound.

End ResponseTimeAnalysisFP.