From nobody Sat Oct 4 15:57:18 2025 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 52B07321444 for ; Thu, 14 Aug 2025 15:08:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184122; cv=none; b=rbask5JWDtPOTN+/qwgz9lGRYPMmJab+3hDG3m8J5SryqjwzZRko2cPngIQsqKAAoCd04sknT/V/iJRjXNtkvTDRokVn17i0uDvV2nGBZRuqX4cr6hQybnxp2F9JHCwmEmqh58IRS0yW4jMiu7/TSDdNJwwihFNq88xnwy0lDJM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184122; c=relaxed/simple; bh=g/CjXAeEjTK+RBzhYQM5zQO39wThDgMYGFOAd7GLAtI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=frfk57OOoEi+pvE5S+y7YZPAFDPVxrx+lIl/KMRcimszGvTCKgOTnGivpwgwsCflyLE7PG10Y4ivjF5T+C/Sa6XOSFBoTcNEueEsMMIIBapHRz5sYjpvZbCfukeBg2KBXEY96DrqPcfoJaxcrszPHJM/JJgI/YBQDQcqSeMIvyQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=A8zps4n0; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="A8zps4n0" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1755184117; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=uI/Qrwd4n0Y/jNCuCqp27zNYxtzJNPSN1wm6MjC6uLw=; b=A8zps4n0i2fPuROF5ZNV/F9wokVaKeETZrSOtDIu1EecR0TXXM+Zu1O/p7xmzsA3RPCo+U dPizJUtmNnBIJF5ic8OEMgJOsmKwhGxe+HuhBDjlpL8wwqmCsDVSgLTTa5hEBa4ypbxaaI eFRtsp1RfO41YTztXFNQlG+0OqlMk8s= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-551-LlJMlfYlPL2rAr3RgdODcw-1; Thu, 14 Aug 2025 11:08:34 -0400 X-MC-Unique: LlJMlfYlPL2rAr3RgdODcw-1 X-Mimecast-MFC-AGG-ID: LlJMlfYlPL2rAr3RgdODcw_1755184113 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id E14AD1882615; Thu, 14 Aug 2025 15:08:32 +0000 (UTC) Received: from gmonaco-thinkpadt14gen3.rmtit.com (unknown [10.44.32.52]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id D42DF180047F; Thu, 14 Aug 2025 15:08:27 +0000 (UTC) From: Gabriele Monaco To: linux-kernel@vger.kernel.org, Steven Rostedt , Masami Hiramatsu , linux-trace-kernel@vger.kernel.org Cc: Gabriele Monaco , Nam Cao , Tomas Glozar , Juri Lelli , Clark Williams , John Kacur Subject: [RFC PATCH 01/17] rv: Refactor da_monitor to minimise macros Date: Thu, 14 Aug 2025 17:07:53 +0200 Message-ID: <20250814150809.140739-2-gmonaco@redhat.com> In-Reply-To: <20250814150809.140739-1-gmonaco@redhat.com> References: <20250814150809.140739-1-gmonaco@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 Content-Type: text/plain; charset="utf-8" The da_monitor helper functions are generated from macros of the type: DECLARE_DA_FUNCTION(name, type) \ static void da_func_x_##name(type arg) {} \ static void da_func_y_##name(type arg) {} \ This is good to minimise code duplication but the long macros made of skipped end of lines is rather hard to parse. Since functions are static, the advantage of naming them differently for each monitor is minimal. Refactor the da_monitor.h file to minimise macros, instead of declaring functions from macros, we simply declare them with the same name for all monitors (e.g. da_func_x) and for any remaining reference to the monitor name (e.g. tracepoints, enums, global variables) we use the CONCATENATE macro. In this way the file is much easier to maintain while keeping the same generality. Functions depending on the monitor types are now conditionally compiled according to the value of RV_MON_TYPE, which must be defined in the monitor source. The monitor type can be specified as in the original implementation, although it's best to keep the default implementation (unsigned char) as not all parts of code support larger data types, and likely there's no need. We keep the empty macro definitions to ease review of this change with diff tools, but cleanup is required. Also adapt existing monitors to keep the build working. Signed-off-by: Gabriele Monaco --- include/linux/rv.h | 4 + include/rv/automata.h | 132 ++-- include/rv/da_monitor.h | 912 ++++++++++++------------- kernel/trace/rv/monitors/nrp/nrp.c | 22 +- kernel/trace/rv/monitors/nrp/nrp.h | 2 + kernel/trace/rv/monitors/opid/opid.c | 32 +- kernel/trace/rv/monitors/opid/opid.h | 2 + kernel/trace/rv/monitors/sco/sco.c | 18 +- kernel/trace/rv/monitors/sco/sco.h | 2 + kernel/trace/rv/monitors/scpd/scpd.c | 20 +- kernel/trace/rv/monitors/scpd/scpd.h | 2 + kernel/trace/rv/monitors/snep/snep.c | 20 +- kernel/trace/rv/monitors/snep/snep.h | 2 + kernel/trace/rv/monitors/snroc/snroc.c | 18 +- kernel/trace/rv/monitors/snroc/snroc.h | 2 + kernel/trace/rv/monitors/sssw/sssw.c | 30 +- kernel/trace/rv/monitors/sssw/sssw.h | 2 + kernel/trace/rv/monitors/sts/sts.c | 26 +- kernel/trace/rv/monitors/sts/sts.h | 2 + kernel/trace/rv/monitors/wip/wip.c | 18 +- kernel/trace/rv/monitors/wip/wip.h | 2 + kernel/trace/rv/monitors/wwnr/wwnr.c | 20 +- kernel/trace/rv/monitors/wwnr/wwnr.h | 2 + 23 files changed, 650 insertions(+), 642 deletions(-) diff --git a/include/linux/rv.h b/include/linux/rv.h index 14410a42faef..3134681553b4 100644 --- a/include/linux/rv.h +++ b/include/linux/rv.h @@ -13,6 +13,10 @@ #define MAX_DA_NAME_LEN 32 #define MAX_DA_RETRY_RACING_EVENTS 3 =20 +#define RV_MON_GLOBAL 0 +#define RV_MON_PER_CPU 1 +#define RV_MON_PER_TASK 2 + #ifdef CONFIG_RV #include #include diff --git a/include/rv/automata.h b/include/rv/automata.h index eb9e636809a0..5b5d2e94c034 100644 --- a/include/rv/automata.h +++ b/include/rv/automata.h @@ -6,6 +6,20 @@ * models in C generated by the dot2k tool. */ =20 +#ifndef MONITOR_NAME +#error "MONITOR_NAME macro is not defined. Did you include $(MODEL_NAME).h= generated by rvgen?" +#endif + +#ifndef type +#define type unsigned char +#endif + +#define RV_AUTOMATON_NAME CONCATENATE(automaton_, MONITOR_NAME) +#define EVENT_MAX CONCATENATE(event_max_, MONITOR_NAME) +#define STATE_MAX CONCATENATE(state_max_, MONITOR_NAME) +#define events CONCATENATE(events_, MONITOR_NAME) +#define states CONCATENATE(states_, MONITOR_NAME) + /* * DECLARE_AUTOMATA_HELPERS - define a set of helper functions for automata * @@ -13,63 +27,63 @@ * as suffix for the functions and data. These functions will handle autom= aton * with data type 'type'. */ -#define DECLARE_AUTOMATA_HELPERS(name, type) \ - \ -/* \ - * model_get_state_name_##name - return the (string) name of the given sta= te \ - */ \ -static char *model_get_state_name_##name(enum states_##name state) \ -{ \ - if ((state < 0) || (state >=3D state_max_##name)) \ - return "INVALID"; \ - \ - return automaton_##name.state_names[state]; \ -} \ - \ -/* \ - * model_get_event_name_##name - return the (string) name of the given eve= nt \ - */ \ -static char *model_get_event_name_##name(enum events_##name event) \ -{ \ - if ((event < 0) || (event >=3D event_max_##name)) \ - return "INVALID"; \ - \ - return automaton_##name.event_names[event]; \ -} \ - \ -/* \ - * model_get_initial_state_##name - return the automaton's initial state \ - */ \ -static inline type model_get_initial_state_##name(void) \ -{ \ - return automaton_##name.initial_state; \ -} \ - \ -/* \ - * model_get_next_state_##name - process an automaton event occurrence \ - * \ - * Given the current state (curr_state) and the event (event), returns \ - * the next state, or INVALID_STATE in case of error. \ - */ \ -static inline type model_get_next_state_##name(enum states_##name curr_sta= te, \ - enum events_##name event) \ -{ \ - if ((curr_state < 0) || (curr_state >=3D state_max_##name)) \ - return INVALID_STATE; \ - \ - if ((event < 0) || (event >=3D event_max_##name)) \ - return INVALID_STATE; \ - \ - return automaton_##name.function[curr_state][event]; \ -} \ - \ -/* \ - * model_is_final_state_##name - check if the given state is a final state= \ - */ \ -static inline bool model_is_final_state_##name(enum states_##name state) \ -{ \ - if ((state < 0) || (state >=3D state_max_##name)) \ - return 0; \ - \ - return automaton_##name.final_states[state]; \ +#define DECLARE_AUTOMATA_HELPERS(name, type) + +/* + * model_get_state_name - return the (string) name of the given state + */ +static char *model_get_state_name(enum states state) +{ + if ((state < 0) || (state >=3D STATE_MAX)) + return "INVALID"; + + return RV_AUTOMATON_NAME.state_names[state]; +} + +/* + * model_get_event_name - return the (string) name of the given event + */ +static char *model_get_event_name(enum events event) +{ + if ((event < 0) || (event >=3D EVENT_MAX)) + return "INVALID"; + + return RV_AUTOMATON_NAME.event_names[event]; +} + +/* + * model_get_initial_state - return the automaton's initial state + */ +static inline type model_get_initial_state(void) +{ + return RV_AUTOMATON_NAME.initial_state; +} + +/* + * model_get_next_state - process an automaton event occurrence + * + * Given the current state (curr_state) and the event (event), returns + * the next state, or INVALID_STATE in case of error. + */ +static inline type model_get_next_state(enum states curr_state, + enum events event) +{ + if ((curr_state < 0) || (curr_state >=3D STATE_MAX)) + return INVALID_STATE; + + if ((event < 0) || (event >=3D EVENT_MAX)) + return INVALID_STATE; + + return RV_AUTOMATON_NAME.function[curr_state][event]; +} + +/* + * model_is_final_state - check if the given state is a final state + */ +static inline bool model_is_final_state(enum states state) +{ + if ((state < 0) || (state >=3D STATE_MAX)) + return 0; + + return RV_AUTOMATON_NAME.final_states[state]; } diff --git a/include/rv/da_monitor.h b/include/rv/da_monitor.h index 17fa4f6e5ea6..bc02334aa8be 100644 --- a/include/rv/da_monitor.h +++ b/include/rv/da_monitor.h @@ -13,97 +13,102 @@ =20 #include #include +#include #include #include =20 +#define RV_MONITOR_NAME CONCATENATE(rv_, MONITOR_NAME) +#define RV_DA_MON_NAME CONCATENATE(da_mon_, MONITOR_NAME) +static struct rv_monitor RV_MONITOR_NAME; + #ifdef CONFIG_RV_REACTORS =20 -#define DECLARE_RV_REACTING_HELPERS(name, type) \ -static void cond_react_##name(type curr_state, type event) \ -{ \ - if (!rv_reacting_on() || !rv_##name.react) \ - return; \ - rv_##name.react("rv: monitor %s does not allow event %s on state %s\n", = \ - #name, \ - model_get_event_name_##name(event), \ - model_get_state_name_##name(curr_state)); \ +#define DECLARE_RV_REACTING_HELPERS(name, type) +static void cond_react(type curr_state, type event) +{ + if (!rv_reacting_on() || !RV_MONITOR_NAME.react) + return; + RV_MONITOR_NAME.react("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)); } =20 #else /* CONFIG_RV_REACTOR */ =20 -#define DECLARE_RV_REACTING_HELPERS(name, type) \ -static void cond_react_##name(type curr_state, type event) \ -{ \ - return; \ +#define DECLARE_RV_REACTING_HELPERS(name, type) +static void cond_react(type curr_state, type event) +{ + return; } #endif =20 /* * Generic helpers for all types of deterministic automata monitors. */ -#define DECLARE_DA_MON_GENERIC_HELPERS(name, type) \ - \ -DECLARE_RV_REACTING_HELPERS(name, type) \ - \ -/* \ - * da_monitor_reset_##name - reset a monitor and setting it to init state = \ - */ \ -static inline void da_monitor_reset_##name(struct da_monitor *da_mon) \ -{ \ - da_mon->monitoring =3D 0; \ - da_mon->curr_state =3D model_get_initial_state_##name(); \ -} \ - \ -/* \ - * da_monitor_start_##name - start monitoring \ - * \ - * The monitor will ignore all events until monitoring is set to true. Thi= s \ - * function needs to be called to tell the monitor to start monitoring. = \ - */ \ -static inline void da_monitor_start_##name(struct da_monitor *da_mon) \ -{ \ - da_mon->curr_state =3D model_get_initial_state_##name(); \ - da_mon->monitoring =3D 1; \ -} \ - \ -/* \ - * da_monitoring_##name - returns true if the monitor is processing events= \ - */ \ -static inline bool da_monitoring_##name(struct da_monitor *da_mon) \ -{ \ - return da_mon->monitoring; \ -} \ - \ -/* \ - * da_monitor_enabled_##name - checks if the monitor is enabled \ - */ \ -static inline bool da_monitor_enabled_##name(void) \ -{ \ - /* global switch */ \ - if (unlikely(!rv_monitoring_on())) \ - return 0; \ - \ - /* monitor enabled */ \ - if (unlikely(!rv_##name.enabled)) \ - return 0; \ - \ - return 1; \ -} \ - \ -/* \ - * da_monitor_handling_event_##name - checks if the monitor is ready to ha= ndle events \ - */ \ -static inline bool da_monitor_handling_event_##name(struct da_monitor *da_= mon) \ -{ \ - \ - if (!da_monitor_enabled_##name()) \ - return 0; \ - \ - /* monitor is actually monitoring */ \ - if (unlikely(!da_monitoring_##name(da_mon))) \ - return 0; \ - \ - return 1; \ +#define DECLARE_DA_MON_GENERIC_HELPERS(name, type) + +DECLARE_RV_REACTING_HELPERS(name, type) + +/* + * da_monitor_reset - reset a monitor and setting it to init state + */ +static inline void da_monitor_reset(struct da_monitor *da_mon) +{ + da_mon->monitoring =3D 0; + da_mon->curr_state =3D model_get_initial_state(); +} + +/* + * 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 =3D model_get_initial_state(); + da_mon->monitoring =3D 1; +} + +/* + * da_monitoring - returns true if the monitor is processing events + */ +static inline bool da_monitoring(struct da_monitor *da_mon) +{ + return 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_MONITOR_NAME.enabled)) + return 0; + + return 1; +} + +/* + * da_monitor_handling_event - checks if the monitor is ready to handle ev= ents + */ +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; } =20 /* @@ -115,37 +120,37 @@ static inline bool da_monitor_handling_event_##name(s= truct da_monitor *da_mon) * warn and reset the monitor if it runs out of retries. The monitor shoul= d be * able to handle various orders. */ -#define DECLARE_DA_MON_MODEL_HANDLER_IMPLICIT(name, type) \ - \ -static inline bool \ -da_event_##name(struct da_monitor *da_mon, enum events_##name event) \ -{ \ - enum states_##name curr_state, next_state; \ - \ - curr_state =3D READ_ONCE(da_mon->curr_state); \ - for (int i =3D 0; i < MAX_DA_RETRY_RACING_EVENTS; i++) { \ - next_state =3D model_get_next_state_##name(curr_state, event); \ - if (next_state =3D=3D INVALID_STATE) { \ - cond_react_##name(curr_state, event); \ - trace_error_##name(model_get_state_name_##name(curr_state), \ - model_get_event_name_##name(event)); \ - return false; \ - } \ - if (likely(try_cmpxchg(&da_mon->curr_state, &curr_state, next_state))) {= \ - trace_event_##name(model_get_state_name_##name(curr_state), \ - model_get_event_name_##name(event), \ - model_get_state_name_##name(next_state), \ - model_is_final_state_##name(next_state)); \ - return true; \ - } \ - } \ - \ - trace_rv_retries_error(#name, model_get_event_name_##name(event)); \ - pr_warn("rv: " __stringify(MAX_DA_RETRY_RACING_EVENTS) \ - " retries reached for event %s, resetting monitor %s", \ - model_get_event_name_##name(event), #name); \ - return false; \ -} \ +#if RV_MON_TYPE =3D=3D RV_MON_GLOBAL || RV_MON_TYPE =3D=3D RV_MON_PER_CPU + +static inline bool +da_event(struct da_monitor *da_mon, enum events event) +{ + enum states curr_state, next_state; + + curr_state =3D READ_ONCE(da_mon->curr_state); + for (int i =3D 0; i < MAX_DA_RETRY_RACING_EVENTS; i++) { + next_state =3D model_get_next_state(curr_state, event); + if (next_state =3D=3D INVALID_STATE) { + cond_react(curr_state, event); + CONCATENATE(trace_error_, MONITOR_NAME)(model_get_state_name(curr_state= ), + model_get_event_name(event)); + return false; + } + if (likely(try_cmpxchg(&da_mon->curr_state, &curr_state, next_state))) { + CONCATENATE(trace_event_, MONITOR_NAME)(model_get_state_name(curr_state= ), + model_get_event_name(event), + model_get_state_name(next_state), + model_is_final_state(next_state)); + return true; + } + } + + trace_rv_retries_error(__stringify(MONITOR_NAME), model_get_event_name(ev= ent)); + 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; +} =20 /* * Event handler for per_task monitors. @@ -154,395 +159,380 @@ da_event_##name(struct da_monitor *da_mon, enum eve= nts_##name event) \ * warn and reset the monitor if it runs out of retries. The monitor shoul= d be * able to handle various orders. */ -#define DECLARE_DA_MON_MODEL_HANDLER_PER_TASK(name, type) \ - \ -static inline bool da_event_##name(struct da_monitor *da_mon, struct task_= struct *tsk, \ - enum events_##name event) \ -{ \ - enum states_##name curr_state, next_state; \ - \ - curr_state =3D READ_ONCE(da_mon->curr_state); \ - for (int i =3D 0; i < MAX_DA_RETRY_RACING_EVENTS; i++) { \ - next_state =3D model_get_next_state_##name(curr_state, event); \ - if (next_state =3D=3D INVALID_STATE) { \ - cond_react_##name(curr_state, event); \ - trace_error_##name(tsk->pid, \ - model_get_state_name_##name(curr_state), \ - model_get_event_name_##name(event)); \ - return false; \ - } \ - if (likely(try_cmpxchg(&da_mon->curr_state, &curr_state, next_state))) {= \ - trace_event_##name(tsk->pid, \ - model_get_state_name_##name(curr_state), \ - model_get_event_name_##name(event), \ - model_get_state_name_##name(next_state), \ - model_is_final_state_##name(next_state)); \ - return true; \ - } \ - } \ - \ - trace_rv_retries_error(#name, model_get_event_name_##name(event)); \ - pr_warn("rv: " __stringify(MAX_DA_RETRY_RACING_EVENTS) \ - " retries reached for event %s, resetting monitor %s", \ - model_get_event_name_##name(event), #name); \ - return false; \ +#elif RV_MON_TYPE =3D=3D RV_MON_PER_TASK + +static inline bool da_event(struct da_monitor *da_mon, struct task_struct = *tsk, + enum events event) +{ + enum states curr_state, next_state; + + curr_state =3D READ_ONCE(da_mon->curr_state); + for (int i =3D 0; i < MAX_DA_RETRY_RACING_EVENTS; i++) { + next_state =3D model_get_next_state(curr_state, event); + if (next_state =3D=3D INVALID_STATE) { + cond_react(curr_state, event); + CONCATENATE(trace_error_, MONITOR_NAME)(tsk->pid, + model_get_state_name(curr_state), + model_get_event_name(event)); + return false; + } + if (likely(try_cmpxchg(&da_mon->curr_state, &curr_state, next_state))) { + CONCATENATE(trace_event_, MONITOR_NAME)(tsk->pid, + model_get_state_name(curr_state), + model_get_event_name(event), + model_get_state_name(next_state), + model_is_final_state(next_state)); + return true; + } + } + + trace_rv_retries_error(__stringify(MONITOR_NAME), model_get_event_name(ev= ent)); + 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; } +#endif =20 /* * Functions to define, init and get a global monitor. */ -#define DECLARE_DA_MON_INIT_GLOBAL(name, type) \ - \ -/* \ - * global monitor (a single variable) \ - */ \ -static struct da_monitor da_mon_##name; \ - \ -/* \ - * da_get_monitor_##name - return the global monitor address \ - */ \ -static struct da_monitor *da_get_monitor_##name(void) \ -{ \ - return &da_mon_##name; \ -} \ - \ -/* \ - * da_monitor_reset_all_##name - reset the single monitor \ - */ \ -static void da_monitor_reset_all_##name(void) \ -{ \ - da_monitor_reset_##name(da_get_monitor_##name()); \ -} \ - \ -/* \ - * da_monitor_init_##name - initialize a monitor \ - */ \ -static inline int da_monitor_init_##name(void) \ -{ \ - da_monitor_reset_all_##name(); \ - return 0; \ -} \ - \ -/* \ - * da_monitor_destroy_##name - destroy the monitor \ - */ \ -static inline void da_monitor_destroy_##name(void) \ -{ \ - return; \ +#if RV_MON_TYPE =3D=3D RV_MON_GLOBAL + +/* + * global monitor (a single variable) + */ +static struct da_monitor RV_DA_MON_NAME; + +/* + * da_get_monitor - return the global monitor address + */ +static struct da_monitor *da_get_monitor(void) +{ + return &RV_DA_MON_NAME; +} + +/* + * da_monitor_reset_all - reset the single monitor + */ +static void da_monitor_reset_all(void) +{ + da_monitor_reset(da_get_monitor()); +} + +/* + * da_monitor_init - initialize a monitor + */ +static inline int da_monitor_init(void) +{ + da_monitor_reset_all(); + return 0; +} + +/* + * da_monitor_destroy - destroy the monitor + */ +static inline void da_monitor_destroy(void) +{ + return; } =20 /* * Functions to define, init and get a per-cpu monitor. */ -#define DECLARE_DA_MON_INIT_PER_CPU(name, type) \ - \ -/* \ - * per-cpu monitor variables \ - */ \ -static DEFINE_PER_CPU(struct da_monitor, da_mon_##name); \ - \ -/* \ - * da_get_monitor_##name - return current CPU monitor address \ - */ \ -static struct da_monitor *da_get_monitor_##name(void) \ -{ \ - return this_cpu_ptr(&da_mon_##name); \ -} \ - \ -/* \ - * da_monitor_reset_all_##name - reset all CPUs' monitor \ - */ \ -static void da_monitor_reset_all_##name(void) \ -{ \ - struct da_monitor *da_mon; \ - int cpu; \ - for_each_cpu(cpu, cpu_online_mask) { \ - da_mon =3D per_cpu_ptr(&da_mon_##name, cpu); \ - da_monitor_reset_##name(da_mon); \ - } \ -} \ - \ -/* \ - * da_monitor_init_##name - initialize all CPUs' monitor \ - */ \ -static inline int da_monitor_init_##name(void) \ -{ \ - da_monitor_reset_all_##name(); \ - return 0; \ -} \ - \ -/* \ - * da_monitor_destroy_##name - destroy the monitor \ - */ \ -static inline void da_monitor_destroy_##name(void) \ -{ \ - return; \ +#elif RV_MON_TYPE =3D=3D RV_MON_PER_CPU + +/* + * per-cpu monitor variables + */ +static DEFINE_PER_CPU(struct da_monitor, RV_DA_MON_NAME); + +/* + * da_get_monitor - return current CPU monitor address + */ +static struct da_monitor *da_get_monitor(void) +{ + return this_cpu_ptr(&RV_DA_MON_NAME); +} + +/* + * da_monitor_reset_all - reset all CPUs' monitor + */ +static void da_monitor_reset_all(void) +{ + struct da_monitor *da_mon; + int cpu; + for_each_cpu(cpu, cpu_online_mask) { + da_mon =3D per_cpu_ptr(&RV_DA_MON_NAME, cpu); + da_monitor_reset(da_mon); + } +} + +/* + * da_monitor_init - initialize all CPUs' monitor + */ +static inline int da_monitor_init(void) +{ + da_monitor_reset_all(); + return 0; +} + +/* + * da_monitor_destroy - destroy the monitor + */ +static inline void da_monitor_destroy(void) +{ + return; } =20 /* * Functions to define, init and get a per-task monitor. */ -#define DECLARE_DA_MON_INIT_PER_TASK(name, type) \ - \ -/* \ - * The per-task monitor is stored a vector in the task struct. This variab= le \ - * stores the position on the vector reserved for this monitor. \ - */ \ -static int task_mon_slot_##name =3D RV_PER_TASK_MONITOR_INIT; \ - \ -/* \ - * da_get_monitor_##name - return the monitor in the allocated slot for ts= k \ - */ \ -static inline struct da_monitor *da_get_monitor_##name(struct task_struct = *tsk) \ -{ \ - return &tsk->rv[task_mon_slot_##name].da_mon; \ -} \ - \ -static void da_monitor_reset_all_##name(void) \ -{ \ - struct task_struct *g, *p; \ - int cpu; \ - \ - read_lock(&tasklist_lock); \ - for_each_process_thread(g, p) \ - da_monitor_reset_##name(da_get_monitor_##name(p)); \ - for_each_present_cpu(cpu) \ - da_monitor_reset_##name(da_get_monitor_##name(idle_task(cpu))); \ - read_unlock(&tasklist_lock); \ -} \ - \ -/* \ - * da_monitor_init_##name - 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_##name(void) \ -{ \ - int slot; \ - \ - slot =3D rv_get_task_monitor_slot(); \ - if (slot < 0 || slot >=3D RV_PER_TASK_MONITOR_INIT) \ - return slot; \ - \ - task_mon_slot_##name =3D slot; \ - \ - da_monitor_reset_all_##name(); \ - return 0; \ -} \ - \ -/* \ - * da_monitor_destroy_##name - return the allocated slot \ - */ \ -static inline void da_monitor_destroy_##name(void) \ -{ \ - if (task_mon_slot_##name =3D=3D RV_PER_TASK_MONITOR_INIT) { \ - WARN_ONCE(1, "Disabling a disabled monitor: " #name); \ - return; \ - } \ - rv_put_task_monitor_slot(task_mon_slot_##name); \ - task_mon_slot_##name =3D RV_PER_TASK_MONITOR_INIT; \ - return; \ +#elif RV_MON_TYPE =3D=3D RV_MON_PER_TASK + +/* + * The per-task monitor is stored a vector in the task struct. This variab= le + * stores the position on the vector reserved for this monitor. + */ +static int task_mon_slot =3D 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; +} + +static void da_monitor_reset_all(void) +{ + struct task_struct *g, *p; + int cpu; + + read_lock(&tasklist_lock); + for_each_process_thread(g, p) + da_monitor_reset(da_get_monitor(p)); + for_each_present_cpu(cpu) + da_monitor_reset(da_get_monitor(idle_task(cpu))); + read_unlock(&tasklist_lock); } =20 /* - * Handle event for implicit monitor: da_get_monitor_##name() will figure = out + * 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 =3D rv_get_task_monitor_slot(); + if (slot < 0 || slot >=3D RV_PER_TASK_MONITOR_INIT) + return slot; + + task_mon_slot =3D slot; + + da_monitor_reset_all(); + return 0; +} + +/* + * da_monitor_destroy - return the allocated slot + */ +static inline void da_monitor_destroy(void) +{ + if (task_mon_slot =3D=3D RV_PER_TASK_MONITOR_INIT) { + WARN_ONCE(1, "Disabling a disabled monitor: " __stringify(MONITOR_NAME)); + return; + } + rv_put_task_monitor_slot(task_mon_slot); + task_mon_slot =3D RV_PER_TASK_MONITOR_INIT; + return; +} +#endif + +/* + * Handle event for implicit monitor: da_get_monitor() will figure out * the monitor. */ -#define DECLARE_DA_MON_MONITOR_HANDLER_IMPLICIT(name, type) \ - \ -static inline void __da_handle_event_##name(struct da_monitor *da_mon, \ - enum events_##name event) \ -{ \ - bool retval; \ - \ - retval =3D da_event_##name(da_mon, event); \ - if (!retval) \ - da_monitor_reset_##name(da_mon); \ -} \ - \ -/* \ - * da_handle_event_##name - handle an event \ - */ \ -static inline void da_handle_event_##name(enum events_##name event) \ -{ \ - struct da_monitor *da_mon =3D da_get_monitor_##name(); \ - bool retval; \ - \ - retval =3D da_monitor_handling_event_##name(da_mon); \ - if (!retval) \ - return; \ - \ - __da_handle_event_##name(da_mon, event); \ -} \ - \ -/* \ - * da_handle_start_event_##name - start monitoring or handle event \ - * \ - * This function is used to notify the monitor that the system is returnin= g \ - * to the initial state, so the monitor can start monitoring in the next e= vent. \ - * 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_##name(enum events_##name event) = \ -{ \ - struct da_monitor *da_mon; \ - \ - if (!da_monitor_enabled_##name()) \ - return 0; \ - \ - da_mon =3D da_get_monitor_##name(); \ - \ - if (unlikely(!da_monitoring_##name(da_mon))) { \ - da_monitor_start_##name(da_mon); \ - return 0; \ - } \ - \ - __da_handle_event_##name(da_mon, event); \ - \ - return 1; \ -} \ - \ -/* \ - * da_handle_start_run_event_##name - 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_##name(enum events_##name eve= nt) \ -{ \ - struct da_monitor *da_mon; \ - \ - if (!da_monitor_enabled_##name()) \ - return 0; \ - \ - da_mon =3D da_get_monitor_##name(); \ - \ - if (unlikely(!da_monitoring_##name(da_mon))) \ - da_monitor_start_##name(da_mon); \ - \ - __da_handle_event_##name(da_mon, event); \ - \ - return 1; \ +#if RV_MON_TYPE =3D=3D RV_MON_GLOBAL || RV_MON_TYPE =3D=3D RV_MON_PER_CPU + +static inline void __da_handle_event(struct da_monitor *da_mon, + enum events event) +{ + bool retval; + + retval =3D da_event(da_mon, event); + if (!retval) + da_monitor_reset(da_mon); +} + +/* + * da_handle_event - handle an event + */ +static inline void da_handle_event(enum events event) +{ + struct da_monitor *da_mon =3D da_get_monitor(); + bool retval; + + retval =3D da_monitor_handling_event(da_mon); + if (!retval) + return; + + __da_handle_event(da_mon, event); +} + +/* + * 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 e= vent. + * 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) +{ + struct da_monitor *da_mon; + + if (!da_monitor_enabled()) + return 0; + + da_mon =3D da_get_monitor(); + + if (unlikely(!da_monitoring(da_mon))) { + da_monitor_start(da_mon); + return 0; + } + + __da_handle_event(da_mon, event); + + return 1; +} + +/* + * 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) +{ + struct da_monitor *da_mon; + + if (!da_monitor_enabled()) + return 0; + + da_mon =3D da_get_monitor(); + + if (unlikely(!da_monitoring(da_mon))) + da_monitor_start(da_mon); + + __da_handle_event(da_mon, event); + + return 1; } =20 /* * Handle event for per task. */ -#define DECLARE_DA_MON_MONITOR_HANDLER_PER_TASK(name, type) \ - \ -static inline void \ -__da_handle_event_##name(struct da_monitor *da_mon, struct task_struct *ts= k, \ - enum events_##name event) \ -{ \ - bool retval; \ - \ - retval =3D da_event_##name(da_mon, tsk, event); \ - if (!retval) \ - da_monitor_reset_##name(da_mon); \ -} \ - \ -/* \ - * da_handle_event_##name - handle an event \ - */ \ -static inline void \ -da_handle_event_##name(struct task_struct *tsk, enum events_##name event) = \ -{ \ - struct da_monitor *da_mon =3D da_get_monitor_##name(tsk); \ - bool retval; \ - \ - retval =3D da_monitor_handling_event_##name(da_mon); \ - if (!retval) \ - return; \ - \ - __da_handle_event_##name(da_mon, tsk, event); \ -} \ - \ -/* \ - * da_handle_start_event_##name - start monitoring or handle event \ - * \ - * This function is used to notify the monitor that the system is returnin= g \ - * to the initial state, so the monitor can start monitoring in the next e= vent. \ - * 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_##name(struct task_struct *tsk, enum events_##name e= vent) \ -{ \ - struct da_monitor *da_mon; \ - \ - if (!da_monitor_enabled_##name()) \ - return 0; \ - \ - da_mon =3D da_get_monitor_##name(tsk); \ - \ - if (unlikely(!da_monitoring_##name(da_mon))) { \ - da_monitor_start_##name(da_mon); \ - return 0; \ - } \ - \ - __da_handle_event_##name(da_mon, tsk, event); \ - \ - return 1; \ -} \ - \ -/* \ - * da_handle_start_run_event_##name - 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_##name(struct task_struct *tsk, enum events_##na= me event) \ -{ \ - struct da_monitor *da_mon; \ - \ - if (!da_monitor_enabled_##name()) \ - return 0; \ - \ - da_mon =3D da_get_monitor_##name(tsk); \ - \ - if (unlikely(!da_monitoring_##name(da_mon))) \ - da_monitor_start_##name(da_mon); \ - \ - __da_handle_event_##name(da_mon, tsk, event); \ - \ - return 1; \ +#elif RV_MON_TYPE =3D=3D RV_MON_PER_TASK + +static inline void +__da_handle_event(struct da_monitor *da_mon, struct task_struct *tsk, + enum events event) +{ + bool retval; + + retval =3D da_event(da_mon, tsk, event); + if (!retval) + da_monitor_reset(da_mon); +} + +/* + * da_handle_event - handle an event + */ +static inline void +da_handle_event(struct task_struct *tsk, enum events event) +{ + struct da_monitor *da_mon =3D da_get_monitor(tsk); + bool retval; + + retval =3D da_monitor_handling_event(da_mon); + if (!retval) + return; + + __da_handle_event(da_mon, tsk, event); +} + +/* + * 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 e= vent. + * 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) +{ + struct da_monitor *da_mon; + + if (!da_monitor_enabled()) + return 0; + + da_mon =3D da_get_monitor(tsk); + + if (unlikely(!da_monitoring(da_mon))) { + da_monitor_start(da_mon); + return 0; + } + + __da_handle_event(da_mon, tsk, event); + + return 1; } =20 +/* + * 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) +{ + struct da_monitor *da_mon; + + if (!da_monitor_enabled()) + return 0; + + da_mon =3D da_get_monitor(tsk); + + if (unlikely(!da_monitoring(da_mon))) + da_monitor_start(da_mon); + + __da_handle_event(da_mon, tsk, event); + + return 1; +} +#endif + /* * Entry point for the global monitor. */ -#define DECLARE_DA_MON_GLOBAL(name, type) \ - \ -DECLARE_AUTOMATA_HELPERS(name, type) \ -DECLARE_DA_MON_GENERIC_HELPERS(name, type) \ -DECLARE_DA_MON_MODEL_HANDLER_IMPLICIT(name, type) \ -DECLARE_DA_MON_INIT_GLOBAL(name, type) \ -DECLARE_DA_MON_MONITOR_HANDLER_IMPLICIT(name, type) +#define DECLARE_DA_MON_GLOBAL(name, type) =20 /* * Entry point for the per-cpu monitor. */ -#define DECLARE_DA_MON_PER_CPU(name, type) \ - \ -DECLARE_AUTOMATA_HELPERS(name, type) \ -DECLARE_DA_MON_GENERIC_HELPERS(name, type) \ -DECLARE_DA_MON_MODEL_HANDLER_IMPLICIT(name, type) \ -DECLARE_DA_MON_INIT_PER_CPU(name, type) \ -DECLARE_DA_MON_MONITOR_HANDLER_IMPLICIT(name, type) +#define DECLARE_DA_MON_PER_CPU(name, type) =20 /* * Entry point for the per-task monitor. */ -#define DECLARE_DA_MON_PER_TASK(name, type) \ - \ -DECLARE_AUTOMATA_HELPERS(name, type) \ -DECLARE_DA_MON_GENERIC_HELPERS(name, type) \ -DECLARE_DA_MON_MODEL_HANDLER_PER_TASK(name, type) \ -DECLARE_DA_MON_INIT_PER_TASK(name, type) \ -DECLARE_DA_MON_MONITOR_HANDLER_PER_TASK(name, type) +#define DECLARE_DA_MON_PER_TASK(name, type) diff --git a/kernel/trace/rv/monitors/nrp/nrp.c b/kernel/trace/rv/monitors/= nrp/nrp.c index 5a83b7171432..6bb252bfe980 100644 --- a/kernel/trace/rv/monitors/nrp/nrp.c +++ b/kernel/trace/rv/monitors/nrp/nrp.c @@ -6,7 +6,6 @@ #include #include #include -#include =20 #define MODULE_NAME "nrp" =20 @@ -15,17 +14,16 @@ #include #include =20 +#define RV_MON_TYPE RV_MON_PER_TASK #include "nrp.h" - -static struct rv_monitor rv_nrp; -DECLARE_DA_MON_PER_TASK(nrp, unsigned char); +#include =20 #ifdef CONFIG_X86_LOCAL_APIC #include =20 static void handle_vector_irq_entry(void *data, int vector) { - da_handle_event_nrp(current, irq_entry_nrp); + da_handle_event(current, irq_entry_nrp); } =20 static void attach_vector_irq(void) @@ -60,7 +58,7 @@ static void detach_vector_irq(void) { } =20 static void handle_irq_entry(void *data, int irq, struct irqaction *action) { - da_handle_event_nrp(current, irq_entry_nrp); + da_handle_event(current, irq_entry_nrp); } =20 static void handle_sched_need_resched(void *data, struct task_struct *tsk, @@ -72,22 +70,22 @@ static void handle_sched_need_resched(void *data, struc= t task_struct *tsk, * which may not mirror the system state but makes the monitor simpler, */ if (tif =3D=3D TIF_NEED_RESCHED) - da_handle_start_event_nrp(tsk, sched_need_resched_nrp); + da_handle_start_event(tsk, sched_need_resched_nrp); } =20 static void handle_schedule_entry(void *data, bool preempt) { if (preempt) - da_handle_event_nrp(current, schedule_entry_preempt_nrp); + da_handle_event(current, schedule_entry_preempt_nrp); else - da_handle_event_nrp(current, schedule_entry_nrp); + da_handle_event(current, schedule_entry_nrp); } =20 static int enable_nrp(void) { int retval; =20 - retval =3D da_monitor_init_nrp(); + retval =3D da_monitor_init(); if (retval) return retval; =20 @@ -108,7 +106,7 @@ static void disable_nrp(void) rv_detach_trace_probe("nrp", sched_entry_tp, handle_schedule_entry); detach_vector_irq(); =20 - da_monitor_destroy_nrp(); + da_monitor_destroy(); } =20 static struct rv_monitor rv_nrp =3D { @@ -116,7 +114,7 @@ static struct rv_monitor rv_nrp =3D { .description =3D "need resched preempts.", .enable =3D enable_nrp, .disable =3D disable_nrp, - .reset =3D da_monitor_reset_all_nrp, + .reset =3D da_monitor_reset_all, .enabled =3D 0, }; =20 diff --git a/kernel/trace/rv/monitors/nrp/nrp.h b/kernel/trace/rv/monitors/= nrp/nrp.h index c9f12207cbf6..c2ec83da2124 100644 --- a/kernel/trace/rv/monitors/nrp/nrp.h +++ b/kernel/trace/rv/monitors/nrp/nrp.h @@ -5,6 +5,8 @@ * Documentation/trace/rv/deterministic_automata.rst */ =20 +#define MONITOR_NAME nrp + enum states_nrp { preempt_irq_nrp =3D 0, any_thread_running_nrp, diff --git a/kernel/trace/rv/monitors/opid/opid.c b/kernel/trace/rv/monitor= s/opid/opid.c index 50d64e7fb8c4..7e9f23a76867 100644 --- a/kernel/trace/rv/monitors/opid/opid.c +++ b/kernel/trace/rv/monitors/opid/opid.c @@ -6,7 +6,6 @@ #include #include #include -#include =20 #define MODULE_NAME "opid" =20 @@ -16,17 +15,16 @@ #include #include =20 +#define RV_MON_TYPE RV_MON_PER_CPU #include "opid.h" - -static struct rv_monitor rv_opid; -DECLARE_DA_MON_PER_CPU(opid, unsigned char); +#include =20 #ifdef CONFIG_X86_LOCAL_APIC #include =20 static void handle_vector_irq_entry(void *data, int vector) { - da_handle_event_opid(irq_entry_opid); + da_handle_event(irq_entry_opid); } =20 static void attach_vector_irq(void) @@ -61,52 +59,52 @@ static void detach_vector_irq(void) { } =20 static void handle_irq_disable(void *data, unsigned long ip, unsigned long= parent_ip) { - da_handle_event_opid(irq_disable_opid); + da_handle_event(irq_disable_opid); } =20 static void handle_irq_enable(void *data, unsigned long ip, unsigned long = parent_ip) { - da_handle_event_opid(irq_enable_opid); + da_handle_event(irq_enable_opid); } =20 static void handle_irq_entry(void *data, int irq, struct irqaction *action) { - da_handle_event_opid(irq_entry_opid); + da_handle_event(irq_entry_opid); } =20 static void handle_preempt_disable(void *data, unsigned long ip, unsigned = long parent_ip) { - da_handle_event_opid(preempt_disable_opid); + da_handle_event(preempt_disable_opid); } =20 static void handle_preempt_enable(void *data, unsigned long ip, unsigned l= ong parent_ip) { - da_handle_event_opid(preempt_enable_opid); + da_handle_event(preempt_enable_opid); } =20 static void handle_sched_need_resched(void *data, struct task_struct *tsk,= int cpu, int tif) { /* The monitor's intitial state is not in_irq */ if (this_cpu_read(hardirq_context)) - da_handle_event_opid(sched_need_resched_opid); + da_handle_event(sched_need_resched_opid); else - da_handle_start_event_opid(sched_need_resched_opid); + da_handle_start_event(sched_need_resched_opid); } =20 static void handle_sched_waking(void *data, struct task_struct *p) { /* The monitor's intitial state is not in_irq */ if (this_cpu_read(hardirq_context)) - da_handle_event_opid(sched_waking_opid); + da_handle_event(sched_waking_opid); else - da_handle_start_event_opid(sched_waking_opid); + da_handle_start_event(sched_waking_opid); } =20 static int enable_opid(void) { int retval; =20 - retval =3D da_monitor_init_opid(); + retval =3D da_monitor_init(); if (retval) return retval; =20 @@ -135,7 +133,7 @@ static void disable_opid(void) rv_detach_trace_probe("opid", sched_waking, handle_sched_waking); detach_vector_irq(); =20 - da_monitor_destroy_opid(); + da_monitor_destroy(); } =20 /* @@ -146,7 +144,7 @@ static struct rv_monitor rv_opid =3D { .description =3D "operations with preemption and irq disabled.", .enable =3D enable_opid, .disable =3D disable_opid, - .reset =3D da_monitor_reset_all_opid, + .reset =3D da_monitor_reset_all, .enabled =3D 0, }; =20 diff --git a/kernel/trace/rv/monitors/opid/opid.h b/kernel/trace/rv/monitor= s/opid/opid.h index b4b8c2ff7f64..5014f1b85ecf 100644 --- a/kernel/trace/rv/monitors/opid/opid.h +++ b/kernel/trace/rv/monitors/opid/opid.h @@ -5,6 +5,8 @@ * Documentation/trace/rv/deterministic_automata.rst */ =20 +#define MONITOR_NAME opid + enum states_opid { disabled_opid =3D 0, enabled_opid, diff --git a/kernel/trace/rv/monitors/sco/sco.c b/kernel/trace/rv/monitors/= sco/sco.c index 04c36405e2e3..1f413f839cf3 100644 --- a/kernel/trace/rv/monitors/sco/sco.c +++ b/kernel/trace/rv/monitors/sco/sco.c @@ -6,7 +6,6 @@ #include #include #include -#include =20 #define MODULE_NAME "sco" =20 @@ -14,31 +13,30 @@ #include #include =20 +#define RV_MON_TYPE RV_MON_PER_CPU #include "sco.h" - -static struct rv_monitor rv_sco; -DECLARE_DA_MON_PER_CPU(sco, unsigned char); +#include =20 static void handle_sched_set_state(void *data, struct task_struct *tsk, in= t state) { - da_handle_start_event_sco(sched_set_state_sco); + da_handle_start_event(sched_set_state_sco); } =20 static void handle_schedule_entry(void *data, bool preempt) { - da_handle_event_sco(schedule_entry_sco); + da_handle_event(schedule_entry_sco); } =20 static void handle_schedule_exit(void *data, bool is_switch) { - da_handle_start_event_sco(schedule_exit_sco); + da_handle_start_event(schedule_exit_sco); } =20 static int enable_sco(void) { int retval; =20 - retval =3D da_monitor_init_sco(); + retval =3D da_monitor_init(); if (retval) return retval; =20 @@ -57,7 +55,7 @@ static void disable_sco(void) rv_detach_trace_probe("sco", sched_entry_tp, handle_schedule_entry); rv_detach_trace_probe("sco", sched_exit_tp, handle_schedule_exit); =20 - da_monitor_destroy_sco(); + da_monitor_destroy(); } =20 static struct rv_monitor rv_sco =3D { @@ -65,7 +63,7 @@ static struct rv_monitor rv_sco =3D { .description =3D "scheduling context operations.", .enable =3D enable_sco, .disable =3D disable_sco, - .reset =3D da_monitor_reset_all_sco, + .reset =3D da_monitor_reset_all, .enabled =3D 0, }; =20 diff --git a/kernel/trace/rv/monitors/sco/sco.h b/kernel/trace/rv/monitors/= sco/sco.h index 7a4c1f2d5ca1..06b1c420ce54 100644 --- a/kernel/trace/rv/monitors/sco/sco.h +++ b/kernel/trace/rv/monitors/sco/sco.h @@ -5,6 +5,8 @@ * Documentation/trace/rv/deterministic_automata.rst */ =20 +#define MONITOR_NAME sco + enum states_sco { thread_context_sco =3D 0, scheduling_context_sco, diff --git a/kernel/trace/rv/monitors/scpd/scpd.c b/kernel/trace/rv/monitor= s/scpd/scpd.c index 1e351ba52fee..de032a9cbd3a 100644 --- a/kernel/trace/rv/monitors/scpd/scpd.c +++ b/kernel/trace/rv/monitors/scpd/scpd.c @@ -6,7 +6,6 @@ #include #include #include -#include =20 #define MODULE_NAME "scpd" =20 @@ -15,36 +14,35 @@ #include #include =20 +#define RV_MON_TYPE RV_MON_PER_CPU #include "scpd.h" - -static struct rv_monitor rv_scpd; -DECLARE_DA_MON_PER_CPU(scpd, unsigned char); +#include =20 static void handle_preempt_disable(void *data, unsigned long ip, unsigned = long parent_ip) { - da_handle_event_scpd(preempt_disable_scpd); + da_handle_event(preempt_disable_scpd); } =20 static void handle_preempt_enable(void *data, unsigned long ip, unsigned l= ong parent_ip) { - da_handle_start_event_scpd(preempt_enable_scpd); + da_handle_start_event(preempt_enable_scpd); } =20 static void handle_schedule_entry(void *data, bool preempt) { - da_handle_event_scpd(schedule_entry_scpd); + da_handle_event(schedule_entry_scpd); } =20 static void handle_schedule_exit(void *data, bool is_switch) { - da_handle_event_scpd(schedule_exit_scpd); + da_handle_event(schedule_exit_scpd); } =20 static int enable_scpd(void) { int retval; =20 - retval =3D da_monitor_init_scpd(); + retval =3D da_monitor_init(); if (retval) return retval; =20 @@ -65,7 +63,7 @@ static void disable_scpd(void) rv_detach_trace_probe("scpd", sched_entry_tp, handle_schedule_entry); rv_detach_trace_probe("scpd", sched_exit_tp, handle_schedule_exit); =20 - da_monitor_destroy_scpd(); + da_monitor_destroy(); } =20 static struct rv_monitor rv_scpd =3D { @@ -73,7 +71,7 @@ static struct rv_monitor rv_scpd =3D { .description =3D "schedule called with preemption disabled.", .enable =3D enable_scpd, .disable =3D disable_scpd, - .reset =3D da_monitor_reset_all_scpd, + .reset =3D da_monitor_reset_all, .enabled =3D 0, }; =20 diff --git a/kernel/trace/rv/monitors/scpd/scpd.h b/kernel/trace/rv/monitor= s/scpd/scpd.h index 295f735a5811..4a725a68085a 100644 --- a/kernel/trace/rv/monitors/scpd/scpd.h +++ b/kernel/trace/rv/monitors/scpd/scpd.h @@ -5,6 +5,8 @@ * Documentation/trace/rv/deterministic_automata.rst */ =20 +#define MONITOR_NAME scpd + enum states_scpd { cant_sched_scpd =3D 0, can_sched_scpd, diff --git a/kernel/trace/rv/monitors/snep/snep.c b/kernel/trace/rv/monitor= s/snep/snep.c index 558950f524a5..5f90fec5ff5a 100644 --- a/kernel/trace/rv/monitors/snep/snep.c +++ b/kernel/trace/rv/monitors/snep/snep.c @@ -6,7 +6,6 @@ #include #include #include -#include =20 #define MODULE_NAME "snep" =20 @@ -15,36 +14,35 @@ #include #include =20 +#define RV_MON_TYPE RV_MON_PER_CPU #include "snep.h" - -static struct rv_monitor rv_snep; -DECLARE_DA_MON_PER_CPU(snep, unsigned char); +#include =20 static void handle_preempt_disable(void *data, unsigned long ip, unsigned = long parent_ip) { - da_handle_start_event_snep(preempt_disable_snep); + da_handle_start_event(preempt_disable_snep); } =20 static void handle_preempt_enable(void *data, unsigned long ip, unsigned l= ong parent_ip) { - da_handle_start_event_snep(preempt_enable_snep); + da_handle_start_event(preempt_enable_snep); } =20 static void handle_schedule_entry(void *data, bool preempt) { - da_handle_event_snep(schedule_entry_snep); + da_handle_event(schedule_entry_snep); } =20 static void handle_schedule_exit(void *data, bool is_switch) { - da_handle_start_event_snep(schedule_exit_snep); + da_handle_start_event(schedule_exit_snep); } =20 static int enable_snep(void) { int retval; =20 - retval =3D da_monitor_init_snep(); + retval =3D da_monitor_init(); if (retval) return retval; =20 @@ -65,7 +63,7 @@ static void disable_snep(void) rv_detach_trace_probe("snep", sched_entry_tp, handle_schedule_entry); rv_detach_trace_probe("snep", sched_exit_tp, handle_schedule_exit); =20 - da_monitor_destroy_snep(); + da_monitor_destroy(); } =20 static struct rv_monitor rv_snep =3D { @@ -73,7 +71,7 @@ static struct rv_monitor rv_snep =3D { .description =3D "schedule does not enable preempt.", .enable =3D enable_snep, .disable =3D disable_snep, - .reset =3D da_monitor_reset_all_snep, + .reset =3D da_monitor_reset_all, .enabled =3D 0, }; =20 diff --git a/kernel/trace/rv/monitors/snep/snep.h b/kernel/trace/rv/monitor= s/snep/snep.h index 4cd9abb77b7b..753080dc5fa1 100644 --- a/kernel/trace/rv/monitors/snep/snep.h +++ b/kernel/trace/rv/monitors/snep/snep.h @@ -5,6 +5,8 @@ * Documentation/trace/rv/deterministic_automata.rst */ =20 +#define MONITOR_NAME snep + enum states_snep { non_scheduling_context_snep =3D 0, scheduling_contex_snep, diff --git a/kernel/trace/rv/monitors/snroc/snroc.c b/kernel/trace/rv/monit= ors/snroc/snroc.c index 540e686e699f..7e0a487d07ac 100644 --- a/kernel/trace/rv/monitors/snroc/snroc.c +++ b/kernel/trace/rv/monitors/snroc/snroc.c @@ -6,7 +6,6 @@ #include #include #include -#include =20 #define MODULE_NAME "snroc" =20 @@ -14,14 +13,13 @@ #include #include =20 +#define RV_MON_TYPE RV_MON_PER_TASK #include "snroc.h" - -static struct rv_monitor rv_snroc; -DECLARE_DA_MON_PER_TASK(snroc, unsigned char); +#include =20 static void handle_sched_set_state(void *data, struct task_struct *tsk, in= t state) { - da_handle_event_snroc(tsk, sched_set_state_snroc); + da_handle_event(tsk, sched_set_state_snroc); } =20 static void handle_sched_switch(void *data, bool preempt, @@ -29,15 +27,15 @@ static void handle_sched_switch(void *data, bool preemp= t, struct task_struct *next, unsigned int prev_state) { - da_handle_start_event_snroc(prev, sched_switch_out_snroc); - da_handle_event_snroc(next, sched_switch_in_snroc); + da_handle_start_event(prev, sched_switch_out_snroc); + da_handle_event(next, sched_switch_in_snroc); } =20 static int enable_snroc(void) { int retval; =20 - retval =3D da_monitor_init_snroc(); + retval =3D da_monitor_init(); if (retval) return retval; =20 @@ -54,7 +52,7 @@ static void disable_snroc(void) rv_detach_trace_probe("snroc", sched_set_state_tp, handle_sched_set_state= ); rv_detach_trace_probe("snroc", sched_switch, handle_sched_switch); =20 - da_monitor_destroy_snroc(); + da_monitor_destroy(); } =20 static struct rv_monitor rv_snroc =3D { @@ -62,7 +60,7 @@ static struct rv_monitor rv_snroc =3D { .description =3D "set non runnable on its own context.", .enable =3D enable_snroc, .disable =3D disable_snroc, - .reset =3D da_monitor_reset_all_snroc, + .reset =3D da_monitor_reset_all, .enabled =3D 0, }; =20 diff --git a/kernel/trace/rv/monitors/snroc/snroc.h b/kernel/trace/rv/monit= ors/snroc/snroc.h index c3650a2b1b10..ada5ee08bdab 100644 --- a/kernel/trace/rv/monitors/snroc/snroc.h +++ b/kernel/trace/rv/monitors/snroc/snroc.h @@ -5,6 +5,8 @@ * Documentation/trace/rv/deterministic_automata.rst */ =20 +#define MONITOR_NAME snroc + enum states_snroc { other_context_snroc =3D 0, own_context_snroc, diff --git a/kernel/trace/rv/monitors/sssw/sssw.c b/kernel/trace/rv/monitor= s/sssw/sssw.c index 84b8d890d9d4..8df599ccd7d8 100644 --- a/kernel/trace/rv/monitors/sssw/sssw.c +++ b/kernel/trace/rv/monitors/sssw/sssw.c @@ -6,7 +6,6 @@ #include #include #include -#include =20 #define MODULE_NAME "sssw" =20 @@ -15,17 +14,16 @@ #include #include =20 +#define RV_MON_TYPE RV_MON_PER_TASK #include "sssw.h" - -static struct rv_monitor rv_sssw; -DECLARE_DA_MON_PER_TASK(sssw, unsigned char); +#include =20 static void handle_sched_set_state(void *data, struct task_struct *tsk, in= t state) { if (state =3D=3D TASK_RUNNING) - da_handle_start_event_sssw(tsk, sched_set_state_runnable_sssw); + da_handle_start_event(tsk, sched_set_state_runnable_sssw); else - da_handle_event_sssw(tsk, sched_set_state_sleepable_sssw); + da_handle_event(tsk, sched_set_state_sleepable_sssw); } =20 static void handle_sched_switch(void *data, bool preempt, @@ -34,15 +32,15 @@ static void handle_sched_switch(void *data, bool preemp= t, unsigned int prev_state) { if (preempt) - da_handle_event_sssw(prev, sched_switch_preempt_sssw); + da_handle_event(prev, sched_switch_preempt_sssw); else if (prev_state =3D=3D TASK_RUNNING) - da_handle_event_sssw(prev, sched_switch_yield_sssw); + da_handle_event(prev, sched_switch_yield_sssw); else if (prev_state =3D=3D TASK_RTLOCK_WAIT) /* special case of sleeping task with racy conditions */ - da_handle_event_sssw(prev, sched_switch_blocking_sssw); + da_handle_event(prev, sched_switch_blocking_sssw); else - da_handle_event_sssw(prev, sched_switch_suspend_sssw); - da_handle_event_sssw(next, sched_switch_in_sssw); + da_handle_event(prev, sched_switch_suspend_sssw); + da_handle_event(next, sched_switch_in_sssw); } =20 static void handle_sched_wakeup(void *data, struct task_struct *p) @@ -51,21 +49,21 @@ static void handle_sched_wakeup(void *data, struct task= _struct *p) * Wakeup can also lead to signal_wakeup although the system is * actually runnable. The monitor can safely start with this event. */ - da_handle_start_event_sssw(p, sched_wakeup_sssw); + da_handle_start_event(p, sched_wakeup_sssw); } =20 static void handle_signal_deliver(void *data, int sig, struct kernel_siginfo *info, struct k_sigaction *ka) { - da_handle_event_sssw(current, signal_deliver_sssw); + da_handle_event(current, signal_deliver_sssw); } =20 static int enable_sssw(void) { int retval; =20 - retval =3D da_monitor_init_sssw(); + retval =3D da_monitor_init(); if (retval) return retval; =20 @@ -86,7 +84,7 @@ static void disable_sssw(void) rv_detach_trace_probe("sssw", sched_wakeup, handle_sched_wakeup); rv_detach_trace_probe("sssw", signal_deliver, handle_signal_deliver); =20 - da_monitor_destroy_sssw(); + da_monitor_destroy(); } =20 static struct rv_monitor rv_sssw =3D { @@ -94,7 +92,7 @@ static struct rv_monitor rv_sssw =3D { .description =3D "set state sleep and wakeup.", .enable =3D enable_sssw, .disable =3D disable_sssw, - .reset =3D da_monitor_reset_all_sssw, + .reset =3D da_monitor_reset_all, .enabled =3D 0, }; =20 diff --git a/kernel/trace/rv/monitors/sssw/sssw.h b/kernel/trace/rv/monitor= s/sssw/sssw.h index 243d54050c94..8409eaadc7e0 100644 --- a/kernel/trace/rv/monitors/sssw/sssw.h +++ b/kernel/trace/rv/monitors/sssw/sssw.h @@ -5,6 +5,8 @@ * Documentation/trace/rv/deterministic_automata.rst */ =20 +#define MONITOR_NAME sssw + enum states_sssw { runnable_sssw =3D 0, signal_wakeup_sssw, diff --git a/kernel/trace/rv/monitors/sts/sts.c b/kernel/trace/rv/monitors/= sts/sts.c index c4a9cd67c1d2..4d9d2fc3afb8 100644 --- a/kernel/trace/rv/monitors/sts/sts.c +++ b/kernel/trace/rv/monitors/sts/sts.c @@ -6,7 +6,6 @@ #include #include #include -#include =20 #define MODULE_NAME "sts" =20 @@ -16,17 +15,16 @@ #include #include =20 +#define RV_MON_TYPE RV_MON_PER_CPU #include "sts.h" - -static struct rv_monitor rv_sts; -DECLARE_DA_MON_PER_CPU(sts, unsigned char); +#include =20 #ifdef CONFIG_X86_LOCAL_APIC #include =20 static void handle_vector_irq_entry(void *data, int vector) { - da_handle_event_sts(irq_entry_sts); + da_handle_event(irq_entry_sts); } =20 static void attach_vector_irq(void) @@ -61,17 +59,17 @@ static void detach_vector_irq(void) { } =20 static void handle_irq_disable(void *data, unsigned long ip, unsigned long= parent_ip) { - da_handle_event_sts(irq_disable_sts); + da_handle_event(irq_disable_sts); } =20 static void handle_irq_enable(void *data, unsigned long ip, unsigned long = parent_ip) { - da_handle_event_sts(irq_enable_sts); + da_handle_event(irq_enable_sts); } =20 static void handle_irq_entry(void *data, int irq, struct irqaction *action) { - da_handle_event_sts(irq_entry_sts); + da_handle_event(irq_entry_sts); } =20 static void handle_sched_switch(void *data, bool preempt, @@ -79,24 +77,24 @@ static void handle_sched_switch(void *data, bool preemp= t, struct task_struct *next, unsigned int prev_state) { - da_handle_event_sts(sched_switch_sts); + da_handle_event(sched_switch_sts); } =20 static void handle_schedule_entry(void *data, bool preempt) { - da_handle_event_sts(schedule_entry_sts); + da_handle_event(schedule_entry_sts); } =20 static void handle_schedule_exit(void *data, bool is_switch) { - da_handle_start_event_sts(schedule_exit_sts); + da_handle_start_event(schedule_exit_sts); } =20 static int enable_sts(void) { int retval; =20 - retval =3D da_monitor_init_sts(); + retval =3D da_monitor_init(); if (retval) return retval; =20 @@ -123,7 +121,7 @@ static void disable_sts(void) rv_detach_trace_probe("sts", sched_exit_tp, handle_schedule_exit); detach_vector_irq(); =20 - da_monitor_destroy_sts(); + da_monitor_destroy(); } =20 /* @@ -134,7 +132,7 @@ static struct rv_monitor rv_sts =3D { .description =3D "schedule implies task switch.", .enable =3D enable_sts, .disable =3D disable_sts, - .reset =3D da_monitor_reset_all_sts, + .reset =3D da_monitor_reset_all, .enabled =3D 0, }; =20 diff --git a/kernel/trace/rv/monitors/sts/sts.h b/kernel/trace/rv/monitors/= sts/sts.h index 3368b6599a00..3779d7f99404 100644 --- a/kernel/trace/rv/monitors/sts/sts.h +++ b/kernel/trace/rv/monitors/sts/sts.h @@ -5,6 +5,8 @@ * Documentation/trace/rv/deterministic_automata.rst */ =20 +#define MONITOR_NAME sts + enum states_sts { can_sched_sts =3D 0, cant_sched_sts, diff --git a/kernel/trace/rv/monitors/wip/wip.c b/kernel/trace/rv/monitors/= wip/wip.c index 4b4e99615a11..394fd12e27b2 100644 --- a/kernel/trace/rv/monitors/wip/wip.c +++ b/kernel/trace/rv/monitors/wip/wip.c @@ -6,7 +6,6 @@ #include #include #include -#include =20 #define MODULE_NAME "wip" =20 @@ -14,31 +13,30 @@ #include #include =20 +#define RV_MON_TYPE RV_MON_PER_CPU #include "wip.h" - -static struct rv_monitor rv_wip; -DECLARE_DA_MON_PER_CPU(wip, unsigned char); +#include =20 static void handle_preempt_disable(void *data, unsigned long ip, unsigned = long parent_ip) { - da_handle_event_wip(preempt_disable_wip); + da_handle_event(preempt_disable_wip); } =20 static void handle_preempt_enable(void *data, unsigned long ip, unsigned l= ong parent_ip) { - da_handle_start_event_wip(preempt_enable_wip); + da_handle_start_event(preempt_enable_wip); } =20 static void handle_sched_waking(void *data, struct task_struct *task) { - da_handle_event_wip(sched_waking_wip); + da_handle_event(sched_waking_wip); } =20 static int enable_wip(void) { int retval; =20 - retval =3D da_monitor_init_wip(); + retval =3D da_monitor_init(); if (retval) return retval; =20 @@ -57,7 +55,7 @@ static void disable_wip(void) rv_detach_trace_probe("wip", preempt_enable, handle_preempt_enable); rv_detach_trace_probe("wip", sched_waking, handle_sched_waking); =20 - da_monitor_destroy_wip(); + da_monitor_destroy(); } =20 static struct rv_monitor rv_wip =3D { @@ -65,7 +63,7 @@ static struct rv_monitor rv_wip =3D { .description =3D "wakeup in preemptive per-cpu testing monitor.", .enable =3D enable_wip, .disable =3D disable_wip, - .reset =3D da_monitor_reset_all_wip, + .reset =3D da_monitor_reset_all, .enabled =3D 0, }; =20 diff --git a/kernel/trace/rv/monitors/wip/wip.h b/kernel/trace/rv/monitors/= wip/wip.h index c7193748bf36..cfdc52975354 100644 --- a/kernel/trace/rv/monitors/wip/wip.h +++ b/kernel/trace/rv/monitors/wip/wip.h @@ -5,6 +5,8 @@ * Documentation/trace/rv/deterministic_automata.rst */ =20 +#define MONITOR_NAME wip + enum states_wip { preemptive_wip =3D 0, non_preemptive_wip, diff --git a/kernel/trace/rv/monitors/wwnr/wwnr.c b/kernel/trace/rv/monitor= s/wwnr/wwnr.c index 4145bea2729e..75d3a2a31f72 100644 --- a/kernel/trace/rv/monitors/wwnr/wwnr.c +++ b/kernel/trace/rv/monitors/wwnr/wwnr.c @@ -6,40 +6,38 @@ #include #include #include -#include =20 #define MODULE_NAME "wwnr" =20 #include #include =20 +#define RV_MON_TYPE RV_MON_PER_TASK #include "wwnr.h" - -static struct rv_monitor rv_wwnr; -DECLARE_DA_MON_PER_TASK(wwnr, unsigned char); +#include =20 static void handle_switch(void *data, bool preempt, struct task_struct *p, struct task_struct *n, unsigned int prev_state) { /* start monitoring only after the first suspension */ if (prev_state =3D=3D TASK_INTERRUPTIBLE) - da_handle_start_event_wwnr(p, switch_out_wwnr); + da_handle_start_event(p, switch_out_wwnr); else - da_handle_event_wwnr(p, switch_out_wwnr); + da_handle_event(p, switch_out_wwnr); =20 - da_handle_event_wwnr(n, switch_in_wwnr); + da_handle_event(n, switch_in_wwnr); } =20 static void handle_wakeup(void *data, struct task_struct *p) { - da_handle_event_wwnr(p, wakeup_wwnr); + da_handle_event(p, wakeup_wwnr); } =20 static int enable_wwnr(void) { int retval; =20 - retval =3D da_monitor_init_wwnr(); + retval =3D da_monitor_init(); if (retval) return retval; =20 @@ -56,7 +54,7 @@ static void disable_wwnr(void) rv_detach_trace_probe("wwnr", sched_switch, handle_switch); rv_detach_trace_probe("wwnr", sched_wakeup, handle_wakeup); =20 - da_monitor_destroy_wwnr(); + da_monitor_destroy(); } =20 static struct rv_monitor rv_wwnr =3D { @@ -64,7 +62,7 @@ static struct rv_monitor rv_wwnr =3D { .description =3D "wakeup while not running per-task testing model.", .enable =3D enable_wwnr, .disable =3D disable_wwnr, - .reset =3D da_monitor_reset_all_wwnr, + .reset =3D da_monitor_reset_all, .enabled =3D 0, }; =20 diff --git a/kernel/trace/rv/monitors/wwnr/wwnr.h b/kernel/trace/rv/monitor= s/wwnr/wwnr.h index 0a59d23edf61..85d12e42a955 100644 --- a/kernel/trace/rv/monitors/wwnr/wwnr.h +++ b/kernel/trace/rv/monitors/wwnr/wwnr.h @@ -5,6 +5,8 @@ * Documentation/trace/rv/deterministic_automata.rst */ =20 +#define MONITOR_NAME wwnr + enum states_wwnr { not_running_wwnr =3D 0, running_wwnr, --=20 2.50.1 From nobody Sat Oct 4 15:57:18 2025 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BFFF921930B for ; Thu, 14 Aug 2025 15:08:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184131; cv=none; b=ZReUjRl0b3x2stb4TBAM/fQ/3WXmX31pzt+XBdFC5nUQ5ImjinZrQ4Wfw5aPvkBfCRyZsOi5AwaTWPEWCcWN34BTX5H/iFUiK8gIEKVqTSL0iONgcjVI70DEgC5r7w8FxtCteUpeH+8VapqIqjs7aRrpOV87Q46dS3g8u62tbZs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184131; c=relaxed/simple; bh=zxkq2v/5fdyS8/CbCq48fUaZVyoPJcpyXWILGFWxyEc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=tKhL/pYBBKRjgr2rdyDRssbacQf99vI7pUuXTKhAYwRcG2QZDIdrbLCvDsy4aJ3Z2kpBi6vMAZJT/Dy0OJsRv5XRmV3DO+aRsF9Wm6j2vwcG7PQSvS7Mhi26hGeTg3/YyxDneuGfB1qJR8sHH1J1W/l8/kOmV5lsjXY6TEkvTqM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=h/rWA5ex; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="h/rWA5ex" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1755184127; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=319VYPU2hZPq1VwYGfLtIkcmFiHEOht5GU2rt/S+4tY=; b=h/rWA5exeO9BIk5u0HNZYenlUgYr3CXt2dg71LrVlRsbgaF6ogf5HdeyMUVVCD2WJex0XK ABMDd58p+n8PL4tclT65kUK3hDGEaKSCLcgTaVzahEWZwn5xluABfFUOqHQwDx1Fk4awDn sPdf69BNLKQnfPEhVH5KmPEo0++XUdI= Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-556-tKboO8RNN9e5VXfFQ3q5xg-1; Thu, 14 Aug 2025 11:08:44 -0400 X-MC-Unique: tKboO8RNN9e5VXfFQ3q5xg-1 X-Mimecast-MFC-AGG-ID: tKboO8RNN9e5VXfFQ3q5xg_1755184123 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 37490180895F; Thu, 14 Aug 2025 15:08:38 +0000 (UTC) Received: from gmonaco-thinkpadt14gen3.rmtit.com (unknown [10.44.32.52]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 1A7071800280; Thu, 14 Aug 2025 15:08:33 +0000 (UTC) From: Gabriele Monaco To: linux-kernel@vger.kernel.org, Steven Rostedt , linux-trace-kernel@vger.kernel.org Cc: Gabriele Monaco , Nam Cao , Tomas Glozar , Juri Lelli , Clark Williams , John Kacur Subject: [RFC PATCH 02/17] rv: Cleanup da_monitor after refactor Date: Thu, 14 Aug 2025 17:07:54 +0200 Message-ID: <20250814150809.140739-3-gmonaco@redhat.com> In-Reply-To: <20250814150809.140739-1-gmonaco@redhat.com> References: <20250814150809.140739-1-gmonaco@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 Content-Type: text/plain; charset="utf-8" Previous changes refactored the da_monitor header file to avoid using macros, however empty macros (e.g. DECLARE_DA_FUNCTION) were left to ease review with diff tools. Most macros also get the argument type which doesn't really have a purpose since states have their own enum and the storage in struct da_monitor is fixed to unsigned int. Remove empty and no longer required macros and substitute the type parameter with the appropriate enum. Additionally break long line and adjust the format overall. Signed-off-by: Gabriele Monaco --- include/rv/automata.h | 24 +++------ include/rv/da_monitor.h | 107 ++++++++++++++++------------------------ 2 files changed, 51 insertions(+), 80 deletions(-) diff --git a/include/rv/automata.h b/include/rv/automata.h index 5b5d2e94c034..4a4eb40cf09a 100644 --- a/include/rv/automata.h +++ b/include/rv/automata.h @@ -6,29 +6,19 @@ * models in C generated by the dot2k tool. */ =20 +#ifndef _RV_AUTOMATA_H +#define _RV_AUTOMATA_H + #ifndef MONITOR_NAME #error "MONITOR_NAME macro is not defined. Did you include $(MODEL_NAME).h= generated by rvgen?" #endif =20 -#ifndef type -#define type unsigned char -#endif - #define RV_AUTOMATON_NAME CONCATENATE(automaton_, MONITOR_NAME) #define EVENT_MAX CONCATENATE(event_max_, MONITOR_NAME) #define STATE_MAX CONCATENATE(state_max_, MONITOR_NAME) #define events CONCATENATE(events_, MONITOR_NAME) #define states CONCATENATE(states_, MONITOR_NAME) =20 -/* - * DECLARE_AUTOMATA_HELPERS - define a set of helper functions for automata - * - * Define a set of helper functions for automata. The 'name' argument is u= sed - * as suffix for the functions and data. These functions will handle autom= aton - * with data type 'type'. - */ -#define DECLARE_AUTOMATA_HELPERS(name, type) - /* * model_get_state_name - return the (string) name of the given state */ @@ -54,7 +44,7 @@ static char *model_get_event_name(enum events event) /* * model_get_initial_state - return the automaton's initial state */ -static inline type model_get_initial_state(void) +static inline enum states model_get_initial_state(void) { return RV_AUTOMATON_NAME.initial_state; } @@ -65,8 +55,8 @@ static inline type model_get_initial_state(void) * Given the current state (curr_state) and the event (event), returns * the next state, or INVALID_STATE in case of error. */ -static inline type model_get_next_state(enum states curr_state, - enum events event) +static inline enum states model_get_next_state(enum states curr_state, + enum events event) { if ((curr_state < 0) || (curr_state >=3D STATE_MAX)) return INVALID_STATE; @@ -87,3 +77,5 @@ static inline bool model_is_final_state(enum states state) =20 return RV_AUTOMATON_NAME.final_states[state]; } + +#endif diff --git a/include/rv/da_monitor.h b/include/rv/da_monitor.h index bc02334aa8be..57a6e742d454 100644 --- a/include/rv/da_monitor.h +++ b/include/rv/da_monitor.h @@ -11,6 +11,9 @@ * Documentation/trace/rv/da_monitor_synthesis.rst */ =20 +#ifndef _RV_DA_MONITOR_H +#define _RV_DA_MONITOR_H + #include #include #include @@ -23,33 +26,24 @@ static struct rv_monitor RV_MONITOR_NAME; =20 #ifdef CONFIG_RV_REACTORS =20 -#define DECLARE_RV_REACTING_HELPERS(name, type) -static void cond_react(type curr_state, type event) +static void cond_react(enum states curr_state, enum events event) { if (!rv_reacting_on() || !RV_MONITOR_NAME.react) return; RV_MONITOR_NAME.react("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)); + __stringify(MONITOR_NAME), + model_get_event_name(event), + model_get_state_name(curr_state)); } =20 #else /* CONFIG_RV_REACTOR */ =20 -#define DECLARE_RV_REACTING_HELPERS(name, type) -static void cond_react(type curr_state, type event) +static void cond_react(enum states curr_state, enum events event) { return; } #endif =20 -/* - * Generic helpers for all types of deterministic automata monitors. - */ -#define DECLARE_DA_MON_GENERIC_HELPERS(name, type) - -DECLARE_RV_REACTING_HELPERS(name, type) - /* * da_monitor_reset - reset a monitor and setting it to init state */ @@ -100,7 +94,6 @@ static inline bool da_monitor_enabled(void) */ static inline bool da_monitor_handling_event(struct da_monitor *da_mon) { - if (!da_monitor_enabled()) return 0; =20 @@ -111,6 +104,7 @@ static inline bool da_monitor_handling_event(struct da_= monitor *da_mon) return 1; } =20 +#if RV_MON_TYPE =3D=3D RV_MON_GLOBAL || RV_MON_TYPE =3D=3D RV_MON_PER_CPU /* * Event handler for implicit monitors. Implicit monitor is the one which = the * handler does not need to specify which da_monitor to manipulate. Exampl= es @@ -120,10 +114,8 @@ static inline bool da_monitor_handling_event(struct da= _monitor *da_mon) * warn and reset the monitor if it runs out of retries. The monitor shoul= d be * able to handle various orders. */ -#if RV_MON_TYPE =3D=3D RV_MON_GLOBAL || RV_MON_TYPE =3D=3D RV_MON_PER_CPU =20 -static inline bool -da_event(struct da_monitor *da_mon, enum events event) +static inline bool da_event(struct da_monitor *da_mon, enum events event) { enum states curr_state, next_state; =20 @@ -132,15 +124,17 @@ da_event(struct da_monitor *da_mon, enum events event) next_state =3D model_get_next_state(curr_state, event); if (next_state =3D=3D INVALID_STATE) { cond_react(curr_state, event); - CONCATENATE(trace_error_, MONITOR_NAME)(model_get_state_name(curr_state= ), - model_get_event_name(event)); + CONCATENATE(trace_error_, MONITOR_NAME)( + model_get_state_name(curr_state), + model_get_event_name(event)); return false; } if (likely(try_cmpxchg(&da_mon->curr_state, &curr_state, next_state))) { - CONCATENATE(trace_event_, MONITOR_NAME)(model_get_state_name(curr_state= ), - model_get_event_name(event), - model_get_state_name(next_state), - model_is_final_state(next_state)); + CONCATENATE(trace_event_, MONITOR_NAME)( + model_get_state_name(curr_state), + model_get_event_name(event), + model_get_state_name(next_state), + model_is_final_state(next_state)); return true; } } @@ -152,6 +146,7 @@ da_event(struct da_monitor *da_mon, enum events event) return false; } =20 +#elif RV_MON_TYPE =3D=3D RV_MON_PER_TASK /* * Event handler for per_task monitors. * @@ -159,10 +154,9 @@ da_event(struct da_monitor *da_mon, enum events event) * warn and reset the monitor if it runs out of retries. The monitor shoul= d be * able to handle various orders. */ -#elif RV_MON_TYPE =3D=3D RV_MON_PER_TASK =20 static inline bool da_event(struct da_monitor *da_mon, struct task_struct = *tsk, - enum events event) + enum events event) { enum states curr_state, next_state; =20 @@ -172,16 +166,16 @@ static inline bool da_event(struct da_monitor *da_mon= , struct task_struct *tsk, if (next_state =3D=3D INVALID_STATE) { cond_react(curr_state, event); CONCATENATE(trace_error_, MONITOR_NAME)(tsk->pid, - model_get_state_name(curr_state), - model_get_event_name(event)); + model_get_state_name(curr_state), + model_get_event_name(event)); return false; } if (likely(try_cmpxchg(&da_mon->curr_state, &curr_state, next_state))) { CONCATENATE(trace_event_, MONITOR_NAME)(tsk->pid, - model_get_state_name(curr_state), - model_get_event_name(event), - model_get_state_name(next_state), - model_is_final_state(next_state)); + model_get_state_name(curr_state), + model_get_event_name(event), + model_get_state_name(next_state), + model_is_final_state(next_state)); return true; } } @@ -192,12 +186,12 @@ static inline bool da_event(struct da_monitor *da_mon= , struct task_struct *tsk, model_get_event_name(event), __stringify(MONITOR_NAME)); return false; } -#endif +#endif /* RV_MON_TYPE */ =20 +#if RV_MON_TYPE =3D=3D RV_MON_GLOBAL /* * Functions to define, init and get a global monitor. */ -#if RV_MON_TYPE =3D=3D RV_MON_GLOBAL =20 /* * global monitor (a single variable) @@ -237,10 +231,10 @@ static inline void da_monitor_destroy(void) return; } =20 +#elif RV_MON_TYPE =3D=3D RV_MON_PER_CPU /* * Functions to define, init and get a per-cpu monitor. */ -#elif RV_MON_TYPE =3D=3D RV_MON_PER_CPU =20 /* * per-cpu monitor variables @@ -285,10 +279,10 @@ static inline void da_monitor_destroy(void) return; } =20 +#elif RV_MON_TYPE =3D=3D RV_MON_PER_TASK /* * Functions to define, init and get a per-task monitor. */ -#elif RV_MON_TYPE =3D=3D RV_MON_PER_TASK =20 /* * The per-task monitor is stored a vector in the task struct. This variab= le @@ -350,16 +344,16 @@ static inline void da_monitor_destroy(void) task_mon_slot =3D RV_PER_TASK_MONITOR_INIT; return; } -#endif +#endif /* RV_MON_TYPE */ =20 +#if RV_MON_TYPE =3D=3D RV_MON_GLOBAL || RV_MON_TYPE =3D=3D RV_MON_PER_CPU /* * Handle event for implicit monitor: da_get_monitor() will figure out * the monitor. */ -#if RV_MON_TYPE =3D=3D RV_MON_GLOBAL || RV_MON_TYPE =3D=3D RV_MON_PER_CPU =20 static inline void __da_handle_event(struct da_monitor *da_mon, - enum events event) + enum events event) { bool retval; =20 @@ -435,14 +429,13 @@ static inline bool da_handle_start_run_event(enum eve= nts event) return 1; } =20 +#elif RV_MON_TYPE =3D=3D RV_MON_PER_TASK /* * Handle event for per task. */ -#elif RV_MON_TYPE =3D=3D RV_MON_PER_TASK =20 -static inline void -__da_handle_event(struct da_monitor *da_mon, struct task_struct *tsk, - enum events event) +static inline void __da_handle_event(struct da_monitor *da_mon, + struct task_struct *tsk, enum events event) { bool retval; =20 @@ -454,8 +447,7 @@ __da_handle_event(struct da_monitor *da_mon, struct tas= k_struct *tsk, /* * da_handle_event - handle an event */ -static inline void -da_handle_event(struct task_struct *tsk, enum events event) +static inline void da_handle_event(struct task_struct *tsk, enum events ev= ent) { struct da_monitor *da_mon =3D da_get_monitor(tsk); bool retval; @@ -477,8 +469,8 @@ da_handle_event(struct task_struct *tsk, enum events ev= ent) * 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) +static inline bool da_handle_start_event(struct task_struct *tsk, + enum events event) { struct da_monitor *da_mon; =20 @@ -503,8 +495,8 @@ da_handle_start_event(struct task_struct *tsk, enum eve= nts 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) +static inline bool da_handle_start_run_event(struct task_struct *tsk, + enum events event) { struct da_monitor *da_mon; =20 @@ -520,19 +512,6 @@ da_handle_start_run_event(struct task_struct *tsk, enu= m events event) =20 return 1; } -#endif - -/* - * Entry point for the global monitor. - */ -#define DECLARE_DA_MON_GLOBAL(name, type) - -/* - * Entry point for the per-cpu monitor. - */ -#define DECLARE_DA_MON_PER_CPU(name, type) +#endif /* RV_MON_TYPE */ =20 -/* - * Entry point for the per-task monitor. - */ -#define DECLARE_DA_MON_PER_TASK(name, type) +#endif --=20 2.50.1 From nobody Sat Oct 4 15:57:18 2025 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D170721C9F4 for ; Thu, 14 Aug 2025 15:08:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184138; cv=none; b=jlpz2uJmooBwdamcy2wl8YozNXdEcDtUVSeVF9ofrSSWsNXAeiDE90lfMDXgHruf8fxI1OICjLfgMN+3qZExW2Em/usV2Ws/ObN+Q9gYOtCvFPImtz9MKZv/TqhL/p3jIrn1VHHrB5V0Hw+TjcJBVCGN70C0uBjiA/h1Gl8NdaE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184138; c=relaxed/simple; bh=TQ+yq4R5SsUlFYq2LKwIsgtXhQ8BHRVmJ5Fa3o26TkM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=XQmN98cDg8GwkmE2H2KVsS38tUk3fBBBDjZFIxnFRNaftmX1e7aV70cxkkkjMBQNgB0pLdfoVAOTJqNnq4Stw4AqsVjSQ3Tw5xoRcInJgyeVxUIqieSBgKFWiJYz2hJMgIUdHMc8B65lQkJQKS6cyISxK4CK8bnNp7y2c61R8R8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=fxspNEqF; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="fxspNEqF" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1755184135; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=NFx756cjjS5MiQCtuQPJDvDAXhbdcRmOEdC2k9EimRA=; b=fxspNEqFwJhz5rjtugLEH+KrIIQXLn/EIyeS8e3stvQ+OKqL5XJ4RBiC/+hJ19gQmQwpAc WjBzZjdYZBM/0qL2Wp8hdCvat82OjdVRPzK/JYJCuZjxzuYkuBWkbcFLSu2HVUlEU8SBrH nWGqw5Jqb0paeyTYS6UalLu5JsK2lfs= Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-620-i4dlrAafNsSNcnKSDkG5IA-1; Thu, 14 Aug 2025 11:08:53 -0400 X-MC-Unique: i4dlrAafNsSNcnKSDkG5IA-1 X-Mimecast-MFC-AGG-ID: i4dlrAafNsSNcnKSDkG5IA_1755184132 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 5B0A81800EEF; Thu, 14 Aug 2025 15:08:44 +0000 (UTC) Received: from gmonaco-thinkpadt14gen3.rmtit.com (unknown [10.44.32.52]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 50595180047F; Thu, 14 Aug 2025 15:08:39 +0000 (UTC) From: Gabriele Monaco To: linux-kernel@vger.kernel.org, Steven Rostedt , Jonathan Corbet , linux-trace-kernel@vger.kernel.org, linux-doc@vger.kernel.org Cc: Gabriele Monaco , Nam Cao , Tomas Glozar , Juri Lelli , Clark Williams , John Kacur Subject: [RFC PATCH 03/17] Documentation/rv: Adapt documentation after da_monitor refactoring Date: Thu, 14 Aug 2025 17:07:55 +0200 Message-ID: <20250814150809.140739-4-gmonaco@redhat.com> In-Reply-To: <20250814150809.140739-1-gmonaco@redhat.com> References: <20250814150809.140739-1-gmonaco@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 Content-Type: text/plain; charset="utf-8" Signed-off-by: Gabriele Monaco --- Documentation/trace/rv/monitor_synthesis.rst | 44 ++++++++++---------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/Documentation/trace/rv/monitor_synthesis.rst b/Documentation/t= race/rv/monitor_synthesis.rst index ac808a7554f5..ce0c1a5104d4 100644 --- a/Documentation/trace/rv/monitor_synthesis.rst +++ b/Documentation/trace/rv/monitor_synthesis.rst @@ -100,54 +100,52 @@ rv/da_monitor.h =20 This initial implementation presents three different types of monitor inst= ances: =20 -- ``#define DECLARE_DA_MON_GLOBAL(name, type)`` -- ``#define DECLARE_DA_MON_PER_CPU(name, type)`` -- ``#define DECLARE_DA_MON_PER_TASK(name, type)`` +- ``#define RV_MON_TYPE RV_MON_GLOBAL`` +- ``#define RV_MON_TYPE RV_MON_PER_CPU`` +- ``#define RV_MON_TYPE RV_MON_PER_TASK`` =20 -The first declares the functions for a global deterministic automata monit= or, -the second for monitors with per-cpu instances, and the third with per-task -instances. +The first sets up functions declaration for a global deterministic automata +monitor, the second for monitors with per-cpu instances, and the third with +per-task instances. =20 -In all cases, the 'name' argument is a string that identifies the monitor,= and -the 'type' argument is the data type used by rvgen on the representation of -the model in C. +In all cases, the C file must include the $(MODEL_NAME).h file (generated = by +`rvgen`), for example, to define the per-cpu 'wip' monitor, the `wip.c` so= urce +file must include:: =20 -For example, the wip model with two states and three events can be -stored in an 'unsigned char' type. Considering that the preemption control -is a per-cpu behavior, the monitor declaration in the 'wip.c' file is:: - - DECLARE_DA_MON_PER_CPU(wip, unsigned char); + #define RV_MON_TYPE RV_MON_PER_CPU + #include "wip.h" + #include =20 The monitor is executed by sending events to be processed via the functions presented below:: =20 - da_handle_event_$(MONITOR_NAME)($(event from event enum)); - da_handle_start_event_$(MONITOR_NAME)($(event from event enum)); - da_handle_start_run_event_$(MONITOR_NAME)($(event from event enum)); + da_handle_event($(event from event enum)); + da_handle_start_event($(event from event enum)); + da_handle_start_run_event($(event from event enum)); =20 -The function ``da_handle_event_$(MONITOR_NAME)()`` is the regular case whe= re +The function ``da_handle_event()`` is the regular case where the event will be processed if the monitor is processing events. =20 When a monitor is enabled, it is placed in the initial state of the automa= ta. However, the monitor does not know if the system is in the *initial state*. =20 -The ``da_handle_start_event_$(MONITOR_NAME)()`` function is used to notify= the +The ``da_handle_start_event()`` function is used to notify the monitor that the system is returning to the initial state, so the monitor = can start monitoring the next event. =20 -The ``da_handle_start_run_event_$(MONITOR_NAME)()`` function is used to no= tify +The ``da_handle_start_run_event()`` function is used to notify the monitor that the system is known to be in the initial state, so the monitor can start monitoring and monitor the current event. =20 Using the wip model as example, the events "preempt_disable" and "sched_waking" should be sent to monitor, respectively, via [2]:: =20 - da_handle_event_wip(preempt_disable_wip); - da_handle_event_wip(sched_waking_wip); + da_handle_event(preempt_disable_wip); + da_handle_event(sched_waking_wip); =20 While the event "preempt_enabled" will use:: =20 - da_handle_start_event_wip(preempt_enable_wip); + da_handle_start_event(preempt_enable_wip); =20 To notify the monitor that the system will be returning to the initial sta= te, so the system and the monitor should be in sync. --=20 2.50.1 From nobody Sat Oct 4 15:57:18 2025 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BA13121B91D for ; Thu, 14 Aug 2025 15:08:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184135; cv=none; b=YxNyRHFa5S4RfbInbzLBNlkFIevL54iOR8UwMjubAu1vCDuuJy82xoHFAHvkc9dXkmj2Kpa2tq9r8O/XH9N7oB+zvK5l5ThfStoWO2wfbxlHRYHAAky7jPkB4/oV5TkBQEpdC9kTmX10hbhSJVrSGaSpYpmSM4CVd71WxGROi9w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184135; c=relaxed/simple; bh=BQuuF/m48mqxqBW4OTWlVJMPNfADUK+WR7ewpHKVei4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=AATiNrUmiXIGttOkjHOcRlpaMjJs3+cRnvMVGgiMV8vIDoAUa0JagxohdOfuXQbSacSYgwPnCzCf7biec4+hglj5X7zA3ENO9URMgy2nBFAxzKGKPwGHbL9W6duf00zEwV/UgyDTirQ4nc4c7xF8RmlkdGs3sC5JeZEhk0GV//4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=hF1iczMp; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="hF1iczMp" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1755184132; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=psBVSxqXLnc1DsK/Ni3tlWaJk27BOGfou6qbx9P9nYM=; b=hF1iczMpEzKjxbLOfjr84a4+dbsTXISmuktqmwUCPDwqMenEvUmTeWzBymkeK4kuTXp7Qk 06LJeKPNHGkrD3IddOIQrq5iOWgJIZBUvJKMnUU5ZvCXFAKbdB8gsasHCq5r/pQrb8KjbC keUjLl8AGOcV1sZ+KGD1IAMEfOAeJwA= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-12-t30n1DpHNlWpq7BDkBZwkw-1; Thu, 14 Aug 2025 11:08:51 -0400 X-MC-Unique: t30n1DpHNlWpq7BDkBZwkw-1 X-Mimecast-MFC-AGG-ID: t30n1DpHNlWpq7BDkBZwkw_1755184129 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id A580C1954236; Thu, 14 Aug 2025 15:08:49 +0000 (UTC) Received: from gmonaco-thinkpadt14gen3.rmtit.com (unknown [10.44.32.52]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 8D1071800295; Thu, 14 Aug 2025 15:08:45 +0000 (UTC) From: Gabriele Monaco To: linux-kernel@vger.kernel.org, Steven Rostedt , linux-trace-kernel@vger.kernel.org Cc: Gabriele Monaco , Nam Cao , Tomas Glozar , Juri Lelli , Clark Williams , John Kacur Subject: [RFC PATCH 04/17] verification/rvgen: Adapt dot2k and templates after refactoring da_monitor.h Date: Thu, 14 Aug 2025 17:07:56 +0200 Message-ID: <20250814150809.140739-5-gmonaco@redhat.com> In-Reply-To: <20250814150809.140739-1-gmonaco@redhat.com> References: <20250814150809.140739-1-gmonaco@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 Content-Type: text/plain; charset="utf-8" Signed-off-by: Gabriele Monaco --- tools/verification/rvgen/rvgen/dot2k.py | 6 ++++-- .../rvgen/rvgen/templates/dot2k/main.c | 17 +++++------------ 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/tools/verification/rvgen/rvgen/dot2k.py b/tools/verification/r= vgen/rvgen/dot2k.py index ed0a3c901106..d618a842fc52 100644 --- a/tools/verification/rvgen/rvgen/dot2k.py +++ b/tools/verification/rvgen/rvgen/dot2k.py @@ -38,9 +38,9 @@ class dot2k(Monitor, Dot2c): handle =3D "handle_start_run_event" if self.monitor_type =3D=3D "per_task": buff.append("\tstruct task_struct *p =3D /* XXX: how do I = get p? */;"); - buff.append("\tda_%s_%s(p, %s%s);" % (handle, self.name, e= vent, self.enum_suffix)); + buff.append("\tda_%s(p, %s%s);" % (handle, event, self.enu= m_suffix)); else: - buff.append("\tda_%s_%s(%s%s);" % (handle, self.name, even= t, self.enum_suffix)); + buff.append("\tda_%s(%s%s);" % (handle, event, self.enum_s= uffix)); buff.append("}") buff.append("") return '\n'.join(buff) @@ -66,6 +66,8 @@ class dot2k(Monitor, Dot2c): buff.append(" * Documentation/trace/rv/deterministic_automata.rs= t") buff.append(" */") buff.append("") + buff.append("#define MONITOR_NAME %s" % (self.name)) + buff.append("") =20 return buff =20 diff --git a/tools/verification/rvgen/rvgen/templates/dot2k/main.c b/tools/= verification/rvgen/rvgen/templates/dot2k/main.c index e0fd1134bd85..f14a0e7ad56b 100644 --- a/tools/verification/rvgen/rvgen/templates/dot2k/main.c +++ b/tools/verification/rvgen/rvgen/templates/dot2k/main.c @@ -6,7 +6,6 @@ #include #include #include -#include =20 #define MODULE_NAME "%%MODEL_NAME%%" =20 @@ -20,15 +19,9 @@ * This is the self-generated part of the monitor. Generally, there is no = need * to touch this section. */ +#define RV_MON_TYPE RV_MON_%%MONITOR_TYPE%% #include "%%MODEL_NAME%%.h" - -/* - * Declare the deterministic automata monitor. - * - * The rv monitor reference is needed for the monitor declaration. - */ -static struct rv_monitor rv_%%MODEL_NAME%%; -DECLARE_DA_MON_%%MONITOR_TYPE%%(%%MODEL_NAME%%, %%MIN_TYPE%%); +#include =20 /* * This is the instrumentation part of the monitor. @@ -42,7 +35,7 @@ static int enable_%%MODEL_NAME%%(void) { int retval; =20 - retval =3D da_monitor_init_%%MODEL_NAME%%(); + retval =3D da_monitor_init(); if (retval) return retval; =20 @@ -57,7 +50,7 @@ static void disable_%%MODEL_NAME%%(void) =20 %%TRACEPOINT_DETACH%% =20 - da_monitor_destroy_%%MODEL_NAME%%(); + da_monitor_destroy(); } =20 /* @@ -68,7 +61,7 @@ static struct rv_monitor rv_%%MODEL_NAME%% =3D { .description =3D "%%DESCRIPTION%%", .enable =3D enable_%%MODEL_NAME%%, .disable =3D disable_%%MODEL_NAME%%, - .reset =3D da_monitor_reset_all_%%MODEL_NAME%%, + .reset =3D da_monitor_reset_all, .enabled =3D 0, }; =20 --=20 2.50.1 From nobody Sat Oct 4 15:57:18 2025 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 45699224228 for ; Thu, 14 Aug 2025 15:08:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184141; cv=none; b=kQzzAt9oboFc2fEKTU7Cc2zsGptNHPkZFNTlq2uBsGuJnIFt6U+ytrwzLHTKQ8h8ky3FmSbBnrf0uAXZdTnyIjTKinpywtvtzqgLKLqu5+mAO7E6xBOiP5KaH6goZZvEkh90CL/BNMxwBzThH8tYevxk8ugAh46CbV6DffF9WcQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184141; c=relaxed/simple; bh=Zzh+s0TBTbxQf5GqjqV5LypRBS8JqKHi6iI6t/GTclY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=M43hsWOFmeJ1EecfJAeZbYnBb0DcEGfXDTNeUxpDd5Z+DyAMyfnFn6Tsxyf9lYr/URMrf6ZTpzlTG9ncOucQBL3hFkAsx5VGEtNQnowT/sjymfkLsBIl1FJeFvjTlgObjMbdWV0zIKFn0Wh6krUqnVQBzFCfGGfDbwZP93WlobA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=gUDXOVHs; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="gUDXOVHs" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1755184138; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=SurW7ViA88V/OnAbZboPIXQEF2TGj+piZ+8ocX5upMc=; b=gUDXOVHstGj2TC7ldTBO3c816vGmUqNRZv4pRfIdawWX8HJ7mODz7LtiI3Wo0BTgg+Inyo EPAZpCU6dyBgLsKCXjXh2lSyNRwQmSW+tg7DETosj5r3YpUq3iSdmj4/U3JJWYRgwSvHHE zg9uBXUDEnZLWwP+5fIWJszAlZ4KTCo= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-493-gHyunOC2MO-k-tXGW2kT3g-1; Thu, 14 Aug 2025 11:08:56 -0400 X-MC-Unique: gHyunOC2MO-k-tXGW2kT3g-1 X-Mimecast-MFC-AGG-ID: gHyunOC2MO-k-tXGW2kT3g_1755184135 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 05CC01956060; Thu, 14 Aug 2025 15:08:55 +0000 (UTC) Received: from gmonaco-thinkpadt14gen3.rmtit.com (unknown [10.44.32.52]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id BCDAB180047F; Thu, 14 Aug 2025 15:08:50 +0000 (UTC) From: Gabriele Monaco To: linux-kernel@vger.kernel.org, Steven Rostedt , linux-trace-kernel@vger.kernel.org Cc: Gabriele Monaco , Nam Cao , Tomas Glozar , Juri Lelli , Clark Williams , John Kacur Subject: [RFC PATCH 05/17] verification/rvgen: Annotate DA functions with types Date: Thu, 14 Aug 2025 17:07:57 +0200 Message-ID: <20250814150809.140739-6-gmonaco@redhat.com> In-Reply-To: <20250814150809.140739-1-gmonaco@redhat.com> References: <20250814150809.140739-1-gmonaco@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 Content-Type: text/plain; charset="utf-8" Functions in automata.py, dot2c.py and dot2k.py don't have type annotations and it can get complicated to remember how to use them. Add minimal type annotations. Signed-off-by: Gabriele Monaco --- tools/verification/rvgen/rvgen/automata.py | 18 ++++++------- tools/verification/rvgen/rvgen/dot2c.py | 30 +++++++++++----------- tools/verification/rvgen/rvgen/dot2k.py | 18 ++++++------- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/tools/verification/rvgen/rvgen/automata.py b/tools/verificatio= n/rvgen/rvgen/automata.py index d9a3fe2b74bf..76d7a3cfaec6 100644 --- a/tools/verification/rvgen/rvgen/automata.py +++ b/tools/verification/rvgen/rvgen/automata.py @@ -28,7 +28,7 @@ class Automata: self.function =3D self.__create_matrix() self.events_start, self.events_start_run =3D self.__store_init_eve= nts() =20 - def __get_model_name(self): + def __get_model_name(self) -> str: basename =3D ntpath.basename(self.__dot_path) if not basename.endswith(".dot") and not basename.endswith(".gv"): print("not a dot file") @@ -40,7 +40,7 @@ class Automata: =20 return model_name =20 - def __open_dot(self): + def __open_dot(self) -> list[str]: cursor =3D 0 dot_lines =3D [] try: @@ -60,13 +60,13 @@ class Automata: cursor +=3D 1 return dot_lines =20 - def __get_cursor_begin_states(self): + def __get_cursor_begin_states(self) -> int: cursor =3D 0 while self.__dot_lines[cursor].split()[0] !=3D "{node": cursor +=3D 1 return cursor =20 - def __get_cursor_begin_events(self): + def __get_cursor_begin_events(self) -> int: cursor =3D 0 while self.__dot_lines[cursor].split()[0] !=3D "{node": cursor +=3D 1 @@ -76,7 +76,7 @@ class Automata: cursor +=3D 1 return cursor =20 - def __get_state_variables(self): + def __get_state_variables(self) -> tuple[list[str], str, list[str]]: # wait for node declaration states =3D [] final_states =3D [] @@ -116,7 +116,7 @@ class Automata: =20 return states, initial_state, final_states =20 - def __get_event_variables(self): + def __get_event_variables(self) -> tuple[list[str], list[str]]: # here we are at the begin of transitions, take a note, we will re= turn later. cursor =3D self.__get_cursor_begin_events() =20 @@ -174,7 +174,7 @@ class Automata: =20 return matrix =20 - def __store_init_events(self): + def __store_init_events(self) -> tuple[list[bool], list[bool]]: events_start =3D [False] * len(self.events) events_start_run =3D [False] * len(self.events) for i, _ in enumerate(self.events): @@ -196,10 +196,10 @@ class Automata: events_start_run[i] =3D True return events_start, events_start_run =20 - def is_start_event(self, event): + def is_start_event(self, event: str) -> bool: return self.events_start[self.events.index(event)] =20 - def is_start_run_event(self, event): + def is_start_run_event(self, event: str) -> bool: # prefer handle_start_event if there if any(self.events_start): return False diff --git a/tools/verification/rvgen/rvgen/dot2c.py b/tools/verification/r= vgen/rvgen/dot2c.py index b9b6f14cc536..e92567045c9c 100644 --- a/tools/verification/rvgen/rvgen/dot2c.py +++ b/tools/verification/rvgen/rvgen/dot2c.py @@ -57,7 +57,7 @@ class Dot2c(Automata): =20 return buff =20 - def __get_enum_events_content(self): + def __get_enum_events_content(self) -> list[str]: buff =3D [] first =3D True for event in self.events: @@ -97,7 +97,7 @@ class Dot2c(Automata): =20 return min_type =20 - def format_automaton_definition(self): + def format_automaton_definition(self) -> list[str]: min_type =3D self.get_minimun_type() buff =3D [] buff.append("struct %s {" % self.struct_automaton_def) @@ -109,12 +109,12 @@ class Dot2c(Automata): buff.append("};\n") return buff =20 - def format_aut_init_header(self): + def format_aut_init_header(self) -> list[str]: buff =3D [] buff.append("static const struct %s %s =3D {" % (self.struct_autom= aton_def, self.var_automaton_def)) return buff =20 - def __get_string_vector_per_line_content(self, buff): + def __get_string_vector_per_line_content(self, buff: list[str]) -> str: first =3D True string =3D "" for entry in buff: @@ -140,7 +140,7 @@ class Dot2c(Automata): buff.append("\t},") return buff =20 - def format_aut_init_states_string(self): + def format_aut_init_states_string(self) -> list[str]: buff =3D [] buff.append("\t.state_names =3D {") buff.append(self.get_aut_init_states_string()) @@ -152,7 +152,7 @@ class Dot2c(Automata): max_state_name =3D max(self.states, key =3D len).__len__() return max(max_state_name, self.invalid_state_str.__len__()) =20 - def get_aut_init_function(self): + def get_aut_init_function(self) -> str: nr_states =3D self.states.__len__() nr_events =3D self.events.__len__() buff =3D [] @@ -180,7 +180,7 @@ class Dot2c(Automata): =20 return self.__buff_to_string(buff) =20 - def format_aut_init_function(self): + def format_aut_init_function(self) -> list[str]: buff =3D [] buff.append("\t.function =3D {") buff.append(self.get_aut_init_function()) @@ -188,17 +188,17 @@ class Dot2c(Automata): =20 return buff =20 - def get_aut_init_initial_state(self): + def get_aut_init_initial_state(self) -> str: return self.initial_state =20 - def format_aut_init_initial_state(self): + def format_aut_init_initial_state(self) -> list[str]: buff =3D [] initial_state =3D self.get_aut_init_initial_state() buff.append("\t.initial_state =3D " + initial_state + self.enum_su= ffix + ",") =20 return buff =20 - def get_aut_init_final_states(self): + def get_aut_init_final_states(self) -> str: line =3D "" first =3D True for state in self.states: @@ -213,29 +213,29 @@ class Dot2c(Automata): line =3D line + '0' return line =20 - def format_aut_init_final_states(self): + def format_aut_init_final_states(self) -> list[str]: buff =3D [] buff.append("\t.final_states =3D { %s }," % self.get_aut_init_final= _states()) =20 return buff =20 - def __get_automaton_initialization_footer_string(self): + def __get_automaton_initialization_footer_string(self) -> str: footer =3D "};\n" return footer =20 - def format_aut_init_footer(self): + def format_aut_init_footer(self) -> list[str]: buff =3D [] buff.append(self.__get_automaton_initialization_footer_string()) =20 return buff =20 - def format_invalid_state(self): + def format_invalid_state(self) -> list[str]: buff =3D [] buff.append("#define %s state_max%s\n" % (self.invalid_state_str, = self.enum_suffix)) =20 return buff =20 - def format_model(self): + def format_model(self) -> list[str]: buff =3D [] buff +=3D self.format_states_enum() buff +=3D self.format_invalid_state() diff --git a/tools/verification/rvgen/rvgen/dot2k.py b/tools/verification/r= vgen/rvgen/dot2k.py index d618a842fc52..a48e32c4ca73 100644 --- a/tools/verification/rvgen/rvgen/dot2k.py +++ b/tools/verification/rvgen/rvgen/dot2k.py @@ -21,10 +21,10 @@ class dot2k(Monitor, Dot2c): Dot2c.__init__(self, file_path, extra_params.get("model_name")) self.enum_suffix =3D "_%s" % self.name =20 - def fill_monitor_type(self): + def fill_monitor_type(self) -> str: return self.monitor_type.upper() =20 - def fill_tracepoint_handlers_skel(self): + def fill_tracepoint_handlers_skel(self) -> str: buff =3D [] for event in self.events: buff.append("static void handle_%s(void *data, /* XXX: fill he= ader */)" % event) @@ -45,19 +45,19 @@ class dot2k(Monitor, Dot2c): buff.append("") return '\n'.join(buff) =20 - def fill_tracepoint_attach_probe(self): + def fill_tracepoint_attach_probe(self) -> str: buff =3D [] for event in self.events: buff.append("\trv_attach_trace_probe(\"%s\", /* XXX: tracepoin= t */, handle_%s);" % (self.name, event)) return '\n'.join(buff) =20 - def fill_tracepoint_detach_helper(self): + def fill_tracepoint_detach_helper(self) -> str: buff =3D [] for event in self.events: buff.append("\trv_detach_trace_probe(\"%s\", /* XXX: tracepoin= t */, handle_%s);" % (self.name, event)) return '\n'.join(buff) =20 - def fill_model_h_header(self): + def fill_model_h_header(self) -> list[str]: buff =3D [] buff.append("/* SPDX-License-Identifier: GPL-2.0 */") buff.append("/*") @@ -71,7 +71,7 @@ class dot2k(Monitor, Dot2c): =20 return buff =20 - def fill_model_h(self): + def fill_model_h(self) -> str: # # Adjust the definition names # @@ -85,17 +85,17 @@ class dot2k(Monitor, Dot2c): =20 return '\n'.join(buff) =20 - def fill_monitor_class_type(self): + def fill_monitor_class_type(self) -> str: if self.monitor_type =3D=3D "per_task": return "DA_MON_EVENTS_ID" return "DA_MON_EVENTS_IMPLICIT" =20 - def fill_monitor_class(self): + def fill_monitor_class(self) -> str: if self.monitor_type =3D=3D "per_task": return "da_monitor_id" return "da_monitor" =20 - def fill_tracepoint_args_skel(self, tp_type): + def fill_tracepoint_args_skel(self, tp_type: str) -> str: buff =3D [] tp_args_event =3D [ ("char *", "state"), --=20 2.50.1 From nobody Sat Oct 4 15:57:18 2025 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 300FB23815C for ; Thu, 14 Aug 2025 15:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184145; cv=none; b=fB/6A4+4KoRlXlqdx3BUn8AzwWi+bvXE1WGZpIxipvxR3AOaAV2SJgnvTSn5A/tWDFr7NWRittYCYrVwFCVyq+eoR2xTKFWI42PAZkf54eyFNPPFTLXofK0IcolLJlZb1odGbVVvK/4zv4fOvQiBc8Xyp/q9wGct0rjzLJOVJ3I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184145; c=relaxed/simple; bh=WAxcjs4mlkMLH+eYsjSq2oW557Bf70HFosY1YxOzAw0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=niwXRgiJYYMzN/F73axjCV/pgl7iEj5R/2f1GoDpSEu04yHxkD9TLZgOpFoWQo6Yw59UXgLQ9b5MBz9rpU21Gd/H6UTkKMMaj+Ekcx4Rv4f14mR9h7ykwKm/vrvaEJvZvMrYI5DAWNAiLKUPf2Jmg7gTMuWpjAqQdwYrp1DThSA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=Ayft3NHO; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="Ayft3NHO" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1755184143; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=fLLP4VphC+IL2ZyK3aT8DJKm5lQs3XZabYHcpadQkr0=; b=Ayft3NHO2wE/srETAoQjvD4iD08GtzlZJUQB/ULXzU/PMm1pDuZAfxO7uaEyw37dsdo8h6 QuDV3AO/qd+P9xgIb98N23bvuIRkgR+j1P76h9Gns/WRZsv9PdhmytLnyGPY/vAraq74vF wn5epNTbWBXWYcflt0C2+SEp9bJ/oI4= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-515-JJKyOl7vNm2Q1lKPq051Hg-1; Thu, 14 Aug 2025 11:09:00 -0400 X-MC-Unique: JJKyOl7vNm2Q1lKPq051Hg-1 X-Mimecast-MFC-AGG-ID: JJKyOl7vNm2Q1lKPq051Hg_1755184140 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id CF89E19560AD; Thu, 14 Aug 2025 15:08:59 +0000 (UTC) Received: from gmonaco-thinkpadt14gen3.rmtit.com (unknown [10.44.32.52]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id E01841800280; Thu, 14 Aug 2025 15:08:55 +0000 (UTC) From: Gabriele Monaco To: linux-kernel@vger.kernel.org, Steven Rostedt , linux-trace-kernel@vger.kernel.org Cc: Gabriele Monaco , Nam Cao , Tomas Glozar , Juri Lelli , Clark Williams , John Kacur Subject: [RFC PATCH 06/17] verification/dot2c: Remove __buff_to_string() and cleanup Date: Thu, 14 Aug 2025 17:07:58 +0200 Message-ID: <20250814150809.140739-7-gmonaco@redhat.com> In-Reply-To: <20250814150809.140739-1-gmonaco@redhat.com> References: <20250814150809.140739-1-gmonaco@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 Content-Type: text/plain; charset="utf-8" str.join() can do what __buff_to_string() does. Therefore replace __buff_to_string() to make the scripts more pythonic. Also clean and remove some intermediate functions. Signed-off-by: Gabriele Monaco Reviewed-by: Nam Cao --- tools/verification/rvgen/rvgen/dot2c.py | 35 +++++-------------------- 1 file changed, 6 insertions(+), 29 deletions(-) diff --git a/tools/verification/rvgen/rvgen/dot2c.py b/tools/verification/r= vgen/rvgen/dot2c.py index e92567045c9c..1771a2c9f6b9 100644 --- a/tools/verification/rvgen/rvgen/dot2c.py +++ b/tools/verification/rvgen/rvgen/dot2c.py @@ -26,15 +26,6 @@ class Dot2c(Automata): super().__init__(file_path, model_name) self.line_length =3D 100 =20 - def __buff_to_string(self, buff): - string =3D "" - - for line in buff: - string =3D string + line + "\n" - - # cut off the last \n - return string[:-1] - def __get_enum_states_content(self): buff =3D [] buff.append("\t%s%s =3D 0," % (self.initial_state, self.enum_suffi= x)) @@ -45,14 +36,10 @@ class Dot2c(Automata): =20 return buff =20 - def get_enum_states_string(self): - buff =3D self.__get_enum_states_content() - return self.__buff_to_string(buff) - def format_states_enum(self): buff =3D [] buff.append("enum %s {" % self.enum_states_def) - buff.append(self.get_enum_states_string()) + buff +=3D self.__get_enum_states_content() buff.append("};\n") =20 return buff @@ -71,14 +58,10 @@ class Dot2c(Automata): =20 return buff =20 - def get_enum_events_string(self): - buff =3D self.__get_enum_events_content() - return self.__buff_to_string(buff) - def format_events_enum(self): buff =3D [] buff.append("enum %s {" % self.enum_events_def) - buff.append(self.get_enum_events_string()) + buff +=3D self.__get_enum_events_content() buff.append("};\n") =20 return buff @@ -127,23 +110,17 @@ class Dot2c(Automata): =20 return string =20 - def get_aut_init_events_string(self): - return self.__get_string_vector_per_line_content(self.events) - - def get_aut_init_states_string(self): - return self.__get_string_vector_per_line_content(self.states) - def format_aut_init_events_string(self): buff =3D [] buff.append("\t.event_names =3D {") - buff.append(self.get_aut_init_events_string()) + buff.append(self.__get_string_vector_per_line_content(self.events)) buff.append("\t},") return buff =20 def format_aut_init_states_string(self) -> list[str]: buff =3D [] buff.append("\t.state_names =3D {") - buff.append(self.get_aut_init_states_string()) + buff.append(self.__get_string_vector_per_line_content(self.states)) buff.append("\t},") =20 return buff @@ -178,7 +155,7 @@ class Dot2c(Automata): line +=3D "\n\t\t}," if linetoolong else " }," buff.append(line) =20 - return self.__buff_to_string(buff) + return '\n'.join(buff) =20 def format_aut_init_function(self) -> list[str]: buff =3D [] @@ -253,4 +230,4 @@ class Dot2c(Automata): =20 def print_model_classic(self): buff =3D self.format_model() - print(self.__buff_to_string(buff)) + print('\n'.join(buff)) --=20 2.50.1 From nobody Sat Oct 4 15:57:18 2025 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0D93321B9D6 for ; Thu, 14 Aug 2025 15:09:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184153; cv=none; b=mbeEtPxa2tkvP2ee/HfK4z/vVqzDgWFs2Z1X/RI8JZgYfMOaluCbemfPD5IRnOCFrzsUkPuqvM6eoMZzegvIuQQ1wf4Ov/OIsb7aAb2HZHxik621FyoL2E1dTOlb4OXoUa+ZiyiiM/bjRlsLL7LBgwUiV85QNLEjkQ772A7/5Dk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184153; c=relaxed/simple; bh=PDHixAkCz2hUGn/9QvUEliZqbUZBqmNiqr4tcOzsd9o=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=HJ3E462Ifr1zr6DJbCJdS8KJBG2ESbngl+szSRBEzLKPieStJfS/MHWckAXjNeE3MVK2k2p1XbWCufhoE3KZ7/nPWmJE2OehGUDqz/AFZtsZBPoPTdUmMNaFGnDKj4opbNpu+iZAmZniN1TutC3+AcRm6lrlhFXCIQhrNzHp+2Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=Pffvzpt6; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="Pffvzpt6" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1755184150; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=SgY1l2hyeaoTtIgLtZ3jRMog3VIP/D81DWJyXRv2tfE=; b=Pffvzpt6iGgRDEuMzXkkJcuwzQ8V0hCsEKcOaHDOshiMHuYiLccMu4rbQMfzZJ885mckQc Qfsqdt5ibpeTW/0hO3SH1GwxNbeepcjh+szPTotxYFOoWxzWOKlRVtPLZB1/3DOtyOcetg p00Y7MieEYXsntS5lOgYBDv4ypp6y3o= Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-81-b7gB0mZxOAWE-kngaOnexQ-1; Thu, 14 Aug 2025 11:09:06 -0400 X-MC-Unique: b7gB0mZxOAWE-kngaOnexQ-1 X-Mimecast-MFC-AGG-ID: b7gB0mZxOAWE-kngaOnexQ_1755184145 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 718EF18002CD; Thu, 14 Aug 2025 15:09:05 +0000 (UTC) Received: from gmonaco-thinkpadt14gen3.rmtit.com (unknown [10.44.32.52]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 229361800280; Thu, 14 Aug 2025 15:09:00 +0000 (UTC) From: Gabriele Monaco To: linux-kernel@vger.kernel.org, Steven Rostedt , Masami Hiramatsu , linux-trace-kernel@vger.kernel.org Cc: Gabriele Monaco , Nam Cao , Tomas Glozar , Juri Lelli , Clark Williams , John Kacur Subject: [RFC PATCH 07/17] verification/rvgen: Remove unused variable declaration from containers Date: Thu, 14 Aug 2025 17:07:59 +0200 Message-ID: <20250814150809.140739-8-gmonaco@redhat.com> In-Reply-To: <20250814150809.140739-1-gmonaco@redhat.com> References: <20250814150809.140739-1-gmonaco@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 Content-Type: text/plain; charset="utf-8" The monitor container source files contained a declaration and a definition for the rv_monitor variable. The former is superfluous and can be removed. Remove the variable declaration from the template as well as the existing monitor containers. Signed-off-by: Gabriele Monaco Reviewed-by: Nam Cao --- kernel/trace/rv/monitors/rtapp/rtapp.c | 2 -- kernel/trace/rv/monitors/sched/sched.c | 2 -- tools/verification/rvgen/rvgen/templates/container/main.c | 2 -- 3 files changed, 6 deletions(-) diff --git a/kernel/trace/rv/monitors/rtapp/rtapp.c b/kernel/trace/rv/monit= ors/rtapp/rtapp.c index fd75fc927d65..17f271231c99 100644 --- a/kernel/trace/rv/monitors/rtapp/rtapp.c +++ b/kernel/trace/rv/monitors/rtapp/rtapp.c @@ -8,8 +8,6 @@ =20 #include "rtapp.h" =20 -struct rv_monitor rv_rtapp; - struct rv_monitor rv_rtapp =3D { .name =3D "rtapp", .description =3D "Collection of monitors for detecting problems with real= -time applications", diff --git a/kernel/trace/rv/monitors/sched/sched.c b/kernel/trace/rv/monit= ors/sched/sched.c index d04db4b543f9..dd9d96fc6e21 100644 --- a/kernel/trace/rv/monitors/sched/sched.c +++ b/kernel/trace/rv/monitors/sched/sched.c @@ -8,8 +8,6 @@ =20 #include "sched.h" =20 -struct rv_monitor rv_sched; - struct rv_monitor rv_sched =3D { .name =3D "sched", .description =3D "container for several scheduler monitor specifications.= ", diff --git a/tools/verification/rvgen/rvgen/templates/container/main.c b/to= ols/verification/rvgen/rvgen/templates/container/main.c index 7d9b2f95c7e9..5fc89b46f279 100644 --- a/tools/verification/rvgen/rvgen/templates/container/main.c +++ b/tools/verification/rvgen/rvgen/templates/container/main.c @@ -8,8 +8,6 @@ =20 #include "%%MODEL_NAME%%.h" =20 -struct rv_monitor rv_%%MODEL_NAME%%; - struct rv_monitor rv_%%MODEL_NAME%% =3D { .name =3D "%%MODEL_NAME%%", .description =3D "%%DESCRIPTION%%", --=20 2.50.1 From nobody Sat Oct 4 15:57:18 2025 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 90C2723BF9B for ; Thu, 14 Aug 2025 15:09:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184157; cv=none; b=MvhHtkcVKK+GsPegreG6Z4iokEOHUPlur2pCdtVevmRP4bA1BN73UK3UGpHSC7nHsSEBluoa7D/gzt2RgnBcyzc01a/1B04cH0dtsYgFpVIak/29Q4gxZy36ltLq+9xUxkQybduRUPCpk9EqGfwMHWic5cvJEcGBk/BB/jhCCBU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184157; c=relaxed/simple; bh=+dveDZ+/QQnzwjDZ/ZZc+P+avH8tswQA4hF8LIZqgG4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=PuP4VVzlEZc7gFi5N/gCyTCnxusqHBEzHTO/3DL5dCSfLvmmCmZMw3Nu2sMtuoDszMl8Ojjg8OMTSsJDZocWbs/q3AbIOo6bJu8R6K4eWx5O6f3ZGhqKN2QujMVfb6L6smkrf2JanA34Vc4M+wQNJdUlyEnf2Tb+HK0IP1LyZXs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=Pwzd2iqL; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="Pwzd2iqL" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1755184154; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=sl6ud9f0tCPqGqOO8OxXwzKz428gGR/oiYCMShDgB5Y=; b=Pwzd2iqL64b7vpLIwVG1a1ZA/fD6JZtjRjtdCtXO9/imJz8ocnI6nvaZTE3+YA1MS2vv7X fslHMPHt4g/p9KqMcDxfGyPMBbNBNk3ligT/U9nNkAVzsU3gMl7uoVxAyR4dylFg1e8Zky FAPH3xVulNXE0PxlN4whNS8X5wmdEzQ= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-306-ZMo_Bs_-MKOx_j3zCWp_BA-1; Thu, 14 Aug 2025 11:09:13 -0400 X-MC-Unique: ZMo_Bs_-MKOx_j3zCWp_BA-1 X-Mimecast-MFC-AGG-ID: ZMo_Bs_-MKOx_j3zCWp_BA_1755184151 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id B1EBE19560A2; Thu, 14 Aug 2025 15:09:11 +0000 (UTC) Received: from gmonaco-thinkpadt14gen3.rmtit.com (unknown [10.44.32.52]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 13B13180029A; Thu, 14 Aug 2025 15:09:06 +0000 (UTC) From: Gabriele Monaco To: linux-kernel@vger.kernel.org, Steven Rostedt , Masami Hiramatsu , linux-trace-kernel@vger.kernel.org Cc: Gabriele Monaco , Nam Cao , Tomas Glozar , Juri Lelli , Clark Williams , John Kacur Subject: [RFC PATCH 08/17] rv: Add Hybrid Automata monitor type Date: Thu, 14 Aug 2025 17:08:00 +0200 Message-ID: <20250814150809.140739-9-gmonaco@redhat.com> In-Reply-To: <20250814150809.140739-1-gmonaco@redhat.com> References: <20250814150809.140739-1-gmonaco@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 Content-Type: text/plain; charset="utf-8" Deterministic automata define which events are allowed in every state, but cannot define more sophisticated constraint taking into account the system's environment (e.g. time or other states not producing events). Add the Hybrid Automata monitor type as an extension of Deterministic automata where each state transition is validating a constraint on a finite number of environment variables. Hybrid automata can be used to implement timed automata, where the environment variables are clocks. Also implement the necessary functionality to handle clock constraints (ns or jiffy granularity) on state and events. Signed-off-by: Gabriele Monaco --- include/linux/rv.h | 26 +++ include/rv/da_monitor.h | 45 ++++- include/rv/ha_monitor.h | 384 +++++++++++++++++++++++++++++++++++++ kernel/trace/rv/Kconfig | 13 ++ kernel/trace/rv/rv_trace.h | 63 ++++++ 5 files changed, 526 insertions(+), 5 deletions(-) create mode 100644 include/rv/ha_monitor.h diff --git a/include/linux/rv.h b/include/linux/rv.h index 3134681553b4..6a7594080db1 100644 --- a/include/linux/rv.h +++ b/include/linux/rv.h @@ -83,11 +83,37 @@ struct ltl_monitor {}; =20 #endif /* CONFIG_RV_LTL_MONITOR */ =20 +#ifdef CONFIG_RV_HA_MONITOR +/* + * In the future, hybrid automata may rely on multiple + * environment variables, e.g. different clocks started at + * different times or running at different speed. + * For now we support only 1 variable. + */ +#define MAX_HA_ENV_LEN 1 + +/* + * Hybrid automaton per-object variables. + */ +struct ha_monitor { + struct da_monitor da_mon; + u64 env_store[MAX_HA_ENV_LEN]; + struct hrtimer timer; +}; +#define to_ha_monitor(da) container_of(da, struct ha_monitor, da_mon) + +#else + +struct ha_monitor { }; + +#endif /* CONFIG_RV_HA_MONITOR */ + #define RV_PER_TASK_MONITOR_INIT (CONFIG_RV_PER_TASK_MONITORS) =20 union rv_task_monitor { struct da_monitor da_mon; struct ltl_monitor ltl_mon; + struct ha_monitor ha_mon; }; =20 #ifdef CONFIG_RV_REACTORS diff --git a/include/rv/da_monitor.h b/include/rv/da_monitor.h index 57a6e742d454..d95ece5b7908 100644 --- a/include/rv/da_monitor.h +++ b/include/rv/da_monitor.h @@ -24,6 +24,33 @@ #define RV_DA_MON_NAME CONCATENATE(da_mon_, MONITOR_NAME) static struct rv_monitor RV_MONITOR_NAME; =20 +/* + * Hook to allow the implementation of hybrid automata: define it with a + * function that takes curr_state, event and next_state and returns true i= f 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. res= et + * all clocks). + */ +#ifndef da_monitor_reset_hook +#define da_monitor_reset_hook(da_mon) +#endif + #ifdef CONFIG_RV_REACTORS =20 static void cond_react(enum states curr_state, enum events event) @@ -51,6 +78,7 @@ static inline void da_monitor_reset(struct da_monitor *da= _mon) { da_mon->monitoring =3D 0; da_mon->curr_state =3D model_get_initial_state(); + da_monitor_reset_hook(da_mon); } =20 /* @@ -63,6 +91,7 @@ static inline void da_monitor_start(struct da_monitor *da= _mon) { da_mon->curr_state =3D model_get_initial_state(); da_mon->monitoring =3D 1; + da_monitor_init_hook(da_mon); } =20 /* @@ -130,6 +159,9 @@ static inline bool da_event(struct da_monitor *da_mon, = enum events event) return false; } if (likely(try_cmpxchg(&da_mon->curr_state, &curr_state, next_state))) { + if (!da_monitor_event_hook(curr_state, event, next_state)) + return false; + CONCATENATE(trace_event_, MONITOR_NAME)( model_get_state_name(curr_state), model_get_event_name(event), @@ -171,6 +203,9 @@ static inline bool da_event(struct da_monitor *da_mon, = struct task_struct *tsk, return false; } if (likely(try_cmpxchg(&da_mon->curr_state, &curr_state, next_state))) { + if (!da_monitor_event_hook(tsk, curr_state, event, next_state)) + return false; + CONCATENATE(trace_event_, MONITOR_NAME)(tsk->pid, model_get_state_name(curr_state), model_get_event_name(event), @@ -196,14 +231,14 @@ static inline bool da_event(struct da_monitor *da_mon= , struct task_struct *tsk, /* * global monitor (a single variable) */ -static struct da_monitor RV_DA_MON_NAME; +static union rv_task_monitor RV_DA_MON_NAME; =20 /* * da_get_monitor - return the global monitor address */ static struct da_monitor *da_get_monitor(void) { - return &RV_DA_MON_NAME; + return &(RV_DA_MON_NAME.da_mon); } =20 /* @@ -239,14 +274,14 @@ static inline void da_monitor_destroy(void) /* * per-cpu monitor variables */ -static DEFINE_PER_CPU(struct da_monitor, RV_DA_MON_NAME); +static DEFINE_PER_CPU(union rv_task_monitor, RV_DA_MON_NAME); =20 /* * da_get_monitor - return current CPU monitor address */ static struct da_monitor *da_get_monitor(void) { - return this_cpu_ptr(&RV_DA_MON_NAME); + return &this_cpu_ptr(&RV_DA_MON_NAME)->da_mon; } =20 /* @@ -257,7 +292,7 @@ static void da_monitor_reset_all(void) struct da_monitor *da_mon; int cpu; for_each_cpu(cpu, cpu_online_mask) { - da_mon =3D per_cpu_ptr(&RV_DA_MON_NAME, cpu); + da_mon =3D &per_cpu_ptr(&RV_DA_MON_NAME, cpu)->da_mon; da_monitor_reset(da_mon); } } diff --git a/include/rv/ha_monitor.h b/include/rv/ha_monitor.h new file mode 100644 index 000000000000..a7c7297a9759 --- /dev/null +++ b/include/rv/ha_monitor.h @@ -0,0 +1,384 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2025-2028 Red Hat, Inc. Gabriele Monaco + * + * Hybrid automata (HA) monitor functions, to be used together + * with automata models in C generated by the dot2k tool. + * + * This type of monitors extends the Deterministic automata (DA) class by + * adding a set of environment variables (e.g. clocks) that can be used to + * constraint the valid transitions. + * + * The dot2k tool is available at tools/verification/dot2k/ + * + * For further information, see: + * Documentation/trace/rv/ha_monitor_synthesis.rst + */ + +#ifndef _RV_HA_MONITOR_H +#define _RV_HA_MONITOR_H + +#include + +#if RV_MON_TYPE =3D=3D RV_MON_GLOBAL || RV_MON_TYPE =3D=3D RV_MON_PER_CPU +static bool ha_monitor_handle_constraint(enum states curr_state, enum even= ts event, enum states next_state); +#elif RV_MON_TYPE =3D=3D RV_MON_PER_TASK +static bool ha_monitor_handle_constraint(struct task_struct *tsk, enum sta= tes curr_state, enum events event, enum states next_state); +#endif +static inline void ha_monitor_init_env(struct da_monitor *da_mon); +static inline void ha_monitor_reset_env(struct da_monitor *da_mon); +static inline bool ha_cancel_timer(struct ha_monitor *ha_mon); +#define da_monitor_event_hook ha_monitor_handle_constraint +#define da_monitor_init_hook ha_monitor_init_env +#define da_monitor_reset_hook ha_monitor_reset_env + +#include +#include + +#define ENV_MAX CONCATENATE(env_max_, MONITOR_NAME) +#define ENV_MAX_STORED CONCATENATE(env_max_stored_, MONITOR_NAME) +#define envs CONCATENATE(envs_, MONITOR_NAME) + +/* Environment storage before being reset */ +#define ENV_INVALID_VALUE U64_MAX +/* Error with no event occurs only on timeouts */ +#define EVENT_NONE EVENT_MAX +#define EVENT_NONE_LBL "none" + +#ifdef CONFIG_RV_REACTORS + +/* + * Trigger the reaction after a failed environment constraint (e.g. relate= d to a + * clock). + */ +static void ha_cond_react(enum states curr_state, enum events event, char = *env) +{ + if (!rv_reacting_on() || !RV_MONITOR_NAME.react) + return; + RV_MONITOR_NAME.react("rv: monitor %s does not allow event %s on state %s= with env %s\n", + __stringify(MONITOR_NAME), + event =3D=3D EVENT_NONE ? EVENT_NONE_LBL : model_get_event_name(event), + model_get_state_name(curr_state), + env); +} + +#else /* CONFIG_RV_REACTOR */ + +static void ha_cond_react(enum states curr_state, enum events event, char = *env) +{ + return; +} +#endif + +/* + * model_get_state_name - return the (string) name of the given state + */ +static char *model_get_env_name(enum envs env) +{ + if ((env < 0) || (env >=3D ENV_MAX)) + return "INVALID"; + + return RV_AUTOMATON_NAME.env_names[env]; +} +static enum hrtimer_restart ha_monitor_timer_callback(struct hrtimer *time= r); + +/* Should be supplied by the monitor */ +static u64 ha_get_env(struct ha_monitor *ha_mon, enum envs env); +static bool ha_verify_constraint(struct ha_monitor *ha_mon, + enum states curr_state, + enum events event, + enum states next_state); + +/* + * ha_monitor_reset_all_stored - reset all environment variables in the mo= nitor + */ +static inline void ha_monitor_reset_all_stored(struct ha_monitor *ha_mon) +{ + for (int i =3D 0; i < ENV_MAX_STORED; i++) + smp_store_mb(ha_mon->env_store[i], ENV_INVALID_VALUE); +} + +/* + * ha_monitor_init_env - setup timer and reset all environment + * + * Called from a hook in the DA start functions, it supplies the da_mon + * corresponding to the current ha_mon. + * Not all hybrid automata require the timer, still set it for simplicity. + */ +static inline void ha_monitor_init_env(struct da_monitor *da_mon) +{ + struct ha_monitor *ha_mon =3D to_ha_monitor(da_mon); + + ha_monitor_reset_all_stored(ha_mon); + if (unlikely(!ha_mon->timer.base)) + hrtimer_setup(&ha_mon->timer, ha_monitor_timer_callback, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); +} + +/* + * ha_monitor_reset_env - stop timer and reset all environment + * + * Called from a hook in the DA reset functions, it supplies the da_mon + * corresponding to the current ha_mon. + * Not all hybrid automata require the timer, still clear it for simplicit= y. + */ +static inline void ha_monitor_reset_env(struct da_monitor *da_mon) +{ + struct ha_monitor *ha_mon =3D to_ha_monitor(da_mon); + + ha_monitor_reset_all_stored(ha_mon); + /* Initialisation resets the monitor before initialising the timer */ + if (likely(ha_mon->timer.base)) + ha_cancel_timer(ha_mon); +} + +/* + * ha_monitor_env_invalid - return true if env has not been initialised + */ +static inline bool ha_monitor_env_invalid(struct ha_monitor *ha_mon, enum = envs env) +{ + return READ_ONCE(ha_mon->env_store[env]) =3D=3D ENV_INVALID_VALUE; +} + +/* + * ha_monitor_all_env_is_valid - return true if all env has been initialis= ed + */ +static inline bool ha_monitor_all_env_is_valid(struct ha_monitor *ha_mon) +{ + for (int i =3D 0; i < ENV_MAX_STORED; i++) { + if (ha_monitor_env_invalid(ha_mon, i)) + return false; + } + return true; +} + +static inline void ha_get_env_string(struct seq_buf * s, struct ha_monitor= *ha_mon) +{ + const char *format_str =3D "%s=3D%llu"; + + for (int i =3D 0; i < ENV_MAX; i++) { + seq_buf_printf(s, format_str, model_get_env_name(i), + ha_get_env(ha_mon, i)); + format_str =3D ",%s=3D%llu"; + } +} + +#if RV_MON_TYPE =3D=3D RV_MON_GLOBAL || RV_MON_TYPE =3D=3D RV_MON_PER_CPU + +/* + * ha_get_monitor - return the global or per-cpu monitor address + */ +static inline struct ha_monitor *ha_get_monitor(void) +{ + return to_ha_monitor(da_get_monitor()); +} + +/* + * ha_monitor_handle_constraint - handle the constraint on the current tra= nsition + * + * If the monitor implementation defines a constraint in the transition fr= om + * curr_state to event, react and trace appropriately as well as return fa= lse. + * This function is called from the hook in the DA event handle function a= nd + * triggers a failure in the monitor. + */ +static bool ha_monitor_handle_constraint(enum states curr_state, + enum events event, + enum states next_state) +{ + struct ha_monitor *ha_mon =3D ha_get_monitor(); + DECLARE_SEQ_BUF(env_string, MAX_DA_NAME_LEN); + bool env_was_valid; + + env_was_valid =3D ha_monitor_all_env_is_valid(ha_mon); + + if (ha_verify_constraint(ha_mon, curr_state, event, next_state)) + return true; + + /* + * If we started with an uninitialised environment, chances are the + * failure is a false positive. Do not fail the monitor just this time. + * Note that we need to run ha_verify_constraint first as it might + * initialise the environment. + */ + if (!env_was_valid) + return true; + + ha_get_env_string(&env_string, ha_mon); + ha_cond_react(curr_state, event, env_string.buffer); + CONCATENATE(trace_error_env_, MONITOR_NAME)( + model_get_state_name(curr_state), + model_get_event_name(event), + env_string.buffer); + return false; +} + +static enum hrtimer_restart ha_monitor_timer_callback(struct hrtimer *time= r) +{ + struct ha_monitor *ha_mon =3D container_of(timer, struct ha_monitor, time= r); + enum states curr_state =3D READ_ONCE(ha_mon->da_mon.curr_state); + DECLARE_SEQ_BUF(env_string, MAX_DA_NAME_LEN); + + ha_get_env_string(&env_string, ha_mon); + ha_cond_react(curr_state, EVENT_NONE, env_string.buffer); + CONCATENATE(trace_error_env_, MONITOR_NAME)( + model_get_state_name(curr_state), + EVENT_NONE_LBL, + env_string.buffer); + + da_monitor_reset(&ha_mon->da_mon); + + return HRTIMER_NORESTART; +} + +#elif RV_MON_TYPE =3D=3D RV_MON_PER_TASK + +/* + * ha_get_monitor - return the per-task monitor address + */ +static inline struct ha_monitor *ha_get_monitor(struct task_struct *tsk) +{ + return to_ha_monitor(da_get_monitor(tsk)); +} + +/* + * ha_monitor_handle_constraint - handle the constraint on the current tra= nsition + * + * If the monitor implementation defines a constraint in the transition fr= om + * curr_state to event, react and trace appropriately as well as return fa= lse. + * This function is called from the hook in the DA event handle function a= nd + * triggers a failure in the monitor. + */ +static bool ha_monitor_handle_constraint(struct task_struct *tsk, + enum states curr_state, + enum events event, + enum states next_state) +{ + struct ha_monitor *ha_mon =3D ha_get_monitor(tsk); + DECLARE_SEQ_BUF(env_string, 32); + bool env_was_valid; + + env_was_valid =3D ha_monitor_all_env_is_valid(ha_mon); + + if (ha_verify_constraint(ha_mon, curr_state, event, next_state)) + return true; + + /* + * If we started with an uninitialised environment, chances are the + * failure is a false positive. Do not fail the monitor just this time. + * Note that we need to run ha_verify_constraint first as it might + * initialise the environment. + */ + if (!env_was_valid) + return true; + + ha_get_env_string(&env_string, ha_mon); + ha_cond_react(curr_state, event, env_string.buffer); + CONCATENATE(trace_error_env_, MONITOR_NAME)(tsk->pid, + model_get_state_name(curr_state), + model_get_event_name(event), + env_string.buffer); + return false; +} + +static enum hrtimer_restart ha_monitor_timer_callback(struct hrtimer *time= r) +{ + struct ha_monitor *ha_mon =3D container_of(timer, struct ha_monitor, time= r); + enum states curr_state =3D READ_ONCE(ha_mon->da_mon.curr_state); + DECLARE_SEQ_BUF(env_string, MAX_DA_NAME_LEN); + struct task_struct *tsk; + + tsk =3D container_of(ha_mon, struct task_struct, rv[task_mon_slot].ha_mon= ); + ha_get_env_string(&env_string, ha_mon); + ha_cond_react(curr_state, EVENT_NONE, env_string.buffer); + CONCATENATE(trace_error_env_, MONITOR_NAME)(tsk->pid, + model_get_state_name(curr_state), + EVENT_NONE_LBL, + env_string.buffer); + + da_monitor_reset(&ha_mon->da_mon); + + return HRTIMER_NORESTART; +} + +#endif /* RV_MON_TYPE */ + +/* + * Helper functions for env variables describing clocks with ns granularity + */ +static inline u64 ha_get_clk_ns(struct ha_monitor *ha_mon, enum envs env) +{ + return ktime_get_ns() - READ_ONCE(ha_mon->env_store[env]); +} +static inline void ha_reset_clk_ns(struct ha_monitor *ha_mon, enum envs en= v) +{ + smp_store_mb(ha_mon->env_store[env], ktime_get_ns()); +} + +/* + * Helper functions for env variables describing clocks with jiffy granula= rity + */ +static inline u64 ha_get_clk_jiffy(struct ha_monitor *ha_mon, enum envs en= v) +{ + return jiffies - READ_ONCE(ha_mon->env_store[env]); +} +static inline void ha_reset_clk_jiffy(struct ha_monitor *ha_mon, enum envs= env) +{ + smp_store_mb(ha_mon->env_store[env], jiffies); +} + +/* + * Helper functions to handle the monitor timer. + * Not all monitors require a timer, in such case the timer will be set up= but + * never armed. + * Timers start since the last reset of the supplied env or from now if en= v is + * not an environment variable. If env was not initialised no timer starts. + * Timers can expire on any CPU unless the monitor is per-cpu, + * where we assume every event occurs on the local CPU. + */ +static inline void ha_start_timer_ns(struct ha_monitor *ha_mon, enum envs = env, + u64 expire) +{ + int mode =3D HRTIMER_MODE_REL; + u64 passed =3D 0; + + if (env >=3D 0 && env < ENV_MAX_STORED) { + if (ha_monitor_env_invalid(ha_mon, env)) + return; + passed =3D ha_get_env(ha_mon, env); + } + if (RV_MON_TYPE =3D=3D RV_MON_PER_CPU) + mode |=3D HRTIMER_MODE_PINNED; + hrtimer_start(&ha_mon->timer, ns_to_ktime(expire - passed), mode); +} +static inline void ha_start_timer_jiffy(struct ha_monitor *ha_mon, + enum envs env, u64 expire) +{ + u64 passed =3D 0; + + if (env >=3D 0 && env < ENV_MAX_STORED) { + if (ha_monitor_env_invalid(ha_mon, env)) + return; + passed =3D ha_get_env(ha_mon, env); + } + ha_start_timer_ns(ha_mon, ENV_MAX_STORED, + jiffies_to_nsecs(expire - passed)); +} +/* + * ha_cancel_timer - Cancel the timer and return whether it expired + * + * Return true if the timer was cancelled after expiration but before the + * callback could run. + */ +static inline bool ha_cancel_timer(struct ha_monitor *ha_mon) +{ + ktime_t remaining; + + if (!hrtimer_active(&ha_mon->timer)) + return false; + remaining =3D hrtimer_get_remaining(&ha_mon->timer); + if (hrtimer_try_to_cancel(&ha_mon->timer) =3D=3D 1 && remaining < 0) + return true; + return false; +} + +#endif diff --git a/kernel/trace/rv/Kconfig b/kernel/trace/rv/Kconfig index 5b4be87ba59d..4ad392dfc57f 100644 --- a/kernel/trace/rv/Kconfig +++ b/kernel/trace/rv/Kconfig @@ -23,6 +23,19 @@ config LTL_MON_EVENTS_ID config RV_LTL_MONITOR bool =20 +config RV_HA_MONITOR + bool + +config HA_MON_EVENTS_IMPLICIT + select DA_MON_EVENTS_IMPLICIT + select RV_HA_MONITOR + bool + +config HA_MON_EVENTS_ID + select DA_MON_EVENTS_ID + select RV_HA_MONITOR + bool + menuconfig RV bool "Runtime Verification" select TRACING diff --git a/kernel/trace/rv/rv_trace.h b/kernel/trace/rv/rv_trace.h index 4a6faddac614..dbb0cbbe15ca 100644 --- a/kernel/trace/rv/rv_trace.h +++ b/kernel/trace/rv/rv_trace.h @@ -65,6 +65,36 @@ DECLARE_EVENT_CLASS(error_da_monitor, #include // Add new monitors based on CONFIG_DA_MON_EVENTS_IMPLICIT here =20 +#ifdef CONFIG_HA_MON_EVENTS_IMPLICIT +/* For simplicity this class is marked as DA although relevant only for HA= */ +DECLARE_EVENT_CLASS(error_env_da_monitor, + + TP_PROTO(char *state, char *event, char *env), + + TP_ARGS(state, event, env), + + TP_STRUCT__entry( + __array( char, state, MAX_DA_NAME_LEN ) + __array( char, event, MAX_DA_NAME_LEN ) + __array( char, env, MAX_DA_NAME_LEN ) + ), + + TP_fast_assign( + memcpy(__entry->state, state, MAX_DA_NAME_LEN); + memcpy(__entry->event, event, MAX_DA_NAME_LEN); + memcpy(__entry->env, env, MAX_DA_NAME_LEN); + ), + + TP_printk("event %s not expected in the state %s with env %s\n", + __entry->event, + __entry->state, + __entry->env) +); + +// Add new monitors based on CONFIG_HA_MON_EVENTS_IMPLICIT here + +#endif + #endif /* CONFIG_DA_MON_EVENTS_IMPLICIT */ =20 #ifdef CONFIG_DA_MON_EVENTS_ID @@ -128,6 +158,39 @@ DECLARE_EVENT_CLASS(error_da_monitor_id, #include // Add new monitors based on CONFIG_DA_MON_EVENTS_ID here =20 +#ifdef CONFIG_HA_MON_EVENTS_ID +/* For simplicity this class is marked as DA although relevant only for HA= */ +DECLARE_EVENT_CLASS(error_env_da_monitor_id, + + TP_PROTO(int id, char *state, char *event, char *env), + + TP_ARGS(id, state, event, env), + + TP_STRUCT__entry( + __field( int, id ) + __array( char, state, MAX_DA_NAME_LEN ) + __array( char, event, MAX_DA_NAME_LEN ) + __array( char, env, MAX_DA_NAME_LEN ) + ), + + TP_fast_assign( + memcpy(__entry->state, state, MAX_DA_NAME_LEN); + memcpy(__entry->event, event, MAX_DA_NAME_LEN); + memcpy(__entry->env, env, MAX_DA_NAME_LEN); + __entry->id =3D id; + ), + + TP_printk("%d: event %s not expected in the state %s with env %s\n", + __entry->id, + __entry->event, + __entry->state, + __entry->env) +); + +// Add new monitors based on CONFIG_HA_MON_EVENTS_ID here + +#endif + #endif /* CONFIG_DA_MON_EVENTS_ID */ #ifdef CONFIG_LTL_MON_EVENTS_ID DECLARE_EVENT_CLASS(event_ltl_monitor_id, --=20 2.50.1 From nobody Sat Oct 4 15:57:18 2025 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 43F1A23FC49 for ; Thu, 14 Aug 2025 15:09:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184164; cv=none; b=ZmLYVooCLQZj2aGHsTj5yilundTkrErBVdJULt/oAQIYqXtDyhF0aVrfmFHoMgO5nX6LIYHCQrXYaf3f1RwV2Y34YnWGoR0wF1dOYdx7caWC8T48l79hLo9p/elJoRYjJZm2+4OlrsbSkSQFfzsg5z8GDNnQiaQt85DdUq3SV2w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184164; c=relaxed/simple; bh=xXDVfNgGGP7QQqfh7zOZfxCp5lm8BioslVi+eNdEt9U=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Bfz1+em/Yp9TcIOdRvR5oK9eYmMVDIfMrBmjqARtTGufShYUAkoBGO0dsfVy6CkGFkgm8vfZi7rkr2UkEwCYPjNL0baIN3Awy7MVquYRj4aUVbBP9SuDyE6Dfb1OsksiZXknm3rtaoloUmwkjHN7yaxMb0TUi6W2eiGopIQCmLU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=Ehv3uonr; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="Ehv3uonr" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1755184162; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=rNhapDhZFCMPZA/TgP7fSi8baQpPGqcLi/advpOV3TY=; b=Ehv3uonr6l4XsV7W/CZUdWoYSxMXdWQ/M1SjwYIbPvQ3ucjLR8hL1WPWCLtBNgxDuP+Sj0 Nmm5BSS+8inx4DztdzhTMamBUmckNYYF7iFXWxGDXoN4le600spjAWK34X8Q5r+MZEHJy6 ppzAsvH4xWSNPDKMdrs6c9G3v/b2WmQ= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-67-FWwHpVozMmierZWoRaS6nA-1; Thu, 14 Aug 2025 11:09:18 -0400 X-MC-Unique: FWwHpVozMmierZWoRaS6nA-1 X-Mimecast-MFC-AGG-ID: FWwHpVozMmierZWoRaS6nA_1755184157 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id E57A419775E8; Thu, 14 Aug 2025 15:09:16 +0000 (UTC) Received: from gmonaco-thinkpadt14gen3.rmtit.com (unknown [10.44.32.52]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id C8145180047F; Thu, 14 Aug 2025 15:09:12 +0000 (UTC) From: Gabriele Monaco To: linux-kernel@vger.kernel.org, Steven Rostedt , linux-trace-kernel@vger.kernel.org Cc: Gabriele Monaco , Nam Cao , Tomas Glozar , Juri Lelli , Clark Williams , John Kacur Subject: [RFC PATCH 09/17] verification/rvgen: Allow spaces in and events strings Date: Thu, 14 Aug 2025 17:08:01 +0200 Message-ID: <20250814150809.140739-10-gmonaco@redhat.com> In-Reply-To: <20250814150809.140739-1-gmonaco@redhat.com> References: <20250814150809.140739-1-gmonaco@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 Content-Type: text/plain; charset="utf-8" Currently the automata parser assumes event strings don't have any space, this stands true for event names, but can be a wrong assumption if we want to store other information in the event strings (e.g. constraints for hybrid automata). Adapt the parser logic to allow spaces in the event strings. Signed-off-by: Gabriele Monaco --- tools/verification/rvgen/rvgen/automata.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tools/verification/rvgen/rvgen/automata.py b/tools/verificatio= n/rvgen/rvgen/automata.py index 76d7a3cfaec6..c637cf4ee749 100644 --- a/tools/verification/rvgen/rvgen/automata.py +++ b/tools/verification/rvgen/rvgen/automata.py @@ -127,14 +127,13 @@ class Automata: # ------------ event is here ------------^^^^^ if self.__dot_lines[cursor].split()[1] =3D=3D "->": line =3D self.__dot_lines[cursor].split() - event =3D line[-2].replace('"','') + event =3D "".join(line[line.index("label")+2:-1]).replace(= '"','') =20 # when a transition has more than one lables, they are lik= e this # "local_irq_enable\nhw_local_irq_enable_n" # so split them. =20 - event =3D event.replace("\\n", " ") - for i in event.split(): + for i in event.split("\\n"): events.append(i) cursor +=3D 1 =20 @@ -167,8 +166,8 @@ class Automata: line =3D self.__dot_lines[cursor].split() origin_state =3D line[0].replace('"','').replace(',','_') dest_state =3D line[2].replace('"','').replace(',','_') - possible_events =3D line[-2].replace('"','').replace("\\n"= , " ") - for event in possible_events.split(): + possible_events =3D "".join(line[line.index("label")+2:-1]= ).replace('"','') + for event in possible_events.split("\\n"): matrix[states_dict[origin_state]][events_dict[event]] = =3D dest_state cursor +=3D 1 =20 --=20 2.50.1 From nobody Sat Oct 4 15:57:18 2025 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D1EF521A443 for ; Thu, 14 Aug 2025 15:09:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184171; cv=none; b=taNaHK1NJFC8RBmxJzB0H7yY9YCUg1kcMUJ2OObhxWZDbgKCGcyd0S+3nQWxNmtonpbfQUQrUl+MLqic2mFzhQ53l8A3vAenmFbs/k1UKoV+Kbk+G9863NqjO3VUoQx7dIZCu//Zf10MjE8dHGNclhGVP9tIlJ991Zfb6vaXDZA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184171; c=relaxed/simple; bh=AHaM+GbN7jA5MWydmwupUPhF964tCV/5zk0w+7BP88M=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZGOTviP2vlTUTnqpHwybD3LnskL5JcwucXGbtSaahTyfXY2gWanu7M3iAMCBjlD5Ft4yz5u54fkydoFsRWNry/ENhLdyUf2T4gemmKONs5BoRp+Ou7hcdde84BBDvkbzitjieVz8abrYsfU7RdCh0dh+Ek8B4Ba2tkVJprr1HG4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=Kpr+MQEW; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="Kpr+MQEW" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1755184167; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Qvhs9L7pNYJYNGo454j7xHaZFOABn9WAkijZAU9p8cc=; b=Kpr+MQEWIR9vEBj/ogBFInq7K1rV9hE8KHGq0LBGRX6edqTUr7m6J6VTtwX/PO9Tx8sMII f7wBTIgLSgcxUuRomd1RgtJE52KTdACezDTX1FyxMLAPTkbcV9K+Z39egqrrEYTIj/27ZP VYtsIW/kZFGG5WUxwbVyPbLJ01eNwKA= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-240-W_WtjSZbO3SKAOmZJhxDCQ-1; Thu, 14 Aug 2025 11:09:24 -0400 X-MC-Unique: W_WtjSZbO3SKAOmZJhxDCQ-1 X-Mimecast-MFC-AGG-ID: W_WtjSZbO3SKAOmZJhxDCQ_1755184163 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 4B3B219775E2; Thu, 14 Aug 2025 15:09:23 +0000 (UTC) Received: from gmonaco-thinkpadt14gen3.rmtit.com (unknown [10.44.32.52]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id B935D1800280; Thu, 14 Aug 2025 15:09:18 +0000 (UTC) From: Gabriele Monaco To: linux-kernel@vger.kernel.org, Steven Rostedt , linux-trace-kernel@vger.kernel.org Cc: Gabriele Monaco , Nam Cao , Tomas Glozar , Juri Lelli , Clark Williams , John Kacur Subject: [RFC PATCH 10/17] verification/rvgen: Add support for Hybrid Automata Date: Thu, 14 Aug 2025 17:08:02 +0200 Message-ID: <20250814150809.140739-11-gmonaco@redhat.com> In-Reply-To: <20250814150809.140739-1-gmonaco@redhat.com> References: <20250814150809.140739-1-gmonaco@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 Content-Type: text/plain; charset="utf-8" Add the possibility to parse dot files as hybrid automata and generate the necessary code from rvgen. Hybrid automata are very similar to deterministic ones and most functionality is shared, the dot files include also constraints together with event names (separated by ;) and state names (separated by \n). The tool can now generate the appropriate code to validate constraints at runtime according to the dot specification. Signed-off-by: Gabriele Monaco --- tools/verification/rvgen/__main__.py | 8 +- tools/verification/rvgen/rvgen/automata.py | 128 +++++++- tools/verification/rvgen/rvgen/dot2c.py | 64 +++- tools/verification/rvgen/rvgen/dot2k.py | 273 +++++++++++++++++- tools/verification/rvgen/rvgen/generator.py | 2 + .../rvgen/rvgen/templates/dot2k/main.c | 2 +- .../rvgen/templates/dot2k/trace_hybrid.h | 16 + 7 files changed, 475 insertions(+), 18 deletions(-) create mode 100644 tools/verification/rvgen/rvgen/templates/dot2k/trace_hy= brid.h diff --git a/tools/verification/rvgen/__main__.py b/tools/verification/rvge= n/__main__.py index fa6fc1f4de2f..b8e07e463293 100644 --- a/tools/verification/rvgen/__main__.py +++ b/tools/verification/rvgen/__main__.py @@ -9,7 +9,7 @@ # Documentation/trace/rv/da_monitor_synthesis.rst =20 if __name__ =3D=3D '__main__': - from rvgen.dot2k import dot2k + from rvgen.dot2k import da2k, ha2k from rvgen.generator import Monitor from rvgen.container import Container from rvgen.ltl2k import ltl2k @@ -29,7 +29,7 @@ if __name__ =3D=3D '__main__': monitor_parser.add_argument("-p", "--parent", dest=3D"parent", required=3DFalse, help=3D"Create a monitor= nested to parent") monitor_parser.add_argument('-c', "--class", dest=3D"monitor_class", - help=3D"Monitor class, either \"da\" or \"= ltl\"") + help=3D"Monitor class, either \"da\", \"ha= \" or \"ltl\"") monitor_parser.add_argument('-s', "--spec", dest=3D"spec", help=3D"Mon= itor specification file") monitor_parser.add_argument('-t', "--monitor_type", dest=3D"monitor_ty= pe", help=3Df"Available options: {', '.join(Mon= itor.monitor_types.keys())}") @@ -43,7 +43,9 @@ if __name__ =3D=3D '__main__': if params.subcmd =3D=3D "monitor": print("Opening and parsing the specification file %s" % params= .spec) if params.monitor_class =3D=3D "da": - monitor =3D dot2k(params.spec, params.monitor_type, vars(p= arams)) + monitor =3D da2k(params.spec, params.monitor_type, vars(pa= rams)) + elif params.monitor_class =3D=3D "ha": + monitor =3D ha2k(params.spec, params.monitor_type, vars(pa= rams)) elif params.monitor_class =3D=3D "ltl": monitor =3D ltl2k(params.spec, params.monitor_type, vars(p= arams)) else: diff --git a/tools/verification/rvgen/rvgen/automata.py b/tools/verificatio= n/rvgen/rvgen/automata.py index c637cf4ee749..c6525b95b15c 100644 --- a/tools/verification/rvgen/rvgen/automata.py +++ b/tools/verification/rvgen/rvgen/automata.py @@ -9,24 +9,50 @@ # Documentation/trace/rv/deterministic_automata.rst =20 import ntpath +import re +from typing import Iterator =20 class Automata: """Automata class: Reads a dot file and part it as an automata. =20 + It supports both deterministic and hybrid automata. + Attributes: dot_file: A dot file with an state_automaton definition. """ =20 invalid_state_str =3D "INVALID_STATE" + # val can be numerical, uppercase (constant or macro), lowercase (para= meter or function) + # only numerical values should have units + constraint_rule =3D re.compile(r""" + ^ + (?P[a-zA-Z_][a-zA-Z0-9_]+) # C-like identifier for the env v= ar + (?P[!<=3D>]{1,2}) # operator + (?P + [0-9]+ | # numerical value + [A-Z_]+\(\) | # macro + [A-Z_]+ | # constant + [a-z_]+\(\) | # function + [a-z_]+ # parameter + ) + (?P[a-z]{1,2})? # optional unit for numerical val= ues + """, re.VERBOSE) + constraint_reset =3D re.compile(r"^reset\((?P[a-zA-Z_][a-zA-Z0-9_= ]+)\)") =20 def __init__(self, file_path, model_name=3DNone): self.__dot_path =3D file_path self.name =3D model_name or self.__get_model_name() self.__dot_lines =3D self.__open_dot() self.states, self.initial_state, self.final_states =3D self.__get_= state_variables() - self.events =3D self.__get_event_variables() - self.function =3D self.__create_matrix() + self.env_types =3D {} + self.env_stored =3D set() + self.constraint_vars =3D set() + self.self_loop_reset_events =3D set() + self.events, self.envs =3D self.__get_event_variables() + self.function, self.constraints =3D self.__create_matrix() self.events_start, self.events_start_run =3D self.__store_init_eve= nts() + self.env_stored =3D sorted(self.env_stored) + self.constraint_vars =3D sorted(self.constraint_vars) =20 def __get_model_name(self) -> str: basename =3D ntpath.basename(self.__dot_path) @@ -121,6 +147,7 @@ class Automata: cursor =3D self.__get_cursor_begin_events() =20 events =3D [] + envs =3D [] while self.__dot_lines[cursor].lstrip()[0] =3D=3D '"': # transitions have the format: # "all_fired" -> "both_fired" [ label =3D "disable_irq" ]; @@ -134,12 +161,77 @@ class Automata: # so split them. =20 for i in event.split("\\n"): + if ";" in i: + # if the event contains a constraint (hybrid autom= ata), + # it will be separated by a ";": + # "sched_switch;x<1000;reset(x)" + line =3D i.split(";") + i =3D line.pop(0) + if len(line) > 2: + raise ValueError("Only 1 constraint and 1 rese= t are supported") + envs +=3D self.__extract_env_var(line) events.append(i) + else: + # state labels have the format: + # "enable_fired" [label =3D "enable_fired\ncondition"]; + # ----- label is here -----^^^^^ + # label and node name must be the same, condition is optio= nal + state =3D self.__dot_lines[cursor].split("label")[1].split= ('"')[1] + if "\\n" in state: + line =3D state.split("\\n") + line.pop(0) + if len(line) > 1: + raise ValueError("Only 1 constraint is supported i= n the state") + envs +=3D self.__extract_env_var([line[0].replace(" ",= "")]) cursor +=3D 1 =20 - return sorted(set(events)) - - def __create_matrix(self): + return sorted(set(events)), sorted(set(envs)) + + def _split_constraint_expr(self, constr: list[str]) -> Iterator[tuple[= str, + = str | None]]: + """ + Get a list of strings of the type constr1 && constr2 and returns a= list of + constraints and separators: [[constr1,"&&"],[constr2,None]] + """ + exprs =3D [] + seps =3D [] + for c in constr: + while "&&" in c or "||" in c: + a =3D c.find("&&") + o =3D c.find("||") + pos =3D a if o < 0 or 0 < a < o else o + exprs.append(c[:pos].replace(" ", "")) + seps.append(c[pos:pos+2].replace(" ", "")) + c =3D c[pos+2:].replace(" ", "") + exprs.append(c) + seps.append(None) + return zip(exprs, seps) + + def __extract_env_var(self, constraint: list[str]) -> list[str]: + env =3D [] + for c, _ in self._split_constraint_expr(constraint): + rule =3D self.constraint_rule.search(c) + reset =3D self.constraint_reset.search(c) + if rule: + env.append(rule["env"]) + if rule.groupdict().get("unit"): + self.env_types[rule["env"]] =3D rule["unit"] + if rule["val"][0].isalpha(): + self.constraint_vars.add(rule["val"]) + # try to infer unit from constants or parameters + val_for_unit =3D rule["val"].lower().replace("()", "") + if val_for_unit.endswith("_ns"): + self.env_types[rule["env"]] =3D "ns" + if val_for_unit.endswith("_jiffies"): + self.env_types[rule["env"]] =3D "j" + if reset: + env.append(reset["env"]) + # environment variables that are reset need a storage + self.env_stored.add(reset["env"]) + return env + + def __create_matrix(self) -> tuple[list[list[str]], dict[tuple[int, in= t] | + int, list[str= ]]]: # transform the array into a dictionary events =3D self.events states =3D self.states @@ -157,6 +249,7 @@ class Automata: =20 # declare the matrix.... matrix =3D [[ self.invalid_state_str for x in range(nr_event)] for= y in range(nr_state)] + constraints =3D {} =20 # and we are back! Let's fill the matrix cursor =3D self.__get_cursor_begin_events() @@ -168,10 +261,23 @@ class Automata: dest_state =3D line[2].replace('"','').replace(',','_') possible_events =3D "".join(line[line.index("label")+2:-1]= ).replace('"','') for event in possible_events.split("\\n"): + if ";" in event: + line =3D event.split(";") + event =3D line.pop(0) + constraints[states_dict[origin_state],events_dict[= event]] =3D line + # those events reset also on self loops + if origin_state =3D=3D dest_state and "reset" in "= ".join(line): + self.self_loop_reset_events.add(event) matrix[states_dict[origin_state]][events_dict[event]] = =3D dest_state + else: + state =3D self.__dot_lines[cursor].split("label")[1].split= ('"')[1] + if "\\n" in state: + line =3D state.replace(" ", "").split("\\n") + state =3D line.pop(0) + constraints[states_dict[state]] =3D line cursor +=3D 1 =20 - return matrix + return matrix, constraints =20 def __store_init_events(self) -> tuple[list[bool], list[bool]]: events_start =3D [False] * len(self.events) @@ -203,3 +309,13 @@ class Automata: if any(self.events_start): return False return self.events_start_run[self.events.index(event)] + + def is_hybrid_automata(self) -> bool: + return bool(self.envs) + + def is_event_constraint(self, key: tuple[int, int] | int) -> bool: + """ + Given the key in self.constraints return true if it is an event + constraint, false if it is a state constraint + """ + return isinstance(key, tuple) diff --git a/tools/verification/rvgen/rvgen/dot2c.py b/tools/verification/r= vgen/rvgen/dot2c.py index 1771a2c9f6b9..58b3ee70033a 100644 --- a/tools/verification/rvgen/rvgen/dot2c.py +++ b/tools/verification/rvgen/rvgen/dot2c.py @@ -19,6 +19,7 @@ class Dot2c(Automata): enum_suffix =3D "" enum_states_def =3D "states" enum_events_def =3D "events" + enum_envs_def =3D "envs" struct_automaton_def =3D "automaton" var_automaton_def =3D "aut" =20 @@ -26,7 +27,7 @@ class Dot2c(Automata): super().__init__(file_path, model_name) self.line_length =3D 100 =20 - def __get_enum_states_content(self): + def __get_enum_states_content(self) -> list[str]: buff =3D [] buff.append("\t%s%s =3D 0," % (self.initial_state, self.enum_suffi= x)) for state in self.states: @@ -36,7 +37,7 @@ class Dot2c(Automata): =20 return buff =20 - def format_states_enum(self): + def format_states_enum(self) -> list[str]: buff =3D [] buff.append("enum %s {" % self.enum_states_def) buff +=3D self.__get_enum_states_content() @@ -58,7 +59,7 @@ class Dot2c(Automata): =20 return buff =20 - def format_events_enum(self): + def format_events_enum(self) -> list[str]: buff =3D [] buff.append("enum %s {" % self.enum_events_def) buff +=3D self.__get_enum_events_content() @@ -66,7 +67,43 @@ class Dot2c(Automata): =20 return buff =20 - def get_minimun_type(self): + def __get_non_stored_envs(self) -> list[str]: + return [ e for e in self.envs if e not in self.env_stored ] + + def __get_enum_envs_content(self) -> list[str]: + buff =3D [] + first =3D True + # We first place env variables that have a u64 storage. + # Those are limited by MAX_HA_ENV_LEN, other variables + # are read only and don't require a storage. + unstored =3D self.__get_non_stored_envs() + for env in list(self.env_stored) + unstored: + if first: + buff.append("\t%s%s =3D 0," % (env, self.enum_suffix)) + first =3D False + else: + buff.append("\t%s%s," % (env, self.enum_suffix)) + + buff.append("\tenv_max%s," % self.enum_suffix) + buff.append("\tenv_max_stored%s =3D %s%s" % + (self.enum_suffix, + unstored[0] if len(unstored) else "env_max", + self.enum_suffix)) + + return buff + + def format_envs_enum(self) -> list[str]: + buff =3D [] + if self.is_hybrid_automata(): + buff.append("enum %s {" % self.enum_envs_def) + buff +=3D self.__get_enum_envs_content() + buff.append("};\n") + buff.append('_Static_assert(env_max_stored%s <=3D MAX_HA_ENV_L= EN, "Not enough slots");\n' % + (self.enum_suffix)) + + return buff + + def get_minimun_type(self) -> str: min_type =3D "unsigned char" =20 if self.states.__len__() > 255: @@ -86,6 +123,8 @@ class Dot2c(Automata): buff.append("struct %s {" % self.struct_automaton_def) buff.append("\tchar *state_names[state_max%s];" % (self.enum_suffi= x)) buff.append("\tchar *event_names[event_max%s];" % (self.enum_suffi= x)) + if self.is_hybrid_automata(): + buff.append("\tchar *env_names[env_max%s];" % (self.enum_suffi= x)) buff.append("\t%s function[state_max%s][event_max%s];" % (min_type= , self.enum_suffix, self.enum_suffix)) buff.append("\t%s initial_state;" % min_type) buff.append("\tbool final_states[state_max%s];" % (self.enum_suffi= x)) @@ -110,7 +149,7 @@ class Dot2c(Automata): =20 return string =20 - def format_aut_init_events_string(self): + def format_aut_init_events_string(self) -> list[str]: buff =3D [] buff.append("\t.event_names =3D {") buff.append(self.__get_string_vector_per_line_content(self.events)) @@ -125,7 +164,18 @@ class Dot2c(Automata): =20 return buff =20 - def __get_max_strlen_of_states(self): + def format_aut_init_envs_string(self) -> list[str]: + buff =3D [] + if self.is_hybrid_automata(): + buff.append("\t.env_names =3D {") + # maintain consistent order with the enum + ordered_envs =3D list(self.env_stored) + self.__get_non_stored= _envs() + buff.append(self.__get_string_vector_per_line_content(ordered_= envs)) + buff.append("\t},") + + return buff + + def __get_max_strlen_of_states(self) -> int: max_state_name =3D max(self.states, key =3D len).__len__() return max(max_state_name, self.invalid_state_str.__len__()) =20 @@ -217,10 +267,12 @@ class Dot2c(Automata): buff +=3D self.format_states_enum() buff +=3D self.format_invalid_state() buff +=3D self.format_events_enum() + buff +=3D self.format_envs_enum() buff +=3D self.format_automaton_definition() buff +=3D self.format_aut_init_header() buff +=3D self.format_aut_init_states_string() buff +=3D self.format_aut_init_events_string() + buff +=3D self.format_aut_init_envs_string() buff +=3D self.format_aut_init_function() buff +=3D self.format_aut_init_initial_state() buff +=3D self.format_aut_init_final_states() diff --git a/tools/verification/rvgen/rvgen/dot2k.py b/tools/verification/r= vgen/rvgen/dot2k.py index a48e32c4ca73..1f6ad11117ac 100644 --- a/tools/verification/rvgen/rvgen/dot2k.py +++ b/tools/verification/rvgen/rvgen/dot2k.py @@ -20,12 +20,14 @@ class dot2k(Monitor, Dot2c): Monitor.__init__(self, extra_params) Dot2c.__init__(self, file_path, extra_params.get("model_name")) self.enum_suffix =3D "_%s" % self.name + self.monitor_class =3D extra_params["monitor_class"] =20 def fill_monitor_type(self) -> str: return self.monitor_type.upper() =20 def fill_tracepoint_handlers_skel(self) -> str: buff =3D [] + buff +=3D self.fill_hybrid_definitions() for event in self.events: buff.append("static void handle_%s(void *data, /* XXX: fill he= ader */)" % event) buff.append("{") @@ -77,6 +79,7 @@ class dot2k(Monitor, Dot2c): # self.enum_states_def =3D "states_%s" % self.name self.enum_events_def =3D "events_%s" % self.name + self.enum_envs_def =3D "envs_%s" % self.name self.struct_automaton_def =3D "automaton_%s" % self.name self.var_automaton_def =3D "automaton_%s" % self.name =20 @@ -107,8 +110,14 @@ class dot2k(Monitor, Dot2c): ("char *", "state"), ("char *", "event"), ] + tp_args_error_env =3D tp_args_error + [("char *", "env")] + tp_args_dict =3D { + "event": tp_args_event, + "error": tp_args_error, + "error_env": tp_args_error_env + } tp_args_id =3D ("int ", "id") - tp_args =3D tp_args_event if tp_type =3D=3D "event" else tp_args_e= rror + tp_args =3D tp_args_dict[tp_type] if self.monitor_type =3D=3D "per_task": tp_args.insert(0, tp_args_id) tp_proto_c =3D ", ".join([a+b for a,b in tp_args]) @@ -117,7 +126,11 @@ class dot2k(Monitor, Dot2c): buff.append(" TP_ARGS(%s)" % tp_args_c) return '\n'.join(buff) =20 - def fill_main_c(self): + def fill_hybrid_definitions(self) -> list: + """Stub, not valid for deterministic automata""" + return [] + + def fill_main_c(self) -> str: main_c =3D super().fill_main_c() =20 min_type =3D self.get_minimun_type() @@ -127,5 +140,261 @@ class dot2k(Monitor, Dot2c): main_c =3D main_c.replace("%%MIN_TYPE%%", min_type) main_c =3D main_c.replace("%%NR_EVENTS%%", str(nr_events)) main_c =3D main_c.replace("%%MONITOR_TYPE%%", monitor_type) + main_c =3D main_c.replace("%%MONITOR_CLASS%%", self.monitor_class) =20 return main_c + +class da2k(dot2k): + """Deterministic automata only""" + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if self.is_hybrid_automata(): + raise ValueError("Detected hybrid automata, use the 'ha' class= ") + +class ha2k(dot2k): + """Hybrid automata only""" + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if not self.is_hybrid_automata(): + raise ValueError("Detected deterministic automata, use the 'da= ' class") + self.trace_h =3D self._read_template_file("trace_hybrid.h") + self.__parse_constraints() + + def fill_monitor_class_type(self) -> str: + if self.monitor_type =3D=3D "per_task": + return "HA_MON_EVENTS_ID" + return "HA_MON_EVENTS_IMPLICIT" + + def fill_monitor_class(self) -> str: + """ + Used for tracepoint classes, since they are shared we keep da + instead of ha (also for the ha specific tracepoints). + The tracepoint class is not visible to the tools. + """ + return super().fill_monitor_class() + + def __adjust_value(self, value: str | int, unit: str | None) -> str: + """Adjust the value in ns""" + try: + value =3D int(value) + except ValueError: + # it's a constant, a parameter or a function + if value.endswith("()"): + return value.replace("()", "(ha_mon)") + return value + match unit: + case "us": + value *=3D 1000 + case "ms": + value *=3D 1000000 + case "s": + value *=3D 1000000000 + return str(value) + "ull" + + def __parse_state_constraint(self, rule: dict, value: str) -> str: + # by default assume the timer has ns expiration + clock_type =3D "ns" + if self.env_types.get(rule["env"]) =3D=3D "j": + clock_type =3D "jiffy" + + return "ha_start_timer_%s(ha_mon, %s%s, %s)" % (clock_type, rule["= env"], + self.enum_suffix, = value) + + def __validate_constraint(self, key: tuple[int, int] | int, constr: st= r, + rule, reset) -> None: + # event constrains are tuples and allow both rules and reset + # state constraints are only used for expirations (e.g. clk None: + for key, constraint in self.constraints.items(): + rules =3D [] + resets =3D [] + for c, sep in self._split_constraint_expr(constraint): + rule =3D self.constraint_rule.search(c) + reset =3D self.constraint_reset.search(c) + self.__validate_constraint(key, c, rule, reset) + if rule: + value =3D rule["val"] + value_len =3D len(rule["val"]) + unit =3D None + if rule.groupdict().get("unit"): + value_len +=3D len(rule["unit"]) + unit =3D rule["unit"] + c =3D c[:-(value_len)] + value =3D self.__adjust_value(value, unit) + if self.is_event_constraint(key): + c =3D "ha_get_env(ha_mon, %s%s) %s %s" % ( + rule["env"], self.enum_suffix, rule["op"],= value) + else: + c =3D self.__parse_state_constraint(rule, value) + if sep: + c +=3D f" {sep} " + rules.append(c) + if reset: + c =3D "ha_reset_env(ha_mon, %s%s)" % (reset["env"], se= lf.enum_suffix) + resets.append(c) + if self.is_event_constraint(key): + res =3D (["res =3D " + "".join(rules)] if rules else []) += resets + self.constraints[key] =3D ";".join(res) + else: + self.constraints[key] =3D rules[0] + + def __get_constr_condition(self) -> list[str]: + buff =3D [] + _else =3D "" + for edge, constr in self.constraints.items(): + # skip state constraints + if not self.is_event_constraint(edge): + continue + buff.append("\t%sif (curr_state =3D=3D %s%s && event =3D=3D %s= %s)" % + (_else, self.states[edge[0]], self.enum_suffix, + self.events[edge[1]], self.enum_suffix)) + if constr.count(";") > 0: + buff[-1] +=3D " {" + buff +=3D [ "\t\t%s;" % c for c in constr.split(";") ] + if constr.count(";") > 0: + buff.append("\t}") + _else =3D "else " + return buff + + def __get_state_constr_condition(self) -> list[str]: + buff =3D [] + # do not even print this if no state constraint is present + if not [c for c in self.constraints.keys() if not self.is_event_co= nstraint(c)]: + return buff + + # normally leaving the state with a constraint doesn't touch the t= imer, + # but if that event does reset, we need to carry on with the checks + conditions =3D ["next_state =3D=3D curr_state"] + conditions +=3D ["event !=3D %s%s" % (e, self.enum_suffix) + for e in self.self_loop_reset_events] + condition_str =3D " && ".join(conditions) + if len(conditions) > 1: + condition_str =3D f"({condition_str})" + buff.append("\n\tif (%s || !res)\n\t\treturn res;" % condition_str) + + _else =3D "" + constrained_states =3D set() + for state, constr in self.constraints.items(): + # skip event constraints + if self.is_event_constraint(state): + continue + constrained_states.add(self.states[state]) + buff.append("\t%sif (next_state =3D=3D %s%s)" % + (_else, self.states[state], self.enum_suffix)) + buff.append("\t\t%s;" % constr) + _else =3D "else " + buff.append("\telse if (%s)\n\t\tres =3D !ha_cancel_timer(ha_mon);= " % + " ||".join(["curr_state =3D=3D %s%s" % (s, self.enum_s= uffix) + for s in constrained_states])) + return buff + + def fill_constr_func(self) -> list[str]: + buff =3D [] + if self.constraints: + buff.append( +"""/* + * This function is used to validate state transitions. + * + * It is generated by parsing the model, there is usually no need to chang= e it, + * unless conditions were incorrectly specified or too complex for the par= ser. + * If the monitor requires a timer, this function is responsible to arm it= when + * the next state has a constraint and cancel it in any other case. Transi= tions + * to the same state never affect timers. + */ +static bool ha_verify_constraint(struct ha_monitor *ha_mon, +\t\t\t\t enum %s curr_state, enum %s event, +\t\t\t\t enum %s next_state) +{ +\tbool res =3D true; +""" % (self.enum_states_def, self.enum_events_def, self.enum_states_def)) + buff +=3D self.__get_constr_condition() + buff +=3D self.__get_state_constr_condition() + buff.append("""\treturn res; +}\n""") + return buff + + def __fill_env_getter(self, env: str) -> str: + if env in self.env_types: + match self.env_types[env]: + case "ns" | "us" | "ms" | "s": + return "ha_get_clk_ns(ha_mon, env);" + case "j": + return "ha_get_clk_jiffy(ha_mon, env);" + return "/* XXX: how do I read %s? */" % env + + def __fill_env_resetter(self, env: str) -> str: + if env in self.env_types: + match self.env_types[env]: + case "ns" | "us" | "ms" | "s": + return "ha_reset_clk_ns(ha_mon, env);" + case "j": + return "ha_reset_clk_jiffy(ha_mon, env);" + return "/* XXX: how do I reset %s? */" % env + + def fill_hybrid_get_reset_functions(self) -> list[str]: + buff =3D [] + if self.is_hybrid_automata(): + for var in self.constraint_vars: + if var.endswith("()"): + func_name =3D var.replace("()", "") + if func_name.isupper(): + buff.append("#define %s(ha_mon) /* XXX: what is %s= (ha_mon)? */\n" % (func_name, func_name)) + else: + buff.append("static inline u64 %s(struct ha_monito= r *ha_mon)\n{" % func_name) + buff.append("\treturn /* XXX: what is %s(ha_mon)? = */;" % func_name) + buff.append("}\n") + elif var.isupper(): + buff.append("#define %s /* XXX: what is %s? */\n" % (v= ar, var)) + else: + buff.append("static u64 %s =3D /* XXX: default value *= /;" % var) + buff.append("module_param(%s, ullong, 0644);\n" % var) + buff.append("""/* + * These functions define how to read and reset the environment variable. + * + * Common environment variables like ns-based and jiffy-based clocks have + * pre-define getters and resetters you can use. The parser can infer the = type + * of the environment variable if you supply a measure unit in the constra= int. + * If you define your own functions, make sure to add appropriate memory + * barriers if required. + * Some environment variables don't require a storage as they read a system + * state (e.g. preemption count). Those variables are never reset, so we d= on't + * define a reset function on monitors only relying on this type of variab= les. + */""") + buff.append("static u64 ha_get_env(struct ha_monitor *ha_mon, = enum envs%s env)\n{" % self.enum_suffix) + _else =3D "" + for env in self.envs: + buff.append("\t%sif (env =3D=3D %s%s)\n\t\treturn %s" % + (_else, env, self.enum_suffix, self.__fill_env= _getter(env))) + _else =3D "else " + buff.append("\treturn ENV_INVALID_VALUE;\n}\n") + if len(self.env_stored): + buff.append("static void ha_reset_env(struct ha_monitor *h= a_mon, enum envs%s env)\n{" % self.enum_suffix) + _else =3D "" + for env in self.env_stored: + buff.append("\t%sif (env =3D=3D %s%s)\n\t\t%s" % + (_else, env, self.enum_suffix, self.__fill= _env_resetter(env))) + _else =3D "else " + buff.append("}\n") + return buff + + def fill_hybrid_definitions(self) -> list[str]: + return self.fill_hybrid_get_reset_functions() + self.fill_constr_f= unc() diff --git a/tools/verification/rvgen/rvgen/generator.py b/tools/verificati= on/rvgen/rvgen/generator.py index 3441385c1177..b80af3fd6701 100644 --- a/tools/verification/rvgen/rvgen/generator.py +++ b/tools/verification/rvgen/rvgen/generator.py @@ -255,12 +255,14 @@ class Monitor(RVGenerator): monitor_class_type =3D self.fill_monitor_class_type() tracepoint_args_skel_event =3D self.fill_tracepoint_args_skel("eve= nt") tracepoint_args_skel_error =3D self.fill_tracepoint_args_skel("err= or") + tracepoint_args_skel_error_env =3D self.fill_tracepoint_args_skel(= "error_env") trace_h =3D trace_h.replace("%%MODEL_NAME%%", self.name) trace_h =3D trace_h.replace("%%MODEL_NAME_UP%%", self.name.upper()) trace_h =3D trace_h.replace("%%MONITOR_CLASS%%", monitor_class) trace_h =3D trace_h.replace("%%MONITOR_CLASS_TYPE%%", monitor_clas= s_type) trace_h =3D trace_h.replace("%%TRACEPOINT_ARGS_SKEL_EVENT%%", trac= epoint_args_skel_event) trace_h =3D trace_h.replace("%%TRACEPOINT_ARGS_SKEL_ERROR%%", trac= epoint_args_skel_error) + trace_h =3D trace_h.replace("%%TRACEPOINT_ARGS_SKEL_ERROR_ENV%%", = tracepoint_args_skel_error_env) return trace_h =20 def print_files(self): diff --git a/tools/verification/rvgen/rvgen/templates/dot2k/main.c b/tools/= verification/rvgen/rvgen/templates/dot2k/main.c index f14a0e7ad56b..c9335cd32fc2 100644 --- a/tools/verification/rvgen/rvgen/templates/dot2k/main.c +++ b/tools/verification/rvgen/rvgen/templates/dot2k/main.c @@ -21,7 +21,7 @@ */ #define RV_MON_TYPE RV_MON_%%MONITOR_TYPE%% #include "%%MODEL_NAME%%.h" -#include +#include =20 /* * This is the instrumentation part of the monitor. diff --git a/tools/verification/rvgen/rvgen/templates/dot2k/trace_hybrid.h = b/tools/verification/rvgen/rvgen/templates/dot2k/trace_hybrid.h new file mode 100644 index 000000000000..c8290e9ba2f4 --- /dev/null +++ b/tools/verification/rvgen/rvgen/templates/dot2k/trace_hybrid.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Snippet to be included in rv_trace.h + */ + +#ifdef CONFIG_RV_MON_%%MODEL_NAME_UP%% +DEFINE_EVENT(event_%%MONITOR_CLASS%%, event_%%MODEL_NAME%%, +%%TRACEPOINT_ARGS_SKEL_EVENT%%); + +DEFINE_EVENT(error_%%MONITOR_CLASS%%, error_%%MODEL_NAME%%, +%%TRACEPOINT_ARGS_SKEL_ERROR%%); + +DEFINE_EVENT(error_env_%%MONITOR_CLASS%%, error_env_%%MODEL_NAME%%, +%%TRACEPOINT_ARGS_SKEL_ERROR_ENV%%); +#endif /* CONFIG_RV_MON_%%MODEL_NAME_UP%% */ --=20 2.50.1 From nobody Sat Oct 4 15:57:18 2025 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3D8A8220F2F for ; Thu, 14 Aug 2025 15:09:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184177; cv=none; b=E0k12HoRSCtArmEg4gBwer8QgtifsDG94VOZQcpq6huwhL91KwahKGu/Yy0LjofhCn2qaOTcoG1x/HUIFGzEtDgIrYTRaxBfhnXq2GOLWHRpBrsLwc9qpfMkYHl93bABadOz0nbKH6HfUePuicqgWwLjapXOdLU6zs434RK7Aos= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184177; c=relaxed/simple; bh=Ytt+GwLOQF6lNbGpxKq6MbUNEhgVYH/43PlqhE/y+4g=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=uzVdk6n80gGZzvrqN7FQ2g9op0lG71Og5ImQ3n/cMhojuflJd/LFJ2+ZCUcLmiUoN9XVq43FzCDMbm7Gnx2yiodNuxtQEhXtQJlnUiVzRi2fC2Gt3bEGBvmCD67tf7Ic8zRQy/UYng0u+6UsYA+mBB5Zu89EXT0YJPtK/H7Xd08= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=CLlxAXhP; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="CLlxAXhP" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1755184174; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=1mohDuCl0ZZagmBOAuDUIch+Z9jd935gCNczHyneeaQ=; b=CLlxAXhPrKWRW/Fl7QIXMfSH+PLpqebfVwMVeczS3roQJoWgBTjG/+K9M9sX0keaaqnM9J N2rRysmollXzinjNw/l/kPITWSRF066XqETE53A84yfKUBqtXUb3nnw1FjopysuEeb5BCU vq5UAyAIMYPo+AY9jkPK63oyDKmBczs= Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-641-BwlvoRA1PQ6zYf1TEnSfnQ-1; Thu, 14 Aug 2025 11:09:31 -0400 X-MC-Unique: BwlvoRA1PQ6zYf1TEnSfnQ-1 X-Mimecast-MFC-AGG-ID: BwlvoRA1PQ6zYf1TEnSfnQ_1755184170 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id A12FD1800292; Thu, 14 Aug 2025 15:09:30 +0000 (UTC) Received: from gmonaco-thinkpadt14gen3.rmtit.com (unknown [10.44.32.52]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 024D1180047F; Thu, 14 Aug 2025 15:09:24 +0000 (UTC) From: Gabriele Monaco To: linux-kernel@vger.kernel.org, Steven Rostedt , Jonathan Corbet , linux-trace-kernel@vger.kernel.org, linux-doc@vger.kernel.org Cc: Gabriele Monaco , Nam Cao , Tomas Glozar , Juri Lelli , Clark Williams , John Kacur Subject: [RFC PATCH 11/17] Documentation/rv: Add documentation about hybrid automata Date: Thu, 14 Aug 2025 17:08:03 +0200 Message-ID: <20250814150809.140739-12-gmonaco@redhat.com> In-Reply-To: <20250814150809.140739-1-gmonaco@redhat.com> References: <20250814150809.140739-1-gmonaco@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 Content-Type: text/plain; charset="utf-8" Describe theory and implementation of hybrid automata in the dedicated page hybrid_automata.rst Include a section on how to integrate a hybrid automaton in monitor_synthesis.rst Also remove a hanging $ in deterministic_automata.rst Signed-off-by: Gabriele Monaco --- .../trace/rv/deterministic_automata.rst | 2 +- Documentation/trace/rv/hybrid_automata.rst | 307 ++++++++++++++++++ Documentation/trace/rv/index.rst | 1 + Documentation/trace/rv/monitor_synthesis.rst | 86 +++++ 4 files changed, 395 insertions(+), 1 deletion(-) create mode 100644 Documentation/trace/rv/hybrid_automata.rst diff --git a/Documentation/trace/rv/deterministic_automata.rst b/Documentat= ion/trace/rv/deterministic_automata.rst index d0638f95a455..7a1c2b20ec72 100644 --- a/Documentation/trace/rv/deterministic_automata.rst +++ b/Documentation/trace/rv/deterministic_automata.rst @@ -11,7 +11,7 @@ where: - *E* is the finite set of events; - x\ :subscript:`0` is the initial state; - X\ :subscript:`m` (subset of *X*) is the set of marked (or final) states. -- *f* : *X* x *E* -> *X* $ is the transition function. It defines the state +- *f* : *X* x *E* -> *X* is the transition function. It defines the state transition in the occurrence of an event from *E* in the state *X*. In t= he special case of deterministic automata, the occurrence of the event in *= E* in a state in *X* has a deterministic next state from *X*. diff --git a/Documentation/trace/rv/hybrid_automata.rst b/Documentation/tra= ce/rv/hybrid_automata.rst new file mode 100644 index 000000000000..ecfff26d65bd --- /dev/null +++ b/Documentation/trace/rv/hybrid_automata.rst @@ -0,0 +1,307 @@ +Hybrid Automata +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Hybrid automata are an extension of deterministic automata, there are seve= ral +definitions of hybrid automata in the literature. The adaptation implement= ed +here is formally denoted by G and defined as a 7-tuple: + + *G* =3D { *X*, *E*, *V*, *f*, x\ :subscript:`0`, X\ :subscript:`m`= , *i* } + +- *X* is the set of states; +- *E* is the finite set of events; +- *V* is the finite set of environment variables; +- x\ :subscript:`0` is the initial state; +- X\ :subscript:`m` (subset of *X*) is the set of marked (or final) states. +- *f* : *X* x *E* x *C(V)* -> *X* is the transition function. + It defines the state transition in the occurrence of an event from *E* i= n the + state *X*. Unlike deterministic automata, the transition function also + includes guards from the set of all possible constraints (defined as *C(= V)*). + Guards can be true or false with the valuation of *V* when the event occ= urs, + and the transition is possible only when constraints are true. Similarly= to + deterministic automata, the occurrence of the event in *E* in a state in= *X* + has a deterministic next state from *X*, if the guard is true. +- *i* : *X* -> *C(V)* is the invariant assignment function, this is a + constraint assigned to each state in *X*, every state in *X* must be left + before the invariant turns to false. We can omit the representation of + invariants whose value is true regardless of the valuation of *V*. + +The set of all possible constraints *C(V)* is defined according to the +following grammar: + + g =3D v < c | v > c | v <=3D c | v >=3D c | v =3D=3D c | v !=3D c = | g && g | true + +With v a variable in *V* and c a numerical value. + +We define the special case of hybrid automata whose variables grow with un= iform +rates as timed automata. In this case, the variables are called clocks. +As the name implies, timed automata can be used to describe real time. +Additionally, clocks support another type of guard which always evaluates = to true: + + reset(v) + +The reset constraint is used to set the value of a clock to 0. + +It is important to note that any valid hybrid automaton is a valid +deterministic automaton with additional guards and invariants. Those can o= nly +further constrain what transitions are valid but it is not possible to def= ine +transition functions starting from the same state in *X* and the same even= t in +*E* but ending up in different states in *X* based on the valuation of *V*. + +Examples +-------- + +The 'wip' (wakeup in preemptive) example introduced as a deterministic aut= omaton +can also be described as: + +- *X* =3D { ``any_thread_running`` } +- *E* =3D { ``sched_waking`` } +- *V* =3D { ``preemptive`` } +- x\ :subscript:`0` =3D ``any_thread_running`` +- X\ :subscript:`m` =3D {``any_thread_running``} +- *f* =3D + - *f*\ (``any_thread_running``, ``sched_waking``, ``preemptive=3D=3D0``= ) =3D ``any_thread_running`` +- *i* =3D + - *i*\ (``any_thread_running``) =3D ``true`` + +Which can be represented graphically as:: + + | + | + v + #=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D# sched_= waking;preemptive=3D=3D0 + H H ------------------------------+ + H any_thread_running H | + H H <-----------------------------+ + #=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D# + +In this example, by using the preemptive state of the system as an environ= ment +variable, we can assert this constraint on ``sched_waking`` without requir= ing +preemption events (as we would in a deterministic automaton), which can be +useful in case those events are not available or not reliable on the syste= m. + +Since all the invariants in *i* are true, we can omit them from the repres= entation. + +As a sample timed automaton we can define 'stall' as: + +- *X* =3D { ``dequeued``, ``enqueued``, ``running``} +- *E* =3D { ``enqueue``, ``dequeue``, ``switch_in``} +- *V* =3D { ``clk`` } +- x\ :subscript:`0` =3D ``dequeue`` +- X\ :subscript:`m` =3D {``dequeue``} +- *f* =3D + - *f*\ (``enqueued``, ``switch_in``, ``clk < threshold``) =3D ``running= `` + - *f*\ (``running``, ``dequeue``) =3D ``dequeued`` + - *f*\ (``dequeued``, ``enqueue``, ``reset(clk)``) =3D ``enqueued`` +- *i* =3D *omitted as all true* + +Graphically represented as:: + + | + | + v + #=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D# + H dequeued H <+ + #=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D# | + | | + | enqueue; reset(clk) | + v | + +----------------------------+ | + | enqueued | | dequeue + +----------------------------+ | + | | + | switch_in; clk < threshold | + v | + +----------------------------+ | + | running | -+ + +----------------------------+ + +This model imposes that the time between when a task is enqueued (it becom= es +runnable) and when the task gets to run must be lower than a certain thres= hold. +A failure in this model means that the task is starving. +One problem in using guards on the edges in this case is that the model wi= ll +not report a failure until the ``switch_in`` event occurs. This means that, +according to the model, it is valid for the task never to run. +As this is not exactly what is intended, we can change the model as: + +- *X* =3D { ``dequeued``, ``enqueued``, ``running``} +- *E* =3D { ``enqueue``, ``dequeue``, ``switch_in``} +- *V* =3D { ``clk`` } +- x\ :subscript:`0` =3D ``dequeue`` +- X\ :subscript:`m` =3D {``dequeue``} +- *f* =3D + - *f*\ (``enqueued``, ``switch_in``) =3D ``running`` + - *f*\ (``running``, ``dequeue``) =3D ``dequeued`` + - *f*\ (``dequeued``, ``enqueue``, ``reset(clk)``) =3D ``enqueued`` +- *i* =3D + - *i*\ (``enqueued``) =3D ``clk < threshold`` + +Graphically:: + + | + | + v + #=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D# + H dequeued H <+ + #=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D# | + | | + | enqueue; reset(clk) | + v | + +-------------------------+ | + | enqueued | | + | clk < threshold | | dequeue + +-------------------------+ | + | | + | switch_in | + v | + +-------------------------+ | + | running | -+ + +-------------------------+ + +In this case, we moved the guard as an invariant to the ``enqueued`` state, +this means we not only forbid the occurrence of ``switch_in`` when ``clk``= is +past the threshold but also mark as invalid in case we are *still* in +``enqueued`` after the threshold. This model is effectively in an invalid = state +as soon as a task is starving, rather than when the starving task finally = runs. + +Hybrid Automaton in C +--------------------- + +The definition of hybrid automata in C is heavily based on the determinist= ic +automata one. Specifically, we add the set of environment variables and the +constraints (both guards on transitions and invariants on states) as follo= ws:: + + /* enum representation of X (set of states) to be used as index */ + enum states { + dequeued =3D 0, + enqueued, + running, + state_max + }; + + #define INVALID_STATE state_max + + /* enum representation of E (set of events) to be used as index */ + enum events { + dequeue =3D 0, + enqueue, + switch_in, + event_max + }; + + /* enum representation of V (set of environment variables) to be used as= index */ + enum envs { + clk =3D 0, + env_max, + env_max_stored =3D env_max + }; + + struct automaton { + char *state_names[state_max]; // X: the set of states + char *event_names[event_max]; // E: the finite set of ev= ents + char *env_names[env_max]; // V: the finite set of en= v vars + unsigned char function[state_max][event_max]; // f: transition function + unsigned char initial_state; // x_0: the initial state + bool final_states[state_max]; // X_m: the set of marked = states + }; + + struct automaton aut =3D { + .state_names =3D { + "dequeued", + "enqueued", + "running" + }, + .event_names =3D { + "dequeue", + "enqueue", + "switch_in" + }, + .env_names =3D { + "clk" + }, + .function =3D { + { INVALID_STATE, enqueued, INVALID_STATE }, + { INVALID_STATE, INVALID_STATE, running }, + { dequeued, INVALID_STATE, INVALID_STATE }, + }, + .initial_state =3D dequeued, + .final_states =3D { 1, 0, 0 }, + }; + + static bool verify_constraint(enum states curr_state, enum events event, + enum states next_state) + { + bool res =3D true; + + /* Validate guards as part of f */ + if (curr_state =3D=3D enqueued && event =3D=3D sched_switch_in) + res =3D get_env(clk) < threshold; + else if (curr_state =3D=3D dequeued && event =3D=3D sched_wakeup) + reset_env(clk); + + /* Validate invariants in i */ + if (next_state =3D=3D curr_state) + return res; + if (next_state =3D=3D enqueued && res) + start_timer(clk, threshold); + else + cancel_timer(); + return res; + } + +The function ``verify_constraint``, here reported as simplified, checks gu= ards, +performs resets and starts timers to validate invariants according to +specification. +Due to the complex nature of environment variables, the user needs to prov= ide +functions to get and reset environment variables, although we provide some +helpers for common types (e.g. clocks with ns or jiffy granularity). +Invariants defined in this way only make sense as clock expirations (e.g. = *clk +< threshold*). + +Graphviz .dot format +-------------------- + +Also the Graphviz representation of hybrid automata is an extension of the +deterministic automata one. Specifically, guards can be provided in the ev= ent +name separated by ``;``:: + + "state_start" -> "state_dest" [ label =3D "sched_waking;preemptible=3D= =3D0;reset(clk)" ]; + +Invariant can be specified in the state label (not the node name!) separat= ed by ``\n``:: + + "enqueued" [label =3D "enqueued\nclk < threshold_jiffies"]; + +Constraints can be specified as valid C comparisons and allow spaces, the = first +element of the comparison must be the clock while the second is a numerica= l or +parametrised value. Guards allow comparisons to be combined with boolean +operations (``&&`` and ``||``), resets must be separated from other constr= aints. + +This is the full example of the last version of the 'stall' model in DOT:: + + digraph state_automaton { + {node [shape =3D circle] "enqueued"}; + {node [shape =3D plaintext, style=3Dinvis, label=3D""] "__init_deque= ued"}; + {node [shape =3D doublecircle] "dequeued"}; + {node [shape =3D circle] "running"}; + "__init_dequeued" -> "dequeued"; + "enqueued" [label =3D "enqueued\nclk < threshold_jiffies"]; + "running" [label =3D "running"]; + "dequeued" [label =3D "dequeued"]; + "enqueued" -> "running" [ label =3D "switch_in" ]; + "running" -> "dequeued" [ label =3D "dequeue" ]; + "dequeued" -> "enqueued" [ label =3D "enqueue;reset(clk)" ]; + { rank =3D min ; + "__init_dequeued"; + "dequeued"; + } + } + +References +---------- + +One book covering model checking and timed automata is:: + + Christel Baier and Joost-Pieter Katoen: Principles of Model Checking, Th= e MIT Press, 2008. + +Hybrid automata are described in detail in:: + + Thomas Henzinger: The theory of hybrid automata, Proceedings 11th Annual= IEEE Symposium on Logic in Computer Science (1996): 278-292. diff --git a/Documentation/trace/rv/index.rst b/Documentation/trace/rv/inde= x.rst index a2812ac5cfeb..ad298784bda2 100644 --- a/Documentation/trace/rv/index.rst +++ b/Documentation/trace/rv/index.rst @@ -9,6 +9,7 @@ Runtime Verification runtime-verification.rst deterministic_automata.rst linear_temporal_logic.rst + hybrid_automata.rst monitor_synthesis.rst da_monitor_instrumentation.rst monitor_wip.rst diff --git a/Documentation/trace/rv/monitor_synthesis.rst b/Documentation/t= race/rv/monitor_synthesis.rst index ce0c1a5104d4..fd886d842693 100644 --- a/Documentation/trace/rv/monitor_synthesis.rst +++ b/Documentation/trace/rv/monitor_synthesis.rst @@ -45,6 +45,7 @@ creating monitors. The header files are: =20 * rv/da_monitor.h for deterministic automaton monitor. * rv/ltl_monitor.h for linear temporal logic monitor. + * rv/ha_monitor.h for hybrid automaton monitor. =20 rvgen ----- @@ -252,6 +253,91 @@ the task, the monitor may need some time to start vali= dating tasks which have been running before the monitor is enabled. Therefore, it is recommended to start the tasks of interest after enabling the monitor. =20 +rv/ha_monitor.h ++++++++++++++++ + +The implementation of hybrid automaton monitors derives directly from the +deterministic automaton one. Despite using a different header +(``ha_monitor.h``) the functions to handle events are the same (e.g. +``da_handle_event``). + +Additionally, the `rvgen` tool populates skeletons for the +``ha_verify_constraint``, ``ha_get_env`` and ``ha_reset_env`` based on the +monitor specification in the monitor source file. + +Unless the monitor relies on complex constraints, ``ha_verify_constraint`` +is typically ready as it is generated by `rvgen`: + +* standard constraints on edges are turned into the form:: + + res =3D ha_get_env(ha_mon, ENV) < VALUE; + +* reset constraints are turned into the form:: + + ha_reset_env(ha_mon, ENV); + +* constraints on the state are implemented using timers + + - armed before entering the state + + - cancelled while entering any other state + + - untouched if the state does not change as a result of the event + +Constraint values can be specified in 3 forms: + +* literal value (with optional unit). E.g.:: + + preemptive =3D=3D 0 + clk < 100ns + threshold <=3D 10j + +* constant value (uppercase string). E.g.:: + + clk < MAX_NS + +* parameter (lowercase string). E.g.:: + + clk <=3D threshold_jiffies + +In all cases, `rvgen` will try to understand the type of the environment +variable from the name or unit. For instance, constants or parameters +terminating with `_NS` or `_jiffies` are intended as clocks with ns and ji= ffy +granularity, respectively. Literals with measure unit `j` are jiffies and = if a +time unit is specified (`ns` to `s`), `rvgen` will convert the value to `n= s`. + +Constants need to be defined by the user (but unlike the name, they don't +necessarily need to be defined as constants). Parameters get converted to +module parameters and the user needs to provide a default value. + +If `rvgen` determines that the variable is a clock, it provides the getter= and +resetter based on the unit. Otherwise, the user needs to provide an approp= riate +definition. +Typically non-clock environment variables are not reset. In such case only= the +getter skeleton will be present in the file generated by `rvgen`. +For instance, the getter for preemptive can be filled as:: + + static u64 ha_get_env(struct ha_monitor *ha_mon, enum envs env) + { + if (env =3D=3D preemptible) + return preempt_count() =3D=3D 0; + return ENV_INVALID_VALUE; + } + +The function is supplied the ``ha_mon`` parameter in case some storage is +required (as it is for clocks), but environment variables without reset do= not +require a storage and can ignore that argument. +The number of environment variables requiring a storage is limited by +``MAX_HA_ENV_LEN``, however such limitation doesn't stand for other variab= les. + +Finally, constraints on states are only valid for clocks and only if the +constraint is of the form `clk < N`. This is because such constraints are +implemented with the expiration of a timer. +Typically the clock variables are reset just before arming the timer, but = this +doesn't have to be the case and the available functions take care of it. +It is a responsibility of per-task monitors to make sure no timer is left +running when the task exits. + Final remarks ------------- =20 --=20 2.50.1 From nobody Sat Oct 4 15:57:18 2025 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 16438212573 for ; Thu, 14 Aug 2025 15:09:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184187; cv=none; b=g9esa+1W1DDFj9TmmyZ8JXqE3CV6ekkOCxg22Z3+RpUBf5JVhRT3Z43mcS8e52l7TCkmMmEm+TaWEL45dnwMAZLitrw0eimN3XzJD8cjvw+frMNsnDd7t3nXNo1CCb0S3gvhcvNNw7+g1fXwShF56C3jQgr9mzbLLZOxhA7XGwk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184187; c=relaxed/simple; bh=DaTo0a2SgdMK7730P2eup1Sohj6ne4zuS74GlH6CKyY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=pdYABlpS/Uwz+kvfS/1fN2/NeJZVbrnNv41wBLL94IEozR/PpXoG0W7aYydxkuBs0kXXnlXx1fNxDB5HcX7ty6Rw20CPjIZyzkNtnslVJK0Ls9EM3fSq9u5PYd8d10kiR+d/64eG9fyoKxyH9yOfqLaX2FpysIMDWujamhb7xOo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=Cxeyq2Xv; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="Cxeyq2Xv" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1755184183; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=DOMfw4EB+v4yoeooSIDCyC+G3wgkvmCh/SxX2hLFtDw=; b=Cxeyq2XvyCxXrcYduIy7rKpdMGM/GlqxVnJvz1NbmJRz5Edc6R/Kh1aQ870k08auspemL7 v10rSVdfSgw1RGq6FyoXAyoIwvDC7RzO2MRkxZfpYmm4GTaPQYeRBXgxhODihykicIzBmt 9Ouafhzm2FyZEhzuO2Mif1ND6OwzhKc= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-79-dJF9Av4ZMHSQ2QkpNKgSlQ-1; Thu, 14 Aug 2025 11:09:40 -0400 X-MC-Unique: dJF9Av4ZMHSQ2QkpNKgSlQ-1 X-Mimecast-MFC-AGG-ID: dJF9Av4ZMHSQ2QkpNKgSlQ_1755184178 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 6D78B19775A5; Thu, 14 Aug 2025 15:09:38 +0000 (UTC) Received: from gmonaco-thinkpadt14gen3.rmtit.com (unknown [10.44.32.52]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id D9D8D1800295; Thu, 14 Aug 2025 15:09:32 +0000 (UTC) From: Gabriele Monaco To: linux-kernel@vger.kernel.org, Jonathan Corbet , Steven Rostedt , Masami Hiramatsu , linux-doc@vger.kernel.org, linux-trace-kernel@vger.kernel.org Cc: Gabriele Monaco , Nam Cao , Tomas Glozar , Juri Lelli , Clark Williams , John Kacur Subject: [RFC PATCH 12/17] rv: Add sample hybrid monitors stall Date: Thu, 14 Aug 2025 17:08:04 +0200 Message-ID: <20250814150809.140739-13-gmonaco@redhat.com> In-Reply-To: <20250814150809.140739-1-gmonaco@redhat.com> References: <20250814150809.140739-1-gmonaco@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 Content-Type: text/plain; charset="utf-8" Add a sample monitor to showcase hybrid/timed automata. The stall monitor identifies tasks stalled for longer than a threshold and reacts when that happens. Signed-off-by: Gabriele Monaco --- Documentation/tools/rv/rv-mon-stall.rst | 44 +++++++ Documentation/trace/rv/monitor_stall.rst | 43 +++++++ kernel/trace/rv/Kconfig | 1 + kernel/trace/rv/Makefile | 1 + kernel/trace/rv/monitors/stall/Kconfig | 9 ++ kernel/trace/rv/monitors/stall/stall.c | 116 +++++++++++++++++++ kernel/trace/rv/monitors/stall/stall.h | 64 ++++++++++ kernel/trace/rv/monitors/stall/stall_trace.h | 19 +++ kernel/trace/rv/rv_trace.h | 1 + tools/verification/models/stall.dot | 20 ++++ 10 files changed, 318 insertions(+) create mode 100644 Documentation/tools/rv/rv-mon-stall.rst create mode 100644 Documentation/trace/rv/monitor_stall.rst create mode 100644 kernel/trace/rv/monitors/stall/Kconfig create mode 100644 kernel/trace/rv/monitors/stall/stall.c create mode 100644 kernel/trace/rv/monitors/stall/stall.h create mode 100644 kernel/trace/rv/monitors/stall/stall_trace.h create mode 100644 tools/verification/models/stall.dot diff --git a/Documentation/tools/rv/rv-mon-stall.rst b/Documentation/tools/= rv/rv-mon-stall.rst new file mode 100644 index 000000000000..c79d7c2e4dd4 --- /dev/null +++ b/Documentation/tools/rv/rv-mon-stall.rst @@ -0,0 +1,44 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +rv-mon-stall +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +-------------------- +Stalled task monitor +-------------------- + +:Manual section: 1 + +SYNOPSIS +=3D=3D=3D=3D=3D=3D=3D=3D + +**rv mon stall** [*OPTIONS*] + +DESCRIPTION +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The stalled task (**stall**) monitor is a sample per-task timed monitor th= at +checks if tasks are scheduled within a defined threshold after they are re= ady. + +See kernel documentation for further information about this monitor: + + +OPTIONS +=3D=3D=3D=3D=3D=3D=3D + +.. include:: common_ikm.rst + +SEE ALSO +=3D=3D=3D=3D=3D=3D=3D=3D + +**rv**\(1), **rv-mon**\(1) + +Linux kernel *RV* documentation: + + +AUTHOR +=3D=3D=3D=3D=3D=3D + +Written by Gabriele Monaco + +.. include:: common_appendix.rst diff --git a/Documentation/trace/rv/monitor_stall.rst b/Documentation/trace= /rv/monitor_stall.rst new file mode 100644 index 000000000000..e4d9b050a32f --- /dev/null +++ b/Documentation/trace/rv/monitor_stall.rst @@ -0,0 +1,43 @@ +Monitor stall +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +- Name: stall - wakeup in preemptive +- Type: per-task hybrid automaton +- Author: Gabriele Monaco + +Description +----------- + +The stalled task (stall) monitor is a sample per-task timed monitor that c= hecks +if tasks are scheduled within a defined threshold after they are ready:: + + | + | + v + #=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D# + H dequeued H <+ + #=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D# | + | | + | sched_wakeup;reset(clk) | + v | + +----------------------------------+ | + | enqueued | | + | clk < threshold_jiffies | | sched_switch= _wait + +----------------------------------+ | + | | + | sched_switch_in | + sched_switch_in v | + sched_wakeup +----------------------------------+ | + +------------------ | | | + | | running | | + +-----------------> | | -+ + +----------------------------------+ + + +The threshold can be configured as a parameter by either booting with the +``stall.threshold_jiffies=3D`` argument or writing a new value = to +``/sys/module/stall/parameters/threshold_jiffies``. + +Specification +------------- +Grapviz Dot file in tools/verification/models/stall.dot diff --git a/kernel/trace/rv/Kconfig b/kernel/trace/rv/Kconfig index 4ad392dfc57f..720fbe4935f8 100644 --- a/kernel/trace/rv/Kconfig +++ b/kernel/trace/rv/Kconfig @@ -78,6 +78,7 @@ source "kernel/trace/rv/monitors/pagefault/Kconfig" source "kernel/trace/rv/monitors/sleep/Kconfig" # Add new rtapp monitors here =20 +source "kernel/trace/rv/monitors/stall/Kconfig" # Add new monitors here =20 config RV_REACTORS diff --git a/kernel/trace/rv/Makefile b/kernel/trace/rv/Makefile index 750e4ad6fa0f..51c95e2d2da6 100644 --- a/kernel/trace/rv/Makefile +++ b/kernel/trace/rv/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_RV_MON_STS) +=3D monitors/sts/sts.o obj-$(CONFIG_RV_MON_NRP) +=3D monitors/nrp/nrp.o obj-$(CONFIG_RV_MON_SSSW) +=3D monitors/sssw/sssw.o obj-$(CONFIG_RV_MON_OPID) +=3D monitors/opid/opid.o +obj-$(CONFIG_RV_MON_STALL) +=3D monitors/stall/stall.o # Add new monitors here obj-$(CONFIG_RV_REACTORS) +=3D rv_reactors.o obj-$(CONFIG_RV_REACT_PRINTK) +=3D reactor_printk.o diff --git a/kernel/trace/rv/monitors/stall/Kconfig b/kernel/trace/rv/monit= ors/stall/Kconfig new file mode 100644 index 000000000000..b19ba970c8af --- /dev/null +++ b/kernel/trace/rv/monitors/stall/Kconfig @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +config RV_MON_STALL + depends on RV + # XXX: add dependencies if there + select HA_MON_EVENTS_ID + bool "stall monitor" + help + auto-generated diff --git a/kernel/trace/rv/monitors/stall/stall.c b/kernel/trace/rv/monit= ors/stall/stall.c new file mode 100644 index 000000000000..c98ad9838146 --- /dev/null +++ b/kernel/trace/rv/monitors/stall/stall.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "stall" + +#include +#include + +#define RV_MON_TYPE RV_MON_PER_TASK +#include "stall.h" +#include + +static u64 threshold_jiffies =3D 1000; +module_param(threshold_jiffies, ullong, 0644); + +static u64 ha_get_env(struct ha_monitor *ha_mon, enum envs_stall env) +{ + if (env =3D=3D clk_stall) + return ha_get_clk_jiffy(ha_mon, env); + return ENV_INVALID_VALUE; +} + +static void ha_reset_env(struct ha_monitor *ha_mon, enum envs_stall env) +{ + if (env =3D=3D clk_stall) + ha_reset_clk_jiffy(ha_mon, env); +} + +static bool ha_verify_constraint(struct ha_monitor *ha_mon, + enum states curr_state, enum events event, + enum states next_state) +{ + bool res =3D true; + + if (curr_state =3D=3D dequeued_stall && event =3D=3D sched_wakeup_stall) + ha_reset_env(ha_mon, clk_stall); + + if (next_state =3D=3D curr_state || !res) + return res; + if (next_state =3D=3D enqueued_stall) + ha_start_timer_jiffy(ha_mon, clk_stall, threshold_jiffies); + else if (curr_state =3D=3D enqueued_stall) + res =3D !ha_cancel_timer(ha_mon); + return res; +} + +static void handle_sched_switch(void *data, bool preempt, + struct task_struct *prev, + struct task_struct *next, + unsigned int prev_state) +{ + if (!preempt && prev_state !=3D TASK_RUNNING) + da_handle_start_event(prev, sched_switch_wait_stall); + da_handle_event(next, sched_switch_in_stall); +} + +static void handle_sched_wakeup(void *data, struct task_struct *p) +{ + da_handle_event(p, sched_wakeup_stall); +} + +static int enable_stall(void) +{ + int retval; + + retval =3D da_monitor_init(); + if (retval) + return retval; + + rv_attach_trace_probe("stall", sched_switch, handle_sched_switch); + rv_attach_trace_probe("stall", sched_wakeup, handle_sched_wakeup); + + return 0; +} + +static void disable_stall(void) +{ + rv_stall.enabled =3D 0; + + rv_detach_trace_probe("stall", sched_switch, handle_sched_switch); + rv_detach_trace_probe("stall", sched_wakeup, handle_sched_wakeup); + + da_monitor_destroy(); +} + +static struct rv_monitor rv_stall =3D { + .name =3D "stall", + .description =3D "identify tasks stalled for longer than a threshold.", + .enable =3D enable_stall, + .disable =3D disable_stall, + .reset =3D da_monitor_reset_all, + .enabled =3D 0, +}; + +static int __init register_stall(void) +{ + return rv_register_monitor(&rv_stall, NULL); +} + +static void __exit unregister_stall(void) +{ + rv_unregister_monitor(&rv_stall); +} + +module_init(register_stall); +module_exit(unregister_stall); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Gabriele Monaco "); +MODULE_DESCRIPTION("stall: identify tasks stalled for longer than a thresh= old."); diff --git a/kernel/trace/rv/monitors/stall/stall.h b/kernel/trace/rv/monit= ors/stall/stall.h new file mode 100644 index 000000000000..89b32c97c455 --- /dev/null +++ b/kernel/trace/rv/monitors/stall/stall.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Automatically generated C representation of stall automaton + * For further information about this format, see kernel documentation: + * Documentation/trace/rv/deterministic_automata.rst + */ + +#define MONITOR_NAME stall + +enum states_stall { + dequeued_stall =3D 0, + enqueued_stall, + running_stall, + state_max_stall +}; + +#define INVALID_STATE state_max_stall + +enum events_stall { + sched_switch_in_stall =3D 0, + sched_switch_wait_stall, + sched_wakeup_stall, + event_max_stall +}; + +enum envs_stall { + clk_stall =3D 0, + env_max_stall, + env_max_stored_stall =3D env_max_stall +}; + +_Static_assert(env_max_stored_stall <=3D MAX_HA_ENV_LEN, "Not enough slots= "); + +struct automaton_stall { + char *state_names[state_max_stall]; + char *event_names[event_max_stall]; + char *env_names[env_max_stall]; + unsigned char function[state_max_stall][event_max_stall]; + unsigned char initial_state; + bool final_states[state_max_stall]; +}; + +static const struct automaton_stall automaton_stall =3D { + .state_names =3D { + "dequeued", + "enqueued", + "running" + }, + .event_names =3D { + "sched_switch_in", + "sched_switch_wait", + "sched_wakeup" + }, + .env_names =3D { + "clk" + }, + .function =3D { + { INVALID_STATE, INVALID_STATE, enqueued_stall }, + { running_stall, INVALID_STATE, INVALID_STATE }, + { running_stall, dequeued_stall, running_stall }, + }, + .initial_state =3D dequeued_stall, + .final_states =3D { 1, 0, 0 }, +}; diff --git a/kernel/trace/rv/monitors/stall/stall_trace.h b/kernel/trace/rv= /monitors/stall/stall_trace.h new file mode 100644 index 000000000000..6a7cc1b1d040 --- /dev/null +++ b/kernel/trace/rv/monitors/stall/stall_trace.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Snippet to be included in rv_trace.h + */ + +#ifdef CONFIG_RV_MON_STALL +DEFINE_EVENT(event_da_monitor_id, event_stall, + TP_PROTO(int id, char *state, char *event, char *next_state, bool fi= nal_state), + TP_ARGS(id, state, event, next_state, final_state)); + +DEFINE_EVENT(error_da_monitor_id, error_stall, + TP_PROTO(int id, char *state, char *event), + TP_ARGS(id, state, event)); + +DEFINE_EVENT(error_env_da_monitor_id, error_env_stall, + TP_PROTO(int id, char *state, char *event, char *env), + TP_ARGS(id, state, event, env)); +#endif /* CONFIG_RV_MON_STALL */ diff --git a/kernel/trace/rv/rv_trace.h b/kernel/trace/rv/rv_trace.h index dbb0cbbe15ca..3d9a4c70f523 100644 --- a/kernel/trace/rv/rv_trace.h +++ b/kernel/trace/rv/rv_trace.h @@ -187,6 +187,7 @@ DECLARE_EVENT_CLASS(error_env_da_monitor_id, __entry->env) ); =20 +#include // Add new monitors based on CONFIG_HA_MON_EVENTS_ID here =20 #endif diff --git a/tools/verification/models/stall.dot b/tools/verification/model= s/stall.dot new file mode 100644 index 000000000000..98e3ae47e104 --- /dev/null +++ b/tools/verification/models/stall.dot @@ -0,0 +1,20 @@ +digraph state_automaton { + center =3D true; + size =3D "7,11"; + {node [shape =3D circle] "enqueued"}; + {node [shape =3D plaintext, style=3Dinvis, label=3D""] "__init_dequeued"}; + {node [shape =3D doublecircle] "dequeued"}; + {node [shape =3D circle] "running"}; + "__init_dequeued" -> "dequeued"; + "enqueued" [label =3D "enqueued\nclk < threshold_jiffies"]; + "running" [label =3D "running"]; + "dequeued" [label =3D "dequeued", color =3D green3]; + "running" -> "running" [ label =3D "sched_switch_in\nsched_wakeup" ]; + "enqueued" -> "running" [ label =3D "sched_switch_in" ]; + "running" -> "dequeued" [ label =3D "sched_switch_wait" ]; + "dequeued" -> "enqueued" [ label =3D "sched_wakeup;reset(clk)" ]; + { rank =3D min ; + "__init_dequeued"; + "dequeued"; + } +} --=20 2.50.1 From nobody Sat Oct 4 15:57:18 2025 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 872B424EA8D for ; Thu, 14 Aug 2025 15:09:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184193; cv=none; b=ShWfcJ7dyLXq8QpDUhSgOmAhEL/NUJa7sEajsOKwWSkedd6DQsFulstsBnq+tILnN6Lpj2aKmklpgV9VkPQVAE71MP4evBxNeleoV+TXpJvr9DeoOWDZfMUY8QwvaXWpCEi+18NU2KJRJPdMadb2qqHA9m9Z96EfOh0iMMe5b1E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184193; c=relaxed/simple; bh=ja5mHrcZ1U1iD/ctofa2Ybi7+SMjYtWvrATSMWpdIH0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=twqacXkr+DhGDi832y5D1KxnStJeILEC4VlKHGGwhji5zrGAAghILUlkUJ2zlYfsKvP4t36IrgIZGSsq6LUftr7xl+kxk2ABJokGNxIaGMJ6uSB8n8zNxd1lnmpzF9eLY9jo41Lr4iwxIIWg/Nsn5jIqzZjCpuD0GoJ+u5qHWDI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=iVNKvR5Y; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="iVNKvR5Y" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1755184189; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Fs2rXWhD8HMpQa7A8RBvUe4TIFLUYWsfqJQ6/IX4RDM=; b=iVNKvR5Y5uJbVjqoxTT9YMW4Ih5S1cR5Wa7bPOdHU4yAzkcYK/JXDtEP2JhaMeIFErqA2E f+rqmej1nCnenNZDWTXZrQjd4FbN1TWOLRcB9NA8Sq0dnTaBfJ9YcFD05FDROU4zYZf71P OC7FadmAuMEjhZRolEdzAmTXLKDQH1Q= Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-511-Uq1dOcOkNSqiKfddU1jDUg-1; Thu, 14 Aug 2025 11:09:46 -0400 X-MC-Unique: Uq1dOcOkNSqiKfddU1jDUg-1 X-Mimecast-MFC-AGG-ID: Uq1dOcOkNSqiKfddU1jDUg_1755184184 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 8FD111800446; Thu, 14 Aug 2025 15:09:44 +0000 (UTC) Received: from gmonaco-thinkpadt14gen3.rmtit.com (unknown [10.44.32.52]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 08BAF180047F; Thu, 14 Aug 2025 15:09:39 +0000 (UTC) From: Gabriele Monaco To: linux-kernel@vger.kernel.org, Steven Rostedt , Jonathan Corbet , Masami Hiramatsu , linux-trace-kernel@vger.kernel.org, linux-doc@vger.kernel.org Cc: Gabriele Monaco , Nam Cao , Tomas Glozar , Juri Lelli , Clark Williams , John Kacur Subject: [RFC PATCH 13/17] rv: Convert the opid monitor to a hybrid automaton Date: Thu, 14 Aug 2025 17:08:05 +0200 Message-ID: <20250814150809.140739-14-gmonaco@redhat.com> In-Reply-To: <20250814150809.140739-1-gmonaco@redhat.com> References: <20250814150809.140739-1-gmonaco@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 Content-Type: text/plain; charset="utf-8" The opid monitor validates that wakeup and need_resched events only occur with interrupts and preemption disabled by following the preemptirq tracepoints. As reported in [1], those tracepoints might be inaccurate in some situations (e.g. NMIs). Since the monitor doesn't validate other ordering properties, remove the dependency on preemptirq tracepoints and convert the monitor to a hybrid automaton to validate the constraint during event handling. This makes the monitor more robust by also removing the workaround for interrupts missing the preemption tracepoints, which was working on PREEMPT_RT only and allows the monitor to be built on kernels without the preemptirqs tracepoints. [1] - https://lore.kernel.org/lkml/20250625120823.60600-1-gmonaco@redhat.com Signed-off-by: Gabriele Monaco --- Documentation/trace/rv/monitor_sched.rst | 62 +++--------- kernel/trace/rv/monitors/opid/Kconfig | 11 +-- kernel/trace/rv/monitors/opid/opid.c | 109 ++++++--------------- kernel/trace/rv/monitors/opid/opid.h | 88 ++++------------- kernel/trace/rv/monitors/opid/opid_trace.h | 4 + kernel/trace/rv/rv_trace.h | 2 +- tools/verification/models/sched/opid.dot | 36 ++----- 7 files changed, 77 insertions(+), 235 deletions(-) diff --git a/Documentation/trace/rv/monitor_sched.rst b/Documentation/trace= /rv/monitor_sched.rst index 3f8381ad9ec7..0b96d6e147c6 100644 --- a/Documentation/trace/rv/monitor_sched.rst +++ b/Documentation/trace/rv/monitor_sched.rst @@ -346,55 +346,21 @@ Monitor opid =20 The operations with preemption and irq disabled (opid) monitor ensures operations like ``wakeup`` and ``need_resched`` occur with interrupts and -preemption disabled or during interrupt context, in such case preemption m= ay -not be disabled explicitly. +preemption disabled. ``need_resched`` can be set by some RCU internals functions, in which case= it -doesn't match a task wakeup and might occur with only interrupts disabled:: - - | sched_need_resched - | sched_waking - | irq_entry - | +--------------------+ - v v | - +------------------------------------------------------+ - +----------- | disabled | <+ - | +------------------------------------------------------+ | - | | ^ | - | | preempt_disable sched_need_resched | - | preempt_enable | +--------------------+ | - | v | v | | - | +------------------------------------------------------+ | - | | irq_disabled | | - | +------------------------------------------------------+ | - | | | ^ | - | irq_entry irq_entry | | | - | sched_need_resched v | irq_disable | - | sched_waking +--------------+ | | | - | +----- | | irq_enable | | - | | | in_irq | | | | - | +----> | | | | | - | +--------------+ | | irq_dis= able - | | | | | - | irq_enable | irq_enable | | | - | v v | | - | #=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D# | - | H enabled H | - | #=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D# | - | | ^ ^ preempt_enable | | - | preempt_disable preempt_enable +--------------------+ | - | v | | - | +------------------+ | | - +----------> | preempt_disabled | -+ | - +------------------+ | - | | - +-------------------------------------------------------+ - -This monitor is designed to work on ``PREEMPT_RT`` kernels, the special ca= se of -events occurring in interrupt context is a shortcut to identify valid scen= arios -where the preemption tracepoints might not be visible, during interrupts -preemption is always disabled. On non- ``PREEMPT_RT`` kernels, the interru= pts -might invoke a softirq to set ``need_resched`` and wake up a task. This is -another special case that is currently not supported by the monitor. +doesn't match a task wakeup and might occur with only interrupts disabled. +The interrupt and preemption status are validated by the hybrid automaton +constraints when processing the events:: + + | + | + v + #=3D=3D=3D=3D=3D=3D=3D=3D=3D# sched_need_resched;irq_off =3D=3D 1 + H H sched_waking;irq_off =3D=3D 1 && preempt_off =3D=3D 1 + H any H ------------------------------------------------+ + H H | + H H <-----------------------------------------------+ + #=3D=3D=3D=3D=3D=3D=3D=3D=3D# =20 References ---------- diff --git a/kernel/trace/rv/monitors/opid/Kconfig b/kernel/trace/rv/monito= rs/opid/Kconfig index 561d32da572b..6d02e239b684 100644 --- a/kernel/trace/rv/monitors/opid/Kconfig +++ b/kernel/trace/rv/monitors/opid/Kconfig @@ -2,18 +2,13 @@ # config RV_MON_OPID depends on RV - depends on TRACE_IRQFLAGS - depends on TRACE_PREEMPT_TOGGLE depends on RV_MON_SCHED - default y if PREEMPT_RT - select DA_MON_EVENTS_IMPLICIT + default y + select HA_MON_EVENTS_IMPLICIT bool "opid monitor" help Monitor to ensure operations like wakeup and need resched occur with - interrupts and preemption disabled or during IRQs, where preemption - may not be disabled explicitly. - - This monitor is unstable on !PREEMPT_RT, say N unless you are testing i= t. + interrupts and preemption disabled. =20 For further information, see: Documentation/trace/rv/monitor_sched.rst diff --git a/kernel/trace/rv/monitors/opid/opid.c b/kernel/trace/rv/monitor= s/opid/opid.c index 7e9f23a76867..396841106aa4 100644 --- a/kernel/trace/rv/monitors/opid/opid.c +++ b/kernel/trace/rv/monitors/opid/opid.c @@ -10,94 +10,53 @@ #define MODULE_NAME "opid" =20 #include -#include -#include #include #include =20 #define RV_MON_TYPE RV_MON_PER_CPU #include "opid.h" -#include +#include =20 -#ifdef CONFIG_X86_LOCAL_APIC -#include - -static void handle_vector_irq_entry(void *data, int vector) -{ - da_handle_event(irq_entry_opid); -} - -static void attach_vector_irq(void) +static u64 ha_get_env(struct ha_monitor *ha_mon, enum envs_opid env) { - rv_attach_trace_probe("opid", local_timer_entry, handle_vector_irq_entry); - if (IS_ENABLED(CONFIG_IRQ_WORK)) - rv_attach_trace_probe("opid", irq_work_entry, handle_vector_irq_entry); - if (IS_ENABLED(CONFIG_SMP)) { - rv_attach_trace_probe("opid", reschedule_entry, handle_vector_irq_entry); - rv_attach_trace_probe("opid", call_function_entry, handle_vector_irq_ent= ry); - rv_attach_trace_probe("opid", call_function_single_entry, handle_vector_= irq_entry); + if (env =3D=3D irq_off_opid) + return irqs_disabled(); + else if (env =3D=3D preempt_off_opid) { + /* + * If CONFIG_PREEMPTION is enabled, then the tracepoint itself disables + * preemption (adding one to the preempt_count). Since we are + * interested in the preempt_count at the time the tracepoint was + * hit, we consider 1 as still enabled. + */ + if (IS_ENABLED(CONFIG_PREEMPTION)) + return (preempt_count() & PREEMPT_MASK) > 1; + return true; } + return ENV_INVALID_VALUE; } =20 -static void detach_vector_irq(void) -{ - rv_detach_trace_probe("opid", local_timer_entry, handle_vector_irq_entry); - if (IS_ENABLED(CONFIG_IRQ_WORK)) - rv_detach_trace_probe("opid", irq_work_entry, handle_vector_irq_entry); - if (IS_ENABLED(CONFIG_SMP)) { - rv_detach_trace_probe("opid", reschedule_entry, handle_vector_irq_entry); - rv_detach_trace_probe("opid", call_function_entry, handle_vector_irq_ent= ry); - rv_detach_trace_probe("opid", call_function_single_entry, handle_vector_= irq_entry); - } -} - -#else -/* We assume irq_entry tracepoints are sufficient on other architectures */ -static void attach_vector_irq(void) { } -static void detach_vector_irq(void) { } -#endif - -static void handle_irq_disable(void *data, unsigned long ip, unsigned long= parent_ip) -{ - da_handle_event(irq_disable_opid); -} - -static void handle_irq_enable(void *data, unsigned long ip, unsigned long = parent_ip) -{ - da_handle_event(irq_enable_opid); -} - -static void handle_irq_entry(void *data, int irq, struct irqaction *action) -{ - da_handle_event(irq_entry_opid); -} - -static void handle_preempt_disable(void *data, unsigned long ip, unsigned = long parent_ip) -{ - da_handle_event(preempt_disable_opid); -} - -static void handle_preempt_enable(void *data, unsigned long ip, unsigned l= ong parent_ip) +static bool ha_verify_constraint(struct ha_monitor *ha_mon, + enum states curr_state, enum events event, + enum states next_state) { - da_handle_event(preempt_enable_opid); + bool res =3D true; + + if (curr_state =3D=3D any_opid && event =3D=3D sched_need_resched_opid) + res =3D ha_get_env(ha_mon, irq_off_opid) =3D=3D 1ull; + else if (curr_state =3D=3D any_opid && event =3D=3D sched_waking_opid) + res =3D ha_get_env(ha_mon, irq_off_opid) =3D=3D 1ull && + ha_get_env(ha_mon, preempt_off_opid) =3D=3D 1ull; + return res; } =20 static void handle_sched_need_resched(void *data, struct task_struct *tsk,= int cpu, int tif) { - /* The monitor's intitial state is not in_irq */ - if (this_cpu_read(hardirq_context)) - da_handle_event(sched_need_resched_opid); - else - da_handle_start_event(sched_need_resched_opid); + da_handle_start_event(sched_need_resched_opid); } =20 static void handle_sched_waking(void *data, struct task_struct *p) { - /* The monitor's intitial state is not in_irq */ - if (this_cpu_read(hardirq_context)) - da_handle_event(sched_waking_opid); - else - da_handle_start_event(sched_waking_opid); + da_handle_start_event(sched_waking_opid); } =20 static int enable_opid(void) @@ -108,14 +67,8 @@ static int enable_opid(void) if (retval) return retval; =20 - rv_attach_trace_probe("opid", irq_disable, handle_irq_disable); - rv_attach_trace_probe("opid", irq_enable, handle_irq_enable); - rv_attach_trace_probe("opid", irq_handler_entry, handle_irq_entry); - rv_attach_trace_probe("opid", preempt_disable, handle_preempt_disable); - rv_attach_trace_probe("opid", preempt_enable, handle_preempt_enable); rv_attach_trace_probe("opid", sched_set_need_resched_tp, handle_sched_nee= d_resched); rv_attach_trace_probe("opid", sched_waking, handle_sched_waking); - attach_vector_irq(); =20 return 0; } @@ -124,14 +77,8 @@ static void disable_opid(void) { rv_opid.enabled =3D 0; =20 - rv_detach_trace_probe("opid", irq_disable, handle_irq_disable); - rv_detach_trace_probe("opid", irq_enable, handle_irq_enable); - rv_detach_trace_probe("opid", irq_handler_entry, handle_irq_entry); - rv_detach_trace_probe("opid", preempt_disable, handle_preempt_disable); - rv_detach_trace_probe("opid", preempt_enable, handle_preempt_enable); rv_detach_trace_probe("opid", sched_set_need_resched_tp, handle_sched_nee= d_resched); rv_detach_trace_probe("opid", sched_waking, handle_sched_waking); - detach_vector_irq(); =20 da_monitor_destroy(); } diff --git a/kernel/trace/rv/monitors/opid/opid.h b/kernel/trace/rv/monitor= s/opid/opid.h index 5014f1b85ecf..7c39641c65eb 100644 --- a/kernel/trace/rv/monitors/opid/opid.h +++ b/kernel/trace/rv/monitors/opid/opid.h @@ -8,30 +8,31 @@ #define MONITOR_NAME opid =20 enum states_opid { - disabled_opid =3D 0, - enabled_opid, - in_irq_opid, - irq_disabled_opid, - preempt_disabled_opid, + any_opid =3D 0, state_max_opid }; =20 #define INVALID_STATE state_max_opid =20 enum events_opid { - irq_disable_opid =3D 0, - irq_enable_opid, - irq_entry_opid, - preempt_disable_opid, - preempt_enable_opid, - sched_need_resched_opid, + sched_need_resched_opid =3D 0, sched_waking_opid, event_max_opid }; =20 +enum envs_opid { + irq_off_opid =3D 0, + preempt_off_opid, + env_max_opid, + env_max_stored_opid =3D irq_off_opid +}; + +_Static_assert(env_max_stored_opid <=3D MAX_HA_ENV_LEN, "Not enough slots"= ); + struct automaton_opid { char *state_names[state_max_opid]; char *event_names[event_max_opid]; + char *env_names[env_max_opid]; unsigned char function[state_max_opid][event_max_opid]; unsigned char initial_state; bool final_states[state_max_opid]; @@ -39,68 +40,19 @@ struct automaton_opid { =20 static const struct automaton_opid automaton_opid =3D { .state_names =3D { - "disabled", - "enabled", - "in_irq", - "irq_disabled", - "preempt_disabled" + "any" }, .event_names =3D { - "irq_disable", - "irq_enable", - "irq_entry", - "preempt_disable", - "preempt_enable", "sched_need_resched", "sched_waking" }, + .env_names =3D { + "irq_off", + "preempt_off" + }, .function =3D { - { - INVALID_STATE, - preempt_disabled_opid, - disabled_opid, - INVALID_STATE, - irq_disabled_opid, - disabled_opid, - disabled_opid - }, - { - irq_disabled_opid, - INVALID_STATE, - INVALID_STATE, - preempt_disabled_opid, - enabled_opid, - INVALID_STATE, - INVALID_STATE - }, - { - INVALID_STATE, - enabled_opid, - in_irq_opid, - INVALID_STATE, - INVALID_STATE, - in_irq_opid, - in_irq_opid - }, - { - INVALID_STATE, - enabled_opid, - in_irq_opid, - disabled_opid, - INVALID_STATE, - irq_disabled_opid, - INVALID_STATE - }, - { - disabled_opid, - INVALID_STATE, - INVALID_STATE, - INVALID_STATE, - enabled_opid, - INVALID_STATE, - INVALID_STATE - }, + { any_opid, any_opid }, }, - .initial_state =3D disabled_opid, - .final_states =3D { 0, 1, 0, 0, 0 }, + .initial_state =3D any_opid, + .final_states =3D { 1 }, }; diff --git a/kernel/trace/rv/monitors/opid/opid_trace.h b/kernel/trace/rv/m= onitors/opid/opid_trace.h index 3df6ff955c30..b04005b64208 100644 --- a/kernel/trace/rv/monitors/opid/opid_trace.h +++ b/kernel/trace/rv/monitors/opid/opid_trace.h @@ -12,4 +12,8 @@ DEFINE_EVENT(event_da_monitor, event_opid, DEFINE_EVENT(error_da_monitor, error_opid, TP_PROTO(char *state, char *event), TP_ARGS(state, event)); + +DEFINE_EVENT(error_env_da_monitor, error_env_opid, + TP_PROTO(char *state, char *event, char *env), + TP_ARGS(state, event, env)); #endif /* CONFIG_RV_MON_OPID */ diff --git a/kernel/trace/rv/rv_trace.h b/kernel/trace/rv/rv_trace.h index 3d9a4c70f523..601b03179328 100644 --- a/kernel/trace/rv/rv_trace.h +++ b/kernel/trace/rv/rv_trace.h @@ -62,7 +62,6 @@ DECLARE_EVENT_CLASS(error_da_monitor, #include #include #include -#include // Add new monitors based on CONFIG_DA_MON_EVENTS_IMPLICIT here =20 #ifdef CONFIG_HA_MON_EVENTS_IMPLICIT @@ -92,6 +91,7 @@ DECLARE_EVENT_CLASS(error_env_da_monitor, ); =20 // Add new monitors based on CONFIG_HA_MON_EVENTS_IMPLICIT here +#include =20 #endif =20 diff --git a/tools/verification/models/sched/opid.dot b/tools/verification/= models/sched/opid.dot index 840052f6952b..511051fce430 100644 --- a/tools/verification/models/sched/opid.dot +++ b/tools/verification/models/sched/opid.dot @@ -1,35 +1,13 @@ digraph state_automaton { center =3D true; size =3D "7,11"; - {node [shape =3D plaintext, style=3Dinvis, label=3D""] "__init_disabled"}; - {node [shape =3D circle] "disabled"}; - {node [shape =3D doublecircle] "enabled"}; - {node [shape =3D circle] "enabled"}; - {node [shape =3D circle] "in_irq"}; - {node [shape =3D circle] "irq_disabled"}; - {node [shape =3D circle] "preempt_disabled"}; - "__init_disabled" -> "disabled"; - "disabled" [label =3D "disabled"]; - "disabled" -> "disabled" [ label =3D "sched_need_resched\nsched_waking\ni= rq_entry" ]; - "disabled" -> "irq_disabled" [ label =3D "preempt_enable" ]; - "disabled" -> "preempt_disabled" [ label =3D "irq_enable" ]; - "enabled" [label =3D "enabled", color =3D green3]; - "enabled" -> "enabled" [ label =3D "preempt_enable" ]; - "enabled" -> "irq_disabled" [ label =3D "irq_disable" ]; - "enabled" -> "preempt_disabled" [ label =3D "preempt_disable" ]; - "in_irq" [label =3D "in_irq"]; - "in_irq" -> "enabled" [ label =3D "irq_enable" ]; - "in_irq" -> "in_irq" [ label =3D "sched_need_resched\nsched_waking\nirq_e= ntry" ]; - "irq_disabled" [label =3D "irq_disabled"]; - "irq_disabled" -> "disabled" [ label =3D "preempt_disable" ]; - "irq_disabled" -> "enabled" [ label =3D "irq_enable" ]; - "irq_disabled" -> "in_irq" [ label =3D "irq_entry" ]; - "irq_disabled" -> "irq_disabled" [ label =3D "sched_need_resched" ]; - "preempt_disabled" [label =3D "preempt_disabled"]; - "preempt_disabled" -> "disabled" [ label =3D "irq_disable" ]; - "preempt_disabled" -> "enabled" [ label =3D "preempt_enable" ]; + {node [shape =3D plaintext, style=3Dinvis, label=3D""] "__init_any"}; + {node [shape =3D doublecircle] "any"}; + "__init_any" -> "any"; + "any" [label =3D "any", color =3D green3]; + "any" -> "any" [ label =3D "sched_need_resched;irq_off =3D=3D 1\nsched_wa= king;irq_off =3D=3D 1 && preempt_off =3D=3D 1" ]; { rank =3D min ; - "__init_disabled"; - "disabled"; + "__init_any"; + "any"; } } --=20 2.50.1 From nobody Sat Oct 4 15:57:18 2025 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9C472255E23 for ; Thu, 14 Aug 2025 15:09:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184199; cv=none; b=V2As5rVa/iXOZTl47gkHmg7PVfbNkhbmPjH431pae3xGKwcJib7e94Vw143heuJZwq85F6vi3X7+DDTh1bl2oPECL6o3337bhXZZmouU3s+FxlEELc7ZPVqXJwuEir6c5NJ4sLO0cz57gvpZH0DyyiOHebwPhoD3H0bgcwKZoyI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184199; c=relaxed/simple; bh=QRDBhWFnNSTSVkcgvzU4Y7LqOYfyE5TCUTroeOjHI7w=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MFwgsuxmtzUxe2UidMESWutmV0r5aoWyCr9/minQEYl6N/FST4upLeB2+EiL2FLj89tL075kFhLJmKBzh4fPVu7B/zTlxtui6lv868+7/EW1YHBD/A63hn+ucRnxrQDmy5lFKEgu50boVF1lBor6zDWCvWK+TR6p4RukaTktzho= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=Q176eCG9; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="Q176eCG9" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1755184196; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=cRA31LDqBAXKj36TOsIql+9CJrq3iDC+cmEwC64LKIE=; b=Q176eCG9L24Bvd09zzQXR8nIZfJ4938t7wyj6/rDfAO/ALsZ2M5lIR5wRXSjumjsF/eV4j tgI7eGXcN5BpwAcyoSDgIbaiL0utIs+kF6qCQvxOc09ohCLuyCaUisbiJFuwLe8fm3iNAm l5dyA9gg6Uqtiov/0+OKZ1TkEieRrp0= Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-359-Tq68DzZBOQaJ0N6rBLusnQ-1; Thu, 14 Aug 2025 11:09:53 -0400 X-MC-Unique: Tq68DzZBOQaJ0N6rBLusnQ-1 X-Mimecast-MFC-AGG-ID: Tq68DzZBOQaJ0N6rBLusnQ_1755184192 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 9F2C218003FD; Thu, 14 Aug 2025 15:09:51 +0000 (UTC) Received: from gmonaco-thinkpadt14gen3.rmtit.com (unknown [10.44.32.52]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id B29FC180047F; Thu, 14 Aug 2025 15:09:45 +0000 (UTC) From: Gabriele Monaco To: linux-kernel@vger.kernel.org, Steven Rostedt , Masami Hiramatsu , Ingo Molnar , Peter Zijlstra , linux-trace-kernel@vger.kernel.org Cc: Gabriele Monaco , Nam Cao , Tomas Glozar , Juri Lelli , Clark Williams , John Kacur Subject: [RFC PATCH 14/17] sched: Add deadline tracepoints Date: Thu, 14 Aug 2025 17:08:06 +0200 Message-ID: <20250814150809.140739-15-gmonaco@redhat.com> In-Reply-To: <20250814150809.140739-1-gmonaco@redhat.com> References: <20250814150809.140739-1-gmonaco@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 Content-Type: text/plain; charset="utf-8" Add the following tracepoints: * sched_dl_throttle(dl): Called when a deadline entity is throttled * sched_dl_replenish(dl): Called when a deadline entity's runtime is replenished * sched_dl_server_start(dl): Called when a deadline server is started * sched_dl_server_stop(dl, hard): Called when a deadline server is stopped (hard) or put to idle waiting for the next period (!hard) Those tracepoints can be useful to validate the deadline scheduler with RV and are not exported to tracefs. Signed-off-by: Gabriele Monaco --- include/trace/events/sched.h | 55 ++++++++++++++++++++++++++++++++++++ kernel/sched/deadline.c | 8 ++++++ 2 files changed, 63 insertions(+) diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index 7b2645b50e78..f34cc1dc4a13 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -609,6 +609,45 @@ TRACE_EVENT(sched_pi_setprio, __entry->oldprio, __entry->newprio) ); =20 +/* +DECLARE_EVENT_CLASS(sched_dl_template, + + TP_PROTO(struct sched_dl_entity *dl), + + TP_ARGS(dl), + + TP_STRUCT__entry( + __field( struct task_struct *, tsk ) + __string( comm, dl->dl_server ? "server" : container_of(dl, struct task= _struct, dl)->comm ) + __field( pid_t, pid ) + __field( s64, runtime ) + __field( u64, deadline ) + __field( int, dl_yielded ) + ), + + TP_fast_assign( + __assign_str(comm); + __entry->pid =3D dl->dl_server ? -1 : container_of(dl, struct task_stru= ct, dl)->pid; + __entry->runtime =3D dl->runtime; + __entry->deadline =3D dl->deadline; + __entry->dl_yielded =3D dl->dl_yielded; + ), + + TP_printk("comm=3D%s pid=3D%d runtime=3D%lld deadline=3D%lld yielded=3D%d= ", + __get_str(comm), __entry->pid, + __entry->runtime, __entry->deadline, + __entry->dl_yielded) +); + +DEFINE_EVENT(sched_dl_template, sched_dl_throttle, + TP_PROTO(struct sched_dl_entity *dl), + TP_ARGS(dl)); + +DEFINE_EVENT(sched_dl_template, sched_dl_replenish, + TP_PROTO(struct sched_dl_entity *dl), + TP_ARGS(dl)); +*/ + #ifdef CONFIG_DETECT_HUNG_TASK TRACE_EVENT(sched_process_hang, TP_PROTO(struct task_struct *tsk), @@ -896,6 +935,22 @@ DECLARE_TRACE(sched_set_need_resched, TP_PROTO(struct task_struct *tsk, int cpu, int tif), TP_ARGS(tsk, cpu, tif)); =20 +DECLARE_TRACE(sched_dl_throttle, + TP_PROTO(struct sched_dl_entity *dl), + TP_ARGS(dl)); + +DECLARE_TRACE(sched_dl_replenish, + TP_PROTO(struct sched_dl_entity *dl), + TP_ARGS(dl)); + +DECLARE_TRACE(sched_dl_server_start, + TP_PROTO(struct sched_dl_entity *dl), + TP_ARGS(dl)); + +DECLARE_TRACE(sched_dl_server_stop, + TP_PROTO(struct sched_dl_entity *dl, bool hard), + TP_ARGS(dl, hard)); + #endif /* _TRACE_SCHED_H */ =20 /* This part must be outside protection */ diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index e2d51f4306b3..f8284accb6b4 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -742,6 +742,7 @@ static inline void replenish_dl_new_period(struct sched= _dl_entity *dl_se, dl_se->dl_throttled =3D 1; dl_se->dl_defer_armed =3D 1; } + trace_sched_dl_replenish_tp(dl_se); } =20 /* @@ -852,6 +853,9 @@ static void replenish_dl_entity(struct sched_dl_entity = *dl_se) if (dl_time_before(dl_se->deadline, rq_clock(rq))) { printk_deferred_once("sched: DL replenish lagged too much\n"); replenish_dl_new_period(dl_se, rq); + } else { + /* replenish_dl_new_period is also tracing */ + trace_sched_dl_replenish_tp(dl_se); } =20 if (dl_se->dl_yielded) @@ -1482,6 +1486,7 @@ static void update_curr_dl_se(struct rq *rq, struct s= ched_dl_entity *dl_se, s64 =20 throttle: if (dl_runtime_exceeded(dl_se) || dl_se->dl_yielded) { + trace_sched_dl_throttle_tp(dl_se); dl_se->dl_throttled =3D 1; =20 /* If requested, inform the user about runtime overruns. */ @@ -1590,6 +1595,7 @@ void dl_server_start(struct sched_dl_entity *dl_se) if (!dl_server(dl_se) || dl_se->dl_server_active) return; =20 + trace_sched_dl_server_start_tp(dl_se); dl_se->dl_server_active =3D 1; enqueue_dl_entity(dl_se, ENQUEUE_WAKEUP); if (!dl_task(dl_se->rq->curr) || dl_entity_preempt(dl_se, &rq->curr->dl)) @@ -1601,6 +1607,7 @@ void dl_server_stop(struct sched_dl_entity *dl_se) if (!dl_server(dl_se) || !dl_server_active(dl_se)) return; =20 + trace_sched_dl_server_stop_tp(dl_se, true); dequeue_dl_entity(dl_se, DEQUEUE_SLEEP); hrtimer_try_to_cancel(&dl_se->dl_timer); dl_se->dl_defer_armed =3D 0; @@ -1618,6 +1625,7 @@ static bool dl_server_stopped(struct sched_dl_entity = *dl_se) return true; } =20 + trace_sched_dl_server_stop_tp(dl_se, false); dl_se->dl_server_idle =3D 1; return false; } --=20 2.50.1 From nobody Sat Oct 4 15:57:18 2025 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E86C92264D6 for ; Thu, 14 Aug 2025 15:10:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184205; cv=none; b=SqnvS9ZGHCi6UMWeHizwuyNPaHUXnguPTHJMjefsetal20u1T79H2ULcc//CdLXxAhA6inz4zmQy/Lo+TnS+kQP61eVi55m3Z1sNhcmJvEkJnVvrBDu6NqmS7Ze9izCI7JvTVT9p86D67zk5/8C8w4tg7lQtM7QOfE55VjHqKNo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184205; c=relaxed/simple; bh=bud/1gruZ9iKND/veVSmVqKHAQZnl0XC8A1Lj6PTrig=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Y5hAPBQTwbqv4D0mtLXJhdCwamufGVNPubq8KC1AG9VVkrn3+XZi9eDMNmVfFr2GqEXO5sVcjdbccQppTwMFgJeRWcyTfKNBvzs38o6AL3LspicdFHmLj8d0z3rcV5ZZSJe8fScniIsUktsNg3DcKskv9DO7N15Jtotphs/oy3g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=DsIJlgiL; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="DsIJlgiL" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1755184201; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=KFaY0/yysxY5stqXu/Bmvf5pW5QIS/j1dztanLkj1Ng=; b=DsIJlgiLXz1RoweOnK6HYGHo0wbMcAc1yqhv3rdPa4ayweLrWU/vCFr7kSsEfozLvTnOb5 Eue+GIVwP/e6amwCmWxwjc037kyQX61K/iBJKlowCVuidxQBfKRdEICzmrPVn9lhnZKrVS kJPaL7MJgK752CKl9bvpONboF9RAAqA= Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-651-Op84w7lnPp2mRF27uvncPA-1; Thu, 14 Aug 2025 11:09:58 -0400 X-MC-Unique: Op84w7lnPp2mRF27uvncPA-1 X-Mimecast-MFC-AGG-ID: Op84w7lnPp2mRF27uvncPA_1755184197 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id AAF9F180048E; Thu, 14 Aug 2025 15:09:57 +0000 (UTC) Received: from gmonaco-thinkpadt14gen3.rmtit.com (unknown [10.44.32.52]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 2B7821800280; Thu, 14 Aug 2025 15:09:52 +0000 (UTC) From: Gabriele Monaco To: linux-kernel@vger.kernel.org, Steven Rostedt , linux-trace-kernel@vger.kernel.org Cc: Gabriele Monaco , Nam Cao , Tomas Glozar , Juri Lelli , Clark Williams , John Kacur Subject: [RFC PATCH 15/17] rv: Add support for per-object monitors in DA/HA Date: Thu, 14 Aug 2025 17:08:07 +0200 Message-ID: <20250814150809.140739-16-gmonaco@redhat.com> In-Reply-To: <20250814150809.140739-1-gmonaco@redhat.com> References: <20250814150809.140739-1-gmonaco@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 Content-Type: text/plain; charset="utf-8" RV deterministic and hybrid automata currently only support global, per-cpu and per-task monitors. It isn't possible to write a model that would follow some different type of object, like a deadline entity or a lock. Define the generic per-object monitor implementation which shares most of the implementation with the per-task monitors. Specifically, the user needs to define how to get an id for the object (e.g. pid for tasks) and the data type for the monitor_target (e.g. struct task_struct * for tasks). The monitor storage (e.g. the rv monitor, pointer to the target, etc.) is stored in a hash table indexed by id. Monitor storage objects are automatically allocated unless specified otherwise (e.g. if the creation context is unsafe for allocation). Signed-off-by: Gabriele Monaco --- include/linux/rv.h | 2 +- include/rv/da_common.h | 21 +++ include/rv/da_monitor.h | 325 +++++++++++++++++++++++++++++++++++++--- include/rv/ha_monitor.h | 36 +++-- 4 files changed, 352 insertions(+), 32 deletions(-) create mode 100644 include/rv/da_common.h diff --git a/include/linux/rv.h b/include/linux/rv.h index 6a7594080db1..6c49db8d4d07 100644 --- a/include/linux/rv.h +++ b/include/linux/rv.h @@ -16,6 +16,7 @@ #define RV_MON_GLOBAL 0 #define RV_MON_PER_CPU 1 #define RV_MON_PER_TASK 2 +#define RV_MON_PER_OBJ 3 =20 #ifdef CONFIG_RV #include @@ -100,7 +101,6 @@ struct ha_monitor { u64 env_store[MAX_HA_ENV_LEN]; struct hrtimer timer; }; -#define to_ha_monitor(da) container_of(da, struct ha_monitor, da_mon) =20 #else =20 diff --git a/include/rv/da_common.h b/include/rv/da_common.h new file mode 100644 index 000000000000..67d817aee18e --- /dev/null +++ b/include/rv/da_common.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2025 Red Hat, Inc. + * + * Common definitions for DA and HA monitors. + */ +#ifndef _RV_DA_COMMON_H +#define _RV_DA_COMMON_H + +#include + +/* + * ID monitor types (per-task and per-object) have an opaque type, this is + * defined by default for the per-task case but must be defined by the mon= itor + * in case of per-object monitors. + */ +#if RV_MON_TYPE =3D=3D RV_MON_PER_TASK +typedef struct task_struct *monitor_target; +#endif + +#endif /* _RV_DA_COMMON_H */ diff --git a/include/rv/da_monitor.h b/include/rv/da_monitor.h index d95ece5b7908..737bfeb2b48f 100644 --- a/include/rv/da_monitor.h +++ b/include/rv/da_monitor.h @@ -14,11 +14,13 @@ #ifndef _RV_DA_MONITOR_H #define _RV_DA_MONITOR_H =20 +#include #include #include #include #include -#include +#include +#include =20 #define RV_MONITOR_NAME CONCATENATE(rv_, MONITOR_NAME) #define RV_DA_MON_NAME CONCATENATE(da_mon_, MONITOR_NAME) @@ -51,6 +53,38 @@ static struct rv_monitor RV_MONITOR_NAME; #define da_monitor_reset_hook(da_mon) #endif =20 +/* + * Hook to allow the implementation of per-obj monitors: define it with a + * function that takes the object and a da_mon (can be NULL) and returns t= he + * (newly created) da_monitor for the objects. + * The monitor can run allocation manually if the start condition is in a + * context potentially problematic for allocation (e.g. while scheduling). + * In such case, if the storage was pre-allocated without a target, set it= now. + */ +#if !defined(da_monitor_start_hook) && RV_MON_TYPE =3D=3D RV_MON_PER_OBJ +#ifdef DA_SKIP_AUTO_ALLOC +static inline struct da_monitor * +da_fill_empty_storage(monitor_target target, struct da_monitor *da_mon); +#define da_monitor_start_hook da_fill_empty_storage +#else +#define da_monitor_start_hook da_create_storage +static struct da_monitor *da_create_storage(monitor_target target, struct = da_monitor *da_mon); +#endif /* DA_SKIP_AUTO_ALLOC */ +#else +#define da_monitor_start_hook(target,da_mon) (da_mon) +#endif + +/* + * Define a guard (e.g. lock/RCU) for access to the da_monitor. + * This is used to synchronise readers (e.g. da_get_monitor) but also writ= ers + * (da_create_storage and da_destroy_storage) against da_monitor_destroy. + */ +#if !defined(da_guard_monitor) && RV_MON_TYPE =3D=3D RV_MON_PER_OBJ +#define da_guard_monitor() guard(rcu)() +#else +#define da_guard_monitor() +#endif + #ifdef CONFIG_RV_REACTORS =20 static void cond_react(enum states curr_state, enum events event) @@ -178,16 +212,21 @@ static inline bool da_event(struct da_monitor *da_mon= , enum events event) return false; } =20 -#elif RV_MON_TYPE =3D=3D RV_MON_PER_TASK +#elif RV_MON_TYPE =3D=3D RV_MON_PER_TASK || RV_MON_TYPE =3D=3D RV_MON_PER_= OBJ /* - * Event handler for per_task monitors. + * Event handler for per_task/per_object monitors. * * Retry in case there is a race between getting and setting the next stat= e, * warn and reset the monitor if it runs out of retries. The monitor shoul= d be * able to handle various orders. */ =20 -static inline bool da_event(struct da_monitor *da_mon, struct task_struct = *tsk, +#ifndef da_id_type +#define da_id_type int +#endif +static inline da_id_type da_get_id(monitor_target target); + +static inline bool da_event(struct da_monitor *da_mon, monitor_target targ= et, enum events event) { enum states curr_state, next_state; @@ -197,16 +236,16 @@ static inline bool da_event(struct da_monitor *da_mon= , struct task_struct *tsk, next_state =3D model_get_next_state(curr_state, event); if (next_state =3D=3D INVALID_STATE) { cond_react(curr_state, event); - CONCATENATE(trace_error_, MONITOR_NAME)(tsk->pid, + CONCATENATE(trace_error_, MONITOR_NAME)(da_get_id(target), model_get_state_name(curr_state), model_get_event_name(event)); return false; } if (likely(try_cmpxchg(&da_mon->curr_state, &curr_state, next_state))) { - if (!da_monitor_event_hook(tsk, curr_state, event, next_state)) + if (!da_monitor_event_hook(target, curr_state, event, next_state)) return false; =20 - CONCATENATE(trace_event_, MONITOR_NAME)(tsk->pid, + CONCATENATE(trace_event_, MONITOR_NAME)(da_get_id(target), model_get_state_name(curr_state), model_get_event_name(event), model_get_state_name(next_state), @@ -333,6 +372,24 @@ static inline struct da_monitor *da_get_monitor(struct= task_struct *tsk) return &tsk->rv[task_mon_slot].da_mon; } =20 +/* + * 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 task_struct, rv[task_mon_slot].da_mon); +} + +/* + * da_get_id - return the id associated to the target + * + * For per-task monitors, the id is the task's PID. + */ +static inline da_id_type da_get_id(monitor_target target) +{ + return target->pid; +} + static void da_monitor_reset_all(void) { struct task_struct *g, *p; @@ -379,6 +436,208 @@ static inline void da_monitor_destroy(void) task_mon_slot =3D RV_PER_TASK_MONITOR_INIT; return; } + +#elif RV_MON_TYPE =3D=3D RV_MON_PER_OBJ +/* + * Functions to define, init and get a per-object monitor. + */ + +static struct kmem_cache *da_monitor_cache; + +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_typ= e id) +{ + struct da_monitor_storage *mon_storage; + + // TODO perhaps no need for the NOWAIT if not auto allocating + // NOT from tracepoints! Perhaps get it as an argument.. + mon_storage =3D kmem_cache_zalloc(da_monitor_cache, GFP_NOWAIT); + if (!mon_storage) + return NULL; + + hash_add_rcu(da_monitor_ht, &mon_storage->node, id); + mon_storage->id =3D 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 si= ngle + * event for target (e.g. sched_switch), it's safe to call this without lo= cks. + */ +static inline struct da_monitor *da_create_storage(monitor_target target, = struct da_monitor *da_mon) +{ + struct da_monitor_storage *mon_storage; + da_id_type id =3D da_get_id(target); + + if (da_mon) + return da_mon; + + mon_storage =3D da_create_empty_storage(id); + if (!mon_storage) + return NULL; + + mon_storage->target =3D 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 i= d) +{ + 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 =3D=3D id) + return mon_storage; + } + + return NULL; +} + +/* + * da_get_monitor - return the monitor for target + */ +static struct da_monitor *da_get_monitor(monitor_target target) +{ + struct da_monitor_storage *mon_storage; + + mon_storage =3D __da_get_mon_storage(da_get_id(target)); + 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_create_conditional - create the per-object storage if not already th= ere + * + * This needs a lookup so should be guarded by RCU, the condition is check= ed + * directly in da_create_storage() + */ +static inline void da_create_conditional(monitor_target target) +{ + da_guard_monitor(); + da_create_storage(target, da_get_monitor(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 monito= r in + * an environment where allocation is unsafe. + */ +static inline struct da_monitor * +da_fill_empty_storage(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 =3D t= arget; + 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; + + da_guard_monitor(); + mon_storage =3D __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 wi= thout + * locks. + * This function includes an RCU read-side critical section to synchronise + * against da_monitor_destroy(). + */ +static inline void da_destroy_storage(monitor_target target) +{ + struct da_monitor_storage *mon_storage; + + da_guard_monitor(); + mon_storage =3D __da_get_mon_storage(da_get_id(target)); + + if (!mon_storage) + return; + hash_del_rcu(&mon_storage->node); + kfree_rcu(mon_storage, rcu); +} + +static void da_monitor_reset_all(void) +{ + struct da_monitor_storage *mon_storage; + int bkt; + + rcu_read_lock(); + hash_for_each_rcu(da_monitor_ht, bkt, mon_storage, node) + da_monitor_reset(&mon_storage->rv.da_mon); + rcu_read_unlock(); +} + +static inline int da_monitor_init(void) +{ + hash_init(da_monitor_ht); + da_monitor_cache =3D kmem_cache_create(__stringify(MONITOR_NAME) "-cache", + sizeof(struct da_monitor_storage), + NULL, 0); + if (!da_monitor_cache) + return -ENOMEM; + return 0; +} + +static inline void da_monitor_destroy(void) +{ + struct da_monitor_storage *mon_storage; + struct hlist_node *tmp; + int bkt; + + /* + * This function is called after all probes are disabled, we need only + * worry about concurrency against old events. + */ + synchronize_rcu(); + hash_for_each_safe(da_monitor_ht, bkt, tmp, mon_storage, node) { + hash_del_rcu(&mon_storage->node); + kfree(mon_storage); + } + rcu_barrier(); + kmem_cache_destroy(da_monitor_cache); +} + #endif /* RV_MON_TYPE */ =20 #if RV_MON_TYPE =3D=3D RV_MON_GLOBAL || RV_MON_TYPE =3D=3D RV_MON_PER_CPU @@ -464,17 +723,17 @@ static inline bool da_handle_start_run_event(enum eve= nts event) return 1; } =20 -#elif RV_MON_TYPE =3D=3D RV_MON_PER_TASK +#elif RV_MON_TYPE =3D=3D RV_MON_PER_TASK || RV_MON_TYPE =3D=3D RV_MON_PER_= OBJ /* - * Handle event for per task. + * Handle event for per task/object. */ =20 static inline void __da_handle_event(struct da_monitor *da_mon, - struct task_struct *tsk, enum events event) + monitor_target target, enum events event) { bool retval; =20 - retval =3D da_event(da_mon, tsk, event); + retval =3D da_event(da_mon, target, event); if (!retval) da_monitor_reset(da_mon); } @@ -482,16 +741,22 @@ static inline void __da_handle_event(struct da_monito= r *da_mon, /* * da_handle_event - handle an event */ -static inline void da_handle_event(struct task_struct *tsk, enum events ev= ent) +static inline void da_handle_event(monitor_target target, enum events even= t) { - struct da_monitor *da_mon =3D da_get_monitor(tsk); + struct da_monitor *da_mon; bool retval; =20 + da_guard_monitor(); + da_mon =3D da_get_monitor(target); + + if (unlikely(!da_mon)) + return; + retval =3D da_monitor_handling_event(da_mon); if (!retval) return; =20 - __da_handle_event(da_mon, tsk, event); + __da_handle_event(da_mon, target, event); } =20 /* @@ -504,7 +769,7 @@ static inline void da_handle_event(struct task_struct *= tsk, enum events event) * 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, +static inline bool da_handle_start_event(monitor_target target, enum events event) { struct da_monitor *da_mon; @@ -512,14 +777,19 @@ static inline bool da_handle_start_event(struct task_= struct *tsk, if (!da_monitor_enabled()) return 0; =20 - da_mon =3D da_get_monitor(tsk); + da_guard_monitor(); + da_mon =3D da_get_monitor(target); + da_mon =3D da_monitor_start_hook(target, da_mon); + + if (unlikely(!da_mon)) + return 0; =20 if (unlikely(!da_monitoring(da_mon))) { da_monitor_start(da_mon); return 0; } =20 - __da_handle_event(da_mon, tsk, event); + __da_handle_event(da_mon, target, event); =20 return 1; } @@ -530,7 +800,7 @@ static inline bool da_handle_start_event(struct task_st= ruct *tsk, * 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, +static inline bool da_handle_start_run_event(monitor_target target, enum events event) { struct da_monitor *da_mon; @@ -538,15 +808,30 @@ static inline bool da_handle_start_run_event(struct t= ask_struct *tsk, if (!da_monitor_enabled()) return 0; =20 - da_mon =3D da_get_monitor(tsk); + da_guard_monitor(); + da_mon =3D da_get_monitor(target); + da_mon =3D da_monitor_start_hook(target, da_mon); + + if (unlikely(!da_mon)) + return 0; =20 if (unlikely(!da_monitoring(da_mon))) da_monitor_start(da_mon); =20 - __da_handle_event(da_mon, tsk, event); + __da_handle_event(da_mon, target, event); =20 return 1; } + +static inline void da_reset(monitor_target target) +{ + struct da_monitor *da_mon; + + da_guard_monitor(); + da_mon =3D da_get_monitor(target); + if (likely(da_mon)) + da_monitor_reset(da_mon); +} #endif /* RV_MON_TYPE */ =20 #endif diff --git a/include/rv/ha_monitor.h b/include/rv/ha_monitor.h index a7c7297a9759..0c7ab52214c0 100644 --- a/include/rv/ha_monitor.h +++ b/include/rv/ha_monitor.h @@ -19,11 +19,12 @@ #define _RV_HA_MONITOR_H =20 #include +#include =20 #if RV_MON_TYPE =3D=3D RV_MON_GLOBAL || RV_MON_TYPE =3D=3D RV_MON_PER_CPU static bool ha_monitor_handle_constraint(enum states curr_state, enum even= ts event, enum states next_state); -#elif RV_MON_TYPE =3D=3D RV_MON_PER_TASK -static bool ha_monitor_handle_constraint(struct task_struct *tsk, enum sta= tes curr_state, enum events event, enum states next_state); +#elif RV_MON_TYPE =3D=3D RV_MON_PER_TASK || RV_MON_TYPE =3D=3D RV_MON_PER_= OBJ +static bool ha_monitor_handle_constraint(monitor_target target, enum state= s curr_state, enum events event, enum states next_state); #endif static inline void ha_monitor_init_env(struct da_monitor *da_mon); static inline void ha_monitor_reset_env(struct da_monitor *da_mon); @@ -35,6 +36,11 @@ static inline bool ha_cancel_timer(struct ha_monitor *ha= _mon); #include #include =20 +/* This simplifies things since da_mon and ha_mon coexist in the same unio= n */ +_Static_assert(offsetof(struct ha_monitor, da_mon) =3D=3D 0, + "da_mon must be the first element in an ha_mon!"); +#define to_ha_monitor(da) ((struct ha_monitor *)da) + #define ENV_MAX CONCATENATE(env_max_, MONITOR_NAME) #define ENV_MAX_STORED CONCATENATE(env_max_stored_, MONITOR_NAME) #define envs CONCATENATE(envs_, MONITOR_NAME) @@ -230,14 +236,23 @@ static enum hrtimer_restart ha_monitor_timer_callback= (struct hrtimer *timer) return HRTIMER_NORESTART; } =20 -#elif RV_MON_TYPE =3D=3D RV_MON_PER_TASK +#elif RV_MON_TYPE =3D=3D RV_MON_PER_TASK || RV_MON_TYPE =3D=3D RV_MON_PER_= OBJ =20 /* * ha_get_monitor - return the per-task monitor address */ -static inline struct ha_monitor *ha_get_monitor(struct task_struct *tsk) +static inline struct ha_monitor *ha_get_monitor(monitor_target target) +{ + return to_ha_monitor(da_get_monitor(target)); +} + +/* + * ha_get_target - return the object associated to the monitor + */ +static inline monitor_target ha_get_target(struct ha_monitor *ha_mon) { - return to_ha_monitor(da_get_monitor(tsk)); + /* tasks have a union, the offset of da_mon and ha_mon are the same */ + return da_get_target((struct da_monitor *)ha_mon); } =20 /* @@ -248,12 +263,12 @@ static inline struct ha_monitor *ha_get_monitor(struc= t task_struct *tsk) * This function is called from the hook in the DA event handle function a= nd * triggers a failure in the monitor. */ -static bool ha_monitor_handle_constraint(struct task_struct *tsk, +static bool ha_monitor_handle_constraint(monitor_target target, enum states curr_state, enum events event, enum states next_state) { - struct ha_monitor *ha_mon =3D ha_get_monitor(tsk); + struct ha_monitor *ha_mon =3D ha_get_monitor(target); DECLARE_SEQ_BUF(env_string, 32); bool env_was_valid; =20 @@ -273,7 +288,7 @@ static bool ha_monitor_handle_constraint(struct task_st= ruct *tsk, =20 ha_get_env_string(&env_string, ha_mon); ha_cond_react(curr_state, event, env_string.buffer); - CONCATENATE(trace_error_env_, MONITOR_NAME)(tsk->pid, + CONCATENATE(trace_error_env_, MONITOR_NAME)(da_get_id(target), model_get_state_name(curr_state), model_get_event_name(event), env_string.buffer); @@ -285,12 +300,11 @@ static enum hrtimer_restart ha_monitor_timer_callback= (struct hrtimer *timer) struct ha_monitor *ha_mon =3D container_of(timer, struct ha_monitor, time= r); enum states curr_state =3D READ_ONCE(ha_mon->da_mon.curr_state); DECLARE_SEQ_BUF(env_string, MAX_DA_NAME_LEN); - struct task_struct *tsk; + monitor_target target =3D ha_get_target(ha_mon); =20 - tsk =3D container_of(ha_mon, struct task_struct, rv[task_mon_slot].ha_mon= ); ha_get_env_string(&env_string, ha_mon); ha_cond_react(curr_state, EVENT_NONE, env_string.buffer); - CONCATENATE(trace_error_env_, MONITOR_NAME)(tsk->pid, + CONCATENATE(trace_error_env_, MONITOR_NAME)(da_get_id(target), model_get_state_name(curr_state), EVENT_NONE_LBL, env_string.buffer); --=20 2.50.1 From nobody Sat Oct 4 15:57:18 2025 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5205925B692 for ; Thu, 14 Aug 2025 15:10:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184209; cv=none; b=UU4DqR7KrHrIeZC74yDOU9m27Adc8UlaRoVakFS5KwEzVnaKdbcqnrhUAcp5prlVK+v+QH1Cwd52qLxU0ChF9Sw2JbYJHtIzXdiF8JzgsuiiHEPrt1BYg2t49KZZ4uXfbdQfgLm+ng5xh6amq2EBwtAXxWeumY2RxO2/Ibz+ALQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184209; c=relaxed/simple; bh=TbAyM75ete4KxOq3Wb5GKnqC5jxCBw/JYCErZmv9xuU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=PRVyucpbuf623p7wuhBUaH7UIfb1t4bOd1Z1yUo4S8OhEoGmIzxh7L+f4gdmdyn7zl7/qriKQOdNrBZeIZOsVv8IvAxO/Wibvm4ICQWkLRloisp++fkmIOK42O0Az8desn6kVYeT+GGguLOS/LhHa2ky4Bx8bylTAGU4KcCgyd0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=QzdChFPJ; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="QzdChFPJ" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1755184207; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=EKc50jFzFH6l+wMP8OBNSn2fspct4LoKqLl2E2Pv+sk=; b=QzdChFPJVL7Nhetvu71WTa85kEguhO7pRgEGm0+0ZkdZLvSJBDL3QGfbIS1jSs/hhxM8VO mSbOcx9hTw1srQ9GoHtNjyJ55hkKwCHGwHdvZDMm40/S6YsTOaQm5uhdxUU87krFu64/M4 iw18xguI6ns0G1nxQmxgzqvta3YohLU= Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-596-oSC69V7cOmmzSB6WD2x4wQ-1; Thu, 14 Aug 2025 11:10:04 -0400 X-MC-Unique: oSC69V7cOmmzSB6WD2x4wQ-1 X-Mimecast-MFC-AGG-ID: oSC69V7cOmmzSB6WD2x4wQ_1755184203 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 35998180035F; Thu, 14 Aug 2025 15:10:03 +0000 (UTC) Received: from gmonaco-thinkpadt14gen3.rmtit.com (unknown [10.44.32.52]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id DB7A4180047F; Thu, 14 Aug 2025 15:09:58 +0000 (UTC) From: Gabriele Monaco To: linux-kernel@vger.kernel.org, Steven Rostedt , linux-trace-kernel@vger.kernel.org Cc: Gabriele Monaco , Nam Cao , Tomas Glozar , Juri Lelli , Clark Williams , John Kacur Subject: [RFC PATCH 16/17] verification/rvgen: Add support for per-obj monitors Date: Thu, 14 Aug 2025 17:08:08 +0200 Message-ID: <20250814150809.140739-17-gmonaco@redhat.com> In-Reply-To: <20250814150809.140739-1-gmonaco@redhat.com> References: <20250814150809.140739-1-gmonaco@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 Content-Type: text/plain; charset="utf-8" The special per-object monitor type was just introduced in RV, this requires the user to define some functions and type specific to the object. Adapt rvgen to add stub definitions for the monitor_target type, the da_get_id() function and other modifications required to create per-object monitors. Signed-off-by: Gabriele Monaco --- tools/verification/rvgen/rvgen/dot2k.py | 20 ++++++++++++++++++++ tools/verification/rvgen/rvgen/generator.py | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/tools/verification/rvgen/rvgen/dot2k.py b/tools/verification/r= vgen/rvgen/dot2k.py index 1f6ad11117ac..951bdb893592 100644 --- a/tools/verification/rvgen/rvgen/dot2k.py +++ b/tools/verification/rvgen/rvgen/dot2k.py @@ -23,10 +23,14 @@ class dot2k(Monitor, Dot2c): self.monitor_class =3D extra_params["monitor_class"] =20 def fill_monitor_type(self) -> str: + if self.monitor_type =3D=3D "per_obj": + return self.monitor_type.upper() + """ +typedef /* XXX: define the target type */ *monitor_target;""" return self.monitor_type.upper() =20 def fill_tracepoint_handlers_skel(self) -> str: buff =3D [] + buff +=3D self.fill_per_obj_definitions() buff +=3D self.fill_hybrid_definitions() for event in self.events: buff.append("static void handle_%s(void *data, /* XXX: fill he= ader */)" % event) @@ -41,6 +45,9 @@ class dot2k(Monitor, Dot2c): if self.monitor_type =3D=3D "per_task": buff.append("\tstruct task_struct *p =3D /* XXX: how do I = get p? */;"); buff.append("\tda_%s(p, %s%s);" % (handle, event, self.enu= m_suffix)); + elif self.monitor_type =3D=3D "per_obj": + buff.append("\tmonitor_target t =3D /* XXX: how do I get t= ? */;"); + buff.append("\tda_%s(t, %s%s);" % (handle, event, self.enu= m_suffix)); else: buff.append("\tda_%s(%s%s);" % (handle, event, self.enum_s= uffix)); buff.append("}") @@ -130,6 +137,19 @@ class dot2k(Monitor, Dot2c): """Stub, not valid for deterministic automata""" return [] =20 + def fill_per_obj_definitions(self) -> list: + if self.monitor_type =3D=3D "per_obj": + return [""" +/* + * da_get_id - Get the id from a target + */ +static inline da_id_type da_get_id(monitor_target target) +{ + return /* XXX: define how to get an id from the target */; +} +"""] + return [] + def fill_main_c(self) -> str: main_c =3D super().fill_main_c() =20 diff --git a/tools/verification/rvgen/rvgen/generator.py b/tools/verificati= on/rvgen/rvgen/generator.py index b80af3fd6701..5eac12e110dc 100644 --- a/tools/verification/rvgen/rvgen/generator.py +++ b/tools/verification/rvgen/rvgen/generator.py @@ -243,7 +243,7 @@ obj-$(CONFIG_RV_MON_%s) +=3D monitors/%s/%s.o =20 =20 class Monitor(RVGenerator): - monitor_types =3D { "global" : 1, "per_cpu" : 2, "per_task" : 3 } + monitor_types =3D { "global" : 1, "per_cpu" : 2, "per_task" : 3, "per_= obj" : 4 } =20 def __init__(self, extra_params=3D{}): super().__init__(extra_params) --=20 2.50.1 From nobody Sat Oct 4 15:57:18 2025 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 699141A3164 for ; Thu, 14 Aug 2025 15:10:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184221; cv=none; b=Z2lGS/ufjG26FtNZug9QcxrTDe62M2M+BwbAfT3jXDw83W5z+q68FFrt9az+jrFfwgjPztl+DEH9bzinmBYrEFE5e9gtzif2VLLnHgQWp9pVeiDUDzDCJy9WmdpmO0Su8aKih0oddAPfxBXi0mCdV/Ul8RTxS/076R3Vu54q+qY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755184221; c=relaxed/simple; bh=vbNUuQ+LEC8jHEYcLR4c+coDZ+fgEIVwo79nvcHDkCo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=okgnIQXZtuAjheTNV7UPIvaA/0CjWLB1mnORDzUpf0mf+oS64YwhKuLgUga6WoMhjJmp6QvPtesTHYeD5JhYWsMWBjXLpx6Xun4GergQHEQyUBA42eqmfNPOdHzAq6w0zk62oCCJ7Idl2cd2zY3vkBsh54YoKknLkwaP1DhaQ3A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=CsX5AljP; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="CsX5AljP" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1755184217; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=khBqRM5AjZJd10ToKw1jOg1G418nf/ZBqYYXNkLs71k=; b=CsX5AljPwGHPv1kg/rxU5aFZOR8279f7S8/FSeM5RH/HZi1nh9pEWCXORR0Rpvh8JFH8Ah fKBkYb/XxCPJProAWrT3qq1drNFAzQa3RmlYIa/w1VYtIilNCq9HGVeRpAJ9bJcz4N2+Ka v8iESo55PRZpmL1YjxhApyqB+qD8yFs= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-261-PpfKwIFyOOWQVGm5kGsYkw-1; Thu, 14 Aug 2025 11:10:13 -0400 X-MC-Unique: PpfKwIFyOOWQVGm5kGsYkw-1 X-Mimecast-MFC-AGG-ID: PpfKwIFyOOWQVGm5kGsYkw_1755184211 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id CEE68195F166; Thu, 14 Aug 2025 15:10:11 +0000 (UTC) Received: from gmonaco-thinkpadt14gen3.rmtit.com (unknown [10.44.32.52]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 0FD74180047F; Thu, 14 Aug 2025 15:10:05 +0000 (UTC) From: Gabriele Monaco To: linux-kernel@vger.kernel.org, Steven Rostedt , Jonathan Corbet , Masami Hiramatsu , linux-trace-kernel@vger.kernel.org, linux-doc@vger.kernel.org Cc: Gabriele Monaco , Nam Cao , Tomas Glozar , Juri Lelli , Clark Williams , John Kacur Subject: [RFC PATCH 17/17] rv: Add deadline monitors Date: Thu, 14 Aug 2025 17:08:09 +0200 Message-ID: <20250814150809.140739-18-gmonaco@redhat.com> In-Reply-To: <20250814150809.140739-1-gmonaco@redhat.com> References: <20250814150809.140739-1-gmonaco@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 Content-Type: text/plain; charset="utf-8" Add the deadline monitors collection to validate the deadline scheduler, both for deadline tasks and servers. The currently implemented monitors are: * throttle: validate dl entities are throttled when they use up their runtime * nomiss: validate dl entities run to completion before their deadiline Signed-off-by: Gabriele Monaco --- Documentation/trace/rv/monitor_deadline.rst | 111 ++++++++ kernel/trace/rv/Kconfig | 5 + kernel/trace/rv/Makefile | 3 + kernel/trace/rv/monitors/deadline/Kconfig | 5 + kernel/trace/rv/monitors/deadline/deadline.c | 35 +++ kernel/trace/rv/monitors/deadline/deadline.h | 82 ++++++ kernel/trace/rv/monitors/nomiss/Kconfig | 15 + kernel/trace/rv/monitors/nomiss/nomiss.c | 234 ++++++++++++++++ kernel/trace/rv/monitors/nomiss/nomiss.h | 81 ++++++ .../trace/rv/monitors/nomiss/nomiss_trace.h | 19 ++ kernel/trace/rv/monitors/throttle/Kconfig | 15 + kernel/trace/rv/monitors/throttle/throttle.c | 259 ++++++++++++++++++ kernel/trace/rv/monitors/throttle/throttle.h | 115 ++++++++ .../rv/monitors/throttle/throttle_trace.h | 19 ++ kernel/trace/rv/rv_trace.h | 2 + tools/verification/models/deadline/nomiss.dot | 23 ++ .../verification/models/deadline/throttle.dot | 43 +++ 17 files changed, 1066 insertions(+) create mode 100644 Documentation/trace/rv/monitor_deadline.rst create mode 100644 kernel/trace/rv/monitors/deadline/Kconfig create mode 100644 kernel/trace/rv/monitors/deadline/deadline.c create mode 100644 kernel/trace/rv/monitors/deadline/deadline.h create mode 100644 kernel/trace/rv/monitors/nomiss/Kconfig create mode 100644 kernel/trace/rv/monitors/nomiss/nomiss.c create mode 100644 kernel/trace/rv/monitors/nomiss/nomiss.h create mode 100644 kernel/trace/rv/monitors/nomiss/nomiss_trace.h create mode 100644 kernel/trace/rv/monitors/throttle/Kconfig create mode 100644 kernel/trace/rv/monitors/throttle/throttle.c create mode 100644 kernel/trace/rv/monitors/throttle/throttle.h create mode 100644 kernel/trace/rv/monitors/throttle/throttle_trace.h create mode 100644 tools/verification/models/deadline/nomiss.dot create mode 100644 tools/verification/models/deadline/throttle.dot diff --git a/Documentation/trace/rv/monitor_deadline.rst b/Documentation/tr= ace/rv/monitor_deadline.rst new file mode 100644 index 000000000000..af144605bbb0 --- /dev/null +++ b/Documentation/trace/rv/monitor_deadline.rst @@ -0,0 +1,111 @@ +Scheduler monitors +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +- Name: deadline +- Type: container for multiple monitors +- Author: Gabriele Monaco + +Description +----------- + +The deadline monitor is a set of specifications to describe the deadline +scheduler behaviour. It includes monitors per scheduling entity (deadline = tasks +and servers) that work independently to verify different specifications the +deadline scheduler should follow. + +Specifications +-------------- + +Monitor throttle +~~~~~~~~~~~~~~~~ + +The throttle monitor ensures deadline entities are throttled when they use= up +their runtime. Deadline tasks can be only ``running``, ``preempted`` and +``throttled``, the runtime is enforced only in ``running`` based on an int= ernal +clock and the runtime value in the deadline entity. + +Servers can be also in the ``armed`` state, which corresponds to when the +server is consuming bandwidth in background (e.g. idle or normal tasks are +running without any boost). From this state the server can be throttled bu= t it +can also use more runtime than available. A server is considered ``running= `` +when it's actively boosting a task, only there the runtime is enforced:: + + | + | + dl_replenish;reset(clk) v + sched_switch_in #=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D# sched_switch_in; + +--------------- H H reset(clk) + | H H <-------------= ---+ + +--------------> H running H = | + dl_throttle H clk < runtime_left_ns() H = | + +---------------------------- H H sched_switch_o= ut | + | +--------------------> H H -------------+= | + | dl_replenish; #=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D# | | + | reset(clk) | ^ |= | + | | dl_defer_arm sched_switch_in; |= | + | | | reset(clk) |= | + v | v | |= | + +------------+ dl_replenish +----------------+ |= | + | | dl_defer_arm | | sched_switch_out |= | + | throttled | +---------- | armed | -------------+ |= | + | | | | | <--------+ | |= | + +------------+ +---------> | | dl_defer_arm | |= | + | | +----------------+ | | |= | + | | | ^ | | |= | + | | dl_throttle dl_replenish | | |= | + | | dl_throttle;yielded=3D=3D1 v | | | = | | + | | dl_defer_arm +--------------------+ | v v= | + | | +---------- | | +-----------= ---+ + | | | | | | = | + | | +---------> | armed_throttled | | preempted= | + | | | | | = | + | +----------------------> | | +-----------= ---+ + | dl_defer_arm +--------------------+ ^ + | | ^ | + | sched_switch_out | dl_defer_arm | + | v | | + | sched_switch_out +-------------------------+ | + | +-------------- | | dl_replen= ish + | | | | | + | +-------------> | preempted_throttled | --------+ + | | | + +-----------------------------> | | + sched_switch_out +-------------------------+ + + +Monitor nomiss +~~~~~~~~~~~~~~ + +The nomiss monitor ensures dl entities run to completion before their +deadiline. An entity is considered done if throttled, either because it yi= elded +or used up its runtime, or when it goes to sleep. +The monitor includes a user configurable deadline threshold. If the total +utilisation of deadline tasks is larger than 1, they are only guaranteed +bounded tardiness. See Documentation/scheduler/sched-deadline.rst for more +details. The threshold (module parameter ``nomiss.deadline_thresh``) can be +configured to avoid the monitor to fail based on the acceptable tardiness = in +the system:: + + sched_switch_in + sched_wakeup + +----------------------+ + v | + #=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D# sched_switch_suspend + --------> H H ----------------+ + H H v + H H +---------= -+ + H H | sleeping= | + H running H +---------= -+ + H clk < DEADLINE_LEFT_NS() H sched_wakeup; | + H H reset(clk) | + H H <---------------+ + +-----------------> H H -+ + | #=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D# | + | | + | sched_switch_suspend | + sched_switch_in dl_throttle | + sched_wakeup;reset(clk) +----------------------+ | dl_throttle + | v | | + | +--------------------------+ | + +------------------ | throttled | <+ + +--------------------------+ diff --git a/kernel/trace/rv/Kconfig b/kernel/trace/rv/Kconfig index 720fbe4935f8..719cdcfb6d41 100644 --- a/kernel/trace/rv/Kconfig +++ b/kernel/trace/rv/Kconfig @@ -79,6 +79,11 @@ source "kernel/trace/rv/monitors/sleep/Kconfig" # Add new rtapp monitors here =20 source "kernel/trace/rv/monitors/stall/Kconfig" +source "kernel/trace/rv/monitors/deadline/Kconfig" +source "kernel/trace/rv/monitors/nomiss/Kconfig" +source "kernel/trace/rv/monitors/throttle/Kconfig" +# Add new deadline monitors here + # Add new monitors here =20 config RV_REACTORS diff --git a/kernel/trace/rv/Makefile b/kernel/trace/rv/Makefile index 51c95e2d2da6..15a1edc8bd0f 100644 --- a/kernel/trace/rv/Makefile +++ b/kernel/trace/rv/Makefile @@ -18,6 +18,9 @@ obj-$(CONFIG_RV_MON_NRP) +=3D monitors/nrp/nrp.o obj-$(CONFIG_RV_MON_SSSW) +=3D monitors/sssw/sssw.o obj-$(CONFIG_RV_MON_OPID) +=3D monitors/opid/opid.o obj-$(CONFIG_RV_MON_STALL) +=3D monitors/stall/stall.o +obj-$(CONFIG_RV_MON_DEADLINE) +=3D monitors/deadline/deadline.o +obj-$(CONFIG_RV_MON_NOMISS) +=3D monitors/nomiss/nomiss.o +obj-$(CONFIG_RV_MON_THROTTLE) +=3D monitors/throttle/throttle.o # Add new monitors here obj-$(CONFIG_RV_REACTORS) +=3D rv_reactors.o obj-$(CONFIG_RV_REACT_PRINTK) +=3D reactor_printk.o diff --git a/kernel/trace/rv/monitors/deadline/Kconfig b/kernel/trace/rv/mo= nitors/deadline/Kconfig new file mode 100644 index 000000000000..652876730a39 --- /dev/null +++ b/kernel/trace/rv/monitors/deadline/Kconfig @@ -0,0 +1,5 @@ +config RV_MON_DEADLINE + depends on RV + bool "deadline monitor" + help + auto-generated diff --git a/kernel/trace/rv/monitors/deadline/deadline.c b/kernel/trace/rv= /monitors/deadline/deadline.c new file mode 100644 index 000000000000..61564fbbe333 --- /dev/null +++ b/kernel/trace/rv/monitors/deadline/deadline.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include + +#define MODULE_NAME "deadline" + +#include "deadline.h" + +struct rv_monitor rv_deadline =3D { + .name =3D "deadline", + .description =3D "auto-generated", + .enable =3D NULL, + .disable =3D NULL, + .reset =3D NULL, + .enabled =3D 0, +}; + +static int __init register_deadline(void) +{ + return rv_register_monitor(&rv_deadline, NULL); +} + +static void __exit unregister_deadline(void) +{ + rv_unregister_monitor(&rv_deadline); +} + +module_init(register_deadline); +module_exit(unregister_deadline); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("dot2k: auto-generated"); +MODULE_DESCRIPTION("deadline: auto-generated"); diff --git a/kernel/trace/rv/monitors/deadline/deadline.h b/kernel/trace/rv= /monitors/deadline/deadline.h new file mode 100644 index 000000000000..20f51e1de866 --- /dev/null +++ b/kernel/trace/rv/monitors/deadline/deadline.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include +#include +#include + +/* + * Dummy values if not available + */ +#ifndef __NR_sched_setscheduler +#define __NR_sched_setscheduler -1 +#endif +#ifndef __NR_sched_setattr +#define __NR_sched_setattr -2 +#endif + +extern struct rv_monitor rv_deadline; + +/* + * If both have dummy values, the syscalls are not supported and we don't = even + * need to register the handler. + */ +static inline bool should_skip_syscall_handle(void) +{ + return __NR_sched_setattr < 0 && __NR_sched_setscheduler < 0; +} + +static inline int get_server_id(void) +{ + /* + * Use negative numbers for the server. + * Currently only one fair server per CPU, may change in the future. + */ + return -__smp_processor_id(); +} + +static inline int extract_params(struct pt_regs *regs, long id, struct tas= k_struct **p) +{ + size_t size =3D offsetof(struct sched_attr, sched_nice); + struct sched_attr __user *uattr, attr; + int new_policy =3D -1, ret; + unsigned long args[6]; + pid_t pid; + + switch (id) { + case __NR_sched_setscheduler: + syscall_get_arguments(current, regs, args); + pid =3D args[0]; + new_policy =3D args[1]; + break; + case __NR_sched_setattr: + syscall_get_arguments(current, regs, args); + pid =3D args[0]; + uattr =3D (void *)args[1]; + /* + * Just copy up to sched_flags, we are not interested after that + */ + ret =3D copy_struct_from_user(&attr, size, uattr, size); + if (ret) + return ret; + if (attr.sched_flags & SCHED_FLAG_KEEP_POLICY) + return -EINVAL; + new_policy =3D attr.sched_policy; + break; + default: + return -EINVAL; + } + if (!pid) + *p =3D current; + else { + /* + * Required for find_task_by_vpid, make sure the caller doesn't + * need to get_task_struct(). + */ + guard(rcu)(); + *p =3D find_task_by_vpid(pid); + if (unlikely(!p)) + return -EINVAL; + } + + return new_policy; +} diff --git a/kernel/trace/rv/monitors/nomiss/Kconfig b/kernel/trace/rv/moni= tors/nomiss/Kconfig new file mode 100644 index 000000000000..e1886c3a0dd9 --- /dev/null +++ b/kernel/trace/rv/monitors/nomiss/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +config RV_MON_NOMISS + depends on RV + depends on HAVE_SYSCALL_TRACEPOINTS + depends on RV_MON_DEADLINE + default y + select HA_MON_EVENTS_ID + bool "nomiss monitor" + help + Monitor to ensure dl entities run to completion before their deadiline. + This monitor is part of the deadline monitors collection. + + For further information, see: + Documentation/trace/rv/monitor_deadline.rst diff --git a/kernel/trace/rv/monitors/nomiss/nomiss.c b/kernel/trace/rv/mon= itors/nomiss/nomiss.c new file mode 100644 index 000000000000..4b61a861a62c --- /dev/null +++ b/kernel/trace/rv/monitors/nomiss/nomiss.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "nomiss" + +#include +#include +#include +#include +#include +#include + +#define RV_MON_TYPE RV_MON_PER_OBJ +/* The start condition is on sched_switch, it's dangerous to allocate ther= e */ +#define DA_SKIP_AUTO_ALLOC +typedef struct sched_dl_entity *monitor_target; +#include "nomiss.h" +#include + +/* + * da_get_id - Get the id from a dl server + * + * Deadline tasks use the task's PID, while fair servers use the negated c= pu. + */ +static inline da_id_type da_get_id(monitor_target target) +{ + if (target->dl_server) + return get_server_id(); + return container_of(target, struct task_struct, dl)->pid; +} + +/* + * User configurable deadline threshold. If the total utilisation of deadl= ine + * tasks is larger than 1, they are only guaranteed bounded tardiness. See + * Documentation/scheduler/sched-deadline.rst for more details. + */ +static u64 deadline_thresh =3D 0; +module_param(deadline_thresh, ullong, 0644); +#define DEADLINE_LEFT_NS(ha_mon) (ha_get_target(ha_mon)->deadline + deadli= ne_thresh) + +static u64 ha_get_env(struct ha_monitor *ha_mon, enum envs_nomiss env) +{ + if (env =3D=3D clk_nomiss) + return ha_get_clk_ns(ha_mon, env); + return ENV_INVALID_VALUE; +} + +static void ha_reset_env(struct ha_monitor *ha_mon, enum envs_nomiss env) +{ + if (env =3D=3D clk_nomiss) + ha_reset_clk_ns(ha_mon, env); +} + +static bool ha_verify_constraint(struct ha_monitor *ha_mon, + enum states curr_state, enum events event, + enum states next_state) +{ + bool res =3D true; + + if (curr_state =3D=3D sleeping_nomiss && event =3D=3D sched_switch_in_nom= iss) + ha_reset_env(ha_mon, clk_nomiss); + else if (curr_state =3D=3D throttled_nomiss && event =3D=3D sched_switch_= in_nomiss) + ha_reset_env(ha_mon, clk_nomiss); + + if (next_state =3D=3D curr_state || !res) + return res; + if (next_state =3D=3D running_nomiss) + ha_start_timer_ns(ha_mon, clk_nomiss, DEADLINE_LEFT_NS(ha_mon)); + else if (curr_state =3D=3D running_nomiss) + res =3D !ha_cancel_timer(ha_mon); + return res; +} + +static void handle_dl_throttle(void *data, struct sched_dl_entity *dl) +{ + da_handle_event(dl, dl_throttle_nomiss); +} + +static void handle_dl_server_start(void *data, struct sched_dl_entity *dl) +{ + da_handle_start_event(dl, sched_switch_in_nomiss); +} + +static void handle_dl_server_stop(void *data, struct sched_dl_entity *dl, = bool hard) +{ + if (hard) + da_handle_event(dl, sched_switch_suspend_nomiss); +} + +static void handle_sched_switch(void *data, bool preempt, struct task_stru= ct *prev, struct task_struct *next, unsigned int prev_state) +{ + if (prev_state !=3D TASK_RUNNING && prev->policy =3D=3D SCHED_DEADLINE) + da_handle_event(&prev->dl, sched_switch_suspend_nomiss); + if (next->policy =3D=3D SCHED_DEADLINE) + da_handle_start_event(&next->dl, sched_switch_in_nomiss); +} + +static void handle_syscall(void *data, struct pt_regs *regs, long id) +{ + struct task_struct *p; + int new_policy =3D -1; + + new_policy =3D extract_params(regs, id, &p); + if (new_policy < 0 || new_policy =3D=3D p->policy) + return; + if (p->policy =3D=3D SCHED_DEADLINE) + da_reset(&p->dl); + else if (new_policy =3D=3D SCHED_DEADLINE) + da_create_conditional(&p->dl); +} + +static void handle_sched_wakeup(void *data, struct task_struct *tsk) +{ + if (tsk->policy =3D=3D SCHED_DEADLINE) + da_handle_start_event(&tsk->dl, sched_wakeup_nomiss); +} + +static void handle_newtask(void *data, struct task_struct *task, unsigned = long flags) +{ + /* Might be superfluous as tasks are not started with this policy.. */ + if (task->policy =3D=3D SCHED_DEADLINE) + da_create_storage(&task->dl, NULL); +} + +static void handle_exit(void *data, struct task_struct *p, bool group_dead) +{ + if (p->policy =3D=3D SCHED_DEADLINE) + da_destroy_storage(&p->dl); +} + +/* + * Initialise monitors for all tasks and pre-allocate the storage for serv= ers. + * This is necessary since we don't have access to the servers here and + * allocation can cause deadlocks from their tracepoints. We can only fill + * pre-initialised storage from there. + */ +static inline int init_storage(void) +{ + struct task_struct *g, *p; + int cpu; + + for_each_possible_cpu(cpu) { + /* The servers' ids are determined according to da_get_id */ + if (!da_create_empty_storage(-cpu)) + goto fail; + } + + for_each_process_thread(g, p) { + if (p->policy =3D=3D SCHED_DEADLINE) { + if (!da_create_storage(&p->dl, NULL)) + goto fail; + } + } + return 0; + +fail: + da_monitor_destroy(); + return -ENOMEM; +} + +static int enable_nomiss(void) +{ + int retval; + + retval =3D da_monitor_init(); + if (retval) + return retval; + + retval =3D init_storage(); + if (retval) + return retval; + rv_attach_trace_probe("nomiss", sched_dl_throttle_tp, handle_dl_throttle); + rv_attach_trace_probe("nomiss", sched_dl_server_start_tp, handle_dl_serve= r_start); + rv_attach_trace_probe("nomiss", sched_dl_server_stop_tp, handle_dl_server= _stop); + rv_attach_trace_probe("nomiss", sched_switch, handle_sched_switch); + rv_attach_trace_probe("nomiss", sched_wakeup, handle_sched_wakeup); + if (!should_skip_syscall_handle()) + rv_attach_trace_probe("nomiss", sys_enter, handle_syscall); + rv_attach_trace_probe("nomiss", task_newtask, handle_newtask); + rv_attach_trace_probe("nomiss", sched_process_exit, handle_exit); + + return 0; +} + +static void disable_nomiss(void) +{ + rv_nomiss.enabled =3D 0; + + /* Those are RCU writers, detach earlier hoping to close a bit faster */ + rv_detach_trace_probe("nomiss", task_newtask, handle_newtask); + rv_detach_trace_probe("nomiss", sched_process_exit, handle_exit); + if (!should_skip_syscall_handle()) + rv_detach_trace_probe("nomiss", sys_enter, handle_syscall); + + rv_detach_trace_probe("nomiss", sched_dl_throttle_tp, handle_dl_throttle); + rv_detach_trace_probe("nomiss", sched_dl_server_start_tp, handle_dl_serve= r_start); + rv_detach_trace_probe("nomiss", sched_dl_server_stop_tp, handle_dl_server= _stop); + rv_detach_trace_probe("nomiss", sched_switch, handle_sched_switch); + rv_detach_trace_probe("nomiss", sched_wakeup, handle_sched_wakeup); + + da_monitor_destroy(); +} + +static struct rv_monitor rv_nomiss =3D { + .name =3D "nomiss", + .description =3D "dl entities run to completion before their deadiline.", + .enable =3D enable_nomiss, + .disable =3D disable_nomiss, + .reset =3D da_monitor_reset_all, + .enabled =3D 0, +}; + +static int __init register_nomiss(void) +{ + return rv_register_monitor(&rv_nomiss, &rv_deadline); +} + +static void __exit unregister_nomiss(void) +{ + rv_unregister_monitor(&rv_nomiss); +} + +module_init(register_nomiss); +module_exit(unregister_nomiss); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Gabriele Monaco "); +MODULE_DESCRIPTION("nomiss: dl entities run to completion before their dea= diline."); diff --git a/kernel/trace/rv/monitors/nomiss/nomiss.h b/kernel/trace/rv/mon= itors/nomiss/nomiss.h new file mode 100644 index 000000000000..a4059b34c60d --- /dev/null +++ b/kernel/trace/rv/monitors/nomiss/nomiss.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Automatically generated C representation of nomiss automaton + * For further information about this format, see kernel documentation: + * Documentation/trace/rv/deterministic_automata.rst + */ + +#define MONITOR_NAME nomiss + +enum states_nomiss { + running_nomiss =3D 0, + sleeping_nomiss, + throttled_nomiss, + state_max_nomiss +}; + +#define INVALID_STATE state_max_nomiss + +enum events_nomiss { + dl_throttle_nomiss =3D 0, + sched_switch_in_nomiss, + sched_switch_suspend_nomiss, + sched_wakeup_nomiss, + event_max_nomiss +}; + +enum envs_nomiss { + clk_nomiss =3D 0, + env_max_nomiss, + env_max_stored_nomiss =3D env_max_nomiss +}; + +_Static_assert(env_max_stored_nomiss <=3D MAX_HA_ENV_LEN, "Not enough slot= s"); + +struct automaton_nomiss { + char *state_names[state_max_nomiss]; + char *event_names[event_max_nomiss]; + char *env_names[env_max_nomiss]; + unsigned char function[state_max_nomiss][event_max_nomiss]; + unsigned char initial_state; + bool final_states[state_max_nomiss]; +}; + +static const struct automaton_nomiss automaton_nomiss =3D { + .state_names =3D { + "running", + "sleeping", + "throttled" + }, + .event_names =3D { + "dl_throttle", + "sched_switch_in", + "sched_switch_suspend", + "sched_wakeup" + }, + .env_names =3D { + "clk" + }, + .function =3D { + { + throttled_nomiss, + running_nomiss, + sleeping_nomiss, + running_nomiss + }, + { + INVALID_STATE, + INVALID_STATE, + INVALID_STATE, + running_nomiss + }, + { + throttled_nomiss, + running_nomiss, + throttled_nomiss, + running_nomiss + }, + }, + .initial_state =3D running_nomiss, + .final_states =3D { 1, 0, 0 }, +}; diff --git a/kernel/trace/rv/monitors/nomiss/nomiss_trace.h b/kernel/trace/= rv/monitors/nomiss/nomiss_trace.h new file mode 100644 index 000000000000..42e7efaca4e7 --- /dev/null +++ b/kernel/trace/rv/monitors/nomiss/nomiss_trace.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Snippet to be included in rv_trace.h + */ + +#ifdef CONFIG_RV_MON_NOMISS +DEFINE_EVENT(event_da_monitor_id, event_nomiss, + TP_PROTO(int id, char *state, char *event, char *next_state, bool fi= nal_state), + TP_ARGS(id, state, event, next_state, final_state)); + +DEFINE_EVENT(error_da_monitor_id, error_nomiss, + TP_PROTO(int id, char *state, char *event), + TP_ARGS(id, state, event)); + +DEFINE_EVENT(error_env_da_monitor_id, error_env_nomiss, + TP_PROTO(int id, char *state, char *event, char *env), + TP_ARGS(id, state, event, env)); +#endif /* CONFIG_RV_MON_NOMISS */ diff --git a/kernel/trace/rv/monitors/throttle/Kconfig b/kernel/trace/rv/mo= nitors/throttle/Kconfig new file mode 100644 index 000000000000..d9bd2dc903cd --- /dev/null +++ b/kernel/trace/rv/monitors/throttle/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +config RV_MON_THROTTLE + depends on RV + depends on HAVE_SYSCALL_TRACEPOINTS + depends on RV_MON_DEADLINE + default y + select HA_MON_EVENTS_ID + bool "throttle monitor" + help + Monitor to ensure dl entities are throttled when they use up their runt= ime. + This monitor is part of the deadline monitors collection. + + For further information, see: + Documentation/trace/rv/monitor_deadline.rst diff --git a/kernel/trace/rv/monitors/throttle/throttle.c b/kernel/trace/rv= /monitors/throttle/throttle.c new file mode 100644 index 000000000000..412b53b268f5 --- /dev/null +++ b/kernel/trace/rv/monitors/throttle/throttle.c @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "throttle" + +#include +#include +#include +#include +#include +#include + +#define RV_MON_TYPE RV_MON_PER_OBJ +/* The start condition is on sched_switch, it's dangerous to allocate ther= e */ +#define DA_SKIP_AUTO_ALLOC +typedef struct sched_dl_entity *monitor_target; +#include "throttle.h" +#include + +/* + * da_get_id - Get the id from a dl server + * + * Deadline tasks use the task's PID, while fair servers use the negated c= pu. + */ +static inline da_id_type da_get_id(monitor_target target) +{ + if (target->dl_server) + return get_server_id(); + return container_of(target, struct task_struct, dl)->pid; +} + +/* with sched_feat(HRTICK_DL) the threshold should be lower */ +#define RUNTIME_THRESH jiffies_to_nsecs(1) + +static inline u64 runtime_left_ns(struct ha_monitor *ha_mon) +{ + return ha_get_target(ha_mon)->runtime + RUNTIME_THRESH; +} + +static u64 ha_get_env(struct ha_monitor *ha_mon, enum envs_throttle env) +{ + if (env =3D=3D clk_throttle) + return ha_get_clk_ns(ha_mon, env); + else if (env =3D=3D yielded_throttle) + return ha_get_target(ha_mon)->dl_yielded; + return ENV_INVALID_VALUE; +} + +static void ha_reset_env(struct ha_monitor *ha_mon, enum envs_throttle env) +{ + if (env =3D=3D clk_throttle) + ha_reset_clk_ns(ha_mon, env); +} + +static bool ha_verify_constraint(struct ha_monitor *ha_mon, + enum states curr_state, enum events event, + enum states next_state) +{ + bool res =3D true; + + if (curr_state =3D=3D armed_throttle && event =3D=3D sched_switch_in_thro= ttle) + ha_reset_env(ha_mon, clk_throttle); + else if (curr_state =3D=3D armed_throttled_throttle && event =3D=3D dl_th= rottle_throttle) + res =3D ha_get_env(ha_mon, yielded_throttle) =3D=3D 1ull; + else if (curr_state =3D=3D preempted_throttle && event =3D=3D sched_switc= h_in_throttle) + ha_reset_env(ha_mon, clk_throttle); + else if (curr_state =3D=3D running_throttle && event =3D=3D dl_replenish_= throttle) + ha_reset_env(ha_mon, clk_throttle); + else if (curr_state =3D=3D throttled_throttle && event =3D=3D dl_replenis= h_throttle) + ha_reset_env(ha_mon, clk_throttle); + + if ((next_state =3D=3D curr_state && event !=3D dl_replenish_throttle) ||= !res) + return res; + if (next_state =3D=3D running_throttle) + ha_start_timer_ns(ha_mon, clk_throttle, runtime_left_ns(ha_mon)); + else if (curr_state =3D=3D running_throttle) + res =3D !ha_cancel_timer(ha_mon); + return res; +} + +static void handle_dl_replenish(void *data, struct sched_dl_entity *dl) +{ + da_handle_event(dl, dl_replenish_throttle); +} + +static void handle_dl_throttle(void *data, struct sched_dl_entity *dl) +{ + da_handle_event(dl, dl_throttle_throttle); +} + +static inline struct sched_dl_entity *get_fair_server(struct task_struct *= tsk) +{ + if (tsk->dl_server) + return tsk->dl_server; + return da_get_target_by_id(get_server_id()); +} + +static void handle_sched_switch(void *data, bool preempt, struct task_stru= ct *prev, struct task_struct *next, unsigned int prev_state) +{ + struct sched_dl_entity *dl; + + if (prev->policy =3D=3D SCHED_DEADLINE) + da_handle_event(&prev->dl, sched_switch_out_throttle); + if (next->policy =3D=3D SCHED_DEADLINE) + da_handle_start_event(&next->dl, sched_switch_in_throttle); + + /* + * The server is available in next only if the next task is boosted, + * otherwise we need to retrieve it. + */ + dl =3D get_fair_server(next); + if (!dl) + return; + if (next->dl_server) + da_handle_start_event(next->dl_server, sched_switch_in_throttle); + else if (is_idle_task(next) || next->policy =3D=3D SCHED_NORMAL) + da_handle_event(dl, dl_defer_arm_throttle); + else + da_handle_event(dl, sched_switch_out_throttle); +} + +static void handle_syscall(void *data, struct pt_regs *regs, long id) +{ + struct task_struct *p; + int new_policy =3D -1; + + new_policy =3D extract_params(regs, id, &p); + if (new_policy < 0 || new_policy =3D=3D p->policy) + return; + if (p->policy =3D=3D SCHED_DEADLINE) { + da_reset(&p->dl); + /* + * When a task changes from SCHED_DEADLINE to SCHED_NORMAL, the + * runtime after the change is counted in the fair server. + */ + if (new_policy =3D=3D SCHED_NORMAL) { + struct sched_dl_entity *dl =3D get_fair_server(p); + if (!dl) + return; + da_handle_event(dl, dl_defer_arm_throttle); + } + } else if (new_policy =3D=3D SCHED_DEADLINE) { + da_create_conditional(&p->dl); + } +} + +static void handle_newtask(void *data, struct task_struct *task, unsigned = long flags) +{ + /* Might be superfluous as tasks are not started with this policy.. */ + if (task->policy =3D=3D SCHED_DEADLINE) + da_create_storage(&task->dl, NULL); +} + +static void handle_exit(void *data, struct task_struct *p, bool group_dead) +{ + if (p->policy =3D=3D SCHED_DEADLINE) + da_destroy_storage(&p->dl); +} + +/* + * Initialise monitors for all tasks and pre-allocate the storage for serv= ers. + * This is necessary since we don't have access to the servers here and + * allocation can cause deadlocks from their tracepoints. We can only fill + * pre-initialised storage from there. + */ +static inline int init_storage(void) +{ + struct task_struct *g, *p; + int cpu; + + for_each_possible_cpu(cpu) { + /* The servers' ids are determined according to da_get_id */ + if (!da_create_empty_storage(-cpu)) + goto fail; + } + + for_each_process_thread(g, p) { + if (p->policy =3D=3D SCHED_DEADLINE) { + if (!da_create_storage(&p->dl, NULL)) + goto fail; + } + } + return 0; + +fail: + da_monitor_destroy(); + return -ENOMEM; +} + +static int enable_throttle(void) +{ + int retval; + + retval =3D da_monitor_init(); + if (retval) + return retval; + + retval =3D init_storage(); + if (retval) + return retval; + rv_attach_trace_probe("throttle", sched_dl_replenish_tp, handle_dl_replen= ish); + rv_attach_trace_probe("throttle", sched_dl_throttle_tp, handle_dl_throttl= e); + rv_attach_trace_probe("throttle", sched_switch, handle_sched_switch); + if (!should_skip_syscall_handle()) + rv_attach_trace_probe("throttle", sys_enter, handle_syscall); + rv_attach_trace_probe("throttle", task_newtask, handle_newtask); + rv_attach_trace_probe("throttle", sched_process_exit, handle_exit); + + return 0; +} + +static void disable_throttle(void) +{ + rv_throttle.enabled =3D 0; + + /* Those are RCU writers, detach earlier hoping to close a bit faster */ + rv_detach_trace_probe("throttle", task_newtask, handle_newtask); + rv_detach_trace_probe("throttle", sched_process_exit, handle_exit); + if (!should_skip_syscall_handle()) + rv_detach_trace_probe("throttle", sys_enter, handle_syscall); + + rv_detach_trace_probe("throttle", sched_dl_replenish_tp, handle_dl_replen= ish); + rv_detach_trace_probe("throttle", sched_dl_throttle_tp, handle_dl_throttl= e); + rv_detach_trace_probe("throttle", sched_switch, handle_sched_switch); + + da_monitor_destroy(); +} + +static struct rv_monitor rv_throttle =3D { + .name =3D "throttle", + .description =3D "throttle dl entities when they use up their runtime.", + .enable =3D enable_throttle, + .disable =3D disable_throttle, + .reset =3D da_monitor_reset_all, + .enabled =3D 0, +}; + +static int __init register_throttle(void) +{ + return rv_register_monitor(&rv_throttle, &rv_deadline); +} + +static void __exit unregister_throttle(void) +{ + rv_unregister_monitor(&rv_throttle); +} + +module_init(register_throttle); +module_exit(unregister_throttle); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Gabriele Monaco "); +MODULE_DESCRIPTION("throttle: throttle dl entities when they use up their = runtime."); diff --git a/kernel/trace/rv/monitors/throttle/throttle.h b/kernel/trace/rv= /monitors/throttle/throttle.h new file mode 100644 index 000000000000..084221556bae --- /dev/null +++ b/kernel/trace/rv/monitors/throttle/throttle.h @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Automatically generated C representation of throttle automaton + * For further information about this format, see kernel documentation: + * Documentation/trace/rv/deterministic_automata.rst + */ + +#define MONITOR_NAME throttle + +enum states_throttle { + running_throttle =3D 0, + armed_throttle, + armed_throttled_throttle, + preempted_throttle, + preempted_throttled_throttle, + throttled_throttle, + state_max_throttle +}; + +#define INVALID_STATE state_max_throttle + +enum events_throttle { + dl_defer_arm_throttle =3D 0, + dl_replenish_throttle, + dl_throttle_throttle, + sched_switch_in_throttle, + sched_switch_out_throttle, + event_max_throttle +}; + +enum envs_throttle { + clk_throttle =3D 0, + yielded_throttle, + env_max_throttle, + env_max_stored_throttle =3D yielded_throttle +}; + +_Static_assert(env_max_stored_throttle <=3D MAX_HA_ENV_LEN, "Not enough sl= ots"); + +struct automaton_throttle { + char *state_names[state_max_throttle]; + char *event_names[event_max_throttle]; + char *env_names[env_max_throttle]; + unsigned char function[state_max_throttle][event_max_throttle]; + unsigned char initial_state; + bool final_states[state_max_throttle]; +}; + +static const struct automaton_throttle automaton_throttle =3D { + .state_names =3D { + "running", + "armed", + "armed_throttled", + "preempted", + "preempted_throttled", + "throttled" + }, + .event_names =3D { + "dl_defer_arm", + "dl_replenish", + "dl_throttle", + "sched_switch_in", + "sched_switch_out" + }, + .env_names =3D { + "clk", + "yielded" + }, + .function =3D { + { + armed_throttle, + running_throttle, + throttled_throttle, + running_throttle, + preempted_throttle + }, + { + armed_throttle, + armed_throttle, + armed_throttled_throttle, + running_throttle, + preempted_throttle + }, + { + armed_throttled_throttle, + armed_throttle, + armed_throttled_throttle, + INVALID_STATE, + preempted_throttled_throttle + }, + { + armed_throttle, + preempted_throttle, + INVALID_STATE, + running_throttle, + preempted_throttle + }, + { + armed_throttled_throttle, + preempted_throttle, + INVALID_STATE, + INVALID_STATE, + preempted_throttled_throttle + }, + { + armed_throttled_throttle, + running_throttle, + INVALID_STATE, + INVALID_STATE, + preempted_throttled_throttle + }, + }, + .initial_state =3D running_throttle, + .final_states =3D { 1, 0, 0, 0, 0, 0 }, +}; diff --git a/kernel/trace/rv/monitors/throttle/throttle_trace.h b/kernel/tr= ace/rv/monitors/throttle/throttle_trace.h new file mode 100644 index 000000000000..7e376d3aec60 --- /dev/null +++ b/kernel/trace/rv/monitors/throttle/throttle_trace.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Snippet to be included in rv_trace.h + */ + +#ifdef CONFIG_RV_MON_THROTTLE +DEFINE_EVENT(event_da_monitor_id, event_throttle, + TP_PROTO(int id, char *state, char *event, char *next_state, bool fi= nal_state), + TP_ARGS(id, state, event, next_state, final_state)); + +DEFINE_EVENT(error_da_monitor_id, error_throttle, + TP_PROTO(int id, char *state, char *event), + TP_ARGS(id, state, event)); + +DEFINE_EVENT(error_env_da_monitor_id, error_env_throttle, + TP_PROTO(int id, char *state, char *event, char *env), + TP_ARGS(id, state, event, env)); +#endif /* CONFIG_RV_MON_THROTTLE */ diff --git a/kernel/trace/rv/rv_trace.h b/kernel/trace/rv/rv_trace.h index 601b03179328..40b19e2aa69c 100644 --- a/kernel/trace/rv/rv_trace.h +++ b/kernel/trace/rv/rv_trace.h @@ -188,6 +188,8 @@ DECLARE_EVENT_CLASS(error_env_da_monitor_id, ); =20 #include +#include +#include // Add new monitors based on CONFIG_HA_MON_EVENTS_ID here =20 #endif diff --git a/tools/verification/models/deadline/nomiss.dot b/tools/verifica= tion/models/deadline/nomiss.dot new file mode 100644 index 000000000000..81d7028cfc3b --- /dev/null +++ b/tools/verification/models/deadline/nomiss.dot @@ -0,0 +1,23 @@ +digraph state_automaton { + center =3D true; + size =3D "7,11"; + {node [shape =3D plaintext, style=3Dinvis, label=3D""] "__init_running"}; + {node [shape =3D doublecircle] "running"}; + {node [shape =3D circle] "running"}; + {node [shape =3D circle] "sleeping"}; + {node [shape =3D circle] "throttled"}; + "__init_running" -> "running"; + "running" [label =3D "running\nclk < DEADLINE_LEFT_NS()", color =3D green= 3]; + "running" -> "running" [ label =3D "sched_switch_in\nsched_wakeup" ]; + "running" -> "sleeping" [ label =3D "sched_switch_suspend" ]; + "running" -> "throttled" [ label =3D "dl_throttle" ]; + "sleeping" [label =3D "sleeping"]; + "sleeping" -> "running" [ label =3D "sched_wakeup;reset(clk)" ]; + "throttled" [label =3D "throttled"]; + "throttled" -> "running" [ label =3D "sched_switch_in\nsched_wakeup;reset= (clk)" ]; + "throttled" -> "throttled" [ label =3D "sched_switch_suspend\ndl_throttle= " ]; + { rank =3D min ; + "__init_running"; + "running"; + } +} diff --git a/tools/verification/models/deadline/throttle.dot b/tools/verifi= cation/models/deadline/throttle.dot new file mode 100644 index 000000000000..2477115aa286 --- /dev/null +++ b/tools/verification/models/deadline/throttle.dot @@ -0,0 +1,43 @@ +digraph state_automaton { + center =3D true; + size =3D "7,11"; + {node [shape =3D circle] "armed"}; + {node [shape =3D circle] "armed_throttled"}; + {node [shape =3D circle] "preempted"}; + {node [shape =3D circle] "preempted_throttled"}; + {node [shape =3D plaintext, style=3Dinvis, label=3D""] "__init_running"}; + {node [shape =3D doublecircle] "running"}; + {node [shape =3D circle] "running"}; + {node [shape =3D circle] "throttled"}; + "__init_running" -> "running"; + "armed" [label =3D "armed"]; + "armed" -> "armed" [ label =3D "dl_replenish\ndl_defer_arm" ]; + "armed" -> "armed_throttled" [ label =3D "dl_throttle" ]; + "armed" -> "preempted" [ label =3D "sched_switch_out" ]; + "armed" -> "running" [ label =3D "sched_switch_in;reset(clk)" ]; + "armed_throttled" [label =3D "armed_throttled"]; + "armed_throttled" -> "armed" [ label =3D "dl_replenish" ]; + "armed_throttled" -> "armed_throttled" [ label =3D "dl_defer_arm\ndl_thro= ttle;yielded=3D=3D1" ]; + "armed_throttled" -> "preempted_throttled" [ label =3D "sched_switch_out"= ]; + "preempted" [label =3D "preempted"]; + "preempted" -> "armed" [ label =3D "dl_defer_arm" ]; + "preempted" -> "preempted" [ label =3D "dl_replenish\nsched_switch_out" ]; + "preempted" -> "running" [ label =3D "sched_switch_in;reset(clk)" ]; + "preempted_throttled" [label =3D "preempted_throttled"]; + "preempted_throttled" -> "armed_throttled" [ label =3D "dl_defer_arm" ]; + "preempted_throttled" -> "preempted" [ label =3D "dl_replenish" ]; + "preempted_throttled" -> "preempted_throttled" [ label =3D "sched_switch_= out" ]; + "running" [label =3D "running\nclk < runtime_left_ns()", color =3D green3= ]; + "running" -> "armed" [ label =3D "dl_defer_arm" ]; + "running" -> "preempted" [ label =3D "sched_switch_out" ]; + "running" -> "running" [ label =3D "dl_replenish;reset(clk)\nsched_switch= _in" ]; + "running" -> "throttled" [ label =3D "dl_throttle" ]; + "throttled" [label =3D "throttled"]; + "throttled" -> "armed_throttled" [ label =3D "dl_defer_arm" ]; + "throttled" -> "preempted_throttled" [ label =3D "sched_switch_out" ]; + "throttled" -> "running" [ label =3D "dl_replenish;reset(clk)" ]; + { rank =3D min ; + "__init_running"; + "running"; + } +} --=20 2.50.1