CXL spec 3.1 section 8.2.9.9.5.3 describes media operations commands. CXL devices supports media operations Sanitize and Write zero command.
Signed-off-by: Vinayak Holikatti <vinayak...@samsung.com> --- hw/cxl/cxl-mailbox-utils.c | 217 ++++++++++++++++++++++++++++++++++-- include/hw/cxl/cxl_device.h | 11 ++ 2 files changed, 220 insertions(+), 8 deletions(-) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 2315d07fb1..89847ddd9d 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -1722,6 +1722,145 @@ static CXLRetCode cmd_sanitize_overwrite(const struct cxl_cmd *cmd, return CXL_MBOX_BG_STARTED; } +#define DPA_RANGE_GRANULARITY 64 +struct dpa_range_list_entry { + uint64_t starting_dpa; + uint64_t length; +}; + +static int validate_dpa_addr(CXLType3Dev *ct3d, uint64_t dpa_addr, + size_t length) +{ + MemoryRegion *vmr = NULL, *pmr = NULL, *dc_mr = NULL; + uint64_t vmr_size = 0, pmr_size = 0, dc_size = 0; + int rc = 0; + + if ((dpa_addr % DPA_RANGE_GRANULARITY) || + (length % DPA_RANGE_GRANULARITY)) { + return -EINVAL; + } + + if (ct3d->hostvmem) { + vmr = host_memory_backend_get_memory(ct3d->hostvmem); + vmr_size = memory_region_size(vmr); + } + if (ct3d->hostpmem) { + pmr = host_memory_backend_get_memory(ct3d->hostpmem); + pmr_size = memory_region_size(pmr); + } + if (ct3d->dc.host_dc) { + dc_mr = host_memory_backend_get_memory(ct3d->dc.host_dc); + dc_size = memory_region_size(dc_mr); + } + + if (!vmr && !pmr && !dc_mr) { + return -ENODEV; + } + + if (dpa_addr >= vmr_size + pmr_size + dc_size || + (dpa_addr + length) > vmr_size + pmr_size + dc_size) { + return -EINVAL; + } + + if (dpa_addr > vmr_size + pmr_size) { + if (!ct3_test_region_block_backed(ct3d, dpa_addr, length)) { + return -ENODEV; + } + } + + + return rc; +} + +static int sanitize_range(CXLType3Dev *ct3d, uint64_t dpa_addr, size_t length, + uint8_t fill_value) +{ + + MemoryRegion *vmr = NULL, *pmr = NULL; + uint64_t vmr_size = 0, pmr_size = 0; + AddressSpace *as = NULL; + MemTxAttrs mem_attrs = {0}; + + if (ct3d->hostvmem) { + vmr = host_memory_backend_get_memory(ct3d->hostvmem); + vmr_size = memory_region_size(vmr); + } + if (ct3d->hostpmem) { + pmr = host_memory_backend_get_memory(ct3d->hostpmem); + pmr_size = memory_region_size(pmr); + } + + if (dpa_addr < vmr_size) { + as = &ct3d->hostvmem_as; + } else if (dpa_addr < vmr_size + pmr_size) { + as = &ct3d->hostpmem_as; + } else { + if (!ct3_test_region_block_backed(ct3d, dpa_addr, length)) { + return -ENODEV; + } + as = &ct3d->dc.host_dc_as; + } + + return address_space_set(as, dpa_addr, + fill_value, length, mem_attrs); +} + +/* Perform the actual device zeroing */ +static void __do_sanitize(CXLType3Dev *ct3d) +{ + struct CXLSanitizeInfo *san_info = ct3d->media_op_sanitize; + int dpa_range_count = san_info->dpa_range_count; + int rc = 0; + + for (int i = 0; i < dpa_range_count; i++) { + rc = sanitize_range(ct3d, san_info->dpa_range_list[i].starting_dpa, + san_info->dpa_range_list[i].length, san_info->fill_value); + if (rc) { + goto exit; + } + } + cxl_discard_all_event_records(&ct3d->cxl_dstate); +exit: + g_free(ct3d->media_op_sanitize); + ct3d->media_op_sanitize = NULL; + return; +} + +static int get_sanitize_duration(uint64_t total_mem) +{ + int secs = 0; + + if (total_mem <= 512) { + secs = 4; + } else if (total_mem <= 1024) { + secs = 8; + } else if (total_mem <= 2 * 1024) { + secs = 15; + } else if (total_mem <= 4 * 1024) { + secs = 30; + } else if (total_mem <= 8 * 1024) { + secs = 60; + } else if (total_mem <= 16 * 1024) { + secs = 2 * 60; + } else if (total_mem <= 32 * 1024) { + secs = 4 * 60; + } else if (total_mem <= 64 * 1024) { + secs = 8 * 60; + } else if (total_mem <= 128 * 1024) { + secs = 15 * 60; + } else if (total_mem <= 256 * 1024) { + secs = 30 * 60; + } else if (total_mem <= 512 * 1024) { + secs = 60 * 60; + } else if (total_mem <= 1024 * 1024) { + secs = 120 * 60; + } else { + secs = 240 * 60; /* max 4 hrs */ + } + + return secs; +} + enum { MEDIA_OP_GENERAL = 0x0, MEDIA_OP_SANITIZE = 0x1, @@ -1729,10 +1868,9 @@ enum { } MEDIA_OPERATION_CLASS; enum { - MEDIA_OP_SUB_DISCOVERY = 0x0, - MEDIA_OP_SUB_SANITIZE = 0x0, - MEDIA_OP_SUB_ZERO = 0x1, - MEDIA_OP_SUB_CLASS_MAX + MEDIA_OP_GEN_DISCOVERY = 0x0, + MEDIA_OP_SAN_SANITIZE = 0x0, + MEDIA_OP_SAN_ZERO = 0x1, } MEDIA_OPERATION_SUB_CLASS; struct media_op_supported_list_entry { @@ -1777,9 +1915,14 @@ static CXLRetCode cmd_media_operations(const struct cxl_cmd *cmd, }; } QEMU_PACKED *media_op_in_pl = (void *)payload_in; + + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); uint8_t media_op_cl = media_op_in_pl->media_operation_class; uint8_t media_op_subclass = media_op_in_pl->media_operation_subclass; uint32_t dpa_range_count = media_op_in_pl->dpa_range_count; + uint8_t fill_value = 0; + uint64_t total_mem = 0; + int secs = 0; if (len_in < sizeof(*media_op_in_pl)) { return CXL_MBOX_INVALID_PAYLOAD_LENGTH; @@ -1788,7 +1931,7 @@ static CXLRetCode cmd_media_operations(const struct cxl_cmd *cmd, switch (media_op_cl) { case MEDIA_OP_GENERAL: switch (media_op_subclass) { - case MEDIA_OP_SUB_DISCOVERY: + case MEDIA_OP_GEN_DISCOVERY: int count = 0; struct media_op_discovery_out_pl *media_out_pl = (void *)payload_out; @@ -1806,7 +1949,7 @@ static CXLRetCode cmd_media_operations(const struct cxl_cmd *cmd, return CXL_MBOX_INVALID_INPUT; } - media_out_pl->dpa_range_granularity = CXL_CAPACITY_MULTIPLIER; + media_out_pl->dpa_range_granularity = DPA_RANGE_GRANULARITY; media_out_pl->total_supported_operations = MAX_SUPPORTED_OPS; if (num_ops > 0) { for (int i = start_index; i < MAX_SUPPORTED_OPS; i++) { @@ -1824,22 +1967,73 @@ disc_out: media_out_pl->num_of_supported_operations = count; *len_out = sizeof(struct media_op_discovery_out_pl) + (sizeof(struct media_op_supported_list_entry) * count); - break; + goto exit; default: return CXL_MBOX_UNSUPPORTED; } break; case MEDIA_OP_SANITIZE: + { switch (media_op_subclass) { - + case MEDIA_OP_SAN_SANITIZE: + if (len_in < (sizeof(*media_op_in_pl) + + (dpa_range_count * sizeof(struct dpa_range_list_entry)))) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } + fill_value = 0xF; + goto sanitize_range; + case MEDIA_OP_SAN_ZERO: + if (len_in < (sizeof(*media_op_in_pl) + + (dpa_range_count * sizeof(struct dpa_range_list_entry)))) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } + fill_value = 0; + goto sanitize_range; default: return CXL_MBOX_UNSUPPORTED; } break; + } default: return CXL_MBOX_UNSUPPORTED; } +sanitize_range: + if (dpa_range_count > 0) { + for (int i = 0; i < dpa_range_count; i++) { + if (validate_dpa_addr(ct3d, + media_op_in_pl->dpa_range_list[i].starting_dpa, + media_op_in_pl->dpa_range_list[i].length)) { + return CXL_MBOX_INVALID_INPUT; + } + total_mem += media_op_in_pl->dpa_range_list[i].length; + } + ct3d->media_op_sanitize = g_malloc0(sizeof(struct CXLSanitizeInfo) + + (dpa_range_count * + sizeof(struct dpa_range_list_entry))); + + if (ct3d->media_op_sanitize) { + ct3d->media_op_sanitize->dpa_range_count = dpa_range_count; + ct3d->media_op_sanitize->fill_value = fill_value; + memcpy(ct3d->media_op_sanitize->dpa_range_list, + media_op_in_pl->dpa_range_list, + (dpa_range_count * + sizeof(struct dpa_range_list_entry))); + secs = get_sanitize_duration(total_mem >> 20); + goto start_bg; + } + } else if (dpa_range_count == 0) { + goto exit; + } + +start_bg: + /* EBUSY other bg cmds as of now */ + cci->bg.runtime = secs * 1000UL; + *len_out = 0; + /* sanitize when done */ + cxl_dev_disable_media(&ct3d->cxl_dstate); + return CXL_MBOX_BG_STARTED; +exit: return CXL_MBOX_SUCCESS; } @@ -3154,6 +3348,13 @@ static void bg_timercb(void *opaque) cxl_dev_enable_media(&ct3d->cxl_dstate); } break; + case 0x4402: /* Media Operations sanitize */ + { + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + __do_sanitize(ct3d); + cxl_dev_enable_media(&ct3d->cxl_dstate); + } + break; case 0x4304: /* scan media */ { CXLType3Dev *ct3d = CXL_TYPE3(cci->d); diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index a64739be25..6d82eb266a 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -581,6 +581,15 @@ typedef struct CXLSetFeatureInfo { size_t data_size; } CXLSetFeatureInfo; +struct CXLSanitizeInfo { + uint32_t dpa_range_count; + uint8_t fill_value; + struct { + uint64_t starting_dpa; + uint64_t length; + } dpa_range_list[0]; +}; + struct CXLType3Dev { /* Private */ PCIDevice parent_obj; @@ -651,6 +660,8 @@ struct CXLType3Dev { uint8_t num_regions; /* 0-8 regions */ CXLDCRegion regions[DCD_MAX_NUM_REGION]; } dc; + + struct CXLSanitizeInfo *media_op_sanitize; }; #define TYPE_CXL_TYPE3 "cxl-type3" -- 2.34.1