+/*
+ * Evaluates to false if transaction begins, true if it has failed due to SIGBUS.
+ * The entire transaction must complete before the current function returns.
+ * A transaction can contain 0 or more tracked ranges as sigbus begin/end pairs.
+ */
+#define sigbus_begin() \
+({ \
+ assert(!lttng_ust_sigbus_state.jmp_ready); \
+ if (!lttng_ust_sigbus_state.head.next) { \
+ /* \
+ * Lazy init because static list initialisation is \
+ * problematic for TLS variable. \
+ */ \
+ CDS_INIT_LIST_HEAD(<tng_ust_sigbus_state.head); \
+ } \
+ if (sigsetjmp(lttng_ust_sigbus_state.sj_env, 1)) { \
+ /* SIGBUS. */ \
+ CMM_STORE_SHARED(lttng_ust_sigbus_state.jmp_ready, 0); \
+ true; \
+ } \
+ cmm_barrier(); \
+ CMM_STORE_SHARED(lttng_ust_sigbus_state.jmp_ready, 1); \
+ false; \
+})
+
+static void sigbus_end(void)
+{
+ assert(lttng_ust_sigbus_state.jmp_ready);
+ cmm_barrier();
+ CMM_STORE_SHARED(lttng_ust_sigbus_state.jmp_ready, 0);
+}
+
+static
+void lttng_ust_sigbus_add_range(struct lttng_ust_sigbus_range *range, void *start, size_t len)
+{
+ range->start = start;
+ range->end = (char *)start + len;
+ cds_list_add_rcu(&range->node, <tng_ust_sigbus_state.head);
+ cmm_barrier();
+}
+
+static
+void lttng_ust_sigbus_del_range(struct lttng_ust_sigbus_range *range)
+{
+ cmm_barrier();
+ cds_list_del_rcu(&range->node);
+}
+
+void lttng_ust_ctl_sigbus_handle(void *addr)
+{
+ struct lttng_ust_sigbus_range *range;
+
+ if (!CMM_LOAD_SHARED(lttng_ust_sigbus_state.jmp_ready))
+ return;
+ cds_list_for_each_entry_rcu(range, <tng_ust_sigbus_state.head, node) {
+ if (addr < range->start || addr >= range->end)
+ continue;
+ siglongjmp(lttng_ust_sigbus_state.sj_env, 1);
+ }
+}
+