From nobody Sat Feb 7 16:26:56 2026 Received: from mail-pj1-f74.google.com (mail-pj1-f74.google.com [209.85.216.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 404251F3B9E for ; Thu, 3 Apr 2025 20:24:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1743711892; cv=none; b=qU5PaSy5ciHxOwBlKOByruCTTEDyukS8Xam6pLUpDwZmgauoeJN8CzfFf+/4H8+81/CVw4i0DrsPwD/B5KNTORaK6I6imNn+AaARJLbm61KO+3pjXihKoamDvXbHc1oVsYQ+3xtdRYgE3BSqYvHR9Hy9herzjNKJQcrkVBX1bbg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1743711892; c=relaxed/simple; bh=HRxknASP9NJ4vjMn11MtRE7n7ZMTlVait7+eCV8bpfo=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=KJn+jSXNqMaOgLg1VrtNCVZ3YUjvLgYkJbvqTH/nOKo1ysRLNcWTB1OxSpbQPMPOlvdNAfb3letfkIRfhAsQKqDb9GaMT/ytRFuoVFUdEYO+B0GFnG1+jZSgbbWvnEsYjPNh1UvTt158y72H8ux9lRKjNbK41KB1E2Sb/XL4PrI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=IXG/B3ao; arc=none smtp.client-ip=209.85.216.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="IXG/B3ao" Received: by mail-pj1-f74.google.com with SMTP id 98e67ed59e1d1-3032f4eca83so1312714a91.3 for ; Thu, 03 Apr 2025 13:24:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1743711890; x=1744316690; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=Pil1LeiyQzc9Wfedi7qRLX2THzbN1bSqcXBycZBxx3E=; b=IXG/B3ao6mce32Y4EGcUT55zcg6iswx7YV3PtmbRV82sA2SKnawyivwF5U0brDTQv8 Qeq6AIAQE3u66nCCIyH1j0ZfeX9r8+FrpMQVkjuo3fMcqQILIQhlhZ0xBDV4vvPxTz2r XR72pjCmrNXG8prdrwW4yx/Ist6VHkUCYSnHOL0qXySJWTgjRLPc7Irq4RMTmJs9hBgQ glUuCjhIpc7Tqv8wHf698j36V12nLFwMcnZQNlO7L0HRYXp8tGqfovN6SyyZ+XAtPYp6 aBMnbQtijmxSc2lrMBMWOasCI/AMvT59e1DX0hc9sK8dc5IdMb4Jtn4oac+g4iqN8/iS ONXw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1743711890; x=1744316690; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=Pil1LeiyQzc9Wfedi7qRLX2THzbN1bSqcXBycZBxx3E=; b=ky5a5fCyuDf+HXzpy3n6Qz0P8a7bbkRhY/QDZZLf/mKaxoimRjt+j5S1Bjf2l8aCod CAbZQZYwt6gsWeBymtC3IaPsT5eN1PxexI5ymFWZFexAxuB7Wh9oT9rnlsPKIVtVYtbQ bk77Od9jjuraTMwtoOgSSWLyib410fml3t436XB1ypHXECNdGUf0uNkimyt92iztwfp1 g1/UCdRW0fPd+53hagUF09fVMB3v8CIxBjTflwweKePYVo2BtHbvyJD5iBDSC8oAGMmb uG6Q+V0q4wbJlI6/AwzAl/dQOc/1Mi8c5BWFW16OtOb0W2gz28pOfcd79Cxnc0UlRj2m TVYw== X-Forwarded-Encrypted: i=1; AJvYcCVYcR1iPXGtuD9KaFVHQ/7vY2Wf8BMNpB5AI3EZzzungcCoBw+CnU49otigg0xyFV7L3bax/sRGLENkZxE=@vger.kernel.org X-Gm-Message-State: AOJu0YyS/9n6dNSO2kKNAAwD27zeBjFQsWGxVlGDmCrT4wS/pkjMPVWE 6TDb7vornV6tvrqABMk7JVvzwDT6tpC7CqyVEcaowD/dkty58+nBwnkTSzxp9yyrk1NpahCwY+C LoXUQbg== X-Google-Smtp-Source: AGHT+IHECJgbaJYB5gyuhkerty/z5QcoT9YXQkYlH1mJyNC2bK+Z2YOQrOHG9csQbYSvdr6YjtqHqs0SwrVx X-Received: from pjg12.prod.google.com ([2002:a17:90b:3f4c:b0:301:1bf5:2efc]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:51cc:b0:2fa:1e3e:9be5 with SMTP id 98e67ed59e1d1-306a4757ff7mr1693465a91.0.1743711890504; Thu, 03 Apr 2025 13:24:50 -0700 (PDT) Date: Thu, 3 Apr 2025 13:24:36 -0700 In-Reply-To: <20250403202439.57791-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250403202439.57791-1-irogers@google.com> X-Mailer: git-send-email 2.49.0.504.g3bcea36a83-goog Message-ID: <20250403202439.57791-2-irogers@google.com> Subject: [PATCH v3 1/4] perf parse-events: Avoid scanning PMUs that can't contain events From: Ian Rogers To: Peter Zijlstra , Ingo Molnar , Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Alexander Shishkin , Jiri Olsa , Ian Rogers , Adrian Hunter , Kan Liang , "Masami Hiramatsu (Google)" , James Clark , Weilin Wang , Dominique Martinet , Thomas Richter , Junhao He , Jean-Philippe Romain , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add perf_pmus__scan_for_event that only reads sysfs for pmus that could contain a given event. Signed-off-by: Ian Rogers --- tools/perf/util/parse-events.c | 4 ++-- tools/perf/util/pmus.c | 35 ++++++++++++++++++++++++++++++++++ tools/perf/util/pmus.h | 1 + 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 5152fd5a6ead..d77af1d24985 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -434,7 +434,7 @@ int parse_events_add_cache(struct list_head *list, int = *idx, const char *name, const char *config_name =3D get_config_name(parsed_terms); const char *metric_id =3D get_config_metric_id(parsed_terms); =20 - while ((pmu =3D perf_pmus__scan(pmu)) !=3D NULL) { + while ((pmu =3D perf_pmus__scan_for_event(pmu, name)) !=3D NULL) { LIST_HEAD(config_terms); struct perf_event_attr attr; int ret; @@ -1585,7 +1585,7 @@ int parse_events_multi_pmu_add(struct parse_events_st= ate *parse_state, =20 INIT_LIST_HEAD(list); =20 - while ((pmu =3D perf_pmus__scan(pmu)) !=3D NULL) { + while ((pmu =3D perf_pmus__scan_for_event(pmu, event_name)) !=3D NULL) { bool auto_merge_stats; =20 if (parse_events__filter_pmu(parse_state, pmu)) diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c index b99292de7669..f2706c395509 100644 --- a/tools/perf/util/pmus.c +++ b/tools/perf/util/pmus.c @@ -350,6 +350,41 @@ struct perf_pmu *perf_pmus__scan_core(struct perf_pmu = *pmu) return NULL; } =20 +struct perf_pmu *perf_pmus__scan_for_event(struct perf_pmu *pmu, const cha= r *event) +{ + bool use_core_pmus =3D !pmu || pmu->is_core; + + if (!pmu) { + /* Hwmon filename values that aren't used. */ + enum hwmon_type type; + int number; + /* + * Core PMUs, other sysfs PMUs and tool PMU can take all event + * types or aren't wother optimizing for. + */ + unsigned int to_read_pmus =3D PERF_TOOL_PMU_TYPE_PE_CORE_MASK | + PERF_TOOL_PMU_TYPE_PE_OTHER_MASK | + PERF_TOOL_PMU_TYPE_TOOL_MASK; + + /* Could the event be a hwmon event? */ + if (parse_hwmon_filename(event, &type, &number, /*item=3D*/NULL, /*alarm= =3D*/NULL)) + to_read_pmus |=3D PERF_TOOL_PMU_TYPE_HWMON_MASK; + + pmu_read_sysfs(to_read_pmus); + pmu =3D list_prepare_entry(pmu, &core_pmus, list); + } + if (use_core_pmus) { + list_for_each_entry_continue(pmu, &core_pmus, list) + return pmu; + + pmu =3D NULL; + pmu =3D list_prepare_entry(pmu, &other_pmus, list); + } + list_for_each_entry_continue(pmu, &other_pmus, list) + return pmu; + return NULL; +} + static struct perf_pmu *perf_pmus__scan_skip_duplicates(struct perf_pmu *p= mu) { bool use_core_pmus =3D !pmu || pmu->is_core; diff --git a/tools/perf/util/pmus.h b/tools/perf/util/pmus.h index 8def20e615ad..213ee65306d6 100644 --- a/tools/perf/util/pmus.h +++ b/tools/perf/util/pmus.h @@ -19,6 +19,7 @@ struct perf_pmu *perf_pmus__find_by_type(unsigned int typ= e); =20 struct perf_pmu *perf_pmus__scan(struct perf_pmu *pmu); struct perf_pmu *perf_pmus__scan_core(struct perf_pmu *pmu); +struct perf_pmu *perf_pmus__scan_for_event(struct perf_pmu *pmu, const cha= r *event); =20 const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str); =20 --=20 2.49.0.504.g3bcea36a83-goog From nobody Sat Feb 7 16:26:56 2026 Received: from mail-pg1-f202.google.com (mail-pg1-f202.google.com [209.85.215.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4E5911F4604 for ; Thu, 3 Apr 2025 20:24:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1743711895; cv=none; b=RfjK3vNtv2FjcZK+6ix/2LezYD2gDc78P7SunKL4IZcJWYkZ421vo41AeZ6foxmxzvFxRL+K+TD+4JfKAucmYxr2lB6srIaM+2g9KDxCm6+7SBaZ3OFRcpaZVOijPs2KmEa2M3e33fZOoi/cHTmTZqh0m9h37TRdGHxa5SQhiKE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1743711895; c=relaxed/simple; bh=90KggfginsQzDP95X+k629D/YxTbuK86i9pbsXIr3+g=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=WWcBLjC9Tb/bzlZ63LJiJ5DtnoBu5qoHEQsbIBUNrYNPktFo6Q+tnEBp6u8Py79XCUhRI3646rb32hEkc7y/NbkWMOpQPu60AHykGVjehRDm4Z0Dhbl3/6Th+mJ4jK+N5r7M21DYucmrjFwc0JKewsCSGobnaYIVjllly1UvgUk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=1W7TrG0h; arc=none smtp.client-ip=209.85.215.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="1W7TrG0h" Received: by mail-pg1-f202.google.com with SMTP id 41be03b00d2f7-af8e645a1d1so844714a12.3 for ; Thu, 03 Apr 2025 13:24:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1743711892; x=1744316692; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=Tilk3aFHymzdwH/49hi2NVgSsKIEgE1zvVteMHXS+8s=; b=1W7TrG0h2Ybjmbv94SZ9gSNLTeJLlOKtUKpm9w+Luj5BLFNpi54oITOSqXZtqEUxlI RoWjM++Y7OiqVcRVQLXyeABQrDc9ECgKZCLLw+EQtrbLXlBa4BMhSe4JEFRfHY8MA/xE JFFo7TeWV99Z6FQItNNWm8lHB/cCNKDXsGP+f/Pw1f021YDvvTl1k/sE9FTdHbCQHGDc CGoxIA+YQ8DzGqRq77qFO3CDTLKqMSCSao/nE+hkolbydgEU8Z5EpKj1L6aeVJzkEZuV qshUivyCPBL7JmaiRne9yF+iU1xqYNVDC4awwyKZcDHpGd2/Xqsy7ESlKGCZ3g28x4Ed Iz/A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1743711892; x=1744316692; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=Tilk3aFHymzdwH/49hi2NVgSsKIEgE1zvVteMHXS+8s=; b=u3MaPtB9Gp29gdvwxxqKqgey2aLNpGb5Chl38/O3WS+vtOqZOfRdXfKAgXjtPKB4i/ KGNaxV7bR0m8z9/JEZHKBNkUaG4kq09sR+NqeCoSbkeCgo1DDoHmaGRtC+NGwkSkRmr6 OSo/ZFsYliHnXVfNUEIIiBKH6uf5IlW6ft2dcYK0jcFjZHkn7Z5n4nX7zhrJQaXBWS7I bSAoUQRmkTcHQz+PeP5uN9SpGCu+4DFNB1mszsRXuK2Bcoo56FBaESIxc400+nZM3w8p AqdtBIU7Tbgp7tY5Y61HIDsQvvs5FcHaq7ZdPvBOOB6wAY7T1oCiYNFB8qB/FMmw6zHe b60Q== X-Forwarded-Encrypted: i=1; AJvYcCXMypAoGbtoKtXnCZzT5Kygt4TCWgqWoERZhIDvMgvJ8xEqxHVipFMhI006YUU7ZYRAX+h1zd0UDFVlFW4=@vger.kernel.org X-Gm-Message-State: AOJu0YzzGeUefnz1PVZEivWTL9yJ1IgCb9/KBzX77H7OalHFUb8NRiVp NWsMjluA4WcAC1GEHrrch5vecplnG6ttwXbHREW0xaWrGeYJs105u1Ll1B2puRUW87HT/7J9UAA IoR8Oxw== X-Google-Smtp-Source: AGHT+IEryC+x1Td+fTmPyBksTzhsJltkWaZNz7ZcV3ft48rRChAcFSC8nQC73Hy3G/qeYPy+qRrlXc4bl5d8 X-Received: from pjbqn7.prod.google.com ([2002:a17:90b:3d47:b0:2ff:5f6a:835c]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:514e:b0:304:ec28:4437 with SMTP id 98e67ed59e1d1-306a4b70e72mr996487a91.22.1743711892605; Thu, 03 Apr 2025 13:24:52 -0700 (PDT) Date: Thu, 3 Apr 2025 13:24:37 -0700 In-Reply-To: <20250403202439.57791-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250403202439.57791-1-irogers@google.com> X-Mailer: git-send-email 2.49.0.504.g3bcea36a83-goog Message-ID: <20250403202439.57791-3-irogers@google.com> Subject: [PATCH v3 2/4] perf parse-events: Avoid scanning PMUs that can't match a wildcard From: Ian Rogers To: Peter Zijlstra , Ingo Molnar , Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Alexander Shishkin , Jiri Olsa , Ian Rogers , Adrian Hunter , Kan Liang , "Masami Hiramatsu (Google)" , James Clark , Weilin Wang , Dominique Martinet , Thomas Richter , Junhao He , Jean-Philippe Romain , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add perf_pmus__scan_matching_wildcard that only reads sysfs for pmus that could wildcard match. For example, if searching for the event 'inst_retired.any/period=3D1000000/' first 'inst_retired.any' will try to be directly looked up as a PMU, then 'inst_retired.any' will try to be wildcard matched against all PMUs, finally it is assumed 'inst_retired.any' is an event name and opened on all PMUs avoiding reading PMUs that can't have such an event. However, before this patch such a case would read all PMUs for the wildcard matching. Now if the wildcard can't be a hwmon PMU wildcard, reading hwmon PMUs is avoided. On my laptop this reduces 'perf stat -e inst_retired.any true' from performing 253 openat system calls down to 234. Signed-off-by: Ian Rogers --- tools/perf/util/parse-events.c | 26 +++++++++++---------- tools/perf/util/pmus.c | 42 ++++++++++++++++++++++++++++++++++ tools/perf/util/pmus.h | 1 + 3 files changed, 57 insertions(+), 12 deletions(-) diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index d77af1d24985..19caf1ed9f5d 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -1664,18 +1664,20 @@ int parse_events_multi_pmu_add_or_add_pmu(struct pa= rse_events_state *parse_state =20 pmu =3D NULL; /* Failed to add, try wildcard expansion of event_or_pmu as a PMU name. */ - while ((pmu =3D perf_pmus__scan(pmu)) !=3D NULL) { - if (!parse_events__filter_pmu(parse_state, pmu) && - perf_pmu__wildcard_match(pmu, event_or_pmu)) { - bool auto_merge_stats =3D perf_pmu__auto_merge_stats(pmu); - - if (!parse_events_add_pmu(parse_state, *listp, pmu, - const_parsed_terms, - auto_merge_stats, - /*alternate_hw_config=3D*/PERF_COUNT_HW_MAX)) { - ok++; - parse_state->wild_card_pmus =3D true; - } + while ((pmu =3D perf_pmus__scan_matching_wildcard(pmu, event_or_pmu)) != =3D NULL) { + bool auto_merge_stats; + + if (parse_events__filter_pmu(parse_state, pmu)) + continue; + + auto_merge_stats =3D perf_pmu__auto_merge_stats(pmu); + + if (!parse_events_add_pmu(parse_state, *listp, pmu, + const_parsed_terms, + auto_merge_stats, + /*alternate_hw_config=3D*/PERF_COUNT_HW_MAX)) { + ok++; + parse_state->wild_card_pmus =3D true; } } if (ok) diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c index f2706c395509..93d5db1c89b4 100644 --- a/tools/perf/util/pmus.c +++ b/tools/perf/util/pmus.c @@ -19,6 +19,7 @@ #include "tool_pmu.h" #include "print-events.h" #include "strbuf.h" +#include "string2.h" =20 /* * core_pmus: A PMU belongs to core_pmus if it's name is "cpu" or it's sy= sfs @@ -385,6 +386,47 @@ struct perf_pmu *perf_pmus__scan_for_event(struct perf= _pmu *pmu, const char *eve return NULL; } =20 +struct perf_pmu *perf_pmus__scan_matching_wildcard(struct perf_pmu *pmu, c= onst char *wildcard) +{ + bool use_core_pmus =3D !pmu || pmu->is_core; + + if (!pmu) { + /* + * Core PMUs, other sysfs PMUs and tool PMU can have any name or + * aren't wother optimizing for. + */ + unsigned int to_read_pmus =3D PERF_TOOL_PMU_TYPE_PE_CORE_MASK | + PERF_TOOL_PMU_TYPE_PE_OTHER_MASK | + PERF_TOOL_PMU_TYPE_TOOL_MASK; + + /* + * Hwmon PMUs have an alias from a sysfs name like hwmon0, + * hwmon1, etc. or have a name of hwmon_. They therefore + * can only have a wildcard match if the wildcard begins with + * "hwmon". + */ + if (strisglob(wildcard) || + (strlen(wildcard) >=3D 5 && strncmp("hwmon", wildcard, 5) =3D=3D 0)) + to_read_pmus |=3D PERF_TOOL_PMU_TYPE_HWMON_MASK; + + pmu_read_sysfs(to_read_pmus); + pmu =3D list_prepare_entry(pmu, &core_pmus, list); + } + if (use_core_pmus) { + list_for_each_entry_continue(pmu, &core_pmus, list) { + if (perf_pmu__wildcard_match(pmu, wildcard)) + return pmu; + } + pmu =3D NULL; + pmu =3D list_prepare_entry(pmu, &other_pmus, list); + } + list_for_each_entry_continue(pmu, &other_pmus, list) { + if (perf_pmu__wildcard_match(pmu, wildcard)) + return pmu; + } + return NULL; +} + static struct perf_pmu *perf_pmus__scan_skip_duplicates(struct perf_pmu *p= mu) { bool use_core_pmus =3D !pmu || pmu->is_core; diff --git a/tools/perf/util/pmus.h b/tools/perf/util/pmus.h index 213ee65306d6..2794d8c3a466 100644 --- a/tools/perf/util/pmus.h +++ b/tools/perf/util/pmus.h @@ -20,6 +20,7 @@ struct perf_pmu *perf_pmus__find_by_type(unsigned int typ= e); struct perf_pmu *perf_pmus__scan(struct perf_pmu *pmu); struct perf_pmu *perf_pmus__scan_core(struct perf_pmu *pmu); struct perf_pmu *perf_pmus__scan_for_event(struct perf_pmu *pmu, const cha= r *event); +struct perf_pmu *perf_pmus__scan_matching_wildcard(struct perf_pmu *pmu, c= onst char *wildcard); =20 const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str); =20 --=20 2.49.0.504.g3bcea36a83-goog From nobody Sat Feb 7 16:26:56 2026 Received: from mail-pl1-f201.google.com (mail-pl1-f201.google.com [209.85.214.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 805F61F4CA8 for ; Thu, 3 Apr 2025 20:24:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1743711898; cv=none; b=bt2qfLKoEg/sY81IDyIVxPppS2pG4G3NAAuhMr0/yIfk3xTyFk+BHOIYCRHZpFCzA4hDvDh5MTb51dOmVLwaRHNZQGO4jxR+Un8MONjcGWnMscj3kkIFeAD4wxLenA5zwGHCgzZPedxW+9QKjbIOyAq5oxVil83yujTCI5tvRXs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1743711898; c=relaxed/simple; bh=8jOAPN5SkssGVVMyQwsx3QMzWVtsQ0tc1RT8HSL25Ts=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=pox50MAtpLPv4in7ofPB/McMQFUKOveaLiWDK1sVqixQaywjeOiKSPFaX0y8ACGtjSYqjOZpjsMcCeI03gPiec3YFT5I+kaeR+hYCIErKDz6UsT4Jtzf1WOECEeoDfIc7fqVa8giFEmr6+HvixfmCQPgEf37Ck74jW9vSLrPtOI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=Jy1tt/ro; arc=none smtp.client-ip=209.85.214.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="Jy1tt/ro" Received: by mail-pl1-f201.google.com with SMTP id d9443c01a7336-2264e5b2b7cso12038065ad.2 for ; Thu, 03 Apr 2025 13:24:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1743711895; x=1744316695; darn=vger.kernel.org; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:from:to:cc:subject:date:message-id :reply-to; bh=UwTlJ8Whi38OQ1sqGLMQF0emdI9dCb+W76xjeck5BXE=; b=Jy1tt/roznz6wSE+FerULJatmH6gR5A3sjRdqXs4qgcfKuPLyTzsG2vK8kvDmBTttf Dh1tS9rux3B3CGJy28XIm638CNl6ySUFCLPvots6OEqDPq8xbCCdH0sR+gj02xfzV/Jy x1W2Z/x7yNaj0L2fqdc7pYwprUOiPlGhU3qo8XqJ3utuY8/AErZ6ENs7ziGmxYNK7bef rrjStqGkVO4rOdoJOg5Xx62ML9dYCLFlnNV0low/UgKzNGuIPpt13qOs9FzfZSvml2Vs ghPzpEG1paWfUZ01Dt5hXjpFesZHdAX5NrQ57ZPBHQVxYnqxpC+OCjO1N9ItXJNQ0uMB zgAg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1743711895; x=1744316695; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:x-gm-message-state:from:to:cc:subject :date:message-id:reply-to; bh=UwTlJ8Whi38OQ1sqGLMQF0emdI9dCb+W76xjeck5BXE=; b=wmIaskaBrmGn+bEi6xTuIEgSItWYnpGtqT/qAviubvXCuiIXG0+mkeTNanKjllPkM/ Ls2BfDAe8/QnA+ElrAuaNnTi6KgPqzVjhK8E89csXldvk9fbq0uEpG/16mItkvaAqCVC vLugVB98Al7sR/ebMPIP/Unc5FIZOJyfpePSSrJjvoXW0/GSLB6pMHlZFpv5hNOhMwSp fwjtsyfV10H7nTO8b7IjBhfknY7W8ePZLisU7XpEGqZkThBLNA6e6yhsQEjWFxMQLmSK l+6z6Vm3q/1jY8hOqD1jetQ6s5CYkHBNTaZMwgNVoVI4VydapH4LGhKHumz+64W8GpyX o4tQ== X-Forwarded-Encrypted: i=1; AJvYcCVzf+QixTYK8Ei15WLmyPZVmt9POmuRpHKbg8YSk6TTVejP3+s0Dp1+vZO+wUZob3ybj4m7YtHPRP2a7jg=@vger.kernel.org X-Gm-Message-State: AOJu0YzsWApmXeKmQSMW8VxvHvc83QexiUVrf6rINgbBKVJEVi3eeQ1i NkBSB5ebhpt0syi6225e47WwCYbSR3wcgq9XP5xedo8/O9XOJoX4EboZ+bVVJaDPTmrgDOnQD3H bjI/rtQ== X-Google-Smtp-Source: AGHT+IEGrqjDfiI8q2zEXDxqGVxBaAuUweXAoGOcAMRtNOS9DZBdzVyTQk/G9x2OuQT8Gz21sTAsYUC+qVx7 X-Received: from plbb20.prod.google.com ([2002:a17:903:c14:b0:227:b826:af9e]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a17:902:ebc9:b0:223:3b76:4e22 with SMTP id d9443c01a7336-22a8a858ee8mr3253865ad.6.1743711894787; Thu, 03 Apr 2025 13:24:54 -0700 (PDT) Date: Thu, 3 Apr 2025 13:24:38 -0700 In-Reply-To: <20250403202439.57791-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250403202439.57791-1-irogers@google.com> X-Mailer: git-send-email 2.49.0.504.g3bcea36a83-goog Message-ID: <20250403202439.57791-4-irogers@google.com> Subject: [PATCH v3 3/4] perf drm_pmu: Add a tool like PMU to expose DRM information From: Ian Rogers To: Peter Zijlstra , Ingo Molnar , Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Alexander Shishkin , Jiri Olsa , Ian Rogers , Adrian Hunter , Kan Liang , "Masami Hiramatsu (Google)" , James Clark , Weilin Wang , Dominique Martinet , Thomas Richter , Junhao He , Jean-Philippe Romain , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" DRM clients expose information through usage stats as documented in Documentation/gpu/drm-usage-stats.rst (available online at https://docs.kernel.org/gpu/drm-usage-stats.html). Add a tool like PMU, similar to the hwmon PMU, that exposes DRM information. For example on a tigerlake laptop: ``` $ perf list drm List of pre-defined events (to be used in -e or -M): drm: drm-active-stolen-system0 [Total memory active in one or more engines. Unit: drm_i915] drm-active-system0 [Total memory active in one or more engines. Unit: drm_i915] drm-engine-capacity-video [Engine capacity. Unit: drm_i915] drm-engine-copy [Utilization in ns. Unit: drm_i915] drm-engine-render [Utilization in ns. Unit: drm_i915] drm-engine-video [Utilization in ns. Unit: drm_i915] drm-engine-video-enhance [Utilization in ns. Unit: drm_i915] drm-purgeable-stolen-system0 [Size of resident and purgeable memory bufers. Unit: drm_i915] drm-purgeable-system0 [Size of resident and purgeable memory bufers. Unit: drm_i915] drm-resident-stolen-system0 [Size of resident memory bufers. Unit: drm_i915] drm-resident-system0 [Size of resident memory bufers. Unit: drm_i915] drm-shared-stolen-system0 [Size of shared memory bufers. Unit: drm_i915] drm-shared-system0 [Size of shared memory bufers. Unit: drm_i915] drm-total-stolen-system0 [Size of shared and private memory. Unit: drm_i915] drm-total-system0 [Size of shared and private memory. Unit: drm_i915] ``` System wide data can be gathered: ``` $ perf stat -x, -I 1000 -e drm-active-stolen-system0,drm-active-system0,drm= -engine-capacity-video,drm-engine-copy,drm-engine-render,drm-engine-video,d= rm-engine-video-enhance,drm-purgeable-stolen-system0,drm-purgeable-system0,= drm-resident-stolen-system0,drm-resident-system0,drm-shared-stolen-system0,= drm-shared-system0,drm-total-stolen-system0,drm-total-system0 1.000904910,0,bytes,drm-active-stolen-system0,1,100.00,, 1.000904910,0,bytes,drm-active-system0,1,100.00,, 1.000904910,36,capacity,drm-engine-capacity-video,1,100.00,, 1.000904910,0,ns,drm-engine-copy,1,100.00,, 1.000904910,1472970566175,ns,drm-engine-render,1,100.00,, 1.000904910,0,ns,drm-engine-video,1,100.00,, 1.000904910,0,ns,drm-engine-video-enhance,1,100.00,, 1.000904910,0,bytes,drm-purgeable-stolen-system0,1,100.00,, 1.000904910,38199296,bytes,drm-purgeable-system0,1,100.00,, 1.000904910,0,bytes,drm-resident-stolen-system0,1,100.00,, 1.000904910,4643196928,bytes,drm-resident-system0,1,100.00,, 1.000904910,0,bytes,drm-shared-stolen-system0,1,100.00,, 1.000904910,1886871552,bytes,drm-shared-system0,1,100.00,, 1.000904910,0,bytes,drm-total-stolen-system0,1,100.00,, 1.000904910,4643196928,bytes,drm-total-system0,1,100.00,, 2.264426839,0,bytes,drm-active-stolen-system0,1,100.00,, ``` Or for a particular process: ``` $ perf stat -x, -I 1000 -e drm-active-stolen-system0,drm-active-system0,drm= -engine-capacity-video,drm-engine-copy,drm-engine-render,drm-engine-video,d= rm-engine-video-enhance,drm-purgeable-stolen-system0,drm-purgeable-system0,= drm-resident-stolen-system0,drm-resident-system0,drm-shared-stolen-system0,= drm-shared-system0,drm-total-stolen-system0,drm-total-system0 -p 200027 1.001040274,0,bytes,drm-active-stolen-system0,6,100.00,, 1.001040274,0,bytes,drm-active-system0,6,100.00,, 1.001040274,12,capacity,drm-engine-capacity-video,6,100.00,, 1.001040274,0,ns,drm-engine-copy,6,100.00,, 1.001040274,1542300,ns,drm-engine-render,6,100.00,, 1.001040274,0,ns,drm-engine-video,6,100.00,, 1.001040274,0,ns,drm-engine-video-enhance,6,100.00,, 1.001040274,0,bytes,drm-purgeable-stolen-system0,6,100.00,, 1.001040274,13516800,bytes,drm-purgeable-system0,6,100.00,, 1.001040274,0,bytes,drm-resident-stolen-system0,6,100.00,, 1.001040274,27746304,bytes,drm-resident-system0,6,100.00,, 1.001040274,0,bytes,drm-shared-stolen-system0,6,100.00,, 1.001040274,0,bytes,drm-shared-system0,6,100.00,, 1.001040274,0,bytes,drm-total-stolen-system0,6,100.00,, 1.001040274,27746304,bytes,drm-total-system0,6,100.00,, 2.016629075,0,bytes,drm-active-stolen-system0,6,100.00,, ``` As with the hwmon PMU, high numbered PMU types are used to encode multiple possible "DRM" PMUs. The appropriate fdinfo is found by scanning /proc and filtering which fdinfos to read with stat. To avoid some unneeding scanning, events not starting with "drm-" are ignored. The patch builds on commit 57e13264dcea ("perf pmus: Restructure pmu_read_sysfs to scan fewer PMUs") and later so that only if full wild carding is being done, the PMU starts with "drm_" or the event starts with "drm-" will /proc be scanned. That is there should be little to no cost in this PMU unless DRM events are requested. Signed-off-by: Ian Rogers --- tools/perf/util/Build | 1 + tools/perf/util/drm_pmu.c | 689 ++++++++++++++++++++++++++++++++++++++ tools/perf/util/drm_pmu.h | 39 +++ tools/perf/util/evsel.c | 9 + tools/perf/util/pmu.c | 15 + tools/perf/util/pmu.h | 4 +- tools/perf/util/pmus.c | 30 +- 7 files changed, 782 insertions(+), 5 deletions(-) create mode 100644 tools/perf/util/drm_pmu.c create mode 100644 tools/perf/util/drm_pmu.h diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 946bce6628f3..d55db6ec6698 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -84,6 +84,7 @@ perf-util-y +=3D pmu.o perf-util-y +=3D pmus.o perf-util-y +=3D pmu-flex.o perf-util-y +=3D pmu-bison.o +perf-util-y +=3D drm_pmu.o perf-util-y +=3D hwmon_pmu.o perf-util-y +=3D tool_pmu.o perf-util-y +=3D svghelper.o diff --git a/tools/perf/util/drm_pmu.c b/tools/perf/util/drm_pmu.c new file mode 100644 index 000000000000..a76bba4fe47f --- /dev/null +++ b/tools/perf/util/drm_pmu.c @@ -0,0 +1,689 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +#include "drm_pmu.h" +#include "counts.h" +#include "cpumap.h" +#include "debug.h" +#include "evsel.h" +#include "pmu.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum drm_pmu_unit { + DRM_PMU_UNIT_BYTES, + DRM_PMU_UNIT_CAPACITY, + DRM_PMU_UNIT_CYCLES, + DRM_PMU_UNIT_HZ, + DRM_PMU_UNIT_NS, + + DRM_PMU_UNIT_MAX, +}; + +struct drm_pmu_event { + const char *name; + const char *desc; + enum drm_pmu_unit unit; +}; + +struct drm_pmu { + struct perf_pmu pmu; + struct drm_pmu_event *events; + int num_events; +}; + +static const char * const drm_pmu_unit_strs[DRM_PMU_UNIT_MAX] =3D { + "bytes", + "capacity", + "cycles", + "hz", + "ns", +}; + +static const char * const drm_pmu_scale_unit_strs[DRM_PMU_UNIT_MAX] =3D { + "1bytes", + "1capacity", + "1cycles", + "1hz", + "1ns", +}; + +bool perf_pmu__is_drm(const struct perf_pmu *pmu) +{ + return pmu && pmu->type >=3D PERF_PMU_TYPE_DRM_START && + pmu->type <=3D PERF_PMU_TYPE_DRM_END; +} + +bool evsel__is_drm(const struct evsel *evsel) +{ + return perf_pmu__is_drm(evsel->pmu); +} + +static struct drm_pmu *add_drm_pmu(struct list_head *pmus, char *line, siz= e_t line_len) +{ + struct drm_pmu *drm; + struct perf_pmu *pmu; + const char *name; + __u32 max_drm_pmu_type =3D 0; + int i =3D 12; + + if (line[line_len - 1] =3D=3D '\n') + line[line_len - 1] =3D '\0'; + while (isspace(line[i])) + i++; + + line[--i] =3D '_'; + line[--i] =3D 'm'; + line[--i] =3D 'r'; + line[--i] =3D 'd'; + name =3D &line[i]; + + list_for_each_entry(pmu, pmus, list) { + if (!perf_pmu__is_drm(pmu)) + continue; + if (pmu->type > max_drm_pmu_type) + max_drm_pmu_type =3D pmu->type; + if (!strcmp(pmu->name, name)) { + /* PMU already exists. */ + return NULL; + } + } + + drm =3D zalloc(sizeof(*drm)); + if (!drm) + return NULL; + + if (max_drm_pmu_type !=3D 0) + drm->pmu.type =3D max_drm_pmu_type + 1; + else + drm->pmu.type =3D PERF_PMU_TYPE_DRM_START; + + if (drm->pmu.type > PERF_PMU_TYPE_DRM_END) { + zfree(&drm); + pr_err("Unable to encode DRM PMU type for %s\n", name); + return NULL; + } + drm->pmu.name =3D strdup(name); + if (!drm->pmu.name) { + zfree(&drm); + return NULL; + } + drm->pmu.cpus =3D perf_cpu_map__new("0"); + if (!drm->pmu.cpus) { + zfree(&drm->pmu.name); + zfree(&drm); + return NULL; + } + INIT_LIST_HEAD(&drm->pmu.format); + INIT_LIST_HEAD(&drm->pmu.aliases); + INIT_LIST_HEAD(&drm->pmu.caps); + return drm; +} + + +static bool starts_with(const char *str, const char *prefix) +{ + return !strncmp(prefix, str, strlen(prefix)); +} + +static int add_event(struct drm_pmu_event **events, int *num_events, + const char *line, enum drm_pmu_unit unit, const char *desc) +{ + const char *colon =3D strchr(line, ':'); + struct drm_pmu_event *tmp; + + if (!colon) + return -EINVAL; + + tmp =3D reallocarray(*events, *num_events + 1, sizeof(struct drm_pmu_even= t)); + if (!tmp) + return -ENOMEM; + tmp[*num_events].unit =3D unit; + tmp[*num_events].desc =3D desc; + tmp[*num_events].name =3D strndup(line, colon - line); + if (!tmp[*num_events].name) + return -ENOMEM; + (*num_events)++; + *events =3D tmp; + return 0; +} + +static int read_drm_pmus_cb(void *args, int fdinfo_dir_fd, const char *fd_= name) +{ + struct list_head *pmus =3D args; + char buf[640]; + struct io io; + char *line =3D NULL; + size_t line_len; + struct drm_pmu *drm =3D NULL; + struct drm_pmu_event *events =3D NULL; + int num_events =3D 0; + + io__init(&io, openat(fdinfo_dir_fd, fd_name, O_RDONLY), buf, sizeof(buf)); + if (io.fd =3D=3D -1) { + /* Failed to open file, ignore. */ + return 0; + } + + while (io__getline(&io, &line, &line_len) > 0) { + if (starts_with(line, "drm-driver:")) { + drm =3D add_drm_pmu(pmus, line, line_len); + if (!drm) + break; + continue; + } + /* + * Note the string matching below is alphabetical, with more + * specific matches appearing before less specific. + */ + if (starts_with(line, "drm-active-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, + "Total memory active in one or more engines"); + continue; + } + if (starts_with(line, "drm-cycles-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_CYCLES, + "Busy cycles"); + continue; + } + if (starts_with(line, "drm-engine-capacity-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_CAPACITY, + "Engine capacity"); + continue; + } + if (starts_with(line, "drm-engine-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_NS, + "Utilization in ns"); + continue; + } + if (starts_with(line, "drm-maxfreq-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_HZ, + "Maximum frequency"); + continue; + } + if (starts_with(line, "drm-purgeable-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, + "Size of resident and purgeable memory bufers"); + continue; + } + if (starts_with(line, "drm-resident-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, + "Size of resident memory bufers"); + continue; + } + if (starts_with(line, "drm-shared-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, + "Size of shared memory bufers"); + continue; + } + if (starts_with(line, "drm-total-cycles-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, + "Total busy cycles"); + continue; + } + if (starts_with(line, "drm-total-")) { + add_event(&events, &num_events, line, DRM_PMU_UNIT_BYTES, + "Size of shared and private memory"); + continue; + } + if (verbose > 1 && starts_with(line, "drm-") && + !starts_with(line, "drm-client-id:") && + !starts_with(line, "drm-pdev:")) + pr_debug("Unhandled DRM PMU fdinfo line match '%s'\n", line); + } + if (drm) { + drm->events =3D events; + drm->num_events =3D num_events; + list_add_tail(&drm->pmu.list, pmus); + } + free(line); + if (io.fd !=3D -1) + close(io.fd); + return 0; +} + +void drm_pmu__exit(struct perf_pmu *pmu) +{ + struct drm_pmu *drm =3D container_of(pmu, struct drm_pmu, pmu); + + free(drm->events); +} + +bool drm_pmu__have_event(const struct perf_pmu *pmu, const char *name) +{ + struct drm_pmu *drm =3D container_of(pmu, struct drm_pmu, pmu); + + if (!starts_with(name, "drm-")) + return false; + + for (int i =3D 0; i < drm->num_events; i++) { + if (!strcasecmp(drm->events[i].name, name)) + return true; + } + return false; +} + +int drm_pmu__for_each_event(const struct perf_pmu *pmu, void *state, pmu_e= vent_callback cb) +{ + struct drm_pmu *drm =3D container_of(pmu, struct drm_pmu, pmu); + + for (int i =3D 0; i < drm->num_events; i++) { + char encoding_buf[128]; + struct pmu_event_info info =3D { + .pmu =3D pmu, + .name =3D drm->events[i].name, + .alias =3D NULL, + .scale_unit =3D drm_pmu_scale_unit_strs[drm->events[i].unit], + .desc =3D drm->events[i].desc, + .long_desc =3D NULL, + .encoding_desc =3D encoding_buf, + .topic =3D "drm", + .pmu_name =3D pmu->name, + .event_type_desc =3D "DRM event", + }; + int ret; + + snprintf(encoding_buf, sizeof(encoding_buf), "%s/config=3D0x%x/", pmu->n= ame, i); + + ret =3D cb(state, &info); + if (ret) + return ret; + } + return 0; +} + +size_t drm_pmu__num_events(const struct perf_pmu *pmu) +{ + const struct drm_pmu *drm =3D container_of(pmu, struct drm_pmu, pmu); + + return drm->num_events; +} + +static int drm_pmu__index_for_event(const struct drm_pmu *drm, const char = *name) +{ + for (int i =3D 0; i < drm->num_events; i++) { + if (!strcmp(drm->events[i].name, name)) + return i; + } + return -1; +} + +static int drm_pmu__config_term(const struct drm_pmu *drm, + struct perf_event_attr *attr, + struct parse_events_term *term, + struct parse_events_error *err) +{ + if (term->type_term =3D=3D PARSE_EVENTS__TERM_TYPE_USER) { + int i =3D drm_pmu__index_for_event(drm, term->config); + + if (i >=3D 0) { + attr->config =3D i; + return 0; + } + } + if (err) { + char *err_str; + + parse_events_error__handle(err, term->err_val, + asprintf(&err_str, + "unexpected drm event term (%s) %s", + parse_events__term_type_str(term->type_term), + term->config) < 0 + ? strdup("unexpected drm event term") + : err_str, + NULL); + } + return -EINVAL; +} + +int drm_pmu__config_terms(const struct perf_pmu *pmu, + struct perf_event_attr *attr, + struct parse_events_terms *terms, + struct parse_events_error *err) +{ + struct drm_pmu *drm =3D container_of(pmu, struct drm_pmu, pmu); + struct parse_events_term *term; + + list_for_each_entry(term, &terms->terms, list) { + if (drm_pmu__config_term(drm, attr, term, err)) + return -EINVAL; + } + + return 0; +} + +int drm_pmu__check_alias(const struct perf_pmu *pmu, struct parse_events_t= erms *terms, + struct perf_pmu_info *info, struct parse_events_error *err) +{ + struct drm_pmu *drm =3D container_of(pmu, struct drm_pmu, pmu); + struct parse_events_term *term =3D + list_first_entry(&terms->terms, struct parse_events_term, list); + + if (term->type_term =3D=3D PARSE_EVENTS__TERM_TYPE_USER) { + int i =3D drm_pmu__index_for_event(drm, term->config); + + if (i >=3D 0) { + info->unit =3D drm_pmu_unit_strs[drm->events[i].unit]; + info->scale =3D 1; + return 0; + } + } + if (err) { + char *err_str; + + parse_events_error__handle(err, term->err_val, + asprintf(&err_str, + "unexpected drm event term (%s) %s", + parse_events__term_type_str(term->type_term), + term->config) < 0 + ? strdup("unexpected drm event term") + : err_str, + NULL); + } + return -EINVAL; +} + +struct minor_info { + unsigned int *minors; + int minors_num, minors_len; +}; + +static int for_each_drm_fdinfo_in_dir(int (*cb)(void *args, int fdinfo_dir= _fd, const char *fd_name), + void *args, int proc_dir, const char *pid_name, + struct minor_info *minors) +{ + char buf[256]; + DIR *fd_dir; + struct dirent *fd_entry; + int fd_dir_fd, fdinfo_dir_fd =3D -1; + + + scnprintf(buf, sizeof(buf), "%s/fd", pid_name); + fd_dir_fd =3D openat(proc_dir, buf, O_DIRECTORY); + if (fd_dir_fd =3D=3D -1) + return 0; /* Presumably lost race to open. */ + fd_dir =3D fdopendir(fd_dir_fd); + if (!fd_dir) { + close(fd_dir_fd); + return -ENOMEM; + } + while ((fd_entry =3D readdir(fd_dir)) !=3D NULL) { + struct stat stat; + unsigned int minor; + bool is_dup =3D false; + int ret; + + if (fd_entry->d_type !=3D DT_LNK) + continue; + + if (fstatat(fd_dir_fd, fd_entry->d_name, &stat, 0) !=3D 0) + continue; + + if ((stat.st_mode & S_IFMT) !=3D S_IFCHR || major(stat.st_rdev) !=3D 226) + continue; + + minor =3D minor(stat.st_rdev); + for (int i =3D 0; i < minors->minors_num; i++) { + if (minor(stat.st_rdev) =3D=3D minors->minors[i]) { + is_dup =3D true; + break; + } + } + if (is_dup) + continue; + + if (minors->minors_num =3D=3D minors->minors_len) { + unsigned int *tmp =3D reallocarray(minors->minors, minors->minors_len += 4, + sizeof(unsigned int)); + + if (tmp) { + minors->minors =3D tmp; + minors->minors_len +=3D 4; + } + } + minors->minors[minors->minors_num++] =3D minor; + if (fdinfo_dir_fd =3D=3D -1) { + /* Open fdinfo dir if we have a DRM fd. */ + scnprintf(buf, sizeof(buf), "%s/fdinfo", pid_name); + fdinfo_dir_fd =3D openat(proc_dir, buf, O_DIRECTORY); + if (fdinfo_dir_fd =3D=3D -1) + continue; + } + ret =3D cb(args, fdinfo_dir_fd, fd_entry->d_name); + if (ret) + return ret; + } + if (fdinfo_dir_fd !=3D -1) + close(fdinfo_dir_fd); + closedir(fd_dir); + return 0; +} + +static int for_each_drm_fdinfo(bool skip_all_duplicates, + int (*cb)(void *args, int fdinfo_dir_fd, const char *fd_name), + void *args) +{ + DIR *proc_dir; + struct dirent *proc_entry; + int ret; + /* + * minors maintains an array of DRM minor device numbers seen for a pid, + * or for all pids if skip_all_duplicates is true, so that duplicates + * are ignored. + */ + struct minor_info minors =3D { + .minors =3D NULL, + .minors_num =3D 0, + .minors_len =3D 0, + }; + + proc_dir =3D opendir(procfs__mountpoint()); + if (!proc_dir) + return 0; + + /* Walk through the /proc directory. */ + while ((proc_entry =3D readdir(proc_dir)) !=3D NULL) { + if (proc_entry->d_type !=3D DT_DIR || + !isdigit(proc_entry->d_name[0])) + continue; + if (!skip_all_duplicates) { + /* Reset the seen minor numbers for each pid. */ + minors.minors_num =3D 0; + } + ret =3D for_each_drm_fdinfo_in_dir(cb, args, + dirfd(proc_dir), proc_entry->d_name, + &minors); + if (ret) + break; + } + free(minors.minors); + closedir(proc_dir); + return ret; +} + +int perf_pmus__read_drm_pmus(struct list_head *pmus) +{ + return for_each_drm_fdinfo(/*skip_all_duplicates=3D*/true, read_drm_pmus_= cb, pmus); +} + +int evsel__drm_pmu_open(struct evsel *evsel, + struct perf_thread_map *threads, + int start_cpu_map_idx, int end_cpu_map_idx) +{ + (void)evsel; + (void)threads; + (void)start_cpu_map_idx; + (void)end_cpu_map_idx; + return 0; +} + +static uint64_t read_count_and_apply_unit(const char *count_and_unit, enum= drm_pmu_unit unit) +{ + char *unit_ptr =3D NULL; + uint64_t count =3D strtoul(count_and_unit, &unit_ptr, 10); + + if (!unit_ptr) + return 0; + + while (isblank(*unit_ptr)) + unit_ptr++; + + switch (unit) { + case DRM_PMU_UNIT_BYTES: + if (*unit_ptr =3D=3D '\0') + assert(count =3D=3D 0); /* Generally undocumented, happens for 0. */ + else if (!strcmp(unit_ptr, "KiB")) + count *=3D 1024; + else if (!strcmp(unit_ptr, "MiB")) + count *=3D 1024 * 1024; + else + pr_err("Unexpected bytes unit '%s'\n", unit_ptr); + break; + case DRM_PMU_UNIT_CAPACITY: + /* No units expected. */ + break; + case DRM_PMU_UNIT_CYCLES: + /* No units expected. */ + break; + case DRM_PMU_UNIT_HZ: + if (!strcmp(unit_ptr, "Hz")) + count *=3D 1; + else if (!strcmp(unit_ptr, "KHz")) + count *=3D 1000; + else if (!strcmp(unit_ptr, "MHz")) + count *=3D 1000000; + else + pr_err("Unexpected hz unit '%s'\n", unit_ptr); + break; + case DRM_PMU_UNIT_NS: + /* Only unit ns expected. */ + break; + case DRM_PMU_UNIT_MAX: + default: + break; + } + return count; +} + +static uint64_t read_drm_event(int fdinfo_dir_fd, const char *fd_name, + const char *match, enum drm_pmu_unit unit) +{ + char buf[640]; + struct io io; + char *line =3D NULL; + size_t line_len; + uint64_t count =3D 0; + + io__init(&io, openat(fdinfo_dir_fd, fd_name, O_RDONLY), buf, sizeof(buf)); + if (io.fd =3D=3D -1) { + /* Failed to open file, ignore. */ + return 0; + } + while (io__getline(&io, &line, &line_len) > 0) { + size_t i =3D strlen(match); + + if (strncmp(line, match, i)) + continue; + if (line[i] !=3D ':') + continue; + while (isblank(line[++i])) + ; + if (line[line_len - 1] =3D=3D '\n') + line[line_len - 1] =3D '\0'; + count =3D read_count_and_apply_unit(&line[i], unit); + break; + } + free(line); + close(io.fd); + return count; +} + +struct read_drm_event_cb_args { + const char *match; + uint64_t count; + enum drm_pmu_unit unit; +}; + +static int read_drm_event_cb(void *vargs, int fdinfo_dir_fd, const char *f= d_name) +{ + struct read_drm_event_cb_args *args =3D vargs; + + args->count +=3D read_drm_event(fdinfo_dir_fd, fd_name, args->match, args= ->unit); + return 0; +} + +static uint64_t drm_pmu__read_system_wide(struct drm_pmu *drm, struct evse= l *evsel) +{ + struct read_drm_event_cb_args args =3D { + .count =3D 0, + .match =3D drm->events[evsel->core.attr.config].name, + .unit =3D drm->events[evsel->core.attr.config].unit, + }; + + for_each_drm_fdinfo(/*skip_all_duplicates=3D*/false, read_drm_event_cb, &= args); + return args.count; +} + +static uint64_t drm_pmu__read_for_pid(struct drm_pmu *drm, struct evsel *e= vsel, int pid) +{ + struct read_drm_event_cb_args args =3D { + .count =3D 0, + .match =3D drm->events[evsel->core.attr.config].name, + .unit =3D drm->events[evsel->core.attr.config].unit, + }; + struct minor_info minors =3D { + .minors =3D NULL, + .minors_num =3D 0, + .minors_len =3D 0, + }; + int proc_dir =3D open(procfs__mountpoint(), O_DIRECTORY); + char pid_name[12]; + int ret; + + if (proc_dir < 0) + return 0; + + snprintf(pid_name, sizeof(pid_name), "%d", pid); + ret =3D for_each_drm_fdinfo_in_dir(read_drm_event_cb, &args, proc_dir, pi= d_name, &minors); + free(minors.minors); + close(proc_dir); + return ret =3D=3D 0 ? args.count : 0; +} + +int evsel__drm_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread) +{ + struct drm_pmu *drm =3D container_of(evsel->pmu, struct drm_pmu, pmu); + struct perf_counts_values *count, *old_count =3D NULL; + int pid =3D perf_thread_map__pid(evsel->core.threads, thread); + uint64_t counter; + + if (pid !=3D -1) + counter =3D drm_pmu__read_for_pid(drm, evsel, pid); + else + counter =3D drm_pmu__read_system_wide(drm, evsel); + + if (evsel->prev_raw_counts) + old_count =3D perf_counts(evsel->prev_raw_counts, cpu_map_idx, thread); + + count =3D perf_counts(evsel->counts, cpu_map_idx, thread); + if (old_count) { + count->val =3D old_count->val + counter; + count->run =3D old_count->run + 1; + count->ena =3D old_count->ena + 1; + } else { + count->val =3D counter; + count->run++; + count->ena++; + } + return 0; +} diff --git a/tools/perf/util/drm_pmu.h b/tools/perf/util/drm_pmu.h new file mode 100644 index 000000000000..e7f366fca8a4 --- /dev/null +++ b/tools/perf/util/drm_pmu.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +#ifndef __DRM_PMU_H +#define __DRM_PMU_H +/* + * Linux DRM clients expose information through usage stats as documented = in + * Documentation/gpu/drm-usage-stats.rst (available online at + * https://docs.kernel.org/gpu/drm-usage-stats.html). This is a tool like = PMU + * that exposes DRM information. + */ + +#include "pmu.h" +#include + +struct list_head; +struct perf_thread_map; + +void drm_pmu__exit(struct perf_pmu *pmu); +bool drm_pmu__have_event(const struct perf_pmu *pmu, const char *name); +int drm_pmu__for_each_event(const struct perf_pmu *pmu, void *state, pmu_e= vent_callback cb); +size_t drm_pmu__num_events(const struct perf_pmu *pmu); +int drm_pmu__config_terms(const struct perf_pmu *pmu, + struct perf_event_attr *attr, + struct parse_events_terms *terms, + struct parse_events_error *err); +int drm_pmu__check_alias(const struct perf_pmu *pmu, struct parse_events_t= erms *terms, + struct perf_pmu_info *info, struct parse_events_error *err); + + +bool perf_pmu__is_drm(const struct perf_pmu *pmu); +bool evsel__is_drm(const struct evsel *evsel); + +int perf_pmus__read_drm_pmus(struct list_head *pmus); + +int evsel__drm_pmu_open(struct evsel *evsel, + struct perf_thread_map *threads, + int start_cpu_map_idx, int end_cpu_map_idx); +int evsel__drm_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread); + +#endif /* __DRM_PMU_H */ diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 1974395492d7..6722fbdbc72b 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -56,6 +56,7 @@ #include "off_cpu.h" #include "pmu.h" #include "pmus.h" +#include "drm_pmu.h" #include "hwmon_pmu.h" #include "tool_pmu.h" #include "rlimit.h" @@ -1888,6 +1889,9 @@ int evsel__read_counter(struct evsel *evsel, int cpu_= map_idx, int thread) if (evsel__is_hwmon(evsel)) return evsel__hwmon_pmu_read(evsel, cpu_map_idx, thread); =20 + if (evsel__is_drm(evsel)) + return evsel__drm_pmu_read(evsel, cpu_map_idx, thread); + if (evsel__is_retire_lat(evsel)) return evsel__read_retire_lat(evsel, cpu_map_idx, thread); =20 @@ -2628,6 +2632,11 @@ static int evsel__open_cpu(struct evsel *evsel, stru= ct perf_cpu_map *cpus, start_cpu_map_idx, end_cpu_map_idx); } + if (evsel__is_drm(evsel)) { + return evsel__drm_pmu_open(evsel, threads, + start_cpu_map_idx, + end_cpu_map_idx); + } =20 for (idx =3D start_cpu_map_idx; idx < end_cpu_map_idx; idx++) { cpu =3D perf_cpu_map__cpu(cpus, idx); diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index b7ebac5ab1d1..6a2561f8b153 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -20,6 +20,7 @@ #include "debug.h" #include "evsel.h" #include "pmu.h" +#include "drm_pmu.h" #include "hwmon_pmu.h" #include "pmus.h" #include "tool_pmu.h" @@ -1546,6 +1547,8 @@ int perf_pmu__config_terms(const struct perf_pmu *pmu, =20 if (perf_pmu__is_hwmon(pmu)) return hwmon_pmu__config_terms(pmu, attr, terms, err); + if (perf_pmu__is_drm(pmu)) + return drm_pmu__config_terms(pmu, attr, terms, err); =20 list_for_each_entry(term, &terms->terms, list) { if (pmu_config_term(pmu, attr, term, terms, zero, apply_hardcoded, err)) @@ -1683,6 +1686,10 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, stru= ct parse_events_terms *head_ ret =3D hwmon_pmu__check_alias(head_terms, info, err); goto out; } + if (perf_pmu__is_drm(pmu)) { + ret =3D drm_pmu__check_alias(pmu, head_terms, info, err); + goto out; + } =20 /* Fake PMU doesn't rewrite terms. */ if (perf_pmu__is_fake(pmu)) @@ -1860,6 +1867,8 @@ bool perf_pmu__have_event(struct perf_pmu *pmu, const= char *name) return false; if (perf_pmu__is_hwmon(pmu)) return hwmon_pmu__have_event(pmu, name); + if (perf_pmu__is_drm(pmu)) + return drm_pmu__have_event(pmu, name); if (perf_pmu__find_alias(pmu, name, /*load=3D*/ true) !=3D NULL) return true; if (pmu->cpu_aliases_added || !pmu->events_table) @@ -1873,6 +1882,8 @@ size_t perf_pmu__num_events(struct perf_pmu *pmu) =20 if (perf_pmu__is_hwmon(pmu)) return hwmon_pmu__num_events(pmu); + if (perf_pmu__is_drm(pmu)) + return drm_pmu__num_events(pmu); =20 pmu_aliases_parse(pmu); nr =3D pmu->sysfs_aliases + pmu->sys_json_aliases; @@ -1940,6 +1951,8 @@ int perf_pmu__for_each_event(struct perf_pmu *pmu, bo= ol skip_duplicate_pmus, =20 if (perf_pmu__is_hwmon(pmu)) return hwmon_pmu__for_each_event(pmu, state, cb); + if (perf_pmu__is_drm(pmu)) + return drm_pmu__for_each_event(pmu, state, cb); =20 strbuf_init(&sb, /*hint=3D*/ 0); pmu_aliases_parse(pmu); @@ -2403,6 +2416,8 @@ void perf_pmu__delete(struct perf_pmu *pmu) { if (perf_pmu__is_hwmon(pmu)) hwmon_pmu__exit(pmu); + else if (perf_pmu__is_drm(pmu)) + drm_pmu__exit(pmu); =20 perf_pmu__del_formats(&pmu->format); perf_pmu__del_aliases(pmu); diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index b93014cc3670..7bc5ac61e93a 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -38,7 +38,9 @@ struct perf_pmu_caps { =20 enum { PERF_PMU_TYPE_PE_START =3D 0, - PERF_PMU_TYPE_PE_END =3D 0xFFFEFFFF, + PERF_PMU_TYPE_PE_END =3D 0xFFFDFFFF, + PERF_PMU_TYPE_DRM_START =3D 0xFFFE0000, + PERF_PMU_TYPE_DRM_END =3D 0xFFFEFFFF, PERF_PMU_TYPE_HWMON_START =3D 0xFFFF0000, PERF_PMU_TYPE_HWMON_END =3D 0xFFFFFFFD, PERF_PMU_TYPE_TOOL =3D 0xFFFFFFFE, diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c index 93d5db1c89b4..f7782d6c396a 100644 --- a/tools/perf/util/pmus.c +++ b/tools/perf/util/pmus.c @@ -12,6 +12,7 @@ #include #include "cpumap.h" #include "debug.h" +#include "drm_pmu.h" #include "evsel.h" #include "pmus.h" #include "pmu.h" @@ -43,16 +44,19 @@ enum perf_tool_pmu_type { PERF_TOOL_PMU_TYPE_PE_OTHER, PERF_TOOL_PMU_TYPE_TOOL, PERF_TOOL_PMU_TYPE_HWMON, + PERF_TOOL_PMU_TYPE_DRM, =20 #define PERF_TOOL_PMU_TYPE_PE_CORE_MASK (1 << PERF_TOOL_PMU_TYPE_PE_CORE) #define PERF_TOOL_PMU_TYPE_PE_OTHER_MASK (1 << PERF_TOOL_PMU_TYPE_PE_OTHER) #define PERF_TOOL_PMU_TYPE_TOOL_MASK (1 << PERF_TOOL_PMU_TYPE_TOOL) #define PERF_TOOL_PMU_TYPE_HWMON_MASK (1 << PERF_TOOL_PMU_TYPE_HWMON) +#define PERF_TOOL_PMU_TYPE_DRM_MASK (1 << PERF_TOOL_PMU_TYPE_DRM) =20 #define PERF_TOOL_PMU_TYPE_ALL_MASK (PERF_TOOL_PMU_TYPE_PE_CORE_MASK | \ PERF_TOOL_PMU_TYPE_PE_OTHER_MASK | \ PERF_TOOL_PMU_TYPE_TOOL_MASK | \ - PERF_TOOL_PMU_TYPE_HWMON_MASK) + PERF_TOOL_PMU_TYPE_HWMON_MASK | \ + PERF_TOOL_PMU_TYPE_DRM_MASK) }; static unsigned int read_pmu_types; =20 @@ -173,6 +177,8 @@ struct perf_pmu *perf_pmus__find(const char *name) /* Looking up an individual perf event PMU failed, check if a tool PMU sh= ould be read. */ if (!strncmp(name, "hwmon_", 6)) to_read_pmus |=3D PERF_TOOL_PMU_TYPE_HWMON_MASK; + else if (!strncmp(name, "drm_", 4)) + to_read_pmus |=3D PERF_TOOL_PMU_TYPE_DRM_MASK; else if (!strcmp(name, "tool")) to_read_pmus |=3D PERF_TOOL_PMU_TYPE_TOOL_MASK; =20 @@ -273,6 +279,10 @@ static void pmu_read_sysfs(unsigned int to_read_types) (read_pmu_types & PERF_TOOL_PMU_TYPE_HWMON_MASK) =3D=3D 0) perf_pmus__read_hwmon_pmus(&other_pmus); =20 + if ((to_read_types & PERF_TOOL_PMU_TYPE_DRM_MASK) !=3D 0 && + (read_pmu_types & PERF_TOOL_PMU_TYPE_DRM_MASK) =3D=3D 0) + perf_pmus__read_drm_pmus(&other_pmus); + list_sort(NULL, &other_pmus, pmus_cmp); =20 read_pmu_types |=3D to_read_types; @@ -305,6 +315,8 @@ struct perf_pmu *perf_pmus__find_by_type(unsigned int t= ype) if (type >=3D PERF_PMU_TYPE_PE_START && type <=3D PERF_PMU_TYPE_PE_END) { to_read_pmus =3D PERF_TOOL_PMU_TYPE_PE_CORE_MASK | PERF_TOOL_PMU_TYPE_PE_OTHER_MASK; + } else if (type >=3D PERF_PMU_TYPE_DRM_START && type <=3D PERF_PMU_TYPE_D= RM_END) { + to_read_pmus =3D PERF_TOOL_PMU_TYPE_DRM_MASK; } else if (type >=3D PERF_PMU_TYPE_HWMON_START && type <=3D PERF_PMU_TYPE= _HWMON_END) { to_read_pmus =3D PERF_TOOL_PMU_TYPE_HWMON_MASK; } else { @@ -371,6 +383,10 @@ struct perf_pmu *perf_pmus__scan_for_event(struct perf= _pmu *pmu, const char *eve if (parse_hwmon_filename(event, &type, &number, /*item=3D*/NULL, /*alarm= =3D*/NULL)) to_read_pmus |=3D PERF_TOOL_PMU_TYPE_HWMON_MASK; =20 + /* Could the event be a DRM event? */ + if (strlen(event) > 4 && strncmp("drm-", event, 4) =3D=3D 0) + to_read_pmus |=3D PERF_TOOL_PMU_TYPE_DRM_MASK; + pmu_read_sysfs(to_read_pmus); pmu =3D list_prepare_entry(pmu, &core_pmus, list); } @@ -403,11 +419,17 @@ struct perf_pmu *perf_pmus__scan_matching_wildcard(st= ruct perf_pmu *pmu, const c * Hwmon PMUs have an alias from a sysfs name like hwmon0, * hwmon1, etc. or have a name of hwmon_. They therefore * can only have a wildcard match if the wildcard begins with - * "hwmon". + * "hwmon". Similarly drm PMUs must start "drm_", avoid reading + * such events unless the PMU could match. */ - if (strisglob(wildcard) || - (strlen(wildcard) >=3D 5 && strncmp("hwmon", wildcard, 5) =3D=3D 0)) + if (strisglob(wildcard)) { + to_read_pmus |=3D PERF_TOOL_PMU_TYPE_HWMON_MASK | + PERF_TOOL_PMU_TYPE_DRM_MASK; + } else if (strlen(wildcard) >=3D 4 && strncmp("drm_", wildcard, 4) =3D= =3D 0) { + to_read_pmus |=3D PERF_TOOL_PMU_TYPE_DRM_MASK; + } else if (strlen(wildcard) >=3D 5 && strncmp("hwmon", wildcard, 5) =3D= =3D 0) { to_read_pmus |=3D PERF_TOOL_PMU_TYPE_HWMON_MASK; + } =20 pmu_read_sysfs(to_read_pmus); pmu =3D list_prepare_entry(pmu, &core_pmus, list); --=20 2.49.0.504.g3bcea36a83-goog From nobody Sat Feb 7 16:26:56 2026 Received: from mail-pl1-f201.google.com (mail-pl1-f201.google.com [209.85.214.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BAD5C1F55EB for ; Thu, 3 Apr 2025 20:24:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1743711899; cv=none; b=r+zKNrvvWJY1ZWHbVKXcm8tLxwz4MEwC0tyesVeVdT6tKHIYa7f4rEQCk2N26Qdyp6lA+jEiPc4WXxXjYlvfbzdhQdP3n6kLS73VscSJqEfUT+o3ny6f5Rlmc8x78Ht27+I19ksaa5XGkDVfVH+gTz9EQq/XViamu4SRHFck1ks= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1743711899; c=relaxed/simple; bh=gNKtXVPbbB+3G3c1/vIwnhim6kYBeq02fpYtM7jeUSA=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=LZR3mdEY7CHCknFLMg8/HI6gFWsOijkFP/DxKCouxEkIsw61U8SPsc/RzqTBM39G0EXa4P9J4fmsYDNl805FXXwpIM/mUHZHgOxvH5RIRsJLqUNMVfo9RGUaFMH9lePE0UF+p7WiwAz/v+/ZILXMEzMfn9Q3MhVVdO+a/2lWXoc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=bHMl2oCV; arc=none smtp.client-ip=209.85.214.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="bHMl2oCV" Received: by mail-pl1-f201.google.com with SMTP id d9443c01a7336-224364f2492so11494645ad.3 for ; Thu, 03 Apr 2025 13:24:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1743711897; x=1744316697; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=5JjsjFOUC3YO0iochXENC9eaId60vwka6iugGNbtu6g=; b=bHMl2oCV0NSsEuL75SZKT20HqYO/Xjnbzds/OdHU6ejlw9glzpP/pPQZ2MW+7qt5HK 8zEbyt3fzWgt3Iab/mzzQreY9gwVJmlUHvXv8T1a9ZVlyLKSTPnCgmehEp8r+Ng9ym1F 8vEVB/SwZHDr4j4dbBRnK+52FG/cADVS0YAxP/GD4TQy6Bcp/LUu1qckhvijYqpLm+Yv MFiLcdrDb0vaMREmc/VTMDbWOHaHBkyiBl4vreQMoyV3KH4OUReEvDhkGALsa4yw2Xh9 iQLbX8wk9J5uGbMdkfadcDFWd5ROiGy4v77uDBY4qaSkrp2R5v84nwa5EfI6S1ohuuNT 6nhw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1743711897; x=1744316697; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=5JjsjFOUC3YO0iochXENC9eaId60vwka6iugGNbtu6g=; b=AWS7/SVYvxB6FvfeEiRFXdqOOpewteBxZETSjl/ncilicmDvNKpq7yUEyESDOXdGZo ys9i0NJlM1sCkG/lzFr0lBZ2fxuAHl/FrqLzzGj+ZoKhp/SM5tAqnjuTYLGAhJDiPc9W z5zKP4a4oxonxc52IuFjRcgRforepOBRaeACME4IcLUAwCx5xChhV2K17cW5JeUpQbtW AhYu5GT2LmQF0ubpnh/ouoYr2E+pCgkOnDqD45LiwlT4HbIcD2M/sXpJZ+p00WgHGw38 v8G8/KiY36EV1o4S465eq+wl0VmB+/JGoahGNzUfn4+jS6pPJLFNlPqGsRUgVtFmxfxQ qDLQ== X-Forwarded-Encrypted: i=1; AJvYcCXHspTVxPSg2jh3AWgRkll4Tkr+BZ58Alb16WHHKg66MtI7rA2CqtdAn12Wla9bF7kojff4coivfL4Apgs=@vger.kernel.org X-Gm-Message-State: AOJu0Yyx0g51chgZakV1w0tn/NsteJQLcoJ8ov+iaRChZ+dVca4ehfME tVrryIm75CZaFzN5dxBzfazGS0ToX9mrXCQp4ssPaw51XLTZU4esbvvau7lBmeyhf/qPGMsAuo3 aDKiONg== X-Google-Smtp-Source: AGHT+IHRfFDzCYIPq5Bp5DbUlRqc8I14sxeIBKF4QG8mXK1Q6lEJ5VL4ONiwvatjg5A7PeaRVPG/gKZbjZDM X-Received: from plbja4.prod.google.com ([2002:a17:902:efc4:b0:223:52c5:17f6]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a17:903:94e:b0:229:1619:ab58 with SMTP id d9443c01a7336-22a8a0b33e8mr7237285ad.43.1743711896952; Thu, 03 Apr 2025 13:24:56 -0700 (PDT) Date: Thu, 3 Apr 2025 13:24:39 -0700 In-Reply-To: <20250403202439.57791-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250403202439.57791-1-irogers@google.com> X-Mailer: git-send-email 2.49.0.504.g3bcea36a83-goog Message-ID: <20250403202439.57791-5-irogers@google.com> Subject: [PATCH v3 4/4] perf tests: Add a DRM PMU test From: Ian Rogers To: Peter Zijlstra , Ingo Molnar , Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Alexander Shishkin , Jiri Olsa , Ian Rogers , Adrian Hunter , Kan Liang , "Masami Hiramatsu (Google)" , James Clark , Weilin Wang , Dominique Martinet , Thomas Richter , Junhao He , Jean-Philippe Romain , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The test opens any DRM devices so that the shell has fdinfo files containing the DRM data. The test then uses perf stat to make sure the events can be read. Signed-off-by: Ian Rogers --- tools/perf/tests/shell/drm_pmu.sh | 78 +++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100755 tools/perf/tests/shell/drm_pmu.sh diff --git a/tools/perf/tests/shell/drm_pmu.sh b/tools/perf/tests/shell/drm= _pmu.sh new file mode 100755 index 000000000000..e629fe0e8463 --- /dev/null +++ b/tools/perf/tests/shell/drm_pmu.sh @@ -0,0 +1,78 @@ +#!/bin/bash +# DRM PMU +# SPDX-License-Identifier: GPL-2.0 + +set -e + +output=3D$(mktemp /tmp/perf.drm_pmu.XXXXXX.txt) + +cleanup() { + rm -f "${output}" + + trap - EXIT TERM INT +} + +trap_cleanup() { + echo "Unexpected signal in ${FUNCNAME[1]}" + cleanup + exit 1 +} +trap trap_cleanup EXIT TERM INT + +# Array to store file descriptors and device names +declare -A device_fds + +# Open all devices and store file descriptors. Opening the device will cre= ate a +# /proc/$$/fdinfo file containing the DRM statistics. +fd_count=3D3 # Start with file descriptor 3 +for device in /dev/dri/* +do + if [[ ! -c "$device" ]] + then + continue + fi + major=3D$(stat -c "%Hr" "$device") + if [[ "$major" !=3D 226 ]] + then + continue + fi + echo "Opening $device" + eval "exec $fd_count<\"$device\"" + echo "fdinfo for: $device (FD: $fd_count)" + cat "/proc/$$/fdinfo/$fd_count" + echo + device_fds["$device"]=3D"$fd_count" + fd_count=3D$((fd_count + 1)) +done + +if [[ ${#device_fds[@]} -eq 0 ]] +then + echo "No DRM devices found [Skip]" + cleanup + exit 2 +fi + +# For each DRM event +err=3D0 +for p in $(perf list --raw-dump drm-) +do + echo -n "Testing perf stat of $p. " + perf stat -e "$p" --pid=3D$$ true > "$output" 2>&1 + if ! grep -q "$p" "$output" + then + echo "Missing DRM event in: [Failed]" + cat "$output" + err=3D1 + else + echo "[OK]" + fi +done + +# Close all file descriptors +for fd in "${device_fds[@]}"; do + eval "exec $fd<&-" +done + +# Finished +cleanup +exit $err --=20 2.49.0.504.g3bcea36a83-goog