Commit 7232ab8b authored by Kan Liang's avatar Kan Liang Committed by Joerg Roedel

iommu/vt-d: Add IOMMU perfmon support

Implement the IOMMU performance monitor capability, which supports the
collection of information about key events occurring during operation of
the remapping hardware, to aid performance tuning and debug.

The IOMMU perfmon support is implemented as part of the IOMMU driver and
interfaces with the Linux perf subsystem.

The IOMMU PMU has the following unique features compared with the other
PMUs.
- Support counting. Not support sampling.
- Does not support per-thread counting. The scope is system-wide.
- Support per-counter capability register. The event constraints can be
  enumerated.
- The available event and event group can also be enumerated.
- Extra Enhanced Commands are introduced to control the counters.

Add a new variable, struct iommu_pmu *pmu, to in the struct intel_iommu
to track the PMU related information.

Add iommu_pmu_register() and iommu_pmu_unregister() to register and
unregister a IOMMU PMU. The register function setup the IOMMU PMU ops
and invoke the standard perf_pmu_register() interface to register a PMU
in the perf subsystem. This patch only exposes the functions. The
following patch will enable them in the IOMMU driver.

The IOMMU PMUs can be found under /sys/bus/event_source/devices/dmar*

The available filters and event format can be found at the format folder

 $ ls /sys/bus/event_source/devices/dmar1/format/
 event  event_group  filter_ats  filter_ats_en  filter_page_table
 filter_page_table_en

The supported events can be found at the events folder

 $ ls /sys/bus/event_source/devices/dmar1/events/
 ats_blocked        fs_nonleaf_hit           int_cache_hit_posted
 iommu_mem_blocked  iotlb_hit        pasid_cache_lookup  ss_nonleaf_hit
 ctxt_cache_hit     fs_nonleaf_lookup        int_cache_lookup
 iommu_mrds         iotlb_lookup     pg_req_posted    ss_nonleaf_lookup
 ctxt_cache_lookup  int_cache_hit_nonposted  iommu_clocks
 iommu_requests     pasid_cache_hit  pw_occupancy

The command below illustrates filter usage with a simple example.

 $ perf stat -e dmar1/iommu_requests,filter_ats_en=0x1,filter_ats=0x1/
   -a sleep 1

 Performance counter stats for 'system wide':

   368,947      dmar1/iommu_requests,filter_ats_en=0x1,filter_ats=0x1/

 1.002592074 seconds time elapsed
Signed-off-by: default avatarKan Liang <kan.liang@linux.intel.com>
Link: https://lore.kernel.org/r/20230128200428.1459118-5-kan.liang@linux.intel.comSigned-off-by: default avatarLu Baolu <baolu.lu@linux.intel.com>
Signed-off-by: default avatarJoerg Roedel <jroedel@suse.de>
parent dc578758
What: /sys/bus/event_source/devices/dmar*/format
Date: Jan 2023
KernelVersion: 6.3
Contact: Kan Liang <kan.liang@linux.intel.com>
Description: Read-only. Attribute group to describe the magic bits
that go into perf_event_attr.config,
perf_event_attr.config1 or perf_event_attr.config2 for
the IOMMU pmu. (See also
ABI/testing/sysfs-bus-event_source-devices-format).
Each attribute in this group defines a bit range in
perf_event_attr.config, perf_event_attr.config1,
or perf_event_attr.config2. All supported attributes
are listed below (See the VT-d Spec 4.0 for possible
attribute values)::
event = "config:0-27" - event ID
event_group = "config:28-31" - event group ID
filter_requester_en = "config1:0" - Enable Requester ID filter
filter_domain_en = "config1:1" - Enable Domain ID filter
filter_pasid_en = "config1:2" - Enable PASID filter
filter_ats_en = "config1:3" - Enable Address Type filter
filter_page_table_en= "config1:4" - Enable Page Table Level filter
filter_requester_id = "config1:16-31" - Requester ID filter
filter_domain = "config1:32-47" - Domain ID filter
filter_pasid = "config2:0-21" - PASID filter
filter_ats = "config2:24-28" - Address Type filter
filter_page_table = "config2:32-36" - Page Table Level filter
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/ioasid.h> #include <linux/ioasid.h>
#include <linux/bitfield.h> #include <linux/bitfield.h>
#include <linux/xarray.h> #include <linux/xarray.h>
#include <linux/perf_event.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <asm/iommu.h> #include <asm/iommu.h>
...@@ -606,6 +607,16 @@ struct dmar_domain { ...@@ -606,6 +607,16 @@ struct dmar_domain {
iommu core */ iommu core */
}; };
/*
* In theory, the VT-d 4.0 spec can support up to 2 ^ 16 counters.
* But in practice, there are only 14 counters for the existing
* platform. Setting the max number of counters to 64 should be good
* enough for a long time. Also, supporting more than 64 counters
* requires more extras, e.g., extra freeze and overflow registers,
* which is not necessary for now.
*/
#define IOMMU_PMU_IDX_MAX 64
struct iommu_pmu { struct iommu_pmu {
struct intel_iommu *iommu; struct intel_iommu *iommu;
u32 num_cntr; /* Number of counters */ u32 num_cntr; /* Number of counters */
...@@ -620,6 +631,10 @@ struct iommu_pmu { ...@@ -620,6 +631,10 @@ struct iommu_pmu {
u64 *evcap; /* Indicates all supported events */ u64 *evcap; /* Indicates all supported events */
u32 **cntr_evcap; /* Supported events of each counter. */ u32 **cntr_evcap; /* Supported events of each counter. */
struct pmu pmu;
DECLARE_BITMAP(used_mask, IOMMU_PMU_IDX_MAX);
struct perf_event *event_list[IOMMU_PMU_IDX_MAX];
}; };
struct intel_iommu { struct intel_iommu {
......
This diff is collapsed.
...@@ -7,6 +7,14 @@ ...@@ -7,6 +7,14 @@
#define IOMMU_PMU_NUM_OFF_REGS 4 #define IOMMU_PMU_NUM_OFF_REGS 4
#define IOMMU_PMU_OFF_REGS_STEP 4 #define IOMMU_PMU_OFF_REGS_STEP 4
#define IOMMU_PMU_FILTER_REQUESTER_ID 0x01
#define IOMMU_PMU_FILTER_DOMAIN 0x02
#define IOMMU_PMU_FILTER_PASID 0x04
#define IOMMU_PMU_FILTER_ATS 0x08
#define IOMMU_PMU_FILTER_PAGE_TABLE 0x10
#define IOMMU_PMU_FILTER_EN BIT(31)
#define IOMMU_PMU_CFG_OFFSET 0x100 #define IOMMU_PMU_CFG_OFFSET 0x100
#define IOMMU_PMU_CFG_CNTRCAP_OFFSET 0x80 #define IOMMU_PMU_CFG_CNTRCAP_OFFSET 0x80
#define IOMMU_PMU_CFG_CNTREVCAP_OFFSET 0x84 #define IOMMU_PMU_CFG_CNTREVCAP_OFFSET 0x84
...@@ -20,12 +28,18 @@ ...@@ -20,12 +28,18 @@
#define iommu_cntrcap_ios(p) (((p) >> 16) & 0x1) #define iommu_cntrcap_ios(p) (((p) >> 16) & 0x1)
#define iommu_cntrcap_egcnt(p) (((p) >> 28) & 0xf) #define iommu_cntrcap_egcnt(p) (((p) >> 28) & 0xf)
#define IOMMU_EVENT_CFG_EGI_SHIFT 8
#define IOMMU_EVENT_CFG_ES_SHIFT 32
#define IOMMU_EVENT_CFG_INT BIT_ULL(1)
#define iommu_event_select(p) ((p) & 0xfffffff) #define iommu_event_select(p) ((p) & 0xfffffff)
#define iommu_event_group(p) (((p) >> 28) & 0xf) #define iommu_event_group(p) (((p) >> 28) & 0xf)
#ifdef CONFIG_INTEL_IOMMU_PERF_EVENTS #ifdef CONFIG_INTEL_IOMMU_PERF_EVENTS
int alloc_iommu_pmu(struct intel_iommu *iommu); int alloc_iommu_pmu(struct intel_iommu *iommu);
void free_iommu_pmu(struct intel_iommu *iommu); void free_iommu_pmu(struct intel_iommu *iommu);
void iommu_pmu_register(struct intel_iommu *iommu);
void iommu_pmu_unregister(struct intel_iommu *iommu);
#else #else
static inline int static inline int
alloc_iommu_pmu(struct intel_iommu *iommu) alloc_iommu_pmu(struct intel_iommu *iommu)
...@@ -37,4 +51,14 @@ static inline void ...@@ -37,4 +51,14 @@ static inline void
free_iommu_pmu(struct intel_iommu *iommu) free_iommu_pmu(struct intel_iommu *iommu)
{ {
} }
static inline void
iommu_pmu_register(struct intel_iommu *iommu)
{
}
static inline void
iommu_pmu_unregister(struct intel_iommu *iommu)
{
}
#endif /* CONFIG_INTEL_IOMMU_PERF_EVENTS */ #endif /* CONFIG_INTEL_IOMMU_PERF_EVENTS */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment