try-catch.c 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  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 "try-catch-impl.h"
  14. void __noreturn kunit_try_catch_throw(struct kunit_try_catch *try_catch)
  15. {
  16. try_catch->try_result = -EFAULT;
  17. complete_and_exit(try_catch->try_completion, -EFAULT);
  18. }
  19. EXPORT_SYMBOL_GPL(kunit_try_catch_throw);
  20. static int kunit_generic_run_threadfn_adapter(void *data)
  21. {
  22. struct kunit_try_catch *try_catch = data;
  23. try_catch->try(try_catch->context);
  24. complete_and_exit(try_catch->try_completion, 0);
  25. }
  26. static unsigned long kunit_test_timeout(void)
  27. {
  28. /*
  29. * TODO(brendanhiggins@google.com): We should probably have some type of
  30. * variable timeout here. The only question is what that timeout value
  31. * should be.
  32. *
  33. * The intention has always been, at some point, to be able to label
  34. * tests with some type of size bucket (unit/small, integration/medium,
  35. * large/system/end-to-end, etc), where each size bucket would get a
  36. * default timeout value kind of like what Bazel does:
  37. * https://docs.bazel.build/versions/master/be/common-definitions.html#test.size
  38. * There is still some debate to be had on exactly how we do this. (For
  39. * one, we probably want to have some sort of test runner level
  40. * timeout.)
  41. *
  42. * For more background on this topic, see:
  43. * https://mike-bland.com/2011/11/01/small-medium-large.html
  44. *
  45. * If tests timeout due to exceeding sysctl_hung_task_timeout_secs,
  46. * the task will be killed and an oops generated.
  47. */
  48. return 300 * msecs_to_jiffies(MSEC_PER_SEC); /* 5 min */
  49. }
  50. void kunit_try_catch_run(struct kunit_try_catch *try_catch, void *context)
  51. {
  52. DECLARE_COMPLETION_ONSTACK(try_completion);
  53. struct kunit *test = try_catch->test;
  54. struct task_struct *task_struct;
  55. int exit_code, time_remaining;
  56. try_catch->context = context;
  57. try_catch->try_completion = &try_completion;
  58. try_catch->try_result = 0;
  59. task_struct = kthread_run(kunit_generic_run_threadfn_adapter,
  60. try_catch,
  61. "kunit_try_catch_thread");
  62. if (IS_ERR(task_struct)) {
  63. try_catch->catch(try_catch->context);
  64. return;
  65. }
  66. time_remaining = wait_for_completion_timeout(&try_completion,
  67. kunit_test_timeout());
  68. if (time_remaining == 0) {
  69. kunit_err(test, "try timed out\n");
  70. try_catch->try_result = -ETIMEDOUT;
  71. }
  72. exit_code = try_catch->try_result;
  73. if (!exit_code)
  74. return;
  75. if (exit_code == -EFAULT)
  76. try_catch->try_result = 0;
  77. else if (exit_code == -EINTR)
  78. kunit_err(test, "wake_up_process() was never called\n");
  79. else if (exit_code)
  80. kunit_err(test, "Unknown error: %d\n", exit_code);
  81. try_catch->catch(try_catch->context);
  82. }
  83. EXPORT_SYMBOL_GPL(kunit_try_catch_run);