try-catch.c 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  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. #include <kunit/test.h>
  10. #include <linux/completion.h>
  11. #include <linux/kernel.h>
  12. #include <linux/kthread.h>
  13. #include <linux/sched/task.h>
  14. #include "try-catch-impl.h"
  15. void __noreturn kunit_try_catch_throw(struct kunit_try_catch *try_catch)
  16. {
  17. try_catch->try_result = -EFAULT;
  18. kthread_exit(0);
  19. }
  20. EXPORT_SYMBOL_GPL(kunit_try_catch_throw);
  21. static int kunit_generic_run_threadfn_adapter(void *data)
  22. {
  23. struct kunit_try_catch *try_catch = data;
  24. try_catch->try_result = -EINTR;
  25. try_catch->try(try_catch->context);
  26. if (try_catch->try_result == -EINTR)
  27. try_catch->try_result = 0;
  28. return 0;
  29. }
  30. void kunit_try_catch_run(struct kunit_try_catch *try_catch, void *context)
  31. {
  32. struct kunit *test = try_catch->test;
  33. struct task_struct *task_struct;
  34. struct completion *task_done;
  35. int exit_code, time_remaining;
  36. try_catch->context = context;
  37. try_catch->try_result = 0;
  38. task_struct = kthread_create(kunit_generic_run_threadfn_adapter,
  39. try_catch, "kunit_try_catch_thread");
  40. if (IS_ERR(task_struct)) {
  41. try_catch->try_result = PTR_ERR(task_struct);
  42. try_catch->catch(try_catch->context);
  43. return;
  44. }
  45. get_task_struct(task_struct);
  46. /*
  47. * As for a vfork(2), task_struct->vfork_done (pointing to the
  48. * underlying kthread->exited) can be used to wait for the end of a
  49. * kernel thread. It is set to NULL when the thread exits, so we
  50. * keep a copy here.
  51. */
  52. task_done = task_struct->vfork_done;
  53. wake_up_process(task_struct);
  54. time_remaining = wait_for_completion_timeout(
  55. task_done, try_catch->timeout);
  56. if (time_remaining == 0) {
  57. try_catch->try_result = -ETIMEDOUT;
  58. kthread_stop(task_struct);
  59. }
  60. put_task_struct(task_struct);
  61. exit_code = try_catch->try_result;
  62. if (!exit_code)
  63. return;
  64. if (exit_code == -EFAULT)
  65. try_catch->try_result = 0;
  66. else if (exit_code == -EINTR) {
  67. if (test->last_seen.file)
  68. kunit_err(test, "try faulted: last line seen %s:%d\n",
  69. test->last_seen.file, test->last_seen.line);
  70. else
  71. kunit_err(test, "try faulted\n");
  72. } else if (exit_code == -ETIMEDOUT)
  73. kunit_err(test, "try timed out\n");
  74. else if (exit_code)
  75. kunit_err(test, "Unknown error: %d\n", exit_code);
  76. try_catch->catch(try_catch->context);
  77. }
  78. EXPORT_SYMBOL_GPL(kunit_try_catch_run);