diff options
| author | Bjoern Brandenburg <bbb@mpi-sws.org> | 2015-08-09 07:18:48 -0400 |
|---|---|---|
| committer | Bjoern Brandenburg <bbb@mpi-sws.org> | 2017-05-26 17:12:28 -0400 |
| commit | 3baa55c19ffb567aa48568fa69dd17ad6f70d31d (patch) | |
| tree | 7e79fd398705929f2db40ba239895cc60762f61f /include/litmus | |
| parent | cbe61859a233702ed8e6723b3b133d1f2ae1ae2c (diff) | |
Add LITMUS^RT core implementation
This patch adds the core of LITMUS^RT:
- library functionality (heaps, rt_domain, prioritization, etc.)
- budget enforcement logic
- job management
- system call backends
- virtual devices (control page, etc.)
- scheduler plugin API (and dummy plugin)
This code compiles, but is not yet integrated with the rest of Linux.
Squashed changes:
LITMUS^RT Core: add get_current_budget() system call
Allow userspace to figure out the used-up and remaining budget
of a task.
Adds deadline field to control page and updates it when setting up jobs for release.
Adds control page deadline offset
ftdev: respect O_NONBLOCK flag in ftdev_read()
Don't block if userspace wants to go on doing something else.
Export job release time and job sequence number in ctrl page
Add alternate complete_job() default implementation
Let jobs sleep like regular Linux tasks by suspending and waking them
with a one-shot timer. Plugins can opt into using this implementation
instead of the classic complete_job() implementation (or custom
implementations).
Fix RCU locking in sys_get_rt_task_param()
sys_get_rt_task_param() is rarely used and apparently attracted some
bitrot.
Free before setting NULL to prevent memory leak
Add hrtimer_start_on() support
This patch replaces the previous implementation of hrtimer_start_on() by
now using smp_call_function_single_async() to arm hrtimers on remote
CPUs.
Expose LITMUS^RT system calls via control page ioctl()
Rationale: make LITMUS^RT ops available in a way that does not create
merge conflicts each time we rebase LITMUS^RT on top of a new kernel
version. This also helps with portability to different architectures,
as we no longer need to patch each architecture's syscall table.
Pick non-zero syscall ID start range
To avoid interfering with Linux's magic reserved IOCTL numbers
Don't preempt before time check in sleep_until_next_release()
Avoid preempting jobs that are about to go to sleep soon anyway.
LITMUS^RT proc: fix wrong memset()
TRACE(): add TRACE_WARN_ON() helper
Useful to replace BUG_ON() and WARN_ON() with a non-fatal
TRACE()-based equivalent.
Add void* plugin_state pointer to task_struct
LITMUS^RT: split task admission into two functions
Plugin interface: add fork_task() callback
LITMUS^RT: Enable plugins to permit RT tasks to fork
one-shot complete_job(): set completed flag
This could race with a SIGSTOP or some other forced suspension, but
we'll let plugins handle this, should they actually care.
FP: add list-based ready queue
LITMUS^RT core: add should_wait_for_stack() callback
Allow plugins to give up when waiting for a stack to become available.
LITMUS^RT core: add next_became_invalid() callback
LITMUS^RT core: add post-migration validation callback
LITMUS^RT core: be more careful when pull-migrating tasks
Close more race windows and give plugins a chance to validate
tasks after they have been migrated.
Add KConfig options for timer latency warnings
Add reservation creation API to plugin interface & syscalls
LITMUS^RT syscall: expose sys_reservation_create() via ioctl()
Add reservation configuration types to rt_param.h
Add basic generic reservation-based scheduling infrastructure
Switch to aligned quanta by default.
For first-time users, aligned quanta is likely what's expected.
LITMUS^RT core: keep track of time of last suspension
This information is needed to insert ST_COMPLETION records for
sporadic tasks.
add fields for clock_nanosleep() support
Need to communicate the intended wake-up time to the plugin wake-up handler.
LITMUS^RT core: add generic handler for sporadic job arrivals
In particular, check if a job arrival is triggered from a
clock_nanosleep() call.
add litmus->task_change_params() callback to plugin interface
Will be used by adaptive C-EDF.
Call litmus->task_change_params() from sys_set_rt_task_param()
Move trace point definition to litmus/litmus.c
If !CONFIG_SCHED_TASK_TRACE, but CONFIG_SCHED_LITMUS_TRACEPOINT, then
we still need to define the tracepoint structures.
This patch should be integrated with the earlier sched_task_trace.c
patches during one of the next major rebasing efforts.
LITMUS^RT scheduling class: mark enqueued task as present
Remove unistd_*.h
rebase fix: update to new hrtimer API
The new API is actually nicer and cleaner.
rebase fix: call lockdep_unpin_lock(&rq->lock, cookie)
The LITMUS^RT scheduling class should also do the LOCKDEP dance.
LITMUS^RT core: break out non-preemptive flag defs
Not every file including litmus.h needs to know this.
LITMUS^RT core: don't include debug_trace.h in litmus.h
Including debug_trace.h introduces the TRACE() macro, which causes
symbol clashes in some (rather obscure) drivers.
LITMUS^RT core: add litmus_preemption_in_progress flags
Used to communicate that a preemption is in progress. Set by the
scheduler; read by the plugins.
LITMUS^RT core: revise is_current_running() macro
Diffstat (limited to 'include/litmus')
28 files changed, 2403 insertions, 53 deletions
diff --git a/include/litmus/affinity.h b/include/litmus/affinity.h new file mode 100644 index 000000000000..4d7c618c8175 --- /dev/null +++ b/include/litmus/affinity.h | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | #ifndef __LITMUS_AFFINITY_H | ||
| 2 | #define __LITMUS_AFFINITY_H | ||
| 3 | |||
| 4 | #include <linux/cpumask.h> | ||
| 5 | |||
| 6 | /* Works like: | ||
| 7 | void get_nearest_available_cpu( | ||
| 8 | cpu_entry_t **nearest, | ||
| 9 | cpu_entry_t *start, | ||
| 10 | cpu_entry_t *entries, | ||
| 11 | int release_master, | ||
| 12 | cpumask_var_t cpus_to_test) | ||
| 13 | |||
| 14 | Set release_master = NO_CPU for no Release Master. | ||
| 15 | |||
| 16 | We use a macro here to exploit the fact that C-EDF and G-EDF | ||
| 17 | have similar structures for their cpu_entry_t structs, even though | ||
| 18 | they do not share a common base-struct. The macro allows us to | ||
| 19 | avoid code duplication. | ||
| 20 | |||
| 21 | */ | ||
| 22 | #define get_nearest_available_cpu(nearest, start, entries, release_master, cpus_to_test) \ | ||
| 23 | { \ | ||
| 24 | (nearest) = NULL; \ | ||
| 25 | if (!(start)->linked && likely((start)->cpu != (release_master))) { \ | ||
| 26 | (nearest) = (start); \ | ||
| 27 | } else { \ | ||
| 28 | int __cpu; \ | ||
| 29 | \ | ||
| 30 | /* FIXME: get rid of the iteration with a bitmask + AND */ \ | ||
| 31 | for_each_cpu(__cpu, cpus_to_test) { \ | ||
| 32 | if (likely(__cpu != release_master)) { \ | ||
| 33 | cpu_entry_t *__entry = &per_cpu((entries), __cpu); \ | ||
| 34 | if (cpus_share_cache((start)->cpu, __entry->cpu) \ | ||
| 35 | && !__entry->linked) { \ | ||
| 36 | (nearest) = __entry; \ | ||
| 37 | break; \ | ||
| 38 | } \ | ||
| 39 | } \ | ||
| 40 | } \ | ||
| 41 | } \ | ||
| 42 | \ | ||
| 43 | if ((nearest)) { \ | ||
| 44 | TRACE("P%d is closest available CPU to P%d\n", \ | ||
| 45 | (nearest)->cpu, (start)->cpu); \ | ||
| 46 | } else { \ | ||
| 47 | TRACE("Could not find an available CPU close to P%d\n", \ | ||
| 48 | (start)->cpu); \ | ||
| 49 | } \ | ||
| 50 | } | ||
| 51 | |||
| 52 | #endif | ||
diff --git a/include/litmus/bheap.h b/include/litmus/bheap.h new file mode 100644 index 000000000000..cf4864a498d8 --- /dev/null +++ b/include/litmus/bheap.h | |||
| @@ -0,0 +1,77 @@ | |||
| 1 | /* bheaps.h -- Binomial Heaps | ||
| 2 | * | ||
| 3 | * (c) 2008, 2009 Bjoern Brandenburg | ||
| 4 | */ | ||
| 5 | |||
| 6 | #ifndef BHEAP_H | ||
| 7 | #define BHEAP_H | ||
| 8 | |||
| 9 | #define NOT_IN_HEAP UINT_MAX | ||
| 10 | |||
| 11 | struct bheap_node { | ||
| 12 | struct bheap_node* parent; | ||
| 13 | struct bheap_node* next; | ||
| 14 | struct bheap_node* child; | ||
| 15 | |||
| 16 | unsigned int degree; | ||
| 17 | void* value; | ||
| 18 | struct bheap_node** ref; | ||
| 19 | }; | ||
| 20 | |||
| 21 | struct bheap { | ||
| 22 | struct bheap_node* head; | ||
| 23 | /* We cache the minimum of the heap. | ||
| 24 | * This speeds up repeated peek operations. | ||
| 25 | */ | ||
| 26 | struct bheap_node* min; | ||
| 27 | }; | ||
| 28 | |||
| 29 | typedef int (*bheap_prio_t)(struct bheap_node* a, struct bheap_node* b); | ||
| 30 | |||
| 31 | void bheap_init(struct bheap* heap); | ||
| 32 | void bheap_node_init(struct bheap_node** ref_to_bheap_node_ptr, void* value); | ||
| 33 | |||
| 34 | static inline int bheap_node_in_heap(struct bheap_node* h) | ||
| 35 | { | ||
| 36 | return h->degree != NOT_IN_HEAP; | ||
| 37 | } | ||
| 38 | |||
| 39 | static inline int bheap_empty(struct bheap* heap) | ||
| 40 | { | ||
| 41 | return heap->head == NULL && heap->min == NULL; | ||
| 42 | } | ||
| 43 | |||
| 44 | /* insert (and reinitialize) a node into the heap */ | ||
| 45 | void bheap_insert(bheap_prio_t higher_prio, | ||
| 46 | struct bheap* heap, | ||
| 47 | struct bheap_node* node); | ||
| 48 | |||
| 49 | /* merge addition into target */ | ||
| 50 | void bheap_union(bheap_prio_t higher_prio, | ||
| 51 | struct bheap* target, | ||
| 52 | struct bheap* addition); | ||
| 53 | |||
| 54 | struct bheap_node* bheap_peek(bheap_prio_t higher_prio, | ||
| 55 | struct bheap* heap); | ||
| 56 | |||
| 57 | struct bheap_node* bheap_take(bheap_prio_t higher_prio, | ||
| 58 | struct bheap* heap); | ||
| 59 | |||
| 60 | void bheap_uncache_min(bheap_prio_t higher_prio, struct bheap* heap); | ||
| 61 | int bheap_decrease(bheap_prio_t higher_prio, struct bheap_node* node); | ||
| 62 | |||
| 63 | void bheap_delete(bheap_prio_t higher_prio, | ||
| 64 | struct bheap* heap, | ||
| 65 | struct bheap_node* node); | ||
| 66 | |||
| 67 | /* allocate from memcache */ | ||
| 68 | struct bheap_node* bheap_node_alloc(int gfp_flags); | ||
| 69 | void bheap_node_free(struct bheap_node* hn); | ||
| 70 | |||
| 71 | /* allocate a heap node for value and insert into the heap */ | ||
| 72 | int bheap_add(bheap_prio_t higher_prio, struct bheap* heap, | ||
| 73 | void* value, int gfp_flags); | ||
| 74 | |||
| 75 | void* bheap_take_del(bheap_prio_t higher_prio, | ||
| 76 | struct bheap* heap); | ||
| 77 | #endif | ||
diff --git a/include/litmus/binheap.h b/include/litmus/binheap.h new file mode 100644 index 000000000000..1cf364701da8 --- /dev/null +++ b/include/litmus/binheap.h | |||
| @@ -0,0 +1,205 @@ | |||
| 1 | #ifndef LITMUS_BINARY_HEAP_H | ||
| 2 | #define LITMUS_BINARY_HEAP_H | ||
| 3 | |||
| 4 | #include <linux/kernel.h> | ||
| 5 | |||
| 6 | /** | ||
| 7 | * Simple binary heap with add, arbitrary delete, delete_root, and top | ||
| 8 | * operations. | ||
| 9 | * | ||
| 10 | * Style meant to conform with list.h. | ||
| 11 | * | ||
| 12 | * Motivation: Linux's prio_heap.h is of fixed size. Litmus's binomial | ||
| 13 | * heap may be overkill (and perhaps not general enough) for some applications. | ||
| 14 | * | ||
| 15 | * Note: In order to make node swaps fast, a node inserted with a data pointer | ||
| 16 | * may not always hold said data pointer. This is similar to the binomial heap | ||
| 17 | * implementation. This does make node deletion tricky since we have to | ||
| 18 | * (1) locate the node that holds the data pointer to delete, and (2) the | ||
| 19 | * node that was originally inserted with said data pointer. These have to be | ||
| 20 | * coalesced into a single node before removal (see usage of | ||
| 21 | * __binheap_safe_swap()). We have to track node references to accomplish this. | ||
| 22 | */ | ||
| 23 | |||
| 24 | struct binheap_node { | ||
| 25 | void *data; | ||
| 26 | struct binheap_node *parent; | ||
| 27 | struct binheap_node *left; | ||
| 28 | struct binheap_node *right; | ||
| 29 | |||
| 30 | /* pointer to binheap_node that holds *data for which this binheap_node | ||
| 31 | * was originally inserted. (*data "owns" this node) | ||
| 32 | */ | ||
| 33 | struct binheap_node *ref; | ||
| 34 | struct binheap_node **ref_ptr; | ||
| 35 | }; | ||
| 36 | |||
| 37 | /** | ||
| 38 | * Signature of compator function. Assumed 'less-than' (min-heap). | ||
| 39 | * Pass in 'greater-than' for max-heap. | ||
| 40 | * | ||
| 41 | * TODO: Consider macro-based implementation that allows comparator to be | ||
| 42 | * inlined (similar to Linux red/black tree) for greater efficiency. | ||
| 43 | */ | ||
| 44 | typedef int (*binheap_order_t)(struct binheap_node *a, | ||
| 45 | struct binheap_node *b); | ||
| 46 | |||
| 47 | |||
| 48 | struct binheap { | ||
| 49 | struct binheap_node *root; | ||
| 50 | |||
| 51 | /* pointer to node to take next inserted child */ | ||
| 52 | struct binheap_node *next; | ||
| 53 | |||
| 54 | /* pointer to last node in complete binary tree */ | ||
| 55 | struct binheap_node *last; | ||
| 56 | |||
| 57 | /* comparator function pointer */ | ||
| 58 | binheap_order_t compare; | ||
| 59 | }; | ||
| 60 | |||
| 61 | |||
| 62 | /* Initialized heap nodes not in a heap have parent | ||
| 63 | * set to BINHEAP_POISON. | ||
| 64 | */ | ||
| 65 | #define BINHEAP_POISON ((void*)(0xdeadbeef)) | ||
| 66 | |||
| 67 | |||
| 68 | /** | ||
| 69 | * binheap_entry - get the struct for this heap node. | ||
| 70 | * Only valid when called upon heap nodes other than the root handle. | ||
| 71 | * @ptr: the heap node. | ||
| 72 | * @type: the type of struct pointed to by binheap_node::data. | ||
| 73 | * @member: unused. | ||
| 74 | */ | ||
| 75 | #define binheap_entry(ptr, type, member) \ | ||
| 76 | ((type *)((ptr)->data)) | ||
| 77 | |||
| 78 | /** | ||
| 79 | * binheap_node_container - get the struct that contains this node. | ||
| 80 | * Only valid when called upon heap nodes other than the root handle. | ||
| 81 | * @ptr: the heap node. | ||
| 82 | * @type: the type of struct the node is embedded in. | ||
| 83 | * @member: the name of the binheap_struct within the (type) struct. | ||
| 84 | */ | ||
| 85 | #define binheap_node_container(ptr, type, member) \ | ||
| 86 | container_of((ptr), type, member) | ||
| 87 | |||
| 88 | /** | ||
| 89 | * binheap_top_entry - get the struct for the node at the top of the heap. | ||
| 90 | * Only valid when called upon the heap handle node. | ||
| 91 | * @ptr: the special heap-handle node. | ||
| 92 | * @type: the type of the struct the head is embedded in. | ||
| 93 | * @member: the name of the binheap_struct within the (type) struct. | ||
| 94 | */ | ||
| 95 | #define binheap_top_entry(ptr, type, member) \ | ||
| 96 | binheap_entry((ptr)->root, type, member) | ||
| 97 | |||
| 98 | /** | ||
| 99 | * binheap_delete_root - remove the root element from the heap. | ||
| 100 | * @handle: handle to the heap. | ||
| 101 | * @type: the type of the struct the head is embedded in. | ||
| 102 | * @member: the name of the binheap_struct within the (type) struct. | ||
| 103 | */ | ||
| 104 | #define binheap_delete_root(handle, type, member) \ | ||
| 105 | __binheap_delete_root((handle), &((type *)((handle)->root->data))->member) | ||
| 106 | |||
| 107 | /** | ||
| 108 | * binheap_delete - remove an arbitrary element from the heap. | ||
| 109 | * @to_delete: pointer to node to be removed. | ||
| 110 | * @handle: handle to the heap. | ||
| 111 | */ | ||
| 112 | #define binheap_delete(to_delete, handle) \ | ||
| 113 | __binheap_delete((to_delete), (handle)) | ||
| 114 | |||
| 115 | /** | ||
| 116 | * binheap_add - insert an element to the heap | ||
| 117 | * new_node: node to add. | ||
| 118 | * @handle: handle to the heap. | ||
| 119 | * @type: the type of the struct the head is embedded in. | ||
| 120 | * @member: the name of the binheap_struct within the (type) struct. | ||
| 121 | */ | ||
| 122 | #define binheap_add(new_node, handle, type, member) \ | ||
| 123 | __binheap_add((new_node), (handle), container_of((new_node), type, member)) | ||
| 124 | |||
| 125 | /** | ||
| 126 | * binheap_decrease - re-eval the position of a node (based upon its | ||
| 127 | * original data pointer). | ||
| 128 | * @handle: handle to the heap. | ||
| 129 | * @orig_node: node that was associated with the data pointer | ||
| 130 | * (whose value has changed) when said pointer was | ||
| 131 | * added to the heap. | ||
| 132 | */ | ||
| 133 | #define binheap_decrease(orig_node, handle) \ | ||
| 134 | __binheap_decrease((orig_node), (handle)) | ||
| 135 | |||
| 136 | #define BINHEAP_NODE_INIT() { NULL, BINHEAP_POISON, NULL, NULL , NULL, NULL} | ||
| 137 | |||
| 138 | #define BINHEAP_NODE(name) \ | ||
| 139 | struct binheap_node name = BINHEAP_NODE_INIT() | ||
| 140 | |||
| 141 | |||
| 142 | static inline void INIT_BINHEAP_NODE(struct binheap_node *n) | ||
| 143 | { | ||
| 144 | n->data = NULL; | ||
| 145 | n->parent = BINHEAP_POISON; | ||
| 146 | n->left = NULL; | ||
| 147 | n->right = NULL; | ||
| 148 | n->ref = NULL; | ||
| 149 | n->ref_ptr = NULL; | ||
| 150 | } | ||
| 151 | |||
| 152 | static inline void INIT_BINHEAP_HANDLE(struct binheap *handle, | ||
| 153 | binheap_order_t compare) | ||
| 154 | { | ||
| 155 | handle->root = NULL; | ||
| 156 | handle->next = NULL; | ||
| 157 | handle->last = NULL; | ||
| 158 | handle->compare = compare; | ||
| 159 | } | ||
| 160 | |||
| 161 | /* Returns true if binheap is empty. */ | ||
| 162 | static inline int binheap_empty(struct binheap *handle) | ||
| 163 | { | ||
| 164 | return(handle->root == NULL); | ||
| 165 | } | ||
| 166 | |||
| 167 | /* Returns true if binheap node is in a heap. */ | ||
| 168 | static inline int binheap_is_in_heap(struct binheap_node *node) | ||
| 169 | { | ||
| 170 | return (node->parent != BINHEAP_POISON); | ||
| 171 | } | ||
| 172 | |||
| 173 | /* Returns true if binheap node is in given heap. */ | ||
| 174 | int binheap_is_in_this_heap(struct binheap_node *node, struct binheap* heap); | ||
| 175 | |||
| 176 | /* Add a node to a heap */ | ||
| 177 | void __binheap_add(struct binheap_node *new_node, | ||
| 178 | struct binheap *handle, | ||
| 179 | void *data); | ||
| 180 | |||
| 181 | /** | ||
| 182 | * Removes the root node from the heap. The node is removed after coalescing | ||
| 183 | * the binheap_node with its original data pointer at the root of the tree. | ||
| 184 | * | ||
| 185 | * The 'last' node in the tree is then swapped up to the root and bubbled | ||
| 186 | * down. | ||
| 187 | */ | ||
| 188 | void __binheap_delete_root(struct binheap *handle, | ||
| 189 | struct binheap_node *container); | ||
| 190 | |||
| 191 | /** | ||
| 192 | * Delete an arbitrary node. Bubble node to delete up to the root, | ||
| 193 | * and then delete to root. | ||
| 194 | */ | ||
| 195 | void __binheap_delete(struct binheap_node *node_to_delete, | ||
| 196 | struct binheap *handle); | ||
| 197 | |||
| 198 | /** | ||
| 199 | * Bubble up a node whose pointer has decreased in value. | ||
| 200 | */ | ||
| 201 | void __binheap_decrease(struct binheap_node *orig_node, | ||
| 202 | struct binheap *handle); | ||
| 203 | |||
| 204 | |||
| 205 | #endif | ||
diff --git a/include/litmus/budget.h b/include/litmus/budget.h new file mode 100644 index 000000000000..60eb814fc82b --- /dev/null +++ b/include/litmus/budget.h | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | #ifndef _LITMUS_BUDGET_H_ | ||
| 2 | #define _LITMUS_BUDGET_H_ | ||
| 3 | |||
| 4 | /* Update the per-processor enforcement timer (arm/reproram/cancel) for | ||
| 5 | * the next task. */ | ||
| 6 | void update_enforcement_timer(struct task_struct* t); | ||
| 7 | |||
| 8 | inline static int budget_exhausted(struct task_struct* t) | ||
| 9 | { | ||
| 10 | return get_exec_time(t) >= get_exec_cost(t); | ||
| 11 | } | ||
| 12 | |||
| 13 | inline static lt_t budget_remaining(struct task_struct* t) | ||
| 14 | { | ||
| 15 | if (!budget_exhausted(t)) | ||
| 16 | return get_exec_cost(t) - get_exec_time(t); | ||
| 17 | else | ||
| 18 | /* avoid overflow */ | ||
| 19 | return 0; | ||
| 20 | } | ||
| 21 | |||
| 22 | #define budget_enforced(t) (tsk_rt(t)->task_params.budget_policy != NO_ENFORCEMENT) | ||
| 23 | |||
| 24 | #define budget_precisely_enforced(t) (tsk_rt(t)->task_params.budget_policy \ | ||
| 25 | == PRECISE_ENFORCEMENT) | ||
| 26 | |||
| 27 | static inline int requeue_preempted_job(struct task_struct* t) | ||
| 28 | { | ||
| 29 | /* Add task to ready queue only if not subject to budget enforcement or | ||
| 30 | * if the job has budget remaining. t may be NULL. | ||
| 31 | */ | ||
| 32 | return t && !is_completed(t) && | ||
| 33 | (!budget_exhausted(t) || !budget_enforced(t)); | ||
| 34 | } | ||
| 35 | |||
| 36 | void litmus_current_budget(lt_t *used_so_far, lt_t *remaining); | ||
| 37 | |||
| 38 | #endif | ||
diff --git a/include/litmus/ceiling.h b/include/litmus/ceiling.h new file mode 100644 index 000000000000..f3d3889315f7 --- /dev/null +++ b/include/litmus/ceiling.h | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | #ifndef _LITMUS_CEILING_H_ | ||
| 2 | #define _LITMUS_CEILING_H_ | ||
| 3 | |||
| 4 | #ifdef CONFIG_LITMUS_LOCKING | ||
| 5 | |||
| 6 | void __srp_ceiling_block(struct task_struct *cur); | ||
| 7 | |||
| 8 | DECLARE_PER_CPU(int, srp_objects_in_use); | ||
| 9 | |||
| 10 | /* assumes preemptions off */ | ||
| 11 | void srp_ceiling_block(void) | ||
| 12 | { | ||
| 13 | struct task_struct *tsk = current; | ||
| 14 | |||
| 15 | /* Only applies to real-time tasks. */ | ||
| 16 | if (!is_realtime(tsk)) | ||
| 17 | return; | ||
| 18 | |||
| 19 | /* Bail out early if there aren't any SRP resources around. */ | ||
| 20 | if (likely(!raw_cpu_read(srp_objects_in_use))) | ||
| 21 | return; | ||
| 22 | |||
| 23 | /* Avoid recursive ceiling blocking. */ | ||
| 24 | if (unlikely(tsk->rt_param.srp_non_recurse)) | ||
| 25 | return; | ||
| 26 | |||
| 27 | /* must take slow path */ | ||
| 28 | __srp_ceiling_block(tsk); | ||
| 29 | } | ||
| 30 | |||
| 31 | #else | ||
| 32 | #define srp_ceiling_block() /* nothing */ | ||
| 33 | #endif | ||
| 34 | |||
| 35 | |||
| 36 | #endif \ No newline at end of file | ||
diff --git a/include/litmus/clustered.h b/include/litmus/clustered.h new file mode 100644 index 000000000000..fc7f0f87966e --- /dev/null +++ b/include/litmus/clustered.h | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | #ifndef CLUSTERED_H | ||
| 2 | #define CLUSTERED_H | ||
| 3 | |||
| 4 | /* Which cache level should be used to group CPUs into clusters? | ||
| 5 | * GLOBAL_CLUSTER means that all CPUs form a single cluster (just like under | ||
| 6 | * global scheduling). | ||
| 7 | */ | ||
| 8 | enum cache_level { | ||
| 9 | GLOBAL_CLUSTER = 0, | ||
| 10 | L1_CLUSTER = 1, | ||
| 11 | L2_CLUSTER = 2, | ||
| 12 | L3_CLUSTER = 3 | ||
| 13 | }; | ||
| 14 | |||
| 15 | int parse_cache_level(const char *str, enum cache_level *level); | ||
| 16 | const char* cache_level_name(enum cache_level level); | ||
| 17 | |||
| 18 | /* expose a cache level in a /proc dir */ | ||
| 19 | struct proc_dir_entry* create_cluster_file(struct proc_dir_entry* parent, | ||
| 20 | enum cache_level* level); | ||
| 21 | |||
| 22 | |||
| 23 | |||
| 24 | struct scheduling_cluster { | ||
| 25 | unsigned int id; | ||
| 26 | /* list of CPUs that are part of this cluster */ | ||
| 27 | struct list_head cpus; | ||
| 28 | }; | ||
| 29 | |||
| 30 | struct cluster_cpu { | ||
| 31 | unsigned int id; /* which CPU is this? */ | ||
| 32 | struct list_head cluster_list; /* List of the CPUs in this cluster. */ | ||
| 33 | struct scheduling_cluster* cluster; /* The cluster that this CPU belongs to. */ | ||
| 34 | }; | ||
| 35 | |||
| 36 | int get_cluster_size(enum cache_level level); | ||
| 37 | |||
| 38 | int assign_cpus_to_clusters(enum cache_level level, | ||
| 39 | struct scheduling_cluster* clusters[], | ||
| 40 | unsigned int num_clusters, | ||
| 41 | struct cluster_cpu* cpus[], | ||
| 42 | unsigned int num_cpus); | ||
| 43 | |||
| 44 | int get_shared_cpu_map(cpumask_var_t mask, unsigned int cpu, unsigned int index); | ||
| 45 | |||
| 46 | #endif | ||
diff --git a/include/litmus/ctrlpage.h b/include/litmus/ctrlpage.h new file mode 100644 index 000000000000..f7b03e1aedd6 --- /dev/null +++ b/include/litmus/ctrlpage.h | |||
| @@ -0,0 +1,105 @@ | |||
| 1 | #ifndef _LITMUS_CTRLPAGE_H_ | ||
| 2 | #define _LITMUS_CTRLPAGE_H_ | ||
| 3 | |||
| 4 | #include <litmus/rt_param.h> | ||
| 5 | |||
| 6 | union np_flag { | ||
| 7 | uint32_t raw; | ||
| 8 | struct { | ||
| 9 | /* Is the task currently in a non-preemptive section? */ | ||
| 10 | uint32_t flag:31; | ||
| 11 | /* Should the task call into the scheduler? */ | ||
| 12 | uint32_t preempt:1; | ||
| 13 | } np; | ||
| 14 | }; | ||
| 15 | |||
| 16 | /* The definition of the data that is shared between the kernel and real-time | ||
| 17 | * tasks via a shared page (see litmus/ctrldev.c). | ||
| 18 | * | ||
| 19 | * WARNING: User space can write to this, so don't trust | ||
| 20 | * the correctness of the fields! | ||
| 21 | * | ||
| 22 | * This servees two purposes: to enable efficient signaling | ||
| 23 | * of non-preemptive sections (user->kernel) and | ||
| 24 | * delayed preemptions (kernel->user), and to export | ||
| 25 | * some real-time relevant statistics such as preemption and | ||
| 26 | * migration data to user space. We can't use a device to export | ||
| 27 | * statistics because we want to avoid system call overhead when | ||
| 28 | * determining preemption/migration overheads). | ||
| 29 | */ | ||
| 30 | struct control_page { | ||
| 31 | /* This flag is used by userspace to communicate non-preempive | ||
| 32 | * sections. */ | ||
| 33 | volatile __attribute__ ((aligned (8))) union np_flag sched; | ||
| 34 | |||
| 35 | /* Incremented by the kernel each time an IRQ is handled. */ | ||
| 36 | volatile __attribute__ ((aligned (8))) uint64_t irq_count; | ||
| 37 | |||
| 38 | /* Locking overhead tracing: userspace records here the time stamp | ||
| 39 | * and IRQ counter prior to starting the system call. */ | ||
| 40 | uint64_t ts_syscall_start; /* Feather-Trace cycles */ | ||
| 41 | uint64_t irq_syscall_start; /* Snapshot of irq_count when the syscall | ||
| 42 | * started. */ | ||
| 43 | |||
| 44 | lt_t deadline; /* Deadline for the currently executing job */ | ||
| 45 | lt_t release; /* Release time of current job */ | ||
| 46 | uint64_t job_index; /* Job sequence number of current job */ | ||
| 47 | |||
| 48 | /* to be extended */ | ||
| 49 | }; | ||
| 50 | |||
| 51 | /* Expected offsets within the control page. */ | ||
| 52 | |||
| 53 | #define LITMUS_CP_OFFSET_SCHED 0 | ||
| 54 | #define LITMUS_CP_OFFSET_IRQ_COUNT 8 | ||
| 55 | #define LITMUS_CP_OFFSET_TS_SC_START 16 | ||
| 56 | #define LITMUS_CP_OFFSET_IRQ_SC_START 24 | ||
| 57 | #define LITMUS_CP_OFFSET_DEADLINE 32 | ||
| 58 | #define LITMUS_CP_OFFSET_RELEASE 40 | ||
| 59 | #define LITMUS_CP_OFFSET_JOB_INDEX 48 | ||
| 60 | |||
| 61 | /* System call emulation via ioctl() */ | ||
| 62 | |||
| 63 | typedef enum { | ||
| 64 | LRT_null_call = 2006, | ||
| 65 | LRT_set_rt_task_param, | ||
| 66 | LRT_get_rt_task_param, | ||
| 67 | LRT_reservation_create, | ||
| 68 | LRT_complete_job, | ||
| 69 | LRT_od_open, | ||
| 70 | LRT_od_close, | ||
| 71 | LRT_litmus_lock, | ||
| 72 | LRT_litmus_unlock, | ||
| 73 | LRT_wait_for_job_release, | ||
| 74 | LRT_wait_for_ts_release, | ||
| 75 | LRT_release_ts, | ||
| 76 | LRT_get_current_budget, | ||
| 77 | } litmus_syscall_id_t; | ||
| 78 | |||
| 79 | union litmus_syscall_args { | ||
| 80 | struct { | ||
| 81 | pid_t pid; | ||
| 82 | struct rt_task __user *param; | ||
| 83 | } get_set_task_param; | ||
| 84 | |||
| 85 | struct { | ||
| 86 | uint32_t type; | ||
| 87 | void __user *config; | ||
| 88 | } reservation_create; | ||
| 89 | |||
| 90 | struct { | ||
| 91 | uint32_t fd; | ||
| 92 | uint32_t obj_type; | ||
| 93 | uint32_t obj_id; | ||
| 94 | void __user *config; | ||
| 95 | } od_open; | ||
| 96 | |||
| 97 | struct { | ||
| 98 | lt_t __user *expended; | ||
| 99 | lt_t __user *remaining; | ||
| 100 | } get_current_budget; | ||
| 101 | }; | ||
| 102 | |||
| 103 | |||
| 104 | #endif | ||
| 105 | |||
diff --git a/include/litmus/debug_trace.h b/include/litmus/debug_trace.h index 1266ac6a760c..a760631d4fca 100644 --- a/include/litmus/debug_trace.h +++ b/include/litmus/debug_trace.h | |||
| @@ -37,4 +37,9 @@ extern atomic_t __log_seq_no; | |||
| 37 | #define TRACE_CUR(fmt, args...) \ | 37 | #define TRACE_CUR(fmt, args...) \ |
| 38 | TRACE_TASK(current, fmt, ## args) | 38 | TRACE_TASK(current, fmt, ## args) |
| 39 | 39 | ||
| 40 | #define TRACE_WARN_ON(cond) \ | ||
| 41 | if (unlikely(cond)) \ | ||
| 42 | TRACE("WARNING: '%s' [%s@%s:%d]\n", \ | ||
| 43 | #cond, __FUNCTION__, __FILE__, __LINE__) | ||
| 44 | |||
| 40 | #endif | 45 | #endif |
diff --git a/include/litmus/edf_common.h b/include/litmus/edf_common.h new file mode 100644 index 000000000000..bbaf22ea7f12 --- /dev/null +++ b/include/litmus/edf_common.h | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | /* | ||
| 2 | * EDF common data structures and utility functions shared by all EDF | ||
| 3 | * based scheduler plugins | ||
| 4 | */ | ||
| 5 | |||
| 6 | /* CLEANUP: Add comments and make it less messy. | ||
| 7 | * | ||
| 8 | */ | ||
| 9 | |||
| 10 | #ifndef __UNC_EDF_COMMON_H__ | ||
| 11 | #define __UNC_EDF_COMMON_H__ | ||
| 12 | |||
| 13 | #include <litmus/rt_domain.h> | ||
| 14 | |||
| 15 | void edf_domain_init(rt_domain_t* rt, check_resched_needed_t resched, | ||
| 16 | release_jobs_t release); | ||
| 17 | |||
| 18 | int edf_higher_prio(struct task_struct* first, | ||
| 19 | struct task_struct* second); | ||
| 20 | |||
| 21 | int edf_ready_order(struct bheap_node* a, struct bheap_node* b); | ||
| 22 | |||
| 23 | int edf_preemption_needed(rt_domain_t* rt, struct task_struct *t); | ||
| 24 | |||
| 25 | #endif | ||
diff --git a/include/litmus/fdso.h b/include/litmus/fdso.h new file mode 100644 index 000000000000..fd9b30dbfb34 --- /dev/null +++ b/include/litmus/fdso.h | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | /* fdso.h - file descriptor attached shared objects | ||
| 2 | * | ||
| 3 | * (c) 2007 B. Brandenburg, LITMUS^RT project | ||
| 4 | */ | ||
| 5 | |||
| 6 | #ifndef _LINUX_FDSO_H_ | ||
| 7 | #define _LINUX_FDSO_H_ | ||
| 8 | |||
| 9 | #include <linux/list.h> | ||
| 10 | #include <asm/atomic.h> | ||
| 11 | |||
| 12 | #include <linux/fs.h> | ||
| 13 | #include <linux/slab.h> | ||
| 14 | |||
| 15 | #define MAX_OBJECT_DESCRIPTORS 85 | ||
| 16 | |||
| 17 | typedef enum { | ||
| 18 | MIN_OBJ_TYPE = 0, | ||
| 19 | |||
| 20 | FMLP_SEM = 0, | ||
| 21 | SRP_SEM = 1, | ||
| 22 | |||
| 23 | MPCP_SEM = 2, | ||
| 24 | MPCP_VS_SEM = 3, | ||
| 25 | DPCP_SEM = 4, | ||
| 26 | PCP_SEM = 5, | ||
| 27 | |||
| 28 | DFLP_SEM = 6, | ||
| 29 | |||
| 30 | MAX_OBJ_TYPE = 6 | ||
| 31 | } obj_type_t; | ||
| 32 | |||
| 33 | struct inode_obj_id { | ||
| 34 | struct list_head list; | ||
| 35 | atomic_t count; | ||
| 36 | struct inode* inode; | ||
| 37 | |||
| 38 | obj_type_t type; | ||
| 39 | void* obj; | ||
| 40 | unsigned int id; | ||
| 41 | }; | ||
| 42 | |||
| 43 | struct fdso_ops; | ||
| 44 | |||
| 45 | struct od_table_entry { | ||
| 46 | unsigned int used; | ||
| 47 | |||
| 48 | struct inode_obj_id* obj; | ||
| 49 | const struct fdso_ops* class; | ||
| 50 | }; | ||
| 51 | |||
| 52 | struct fdso_ops { | ||
| 53 | int (*create)(void** obj_ref, obj_type_t type, void* __user); | ||
| 54 | void (*destroy)(obj_type_t type, void*); | ||
| 55 | int (*open) (struct od_table_entry*, void* __user); | ||
| 56 | int (*close) (struct od_table_entry*); | ||
| 57 | }; | ||
| 58 | |||
| 59 | /* translate a userspace supplied od into the raw table entry | ||
| 60 | * returns NULL if od is invalid | ||
| 61 | */ | ||
| 62 | struct od_table_entry* get_entry_for_od(int od); | ||
| 63 | |||
| 64 | /* translate a userspace supplied od into the associated object | ||
| 65 | * returns NULL if od is invalid | ||
| 66 | */ | ||
| 67 | static inline void* od_lookup(int od, obj_type_t type) | ||
| 68 | { | ||
| 69 | struct od_table_entry* e = get_entry_for_od(od); | ||
| 70 | return e && e->obj->type == type ? e->obj->obj : NULL; | ||
| 71 | } | ||
| 72 | |||
| 73 | #define lookup_fmlp_sem(od)((struct pi_semaphore*) od_lookup(od, FMLP_SEM)) | ||
| 74 | #define lookup_srp_sem(od) ((struct srp_semaphore*) od_lookup(od, SRP_SEM)) | ||
| 75 | #define lookup_ics(od) ((struct ics*) od_lookup(od, ICS_ID)) | ||
| 76 | |||
| 77 | |||
| 78 | #endif | ||
diff --git a/include/litmus/fp_common.h b/include/litmus/fp_common.h new file mode 100644 index 000000000000..71c0d0142fc4 --- /dev/null +++ b/include/litmus/fp_common.h | |||
| @@ -0,0 +1,183 @@ | |||
| 1 | /* Fixed-priority scheduler support. | ||
| 2 | */ | ||
| 3 | |||
| 4 | #ifndef __FP_COMMON_H__ | ||
| 5 | #define __FP_COMMON_H__ | ||
| 6 | |||
| 7 | #include <litmus/rt_domain.h> | ||
| 8 | |||
| 9 | #include <asm/bitops.h> | ||
| 10 | |||
| 11 | |||
| 12 | void fp_domain_init(rt_domain_t* rt, check_resched_needed_t resched, | ||
| 13 | release_jobs_t release); | ||
| 14 | |||
| 15 | int fp_higher_prio(struct task_struct* first, | ||
| 16 | struct task_struct* second); | ||
| 17 | |||
| 18 | int fp_ready_order(struct bheap_node* a, struct bheap_node* b); | ||
| 19 | |||
| 20 | #define FP_PRIO_BIT_WORDS (LITMUS_MAX_PRIORITY / BITS_PER_LONG) | ||
| 21 | |||
| 22 | #if (LITMUS_MAX_PRIORITY % BITS_PER_LONG) | ||
| 23 | #error LITMUS_MAX_PRIORITY must be a multiple of BITS_PER_LONG | ||
| 24 | #endif | ||
| 25 | |||
| 26 | /* bitmask-inexed priority queue */ | ||
| 27 | struct fp_prio_queue { | ||
| 28 | unsigned long bitmask[FP_PRIO_BIT_WORDS]; | ||
| 29 | struct bheap queue[LITMUS_MAX_PRIORITY]; | ||
| 30 | }; | ||
| 31 | |||
| 32 | void fp_prio_queue_init(struct fp_prio_queue* q); | ||
| 33 | |||
| 34 | static inline void fpq_set(struct fp_prio_queue* q, unsigned int index) | ||
| 35 | { | ||
| 36 | unsigned long *word = q->bitmask + (index / BITS_PER_LONG); | ||
| 37 | __set_bit(index % BITS_PER_LONG, word); | ||
| 38 | } | ||
| 39 | |||
| 40 | static inline void fpq_clear(struct fp_prio_queue* q, unsigned int index) | ||
| 41 | { | ||
| 42 | unsigned long *word = q->bitmask + (index / BITS_PER_LONG); | ||
| 43 | __clear_bit(index % BITS_PER_LONG, word); | ||
| 44 | } | ||
| 45 | |||
| 46 | static inline unsigned int fpq_find(struct fp_prio_queue* q) | ||
| 47 | { | ||
| 48 | int i; | ||
| 49 | |||
| 50 | /* loop optimizer should unroll this */ | ||
| 51 | for (i = 0; i < FP_PRIO_BIT_WORDS; i++) | ||
| 52 | if (q->bitmask[i]) | ||
| 53 | return __ffs(q->bitmask[i]) + i * BITS_PER_LONG; | ||
| 54 | |||
| 55 | return LITMUS_MAX_PRIORITY; /* nothing found */ | ||
| 56 | } | ||
| 57 | |||
| 58 | static inline void fp_prio_add(struct fp_prio_queue* q, struct task_struct* t, unsigned int index) | ||
| 59 | { | ||
| 60 | BUG_ON(index >= LITMUS_MAX_PRIORITY); | ||
| 61 | BUG_ON(bheap_node_in_heap(tsk_rt(t)->heap_node)); | ||
| 62 | |||
| 63 | fpq_set(q, index); | ||
| 64 | bheap_insert(fp_ready_order, &q->queue[index], tsk_rt(t)->heap_node); | ||
| 65 | } | ||
| 66 | |||
| 67 | static inline void fp_prio_remove(struct fp_prio_queue* q, struct task_struct* t, unsigned int index) | ||
| 68 | { | ||
| 69 | BUG_ON(!is_queued(t)); | ||
| 70 | |||
| 71 | bheap_delete(fp_ready_order, &q->queue[index], tsk_rt(t)->heap_node); | ||
| 72 | if (likely(bheap_empty(&q->queue[index]))) | ||
| 73 | fpq_clear(q, index); | ||
| 74 | } | ||
| 75 | |||
| 76 | static inline struct task_struct* fp_prio_peek(struct fp_prio_queue* q) | ||
| 77 | { | ||
| 78 | unsigned int idx = fpq_find(q); | ||
| 79 | struct bheap_node* hn; | ||
| 80 | |||
| 81 | if (idx < LITMUS_MAX_PRIORITY) { | ||
| 82 | hn = bheap_peek(fp_ready_order, &q->queue[idx]); | ||
| 83 | return bheap2task(hn); | ||
| 84 | } else | ||
| 85 | return NULL; | ||
| 86 | } | ||
| 87 | |||
| 88 | static inline struct task_struct* fp_prio_take(struct fp_prio_queue* q) | ||
| 89 | { | ||
| 90 | unsigned int idx = fpq_find(q); | ||
| 91 | struct bheap_node* hn; | ||
| 92 | |||
| 93 | if (idx < LITMUS_MAX_PRIORITY) { | ||
| 94 | hn = bheap_take(fp_ready_order, &q->queue[idx]); | ||
| 95 | if (likely(bheap_empty(&q->queue[idx]))) | ||
| 96 | fpq_clear(q, idx); | ||
| 97 | return bheap2task(hn); | ||
| 98 | } else | ||
| 99 | return NULL; | ||
| 100 | } | ||
| 101 | |||
| 102 | int fp_preemption_needed(struct fp_prio_queue* q, struct task_struct *t); | ||
| 103 | |||
| 104 | |||
| 105 | /* ******* list-based version ******** */ | ||
| 106 | |||
| 107 | /* bitmask-inexed priority queue */ | ||
| 108 | struct fp_ready_list { | ||
| 109 | unsigned long bitmask[FP_PRIO_BIT_WORDS]; | ||
| 110 | struct list_head queue[LITMUS_MAX_PRIORITY]; | ||
| 111 | }; | ||
| 112 | |||
| 113 | void fp_ready_list_init(struct fp_ready_list* q); | ||
| 114 | |||
| 115 | static inline void fp_rl_set(struct fp_ready_list* q, unsigned int index) | ||
| 116 | { | ||
| 117 | unsigned long *word = q->bitmask + (index / BITS_PER_LONG); | ||
| 118 | __set_bit(index % BITS_PER_LONG, word); | ||
| 119 | } | ||
| 120 | |||
| 121 | static inline void fp_rl_clear(struct fp_ready_list* q, unsigned int index) | ||
| 122 | { | ||
| 123 | unsigned long *word = q->bitmask + (index / BITS_PER_LONG); | ||
| 124 | __clear_bit(index % BITS_PER_LONG, word); | ||
| 125 | } | ||
| 126 | |||
| 127 | static inline unsigned int fp_rl_find(struct fp_ready_list* q) | ||
| 128 | { | ||
| 129 | int i; | ||
| 130 | |||
| 131 | /* loop optimizer should unroll this */ | ||
| 132 | for (i = 0; i < FP_PRIO_BIT_WORDS; i++) | ||
| 133 | if (q->bitmask[i]) | ||
| 134 | return __ffs(q->bitmask[i]) + i * BITS_PER_LONG; | ||
| 135 | |||
| 136 | return LITMUS_MAX_PRIORITY; /* nothing found */ | ||
| 137 | } | ||
| 138 | |||
| 139 | static inline void fp_ready_list_add( | ||
| 140 | struct fp_ready_list* q, struct list_head* lh, unsigned int index) | ||
| 141 | { | ||
| 142 | BUG_ON(index >= LITMUS_MAX_PRIORITY); | ||
| 143 | BUG_ON(in_list(lh)); | ||
| 144 | |||
| 145 | fp_rl_set(q, index); | ||
| 146 | list_add_tail(lh, &q->queue[index]); | ||
| 147 | } | ||
| 148 | |||
| 149 | static inline void fp_ready_list_remove( | ||
| 150 | struct fp_ready_list* q, struct list_head* lh, unsigned int index) | ||
| 151 | { | ||
| 152 | BUG_ON(!in_list(lh)); | ||
| 153 | |||
| 154 | list_del(lh); | ||
| 155 | if (likely(list_empty(q->queue + index))) | ||
| 156 | fp_rl_clear(q, index); | ||
| 157 | } | ||
| 158 | |||
| 159 | static inline struct list_head* fp_ready_list_peek(struct fp_ready_list* q) | ||
| 160 | { | ||
| 161 | unsigned int idx = fp_rl_find(q); | ||
| 162 | |||
| 163 | if (idx < LITMUS_MAX_PRIORITY) { | ||
| 164 | return q->queue[idx].next; | ||
| 165 | } else | ||
| 166 | return NULL; | ||
| 167 | } | ||
| 168 | |||
| 169 | static inline struct list_head* fp_ready_list_take(struct fp_ready_list* q) | ||
| 170 | { | ||
| 171 | unsigned int idx = fp_rl_find(q); | ||
| 172 | struct list_head* lh; | ||
| 173 | |||
| 174 | if (idx < LITMUS_MAX_PRIORITY) { | ||
| 175 | lh = q->queue[idx].next; | ||
| 176 | fp_ready_list_remove(q, lh, idx); | ||
| 177 | return lh; | ||
| 178 | } else | ||
| 179 | return NULL; | ||
| 180 | } | ||
| 181 | |||
| 182 | |||
| 183 | #endif | ||
diff --git a/include/litmus/fpmath.h b/include/litmus/fpmath.h new file mode 100644 index 000000000000..642de98542c8 --- /dev/null +++ b/include/litmus/fpmath.h | |||
| @@ -0,0 +1,147 @@ | |||
| 1 | #ifndef __FP_MATH_H__ | ||
| 2 | #define __FP_MATH_H__ | ||
| 3 | |||
| 4 | #include <linux/math64.h> | ||
| 5 | |||
| 6 | #ifndef __KERNEL__ | ||
| 7 | #include <stdint.h> | ||
| 8 | #define abs(x) (((x) < 0) ? -(x) : x) | ||
| 9 | #endif | ||
| 10 | |||
| 11 | // Use 64-bit because we want to track things at the nanosecond scale. | ||
| 12 | // This can lead to very large numbers. | ||
| 13 | typedef int64_t fpbuf_t; | ||
| 14 | typedef struct | ||
| 15 | { | ||
| 16 | fpbuf_t val; | ||
| 17 | } fp_t; | ||
| 18 | |||
| 19 | #define FP_SHIFT 10 | ||
| 20 | #define ROUND_BIT (FP_SHIFT - 1) | ||
| 21 | |||
| 22 | #define _fp(x) ((fp_t) {x}) | ||
| 23 | |||
| 24 | #ifdef __KERNEL__ | ||
| 25 | static const fp_t LITMUS_FP_ZERO = {.val = 0}; | ||
| 26 | static const fp_t LITMUS_FP_ONE = {.val = (1 << FP_SHIFT)}; | ||
| 27 | #endif | ||
| 28 | |||
| 29 | static inline fp_t FP(fpbuf_t x) | ||
| 30 | { | ||
| 31 | return _fp(((fpbuf_t) x) << FP_SHIFT); | ||
| 32 | } | ||
| 33 | |||
| 34 | /* divide two integers to obtain a fixed point value */ | ||
| 35 | static inline fp_t _frac(fpbuf_t a, fpbuf_t b) | ||
| 36 | { | ||
| 37 | return _fp(div64_s64(FP(a).val, (b))); | ||
| 38 | } | ||
| 39 | |||
| 40 | static inline fpbuf_t _point(fp_t x) | ||
| 41 | { | ||
| 42 | return (x.val % (1 << FP_SHIFT)); | ||
| 43 | |||
| 44 | } | ||
| 45 | |||
| 46 | #define fp2str(x) x.val | ||
| 47 | /*(x.val >> FP_SHIFT), (x.val % (1 << FP_SHIFT)) */ | ||
| 48 | #define _FP_ "%ld/1024" | ||
| 49 | |||
| 50 | static inline fpbuf_t _floor(fp_t x) | ||
| 51 | { | ||
| 52 | return x.val >> FP_SHIFT; | ||
| 53 | } | ||
| 54 | |||
| 55 | /* FIXME: negative rounding */ | ||
| 56 | static inline fpbuf_t _round(fp_t x) | ||
| 57 | { | ||
| 58 | return _floor(x) + ((x.val >> ROUND_BIT) & 1); | ||
| 59 | } | ||
| 60 | |||
| 61 | /* multiply two fixed point values */ | ||
| 62 | static inline fp_t _mul(fp_t a, fp_t b) | ||
| 63 | { | ||
| 64 | return _fp((a.val * b.val) >> FP_SHIFT); | ||
| 65 | } | ||
| 66 | |||
| 67 | static inline fp_t _div(fp_t a, fp_t b) | ||
| 68 | { | ||
| 69 | #if !defined(__KERNEL__) && !defined(unlikely) | ||
| 70 | #define unlikely(x) (x) | ||
| 71 | #define DO_UNDEF_UNLIKELY | ||
| 72 | #endif | ||
| 73 | /* try not to overflow */ | ||
| 74 | if (unlikely( a.val > (2l << ((sizeof(fpbuf_t)*8) - FP_SHIFT)) )) | ||
| 75 | return _fp((a.val / b.val) << FP_SHIFT); | ||
| 76 | else | ||
| 77 | return _fp((a.val << FP_SHIFT) / b.val); | ||
| 78 | #ifdef DO_UNDEF_UNLIKELY | ||
| 79 | #undef unlikely | ||
| 80 | #undef DO_UNDEF_UNLIKELY | ||
| 81 | #endif | ||
| 82 | } | ||
| 83 | |||
| 84 | static inline fp_t _add(fp_t a, fp_t b) | ||
| 85 | { | ||
| 86 | return _fp(a.val + b.val); | ||
| 87 | } | ||
| 88 | |||
| 89 | static inline fp_t _sub(fp_t a, fp_t b) | ||
| 90 | { | ||
| 91 | return _fp(a.val - b.val); | ||
| 92 | } | ||
| 93 | |||
| 94 | static inline fp_t _neg(fp_t x) | ||
| 95 | { | ||
| 96 | return _fp(-x.val); | ||
| 97 | } | ||
| 98 | |||
| 99 | static inline fp_t _abs(fp_t x) | ||
| 100 | { | ||
| 101 | return _fp(abs(x.val)); | ||
| 102 | } | ||
| 103 | |||
| 104 | /* works the same as casting float/double to integer */ | ||
| 105 | static inline fpbuf_t _fp_to_integer(fp_t x) | ||
| 106 | { | ||
| 107 | return _floor(_abs(x)) * ((x.val > 0) ? 1 : -1); | ||
| 108 | } | ||
| 109 | |||
| 110 | static inline fp_t _integer_to_fp(fpbuf_t x) | ||
| 111 | { | ||
| 112 | return _frac(x,1); | ||
| 113 | } | ||
| 114 | |||
| 115 | static inline int _leq(fp_t a, fp_t b) | ||
| 116 | { | ||
| 117 | return a.val <= b.val; | ||
| 118 | } | ||
| 119 | |||
| 120 | static inline int _geq(fp_t a, fp_t b) | ||
| 121 | { | ||
| 122 | return a.val >= b.val; | ||
| 123 | } | ||
| 124 | |||
| 125 | static inline int _lt(fp_t a, fp_t b) | ||
| 126 | { | ||
| 127 | return a.val < b.val; | ||
| 128 | } | ||
| 129 | |||
| 130 | static inline int _gt(fp_t a, fp_t b) | ||
| 131 | { | ||
| 132 | return a.val > b.val; | ||
| 133 | } | ||
| 134 | |||
| 135 | static inline int _eq(fp_t a, fp_t b) | ||
| 136 | { | ||
| 137 | return a.val == b.val; | ||
| 138 | } | ||
| 139 | |||
| 140 | static inline fp_t _max(fp_t a, fp_t b) | ||
| 141 | { | ||
| 142 | if (a.val < b.val) | ||
| 143 | return b; | ||
| 144 | else | ||
| 145 | return a; | ||
| 146 | } | ||
| 147 | #endif | ||
diff --git a/include/litmus/jobs.h b/include/litmus/jobs.h new file mode 100644 index 000000000000..7033393148df --- /dev/null +++ b/include/litmus/jobs.h | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | #ifndef __LITMUS_JOBS_H__ | ||
| 2 | #define __LITMUS_JOBS_H__ | ||
| 3 | |||
| 4 | void prepare_for_next_period(struct task_struct *t); | ||
| 5 | void release_at(struct task_struct *t, lt_t start); | ||
| 6 | |||
| 7 | void inferred_sporadic_job_release_at(struct task_struct *t, lt_t when); | ||
| 8 | |||
| 9 | long default_wait_for_release_at(lt_t release_time); | ||
| 10 | long complete_job(void); | ||
| 11 | long complete_job_oneshot(void); | ||
| 12 | |||
| 13 | #endif | ||
diff --git a/include/litmus/litmus.h b/include/litmus/litmus.h index c87863c9b231..f550367ddd4b 100644 --- a/include/litmus/litmus.h +++ b/include/litmus/litmus.h | |||
| @@ -6,7 +6,50 @@ | |||
| 6 | #ifndef _LINUX_LITMUS_H_ | 6 | #ifndef _LINUX_LITMUS_H_ |
| 7 | #define _LINUX_LITMUS_H_ | 7 | #define _LINUX_LITMUS_H_ |
| 8 | 8 | ||
| 9 | #include <litmus/ctrlpage.h> | ||
| 10 | |||
| 11 | #ifdef CONFIG_RELEASE_MASTER | ||
| 12 | extern atomic_t release_master_cpu; | ||
| 13 | #endif | ||
| 14 | |||
| 15 | /* in_list - is a given list_head queued on some list? | ||
| 16 | */ | ||
| 17 | static inline int in_list(struct list_head* list) | ||
| 18 | { | ||
| 19 | return !( /* case 1: deleted */ | ||
| 20 | (list->next == LIST_POISON1 && | ||
| 21 | list->prev == LIST_POISON2) | ||
| 22 | || | ||
| 23 | /* case 2: initialized */ | ||
| 24 | (list->next == list && | ||
| 25 | list->prev == list) | ||
| 26 | ); | ||
| 27 | } | ||
| 28 | |||
| 29 | struct task_struct* __waitqueue_remove_first(wait_queue_head_t *wq); | ||
| 30 | |||
| 31 | #define NO_CPU 0xffffffff | ||
| 32 | |||
| 33 | void litmus_fork(struct task_struct *tsk); | ||
| 34 | void litmus_exec(void); | ||
| 35 | /* clean up real-time state of a task */ | ||
| 36 | void litmus_clear_state(struct task_struct *dead_tsk); | ||
| 37 | void exit_litmus(struct task_struct *dead_tsk); | ||
| 38 | |||
| 39 | /* Prevent the plugin from being switched-out from underneath a code | ||
| 40 | * path. Might sleep, so may be called only from non-atomic context. */ | ||
| 41 | void litmus_plugin_switch_disable(void); | ||
| 42 | void litmus_plugin_switch_enable(void); | ||
| 43 | |||
| 44 | long litmus_admit_task(struct task_struct *tsk); | ||
| 45 | void litmus_exit_task(struct task_struct *tsk); | ||
| 46 | void litmus_dealloc(struct task_struct *tsk); | ||
| 47 | void litmus_do_exit(struct task_struct *tsk); | ||
| 48 | int litmus_be_migrate_to(int cpu); | ||
| 49 | |||
| 9 | #define is_realtime(t) ((t)->policy == SCHED_LITMUS) | 50 | #define is_realtime(t) ((t)->policy == SCHED_LITMUS) |
| 51 | #define rt_transition_pending(t) \ | ||
| 52 | ((t)->rt_param.transition_pending) | ||
| 10 | 53 | ||
| 11 | #define tsk_rt(t) (&(t)->rt_param) | 54 | #define tsk_rt(t) (&(t)->rt_param) |
| 12 | 55 | ||
| @@ -28,6 +71,7 @@ | |||
| 28 | #define get_partition(t) (tsk_rt(t)->task_params.cpu) | 71 | #define get_partition(t) (tsk_rt(t)->task_params.cpu) |
| 29 | #define get_priority(t) (tsk_rt(t)->task_params.priority) | 72 | #define get_priority(t) (tsk_rt(t)->task_params.priority) |
| 30 | #define get_class(t) (tsk_rt(t)->task_params.cls) | 73 | #define get_class(t) (tsk_rt(t)->task_params.cls) |
| 74 | #define get_release_policy(t) (tsk_rt(t)->task_params.release_policy) | ||
| 31 | 75 | ||
| 32 | /* job_param macros */ | 76 | /* job_param macros */ |
| 33 | #define get_exec_time(t) (tsk_rt(t)->job_params.exec_time) | 77 | #define get_exec_time(t) (tsk_rt(t)->job_params.exec_time) |
| @@ -35,6 +79,15 @@ | |||
| 35 | #define get_release(t) (tsk_rt(t)->job_params.release) | 79 | #define get_release(t) (tsk_rt(t)->job_params.release) |
| 36 | #define get_lateness(t) (tsk_rt(t)->job_params.lateness) | 80 | #define get_lateness(t) (tsk_rt(t)->job_params.lateness) |
| 37 | 81 | ||
| 82 | /* release policy macros */ | ||
| 83 | #define is_periodic(t) (get_release_policy(t) == TASK_PERIODIC) | ||
| 84 | #define is_sporadic(t) (get_release_policy(t) == TASK_SPORADIC) | ||
| 85 | #ifdef CONFIG_ALLOW_EARLY_RELEASE | ||
| 86 | #define is_early_releasing(t) (get_release_policy(t) == TASK_EARLY) | ||
| 87 | #else | ||
| 88 | #define is_early_releasing(t) (0) | ||
| 89 | #endif | ||
| 90 | |||
| 38 | #define is_hrt(t) \ | 91 | #define is_hrt(t) \ |
| 39 | (tsk_rt(t)->task_params.cls == RT_CLASS_HARD) | 92 | (tsk_rt(t)->task_params.cls == RT_CLASS_HARD) |
| 40 | #define is_srt(t) \ | 93 | #define is_srt(t) \ |
| @@ -48,6 +101,67 @@ static inline lt_t litmus_clock(void) | |||
| 48 | return ktime_to_ns(ktime_get()); | 101 | return ktime_to_ns(ktime_get()); |
| 49 | } | 102 | } |
| 50 | 103 | ||
| 104 | /* A macro to convert from nanoseconds to ktime_t. */ | ||
| 105 | #define ns_to_ktime(t) ktime_add_ns(ktime_set(0, 0), t) | ||
| 106 | |||
| 107 | #define is_released(t, now) \ | ||
| 108 | (lt_before_eq(get_release(t), now)) | ||
| 109 | #define is_tardy(t, now) \ | ||
| 110 | (lt_before_eq(tsk_rt(t)->job_params.deadline, now)) | ||
| 111 | |||
| 112 | /* real-time comparison macros */ | ||
| 113 | #define earlier_deadline(a, b) (lt_before(\ | ||
| 114 | (a)->rt_param.job_params.deadline,\ | ||
| 115 | (b)->rt_param.job_params.deadline)) | ||
| 116 | #define earlier_release(a, b) (lt_before(\ | ||
| 117 | (a)->rt_param.job_params.release,\ | ||
| 118 | (b)->rt_param.job_params.release)) | ||
| 119 | |||
| 120 | void preempt_if_preemptable(struct task_struct* t, int on_cpu); | ||
| 121 | |||
| 122 | #define bheap2task(hn) ((struct task_struct*) hn->value) | ||
| 123 | |||
| 124 | static inline int is_present(struct task_struct* t) | ||
| 125 | { | ||
| 126 | return t && tsk_rt(t)->present; | ||
| 127 | } | ||
| 128 | |||
| 129 | static inline int is_completed(struct task_struct* t) | ||
| 130 | { | ||
| 131 | return t && tsk_rt(t)->completed; | ||
| 132 | } | ||
| 133 | |||
| 134 | |||
| 135 | /* Used to convert ns-specified execution costs and periods into | ||
| 136 | * integral quanta equivalents. | ||
| 137 | */ | ||
| 138 | #define LITMUS_QUANTUM_LENGTH_NS (CONFIG_LITMUS_QUANTUM_LENGTH_US * 1000ULL) | ||
| 139 | |||
| 140 | /* make the unit explicit */ | ||
| 141 | typedef unsigned long quanta_t; | ||
| 142 | |||
| 143 | enum round { | ||
| 144 | FLOOR, | ||
| 145 | CEIL | ||
| 146 | }; | ||
| 147 | |||
| 148 | static inline quanta_t time2quanta(lt_t time, enum round round) | ||
| 149 | { | ||
| 150 | s64 quantum_length = LITMUS_QUANTUM_LENGTH_NS; | ||
| 151 | |||
| 152 | if (do_div(time, quantum_length) && round == CEIL) | ||
| 153 | time++; | ||
| 154 | return (quanta_t) time; | ||
| 155 | } | ||
| 156 | |||
| 157 | static inline lt_t quanta2time(quanta_t quanta) | ||
| 158 | { | ||
| 159 | return quanta * LITMUS_QUANTUM_LENGTH_NS; | ||
| 160 | } | ||
| 161 | |||
| 162 | /* By how much is cpu staggered behind CPU 0? */ | ||
| 163 | u64 cpu_stagger_offset(int cpu); | ||
| 164 | |||
| 51 | static inline struct control_page* get_control_page(struct task_struct *t) | 165 | static inline struct control_page* get_control_page(struct task_struct *t) |
| 52 | { | 166 | { |
| 53 | return tsk_rt(t)->ctrl_page; | 167 | return tsk_rt(t)->ctrl_page; |
| @@ -58,4 +172,53 @@ static inline int has_control_page(struct task_struct* t) | |||
| 58 | return tsk_rt(t)->ctrl_page != NULL; | 172 | return tsk_rt(t)->ctrl_page != NULL; |
| 59 | } | 173 | } |
| 60 | 174 | ||
| 175 | |||
| 176 | #ifdef CONFIG_SCHED_OVERHEAD_TRACE | ||
| 177 | |||
| 178 | #define TS_SYSCALL_IN_START \ | ||
| 179 | if (has_control_page(current)) { \ | ||
| 180 | __TS_SYSCALL_IN_START(&get_control_page(current)->ts_syscall_start); \ | ||
| 181 | } | ||
| 182 | |||
| 183 | #define TS_SYSCALL_IN_END \ | ||
| 184 | if (has_control_page(current)) { \ | ||
| 185 | unsigned long flags; \ | ||
| 186 | uint64_t irqs; \ | ||
| 187 | local_irq_save(flags); \ | ||
| 188 | irqs = get_control_page(current)->irq_count - \ | ||
| 189 | get_control_page(current)->irq_syscall_start; \ | ||
| 190 | __TS_SYSCALL_IN_END(&irqs); \ | ||
| 191 | local_irq_restore(flags); \ | ||
| 192 | } | ||
| 193 | |||
| 194 | #else | ||
| 195 | |||
| 196 | #define TS_SYSCALL_IN_START | ||
| 197 | #define TS_SYSCALL_IN_END | ||
| 198 | |||
| 199 | #endif | ||
| 200 | |||
| 201 | #ifdef CONFIG_SMP | ||
| 202 | |||
| 203 | /* | ||
| 204 | * struct hrtimer_start_on_info - timer info on remote cpu | ||
| 205 | * @timer: timer to be triggered on remote cpu | ||
| 206 | * @time: time event | ||
| 207 | * @mode: timer mode | ||
| 208 | * @csd: smp_call_function parameter to call hrtimer_pull on remote cpu | ||
| 209 | */ | ||
| 210 | struct hrtimer_start_on_info { | ||
| 211 | struct hrtimer *timer; | ||
| 212 | ktime_t time; | ||
| 213 | enum hrtimer_mode mode; | ||
| 214 | struct call_single_data csd; | ||
| 215 | }; | ||
| 216 | |||
| 217 | void hrtimer_pull(void *csd_info); | ||
| 218 | extern void hrtimer_start_on(int cpu, struct hrtimer_start_on_info *info, | ||
| 219 | struct hrtimer *timer, ktime_t time, | ||
| 220 | const enum hrtimer_mode mode); | ||
| 221 | |||
| 222 | #endif | ||
| 223 | |||
| 61 | #endif | 224 | #endif |
diff --git a/include/litmus/litmus_proc.h b/include/litmus/litmus_proc.h new file mode 100644 index 000000000000..a5db24c03ec0 --- /dev/null +++ b/include/litmus/litmus_proc.h | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | #include <litmus/sched_plugin.h> | ||
| 2 | #include <linux/proc_fs.h> | ||
| 3 | |||
| 4 | int __init init_litmus_proc(void); | ||
| 5 | void exit_litmus_proc(void); | ||
| 6 | |||
| 7 | struct cd_mapping | ||
| 8 | { | ||
| 9 | int id; | ||
| 10 | cpumask_var_t mask; | ||
| 11 | struct proc_dir_entry *proc_file; | ||
| 12 | }; | ||
| 13 | |||
| 14 | struct domain_proc_info | ||
| 15 | { | ||
| 16 | int num_cpus; | ||
| 17 | int num_domains; | ||
| 18 | |||
| 19 | struct cd_mapping *cpu_to_domains; | ||
| 20 | struct cd_mapping *domain_to_cpus; | ||
| 21 | }; | ||
| 22 | |||
| 23 | /* | ||
| 24 | * On success, returns 0 and sets the pointer to the location of the new | ||
| 25 | * proc dir entry, otherwise returns an error code and sets pde to NULL. | ||
| 26 | */ | ||
| 27 | long make_plugin_proc_dir(struct sched_plugin* plugin, | ||
| 28 | struct proc_dir_entry** pde); | ||
| 29 | |||
| 30 | /* | ||
| 31 | * Plugins should deallocate all child proc directory entries before | ||
| 32 | * calling this, to avoid memory leaks. | ||
| 33 | */ | ||
| 34 | void remove_plugin_proc_dir(struct sched_plugin* plugin); | ||
| 35 | |||
| 36 | /* | ||
| 37 | * Setup the CPU <-> sched domain mappings in proc | ||
| 38 | */ | ||
| 39 | long activate_domain_proc(struct domain_proc_info* map); | ||
| 40 | |||
| 41 | /* | ||
| 42 | * Remove the CPU <-> sched domain mappings from proc | ||
| 43 | */ | ||
| 44 | long deactivate_domain_proc(void); | ||
| 45 | |||
| 46 | /* | ||
| 47 | * Alloc memory for the mapping | ||
| 48 | * Note: Does not set up proc files. Use make_sched_domain_maps for that. | ||
| 49 | */ | ||
| 50 | long init_domain_proc_info(struct domain_proc_info* map, | ||
| 51 | int num_cpus, int num_domains); | ||
| 52 | |||
| 53 | /* | ||
| 54 | * Free memory of the mapping | ||
| 55 | * Note: Does not clean up proc files. Use deactivate_domain_proc for that. | ||
| 56 | */ | ||
| 57 | void destroy_domain_proc_info(struct domain_proc_info* map); | ||
| 58 | |||
| 59 | /* Copy at most size-1 bytes from ubuf into kbuf, null-terminate buf, and | ||
| 60 | * remove a '\n' if present. Returns the number of bytes that were read or | ||
| 61 | * -EFAULT. */ | ||
| 62 | int copy_and_chomp(char *kbuf, unsigned long ksize, | ||
| 63 | __user const char* ubuf, unsigned long ulength); | ||
diff --git a/include/litmus/locking.h b/include/litmus/locking.h new file mode 100644 index 000000000000..4d7b870cb443 --- /dev/null +++ b/include/litmus/locking.h | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | #ifndef LITMUS_LOCKING_H | ||
| 2 | #define LITMUS_LOCKING_H | ||
| 3 | |||
| 4 | struct litmus_lock_ops; | ||
| 5 | |||
| 6 | /* Generic base struct for LITMUS^RT userspace semaphores. | ||
| 7 | * This structure should be embedded in protocol-specific semaphores. | ||
| 8 | */ | ||
| 9 | struct litmus_lock { | ||
| 10 | struct litmus_lock_ops *ops; | ||
| 11 | int type; | ||
| 12 | }; | ||
| 13 | |||
| 14 | struct litmus_lock_ops { | ||
| 15 | /* Current task tries to obtain / drop a reference to a lock. | ||
| 16 | * Optional methods, allowed by default. */ | ||
| 17 | int (*open)(struct litmus_lock*, void* __user); | ||
| 18 | int (*close)(struct litmus_lock*); | ||
| 19 | |||
| 20 | /* Current tries to lock/unlock this lock (mandatory methods). */ | ||
| 21 | int (*lock)(struct litmus_lock*); | ||
| 22 | int (*unlock)(struct litmus_lock*); | ||
| 23 | |||
| 24 | /* The lock is no longer being referenced (mandatory method). */ | ||
| 25 | void (*deallocate)(struct litmus_lock*); | ||
| 26 | }; | ||
| 27 | |||
| 28 | #endif | ||
diff --git a/include/litmus/np.h b/include/litmus/np.h new file mode 100644 index 000000000000..dbe2b695f74a --- /dev/null +++ b/include/litmus/np.h | |||
| @@ -0,0 +1,121 @@ | |||
| 1 | #ifndef _LITMUS_NP_H_ | ||
| 2 | #define _LITMUS_NP_H_ | ||
| 3 | |||
| 4 | /* Definitions related to non-preemptive sections signaled via the control | ||
| 5 | * page | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifdef CONFIG_NP_SECTION | ||
| 9 | |||
| 10 | static inline int is_kernel_np(struct task_struct *t) | ||
| 11 | { | ||
| 12 | return tsk_rt(t)->kernel_np; | ||
| 13 | } | ||
| 14 | |||
| 15 | static inline int is_user_np(struct task_struct *t) | ||
| 16 | { | ||
| 17 | return tsk_rt(t)->ctrl_page ? tsk_rt(t)->ctrl_page->sched.np.flag : 0; | ||
| 18 | } | ||
| 19 | |||
| 20 | static inline void request_exit_np(struct task_struct *t) | ||
| 21 | { | ||
| 22 | if (is_user_np(t)) { | ||
| 23 | /* Set the flag that tells user space to call | ||
| 24 | * into the kernel at the end of a critical section. */ | ||
| 25 | if (likely(tsk_rt(t)->ctrl_page)) { | ||
| 26 | TRACE_TASK(t, "setting delayed_preemption flag\n"); | ||
| 27 | tsk_rt(t)->ctrl_page->sched.np.preempt = 1; | ||
| 28 | } | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | static inline void make_np(struct task_struct *t) | ||
| 33 | { | ||
| 34 | tsk_rt(t)->kernel_np++; | ||
| 35 | } | ||
| 36 | |||
| 37 | /* Caller should check if preemption is necessary when | ||
| 38 | * the function return 0. | ||
| 39 | */ | ||
| 40 | static inline int take_np(struct task_struct *t) | ||
| 41 | { | ||
| 42 | return --tsk_rt(t)->kernel_np; | ||
| 43 | } | ||
| 44 | |||
| 45 | /* returns 0 if remote CPU needs an IPI to preempt, 1 if no IPI is required */ | ||
| 46 | static inline int request_exit_np_atomic(struct task_struct *t) | ||
| 47 | { | ||
| 48 | union np_flag old, new; | ||
| 49 | |||
| 50 | if (tsk_rt(t)->ctrl_page) { | ||
| 51 | old.raw = tsk_rt(t)->ctrl_page->sched.raw; | ||
| 52 | if (old.np.flag == 0) { | ||
| 53 | /* no longer non-preemptive */ | ||
| 54 | return 0; | ||
| 55 | } else if (old.np.preempt) { | ||
| 56 | /* already set, nothing for us to do */ | ||
| 57 | return 1; | ||
| 58 | } else { | ||
| 59 | /* non preemptive and flag not set */ | ||
| 60 | new.raw = old.raw; | ||
| 61 | new.np.preempt = 1; | ||
| 62 | /* if we get old back, then we atomically set the flag */ | ||
| 63 | return cmpxchg(&tsk_rt(t)->ctrl_page->sched.raw, old.raw, new.raw) == old.raw; | ||
| 64 | /* If we raced with a concurrent change, then so be | ||
| 65 | * it. Deliver it by IPI. We don't want an unbounded | ||
| 66 | * retry loop here since tasks might exploit that to | ||
| 67 | * keep the kernel busy indefinitely. */ | ||
| 68 | } | ||
| 69 | } else | ||
| 70 | return 0; | ||
| 71 | } | ||
| 72 | |||
| 73 | #else | ||
| 74 | |||
| 75 | static inline int is_kernel_np(struct task_struct* t) | ||
| 76 | { | ||
| 77 | return 0; | ||
| 78 | } | ||
| 79 | |||
| 80 | static inline int is_user_np(struct task_struct* t) | ||
| 81 | { | ||
| 82 | return 0; | ||
| 83 | } | ||
| 84 | |||
| 85 | static inline void request_exit_np(struct task_struct *t) | ||
| 86 | { | ||
| 87 | /* request_exit_np() shouldn't be called if !CONFIG_NP_SECTION */ | ||
| 88 | BUG(); | ||
| 89 | } | ||
| 90 | |||
| 91 | static inline int request_exit_np_atomic(struct task_struct *t) | ||
| 92 | { | ||
| 93 | return 0; | ||
| 94 | } | ||
| 95 | |||
| 96 | #endif | ||
| 97 | |||
| 98 | static inline void clear_exit_np(struct task_struct *t) | ||
| 99 | { | ||
| 100 | if (likely(tsk_rt(t)->ctrl_page)) | ||
| 101 | tsk_rt(t)->ctrl_page->sched.np.preempt = 0; | ||
| 102 | } | ||
| 103 | |||
| 104 | static inline int is_np(struct task_struct *t) | ||
| 105 | { | ||
| 106 | #ifdef CONFIG_SCHED_DEBUG_TRACE | ||
| 107 | int kernel, user; | ||
| 108 | kernel = is_kernel_np(t); | ||
| 109 | user = is_user_np(t); | ||
| 110 | if (kernel || user) | ||
| 111 | TRACE_TASK(t, " is non-preemptive: kernel=%d user=%d\n", | ||
| 112 | |||
| 113 | kernel, user); | ||
| 114 | return kernel || user; | ||
| 115 | #else | ||
| 116 | return unlikely(is_kernel_np(t) || is_user_np(t)); | ||
| 117 | #endif | ||
| 118 | } | ||
| 119 | |||
| 120 | #endif | ||
| 121 | |||
diff --git a/include/litmus/preempt.h b/include/litmus/preempt.h new file mode 100644 index 000000000000..8f8bb635cb21 --- /dev/null +++ b/include/litmus/preempt.h | |||
| @@ -0,0 +1,188 @@ | |||
| 1 | #ifndef LITMUS_PREEMPT_H | ||
| 2 | #define LITMUS_PREEMPT_H | ||
| 3 | |||
| 4 | #include <linux/types.h> | ||
| 5 | #include <linux/cache.h> | ||
| 6 | #include <linux/percpu.h> | ||
| 7 | #include <asm/atomic.h> | ||
| 8 | |||
| 9 | #include <litmus/debug_trace.h> | ||
| 10 | |||
| 11 | DECLARE_PER_CPU(bool, litmus_preemption_in_progress); | ||
| 12 | |||
| 13 | /* is_current_running() is legacy macro (and a hack) that is used to make | ||
| 14 | * the plugin logic, which still stems from the 2.6.20 era, work with current | ||
| 15 | * kernels. | ||
| 16 | * | ||
| 17 | * It used to honor the flag in the preempt_count variable that was | ||
| 18 | * set when scheduling is in progress. This doesn't exist anymore in recent | ||
| 19 | * Linux versions. Instead, Linux has moved to passing a 'preempt' flag to | ||
| 20 | * __schedule(). In particular, Linux ignores prev->state != TASK_RUNNING and | ||
| 21 | * does *not* process self-suspensions if an interrupt (i.e., a preemption) | ||
| 22 | * races with a task that is about to call schedule() anyway. | ||
| 23 | * | ||
| 24 | * The value of the 'preempt' flag in __schedule() is crucial | ||
| 25 | * information for some of the LITMUS^RT plugins, which must re-add | ||
| 26 | * soon-to-block tasks to the ready queue if the rest of the system doesn't | ||
| 27 | * process the preemption yet. Unfortunately, the flag is not passed to | ||
| 28 | * pick_next_task(). Hence, as a hack, we communicate it out of band via the | ||
| 29 | * global, per-core variable litmus_preemption_in_progress, which is set by | ||
| 30 | * the scheduler in __schedule() and read by the plugins via the | ||
| 31 | * is_current_running() macro. | ||
| 32 | */ | ||
| 33 | #define is_current_running() \ | ||
| 34 | ((current)->state == TASK_RUNNING || \ | ||
| 35 | this_cpu_read(litmus_preemption_in_progress)) | ||
| 36 | |||
| 37 | DECLARE_PER_CPU_SHARED_ALIGNED(atomic_t, resched_state); | ||
| 38 | |||
| 39 | #ifdef CONFIG_PREEMPT_STATE_TRACE | ||
| 40 | const char* sched_state_name(int s); | ||
| 41 | #define TRACE_STATE(fmt, args...) TRACE("SCHED_STATE " fmt, args) | ||
| 42 | #else | ||
| 43 | #define TRACE_STATE(fmt, args...) /* ignore */ | ||
| 44 | #endif | ||
| 45 | |||
| 46 | #define VERIFY_SCHED_STATE(x) \ | ||
| 47 | do { int __s = get_sched_state(); \ | ||
| 48 | if ((__s & (x)) == 0) \ | ||
| 49 | TRACE_STATE("INVALID s=0x%x (%s) not " \ | ||
| 50 | "in 0x%x (%s) [%s]\n", \ | ||
| 51 | __s, sched_state_name(__s), \ | ||
| 52 | (x), #x, __FUNCTION__); \ | ||
| 53 | } while (0); | ||
| 54 | |||
| 55 | #define TRACE_SCHED_STATE_CHANGE(x, y, cpu) \ | ||
| 56 | TRACE_STATE("[P%d] 0x%x (%s) -> 0x%x (%s)\n", \ | ||
| 57 | cpu, (x), sched_state_name(x), \ | ||
| 58 | (y), sched_state_name(y)) | ||
| 59 | |||
| 60 | |||
| 61 | typedef enum scheduling_state { | ||
| 62 | TASK_SCHEDULED = (1 << 0), /* The currently scheduled task is the one that | ||
| 63 | * should be scheduled, and the processor does not | ||
| 64 | * plan to invoke schedule(). */ | ||
| 65 | SHOULD_SCHEDULE = (1 << 1), /* A remote processor has determined that the | ||
| 66 | * processor should reschedule, but this has not | ||
| 67 | * been communicated yet (IPI still pending). */ | ||
| 68 | WILL_SCHEDULE = (1 << 2), /* The processor has noticed that it has to | ||
| 69 | * reschedule and will do so shortly. */ | ||
| 70 | TASK_PICKED = (1 << 3), /* The processor is currently executing schedule(), | ||
| 71 | * has selected a new task to schedule, but has not | ||
| 72 | * yet performed the actual context switch. */ | ||
| 73 | PICKED_WRONG_TASK = (1 << 4), /* The processor has not yet performed the context | ||
| 74 | * switch, but a remote processor has already | ||
| 75 | * determined that a higher-priority task became | ||
| 76 | * eligible after the task was picked. */ | ||
| 77 | } sched_state_t; | ||
| 78 | |||
| 79 | static inline sched_state_t get_sched_state_on(int cpu) | ||
| 80 | { | ||
| 81 | return atomic_read(&per_cpu(resched_state, cpu)); | ||
| 82 | } | ||
| 83 | |||
| 84 | static inline sched_state_t get_sched_state(void) | ||
| 85 | { | ||
| 86 | return atomic_read(this_cpu_ptr(&resched_state)); | ||
| 87 | } | ||
| 88 | |||
| 89 | static inline int is_in_sched_state(int possible_states) | ||
| 90 | { | ||
| 91 | return get_sched_state() & possible_states; | ||
| 92 | } | ||
| 93 | |||
| 94 | static inline int cpu_is_in_sched_state(int cpu, int possible_states) | ||
| 95 | { | ||
| 96 | return get_sched_state_on(cpu) & possible_states; | ||
| 97 | } | ||
| 98 | |||
| 99 | static inline void set_sched_state(sched_state_t s) | ||
| 100 | { | ||
| 101 | TRACE_SCHED_STATE_CHANGE(get_sched_state(), s, smp_processor_id()); | ||
| 102 | atomic_set(this_cpu_ptr(&resched_state), s); | ||
| 103 | } | ||
| 104 | |||
| 105 | static inline int sched_state_transition(sched_state_t from, sched_state_t to) | ||
| 106 | { | ||
| 107 | sched_state_t old_state; | ||
| 108 | |||
| 109 | old_state = atomic_cmpxchg(this_cpu_ptr(&resched_state), from, to); | ||
| 110 | if (old_state == from) { | ||
| 111 | TRACE_SCHED_STATE_CHANGE(from, to, smp_processor_id()); | ||
| 112 | return 1; | ||
| 113 | } else | ||
| 114 | return 0; | ||
| 115 | } | ||
| 116 | |||
| 117 | static inline int sched_state_transition_on(int cpu, | ||
| 118 | sched_state_t from, | ||
| 119 | sched_state_t to) | ||
| 120 | { | ||
| 121 | sched_state_t old_state; | ||
| 122 | |||
| 123 | old_state = atomic_cmpxchg(&per_cpu(resched_state, cpu), from, to); | ||
| 124 | if (old_state == from) { | ||
| 125 | TRACE_SCHED_STATE_CHANGE(from, to, cpu); | ||
| 126 | return 1; | ||
| 127 | } else | ||
| 128 | return 0; | ||
| 129 | } | ||
| 130 | |||
| 131 | /* Plugins must call this function after they have decided which job to | ||
| 132 | * schedule next. IMPORTANT: this function must be called while still holding | ||
| 133 | * the lock that is used to serialize scheduling decisions. | ||
| 134 | * | ||
| 135 | * (Ideally, we would like to use runqueue locks for this purpose, but that | ||
| 136 | * would lead to deadlocks with the migration code.) | ||
| 137 | */ | ||
| 138 | static inline void sched_state_task_picked(void) | ||
| 139 | { | ||
| 140 | VERIFY_SCHED_STATE(WILL_SCHEDULE); | ||
| 141 | |||
| 142 | /* WILL_SCHEDULE has only a local tansition => simple store is ok */ | ||
| 143 | set_sched_state(TASK_PICKED); | ||
| 144 | } | ||
| 145 | |||
| 146 | static inline void sched_state_entered_schedule(void) | ||
| 147 | { | ||
| 148 | /* Update state for the case that we entered schedule() not due to | ||
| 149 | * set_tsk_need_resched() */ | ||
| 150 | set_sched_state(WILL_SCHEDULE); | ||
| 151 | } | ||
| 152 | |||
| 153 | /* Called by schedule() to check if the scheduling decision is still valid | ||
| 154 | * after a context switch. Returns 1 if the CPU needs to reschdule. */ | ||
| 155 | static inline int sched_state_validate_switch(void) | ||
| 156 | { | ||
| 157 | int decision_ok = 0; | ||
| 158 | |||
| 159 | VERIFY_SCHED_STATE(PICKED_WRONG_TASK | TASK_PICKED | WILL_SCHEDULE); | ||
| 160 | |||
| 161 | if (is_in_sched_state(TASK_PICKED)) { | ||
| 162 | /* Might be good; let's try to transition out of this | ||
| 163 | * state. This must be done atomically since remote processors | ||
| 164 | * may try to change the state, too. */ | ||
| 165 | decision_ok = sched_state_transition(TASK_PICKED, TASK_SCHEDULED); | ||
| 166 | } | ||
| 167 | |||
| 168 | if (!decision_ok) | ||
| 169 | TRACE_STATE("validation failed (%s)\n", | ||
| 170 | sched_state_name(get_sched_state())); | ||
| 171 | |||
| 172 | return !decision_ok; | ||
| 173 | } | ||
| 174 | |||
| 175 | /* State transition events. See litmus/preempt.c for details. */ | ||
| 176 | void sched_state_will_schedule(struct task_struct* tsk); | ||
| 177 | void sched_state_ipi(void); | ||
| 178 | /* Cause a CPU (remote or local) to reschedule. */ | ||
| 179 | void litmus_reschedule(int cpu); | ||
| 180 | void litmus_reschedule_local(void); | ||
| 181 | |||
| 182 | #ifdef CONFIG_DEBUG_KERNEL | ||
| 183 | void sched_state_plugin_check(void); | ||
| 184 | #else | ||
| 185 | #define sched_state_plugin_check() /* no check */ | ||
| 186 | #endif | ||
| 187 | |||
| 188 | #endif | ||
diff --git a/include/litmus/reservations/alloc.h b/include/litmus/reservations/alloc.h new file mode 100644 index 000000000000..b3471288c9f1 --- /dev/null +++ b/include/litmus/reservations/alloc.h | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | #ifndef LITMUS_RESERVATIONS_ALLOC_H | ||
| 2 | #define LITMUS_RESERVATIONS_ALLOC_H | ||
| 3 | |||
| 4 | #include <litmus/reservations/reservation.h> | ||
| 5 | |||
| 6 | long alloc_polling_reservation( | ||
| 7 | int res_type, | ||
| 8 | struct reservation_config *config, | ||
| 9 | struct reservation **_res); | ||
| 10 | |||
| 11 | long alloc_table_driven_reservation( | ||
| 12 | struct reservation_config *config, | ||
| 13 | struct reservation **_res); | ||
| 14 | |||
| 15 | #endif \ No newline at end of file | ||
diff --git a/include/litmus/reservations/budget-notifier.h b/include/litmus/reservations/budget-notifier.h new file mode 100644 index 000000000000..d831fa9d5153 --- /dev/null +++ b/include/litmus/reservations/budget-notifier.h | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | #ifndef LITMUS_BUDGET_NOTIFIER_H | ||
| 2 | #define LITMUS_BUDGET_NOTIFIER_H | ||
| 3 | |||
| 4 | #include <linux/list.h> | ||
| 5 | #include <linux/spinlock.h> | ||
| 6 | |||
| 7 | struct budget_notifier; | ||
| 8 | |||
| 9 | typedef void (*budget_callback_t) ( | ||
| 10 | struct budget_notifier *bn | ||
| 11 | ); | ||
| 12 | |||
| 13 | struct budget_notifier { | ||
| 14 | struct list_head list; | ||
| 15 | budget_callback_t budget_exhausted; | ||
| 16 | budget_callback_t budget_replenished; | ||
| 17 | }; | ||
| 18 | |||
| 19 | struct budget_notifier_list { | ||
| 20 | struct list_head list; | ||
| 21 | raw_spinlock_t lock; | ||
| 22 | }; | ||
| 23 | |||
| 24 | void budget_notifier_list_init(struct budget_notifier_list* bnl); | ||
| 25 | |||
| 26 | static inline void budget_notifier_add( | ||
| 27 | struct budget_notifier_list *bnl, | ||
| 28 | struct budget_notifier *bn) | ||
| 29 | { | ||
| 30 | unsigned long flags; | ||
| 31 | |||
| 32 | raw_spin_lock_irqsave(&bnl->lock, flags); | ||
| 33 | list_add(&bn->list, &bnl->list); | ||
| 34 | raw_spin_unlock_irqrestore(&bnl->lock, flags); | ||
| 35 | } | ||
| 36 | |||
| 37 | static inline void budget_notifier_remove( | ||
| 38 | struct budget_notifier_list *bnl, | ||
| 39 | struct budget_notifier *bn) | ||
| 40 | { | ||
| 41 | unsigned long flags; | ||
| 42 | |||
| 43 | raw_spin_lock_irqsave(&bnl->lock, flags); | ||
| 44 | list_del(&bn->list); | ||
| 45 | raw_spin_unlock_irqrestore(&bnl->lock, flags); | ||
| 46 | } | ||
| 47 | |||
| 48 | void budget_notifiers_fire(struct budget_notifier_list *bnl, bool replenished); | ||
| 49 | |||
| 50 | #endif | ||
diff --git a/include/litmus/reservations/polling.h b/include/litmus/reservations/polling.h new file mode 100644 index 000000000000..230e12b1088a --- /dev/null +++ b/include/litmus/reservations/polling.h | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | #ifndef LITMUS_POLLING_RESERVATIONS_H | ||
| 2 | #define LITMUS_POLLING_RESERVATIONS_H | ||
| 3 | |||
| 4 | #include <litmus/reservations/reservation.h> | ||
| 5 | |||
| 6 | struct polling_reservation { | ||
| 7 | /* extend basic reservation */ | ||
| 8 | struct reservation res; | ||
| 9 | |||
| 10 | lt_t max_budget; | ||
| 11 | lt_t period; | ||
| 12 | lt_t deadline; | ||
| 13 | lt_t offset; | ||
| 14 | }; | ||
| 15 | |||
| 16 | void polling_reservation_init(struct polling_reservation *pres, int use_edf_prio, | ||
| 17 | int use_periodic_polling, lt_t budget, lt_t period, lt_t deadline, lt_t offset); | ||
| 18 | |||
| 19 | #endif | ||
diff --git a/include/litmus/reservations/reservation.h b/include/litmus/reservations/reservation.h new file mode 100644 index 000000000000..1752dac4e698 --- /dev/null +++ b/include/litmus/reservations/reservation.h | |||
| @@ -0,0 +1,224 @@ | |||
| 1 | #ifndef LITMUS_RESERVATION_H | ||
| 2 | #define LITMUS_RESERVATION_H | ||
| 3 | |||
| 4 | #include <linux/list.h> | ||
| 5 | #include <linux/hrtimer.h> | ||
| 6 | |||
| 7 | #include <litmus/debug_trace.h> | ||
| 8 | #include <litmus/reservations/budget-notifier.h> | ||
| 9 | |||
| 10 | struct reservation_client; | ||
| 11 | struct reservation_environment; | ||
| 12 | struct reservation; | ||
| 13 | |||
| 14 | typedef enum { | ||
| 15 | /* reservation has no clients, is not consuming budget */ | ||
| 16 | RESERVATION_INACTIVE = 0, | ||
| 17 | |||
| 18 | /* reservation has clients, consumes budget when scheduled */ | ||
| 19 | RESERVATION_ACTIVE, | ||
| 20 | |||
| 21 | /* reservation has no clients, but may be consuming budget */ | ||
| 22 | RESERVATION_ACTIVE_IDLE, | ||
| 23 | |||
| 24 | /* Reservation has no budget and waits for | ||
| 25 | * replenishment. May or may not have clients. */ | ||
| 26 | RESERVATION_DEPLETED, | ||
| 27 | } reservation_state_t; | ||
| 28 | |||
| 29 | |||
| 30 | /* ************************************************************************** */ | ||
| 31 | |||
| 32 | /* Select which task to dispatch. If NULL is returned, it means there is nothing | ||
| 33 | * to schedule right now and background work can be scheduled. */ | ||
| 34 | typedef struct task_struct * (*dispatch_t) ( | ||
| 35 | struct reservation_client *client | ||
| 36 | ); | ||
| 37 | |||
| 38 | /* Something that can be managed in a reservation and that can yield | ||
| 39 | * a process for dispatching. Contains a pointer to the reservation | ||
| 40 | * to which it "belongs". */ | ||
| 41 | struct reservation_client { | ||
| 42 | struct list_head list; | ||
| 43 | struct reservation* reservation; | ||
| 44 | dispatch_t dispatch; | ||
| 45 | }; | ||
| 46 | |||
| 47 | |||
| 48 | /* ************************************************************************** */ | ||
| 49 | |||
| 50 | /* Called by reservations to request state change. */ | ||
| 51 | typedef void (*reservation_change_state_t) ( | ||
| 52 | struct reservation_environment* env, | ||
| 53 | struct reservation *res, | ||
| 54 | reservation_state_t new_state | ||
| 55 | ); | ||
| 56 | |||
| 57 | /* Called by reservations to request replenishment while not DEPLETED. | ||
| 58 | * Useful for soft reservations that remain ACTIVE with lower priority. */ | ||
| 59 | typedef void (*request_replenishment_t)( | ||
| 60 | struct reservation_environment* env, | ||
| 61 | struct reservation *res | ||
| 62 | ); | ||
| 63 | |||
| 64 | /* The framework within wich reservations operate. */ | ||
| 65 | struct reservation_environment { | ||
| 66 | lt_t time_zero; | ||
| 67 | lt_t current_time; | ||
| 68 | |||
| 69 | /* services invoked by reservations */ | ||
| 70 | reservation_change_state_t change_state; | ||
| 71 | request_replenishment_t request_replenishment; | ||
| 72 | }; | ||
| 73 | |||
| 74 | /* ************************************************************************** */ | ||
| 75 | |||
| 76 | /* A new client is added or an existing client resumes. */ | ||
| 77 | typedef void (*client_arrives_t) ( | ||
| 78 | struct reservation *reservation, | ||
| 79 | struct reservation_client *client | ||
| 80 | ); | ||
| 81 | |||
| 82 | /* A client suspends or terminates. */ | ||
| 83 | typedef void (*client_departs_t) ( | ||
| 84 | struct reservation *reservation, | ||
| 85 | struct reservation_client *client, | ||
| 86 | int did_signal_job_completion | ||
| 87 | ); | ||
| 88 | |||
| 89 | /* A previously requested replenishment has occurred. */ | ||
| 90 | typedef void (*on_replenishment_timer_t) ( | ||
| 91 | struct reservation *reservation | ||
| 92 | ); | ||
| 93 | |||
| 94 | /* Update the reservation's budget to reflect execution or idling. */ | ||
| 95 | typedef void (*drain_budget_t) ( | ||
| 96 | struct reservation *reservation, | ||
| 97 | lt_t how_much | ||
| 98 | ); | ||
| 99 | |||
| 100 | /* Select a ready task from one of the clients for scheduling. */ | ||
| 101 | typedef struct task_struct* (*dispatch_client_t) ( | ||
| 102 | struct reservation *reservation, | ||
| 103 | lt_t *time_slice /* May be used to force rescheduling after | ||
| 104 | some amount of time. 0 => no limit */ | ||
| 105 | ); | ||
| 106 | |||
| 107 | /* Destructor: called before scheduler is deactivated. */ | ||
| 108 | typedef void (*shutdown_t)(struct reservation *reservation); | ||
| 109 | |||
| 110 | struct reservation_ops { | ||
| 111 | dispatch_client_t dispatch_client; | ||
| 112 | |||
| 113 | client_arrives_t client_arrives; | ||
| 114 | client_departs_t client_departs; | ||
| 115 | |||
| 116 | on_replenishment_timer_t replenish; | ||
| 117 | drain_budget_t drain_budget; | ||
| 118 | |||
| 119 | shutdown_t shutdown; | ||
| 120 | }; | ||
| 121 | |||
| 122 | #define RESERVATION_BACKGROUND_PRIORITY ULLONG_MAX | ||
| 123 | |||
| 124 | struct reservation { | ||
| 125 | /* used to queue in environment */ | ||
| 126 | struct list_head list; | ||
| 127 | struct list_head replenish_list; | ||
| 128 | |||
| 129 | reservation_state_t state; | ||
| 130 | unsigned int id; | ||
| 131 | unsigned int kind; | ||
| 132 | |||
| 133 | /* exact meaning defined by impl. */ | ||
| 134 | lt_t priority; | ||
| 135 | lt_t cur_budget; | ||
| 136 | lt_t next_replenishment; | ||
| 137 | |||
| 138 | /* budget stats */ | ||
| 139 | lt_t budget_consumed; /* how much budget consumed in this allocation cycle? */ | ||
| 140 | lt_t budget_consumed_total; | ||
| 141 | |||
| 142 | /* list of registered budget callbacks */ | ||
| 143 | struct budget_notifier_list budget_notifiers; | ||
| 144 | |||
| 145 | /* for memory reclamation purposes */ | ||
| 146 | struct list_head all_list; | ||
| 147 | |||
| 148 | /* interaction with framework */ | ||
| 149 | struct reservation_environment *env; | ||
| 150 | struct reservation_ops *ops; | ||
| 151 | |||
| 152 | struct list_head clients; | ||
| 153 | }; | ||
| 154 | |||
| 155 | void reservation_init(struct reservation *res); | ||
| 156 | |||
| 157 | /* Default implementations */ | ||
| 158 | |||
| 159 | /* simply select the first client in the list, set *for_at_most to zero */ | ||
| 160 | struct task_struct* default_dispatch_client( | ||
| 161 | struct reservation *res, | ||
| 162 | lt_t *for_at_most | ||
| 163 | ); | ||
| 164 | |||
| 165 | /* drain budget at linear rate, enter DEPLETED state when budget used up */ | ||
| 166 | void common_drain_budget(struct reservation *res, lt_t how_much); | ||
| 167 | |||
| 168 | /* "connector" reservation client to hook up tasks with reservations */ | ||
| 169 | struct task_client { | ||
| 170 | struct reservation_client client; | ||
| 171 | struct task_struct *task; | ||
| 172 | }; | ||
| 173 | |||
| 174 | void task_client_init(struct task_client *tc, struct task_struct *task, | ||
| 175 | struct reservation *reservation); | ||
| 176 | |||
| 177 | #define SUP_RESCHEDULE_NOW (0) | ||
| 178 | #define SUP_NO_SCHEDULER_UPDATE (ULLONG_MAX) | ||
| 179 | |||
| 180 | /* A simple uniprocessor (SUP) flat (i.e., non-hierarchical) reservation | ||
| 181 | * environment. | ||
| 182 | */ | ||
| 183 | struct sup_reservation_environment { | ||
| 184 | struct reservation_environment env; | ||
| 185 | |||
| 186 | /* ordered by priority */ | ||
| 187 | struct list_head active_reservations; | ||
| 188 | |||
| 189 | /* ordered by next_replenishment */ | ||
| 190 | struct list_head depleted_reservations; | ||
| 191 | |||
| 192 | /* unordered */ | ||
| 193 | struct list_head inactive_reservations; | ||
| 194 | |||
| 195 | /* list of all reservations */ | ||
| 196 | struct list_head all_reservations; | ||
| 197 | |||
| 198 | /* - SUP_RESCHEDULE_NOW means call sup_dispatch() now | ||
| 199 | * - SUP_NO_SCHEDULER_UPDATE means nothing to do | ||
| 200 | * any other value means program a timer for the given time | ||
| 201 | */ | ||
| 202 | lt_t next_scheduler_update; | ||
| 203 | /* set to true if a call to sup_dispatch() is imminent */ | ||
| 204 | bool will_schedule; | ||
| 205 | }; | ||
| 206 | |||
| 207 | /* Contract: | ||
| 208 | * - before calling into sup_ code, or any reservation methods, | ||
| 209 | * update the time with sup_update_time(); and | ||
| 210 | * - after calling into sup_ code, or any reservation methods, | ||
| 211 | * check next_scheduler_update and program timer or trigger | ||
| 212 | * scheduler invocation accordingly. | ||
| 213 | */ | ||
| 214 | |||
| 215 | void sup_init(struct sup_reservation_environment* sup_env); | ||
| 216 | void sup_add_new_reservation(struct sup_reservation_environment* sup_env, | ||
| 217 | struct reservation* new_res); | ||
| 218 | void sup_update_time(struct sup_reservation_environment* sup_env, lt_t now); | ||
| 219 | struct task_struct* sup_dispatch(struct sup_reservation_environment* sup_env); | ||
| 220 | |||
| 221 | struct reservation* sup_find_by_id(struct sup_reservation_environment* sup_env, | ||
| 222 | unsigned int id); | ||
| 223 | |||
| 224 | #endif | ||
diff --git a/include/litmus/reservations/table-driven.h b/include/litmus/reservations/table-driven.h new file mode 100644 index 000000000000..b6302a2f200d --- /dev/null +++ b/include/litmus/reservations/table-driven.h | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | #ifndef LITMUS_RESERVATIONS_TABLE_DRIVEN_H | ||
| 2 | #define LITMUS_RESERVATIONS_TABLE_DRIVEN_H | ||
| 3 | |||
| 4 | #include <litmus/reservations/reservation.h> | ||
| 5 | |||
| 6 | struct table_driven_reservation { | ||
| 7 | /* extend basic reservation */ | ||
| 8 | struct reservation res; | ||
| 9 | |||
| 10 | lt_t major_cycle; | ||
| 11 | unsigned int next_interval; | ||
| 12 | unsigned int num_intervals; | ||
| 13 | struct lt_interval *intervals; | ||
| 14 | |||
| 15 | /* info about current scheduling slot */ | ||
| 16 | struct lt_interval cur_interval; | ||
| 17 | lt_t major_cycle_start; | ||
| 18 | }; | ||
| 19 | |||
| 20 | void table_driven_reservation_init(struct table_driven_reservation *tdres, | ||
| 21 | lt_t major_cycle, struct lt_interval *intervals, unsigned int num_intervals); | ||
| 22 | |||
| 23 | #endif | ||
diff --git a/include/litmus/rt_domain.h b/include/litmus/rt_domain.h new file mode 100644 index 000000000000..ac249292e866 --- /dev/null +++ b/include/litmus/rt_domain.h | |||
| @@ -0,0 +1,182 @@ | |||
| 1 | /* CLEANUP: Add comments and make it less messy. | ||
| 2 | * | ||
| 3 | */ | ||
| 4 | |||
| 5 | #ifndef __UNC_RT_DOMAIN_H__ | ||
| 6 | #define __UNC_RT_DOMAIN_H__ | ||
| 7 | |||
| 8 | #include <litmus/bheap.h> | ||
| 9 | |||
| 10 | #define RELEASE_QUEUE_SLOTS 127 /* prime */ | ||
| 11 | |||
| 12 | struct _rt_domain; | ||
| 13 | |||
| 14 | typedef int (*check_resched_needed_t)(struct _rt_domain *rt); | ||
| 15 | typedef void (*release_jobs_t)(struct _rt_domain *rt, struct bheap* tasks); | ||
| 16 | |||
| 17 | struct release_queue { | ||
| 18 | /* each slot maintains a list of release heaps sorted | ||
| 19 | * by release time */ | ||
| 20 | struct list_head slot[RELEASE_QUEUE_SLOTS]; | ||
| 21 | }; | ||
| 22 | |||
| 23 | typedef struct _rt_domain { | ||
| 24 | /* runnable rt tasks are in here */ | ||
| 25 | raw_spinlock_t ready_lock; | ||
| 26 | struct bheap ready_queue; | ||
| 27 | |||
| 28 | /* real-time tasks waiting for release are in here */ | ||
| 29 | raw_spinlock_t release_lock; | ||
| 30 | struct release_queue release_queue; | ||
| 31 | |||
| 32 | #ifdef CONFIG_RELEASE_MASTER | ||
| 33 | int release_master; | ||
| 34 | #endif | ||
| 35 | |||
| 36 | /* for moving tasks to the release queue */ | ||
| 37 | raw_spinlock_t tobe_lock; | ||
| 38 | struct list_head tobe_released; | ||
| 39 | |||
| 40 | /* how do we check if we need to kick another CPU? */ | ||
| 41 | check_resched_needed_t check_resched; | ||
| 42 | |||
| 43 | /* how do we release jobs? */ | ||
| 44 | release_jobs_t release_jobs; | ||
| 45 | |||
| 46 | /* how are tasks ordered in the ready queue? */ | ||
| 47 | bheap_prio_t order; | ||
| 48 | } rt_domain_t; | ||
| 49 | |||
| 50 | struct release_heap { | ||
| 51 | /* list_head for per-time-slot list */ | ||
| 52 | struct list_head list; | ||
| 53 | lt_t release_time; | ||
| 54 | /* all tasks to be released at release_time */ | ||
| 55 | struct bheap heap; | ||
| 56 | /* used to trigger the release */ | ||
| 57 | struct hrtimer timer; | ||
| 58 | |||
| 59 | #ifdef CONFIG_RELEASE_MASTER | ||
| 60 | /* used to delegate releases */ | ||
| 61 | struct hrtimer_start_on_info info; | ||
| 62 | #endif | ||
| 63 | /* required for the timer callback */ | ||
| 64 | rt_domain_t* dom; | ||
| 65 | }; | ||
| 66 | |||
| 67 | |||
| 68 | static inline struct task_struct* __next_ready(rt_domain_t* rt) | ||
| 69 | { | ||
| 70 | struct bheap_node *hn = bheap_peek(rt->order, &rt->ready_queue); | ||
| 71 | if (hn) | ||
| 72 | return bheap2task(hn); | ||
| 73 | else | ||
| 74 | return NULL; | ||
| 75 | } | ||
| 76 | |||
| 77 | void rt_domain_init(rt_domain_t *rt, bheap_prio_t order, | ||
| 78 | check_resched_needed_t check, | ||
| 79 | release_jobs_t relase); | ||
| 80 | |||
| 81 | void __add_ready(rt_domain_t* rt, struct task_struct *new); | ||
| 82 | void __merge_ready(rt_domain_t* rt, struct bheap *tasks); | ||
| 83 | void __add_release(rt_domain_t* rt, struct task_struct *task); | ||
| 84 | |||
| 85 | static inline struct task_struct* __take_ready(rt_domain_t* rt) | ||
| 86 | { | ||
| 87 | struct bheap_node* hn = bheap_take(rt->order, &rt->ready_queue); | ||
| 88 | if (hn) | ||
| 89 | return bheap2task(hn); | ||
| 90 | else | ||
| 91 | return NULL; | ||
| 92 | } | ||
| 93 | |||
| 94 | static inline struct task_struct* __peek_ready(rt_domain_t* rt) | ||
| 95 | { | ||
| 96 | struct bheap_node* hn = bheap_peek(rt->order, &rt->ready_queue); | ||
| 97 | if (hn) | ||
| 98 | return bheap2task(hn); | ||
| 99 | else | ||
| 100 | return NULL; | ||
| 101 | } | ||
| 102 | |||
| 103 | static inline int is_queued(struct task_struct *t) | ||
| 104 | { | ||
| 105 | BUG_ON(!tsk_rt(t)->heap_node); | ||
| 106 | return bheap_node_in_heap(tsk_rt(t)->heap_node); | ||
| 107 | } | ||
| 108 | |||
| 109 | static inline void remove(rt_domain_t* rt, struct task_struct *t) | ||
| 110 | { | ||
| 111 | bheap_delete(rt->order, &rt->ready_queue, tsk_rt(t)->heap_node); | ||
| 112 | } | ||
| 113 | |||
| 114 | static inline void add_ready(rt_domain_t* rt, struct task_struct *new) | ||
| 115 | { | ||
| 116 | unsigned long flags; | ||
| 117 | /* first we need the write lock for rt_ready_queue */ | ||
| 118 | raw_spin_lock_irqsave(&rt->ready_lock, flags); | ||
| 119 | __add_ready(rt, new); | ||
| 120 | raw_spin_unlock_irqrestore(&rt->ready_lock, flags); | ||
| 121 | } | ||
| 122 | |||
| 123 | static inline void merge_ready(rt_domain_t* rt, struct bheap* tasks) | ||
| 124 | { | ||
| 125 | unsigned long flags; | ||
| 126 | raw_spin_lock_irqsave(&rt->ready_lock, flags); | ||
| 127 | __merge_ready(rt, tasks); | ||
| 128 | raw_spin_unlock_irqrestore(&rt->ready_lock, flags); | ||
| 129 | } | ||
| 130 | |||
| 131 | static inline struct task_struct* take_ready(rt_domain_t* rt) | ||
| 132 | { | ||
| 133 | unsigned long flags; | ||
| 134 | struct task_struct* ret; | ||
| 135 | /* first we need the write lock for rt_ready_queue */ | ||
| 136 | raw_spin_lock_irqsave(&rt->ready_lock, flags); | ||
| 137 | ret = __take_ready(rt); | ||
| 138 | raw_spin_unlock_irqrestore(&rt->ready_lock, flags); | ||
| 139 | return ret; | ||
| 140 | } | ||
| 141 | |||
| 142 | |||
| 143 | static inline void add_release(rt_domain_t* rt, struct task_struct *task) | ||
| 144 | { | ||
| 145 | unsigned long flags; | ||
| 146 | raw_spin_lock_irqsave(&rt->tobe_lock, flags); | ||
| 147 | __add_release(rt, task); | ||
| 148 | raw_spin_unlock_irqrestore(&rt->tobe_lock, flags); | ||
| 149 | } | ||
| 150 | |||
| 151 | #ifdef CONFIG_RELEASE_MASTER | ||
| 152 | void __add_release_on(rt_domain_t* rt, struct task_struct *task, | ||
| 153 | int target_cpu); | ||
| 154 | |||
| 155 | static inline void add_release_on(rt_domain_t* rt, | ||
| 156 | struct task_struct *task, | ||
| 157 | int target_cpu) | ||
| 158 | { | ||
| 159 | unsigned long flags; | ||
| 160 | raw_spin_lock_irqsave(&rt->tobe_lock, flags); | ||
| 161 | __add_release_on(rt, task, target_cpu); | ||
| 162 | raw_spin_unlock_irqrestore(&rt->tobe_lock, flags); | ||
| 163 | } | ||
| 164 | #endif | ||
| 165 | |||
| 166 | static inline int __jobs_pending(rt_domain_t* rt) | ||
| 167 | { | ||
| 168 | return !bheap_empty(&rt->ready_queue); | ||
| 169 | } | ||
| 170 | |||
| 171 | static inline int jobs_pending(rt_domain_t* rt) | ||
| 172 | { | ||
| 173 | unsigned long flags; | ||
| 174 | int ret; | ||
| 175 | /* first we need the write lock for rt_ready_queue */ | ||
| 176 | raw_spin_lock_irqsave(&rt->ready_lock, flags); | ||
| 177 | ret = !bheap_empty(&rt->ready_queue); | ||
| 178 | raw_spin_unlock_irqrestore(&rt->ready_lock, flags); | ||
| 179 | return ret; | ||
| 180 | } | ||
| 181 | |||
| 182 | #endif | ||
diff --git a/include/litmus/rt_param.h b/include/litmus/rt_param.h index 8c7869f46bfb..9b291343714f 100644 --- a/include/litmus/rt_param.h +++ b/include/litmus/rt_param.h | |||
| @@ -62,6 +62,7 @@ typedef enum { | |||
| 62 | #define LITMUS_MAX_PRIORITY 512 | 62 | #define LITMUS_MAX_PRIORITY 512 |
| 63 | #define LITMUS_HIGHEST_PRIORITY 1 | 63 | #define LITMUS_HIGHEST_PRIORITY 1 |
| 64 | #define LITMUS_LOWEST_PRIORITY (LITMUS_MAX_PRIORITY - 1) | 64 | #define LITMUS_LOWEST_PRIORITY (LITMUS_MAX_PRIORITY - 1) |
| 65 | #define LITMUS_NO_PRIORITY UINT_MAX | ||
| 65 | 66 | ||
| 66 | /* Provide generic comparison macros for userspace, | 67 | /* Provide generic comparison macros for userspace, |
| 67 | * in case that we change this later. */ | 68 | * in case that we change this later. */ |
| @@ -71,6 +72,46 @@ typedef enum { | |||
| 71 | ((p) >= LITMUS_HIGHEST_PRIORITY && \ | 72 | ((p) >= LITMUS_HIGHEST_PRIORITY && \ |
| 72 | (p) <= LITMUS_LOWEST_PRIORITY) | 73 | (p) <= LITMUS_LOWEST_PRIORITY) |
| 73 | 74 | ||
| 75 | /* reservation support */ | ||
| 76 | |||
| 77 | typedef enum { | ||
| 78 | PERIODIC_POLLING = 10, | ||
| 79 | SPORADIC_POLLING, | ||
| 80 | TABLE_DRIVEN, | ||
| 81 | } reservation_type_t; | ||
| 82 | |||
| 83 | struct lt_interval { | ||
| 84 | lt_t start; | ||
| 85 | lt_t end; | ||
| 86 | }; | ||
| 87 | |||
| 88 | #ifndef __KERNEL__ | ||
| 89 | #define __user | ||
| 90 | #endif | ||
| 91 | |||
| 92 | struct reservation_config { | ||
| 93 | unsigned int id; | ||
| 94 | lt_t priority; | ||
| 95 | int cpu; | ||
| 96 | |||
| 97 | union { | ||
| 98 | struct { | ||
| 99 | lt_t period; | ||
| 100 | lt_t budget; | ||
| 101 | lt_t relative_deadline; | ||
| 102 | lt_t offset; | ||
| 103 | } polling_params; | ||
| 104 | |||
| 105 | struct { | ||
| 106 | lt_t major_cycle_length; | ||
| 107 | unsigned int num_intervals; | ||
| 108 | struct lt_interval __user *intervals; | ||
| 109 | } table_driven_params; | ||
| 110 | }; | ||
| 111 | }; | ||
| 112 | |||
| 113 | /* regular sporadic task support */ | ||
| 114 | |||
| 74 | struct rt_task { | 115 | struct rt_task { |
| 75 | lt_t exec_cost; | 116 | lt_t exec_cost; |
| 76 | lt_t period; | 117 | lt_t period; |
| @@ -83,54 +124,6 @@ struct rt_task { | |||
| 83 | release_policy_t release_policy; | 124 | release_policy_t release_policy; |
| 84 | }; | 125 | }; |
| 85 | 126 | ||
| 86 | union np_flag { | ||
| 87 | uint64_t raw; | ||
| 88 | struct { | ||
| 89 | /* Is the task currently in a non-preemptive section? */ | ||
| 90 | uint64_t flag:31; | ||
| 91 | /* Should the task call into the scheduler? */ | ||
| 92 | uint64_t preempt:1; | ||
| 93 | } np; | ||
| 94 | }; | ||
| 95 | |||
| 96 | /* The definition of the data that is shared between the kernel and real-time | ||
| 97 | * tasks via a shared page (see litmus/ctrldev.c). | ||
| 98 | * | ||
| 99 | * WARNING: User space can write to this, so don't trust | ||
| 100 | * the correctness of the fields! | ||
| 101 | * | ||
| 102 | * This servees two purposes: to enable efficient signaling | ||
| 103 | * of non-preemptive sections (user->kernel) and | ||
| 104 | * delayed preemptions (kernel->user), and to export | ||
| 105 | * some real-time relevant statistics such as preemption and | ||
| 106 | * migration data to user space. We can't use a device to export | ||
| 107 | * statistics because we want to avoid system call overhead when | ||
| 108 | * determining preemption/migration overheads). | ||
| 109 | */ | ||
| 110 | struct control_page { | ||
| 111 | /* This flag is used by userspace to communicate non-preempive | ||
| 112 | * sections. */ | ||
| 113 | volatile union np_flag sched; | ||
| 114 | |||
| 115 | volatile uint64_t irq_count; /* Incremented by the kernel each time an IRQ is | ||
| 116 | * handled. */ | ||
| 117 | |||
| 118 | /* Locking overhead tracing: userspace records here the time stamp | ||
| 119 | * and IRQ counter prior to starting the system call. */ | ||
| 120 | uint64_t ts_syscall_start; /* Feather-Trace cycles */ | ||
| 121 | uint64_t irq_syscall_start; /* Snapshot of irq_count when the syscall | ||
| 122 | * started. */ | ||
| 123 | |||
| 124 | /* to be extended */ | ||
| 125 | }; | ||
| 126 | |||
| 127 | /* Expected offsets within the control page. */ | ||
| 128 | |||
| 129 | #define LITMUS_CP_OFFSET_SCHED 0 | ||
| 130 | #define LITMUS_CP_OFFSET_IRQ_COUNT 8 | ||
| 131 | #define LITMUS_CP_OFFSET_TS_SC_START 16 | ||
| 132 | #define LITMUS_CP_OFFSET_IRQ_SC_START 24 | ||
| 133 | |||
| 134 | /* don't export internal data structures to user space (liblitmus) */ | 127 | /* don't export internal data structures to user space (liblitmus) */ |
| 135 | #ifdef __KERNEL__ | 128 | #ifdef __KERNEL__ |
| 136 | 129 | ||
| @@ -177,9 +170,6 @@ struct pfair_param; | |||
| 177 | * be explicitly set up before the task set is launched. | 170 | * be explicitly set up before the task set is launched. |
| 178 | */ | 171 | */ |
| 179 | struct rt_param { | 172 | struct rt_param { |
| 180 | /* Generic flags available for plugin-internal use. */ | ||
| 181 | unsigned int flags:8; | ||
| 182 | |||
| 183 | /* do we need to check for srp blocking? */ | 173 | /* do we need to check for srp blocking? */ |
| 184 | unsigned int srp_non_recurse:1; | 174 | unsigned int srp_non_recurse:1; |
| 185 | 175 | ||
| @@ -207,13 +197,19 @@ struct rt_param { | |||
| 207 | /* timing parameters */ | 197 | /* timing parameters */ |
| 208 | struct rt_job job_params; | 198 | struct rt_job job_params; |
| 209 | 199 | ||
| 200 | |||
| 201 | /* Special handling for periodic tasks executing | ||
| 202 | * clock_nanosleep(CLOCK_MONOTONIC, ...). | ||
| 203 | */ | ||
| 204 | lt_t nanosleep_wakeup; | ||
| 205 | unsigned int doing_abs_nanosleep:1; | ||
| 206 | |||
| 210 | /* Should the next job be released at some time other than | 207 | /* Should the next job be released at some time other than |
| 211 | * just period time units after the last release? | 208 | * just period time units after the last release? |
| 212 | */ | 209 | */ |
| 213 | unsigned int sporadic_release:1; | 210 | unsigned int sporadic_release:1; |
| 214 | lt_t sporadic_release_time; | 211 | lt_t sporadic_release_time; |
| 215 | 212 | ||
| 216 | |||
| 217 | /* task representing the current "inherited" task | 213 | /* task representing the current "inherited" task |
| 218 | * priority, assigned by inherit_priority and | 214 | * priority, assigned by inherit_priority and |
| 219 | * return priority in the scheduler plugins. | 215 | * return priority in the scheduler plugins. |
| @@ -255,7 +251,10 @@ struct rt_param { | |||
| 255 | volatile int linked_on; | 251 | volatile int linked_on; |
| 256 | 252 | ||
| 257 | /* PFAIR/PD^2 state. Allocated on demand. */ | 253 | /* PFAIR/PD^2 state. Allocated on demand. */ |
| 258 | struct pfair_param* pfair; | 254 | union { |
| 255 | void *plugin_state; | ||
| 256 | struct pfair_param *pfair; | ||
| 257 | }; | ||
| 259 | 258 | ||
| 260 | /* Fields saved before BE->RT transition. | 259 | /* Fields saved before BE->RT transition. |
| 261 | */ | 260 | */ |
diff --git a/include/litmus/sched_plugin.h b/include/litmus/sched_plugin.h new file mode 100644 index 000000000000..0923f26b745a --- /dev/null +++ b/include/litmus/sched_plugin.h | |||
| @@ -0,0 +1,180 @@ | |||
| 1 | /* | ||
| 2 | * Definition of the scheduler plugin interface. | ||
| 3 | * | ||
| 4 | */ | ||
| 5 | #ifndef _LINUX_SCHED_PLUGIN_H_ | ||
| 6 | #define _LINUX_SCHED_PLUGIN_H_ | ||
| 7 | |||
| 8 | #include <linux/sched.h> | ||
| 9 | |||
| 10 | #ifdef CONFIG_LITMUS_LOCKING | ||
| 11 | #include <litmus/locking.h> | ||
| 12 | #endif | ||
| 13 | |||
| 14 | /************************ setup/tear down ********************/ | ||
| 15 | |||
| 16 | typedef long (*activate_plugin_t) (void); | ||
| 17 | typedef long (*deactivate_plugin_t) (void); | ||
| 18 | |||
| 19 | struct domain_proc_info; | ||
| 20 | typedef long (*get_domain_proc_info_t) (struct domain_proc_info **info); | ||
| 21 | |||
| 22 | |||
| 23 | /********************* scheduler invocation ******************/ | ||
| 24 | /* The main scheduling function, called to select the next task to dispatch. */ | ||
| 25 | typedef struct task_struct* (*schedule_t)(struct task_struct * prev); | ||
| 26 | /* Clean up after the task switch has occured. | ||
| 27 | * This function is called after every (even non-rt) task switch. | ||
| 28 | */ | ||
| 29 | typedef void (*finish_switch_t)(struct task_struct *prev); | ||
| 30 | |||
| 31 | |||
| 32 | /* When waiting for the stack of the task selected by the plugin | ||
| 33 | * to become available, this callback is invoked to give the | ||
| 34 | * plugin a chance to cancel the wait. If the plugin returns false, | ||
| 35 | * the scheduler is invoked again. */ | ||
| 36 | typedef bool (*should_wait_for_stack_t)(struct task_struct *next); | ||
| 37 | |||
| 38 | /* After a pull migration (which involves dropping scheduler locks), | ||
| 39 | * the plugin is given the chance to validate that the task is still | ||
| 40 | * the right one. If the plugin returns false, the scheduler | ||
| 41 | * will be invoked again. */ | ||
| 42 | typedef bool (*post_migration_validate_t)(struct task_struct *next); | ||
| 43 | |||
| 44 | /* After dropping the lock to facilitate a pull migration, the task | ||
| 45 | * state may have changed. In this case, the core notifies the plugin | ||
| 46 | * with this callback and then invokes the scheduler again. */ | ||
| 47 | typedef void (*next_became_invalid_t)(struct task_struct *next); | ||
| 48 | |||
| 49 | /********************* task state changes ********************/ | ||
| 50 | |||
| 51 | /* Called to setup a new real-time task. | ||
| 52 | * Release the first job, enqueue, etc. | ||
| 53 | * Task may already be running. | ||
| 54 | */ | ||
| 55 | typedef void (*task_new_t) (struct task_struct *task, | ||
| 56 | int on_rq, | ||
| 57 | int running); | ||
| 58 | |||
| 59 | /* Called when userspace seeks to set new task parameters for a task | ||
| 60 | * that is already in real-time mode (i.e., is_realtime(task)). | ||
| 61 | */ | ||
| 62 | typedef long (*task_change_params_t) (struct task_struct *task, | ||
| 63 | struct rt_task *new_params); | ||
| 64 | |||
| 65 | /* Called to re-introduce a task after blocking. | ||
| 66 | * Can potentially be called multiple times. | ||
| 67 | */ | ||
| 68 | typedef void (*task_wake_up_t) (struct task_struct *task); | ||
| 69 | /* called to notify the plugin of a blocking real-time task | ||
| 70 | * it will only be called for real-time tasks and before schedule is called */ | ||
| 71 | typedef void (*task_block_t) (struct task_struct *task); | ||
| 72 | /* Called when a real-time task exits or changes to a different scheduling | ||
| 73 | * class. | ||
| 74 | * Free any allocated resources | ||
| 75 | */ | ||
| 76 | typedef void (*task_exit_t) (struct task_struct *); | ||
| 77 | |||
| 78 | /* task_exit() is called with interrupts disabled and runqueue locks held, and | ||
| 79 | * thus and cannot block or spin. task_cleanup() is called sometime later | ||
| 80 | * without any locks being held. | ||
| 81 | */ | ||
| 82 | typedef void (*task_cleanup_t) (struct task_struct *); | ||
| 83 | |||
| 84 | #ifdef CONFIG_LITMUS_LOCKING | ||
| 85 | /* Called when the current task attempts to create a new lock of a given | ||
| 86 | * protocol type. */ | ||
| 87 | typedef long (*allocate_lock_t) (struct litmus_lock **lock, int type, | ||
| 88 | void* __user config); | ||
| 89 | #endif | ||
| 90 | |||
| 91 | |||
| 92 | /********************* sys call backends ********************/ | ||
| 93 | /* This function causes the caller to sleep until the next release */ | ||
| 94 | typedef long (*complete_job_t) (void); | ||
| 95 | |||
| 96 | typedef long (*admit_task_t)(struct task_struct* tsk); | ||
| 97 | |||
| 98 | /* return false to indicate that the plugin does not support forking */ | ||
| 99 | typedef bool (*fork_task_t)(struct task_struct* tsk); | ||
| 100 | |||
| 101 | typedef long (*wait_for_release_at_t)(lt_t release_time); | ||
| 102 | |||
| 103 | /* Informs the plugin when a synchronous release takes place. */ | ||
| 104 | typedef void (*synchronous_release_at_t)(lt_t time_zero); | ||
| 105 | |||
| 106 | /* How much budget has the current task consumed so far, and how much | ||
| 107 | * has it left? The default implementation ties into the per-task | ||
| 108 | * budget enforcement code. Plugins can override this to report | ||
| 109 | * reservation-specific values. */ | ||
| 110 | typedef void (*current_budget_t)(lt_t *used_so_far, lt_t *remaining); | ||
| 111 | |||
| 112 | /* Reservation creation/removal backends. Meaning of reservation_type and | ||
| 113 | * reservation_id are entirely plugin-specific. */ | ||
| 114 | typedef long (*reservation_create_t)(int reservation_type, void* __user config); | ||
| 115 | typedef long (*reservation_destroy_t)(unsigned int reservation_id, int cpu); | ||
| 116 | |||
| 117 | /************************ misc routines ***********************/ | ||
| 118 | |||
| 119 | |||
| 120 | struct sched_plugin { | ||
| 121 | struct list_head list; | ||
| 122 | /* basic info */ | ||
| 123 | char *plugin_name; | ||
| 124 | |||
| 125 | /* setup */ | ||
| 126 | activate_plugin_t activate_plugin; | ||
| 127 | deactivate_plugin_t deactivate_plugin; | ||
| 128 | get_domain_proc_info_t get_domain_proc_info; | ||
| 129 | |||
| 130 | /* scheduler invocation */ | ||
| 131 | schedule_t schedule; | ||
| 132 | finish_switch_t finish_switch; | ||
| 133 | |||
| 134 | /* control over pull migrations */ | ||
| 135 | should_wait_for_stack_t should_wait_for_stack; | ||
| 136 | next_became_invalid_t next_became_invalid; | ||
| 137 | post_migration_validate_t post_migration_validate; | ||
| 138 | |||
| 139 | /* syscall backend */ | ||
| 140 | complete_job_t complete_job; | ||
| 141 | wait_for_release_at_t wait_for_release_at; | ||
| 142 | synchronous_release_at_t synchronous_release_at; | ||
| 143 | |||
| 144 | /* task state changes */ | ||
| 145 | admit_task_t admit_task; | ||
| 146 | fork_task_t fork_task; | ||
| 147 | |||
| 148 | task_new_t task_new; | ||
| 149 | task_wake_up_t task_wake_up; | ||
| 150 | task_block_t task_block; | ||
| 151 | |||
| 152 | /* optional: support task parameter changes at runtime */ | ||
| 153 | task_change_params_t task_change_params; | ||
| 154 | |||
| 155 | task_exit_t task_exit; | ||
| 156 | task_cleanup_t task_cleanup; | ||
| 157 | |||
| 158 | current_budget_t current_budget; | ||
| 159 | |||
| 160 | /* Reservation support */ | ||
| 161 | reservation_create_t reservation_create; | ||
| 162 | reservation_destroy_t reservation_destroy; | ||
| 163 | |||
| 164 | #ifdef CONFIG_LITMUS_LOCKING | ||
| 165 | /* locking protocols */ | ||
| 166 | allocate_lock_t allocate_lock; | ||
| 167 | #endif | ||
| 168 | } __attribute__ ((__aligned__(SMP_CACHE_BYTES))); | ||
| 169 | |||
| 170 | |||
| 171 | extern struct sched_plugin *litmus; | ||
| 172 | |||
| 173 | int register_sched_plugin(struct sched_plugin* plugin); | ||
| 174 | struct sched_plugin* find_sched_plugin(const char* name); | ||
| 175 | void print_sched_plugins(struct seq_file *m); | ||
| 176 | |||
| 177 | |||
| 178 | extern struct sched_plugin linux_sched_plugin; | ||
| 179 | |||
| 180 | #endif | ||
diff --git a/include/litmus/srp.h b/include/litmus/srp.h new file mode 100644 index 000000000000..c9a4552b2bf3 --- /dev/null +++ b/include/litmus/srp.h | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | #ifndef LITMUS_SRP_H | ||
| 2 | #define LITMUS_SRP_H | ||
| 3 | |||
| 4 | struct srp_semaphore; | ||
| 5 | |||
| 6 | struct srp_priority { | ||
| 7 | struct list_head list; | ||
| 8 | unsigned int priority; | ||
| 9 | pid_t pid; | ||
| 10 | }; | ||
| 11 | #define list2prio(l) list_entry(l, struct srp_priority, list) | ||
| 12 | |||
| 13 | /* struct for uniprocessor SRP "semaphore" */ | ||
| 14 | struct srp_semaphore { | ||
| 15 | struct litmus_lock litmus_lock; | ||
| 16 | struct srp_priority ceiling; | ||
| 17 | struct task_struct* owner; | ||
| 18 | int cpu; /* cpu associated with this "semaphore" and resource */ | ||
| 19 | }; | ||
| 20 | |||
| 21 | /* map a task to its SRP preemption level priority */ | ||
| 22 | typedef unsigned int (*srp_prioritization_t)(struct task_struct* t); | ||
| 23 | /* Must be updated by each plugin that uses SRP.*/ | ||
| 24 | extern srp_prioritization_t get_srp_prio; | ||
| 25 | |||
| 26 | struct srp_semaphore* allocate_srp_semaphore(void); | ||
| 27 | |||
| 28 | #endif | ||
diff --git a/include/litmus/wait.h b/include/litmus/wait.h new file mode 100644 index 000000000000..ce1347c355f8 --- /dev/null +++ b/include/litmus/wait.h | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | #ifndef _LITMUS_WAIT_H_ | ||
| 2 | #define _LITMUS_WAIT_H_ | ||
| 3 | |||
| 4 | struct task_struct* __waitqueue_remove_first(wait_queue_head_t *wq); | ||
| 5 | |||
| 6 | /* wrap regular wait_queue_t head */ | ||
| 7 | struct __prio_wait_queue { | ||
| 8 | wait_queue_t wq; | ||
| 9 | |||
| 10 | /* some priority point */ | ||
| 11 | lt_t priority; | ||
| 12 | /* break ties in priority by lower tie_breaker */ | ||
| 13 | unsigned int tie_breaker; | ||
| 14 | }; | ||
| 15 | |||
| 16 | typedef struct __prio_wait_queue prio_wait_queue_t; | ||
| 17 | |||
| 18 | static inline void init_prio_waitqueue_entry(prio_wait_queue_t *pwq, | ||
| 19 | struct task_struct* t, | ||
| 20 | lt_t priority) | ||
| 21 | { | ||
| 22 | init_waitqueue_entry(&pwq->wq, t); | ||
| 23 | pwq->priority = priority; | ||
| 24 | pwq->tie_breaker = 0; | ||
| 25 | } | ||
| 26 | |||
| 27 | static inline void init_prio_waitqueue_entry_tie(prio_wait_queue_t *pwq, | ||
| 28 | struct task_struct* t, | ||
| 29 | lt_t priority, | ||
| 30 | unsigned int tie_breaker) | ||
| 31 | { | ||
| 32 | init_waitqueue_entry(&pwq->wq, t); | ||
| 33 | pwq->priority = priority; | ||
| 34 | pwq->tie_breaker = tie_breaker; | ||
| 35 | } | ||
| 36 | |||
| 37 | unsigned int __add_wait_queue_prio_exclusive( | ||
| 38 | wait_queue_head_t* head, | ||
| 39 | prio_wait_queue_t *new); | ||
| 40 | |||
| 41 | static inline unsigned int add_wait_queue_prio_exclusive( | ||
| 42 | wait_queue_head_t* head, | ||
| 43 | prio_wait_queue_t *new) | ||
| 44 | { | ||
| 45 | unsigned long flags; | ||
| 46 | unsigned int passed; | ||
| 47 | |||
| 48 | spin_lock_irqsave(&head->lock, flags); | ||
| 49 | passed = __add_wait_queue_prio_exclusive(head, new); | ||
| 50 | |||
| 51 | spin_unlock_irqrestore(&head->lock, flags); | ||
| 52 | |||
| 53 | return passed; | ||
| 54 | } | ||
| 55 | |||
| 56 | |||
| 57 | #endif | ||
