* within the expression.
         */
        struct expr_parse_ctx *pctx;
+       const char *pmu;
        /** The name of the metric such as "IPC". */
        const char *metric_name;
        /** Modifier on the metric such as "u" or NULL for none. */
        if (!m->pctx)
                goto out_err;
 
+       m->pmu = pm->pmu ?: "cpu";
        m->metric_name = pm->metric_name;
        m->modifier = NULL;
        if (modifier) {
 /**
  * setup_metric_events - Find a group of events in metric_evlist that correspond
  *                       to the IDs from a parsed metric expression.
+ * @pmu: The PMU for the IDs.
  * @ids: the metric IDs to match.
  * @metric_evlist: the list of perf events.
  * @out_metric_events: holds the created metric events array.
  */
-static int setup_metric_events(struct hashmap *ids,
+static int setup_metric_events(const char *pmu, struct hashmap *ids,
                               struct evlist *metric_evlist,
                               struct evsel ***out_metric_events)
 {
        const char *metric_id;
        struct evsel *ev;
        size_t ids_size, matched_events, i;
+       bool all_pmus = !strcmp(pmu, "all");
 
        *out_metric_events = NULL;
        ids_size = hashmap__size(ids);
        evlist__for_each_entry(metric_evlist, ev) {
                struct expr_id_data *val_ptr;
 
+               if (!all_pmus && strcmp(ev->pmu_name, pmu))
+                       continue;
                /*
                 * Check for duplicate events with the same name. For
                 * example, uncore_imc/cas_count_read/ will turn into 6
        return false;
 }
 
-static bool match_pm_metric(const struct pmu_metric *pm, const char *metric)
+static bool match_pm_metric(const struct pmu_metric *pm, const char *pmu, const char *metric)
 {
+       const char *pm_pmu = pm->pmu ?: "cpu";
+
+       if (strcmp(pmu, "all") && strcmp(pm_pmu, pmu))
+               return false;
+
        return match_metric(pm->metric_group, metric) ||
               match_metric(pm->metric_name, metric);
 }
 
 struct metricgroup_add_iter_data {
        struct list_head *metric_list;
+       const char *pmu;
        const char *metric_name;
        const char *modifier;
        int *ret;
        const struct pmu_metrics_table *table;
 };
 
-static bool metricgroup__find_metric(const char *metric,
+static bool metricgroup__find_metric(const char *pmu,
+                                    const char *metric,
                                     const struct pmu_metrics_table *table,
                                     struct pmu_metric *pm);
 
  * resolve_metric - Locate metrics within the root metric and recursively add
  *                    references to them.
  * @metric_list: The list the metric is added to.
+ * @pmu: The PMU name to resolve metrics on, or "all" for all PMUs.
  * @modifier: if non-null event modifiers like "u".
  * @metric_no_group: Should events written to events be grouped "{}" or
  *                   global. Grouping is the default but due to multiplexing the
  *       architecture perf is running upon.
  */
 static int resolve_metric(struct list_head *metric_list,
+                         const char *pmu,
                          const char *modifier,
                          bool metric_no_group,
                          bool metric_no_threshold,
        hashmap__for_each_entry(root_metric->pctx->ids, cur, bkt) {
                struct pmu_metric pm;
 
-               if (metricgroup__find_metric(cur->pkey, table, &pm)) {
+               if (metricgroup__find_metric(pmu, cur->pkey, table, &pm)) {
                        pending = realloc(pending,
                                        (pending_cnt + 1) * sizeof(struct to_resolve));
                        if (!pending)
        }
        if (!ret) {
                /* Resolve referenced metrics. */
-               ret = resolve_metric(metric_list, modifier, metric_no_group,
+               const char *pmu = pm->pmu ?: "cpu";
+
+               ret = resolve_metric(metric_list, pmu, modifier, metric_no_group,
                                     metric_no_threshold, user_requested_cpu_list,
-                                    system_wide, root_metric, &visited_node, table);
+                                    system_wide, root_metric, &visited_node,
+                                    table);
        }
        if (ret) {
                if (is_root)
 }
 
 struct metricgroup__find_metric_data {
+       const char *pmu;
        const char *metric;
        struct pmu_metric *pm;
 };
                                             void *vdata)
 {
        struct metricgroup__find_metric_data *data = vdata;
+       const char *pm_pmu = pm->pmu ?: "cpu";
+
+       if (strcmp(data->pmu, "all") && strcmp(pm_pmu, data->pmu))
+               return 0;
 
        if (!match_metric(pm->metric_name, data->metric))
                return 0;
        return 1;
 }
 
-static bool metricgroup__find_metric(const char *metric,
+static bool metricgroup__find_metric(const char *pmu,
+                                    const char *metric,
                                     const struct pmu_metrics_table *table,
                                     struct pmu_metric *pm)
 {
        struct metricgroup__find_metric_data data = {
+               .pmu = pmu,
                .metric = metric,
                .pm = pm,
        };
        struct metricgroup_add_iter_data *d = data;
        int ret;
 
-       if (!match_pm_metric(pm, d->metric_name))
+       if (!match_pm_metric(pm, d->pmu, d->metric_name))
                return 0;
 
        ret = add_metric(d->metric_list, pm, d->modifier, d->metric_no_group,
 
 struct metricgroup__add_metric_data {
        struct list_head *list;
+       const char *pmu;
        const char *metric_name;
        const char *modifier;
        const char *user_requested_cpu_list;
        struct metricgroup__add_metric_data *data = vdata;
        int ret = 0;
 
-       if (pm->metric_expr && match_pm_metric(pm, data->metric_name)) {
+       if (pm->metric_expr && match_pm_metric(pm, data->pmu, data->metric_name)) {
                bool metric_no_group = data->metric_no_group ||
                        match_metric(data->metric_name, pm->metricgroup_no_group);
 
 
 /**
  * metricgroup__add_metric - Find and add a metric, or a metric group.
+ * @pmu: The PMU name to search for metrics on, or "all" for all PMUs.
  * @metric_name: The name of the metric or metric group. For example, "IPC"
  *               could be the name of a metric and "TopDownL1" the name of a
  *               metric group.
  * @table: The table that is searched for metrics, most commonly the table for the
  *       architecture perf is running upon.
  */
-static int metricgroup__add_metric(const char *metric_name, const char *modifier,
+static int metricgroup__add_metric(const char *pmu, const char *metric_name, const char *modifier,
                                   bool metric_no_group, bool metric_no_threshold,
                                   const char *user_requested_cpu_list,
                                   bool system_wide,
        {
                struct metricgroup__add_metric_data data = {
                        .list = &list,
+                       .pmu = pmu,
                        .metric_name = metric_name,
                        .modifier = modifier,
                        .metric_no_group = metric_no_group,
                        .fn = metricgroup__add_metric_sys_event_iter,
                        .data = (void *) &(struct metricgroup_add_iter_data) {
                                .metric_list = &list,
+                               .pmu = pmu,
                                .metric_name = metric_name,
                                .modifier = modifier,
                                .metric_no_group = metric_no_group,
 /**
  * metricgroup__add_metric_list - Find and add metrics, or metric groups,
  *                                specified in a list.
+ * @pmu: A pmu to restrict the metrics to, or "all" for all PMUS.
  * @list: the list of metrics or metric groups. For example, "IPC,CPI,TopDownL1"
  *        would match the IPC and CPI metrics, and TopDownL1 would match all
  *        the metrics in the TopDownL1 group.
  * @table: The table that is searched for metrics, most commonly the table for the
  *       architecture perf is running upon.
  */
-static int metricgroup__add_metric_list(const char *list, bool metric_no_group,
+static int metricgroup__add_metric_list(const char *pmu, const char *list,
+                                       bool metric_no_group,
                                        bool metric_no_threshold,
                                        const char *user_requested_cpu_list,
                                        bool system_wide, struct list_head *metric_list,
                if (modifier)
                        *modifier++ = '\0';
 
-               ret = metricgroup__add_metric(metric_name, modifier,
+               ret = metricgroup__add_metric(pmu, metric_name, modifier,
                                              metric_no_group, metric_no_threshold,
                                              user_requested_cpu_list,
                                              system_wide, metric_list, table);
        return ret;
 }
 
-static int parse_groups(struct evlist *perf_evlist, const char *str,
+static int parse_groups(struct evlist *perf_evlist,
+                       const char *pmu, const char *str,
                        bool metric_no_group,
                        bool metric_no_merge,
                        bool metric_no_threshold,
 
        if (metric_events_list->nr_entries == 0)
                metricgroup__rblist_init(metric_events_list);
-       ret = metricgroup__add_metric_list(str, metric_no_group, metric_no_threshold,
+       ret = metricgroup__add_metric_list(pmu, str, metric_no_group, metric_no_threshold,
                                           user_requested_cpu_list,
                                           system_wide, &metric_list, table);
        if (ret)
                                            strcmp(m->modifier, n->modifier)))
                                        continue;
 
+                               if ((!m->pmu && n->pmu) ||
+                                   (m->pmu && !n->pmu) ||
+                                   (m->pmu && n->pmu && strcmp(m->pmu, n->pmu)))
+                                       continue;
+
                                if (expr__subset_of_ids(n->pctx, m->pctx)) {
                                        pr_debug("Events in '%s' fully contained within '%s'\n",
                                                 m->metric_name, n->metric_name);
 
                        metric_evlist = m->evlist;
                }
-               ret = setup_metric_events(m->pctx->ids, metric_evlist, &metric_events);
+               ret = setup_metric_events(fake_pmu ? "all" : m->pmu, m->pctx->ids,
+                                         metric_evlist, &metric_events);
                if (ret) {
                        pr_debug("Cannot resolve IDs for %s: %s\n",
                                m->metric_name, m->metric_expr);
        if (!table)
                return -EINVAL;
 
-       return parse_groups(perf_evlist, str, metric_no_group, metric_no_merge,
+       return parse_groups(perf_evlist, "all", str, metric_no_group, metric_no_merge,
                            metric_no_threshold, user_requested_cpu_list, system_wide,
                            /*fake_pmu=*/NULL, metric_events, table);
 }
                                   const char *str,
                                   struct rblist *metric_events)
 {
-       return parse_groups(evlist, str,
+       return parse_groups(evlist, "all", str,
                            /*metric_no_group=*/false,
                            /*metric_no_merge=*/false,
                            /*metric_no_threshold=*/false,
                            &perf_pmu__fake, metric_events, table);
 }
 
+struct metricgroup__has_metric_data {
+       const char *pmu;
+       const char *metric;
+};
 static int metricgroup__has_metric_callback(const struct pmu_metric *pm,
                                            const struct pmu_metrics_table *table __maybe_unused,
                                            void *vdata)
 {
-       const char *metric = vdata;
-
-       if (match_metric(pm->metric_name, metric) ||
-           match_metric(pm->metric_group, metric))
-               return 1;
+       struct metricgroup__has_metric_data *data = vdata;
 
-       return 0;
+       return match_pm_metric(pm, data->pmu, data->metric) ? 1 : 0;
 }
 
-bool metricgroup__has_metric(const char *metric)
+bool metricgroup__has_metric(const char *pmu, const char *metric)
 {
        const struct pmu_metrics_table *table = pmu_metrics_table__find();
+       struct metricgroup__has_metric_data data = {
+               .pmu = pmu,
+               .metric = metric,
+       };
 
        if (!table)
                return false;
 
-       return pmu_metrics_table_for_each_metric(table, metricgroup__has_metric_callback,
-                                               (void *)metric) ? true : false;
+       return pmu_metrics_table_for_each_metric(table, metricgroup__has_metric_callback, &data)
+               ? true : false;
 }
 
 static int metricgroup__topdown_max_level_callback(const struct pmu_metric *pm,