Skip to content

Commit dc18117

Browse files
AlisonSchofielddavejiang
authored andcommitted
cxl/region: Introduce SPA to DPA address translation
Add infrastructure to translate System Physical Addresses (SPA) to Device Physical Addresses (DPA) within CXL regions. This capability will be used by follow-on patches that add poison inject and clear operations at the region level. The SPA-to-DPA translation process follows these steps: 1. Apply root decoder transformations (SPA to HPA) if configured. 2. Extract the position in region interleave from the HPA offset. 3. Extract the DPA offset from the HPA offset. 4. Use position to find endpoint decoder. 5. Use endpoint decoder to find memdev and calculate DPA from offset. 6. Return the result - a memdev and a DPA. It is Step 1 above that makes this a driver level operation and not work we can push to user space. Rather than exporting the XOR maps for root decoders configured with XOR interleave, the driver performs this complex calculation for the user. Steps 2 and 3 follow the CXL Spec 3.2 Section 8.2.4.20.13 Implementation Note: Device Decode Logic. These calculations mirror much of the logic introduced earlier in DPA to SPA translation, see cxl_dpa_to_hpa(), where the driver needed to reverse the spec defined 'Device Decode Logic'. Signed-off-by: Alison Schofield <alison.schofield@intel.com> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com> Link: https://patch.msgid.link/422f0e27742c6ca9a11f7cd83e6ba9fa1a8d0c74.1754290144.git.alison.schofield@intel.com Signed-off-by: Dave Jiang <dave.jiang@intel.com>
1 parent b83ee96 commit dc18117

1 file changed

Lines changed: 101 additions & 0 deletions

File tree

drivers/cxl/core/region.c

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2923,6 +2923,11 @@ static bool has_hpa_to_spa(struct cxl_root_decoder *cxlrd)
29232923
return cxlrd->ops && cxlrd->ops->hpa_to_spa;
29242924
}
29252925

2926+
static bool has_spa_to_hpa(struct cxl_root_decoder *cxlrd)
2927+
{
2928+
return cxlrd->ops && cxlrd->ops->spa_to_hpa;
2929+
}
2930+
29262931
u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
29272932
u64 dpa)
29282933
{
@@ -2993,6 +2998,102 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
29932998
return hpa;
29942999
}
29953000

3001+
struct dpa_result {
3002+
struct cxl_memdev *cxlmd;
3003+
u64 dpa;
3004+
};
3005+
3006+
static int __maybe_unused region_offset_to_dpa_result(struct cxl_region *cxlr,
3007+
u64 offset,
3008+
struct dpa_result *result)
3009+
{
3010+
struct cxl_region_params *p = &cxlr->params;
3011+
struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
3012+
struct cxl_endpoint_decoder *cxled;
3013+
u64 hpa, hpa_offset, dpa_offset;
3014+
u64 bits_upper, bits_lower;
3015+
u64 shifted, rem, temp;
3016+
u16 eig = 0;
3017+
u8 eiw = 0;
3018+
int pos;
3019+
3020+
lockdep_assert_held(&cxl_rwsem.region);
3021+
lockdep_assert_held(&cxl_rwsem.dpa);
3022+
3023+
/* Input validation ensures valid ways and gran */
3024+
granularity_to_eig(p->interleave_granularity, &eig);
3025+
ways_to_eiw(p->interleave_ways, &eiw);
3026+
3027+
/*
3028+
* If the root decoder has SPA to CXL HPA callback, use it. Otherwise
3029+
* CXL HPA is assumed to equal SPA.
3030+
*/
3031+
if (has_spa_to_hpa(cxlrd)) {
3032+
hpa = cxlrd->ops->spa_to_hpa(cxlrd, p->res->start + offset);
3033+
hpa_offset = hpa - p->res->start;
3034+
} else {
3035+
hpa_offset = offset;
3036+
}
3037+
/*
3038+
* Interleave position: CXL Spec 3.2 Section 8.2.4.20.13
3039+
* eiw < 8
3040+
* Position is in the IW bits at HPA_OFFSET[IG+8+IW-1:IG+8].
3041+
* Per spec "remove IW bits starting with bit position IG+8"
3042+
* eiw >= 8
3043+
* Position is not explicitly stored in HPA_OFFSET bits. It is
3044+
* derived from the modulo operation of the upper bits using
3045+
* the total number of interleave ways.
3046+
*/
3047+
if (eiw < 8) {
3048+
pos = (hpa_offset >> (eig + 8)) & GENMASK(eiw - 1, 0);
3049+
} else {
3050+
shifted = hpa_offset >> (eig + 8);
3051+
div64_u64_rem(shifted, p->interleave_ways, &rem);
3052+
pos = rem;
3053+
}
3054+
if (pos < 0 || pos >= p->nr_targets) {
3055+
dev_dbg(&cxlr->dev, "Invalid position %d for %d targets\n",
3056+
pos, p->nr_targets);
3057+
return -ENXIO;
3058+
}
3059+
3060+
/*
3061+
* DPA offset: CXL Spec 3.2 Section 8.2.4.20.13
3062+
* Lower bits [IG+7:0] pass through unchanged
3063+
* (eiw < 8)
3064+
* Per spec: DPAOffset[51:IG+8] = (HPAOffset[51:IG+IW+8] >> IW)
3065+
* Clear the position bits to isolate upper section, then
3066+
* reverse the left shift by eiw that occurred during DPA->HPA
3067+
* (eiw >= 8)
3068+
* Per spec: DPAOffset[51:IG+8] = HPAOffset[51:IG+IW] / 3
3069+
* Extract upper bits from the correct bit range and divide by 3
3070+
* to recover the original DPA upper bits
3071+
*/
3072+
bits_lower = hpa_offset & GENMASK_ULL(eig + 7, 0);
3073+
if (eiw < 8) {
3074+
temp = hpa_offset &= ~((u64)GENMASK(eig + eiw + 8 - 1, 0));
3075+
dpa_offset = temp >> eiw;
3076+
} else {
3077+
bits_upper = div64_u64(hpa_offset >> (eig + eiw), 3);
3078+
dpa_offset = bits_upper << (eig + 8);
3079+
}
3080+
dpa_offset |= bits_lower;
3081+
3082+
/* Look-up and return the result: a memdev and a DPA */
3083+
for (int i = 0; i < p->nr_targets; i++) {
3084+
cxled = p->targets[i];
3085+
if (cxled->pos != pos)
3086+
continue;
3087+
result->cxlmd = cxled_to_memdev(cxled);
3088+
result->dpa = cxl_dpa_resource_start(cxled) + dpa_offset;
3089+
3090+
return 0;
3091+
}
3092+
dev_err(&cxlr->dev, "No device found for position %d\n", pos);
3093+
3094+
return -ENXIO;
3095+
}
3096+
29963097
static struct lock_class_key cxl_pmem_region_key;
29973098

29983099
static int cxl_pmem_region_alloc(struct cxl_region *cxlr)

0 commit comments

Comments
 (0)