From d54d0fff392a8889e794a10c1e90746f4493cf30 Mon Sep 17 00:00:00 2001 From: Rob Norris Date: Tue, 27 Jun 2023 12:50:18 +1000 Subject: [PATCH] dnode: allow storage class to be overridden by object type spa_preferred_class() selects a storage class based on (among other things) the DMU object type. This only works for old-style object types that match only one specific kind of thing. For DMU_OTN_ types we need another way to signal the storage class. This commit allows the object type to be overridden in the IO policy for the purposes of choosing a storage class. It then adds the ability to set the storage type on a dnode hold, such that all writes generated under that hold will get it. This method has two shortcomings: - it would be better if we could "name" a set of storage class preferences rather than it being implied by the object type. - it would be better if this info were stored in the dnode on disk. In the absence of those things, this seems like the smallest possible change. Reviewed-by: Alexander Motin Reviewed-by: Tony Hutter Reviewed-by: Brian Behlendorf Signed-off-by: Rob Norris Sponsored-by: Klara, Inc. Sponsored-by: iXsystems, Inc. Closes #15894 --- include/sys/dnode.h | 5 +++++ include/sys/zio.h | 1 + module/zfs/dmu.c | 1 + module/zfs/dnode.c | 17 +++++++++++++++++ module/zfs/spa_misc.c | 11 ++++++++++- module/zfs/zio.c | 2 +- 6 files changed, 35 insertions(+), 2 deletions(-) diff --git a/include/sys/dnode.h b/include/sys/dnode.h index dbe7350d4..5d0f0fb26 100644 --- a/include/sys/dnode.h +++ b/include/sys/dnode.h @@ -380,6 +380,9 @@ struct dnode { /* holds prefetch structure */ struct zfetch dn_zfetch; + + /* Not in dn_phys, but should be. set it after taking a hold */ + dmu_object_type_t dn_storage_type; /* type for storage class */ }; /* @@ -462,6 +465,8 @@ void dnode_evict_dbufs(dnode_t *dn); void dnode_evict_bonus(dnode_t *dn); void dnode_free_interior_slots(dnode_t *dn); +void dnode_set_storage_type(dnode_t *dn, dmu_object_type_t type); + #define DNODE_IS_DIRTY(_dn) \ ((_dn)->dn_dirty_txg >= spa_syncing_txg((_dn)->dn_objset->os_spa)) diff --git a/include/sys/zio.h b/include/sys/zio.h index 77c70b9b4..446b64ccd 100644 --- a/include/sys/zio.h +++ b/include/sys/zio.h @@ -356,6 +356,7 @@ typedef struct zio_prop { uint8_t zp_iv[ZIO_DATA_IV_LEN]; uint8_t zp_mac[ZIO_DATA_MAC_LEN]; uint32_t zp_zpl_smallblk; + dmu_object_type_t zp_storage_type; } zio_prop_t; typedef struct zio_cksum_report zio_cksum_report_t; diff --git a/module/zfs/dmu.c b/module/zfs/dmu.c index f45354a7f..3dcf49ceb 100644 --- a/module/zfs/dmu.c +++ b/module/zfs/dmu.c @@ -2362,6 +2362,7 @@ dmu_write_policy(objset_t *os, dnode_t *dn, int level, int wp, zio_prop_t *zp) memset(zp->zp_mac, 0, ZIO_DATA_MAC_LEN); zp->zp_zpl_smallblk = DMU_OT_IS_FILE(zp->zp_type) ? os->os_zpl_special_smallblock : 0; + zp->zp_storage_type = dn ? dn->dn_storage_type : DMU_OT_NONE; ASSERT3U(zp->zp_compress, !=, ZIO_COMPRESS_INHERIT); } diff --git a/module/zfs/dnode.c b/module/zfs/dnode.c index 5058ca374..b6cc512cb 100644 --- a/module/zfs/dnode.c +++ b/module/zfs/dnode.c @@ -543,6 +543,17 @@ dnode_setbonus_type(dnode_t *dn, dmu_object_type_t newtype, dmu_tx_t *tx) rw_exit(&dn->dn_struct_rwlock); } +void +dnode_set_storage_type(dnode_t *dn, dmu_object_type_t newtype) +{ + /* + * This is not in the dnode_phys, but it should be, and perhaps one day + * will. For now we require it be set after taking a hold. + */ + ASSERT3U(zfs_refcount_count(&dn->dn_holds), >=, 1); + dn->dn_storage_type = newtype; +} + void dnode_rm_spill(dnode_t *dn, dmu_tx_t *tx) { @@ -604,6 +615,8 @@ dnode_create(objset_t *os, dnode_phys_t *dnp, dmu_buf_impl_t *db, dn->dn_have_spill = ((dnp->dn_flags & DNODE_FLAG_SPILL_BLKPTR) != 0); dn->dn_id_flags = 0; + dn->dn_storage_type = DMU_OT_NONE; + dmu_zfetch_init(&dn->dn_zfetch, dn); ASSERT(DMU_OT_IS_VALID(dn->dn_phys->dn_type)); @@ -687,6 +700,8 @@ dnode_destroy(dnode_t *dn) dn->dn_newprojid = ZFS_DEFAULT_PROJID; dn->dn_id_flags = 0; + dn->dn_storage_type = DMU_OT_NONE; + dmu_zfetch_fini(&dn->dn_zfetch); kmem_cache_free(dnode_cache, dn); arc_space_return(sizeof (dnode_t), ARC_SPACE_DNODE); @@ -946,6 +961,7 @@ dnode_move_impl(dnode_t *odn, dnode_t *ndn) ndn->dn_newgid = odn->dn_newgid; ndn->dn_newprojid = odn->dn_newprojid; ndn->dn_id_flags = odn->dn_id_flags; + ndn->dn_storage_type = odn->dn_storage_type; dmu_zfetch_init(&ndn->dn_zfetch, ndn); /* @@ -1004,6 +1020,7 @@ dnode_move_impl(dnode_t *odn, dnode_t *ndn) odn->dn_newgid = 0; odn->dn_newprojid = ZFS_DEFAULT_PROJID; odn->dn_id_flags = 0; + odn->dn_storage_type = DMU_OT_NONE; /* * Mark the dnode. diff --git a/module/zfs/spa_misc.c b/module/zfs/spa_misc.c index c9ee36c0f..97191e768 100644 --- a/module/zfs/spa_misc.c +++ b/module/zfs/spa_misc.c @@ -2010,7 +2010,16 @@ metaslab_class_t * spa_preferred_class(spa_t *spa, const zio_t *zio) { const zio_prop_t *zp = &zio->io_prop; - dmu_object_type_t objtype = zp->zp_type; + + /* + * Override object type for the purposes of selecting a storage class. + * Primarily for DMU_OTN_ types where we can't explicitly control their + * storage class; instead, choose a static type most closely matches + * what we want. + */ + dmu_object_type_t objtype = + zp->zp_storage_type == DMU_OT_NONE ? + zp->zp_type : zp->zp_storage_type; /* * ZIL allocations determine their class in zio_alloc_zil(). diff --git a/module/zfs/zio.c b/module/zfs/zio.c index 781011373..00c93e76a 100644 --- a/module/zfs/zio.c +++ b/module/zfs/zio.c @@ -3069,7 +3069,7 @@ zio_write_gang_block(zio_t *pio, metaslab_class_t *mc) zp.zp_checksum = gio->io_prop.zp_checksum; zp.zp_compress = ZIO_COMPRESS_OFF; zp.zp_complevel = gio->io_prop.zp_complevel; - zp.zp_type = DMU_OT_NONE; + zp.zp_type = zp.zp_storage_type = DMU_OT_NONE; zp.zp_level = 0; zp.zp_copies = gio->io_prop.zp_copies; zp.zp_dedup = B_FALSE;