Message ID | 20250127223301.2819108-3-vinay.belgaumkar@intel.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | tests/intel/xe_pmu: Add PMU tests | expand |
On Mon, Jan 27, 2025 at 02:33:01PM -0800, Vinay Belgaumkar wrote: >Simple tests for validating the PMU implementation for GT C6 >residencies. > >v2: Rename rc6-residency-* to gt-c6-residency and remove freq tests. >v3: Keep just gt-c6 tests, add frequency tests later. >v4: Review comments (Riana) > >Cc: Lucas De Marchi <lucas.demarchi@intel.com> >Cc: Riana Tauro <riana.tauro@intel.com> >Cc: Rodrigo Vivi <rodrigo.vivi@intel.com> >Signed-off-by: Vinay Belgaumkar <vinay.belgaumkar@intel.com> >--- > tests/intel/xe_pmu.c | 176 +++++++++++++++++++++++++++++++++++++++++++ > tests/meson.build | 1 + > 2 files changed, 177 insertions(+) > create mode 100644 tests/intel/xe_pmu.c > >diff --git a/tests/intel/xe_pmu.c b/tests/intel/xe_pmu.c >new file mode 100644 >index 000000000..61a7a7e38 >--- /dev/null >+++ b/tests/intel/xe_pmu.c >@@ -0,0 +1,176 @@ >+// SPDX-License-Identifier: MIT >+/* >+ * Copyright © 2025 Intel Corporation >+ */ >+ >+/** >+ * TEST: Test Xe PMU functionality >+ * Category: Perf Monitoring Unit >+ * Mega feature: Perf Monitoring Unit >+ * Sub-category: Power Management this is not accurate... power management is 1 of the possible counters we can expose from kernel to userspace. >+ * Functionality: Power/Perf >+ * Test category: Functional tests >+ */ >+ >+#include <fcntl.h> >+#include <limits.h> >+#include <time.h> >+#include <errno.h> >+#include <dirent.h> >+#include <string.h> >+#include <sys/time.h> >+ >+#include "igt.h" >+#include "igt_device.h" >+#include "igt_power.h" >+#include "igt_sysfs.h" >+#include "igt_perf.h" >+ >+#include "lib/igt_syncobj.h" >+#include "xe/xe_ioctl.h" >+#include "xe/xe_gt.h" >+#include "xe/xe_query.h" >+#include "xe/xe_spin.h" >+#include "xe/xe_util.h" >+ >+#define SLEEP_DURATION 2 /* in seconds */ >+const double tolerance = 0.1; >+const unsigned long batch_duration_ns = 500e6; unused >+const char *no_debug_data = "\0"; no idea what this is for >+ >+#define __assert_within_epsilon(x, ref, tol_up, tol_down, debug_data) \ >+ igt_assert_f((double)(x) <= (1.0 + (tol_up)) * (double)(ref) && \ >+ (double)(x) >= (1.0 - (tol_down)) * (double)(ref), \ >+ "'%s' != '%s' (%f not within +%.1f%%/-%.1f%% tolerance of %f)\n%s\n",\ >+ #x, #ref, (double)(x), \ >+ (tol_up) * 100.0, (tol_down) * 100.0, \ >+ (double)(ref), debug_data) >+ >+#define assert_within_epsilon(x, ref, tolerance) \ >+ __assert_within_epsilon(x, ref, tolerance, tolerance, no_debug_data) git grep __assert_within_epsilon we already have too much of these copy and paste. Please, don't add more. >+ >+static int open_pmu(int xe, uint64_t config) >+{ >+ int fd; >+ >+ fd = perf_xe_open(xe, config); >+ igt_skip_on(fd < 0 && errno == ENODEV); >+ igt_assert(fd >= 0); >+ >+ return fd; >+} >+ >+static uint64_t __pmu_read_single(int fd, uint64_t *ts) >+{ >+ uint64_t data[2]; >+ >+ igt_assert_eq(read(fd, data, sizeof(data)), sizeof(data)); >+ if (ts) >+ *ts = data[1]; >+ >+ return data[0]; >+} >+ >+/** >+ * SUBTEST: gt-c6 >+ * Description: Basic residency test to validate idle residency >+ * measured over a time interval is within the tolerance why is this comment on top of measured_usleep()? >+ */ >+static unsigned int measured_usleep(unsigned int usec) >+{ >+ struct timespec ts = { }; >+ unsigned int slept; >+ >+ slept = igt_nsec_elapsed(&ts); this function is another copy and paste that is wrong everywhere. >+ igt_assert(slept == 0); >+ do { >+ usleep(usec - slept); slept is in nanosec, while usec is in usec. >+ slept = igt_nsec_elapsed(&ts) / 1000; >+ } while (slept < usec); >+ >+ return igt_nsec_elapsed(&ts) / 1000; we have macros to convert usec/msec/nsec >+} >+ >+static unsigned long read_idle_residency(int fd, int gt) >+{ >+ unsigned long residency = 0; >+ int gt_fd; >+ >+ gt_fd = xe_sysfs_gt_open(fd, gt); >+ igt_assert(gt_fd >= 0); >+ igt_assert(igt_sysfs_scanf(gt_fd, "gtidle/idle_residency_ms", "%lu", &residency) == 1); >+ close(gt_fd); >+ >+ return residency; >+} >+ >+static u64 get_event_config(int xe, unsigned int gt, char *event) >+{ >+ int ret; >+ char xe_device[100]; >+ u64 pmu_config; >+ u32 start, end; >+ >+ xe_perf_device(xe, xe_device, sizeof(xe_device)); >+ ret = perf_event_config(xe_device, event, &pmu_config); >+ igt_assert(ret >= 0); >+ ret = perf_event_format(xe_device, "gt", &start, &end); >+ igt_assert(ret >= 0); >+ pmu_config |= (u64) gt << start; >+ >+ return pmu_config; >+} >+ >+static void test_gt_c6(int xe, unsigned int gt) >+{ >+ int pmu_fd; >+ u64 pmu_config; >+ char event[100]; >+ uint64_t ts[2]; >+ unsigned long slept, start, end; >+ uint64_t val; >+ >+ /* Get the PMU config for the gt-c6 event */ >+ sprintf(event, "gt-c6-residency"); >+ pmu_config = get_event_config(xe, gt, event); >+ >+ pmu_fd = open_pmu(xe, pmu_config); >+ >+ igt_require_f(igt_wait(xe_gt_is_in_c6(xe, gt), 1000, 10), "GT %d should be in C6\n", gt); >+ >+ /* While idle check full RC6. */ >+ start = read_idle_residency(xe, gt); >+ val = __pmu_read_single(pmu_fd, &ts[0]); >+ slept = measured_usleep(SLEEP_DURATION * USEC_PER_SEC) / 1000; >+ end = read_idle_residency(xe, gt); >+ val = __pmu_read_single(pmu_fd, &ts[1]) - val; I actually expected 2 subtests: 1) gt_c6_idle we should be in rc6 for the entire time when nothing is running 2) gt_c6_full_load we should not be in rc6 when something is keeping the engine busy For (2), we can start a work load with xe_cork >+ >+ igt_debug("gt%u: slept=%lu, perf=%"PRIu64"\n", >+ gt, slept, val); >+ >+ igt_debug("Start res: %lu, end_res: %lu", start, end); is this reading the value from sysfs to aid debugging? I tested this with the kernel implementation, before merging it, and it worked. I don't mind if this is merged and cleaned on top. I will leave it for you and Riana to decide. thanks Lucas De Marchi >+ >+ assert_within_epsilon(val, >+ (ts[1] - ts[0])/USEC_PER_SEC, >+ tolerance); >+ close(pmu_fd); >+} >+ >+igt_main >+{ >+ int fd, gt; >+ >+ igt_fixture { >+ fd = drm_open_driver(DRIVER_XE); >+ igt_require(!IS_PONTEVECCHIO(xe_dev_id(fd))); >+ } >+ >+ igt_describe("Validate PMU gt-c6 residency counters"); >+ igt_subtest("gt-c6") >+ xe_for_each_gt(fd, gt) >+ test_gt_c6(fd, gt); >+ >+ igt_fixture { >+ close(fd); >+ } >+} >diff --git a/tests/meson.build b/tests/meson.build >index 33dffad31..d20f50766 100644 >--- a/tests/meson.build >+++ b/tests/meson.build >@@ -309,6 +309,7 @@ intel_xe_progs = [ > 'xe_pat', > 'xe_peer2peer', > 'xe_pm', >+ 'xe_pmu', > 'xe_pm_residency', > 'xe_prime_self_import', > 'xe_query', >-- >2.38.1 >
On 1/27/2025 3:56 PM, Lucas De Marchi wrote: > On Mon, Jan 27, 2025 at 02:33:01PM -0800, Vinay Belgaumkar wrote: >> Simple tests for validating the PMU implementation for GT C6 >> residencies. >> >> v2: Rename rc6-residency-* to gt-c6-residency and remove freq tests. >> v3: Keep just gt-c6 tests, add frequency tests later. >> v4: Review comments (Riana) >> >> Cc: Lucas De Marchi <lucas.demarchi@intel.com> >> Cc: Riana Tauro <riana.tauro@intel.com> >> Cc: Rodrigo Vivi <rodrigo.vivi@intel.com> >> Signed-off-by: Vinay Belgaumkar <vinay.belgaumkar@intel.com> >> --- >> tests/intel/xe_pmu.c | 176 +++++++++++++++++++++++++++++++++++++++++++ >> tests/meson.build | 1 + >> 2 files changed, 177 insertions(+) >> create mode 100644 tests/intel/xe_pmu.c >> >> diff --git a/tests/intel/xe_pmu.c b/tests/intel/xe_pmu.c >> new file mode 100644 >> index 000000000..61a7a7e38 >> --- /dev/null >> +++ b/tests/intel/xe_pmu.c >> @@ -0,0 +1,176 @@ >> +// SPDX-License-Identifier: MIT >> +/* >> + * Copyright © 2025 Intel Corporation >> + */ >> + >> +/** >> + * TEST: Test Xe PMU functionality >> + * Category: Perf Monitoring Unit >> + * Mega feature: Perf Monitoring Unit >> + * Sub-category: Power Management > > this is not accurate... power management is 1 of the possible counters > we can expose from kernel to userspace. Ok, maybe Telemetry? > >> + * Functionality: Power/Perf >> + * Test category: Functional tests >> + */ >> + >> +#include <fcntl.h> >> +#include <limits.h> >> +#include <time.h> >> +#include <errno.h> >> +#include <dirent.h> >> +#include <string.h> >> +#include <sys/time.h> >> + >> +#include "igt.h" >> +#include "igt_device.h" >> +#include "igt_power.h" >> +#include "igt_sysfs.h" >> +#include "igt_perf.h" >> + >> +#include "lib/igt_syncobj.h" >> +#include "xe/xe_ioctl.h" >> +#include "xe/xe_gt.h" >> +#include "xe/xe_query.h" >> +#include "xe/xe_spin.h" >> +#include "xe/xe_util.h" >> + >> +#define SLEEP_DURATION 2 /* in seconds */ >> +const double tolerance = 0.1; >> +const unsigned long batch_duration_ns = 500e6; > > unused ok. Remnant from the freq tests. > >> +const char *no_debug_data = "\0"; > > no idea what this is for It is used in the assert macro below or an error message when assert happens. > >> + >> +#define __assert_within_epsilon(x, ref, tol_up, tol_down, debug_data) \ >> + igt_assert_f((double)(x) <= (1.0 + (tol_up)) * (double)(ref) && \ >> + (double)(x) >= (1.0 - (tol_down)) * (double)(ref), \ >> + "'%s' != '%s' (%f not within +%.1f%%/-%.1f%% tolerance >> of %f)\n%s\n",\ >> + #x, #ref, (double)(x), \ >> + (tol_up) * 100.0, (tol_down) * 100.0, \ >> + (double)(ref), debug_data) >> + >> +#define assert_within_epsilon(x, ref, tolerance) \ >> + __assert_within_epsilon(x, ref, tolerance, tolerance, >> no_debug_data) > > git grep __assert_within_epsilon > > we already have too much of these copy and paste. Please, don't add > more. ok, will move to a lib. > >> + >> +static int open_pmu(int xe, uint64_t config) >> +{ >> + int fd; >> + >> + fd = perf_xe_open(xe, config); >> + igt_skip_on(fd < 0 && errno == ENODEV); >> + igt_assert(fd >= 0); >> + >> + return fd; >> +} >> + >> +static uint64_t __pmu_read_single(int fd, uint64_t *ts) >> +{ >> + uint64_t data[2]; >> + >> + igt_assert_eq(read(fd, data, sizeof(data)), sizeof(data)); >> + if (ts) >> + *ts = data[1]; >> + >> + return data[0]; >> +} >> + >> +/** >> + * SUBTEST: gt-c6 >> + * Description: Basic residency test to validate idle residency >> + * measured over a time interval is within the tolerance > > why is this comment on top of measured_usleep()? will move. > >> + */ >> +static unsigned int measured_usleep(unsigned int usec) >> +{ >> + struct timespec ts = { }; >> + unsigned int slept; >> + >> + slept = igt_nsec_elapsed(&ts); > > this function is another copy and paste that is wrong everywhere. ok. > > >> + igt_assert(slept == 0); >> + do { >> + usleep(usec - slept); > > slept is in nanosec, while usec is in usec. we are dividing by 1000, so slept is usec as well (except the first time, but that is asserted to be 0 anyways). > >> + slept = igt_nsec_elapsed(&ts) / 1000; >> + } while (slept < usec); >> + >> + return igt_nsec_elapsed(&ts) / 1000; > > we have macros to convert usec/msec/nsec Yup, I will add a separate patch for moving this to a lib. > >> +} >> + >> +static unsigned long read_idle_residency(int fd, int gt) >> +{ >> + unsigned long residency = 0; >> + int gt_fd; >> + >> + gt_fd = xe_sysfs_gt_open(fd, gt); >> + igt_assert(gt_fd >= 0); >> + igt_assert(igt_sysfs_scanf(gt_fd, "gtidle/idle_residency_ms", >> "%lu", &residency) == 1); >> + close(gt_fd); >> + >> + return residency; >> +} >> + >> +static u64 get_event_config(int xe, unsigned int gt, char *event) >> +{ >> + int ret; >> + char xe_device[100]; >> + u64 pmu_config; >> + u32 start, end; >> + >> + xe_perf_device(xe, xe_device, sizeof(xe_device)); >> + ret = perf_event_config(xe_device, event, &pmu_config); >> + igt_assert(ret >= 0); >> + ret = perf_event_format(xe_device, "gt", &start, &end); >> + igt_assert(ret >= 0); >> + pmu_config |= (u64) gt << start; >> + >> + return pmu_config; >> +} >> + >> +static void test_gt_c6(int xe, unsigned int gt) >> +{ >> + int pmu_fd; >> + u64 pmu_config; >> + char event[100]; >> + uint64_t ts[2]; >> + unsigned long slept, start, end; >> + uint64_t val; >> + >> + /* Get the PMU config for the gt-c6 event */ >> + sprintf(event, "gt-c6-residency"); >> + pmu_config = get_event_config(xe, gt, event); >> + >> + pmu_fd = open_pmu(xe, pmu_config); >> + >> + igt_require_f(igt_wait(xe_gt_is_in_c6(xe, gt), 1000, 10), "GT %d >> should be in C6\n", gt); >> + >> + /* While idle check full RC6. */ >> + start = read_idle_residency(xe, gt); >> + val = __pmu_read_single(pmu_fd, &ts[0]); >> + slept = measured_usleep(SLEEP_DURATION * USEC_PER_SEC) / 1000; >> + end = read_idle_residency(xe, gt); >> + val = __pmu_read_single(pmu_fd, &ts[1]) - val; > > I actually expected 2 subtests: > > 1) gt_c6_idle > > we should be in rc6 for the entire time when nothing is running > > 2) gt_c6_full_load > > we should not be in rc6 when something is keeping the engine > busy > > For (2), we can start a work load with xe_cork I can add a busy test later on, since I will be adding a workload related freq subtest after this. > > >> + >> + igt_debug("gt%u: slept=%lu, perf=%"PRIu64"\n", >> + gt, slept, val); >> + >> + igt_debug("Start res: %lu, end_res: %lu", start, end); > > is this reading the value from sysfs to aid debugging? yes. > > > I tested this with the kernel implementation, before merging it, and it > worked. I don't mind if this is merged and cleaned on top. I will leave > it for you and Riana to decide. Sent another version with most of the above comments addressed. Thanks, Vinay. > > thanks > Lucas De Marchi > >> + >> + assert_within_epsilon(val, >> + (ts[1] - ts[0])/USEC_PER_SEC, >> + tolerance); >> + close(pmu_fd); >> +} >> + >> +igt_main >> +{ >> + int fd, gt; >> + >> + igt_fixture { >> + fd = drm_open_driver(DRIVER_XE); >> + igt_require(!IS_PONTEVECCHIO(xe_dev_id(fd))); >> + } >> + >> + igt_describe("Validate PMU gt-c6 residency counters"); >> + igt_subtest("gt-c6") >> + xe_for_each_gt(fd, gt) >> + test_gt_c6(fd, gt); >> + >> + igt_fixture { >> + close(fd); >> + } >> +} >> diff --git a/tests/meson.build b/tests/meson.build >> index 33dffad31..d20f50766 100644 >> --- a/tests/meson.build >> +++ b/tests/meson.build >> @@ -309,6 +309,7 @@ intel_xe_progs = [ >> 'xe_pat', >> 'xe_peer2peer', >> 'xe_pm', >> + 'xe_pmu', >> 'xe_pm_residency', >> 'xe_prime_self_import', >> 'xe_query', >> -- >> 2.38.1 >>
diff --git a/tests/intel/xe_pmu.c b/tests/intel/xe_pmu.c new file mode 100644 index 000000000..61a7a7e38 --- /dev/null +++ b/tests/intel/xe_pmu.c @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2025 Intel Corporation + */ + +/** + * TEST: Test Xe PMU functionality + * Category: Perf Monitoring Unit + * Mega feature: Perf Monitoring Unit + * Sub-category: Power Management + * Functionality: Power/Perf + * Test category: Functional tests + */ + +#include <fcntl.h> +#include <limits.h> +#include <time.h> +#include <errno.h> +#include <dirent.h> +#include <string.h> +#include <sys/time.h> + +#include "igt.h" +#include "igt_device.h" +#include "igt_power.h" +#include "igt_sysfs.h" +#include "igt_perf.h" + +#include "lib/igt_syncobj.h" +#include "xe/xe_ioctl.h" +#include "xe/xe_gt.h" +#include "xe/xe_query.h" +#include "xe/xe_spin.h" +#include "xe/xe_util.h" + +#define SLEEP_DURATION 2 /* in seconds */ +const double tolerance = 0.1; +const unsigned long batch_duration_ns = 500e6; +const char *no_debug_data = "\0"; + +#define __assert_within_epsilon(x, ref, tol_up, tol_down, debug_data) \ + igt_assert_f((double)(x) <= (1.0 + (tol_up)) * (double)(ref) && \ + (double)(x) >= (1.0 - (tol_down)) * (double)(ref), \ + "'%s' != '%s' (%f not within +%.1f%%/-%.1f%% tolerance of %f)\n%s\n",\ + #x, #ref, (double)(x), \ + (tol_up) * 100.0, (tol_down) * 100.0, \ + (double)(ref), debug_data) + +#define assert_within_epsilon(x, ref, tolerance) \ + __assert_within_epsilon(x, ref, tolerance, tolerance, no_debug_data) + +static int open_pmu(int xe, uint64_t config) +{ + int fd; + + fd = perf_xe_open(xe, config); + igt_skip_on(fd < 0 && errno == ENODEV); + igt_assert(fd >= 0); + + return fd; +} + +static uint64_t __pmu_read_single(int fd, uint64_t *ts) +{ + uint64_t data[2]; + + igt_assert_eq(read(fd, data, sizeof(data)), sizeof(data)); + if (ts) + *ts = data[1]; + + return data[0]; +} + +/** + * SUBTEST: gt-c6 + * Description: Basic residency test to validate idle residency + * measured over a time interval is within the tolerance + */ +static unsigned int measured_usleep(unsigned int usec) +{ + struct timespec ts = { }; + unsigned int slept; + + slept = igt_nsec_elapsed(&ts); + igt_assert(slept == 0); + do { + usleep(usec - slept); + slept = igt_nsec_elapsed(&ts) / 1000; + } while (slept < usec); + + return igt_nsec_elapsed(&ts) / 1000; +} + +static unsigned long read_idle_residency(int fd, int gt) +{ + unsigned long residency = 0; + int gt_fd; + + gt_fd = xe_sysfs_gt_open(fd, gt); + igt_assert(gt_fd >= 0); + igt_assert(igt_sysfs_scanf(gt_fd, "gtidle/idle_residency_ms", "%lu", &residency) == 1); + close(gt_fd); + + return residency; +} + +static u64 get_event_config(int xe, unsigned int gt, char *event) +{ + int ret; + char xe_device[100]; + u64 pmu_config; + u32 start, end; + + xe_perf_device(xe, xe_device, sizeof(xe_device)); + ret = perf_event_config(xe_device, event, &pmu_config); + igt_assert(ret >= 0); + ret = perf_event_format(xe_device, "gt", &start, &end); + igt_assert(ret >= 0); + pmu_config |= (u64) gt << start; + + return pmu_config; +} + +static void test_gt_c6(int xe, unsigned int gt) +{ + int pmu_fd; + u64 pmu_config; + char event[100]; + uint64_t ts[2]; + unsigned long slept, start, end; + uint64_t val; + + /* Get the PMU config for the gt-c6 event */ + sprintf(event, "gt-c6-residency"); + pmu_config = get_event_config(xe, gt, event); + + pmu_fd = open_pmu(xe, pmu_config); + + igt_require_f(igt_wait(xe_gt_is_in_c6(xe, gt), 1000, 10), "GT %d should be in C6\n", gt); + + /* While idle check full RC6. */ + start = read_idle_residency(xe, gt); + val = __pmu_read_single(pmu_fd, &ts[0]); + slept = measured_usleep(SLEEP_DURATION * USEC_PER_SEC) / 1000; + end = read_idle_residency(xe, gt); + val = __pmu_read_single(pmu_fd, &ts[1]) - val; + + igt_debug("gt%u: slept=%lu, perf=%"PRIu64"\n", + gt, slept, val); + + igt_debug("Start res: %lu, end_res: %lu", start, end); + + assert_within_epsilon(val, + (ts[1] - ts[0])/USEC_PER_SEC, + tolerance); + close(pmu_fd); +} + +igt_main +{ + int fd, gt; + + igt_fixture { + fd = drm_open_driver(DRIVER_XE); + igt_require(!IS_PONTEVECCHIO(xe_dev_id(fd))); + } + + igt_describe("Validate PMU gt-c6 residency counters"); + igt_subtest("gt-c6") + xe_for_each_gt(fd, gt) + test_gt_c6(fd, gt); + + igt_fixture { + close(fd); + } +} diff --git a/tests/meson.build b/tests/meson.build index 33dffad31..d20f50766 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -309,6 +309,7 @@ intel_xe_progs = [ 'xe_pat', 'xe_peer2peer', 'xe_pm', + 'xe_pmu', 'xe_pm_residency', 'xe_prime_self_import', 'xe_query',
Simple tests for validating the PMU implementation for GT C6 residencies. v2: Rename rc6-residency-* to gt-c6-residency and remove freq tests. v3: Keep just gt-c6 tests, add frequency tests later. v4: Review comments (Riana) Cc: Lucas De Marchi <lucas.demarchi@intel.com> Cc: Riana Tauro <riana.tauro@intel.com> Cc: Rodrigo Vivi <rodrigo.vivi@intel.com> Signed-off-by: Vinay Belgaumkar <vinay.belgaumkar@intel.com> --- tests/intel/xe_pmu.c | 176 +++++++++++++++++++++++++++++++++++++++++++ tests/meson.build | 1 + 2 files changed, 177 insertions(+) create mode 100644 tests/intel/xe_pmu.c