Commit 28a3ae4f authored by Alison Schofield's avatar Alison Schofield Committed by Dan Williams
Browse files

cxl/trace: Add an HPA to cxl_poison trace events



When a cxl_poison trace event is reported for a region, the poisoned
Device Physical Address (DPA) can be translated to a Host Physical
Address (HPA) for consumption by user space.

Translate and add the resulting HPA to the cxl_poison trace event.
Follow the device decode logic as defined in the CXL Spec 3.0 Section
8.2.4.19.13.

If no region currently maps the poison, assign ULLONG_MAX to the
cxl_poison event hpa field.

Signed-off-by: default avatarAlison Schofield <alison.schofield@intel.com>
Reviewed-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: default avatarDave Jiang <dave.jiang@intel.com>
Reviewed-by: default avatarIra Weiny <ira.weiny@intel.com>
Link: https://lore.kernel.org/r/6d3cd726f9042a59902785b0a2cb3ddfb70e0219.1681838292.git.alison.schofield@intel.com


Tested-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
parent f0832a58
Loading
Loading
Loading
Loading
+94 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2022 Intel Corporation. All rights reserved. */

#include <cxl.h>
#include "core.h"

#define CREATE_TRACE_POINTS
#include "trace.h"

static bool cxl_is_hpa_in_range(u64 hpa, struct cxl_region *cxlr, int pos)
{
	struct cxl_region_params *p = &cxlr->params;
	int gran = p->interleave_granularity;
	int ways = p->interleave_ways;
	u64 offset;

	/* Is the hpa within this region at all */
	if (hpa < p->res->start || hpa > p->res->end) {
		dev_dbg(&cxlr->dev,
			"Addr trans fail: hpa 0x%llx not in region\n", hpa);
		return false;
	}

	/* Is the hpa in an expected chunk for its pos(-ition) */
	offset = hpa - p->res->start;
	offset = do_div(offset, gran * ways);
	if ((offset >= pos * gran) && (offset < (pos + 1) * gran))
		return true;

	dev_dbg(&cxlr->dev,
		"Addr trans fail: hpa 0x%llx not in expected chunk\n", hpa);

	return false;
}

static u64 cxl_dpa_to_hpa(u64 dpa,  struct cxl_region *cxlr,
			  struct cxl_endpoint_decoder *cxled)
{
	u64 dpa_offset, hpa_offset, bits_upper, mask_upper, hpa;
	struct cxl_region_params *p = &cxlr->params;
	int pos = cxled->pos;
	u16 eig = 0;
	u8 eiw = 0;

	ways_to_eiw(p->interleave_ways, &eiw);
	granularity_to_eig(p->interleave_granularity, &eig);

	/*
	 * The device position in the region interleave set was removed
	 * from the offset at HPA->DPA translation. To reconstruct the
	 * HPA, place the 'pos' in the offset.
	 *
	 * The placement of 'pos' in the HPA is determined by interleave
	 * ways and granularity and is defined in the CXL Spec 3.0 Section
	 * 8.2.4.19.13 Implementation Note: Device Decode Logic
	 */

	/* Remove the dpa base */
	dpa_offset = dpa - cxl_dpa_resource_start(cxled);

	mask_upper = GENMASK_ULL(51, eig + 8);

	if (eiw < 8) {
		hpa_offset = (dpa_offset & mask_upper) << eiw;
		hpa_offset |= pos << (eig + 8);
	} else {
		bits_upper = (dpa_offset & mask_upper) >> (eig + 8);
		bits_upper = bits_upper * 3;
		hpa_offset = ((bits_upper << (eiw - 8)) + pos) << (eig + 8);
	}

	/* The lower bits remain unchanged */
	hpa_offset |= dpa_offset & GENMASK_ULL(eig + 7, 0);

	/* Apply the hpa_offset to the region base address */
	hpa = hpa_offset + p->res->start;

	if (!cxl_is_hpa_in_range(hpa, cxlr, cxled->pos))
		return ULLONG_MAX;

	return hpa;
}

u64 cxl_trace_hpa(struct cxl_region *cxlr, struct cxl_memdev *cxlmd,
		  u64 dpa)
{
	struct cxl_region_params *p = &cxlr->params;
	struct cxl_endpoint_decoder *cxled = NULL;

	for (int i = 0; i <  p->nr_targets; i++) {
		cxled = p->targets[i];
		if (cxlmd == cxled_to_memdev(cxled))
			break;
	}
	if (!cxled || cxlmd != cxled_to_memdev(cxled))
		return ULLONG_MAX;

	return cxl_dpa_to_hpa(dpa, cxlr, cxled);
}
+9 −2
Original line number Diff line number Diff line
@@ -636,6 +636,8 @@ TRACE_EVENT(cxl_memory_module,
#define cxl_poison_overflow(flags, time)				\
	(flags & CXL_POISON_FLAG_OVERFLOW ? le64_to_cpu(time) : 0)

u64 cxl_trace_hpa(struct cxl_region *cxlr, struct cxl_memdev *memdev, u64 dpa);

TRACE_EVENT(cxl_poison,

	TP_PROTO(struct cxl_memdev *cxlmd, struct cxl_region *region,
@@ -651,6 +653,7 @@ TRACE_EVENT(cxl_poison,
		__field(u8, trace_type)
		__string(region, region)
		__field(u64, overflow_ts)
		__field(u64, hpa)
		__field(u64, dpa)
		__field(u32, dpa_length)
		__array(char, uuid, 16)
@@ -671,21 +674,25 @@ TRACE_EVENT(cxl_poison,
		if (region) {
			__assign_str(region, dev_name(&region->dev));
			memcpy(__entry->uuid, &region->params.uuid, 16);
			__entry->hpa = cxl_trace_hpa(region, cxlmd,
						     __entry->dpa);
		} else {
			__assign_str(region, "");
			memset(__entry->uuid, 0, 16);
			__entry->hpa = ULLONG_MAX;
		}
	    ),

	TP_printk("memdev=%s host=%s serial=%lld trace_type=%s region=%s "  \
		"region_uuid=%pU dpa=0x%llx dpa_length=0x%x source=%s "     \
		"flags=%s overflow_time=%llu",
		"region_uuid=%pU hpa=0x%llx dpa=0x%llx dpa_length=0x%x "    \
		"source=%s flags=%s overflow_time=%llu",
		__get_str(memdev),
		__get_str(host),
		__entry->serial,
		show_poison_trace_type(__entry->trace_type),
		__get_str(region),
		__entry->uuid,
		__entry->hpa,
		__entry->dpa,
		__entry->dpa_length,
		show_poison_source(__entry->source),