From nobody Sat Jun 13 12:19:15 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=samsung.com ARC-Seal: i=1; a=rsa-sha256; t=1780978067; cv=none; d=zohomail.com; s=zohoarc; b=KEG5EY91FxkuMVzifv0ElxN0M1Epyi3iWobUGpakhV+DXVlQ175YhX3gaOfMCvuKlMOAeBaCytvtT4f7I3aGvJSJpJvhEjIjxODMQMu40taxXmUdQ64uQWp8c9bBGi9uFFF5DboxThjC4QqqKMoOSi+MklJKebK5FdL7Xg0vtfY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1780978067; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Sender:Subject:Subject:To:To:Message-Id; bh=yionqtf8uC9yRu7laH25iKVNmDeNUChoP17GRAaM6Gw=; b=ISG32dnKxVzWLw85cPDrgkF+EX0Csv5hk/YkypDdgQFwRFvqoFE+16TeH2YTbq0dLlOSCjlpoJwbs4dWjdtjqFJbalQLN/L6t9t/nlK1U+x0EfkYWLm8yYSxge3d+9Tj5s+dv/WuP11Xv73Z8FQQZU49zGhAMVKeW24FpKstLxk= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1780978066946380.586318075298; Mon, 8 Jun 2026 21:07:46 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wWnk1-0001oQ-Ly; Tue, 09 Jun 2026 00:07:05 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wWnjw-0001oB-4l for qemu-devel@nongnu.org; Tue, 09 Jun 2026 00:07:00 -0400 Received: from mailout3.samsung.com ([203.254.224.33]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wWnjq-0004ec-VF for qemu-devel@nongnu.org; Tue, 09 Jun 2026 00:06:59 -0400 Received: from epcas2p1.samsung.com (unknown [182.195.41.53]) by mailout3.samsung.com (KnoxPortal) with ESMTP id 20260609040648epoutp0381fc00509fdfd94b6b8614780b0482f3~3Tg-35qjs2580725807epoutp03G for ; Tue, 9 Jun 2026 04:06:48 +0000 (GMT) Received: from epsnrtp03.localdomain (unknown [182.195.42.155]) by epcas2p4.samsung.com (KnoxPortal) with ESMTPS id 20260609040647epcas2p4a0db772a289a65d74fbaefd389139239~3Tg-oD0zv2170821708epcas2p4m; Tue, 9 Jun 2026 04:06:47 +0000 (GMT) Received: from epcas2p2.samsung.com (unknown [182.195.38.202]) by epsnrtp03.localdomain (Postfix) with ESMTP id 4gZFj33tSMz3hhT7; Tue, 9 Jun 2026 04:06:47 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout3.samsung.com 20260609040648epoutp0381fc00509fdfd94b6b8614780b0482f3~3Tg-35qjs2580725807epoutp03G DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1780978008; bh=yionqtf8uC9yRu7laH25iKVNmDeNUChoP17GRAaM6Gw=; h=Subject:Reply-To:From:To:CC:In-Reply-To:Date:References:From; b=CLWHgbLdhMY5v/mQcrPr19JO3BybQFCqfg5yorYtsVqRY7XC+3W3Nkqp+pC+1cZrb O4iMu3HuPXrokexj+u5wiRdwQQpWdM1NorzsDBsVCNwDgNQRtdYbA8xS6Qss5075Vy DiKriuwnGZDwM5X5m/Dj7V3tHuVVXkGNzDXQawl8= Mime-Version: 1.0 Subject: [PATCH 1/2] hw/ufs: Add Host Initiated Defragmentation (HID) support From: Keoseong Park To: Jeuk Kim , "kwolf@redhat.com" , "hreitz@redhat.com" CC: "qemu-devel@nongnu.org" , "qemu-block@nongnu.org" , Keoseong Park X-Priority: 3 X-Content-Kind-Code: NORMAL In-Reply-To: <20260609023909epcms2p3f8856f12fed093168aff636f8dc8f994@epcms2p3> X-CPGS-Detection: blocking_info_exchange X-Drm-Type: N,general X-Msg-Generator: Mail X-Msg-Type: PERSONAL X-Reply-Demand: N Message-ID: <20260609040646epcms2p676387fefbfd1c652e76b74c7f84de921@epcms2p6> Date: Tue, 09 Jun 2026 13:06:46 +0900 X-CMS-MailID: 20260609040646epcms2p676387fefbfd1c652e76b74c7f84de921 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" X-Sendblock-Type: AUTO_CONFIDENTIAL CMS-TYPE: 102P X-CPGSPASS: Y X-CPGSPASS: Y cpgsPolicy: CPGSC10-223,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20260609023909epcms2p3f8856f12fed093168aff636f8dc8f994 References: <20260609023909epcms2p3f8856f12fed093168aff636f8dc8f994@epcms2p3> Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=203.254.224.33; envelope-from=keosung.park@samsung.com; helo=mailout3.samsung.com X-Spam_score_int: -31 X-Spam_score: -3.2 X-Spam_bar: --- X-Spam_report: (-3.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.445, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: keosung.park@samsung.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @samsung.com) X-ZM-MESSAGEID: 1780978070542154100 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 Reviewed-by: Jeuk Kim --- hw/ufs/lu.c | 16 ++++- hw/ufs/trace-events | 4 ++ hw/ufs/ufs.c | 155 ++++++++++++++++++++++++++++++++++++++++++-- hw/ufs/ufs.h | 4 ++ include/block/ufs.h | 18 +++++ 5 files changed, 190 insertions(+), 7 deletions(-) diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c index f13fc6e342..3cbc904e08 100644 --- a/hw/ufs/lu.c +++ b/hw/ufs/lu.c @@ -154,15 +154,29 @@ static void ufs_wb_process_write_req(UfsRequest *req,= uint32_t transfered_len) ufs_wb_update_avail_buffer(u); } =20 +#define UFS_HID_MAX_FRAGMENTS 1024 +static void ufs_hid_count_fragments(UfsRequest *req) +{ + UfsHc *u =3D req->hc; + + if (!ufs_is_write_req(req)) { + return; + } + + u->hid_fragment_count =3D MIN(u->hid_fragment_count + 1, + UFS_HID_MAX_FRAGMENTS); +} + static void ufs_scsi_command_complete(SCSIRequest *scsi_req, size_t resid) { UfsRequest *req =3D scsi_req->hba_private; int16_t status =3D scsi_req->status; uint32_t transfered_len =3D scsi_req->cmd.xfer - resid; =20 - /* WB accounting should only happen for successful commands */ + /* WB / HID accounting should only happen for successful commands */ if (status =3D=3D GOOD) { ufs_wb_process_write_req(req, transfered_len); + ufs_hid_count_fragments(req); } =20 ufs_build_scsi_response_upiu(req, scsi_req->sense, scsi_req->sense_len, diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events index 6f7ea9c95f..662d9afee3 100644 --- a/hw/ufs/trace-events +++ b/hw/ufs/trace-events @@ -51,3 +51,7 @@ ufs_err_mcq_create_cq_already_exists(uint8_t qid) "mcq cq= id %"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 exist= s" 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"%%" diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 6780d73174..757c851d61 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -40,6 +40,9 @@ #define UFS_TOO_HIGH_TEMP_BOUNDARY 160 #define UFS_TOO_LOW_TEMP_BOUNDARY 60 =20 +#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); =20 @@ -1288,10 +1291,11 @@ static const int attr_permission[UFS_QUERY_ATTR_IDN= _COUNT] =3D { [UFS_QUERY_ATTR_IDN_REFRESH_UNIT] =3D UFS_QUERY_ATTR_READ, [UFS_QUERY_ATTR_IDN_TIMESTAMP] =3D UFS_QUERY_ATTR_WRITE, [UFS_QUERY_ATTR_IDN_DEVICE_LEVEL_EXCEPTION_ID] =3D UFS_QUERY_ATTR_READ, - /* host initiated defragmentation is not supported */ - [UFS_QUERY_ATTR_IDN_DEFRAG_OP] =3D UFS_QUERY_ATTR_READ, + [UFS_QUERY_ATTR_IDN_DEFRAG_OP] =3D + UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE, [UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE] =3D UFS_QUERY_ATTR_READ, - [UFS_QUERY_ATTR_IDN_HID_SIZE] =3D UFS_QUERY_ATTR_READ, + [UFS_QUERY_ATTR_IDN_HID_SIZE] =3D + UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE, [UFS_QUERY_ATTR_IDN_HID_PROG_RATIO] =3D UFS_QUERY_ATTR_READ, [UFS_QUERY_ATTR_IDN_HID_STATE] =3D UFS_QUERY_ATTR_READ, [UFS_QUERY_ATTR_IDN_WB_BUFF_RESIZE_HINT] =3D UFS_QUERY_ATTR_READ, @@ -1371,8 +1375,31 @@ static inline uint32_t ufs_wb_read_resize_status(Ufs= Hc *u) return value; } =20 +static void ufs_hid_reset(UfsHc *u) +{ + u->attributes.defrag_op =3D UFS_HID_OP_DISABLE; + u->attributes.hid_state =3D UFS_HID_STATE_IDLE; + u->attributes.hid_prog_ratio =3D 0; + u->attributes.hid_avail_size =3D cpu_to_be32(0xFFFFFFFF); + u->hid_defrag_total =3D 0; + u->hid_defrag_remaining =3D 0; +} + +static uint32_t ufs_hid_read_progress_ratio(UfsHc *u) +{ + uint32_t value =3D u->attributes.hid_prog_ratio; + + if (value =3D=3D 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 +1478,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 =3D u->attributes.hid_state; + + if (state =3D=3D UFS_HID_STATE_DEFRAG_COMPLETED || + state =3D=3D 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 +1638,26 @@ static bool ufs_wb_pinned_min_size(UfsHc *u, uint32_= t value) return true; } =20 +static QueryRespCode ufs_hid_write_defrag_operation(UfsHc *u, uint32_t val= ue) +{ + 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 =3D value; + u->attributes.hid_state =3D UFS_HID_STATE_ANALYSIS_IN_PROGRESS; + u->attributes.hid_prog_ratio =3D 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 +1722,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 =3D cpu_to_be32(value); + break; default: g_assert_not_reached(); return 0; @@ -2211,11 +2270,92 @@ static void ufs_wb_process_resize(UfsHc *u) u->attributes.wb_buffer_resize_status =3D UFS_WB_RESIZE_COMPLETED; } =20 +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 =3D cpu_to_be32(u->hid_fragment_count= ); + if (u->hid_fragment_count > 0) { + u->attributes.hid_state =3D UFS_HID_STATE_DEFRAG_REQUIRED; + } else { + u->attributes.hid_state =3D UFS_HID_STATE_DEFRAG_NOT_REQUIRED; + } + + if (u->attributes.defrag_op =3D=3D UFS_HID_OP_ANALYSIS || + u->attributes.hid_state =3D=3D UFS_HID_STATE_DEFRAG_NOT_REQUIR= ED) { + u->attributes.defrag_op =3D 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 !=3D UFS_HID_OP_DEFRAG) { + break; + } + + requested =3D MIN(be32_to_cpu(u->attributes.hid_size), + be32_to_cpu(u->attributes.hid_avail_size)); + if (!requested) { + u->attributes.hid_state =3D UFS_HID_STATE_DEFRAG_COMPLETED; + u->attributes.defrag_op =3D UFS_HID_OP_DISABLE; + u->attributes.hid_prog_ratio =3D UFS_HID_PROGRESS_COMPLETE; + + trace_ufs_hid_defrag_operation(u->attributes.defrag_op, + u->attributes.hid_state); + break; + } + + u->attributes.hid_state =3D UFS_HID_STATE_DEFRAG_IN_PROGRESS; + u->hid_defrag_total =3D requested; + u->hid_defrag_remaining =3D requested; + u->attributes.hid_prog_ratio =3D 0; + break; + + case UFS_HID_STATE_DEFRAG_IN_PROGRESS: + if (u->hid_defrag_remaining > 0) { + batch =3D u->hid_defrag_remaining / UFS_HID_DEFRAG_BATCH_DIV; + if (batch =3D=3D 0) { + batch =3D 1; + } + + u->hid_defrag_remaining -=3D batch; + u->hid_fragment_count -=3D batch; + + done =3D u->hid_defrag_total - u->hid_defrag_remaining; + u->attributes.hid_prog_ratio =3D + ((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 =3D UFS_HID_STATE_DEFRAG_COMPLETED; + u->attributes.defrag_op =3D UFS_HID_OP_DISABLE; + u->attributes.hid_prog_ratio =3D 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); } =20 static inline bool ufs_check_idle(UfsHc *u) @@ -2385,7 +2525,8 @@ static void ufs_init_hc(UfsHc *u) uint32_t mcqcap =3D 0; uint32_t ext_wb_sup =3D WB_RESIZE | WB_FIFO | WB_PINNED; uint32_t ext_ufs_feat_sup =3D - UFS_DEV_WB_SUPPORT | UFS_DEV_HIGH_TEMP_NOTIF | UFS_DEV_LOW_TEMP_NO= TIF; + UFS_DEV_WB_SUPPORT | UFS_DEV_HIGH_TEMP_NOTIF | UFS_DEV_LOW_TEMP_NO= TIF | + UFS_DEV_HID_SUPPORT; int64_t now =3D qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT); =20 u->reg_size =3D pow2ceil(ufs_reg_size(u)); @@ -2487,6 +2628,8 @@ static void ufs_init_hc(UfsHc *u) u->attributes.max_num_of_rtt =3D 0x02; u->attributes.device_too_high_temp_boundary =3D UFS_TOO_HIGH_TEMP_BOUN= DARY; u->attributes.device_too_low_temp_boundary =3D UFS_TOO_LOW_TEMP_BOUNDA= RY; + u->attributes.hid_avail_size =3D cpu_to_be32(0xFFFFFFFF); + u->attributes.hid_size =3D cpu_to_be32(0xFFFFFFFF); =20 memset(&u->flags, 0, sizeof(u->flags)); u->flags.permanently_disable_fw_update =3D 1; diff --git a/hw/ufs/ufs.h b/hw/ufs/ufs.h index 8743501810..1246e8209f 100644 --- a/hw/ufs/ufs.h +++ b/hw/ufs/ufs.h @@ -175,6 +175,10 @@ typedef struct UfsHc { uint8_t temperature; =20 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; =20 static inline uint32_t ufs_mcq_sq_tail(UfsHc *u, uint32_t qid) diff --git a/include/block/ufs.h b/include/block/ufs.h index 0f7cc9c21b..6dd91181e5 100644 --- a/include/block/ufs.h +++ b/include/block/ufs.h @@ -951,6 +951,23 @@ enum attr_idn { UFS_QUERY_ATTR_IDN_COUNT, }; =20 +/* HID (Host Initiated Defragmentation) operation values for bDefragOperat= ion */ +enum ufs_hid_op { + UFS_HID_OP_DISABLE =3D 0x00, + UFS_HID_OP_ANALYSIS =3D 0x01, + UFS_HID_OP_DEFRAG =3D 0x02, +}; + +/* HID state values for bHIDState */ +enum ufs_hid_state { + UFS_HID_STATE_IDLE =3D 0x00, + UFS_HID_STATE_ANALYSIS_IN_PROGRESS =3D 0x01, + UFS_HID_STATE_DEFRAG_REQUIRED =3D 0x02, + UFS_HID_STATE_DEFRAG_IN_PROGRESS =3D 0x03, + UFS_HID_STATE_DEFRAG_COMPLETED =3D 0x04, + UFS_HID_STATE_DEFRAG_NOT_REQUIRED =3D 0x05, +}; + /* Descriptor idn for Query requests */ enum desc_idn { UFS_QUERY_DESC_IDN_DEVICE =3D 0x0, @@ -1142,6 +1159,7 @@ enum { /* Possible values for dExtendedUFSFeaturesSupport */ enum { UFS_DEV_WB_SUPPORT =3D BIT(8), + UFS_DEV_HID_SUPPORT =3D BIT(13), }; =20 /* WriteBooster buffer mode */ --=20 2.25.1 From nobody Sat Jun 13 12:19:15 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=samsung.com ARC-Seal: i=1; a=rsa-sha256; t=1780978392; cv=none; d=zohomail.com; s=zohoarc; b=Tfn9kGWNPPs+JaRT+8RJ0evmaLVpbd/2y5ood0LlXTU4bDdhq5MbjSnfSrm3u63GkN9fDsYM3bHh4KfU5IYtgrHmlC+YSBsw4GtW77/ikMPhbNpSA5DF9go12jRghOI1oA6r0axzZyhNmMPWye6wJ5buMOz+y4BpfAaTlqiIB6A= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1780978392; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Sender:Subject:Subject:To:To:Message-Id; bh=TqIvJe3RKwK7aU1y+Q7f2uDi1Gq/BJLAwiVun+M56P0=; b=G7bLWZLho8LAZxLgfRo3YW9YLSEcukh4FXmpc4COBy9fVnw8JkiUi8ggAokOs3aFVhK9w8CoshTpSh0ICDh95R/tGHIa2Pjo9cTo6Wq52wiyFsV4Hd0l17LbNmVQCMjDxgbtBIDeb+0dZmUL4J8Ce2OXFwciRKugzCm1z1ft1Bk= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1780978392952270.66505635063265; Mon, 8 Jun 2026 21:13:12 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wWnpT-0002n6-8Z; Tue, 09 Jun 2026 00:12:43 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wWnpQ-0002mo-8T for qemu-devel@nongnu.org; Tue, 09 Jun 2026 00:12:40 -0400 Received: from mailout1.samsung.com ([203.254.224.24]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wWnpN-0005On-22 for qemu-devel@nongnu.org; Tue, 09 Jun 2026 00:12:40 -0400 Received: from epcas2p1.samsung.com (unknown [182.195.41.53]) by mailout1.samsung.com (KnoxPortal) with ESMTP id 20260609041229epoutp011ffbe97e3afbe083202d91f53b3e4885~3Tl_JmPiS2942329423epoutp01f for ; Tue, 9 Jun 2026 04:12:29 +0000 (GMT) Received: from epsnrtp04.localdomain (unknown [182.195.42.156]) by epcas2p4.samsung.com (KnoxPortal) with ESMTPS id 20260609041229epcas2p4af9c0752bb860e36821c514bcbcba264~3Tl9yzlhr1203712037epcas2p4h; Tue, 9 Jun 2026 04:12:29 +0000 (GMT) Received: from epcas2p3.samsung.com (unknown [182.195.38.211]) by epsnrtp04.localdomain (Postfix) with ESMTP id 4gZFqd1Qlbz6B9mD; Tue, 9 Jun 2026 04:12:29 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.samsung.com 20260609041229epoutp011ffbe97e3afbe083202d91f53b3e4885~3Tl_JmPiS2942329423epoutp01f DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1780978349; bh=TqIvJe3RKwK7aU1y+Q7f2uDi1Gq/BJLAwiVun+M56P0=; h=Subject:Reply-To:From:To:CC:In-Reply-To:Date:References:From; b=HtJj0LYXRisWmurMB4yz8V4OZwPKJO64yK5a1w9zfdO8z24WDTHK4dEO6tuEBEGPm mjdkO/cx7KNzFSKtloWJOdq3SC9PfEs6aBXdQR+5IW3DHZLRTnyy0k4CooZOtbfvBu kpAbpyearnABLwX55V9OLUUFPDx3h0JL39ph23uU= Mime-Version: 1.0 Subject: [PATCH 2/2] tests/qtest: Add UFS HID qtest From: Keoseong Park To: Jeuk Kim , "farosas@suse.de" , "lvivier@redhat.com" , "pbonzini@redhat.com" CC: "qemu-devel@nongnu.org" , Keoseong Park X-Priority: 3 X-Content-Kind-Code: NORMAL In-Reply-To: <20260609023909epcms2p3f8856f12fed093168aff636f8dc8f994@epcms2p3> X-CPGS-Detection: blocking_info_exchange X-Drm-Type: N,general X-Msg-Generator: Mail X-Msg-Type: PERSONAL X-Reply-Demand: N Message-ID: <20260609041228epcms2p511070f9b5c2179967c0a27dd591a6d94@epcms2p5> Date: Tue, 09 Jun 2026 13:12:28 +0900 X-CMS-MailID: 20260609041228epcms2p511070f9b5c2179967c0a27dd591a6d94 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" X-Sendblock-Type: AUTO_CONFIDENTIAL CMS-TYPE: 102P X-CPGSPASS: Y X-CPGSPASS: Y cpgsPolicy: CPGSC10-223,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20260609023909epcms2p3f8856f12fed093168aff636f8dc8f994 References: <20260609023909epcms2p3f8856f12fed093168aff636f8dc8f994@epcms2p3> Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=203.254.224.24; envelope-from=keosung.park@samsung.com; helo=mailout1.samsung.com X-Spam_score_int: -31 X-Spam_score: -3.2 X-Spam_bar: --- X-Spam_report: (-3.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.445, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: keosung.park@samsung.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @samsung.com) X-ZM-MESSAGEID: 1780978395054154100 Cover the HID attribute permission table, the disable / analysis / defrag state transitions, partial defrag bounded by dHIDSize, and the terminal auto-reset behaviors. Signed-off-by: Keoseong Park Reviewed-by: Jeuk Kim --- tests/qtest/ufs-test.c | 290 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 290 insertions(+) diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c index f677896db0..5496276326 100644 --- a/tests/qtest/ufs-test.c +++ b/tests/qtest/ufs-test.c @@ -1386,6 +1386,295 @@ static void *ufs_blk_test_setup(GString *cmd_line, = void *arg) return arg; } =20 +/* + * 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 =3D 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, =3D=3D, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp.header.response, =3D=3D, UFS_COMMAND_RESULT_SUCCE= SS); + 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 =3D 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, =3D=3D, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp.header.response, =3D=3D, UFS_COMMAND_RESULT_SUCCE= SS); +} + +static uint32_t ufs_hid_wait_leave_state(QUfs *ufs, uint32_t from) +{ + uint64_t end_time =3D + g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND; + uint32_t state; + + do { + qtest_clock_step(ufs->dev.bus->qts, 100); + state =3D ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE); + } while (state =3D=3D 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 =3D + g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND; + uint32_t state; + + do { + qtest_clock_step(ufs->dev.bus->qts, 100); + state =3D ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE); + } while (state !=3D expected && g_get_monotonic_time() < end_time); + + g_assert_cmpuint(state, =3D=3D, expected); + return state; +} + +static void ufs_hid_wait_progress_complete(QUfs *ufs) +{ + uint64_t end_time =3D + g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND; + uint32_t val; + + do { + qtest_clock_step(ufs->dev.bus->qts, 100); + val =3D ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_PROG_RATIO); + } while (val !=3D 100 && g_get_monotonic_time() < end_time); + + g_assert_cmpuint(val, =3D=3D, 100); +} + +static void ufstest_hid(void *obj, void *data, QGuestAllocator *alloc) +{ + QUfs *ufs =3D obj; + uint32_t val; + enum UtpOcsCodes ocs; + UtpUpiuRsp rsp; + const int test_lun =3D 1; + const uint8_t request_sense_cdb[UFS_CDB_SIZE] =3D { + REQUEST_SENSE, + }; + const uint8_t write_cdb[UFS_CDB_SIZE] =3D { + /* WRITE(10) to LBA 0, transfer length 8 sectors =3D 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 =3D ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP); + g_assert_cmpuint(val, =3D=3D, UFS_HID_OP_DISABLE); + + val =3D ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE); + g_assert_cmpuint(val, =3D=3D, 0xFFFFFFFF); + + val =3D ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_SIZE); + g_assert_cmpuint(val, =3D=3D, 0xFFFFFFFF); + + val =3D ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_PROG_RATIO); + g_assert_cmpuint(val, =3D=3D, 0); + + val =3D ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE); + g_assert_cmpuint(val, =3D=3D, UFS_HID_STATE_IDLE); + + /* + * 2. Verify read-only attributes reject writes + */ + ocs =3D 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, =3D=3D, UFS_OCS_INVALID_CMD_TABLE_ATTR); + g_assert_cmpuint(rsp.header.response, =3D=3D, + UFS_QUERY_RESULT_NOT_WRITEABLE); + + ocs =3D 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, =3D=3D, UFS_OCS_INVALID_CMD_TABLE_ATTR); + g_assert_cmpuint(rsp.header.response, =3D=3D, + UFS_QUERY_RESULT_NOT_WRITEABLE); + + ocs =3D 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, =3D=3D, UFS_OCS_INVALID_CMD_TABLE_ATTR); + g_assert_cmpuint(rsp.header.response, =3D=3D, + UFS_QUERY_RESULT_NOT_WRITEABLE); + + /* + * 3. Verify invalid bDefragOperation value is rejected + */ + ocs =3D 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, =3D=3D, UFS_OCS_INVALID_CMD_TABLE_ATTR); + g_assert_cmpuint(rsp.header.response, =3D=3D, + UFS_QUERY_RESULT_INVALID_VALUE); + + /* + * 4. dHIDSize is writable and readable + */ + ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_HID_SIZE, 0x100); + val =3D ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_SIZE); + g_assert_cmpuint(val, =3D=3D, 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 =3D ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE); + g_assert_cmpuint(val, =3D=3D, UFS_HID_STATE_ANALYSIS_IN_PROGRESS); + + /* Idle handler advances analysis; first non-analysis read is decisive= */ + val =3D ufs_hid_wait_leave_state(ufs, UFS_HID_STATE_ANALYSIS_IN_PROGRE= SS); + g_assert_cmpuint(val, =3D=3D, UFS_HID_STATE_DEFRAG_NOT_REQUIRED); + + /* Reading Not Required auto-reset to Idle */ + val =3D ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE); + g_assert_cmpuint(val, =3D=3D, UFS_HID_STATE_IDLE); + + val =3D ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE); + g_assert_cmpuint(val, =3D=3D, 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 =3D 0; i < 4; i++) { + ocs =3D ufs_send_scsi_command(ufs, test_lun, write_cdb, write_buf, + sizeof(write_buf), NULL, 0, &rsp); + g_assert_cmpuint(ocs, =3D=3D, UFS_OCS_SUCCESS); + } + + ufs_hid_write_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP, + UFS_HID_OP_ANALYSIS); + + val =3D ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE); + g_assert_cmpuint(val, =3D=3D, UFS_HID_STATE_ANALYSIS_IN_PROGRESS); + + val =3D ufs_hid_wait_leave_state(ufs, UFS_HID_STATE_ANALYSIS_IN_PROGRE= SS); + g_assert_cmpuint(val, =3D=3D, UFS_HID_STATE_DEFRAG_REQUIRED); + + val =3D ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP); + g_assert_cmpuint(val, =3D=3D, UFS_HID_OP_DISABLE); + + val =3D 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 =3D ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE); + g_assert_cmpuint(val, =3D=3D, 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 =3D 0; i < 4; i++) { + ocs =3D ufs_send_scsi_command(ufs, test_lun, write_cdb, write_buf, + sizeof(write_buf), NULL, 0, &rsp); + g_assert_cmpuint(ocs, =3D=3D, 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 =3D ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE); + g_assert_cmpuint(val, =3D=3D, UFS_HID_STATE_ANALYSIS_IN_PROGRESS); + + ufs_hid_wait_progress_complete(ufs); + + val =3D ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE); + g_assert_cmpuint(val, =3D=3D, 0xFFFFFFFF); + + val =3D ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP); + g_assert_cmpuint(val, =3D=3D, UFS_HID_OP_DISABLE); + + val =3D ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_PROG_RATIO); + g_assert_cmpuint(val, =3D=3D, 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 =3D ufs_hid_wait_leave_state(ufs, UFS_HID_STATE_ANALYSIS_IN_PROGRE= SS); + g_assert_cmpuint(val, =3D=3D, UFS_HID_STATE_DEFRAG_REQUIRED); + + val =3D ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE); + g_assert_cmpuint(val, =3D=3D, 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 =3D ufs_hid_wait_state(ufs, UFS_HID_STATE_DEFRAG_COMPLETED); + g_assert_cmpuint(val, =3D=3D, UFS_HID_STATE_DEFRAG_COMPLETED); + + val =3D ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_STATE); + g_assert_cmpuint(val, =3D=3D, UFS_HID_STATE_IDLE); + + val =3D ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_DEFRAG_OP); + g_assert_cmpuint(val, =3D=3D, UFS_HID_OP_DISABLE); + + val =3D ufs_hid_read_attr(ufs, UFS_QUERY_ATTR_IDN_HID_AVAIL_SIZE); + g_assert_cmpuint(val, =3D=3D, 0xFFFFFFFF); + + ufs_exit(ufs, alloc); +} + static void ufs_register_nodes(void) { const char *arch; @@ -1439,6 +1728,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_o= pts); + qos_add_test("hid", "ufs", ufstest_hid, &io_test_opts); } =20 libqos_init(ufs_register_nodes); --=20 2.25.1