powerpc/powernv: Add debugfs interface for imc-mode and imc-command
authorAnju T Sudhakar <anju@linux.vnet.ibm.com>
Wed, 13 Dec 2017 06:09:54 +0000 (11:39 +0530)
committerMichael Ellerman <mpe@ellerman.id.au>
Fri, 19 Jan 2018 12:05:00 +0000 (23:05 +1100)
In memory Collection (IMC) counter pmu driver controls the ucode's
execution state. At the system boot, IMC perf driver pause the ucode.
Ucode state is changed to "running" only when any of the nest units
are monitored or profiled using perf tool.

Nest units support only limited set of hardware counters and ucode is
always programmed in the "production mode" ("accumulation") mode. This
mode is configured to provide key performance metric data for most of
the nest units.

But ucode also supports other modes which would be used for "debug" to
drill down specific nest units. That is, ucode when switched to
"powerbus" debug mode (for example), will dynamically reconfigure the
nest counters to target only "powerbus" related events in the hardware
counters. This allows the IMC nest unit to focus on powerbus related
transactions in the system in more detail. At this point, production
mode events may or may not be counted.

IMC nest counters has both in-band (ucode access) and out of band
access to it. Since not all nest counter configurations are supported
by ucode, out of band tools are used to characterize other nest
counter configurations.

Patch provides an interface via "debugfs" to enable the switching of
ucode modes in the system. To switch ucode mode, one has to first
pause the microcode (imc_cmd), and then write the target mode value to
the "imc_mode" file.

Proposed Approach:

In the proposed approach, the function (export_imc_mode_and_cmd) which
creates the debugfs interface for imc mode and command is implemented
in opal-imc.c. Thus we can use imc_get_mem_addr() to get the homer
base address for each chip.

The interface to expose imc mode and command is required only if we
have nest pmu units registered. Employing the existing data structures
to track whether we have any nest units registered will require to
extend data from perf side to opal-imc.c. Instead an integer is
introduced to hold that information by counting successful nest unit
registration. Debugfs interface is removed based on the integer count.

Example for the interface:

  $ ls /sys/kernel/debug/imc
  imc_cmd_0  imc_cmd_8  imc_mode_0  imc_mode_8

Signed-off-by: Anju T Sudhakar <anju@linux.vnet.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/include/asm/imc-pmu.h
arch/powerpc/platforms/powernv/opal-imc.c

index 080731dc883a3383e15ee981bfe907b603411257..d76cb11be3e3f054b3b49beea5945471db920331 100644 (file)
 #define THREAD_IMC_LDBAR_MASK           0x0003ffffffffe000ULL
 #define THREAD_IMC_ENABLE               0x8000000000000000ULL
 
+/*
+ * For debugfs interface for imc-mode and imc-command
+ */
+#define IMC_CNTL_BLK_OFFSET            0x3FC00
+#define IMC_CNTL_BLK_CMD_OFFSET                8
+#define IMC_CNTL_BLK_MODE_OFFSET       32
+
 /*
  * Structure to hold memory address information for imc units.
  */
index 465ea105b7710ecf0ff7320dcec8fd2b9775cf2e..dd4c9b8b8a81e6967b29061014918b4f591921df 100644 (file)
 #include <asm/io.h>
 #include <asm/imc-pmu.h>
 #include <asm/cputhreads.h>
+#include <asm/debugfs.h>
+
+static struct dentry *imc_debugfs_parent;
+
+/* Helpers to export imc command and mode via debugfs */
+static int imc_mem_get(void *data, u64 *val)
+{
+       *val = cpu_to_be64(*(u64 *)data);
+       return 0;
+}
+
+static int imc_mem_set(void *data, u64 val)
+{
+       *(u64 *)data = cpu_to_be64(val);
+       return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(fops_imc_x64, imc_mem_get, imc_mem_set, "0x%016llx\n");
+
+static struct dentry *imc_debugfs_create_x64(const char *name, umode_t mode,
+                                            struct dentry *parent, u64  *value)
+{
+       return debugfs_create_file_unsafe(name, mode, parent,
+                                         value, &fops_imc_x64);
+}
+
+/*
+ * export_imc_mode_and_cmd: Create a debugfs interface
+ *                     for imc_cmd and imc_mode
+ *                     for each node in the system.
+ *  imc_mode and imc_cmd can be changed by echo into
+ *  this interface.
+ */
+static void export_imc_mode_and_cmd(struct device_node *node,
+                                   struct imc_pmu *pmu_ptr)
+{
+       static u64 loc, *imc_mode_addr, *imc_cmd_addr;
+       int chip = 0, nid;
+       char mode[16], cmd[16];
+       u32 cb_offset;
+
+       imc_debugfs_parent = debugfs_create_dir("imc", powerpc_debugfs_root);
+
+       /*
+        * Return here, either because 'imc' directory already exists,
+        * Or failed to create a new one.
+        */
+       if (!imc_debugfs_parent)
+               return;
+
+       if (of_property_read_u32(node, "cb_offset", &cb_offset))
+               cb_offset = IMC_CNTL_BLK_OFFSET;
+
+       for_each_node(nid) {
+               loc = (u64)(pmu_ptr->mem_info[chip].vbase) + cb_offset;
+               imc_mode_addr = (u64 *)(loc + IMC_CNTL_BLK_MODE_OFFSET);
+               sprintf(mode, "imc_mode_%d", nid);
+               if (!imc_debugfs_create_x64(mode, 0600, imc_debugfs_parent,
+                                           imc_mode_addr))
+                       goto err;
+
+               imc_cmd_addr = (u64 *)(loc + IMC_CNTL_BLK_CMD_OFFSET);
+               sprintf(cmd, "imc_cmd_%d", nid);
+               if (!imc_debugfs_create_x64(cmd, 0600, imc_debugfs_parent,
+                                           imc_cmd_addr))
+                       goto err;
+               chip++;
+       }
+       return;
+
+err:
+       debugfs_remove_recursive(imc_debugfs_parent);
+}
 
 /*
  * imc_get_mem_addr_nest: Function to get nest counter memory region
@@ -65,6 +137,7 @@ static int imc_get_mem_addr_nest(struct device_node *node,
        }
 
        pmu_ptr->imc_counter_mmaped = true;
+       export_imc_mode_and_cmd(node, pmu_ptr);
        kfree(base_addr_arr);
        kfree(chipid_arr);
        return 0;
@@ -213,6 +286,10 @@ static int opal_imc_counters_probe(struct platform_device *pdev)
                }
        }
 
+       /* If none of the nest units are registered, remove debugfs interface */
+       if (pmu_count == 0)
+               debugfs_remove_recursive(imc_debugfs_parent);
+
        return 0;
 }