1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * An API to allow a function, that may fail, to be executed, and recover in a 4 * controlled manner. 5 * 6 * Copyright (C) 2019, Google LLC. 7 * Author: Brendan Higgins <brendanhiggins@google.com> 8 */ 9 10 #include <kunit/test.h> 11 #include <linux/completion.h> 12 #include <linux/kernel.h> 13 #include <linux/kthread.h> 14 #include <linux/sched/task.h> 15 16 #include "try-catch-impl.h" 17 18 void __noreturn kunit_try_catch_throw(struct kunit_try_catch *try_catch) 19 { 20 try_catch->try_result = -EFAULT; 21 kthread_exit(0); 22 } 23 EXPORT_SYMBOL_GPL(kunit_try_catch_throw); 24 25 static int kunit_generic_run_threadfn_adapter(void *data) 26 { 27 struct kunit_try_catch *try_catch = data; 28 29 try_catch->try_result = -EINTR; 30 try_catch->try(try_catch->context); 31 if (try_catch->try_result == -EINTR) 32 try_catch->try_result = 0; 33 34 return 0; 35 } 36 37 void kunit_try_catch_run(struct kunit_try_catch *try_catch, void *context) 38 { 39 struct kunit *test = try_catch->test; 40 struct task_struct *task_struct; 41 struct completion *task_done; 42 int exit_code, time_remaining; 43 44 try_catch->context = context; 45 try_catch->try_result = 0; 46 task_struct = kthread_create(kunit_generic_run_threadfn_adapter, 47 try_catch, "kunit_try_catch_thread"); 48 if (IS_ERR(task_struct)) { 49 try_catch->try_result = PTR_ERR(task_struct); 50 try_catch->catch(try_catch->context); 51 return; 52 } 53 get_task_struct(task_struct); 54 /* 55 * As for a vfork(2), task_struct->vfork_done (pointing to the 56 * underlying kthread->exited) can be used to wait for the end of a 57 * kernel thread. It is set to NULL when the thread exits, so we 58 * keep a copy here. 59 */ 60 task_done = task_struct->vfork_done; 61 wake_up_process(task_struct); 62 63 time_remaining = wait_for_completion_timeout( 64 task_done, try_catch->timeout); 65 if (time_remaining == 0) { 66 try_catch->try_result = -ETIMEDOUT; 67 kthread_stop(task_struct); 68 } 69 70 put_task_struct(task_struct); 71 exit_code = try_catch->try_result; 72 73 if (!exit_code) 74 return; 75 76 if (exit_code == -EFAULT) 77 try_catch->try_result = 0; 78 else if (exit_code == -EINTR) { 79 if (test->last_seen.file) 80 kunit_err(test, "try faulted: last line seen %s:%d\n", 81 test->last_seen.file, test->last_seen.line); 82 else 83 kunit_err(test, "try faulted\n"); 84 } else if (exit_code == -ETIMEDOUT) 85 kunit_err(test, "try timed out\n"); 86 else if (exit_code) 87 kunit_err(test, "Unknown error: %d\n", exit_code); 88 89 try_catch->catch(try_catch->context); 90 } 91 EXPORT_SYMBOL_GPL(kunit_try_catch_run); 92