@samitouri / QOSamiQemu / commits / 08c6d46621

hw/ufs: Add Host Initiated Defragmentation (HID) support

Emulate the UFS HID extended feature. Host interacts via five attributes (IDN 0x35-0x39): bDefragOperation trigger: Disable / Analysis / Defrag dHIDAvailableSize fragmented 4KB units (published by analysis) dHIDSize host-requested defrag target (4KB units) bHIDProgressRatio 0-100%; reading 100 resets HID bHIDState current state; terminal-state read resets HID Successful user-data SCSI WRITE commands increment an internal fragment counter; HID analysis publishes the counter through dHIDAvailableSize. Defrag operates on min(dHIDSize, dHIDAvailableSize), so a small dHIDSize yields a partial defrag. bDefragOperation auto-clears on terminal state. The state machine advances from ufs_process_idle(); transitions occur only while the device is idle. Signed-off-by: Keoseong Park <keosung.park@samsung.com> Reviewed-by: Jeuk Kim <jeuk20.kim@samsung.com> Signed-off-by: Jeuk Kim <jeuk20.kim@samsung.com>

Keoseong Park committed Jun 9, 2026 at 13:06 UTC 08c6d46621cca906fa701714de87a3de93955d95
5 files changed +188 -8
hw/ufs/lu.c
+15 -1
@@ -154,15 +154,29 @@ static void ufs_wb_process_write_req(UfsRequest *req, uint32_t transfered_len)
154 ufs_wb_update_avail_buffer(u);
155 }
156
157 +#define UFS_HID_MAX_FRAGMENTS 1024
158 +static void ufs_hid_count_fragments(UfsRequest *req)
159 +{
160 + UfsHc *u = req->hc;
161 +
162 + if (!ufs_is_write_req(req)) {
163 + return;
164 + }
165 +
166 + u->hid_fragment_count =
167 + MIN(u->hid_fragment_count + 1, UFS_HID_MAX_FRAGMENTS);
168 +}
169 +
170 static void ufs_scsi_command_complete(SCSIRequest *scsi_req, size_t resid)
171 {
172 UfsRequest *req = scsi_req->hba_private;
173 int16_t status = scsi_req->status;
174 uint32_t transfered_len = scsi_req->cmd.xfer - resid;
175
163 - /* WB accounting should only happen for successful commands */
176 + /* WB / HID accounting should only happen for successful commands */
177 if (status == GOOD) {
178 ufs_wb_process_write_req(req, transfered_len);
179 + ufs_hid_count_fragments(req);
180 }
181
182 ufs_build_scsi_response_upiu(req, scsi_req->sense, scsi_req->sense_len,
hw/ufs/trace-events
+4
@@ -51,3 +51,7 @@ ufs_err_mcq_create_cq_already_exists(uint8_t qid) "mcq cqid %"PRIu8 "already exi
51 ufs_err_mcq_delete_cq_invalid_cqid(uint8_t qid) "invalid mcq cqid %"PRIu8""
52 ufs_err_mcq_delete_cq_not_exists(uint8_t qid) "mcq cqid %"PRIu8 "not exists"
53 ufs_err_mcq_delete_cq_sq_not_deleted(uint8_t sqid, uint8_t cqid) "mcq sq %"PRIu8" still has cq %"PRIu8""
54 +
55 +# HID (Host Initiated Defragmentation)
56 +ufs_hid_defrag_operation(uint8_t op, uint8_t state) "HID defrag operation 0x%"PRIx8", new state 0x%"PRIx8""
57 +ufs_hid_defrag_progress(uint32_t remaining, uint8_t progress) "HID defrag remaining %"PRIu32", progress %"PRIu8"%%"
hw/ufs/ufs.c
+147 -7
@@ -40,6 +40,9 @@
40 #define UFS_TOO_HIGH_TEMP_BOUNDARY 160
41 #define UFS_TOO_LOW_TEMP_BOUNDARY 60
42
43 +#define UFS_HID_DEFRAG_BATCH_DIV 10 /* ~10% of remaining per tick */
44 +#define UFS_HID_PROGRESS_COMPLETE 100
45 +
46 static void ufs_exec_req(UfsRequest *req);
47 static void ufs_clear_req(UfsRequest *req);
48
@@ -1288,10 +1291,9 @@ static const int attr_permission[UFS_QUERY_ATTR_IDN_COUNT] = {
1291 [UFS_QUERY_ATTR_IDN_REFRESH_UNIT] = UFS_QUERY_ATTR_READ,
1292 [UFS_QUERY_ATTR_IDN_TIMESTAMP] = UFS_QUERY_ATTR_WRITE,
1293 [UFS_QUERY_ATTR_IDN_DEVICE_LEVEL_EXCEPTION_ID] = UFS_QUERY_ATTR_READ,
1291 - /* host initiated defragmentation is not supported */
1292 - [UFS_QUERY_ATTR_IDN_DEFRAG_OP] = UFS_QUERY_ATTR_READ,
1294 + [UFS_QUERY_ATTR_IDN_DEFRAG_OP] = UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
1295 [UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE] = UFS_QUERY_ATTR_READ,
1294 - [UFS_QUERY_ATTR_IDN_HID_SIZE] = UFS_QUERY_ATTR_READ,
1296 + [UFS_QUERY_ATTR_IDN_HID_SIZE] = UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
1297 [UFS_QUERY_ATTR_IDN_HID_PROG_RATIO] = UFS_QUERY_ATTR_READ,
1298 [UFS_QUERY_ATTR_IDN_HID_STATE] = UFS_QUERY_ATTR_READ,
1299 [UFS_QUERY_ATTR_IDN_WB_BUFF_RESIZE_HINT] = UFS_QUERY_ATTR_READ,
@@ -1371,8 +1373,31 @@ static inline uint32_t ufs_wb_read_resize_status(UfsHc *u)
1373 return value;
1374 }
1375
1376 +static void ufs_hid_reset(UfsHc *u)
1377 +{
1378 + u->attributes.defrag_op = UFS_HID_OP_DISABLE;
1379 + u->attributes.hid_state = UFS_HID_STATE_IDLE;
1380 + u->attributes.hid_prog_ratio = 0;
1381 + u->attributes.hid_avail_size = cpu_to_be32(0xFFFFFFFF);
1382 + u->hid_defrag_total = 0;
1383 + u->hid_defrag_remaining = 0;
1384 +}
1385 +
1386 +static uint32_t ufs_hid_read_progress_ratio(UfsHc *u)
1387 +{
1388 + uint32_t value = u->attributes.hid_prog_ratio;
1389 +
1390 + if (value == UFS_HID_PROGRESS_COMPLETE) {
1391 + ufs_hid_reset(u);
1392 + }
1393 +
1394 + return value;
1395 +}
1396 +
1397 static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn)
1398 {
1399 + uint8_t state;
1400 +
1401 switch (idn) {
1402 case UFS_QUERY_ATTR_IDN_BOOT_LU_EN:
1403 return u->attributes.boot_lun_en;
@@ -1451,9 +1476,16 @@ static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn)
1476 case UFS_QUERY_ATTR_IDN_HID_SIZE:
1477 return be32_to_cpu(u->attributes.hid_size);
1478 case UFS_QUERY_ATTR_IDN_HID_PROG_RATIO:
1454 - return u->attributes.hid_prog_ratio;
1479 + return ufs_hid_read_progress_ratio(u);
1480 case UFS_QUERY_ATTR_IDN_HID_STATE:
1456 - return u->attributes.hid_state;
1481 + state = u->attributes.hid_state;
1482 +
1483 + if (state == UFS_HID_STATE_DEFRAG_COMPLETED ||
1484 + state == UFS_HID_STATE_DEFRAG_NOT_REQUIRED) {
1485 + ufs_hid_reset(u);
1486 + }
1487 +
1488 + return state;
1489 case UFS_QUERY_ATTR_IDN_WB_BUFF_RESIZE_HINT:
1490 return u->attributes.wb_buffer_resize_hint;
1491 case UFS_QUERY_ATTR_IDN_WB_BUFF_RESIZE_STATUS:
@@ -1604,6 +1636,26 @@ static bool ufs_wb_pinned_min_size(UfsHc *u, uint32_t value)
1636 return true;
1637 }
1638
1639 +static QueryRespCode ufs_hid_write_defrag_operation(UfsHc *u, uint32_t value)
1640 +{
1641 + switch (value) {
1642 + case UFS_HID_OP_DISABLE:
1643 + ufs_hid_reset(u);
1644 + break;
1645 + case UFS_HID_OP_ANALYSIS:
1646 + case UFS_HID_OP_DEFRAG:
1647 + u->attributes.defrag_op = value;
1648 + u->attributes.hid_state = UFS_HID_STATE_ANALYSIS_IN_PROGRESS;
1649 + u->attributes.hid_prog_ratio = 0;
1650 + break;
1651 + default:
1652 + return UFS_QUERY_RESULT_INVALID_VALUE;
1653 + }
1654 +
1655 + trace_ufs_hid_defrag_operation(value, u->attributes.hid_state);
1656 + return UFS_QUERY_RESULT_SUCCESS;
1657 +}
1658 +
1659 static QueryRespCode ufs_write_attr_value(UfsHc *u, uint8_t idn, uint32_t value)
1660 {
1661 switch (idn) {
@@ -1668,6 +1720,11 @@ static QueryRespCode ufs_write_attr_value(UfsHc *u, uint8_t idn, uint32_t value)
1720 return UFS_QUERY_RESULT_INVALID_VALUE;
1721 }
1722 break;
1723 + case UFS_QUERY_ATTR_IDN_DEFRAG_OP:
1724 + return ufs_hid_write_defrag_operation(u, value);
1725 + case UFS_QUERY_ATTR_IDN_HID_SIZE:
1726 + u->attributes.hid_size = cpu_to_be32(value);
1727 + break;
1728 default:
1729 g_assert_not_reached();
1730 return 0;
@@ -2211,11 +2268,92 @@ static void ufs_wb_process_resize(UfsHc *u)
2268 u->attributes.wb_buffer_resize_status = UFS_WB_RESIZE_COMPLETED;
2269 }
2270
2271 +static void ufs_hid_process(UfsHc *u)
2272 +{
2273 + uint32_t requested, batch, done;
2274 +
2275 + switch (u->attributes.hid_state) {
2276 + case UFS_HID_STATE_ANALYSIS_IN_PROGRESS:
2277 + u->attributes.hid_avail_size = cpu_to_be32(u->hid_fragment_count);
2278 + if (u->hid_fragment_count > 0) {
2279 + u->attributes.hid_state = UFS_HID_STATE_DEFRAG_REQUIRED;
2280 + } else {
2281 + u->attributes.hid_state = UFS_HID_STATE_DEFRAG_NOT_REQUIRED;
2282 + }
2283 +
2284 + if (u->attributes.defrag_op == UFS_HID_OP_ANALYSIS ||
2285 + u->attributes.hid_state == UFS_HID_STATE_DEFRAG_NOT_REQUIRED) {
2286 + u->attributes.defrag_op = UFS_HID_OP_DISABLE;
2287 + }
2288 +
2289 + trace_ufs_hid_defrag_operation(u->attributes.defrag_op,
2290 + u->attributes.hid_state);
2291 + break;
2292 +
2293 + case UFS_HID_STATE_DEFRAG_REQUIRED:
2294 + if (u->attributes.defrag_op != UFS_HID_OP_DEFRAG) {
2295 + break;
2296 + }
2297 +
2298 + requested = MIN(be32_to_cpu(u->attributes.hid_size),
2299 + be32_to_cpu(u->attributes.hid_avail_size));
2300 + if (!requested) {
2301 + u->attributes.hid_state = UFS_HID_STATE_DEFRAG_COMPLETED;
2302 + u->attributes.defrag_op = UFS_HID_OP_DISABLE;
2303 + u->attributes.hid_prog_ratio = UFS_HID_PROGRESS_COMPLETE;
2304 +
2305 + trace_ufs_hid_defrag_operation(u->attributes.defrag_op,
2306 + u->attributes.hid_state);
2307 + break;
2308 + }
2309 +
2310 + u->attributes.hid_state = UFS_HID_STATE_DEFRAG_IN_PROGRESS;
2311 + u->hid_defrag_total = requested;
2312 + u->hid_defrag_remaining = requested;
2313 + u->attributes.hid_prog_ratio = 0;
2314 + break;
2315 +
2316 + case UFS_HID_STATE_DEFRAG_IN_PROGRESS:
2317 + if (u->hid_defrag_remaining > 0) {
2318 + batch = u->hid_defrag_remaining / UFS_HID_DEFRAG_BATCH_DIV;
2319 + if (batch == 0) {
2320 + batch = 1;
2321 + }
2322 +
2323 + u->hid_defrag_remaining -= batch;
2324 + u->hid_fragment_count -= batch;
2325 +
2326 + done = u->hid_defrag_total - u->hid_defrag_remaining;
2327 + u->attributes.hid_prog_ratio =
2328 + ((uint64_t)done * UFS_HID_PROGRESS_COMPLETE) /
2329 + u->hid_defrag_total;
2330 +
2331 + trace_ufs_hid_defrag_progress(u->hid_defrag_remaining,
2332 + u->attributes.hid_prog_ratio);
2333 + }
2334 +
2335 + if (!u->hid_defrag_remaining) {
2336 + u->attributes.hid_state = UFS_HID_STATE_DEFRAG_COMPLETED;
2337 + u->attributes.defrag_op = UFS_HID_OP_DISABLE;
2338 + u->attributes.hid_prog_ratio = UFS_HID_PROGRESS_COMPLETE;
2339 +
2340 + trace_ufs_hid_defrag_operation(u->attributes.defrag_op,
2341 + u->attributes.hid_state);
2342 + }
2343 +
2344 + break;
2345 +
2346 + default:
2347 + break;
2348 + }
2349 +}
2350 +
2351 static void ufs_process_idle(UfsHc *u)
2352 {
2353 ufs_wb_process_flush(u);
2354 ufs_wb_process_resize(u);
2355 ufs_wb_sync_buffer_size(u);
2356 + ufs_hid_process(u);
2357 }
2358
2359 static inline bool ufs_check_idle(UfsHc *u)
@@ -2384,8 +2522,8 @@ static void ufs_init_hc(UfsHc *u)
2522 uint32_t mcqconfig = 0;
2523 uint32_t mcqcap = 0;
2524 uint32_t ext_wb_sup = WB_RESIZE | WB_FIFO | WB_PINNED;
2387 - uint32_t ext_ufs_feat_sup =
2388 - UFS_DEV_WB_SUPPORT | UFS_DEV_HIGH_TEMP_NOTIF | UFS_DEV_LOW_TEMP_NOTIF;
2525 + uint32_t ext_ufs_feat_sup = UFS_DEV_WB_SUPPORT | UFS_DEV_HIGH_TEMP_NOTIF |
2526 + UFS_DEV_LOW_TEMP_NOTIF | UFS_DEV_HID_SUPPORT;
2527 int64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT);
2528
2529 u->reg_size = pow2ceil(ufs_reg_size(u));
@@ -2487,6 +2625,8 @@ static void ufs_init_hc(UfsHc *u)
2625 u->attributes.max_num_of_rtt = 0x02;
2626 u->attributes.device_too_high_temp_boundary = UFS_TOO_HIGH_TEMP_BOUNDARY;
2627 u->attributes.device_too_low_temp_boundary = UFS_TOO_LOW_TEMP_BOUNDARY;
2628 + u->attributes.hid_avail_size = cpu_to_be32(0xFFFFFFFF);
2629 + u->attributes.hid_size = cpu_to_be32(0xFFFFFFFF);
2630
2631 memset(&u->flags, 0, sizeof(u->flags));
2632 u->flags.permanently_disable_fw_update = 1;
hw/ufs/ufs.h
+4
@@ -175,6 +175,10 @@ typedef struct UfsHc {
175 uint8_t temperature;
176
177 QEMUTimer idle_timer;
178 +
179 + uint32_t hid_fragment_count; /* Remaining fragmented 4KB units */
180 + uint32_t hid_defrag_total; /* Requested units at defrag start */
181 + uint32_t hid_defrag_remaining; /* Requested units left to move */
182 } UfsHc;
183
184 static inline uint32_t ufs_mcq_sq_tail(UfsHc *u, uint32_t qid)
include/block/ufs.h
+18
@@ -951,6 +951,23 @@ enum attr_idn {
951 UFS_QUERY_ATTR_IDN_COUNT,
952 };
953
954 +/* HID (Host Initiated Defragmentation) operation values for bDefragOperation */
955 +enum ufs_hid_op {
956 + UFS_HID_OP_DISABLE = 0x00,
957 + UFS_HID_OP_ANALYSIS = 0x01,
958 + UFS_HID_OP_DEFRAG = 0x02,
959 +};
960 +
961 +/* HID state values for bHIDState */
962 +enum ufs_hid_state {
963 + UFS_HID_STATE_IDLE = 0x00,
964 + UFS_HID_STATE_ANALYSIS_IN_PROGRESS = 0x01,
965 + UFS_HID_STATE_DEFRAG_REQUIRED = 0x02,
966 + UFS_HID_STATE_DEFRAG_IN_PROGRESS = 0x03,
967 + UFS_HID_STATE_DEFRAG_COMPLETED = 0x04,
968 + UFS_HID_STATE_DEFRAG_NOT_REQUIRED = 0x05,
969 +};
970 +
971 /* Descriptor idn for Query requests */
972 enum desc_idn {
973 UFS_QUERY_DESC_IDN_DEVICE = 0x0,
@@ -1142,6 +1159,7 @@ enum {
1159 /* Possible values for dExtendedUFSFeaturesSupport */
1160 enum {
1161 UFS_DEV_WB_SUPPORT = BIT(8),
1162 + UFS_DEV_HID_SUPPORT = BIT(13),
1163 };
1164
1165 /* WriteBooster buffer mode */