@samitouri / QOSamiQemu / commits / da5cafdc4d

hw/cxl: Add emulation for memory sparing control feature

Memory sparing is defined as a repair function that replaces a portion of memory with a portion of functional memory at that same DPA. The subclasses for this operation vary in terms of the scope of the sparing being performed. The Cacheline sparing subclass refers to a sparing action that can replace a full cacheline. Row sparing is provided as an alternative to PPR sparing functions and its scope is that of a single DDR row. Bank sparing allows an entire bank to be replaced. Rank sparing is defined as an operation in which an entire DDR rank is replaced. Memory sparing maintenance operations may be supported by CXL devices that implement CXL.mem protocol. A sparing maintenance operation requests the CXL device to perform a repair operation on its media. For example, a CXL device with DRAM components that support memory sparing features may implement sparing Maintenance operations. The host may issue a query command by setting Query Resources flag in the Input Payload (CXL Spec 3.2 Table 8-120) to determine availability of sparing resources for a given address. In response to a query request, the device shall report the resource availability by producing the Memory Sparing Event Record (CXL Spec 3.2 Table 8-60) in which the Channel, Rank, Nibble Mask, Bank Group, Bank, Row, Column, Sub-Channel fields are a copy of the values specified in the request. During the execution of a sparing maintenance operation, a CXL memory device: - May or may not retain data - May or may not be able to process CXL.mem requests correctly. These CXL memory device capabilities are specified by restriction flags in the memory sparing feature readable attributes. When a CXL device identifies error on a memory component, the device may inform the host about the need for a memory sparing maintenance operation by using DRAM event record, where the 'maintenance needed' flag may set. The event record contains some of the DPA, Channel, Rank, Nibble Mask, Bank Group, Bank, Row, Column, Sub-Channel fields that should be repaired. The userspace tool requests for maintenance operation if the 'maintenance needed' flag set in the CXL DRAM error record. CXL spec 3.2 section 8.2.10.7.2.3 describes the memory sparing feature discovery and configuration. CXL spec 3.2 section 8.2.10.7.1.4 describes the device's memory sparing maintenance operation feature. Add emulation for CXL memory device memory sparing control feature and memory sparing maintenance operation command. Signed-off-by: Shiju Jose <shiju.jose@huawei.com> Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Message-Id: <20250917143330.294698-4-Jonathan.Cameron@huawei.com>

Shiju Jose committed Sep 17, 2025 at 15:33 UTC da5cafdc4dddc9e69983b99f01c8eb43a8140929
4 files changed +395 -4
hw/cxl/cxl-mailbox-utils.c
+313 -4
@@ -1135,6 +1135,10 @@ enum CXL_SUPPORTED_FEATURES_LIST {
1135 CXL_FEATURE_ECS,
1136 CXL_FEATURE_SPPR,
1137 CXL_FEATURE_HPPR,
1138 + CXL_FEATURE_CACHELINE_SPARING,
1139 + CXL_FEATURE_ROW_SPARING,
1140 + CXL_FEATURE_BANK_SPARING,
1141 + CXL_FEATURE_RANK_SPARING,
1142 CXL_FEATURE_MAX
1143 };
1144
@@ -1223,6 +1227,35 @@ typedef struct CXLMemECSSetFeature {
1227 CXLMemECSWriteAttrs feat_data[];
1228 } QEMU_PACKED QEMU_ALIGNED(16) CXLMemECSSetFeature;
1229
1230 +/*
1231 + * CXL r3.2 section 8.2.10.7.2.3:
1232 + * Memory Sparing Features Discovery and Configuration
1233 + */
1234 +static const QemuUUID cacheline_sparing_uuid = {
1235 + .data = UUID(0x96C33386, 0x91dd, 0x44c7, 0x9e, 0xcb,
1236 + 0xfd, 0xaf, 0x65, 0x03, 0xba, 0xc4)
1237 +};
1238 +
1239 +static const QemuUUID row_sparing_uuid = {
1240 + .data = UUID(0x450ebf67, 0xb135, 0x4f97, 0xa4, 0x98,
1241 + 0xc2, 0xd5, 0x7f, 0x27, 0x9b, 0xed)
1242 +};
1243 +
1244 +static const QemuUUID bank_sparing_uuid = {
1245 + .data = UUID(0x78b79636, 0x90ac, 0x4b64, 0xa4, 0xef,
1246 + 0xfa, 0xac, 0x5d, 0x18, 0xa8, 0x63)
1247 +};
1248 +
1249 +static const QemuUUID rank_sparing_uuid = {
1250 + .data = UUID(0x34dbaff5, 0x0552, 0x4281, 0x8f, 0x76,
1251 + 0xda, 0x0b, 0x5e, 0x7a, 0x76, 0xa7)
1252 +};
1253 +
1254 +typedef struct CXLMemSparingSetFeature {
1255 + CXLSetFeatureInHeader hdr;
1256 + CXLMemSparingWriteAttrs feat_data;
1257 +} QEMU_PACKED QEMU_ALIGNED(16) CXLMemSparingSetFeature;
1258 +
1259 /* CXL r3.1 section 8.2.9.6.1: Get Supported Features (Opcode 0500h) */
1260 static CXLRetCode cmd_features_get_supported(const struct cxl_cmd *cmd,
1261 uint8_t *payload_in,
@@ -1323,6 +1356,70 @@ static CXLRetCode cmd_features_get_supported(const struct cxl_cmd *cmd,
1356 CXL_FEAT_ENTRY_SFE_CEL_VALID,
1357 };
1358 break;
1359 + case CXL_FEATURE_CACHELINE_SPARING:
1360 + /* Fill supported feature entry for Cacheline Memory Sparing */
1361 + get_feats_out->feat_entries[entry++] =
1362 + (struct CXLSupportedFeatureEntry) {
1363 + .uuid = cacheline_sparing_uuid,
1364 + .feat_index = index,
1365 + .get_feat_size = sizeof(CXLMemSparingReadAttrs),
1366 + .set_feat_size = sizeof(CXLMemSparingWriteAttrs),
1367 + .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE |
1368 + CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SEL,
1369 + .get_feat_version = CXL_MEMDEV_SPARING_GET_FEATURE_VERSION,
1370 + .set_feat_version = CXL_MEMDEV_SPARING_SET_FEATURE_VERSION,
1371 + .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE |
1372 + CXL_FEAT_ENTRY_SFE_CEL_VALID,
1373 + };
1374 + break;
1375 + case CXL_FEATURE_ROW_SPARING:
1376 + /* Fill supported feature entry for Row Memory Sparing */
1377 + get_feats_out->feat_entries[entry++] =
1378 + (struct CXLSupportedFeatureEntry) {
1379 + .uuid = row_sparing_uuid,
1380 + .feat_index = index,
1381 + .get_feat_size = sizeof(CXLMemSparingReadAttrs),
1382 + .set_feat_size = sizeof(CXLMemSparingWriteAttrs),
1383 + .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE |
1384 + CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SEL,
1385 + .get_feat_version = CXL_MEMDEV_SPARING_GET_FEATURE_VERSION,
1386 + .set_feat_version = CXL_MEMDEV_SPARING_SET_FEATURE_VERSION,
1387 + .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE |
1388 + CXL_FEAT_ENTRY_SFE_CEL_VALID,
1389 + };
1390 + break;
1391 + case CXL_FEATURE_BANK_SPARING:
1392 + /* Fill supported feature entry for Bank Memory Sparing */
1393 + get_feats_out->feat_entries[entry++] =
1394 + (struct CXLSupportedFeatureEntry) {
1395 + .uuid = bank_sparing_uuid,
1396 + .feat_index = index,
1397 + .get_feat_size = sizeof(CXLMemSparingReadAttrs),
1398 + .set_feat_size = sizeof(CXLMemSparingWriteAttrs),
1399 + .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE |
1400 + CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SEL,
1401 + .get_feat_version = CXL_MEMDEV_SPARING_GET_FEATURE_VERSION,
1402 + .set_feat_version = CXL_MEMDEV_SPARING_SET_FEATURE_VERSION,
1403 + .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE |
1404 + CXL_FEAT_ENTRY_SFE_CEL_VALID,
1405 + };
1406 + break;
1407 + case CXL_FEATURE_RANK_SPARING:
1408 + /* Fill supported feature entry for Rank Memory Sparing */
1409 + get_feats_out->feat_entries[entry++] =
1410 + (struct CXLSupportedFeatureEntry) {
1411 + .uuid = rank_sparing_uuid,
1412 + .feat_index = index,
1413 + .get_feat_size = sizeof(CXLMemSparingReadAttrs),
1414 + .set_feat_size = sizeof(CXLMemSparingWriteAttrs),
1415 + .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE |
1416 + CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SEL,
1417 + .get_feat_version = CXL_MEMDEV_SPARING_GET_FEATURE_VERSION,
1418 + .set_feat_version = CXL_MEMDEV_SPARING_SET_FEATURE_VERSION,
1419 + .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE |
1420 + CXL_FEAT_ENTRY_SFE_CEL_VALID,
1421 + };
1422 + break;
1423 default:
1424 __builtin_unreachable();
1425 }
@@ -1411,6 +1508,47 @@ static CXLRetCode cmd_features_get_feature(const struct cxl_cmd *cmd,
1508 memcpy(payload_out,
1509 (uint8_t *)&ct3d->hard_ppr_attrs + get_feature->offset,
1510 bytes_to_copy);
1511 + } else if (qemu_uuid_is_equal(&get_feature->uuid,
1512 + &cacheline_sparing_uuid)) {
1513 + if (get_feature->offset >= sizeof(CXLMemSparingReadAttrs)) {
1514 + return CXL_MBOX_INVALID_INPUT;
1515 + }
1516 + bytes_to_copy = sizeof(CXLMemSparingReadAttrs) -
1517 + get_feature->offset;
1518 + bytes_to_copy = MIN(bytes_to_copy, get_feature->count);
1519 + memcpy(payload_out,
1520 + (uint8_t *)&ct3d->cacheline_sparing_attrs + get_feature->offset,
1521 + bytes_to_copy);
1522 + } else if (qemu_uuid_is_equal(&get_feature->uuid, &row_sparing_uuid)) {
1523 + if (get_feature->offset >= sizeof(CXLMemSparingReadAttrs)) {
1524 + return CXL_MBOX_INVALID_INPUT;
1525 + }
1526 + bytes_to_copy = sizeof(CXLMemSparingReadAttrs) -
1527 + get_feature->offset;
1528 + bytes_to_copy = MIN(bytes_to_copy, get_feature->count);
1529 + memcpy(payload_out,
1530 + (uint8_t *)&ct3d->row_sparing_attrs + get_feature->offset,
1531 + bytes_to_copy);
1532 + } else if (qemu_uuid_is_equal(&get_feature->uuid, &bank_sparing_uuid)) {
1533 + if (get_feature->offset >= sizeof(CXLMemSparingReadAttrs)) {
1534 + return CXL_MBOX_INVALID_INPUT;
1535 + }
1536 + bytes_to_copy = sizeof(CXLMemSparingReadAttrs) -
1537 + get_feature->offset;
1538 + bytes_to_copy = MIN(bytes_to_copy, get_feature->count);
1539 + memcpy(payload_out,
1540 + (uint8_t *)&ct3d->bank_sparing_attrs + get_feature->offset,
1541 + bytes_to_copy);
1542 + } else if (qemu_uuid_is_equal(&get_feature->uuid, &rank_sparing_uuid)) {
1543 + if (get_feature->offset >= sizeof(CXLMemSparingReadAttrs)) {
1544 + return CXL_MBOX_INVALID_INPUT;
1545 + }
1546 + bytes_to_copy = sizeof(CXLMemSparingReadAttrs) -
1547 + get_feature->offset;
1548 + bytes_to_copy = MIN(bytes_to_copy, get_feature->count);
1549 + memcpy(payload_out,
1550 + (uint8_t *)&ct3d->rank_sparing_attrs + get_feature->offset,
1551 + bytes_to_copy);
1552 } else {
1553 return CXL_MBOX_UNSUPPORTED;
1554 }
@@ -1560,6 +1698,78 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd,
1698 ct3d->hard_ppr_attrs.hppr_op_mode =
1699 ct3d->hard_ppr_wr_attrs.hppr_op_mode;
1700 }
1701 + } else if (qemu_uuid_is_equal(&hdr->uuid, &cacheline_sparing_uuid)) {
1702 + CXLMemSparingSetFeature *mem_sparing_set_feature = (void *)payload_in;
1703 + CXLMemSparingWriteAttrs *mem_sparing_write_attrs =
1704 + &mem_sparing_set_feature->feat_data;
1705 +
1706 + if (hdr->version != CXL_MEMDEV_SPARING_SET_FEATURE_VERSION) {
1707 + return CXL_MBOX_UNSUPPORTED;
1708 + }
1709 +
1710 + memcpy((uint8_t *)&ct3d->cacheline_sparing_wr_attrs + hdr->offset,
1711 + mem_sparing_write_attrs, bytes_to_copy);
1712 + set_feat_info->data_size += bytes_to_copy;
1713 +
1714 + if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||
1715 + data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {
1716 + ct3d->cacheline_sparing_attrs.op_mode =
1717 + ct3d->cacheline_sparing_wr_attrs.op_mode;
1718 + }
1719 + } else if (qemu_uuid_is_equal(&hdr->uuid, &row_sparing_uuid)) {
1720 + CXLMemSparingSetFeature *mem_sparing_set_feature = (void *)payload_in;
1721 + CXLMemSparingWriteAttrs *mem_sparing_write_attrs =
1722 + &mem_sparing_set_feature->feat_data;
1723 +
1724 + if (hdr->version != CXL_MEMDEV_SPARING_SET_FEATURE_VERSION) {
1725 + return CXL_MBOX_UNSUPPORTED;
1726 + }
1727 +
1728 + memcpy((uint8_t *)&ct3d->row_sparing_wr_attrs + hdr->offset,
1729 + mem_sparing_write_attrs, bytes_to_copy);
1730 + set_feat_info->data_size += bytes_to_copy;
1731 +
1732 + if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||
1733 + data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {
1734 + ct3d->row_sparing_attrs.op_mode =
1735 + ct3d->row_sparing_wr_attrs.op_mode;
1736 + }
1737 + } else if (qemu_uuid_is_equal(&hdr->uuid, &bank_sparing_uuid)) {
1738 + CXLMemSparingSetFeature *mem_sparing_set_feature = (void *)payload_in;
1739 + CXLMemSparingWriteAttrs *mem_sparing_write_attrs =
1740 + &mem_sparing_set_feature->feat_data;
1741 +
1742 + if (hdr->version != CXL_MEMDEV_SPARING_SET_FEATURE_VERSION) {
1743 + return CXL_MBOX_UNSUPPORTED;
1744 + }
1745 +
1746 + memcpy((uint8_t *)&ct3d->bank_sparing_wr_attrs + hdr->offset,
1747 + mem_sparing_write_attrs, bytes_to_copy);
1748 + set_feat_info->data_size += bytes_to_copy;
1749 +
1750 + if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||
1751 + data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {
1752 + ct3d->bank_sparing_attrs.op_mode =
1753 + ct3d->bank_sparing_wr_attrs.op_mode;
1754 + }
1755 + } else if (qemu_uuid_is_equal(&hdr->uuid, &rank_sparing_uuid)) {
1756 + CXLMemSparingSetFeature *mem_sparing_set_feature = (void *)payload_in;
1757 + CXLMemSparingWriteAttrs *mem_sparing_write_attrs =
1758 + &mem_sparing_set_feature->feat_data;
1759 +
1760 + if (hdr->version != CXL_MEMDEV_SPARING_SET_FEATURE_VERSION) {
1761 + return CXL_MBOX_UNSUPPORTED;
1762 + }
1763 +
1764 + memcpy((uint8_t *)&ct3d->rank_sparing_wr_attrs + hdr->offset,
1765 + mem_sparing_write_attrs, bytes_to_copy);
1766 + set_feat_info->data_size += bytes_to_copy;
1767 +
1768 + if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||
1769 + data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {
1770 + ct3d->rank_sparing_attrs.op_mode =
1771 + ct3d->rank_sparing_wr_attrs.op_mode;
1772 + }
1773 } else {
1774 return CXL_MBOX_UNSUPPORTED;
1775 }
@@ -1576,6 +1786,15 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd,
1786 memset(&ct3d->soft_ppr_wr_attrs, 0, set_feat_info->data_size);
1787 } else if (qemu_uuid_is_equal(&hdr->uuid, &hard_ppr_uuid)) {
1788 memset(&ct3d->hard_ppr_wr_attrs, 0, set_feat_info->data_size);
1789 + } else if (qemu_uuid_is_equal(&hdr->uuid, &cacheline_sparing_uuid)) {
1790 + memset(&ct3d->cacheline_sparing_wr_attrs, 0,
1791 + set_feat_info->data_size);
1792 + } else if (qemu_uuid_is_equal(&hdr->uuid, &row_sparing_uuid)) {
1793 + memset(&ct3d->row_sparing_wr_attrs, 0, set_feat_info->data_size);
1794 + } else if (qemu_uuid_is_equal(&hdr->uuid, &bank_sparing_uuid)) {
1795 + memset(&ct3d->bank_sparing_wr_attrs, 0, set_feat_info->data_size);
1796 + } else if (qemu_uuid_is_equal(&hdr->uuid, &rank_sparing_uuid)) {
1797 + memset(&ct3d->rank_sparing_wr_attrs, 0, set_feat_info->data_size);
1798 }
1799 set_feat_info->data_transfer_flag = 0;
1800 set_feat_info->data_saved_across_reset = false;
@@ -1586,9 +1805,27 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd,
1805 return CXL_MBOX_SUCCESS;
1806 }
1807
1589 -static void cxl_mbox_create_mem_sparing_event_records(CXLType3Dev *ct3d,
1808 +#define CXL_MEM_SPARING_FLAGS_QUERY_RESOURCES BIT(0)
1809 +#define CXL_MEM_SPARING_FLAGS_HARD_SPARING BIT(1)
1810 +#define CXL_MEM_SPARING_FLAGS_SUB_CHANNEL_VALID BIT(2)
1811 +#define CXL_MEM_SPARING_FLAGS_NIB_MASK_VALID BIT(3)
1812 +
1813 +typedef struct CXLMemSparingMaintInPayload {
1814 + uint8_t flags;
1815 + uint8_t channel;
1816 + uint8_t rank;
1817 + uint8_t nibble_mask[3];
1818 + uint8_t bank_group;
1819 + uint8_t bank;
1820 + uint8_t row[3];
1821 + uint16_t column;
1822 + uint8_t sub_channel;
1823 +} QEMU_PACKED CXLMemSparingMaintInPayload;
1824 +
1825 +static void cxl_create_mem_sparing_event_records(CXLType3Dev *ct3d,
1826 uint8_t maint_op_class, uint8_t maint_op_sub_class,
1591 - CXLMaintenance *ent)
1827 + CXLMaintenance *ent,
1828 + CXLMemSparingMaintInPayload *sparing_pi)
1829 {
1830 CXLEventSparing event_rec = {};
1831
@@ -1616,6 +1853,31 @@ static void cxl_mbox_create_mem_sparing_event_records(CXLType3Dev *ct3d,
1853 strncpy((char *)event_rec.component_id, (char *)ent->component_id,
1854 sizeof(event_rec.component_id));
1855 }
1856 + } else if (sparing_pi) {
1857 + event_rec.flags = CXL_MSER_FLAGS_QUERY_RESOURCES;
1858 + event_rec.result = 0;
1859 + event_rec.validity_flags = CXL_MSER_VALID_CHANNEL |
1860 + CXL_MSER_VALID_RANK |
1861 + CXL_MSER_VALID_NIB_MASK |
1862 + CXL_MSER_VALID_BANK_GROUP |
1863 + CXL_MSER_VALID_BANK |
1864 + CXL_MSER_VALID_ROW |
1865 + CXL_MSER_VALID_COLUMN;
1866 + event_rec.res_avail = 1;
1867 + event_rec.channel = sparing_pi->channel;
1868 + event_rec.rank = sparing_pi->rank;
1869 + if (sparing_pi->flags & CXL_MEM_SPARING_FLAGS_NIB_MASK_VALID) {
1870 + memcpy(event_rec.nibble_mask, sparing_pi->nibble_mask,
1871 + sizeof(sparing_pi->nibble_mask));
1872 + }
1873 + event_rec.bank_group = sparing_pi->bank_group;
1874 + event_rec.bank = sparing_pi->bank;
1875 + event_rec.column = sparing_pi->column;
1876 + memcpy(event_rec.row, sparing_pi->row, sizeof(sparing_pi->row));
1877 + if (sparing_pi->flags & CXL_MEM_SPARING_FLAGS_SUB_CHANNEL_VALID) {
1878 + event_rec.sub_channel = sparing_pi->sub_channel;
1879 + event_rec.validity_flags |= CXL_MSER_VALID_SUB_CHANNEL;
1880 + }
1881 } else {
1882 return;
1883 }
@@ -1627,6 +1889,26 @@ static void cxl_mbox_create_mem_sparing_event_records(CXLType3Dev *ct3d,
1889 }
1890 }
1891
1892 +static CXLRetCode cxl_perform_mem_sparing(CXLType3Dev *ct3d, uint8_t sub_class,
1893 + void *maint_pi)
1894 +{
1895 + switch (sub_class) {
1896 + case CXL_MEMDEV_MAINT_SUBCLASS_CACHELINE_SPARING:
1897 + qemu_log("Cacheline Memory Sparing\n");
1898 + return CXL_MBOX_SUCCESS;
1899 + case CXL_MEMDEV_MAINT_SUBCLASS_ROW_SPARING:
1900 + qemu_log("Row Memory Sparing\n");
1901 + return CXL_MBOX_SUCCESS;
1902 + case CXL_MEMDEV_MAINT_SUBCLASS_BANK_SPARING:
1903 + qemu_log("Bank Memory Sparing\n");
1904 + return CXL_MBOX_SUCCESS;
1905 + case CXL_MEMDEV_MAINT_SUBCLASS_RANK_SPARING:
1906 + qemu_log("Rank Memory Sparing\n");
1907 + return CXL_MBOX_SUCCESS;
1908 + default:
1909 + return CXL_MBOX_INVALID_INPUT;
1910 + }
1911 +}
1912
1913 static void cxl_perform_ppr(CXLType3Dev *ct3d, uint64_t dpa)
1914 {
@@ -1637,10 +1919,10 @@ static void cxl_perform_ppr(CXLType3Dev *ct3d, uint64_t dpa)
1919 /* Produce a Memory Sparing Event Record */
1920 if (ct3d->soft_ppr_attrs.sppr_op_mode &
1921 CXL_MEMDEV_SPPR_OP_MODE_MEM_SPARING_EV_REC_EN) {
1640 - cxl_mbox_create_mem_sparing_event_records(ct3d,
1922 + cxl_create_mem_sparing_event_records(ct3d,
1923 CXL_MEMDEV_MAINT_CLASS_SPARING,
1924 CXL_MEMDEV_MAINT_SUBCLASS_CACHELINE_SPARING,
1643 - ent);
1925 + ent, NULL);
1926 }
1927 break;
1928 }
@@ -1664,6 +1946,7 @@ static CXLRetCode cmd_media_perform_maintenance(const struct cxl_cmd *cmd,
1946 uint64_t dpa;
1947 uint8_t nibble_mask[3];
1948 } QEMU_PACKED ppr;
1949 + CXLMemSparingMaintInPayload mem_sparing_pi;
1950 };
1951 } QEMU_PACKED *maint_in = (void *)payload_in;
1952 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
@@ -1689,6 +1972,32 @@ static CXLRetCode cmd_media_perform_maintenance(const struct cxl_cmd *cmd,
1972 return CXL_MBOX_INVALID_INPUT;
1973 }
1974 break;
1975 + case CXL_MEMDEV_MAINT_CLASS_SPARING:
1976 + if (maint_in->mem_sparing_pi.flags &
1977 + CXL_MEM_SPARING_FLAGS_QUERY_RESOURCES) {
1978 + /*
1979 + * CXL r3.2 sect 8.2.10.7.1.4 - Memory Sparing Maintenance Operation
1980 + * Produce Memory Sparing Event record to report resources
1981 + * availability.
1982 + */
1983 + cxl_create_mem_sparing_event_records(ct3d, maint_in->class,
1984 + maint_in->subclass, NULL,
1985 + &maint_in->mem_sparing_pi);
1986 +
1987 + return CXL_MBOX_SUCCESS;
1988 + }
1989 +
1990 + switch (maint_in->subclass) {
1991 + case CXL_MEMDEV_MAINT_SUBCLASS_CACHELINE_SPARING:
1992 + case CXL_MEMDEV_MAINT_SUBCLASS_ROW_SPARING:
1993 + case CXL_MEMDEV_MAINT_SUBCLASS_BANK_SPARING:
1994 + case CXL_MEMDEV_MAINT_SUBCLASS_RANK_SPARING:
1995 + return cxl_perform_mem_sparing(ct3d, maint_in->subclass,
1996 + &maint_in->mem_sparing_pi);
1997 + default:
1998 + return CXL_MBOX_INVALID_INPUT;
1999 + }
2000 + break;
2001 default:
2002 return CXL_MBOX_INVALID_INPUT;
2003 }
hw/mem/cxl_type3.c
+44
@@ -990,6 +990,50 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp)
990 .hppr_op_mode = CXL_MEMDEV_HPPR_OP_MODE_MEM_SPARING_EV_REC_EN
991 };
992
993 + /* Set default value for Cacheline Memory Sparing attributes */
994 + ct3d->cacheline_sparing_attrs = (CXLMemSparingReadAttrs) {
995 + .max_maint_latency = 0x5, /* 100 ms */
996 + .op_caps = 0, /* require host involvement */
997 + .op_mode = 0,
998 + .maint_op_class = CXL_MEMDEV_MAINT_CLASS_SPARING,
999 + .maint_op_subclass = CXL_MEMDEV_MAINT_SUBCLASS_CACHELINE_SPARING,
1000 + .restriction_flags = CXL_MEMDEV_HARD_SPARING_SUPPORT_FLAG |
1001 + CXL_MEMDEV_SOFT_SPARING_SUPPORT_FLAG,
1002 + };
1003 +
1004 + /* Set default value for Row Memory Sparing attributes */
1005 + ct3d->row_sparing_attrs = (CXLMemSparingReadAttrs) {
1006 + .max_maint_latency = 0x5, /* 100 ms */
1007 + .op_caps = 0, /* require host involvement */
1008 + .op_mode = 0,
1009 + .maint_op_class = CXL_MEMDEV_MAINT_CLASS_SPARING,
1010 + .maint_op_subclass = CXL_MEMDEV_MAINT_SUBCLASS_ROW_SPARING,
1011 + .restriction_flags = CXL_MEMDEV_HARD_SPARING_SUPPORT_FLAG |
1012 + CXL_MEMDEV_SOFT_SPARING_SUPPORT_FLAG,
1013 + };
1014 +
1015 + /* Set default value for Bank Memory Sparing attributes */
1016 + ct3d->bank_sparing_attrs = (CXLMemSparingReadAttrs) {
1017 + .max_maint_latency = 0x5, /* 100 ms */
1018 + .op_caps = 0, /* require host involvement */
1019 + .op_mode = 0,
1020 + .maint_op_class = CXL_MEMDEV_MAINT_CLASS_SPARING,
1021 + .maint_op_subclass = CXL_MEMDEV_MAINT_SUBCLASS_BANK_SPARING,
1022 + .restriction_flags = CXL_MEMDEV_HARD_SPARING_SUPPORT_FLAG |
1023 + CXL_MEMDEV_SOFT_SPARING_SUPPORT_FLAG,
1024 + };
1025 +
1026 + /* Set default value for Rank Memory Sparing attributes */
1027 + ct3d->rank_sparing_attrs = (CXLMemSparingReadAttrs) {
1028 + .max_maint_latency = 0x5, /* 100 ms */
1029 + .op_caps = 0, /* require host involvement */
1030 + .op_mode = 0,
1031 + .maint_op_class = CXL_MEMDEV_MAINT_CLASS_SPARING,
1032 + .maint_op_subclass = CXL_MEMDEV_MAINT_SUBCLASS_RANK_SPARING,
1033 + .restriction_flags = CXL_MEMDEV_HARD_SPARING_SUPPORT_FLAG |
1034 + CXL_MEMDEV_SOFT_SPARING_SUPPORT_FLAG,
1035 + };
1036 +
1037 return;
1038
1039 err_release_cdat:
include/hw/cxl/cxl_device.h
+33
@@ -608,6 +608,30 @@ typedef struct CXLMemECSWriteAttrs {
608 CXLMemECSFRUWriteAttrs fru_attrs[CXL_ECS_NUM_MEDIA_FRUS];
609 } QEMU_PACKED CXLMemECSWriteAttrs;
610
611 +/*
612 + * CXL r3.2 section 8.2.10.7.2.3, Table 8-134 and 8-135:
613 + * Memory Sparing Feature Readable/Writable Attributes
614 + */
615 +typedef struct CXLMemSparingReadAttrs {
616 + uint8_t max_maint_latency;
617 + uint16_t op_caps;
618 + uint16_t op_mode;
619 + uint8_t maint_op_class;
620 + uint8_t maint_op_subclass;
621 + uint8_t rsvd[10];
622 + uint16_t restriction_flags;
623 +} QEMU_PACKED CXLMemSparingReadAttrs;
624 +
625 +typedef struct CXLMemSparingWriteAttrs {
626 + uint16_t op_mode;
627 +} QEMU_PACKED CXLMemSparingWriteAttrs;
628 +
629 +#define CXL_MEMDEV_SPARING_GET_FEATURE_VERSION 0x01
630 +#define CXL_MEMDEV_SPARING_SET_FEATURE_VERSION 0x01
631 +#define CXL_MEMDEV_SPARING_SAFE_IN_USE_FLAG BIT(0)
632 +#define CXL_MEMDEV_HARD_SPARING_SUPPORT_FLAG BIT(1)
633 +#define CXL_MEMDEV_SOFT_SPARING_SUPPORT_FLAG BIT(2)
634 +
635 #define DCD_MAX_NUM_REGION 8
636
637 typedef struct CXLDCExtentRaw {
@@ -735,6 +759,15 @@ struct CXLType3Dev {
759 /* ECS control attributes */
760 CXLMemECSReadAttrs ecs_attrs;
761 CXLMemECSWriteAttrs ecs_wr_attrs;
762 + /* Memory Sparing control attributes */
763 + CXLMemSparingReadAttrs cacheline_sparing_attrs;
764 + CXLMemSparingWriteAttrs cacheline_sparing_wr_attrs;
765 + CXLMemSparingReadAttrs row_sparing_attrs;
766 + CXLMemSparingWriteAttrs row_sparing_wr_attrs;
767 + CXLMemSparingReadAttrs bank_sparing_attrs;
768 + CXLMemSparingWriteAttrs bank_sparing_wr_attrs;
769 + CXLMemSparingReadAttrs rank_sparing_attrs;
770 + CXLMemSparingWriteAttrs rank_sparing_wr_attrs;
771
772 struct dynamic_capacity {
773 HostMemoryBackend *host_dc;
include/hw/cxl/cxl_events.h
+5
@@ -223,6 +223,11 @@ typedef enum CXLDCEventType {
223 * CXL r3.2 section Table 8-60: Memory Sparing Event Record
224 * All fields little endian.
225 */
226 +
227 +#define CXL_MSER_FLAGS_QUERY_RESOURCES BIT(0)
228 +#define CXL_MSER_FLAGS_HARD_SPARING BIT(1)
229 +#define CXL_MSER_FLAGS_DEV_INITIATED BIT(2)
230 +
231 #define CXL_MSER_VALID_CHANNEL BIT(0)
232 #define CXL_MSER_VALID_RANK BIT(1)
233 #define CXL_MSER_VALID_NIB_MASK BIT(2)