diff options
author | Glenn Elliott <gelliott@cs.unc.edu> | 2012-05-26 17:29:58 -0400 |
---|---|---|
committer | Glenn Elliott <gelliott@cs.unc.edu> | 2012-05-26 17:29:58 -0400 |
commit | a463f9a9e04385f0729f7435a0a6dff7d89b25de (patch) | |
tree | 00ff42c305926c800e18b13df8440a4de1a1a041 /litmus/locking.c | |
parent | 6a00f206debf8a5c8899055726ad127dbeeed098 (diff) |
GPUSync patch for Litmus 2012.1.
Diffstat (limited to 'litmus/locking.c')
-rw-r--r-- | litmus/locking.c | 393 |
1 files changed, 389 insertions, 4 deletions
diff --git a/litmus/locking.c b/litmus/locking.c index 0c1aa6aa40b7..718a5a3281d7 100644 --- a/litmus/locking.c +++ b/litmus/locking.c | |||
@@ -4,6 +4,15 @@ | |||
4 | 4 | ||
5 | #include <litmus/sched_plugin.h> | 5 | #include <litmus/sched_plugin.h> |
6 | #include <litmus/trace.h> | 6 | #include <litmus/trace.h> |
7 | #include <litmus/litmus.h> | ||
8 | |||
9 | #ifdef CONFIG_LITMUS_DGL_SUPPORT | ||
10 | #include <linux/uaccess.h> | ||
11 | #endif | ||
12 | |||
13 | #if defined(CONFIG_LITMUS_AFFINITY_LOCKING) && defined(CONFIG_LITMUS_NVIDIA) | ||
14 | #include <litmus/gpu_affinity.h> | ||
15 | #endif | ||
7 | 16 | ||
8 | static int create_generic_lock(void** obj_ref, obj_type_t type, void* __user arg); | 17 | static int create_generic_lock(void** obj_ref, obj_type_t type, void* __user arg); |
9 | static int open_generic_lock(struct od_table_entry* entry, void* __user arg); | 18 | static int open_generic_lock(struct od_table_entry* entry, void* __user arg); |
@@ -17,6 +26,9 @@ struct fdso_ops generic_lock_ops = { | |||
17 | .destroy = destroy_generic_lock | 26 | .destroy = destroy_generic_lock |
18 | }; | 27 | }; |
19 | 28 | ||
29 | static atomic_t lock_id_gen = ATOMIC_INIT(0); | ||
30 | |||
31 | |||
20 | static inline bool is_lock(struct od_table_entry* entry) | 32 | static inline bool is_lock(struct od_table_entry* entry) |
21 | { | 33 | { |
22 | return entry->class == &generic_lock_ops; | 34 | return entry->class == &generic_lock_ops; |
@@ -34,8 +46,21 @@ static int create_generic_lock(void** obj_ref, obj_type_t type, void* __user ar | |||
34 | int err; | 46 | int err; |
35 | 47 | ||
36 | err = litmus->allocate_lock(&lock, type, arg); | 48 | err = litmus->allocate_lock(&lock, type, arg); |
37 | if (err == 0) | 49 | if (err == 0) { |
50 | #ifdef CONFIG_LITMUS_NESTED_LOCKING | ||
51 | lock->nest.lock = lock; | ||
52 | lock->nest.hp_waiter_eff_prio = NULL; | ||
53 | |||
54 | INIT_BINHEAP_NODE(&lock->nest.hp_binheap_node); | ||
55 | if(!lock->nest.hp_waiter_ptr) { | ||
56 | TRACE_CUR("BEWARE: hp_waiter_ptr should probably not be NULL in " | ||
57 | "most uses. (exception: IKGLP donors)\n"); | ||
58 | } | ||
59 | #endif | ||
60 | lock->type = type; | ||
61 | lock->ident = atomic_inc_return(&lock_id_gen); | ||
38 | *obj_ref = lock; | 62 | *obj_ref = lock; |
63 | } | ||
39 | return err; | 64 | return err; |
40 | } | 65 | } |
41 | 66 | ||
@@ -74,7 +99,8 @@ asmlinkage long sys_litmus_lock(int lock_od) | |||
74 | entry = get_entry_for_od(lock_od); | 99 | entry = get_entry_for_od(lock_od); |
75 | if (entry && is_lock(entry)) { | 100 | if (entry && is_lock(entry)) { |
76 | l = get_lock(entry); | 101 | l = get_lock(entry); |
77 | TRACE_CUR("attempts to lock 0x%p\n", l); | 102 | //TRACE_CUR("attempts to lock 0x%p\n", l); |
103 | TRACE_CUR("attempts to lock %d\n", l->ident); | ||
78 | err = l->ops->lock(l); | 104 | err = l->ops->lock(l); |
79 | } | 105 | } |
80 | 106 | ||
@@ -96,7 +122,8 @@ asmlinkage long sys_litmus_unlock(int lock_od) | |||
96 | entry = get_entry_for_od(lock_od); | 122 | entry = get_entry_for_od(lock_od); |
97 | if (entry && is_lock(entry)) { | 123 | if (entry && is_lock(entry)) { |
98 | l = get_lock(entry); | 124 | l = get_lock(entry); |
99 | TRACE_CUR("attempts to unlock 0x%p\n", l); | 125 | //TRACE_CUR("attempts to unlock 0x%p\n", l); |
126 | TRACE_CUR("attempts to unlock %d\n", l->ident); | ||
100 | err = l->ops->unlock(l); | 127 | err = l->ops->unlock(l); |
101 | } | 128 | } |
102 | 129 | ||
@@ -121,8 +148,366 @@ struct task_struct* __waitqueue_remove_first(wait_queue_head_t *wq) | |||
121 | return(t); | 148 | return(t); |
122 | } | 149 | } |
123 | 150 | ||
151 | #ifdef CONFIG_LITMUS_NESTED_LOCKING | ||
152 | |||
153 | void print_hp_waiters(struct binheap_node* n, int depth) | ||
154 | { | ||
155 | struct litmus_lock *l; | ||
156 | struct nested_info *nest; | ||
157 | char padding[81] = " "; | ||
158 | struct task_struct *hp = NULL; | ||
159 | struct task_struct *hp_eff = NULL; | ||
160 | struct task_struct *node_prio = NULL; | ||
161 | |||
162 | |||
163 | if(n == NULL) { | ||
164 | TRACE("+-> %p\n", NULL); | ||
165 | return; | ||
166 | } | ||
167 | |||
168 | nest = binheap_entry(n, struct nested_info, hp_binheap_node); | ||
169 | l = nest->lock; | ||
170 | |||
171 | if(depth*2 <= 80) | ||
172 | padding[depth*2] = '\0'; | ||
173 | |||
174 | if(nest->hp_waiter_ptr && *(nest->hp_waiter_ptr)) { | ||
175 | hp = *(nest->hp_waiter_ptr); | ||
176 | |||
177 | if(tsk_rt(hp)->inh_task) { | ||
178 | hp_eff = tsk_rt(hp)->inh_task; | ||
179 | } | ||
180 | } | ||
181 | |||
182 | node_prio = nest->hp_waiter_eff_prio; | ||
183 | |||
184 | TRACE("%s+-> %s/%d [waiter = %s/%d] [waiter's inh = %s/%d] (lock = %d)\n", | ||
185 | padding, | ||
186 | (node_prio) ? node_prio->comm : "nil", | ||
187 | (node_prio) ? node_prio->pid : -1, | ||
188 | (hp) ? hp->comm : "nil", | ||
189 | (hp) ? hp->pid : -1, | ||
190 | (hp_eff) ? hp_eff->comm : "nil", | ||
191 | (hp_eff) ? hp_eff->pid : -1, | ||
192 | l->ident); | ||
193 | |||
194 | if(n->left) print_hp_waiters(n->left, depth+1); | ||
195 | if(n->right) print_hp_waiters(n->right, depth+1); | ||
196 | } | ||
197 | #endif | ||
198 | |||
199 | |||
200 | #ifdef CONFIG_LITMUS_DGL_SUPPORT | ||
201 | |||
202 | void select_next_lock(dgl_wait_state_t* dgl_wait /*, struct litmus_lock* prev_lock*/) | ||
203 | { | ||
204 | /* | ||
205 | We pick the next lock in reverse order. This causes inheritance propagation | ||
206 | from locks received earlier to flow in the same direction as regular nested | ||
207 | locking. This might make fine-grain DGL easier in the future. | ||
208 | */ | ||
209 | |||
210 | BUG_ON(tsk_rt(dgl_wait->task)->blocked_lock); | ||
211 | |||
212 | //WARN_ON(dgl_wait->locks[dgl_wait->last_primary] != prev_lock); | ||
213 | |||
214 | // note reverse order | ||
215 | for(dgl_wait->last_primary = dgl_wait->last_primary - 1; | ||
216 | dgl_wait->last_primary >= 0; | ||
217 | --(dgl_wait->last_primary)){ | ||
218 | if(!dgl_wait->locks[dgl_wait->last_primary]->ops->is_owner( | ||
219 | dgl_wait->locks[dgl_wait->last_primary], dgl_wait->task)) { | ||
220 | |||
221 | tsk_rt(dgl_wait->task)->blocked_lock = | ||
222 | dgl_wait->locks[dgl_wait->last_primary]; | ||
223 | mb(); | ||
224 | |||
225 | TRACE_CUR("New blocked lock is %d\n", | ||
226 | dgl_wait->locks[dgl_wait->last_primary]->ident); | ||
227 | |||
228 | break; | ||
229 | } | ||
230 | } | ||
231 | } | ||
232 | |||
233 | int dgl_wake_up(wait_queue_t *wq_node, unsigned mode, int sync, void *key) | ||
234 | { | ||
235 | // should never be called. | ||
236 | BUG(); | ||
237 | return 1; | ||
238 | } | ||
239 | |||
240 | void __waitqueue_dgl_remove_first(wait_queue_head_t *wq, | ||
241 | dgl_wait_state_t** dgl_wait, | ||
242 | struct task_struct **task) | ||
243 | { | ||
244 | wait_queue_t *q; | ||
245 | |||
246 | *dgl_wait = NULL; | ||
247 | *task = NULL; | ||
248 | |||
249 | if (waitqueue_active(wq)) { | ||
250 | q = list_entry(wq->task_list.next, | ||
251 | wait_queue_t, task_list); | ||
252 | |||
253 | if(q->func == dgl_wake_up) { | ||
254 | *dgl_wait = (dgl_wait_state_t*) q->private; | ||
255 | } | ||
256 | else { | ||
257 | *task = (struct task_struct*) q->private; | ||
258 | } | ||
259 | |||
260 | __remove_wait_queue(wq, q); | ||
261 | } | ||
262 | } | ||
263 | |||
264 | void init_dgl_waitqueue_entry(wait_queue_t *wq_node, dgl_wait_state_t* dgl_wait) | ||
265 | { | ||
266 | init_waitqueue_entry(wq_node, dgl_wait->task); | ||
267 | wq_node->private = dgl_wait; | ||
268 | wq_node->func = dgl_wake_up; | ||
269 | } | ||
270 | |||
271 | |||
272 | static long do_litmus_dgl_lock(dgl_wait_state_t *dgl_wait) | ||
273 | { | ||
274 | int i; | ||
275 | unsigned long irqflags; //, dummyflags; | ||
276 | raw_spinlock_t *dgl_lock = litmus->get_dgl_spinlock(dgl_wait->task); | ||
277 | |||
278 | BUG_ON(dgl_wait->task != current); | ||
279 | |||
280 | raw_spin_lock_irqsave(dgl_lock, irqflags); | ||
281 | |||
282 | |||
283 | dgl_wait->nr_remaining = dgl_wait->size; | ||
284 | |||
285 | TRACE_CUR("Locking DGL with size %d\n", dgl_wait->size); | ||
286 | |||
287 | // try to acquire each lock. enqueue (non-blocking) if it is unavailable. | ||
288 | for(i = 0; i < dgl_wait->size; ++i) { | ||
289 | struct litmus_lock *l = dgl_wait->locks[i]; | ||
290 | |||
291 | // dgl_lock() must set task state to TASK_UNINTERRUPTIBLE if task blocks. | ||
292 | |||
293 | if(l->ops->dgl_lock(l, dgl_wait, &dgl_wait->wq_nodes[i])) { | ||
294 | --(dgl_wait->nr_remaining); | ||
295 | TRACE_CUR("Acquired lock %d immediatly.\n", l->ident); | ||
296 | } | ||
297 | } | ||
298 | |||
299 | if(dgl_wait->nr_remaining == 0) { | ||
300 | // acquired entire group immediatly | ||
301 | TRACE_CUR("Acquired all locks in DGL immediatly!\n"); | ||
302 | } | ||
303 | else { | ||
304 | |||
305 | TRACE_CUR("As many as %d locks in DGL are pending. Suspending.\n", | ||
306 | dgl_wait->nr_remaining); | ||
307 | |||
308 | #if defined(CONFIG_LITMUS_AFFINITY_LOCKING) && defined(CONFIG_LITMUS_NVIDIA) | ||
309 | // KLUDGE: don't count this suspension as time in the critical gpu | ||
310 | // critical section | ||
311 | if(tsk_rt(dgl_wait->task)->held_gpus) { | ||
312 | tsk_rt(dgl_wait->task)->suspend_gpu_tracker_on_block = 1; | ||
313 | } | ||
314 | #endif | ||
315 | |||
316 | // note reverse order. see comments in select_next_lock for reason. | ||
317 | for(i = dgl_wait->size - 1; i >= 0; --i) { | ||
318 | struct litmus_lock *l = dgl_wait->locks[i]; | ||
319 | if(!l->ops->is_owner(l, dgl_wait->task)) { // double-check to be thread safe | ||
320 | |||
321 | TRACE_CUR("Activating priority inheritance on lock %d\n", | ||
322 | l->ident); | ||
323 | |||
324 | TS_DGL_LOCK_SUSPEND; | ||
325 | |||
326 | l->ops->enable_priority(l, dgl_wait); | ||
327 | dgl_wait->last_primary = i; | ||
328 | |||
329 | TRACE_CUR("Suspending for lock %d\n", l->ident); | ||
330 | |||
331 | raw_spin_unlock_irqrestore(dgl_lock, irqflags); // free dgl_lock before suspending | ||
332 | |||
333 | schedule(); // suspend!!! | ||
334 | |||
335 | TS_DGL_LOCK_RESUME; | ||
336 | |||
337 | TRACE_CUR("Woken up from DGL suspension.\n"); | ||
338 | |||
339 | goto all_acquired; // we should hold all locks when we wake up. | ||
340 | } | ||
341 | } | ||
342 | |||
343 | TRACE_CUR("Didn't have to suspend after all, but calling schedule() anyway.\n"); | ||
344 | //BUG(); | ||
345 | } | ||
346 | |||
347 | raw_spin_unlock_irqrestore(dgl_lock, irqflags); | ||
348 | |||
349 | all_acquired: | ||
350 | |||
351 | // FOR SANITY CHECK FOR TESTING | ||
352 | // for(i = 0; i < dgl_wait->size; ++i) { | ||
353 | // struct litmus_lock *l = dgl_wait->locks[i]; | ||
354 | // BUG_ON(!l->ops->is_owner(l, dgl_wait->task)); | ||
355 | // } | ||
356 | |||
357 | TRACE_CUR("Acquired entire DGL\n"); | ||
358 | |||
359 | return 0; | ||
360 | } | ||
361 | |||
362 | static int supports_dgl(struct litmus_lock *l) | ||
363 | { | ||
364 | struct litmus_lock_ops* ops = l->ops; | ||
365 | |||
366 | return (ops->dgl_lock && | ||
367 | ops->is_owner && | ||
368 | ops->enable_priority); | ||
369 | } | ||
370 | |||
371 | asmlinkage long sys_litmus_dgl_lock(void* __user usr_dgl_ods, int dgl_size) | ||
372 | { | ||
373 | struct task_struct *t = current; | ||
374 | long err = -EINVAL; | ||
375 | int dgl_ods[MAX_DGL_SIZE]; | ||
376 | int i; | ||
377 | |||
378 | dgl_wait_state_t dgl_wait_state; // lives on the stack until all resources in DGL are held. | ||
379 | |||
380 | if(dgl_size > MAX_DGL_SIZE || dgl_size < 1) | ||
381 | goto out; | ||
382 | |||
383 | if(!access_ok(VERIFY_READ, usr_dgl_ods, dgl_size*(sizeof(int)))) | ||
384 | goto out; | ||
385 | |||
386 | if(__copy_from_user(&dgl_ods, usr_dgl_ods, dgl_size*(sizeof(int)))) | ||
387 | goto out; | ||
388 | |||
389 | if (!is_realtime(t)) { | ||
390 | err = -EPERM; | ||
391 | goto out; | ||
392 | } | ||
393 | |||
394 | for(i = 0; i < dgl_size; ++i) { | ||
395 | struct od_table_entry *entry = get_entry_for_od(dgl_ods[i]); | ||
396 | if(entry && is_lock(entry)) { | ||
397 | dgl_wait_state.locks[i] = get_lock(entry); | ||
398 | if(!supports_dgl(dgl_wait_state.locks[i])) { | ||
399 | TRACE_CUR("Lock %d does not support all required DGL operations.\n", | ||
400 | dgl_wait_state.locks[i]->ident); | ||
401 | goto out; | ||
402 | } | ||
403 | } | ||
404 | else { | ||
405 | TRACE_CUR("Invalid lock identifier\n"); | ||
406 | goto out; | ||
407 | } | ||
408 | } | ||
409 | |||
410 | dgl_wait_state.task = t; | ||
411 | dgl_wait_state.size = dgl_size; | ||
412 | |||
413 | TS_DGL_LOCK_START; | ||
414 | err = do_litmus_dgl_lock(&dgl_wait_state); | ||
415 | |||
416 | /* Note: task my have been suspended or preempted in between! Take | ||
417 | * this into account when computing overheads. */ | ||
418 | TS_DGL_LOCK_END; | ||
419 | |||
420 | out: | ||
421 | return err; | ||
422 | } | ||
423 | |||
424 | static long do_litmus_dgl_unlock(struct litmus_lock* dgl_locks[], int dgl_size) | ||
425 | { | ||
426 | int i; | ||
427 | long err = 0; | ||
428 | |||
429 | TRACE_CUR("Unlocking a DGL of %d size\n", dgl_size); | ||
430 | |||
431 | for(i = dgl_size - 1; i >= 0; --i) { // unlock in reverse order | ||
432 | |||
433 | struct litmus_lock *l = dgl_locks[i]; | ||
434 | long tmp_err; | ||
435 | |||
436 | TRACE_CUR("Unlocking lock %d of DGL.\n", l->ident); | ||
437 | |||
438 | tmp_err = l->ops->unlock(l); | ||
439 | |||
440 | if(tmp_err) { | ||
441 | TRACE_CUR("There was an error unlocking %d: %d.\n", l->ident, tmp_err); | ||
442 | err = tmp_err; | ||
443 | } | ||
444 | } | ||
445 | |||
446 | TRACE_CUR("DGL unlocked. err = %d\n", err); | ||
447 | |||
448 | return err; | ||
449 | } | ||
450 | |||
451 | asmlinkage long sys_litmus_dgl_unlock(void* __user usr_dgl_ods, int dgl_size) | ||
452 | { | ||
453 | long err = -EINVAL; | ||
454 | int dgl_ods[MAX_DGL_SIZE]; | ||
455 | struct od_table_entry* entry; | ||
456 | int i; | ||
457 | |||
458 | struct litmus_lock* dgl_locks[MAX_DGL_SIZE]; | ||
459 | |||
460 | if(dgl_size > MAX_DGL_SIZE || dgl_size < 1) | ||
461 | goto out; | ||
462 | |||
463 | if(!access_ok(VERIFY_READ, usr_dgl_ods, dgl_size*(sizeof(int)))) | ||
464 | goto out; | ||
465 | |||
466 | if(__copy_from_user(&dgl_ods, usr_dgl_ods, dgl_size*(sizeof(int)))) | ||
467 | goto out; | ||
468 | |||
469 | for(i = 0; i < dgl_size; ++i) { | ||
470 | entry = get_entry_for_od(dgl_ods[i]); | ||
471 | if(entry && is_lock(entry)) { | ||
472 | dgl_locks[i] = get_lock(entry); | ||
473 | if(!supports_dgl(dgl_locks[i])) { | ||
474 | TRACE_CUR("Lock %d does not support all required DGL operations.\n", | ||
475 | dgl_locks[i]->ident); | ||
476 | goto out; | ||
477 | } | ||
478 | } | ||
479 | else { | ||
480 | TRACE_CUR("Invalid lock identifier\n"); | ||
481 | goto out; | ||
482 | } | ||
483 | } | ||
484 | |||
485 | TS_DGL_UNLOCK_START; | ||
486 | err = do_litmus_dgl_unlock(dgl_locks, dgl_size); | ||
487 | |||
488 | /* Note: task my have been suspended or preempted in between! Take | ||
489 | * this into account when computing overheads. */ | ||
490 | TS_DGL_UNLOCK_END; | ||
491 | |||
492 | out: | ||
493 | return err; | ||
494 | } | ||
495 | |||
496 | #else // CONFIG_LITMUS_DGL_SUPPORT | ||
497 | |||
498 | asmlinkage long sys_litmus_dgl_lock(void* __user usr_dgl_ods, int dgl_size) | ||
499 | { | ||
500 | return -ENOSYS; | ||
501 | } | ||
502 | |||
503 | asmlinkage long sys_litmus_dgl_unlock(void* __user usr_dgl_ods, int dgl_size) | ||
504 | { | ||
505 | return -ENOSYS; | ||
506 | } | ||
507 | |||
508 | #endif | ||
124 | 509 | ||
125 | #else | 510 | #else // CONFIG_LITMUS_LOCKING |
126 | 511 | ||
127 | struct fdso_ops generic_lock_ops = {}; | 512 | struct fdso_ops generic_lock_ops = {}; |
128 | 513 | ||