All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Purkait, Soham" <soham.purkait@intel.com>
To: Riana Tauro <riana.tauro@intel.com>,
	<igt-dev@lists.freedesktop.org>, <vinay.belgaumkar@intel.com>,
	<kamil.konieczny@intel.com>, <krzysztof.karas@intel.com>,
	<zbigniew.kempczynski@intel.com>
Cc: <anshuman.gupta@intel.com>, <lucas.demarchi@intel.com>,
	<rodrigo.vivi@intel.com>, <ashutosh.dixit@intel.com>
Subject: Re: [PATCH i-g-t v11 4/5] tools/gputop/xe_gputop: Add gputop support for xe specific devices
Date: Wed, 11 Jun 2025 13:28:24 +0530	[thread overview]
Message-ID: <05d410b4-1d2e-4076-b00a-99d9a2b95b84@intel.com> (raw)
In-Reply-To: <f0c53d07-457d-4ace-ace1-e4b517b25fba@intel.com>


On 05-06-2025 22:46, Riana Tauro wrote:
> Hi Soham
>
> Some minor comments
>
> On 5/22/2025 9:14 PM, Soham Purkait wrote:
>> Add gputop support for xe-specific devices. Separate
>> driver-specific code into respective source files.
>>
>> v2 : Fix for refactoring GPUTOP into a
>>       vendor-agnostic tool. (Lucas)
>>
>> v3 : Separate commit. (Kamil)
>>
>> v4 : Headers in alphabetical order
>>       Engines memory allocation at
>>       the beginning all at once.
>>       Removed PMU normalization. (Riana)
>>
>> v5 : Refactor to eliminate redundant
>>       and unused code segments.
>>       Fix for proper resource cleanup. (Riana)
>>
>> v8 : Allocated card structure memory inplace and
>>       accordingly modified the clean up code.
>>
>> v11 : Loop optimization in xe_populate_engines.
>>        Removed short_name.
>>        PMU fds are closed on cleanup.
>>        Removed unnecessary comments. (Riana)
>
> lot of versions maybe squash all the comments into one and start new 
> series. But upto you
>
>>
>> Signed-off-by: Soham Purkait <soham.purkait@intel.com>
>> ---
>>   tools/gputop/xe_gputop.c | 378 +++++++++++++++++++++++++++++++++++++++
>>   tools/gputop/xe_gputop.h |  62 +++++++
>>   2 files changed, 440 insertions(+)
>>   create mode 100644 tools/gputop/xe_gputop.c
>>   create mode 100644 tools/gputop/xe_gputop.h
>>
>> diff --git a/tools/gputop/xe_gputop.c b/tools/gputop/xe_gputop.c
>> new file mode 100644
>> index 000000000..ac3ed76e6
>> --- /dev/null
>> +++ b/tools/gputop/xe_gputop.c
>> @@ -0,0 +1,378 @@
>> +// SPDX-License-Identifier: MIT
>> +/*
>> + * Copyright © 2025 Intel Corporation
>> + */
>> +
>> +#include "xe_gputop.h"
>> +
>> +#define engine_ptr(engines, n) (&(engines)->engine + (n))
>> +
>> +static void __update_sample(struct xe_pmu_counter *counter, uint64_t 
>> val)
>> +{
>> +    counter->val.prev = counter->val.cur;
>> +    counter->val.cur = val;
>> +}
>> +
>> +static void update_sample(struct xe_pmu_counter *counter, uint64_t 
>> *val)
>> +{
>> +    if (counter->present)
>> +        __update_sample(counter, val[counter->idx]);
>> +}
>> +
>> +static const char *class_display_name(unsigned int class)
>> +{
>> +    switch (class) {
>> +    case DRM_XE_ENGINE_CLASS_RENDER:
>> +        return "Render/3D";
>> +    case DRM_XE_ENGINE_CLASS_COPY:
>> +        return "Blitter";
>> +    case DRM_XE_ENGINE_CLASS_VIDEO_DECODE:
>> +        return "Video";
>> +    case DRM_XE_ENGINE_CLASS_VIDEO_ENHANCE:
>> +        return "VideoEnhance";
>> +    case DRM_XE_ENGINE_CLASS_COMPUTE:
>> +        return "Compute";
>> +    default:
>> +        return "[unknown]";
>> +    }
>> +}
>> +
>> +void xe_clean_up(void *obj, int len)
>> +{
>> +    struct xe_engine *eng;
>> +    struct xe_pmu_counter pmu;
>> +    struct xe_gputop *dev = (struct xe_gputop *)obj;
>
> Use inverted xmas tree
>
>> +
>> +    for (int i = 0; i < len; i++) {
>> +        if ((dev + i)->card)
>> +            free((dev + i)->card);
>> +        if ((dev + i)->eng_obj) {
>> +
>> +            for(int j = 0; j < ((struct xe_pmu_device*)(dev + 
>> i)->eng_obj)->num_engines ; j++) {
>> +                eng = engine_ptr((struct xe_pmu_device*)(dev + 
>> i)->eng_obj, j);
>> +                if (eng->display_name)
>> +                    free(eng->display_name);
>> +
>> +                pmu = eng->engine_active_ticks;
>> +                if (pmu.present)
>> +                    close(pmu.fd);
>> +
>> +                pmu = eng->engine_total_ticks;
>> +                if (pmu.present)
>> +                    close(pmu.fd);
>> +            }
>> +            free(dev->eng_obj);
>> +        }
>> +        if ((dev + i)->pmu_device)
>> +            free(dev->pmu_device);
>> +    }
>> +}
>> +
>> +static char *pmu_name(struct igt_device_card *card)
>> +{
>> +    int card_fd;
>> +    char device[30];
>> +    char *path;
>> +
>> +    if (strlen(card->card))
>> +        card_fd = igt_open_card(card);
>> +    else if (strlen(card->render))
>> +        card_fd = igt_open_render(card);
>> +
>> +    if (card_fd == -1)
>> +        return NULL;
>> +
>> +    xe_perf_device(card_fd, device, sizeof(device));
>> +    path = strdup(device);
>> +    close(card_fd);
>> +    return path;
>> +}
>> +
>> +static int _open_pmu(uint64_t type, unsigned int *cnt, struct 
>> xe_pmu_counter *pmu, int *fd)
>> +{
>> +    int fd__ = igt_perf_open_group(type, pmu->config, *fd);
>> +
>> +    if (fd__ >= 0) {
>> +        if (*fd == -1)
>> +            *fd = fd__;
>> +        pmu->present = true;
>> +        pmu->idx = (*cnt)++;
>> +        pmu->fd = fd__;
>> +    }
>> +
>> +    return fd__;
>> +}
>> +
>> +void xe_gputop_init(void *ptr,
>> +            struct igt_device_card *card)
>> +{
>> +    struct xe_gputop *obj = (struct xe_gputop *)ptr;
>> +
>> +    obj->pmu_device = pmu_name(card);
>> +    if (!obj->pmu_device) {
>> +        fprintf(stderr, "%s : pmu_device path returned NULL", 
>> card->pci_slot_name);
>> +        exit(EXIT_FAILURE);
>> +    }
>> +    obj->card = card;
>> +}
>> +
>> +static int pmu_format_shift(int xe, const char *name)
>> +{
>> +    uint32_t start;
>> +    int format;
>> +    char device[80];
>> +
>> +    format = perf_event_format(xe_perf_device(xe, device, 
>> sizeof(device)),
>> +                   name, &start);
>> +    if (format)
>> +        return 0;
>> +
>> +    return start;
>> +}
>> +
>> +static int engine_cmp(const void *__a, const void *__b)
>> +{
>> +    const struct xe_engine *a = (struct xe_engine *)__a;
>> +    const struct xe_engine *b = (struct xe_engine *)__b;
>> +
>> +    if (a->drm_xe_engine.engine_class != b->drm_xe_engine.engine_class)
>> +        return a->drm_xe_engine.engine_class - 
>> b->drm_xe_engine.engine_class;
>> +    else
>> +        return a->drm_xe_engine.engine_instance - 
>> b->drm_xe_engine.engine_instance;
>> +}
>> +
>> +void *xe_populate_engines(const void *obj)
>> +{
>> +    struct igt_device_card *card = ((struct xe_gputop *)obj)->card;
>> +    struct xe_pmu_device *engines;
>> +    int ret = 0;
>> +    char device[30];
>> +    struct drm_xe_engine_class_instance *hwe;
>> +    int card_fd;
>> +    uint64_t engine_class, engine_instance, gt_shift;
>> +    uint64_t engine_active_config, engine_total_config;
>
> Use inverted xmas tree
>
>> +
>> +    if (!card || !strlen(card->card) || !strlen(card->render))
>> +        return NULL;
>> +
>> +    if (strlen(card->card)) {
>> +        card_fd = igt_open_card(card);
>> +    } else if (strlen(card->render)) {
>> +        card_fd = igt_open_render(card);
>> +    } else {
>> +        fprintf(stderr, "Failed to detect device!\n");
>> +        return NULL;
>> +    }
>> +    xe_device_get(card_fd);
>> +    engines = malloc(sizeof(struct xe_pmu_device) +
>> +             xe_number_engines(card_fd) * sizeof(struct xe_engine));
>> +    if (!engines)
>> +        return NULL;
>> +
>> +    memset(engines, 0, sizeof(struct xe_pmu_device) +
>> +           xe_number_engines(card_fd) * sizeof(struct xe_engine));
>> +
>> +    engines->num_engines = 0;
>> +    engines->device = ((struct xe_gputop *)obj)->pmu_device;
>> +    gt_shift = pmu_format_shift(card_fd, "gt");
>> +    engine_class = pmu_format_shift(card_fd, "engine_class");
>> +    engine_instance = pmu_format_shift(card_fd, "engine_instance");
>> +    xe_perf_device(card_fd, device, sizeof(device));
>> +    ret = perf_event_config(device,
>> +                "engine-active-ticks",
>> +                &engine_active_config);
>
> wrap around at 100
>
>> +    if (ret < 0)
>> +        return NULL;
>> +    ret = perf_event_config(device,
>> +                "engine-total-ticks",
>> +                &engine_total_config);
>> +    if (ret < 0)
>> +        return NULL;
>
> add blank line
>
>> +    xe_for_each_engine(card_fd, hwe) {
>> +        uint64_t  param_config;
>> +        struct xe_engine *engine;
>> +
>> +        engine = engine_ptr(engines, engines->num_engines);
>> +        param_config = (uint64_t)hwe->gt_id << gt_shift | 
>> hwe->engine_class << engine_class
>> +            | hwe->engine_instance << engine_instance;
>> +        engine->drm_xe_engine = *hwe;
>> +        engine->engine_active_ticks.config = engine_active_config | 
>> param_config;
>> +        engine->engine_total_ticks.config = engine_total_config | 
>> param_config;
>> +
>> +        if (engine->engine_active_ticks.config == -1 ||
>> +            engine->engine_total_ticks.config == -1) {
>> +            ret = ENOENT;
>> +            break;
>> +        }
>> +
>> +        ret = asprintf(&engine->display_name, "%s/%u",
>> + class_display_name(engine->drm_xe_engine.engine_class),
>> +                   engine->drm_xe_engine.engine_instance);
>> +
>> +        if (ret <= 0) {
>> +            ret = errno;
>> +            break;
>> +        }
>> +
>> +        engines->num_engines++;
>> +    }
>> +
>> +    if (!ret) {
>> +        errno = ret;
>> +        return NULL;
>> +    }
>> +
>> +    qsort(engine_ptr(engines, 0), engines->num_engines,
>> +          sizeof(struct xe_engine), engine_cmp);
>> +
>> +    ((struct xe_gputop *)obj)->eng_obj = engines;
>> +
>> +    return engines;
>> +}
>> +
>> +static uint64_t pmu_read_multi(int fd, unsigned int num, uint64_t *val)
>> +{
>> +    uint64_t buf[2 + num];
>> +    unsigned int i;
>> +    ssize_t len;
>> +
>> +    memset(buf, 0, sizeof(buf));
>> +
>> +    len = read(fd, buf, sizeof(buf));
>> +    assert(len == sizeof(buf));
>> +
>> +    for (i = 0; i < num; i++)
>> +        val[i] = buf[2 + i];
>> +
>> +    return buf[1];
>> +}
>> +
>> +void xe_pmu_sample(const void *obj)
>> +{
>> +    struct xe_pmu_device *engines = ((struct xe_gputop *)obj)->eng_obj;
>> +    const int num_val = engines->num_counters;
>> +    uint64_t val[2 + num_val];
>> +    unsigned int i;
>> +
>> +    pmu_read_multi(engines->fd, num_val, val);
>> +
>> +    for (i = 0; i < engines->num_engines; i++) {
>> +        struct xe_engine *engine = engine_ptr(engines, i);
>> +
>> +        update_sample(&engine->engine_active_ticks, val);
>> +        update_sample(&engine->engine_total_ticks, val);
>> +    }
>> +}
>> +
>> +int xe_pmu_init(const void *obj)
>> +{
>> +    struct xe_pmu_device *engines = ((struct xe_gputop *)obj)->eng_obj;
>> +    unsigned int i;
>> +    int fd;
>> +    struct xe_engine *engine;
>> +    uint64_t type = igt_perf_type_id(engines->device);
>> +
>> +    engines->fd = -1;
>> +    engines->num_counters = 0;
>> +
>> +    for (i = 0; i < engines->num_engines; i++) {
>> +        engine = engine_ptr(engines, i);
>> +        fd = _open_pmu(type, &engines->num_counters, 
>> &engine->engine_active_ticks,
>> +                   &engines->fd);
>> +        if (fd < 0)
>> +            return -1;
>> +        fd = _open_pmu(type, &engines->num_counters, 
>> &engine->engine_total_ticks,
>> +                   &engines->fd);
>> +        if (fd < 0)
>> +            return -1;
>> +    }
>> +    return 0;
>> +}
>> +
>> +static double pmu_active_percentage(struct xe_engine *engine)
>> +{
>> +    double pmu_active_ticks = engine->engine_active_ticks.val.cur -
>> +                  engine->engine_active_ticks.val.prev;
>> +    double pmu_total_ticks = engine->engine_total_ticks.val.cur -
>> +                 engine->engine_total_ticks.val.prev;
>> +    double percentage;
>> +
>> +    percentage = (pmu_active_ticks * 100) / pmu_total_ticks;
>> +    return percentage;
>> +}
>> +
>> +static int
>> +print_device_description(const void *obj, int lines, int w, int h)
>> +{
>> +    char *desc;
>> +    int len;
>> +
>> +    len = asprintf(&desc, "DRIVER: %s || BDF: %s",
>> +               ((struct xe_gputop *)obj)->card->driver,
>> +               ((struct xe_gputop *)obj)->card->pci_slot_name);
>> +
>> +    printf("\033[7m%s%*s\033[0m\n",
>> +           desc,
>> +           (int)(w - len), " ");
>> +    lines++;
>> +    free(desc);
>> +    return lines;
>> +}
>> +
>> +static int
>> +print_engines_header(struct xe_pmu_device *engines,
>> +             int lines, int con_w, int con_h)
>> +{
>> +    const char *a;
>> +
>> +    for (unsigned int i = 0;
>> +         i < engines->num_engines && lines < con_h;
>> +         i++) {
>> +        struct xe_engine *engine = engine_ptr(engines, i);
>> +
>> +        if (!engine->num_counters)
>> +            continue;
>> +
>> +        a = "            ENGINES   ACTIVITY  ";
>> +
>> +        printf("\033[7m%s%*s\033[0m\n",
>> +               a,
>> +               (int)(con_w - strlen(a)), " ");
>> +        lines++;
>> +
>> +        break;
>> +    }
>> +
>> +    return lines;
>> +}
>> +
>> +static int
>> +print_engine(struct xe_pmu_device *engines, unsigned int i,
>> +         int lines, int con_w, int con_h)
>> +{
>> +    struct xe_engine *engine = engine_ptr(engines, i);
>> +    double percentage = pmu_active_percentage(engine);
>> +
>> +    printf("%*s", (int)(strlen("            ENGINES")), 
>> engine->display_name);
>> +    print_percentage_bar(percentage, con_w - strlen("            
>> ENGINES"));
>> +    printf("\n");
>> +
>> +    return ++lines;
>> +}
>> +
>> +int xe_print_engines(const void *obj, int lines, int w, int h)
>> +{
>> +    struct xe_pmu_device *show = ((struct xe_gputop *)obj)->eng_obj;
>> +
>> +    lines = print_device_description(obj, lines, w, h);
>> +
>> +    lines = print_engines_header(show, lines, w,  h);
>> +
>> +    for (unsigned int i = 0; i < show->num_engines && lines < h; i++)
>> +        lines = print_engine(show, i, lines, w, h);
>> +
>> +    lines = print_engines_footer(lines, w, h);
>> +
>> +    return lines;
>> +}
>> +
>> diff --git a/tools/gputop/xe_gputop.h b/tools/gputop/xe_gputop.h
>> new file mode 100644
>> index 000000000..825ac7e34
>> --- /dev/null
>> +++ b/tools/gputop/xe_gputop.h
>> @@ -0,0 +1,62 @@
>> +/* SPDX-License-Identifier: MIT */
>> +/*
>> + * Copyright © 2025 Intel Corporation
>> + */
>> +
>> +#ifndef __XE_GPUTOP_H__
>> +#define __XE_GPUTOP_H__
>> +
>> +#include <dirent.h>
>> +
>> +#include "igt_device_scan.h"
>> +#include "igt_perf.h"
>> +#include "utils.h"
>> +#include "xe/xe_query.h"
>> +
>> +struct xe_pmu_pair {
>> +    uint64_t cur;
>> +    uint64_t prev;
>> +};
>
> Is an extra struct needed here. you can add it in below struct

Not technically but imo it helps in better readability.

Thanks,
Soham

>
>> +
>> +struct xe_pmu_counter {
>> +    uint64_t type;
>> +    uint64_t config;
>> +    unsigned int idx;
>> +    struct xe_pmu_pair val;
>> +    int fd;
>> +    bool present;
>> +};
>> +
>> +struct xe_engine {
>> +    const char *name;
>> +    char *display_name;
>> +    struct drm_xe_engine_class_instance drm_xe_engine;
>
> %s/drm_xe_engine/engine
>
>> +    unsigned int num_counters;
>> +    struct xe_pmu_counter engine_active_ticks;
>> +    struct xe_pmu_counter engine_total_ticks;
>> +};
>> +
>> +struct xe_pmu_device {
>> +    unsigned int num_engines;
>> +    unsigned int num_counters;
>> +    int fd;
>> +    char *device;
>> +    struct xe_engine engine;
>> +};
>> +
>> +struct xe_gputop {
>> +    char *pmu_device;
>> +    struct igt_device_card *card;
>> +    struct xe_pmu_device *eng_obj;
>> +};
>> +
>> +void xe_gputop_init(void *ptr,
>> +            struct igt_device_card *card);
>
> Wrap around at 100
>
> Thanks
> Riana
>
>> +void xe_populate_device_instances(struct gputop_device *dv);
>> +void *xe_populate_engines(const void *obj);
>> +void xe_pmu_sample(const void *obj);
>> +int xe_pmu_init(const void *obj);
>> +int xe_print_engines(const void *obj, int lines, int w, int h);
>> +void xe_clean_up(void *obj, int len);
>> +
>> +#endif /* __XE_GPUTOP_H__ */
>

  reply	other threads:[~2025-06-11  7:58 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-05-22 15:44 [PATCH i-g-t v11 0/5] Add per-device engine activity stats in GPUTOP Soham Purkait
2025-05-22 15:44 ` [PATCH i-g-t v11 1/5] lib/igt_device_scan: Add support for the device filter Soham Purkait
2025-05-22 15:44 ` [PATCH i-g-t v11 2/5] lib/igt_device_scan: Enable finding all matched IGT devices Soham Purkait
2025-05-22 15:44 ` [PATCH i-g-t v11 3/5] tools/gputop/utils: Add gputop utility functions common to all drivers Soham Purkait
2025-05-22 15:44 ` [PATCH i-g-t v11 4/5] tools/gputop/xe_gputop: Add gputop support for xe specific devices Soham Purkait
2025-06-05 17:16   ` Riana Tauro
2025-06-11  7:58     ` Purkait, Soham [this message]
2025-05-22 15:44 ` [PATCH i-g-t v11 5/5] tools/gputop/gputop: Enable support for multiple GPUs and instances Soham Purkait
2025-06-05 16:57   ` Riana Tauro
2025-06-11  7:11     ` Purkait, Soham
2025-06-11  7:24       ` Riana Tauro
2025-05-22 16:34 ` ✗ Fi.CI.BUILD: failure for Add per-device engine activity stats in GPUTOP (rev7) Patchwork

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=05d410b4-1d2e-4076-b00a-99d9a2b95b84@intel.com \
    --to=soham.purkait@intel.com \
    --cc=anshuman.gupta@intel.com \
    --cc=ashutosh.dixit@intel.com \
    --cc=igt-dev@lists.freedesktop.org \
    --cc=kamil.konieczny@intel.com \
    --cc=krzysztof.karas@intel.com \
    --cc=lucas.demarchi@intel.com \
    --cc=riana.tauro@intel.com \
    --cc=rodrigo.vivi@intel.com \
    --cc=vinay.belgaumkar@intel.com \
    --cc=zbigniew.kempczynski@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.