Merge tag 'pull-ufs-20260612' of https://gitlab.com/jeuk20.kim/qemu into staging

ufs queue

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCgAdFiEEUBfYMVl8eKPZB+73EuIgTA5dtgIFAmorUfAACgkQEuIgTA5d
# tgIQWA/+MkEs+rKdSTQbFOgR4LoK8YEDO0M1cxF3x8NVcWYxwyNrotOk+4dRiSWH
# zy+PaiCmibHgqgtluPkFc1Zzo6iFD/5h6/wb3yJiNfiOTiCbYIzLrIEn/LUqwXtp
# YKNBEKaQSnWFjYF0oRGOeblMQ+KOTmna7vR/zsoLx7S3N9IC0tJB/AUIXBcpW2ur
# d7Qg+YTl/mGlOYQcZ3njBAjf0k2LVuG1TUuMAyBfEcz2fjC6fgR2plKURRjuEL3R
# aGENupphIomHTxIJUZIw+PyOCJ0sdV4/rwUbQGcM5hQni+6dny8ywAZj7obXmAGJ
# esRP2WVV3GVRyNNKUqlEuiD+ReUKJHkmCpzGYcaI41YKC097GEMsJzdk/KpiU5Gl
# e4L6KMvDuwk8HXM5cfK+hvBz279vY30l9+s3Oa3ConZdQC4wXN9OJBNJkboP23HL
# +yLsiSxYaBIbA8/RNNOZmI8VauAMuMmlTG0ukZsVvpk5FUOVeo9c4sDf+2bLrOyG
# YTCTL9fqrWZZCWLXava69+rGQTrPmAA8UvKO5unGz4Wf1yZyYhDD4SWnbFye0U+V
# FCqNXvtg7IAV0spxIhGx0D3Ypdb1AbROEXI4kU7kdiNgsie53HsENLCY/fCv6PY+
# HNxGCx9HpadKVG+pm8lEJgFH/R7vf4qWB9K/TZ08KFr2txXjUwg=
# =NmLP
# -----END PGP SIGNATURE-----
# gpg: Signature made Thu 11 Jun 2026 20:25:20 EDT
# gpg:                using RSA key 5017D831597C78A3D907EEF712E2204C0E5DB602
# gpg: Good signature from "Jeuk Kim <jeuk20.kim@samsung.com>" [unknown]
# gpg:                 aka "Jeuk Kim <jeuk20.kim@gmail.com>" [unknown]
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: 5017 D831 597C 78A3 D907  EEF7 12E2 204C 0E5D B602

* tag 'pull-ufs-20260612' of https://gitlab.com/jeuk20.kim/qemu:
  tests/qtest: Add UFS HID qtest
  hw/ufs: Add Host Initiated Defragmentation (HID) support

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi
2026-06-12 09:01:07 -04:00
6 changed files with 463 additions and 8 deletions

View File

@@ -154,15 +154,29 @@ static void ufs_wb_process_write_req(UfsRequest *req, uint32_t transfered_len)
ufs_wb_update_avail_buffer(u);
}
#define UFS_HID_MAX_FRAGMENTS 1024
static void ufs_hid_count_fragments(UfsRequest *req)
{
UfsHc *u = req->hc;
if (!ufs_is_write_req(req)) {
return;
}
u->hid_fragment_count =
MIN(u->hid_fragment_count + 1, UFS_HID_MAX_FRAGMENTS);
}
static void ufs_scsi_command_complete(SCSIRequest *scsi_req, size_t resid)
{
UfsRequest *req = scsi_req->hba_private;
int16_t status = scsi_req->status;
uint32_t transfered_len = scsi_req->cmd.xfer - resid;
/* WB accounting should only happen for successful commands */
/* WB / HID accounting should only happen for successful commands */
if (status == GOOD) {
ufs_wb_process_write_req(req, transfered_len);
ufs_hid_count_fragments(req);
}
ufs_build_scsi_response_upiu(req, scsi_req->sense, scsi_req->sense_len,

View File

@@ -51,3 +51,7 @@ ufs_err_mcq_create_cq_already_exists(uint8_t qid) "mcq cqid %"PRIu8 "already exi
ufs_err_mcq_delete_cq_invalid_cqid(uint8_t qid) "invalid mcq cqid %"PRIu8""
ufs_err_mcq_delete_cq_not_exists(uint8_t qid) "mcq cqid %"PRIu8 "not exists"
ufs_err_mcq_delete_cq_sq_not_deleted(uint8_t sqid, uint8_t cqid) "mcq sq %"PRIu8" still has cq %"PRIu8""
# HID (Host Initiated Defragmentation)
ufs_hid_defrag_operation(uint8_t op, uint8_t state) "HID defrag operation 0x%"PRIx8", new state 0x%"PRIx8""
ufs_hid_defrag_progress(uint32_t remaining, uint8_t progress) "HID defrag remaining %"PRIu32", progress %"PRIu8"%%"

View File

@@ -40,6 +40,9 @@
#define UFS_TOO_HIGH_TEMP_BOUNDARY 160
#define UFS_TOO_LOW_TEMP_BOUNDARY 60
#define UFS_HID_DEFRAG_BATCH_DIV 10 /* ~10% of remaining per tick */
#define UFS_HID_PROGRESS_COMPLETE 100
static void ufs_exec_req(UfsRequest *req);
static void ufs_clear_req(UfsRequest *req);
@@ -1288,10 +1291,9 @@ static const int attr_permission[UFS_QUERY_ATTR_IDN_COUNT] = {
[UFS_QUERY_ATTR_IDN_REFRESH_UNIT] = UFS_QUERY_ATTR_READ,
[UFS_QUERY_ATTR_IDN_TIMESTAMP] = UFS_QUERY_ATTR_WRITE,
[UFS_QUERY_ATTR_IDN_DEVICE_LEVEL_EXCEPTION_ID] = UFS_QUERY_ATTR_READ,
/* host initiated defragmentation is not supported */
[UFS_QUERY_ATTR_IDN_DEFRAG_OP] = UFS_QUERY_ATTR_READ,
[UFS_QUERY_ATTR_IDN_DEFRAG_OP] = UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
[UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE] = UFS_QUERY_ATTR_READ,
[UFS_QUERY_ATTR_IDN_HID_SIZE] = UFS_QUERY_ATTR_READ,
[UFS_QUERY_ATTR_IDN_HID_SIZE] = UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
[UFS_QUERY_ATTR_IDN_HID_PROG_RATIO] = UFS_QUERY_ATTR_READ,
[UFS_QUERY_ATTR_IDN_HID_STATE] = UFS_QUERY_ATTR_READ,
[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)
return value;
}
static void ufs_hid_reset(UfsHc *u)
{
u->attributes.defrag_op = UFS_HID_OP_DISABLE;
u->attributes.hid_state = UFS_HID_STATE_IDLE;
u->attributes.hid_prog_ratio = 0;
u->attributes.hid_avail_size = cpu_to_be32(0xFFFFFFFF);
u->hid_defrag_total = 0;
u->hid_defrag_remaining = 0;
}
static uint32_t ufs_hid_read_progress_ratio(UfsHc *u)
{
uint32_t value = u->attributes.hid_prog_ratio;
if (value == UFS_HID_PROGRESS_COMPLETE) {
ufs_hid_reset(u);
}
return value;
}
static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn)
{
uint8_t state;
switch (idn) {
case UFS_QUERY_ATTR_IDN_BOOT_LU_EN:
return u->attributes.boot_lun_en;
@@ -1451,9 +1476,16 @@ static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn)
case UFS_QUERY_ATTR_IDN_HID_SIZE:
return be32_to_cpu(u->attributes.hid_size);
case UFS_QUERY_ATTR_IDN_HID_PROG_RATIO:
return u->attributes.hid_prog_ratio;
return ufs_hid_read_progress_ratio(u);
case UFS_QUERY_ATTR_IDN_HID_STATE:
return u->attributes.hid_state;
state = u->attributes.hid_state;
if (state == UFS_HID_STATE_DEFRAG_COMPLETED ||
state == UFS_HID_STATE_DEFRAG_NOT_REQUIRED) {
ufs_hid_reset(u);
}
return state;
case UFS_QUERY_ATTR_IDN_WB_BUFF_RESIZE_HINT:
return u->attributes.wb_buffer_resize_hint;
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)
return true;
}
static QueryRespCode ufs_hid_write_defrag_operation(UfsHc *u, uint32_t value)
{
switch (value) {
case UFS_HID_OP_DISABLE:
ufs_hid_reset(u);
break;
case UFS_HID_OP_ANALYSIS:
case UFS_HID_OP_DEFRAG:
u->attributes.defrag_op = value;
u->attributes.hid_state = UFS_HID_STATE_ANALYSIS_IN_PROGRESS;
u->attributes.hid_prog_ratio = 0;
break;
default:
return UFS_QUERY_RESULT_INVALID_VALUE;
}
trace_ufs_hid_defrag_operation(value, u->attributes.hid_state);
return UFS_QUERY_RESULT_SUCCESS;
}
static QueryRespCode ufs_write_attr_value(UfsHc *u, uint8_t idn, uint32_t value)
{
switch (idn) {
@@ -1668,6 +1720,11 @@ static QueryRespCode ufs_write_attr_value(UfsHc *u, uint8_t idn, uint32_t value)
return UFS_QUERY_RESULT_INVALID_VALUE;
}
break;
case UFS_QUERY_ATTR_IDN_DEFRAG_OP:
return ufs_hid_write_defrag_operation(u, value);
case UFS_QUERY_ATTR_IDN_HID_SIZE:
u->attributes.hid_size = cpu_to_be32(value);
break;
default:
g_assert_not_reached();
return 0;
@@ -2211,11 +2268,92 @@ static void ufs_wb_process_resize(UfsHc *u)
u->attributes.wb_buffer_resize_status = UFS_WB_RESIZE_COMPLETED;
}
static void ufs_hid_process(UfsHc *u)
{
uint32_t requested, batch, done;
switch (u->attributes.hid_state) {
case UFS_HID_STATE_ANALYSIS_IN_PROGRESS:
u->attributes.hid_avail_size = cpu_to_be32(u->hid_fragment_count);
if (u->hid_fragment_count > 0) {
u->attributes.hid_state = UFS_HID_STATE_DEFRAG_REQUIRED;
} else {
u->attributes.hid_state = UFS_HID_STATE_DEFRAG_NOT_REQUIRED;
}
if (u->attributes.defrag_op == UFS_HID_OP_ANALYSIS ||
u->attributes.hid_state == UFS_HID_STATE_DEFRAG_NOT_REQUIRED) {
u->attributes.defrag_op = UFS_HID_OP_DISABLE;
}
trace_ufs_hid_defrag_operation(u->attributes.defrag_op,
u->attributes.hid_state);
break;
case UFS_HID_STATE_DEFRAG_REQUIRED:
if (u->attributes.defrag_op != UFS_HID_OP_DEFRAG) {
break;
}
requested = MIN(be32_to_cpu(u->attributes.hid_size),
be32_to_cpu(u->attributes.hid_avail_size));
if (!requested) {
u->attributes.hid_state = UFS_HID_STATE_DEFRAG_COMPLETED;
u->attributes.defrag_op = UFS_HID_OP_DISABLE;
u->attributes.hid_prog_ratio = UFS_HID_PROGRESS_COMPLETE;
trace_ufs_hid_defrag_operation(u->attributes.defrag_op,
u->attributes.hid_state);
break;
}
u->attributes.hid_state = UFS_HID_STATE_DEFRAG_IN_PROGRESS;
u->hid_defrag_total = requested;
u->hid_defrag_remaining = requested;
u->attributes.hid_prog_ratio = 0;
break;
case UFS_HID_STATE_DEFRAG_IN_PROGRESS:
if (u->hid_defrag_remaining > 0) {
batch = u->hid_defrag_remaining / UFS_HID_DEFRAG_BATCH_DIV;
if (batch == 0) {
batch = 1;
}
u->hid_defrag_remaining -= batch;
u->hid_fragment_count -= batch;
done = u->hid_defrag_total - u->hid_defrag_remaining;
u->attributes.hid_prog_ratio =
((uint64_t)done * UFS_HID_PROGRESS_COMPLETE) /
u->hid_defrag_total;
trace_ufs_hid_defrag_progress(u->hid_defrag_remaining,
u->attributes.hid_prog_ratio);
}
if (!u->hid_defrag_remaining) {
u->attributes.hid_state = UFS_HID_STATE_DEFRAG_COMPLETED;
u->attributes.defrag_op = UFS_HID_OP_DISABLE;
u->attributes.hid_prog_ratio = UFS_HID_PROGRESS_COMPLETE;
trace_ufs_hid_defrag_operation(u->attributes.defrag_op,
u->attributes.hid_state);
}
break;
default:
break;
}
}
static void ufs_process_idle(UfsHc *u)
{
ufs_wb_process_flush(u);
ufs_wb_process_resize(u);
ufs_wb_sync_buffer_size(u);
ufs_hid_process(u);
}
static inline bool ufs_check_idle(UfsHc *u)
@@ -2384,8 +2522,8 @@ static void ufs_init_hc(UfsHc *u)
uint32_t mcqconfig = 0;
uint32_t mcqcap = 0;
uint32_t ext_wb_sup = WB_RESIZE | WB_FIFO | WB_PINNED;
uint32_t ext_ufs_feat_sup =
UFS_DEV_WB_SUPPORT | UFS_DEV_HIGH_TEMP_NOTIF | UFS_DEV_LOW_TEMP_NOTIF;
uint32_t ext_ufs_feat_sup = UFS_DEV_WB_SUPPORT | UFS_DEV_HIGH_TEMP_NOTIF |
UFS_DEV_LOW_TEMP_NOTIF | UFS_DEV_HID_SUPPORT;
int64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT);
u->reg_size = pow2ceil(ufs_reg_size(u));
@@ -2487,6 +2625,8 @@ static void ufs_init_hc(UfsHc *u)
u->attributes.max_num_of_rtt = 0x02;
u->attributes.device_too_high_temp_boundary = UFS_TOO_HIGH_TEMP_BOUNDARY;
u->attributes.device_too_low_temp_boundary = UFS_TOO_LOW_TEMP_BOUNDARY;
u->attributes.hid_avail_size = cpu_to_be32(0xFFFFFFFF);
u->attributes.hid_size = cpu_to_be32(0xFFFFFFFF);
memset(&u->flags, 0, sizeof(u->flags));
u->flags.permanently_disable_fw_update = 1;

View File

@@ -175,6 +175,10 @@ typedef struct UfsHc {
uint8_t temperature;
QEMUTimer idle_timer;
uint32_t hid_fragment_count; /* Remaining fragmented 4KB units */
uint32_t hid_defrag_total; /* Requested units at defrag start */
uint32_t hid_defrag_remaining; /* Requested units left to move */
} UfsHc;
static inline uint32_t ufs_mcq_sq_tail(UfsHc *u, uint32_t qid)

View File

@@ -951,6 +951,23 @@ enum attr_idn {
UFS_QUERY_ATTR_IDN_COUNT,
};
/* HID (Host Initiated Defragmentation) operation values for bDefragOperation */
enum ufs_hid_op {
UFS_HID_OP_DISABLE = 0x00,
UFS_HID_OP_ANALYSIS = 0x01,
UFS_HID_OP_DEFRAG = 0x02,
};
/* HID state values for bHIDState */
enum ufs_hid_state {
UFS_HID_STATE_IDLE = 0x00,
UFS_HID_STATE_ANALYSIS_IN_PROGRESS = 0x01,
UFS_HID_STATE_DEFRAG_REQUIRED = 0x02,
UFS_HID_STATE_DEFRAG_IN_PROGRESS = 0x03,
UFS_HID_STATE_DEFRAG_COMPLETED = 0x04,
UFS_HID_STATE_DEFRAG_NOT_REQUIRED = 0x05,
};
/* Descriptor idn for Query requests */
enum desc_idn {
UFS_QUERY_DESC_IDN_DEVICE = 0x0,
@@ -1142,6 +1159,7 @@ enum {
/* Possible values for dExtendedUFSFeaturesSupport */
enum {
UFS_DEV_WB_SUPPORT = BIT(8),
UFS_DEV_HID_SUPPORT = BIT(13),
};
/* WriteBooster buffer mode */

View File

@@ -1386,6 +1386,280 @@ static void *ufs_blk_test_setup(GString *cmd_line, void *arg)
return arg;
}
/*
* Helper to read a single HID attribute value.
* Returns the host-endian attribute value; asserts OCS success.
*/
static uint32_t ufs_hid_read_attr(QUfs *ufs, uint8_t idn)
{
enum UtpOcsCodes ocs;
UtpUpiuRsp rsp;
ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
UFS_UPIU_QUERY_OPCODE_READ_ATTR, idn, 0, 0, 0, &rsp);
g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
g_assert_cmpuint(rsp.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
return be32_to_cpu(rsp.qr.value);
}
/*
* Helper to write a single HID attribute value.
* Asserts OCS success.
*/
static void ufs_hid_write_attr(QUfs *ufs, uint8_t idn, uint32_t value)
{
enum UtpOcsCodes ocs;
UtpUpiuRsp rsp;
ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, idn, 0, 0, value,
&rsp);
g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
g_assert_cmpuint(rsp.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
}
static uint32_t ufs_hid_wait_leave_state(QUfs *ufs, uint32_t from)
{
uint64_t end_time =
g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND;
uint32_t state;
do {
qtest_clock_step(ufs->dev.bus->qts, 100);
state = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE);
} while (state == from && g_get_monotonic_time() < end_time);
return state;
}
static uint32_t ufs_hid_wait_state(QUfs *ufs, uint32_t expected)
{
uint64_t end_time =
g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND;
uint32_t state;
do {
qtest_clock_step(ufs->dev.bus->qts, 100);
state = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE);
} while (state != expected && g_get_monotonic_time() < end_time);
g_assert_cmpuint(state, ==, expected);
return state;
}
static void ufs_hid_wait_progress_complete(QUfs *ufs)
{
uint64_t end_time =
g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND;
uint32_t val;
do {
qtest_clock_step(ufs->dev.bus->qts, 100);
val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_PROG_RATIO);
} while (val != 100 && g_get_monotonic_time() < end_time);
g_assert_cmpuint(val, ==, 100);
}
static void ufstest_hid(void *obj, void *data, QGuestAllocator *alloc)
{
QUfs *ufs = obj;
uint32_t val;
enum UtpOcsCodes ocs;
UtpUpiuRsp rsp;
const int test_lun = 1;
const uint8_t request_sense_cdb[UFS_CDB_SIZE] = {
REQUEST_SENSE,
};
const uint8_t write_cdb[UFS_CDB_SIZE] = {
/* WRITE(10) to LBA 0, transfer length 8 sectors = 1 fragment */
WRITE_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00
};
uint8_t write_buf[4096];
uint8_t read_buf[4096];
int i;
ufs_init(ufs, alloc);
/* Clear Unit Attention */
ufs_send_scsi_command(ufs, test_lun, request_sense_cdb, NULL, 0, read_buf,
sizeof(read_buf), &rsp);
/*
* 1. Verify HID default attribute values
*/
val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP);
g_assert_cmpuint(val, ==, UFS_HID_OP_DISABLE);
val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE);
g_assert_cmpuint(val, ==, 0xFFFFFFFF);
val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_SIZE);
g_assert_cmpuint(val, ==, 0xFFFFFFFF);
val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_PROG_RATIO);
g_assert_cmpuint(val, ==, 0);
val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE);
g_assert_cmpuint(val, ==, UFS_HID_STATE_IDLE);
/*
* 2. Verify read-only attributes reject writes
*/
ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
UFS_UPIU_QUERY_OPCODE_WRITE_ATTR,
UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE, 0, 0, 0x100, &rsp);
g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR);
g_assert_cmpuint(rsp.header.response, ==, UFS_QUERY_RESULT_NOT_WRITEABLE);
ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
UFS_UPIU_QUERY_OPCODE_WRITE_ATTR,
UFS_QUERY_ATTR_IDN_HID_PROG_RATIO, 0, 0, 50, &rsp);
g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR);
g_assert_cmpuint(rsp.header.response, ==, UFS_QUERY_RESULT_NOT_WRITEABLE);
ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
UFS_UPIU_QUERY_OPCODE_WRITE_ATTR,
UFS_QUERY_ATTR_IDN_HID_STATE, 0, 0, 0x01, &rsp);
g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR);
g_assert_cmpuint(rsp.header.response, ==, UFS_QUERY_RESULT_NOT_WRITEABLE);
/*
* 3. Verify invalid bDefragOperation value is rejected
*/
ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
UFS_UPIU_QUERY_OPCODE_WRITE_ATTR,
UFS_QUERY_ATTR_IDN_DEFRAG_OP, 0, 0, 0x03, &rsp);
g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR);
g_assert_cmpuint(rsp.header.response, ==, UFS_QUERY_RESULT_INVALID_VALUE);
/*
* 4. dHIDSize is writable and readable
*/
ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_HID_SIZE, 0x100);
val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_SIZE);
g_assert_cmpuint(val, ==, 0x100);
/* Restore default */
ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_HID_SIZE, 0xFFFFFFFF);
/*
* 5. Analysis with no fragments -> Defrag Not Required, then reading
* bHIDState in a terminal state auto-resets HID to Idle.
*/
ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP, UFS_HID_OP_ANALYSIS);
val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE);
g_assert_cmpuint(val, ==, UFS_HID_STATE_ANALYSIS_IN_PROGRESS);
/* Idle handler advances analysis; first non-analysis read is decisive */
val = ufs_hid_wait_leave_state(ufs, UFS_HID_STATE_ANALYSIS_IN_PROGRESS);
g_assert_cmpuint(val, ==, UFS_HID_STATE_DEFRAG_NOT_REQUIRED);
/* Reading Not Required auto-reset to Idle */
val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE);
g_assert_cmpuint(val, ==, UFS_HID_STATE_IDLE);
val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE);
g_assert_cmpuint(val, ==, 0xFFFFFFFF);
/*
* 6. Generate fragmentation via SCSI WRITE, then analyze (analysis
* only): the machine settles at Defrag Required.
*/
memset(write_buf, 0xab, sizeof(write_buf));
for (i = 0; i < 4; i++) {
ocs = ufs_send_scsi_command(ufs, test_lun, write_cdb, write_buf,
sizeof(write_buf), NULL, 0, &rsp);
g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
}
ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP, UFS_HID_OP_ANALYSIS);
val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE);
g_assert_cmpuint(val, ==, UFS_HID_STATE_ANALYSIS_IN_PROGRESS);
val = ufs_hid_wait_leave_state(ufs, UFS_HID_STATE_ANALYSIS_IN_PROGRESS);
g_assert_cmpuint(val, ==, UFS_HID_STATE_DEFRAG_REQUIRED);
val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP);
g_assert_cmpuint(val, ==, UFS_HID_OP_DISABLE);
val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE);
g_assert_cmpuint(val, >, 0);
/*
* 7. Disable resets to Idle from any state
*/
ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP, UFS_HID_OP_DISABLE);
val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE);
g_assert_cmpuint(val, ==, UFS_HID_STATE_IDLE);
/*
* 8. Partial defrag cycle: dHIDSize limits the requested defrag size,
* and reading bHIDProgressRatio at 100% resets the HID attributes.
*/
for (i = 0; i < 4; i++) {
ocs = ufs_send_scsi_command(ufs, test_lun, write_cdb, write_buf,
sizeof(write_buf), NULL, 0, &rsp);
g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
}
ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_HID_SIZE, 2);
ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP, UFS_HID_OP_DEFRAG);
/* Should start in Analysis In Progress */
val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE);
g_assert_cmpuint(val, ==, UFS_HID_STATE_ANALYSIS_IN_PROGRESS);
ufs_hid_wait_progress_complete(ufs);
val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE);
g_assert_cmpuint(val, ==, 0xFFFFFFFF);
val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP);
g_assert_cmpuint(val, ==, UFS_HID_OP_DISABLE);
val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_PROG_RATIO);
g_assert_cmpuint(val, ==, 0);
/*
* Re-analyze after the partial defrag. There were 8 fragments before
* defrag, and dHIDSize limited the completed operation to 2.
*/
ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP, UFS_HID_OP_ANALYSIS);
val = ufs_hid_wait_leave_state(ufs, UFS_HID_STATE_ANALYSIS_IN_PROGRESS);
g_assert_cmpuint(val, ==, UFS_HID_STATE_DEFRAG_REQUIRED);
val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE);
g_assert_cmpuint(val, ==, 6);
ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP, UFS_HID_OP_DISABLE);
ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_HID_SIZE, 0xFFFFFFFF);
/*
* 9. Full defrag cycle: reading bHIDState in Completed state returns
* Completed once and resets HID attributes to Idle/default values.
*/
ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP, UFS_HID_OP_DEFRAG);
val = ufs_hid_wait_state(ufs, UFS_HID_STATE_DEFRAG_COMPLETED);
g_assert_cmpuint(val, ==, UFS_HID_STATE_DEFRAG_COMPLETED);
val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE);
g_assert_cmpuint(val, ==, UFS_HID_STATE_IDLE);
val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP);
g_assert_cmpuint(val, ==, UFS_HID_OP_DISABLE);
val = ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE);
g_assert_cmpuint(val, ==, 0xFFFFFFFF);
ufs_exit(ufs, alloc);
}
static void ufs_register_nodes(void)
{
const char *arch;
@@ -1439,6 +1713,7 @@ static void ufs_register_nodes(void)
&io_test_opts);
qos_add_test("wb-init", "ufs", ufstest_wb_init, &wb_test_opts);
qos_add_test("wb-read-write", "ufs", ufstest_wb_read_write, &wb_test_opts);
qos_add_test("hid", "ufs", ufstest_hid, &io_test_opts);
}
libqos_init(ufs_register_nodes);