Selectable block allocators

ZFS historically has had several space allocators that were
dynamically selectable.  While these have been retained in 
OpenZFS, only a single allocator has been statically compiled 
in. This patch compiles all allocators for OpenZFS and provides 
a module parameter to allow for manual selection between them.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Ameer Hamza <ahamza@ixsystems.com>
Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Signed-off-by: Edmund Nadolski <edmund.nadolski@ixsystems.com>
Closes #15218
This commit is contained in:
ednadolski-ix 2023-09-01 19:00:30 -06:00 committed by GitHub
parent 71472bf375
commit 95f71c019d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 127 additions and 31 deletions

View File

@ -73,6 +73,9 @@
#define param_set_deadman_failmode_args(var) \ #define param_set_deadman_failmode_args(var) \
CTLTYPE_STRING, NULL, 0, param_set_deadman_failmode, "A" CTLTYPE_STRING, NULL, 0, param_set_deadman_failmode, "A"
#define param_set_active_allocator_args(var) \
CTLTYPE_STRING, NULL, 0, param_set_active_allocator, "A"
#define param_set_deadman_synctime_args(var) \ #define param_set_deadman_synctime_args(var) \
CTLTYPE_U64, NULL, 0, param_set_deadman_synctime, "QU" CTLTYPE_U64, NULL, 0, param_set_deadman_synctime, "QU"

View File

@ -39,6 +39,7 @@ extern "C" {
typedef struct metaslab_ops { typedef struct metaslab_ops {
const char *msop_name;
uint64_t (*msop_alloc)(metaslab_t *, uint64_t); uint64_t (*msop_alloc)(metaslab_t *, uint64_t);
} metaslab_ops_t; } metaslab_ops_t;

View File

@ -1056,6 +1056,8 @@ extern uint64_t spa_deadman_synctime(spa_t *spa);
extern uint64_t spa_deadman_ziotime(spa_t *spa); extern uint64_t spa_deadman_ziotime(spa_t *spa);
extern uint64_t spa_dirty_data(spa_t *spa); extern uint64_t spa_dirty_data(spa_t *spa);
extern spa_autotrim_t spa_get_autotrim(spa_t *spa); extern spa_autotrim_t spa_get_autotrim(spa_t *spa);
extern int spa_get_allocator(spa_t *spa);
extern void spa_set_allocator(spa_t *spa, const char *allocator);
/* Miscellaneous support routines */ /* Miscellaneous support routines */
extern void spa_load_failed(spa_t *spa, const char *fmt, ...) extern void spa_load_failed(spa_t *spa, const char *fmt, ...)
@ -1207,6 +1209,7 @@ int param_set_deadman_ziotime(ZFS_MODULE_PARAM_ARGS);
int param_set_deadman_synctime(ZFS_MODULE_PARAM_ARGS); int param_set_deadman_synctime(ZFS_MODULE_PARAM_ARGS);
int param_set_slop_shift(ZFS_MODULE_PARAM_ARGS); int param_set_slop_shift(ZFS_MODULE_PARAM_ARGS);
int param_set_deadman_failmode(ZFS_MODULE_PARAM_ARGS); int param_set_deadman_failmode(ZFS_MODULE_PARAM_ARGS);
int param_set_active_allocator(ZFS_MODULE_PARAM_ARGS);
#ifdef ZFS_DEBUG #ifdef ZFS_DEBUG
#define dprintf_bp(bp, fmt, ...) do { \ #define dprintf_bp(bp, fmt, ...) do { \

View File

@ -263,6 +263,7 @@ struct spa {
*/ */
spa_alloc_t *spa_allocs; spa_alloc_t *spa_allocs;
int spa_alloc_count; int spa_alloc_count;
int spa_active_allocator; /* selectable allocator */
spa_aux_vdev_t spa_spares; /* hot spares */ spa_aux_vdev_t spa_spares; /* hot spares */
spa_aux_vdev_t spa_l2cache; /* L2ARC cache devices */ spa_aux_vdev_t spa_l2cache; /* L2ARC cache devices */
@ -467,6 +468,8 @@ extern int param_set_deadman_failmode_common(const char *val);
extern void spa_set_deadman_synctime(hrtime_t ns); extern void spa_set_deadman_synctime(hrtime_t ns);
extern void spa_set_deadman_ziotime(hrtime_t ns); extern void spa_set_deadman_ziotime(hrtime_t ns);
extern const char *spa_history_zone(void); extern const char *spa_history_zone(void);
extern const char *zfs_active_allocator;
extern int param_set_active_allocator_common(const char *val);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -503,6 +503,24 @@ SYSCTL_UINT(_vfs_zfs_zfetch, OID_AUTO, max_idistance,
/* metaslab.c */ /* metaslab.c */
int
param_set_active_allocator(SYSCTL_HANDLER_ARGS)
{
char buf[16];
int rc;
if (req->newptr == NULL)
strlcpy(buf, zfs_active_allocator, sizeof (buf));
rc = sysctl_handle_string(oidp, buf, sizeof (buf), req);
if (rc || req->newptr == NULL)
return (rc);
if (strcmp(buf, zfs_active_allocator) == 0)
return (0);
return (param_set_active_allocator_common(buf));
}
/* /*
* In pools where the log space map feature is not enabled we touch * In pools where the log space map feature is not enabled we touch
* multiple metaslabs (and their respective space maps) with each * multiple metaslabs (and their respective space maps) with each

View File

@ -103,6 +103,18 @@ param_set_slop_shift(const char *buf, zfs_kernel_param_t *kp)
return (0); return (0);
} }
int
param_set_active_allocator(const char *val, zfs_kernel_param_t *kp)
{
int error;
error = -param_set_active_allocator_common(val);
if (error == 0)
error = param_set_charp(val, kp);
return (error);
}
const char * const char *
spa_history_zone(void) spa_history_zone(void)
{ {

View File

@ -40,8 +40,6 @@
#include <sys/zap.h> #include <sys/zap.h>
#include <sys/btree.h> #include <sys/btree.h>
#define WITH_DF_BLOCK_ALLOCATOR
#define GANG_ALLOCATION(flags) \ #define GANG_ALLOCATION(flags) \
((flags) & (METASLAB_GANG_CHILD | METASLAB_GANG_HEADER)) ((flags) & (METASLAB_GANG_CHILD | METASLAB_GANG_HEADER))
@ -1622,9 +1620,6 @@ metaslab_block_find(zfs_btree_t *t, range_tree_t *rt, uint64_t start,
return (rs); return (rs);
} }
#if defined(WITH_DF_BLOCK_ALLOCATOR) || \
defined(WITH_CF_BLOCK_ALLOCATOR)
/* /*
* This is a helper function that can be used by the allocator to find a * This is a helper function that can be used by the allocator to find a
* suitable block to allocate. This will search the specified B-tree looking * suitable block to allocate. This will search the specified B-tree looking
@ -1659,9 +1654,74 @@ metaslab_block_picker(range_tree_t *rt, uint64_t *cursor, uint64_t size,
*cursor = 0; *cursor = 0;
return (-1ULL); return (-1ULL);
} }
#endif /* WITH_DF/CF_BLOCK_ALLOCATOR */
#if defined(WITH_DF_BLOCK_ALLOCATOR) static uint64_t metaslab_df_alloc(metaslab_t *msp, uint64_t size);
static uint64_t metaslab_cf_alloc(metaslab_t *msp, uint64_t size);
static uint64_t metaslab_ndf_alloc(metaslab_t *msp, uint64_t size);
metaslab_ops_t *metaslab_allocator(spa_t *spa);
static metaslab_ops_t metaslab_allocators[] = {
{ "dynamic", metaslab_df_alloc },
{ "cursor", metaslab_cf_alloc },
{ "new-dynamic", metaslab_ndf_alloc },
};
static int
spa_find_allocator_byname(const char *val)
{
int a = ARRAY_SIZE(metaslab_allocators) - 1;
if (strcmp("new-dynamic", val) == 0)
return (-1); /* remove when ndf is working */
for (; a >= 0; a--) {
if (strcmp(val, metaslab_allocators[a].msop_name) == 0)
return (a);
}
return (-1);
}
void
spa_set_allocator(spa_t *spa, const char *allocator)
{
int a = spa_find_allocator_byname(allocator);
if (a < 0) a = 0;
spa->spa_active_allocator = a;
zfs_dbgmsg("spa allocator: %s\n", metaslab_allocators[a].msop_name);
}
int
spa_get_allocator(spa_t *spa)
{
return (spa->spa_active_allocator);
}
#if defined(_KERNEL)
int
param_set_active_allocator_common(const char *val)
{
char *p;
if (val == NULL)
return (SET_ERROR(EINVAL));
if ((p = strchr(val, '\n')) != NULL)
*p = '\0';
int a = spa_find_allocator_byname(val);
if (a < 0)
return (SET_ERROR(EINVAL));
zfs_active_allocator = metaslab_allocators[a].msop_name;
return (0);
}
#endif
metaslab_ops_t *
metaslab_allocator(spa_t *spa)
{
int allocator = spa_get_allocator(spa);
return (&metaslab_allocators[allocator]);
}
/* /*
* ========================================================================== * ==========================================================================
* Dynamic Fit (df) block allocator * Dynamic Fit (df) block allocator
@ -1736,12 +1796,6 @@ metaslab_df_alloc(metaslab_t *msp, uint64_t size)
return (offset); return (offset);
} }
const metaslab_ops_t zfs_metaslab_ops = {
metaslab_df_alloc
};
#endif /* WITH_DF_BLOCK_ALLOCATOR */
#if defined(WITH_CF_BLOCK_ALLOCATOR)
/* /*
* ========================================================================== * ==========================================================================
* Cursor fit block allocator - * Cursor fit block allocator -
@ -1784,12 +1838,6 @@ metaslab_cf_alloc(metaslab_t *msp, uint64_t size)
return (offset); return (offset);
} }
const metaslab_ops_t zfs_metaslab_ops = {
metaslab_cf_alloc
};
#endif /* WITH_CF_BLOCK_ALLOCATOR */
#if defined(WITH_NDF_BLOCK_ALLOCATOR)
/* /*
* ========================================================================== * ==========================================================================
* New dynamic fit allocator - * New dynamic fit allocator -
@ -1846,12 +1894,6 @@ metaslab_ndf_alloc(metaslab_t *msp, uint64_t size)
return (-1ULL); return (-1ULL);
} }
const metaslab_ops_t zfs_metaslab_ops = {
metaslab_ndf_alloc
};
#endif /* WITH_NDF_BLOCK_ALLOCATOR */
/* /*
* ========================================================================== * ==========================================================================
* Metaslabs * Metaslabs
@ -6232,3 +6274,9 @@ ZFS_MODULE_PARAM(zfs_metaslab, zfs_metaslab_, try_hard_before_gang, INT,
ZFS_MODULE_PARAM(zfs_metaslab, zfs_metaslab_, find_max_tries, UINT, ZMOD_RW, ZFS_MODULE_PARAM(zfs_metaslab, zfs_metaslab_, find_max_tries, UINT, ZMOD_RW,
"Normally only consider this many of the best metaslabs in each vdev"); "Normally only consider this many of the best metaslabs in each vdev");
/* BEGIN CSTYLED */
ZFS_MODULE_PARAM_CALL(zfs, zfs_, active_allocator,
param_set_active_allocator, param_get_charp, ZMOD_RW,
"SPA active allocator");
/* END CSTYLED */

View File

@ -1295,24 +1295,26 @@ spa_thread(void *arg)
} }
#endif #endif
extern metaslab_ops_t *metaslab_allocator(spa_t *spa);
/* /*
* Activate an uninitialized pool. * Activate an uninitialized pool.
*/ */
static void static void
spa_activate(spa_t *spa, spa_mode_t mode) spa_activate(spa_t *spa, spa_mode_t mode)
{ {
metaslab_ops_t *msp = metaslab_allocator(spa);
ASSERT(spa->spa_state == POOL_STATE_UNINITIALIZED); ASSERT(spa->spa_state == POOL_STATE_UNINITIALIZED);
spa->spa_state = POOL_STATE_ACTIVE; spa->spa_state = POOL_STATE_ACTIVE;
spa->spa_mode = mode; spa->spa_mode = mode;
spa->spa_read_spacemaps = spa_mode_readable_spacemaps; spa->spa_read_spacemaps = spa_mode_readable_spacemaps;
spa->spa_normal_class = metaslab_class_create(spa, &zfs_metaslab_ops); spa->spa_normal_class = metaslab_class_create(spa, msp);
spa->spa_log_class = metaslab_class_create(spa, &zfs_metaslab_ops); spa->spa_log_class = metaslab_class_create(spa, msp);
spa->spa_embedded_log_class = spa->spa_embedded_log_class = metaslab_class_create(spa, msp);
metaslab_class_create(spa, &zfs_metaslab_ops); spa->spa_special_class = metaslab_class_create(spa, msp);
spa->spa_special_class = metaslab_class_create(spa, &zfs_metaslab_ops); spa->spa_dedup_class = metaslab_class_create(spa, msp);
spa->spa_dedup_class = metaslab_class_create(spa, &zfs_metaslab_ops);
/* Try to create a covering process */ /* Try to create a covering process */
mutex_enter(&spa->spa_proc_lock); mutex_enter(&spa->spa_proc_lock);

View File

@ -389,6 +389,11 @@ static const uint64_t spa_min_slop = 128ULL * 1024 * 1024;
static const uint64_t spa_max_slop = 128ULL * 1024 * 1024 * 1024; static const uint64_t spa_max_slop = 128ULL * 1024 * 1024 * 1024;
static const int spa_allocators = 4; static const int spa_allocators = 4;
/*
* Spa active allocator.
* Valid values are zfs_active_allocator=<dynamic|cursor|new-dynamic>.
*/
const char *zfs_active_allocator = "dynamic";
void void
spa_load_failed(spa_t *spa, const char *fmt, ...) spa_load_failed(spa_t *spa, const char *fmt, ...)
@ -710,6 +715,7 @@ spa_add(const char *name, nvlist_t *config, const char *altroot)
spa->spa_deadman_synctime = MSEC2NSEC(zfs_deadman_synctime_ms); spa->spa_deadman_synctime = MSEC2NSEC(zfs_deadman_synctime_ms);
spa->spa_deadman_ziotime = MSEC2NSEC(zfs_deadman_ziotime_ms); spa->spa_deadman_ziotime = MSEC2NSEC(zfs_deadman_ziotime_ms);
spa_set_deadman_failmode(spa, zfs_deadman_failmode); spa_set_deadman_failmode(spa, zfs_deadman_failmode);
spa_set_allocator(spa, zfs_active_allocator);
zfs_refcount_create(&spa->spa_refcount); zfs_refcount_create(&spa->spa_refcount);
spa_config_lock_init(spa); spa_config_lock_init(spa);