diff options
| author | Glenn Elliott <gelliott@cs.unc.edu> | 2012-08-05 20:22:39 -0400 |
|---|---|---|
| committer | Glenn Elliott <gelliott@cs.unc.edu> | 2012-08-05 20:22:39 -0400 |
| commit | 7952ecbc73db87db30a4ae2b337d65ccbe696e83 (patch) | |
| tree | 66e47fe49d8937ed958cebf51bd21be7c0f3a881 | |
| parent | b53c479a0f44b8990ce106622412a3bf54809944 (diff) | |
Implement hash-based EDF tie-breaking.wip-better-break
Added kernel config option to perform deadline tie-breaking
by comparing the hashes of (pid, job#) pairs. Hashing ensures
that the same task is not repeatedly victimized in overload
conditions in systems where deadline ties are common (say,
several synchronous media players).
| -rw-r--r-- | litmus/Kconfig | 39 | ||||
| -rw-r--r-- | litmus/edf_common.c | 45 |
2 files changed, 82 insertions, 2 deletions
diff --git a/litmus/Kconfig b/litmus/Kconfig index 68459d4dca41..7de5b81d5d0b 100644 --- a/litmus/Kconfig +++ b/litmus/Kconfig | |||
| @@ -79,6 +79,45 @@ config SCHED_CPU_AFFINITY | |||
| 79 | 79 | ||
| 80 | Say Yes if unsure. | 80 | Say Yes if unsure. |
| 81 | 81 | ||
| 82 | choice | ||
| 83 | prompt "EDF Tie-Break Behavior" | ||
| 84 | default EDF_HASH_TIE_BREAK | ||
| 85 | help | ||
| 86 | Allows the configuration of tie-breaking behavior when the deadlines | ||
| 87 | of two EDF-scheduled tasks are equal. | ||
| 88 | |||
| 89 | config EDF_HASH_TIE_BREAK | ||
| 90 | bool "Hash-based Tie Breaks" | ||
| 91 | help | ||
| 92 | Break ties between two jobs, A and B, with equal deadlines by using a | ||
| 93 | uniform hash; i.e.: hash(A.pid, A.job_num) < hash(B.pid, B.job_num). | ||
| 94 | |||
| 95 | This is particularly useful in overloaded systems that process | ||
| 96 | synchronous task sets of tasks with equal relative deadlines (ex. | ||
| 97 | media players). Hashing reduces the likelihood that the same task | ||
| 98 | loses repeated tie-break rounds, always suffering worst-case | ||
| 99 | response time. | ||
| 100 | |||
| 101 | Warning: It may be harder to reproduce bugs since PIDs change each | ||
| 102 | time a task is run. | ||
| 103 | |||
| 104 | NOTES: | ||
| 105 | * This feature can increase response-time jitter, but should improve | ||
| 106 | average case response time. | ||
| 107 | * Tie-break falls back to PIDs in unlikely event two hashes collide. | ||
| 108 | |||
| 109 | config EDF_PID_TIE_BREAK | ||
| 110 | bool "PID-based Tie Breaks" | ||
| 111 | help | ||
| 112 | Break ties based upon OS-assigned process IDs. Use this option if | ||
| 113 | required by algorithm's real-time analysis or per-task response-time | ||
| 114 | jitter must be minimized in overload conditions. | ||
| 115 | |||
| 116 | NOTES: | ||
| 117 | * This tie-breaking method was default in Litmus 2012.2 and before. | ||
| 118 | |||
| 119 | endchoice | ||
| 120 | |||
| 82 | endmenu | 121 | endmenu |
| 83 | 122 | ||
| 84 | menu "Tracing" | 123 | menu "Tracing" |
diff --git a/litmus/edf_common.c b/litmus/edf_common.c index 9b44dc2d8d1e..a603d06afe15 100644 --- a/litmus/edf_common.c +++ b/litmus/edf_common.c | |||
| @@ -14,6 +14,35 @@ | |||
| 14 | 14 | ||
| 15 | #include <litmus/edf_common.h> | 15 | #include <litmus/edf_common.h> |
| 16 | 16 | ||
| 17 | #include <linux/hash.h> | ||
| 18 | |||
| 19 | #ifdef CONFIG_EDF_HASH_TIE_BREAK | ||
| 20 | static inline long edf_hash(struct task_struct *t) | ||
| 21 | { | ||
| 22 | long key; | ||
| 23 | /* pid is 32 bits, so normally we would shove that into the | ||
| 24 | * upper 32-bits and and put the job number in the bottom | ||
| 25 | * and hash the 64-bit number with hash_64(). Sadly, | ||
| 26 | * in testing, hash_64() doesn't distribute keys were the | ||
| 27 | * upper bits are close together (as would be the case with | ||
| 28 | * pids) and job numbers are equal (as would be the case with | ||
| 29 | * synchronous task sets with all relative deadlines equal). | ||
| 30 | * | ||
| 31 | * A 2006 Linux patch proposed the following solution | ||
| 32 | * (but for some reason it wasn't accepted...). | ||
| 33 | * | ||
| 34 | * At least this workaround works for 32-bit systems as well. | ||
| 35 | */ | ||
| 36 | |||
| 37 | /* This would be nice, but hash_64() has problems. | ||
| 38 | key = ((long)t->pid << 32) | ((long)tsk_rt(t)->job_params.job_no & 0xFFFFFFFF); | ||
| 39 | return hash_64(key, 0); | ||
| 40 | */ | ||
| 41 | |||
| 42 | return hash_32(hash_32((u32)tsk_rt(t)->job_params.job_no, 32) ^ t->pid, 32); | ||
| 43 | } | ||
| 44 | #endif | ||
| 45 | |||
| 17 | /* edf_higher_prio - returns true if first has a higher EDF priority | 46 | /* edf_higher_prio - returns true if first has a higher EDF priority |
| 18 | * than second. Deadline ties are broken by PID. | 47 | * than second. Deadline ties are broken by PID. |
| 19 | * | 48 | * |
| @@ -25,6 +54,10 @@ int edf_higher_prio(struct task_struct* first, | |||
| 25 | struct task_struct *first_task = first; | 54 | struct task_struct *first_task = first; |
| 26 | struct task_struct *second_task = second; | 55 | struct task_struct *second_task = second; |
| 27 | 56 | ||
| 57 | #ifdef CONFIG_EDF_HASH_TIE_BREAK | ||
| 58 | long fhash, shash; | ||
| 59 | #endif | ||
| 60 | |||
| 28 | /* There is no point in comparing a task to itself. */ | 61 | /* There is no point in comparing a task to itself. */ |
| 29 | if (first && first == second) { | 62 | if (first && first == second) { |
| 30 | TRACE_TASK(first, | 63 | TRACE_TASK(first, |
| @@ -63,7 +96,6 @@ int edf_higher_prio(struct task_struct* first, | |||
| 63 | 96 | ||
| 64 | #endif | 97 | #endif |
| 65 | 98 | ||
| 66 | |||
| 67 | return !is_realtime(second_task) || | 99 | return !is_realtime(second_task) || |
| 68 | 100 | ||
| 69 | /* is the deadline of the first task earlier? | 101 | /* is the deadline of the first task earlier? |
| @@ -75,13 +107,22 @@ int edf_higher_prio(struct task_struct* first, | |||
| 75 | * Then break by PID. | 107 | * Then break by PID. |
| 76 | */ | 108 | */ |
| 77 | (get_deadline(first_task) == get_deadline(second_task) && | 109 | (get_deadline(first_task) == get_deadline(second_task) && |
| 110 | #ifdef CONFIG_EDF_HASH_TIE_BREAK | ||
| 111 | /* assignment within less-than compare is intentional */ | ||
| 112 | ((fhash = edf_hash(first_task)) < (shash = edf_hash(second_task)) || | ||
| 113 | (fhash == shash && | ||
| 114 | #endif | ||
| 78 | (first_task->pid < second_task->pid || | 115 | (first_task->pid < second_task->pid || |
| 79 | 116 | ||
| 80 | /* If the PIDs are the same then the task with the inherited | 117 | /* If the PIDs are the same then the task with the inherited |
| 81 | * priority wins. | 118 | * priority wins. |
| 82 | */ | 119 | */ |
| 83 | (first_task->pid == second_task->pid && | 120 | (first_task->pid == second_task->pid && |
| 84 | !second->rt_param.inh_task))); | 121 | !second->rt_param.inh_task)) |
| 122 | #ifdef CONFIG_EDF_HASH_TIE_BREAK | ||
| 123 | )) | ||
| 124 | #endif | ||
| 125 | ); | ||
| 85 | } | 126 | } |
| 86 | 127 | ||
| 87 | int edf_ready_order(struct bheap_node* a, struct bheap_node* b) | 128 | int edf_ready_order(struct bheap_node* a, struct bheap_node* b) |
