1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
|
#ifndef LITMUS_H
#define LITMUS_H
#ifdef __cplusplus
extern "C" {
#endif
#include <sys/types.h>
#include <stdint.h>
#include <setjmp.h>
/* Include kernel header.
* This is required for the rt_param
* and control_page structures.
*/
#include "litmus/rt_param.h"
#include "litmus/signal.h"
#include "asm/cycles.h" /* for null_call() */
#include "migration.h"
void init_rt_task_param(struct rt_task* param);
int set_rt_task_param(pid_t pid, struct rt_task* param);
int get_rt_task_param(pid_t pid, struct rt_task* param);
/* Release-master-aware functions for getting the first
* CPU in a particular cluster or partition. Use these
* to set rt_task::cpu for cluster/partitioned scheduling.
*/
int partition_to_cpu(int partition);
int cluster_to_first_cpu(int cluster, int cluster_size);
/* Convenience functions for setting up real-time tasks.
* Default behaviors set by init_rt_task_params() used.
* Also sets affinity masks for clustered/partitions
* functions. Time units in nanoseconds. */
int sporadic_global(lt_t e_ns, lt_t p_ns);
int sporadic_partitioned(lt_t e_ns, lt_t p_ns, int partition);
int sporadic_clustered(lt_t e_ns, lt_t p_ns, int cluster, int cluster_size);
/* simple time unit conversion macros */
#define s2ns(s) ((s)*1000000000LL)
#define s2us(s) ((s)*1000000LL)
#define s2ms(s) ((s)*1000LL)
#define ms2ns(ms) ((ms)*1000000LL)
#define ms2us(ms) ((ms)*1000LL)
#define us2ns(us) ((us)*1000LL)
/* file descriptor attached shared objects support */
typedef enum {
FMLP_SEM = 0,
SRP_SEM = 1,
MPCP_SEM = 2,
MPCP_VS_SEM = 3,
DPCP_SEM = 4,
PCP_SEM = 5,
FIFO_MUTEX = 6,
IKGLP_SEM = 7,
KFMLP_SEM = 8,
IKGLP_SIMPLE_GPU_AFF_OBS = 9,
IKGLP_GPU_AFF_OBS = 10,
KFMLP_SIMPLE_GPU_AFF_OBS = 11,
KFMLP_GPU_AFF_OBS = 12,
PRIOQ_MUTEX = 13,
} obj_type_t;
int lock_protocol_for_name(const char* name);
const char* name_for_lock_protocol(int id);
int od_openx(int fd, obj_type_t type, int obj_id, void* config);
int od_close(int od);
static inline int od_open(int fd, obj_type_t type, int obj_id)
{
return od_openx(fd, type, obj_id, 0);
}
int litmus_open_lock(
obj_type_t protocol, /* which locking protocol to use, e.g., FMLP_SEM */
int lock_id, /* numerical id of the lock, user-specified */
const char* ns, /* path to a shared file */
void *config_param); /* any extra info needed by the protocol (such
* as CPU under SRP and PCP), may be NULL */
/* real-time locking protocol support */
int litmus_lock(int od);
int litmus_unlock(int od);
/* Dynamic group lock support. ods arrays MUST BE PARTIALLY ORDERED!!!!!!
* Use the same ordering for lock and unlock.
*
* Ex:
* litmus_dgl_lock({A, B, C, D}, 4);
* litmus_dgl_unlock({A, B, C, D}, 4);
*/
int litmus_dgl_lock(int* ods, int dgl_size);
int litmus_dgl_unlock(int* ods, int dgl_size);
/* nvidia graphics cards */
int register_nv_device(int nv_device_id);
int unregister_nv_device(int nv_device_id);
/* job control*/
int get_job_no(unsigned int* job_no);
int wait_for_job_release(unsigned int job_no);
int sleep_next_period(void);
/* library functions */
int init_litmus(void);
int init_rt_thread(void);
void exit_litmus(void);
/* A real-time program. */
typedef int (*rt_fn_t)(void*);
/* exec another program as a real-time task. */
int create_rt_task(rt_fn_t rt_prog, void *arg, struct rt_task* param);
/* per-task modes */
enum rt_task_mode_t {
BACKGROUND_TASK = 0,
LITMUS_RT_TASK = 1
};
int task_mode(int target_mode);
void show_rt_param(struct rt_task* tp);
task_class_t str2class(const char* str);
/* non-preemptive section support */
void enter_np(void);
void exit_np(void);
int requested_to_preempt(void);
/* task system support */
int wait_for_ts_release();
int wait_for_ts_release2(struct timespec *release);
int release_ts(lt_t *delay);
int get_nr_ts_release_waiters(void);
int read_litmus_stats(int *ready, int *total);
int enable_aux_rt_tasks(int flags);
int disable_aux_rt_tasks(int flags);
/* sleep for some number of nanoseconds */
int lt_sleep(lt_t timeout);
/* CPU time consumed so far in seconds */
double cputime(void);
/* wall-clock time in seconds */
double wctime(void);
/* semaphore allocation */
typedef int (*open_sem_t)(int fd, int name);
static inline int open_fmlp_sem(int fd, int name)
{
return od_open(fd, FMLP_SEM, name);
}
static inline int open_kfmlp_sem(int fd, int name, unsigned int nr_replicas)
{
if (!nr_replicas)
return -1;
return od_openx(fd, KFMLP_SEM, name, &nr_replicas);
}
static inline int open_srp_sem(int fd, int name)
{
return od_open(fd, SRP_SEM, name);
}
static inline int open_pcp_sem(int fd, int name, int cpu)
{
return od_openx(fd, PCP_SEM, name, &cpu);
}
static inline int open_mpcp_sem(int fd, int name)
{
return od_open(fd, MPCP_SEM, name);
}
static inline int open_dpcp_sem(int fd, int name, int cpu)
{
return od_openx(fd, DPCP_SEM, name, &cpu);
}
static inline int open_fifo_sem(int fd, int name)
{
return od_open(fd, FIFO_MUTEX, name);
}
static inline int open_prioq_sem(int fd, int name)
{
return od_open(fd, PRIOQ_MUTEX, name);
}
int open_ikglp_sem(int fd, int name, unsigned int nr_replicas);
/* KFMLP-based Token Lock for GPUs
* Legacy; mostly untested.
*/
int open_kfmlp_gpu_sem(int fd, int name,
unsigned int num_gpus, unsigned int gpu_offset, unsigned int rho,
int affinity_aware /* bool */);
/* -- Example Configurations --
*
* Optimal IKGLP Configuration:
* max_in_fifos = IKGLP_M_IN_FIFOS
* max_fifo_len = IKGLP_OPTIMAL_FIFO_LEN
*
* IKGLP with Relaxed FIFO Length Constraints:
* max_in_fifos = IKGLP_M_IN_FIFOS
* max_fifo_len = IKGLP_UNLIMITED_FIFO_LEN
* NOTE: max_in_fifos still limits total number of requests in FIFOs.
*
* KFMLP Configuration (FIFO queues only):
* max_in_fifos = IKGLP_UNLIMITED_IN_FIFOS
* max_fifo_len = IKGLP_UNLIMITED_FIFO_LEN
* NOTE: Uses a non-optimal IKGLP configuration, not an actual KFMLP_SEM.
*
* RGEM-like Configuration (priority queues only):
* max_in_fifos = 1..(rho*num_gpus)
* max_fifo_len = 1
*
* For exclusive GPU allocation, use rho = 1
* For trivial token lock, use rho = # of tasks in task set
*
* A simple load-balancing heuristic will still be used if
* enable_affinity_heuristics = 0.
*
* Other constraints:
* - max_in_fifos <= max_fifo_len * rho
* (unless max_in_fifos = IKGLP_UNLIMITED_IN_FIFOS and
* max_fifo_len = IKGLP_UNLIMITED_FIFO_LEN
* - rho > 0
* - num_gpus > 0
*/
// takes names 'name' and 'name+1'
int open_gpusync_token_lock(int fd, int name,
unsigned int num_gpus, unsigned int gpu_offset,
unsigned int rho, unsigned int max_in_fifos,
unsigned int max_fifo_len,
int enable_affinity_heuristics /* bool */);
/* syscall overhead measuring */
int null_call(cycles_t *timestamp);
/*
* get control page:
* atm it is used only by preemption migration overhead code
* but it is very general and can be used for different purposes
*/
struct control_page* get_ctrl_page(void);
/* sched_trace injection */
int inject_name(void);
int inject_param(void); /* sporadic_task_ns*() must have already been called */
int inject_release(lt_t release, lt_t deadline, unsigned int job_no);
int inject_completion(unsigned int job_no);
int inject_gpu_migration(unsigned int to, unsigned int from);
int __inject_action(unsigned int action);
/*
#define inject_action(COUNT) \
do { \
unsigned int temp = (COUNT); \
printf("%s:%d:%d\n",__FUNCTION__,__LINE__,temp); \
__inject_action(temp); \
}while(0);
*/
#define inject_action(COUNT) \
do { \
}while(0);
/* Litmus signal handling */
typedef struct litmus_sigjmp
{
sigjmp_buf env;
struct litmus_sigjmp *prev;
} litmus_sigjmp_t;
void push_sigjmp(litmus_sigjmp_t* buf);
litmus_sigjmp_t* pop_sigjmp(void);
typedef void (*litmus_sig_handler_t)(int);
typedef void (*litmus_sig_actions_t)(int, siginfo_t *, void *);
/* ignore specified signals. all signals raised while ignored are dropped */
void ignore_litmus_signals(unsigned long litmus_sig_mask);
/* register a handler for the given set of litmus signals */
void activate_litmus_signals(unsigned long litmus_sig_mask,
litmus_sig_handler_t handler);
/* register an action signal handler for a given set of signals */
void activate_litmus_signal_actions(unsigned long litmus_sig_mask,
litmus_sig_actions_t handler);
/* Block a given set of litmus signals. Any signals raised while blocked
* are queued and delivered after unblocking. Call ignore_litmus_signals()
* before unblocking if you wish to discard these. Blocking may be
* useful to protect COTS code in Litmus that may not be able to deal
* with exception-raising signals.
*/
void block_litmus_signals(unsigned long litmus_sig_mask);
/* Unblock a given set of litmus signals. */
void unblock_litmus_signals(unsigned long litmus_sig_mask);
#define SIG_BUDGET_MASK 0x00000001
/* more ... */
#define ALL_LITMUS_SIG_MASKS (SIG_BUDGET_MASK)
/* Try/Catch structures useful for implementing abortable jobs.
* Should only be used in legitimate cases. ;)
*/
#define LITMUS_TRY \
do { \
int sigsetjmp_ret_##__FUNCTION__##__LINE__; \
litmus_sigjmp_t lit_env_##__FUNCTION__##__LINE__; \
push_sigjmp(&lit_env_##__FUNCTION__##__LINE__); \
sigsetjmp_ret_##__FUNCTION__##__LINE__ = \
sigsetjmp(lit_env_##__FUNCTION__##__LINE__.env, 1); \
if (sigsetjmp_ret_##__FUNCTION__##__LINE__ == 0) {
#define LITMUS_CATCH(x) \
} else if (sigsetjmp_ret_##__FUNCTION__##__LINE__ == (x)) {
#define END_LITMUS_TRY \
} /* end if-else-if chain */ \
} while(0); /* end do from 'LITMUS_TRY' */
/* Calls siglongjmp(signum). Use with TRY/CATCH.
* Example:
* activate_litmus_signals(SIG_BUDGET_MASK, longjmp_on_litmus_signal);
*/
void longjmp_on_litmus_signal(int signum);
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
/* Expose litmus exceptions if C++.
*
* KLUDGE: We define everything in the header since liblitmus is a C-only
* library, but this header could be included in C++ code.
*/
#include <exception>
namespace litmus
{
class litmus_exception: public std::exception
{
public:
litmus_exception() throw() {}
virtual ~litmus_exception() throw() {}
virtual const char* what() const throw() { return "litmus_exception";}
};
class sigbudget: public litmus_exception
{
public:
sigbudget() throw() {}
virtual ~sigbudget() throw() {}
virtual const char* what() const throw() { return "sigbudget"; }
};
/* Must compile your program with "non-call-exception". */
static __attribute__((used))
void throw_on_litmus_signal(int signum)
{
/* We have to unblock the received signal to get more in the future
* because we are not calling siglongjmp(), which normally restores
* the mask for us.
*/
if (SIG_BUDGET == signum) {
unblock_litmus_signals(SIG_BUDGET_MASK);
throw sigbudget();
}
/* else if (...) */
else {
/* silently ignore */
}
}
}; /* end namespace 'litmus' */
#endif /* end __cplusplus */
#endif
|