]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
x86,fs/resctrl: Support binary fixed point event counters
authorTony Luck <tony.luck@intel.com>
Wed, 17 Dec 2025 17:20:59 +0000 (09:20 -0800)
committerBorislav Petkov (AMD) <bp@alien8.de>
Mon, 5 Jan 2026 15:10:41 +0000 (16:10 +0100)
resctrl assumes that all monitor events can be displayed as unsigned decimal
integers.

Hardware architecture counters may provide some telemetry events with greater
precision where the event is not a simple count, but is a measurement of some
sort (e.g. Joules for energy consumed).

Add a new argument to resctrl_enable_mon_event() for architecture code to
inform the file system that the value for a counter is a fixed-point value
with a specific number of binary places.

Only allow architecture to use floating point format on events that the file
system has marked with mon_evt::is_floating_point which reflects the contract
with user space on how the event values are displayed.

Display fixed point values with values rounded to ceil(binary_bits * log10(2))
decimal places. Special case for zero binary bits to print "{value}.0".

Signed-off-by: Tony Luck <tony.luck@intel.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Reviewed-by: Reinette Chatre <reinette.chatre@intel.com>
Link: https://lore.kernel.org/20251217172121.12030-1-tony.luck@intel.com
arch/x86/kernel/cpu/resctrl/core.c
fs/resctrl/ctrlmondata.c
fs/resctrl/internal.h
fs/resctrl/monitor.c
include/linux/resctrl.h

index bd4a981061530ca786b34e58856a8f9a5969d032..9222eee7ce07014615aaf4be06e50044c9c7f7b8 100644 (file)
@@ -902,15 +902,15 @@ static __init bool get_rdt_mon_resources(void)
        bool ret = false;
 
        if (rdt_cpu_has(X86_FEATURE_CQM_OCCUP_LLC)) {
-               resctrl_enable_mon_event(QOS_L3_OCCUP_EVENT_ID, false);
+               resctrl_enable_mon_event(QOS_L3_OCCUP_EVENT_ID, false, 0);
                ret = true;
        }
        if (rdt_cpu_has(X86_FEATURE_CQM_MBM_TOTAL)) {
-               resctrl_enable_mon_event(QOS_L3_MBM_TOTAL_EVENT_ID, false);
+               resctrl_enable_mon_event(QOS_L3_MBM_TOTAL_EVENT_ID, false, 0);
                ret = true;
        }
        if (rdt_cpu_has(X86_FEATURE_CQM_MBM_LOCAL)) {
-               resctrl_enable_mon_event(QOS_L3_MBM_LOCAL_EVENT_ID, false);
+               resctrl_enable_mon_event(QOS_L3_MBM_LOCAL_EVENT_ID, false, 0);
                ret = true;
        }
        if (rdt_cpu_has(X86_FEATURE_ABMC))
index 2c69fcd70eeb97fc950b528599fe5fca88b5acb0..f319fd1a6de31c26d9b22b76c89ad5def15ee1fc 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <linux/cpu.h>
 #include <linux/kernfs.h>
+#include <linux/math.h>
 #include <linux/seq_file.h>
 #include <linux/slab.h>
 #include <linux/tick.h>
@@ -601,6 +602,77 @@ out_ctx_free:
                resctrl_arch_mon_ctx_free(r, evt->evtid, rr->arch_mon_ctx);
 }
 
+/*
+ * Decimal place precision to use for each number of fixed-point
+ * binary bits computed from ceil(binary_bits * log10(2)) except
+ * binary_bits == 0 which will print "value.0"
+ */
+static const unsigned int decplaces[MAX_BINARY_BITS + 1] = {
+       [0]  =  1,
+       [1]  =  1,
+       [2]  =  1,
+       [3]  =  1,
+       [4]  =  2,
+       [5]  =  2,
+       [6]  =  2,
+       [7]  =  3,
+       [8]  =  3,
+       [9]  =  3,
+       [10] =  4,
+       [11] =  4,
+       [12] =  4,
+       [13] =  4,
+       [14] =  5,
+       [15] =  5,
+       [16] =  5,
+       [17] =  6,
+       [18] =  6,
+       [19] =  6,
+       [20] =  7,
+       [21] =  7,
+       [22] =  7,
+       [23] =  7,
+       [24] =  8,
+       [25] =  8,
+       [26] =  8,
+       [27] =  9
+};
+
+static void print_event_value(struct seq_file *m, unsigned int binary_bits, u64 val)
+{
+       unsigned long long frac = 0;
+
+       if (binary_bits) {
+               /* Mask off the integer part of the fixed-point value. */
+               frac = val & GENMASK_ULL(binary_bits - 1, 0);
+
+               /*
+                * Multiply by 10^{desired decimal places}. The integer part of
+                * the fixed point value is now almost what is needed.
+                */
+               frac *= int_pow(10ull, decplaces[binary_bits]);
+
+               /*
+                * Round to nearest by adding a value that would be a "1" in the
+                * binary_bits + 1 place.  Integer part of fixed point value is
+                * now the needed value.
+                */
+               frac += 1ull << (binary_bits - 1);
+
+               /*
+                * Extract the integer part of the value. This is the decimal
+                * representation of the original fixed-point fractional value.
+                */
+               frac >>= binary_bits;
+       }
+
+       /*
+        * "frac" is now in the range [0 .. 10^decplaces).  I.e. string
+        * representation will fit into chosen number of decimal places.
+        */
+       seq_printf(m, "%llu.%0*llu\n", val >> binary_bits, decplaces[binary_bits], frac);
+}
+
 int rdtgroup_mondata_show(struct seq_file *m, void *arg)
 {
        struct kernfs_open_file *of = m->private;
@@ -678,6 +750,8 @@ checkresult:
                seq_puts(m, "Unavailable\n");
        else if (rr.err == -ENOENT)
                seq_puts(m, "Unassigned\n");
+       else if (evt->is_floating_point)
+               print_event_value(m, evt->binary_bits, rr.val);
        else
                seq_printf(m, "%llu\n", rr.val);
 
index fb0b6e40d022b97b92fa9af0f35097ca1a6fba2a..14e5a9ed1fbd352175dec2413b8c50bd0f9a4801 100644 (file)
@@ -62,6 +62,9 @@ static inline struct rdt_fs_context *rdt_fc2context(struct fs_context *fc)
  *                     Only valid if @evtid is an MBM event.
  * @configurable:      true if the event is configurable
  * @any_cpu:           true if the event can be read from any CPU
+ * @is_floating_point: event values are displayed in floating point format
+ * @binary_bits:       number of fixed-point binary bits from architecture,
+ *                     only valid if @is_floating_point is true
  * @enabled:           true if the event is enabled
  */
 struct mon_evt {
@@ -71,6 +74,8 @@ struct mon_evt {
        u32                     evt_cfg;
        bool                    configurable;
        bool                    any_cpu;
+       bool                    is_floating_point;
+       unsigned int            binary_bits;
        bool                    enabled;
 };
 
@@ -79,6 +84,9 @@ extern struct mon_evt mon_event_all[QOS_NUM_EVENTS];
 #define for_each_mon_event(mevt) for (mevt = &mon_event_all[QOS_FIRST_EVENT];  \
                                      mevt < &mon_event_all[QOS_NUM_EVENTS]; mevt++)
 
+/* Limit for mon_evt::binary_bits */
+#define MAX_BINARY_BITS        27
+
 /**
  * struct mon_data - Monitoring details for each event file.
  * @list:            Member of the global @mon_data_kn_priv_list list.
index 8c76ac133bca3027ae620956ac9ffc832e521bd6..844cf6875f60747bd89ae664af451cadd3101848 100644 (file)
@@ -988,16 +988,22 @@ struct mon_evt mon_event_all[QOS_NUM_EVENTS] = {
        },
 };
 
-void resctrl_enable_mon_event(enum resctrl_event_id eventid, bool any_cpu)
+void resctrl_enable_mon_event(enum resctrl_event_id eventid, bool any_cpu, unsigned int binary_bits)
 {
-       if (WARN_ON_ONCE(eventid < QOS_FIRST_EVENT || eventid >= QOS_NUM_EVENTS))
+       if (WARN_ON_ONCE(eventid < QOS_FIRST_EVENT || eventid >= QOS_NUM_EVENTS ||
+                        binary_bits > MAX_BINARY_BITS))
                return;
        if (mon_event_all[eventid].enabled) {
                pr_warn("Duplicate enable for event %d\n", eventid);
                return;
        }
+       if (binary_bits && !mon_event_all[eventid].is_floating_point) {
+               pr_warn("Event %d may not be floating point\n", eventid);
+               return;
+       }
 
        mon_event_all[eventid].any_cpu = any_cpu;
+       mon_event_all[eventid].binary_bits = binary_bits;
        mon_event_all[eventid].enabled = true;
 }
 
index 22c5d07fe9ff56a5b10173bf805c73a71e332a23..c43526cdf304f2117daa51887f0bb4740a616809 100644 (file)
@@ -412,7 +412,8 @@ u32 resctrl_arch_get_num_closid(struct rdt_resource *r);
 u32 resctrl_arch_system_num_rmid_idx(void);
 int resctrl_arch_update_domains(struct rdt_resource *r, u32 closid);
 
-void resctrl_enable_mon_event(enum resctrl_event_id eventid, bool any_cpu);
+void resctrl_enable_mon_event(enum resctrl_event_id eventid, bool any_cpu,
+                             unsigned int binary_bits);
 
 bool resctrl_is_mon_event_enabled(enum resctrl_event_id eventid);