panfrost: Performance configuration
authorAntonio Caggiano <antonio.caggiano@collabora.com>
Thu, 13 May 2021 14:06:11 +0000 (16:06 +0200)
committerMarge Bot <eric+marge@anholt.net>
Thu, 27 May 2021 13:24:54 +0000 (13:24 +0000)
Configure a panfrost performance structure with tables of categories and
counters for the current product id. An array for storing counter values
read from the GPU is also managed by this structure. A generic read
function can be used to retrieve the value of a counter from the conter
values array.

v2: Generate tables instead of calling register functions.
v3: Simplify counter read function and `pan_gen_perf.py` write method.
v4: Accumulate counter values from all cores.
v5: Wrap `STATIC_ASSERT`s within unused functions.

Signed-off-by: Antonio Caggiano <antonio.caggiano@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/10844>

src/panfrost/meson.build
src/panfrost/perf/meson.build [new file with mode: 0644]
src/panfrost/perf/pan_gen_perf.py [new file with mode: 0644]
src/panfrost/perf/pan_perf.c [new file with mode: 0644]
src/panfrost/perf/pan_perf.h [new file with mode: 0644]

index a2c2911..dc0473b 100644 (file)
@@ -32,6 +32,7 @@ subdir('util')
 subdir('midgard')
 subdir('bifrost')
 subdir('lib')
+subdir('perf')
 
 files_bifrost = files(
   'bifrost/cmdline.c',
diff --git a/src/panfrost/perf/meson.build b/src/panfrost/perf/meson.build
new file mode 100644 (file)
index 0000000..5f762c3
--- /dev/null
@@ -0,0 +1,57 @@
+# Copyright © 2021 Collabora, Ltd.
+# Author: Antonio Caggiano <antonio.caggiano@collabora.com>
+
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+pan_hw_metrics = [
+  'G31', 'G51', 'G52', 'G57', 'G68', 'G71', 'G72', 'G76', 'G77',
+  'G78', 'T72x', 'T76x', 'T82x', 'T83x', 'T86x', 'T88x',
+]
+
+pan_hw_metrics_xml_files = []
+foreach hw : pan_hw_metrics
+  pan_hw_metrics_xml_files += '@0@.xml'.format(hw)
+endforeach
+
+pan_perf_sources = [
+  'pan_perf.c',
+]
+
+pan_perf_sources += custom_target(
+  'pan-perf-sources',
+  input : pan_hw_metrics_xml_files,
+  output : [ 'pan_perf_metrics.c', 'pan_perf_metrics.h' ],
+  command : [
+    prog_python, files('pan_gen_perf.py'),
+    '--code', '@OUTPUT0@', '--header', '@OUTPUT1@',
+    '@INPUT@',
+  ],
+)
+
+libpanfrost_perf = static_library(
+  'panfrost_perf',
+  pan_perf_sources,
+  include_directories : [inc_include, inc_src, inc_panfrost],
+  dependencies: libpanfrost_dep
+)
+
+dep_panfrost_perf = declare_dependency(
+  link_with: libpanfrost_perf,
+  include_directories: [inc_panfrost, inc_src, inc_include]
+)
diff --git a/src/panfrost/perf/pan_gen_perf.py b/src/panfrost/perf/pan_gen_perf.py
new file mode 100644 (file)
index 0000000..2da8025
--- /dev/null
@@ -0,0 +1,227 @@
+# Copyright © 2021 Collabora, Ltd.
+# Author: Antonio Caggiano <antonio.caggiano@collabora.com>
+
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+import argparse
+import textwrap
+import os
+
+import xml.etree.ElementTree as et
+
+
+class SourceFile:
+   def __init__(self, filename):
+      self.file = open(filename, 'w')
+      self._indent = 0
+
+   def write(self, *args):
+      code = ' '.join(map(str,args))
+      for line in code.splitlines():
+         text = ''.rjust(self._indent) + line
+         self.file.write(text.rstrip() + "\n")
+
+   def indent(self, n):
+      self._indent += n
+
+   def outdent(self, n):
+      self._indent -= n
+
+
+class Counter:
+   # category Category owning the counter
+   # xml XML representation of itself
+   def __init__(self, category, xml):
+      self.category = category
+      self.xml = xml
+      self.name = self.xml.get("name")
+      self.desc = self.xml.get("description")
+      self.units = self.xml.get("units")
+      self.offset = int(self.xml.get("offset"))
+      self.underscore_name = self.xml.get("counter").lower()
+
+
+class Category:
+   # product Product owning the gategory
+   # xml XML representation of itself
+   def __init__(self, product, xml):
+      self.product = product
+      self.xml = xml
+      self.name = self.xml.get("name")
+      self.underscore_name = self.name.lower().replace(' ', '_')
+
+      xml_counters = self.xml.findall("event")
+      self.counters = []
+      for xml_counter in xml_counters:
+         self.counters.append(Counter(self, xml_counter))
+
+
+# Wraps an entire *.xml file.
+class Product:
+   def __init__(self, filename):
+      self.filename = filename
+      self.xml = et.parse(self.filename)
+      self.id = self.xml.getroot().get('id').lower()
+      self.categories = []
+
+      for xml_cat in self.xml.findall(".//category"):
+         self.categories.append(Category(self, xml_cat))
+
+
+def main():
+   parser = argparse.ArgumentParser()
+   parser.add_argument("--header", help="Header file to write", required=True)
+   parser.add_argument("--code", help="C file to write", required=True)
+   parser.add_argument("xml_files", nargs='+', help="List of xml metrics files to process")
+
+   args = parser.parse_args()
+
+   c = SourceFile(args.code)
+   h = SourceFile(args.header)
+
+   prods = []
+   for xml_file in args.xml_files:
+      prods.append(Product(xml_file))
+
+   tab_size = 3
+
+   copyright = textwrap.dedent("""\
+      /* Autogenerated file, DO NOT EDIT manually! generated by {}
+       *
+       * Copyright © 2021 Arm Limited
+       * Copyright © 2021 Collabora Ltd.
+       *
+       * Permission is hereby granted, free of charge, to any person obtaining a
+       * copy of this software and associated documentation files (the "Software"),
+       * to deal in the Software without restriction, including without limitation
+       * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+       * and/or sell copies of the Software, and to permit persons to whom the
+       * Software is furnished to do so, subject to the following conditions:
+       *
+       * The above copyright notice and this permission notice (including the next
+       * paragraph) shall be included in all copies or substantial portions of the
+       * Software.
+       *
+       * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+       * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+       * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+       * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+       * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+       * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+       * DEALINGS IN THE SOFTWARE.
+       */
+
+      """).format(os.path.basename(__file__))
+
+   h.write(copyright)
+   h.write(textwrap.dedent("""\
+      #ifndef PAN_PERF_METRICS_H
+      #define PAN_PERF_METRICS_H
+
+      #include "perf/pan_perf.h"
+
+      """))
+
+   c.write(copyright)
+   c.write("#include \"" + os.path.basename(args.header) + "\"")
+   c.write(textwrap.dedent("""\
+
+      #include <util/macros.h>
+      """))
+
+   for prod in prods:
+      h.write("extern const struct panfrost_perf_config panfrost_perf_config_%s;\n" % prod.id)
+
+   for prod in prods:
+      c.write(textwrap.dedent("""
+      static void UNUSED
+      static_asserts_%s(void)
+      {
+      """ % prod.id))
+      c.indent(tab_size)
+
+      n_categories = len(prod.categories)
+      c.write("STATIC_ASSERT(%u <= PAN_PERF_MAX_CATEGORIES);" % n_categories)
+      n_counters = 0
+      for category in prod.categories:
+         category_counters_count = len(category.counters)
+         c.write("STATIC_ASSERT(%u <= PAN_PERF_MAX_COUNTERS);" % category_counters_count)
+         n_counters += category_counters_count
+      
+      c.outdent(tab_size)
+      c.write("}\n")
+
+
+      current_struct_name = "panfrost_perf_config_%s" % prod.id
+      c.write("\nconst struct panfrost_perf_config %s = {" % current_struct_name)
+      c.indent(tab_size)
+      
+      c.write(".n_categories = %u," % len(prod.categories))
+
+      c.write(".categories = {")
+      c.indent(tab_size)
+
+      counter_id = 0
+
+      for i in range(0, len(prod.categories)):
+         category = prod.categories[i]
+
+         c.write("{")
+         c.indent(tab_size)
+         c.write(".name = \"%s\"," % (category.name))
+         c.write(".n_counters = %u," % (len(category.counters)))
+         c.write(".counters = {")
+         c.indent(tab_size)
+
+         for j in range(0, len(category.counters)):
+            counter = category.counters[j]
+
+            assert counter_id < n_counters
+            c.write("{")
+            c.indent(tab_size)
+
+            c.write(".name = \"%s\"," % (counter.name))
+            c.write(".desc = \"%s\"," % (counter.desc.replace("\\", "\\\\")))
+            c.write(".symbol_name = \"%s\"," % (counter.underscore_name))
+            c.write(".units = PAN_PERF_COUNTER_UNITS_%s," % (counter.units.upper()))
+            c.write(".offset = %u," % (counter.offset))
+            c.write(".category = &%s.categories[%u]," % (current_struct_name, i))
+
+            c.outdent(tab_size)
+            c.write("}, // counter")
+
+            counter_id += 1
+
+         c.outdent(tab_size)
+         c.write("}, // counters")
+
+         c.outdent(tab_size)
+         c.write("}, // category")
+
+      c.outdent(tab_size)
+      c.write("}, // categories")
+
+      c.outdent(tab_size)
+      c.write("}; // %s\n" % current_struct_name)
+
+   h.write("\n#endif // PAN_PERF_METRICS_H")
+
+
+if __name__ == '__main__':
+   main()
diff --git a/src/panfrost/perf/pan_perf.c b/src/panfrost/perf/pan_perf.c
new file mode 100644 (file)
index 0000000..44e11af
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright © 2021 Collabora, Ltd.
+ * Author: Antonio Caggiano <antonio.caggiano@collabora.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "pan_perf.h"
+
+#include <pan_perf_metrics.h>
+#include <lib/pan_device.h>
+#include <drm-uapi/panfrost_drm.h>
+
+#define PAN_COUNTERS_PER_CATEGORY 64
+#define PAN_SHADER_CORE_INDEX 2
+
+uint32_t
+panfrost_perf_counter_read(const struct panfrost_perf_counter *counter,
+                           const struct panfrost_perf *perf)
+{
+   assert(counter->offset < perf->n_counter_values);
+   uint32_t ret = perf->counter_values[counter->offset];
+
+   // If counter belongs to shader core, accumulate values for all other cores
+   if (counter->category == &perf->cfg->categories[PAN_SHADER_CORE_INDEX]) {
+      for (uint32_t core = 1; core < perf->dev->core_count; ++core) {
+         ret += perf->counter_values[counter->offset + PAN_COUNTERS_PER_CATEGORY * core];
+      }
+   }
+
+   return ret;
+}
+
+static const struct panfrost_perf_config*
+get_perf_config(unsigned int gpu_id)
+{
+   switch (gpu_id) {
+   case 0x720:
+      return &panfrost_perf_config_t72x;
+   case 0x760:
+      return &panfrost_perf_config_t76x;
+   case 0x820:
+      return &panfrost_perf_config_t82x;
+   case 0x830:
+      return &panfrost_perf_config_t83x;
+   case 0x860:
+      return &panfrost_perf_config_t86x;
+   case 0x880:
+      return &panfrost_perf_config_t88x;
+   case 0x6221:
+      return &panfrost_perf_config_thex;
+   case 0x7093:
+      return &panfrost_perf_config_tdvx;
+   case 0x7212:
+   case 0x7402:
+      return &panfrost_perf_config_tgox;
+   default:
+      unreachable("Invalid GPU ID");
+   }
+}
+
+void
+panfrost_perf_init(struct panfrost_perf *perf, struct panfrost_device *dev)
+{
+   perf->dev = dev;
+   perf->cfg = get_perf_config(dev->gpu_id);
+
+   // Generally counter blocks are laid out in the following order:
+   // Job manager, tiler, L2 cache, and one or more shader cores.
+   uint32_t n_blocks = 3 + dev->core_count;
+   perf->n_counter_values = PAN_COUNTERS_PER_CATEGORY * n_blocks;
+   perf->counter_values = ralloc_array(perf, uint32_t, perf->n_counter_values);
+}
+
+static int
+panfrost_perf_query(struct panfrost_perf *perf, uint32_t enable)
+{
+   struct drm_panfrost_perfcnt_enable perfcnt_enable = {enable, 0};
+   return drmIoctl(perf->dev->fd, DRM_IOCTL_PANFROST_PERFCNT_ENABLE, &perfcnt_enable);
+}
+
+int
+panfrost_perf_enable(struct panfrost_perf *perf)
+{
+   return panfrost_perf_query(perf, 1 /* enable */);
+}
+
+int
+panfrost_perf_disable(struct panfrost_perf *perf)
+{
+   return panfrost_perf_query(perf, 0 /* disable */);
+}
+
+int
+panfrost_perf_dump(struct panfrost_perf *perf)
+{
+   // Dump performance counter values to the memory buffer pointed to by counter_values
+   struct drm_panfrost_perfcnt_dump perfcnt_dump = {(uint64_t)(uintptr_t)perf->counter_values};
+   return drmIoctl(perf->dev->fd, DRM_IOCTL_PANFROST_PERFCNT_DUMP, &perfcnt_dump);
+}
diff --git a/src/panfrost/perf/pan_perf.h b/src/panfrost/perf/pan_perf.h
new file mode 100644 (file)
index 0000000..0a7155b
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright © 2021 Collabora, Ltd.
+ * Author: Antonio Caggiano <antonio.caggiano@collabora.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef PAN_PERF_H
+#define PAN_PERF_H
+
+#include <stdint.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define PAN_PERF_MAX_CATEGORIES 4
+#define PAN_PERF_MAX_COUNTERS 64
+
+struct panfrost_device;
+struct panfrost_perf_category;
+struct panfrost_perf;
+
+enum panfrost_perf_counter_units {
+   PAN_PERF_COUNTER_UNITS_CYCLES,
+   PAN_PERF_COUNTER_UNITS_JOBS,
+   PAN_PERF_COUNTER_UNITS_TASKS,
+   PAN_PERF_COUNTER_UNITS_PRIMITIVES,
+   PAN_PERF_COUNTER_UNITS_BEATS,
+   PAN_PERF_COUNTER_UNITS_REQUESTS,
+   PAN_PERF_COUNTER_UNITS_WARPS,
+   PAN_PERF_COUNTER_UNITS_QUADS,
+   PAN_PERF_COUNTER_UNITS_TILES,
+   PAN_PERF_COUNTER_UNITS_INSTRUCTIONS,
+   PAN_PERF_COUNTER_UNITS_TRANSACTIONS,
+   PAN_PERF_COUNTER_UNITS_THREADS,
+   PAN_PERF_COUNTER_UNITS_BYTES,
+   PAN_PERF_COUNTER_UNITS_PIXELS,
+   PAN_PERF_COUNTER_UNITS_ISSUES,
+};
+
+struct panfrost_perf_counter {
+   const char *name;
+   const char *desc;
+   const char *symbol_name;
+   enum panfrost_perf_counter_units units;
+   // Offset of this counter's value within the counters memory block
+   uint32_t offset;
+
+   const struct panfrost_perf_category *category;
+};
+
+struct panfrost_perf_category {
+   const char *name;
+
+   struct panfrost_perf_counter counters[PAN_PERF_MAX_COUNTERS];
+   uint32_t n_counters;
+};
+
+struct panfrost_perf_config {
+   struct panfrost_perf_category categories[PAN_PERF_MAX_CATEGORIES];
+   uint32_t n_categories;
+};
+
+struct panfrost_perf {
+   struct panfrost_device *dev;
+
+   const struct panfrost_perf_config* cfg;
+
+   // Memory where to dump counter values
+   uint32_t *counter_values;
+   uint32_t n_counter_values;
+};
+
+uint32_t
+panfrost_perf_counter_read(const struct panfrost_perf_counter *counter,
+            const struct panfrost_perf *perf);
+
+void
+panfrost_perf_init(struct panfrost_perf *perf, struct panfrost_device *dev);
+
+int
+panfrost_perf_enable(struct panfrost_perf *perf);
+
+int
+panfrost_perf_disable(struct panfrost_perf *perf);
+
+int
+panfrost_perf_dump(struct panfrost_perf *perf);
+
+#if defined(__cplusplus)
+} // extern "C"
+#endif
+
+#endif // PAN_PERF_H