From 7d4b75a0bd8176f08bc5e68b8e0d1e6d5acc2a19 Mon Sep 17 00:00:00 2001 From: Boris Protopopov Date: Tue, 5 Jan 2016 15:47:58 -0500 Subject: [PATCH] Use zfs range locks in ztest --- cmd/ztest/ztest.c | 154 +++++++++++++++++++++++++++------------ include/sys/zfs_rlock.h | 20 ++++- lib/libzpool/Makefile.am | 1 + module/zfs/zfs_rlock.c | 10 ++- 4 files changed, 136 insertions(+), 49 deletions(-) diff --git a/cmd/ztest/ztest.c b/cmd/ztest/ztest.c index 8df983bb4761..7a63095551ff 100644 --- a/cmd/ztest/ztest.c +++ b/cmd/ztest/ztest.c @@ -101,6 +101,7 @@ #include #include #include +#include #include #include #include @@ -231,15 +232,6 @@ typedef struct bufwad { uint64_t bw_data; } bufwad_t; -/* - * XXX -- fix zfs range locks to be generic so we can use them here. - */ -typedef enum { - RL_READER, - RL_WRITER, - RL_APPEND -} rl_type_t; - typedef struct rll { void *rll_writer; int rll_readers; @@ -247,12 +239,10 @@ typedef struct rll { kcondvar_t rll_cv; } rll_t; -typedef struct rl { - uint64_t rl_object; - uint64_t rl_offset; - uint64_t rl_size; - rll_t *rl_lock; -} rl_t; +typedef struct zll { + list_t z_list; + kmutex_t z_lock; +} zll_t; #define ZTEST_RANGE_LOCKS 64 #define ZTEST_OBJECT_LOCKS 64 @@ -284,7 +274,7 @@ typedef struct ztest_ds { char zd_name[MAXNAMELEN]; kmutex_t zd_dirobj_lock; rll_t zd_object_lock[ZTEST_OBJECT_LOCKS]; - rll_t zd_range_lock[ZTEST_RANGE_LOCKS]; + zll_t zd_range_lock[ZTEST_RANGE_LOCKS]; } ztest_ds_t; /* @@ -1135,6 +1125,93 @@ ztest_spa_prop_set_uint64(zpool_prop_t prop, uint64_t value) return (error); } + +/* + * Object and range lock mechanics + */ +static znode_t * +ztest_znode_init(uint64_t object) +{ + znode_t *zp = umem_alloc(sizeof (*zp), UMEM_NOFAIL); + + list_link_init(&zp->z_lnode); + refcount_create(&zp->z_refcnt); + zp->z_object = object; + mutex_init(&zp->z_range_lock, NULL, MUTEX_DEFAULT, NULL); + avl_create(&zp->z_range_avl, zfs_range_compare, + sizeof (rl_t), offsetof(rl_t, r_node)); + /* skip the append mode processing for simplicity */ + zp->z_is_zvol = B_TRUE; + + return (zp); +} + +static void +ztest_znode_fini(znode_t *zp) +{ + ASSERT(refcount_is_zero(&zp->z_refcnt)); + zp->z_is_zvol = B_FALSE; + avl_destroy(&zp->z_range_avl); + mutex_destroy(&zp->z_range_lock); + zp->z_object = 0; + refcount_destroy(&zp->z_refcnt); + list_link_init(&zp->z_lnode); + umem_free(zp, sizeof(*zp)); +} + +static void +ztest_zll_init(zll_t *zll) +{ + mutex_init(&zll->z_lock, NULL, MUTEX_DEFAULT, NULL); + list_create(&zll->z_list, sizeof (znode_t), offsetof (znode_t, z_lnode)); +} + +static void +ztest_zll_destroy(zll_t *zll) +{ + list_destroy(&zll->z_list); + mutex_destroy(&zll->z_lock); +} + +#define RL_TAG "range_lock" +static znode_t * +ztest_znode_get(ztest_ds_t *zd, uint64_t object) +{ + zll_t *zll = &zd->zd_range_lock[object & (ZTEST_OBJECT_LOCKS - 1)]; + znode_t *zp = NULL; + mutex_enter(&zll->z_lock); + for (zp = list_head(&zll->z_list); (zp); + zp = list_next(&zll->z_list, zp)) { + if (zp->z_object == object) { + refcount_add(&zp->z_refcnt, RL_TAG); + break; + } + } + if (zp == NULL) { + zp = ztest_znode_init(object); + refcount_add(&zp->z_refcnt, RL_TAG); + list_insert_head(&zll->z_list, zp); + } + mutex_exit(&zll->z_lock); + return (zp); +} + +static void +ztest_znode_put(ztest_ds_t *zd, znode_t *zp) +{ + zll_t *zll = NULL; + ASSERT3U(zp->z_object, !=, 0); + zll = &zd->zd_range_lock[zp->z_object & (ZTEST_OBJECT_LOCKS - 1)]; + mutex_enter(&zll->z_lock); + refcount_remove(&zp->z_refcnt, RL_TAG); + if (refcount_is_zero(&zp->z_refcnt)) { + list_remove(&zll->z_list, zp); + ztest_znode_fini(zp); + } + mutex_exit(&zll->z_lock); +} + + static void ztest_rll_init(rll_t *rll) { @@ -1211,29 +1288,16 @@ static rl_t * ztest_range_lock(ztest_ds_t *zd, uint64_t object, uint64_t offset, uint64_t size, rl_type_t type) { - uint64_t hash = object ^ (offset % (ZTEST_RANGE_LOCKS + 1)); - rll_t *rll = &zd->zd_range_lock[hash & (ZTEST_RANGE_LOCKS - 1)]; - rl_t *rl; - - rl = umem_alloc(sizeof (*rl), UMEM_NOFAIL); - rl->rl_object = object; - rl->rl_offset = offset; - rl->rl_size = size; - rl->rl_lock = rll; - - ztest_rll_lock(rll, type); - - return (rl); + znode_t *zp = ztest_znode_get(zd, object); + return (zfs_range_lock(zp, offset, size, type)); } static void -ztest_range_unlock(rl_t *rl) +ztest_range_unlock(ztest_ds_t *zd, rl_t *rl) { - rll_t *rll = rl->rl_lock; - - ztest_rll_unlock(rll); - - umem_free(rl, sizeof (*rl)); + znode_t *zp = rl->r_zp; + zfs_range_unlock(rl); + ztest_znode_put(zd, zp); } static void @@ -1255,7 +1319,7 @@ ztest_zd_init(ztest_ds_t *zd, ztest_shared_ds_t *szd, objset_t *os) ztest_rll_init(&zd->zd_object_lock[l]); for (l = 0; l < ZTEST_RANGE_LOCKS; l++) - ztest_rll_init(&zd->zd_range_lock[l]); + ztest_zll_init(&zd->zd_range_lock[l]); } static void @@ -1270,7 +1334,7 @@ ztest_zd_fini(ztest_ds_t *zd) ztest_rll_destroy(&zd->zd_object_lock[l]); for (l = 0; l < ZTEST_RANGE_LOCKS; l++) - ztest_rll_destroy(&zd->zd_range_lock[l]); + ztest_zll_destroy(&zd->zd_range_lock[l]); } #define TXG_MIGHTWAIT (ztest_random(10) == 0 ? TXG_NOWAIT : TXG_WAIT) @@ -1676,7 +1740,7 @@ ztest_replay_write(ztest_ds_t *zd, lr_write_t *lr, boolean_t byteswap) if (abuf != NULL) dmu_return_arcbuf(abuf); dmu_buf_rele(db, FTAG); - ztest_range_unlock(rl); + ztest_range_unlock(zd, rl); ztest_object_unlock(zd, lr->lr_foid); return (ENOSPC); } @@ -1733,7 +1797,7 @@ ztest_replay_write(ztest_ds_t *zd, lr_write_t *lr, boolean_t byteswap) dmu_tx_commit(tx); - ztest_range_unlock(rl); + ztest_range_unlock(zd, rl); ztest_object_unlock(zd, lr->lr_foid); return (0); @@ -1760,7 +1824,7 @@ ztest_replay_truncate(ztest_ds_t *zd, lr_truncate_t *lr, boolean_t byteswap) txg = ztest_tx_assign(tx, TXG_WAIT, FTAG); if (txg == 0) { - ztest_range_unlock(rl); + ztest_range_unlock(zd, rl); ztest_object_unlock(zd, lr->lr_foid); return (ENOSPC); } @@ -1772,7 +1836,7 @@ ztest_replay_truncate(ztest_ds_t *zd, lr_truncate_t *lr, boolean_t byteswap) dmu_tx_commit(tx); - ztest_range_unlock(rl); + ztest_range_unlock(zd, rl); ztest_object_unlock(zd, lr->lr_foid); return (0); @@ -1880,12 +1944,12 @@ static void ztest_get_done(zgd_t *zgd, int error) { ztest_ds_t *zd = zgd->zgd_private; - uint64_t object = zgd->zgd_rl->rl_object; + uint64_t object = zgd->zgd_rl->r_zp->z_object; if (zgd->zgd_db) dmu_buf_rele(zgd->zgd_db, zgd); - ztest_range_unlock(zgd->zgd_rl); + ztest_range_unlock(zd, zgd->zgd_rl); ztest_object_unlock(zd, object); if (error == 0 && zgd->zgd_bp) @@ -2221,7 +2285,7 @@ ztest_prealloc(ztest_ds_t *zd, uint64_t object, uint64_t offset, uint64_t size) (void) dmu_free_long_range(os, object, offset, size); } - ztest_range_unlock(rl); + ztest_range_unlock(zd, rl); ztest_object_unlock(zd, object); } @@ -4351,7 +4415,7 @@ ztest_write_free(ztest_ds_t *zd, ztest_od_t *od, dmu_object_info_t *doi) break; } - ztest_range_unlock(rl); + ztest_range_unlock(zd, rl); ztest_object_unlock(zd, object); } diff --git a/include/sys/zfs_rlock.h b/include/sys/zfs_rlock.h index ea5e40369291..09e974bcbe46 100644 --- a/include/sys/zfs_rlock.h +++ b/include/sys/zfs_rlock.h @@ -34,6 +34,24 @@ extern "C" { #include +#else + +#include +#include +#include + +/* User mode znode emulation for ztest */ +typedef struct znode { + list_node_t z_lnode; /* needed by ztest */ + refcount_t z_refcnt; /* needed by ztest */ + uint64_t z_object; /* needed by ztest */ + kmutex_t z_range_lock; + avl_tree_t z_range_avl; + boolean_t z_is_zvol; +} znode_t; + +#endif /* _KERNEL */ + typedef enum { RL_READER, RL_WRITER, @@ -78,8 +96,6 @@ void zfs_range_reduce(rl_t *rl, uint64_t off, uint64_t len); */ int zfs_range_compare(const void *arg1, const void *arg2); -#endif /* _KERNEL */ - #ifdef __cplusplus } #endif diff --git a/lib/libzpool/Makefile.am b/lib/libzpool/Makefile.am index 0bcb5e466518..ab7d0019ed5f 100644 --- a/lib/libzpool/Makefile.am +++ b/lib/libzpool/Makefile.am @@ -101,6 +101,7 @@ KERNEL_C = \ zfs_fuid.c \ zfs_sa.c \ zfs_znode.c \ + zfs_rlock.c \ zil.c \ zio.c \ zio_checksum.c \ diff --git a/module/zfs/zfs_rlock.c b/module/zfs/zfs_rlock.c index 5064eb796b79..cd723144e28c 100644 --- a/module/zfs/zfs_rlock.c +++ b/module/zfs/zfs_rlock.c @@ -106,10 +106,15 @@ zfs_range_lock_writer(znode_t *zp, rl_t *new) avl_tree_t *tree = &zp->z_range_avl; rl_t *rl; avl_index_t where; - uint64_t end_size; uint64_t off = new->r_off; uint64_t len = new->r_len; +#ifdef _KERNEL + uint64_t end_size; +#else + ASSERT3U(zp->z_is_zvol,==,B_TRUE); +#endif + for (;;) { /* * Range locking is also used by zvol and uses a @@ -122,6 +127,7 @@ zfs_range_lock_writer(znode_t *zp, rl_t *new) * we could make the range locking code generically available * to other non-zfs consumers. */ +#ifdef _KERNEL if (!zp->z_is_zvol) { /* caller is ZPL */ /* * If in append mode pick up the current end of file. @@ -142,7 +148,7 @@ zfs_range_lock_writer(znode_t *zp, rl_t *new) new->r_len = UINT64_MAX; } } - +#endif /* * First check for the usual case of no locks */