Skip to content

Commit 220d2d2

Browse files
committed
Implement uncached prefetch.
Before this change primarycache property was handled only on dbuf layer, controlling dbuf cache and through a hack an ARC evictions. Since speculative prefetcher is implemented on ARC level, it had to be disabled for uncacheable buffers because otherwise falsely prefetched and never read buffers would stay cached in ARC. This change gives ARC a knowledge about uncacheable buffers. It is passed to arc_read() and arc_write() and stored in ARC header. When remove_reference() drops last reference on the ARC header, it can either immediately destroy it, or if it is marked as prefetch, put it into new arc_uncached state. That state is scanned every second, looking for stale buffers that were not demand read (in which case they are evicted immediately). To handle cases of short or misaligned reads, this change tracks at dbuf layer buffers that were read from the beginning, but not to the end. It is assumed that such buffers may receive further reads, and so they are stored in dbuf cache. If some of following reads reaches the end of such buffer, it is immediately evicted. Otherwise it will follow regular dbuf cache eviction and will be evicted also from ARC the same moment. Since dbuf layer does not know the actual file size, this logic is not applied to the last buffers of dnodes, which are always evicted same as before. Since uncacheable buffers should no longer stay in ARC for long, this patch also tries to optimize I/O by allocating ARC physical buffers as linear to allow buffer sharing. It allows to avoid one of two memory copies for uncompressed data for both reads and writes by the cost of some higher KVA usage in case of prefetch. With the combination of enabled prefetch and avoided memory copy this change improves sequential single-threaded read speed from a wide NVMe pool from 2049 to 3932 MiB/s. During write profiler shows 22% reduction of unhalted CPU cycles at the same throughput of 3653 MiB/s. Signed-off-by: Alexander Motin <[email protected]> Sponsored by: iXsystems, Inc.
1 parent ca50e3f commit 220d2d2

File tree

10 files changed

+226
-139
lines changed

10 files changed

+226
-139
lines changed

include/os/linux/zfs/sys/trace_arc.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ DEFINE_ARC_BUF_HDR_EVENT(zfs_arc__evict);
108108
DEFINE_ARC_BUF_HDR_EVENT(zfs_arc__delete);
109109
DEFINE_ARC_BUF_HDR_EVENT(zfs_new_state__mru);
110110
DEFINE_ARC_BUF_HDR_EVENT(zfs_new_state__mfu);
111+
DEFINE_ARC_BUF_HDR_EVENT(zfs_new_state__uncached);
111112
DEFINE_ARC_BUF_HDR_EVENT(zfs_arc__async__upgrade__sync);
112113
DEFINE_ARC_BUF_HDR_EVENT(zfs_l2arc__hit);
113114
DEFINE_ARC_BUF_HDR_EVENT(zfs_l2arc__miss);
@@ -392,6 +393,7 @@ DEFINE_DTRACE_PROBE1(arc__evict);
392393
DEFINE_DTRACE_PROBE1(arc__delete);
393394
DEFINE_DTRACE_PROBE1(new_state__mru);
394395
DEFINE_DTRACE_PROBE1(new_state__mfu);
396+
DEFINE_DTRACE_PROBE1(new_state__uncached);
395397
DEFINE_DTRACE_PROBE1(arc__async__upgrade__sync);
396398
DEFINE_DTRACE_PROBE1(l2arc__hit);
397399
DEFINE_DTRACE_PROBE1(l2arc__miss);

include/sys/arc.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ typedef enum arc_flags
115115
ARC_FLAG_PREFETCH = 1 << 2, /* I/O is a prefetch */
116116
ARC_FLAG_CACHED = 1 << 3, /* I/O was in cache */
117117
ARC_FLAG_L2CACHE = 1 << 4, /* cache in L2ARC */
118+
ARC_FLAG_UNCACHED = 1 << 5, /* evict after use */
118119
ARC_FLAG_PRESCIENT_PREFETCH = 1 << 6, /* long min lifespan */
119120

120121
/*
@@ -228,6 +229,7 @@ typedef enum arc_state_type {
228229
ARC_STATE_MFU,
229230
ARC_STATE_MFU_GHOST,
230231
ARC_STATE_L2C_ONLY,
232+
ARC_STATE_UNCACHED,
231233
ARC_STATE_NUMTYPES
232234
} arc_state_type_t;
233235

@@ -301,8 +303,8 @@ int arc_referenced(arc_buf_t *buf);
301303
int arc_read(zio_t *pio, spa_t *spa, const blkptr_t *bp,
302304
arc_read_done_func_t *done, void *priv, zio_priority_t priority,
303305
int flags, arc_flags_t *arc_flags, const zbookmark_phys_t *zb);
304-
zio_t *arc_write(zio_t *pio, spa_t *spa, uint64_t txg,
305-
blkptr_t *bp, arc_buf_t *buf, boolean_t l2arc, const zio_prop_t *zp,
306+
zio_t *arc_write(zio_t *pio, spa_t *spa, uint64_t txg, blkptr_t *bp,
307+
arc_buf_t *buf, boolean_t uncached, boolean_t l2arc, const zio_prop_t *zp,
306308
arc_write_done_func_t *ready, arc_write_done_func_t *child_ready,
307309
arc_write_done_func_t *physdone, arc_write_done_func_t *done,
308310
void *priv, zio_priority_t priority, int zio_flags,

include/sys/arc_impl.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ extern "C" {
4646
* ARC_mru_ghost - recently used, no longer in cache
4747
* ARC_mfu - frequently used, currently cached
4848
* ARC_mfu_ghost - frequently used, no longer in cache
49+
* ARC_uncached - uncacheable prefetch, to be evicted
4950
* ARC_l2c_only - exists in L2ARC but not other states
5051
* When there are no active references to the buffer, they are
5152
* are linked onto a list in one of these arc states. These are
@@ -534,6 +535,7 @@ typedef struct arc_stats {
534535
kstat_named_t arcstat_mru_ghost_hits;
535536
kstat_named_t arcstat_mfu_hits;
536537
kstat_named_t arcstat_mfu_ghost_hits;
538+
kstat_named_t arcstat_uncached_hits;
537539
kstat_named_t arcstat_deleted;
538540
/*
539541
* Number of buffers that could not be evicted because the hash lock
@@ -736,6 +738,9 @@ typedef struct arc_stats {
736738
* ARC_BUFC_METADATA, and linked off the arc_mru_ghost state.
737739
*/
738740
kstat_named_t arcstat_mfu_ghost_evictable_metadata;
741+
kstat_named_t arcstat_uncached_size;
742+
kstat_named_t arcstat_uncached_evictable_data;
743+
kstat_named_t arcstat_uncached_evictable_metadata;
739744
kstat_named_t arcstat_l2_hits;
740745
kstat_named_t arcstat_l2_misses;
741746
/*
@@ -886,6 +891,7 @@ typedef struct arc_sums {
886891
wmsum_t arcstat_mru_ghost_hits;
887892
wmsum_t arcstat_mfu_hits;
888893
wmsum_t arcstat_mfu_ghost_hits;
894+
wmsum_t arcstat_uncached_hits;
889895
wmsum_t arcstat_deleted;
890896
wmsum_t arcstat_mutex_miss;
891897
wmsum_t arcstat_access_skip;
@@ -992,6 +998,7 @@ typedef struct arc_evict_waiter {
992998
#define arc_mfu (&ARC_mfu)
993999
#define arc_mfu_ghost (&ARC_mfu_ghost)
9941000
#define arc_l2c_only (&ARC_l2c_only)
1001+
#define arc_uncached (&ARC_uncached)
9951002

9961003
extern taskq_t *arc_prune_taskq;
9971004
extern arc_stats_t arc_stats;

include/sys/dbuf.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ extern "C" {
5555
#define DB_RF_NEVERWAIT (1 << 4)
5656
#define DB_RF_CACHED (1 << 5)
5757
#define DB_RF_NO_DECRYPT (1 << 6)
58+
#define DB_RF_PARTIAL_FIRST (1 << 7)
59+
#define DB_RF_PARTIAL_MORE (1 << 8)
5860

5961
/*
6062
* The simplified state transition diagram for dbufs looks like:
@@ -319,6 +321,9 @@ typedef struct dmu_buf_impl {
319321
uint8_t db_pending_evict;
320322

321323
uint8_t db_dirtycnt;
324+
325+
/* The buffer was partially read. More reads may follow. */
326+
boolean_t db_partial_read;
322327
} dmu_buf_impl_t;
323328

324329
#define DBUF_HASH_MUTEX(h, idx) \

include/sys/dnode.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -457,15 +457,11 @@ void dnode_free_interior_slots(dnode_t *dn);
457457
#define DNODE_IS_DIRTY(_dn) \
458458
((_dn)->dn_dirty_txg >= spa_syncing_txg((_dn)->dn_objset->os_spa))
459459

460-
#define DNODE_IS_CACHEABLE(_dn) \
460+
#define DNODE_LEVEL_IS_CACHEABLE(_dn, _level) \
461461
((_dn)->dn_objset->os_primary_cache == ZFS_CACHE_ALL || \
462-
(DMU_OT_IS_METADATA((_dn)->dn_type) && \
462+
(((_level) > 0 || DMU_OT_IS_METADATA((_dn)->dn_type)) && \
463463
(_dn)->dn_objset->os_primary_cache == ZFS_CACHE_METADATA))
464464

465-
#define DNODE_META_IS_CACHEABLE(_dn) \
466-
((_dn)->dn_objset->os_primary_cache == ZFS_CACHE_ALL || \
467-
(_dn)->dn_objset->os_primary_cache == ZFS_CACHE_METADATA)
468-
469465
/*
470466
* Used for dnodestats kstat.
471467
*/

module/os/freebsd/zfs/sysctl_os.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -366,10 +366,10 @@ SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, anon_size, CTLFLAG_RD,
366366
&ARC_anon.arcs_size.rc_count, 0, "size of anonymous state");
367367
SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, anon_metadata_esize, CTLFLAG_RD,
368368
&ARC_anon.arcs_esize[ARC_BUFC_METADATA].rc_count, 0,
369-
"size of anonymous state");
369+
"size of metadata in anonymous state");
370370
SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, anon_data_esize, CTLFLAG_RD,
371371
&ARC_anon.arcs_esize[ARC_BUFC_DATA].rc_count, 0,
372-
"size of anonymous state");
372+
"size of data in anonymous state");
373373
/* END CSTYLED */
374374

375375
extern arc_state_t ARC_mru;
@@ -424,6 +424,19 @@ SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, mfu_ghost_data_esize, CTLFLAG_RD,
424424
"size of data in mfu ghost state");
425425
/* END CSTYLED */
426426

427+
extern arc_state_t ARC_uncached;
428+
429+
/* BEGIN CSTYLED */
430+
SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, uncached_size, CTLFLAG_RD,
431+
&ARC_uncached.arcs_size.rc_count, 0, "size of uncached state");
432+
SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, uncached_metadata_esize, CTLFLAG_RD,
433+
&ARC_uncached.arcs_esize[ARC_BUFC_METADATA].rc_count, 0,
434+
"size of metadata in uncached state");
435+
SYSCTL_UQUAD(_vfs_zfs, OID_AUTO, uncached_data_esize, CTLFLAG_RD,
436+
&ARC_uncached.arcs_esize[ARC_BUFC_DATA].rc_count, 0,
437+
"size of data in uncached state");
438+
/* END CSTYLED */
439+
427440
extern arc_state_t ARC_l2c_only;
428441

429442
/* BEGIN CSTYLED */

0 commit comments

Comments
 (0)