diff options
Diffstat (limited to 'userspace/src/exec.c')
-rw-r--r-- | userspace/src/exec.c | 142 |
1 files changed, 128 insertions, 14 deletions
diff --git a/userspace/src/exec.c b/userspace/src/exec.c index b9ba1336..8a99437b 100644 --- a/userspace/src/exec.c +++ b/userspace/src/exec.c | |||
@@ -21,6 +21,10 @@ | |||
21 | */ | 21 | */ |
22 | 22 | ||
23 | #include <stdlib.h> | 23 | #include <stdlib.h> |
24 | #include <string.h> | ||
25 | #include <pthread.h> | ||
26 | #include <semaphore.h> | ||
27 | #include <signal.h> | ||
24 | 28 | ||
25 | #include <unit/io.h> | 29 | #include <unit/io.h> |
26 | #include <unit/core.h> | 30 | #include <unit/core.h> |
@@ -31,19 +35,39 @@ | |||
31 | #include <nvgpu/posix/probe.h> | 35 | #include <nvgpu/posix/probe.h> |
32 | 36 | ||
33 | /* | 37 | /* |
38 | * Sempaphore to limit the number of threads | ||
39 | */ | ||
40 | sem_t unit_thread_semaphore; | ||
41 | |||
42 | /* | ||
43 | * C11 thread local storage, used to access test context when a signal is | ||
44 | * received (ex: SIGSEGV) in a thread. | ||
45 | */ | ||
46 | _Thread_local struct unit_module *thread_local_module; | ||
47 | _Thread_local struct unit_module_test *thread_local_test; | ||
48 | |||
49 | /* | ||
34 | * Execute a module and all its subtests. This function builds a gk20a for the | 50 | * Execute a module and all its subtests. This function builds a gk20a for the |
35 | * test to use by executing nvgpu_posix_probe() and nvgpu_posix_cleanup(); | 51 | * test to use by executing nvgpu_posix_probe() and nvgpu_posix_cleanup(); |
36 | */ | 52 | */ |
37 | static int core_exec_module(struct unit_fw *fw, | 53 | static void *core_exec_module(void *module_param) |
38 | struct unit_module *module) | ||
39 | { | 54 | { |
40 | unsigned int i; | 55 | unsigned int i; |
41 | struct gk20a *g = fw->nvgpu.nvgpu_posix_probe(); | 56 | struct unit_module *module = (struct unit_module *) module_param; |
57 | struct gk20a *g; | ||
58 | |||
59 | g = module->fw->nvgpu.nvgpu_posix_probe(); | ||
60 | |||
61 | if (!g) { | ||
62 | core_msg_color(module->fw, C_RED, | ||
63 | " nvgpu_posix_probe failed: Module %s\n", | ||
64 | module->name); | ||
65 | goto thread_exit; | ||
66 | } | ||
42 | 67 | ||
43 | if (!g) | 68 | core_vbs(module->fw, 1, "Execing module: %s\n", module->name); |
44 | return -1; | ||
45 | 69 | ||
46 | core_vbs(fw, 1, "Execing module: %s\n", module->name); | 70 | thread_local_module = module; |
47 | 71 | ||
48 | /* | 72 | /* |
49 | * Execute each test within the module. No reinit is done between tests. | 73 | * Execute each test within the module. No reinit is done between tests. |
@@ -53,21 +77,86 @@ static int core_exec_module(struct unit_fw *fw, | |||
53 | for (i = 0; i < module->nr_tests; i++) { | 77 | for (i = 0; i < module->nr_tests; i++) { |
54 | struct unit_module_test *t = module->tests + i; | 78 | struct unit_module_test *t = module->tests + i; |
55 | int test_status; | 79 | int test_status; |
80 | thread_local_test = t; | ||
56 | 81 | ||
57 | core_msg(fw, "Running %s.%s\n", module->name, t->name); | 82 | core_msg(module->fw, "Running %s.%s\n", module->name, |
83 | t->name); | ||
58 | test_status = t->fn(module, g, t->args); | 84 | test_status = t->fn(module, g, t->args); |
59 | 85 | ||
60 | if (test_status != UNIT_SUCCESS) | 86 | if (test_status != UNIT_SUCCESS) |
61 | core_msg_color(fw, C_RED, | 87 | core_msg_color(module->fw, C_RED, |
62 | " Unit error! Test %s.%s FAILED!\n", | 88 | " Unit error! Test %s.%s FAILED!\n", |
63 | module->name, t->name); | 89 | module->name, t->name); |
64 | 90 | ||
65 | core_add_test_record(fw, module, t, | 91 | core_add_test_record(module->fw, module, t, |
66 | test_status == UNIT_SUCCESS); | 92 | test_status == UNIT_SUCCESS); |
67 | } | 93 | } |
68 | 94 | ||
69 | fw->nvgpu.nvgpu_posix_cleanup(g); | 95 | module->fw->nvgpu.nvgpu_posix_cleanup(g); |
96 | |||
97 | core_vbs(module->fw, 1, "Module completed: %s\n", module->name); | ||
98 | thread_exit: | ||
99 | sem_post(&unit_thread_semaphore); | ||
100 | return NULL; | ||
101 | } | ||
102 | |||
103 | /* | ||
104 | * According to POSIX, "Signals which are generated by some action attributable | ||
105 | * to a particular thread, such as a hardware fault, shall be generated for the | ||
106 | * thread that caused the signal to be generated." | ||
107 | * This custom signal handler will be run from within the thread that caused the | ||
108 | * exception. Thanks to the context being saved in local thread storage, it is | ||
109 | * then trivial to report which test case failed, and then terminate the thread. | ||
110 | */ | ||
111 | static void thread_error_handler(int sig, siginfo_t *siginfo, void *context) | ||
112 | { | ||
113 | core_msg_color(thread_local_module->fw, C_RED, | ||
114 | " Signal %d in Test: %s.%s!\n", sig, | ||
115 | thread_local_module->name, thread_local_test->name); | ||
116 | core_add_test_record(thread_local_module->fw, thread_local_module, | ||
117 | thread_local_test, false); | ||
118 | sem_post(&unit_thread_semaphore); | ||
119 | pthread_exit(NULL); | ||
120 | } | ||
121 | |||
122 | /* | ||
123 | * Install a custom signal handler for several signals to be used when running | ||
124 | * in multithreaded environment. | ||
125 | */ | ||
126 | static int install_thread_error_handler(void) | ||
127 | { | ||
128 | struct sigaction action; | ||
129 | int err; | ||
130 | |||
131 | memset(&action, 0, sizeof(action)); | ||
132 | action.sa_sigaction = &thread_error_handler; | ||
133 | action.sa_flags = SA_SIGINFO; | ||
70 | 134 | ||
135 | /* SIGSEGV: Invalid memory reference */ | ||
136 | err = sigaction(SIGSEGV, &action, NULL); | ||
137 | if (err < 0) { | ||
138 | return err; | ||
139 | } | ||
140 | /* SIGILL: Illegal Instruction */ | ||
141 | err = sigaction(SIGILL, &action, NULL); | ||
142 | if (err < 0) { | ||
143 | return err; | ||
144 | } | ||
145 | /* SIGFPE: Floating-point exception */ | ||
146 | err = sigaction(SIGFPE, &action, NULL); | ||
147 | if (err < 0) { | ||
148 | return err; | ||
149 | } | ||
150 | /* SIGBUS: Bus error */ | ||
151 | err = sigaction(SIGBUS, &action, NULL); | ||
152 | if (err < 0) { | ||
153 | return err; | ||
154 | } | ||
155 | /* SIGSYS: Bad system call */ | ||
156 | err = sigaction(SIGSYS, &action, NULL); | ||
157 | if (err < 0) { | ||
158 | return err; | ||
159 | } | ||
71 | return 0; | 160 | return 0; |
72 | } | 161 | } |
73 | 162 | ||
@@ -76,14 +165,39 @@ static int core_exec_module(struct unit_fw *fw, | |||
76 | */ | 165 | */ |
77 | int core_exec(struct unit_fw *fw) | 166 | int core_exec(struct unit_fw *fw) |
78 | { | 167 | { |
79 | int ret; | ||
80 | struct unit_module **modules; | 168 | struct unit_module **modules; |
169 | int err = 0; | ||
170 | |||
171 | core_vbs(fw, 1, "Using %d threads\n", fw->args->thread_count); | ||
172 | sem_init(&unit_thread_semaphore, 0, fw->args->thread_count); | ||
173 | |||
174 | /* | ||
175 | * If running single threaded, keep the default SIGSEGV handler to make | ||
176 | * interactive debugging easier, otherwise install the custom one. | ||
177 | */ | ||
178 | if (fw->args->thread_count > 1) { | ||
179 | err = install_thread_error_handler(); | ||
180 | if (err != 0) { | ||
181 | core_msg_color(fw, C_RED, | ||
182 | " Failed to install signal handler!\n"); | ||
183 | return err; | ||
184 | } | ||
185 | } | ||
81 | 186 | ||
82 | for (modules = fw->modules; *modules != NULL; modules++) { | 187 | for (modules = fw->modules; *modules != NULL; modules++) { |
83 | ret = core_exec_module(fw, *modules); | 188 | if (fw->args->thread_count == 1) { |
189 | core_exec_module(*modules); | ||
190 | } else { | ||
191 | sem_wait(&unit_thread_semaphore); | ||
192 | pthread_create(&((*modules)->thread), NULL, | ||
193 | core_exec_module, (void *) *modules); | ||
194 | } | ||
195 | } | ||
84 | 196 | ||
85 | if (ret != 0) | 197 | if (fw->args->thread_count > 1) { |
86 | return ret; | 198 | for (modules = fw->modules; *modules != NULL; modules++) { |
199 | pthread_join((*modules)->thread, NULL); | ||
200 | } | ||
87 | } | 201 | } |
88 | 202 | ||
89 | return 0; | 203 | return 0; |