@@ -2076,6 +2076,78 @@ static void femu_test_wide_lba(void *obj, void *data, QGuestAllocator *alloc)
20762076 femu_disable (& c );
20772077}
20782078
2079+ /*
2080+ * An SGL descriptor has its type in the high nibble and a subtype in the low
2081+ * one, and over PCIe only the address subtype exists; a segment is a whole
2082+ * number of 16-byte descriptors. Lists breaking either rule were mapped.
2083+ */
2084+ static uint16_t femu_sgl_write (FemuCtrlState * c , const NvmeSglDescriptor * sgl )
2085+ {
2086+ NvmeRwCmd rw ;
2087+ uint16_t want = c -> cid ;
2088+ uint16_t got ;
2089+ uint16_t status ;
2090+
2091+ memset (& rw , 0 , sizeof (rw ));
2092+ rw .opcode = NVME_CMD_WRITE ;
2093+ rw .flags = 1 << 6 ; /* PSDT: SGL */
2094+ rw .nsid = cpu_to_le32 (1 );
2095+ memcpy (& rw .dptr .sgl , sgl , sizeof (* sgl ));
2096+ rw .nlb = cpu_to_le16 (FEMU_DATA_SIZE / c -> lba_size - 1 );
2097+
2098+ femu_submit (c , & c -> io , (NvmeCmd * )& rw );
2099+ status = femu_complete (c , & c -> io , & got , NULL );
2100+ g_assert_cmpint (got , = = , want );
2101+ return FEMU_SC (status );
2102+ }
2103+
2104+ static void femu_test_sgl (void * obj , void * data , QGuestAllocator * alloc )
2105+ {
2106+ QFemu * femu = obj ;
2107+ QTestState * qts = femu -> dev .bus -> qts ;
2108+ FemuCtrlState c = { 0 };
2109+ NvmeSglDescriptor blk = { 0 };
2110+ NvmeSglDescriptor seg = { 0 };
2111+ uint64_t buf , list ;
2112+
2113+ femu_enable (& c , & femu -> dev , alloc );
2114+ femu_create_io_queues (& c );
2115+
2116+ buf = guest_alloc (alloc , FEMU_DATA_SIZE );
2117+ list = guest_alloc (alloc , 4096 );
2118+ qtest_memset (qts , buf , 0x5a , FEMU_DATA_SIZE );
2119+
2120+ /* one data block, then the same block through a last segment */
2121+ blk .addr = cpu_to_le64 (buf );
2122+ blk .len = cpu_to_le32 (FEMU_DATA_SIZE );
2123+ g_assert_cmpint (femu_sgl_write (& c , & blk ), = = , NVME_SUCCESS );
2124+ qtest_memwrite (qts , list , & blk , sizeof (blk ));
2125+ seg .addr = cpu_to_le64 (list );
2126+ seg .len = cpu_to_le32 (sizeof (blk ));
2127+ seg .type = NVME_SGL_DESCR_TYPE_LAST_SEGMENT << 4 ;
2128+ g_assert_cmpint (femu_sgl_write (& c , & seg ), = = , NVME_SUCCESS );
2129+
2130+ /* a segment length that is not a whole number of descriptors */
2131+ seg .len = cpu_to_le32 (sizeof (blk ) + 4 );
2132+ g_assert_cmpint (femu_sgl_write (& c , & seg ), = = , NVME_INVALID_FIELD );
2133+ seg .len = cpu_to_le32 (sizeof (blk ));
2134+
2135+ /* the offset subtype, which only fabrics define, directly and listed */
2136+ blk .type = 0x1 ;
2137+ g_assert_cmpint (femu_sgl_write (& c , & blk ), = = , NVME_INVALID_FIELD );
2138+ qtest_memwrite (qts , list , & blk , sizeof (blk ));
2139+ g_assert_cmpint (femu_sgl_write (& c , & seg ), = = , NVME_INVALID_FIELD );
2140+ seg .type |= 0x1 ;
2141+ blk .type = 0 ;
2142+ qtest_memwrite (qts , list , & blk , sizeof (blk ));
2143+ g_assert_cmpint (femu_sgl_write (& c , & seg ), = = , NVME_INVALID_FIELD );
2144+
2145+ guest_free (alloc , list );
2146+ guest_free (alloc , buf );
2147+ femu_queue_free (& c , & c .io );
2148+ femu_disable (& c );
2149+ }
2150+
20792151static void femu_register_nodes (void )
20802152{
20812153 QOSGraphEdgeOptions opts = {
@@ -2195,6 +2267,9 @@ static void femu_register_nodes(void)
21952267 "femu_mode=1,secsz=512,secs_per_pg=8,pgs_per_blk=16,"
21962268 "blks_per_pl=80,pls_per_lun=1,luns_per_ch=4,nchs=4"
21972269 });
2270+ qos_add_test ("sgl" , "femu" , femu_test_sgl , & (QOSGraphTestOptions ) {
2271+ .edge .extra_device_opts = "sgl=on"
2272+ });
21982273 qos_add_test ("wide-lba-4k" , "femu" , femu_test_wide_lba ,
21992274 & (QOSGraphTestOptions ) {
22002275 .edge .extra_device_opts =
0 commit comments