From 3566fc6d23f4c0b071d28681b4ff899292f8d574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Du=C5=A1an=20Gvozdenovi=C4=87?= Date: Mon, 1 Dec 2025 15:46:56 +0100 Subject: [PATCH] Re-initialize objsets when feature@project_quota is enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses the issue of project quotas not being usable after pool upgrade coming from an older version which does not support project quotas to the one that does, similarly to how it was solved for user quotas back in the day with `zfs set version=current` -- when feature@project_quota is first enabled, re-initialize the pool's objsets. Signed-off-by: Dušan Gvozdenović Closes #17955 --- include/sys/dmu_objset.h | 1 + module/zfs/dmu_objset.c | 24 ++++++++++++++ module/zfs/zfs_ioctl.c | 71 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) diff --git a/include/sys/dmu_objset.h b/include/sys/dmu_objset.h index 492be29200e4..ef186fd2d32f 100644 --- a/include/sys/dmu_objset.h +++ b/include/sys/dmu_objset.h @@ -261,6 +261,7 @@ boolean_t dmu_objset_projectquota_enabled(objset_t *os); boolean_t dmu_objset_projectquota_present(objset_t *os); boolean_t dmu_objset_projectquota_upgradable(objset_t *os); void dmu_objset_id_quota_upgrade(objset_t *os); +void dmu_objset_id_projectquota_upgrade(objset_t *os); int dmu_get_file_info(objset_t *os, dmu_object_type_t bonustype, const void *data, zfs_file_info_t *zfi); diff --git a/module/zfs/dmu_objset.c b/module/zfs/dmu_objset.c index 8e6b569c2100..513d432eb1ac 100644 --- a/module/zfs/dmu_objset.c +++ b/module/zfs/dmu_objset.c @@ -2439,6 +2439,30 @@ dmu_objset_id_quota_upgrade(objset_t *os) dmu_objset_upgrade(os, dmu_objset_id_quota_upgrade_cb); } +static int +dmu_objset_id_projectquota_upgrade_cb(objset_t *os) +{ + if (dmu_objset_projectquota_present(os)) + return (0); + if (!dmu_objset_projectquota_enabled(os)) + return (SET_ERROR(ENOTSUP)); + + dmu_objset_ds(os)->ds_feature_activation[ + SPA_FEATURE_PROJECT_QUOTA] = (void *)B_TRUE; + + if (dmu_objset_projectquota_enabled(os)) + os->os_flags |= OBJSET_FLAG_PROJECTQUOTA_COMPLETE; + + txg_wait_synced(dmu_objset_pool(os), 0); + return (0); +} + +void +dmu_objset_id_projectquota_upgrade(objset_t *os) +{ + dmu_objset_upgrade(os, dmu_objset_id_projectquota_upgrade_cb); +} + boolean_t dmu_objset_userobjspace_upgradable(objset_t *os) { diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index 1b2392aeaa85..2cfe4c24dc0b 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -3104,6 +3104,67 @@ zfs_ioc_inherit_prop(zfs_cmd_t *zc) return (err); } +static int +zfs_projectquota_upgrade_cb(const char *dataset, void *arg) +{ + zfsvfs_t *zfsvfs; + int error; + + (void) arg; + + error = getzfsvfs(dataset, &zfsvfs); + + if (error != 0) + return (0); + + if (!dmu_objset_projectquota_enabled(zfsvfs->z_os)) { + /* + * If projectquota is not enabled, it may be because the objset + * needs to be closed & reopened (to grow the objset_phys_t). + * Suspend/resume the fs will do that. + */ + dsl_dataset_t *ds, *newds; + + ds = dmu_objset_ds(zfsvfs->z_os); + error = zfs_suspend_fs(zfsvfs); + if (error == 0) { + dmu_objset_refresh_ownership(ds, &newds, + B_TRUE, zfsvfs); + error = zfs_resume_fs(zfsvfs, newds); + } + } + + if (error == 0) { + mutex_enter(&zfsvfs->z_os->os_upgrade_lock); + if (zfsvfs->z_os->os_upgrade_id == 0) { + /* clear potential error code and retry */ + zfsvfs->z_os->os_upgrade_status = 0; + mutex_exit(&zfsvfs->z_os->os_upgrade_lock); + + dsl_pool_config_enter( + dmu_objset_pool(zfsvfs->z_os), FTAG); + dmu_objset_id_projectquota_upgrade(zfsvfs->z_os); + dsl_pool_config_exit( + dmu_objset_pool(zfsvfs->z_os), FTAG); + } else { + mutex_exit(&zfsvfs->z_os->os_upgrade_lock); + } + + taskq_wait_id(zfsvfs->z_os->os_spa->spa_upgrade_taskq, + zfsvfs->z_os->os_upgrade_id); + error = zfsvfs->z_os->os_upgrade_status; + } + + zfs_vfs_rele(zfsvfs); + + if (error != 0) + cmn_err(CE_WARN, + "Failed to activate the project quota feature on dataset " + "%s (%d).", dataset, error); + + return (0); +} + static int zfs_ioc_pool_set_props(zfs_cmd_t *zc) { @@ -3143,6 +3204,16 @@ zfs_ioc_pool_set_props(zfs_cmd_t *zc) error = spa_prop_set(spa, props); + /* + * If we are enabling the project quota feature, try to re-initialize + * the active file systems on that pool and activate the feature -- all + * in best effort. + */ + if ((error == 0) && nvlist_exists(props, "feature@project_quota")) + (void) dmu_objset_find(spa_name(spa), + zfs_projectquota_upgrade_cb, + NULL, DS_FIND_CHILDREN); + nvlist_free(props); spa_close(spa, FTAG);