root/include/rv/da_monitor.h
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright (C) 2019-2022 Red Hat, Inc. Daniel Bristot de Oliveira <bristot@kernel.org>
 *
 * Deterministic automata (DA) monitor functions, to be used together
 * with automata models in C generated by the rvgen tool.
 *
 * The rvgen tool is available at tools/verification/rvgen/
 *
 * For further information, see:
 *   Documentation/trace/rv/monitor_synthesis.rst
 */

#ifndef _RV_DA_MONITOR_H
#define _RV_DA_MONITOR_H

#include <rv/automata.h>
#include <linux/rv.h>
#include <linux/stringify.h>
#include <linux/bug.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/hashtable.h>

/*
 * Per-cpu variables require a unique name although static in some
 * configurations (e.g. CONFIG_DEBUG_FORCE_WEAK_PER_CPU or alpha modules).
 */
#define DA_MON_NAME CONCATENATE(da_mon_, MONITOR_NAME)

static struct rv_monitor rv_this;

/*
 * Hook to allow the implementation of hybrid automata: define it with a
 * function that takes curr_state, event and next_state and returns true if the
 * environment constraints (e.g. timing) are satisfied, false otherwise.
 */
#ifndef da_monitor_event_hook
#define da_monitor_event_hook(...) true
#endif

/*
 * Hook to allow the implementation of hybrid automata: define it with a
 * function that takes the da_monitor and performs further initialisation
 * (e.g. reset set up timers).
 */
#ifndef da_monitor_init_hook
#define da_monitor_init_hook(da_mon)
#endif

/*
 * Hook to allow the implementation of hybrid automata: define it with a
 * function that takes the da_monitor and performs further reset (e.g. reset
 * all clocks).
 */
#ifndef da_monitor_reset_hook
#define da_monitor_reset_hook(da_mon)
#endif

/*
 * Hook to allow the implementation of hybrid automata: define it with a
 * function that waits for the termination of all monitors background
 * activities (e.g. all timers). This hook can sleep.
 */
#ifndef da_monitor_sync_hook
#define da_monitor_sync_hook()
#endif

/*
 * Type for the target id, default to int but can be overridden.
 * A long type can work as hash table key (PER_OBJ) but will be downgraded to
 * int in the event tracepoint.
 * Unused for implicit monitors.
 */
#ifndef da_id_type
#define da_id_type int
#endif

static void react(enum states curr_state, enum events event)
{
        rv_react(&rv_this,
                 "rv: monitor %s does not allow event %s on state %s\n",
                 __stringify(MONITOR_NAME),
                 model_get_event_name(event),
                 model_get_state_name(curr_state));
}

/*
 * da_monitor_reset_state - reset a monitor and setting it to init state
 */
static inline void da_monitor_reset_state(struct da_monitor *da_mon)
{
        WRITE_ONCE(da_mon->monitoring, 0);
        /* Pair with load in __ha_monitor_timer_callback */
        smp_store_release(&da_mon->curr_state, model_get_initial_state());
}

/*
 * da_monitor_reset - reset a monitor and setting it to init state
 */
static inline void da_monitor_reset(struct da_monitor *da_mon)
{
        da_monitor_reset_hook(da_mon);
        da_monitor_reset_state(da_mon);
}

/*
 * da_monitor_start - start monitoring
 *
 * The monitor will ignore all events until monitoring is set to true. This
 * function needs to be called to tell the monitor to start monitoring.
 */
static inline void da_monitor_start(struct da_monitor *da_mon)
{
        da_mon->curr_state = model_get_initial_state();
        da_monitor_init_hook(da_mon);
        /* Pairs with smp_load_acquire in da_monitoring(). */
        smp_store_release(&da_mon->monitoring, 1);
}

/*
 * da_monitoring - returns true if the monitor is processing events
 */
static inline bool da_monitoring(struct da_monitor *da_mon)
{
        /* Pairs with smp_store_release in da_monitor_start(). */
        return smp_load_acquire(&da_mon->monitoring);
}

/*
 * da_monitor_enabled - checks if the monitor is enabled
 */
static inline bool da_monitor_enabled(void)
{
        /* global switch */
        if (unlikely(!rv_monitoring_on()))
                return 0;

        /* monitor enabled */
        if (unlikely(!rv_this.enabled))
                return 0;

        return 1;
}

/*
 * da_monitor_handling_event - checks if the monitor is ready to handle events
 */
static inline bool da_monitor_handling_event(struct da_monitor *da_mon)
{
        if (!da_monitor_enabled())
                return 0;

        /* monitor is actually monitoring */
        if (unlikely(!da_monitoring(da_mon)))
                return 0;

        return 1;
}

#if RV_MON_TYPE == RV_MON_GLOBAL
/*
 * Functions to define, init and get a global monitor.
 */

/*
 * global monitor (a single variable)
 */
static struct da_monitor DA_MON_NAME;

/*
 * da_get_monitor - return the global monitor address
 */
static struct da_monitor *da_get_monitor(void)
{
        return &DA_MON_NAME;
}

/*
 * __da_monitor_reset_all - reset the single monitor
 */
static void __da_monitor_reset_all(void (*reset)(struct da_monitor *))
{
        reset(da_get_monitor());
}

/*
 * da_monitor_reset_all - reset the single monitor
 */
static void da_monitor_reset_all(void)
{
        __da_monitor_reset_all(da_monitor_reset);
}

/*
 * da_monitor_reset_state_all - reset the single monitor
 */
static inline void da_monitor_reset_state_all(void)
{
        __da_monitor_reset_all(da_monitor_reset_state);
}

/*
 * da_monitor_init - initialize a monitor
 */
static inline int da_monitor_init(void)
{
        da_monitor_reset_state_all();
        return 0;
}

/*
 * da_monitor_destroy - destroy the monitor
 */
static inline void da_monitor_destroy(void)
{
        da_monitor_reset_all();
        da_monitor_sync_hook();
}

#ifndef da_implicit_guard
#define da_implicit_guard()
#endif

#elif RV_MON_TYPE == RV_MON_PER_CPU
/*
 * Functions to define, init and get a per-cpu monitor.
 */

/*
 * per-cpu monitor variables
 */
static DEFINE_PER_CPU(struct da_monitor, DA_MON_NAME);

/*
 * da_get_monitor - return current CPU monitor address
 */
static struct da_monitor *da_get_monitor(void)
{
        return this_cpu_ptr(&DA_MON_NAME);
}

/*
 * __da_monitor_reset_all - reset all CPUs' monitor
 */
static void __da_monitor_reset_all(void (*reset)(struct da_monitor *))
{
        struct da_monitor *da_mon;
        int cpu;

        for_each_cpu(cpu, cpu_online_mask) {
                da_mon = per_cpu_ptr(&DA_MON_NAME, cpu);
                reset(da_mon);
        }
}

/*
 * da_monitor_reset_all - reset all CPUs' monitor
 */
static void da_monitor_reset_all(void)
{
        __da_monitor_reset_all(da_monitor_reset);
}

/*
 * da_monitor_reset_state_all - reset all CPUs' monitor
 */
static inline void da_monitor_reset_state_all(void)
{
        __da_monitor_reset_all(da_monitor_reset_state);
}

/*
 * da_monitor_init - initialize all CPUs' monitor
 */
static inline int da_monitor_init(void)
{
        da_monitor_reset_state_all();
        return 0;
}

/*
 * da_monitor_destroy - destroy the monitor
 */
static inline void da_monitor_destroy(void)
{
        da_monitor_reset_all();
        da_monitor_sync_hook();
}

#ifndef da_implicit_guard
#define da_implicit_guard() guard(migrate)()
#endif

#elif RV_MON_TYPE == RV_MON_PER_TASK
/*
 * Functions to define, init and get a per-task monitor.
 */

/*
 * The per-task monitor is stored a vector in the task struct. This variable
 * stores the position on the vector reserved for this monitor.
 */
static int task_mon_slot = RV_PER_TASK_MONITOR_INIT;

/*
 * da_get_monitor - return the monitor in the allocated slot for tsk
 */
static inline struct da_monitor *da_get_monitor(struct task_struct *tsk)
{
        return &tsk->rv[task_mon_slot].da_mon;
}

/*
 * da_get_target - return the task associated to the monitor
 */
static inline struct task_struct *da_get_target(struct da_monitor *da_mon)
{
        return container_of(da_mon, struct task_struct, rv[task_mon_slot].da_mon);
}

/*
 * da_get_id - return the id associated to the monitor
 *
 * For per-task monitors, the id is the task's PID.
 */
static inline da_id_type da_get_id(struct da_monitor *da_mon)
{
        return da_get_target(da_mon)->pid;
}

static void __da_monitor_reset_all(void (*reset)(struct da_monitor *))
{
        struct task_struct *g, *p;
        int cpu;

        read_lock(&tasklist_lock);
        for_each_process_thread(g, p)
                reset(da_get_monitor(p));
        for_each_present_cpu(cpu)
                reset(da_get_monitor(idle_task(cpu)));
        read_unlock(&tasklist_lock);
}

static void da_monitor_reset_all(void)
{
        __da_monitor_reset_all(da_monitor_reset);
}

static inline void da_monitor_reset_state_all(void)
{
        __da_monitor_reset_all(da_monitor_reset_state);
}

/*
 * da_monitor_init - initialize the per-task monitor
 *
 * Try to allocate a slot in the task's vector of monitors. If there
 * is an available slot, use it and reset all task's monitor.
 */
static int da_monitor_init(void)
{
        int slot;

        slot = rv_get_task_monitor_slot();
        if (slot < 0 || slot >= RV_PER_TASK_MONITOR_INIT)
                return slot;

        task_mon_slot = slot;

        da_monitor_reset_state_all();
        return 0;
}

/*
 * da_monitor_destroy - return the allocated slot
 *
 * Wait for all in-flight handlers before returning the slot to avoid
 * out-of-bound accesses.
 */
static inline void da_monitor_destroy(void)
{
        if (task_mon_slot == RV_PER_TASK_MONITOR_INIT) {
                WARN_ONCE(1, "Disabling a disabled monitor: " __stringify(MONITOR_NAME));
                return;
        }

        tracepoint_synchronize_unregister();
        da_monitor_reset_all();
        da_monitor_sync_hook();

        rv_put_task_monitor_slot(task_mon_slot);
        task_mon_slot = RV_PER_TASK_MONITOR_INIT;
}

#elif RV_MON_TYPE == RV_MON_PER_OBJ
/*
 * Functions to define, init and get a per-object monitor.
 */

struct da_monitor_storage {
        da_id_type id;
        monitor_target target;
        union rv_task_monitor rv;
        struct hlist_node node;
        struct rcu_head rcu;
};

#ifndef DA_MONITOR_HT_BITS
#define DA_MONITOR_HT_BITS 10
#endif
static DEFINE_HASHTABLE(da_monitor_ht, DA_MONITOR_HT_BITS);

/*
 * da_create_empty_storage - pre-allocate an empty storage
 */
static inline struct da_monitor_storage *da_create_empty_storage(da_id_type id)
{
        struct da_monitor_storage *mon_storage;

        mon_storage = kmalloc_nolock(sizeof(struct da_monitor_storage),
                                     __GFP_ZERO, NUMA_NO_NODE);
        if (!mon_storage)
                return NULL;

        hash_add_rcu(da_monitor_ht, &mon_storage->node, id);
        mon_storage->id = id;
        return mon_storage;
}

/*
 * da_create_storage - create the per-object storage
 *
 * The caller is responsible to synchronise writers, either with locks or
 * implicitly. For instance, if da_create_storage is only called from a single
 * event for target (e.g. sched_switch), it's safe to call this without locks.
 */
static inline struct da_monitor *da_create_storage(da_id_type id,
                                                   monitor_target target,
                                                   struct da_monitor *da_mon)
{
        struct da_monitor_storage *mon_storage;

        if (da_mon)
                return da_mon;

        mon_storage = da_create_empty_storage(id);
        if (!mon_storage)
                return NULL;

        mon_storage->target = target;
        return &mon_storage->rv.da_mon;
}

/*
 * __da_get_mon_storage - get the monitor storage from the hash table
 */
static inline struct da_monitor_storage *__da_get_mon_storage(da_id_type id)
{
        struct da_monitor_storage *mon_storage;

        lockdep_assert_in_rcu_read_lock();
        hash_for_each_possible_rcu(da_monitor_ht, mon_storage, node, id) {
                if (mon_storage->id == id)
                        return mon_storage;
        }

        return NULL;
}

/*
 * da_get_monitor - return the monitor for target
 */
static struct da_monitor *da_get_monitor(da_id_type id, monitor_target target)
{
        struct da_monitor_storage *mon_storage;

        mon_storage = __da_get_mon_storage(id);
        return mon_storage ? &mon_storage->rv.da_mon : NULL;
}

/*
 * da_get_target - return the object associated to the monitor
 */
static inline monitor_target da_get_target(struct da_monitor *da_mon)
{
        return container_of(da_mon, struct da_monitor_storage, rv.da_mon)->target;
}

/*
 * da_get_id - return the id associated to the monitor
 */
static inline da_id_type da_get_id(struct da_monitor *da_mon)
{
        return container_of(da_mon, struct da_monitor_storage, rv.da_mon)->id;
}

/*
 * da_create_or_get - create the per-object storage if not already there
 *
 * This needs a lookup so should be guarded by RCU, the condition is checked
 * directly in da_create_storage()
 */
static inline void da_create_or_get(da_id_type id, monitor_target target)
{
        guard(rcu)();
        da_create_storage(id, target, da_get_monitor(id, target));
}

/*
 * da_fill_empty_storage - store the target in a pre-allocated storage
 *
 * Can be used as a substitute of da_create_storage when starting a monitor in
 * an environment where allocation is unsafe.
 */
static inline struct da_monitor *da_fill_empty_storage(da_id_type id,
                                                       monitor_target target,
                                                       struct da_monitor *da_mon)
{
        if (unlikely(da_mon && !da_get_target(da_mon)))
                container_of(da_mon, struct da_monitor_storage, rv.da_mon)->target = target;
        return da_mon;
}

/*
 * da_get_target_by_id - return the object associated to the id
 */
static inline monitor_target da_get_target_by_id(da_id_type id)
{
        struct da_monitor_storage *mon_storage;

        guard(rcu)();
        mon_storage = __da_get_mon_storage(id);

        if (unlikely(!mon_storage))
                return NULL;
        return mon_storage->target;
}

/*
 * da_destroy_storage - destroy the per-object storage
 *
 * The caller is responsible to synchronise writers, either with locks or
 * implicitly. For instance, if da_destroy_storage is called at sched_exit and
 * da_create_storage can never occur after that, it's safe to call this without
 * locks.
 * This function includes an RCU read-side critical section to synchronise
 * against da_monitor_destroy().
 */
static inline void da_destroy_storage(da_id_type id)
{
        struct da_monitor_storage *mon_storage;

        guard(rcu)();
        mon_storage = __da_get_mon_storage(id);

        if (!mon_storage)
                return;
        da_monitor_reset_hook(&mon_storage->rv.da_mon);
        hash_del_rcu(&mon_storage->node);
        kfree_rcu(mon_storage, rcu);
}

static void __da_monitor_reset_all(void (*reset)(struct da_monitor *))
{
        struct da_monitor_storage *mon_storage;
        int bkt;

        guard(rcu)();
        hash_for_each_rcu(da_monitor_ht, bkt, mon_storage, node)
                reset(&mon_storage->rv.da_mon);
}

static void da_monitor_reset_all(void)
{
        __da_monitor_reset_all(da_monitor_reset);
}

static inline void da_monitor_reset_state_all(void)
{
        __da_monitor_reset_all(da_monitor_reset_state);
}

static inline int da_monitor_init(void)
{
        hash_init(da_monitor_ht);
        return 0;
}

static inline void da_monitor_destroy(void)
{
        struct da_monitor_storage *mon_storage;
        struct hlist_node *tmp;
        int bkt;

        tracepoint_synchronize_unregister();
        da_monitor_reset_all();
        da_monitor_sync_hook();
        /*
         * This function is called after all probes are disabled and no longer
         * pending, we can safely assume no concurrent user.
         */
        hash_for_each_safe(da_monitor_ht, bkt, tmp, mon_storage, node) {
                hash_del_rcu(&mon_storage->node);
                kfree(mon_storage);
        }
}

/*
 * Allow the per-object monitors to run allocation manually, necessary if the
 * start condition is in a context problematic for allocation (e.g. scheduling).
 * In such case, if the storage was pre-allocated without a target, set it now.
 */
#ifdef DA_SKIP_AUTO_ALLOC
#define da_prepare_storage da_fill_empty_storage
#else
#define da_prepare_storage da_create_storage
#endif /* DA_SKIP_AUTO_ALLOC */

#endif /* RV_MON_TYPE */

#if RV_MON_TYPE == RV_MON_GLOBAL || RV_MON_TYPE == RV_MON_PER_CPU
/*
 * Trace events for implicit monitors. Implicit monitor is the one which the
 * handler does not need to specify which da_monitor to manipulate. Examples
 * of implicit monitor are the per_cpu or the global ones.
 */

static inline void da_trace_event(struct da_monitor *da_mon,
                                  char *curr_state, char *event,
                                  char *next_state, bool is_final,
                                  da_id_type id)
{
        CONCATENATE(trace_event_, MONITOR_NAME)(curr_state, event, next_state,
                                                is_final);
}

static inline void da_trace_error(struct da_monitor *da_mon,
                                  char *curr_state, char *event,
                                  da_id_type id)
{
        CONCATENATE(trace_error_, MONITOR_NAME)(curr_state, event);
}

/*
 * da_get_id - unused for implicit monitors
 */
static inline da_id_type da_get_id(struct da_monitor *da_mon)
{
        return 0;
}

#elif RV_MON_TYPE == RV_MON_PER_TASK || RV_MON_TYPE == RV_MON_PER_OBJ
/*
 * Trace events for per_task/per_object monitors, report the target id.
 */

static inline void da_trace_event(struct da_monitor *da_mon,
                                  char *curr_state, char *event,
                                  char *next_state, bool is_final,
                                  da_id_type id)
{
        CONCATENATE(trace_event_, MONITOR_NAME)(id, curr_state, event,
                                                next_state, is_final);
}

static inline void da_trace_error(struct da_monitor *da_mon,
                                  char *curr_state, char *event,
                                  da_id_type id)
{
        CONCATENATE(trace_error_, MONITOR_NAME)(id, curr_state, event);
}
#endif /* RV_MON_TYPE */

/*
 * da_event - handle an event for the da_mon
 *
 * This function is valid for both implicit and id monitors.
 * Retry in case there is a race between getting and setting the next state,
 * warn and reset the monitor if it runs out of retries. The monitor should be
 * able to handle various orders.
 */
static inline bool da_event(struct da_monitor *da_mon, enum events event, da_id_type id)
{
        enum states curr_state, next_state;

        curr_state = READ_ONCE(da_mon->curr_state);
        for (int i = 0; i < MAX_DA_RETRY_RACING_EVENTS; i++) {
                next_state = model_get_next_state(curr_state, event);
                if (next_state == INVALID_STATE) {
                        react(curr_state, event);
                        da_trace_error(da_mon, model_get_state_name(curr_state),
                                       model_get_event_name(event), id);
                        return false;
                }
                if (likely(try_cmpxchg(&da_mon->curr_state, &curr_state, next_state))) {
                        if (!da_monitor_event_hook(da_mon, curr_state, event, next_state, id))
                                return false;
                        da_trace_event(da_mon, model_get_state_name(curr_state),
                                       model_get_event_name(event),
                                       model_get_state_name(next_state),
                                       model_is_final_state(next_state), id);
                        return true;
                }
        }

        trace_rv_retries_error(__stringify(MONITOR_NAME), model_get_event_name(event));
        pr_warn("rv: " __stringify(MAX_DA_RETRY_RACING_EVENTS)
                " retries reached for event %s, resetting monitor %s",
                model_get_event_name(event), __stringify(MONITOR_NAME));
        return false;
}

static inline void __da_handle_event_common(struct da_monitor *da_mon,
                                            enum events event, da_id_type id)
{
        if (!da_event(da_mon, event, id))
                da_monitor_reset(da_mon);
}

static inline void __da_handle_event(struct da_monitor *da_mon,
                                     enum events event, da_id_type id)
{
        if (da_monitor_handling_event(da_mon))
                __da_handle_event_common(da_mon, event, id);
}

static inline bool __da_handle_start_event(struct da_monitor *da_mon,
                                           enum events event, da_id_type id)
{
        if (!da_monitor_enabled())
                return 0;
        if (unlikely(!da_monitoring(da_mon))) {
                da_monitor_start(da_mon);
                return 0;
        }

        __da_handle_event_common(da_mon, event, id);

        return 1;
}

static inline bool __da_handle_start_run_event(struct da_monitor *da_mon,
                                               enum events event, da_id_type id)
{
        if (!da_monitor_enabled())
                return 0;
        if (unlikely(!da_monitoring(da_mon)))
                da_monitor_start(da_mon);

        __da_handle_event_common(da_mon, event, id);

        return 1;
}

#if RV_MON_TYPE == RV_MON_GLOBAL || RV_MON_TYPE == RV_MON_PER_CPU
/*
 * Handle event for implicit monitor: da_get_monitor() will figure out
 * the monitor.
 */

/*
 * da_handle_event - handle an event
 */
static inline void da_handle_event(enum events event)
{
        da_implicit_guard();
        __da_handle_event(da_get_monitor(), event, 0);
}

/*
 * da_handle_start_event - start monitoring or handle event
 *
 * This function is used to notify the monitor that the system is returning
 * to the initial state, so the monitor can start monitoring in the next event.
 * Thus:
 *
 * If the monitor already started, handle the event.
 * If the monitor did not start yet, start the monitor but skip the event.
 */
static inline bool da_handle_start_event(enum events event)
{
        da_implicit_guard();
        return __da_handle_start_event(da_get_monitor(), event, 0);
}

/*
 * da_handle_start_run_event - start monitoring and handle event
 *
 * This function is used to notify the monitor that the system is in the
 * initial state, so the monitor can start monitoring and handling event.
 */
static inline bool da_handle_start_run_event(enum events event)
{
        da_implicit_guard();
        return __da_handle_start_run_event(da_get_monitor(), event, 0);
}

#elif RV_MON_TYPE == RV_MON_PER_TASK
/*
 * Handle event for per task.
 */

/*
 * da_handle_event - handle an event
 */
static inline void da_handle_event(struct task_struct *tsk, enum events event)
{
        __da_handle_event(da_get_monitor(tsk), event, tsk->pid);
}

/*
 * da_handle_start_event - start monitoring or handle event
 *
 * This function is used to notify the monitor that the system is returning
 * to the initial state, so the monitor can start monitoring in the next event.
 * Thus:
 *
 * If the monitor already started, handle the event.
 * If the monitor did not start yet, start the monitor but skip the event.
 */
static inline bool da_handle_start_event(struct task_struct *tsk,
                                         enum events event)
{
        return __da_handle_start_event(da_get_monitor(tsk), event, tsk->pid);
}

/*
 * da_handle_start_run_event - start monitoring and handle event
 *
 * This function is used to notify the monitor that the system is in the
 * initial state, so the monitor can start monitoring and handling event.
 */
static inline bool da_handle_start_run_event(struct task_struct *tsk,
                                             enum events event)
{
        return __da_handle_start_run_event(da_get_monitor(tsk), event, tsk->pid);
}

#elif RV_MON_TYPE == RV_MON_PER_OBJ
/*
 * Handle event for per object.
 */

/*
 * da_handle_event - handle an event
 */
static inline void da_handle_event(da_id_type id, monitor_target target, enum events event)
{
        struct da_monitor *da_mon;

        guard(rcu)();
        da_mon = da_get_monitor(id, target);
        if (likely(da_mon))
                __da_handle_event(da_mon, event, id);
}

/*
 * da_handle_start_event - start monitoring or handle event
 *
 * This function is used to notify the monitor that the system is returning
 * to the initial state, so the monitor can start monitoring in the next event.
 * Thus:
 *
 * If the monitor already started, handle the event.
 * If the monitor did not start yet, start the monitor but skip the event.
 */
static inline bool da_handle_start_event(da_id_type id, monitor_target target,
                                         enum events event)
{
        struct da_monitor *da_mon;

        guard(rcu)();
        da_mon = da_get_monitor(id, target);
        da_mon = da_prepare_storage(id, target, da_mon);
        if (unlikely(!da_mon))
                return 0;
        return __da_handle_start_event(da_mon, event, id);
}

/*
 * da_handle_start_run_event - start monitoring and handle event
 *
 * This function is used to notify the monitor that the system is in the
 * initial state, so the monitor can start monitoring and handling event.
 */
static inline bool da_handle_start_run_event(da_id_type id, monitor_target target,
                                             enum events event)
{
        struct da_monitor *da_mon;

        guard(rcu)();
        da_mon = da_get_monitor(id, target);
        da_mon = da_prepare_storage(id, target, da_mon);
        if (unlikely(!da_mon))
                return 0;
        return __da_handle_start_run_event(da_mon, event, id);
}

static inline void da_reset(da_id_type id, monitor_target target)
{
        struct da_monitor *da_mon;

        guard(rcu)();
        da_mon = da_get_monitor(id, target);
        if (likely(da_mon))
                da_monitor_reset(da_mon);
}
#endif /* RV_MON_TYPE */

#endif