50
50
#include "lib/zstd.h"
51
51
#include "lib/common/zstd_errors.h"
52
52
53
+ static int zstd_earlyabort_pass = 1 ;
54
+ static int zstd_cutoff_level = ZIO_ZSTD_LEVEL_3 ;
55
+ static unsigned int zstd_abort_size = (128 * 1024 );
56
+
53
57
static kstat_t * zstd_ksp = NULL ;
54
58
55
59
typedef struct zstd_stats {
@@ -62,6 +66,21 @@ typedef struct zstd_stats {
62
66
kstat_named_t zstd_stat_dec_header_inval ;
63
67
kstat_named_t zstd_stat_com_fail ;
64
68
kstat_named_t zstd_stat_dec_fail ;
69
+ /*
70
+ * LZ4 first-pass early abort verdict
71
+ */
72
+ kstat_named_t zstd_stat_lz4pass_allowed ;
73
+ kstat_named_t zstd_stat_lz4pass_rejected ;
74
+ /*
75
+ * zstd-1 second-pass early abort verdict
76
+ */
77
+ kstat_named_t zstd_stat_zstdpass_allowed ;
78
+ kstat_named_t zstd_stat_zstdpass_rejected ;
79
+ /*
80
+ * We excluded this from early abort for some reason
81
+ */
82
+ kstat_named_t zstd_stat_passignored ;
83
+ kstat_named_t zstd_stat_passignored_size ;
65
84
kstat_named_t zstd_stat_buffers ;
66
85
kstat_named_t zstd_stat_size ;
67
86
} zstd_stats_t ;
@@ -76,10 +95,44 @@ static zstd_stats_t zstd_stats = {
76
95
{ "decompress_header_invalid" , KSTAT_DATA_UINT64 },
77
96
{ "compress_failed" , KSTAT_DATA_UINT64 },
78
97
{ "decompress_failed" , KSTAT_DATA_UINT64 },
98
+ { "lz4pass_allowed" , KSTAT_DATA_UINT64 },
99
+ { "lz4pass_rejected" , KSTAT_DATA_UINT64 },
100
+ { "zstdpass_allowed" , KSTAT_DATA_UINT64 },
101
+ { "zstdpass_rejected" , KSTAT_DATA_UINT64 },
102
+ { "passignored" , KSTAT_DATA_UINT64 },
103
+ { "passignored_size" , KSTAT_DATA_UINT64 },
79
104
{ "buffers" , KSTAT_DATA_UINT64 },
80
105
{ "size" , KSTAT_DATA_UINT64 },
81
106
};
82
107
108
+ #ifdef _KERNEL
109
+ static int
110
+ kstat_zstd_update (kstat_t * ksp , int rw )
111
+ {
112
+ ASSERT (ksp != NULL );
113
+
114
+ if (rw == KSTAT_WRITE && ksp == zstd_ksp ) {
115
+ ZSTDSTAT_ZERO (zstd_stat_alloc_fail );
116
+ ZSTDSTAT_ZERO (zstd_stat_alloc_fallback );
117
+ ZSTDSTAT_ZERO (zstd_stat_com_alloc_fail );
118
+ ZSTDSTAT_ZERO (zstd_stat_dec_alloc_fail );
119
+ ZSTDSTAT_ZERO (zstd_stat_com_inval );
120
+ ZSTDSTAT_ZERO (zstd_stat_dec_inval );
121
+ ZSTDSTAT_ZERO (zstd_stat_dec_header_inval );
122
+ ZSTDSTAT_ZERO (zstd_stat_com_fail );
123
+ ZSTDSTAT_ZERO (zstd_stat_dec_fail );
124
+ ZSTDSTAT_ZERO (zstd_stat_lz4pass_allowed );
125
+ ZSTDSTAT_ZERO (zstd_stat_lz4pass_rejected );
126
+ ZSTDSTAT_ZERO (zstd_stat_zstdpass_allowed );
127
+ ZSTDSTAT_ZERO (zstd_stat_zstdpass_rejected );
128
+ ZSTDSTAT_ZERO (zstd_stat_passignored );
129
+ ZSTDSTAT_ZERO (zstd_stat_passignored_size );
130
+ }
131
+
132
+ return (0 );
133
+ }
134
+ #endif
135
+
83
136
/* Enums describing the allocator type specified by kmem_type in zstd_kmem */
84
137
enum zstd_kmem_type {
85
138
ZSTD_KMEM_UNKNOWN = 0 ,
@@ -377,6 +430,64 @@ zstd_enum_to_level(enum zio_zstd_levels level, int16_t *zstd_level)
377
430
}
378
431
379
432
433
+ size_t
434
+ zfs_zstd_compress_wrap (void * s_start , void * d_start , size_t s_len , size_t d_len ,
435
+ int level )
436
+ {
437
+ int16_t zstd_level ;
438
+ if (zstd_enum_to_level (level , & zstd_level )) {
439
+ ZSTDSTAT_BUMP (zstd_stat_com_inval );
440
+ return (s_len );
441
+ }
442
+ /*
443
+ * A zstd early abort heuristic.
444
+ *
445
+ * - Zeroth, if this is <= zstd-3, or < zstd_abort_size (currently
446
+ * 128k), don't try any of this, just go.
447
+ * (because experimentally that was a reasonable cutoff for a perf win
448
+ * with tiny ratio change)
449
+ * - First, we try LZ4 compression, and if it doesn't early abort, we
450
+ * jump directly to whatever compression level we intended to try.
451
+ * - Second, we try zstd-1 - if that errors out (usually, but not
452
+ * exclusively, if it would overflow), we give up early.
453
+ *
454
+ * If it works, instead we go on and compress anyway.
455
+ *
456
+ * Why two passes? LZ4 alone gets you a lot of the way, but on highly
457
+ * compressible data, it was losing up to 8.5% of the compressed
458
+ * savings versus no early abort, and all the zstd-fast levels are
459
+ * worse indications on their own than LZ4, and don't improve the LZ4
460
+ * pass noticably if stacked like this.
461
+ */
462
+ size_t actual_abort_size = zstd_abort_size ;
463
+ if (zstd_earlyabort_pass > 0 && zstd_level >= zstd_cutoff_level &&
464
+ s_len >= actual_abort_size ) {
465
+ int pass_len = 1 ;
466
+ pass_len = lz4_compress_zfs (s_start , d_start , s_len , d_len , 0 );
467
+ if (pass_len < d_len ) {
468
+ ZSTDSTAT_BUMP (zstd_stat_lz4pass_allowed );
469
+ goto keep_trying ;
470
+ }
471
+ ZSTDSTAT_BUMP (zstd_stat_lz4pass_rejected );
472
+
473
+ pass_len = zfs_zstd_compress (s_start , d_start , s_len , d_len ,
474
+ ZIO_ZSTD_LEVEL_1 );
475
+ if (pass_len == s_len || pass_len <= 0 || pass_len > d_len ) {
476
+ ZSTDSTAT_BUMP (zstd_stat_zstdpass_rejected );
477
+ return (s_len );
478
+ }
479
+ ZSTDSTAT_BUMP (zstd_stat_zstdpass_allowed );
480
+ } else {
481
+ ZSTDSTAT_BUMP (zstd_stat_passignored );
482
+ if (s_len < actual_abort_size ) {
483
+ ZSTDSTAT_BUMP (zstd_stat_passignored_size );
484
+ }
485
+ }
486
+ keep_trying :
487
+ return (zfs_zstd_compress (s_start , d_start , s_len , d_len , level ));
488
+
489
+ }
490
+
380
491
/* Compress block using zstd */
381
492
size_t
382
493
zfs_zstd_compress (void * s_start , void * d_start , size_t s_len , size_t d_len ,
@@ -437,8 +548,10 @@ zfs_zstd_compress(void *s_start, void *d_start, size_t s_len, size_t d_len,
437
548
* too small, that is not a failure. Everything else is a
438
549
* failure, so increment the compression failure counter.
439
550
*/
440
- if (ZSTD_getErrorCode (c_len ) != ZSTD_error_dstSize_tooSmall ) {
551
+ int err = ZSTD_getErrorCode (c_len );
552
+ if (err != ZSTD_error_dstSize_tooSmall ) {
441
553
ZSTDSTAT_BUMP (zstd_stat_com_fail );
554
+ dprintf ("Error: %s" , ZSTD_getErrorString (err ));
442
555
}
443
556
return (s_len );
444
557
}
@@ -753,6 +866,9 @@ zstd_init(void)
753
866
if (zstd_ksp != NULL ) {
754
867
zstd_ksp -> ks_data = & zstd_stats ;
755
868
kstat_install (zstd_ksp );
869
+ #ifdef _KERNEL
870
+ zstd_ksp -> ks_update = kstat_zstd_update ;
871
+ #endif
756
872
}
757
873
758
874
return (0 );
@@ -781,8 +897,8 @@ module_init(zstd_init);
781
897
module_exit (zstd_fini );
782
898
#endif
783
899
784
- EXPORT_SYMBOL ( zfs_zstd_compress );
785
- EXPORT_SYMBOL ( zfs_zstd_decompress_level );
786
- EXPORT_SYMBOL ( zfs_zstd_decompress );
787
- EXPORT_SYMBOL ( zfs_zstd_cache_reap_now );
900
+ ZFS_MODULE_PARAM ( zfs , zstd_ , earlyabort_pass , INT , ZMOD_RW ,
901
+ "Enable early abort attempts when using zstd" );
902
+ ZFS_MODULE_PARAM ( zfs , zstd_ , abort_size , UINT , ZMOD_RW ,
903
+ "Minimal size of block to attempt early abort" );
788
904
#endif
0 commit comments