Skip to content

Commit ef66a0a

Browse files
committed
EDAC/amd64: Fix size calculation for Non-Power-of-Two DIMMs
JIRA: https://issues.redhat.com/browse/RHEL-101167 commit a3f3040 Author: Avadhut Naik <avadhut.naik@amd.com> Date: Thu May 29 20:50:04 2025 +0000 EDAC/amd64: Fix size calculation for Non-Power-of-Two DIMMs Each Chip-Select (CS) of a Unified Memory Controller (UMC) on AMD Zen-based SOCs has an Address Mask and a Secondary Address Mask register associated with it. The amd64_edac module logs DIMM sizes on a per-UMC per-CS granularity during init using these two registers. Currently, the module primarily considers only the Address Mask register for computing DIMM sizes. The Secondary Address Mask register is only considered for odd CS. Additionally, if it has been considered, the Address Mask register is ignored altogether for that CS. For power-of-two DIMMs i.e. DIMMs whose total capacity is a power of two (32GB, 64GB, etc), this is not an issue since only the Address Mask register is used. For non-power-of-two DIMMs i.e., DIMMs whose total capacity is not a power of two (48GB, 96GB, etc), however, the Secondary Address Mask register is used in conjunction with the Address Mask register. However, since the module only considers either of the two registers for a CS, the size computed by the module is incorrect. The Secondary Address Mask register is not considered for even CS, and the Address Mask register is not considered for odd CS. Introduce a new helper function so that both Address Mask and Secondary Address Mask registers are considered, when valid, for computing DIMM sizes. Furthermore, also rename some variables for greater clarity. Fixes: 81f5090 ("EDAC/amd64: Support asymmetric dual-rank DIMMs") Closes: https://lore.kernel.org/dbec22b6-00f2-498b-b70d-ab6f8a5ec87e@natrix.lt Reported-by: Žilvinas Žaltiena <zilvinas@natrix.lt> Signed-off-by: Avadhut Naik <avadhut.naik@amd.com> Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de> Reviewed-by: Yazen Ghannam <yazen.ghannam@amd.com> Tested-by: Žilvinas Žaltiena <zilvinas@natrix.lt> Cc: stable@vger.kernel.org Link: https://lore.kernel.org/20250529205013.403450-1-avadhut.naik@amd.com" Signed-off-by: Joel Savitz <jsavitz@redhat.com>
1 parent 6f5748c commit ef66a0a

File tree

1 file changed

+36
-21
lines changed

1 file changed

+36
-21
lines changed

drivers/edac/amd64_edac.c

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1212,7 +1212,9 @@ static int umc_get_cs_mode(int dimm, u8 ctrl, struct amd64_pvt *pvt)
12121212
if (csrow_enabled(2 * dimm + 1, ctrl, pvt))
12131213
cs_mode |= CS_ODD_PRIMARY;
12141214

1215-
/* Asymmetric dual-rank DIMM support. */
1215+
if (csrow_sec_enabled(2 * dimm, ctrl, pvt))
1216+
cs_mode |= CS_EVEN_SECONDARY;
1217+
12161218
if (csrow_sec_enabled(2 * dimm + 1, ctrl, pvt))
12171219
cs_mode |= CS_ODD_SECONDARY;
12181220

@@ -1233,12 +1235,13 @@ static int umc_get_cs_mode(int dimm, u8 ctrl, struct amd64_pvt *pvt)
12331235
return cs_mode;
12341236
}
12351237

1236-
static int __addr_mask_to_cs_size(u32 addr_mask_orig, unsigned int cs_mode,
1237-
int csrow_nr, int dimm)
1238+
static int calculate_cs_size(u32 mask, unsigned int cs_mode)
12381239
{
1239-
u32 msb, weight, num_zero_bits;
1240-
u32 addr_mask_deinterleaved;
1241-
int size = 0;
1240+
int msb, weight, num_zero_bits;
1241+
u32 deinterleaved_mask;
1242+
1243+
if (!mask)
1244+
return 0;
12421245

12431246
/*
12441247
* The number of zero bits in the mask is equal to the number of bits
@@ -1251,19 +1254,30 @@ static int __addr_mask_to_cs_size(u32 addr_mask_orig, unsigned int cs_mode,
12511254
* without swapping with the most significant bit. This can be handled
12521255
* by keeping the MSB where it is and ignoring the single zero bit.
12531256
*/
1254-
msb = fls(addr_mask_orig) - 1;
1255-
weight = hweight_long(addr_mask_orig);
1257+
msb = fls(mask) - 1;
1258+
weight = hweight_long(mask);
12561259
num_zero_bits = msb - weight - !!(cs_mode & CS_3R_INTERLEAVE);
12571260

12581261
/* Take the number of zero bits off from the top of the mask. */
1259-
addr_mask_deinterleaved = GENMASK_ULL(msb - num_zero_bits, 1);
1262+
deinterleaved_mask = GENMASK(msb - num_zero_bits, 1);
1263+
edac_dbg(1, " Deinterleaved AddrMask: 0x%x\n", deinterleaved_mask);
1264+
1265+
return (deinterleaved_mask >> 2) + 1;
1266+
}
1267+
1268+
static int __addr_mask_to_cs_size(u32 addr_mask, u32 addr_mask_sec,
1269+
unsigned int cs_mode, int csrow_nr, int dimm)
1270+
{
1271+
int size;
12601272

12611273
edac_dbg(1, "CS%d DIMM%d AddrMasks:\n", csrow_nr, dimm);
1262-
edac_dbg(1, " Original AddrMask: 0x%x\n", addr_mask_orig);
1263-
edac_dbg(1, " Deinterleaved AddrMask: 0x%x\n", addr_mask_deinterleaved);
1274+
edac_dbg(1, " Primary AddrMask: 0x%x\n", addr_mask);
12641275

12651276
/* Register [31:1] = Address [39:9]. Size is in kBs here. */
1266-
size = (addr_mask_deinterleaved >> 2) + 1;
1277+
size = calculate_cs_size(addr_mask, cs_mode);
1278+
1279+
edac_dbg(1, " Secondary AddrMask: 0x%x\n", addr_mask_sec);
1280+
size += calculate_cs_size(addr_mask_sec, cs_mode);
12671281

12681282
/* Return size in MBs. */
12691283
return size >> 10;
@@ -1272,8 +1286,8 @@ static int __addr_mask_to_cs_size(u32 addr_mask_orig, unsigned int cs_mode,
12721286
static int umc_addr_mask_to_cs_size(struct amd64_pvt *pvt, u8 umc,
12731287
unsigned int cs_mode, int csrow_nr)
12741288
{
1289+
u32 addr_mask = 0, addr_mask_sec = 0;
12751290
int cs_mask_nr = csrow_nr;
1276-
u32 addr_mask_orig;
12771291
int dimm, size = 0;
12781292

12791293
/* No Chip Selects are enabled. */
@@ -1311,13 +1325,13 @@ static int umc_addr_mask_to_cs_size(struct amd64_pvt *pvt, u8 umc,
13111325
if (!pvt->flags.zn_regs_v2)
13121326
cs_mask_nr >>= 1;
13131327

1314-
/* Asymmetric dual-rank DIMM support. */
1315-
if ((csrow_nr & 1) && (cs_mode & CS_ODD_SECONDARY))
1316-
addr_mask_orig = pvt->csels[umc].csmasks_sec[cs_mask_nr];
1317-
else
1318-
addr_mask_orig = pvt->csels[umc].csmasks[cs_mask_nr];
1328+
if (cs_mode & (CS_EVEN_PRIMARY | CS_ODD_PRIMARY))
1329+
addr_mask = pvt->csels[umc].csmasks[cs_mask_nr];
1330+
1331+
if (cs_mode & (CS_EVEN_SECONDARY | CS_ODD_SECONDARY))
1332+
addr_mask_sec = pvt->csels[umc].csmasks_sec[cs_mask_nr];
13191333

1320-
return __addr_mask_to_cs_size(addr_mask_orig, cs_mode, csrow_nr, dimm);
1334+
return __addr_mask_to_cs_size(addr_mask, addr_mask_sec, cs_mode, csrow_nr, dimm);
13211335
}
13221336

13231337
static void umc_debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl)
@@ -3516,9 +3530,10 @@ static void gpu_get_err_info(struct mce *m, struct err_info *err)
35163530
static int gpu_addr_mask_to_cs_size(struct amd64_pvt *pvt, u8 umc,
35173531
unsigned int cs_mode, int csrow_nr)
35183532
{
3519-
u32 addr_mask_orig = pvt->csels[umc].csmasks[csrow_nr];
3533+
u32 addr_mask = pvt->csels[umc].csmasks[csrow_nr];
3534+
u32 addr_mask_sec = pvt->csels[umc].csmasks_sec[csrow_nr];
35203535

3521-
return __addr_mask_to_cs_size(addr_mask_orig, cs_mode, csrow_nr, csrow_nr >> 1);
3536+
return __addr_mask_to_cs_size(addr_mask, addr_mask_sec, cs_mode, csrow_nr, csrow_nr >> 1);
35223537
}
35233538

35243539
static void gpu_debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl)

0 commit comments

Comments
 (0)