scsi-hd implements SYNCHRONIZE CACHE(10) only and fails the 16-byte variant with INVALID COMMAND OPERATION CODE. Linux sends SYNCHRONIZE CACHE(16) to zoned disks (sd_zbc sets use_16_for_sync), so once a logical unit can be zoned, every flush from such a guest fails. With the zoned logical units that a later commit adds and without this change, a Linux 7.3-rc7 guest logged
sd 2:0:0:1: [sdb] tag#9 Add. Sense: Invalid command operation code critical target error, dev sdb, sector 0 op 0x1:(WRITE) flags 0x800 and fsync() on the disk failed with EREMOTEIO. scsi-hd flushes the whole medium for SYNCHRONIZE CACHE(10) and does not look at its LBA range. Hand the 16-byte command to scsi-hd as the 10-byte one and keep the IMMED bit. This keeps the UFS change inside hw/ufs; if scsi-disk learns SYNCHRONIZE CACHE(16), the translation can go. Signed-off-by: Daejun Park <[email protected]> --- hw/ufs/lu.c | 17 +++++++++++++++-- tests/qtest/ufs-test.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c index 1853284acf..b812e1bef8 100644 --- a/hw/ufs/lu.c +++ b/hw/ufs/lu.c @@ -382,6 +382,8 @@ static UfsReqResult ufs_emulate_scsi_cmd(UfsLu *lu, UfsRequest *req) static UfsReqResult ufs_process_scsi_cmd(UfsLu *lu, UfsRequest *req) { uint8_t task_tag = req->req_upiu.header.task_tag; + uint8_t *cdb = req->req_upiu.sc.cdb; + uint8_t sync_cache_10[UFS_CDB_SIZE] = { SYNCHRONIZE_CACHE }; SCSIRequest *scsi_req; uint32_t len; @@ -395,8 +397,19 @@ static UfsReqResult ufs_process_scsi_cmd(UfsLu *lu, UfsRequest *req) return ufs_emulate_scsi_cmd(lu, req); } - scsi_req = scsi_req_new(lu->scsi_dev, task_tag, lu->lun, - req->req_upiu.sc.cdb, UFS_CDB_SIZE, req); + /* + * scsi-hd implements SYNCHRONIZE CACHE(10) only, while Linux sends the + * 16-byte variant to zoned disks. scsi-hd flushes the whole medium for + * SYNCHRONIZE CACHE(10) whatever its LBA range, so pass the 16-byte + * command on as the 10-byte one, keeping IMMED. + */ + if (cdb[0] == SYNCHRONIZE_CACHE_16) { + sync_cache_10[1] = cdb[1] & 0x02; + cdb = sync_cache_10; + } + + scsi_req = scsi_req_new(lu->scsi_dev, task_tag, lu->lun, cdb, UFS_CDB_SIZE, + req); /* * A command that moves data needs a PRDT. Without one there is no SG diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c index 63f805419e..594b91706b 100644 --- a/tests/qtest/ufs-test.c +++ b/tests/qtest/ufs-test.c @@ -814,6 +814,51 @@ static void ufstest_no_prdt(void *obj, void *data, QGuestAllocator *alloc) ufs_exit(ufs, alloc); } +static void ufstest_sync_cache(void *obj, void *data, QGuestAllocator *alloc) +{ + QUfs *ufs = obj; + uint8_t buf[4096]; + const uint8_t request_sense_cdb[UFS_CDB_SIZE] = { + REQUEST_SENSE, + }; + const uint8_t sync_cache_10_cdb[UFS_CDB_SIZE] = { + SYNCHRONIZE_CACHE, + }; + const uint8_t sync_cache_16_cdb[UFS_CDB_SIZE] = { + SYNCHRONIZE_CACHE_16, + }; + const uint8_t sync_cache_16_immed_cdb[UFS_CDB_SIZE] = { + SYNCHRONIZE_CACHE_16, 0x02 /* IMMED */ + }; + enum UtpOcsCodes ocs; + UtpUpiuRsp rsp_upiu; + const int test_lun = 1; + + ufs_init(ufs, alloc); + + /* Clear Unit Attention */ + ocs = ufs_send_scsi_command(ufs, test_lun, request_sense_cdb, NULL, 0, + buf, sizeof(buf), &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); + + ocs = ufs_send_scsi_command(ufs, test_lun, sync_cache_10_cdb, NULL, 0, + NULL, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); + + ocs = ufs_send_scsi_command(ufs, test_lun, sync_cache_16_cdb, NULL, 0, + NULL, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); + + ocs = ufs_send_scsi_command(ufs, test_lun, sync_cache_16_immed_cdb, NULL, + 0, NULL, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); + + ufs_exit(ufs, alloc); +} + static void ufstest_mcq_cq_wraparound(void *obj, void *data, QGuestAllocator *alloc) { @@ -1766,6 +1811,7 @@ static void ufs_register_nodes(void) qos_add_test("legacy-read-write", "ufs", ufstest_read_write, &io_test_opts); qos_add_test("mcq-read-write", "ufs", ufstest_read_write, &mcq_test_opts); qos_add_test("no-prdt", "ufs", ufstest_no_prdt, &io_test_opts); + qos_add_test("sync-cache", "ufs", ufstest_sync_cache, &io_test_opts); qos_add_test("mcq-cq-wraparound", "ufs", ufstest_mcq_cq_wraparound, &mcq_test_opts); qos_add_test("query-flag", "ufs", ufstest_query_flag_request, -- 2.43.0
