Skip to content

Commit

Permalink
Merge branch 'for-6.5/cxl-perf' into for-6.5/cxl
Browse files Browse the repository at this point in the history
Pick up initial support for the CXL 3.0 performance monitoring
definition. Small conflicts with the firmware update work as they both
placed their init code in the same location.
  • Loading branch information
djbw committed Jun 26, 2023
2 parents e2c18eb + c2b34d4 commit d2f9fe6
Show file tree
Hide file tree
Showing 19 changed files with 1,307 additions and 7 deletions.
68 changes: 68 additions & 0 deletions Documentation/admin-guide/perf/cxl.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
.. SPDX-License-Identifier: GPL-2.0
======================================
CXL Performance Monitoring Unit (CPMU)
======================================

The CXL rev 3.0 specification provides a definition of CXL Performance
Monitoring Unit in section 13.2: Performance Monitoring.

CXL components (e.g. Root Port, Switch Upstream Port, End Point) may have
any number of CPMU instances. CPMU capabilities are fully discoverable from
the devices. The specification provides event definitions for all CXL protocol
message types and a set of additional events for things commonly counted on
CXL devices (e.g. DRAM events).

CPMU driver
===========

The CPMU driver registers a perf PMU with the name pmu_mem<X>.<Y> on the CXL bus
representing the Yth CPMU for memX.

/sys/bus/cxl/device/pmu_mem<X>.<Y>

The associated PMU is registered as

/sys/bus/event_sources/devices/cxl_pmu_mem<X>.<Y>

In common with other CXL bus devices, the id has no specific meaning and the
relationship to specific CXL device should be established via the device parent
of the device on the CXL bus.

PMU driver provides description of available events and filter options in sysfs.

The "format" directory describes all formats of the config (event vendor id,
group id and mask) config1 (threshold, filter enables) and config2 (filter
parameters) fields of the perf_event_attr structure. The "events" directory
describes all documented events show in perf list.

The events shown in perf list are the most fine grained events with a single
bit of the event mask set. More general events may be enable by setting
multiple mask bits in config. For example, all Device to Host Read Requests
may be captured on a single counter by setting the bits for all of

* d2h_req_rdcurr
* d2h_req_rdown
* d2h_req_rdshared
* d2h_req_rdany
* d2h_req_rdownnodata

Example of usage::

$#perf list
cxl_pmu_mem0.0/clock_ticks/ [Kernel PMU event]
cxl_pmu_mem0.0/d2h_req_rdshared/ [Kernel PMU event]
cxl_pmu_mem0.0/h2d_req_snpcur/ [Kernel PMU event]
cxl_pmu_mem0.0/h2d_req_snpdata/ [Kernel PMU event]
cxl_pmu_mem0.0/h2d_req_snpinv/ [Kernel PMU event]
-----------------------------------------------------------

$# perf stat -a -e cxl_pmu_mem0.0/clock_ticks/ -e cxl_pmu_mem0.0/d2h_req_rdshared/

Vendor specific events may also be available and if so can be used via

$# perf stat -a -e cxl_pmu_mem0.0/vid=VID,gid=GID,mask=MASK/

The driver does not support sampling so "perf record" is unsupported.
It only supports system-wide counting so attaching to a task is
unsupported.
1 change: 1 addition & 0 deletions Documentation/admin-guide/perf/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ Performance monitor support
alibaba_pmu
nvidia-pmu
meson-ddr-pmu
cxl
7 changes: 7 additions & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -5194,6 +5194,13 @@ S: Maintained
F: drivers/cxl/
F: include/uapi/linux/cxl_mem.h

COMPUTE EXPRESS LINK PMU (CPMU)
M: Jonathan Cameron <jonathan.cameron@huawei.com>
L: linux-cxl@vger.kernel.org
S: Maintained
F: Documentation/admin-guide/perf/cxl.rst
F: drivers/perf/cxl_pmu.c

CONEXANT ACCESSRUNNER USB DRIVER
L: accessrunner-general@lists.sourceforge.net
S: Orphan
Expand Down
13 changes: 13 additions & 0 deletions drivers/cxl/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,17 @@ config CXL_REGION_INVALIDATION_TEST
If unsure, or if this kernel is meant for production environments,
say N.

config CXL_PMU
tristate "CXL Performance Monitoring Unit"
default CXL_BUS
depends on PERF_EVENTS
help
Support performance monitoring as defined in CXL rev 3.0
section 13.2: Performance Monitoring. CXL components may have
one or more CXL Performance Monitoring Units (CPMUs).

Say 'y/m' to enable a driver that will attach to performance
monitoring units and provide standard perf based interfaces.

If unsure say 'm'.
endif
1 change: 1 addition & 0 deletions drivers/cxl/core/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ cxl_core-y += memdev.o
cxl_core-y += mbox.o
cxl_core-y += pci.o
cxl_core-y += hdm.o
cxl_core-y += pmu.o
cxl_core-$(CONFIG_TRACING) += trace.o
cxl_core-$(CONFIG_CXL_REGION) += region.o
1 change: 1 addition & 0 deletions drivers/cxl/core/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

extern const struct device_type cxl_nvdimm_bridge_type;
extern const struct device_type cxl_nvdimm_type;
extern const struct device_type cxl_pmu_type;

extern struct attribute_group cxl_base_attribute_group;

Expand Down
68 changes: 68 additions & 0 deletions drivers/cxl/core/pmu.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2023 Huawei. All rights reserved. */

#include <linux/device.h>
#include <linux/slab.h>
#include <linux/idr.h>
#include <cxlmem.h>
#include <pmu.h>
#include <cxl.h>
#include "core.h"

static void cxl_pmu_release(struct device *dev)
{
struct cxl_pmu *pmu = to_cxl_pmu(dev);

kfree(pmu);
}

const struct device_type cxl_pmu_type = {
.name = "cxl_pmu",
.release = cxl_pmu_release,
};

static void remove_dev(void *dev)
{
device_del(dev);
}

int devm_cxl_pmu_add(struct device *parent, struct cxl_pmu_regs *regs,
int assoc_id, int index, enum cxl_pmu_type type)
{
struct cxl_pmu *pmu;
struct device *dev;
int rc;

pmu = kzalloc(sizeof(*pmu), GFP_KERNEL);
if (!pmu)
return -ENOMEM;

pmu->assoc_id = assoc_id;
pmu->index = index;
pmu->type = type;
pmu->base = regs->pmu;
dev = &pmu->dev;
device_initialize(dev);
device_set_pm_not_required(dev);
dev->parent = parent;
dev->bus = &cxl_bus_type;
dev->type = &cxl_pmu_type;
switch (pmu->type) {
case CXL_PMU_MEMDEV:
rc = dev_set_name(dev, "pmu_mem%d.%d", assoc_id, index);
break;
}
if (rc)
goto err;

rc = device_add(dev);
if (rc)
goto err;

return devm_add_action_or_reset(parent, remove_dev, dev);

err:
put_device(&pmu->dev);
return rc;
}
EXPORT_SYMBOL_NS_GPL(devm_cxl_pmu_add, CXL);
2 changes: 2 additions & 0 deletions drivers/cxl/core/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ static int cxl_device_id(const struct device *dev)
return CXL_DEVICE_MEMORY_EXPANDER;
if (dev->type == CXL_REGION_TYPE())
return CXL_DEVICE_REGION;
if (dev->type == &cxl_pmu_type)
return CXL_DEVICE_PMU;
return 0;
}

Expand Down
75 changes: 69 additions & 6 deletions drivers/cxl/core/regs.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <linux/pci.h>
#include <cxlmem.h>
#include <cxlpci.h>
#include <pmu.h>

#include "core.h"

Expand Down Expand Up @@ -286,20 +287,23 @@ static bool cxl_decode_regblock(struct pci_dev *pdev, u32 reg_lo, u32 reg_hi,
}

/**
* cxl_find_regblock() - Locate register blocks by type
* cxl_find_regblock_instance() - Locate a register block by type / index
* @pdev: The CXL PCI device to enumerate.
* @type: Register Block Indicator id
* @map: Enumeration output, clobbered on error
* @index: Index into which particular instance of a regblock wanted in the
* order found in register locator DVSEC.
*
* Return: 0 if register block enumerated, negative error code otherwise
*
* A CXL DVSEC may point to one or more register blocks, search for them
* by @type.
* by @type and @index.
*/
int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
struct cxl_register_map *map)
int cxl_find_regblock_instance(struct pci_dev *pdev, enum cxl_regloc_type type,
struct cxl_register_map *map, int index)
{
u32 regloc_size, regblocks;
int instance = 0;
int regloc, i;

map->resource = CXL_RESOURCE_NONE;
Expand All @@ -323,15 +327,74 @@ int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
if (!cxl_decode_regblock(pdev, reg_lo, reg_hi, map))
continue;

if (map->reg_type == type)
return 0;
if (map->reg_type == type) {
if (index == instance)
return 0;
instance++;
}
}

map->resource = CXL_RESOURCE_NONE;
return -ENODEV;
}
EXPORT_SYMBOL_NS_GPL(cxl_find_regblock_instance, CXL);

/**
* cxl_find_regblock() - Locate register blocks by type
* @pdev: The CXL PCI device to enumerate.
* @type: Register Block Indicator id
* @map: Enumeration output, clobbered on error
*
* Return: 0 if register block enumerated, negative error code otherwise
*
* A CXL DVSEC may point to one or more register blocks, search for them
* by @type.
*/
int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
struct cxl_register_map *map)
{
return cxl_find_regblock_instance(pdev, type, map, 0);
}
EXPORT_SYMBOL_NS_GPL(cxl_find_regblock, CXL);

/**
* cxl_count_regblock() - Count instances of a given regblock type.
* @pdev: The CXL PCI device to enumerate.
* @type: Register Block Indicator id
*
* Some regblocks may be repeated. Count how many instances.
*
* Return: count of matching regblocks.
*/
int cxl_count_regblock(struct pci_dev *pdev, enum cxl_regloc_type type)
{
struct cxl_register_map map;
int rc, count = 0;

while (1) {
rc = cxl_find_regblock_instance(pdev, type, &map, count);
if (rc)
return count;
count++;
}
}
EXPORT_SYMBOL_NS_GPL(cxl_count_regblock, CXL);

int cxl_map_pmu_regs(struct pci_dev *pdev, struct cxl_pmu_regs *regs,
struct cxl_register_map *map)
{
struct device *dev = &pdev->dev;
resource_size_t phys_addr;

phys_addr = map->resource;
regs->pmu = devm_cxl_iomap_block(dev, phys_addr, CXL_PMU_REGMAP_SIZE);
if (!regs->pmu)
return -ENOMEM;

return 0;
}
EXPORT_SYMBOL_NS_GPL(cxl_map_pmu_regs, CXL);

resource_size_t cxl_rcrb_to_component(struct device *dev,
resource_size_t rcrb,
enum cxl_rcrb which)
Expand Down
16 changes: 16 additions & 0 deletions drivers/cxl/cxl.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,10 @@ struct cxl_regs {
struct_group_tagged(cxl_device_regs, device_regs,
void __iomem *status, *mbox, *memdev;
);

struct_group_tagged(cxl_pmu_regs, pmu_regs,
void __iomem *pmu;
);
};

struct cxl_reg_map {
Expand All @@ -237,6 +241,10 @@ struct cxl_device_reg_map {
struct cxl_reg_map memdev;
};

struct cxl_pmu_reg_map {
struct cxl_reg_map pmu;
};

/**
* struct cxl_register_map - DVSEC harvested register block mapping parameters
* @base: virtual base of the register-block-BAR + @block_offset
Expand All @@ -245,6 +253,7 @@ struct cxl_device_reg_map {
* @reg_type: see enum cxl_regloc_type
* @component_map: cxl_reg_map for component registers
* @device_map: cxl_reg_maps for device registers
* @pmu_map: cxl_reg_maps for CXL Performance Monitoring Units
*/
struct cxl_register_map {
void __iomem *base;
Expand All @@ -254,6 +263,7 @@ struct cxl_register_map {
union {
struct cxl_component_reg_map component_map;
struct cxl_device_reg_map device_map;
struct cxl_pmu_reg_map pmu_map;
};
};

Expand All @@ -266,8 +276,13 @@ int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
unsigned long map_mask);
int cxl_map_device_regs(struct device *dev, struct cxl_device_regs *regs,
const struct cxl_register_map *map);
int cxl_map_pmu_regs(struct pci_dev *pdev, struct cxl_pmu_regs *regs,
struct cxl_register_map *map);

enum cxl_regloc_type;
int cxl_count_regblock(struct pci_dev *pdev, enum cxl_regloc_type type);
int cxl_find_regblock_instance(struct pci_dev *pdev, enum cxl_regloc_type type,
struct cxl_register_map *map, int index);
int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
struct cxl_register_map *map);

Expand Down Expand Up @@ -759,6 +774,7 @@ void cxl_driver_unregister(struct cxl_driver *cxl_drv);
#define CXL_DEVICE_REGION 6
#define CXL_DEVICE_PMEM_REGION 7
#define CXL_DEVICE_DAX_REGION 8
#define CXL_DEVICE_PMU 9

#define MODULE_ALIAS_CXL(type) MODULE_ALIAS("cxl:t" __stringify(type) "*")
#define CXL_MODALIAS_FMT "cxl:t%d"
Expand Down
1 change: 1 addition & 0 deletions drivers/cxl/cxlpci.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ enum cxl_regloc_type {
CXL_REGLOC_RBI_COMPONENT,
CXL_REGLOC_RBI_VIRT,
CXL_REGLOC_RBI_MEMDEV,
CXL_REGLOC_RBI_PMU,
CXL_REGLOC_RBI_TYPES
};

Expand Down
Loading

0 comments on commit d2f9fe6

Please sign in to comment.