Commit 660c0a86 authored by Dave Jiang's avatar Dave Jiang
Browse files

Merge remote-tracking branch 'cxl/for-6.10/dpa-to-hpa' into cxl-for-next

Support for HPA to DPA translation for CXL events cxl_dram and
cxl_general_media.
parents c26a55e5 6aec0013
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -27,7 +27,21 @@ void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled);
int cxl_region_init(void);
void cxl_region_exit(void);
int cxl_get_poison_by_endpoint(struct cxl_port *port);
struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa);
u64 cxl_trace_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
		  u64 dpa);

#else
static inline u64
cxl_trace_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd, u64 dpa)
{
	return ULLONG_MAX;
}
static inline
struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa)
{
	return NULL;
}
static inline int cxl_get_poison_by_endpoint(struct cxl_port *port)
{
	return 0;
+30 −6
Original line number Diff line number Diff line
@@ -854,14 +854,38 @@ void cxl_event_trace_record(const struct cxl_memdev *cxlmd,
			    enum cxl_event_type event_type,
			    const uuid_t *uuid, union cxl_event *evt)
{
	if (event_type == CXL_CPER_EVENT_GEN_MEDIA)
		trace_cxl_general_media(cxlmd, type, &evt->gen_media);
	else if (event_type == CXL_CPER_EVENT_DRAM)
		trace_cxl_dram(cxlmd, type, &evt->dram);
	else if (event_type == CXL_CPER_EVENT_MEM_MODULE)
	if (event_type == CXL_CPER_EVENT_MEM_MODULE) {
		trace_cxl_memory_module(cxlmd, type, &evt->mem_module);
	else
		return;
	}
	if (event_type == CXL_CPER_EVENT_GENERIC) {
		trace_cxl_generic_event(cxlmd, type, uuid, &evt->generic);
		return;
	}

	if (trace_cxl_general_media_enabled() || trace_cxl_dram_enabled()) {
		u64 dpa, hpa = ULLONG_MAX;
		struct cxl_region *cxlr;

		/*
		 * These trace points are annotated with HPA and region
		 * translations. Take topology mutation locks and lookup
		 * { HPA, REGION } from { DPA, MEMDEV } in the event record.
		 */
		guard(rwsem_read)(&cxl_region_rwsem);
		guard(rwsem_read)(&cxl_dpa_rwsem);

		dpa = le64_to_cpu(evt->common.phys_addr) & CXL_DPA_MASK;
		cxlr = cxl_dpa_to_region(cxlmd, dpa);
		if (cxlr)
			hpa = cxl_trace_hpa(cxlr, cxlmd, dpa);

		if (event_type == CXL_CPER_EVENT_GEN_MEDIA)
			trace_cxl_general_media(cxlmd, type, cxlr, hpa,
						&evt->gen_media);
		else if (event_type == CXL_CPER_EVENT_DRAM)
			trace_cxl_dram(cxlmd, type, cxlr, hpa, &evt->dram);
	}
}
EXPORT_SYMBOL_NS_GPL(cxl_event_trace_record, CXL);

+0 −44
Original line number Diff line number Diff line
@@ -251,50 +251,6 @@ int cxl_trigger_poison_list(struct cxl_memdev *cxlmd)
}
EXPORT_SYMBOL_NS_GPL(cxl_trigger_poison_list, CXL);

struct cxl_dpa_to_region_context {
	struct cxl_region *cxlr;
	u64 dpa;
};

static int __cxl_dpa_to_region(struct device *dev, void *arg)
{
	struct cxl_dpa_to_region_context *ctx = arg;
	struct cxl_endpoint_decoder *cxled;
	u64 dpa = ctx->dpa;

	if (!is_endpoint_decoder(dev))
		return 0;

	cxled = to_cxl_endpoint_decoder(dev);
	if (!cxled->dpa_res || !resource_size(cxled->dpa_res))
		return 0;

	if (dpa > cxled->dpa_res->end || dpa < cxled->dpa_res->start)
		return 0;

	dev_dbg(dev, "dpa:0x%llx mapped in region:%s\n", dpa,
		dev_name(&cxled->cxld.region->dev));

	ctx->cxlr = cxled->cxld.region;

	return 1;
}

static struct cxl_region *cxl_dpa_to_region(struct cxl_memdev *cxlmd, u64 dpa)
{
	struct cxl_dpa_to_region_context ctx;
	struct cxl_port *port;

	ctx = (struct cxl_dpa_to_region_context) {
		.dpa = dpa,
	};
	port = cxlmd->endpoint;
	if (port && is_cxl_endpoint(port) && cxl_num_decoders_committed(port))
		device_for_each_child(&port->dev, &ctx, __cxl_dpa_to_region);

	return ctx.cxlr;
}

static int cxl_validate_poison_dpa(struct cxl_memdev *cxlmd, u64 dpa)
{
	struct cxl_dev_state *cxlds = cxlmd->cxlds;
+135 −0
Original line number Diff line number Diff line
@@ -2679,6 +2679,141 @@ int cxl_get_poison_by_endpoint(struct cxl_port *port)
	return rc;
}

struct cxl_dpa_to_region_context {
	struct cxl_region *cxlr;
	u64 dpa;
};

static int __cxl_dpa_to_region(struct device *dev, void *arg)
{
	struct cxl_dpa_to_region_context *ctx = arg;
	struct cxl_endpoint_decoder *cxled;
	u64 dpa = ctx->dpa;

	if (!is_endpoint_decoder(dev))
		return 0;

	cxled = to_cxl_endpoint_decoder(dev);
	if (!cxled->dpa_res || !resource_size(cxled->dpa_res))
		return 0;

	if (dpa > cxled->dpa_res->end || dpa < cxled->dpa_res->start)
		return 0;

	dev_dbg(dev, "dpa:0x%llx mapped in region:%s\n", dpa,
		dev_name(&cxled->cxld.region->dev));

	ctx->cxlr = cxled->cxld.region;

	return 1;
}

struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa)
{
	struct cxl_dpa_to_region_context ctx;
	struct cxl_port *port;

	ctx = (struct cxl_dpa_to_region_context) {
		.dpa = dpa,
	};
	port = cxlmd->endpoint;
	if (port && is_cxl_endpoint(port) && cxl_num_decoders_committed(port))
		device_for_each_child(&port->dev, &ctx, __cxl_dpa_to_region);

	return ctx.cxlr;
}

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, const 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);
}

static struct lock_class_key cxl_pmem_region_key;

static struct cxl_pmem_region *cxl_pmem_region_alloc(struct cxl_region *cxlr)
+0 −91
Original line number Diff line number Diff line
@@ -6,94 +6,3 @@

#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);
}
Loading