From 0c757530e4a47d4ff299f112a9a9849aa54f4478 Mon Sep 17 00:00:00 2001
From: Gionatan Danti <g.danti@assyoma.it>
Date: Sun, 3 Sep 2023 12:17:43 +0200
Subject: [PATCH] add prefetch property

ZFS prefetch is currently governed by the zfs_prefetch_disable
tunable. However, this is a module-wide settings - if a specific
dataset benefits from prefetch, while others have issue with it,
an optimal solution does not exists.

This commit introduce the "prefetch" property, which enable
granular control (at dataset/volume level) for prefetching.

This patch does not remove the zfs_prefetch_disable, which reimains
a system-wide switch for enable/disable prefetch. However, to avoid
duplication, it would be preferable to deprecate and then remove
the module tunable.

Signed-off-by: Gionatan Danti <g.danti@assyoma.it>
---
 include/sys/dmu_objset.h  |  3 +++
 include/sys/fs/zfs.h      |  1 +
 module/zcommon/zfs_prop.c |  4 ++++
 module/zfs/dmu_objset.c   | 17 +++++++++++++++++
 module/zfs/dmu_zfetch.c   |  4 +++-
 5 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/include/sys/dmu_objset.h b/include/sys/dmu_objset.h
index 9f6e0fdd601b..4878a6bee105 100644
--- a/include/sys/dmu_objset.h
+++ b/include/sys/dmu_objset.h
@@ -197,6 +197,9 @@ struct objset {
 	dmu_objset_upgrade_cb_t os_upgrade_cb;
 	boolean_t os_upgrade_exit;
 	int os_upgrade_status;
+
+	/* prefetch */
+	boolean_t os_prefetch;
 };
 
 #define	DMU_META_OBJSET		0
diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h
index bc940e8a7929..87356f32ba42 100644
--- a/include/sys/fs/zfs.h
+++ b/include/sys/fs/zfs.h
@@ -191,6 +191,7 @@ typedef enum {
 	ZFS_PROP_REDACTED,
 	ZFS_PROP_REDACT_SNAPS,
 	ZFS_PROP_SNAPSHOTS_CHANGED,
+	ZFS_PROP_PREFETCH,
 	ZFS_NUM_PROPS
 } zfs_prop_t;
 
diff --git a/module/zcommon/zfs_prop.c b/module/zcommon/zfs_prop.c
index 3db6fd13f4ae..525f18aedc6d 100644
--- a/module/zcommon/zfs_prop.c
+++ b/module/zcommon/zfs_prop.c
@@ -746,6 +746,10 @@ zfs_prop_init(void)
 	    ZFS_TYPE_VOLUME, "<date>", "SNAPSHOTS_CHANGED", B_FALSE, B_TRUE,
 	    B_TRUE, NULL, sfeatures);
 
+        zprop_register_index(ZFS_PROP_PREFETCH, "prefetch", 1, PROP_INHERIT,
+            ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT | ZFS_TYPE_VOLUME,
+            "on | off", "PREFETCH", boolean_table, sfeatures);
+
 	zfs_mod_list_supported_free(sfeatures);
 }
 
diff --git a/module/zfs/dmu_objset.c b/module/zfs/dmu_objset.c
index d134d4958f7c..f980e709cea3 100644
--- a/module/zfs/dmu_objset.c
+++ b/module/zfs/dmu_objset.c
@@ -263,6 +263,18 @@ secondary_cache_changed_cb(void *arg, uint64_t newval)
 	os->os_secondary_cache = newval;
 }
 
+static void
+prefetch_changed_cb(void *arg, uint64_t newval)
+{
+	objset_t *os = arg;
+
+	/*
+	 * Inheritance should have been done by now.
+	*/
+	ASSERT(newval == B_TRUE || newval == B_FALSE);
+	os->os_prefetch = newval;
+}
+
 static void
 sync_changed_cb(void *arg, uint64_t newval)
 {
@@ -562,6 +574,11 @@ dmu_objset_open_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp,
 			    zfs_prop_to_name(ZFS_PROP_SECONDARYCACHE),
 			    secondary_cache_changed_cb, os);
 		}
+		if (err == 0) {
+			err = dsl_prop_register(ds,
+			    zfs_prop_to_name(ZFS_PROP_PREFETCH),
+			    prefetch_changed_cb, os);
+		}
 		if (!ds->ds_is_snapshot) {
 			if (err == 0) {
 				err = dsl_prop_register(ds,
diff --git a/module/zfs/dmu_zfetch.c b/module/zfs/dmu_zfetch.c
index d0acaf502066..d82a7e3a3e77 100644
--- a/module/zfs/dmu_zfetch.c
+++ b/module/zfs/dmu_zfetch.c
@@ -329,9 +329,11 @@ dmu_zfetch_prepare(zfetch_t *zf, uint64_t blkid, uint64_t nblks,
 {
 	zstream_t *zs;
 	spa_t *spa = zf->zf_dnode->dn_objset->os_spa;
+	boolean_t os_prefetch = zf->zf_dnode->dn_objset->os_prefetch;
 
-	if (zfs_prefetch_disable)
+	if (zfs_prefetch_disable || !os_prefetch)
 		return (NULL);
+
 	/*
 	 * If we haven't yet loaded the indirect vdevs' mappings, we
 	 * can only read from blocks that we carefully ensure are on