diff --git a/cmd/Makefile.am b/cmd/Makefile.am index 96040976e53e..8ad66dde4618 100644 --- a/cmd/Makefile.am +++ b/cmd/Makefile.am @@ -98,15 +98,17 @@ endif if USING_PYTHON -bin_SCRIPTS += arc_summary arcstat dbufstat zilstat -CLEANFILES += arc_summary arcstat dbufstat zilstat -dist_noinst_DATA += %D%/arc_summary %D%/arcstat.in %D%/dbufstat.in %D%/zilstat.in +bin_SCRIPTS += arc_summary arcstat dbufstat zilstat zleak +CLEANFILES += arc_summary arcstat dbufstat zilstat zleak +dist_noinst_DATA += %D%/arc_summary %D%/arcstat.in %D%/dbufstat.in %D%/zilstat.in %D%/zleak $(call SUBST,arcstat,%D%/) $(call SUBST,dbufstat,%D%/) $(call SUBST,zilstat,%D%/) arc_summary: %D%/arc_summary $(AM_V_at)cp $< $@ +zleak: %D%/zleak + $(AM_V_at)cp $< $@ endif diff --git a/cmd/zdb/zdb.c b/cmd/zdb/zdb.c index 75b54ab4ea56..be6adcd083cf 100644 --- a/cmd/zdb/zdb.c +++ b/cmd/zdb/zdb.c @@ -107,7 +107,9 @@ extern uint_t zfs_reconstruct_indirect_combinations_max; extern uint_t zfs_btree_verify_intensity; static const char cmdname[] = "zdb"; -uint8_t dump_opt[256]; +uint8_t dump_opt[512]; + +#define ALLOCATABLE_OPT 256 typedef void object_viewer_t(objset_t *, uint64_t, void *data, size_t size); @@ -1650,6 +1652,16 @@ dump_metaslab_stats(metaslab_t *msp) dump_histogram(rt->rt_histogram, ZFS_RANGE_TREE_HISTOGRAM_SIZE, 0); } +static void +dump_allocated(void *arg, uint64_t start, uint64_t size) +{ + uint64_t *off = arg; + if (*off != start) + (void) printf("ALLOC: %"PRIu64" %"PRIu64"\n", *off, + start - *off); + *off = start + size; +} + static void dump_metaslab(metaslab_t *msp) { @@ -1666,13 +1678,24 @@ dump_metaslab(metaslab_t *msp) (u_longlong_t)msp->ms_id, (u_longlong_t)msp->ms_start, (u_longlong_t)space_map_object(sm), freebuf); - if (dump_opt['m'] > 2 && !dump_opt['L']) { + if (dump_opt[ALLOCATABLE_OPT] || + (dump_opt['m'] > 2 && !dump_opt['L'])) { mutex_enter(&msp->ms_lock); VERIFY0(metaslab_load(msp)); + } + + if (dump_opt['m'] > 2 && !dump_opt['L']) { zfs_range_tree_stat_verify(msp->ms_allocatable); dump_metaslab_stats(msp); - metaslab_unload(msp); - mutex_exit(&msp->ms_lock); + } + + if (dump_opt[ALLOCATABLE_OPT]) { + uint64_t off = msp->ms_start; + zfs_range_tree_walk(msp->ms_allocatable, dump_allocated, + &off); + if (off != msp->ms_start + msp->ms_size) + (void) printf("ALLOC: %"PRIu64" %"PRIu64"\n", off, + msp->ms_size - off); } if (dump_opt['m'] > 1 && sm != NULL && @@ -1687,6 +1710,12 @@ dump_metaslab(metaslab_t *msp) SPACE_MAP_HISTOGRAM_SIZE, sm->sm_shift); } + if (dump_opt[ALLOCATABLE_OPT] || + (dump_opt['m'] > 2 && !dump_opt['L'])) { + metaslab_unload(msp); + mutex_exit(&msp->ms_lock); + } + if (vd->vdev_ops == &vdev_draid_ops) ASSERT3U(msp->ms_size, <=, 1ULL << vd->vdev_ms_shift); else @@ -1723,8 +1752,9 @@ print_vdev_metaslab_header(vdev_t *vd) } } - (void) printf("\tvdev %10llu %s", - (u_longlong_t)vd->vdev_id, bias_str); + (void) printf("\tvdev %10llu\t%s metaslab shift %4lld", + (u_longlong_t)vd->vdev_id, bias_str, + (u_longlong_t)vd->vdev_ms_shift); if (ms_flush_data_obj != 0) { (void) printf(" ms_unflushed_phys object %llu", @@ -9315,6 +9345,8 @@ main(int argc, char **argv) {"all-reconstruction", no_argument, NULL, 'Y'}, {"livelist", no_argument, NULL, 'y'}, {"zstd-headers", no_argument, NULL, 'Z'}, + {"allocatable-map", no_argument, NULL, + ALLOCATABLE_OPT}, {0, 0, 0, 0} }; @@ -9345,6 +9377,7 @@ main(int argc, char **argv) case 'u': case 'y': case 'Z': + case ALLOCATABLE_OPT: dump_opt[c]++; dump_all = 0; break; diff --git a/cmd/zdb/zdb.h b/cmd/zdb/zdb.h index 6b6c9169816b..48b561eb202c 100644 --- a/cmd/zdb/zdb.h +++ b/cmd/zdb/zdb.h @@ -29,6 +29,6 @@ #define _ZDB_H void dump_intent_log(zilog_t *); -extern uint8_t dump_opt[256]; +extern uint8_t dump_opt[512]; #endif /* _ZDB_H */ diff --git a/cmd/zdb/zdb_il.c b/cmd/zdb/zdb_il.c index 6b90b08ca1b1..ab63e8bd2b4a 100644 --- a/cmd/zdb/zdb_il.c +++ b/cmd/zdb/zdb_il.c @@ -48,7 +48,7 @@ #include "zdb.h" -extern uint8_t dump_opt[256]; +extern uint8_t dump_opt[512]; static char tab_prefix[4] = "\t\t\t"; diff --git a/cmd/zleak b/cmd/zleak new file mode 100755 index 000000000000..7112ece6fe97 --- /dev/null +++ b/cmd/zleak @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: CDDL-1.0 + +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2025 by Klara, Inc. +# + +import argparse, fileinput, libzfs_core, sys, errno + +def perform_raw_alloc(pool, ms_shift, ms_count, vdev_id, allocs, force, + verbose): + if args.verbose == 1: + print(f"Raw alloc: vdev {vdev_id}, {count} starting with offset " + f"{allocs[0][0]}") + if args.verbose >= 2: + print(f"Raw alloc: {pool} {ms_shift} {ms_count} {vdev_id} {count}") + try: + libzfs_core.lzc_raw_alloc(pool, 1 << ms_shift, ms_count, vdev_id, + allocs, args.force) + except libzfs_core.exceptions.ZFSGenericError as e: + if e.errno == errno.EINVAL: + print("Invalid map for provided pool") + sys.exit(1) + assert (e.errno == errno.E2BIG and force) + sys.exit(0) + +allocs = [] +count = 0 + +parser = argparse.ArgumentParser( + prog='zleak', + description='facility to replicate memory fragmentation in ZFS' +) +parser.add_argument('poolname') +parser.add_argument('-v', '--verbose', action='count', default=0) +parser.add_argument('-f', '--force', action='store_true', default=False) +args = parser.parse_args() + +pool = args.poolname.encode('utf-8') + +for line in fileinput.input('-'): + dump = False + line = line.rstrip() + if not line.startswith(("ALLOC: ", "\tvdev ", "\tmetaslabs ")): + continue + + tokens = line.split() + if line.startswith("\tvdev "): + next_vdev_id = int(tokens[1]) + next_ms_shift = int(tokens[4]) + next_ms_count = 0 + dump = True + elif line.startswith("\tmetaslabs "): + next_ms_count = int(tokens[1]) + else: + start = int(tokens[1]) + size = int(tokens[2]) + allocs.append((start, size)) + count = count + 1 + + if count == 1000000 or (dump and count != 0): + perform_raw_alloc(pool, ms_shift, ms_count, vdev_id, allocs, + args.force, args.verbose) + count = 0 + allocs = [] + vdev_id = next_vdev_id + ms_shift = next_ms_shift + ms_count = next_ms_count + + +if count > 0: + perform_raw_alloc(pool, ms_shift, ms_count, vdev_id, allocs, + args.force, args.verbose) + diff --git a/contrib/debian/openzfs-zfsutils.install b/contrib/debian/openzfs-zfsutils.install index 37284a78ad18..abb2481fd3d4 100644 --- a/contrib/debian/openzfs-zfsutils.install +++ b/contrib/debian/openzfs-zfsutils.install @@ -40,6 +40,7 @@ usr/sbin/arc_summary usr/sbin/arcstat usr/sbin/dbufstat usr/sbin/zilstat +usr/sbin/zleak usr/share/zfs/compatibility.d/ usr/share/bash-completion/completions usr/share/man/man1/arcstat.1 diff --git a/contrib/debian/rules.in b/contrib/debian/rules.in index 3226d604546c..fc6503fd6f6a 100755 --- a/contrib/debian/rules.in +++ b/contrib/debian/rules.in @@ -85,6 +85,7 @@ override_dh_auto_install: mv '$(CURDIR)/debian/tmp/usr/bin/arcstat' '$(CURDIR)/debian/tmp/usr/sbin/arcstat' mv '$(CURDIR)/debian/tmp/usr/bin/dbufstat' '$(CURDIR)/debian/tmp/usr/sbin/dbufstat' mv '$(CURDIR)/debian/tmp/usr/bin/zilstat' '$(CURDIR)/debian/tmp/usr/sbin/zilstat' + mv '$(CURDIR)/debian/tmp/usr/bin/zleak' '$(CURDIR)/debian/tmp/usr/sbin/zleak' @# Zed has dependencies outside of the system root. mv '$(CURDIR)/debian/tmp/sbin/zed' '$(CURDIR)/debian/tmp/usr/sbin/zed' diff --git a/contrib/pyzfs/libzfs_core/__init__.py b/contrib/pyzfs/libzfs_core/__init__.py index 13b50ca4329f..9d8071e6fe57 100644 --- a/contrib/pyzfs/libzfs_core/__init__.py +++ b/contrib/pyzfs/libzfs_core/__init__.py @@ -95,6 +95,7 @@ lzc_set_props, lzc_list_children, lzc_list_snaps, + lzc_raw_alloc, receive_header, ) @@ -151,6 +152,7 @@ 'lzc_set_props', 'lzc_list_children', 'lzc_list_snaps', + 'lzc_raw_alloc', 'receive_header', ] diff --git a/contrib/pyzfs/libzfs_core/_error_translation.py b/contrib/pyzfs/libzfs_core/_error_translation.py index d5491a3245cd..46085c5d589e 100644 --- a/contrib/pyzfs/libzfs_core/_error_translation.py +++ b/contrib/pyzfs/libzfs_core/_error_translation.py @@ -696,6 +696,12 @@ def lzc_list_translate_error(ret, name, opts): raise _generic_exception(ret, name, "Error obtaining a list") +def lzc_raw_alloc_translate_errors(ret, name): + if ret == 0: + return + raise _generic_exception(ret, name, "Error performing raw allocations") + + def _handle_err_list(ret, errlist, names, exception, mapper): ''' Convert one or more errors from an operation into the requested exception. diff --git a/contrib/pyzfs/libzfs_core/_libzfs_core.py b/contrib/pyzfs/libzfs_core/_libzfs_core.py index 0ebf99be67c2..b908f06ea0be 100644 --- a/contrib/pyzfs/libzfs_core/_libzfs_core.py +++ b/contrib/pyzfs/libzfs_core/_libzfs_core.py @@ -2056,6 +2056,38 @@ def lzc_list_snaps(name): return iter(snaps) +def lzc_raw_alloc(poolname, metaslab_size, metaslab_count, vdev_id, + allocations, force): + ''' + Allocate regions of the provided vdev directly; useful primarily for + performance analysis of fragmented pools. Results in space leakage that it + is not currently possible to reclaim. + + :param bytes poolname: the name of the pool to allocate in + :param int metaslab_size: the size of a metaslab in this pool (for + validation) + :param int metaslab_count: the number of metaslabs in this top level + vdev (for validation) + :param int vdev_id: the id of the top-level vdev to perform allocations + from + :param allocations: pairs of offset and size to allocate + :type fromsnap: list of (int, int) + + :raises TooManyArguments: if too many allocations are passed in + ''' + if len(allocations) > 1000000: + raise exceptions.TooManyArguments() + allocs = _ffi.new(f"uint64_t[{2 * len(allocations)}]") + for i in range(len(allocations)): + (s, l) = allocations[i] + allocs[2 * i] = s + allocs[2 * i + 1] = l + ret = _lib.lzc_raw_alloc(poolname, uint64_t(metaslab_size), + uint64_t(metaslab_count), uint64_t(vdev_id), + allocs, 2 * len(allocations), force) + errors.lzc_raw_alloc_translate_errors(ret, poolname) + + # TODO: a better way to init and uninit the library def _initialize(): class LazyInit(object): diff --git a/contrib/pyzfs/libzfs_core/bindings/libzfs_core.py b/contrib/pyzfs/libzfs_core/bindings/libzfs_core.py index ca752f65413d..542deba2c92f 100644 --- a/contrib/pyzfs/libzfs_core/bindings/libzfs_core.py +++ b/contrib/pyzfs/libzfs_core/bindings/libzfs_core.py @@ -140,6 +140,9 @@ int lzc_inherit(const char *fsname, const char *name, nvlist_t *); int lzc_set_props(const char *, nvlist_t *, nvlist_t *, nvlist_t *); int lzc_list (const char *, nvlist_t *); + + int lzc_raw_alloc(const char *, uint64_t, uint64_t, uint64_t, + uint64_t *, uint_t, boolean_t); """ SOURCE = """ diff --git a/contrib/pyzfs/libzfs_core/exceptions.py b/contrib/pyzfs/libzfs_core/exceptions.py index b26a37f5de10..fe610feb0b68 100644 --- a/contrib/pyzfs/libzfs_core/exceptions.py +++ b/contrib/pyzfs/libzfs_core/exceptions.py @@ -605,4 +605,8 @@ class RaidzExpansionRunning(ZFSError): message = "A raidz device is currently expanding" +class TooManyArguments(ZFSError): + error = errno.EOVERFLOW + message = "Too many arguments provided" + # vim: softtabstop=4 tabstop=4 expandtab shiftwidth=4 diff --git a/include/libzfs_core.h b/include/libzfs_core.h index 231beaa69290..009fd8f2f534 100644 --- a/include/libzfs_core.h +++ b/include/libzfs_core.h @@ -164,6 +164,8 @@ _LIBZFS_CORE_H int lzc_scrub(zfs_ioc_t, const char *, nvlist_t *, nvlist_t **); _LIBZFS_CORE_H int lzc_ddt_prune(const char *, zpool_ddt_prune_unit_t, uint64_t); +_LIBZFS_CORE_H int lzc_raw_alloc(const char *, uint64_t, uint64_t, uint64_t, + uint64_t *, uint_t, boolean_t); #ifdef __cplusplus } diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h index c8deb5be419e..5875659fa0cc 100644 --- a/include/sys/fs/zfs.h +++ b/include/sys/fs/zfs.h @@ -1464,7 +1464,7 @@ typedef enum { */ typedef enum zfs_ioc { /* - * Core features - 89/128 numbers reserved. + * Core features - 90/128 numbers reserved. */ #ifdef __FreeBSD__ ZFS_IOC_FIRST = 0, @@ -1562,6 +1562,7 @@ typedef enum zfs_ioc { ZFS_IOC_POOL_SCRUB, /* 0x5a57 */ ZFS_IOC_POOL_PREFETCH, /* 0x5a58 */ ZFS_IOC_DDT_PRUNE, /* 0x5a59 */ + ZFS_IOC_RAW_ALLOC, /* 0x5a5a */ /* * Per-platform (Optional) - 8/128 numbers reserved. diff --git a/include/sys/metaslab.h b/include/sys/metaslab.h index 36cbe06bacce..6824b9c8051f 100644 --- a/include/sys/metaslab.h +++ b/include/sys/metaslab.h @@ -147,6 +147,9 @@ extern int metaslab_debug_load; zfs_range_seg_type_t metaslab_calculate_range_tree_type(vdev_t *vdev, metaslab_t *msp, uint64_t *start, uint64_t *shift); +void metaslab_force_alloc(metaslab_t *msp, uint64_t start, uint64_t size, + dmu_tx_t *tx); + #ifdef __cplusplus } #endif diff --git a/include/sys/vdev.h b/include/sys/vdev.h index 7f5a9aaef1b4..24038fe656a0 100644 --- a/include/sys/vdev.h +++ b/include/sys/vdev.h @@ -230,6 +230,9 @@ extern int vdev_label_init(vdev_t *vd, uint64_t txg, vdev_labeltype_t reason); extern int vdev_prop_set(vdev_t *vd, nvlist_t *innvl, nvlist_t *outnvl); extern int vdev_prop_get(vdev_t *vd, nvlist_t *nvprops, nvlist_t *outnvl); +extern int vdev_raw_alloc(vdev_t *vd, uint64_t *allocations, + uint_t alloc_count); + #ifdef __cplusplus } #endif diff --git a/lib/libzfs_core/libzfs_core.abi b/lib/libzfs_core/libzfs_core.abi index 2af20894853d..fb72bc75c57b 100644 --- a/lib/libzfs_core/libzfs_core.abi +++ b/lib/libzfs_core/libzfs_core.abi @@ -182,6 +182,7 @@ + @@ -1426,6 +1427,11 @@ + + + + + @@ -1437,11 +1443,6 @@ - - - - - @@ -1774,6 +1775,7 @@ + @@ -2739,6 +2741,8 @@ + + @@ -2874,6 +2878,13 @@ + + + + + + + @@ -3391,6 +3402,16 @@ + + + + + + + + + + diff --git a/lib/libzfs_core/libzfs_core.c b/lib/libzfs_core/libzfs_core.c index 9347aa7c6a28..04bd28e27281 100644 --- a/lib/libzfs_core/libzfs_core.c +++ b/lib/libzfs_core/libzfs_core.c @@ -1994,3 +1994,24 @@ lzc_ddt_prune(const char *pool, zpool_ddt_prune_unit_t unit, uint64_t amount) return (error); } + +int +lzc_raw_alloc(const char *pool, uint64_t metaslab_size, + uint64_t metaslab_count, uint64_t vdev_id, + uint64_t *allocations, uint_t alloc_count, boolean_t force) +{ + int error; + nvlist_t *args = fnvlist_alloc(); + + fnvlist_add_uint64(args, "metaslab_size", metaslab_size); + fnvlist_add_uint64(args, "metaslab_count", metaslab_count); + fnvlist_add_uint64(args, "vdev_id", vdev_id); + fnvlist_add_uint64_array(args, "allocations", allocations, alloc_count); + fnvlist_add_boolean_value(args, "force", force); + + error = lzc_ioctl(ZFS_IOC_RAW_ALLOC, pool, args, NULL); + + fnvlist_free(args); + + return (error); +} diff --git a/module/zfs/metaslab.c b/module/zfs/metaslab.c index 082d379cded5..945b3e4167c5 100644 --- a/module/zfs/metaslab.c +++ b/module/zfs/metaslab.c @@ -4756,6 +4756,34 @@ metaslab_trace_fini(zio_alloc_list_t *zal) * ========================================================================== */ +void +metaslab_force_alloc(metaslab_t *msp, uint64_t start, uint64_t size, + dmu_tx_t *tx) +{ + ASSERT(msp->ms_disabled); + ASSERT(MUTEX_HELD(&msp->ms_lock)); + uint64_t txg = dmu_tx_get_txg(tx); + + for (uint64_t off = start; off < start + size; ) { + uint64_t ostart, osize; + boolean_t found = zfs_range_tree_find_in(msp->ms_allocatable, + off, start + size - off, &ostart, &osize); + if (!found) + break; + zfs_range_tree_remove(msp->ms_allocatable, ostart, + osize); + + if (zfs_range_tree_is_empty(msp->ms_allocating[txg & TXG_MASK])) + vdev_dirty(msp->ms_group->mg_vd, VDD_METASLAB, msp, + txg); + + zfs_range_tree_add(msp->ms_allocating[txg & TXG_MASK], ostart, + osize); + msp->ms_allocating_total += osize; + off = ostart + osize; + } +} + static void metaslab_group_alloc_increment(spa_t *spa, uint64_t vdev, int allocator, int flags, uint64_t psize, const void *tag) diff --git a/module/zfs/vdev.c b/module/zfs/vdev.c index 01758b0c54c0..b4211a8e9194 100644 --- a/module/zfs/vdev.c +++ b/module/zfs/vdev.c @@ -6584,6 +6584,47 @@ vdev_prop_get(vdev_t *vd, nvlist_t *innvl, nvlist_t *outnvl) return (0); } +int +vdev_raw_alloc(vdev_t *vd, uint64_t *allocations, uint_t alloc_count) +{ + int error = 0; + metaslab_t *prev = NULL; + dmu_tx_t *tx = NULL; + + for (uint_t i = 0; i < alloc_count; i += 2) { + uint64_t offset = allocations[i]; + uint64_t length = allocations[i + 1]; + if (offset >> vd->vdev_ms_shift >= vd->vdev_ms_count) { + error = E2BIG; + break; + } + + metaslab_t *cur = vd->vdev_ms[offset >> vd->vdev_ms_shift]; + if (prev != cur) { + if (prev) { + dmu_tx_commit(tx); + mutex_exit(&prev->ms_lock); + metaslab_enable(prev, B_FALSE, B_FALSE); + } + ASSERT(cur); + metaslab_disable(cur); + mutex_enter(&cur->ms_lock); + metaslab_load(cur); + prev = cur; + tx = dmu_tx_create_dd( + spa_get_dsl(vd->vdev_spa)->dp_root_dir); + dmu_tx_assign(tx, DMU_TX_WAIT); + } + + metaslab_force_alloc(cur, offset, length, tx); + } + dmu_tx_commit(tx); + mutex_exit(&prev->ms_lock); + metaslab_enable(prev, B_FALSE, B_FALSE); + + return (error); +} + EXPORT_SYMBOL(vdev_fault); EXPORT_SYMBOL(vdev_degrade); EXPORT_SYMBOL(vdev_online); diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index 3a413f4a7bdb..fed26931bc84 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -7306,6 +7306,78 @@ zfs_ioc_change_key(const char *dsname, nvlist_t *innvl, nvlist_t *outnvl) return (ret); } +static const zfs_ioc_key_t zfs_keys_raw_alloc[] = { + {"metaslab_size", DATA_TYPE_UINT64, 0}, + {"metaslab_count", DATA_TYPE_UINT64, 0}, + {"vdev_id", DATA_TYPE_UINT64, 0}, + {"allocations", DATA_TYPE_UINT64_ARRAY, 0}, + {"force", DATA_TYPE_BOOLEAN_VALUE, 0}, +}; + +static int +zfs_ioc_raw_alloc(const char *pool, nvlist_t *innvl, nvlist_t *outnvl) +{ + (void) outnvl; + int error = 0; + spa_t *spa; + + if ((error = spa_open(pool, &spa, FTAG)) != 0) + return (error); + + uint64_t ms_size, ms_count, vdev_id; + uint_t alloc_count; + uint64_t *allocations; + boolean_t force; + + if ((error = nvlist_lookup_uint64(innvl, "metaslab_size", + &ms_size)) != 0) { + spa_close(spa, FTAG); + return (error); + } + if ((error = nvlist_lookup_uint64(innvl, "metaslab_count", + &ms_count)) != 0) { + spa_close(spa, FTAG); + return (error); + } + if ((error = nvlist_lookup_uint64(innvl, "vdev_id", &vdev_id)) != 0) { + spa_close(spa, FTAG); + return (error); + } + if ((error = nvlist_lookup_boolean_value(innvl, "force", &force)) != + 0) { + spa_close(spa, FTAG); + return (error); + } + spa_config_enter(spa, SCL_VDEV | SCL_ALLOC, FTAG, RW_READER); + vdev_t *vd = vdev_lookup_top(spa, vdev_id); + if (vd == NULL) { + error = ENOENT; + goto out; + } + + if ((1ULL << vd->vdev_ms_shift) != ms_size || + (!force && vd->vdev_ms_count != ms_count)) { + error = EINVAL; + goto out; + } + + if ((error = nvlist_lookup_uint64_array(innvl, "allocations", + &allocations, &alloc_count)) != 0) { + goto out; + } + + if (alloc_count == 0 || alloc_count % 2 != 0) { + error = EINVAL; + goto out; + } + + error = vdev_raw_alloc(vd, allocations, alloc_count); +out: + spa_config_exit(spa, SCL_VDEV | SCL_ALLOC, FTAG); + spa_close(spa, FTAG); + return (error); +} + static zfs_ioc_vec_t zfs_ioc_vec[ZFS_IOC_LAST - ZFS_IOC_FIRST]; static void @@ -7612,6 +7684,11 @@ zfs_ioctl_init(void) POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE, zfs_keys_ddt_prune, ARRAY_SIZE(zfs_keys_ddt_prune)); + zfs_ioctl_register("raw_alloc", ZFS_IOC_RAW_ALLOC, + zfs_ioc_raw_alloc, zfs_secpolicy_config, POOL_NAME, + POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_FALSE, + zfs_keys_raw_alloc, ARRAY_SIZE(zfs_keys_raw_alloc)); + /* IOCTLS that use the legacy function signature */ zfs_ioctl_register_legacy(ZFS_IOC_POOL_FREEZE, zfs_ioc_pool_freeze, diff --git a/rpm/generic/zfs.spec.in b/rpm/generic/zfs.spec.in index dddc0a6c8f02..e01239995f5d 100644 --- a/rpm/generic/zfs.spec.in +++ b/rpm/generic/zfs.spec.in @@ -434,7 +434,7 @@ find %{?buildroot}%{_libdir} -name '*.la' -exec rm -f {} \; %if 0%{!?__brp_mangle_shebangs:1} find %{?buildroot}%{_bindir} \ \( -name arc_summary -or -name arcstat -or -name dbufstat \ - -or -name zilstat \) \ + -or -name zilstat -or -name zleak \) \ -exec %{__sed} -i 's|^#!.*|#!%{__python}|' {} \; find %{?buildroot}%{_datadir} \ \( -name test-runner.py -or -name zts-report.py \) \ @@ -513,6 +513,7 @@ systemctl --system daemon-reload >/dev/null || true %{_bindir}/arcstat %{_bindir}/dbufstat %{_bindir}/zilstat +%{_bindir}/zleak # Man pages %{_mandir}/man1/* %{_mandir}/man4/* diff --git a/scripts/spdxcheck.pl b/scripts/spdxcheck.pl index 88f5a235d70c..bd01a88e71f2 100755 --- a/scripts/spdxcheck.pl +++ b/scripts/spdxcheck.pl @@ -87,6 +87,7 @@ cmd/arc_summary cmd/dbufstat.in cmd/zilstat.in + cmd/zleak cmd/zpool/zpool.d/* etc/init.d/zfs-import.in etc/init.d/zfs-load-key.in