Mercurial > illumos > git > illumos-joyent
view usr/src/uts/common/fs/zfs/dmu_zfetch.c @ 25635:ce2b70e7aab0
[illumos-gate merge]
commit 0a554e9f2c0d440dc40a97fae2d18f1d428ca786
13404 man page spelling errors
commit 9f76c6ed5b6ee0cc0bf631daca15ac3dc5fc70c4
13400 zfs-tests: implicit conversion from 'enum dmu_objset_type' to 'enum lzc_dataset_type'
commit ef96fc31fc4f4306719704352d5c3e33573c039f
13399 zfs: error: implicit conversion from 'boolean_t' to 'ds_hold_flags_t'
commit 56870e8c76c2675bcef1fcee5d519585ce9c768e
13393 cheetah: case value '47616' not in enumerated type
commit 8247326397b1a16f37e70cf13f5b7a4f50d06712
13403 zfs: symbol 'g_zfs' is multiply-defined
commit 436b964b19ef06803ad9165542d80d9d731d6486
13402 zpool: symbol 'g_zfs' is multiply-defined
commit 99308ed0417a2b8ab73c5856a8a5345ce2a7aea7
13396 PoolsExecption typo in resource pools javadoc
commit 1575b751c16622553e958c1e5c45e59c86b15c6e
13392 px: case value '3' not in enumerated type
commit 9b0429a10eec9313ec782d8421272aff70adbfdc
13339 Add support for Hygon Dhyana Family 18h processor
commit d20422bd742384b77102bb3bd09e0dc4b7372e50
13351 loader: vbe_find_mode_xydm() is using wrong safety and iteration is buggy
commit 174b8e455f9a6974e69fa4e28792580acde0892d
13311 uptime(1) dazed and confused for a minute after boot
commit f816551bb187d104fbf2757703d7a5d2189a3a18
13401 eeprom: 'lv' may be used uninitialized in this function
commit 5e96da73c99d9d17ff5a58b793fff2ab6dcadf25
13391 fm: build errors with gcc 7 on SPARC
commit 58b55f701e285559e4799354996fd284238ed0d4
13398 libstand: xdrproc_t should return bool
commit c6a28d7650029501a356f7b75b2a10a5c4430cef
13394 fhc: case value '4294967295' not in enumerated type
commit 58d4b16fe601073f2408de78e3db7e9bfa9abfd2
13355 remove topo module warning gags
commit 1473b8d60e902819558a8b0e8a257eb0d754c3c3
13388 ZFS list bookmark not working on zvols
commit 4bba12ca5cd6f92aaf0d4c0d19d05528110bc095
13368 libbe_py should support temporary BE activation
commit a92282e44f968185a6bba094d1e5fece2da819cf
13376 fm: variable may be used uninitialized
commit 8b1df8bf71b7b62e7e4d46fe6b457d4d6447b2b8
13367 beadm activate -t should not promote new BE datasets
commit 9704bf7fb82e71b685e194a967937ff03843e73a
13317 Decrease contention on dn_struct_rwlock
commit 88a08813800ed7ba7c927986421cee437f7f2233
13363 ctfconvert could support more granular ignore for missing debug data
commit 3dd4cd56e7843e01a8ab147a0d102cd4f6d732c1
13342 ctfconvert could encode _Float128 for 32-bit objects
commit 73197b540cc5f0434c409b68ca9e1a514a6ce91b
13336 ctfconvert should be able to unconditionally attempt conversion
commit dd4422524768709a579a2a93a10c78a88a6b0ecb
13280 CTF: provide option to truncate and continue
Conflicts & other fixes (with help from Jason King <jbk@joyent.com>):
usr/src/lib/fm/topo/modules/common/ipmi/ipmi_enum.c
usr/src/lib/libctf/common/ctf_convert.c
usr/src/lib/libctf/common/ctf_lib.c
usr/src/lib/libctf/common/libctf.h
usr/src/lib/libproc/common/Psymtab.c
usr/src/man/man1/ld.so.1.1
usr/src/man/man4/process.4
author | Dan McDonald <danmcd@joyent.com> |
---|---|
date | Mon, 04 Jan 2021 14:49:49 -0500 |
parents | ca76b232c0fe 49e0cb1642f6 |
children |
line wrap: on
line source
/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 2013, 2017 by Delphix. All rights reserved. */ #include <sys/zfs_context.h> #include <sys/dnode.h> #include <sys/dmu_objset.h> #include <sys/dmu_zfetch.h> #include <sys/dmu.h> #include <sys/dbuf.h> #include <sys/kstat.h> /* * This tunable disables predictive prefetch. Note that it leaves "prescient" * prefetch (e.g. prefetch for zfs send) intact. Unlike predictive prefetch, * prescient prefetch never issues i/os that end up not being needed, * so it can't hurt performance. */ boolean_t zfs_prefetch_disable = B_FALSE; /* max # of streams per zfetch */ uint32_t zfetch_max_streams = 8; /* min time before stream reclaim */ uint32_t zfetch_min_sec_reap = 2; /* max bytes to prefetch per stream (default 8MB) */ uint32_t zfetch_max_distance = 8 * 1024 * 1024; /* max bytes to prefetch indirects for per stream (default 64MB) */ uint32_t zfetch_max_idistance = 64 * 1024 * 1024; /* max number of bytes in an array_read in which we allow prefetching (1MB) */ uint64_t zfetch_array_rd_sz = 1024 * 1024; typedef struct zfetch_stats { kstat_named_t zfetchstat_hits; kstat_named_t zfetchstat_misses; kstat_named_t zfetchstat_max_streams; } zfetch_stats_t; static zfetch_stats_t zfetch_stats = { { "hits", KSTAT_DATA_UINT64 }, { "misses", KSTAT_DATA_UINT64 }, { "max_streams", KSTAT_DATA_UINT64 }, }; #define ZFETCHSTAT_BUMP(stat) \ atomic_inc_64(&zfetch_stats.stat.value.ui64); kstat_t *zfetch_ksp; void zfetch_init(void) { zfetch_ksp = kstat_create("zfs", 0, "zfetchstats", "misc", KSTAT_TYPE_NAMED, sizeof (zfetch_stats) / sizeof (kstat_named_t), KSTAT_FLAG_VIRTUAL); if (zfetch_ksp != NULL) { zfetch_ksp->ks_data = &zfetch_stats; kstat_install(zfetch_ksp); } } void zfetch_fini(void) { if (zfetch_ksp != NULL) { kstat_delete(zfetch_ksp); zfetch_ksp = NULL; } } /* * This takes a pointer to a zfetch structure and a dnode. It performs the * necessary setup for the zfetch structure, grokking data from the * associated dnode. */ void dmu_zfetch_init(zfetch_t *zf, dnode_t *dno) { if (zf == NULL) return; zf->zf_dnode = dno; list_create(&zf->zf_stream, sizeof (zstream_t), offsetof(zstream_t, zs_node)); rw_init(&zf->zf_rwlock, NULL, RW_DEFAULT, NULL); } static void dmu_zfetch_stream_remove(zfetch_t *zf, zstream_t *zs) { ASSERT(RW_WRITE_HELD(&zf->zf_rwlock)); list_remove(&zf->zf_stream, zs); mutex_destroy(&zs->zs_lock); kmem_free(zs, sizeof (*zs)); } /* * Clean-up state associated with a zfetch structure (e.g. destroy the * streams). This doesn't free the zfetch_t itself, that's left to the caller. */ void dmu_zfetch_fini(zfetch_t *zf) { zstream_t *zs; ASSERT(!RW_LOCK_HELD(&zf->zf_rwlock)); rw_enter(&zf->zf_rwlock, RW_WRITER); while ((zs = list_head(&zf->zf_stream)) != NULL) dmu_zfetch_stream_remove(zf, zs); rw_exit(&zf->zf_rwlock); list_destroy(&zf->zf_stream); rw_destroy(&zf->zf_rwlock); zf->zf_dnode = NULL; } /* * If there aren't too many streams already, create a new stream. * The "blkid" argument is the next block that we expect this stream to access. * While we're here, clean up old streams (which haven't been * accessed for at least zfetch_min_sec_reap seconds). */ static void dmu_zfetch_stream_create(zfetch_t *zf, uint64_t blkid) { zstream_t *zs_next; int numstreams = 0; ASSERT(RW_WRITE_HELD(&zf->zf_rwlock)); /* * Clean up old streams. */ for (zstream_t *zs = list_head(&zf->zf_stream); zs != NULL; zs = zs_next) { zs_next = list_next(&zf->zf_stream, zs); if (((gethrtime() - zs->zs_atime) / NANOSEC) > zfetch_min_sec_reap) dmu_zfetch_stream_remove(zf, zs); else numstreams++; } /* * The maximum number of streams is normally zfetch_max_streams, * but for small files we lower it such that it's at least possible * for all the streams to be non-overlapping. * * If we are already at the maximum number of streams for this file, * even after removing old streams, then don't create this stream. */ uint32_t max_streams = MAX(1, MIN(zfetch_max_streams, zf->zf_dnode->dn_maxblkid * zf->zf_dnode->dn_datablksz / zfetch_max_distance)); if (numstreams >= max_streams) { ZFETCHSTAT_BUMP(zfetchstat_max_streams); return; } zstream_t *zs = kmem_zalloc(sizeof (*zs), KM_SLEEP); zs->zs_blkid = blkid; zs->zs_pf_blkid = blkid; zs->zs_ipf_blkid = blkid; zs->zs_atime = gethrtime(); mutex_init(&zs->zs_lock, NULL, MUTEX_DEFAULT, NULL); list_insert_head(&zf->zf_stream, zs); } /* * This is the predictive prefetch entry point. It associates dnode access * specified with blkid and nblks arguments with prefetch stream, predicts * further accesses based on that stats and initiates speculative prefetch. * fetch_data argument specifies whether actual data blocks should be fetched: * FALSE -- prefetch only indirect blocks for predicted data blocks; * TRUE -- prefetch predicted data blocks plus following indirect blocks. */ void dmu_zfetch(zfetch_t *zf, uint64_t blkid, uint64_t nblks, boolean_t fetch_data, boolean_t have_lock) { zstream_t *zs; int64_t pf_start, ipf_start, ipf_istart, ipf_iend; int64_t pf_ahead_blks, max_blks; int epbs, max_dist_blks, pf_nblks, ipf_nblks; uint64_t end_of_access_blkid = blkid + nblks; spa_t *spa = zf->zf_dnode->dn_objset->os_spa; if (zfs_prefetch_disable) return; /* * If we haven't yet loaded the indirect vdevs' mappings, we * can only read from blocks that we carefully ensure are on * concrete vdevs (or previously-loaded indirect vdevs). So we * can't allow the predictive prefetcher to attempt reads of other * blocks (e.g. of the MOS's dnode obejct). */ if (!spa_indirect_vdevs_loaded(spa)) return; /* * As a fast path for small (single-block) files, ignore access * to the first block. */ if (blkid == 0) return; if (!have_lock) rw_enter(&zf->zf_dnode->dn_struct_rwlock, RW_READER); rw_enter(&zf->zf_rwlock, RW_READER); /* * Find matching prefetch stream. Depending on whether the accesses * are block-aligned, first block of the new access may either follow * the last block of the previous access, or be equal to it. */ for (zs = list_head(&zf->zf_stream); zs != NULL; zs = list_next(&zf->zf_stream, zs)) { if (blkid == zs->zs_blkid || blkid + 1 == zs->zs_blkid) { mutex_enter(&zs->zs_lock); /* * zs_blkid could have changed before we * acquired zs_lock; re-check them here. */ if (blkid == zs->zs_blkid) { break; } else if (blkid + 1 == zs->zs_blkid) { blkid++; nblks--; if (nblks == 0) { /* Already prefetched this before. */ mutex_exit(&zs->zs_lock); rw_exit(&zf->zf_rwlock); if (!have_lock) { rw_exit(&zf->zf_dnode-> dn_struct_rwlock); } return; } break; } mutex_exit(&zs->zs_lock); } } if (zs == NULL) { /* * This access is not part of any existing stream. Create * a new stream for it. */ ZFETCHSTAT_BUMP(zfetchstat_misses); if (rw_tryupgrade(&zf->zf_rwlock)) dmu_zfetch_stream_create(zf, end_of_access_blkid); rw_exit(&zf->zf_rwlock); if (!have_lock) rw_exit(&zf->zf_dnode->dn_struct_rwlock); return; } /* * This access was to a block that we issued a prefetch for on * behalf of this stream. Issue further prefetches for this stream. * * Normally, we start prefetching where we stopped * prefetching last (zs_pf_blkid). But when we get our first * hit on this stream, zs_pf_blkid == zs_blkid, we don't * want to prefetch the block we just accessed. In this case, * start just after the block we just accessed. */ pf_start = MAX(zs->zs_pf_blkid, end_of_access_blkid); /* * Double our amount of prefetched data, but don't let the * prefetch get further ahead than zfetch_max_distance. */ if (fetch_data) { max_dist_blks = zfetch_max_distance >> zf->zf_dnode->dn_datablkshift; /* * Previously, we were (zs_pf_blkid - blkid) ahead. We * want to now be double that, so read that amount again, * plus the amount we are catching up by (i.e. the amount * read just now). */ pf_ahead_blks = zs->zs_pf_blkid - blkid + nblks; max_blks = max_dist_blks - (pf_start - end_of_access_blkid); pf_nblks = MIN(pf_ahead_blks, max_blks); } else { pf_nblks = 0; } zs->zs_pf_blkid = pf_start + pf_nblks; /* * Do the same for indirects, starting from where we stopped last, * or where we will stop reading data blocks (and the indirects * that point to them). */ ipf_start = MAX(zs->zs_ipf_blkid, zs->zs_pf_blkid); max_dist_blks = zfetch_max_idistance >> zf->zf_dnode->dn_datablkshift; /* * We want to double our distance ahead of the data prefetch * (or reader, if we are not prefetching data). Previously, we * were (zs_ipf_blkid - blkid) ahead. To double that, we read * that amount again, plus the amount we are catching up by * (i.e. the amount read now + the amount of data prefetched now). */ pf_ahead_blks = zs->zs_ipf_blkid - blkid + nblks + pf_nblks; max_blks = max_dist_blks - (ipf_start - end_of_access_blkid); ipf_nblks = MIN(pf_ahead_blks, max_blks); zs->zs_ipf_blkid = ipf_start + ipf_nblks; epbs = zf->zf_dnode->dn_indblkshift - SPA_BLKPTRSHIFT; ipf_istart = P2ROUNDUP(ipf_start, 1 << epbs) >> epbs; ipf_iend = P2ROUNDUP(zs->zs_ipf_blkid, 1 << epbs) >> epbs; zs->zs_atime = gethrtime(); zs->zs_blkid = end_of_access_blkid; mutex_exit(&zs->zs_lock); rw_exit(&zf->zf_rwlock); /* * dbuf_prefetch() is asynchronous (even when it needs to read * indirect blocks), but we still prefer to drop our locks before * calling it to reduce the time we hold them. */ for (int i = 0; i < pf_nblks; i++) { dbuf_prefetch(zf->zf_dnode, 0, pf_start + i, ZIO_PRIORITY_ASYNC_READ, ARC_FLAG_PREDICTIVE_PREFETCH); } for (int64_t iblk = ipf_istart; iblk < ipf_iend; iblk++) { dbuf_prefetch(zf->zf_dnode, 1, iblk, ZIO_PRIORITY_ASYNC_READ, ARC_FLAG_PREDICTIVE_PREFETCH); } if (!have_lock) rw_exit(&zf->zf_dnode->dn_struct_rwlock); ZFETCHSTAT_BUMP(zfetchstat_hits); }