/* * Copyright © 2022-2023 Intel Corporation * * 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. * */ #include #include #include #include #include #include #include #include #include #include "drmtest.h" #include "igt_drm_fdinfo.h" static size_t read_fdinfo(char *buf, const size_t sz, int at, const char *name) { size_t count; int fd; fd = openat(at, name, O_RDONLY); if (fd < 0) return 0; count = read(fd, buf, sz - 1); if (count > 0) buf[count - 1] = 0; close(fd); return count > 0 ? count : 0; } static const char *ignore_space(const char *s) { for (; *s && isspace(*s); s++) ; return s; } static int parse_engine(const char *name, struct drm_client_fdinfo *info, const char **name_map, unsigned int map_entries, uint64_t *val) { const char *p; size_t name_len; int found = -1; unsigned int i; p = strchr(name, ':'); if (!p) return -1; name_len = p - name; if (name_len < 1) return -1; p++; if (name_map) { for (i = 0; i < map_entries; i++) { if (!strncmp(name, name_map[i], name_len)) { found = i; break; } } } else { for (i = 0; i < info->num_engines; i++) { if (!strncmp(name, info->names[i], name_len)) { found = i; break; } } if (found < 0) { assert((info->num_engines + 1) < ARRAY_SIZE(info->names)); assert(name_len < sizeof(info->names[0])); memcpy(info->names[info->num_engines], name, name_len); info->names[info->num_engines][name_len] = '\0'; found = info->num_engines; } } p++; if (found >= 0) *val = strtoull(p, NULL, 10); return found; } static int parse_region(const char *name, struct drm_client_fdinfo *info, const char **region_map, unsigned int region_entries, uint64_t *val) { char *p; ssize_t name_len; int found = -1; unsigned int i; p = strchr(name, ':'); if (!p) return -1; name_len = p - name; if (name_len < 1) return -1; p++; if (region_map) { for (i = 0; i < region_entries; i++) { if (!strncmp(name, region_map[i], name_len)) { found = i; if (!info->region_names[info->num_regions][0]) { assert(name_len < sizeof(info->region_names[i])); strncpy(info->region_names[i], name, name_len); } break; } } } else { for (i = 0; i < info->num_regions; i++) { if (!strncmp(name, info->region_names[i], name_len)) { found = i; break; } } if (found < 0) { assert((info->num_regions + 1) < ARRAY_SIZE(info->region_names)); assert((strlen(name) + 1) < sizeof(info->region_names[0])); strncpy(info->region_names[info->num_regions], name, name_len); found = info->num_regions; } } if (found < 0) goto out; p++; *val = strtoull(p, &p, 10); p = (char *)ignore_space(p); if (!*p) goto out; if (!strcmp(p, "KiB")) { *val *= 1024; } else if (!strcmp(p, "MiB")) { *val *= 1024 * 1024; } else if (!strcmp(p, "GiB")) { *val *= 1024 * 1024 * 1024; } out: return found; } #define UPDATE_REGION(idx, region, val) \ do { \ if (idx >= 0) { \ info->region_mem[idx].region = val; \ if (!regions_found[idx]) { \ info->num_regions++; \ regions_found[idx] = true; \ if (idx > info->last_region_index) \ info->last_region_index = idx; \ } \ } \ } while (0) #define UPDATE_ENGINE(idx, member, val, utilization_key) \ do { \ if (idx >= 0) { \ info->member[idx] = val; \ info->utilization_mask |= utilization_key; \ if (!info->capacity[idx]) \ info->capacity[idx] = 1; \ if (!engines_found[idx]) { \ info->num_engines++; \ engines_found[idx] = true; \ if (idx > info->last_engine_index) \ info->last_engine_index = idx; \ } \ } \ } while (0) #define strstartswith(a, b, plen__) ({ \ *plen__ = strlen(b); \ strncmp(a, b, *plen__) == 0; \ }) unsigned int __igt_parse_drm_fdinfo(int dir, const char *fd, struct drm_client_fdinfo *info, const char **name_map, unsigned int map_entries, const char **region_map, unsigned int region_entries) { bool regions_found[DRM_CLIENT_FDINFO_MAX_REGIONS] = { }; bool engines_found[DRM_CLIENT_FDINFO_MAX_ENGINES] = { }; unsigned int good = 0, num_capacity = 0; char buf[4096], *_buf = buf; char *l, *ctx = NULL; size_t count; count = read_fdinfo(buf, sizeof(buf), dir, fd); if (!count) return 0; while ((l = strtok_r(_buf, "\n", &ctx))) { uint64_t val = 0; size_t keylen; const char *v; char *end_ptr; int idx; _buf = NULL; if (strstartswith(l, "drm-driver:", &keylen)) { v = ignore_space(l + keylen); if (*v) { strncpy(info->driver, v, sizeof(info->driver) - 1); good++; } } else if (strstartswith(l, "drm-client-id:", &keylen)) { v = l + keylen; info->id = strtol(v, &end_ptr, 10); if (end_ptr != v) good++; } else if (strstartswith(l, "drm-pdev:", &keylen)) { v = ignore_space(l + keylen); strncpy(info->pdev, v, sizeof(info->pdev) - 1); } else if (strstartswith(l, "drm-engine-capacity-", &keylen)) { idx = parse_engine(l + keylen, info, name_map, map_entries, &val); if (idx >= 0) { info->capacity[idx] = val; num_capacity++; } } else if (strstartswith(l, "drm-engine-", &keylen)) { idx = parse_engine(l + keylen, info, name_map, map_entries, &val); UPDATE_ENGINE(idx, engine_time, val, DRM_FDINFO_UTILIZATION_ENGINE_TIME); } else if (strstartswith(l, "drm-cycles-", &keylen)) { idx = parse_engine(l + keylen, info, name_map, map_entries, &val); UPDATE_ENGINE(idx, cycles, val, DRM_FDINFO_UTILIZATION_CYCLES); } else if (strstartswith(l, "drm-total-cycles-", &keylen)) { idx = parse_engine(l + keylen, info, name_map, map_entries, &val); UPDATE_ENGINE(idx, total_cycles, val, DRM_FDINFO_UTILIZATION_TOTAL_CYCLES); } else if (strstartswith(l, "drm-total-", &keylen)) { idx = parse_region(l + keylen, info, region_map, region_entries, &val); UPDATE_REGION(idx, total, val); } else if (strstartswith(l, "drm-shared-", &keylen)) { idx = parse_region(l + keylen, info, region_map, region_entries, &val); UPDATE_REGION(idx, shared, val); } else if (strstartswith(l, "drm-resident-", &keylen)) { idx = parse_region(l + keylen, info, region_map, region_entries, &val); UPDATE_REGION(idx, resident, val); } else if (strstartswith(l, "drm-purgeable-", &keylen)) { idx = parse_region(l + keylen, info, region_map, region_entries, &val); UPDATE_REGION(idx, purgeable, val); } else if (strstartswith(l, "drm-active-", &keylen)) { idx = parse_region(l + keylen, info, region_map, region_entries, &val); UPDATE_REGION(idx, active, val); } } if (good < 2 || (!info->num_engines && !info->num_regions)) return 0; /* fdinfo format not as expected */ return good + info->num_engines + num_capacity + info->num_regions; } unsigned int igt_parse_drm_fdinfo(int drm_fd, struct drm_client_fdinfo *info, const char **name_map, unsigned int map_entries, const char **region_map, unsigned int region_entries) { unsigned int res; char fd[64]; int dir, ret; ret = snprintf(fd, sizeof(fd), "%u", drm_fd); if (ret < 0 || ret == sizeof(fd)) return false; dir = open("/proc/self/fdinfo", O_DIRECTORY | O_RDONLY); if (dir < 0) return false; res = __igt_parse_drm_fdinfo(dir, fd, info, name_map, map_entries, region_map, region_entries); close(dir); return res; }