Commit 4cfce57f authored by Alexander Antonov's avatar Alexander Antonov Committed by Peter Zijlstra
Browse files

perf/x86/intel/uncore: Enable UPI topology discovery for Skylake Server



UPI topology discovery relies on data from KTILP0 (offset 0x94) and
KTIPCSTS (offset 0x120) registers which reside under IIO bus(3) on
SKX/CLX.

This patch enable UPI topology discovery on Skylake Server. Topology is
exposed through attributes /sys/devices/uncore_upi_<pmu_idx>/dieX,
where dieX is file which holds "upi_<idx1>:die_<idx2>" connected to
this UPI link.

Signed-off-by: default avatarAlexander Antonov <alexander.antonov@linux.intel.com>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: default avatarKan Liang <kan.liang@linux.intel.com>
Link: https://lore.kernel.org/r/20221117122833.3103580-7-alexander.antonov@linux.intel.com
parent 07813e2a
Loading
Loading
Loading
Loading
+130 −0
Original line number Diff line number Diff line
@@ -4253,6 +4253,132 @@ static struct intel_uncore_ops skx_upi_uncore_pci_ops = {
	.read_counter	= snbep_uncore_pci_read_counter,
};

static umode_t
skx_upi_mapping_visible(struct kobject *kobj, struct attribute *attr, int die)
{
	struct intel_uncore_pmu *pmu = dev_to_uncore_pmu(kobj_to_dev(kobj));

	return pmu->type->topology[die][pmu->pmu_idx].upi->enabled ? attr->mode : 0;
}

static ssize_t skx_upi_mapping_show(struct device *dev,
				    struct device_attribute *attr, char *buf)
{
	struct intel_uncore_pmu *pmu = dev_to_uncore_pmu(dev);
	struct dev_ext_attribute *ea = to_dev_ext_attribute(attr);
	long die = (long)ea->var;
	struct uncore_upi_topology *upi = pmu->type->topology[die][pmu->pmu_idx].upi;

	return sysfs_emit(buf, "upi_%d,die_%d\n", upi->pmu_idx_to, upi->die_to);
}

#define SKX_UPI_REG_DID			0x2058
#define SKX_UPI_REGS_ADDR_DEVICE_LINK0	0x0e
#define SKX_UPI_REGS_ADDR_FUNCTION	0x00

/*
 * UPI Link Parameter 0
 * |  Bit  |  Default  |  Description
 * | 19:16 |     0h    | base_nodeid - The NodeID of the sending socket.
 * | 12:8  |    00h    | sending_port - The processor die port number of the sending port.
 */
#define SKX_KTILP0_OFFSET	0x94

/*
 * UPI Pcode Status. This register is used by PCode to store the link training status.
 * |  Bit  |  Default  |  Description
 * |   4   |     0h    | ll_status_valid — Bit indicates the valid training status
 *                       logged from PCode to the BIOS.
 */
#define SKX_KTIPCSTS_OFFSET	0x120

static int upi_fill_topology(struct pci_dev *dev, struct intel_uncore_topology *tp,
			     int pmu_idx)
{
	int ret;
	u32 upi_conf;
	struct uncore_upi_topology *upi = tp->upi;

	tp->pmu_idx = pmu_idx;
	ret = pci_read_config_dword(dev, SKX_KTIPCSTS_OFFSET, &upi_conf);
	if (ret) {
		ret = pcibios_err_to_errno(ret);
		goto err;
	}
	upi->enabled = (upi_conf >> 4) & 1;
	if (upi->enabled) {
		ret = pci_read_config_dword(dev, SKX_KTILP0_OFFSET,
					    &upi_conf);
		if (ret) {
			ret = pcibios_err_to_errno(ret);
			goto err;
		}
		upi->die_to = (upi_conf >> 16) & 0xf;
		upi->pmu_idx_to = (upi_conf >> 8) & 0x1f;
	}
err:
	return ret;
}

static int skx_upi_topology_cb(struct intel_uncore_type *type, int segment,
				int die, u64 cpu_bus_msr)
{
	int idx, ret;
	struct intel_uncore_topology *upi;
	unsigned int devfn;
	struct pci_dev *dev = NULL;
	u8 bus = cpu_bus_msr >> (3 * BUS_NUM_STRIDE);

	for (idx = 0; idx < type->num_boxes; idx++) {
		upi = &type->topology[die][idx];
		devfn = PCI_DEVFN(SKX_UPI_REGS_ADDR_DEVICE_LINK0 + idx,
				  SKX_UPI_REGS_ADDR_FUNCTION);
		dev = pci_get_domain_bus_and_slot(segment, bus, devfn);
		if (dev) {
			ret = upi_fill_topology(dev, upi, idx);
			if (ret)
				break;
		}
	}

	pci_dev_put(dev);
	return ret;
}

static int skx_upi_get_topology(struct intel_uncore_type *type)
{
	/* CPX case is not supported */
	if (boot_cpu_data.x86_stepping == 11)
		return -EPERM;

	return skx_pmu_get_topology(type, skx_upi_topology_cb);
}

static struct attribute_group skx_upi_mapping_group = {
	.is_visible	= skx_upi_mapping_visible,
};

static const struct attribute_group *skx_upi_attr_update[] = {
	&skx_upi_mapping_group,
	NULL
};

static int
pmu_upi_set_mapping(struct intel_uncore_type *type, struct attribute_group *ag)
{
	return pmu_set_mapping(type, ag, skx_upi_mapping_show, UPI_TOPOLOGY_TYPE);
}

static int skx_upi_set_mapping(struct intel_uncore_type *type)
{
	return pmu_upi_set_mapping(type, &skx_upi_mapping_group);
}

static void skx_upi_cleanup_mapping(struct intel_uncore_type *type)
{
	pmu_cleanup_mapping(type, &skx_upi_mapping_group);
}

static struct intel_uncore_type skx_uncore_upi = {
	.name		= "upi",
	.num_counters   = 4,
@@ -4265,6 +4391,10 @@ static struct intel_uncore_type skx_uncore_upi = {
	.box_ctl	= SKX_UPI_PCI_PMON_BOX_CTL,
	.ops		= &skx_upi_uncore_pci_ops,
	.format_group	= &skx_upi_uncore_format_group,
	.attr_update	= skx_upi_attr_update,
	.get_topology	= skx_upi_get_topology,
	.set_mapping	= skx_upi_set_mapping,
	.cleanup_mapping = skx_upi_cleanup_mapping,
};

static void skx_m2m_uncore_pci_init_box(struct intel_uncore_box *box)