1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3 * CDDL HEADER START
4 *
5 * The contents of this file are subject to the terms of the
6 * Common Development and Distribution License (the "License").
7 * You may not use this file except in compliance with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or https://opensource.org/licenses/CDDL-1.0.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22
23 /*
24 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
25 * Copyright (c) 2012, 2016 by Delphix. All rights reserved.
26 * Copyright (c) 2022 by Pawel Jakub Dawidek
27 * Copyright (c) 2023, Klara Inc.
28 */
29
30 #include <sys/zfs_context.h>
31 #include <sys/spa.h>
32 #include <sys/spa_impl.h>
33 #include <sys/ddt.h>
34 #include <sys/ddt_impl.h>
35
36 static void
ddt_stat_generate(ddt_t * ddt,const ddt_lightweight_entry_t * ddlwe,ddt_stat_t * dds)37 ddt_stat_generate(ddt_t *ddt, const ddt_lightweight_entry_t *ddlwe,
38 ddt_stat_t *dds)
39 {
40 spa_t *spa = ddt->ddt_spa;
41 uint64_t lsize = DDK_GET_LSIZE(&ddlwe->ddlwe_key);
42 uint64_t psize = DDK_GET_PSIZE(&ddlwe->ddlwe_key);
43
44 memset(dds, 0, sizeof (*dds));
45
46 for (int p = 0; p < DDT_NPHYS(ddt); p++) {
47 const ddt_univ_phys_t *ddp = &ddlwe->ddlwe_phys;
48 ddt_phys_variant_t v = DDT_PHYS_VARIANT(ddt, p);
49
50 if (ddt_phys_birth(ddp, v) == 0)
51 continue;
52
53 int ndvas = ddt_phys_dva_count(ddp, v,
54 DDK_GET_CRYPT(&ddlwe->ddlwe_key));
55 const dva_t *dvas = (ddt->ddt_flags & DDT_FLAG_FLAT) ?
56 ddp->ddp_flat.ddp_dva : ddp->ddp_trad[p].ddp_dva;
57
58 uint64_t dsize = 0;
59 for (int d = 0; d < ndvas; d++)
60 dsize += dva_get_dsize_sync(spa, &dvas[d]);
61
62 uint64_t refcnt = ddt_phys_refcnt(ddp, v);
63
64 dds->dds_blocks += 1;
65 dds->dds_lsize += lsize;
66 dds->dds_psize += psize;
67 dds->dds_dsize += dsize;
68
69 dds->dds_ref_blocks += refcnt;
70 dds->dds_ref_lsize += lsize * refcnt;
71 dds->dds_ref_psize += psize * refcnt;
72 dds->dds_ref_dsize += dsize * refcnt;
73 }
74 }
75
76 static void
ddt_stat_add(ddt_stat_t * dst,const ddt_stat_t * src)77 ddt_stat_add(ddt_stat_t *dst, const ddt_stat_t *src)
78 {
79 dst->dds_blocks += src->dds_blocks;
80 dst->dds_lsize += src->dds_lsize;
81 dst->dds_psize += src->dds_psize;
82 dst->dds_dsize += src->dds_dsize;
83 dst->dds_ref_blocks += src->dds_ref_blocks;
84 dst->dds_ref_lsize += src->dds_ref_lsize;
85 dst->dds_ref_psize += src->dds_ref_psize;
86 dst->dds_ref_dsize += src->dds_ref_dsize;
87 }
88
89 static void
ddt_stat_sub(ddt_stat_t * dst,const ddt_stat_t * src)90 ddt_stat_sub(ddt_stat_t *dst, const ddt_stat_t *src)
91 {
92 /* This caught more during development than you might expect... */
93 ASSERT3U(dst->dds_blocks, >=, src->dds_blocks);
94 ASSERT3U(dst->dds_lsize, >=, src->dds_lsize);
95 ASSERT3U(dst->dds_psize, >=, src->dds_psize);
96 ASSERT3U(dst->dds_dsize, >=, src->dds_dsize);
97 ASSERT3U(dst->dds_ref_blocks, >=, src->dds_ref_blocks);
98 ASSERT3U(dst->dds_ref_lsize, >=, src->dds_ref_lsize);
99 ASSERT3U(dst->dds_ref_psize, >=, src->dds_ref_psize);
100 ASSERT3U(dst->dds_ref_dsize, >=, src->dds_ref_dsize);
101
102 dst->dds_blocks -= src->dds_blocks;
103 dst->dds_lsize -= src->dds_lsize;
104 dst->dds_psize -= src->dds_psize;
105 dst->dds_dsize -= src->dds_dsize;
106 dst->dds_ref_blocks -= src->dds_ref_blocks;
107 dst->dds_ref_lsize -= src->dds_ref_lsize;
108 dst->dds_ref_psize -= src->dds_ref_psize;
109 dst->dds_ref_dsize -= src->dds_ref_dsize;
110 }
111
112 void
ddt_histogram_add_entry(ddt_t * ddt,ddt_histogram_t * ddh,const ddt_lightweight_entry_t * ddlwe)113 ddt_histogram_add_entry(ddt_t *ddt, ddt_histogram_t *ddh,
114 const ddt_lightweight_entry_t *ddlwe)
115 {
116 ddt_stat_t dds;
117 int bucket;
118
119 ddt_stat_generate(ddt, ddlwe, &dds);
120
121 bucket = highbit64(dds.dds_ref_blocks) - 1;
122 if (bucket < 0)
123 return;
124
125 ddt_stat_add(&ddh->ddh_stat[bucket], &dds);
126 }
127
128 void
ddt_histogram_sub_entry(ddt_t * ddt,ddt_histogram_t * ddh,const ddt_lightweight_entry_t * ddlwe)129 ddt_histogram_sub_entry(ddt_t *ddt, ddt_histogram_t *ddh,
130 const ddt_lightweight_entry_t *ddlwe)
131 {
132 ddt_stat_t dds;
133 int bucket;
134
135 ddt_stat_generate(ddt, ddlwe, &dds);
136
137 bucket = highbit64(dds.dds_ref_blocks) - 1;
138 if (bucket < 0)
139 return;
140
141 ddt_stat_sub(&ddh->ddh_stat[bucket], &dds);
142 }
143
144 void
ddt_histogram_add(ddt_histogram_t * dst,const ddt_histogram_t * src)145 ddt_histogram_add(ddt_histogram_t *dst, const ddt_histogram_t *src)
146 {
147 for (int h = 0; h < 64; h++)
148 ddt_stat_add(&dst->ddh_stat[h], &src->ddh_stat[h]);
149 }
150
151 void
ddt_histogram_total(ddt_stat_t * dds,const ddt_histogram_t * ddh)152 ddt_histogram_total(ddt_stat_t *dds, const ddt_histogram_t *ddh)
153 {
154 memset(dds, 0, sizeof (*dds));
155
156 for (int h = 0; h < 64; h++)
157 ddt_stat_add(dds, &ddh->ddh_stat[h]);
158 }
159
160 boolean_t
ddt_histogram_empty(const ddt_histogram_t * ddh)161 ddt_histogram_empty(const ddt_histogram_t *ddh)
162 {
163 for (int h = 0; h < 64; h++) {
164 const ddt_stat_t *dds = &ddh->ddh_stat[h];
165
166 if (dds->dds_blocks == 0 &&
167 dds->dds_lsize == 0 &&
168 dds->dds_psize == 0 &&
169 dds->dds_dsize == 0 &&
170 dds->dds_ref_blocks == 0 &&
171 dds->dds_ref_lsize == 0 &&
172 dds->dds_ref_psize == 0 &&
173 dds->dds_ref_dsize == 0)
174 continue;
175
176 return (B_FALSE);
177 }
178
179 return (B_TRUE);
180 }
181
182 void
ddt_get_dedup_object_stats(spa_t * spa,ddt_object_t * ddo_total)183 ddt_get_dedup_object_stats(spa_t *spa, ddt_object_t *ddo_total)
184 {
185 memset(ddo_total, 0, sizeof (*ddo_total));
186
187 for (enum zio_checksum c = 0; c < ZIO_CHECKSUM_FUNCTIONS; c++) {
188 ddt_t *ddt = spa->spa_ddt[c];
189 if (!ddt)
190 continue;
191
192 for (ddt_type_t type = 0; type < DDT_TYPES; type++) {
193 for (ddt_class_t class = 0; class < DDT_CLASSES;
194 class++) {
195 dmu_object_info_t doi;
196 uint64_t cnt;
197 int err;
198
199 /*
200 * These stats were originally calculated
201 * during ddt_object_load().
202 */
203
204 err = ddt_object_info(ddt, type, class, &doi);
205 if (err != 0)
206 continue;
207
208 err = ddt_object_count(ddt, type, class, &cnt);
209 if (err != 0)
210 continue;
211
212 ddt_object_t *ddo =
213 &ddt->ddt_object_stats[type][class];
214
215 ddo->ddo_count = cnt;
216 ddo->ddo_dspace =
217 doi.doi_physical_blocks_512 << 9;
218 ddo->ddo_mspace = doi.doi_fill_count *
219 doi.doi_data_block_size;
220
221 ddo_total->ddo_count += ddo->ddo_count;
222 ddo_total->ddo_dspace += ddo->ddo_dspace;
223 ddo_total->ddo_mspace += ddo->ddo_mspace;
224 }
225 }
226
227 ddt_object_t *ddo = &ddt->ddt_log_stats;
228 ddo_total->ddo_count += ddo->ddo_count;
229 ddo_total->ddo_dspace += ddo->ddo_dspace;
230 ddo_total->ddo_mspace += ddo->ddo_mspace;
231 }
232
233 /*
234 * This returns raw counts (not averages). One of the consumers,
235 * print_dedup_stats(), historically has expected raw counts.
236 */
237
238 spa->spa_dedup_dsize = ddo_total->ddo_dspace;
239 }
240
241 uint64_t
ddt_get_ddt_dsize(spa_t * spa)242 ddt_get_ddt_dsize(spa_t *spa)
243 {
244 ddt_object_t ddo_total;
245
246 /* recalculate after each txg sync */
247 if (spa->spa_dedup_dsize == ~0ULL)
248 ddt_get_dedup_object_stats(spa, &ddo_total);
249
250 return (spa->spa_dedup_dsize);
251 }
252
253 void
ddt_get_dedup_histogram(spa_t * spa,ddt_histogram_t * ddh)254 ddt_get_dedup_histogram(spa_t *spa, ddt_histogram_t *ddh)
255 {
256 for (enum zio_checksum c = 0; c < ZIO_CHECKSUM_FUNCTIONS; c++) {
257 ddt_t *ddt = spa->spa_ddt[c];
258 if (!ddt)
259 continue;
260
261 for (ddt_type_t type = 0; type < DDT_TYPES; type++) {
262 for (ddt_class_t class = 0; class < DDT_CLASSES;
263 class++) {
264 ddt_histogram_add(ddh,
265 &ddt->ddt_histogram_cache[type][class]);
266 }
267 }
268
269 ddt_histogram_add(ddh, &ddt->ddt_log_histogram);
270 }
271 }
272
273 void
ddt_get_dedup_stats(spa_t * spa,ddt_stat_t * dds_total)274 ddt_get_dedup_stats(spa_t *spa, ddt_stat_t *dds_total)
275 {
276 ddt_histogram_t *ddh_total;
277
278 ddh_total = kmem_zalloc(sizeof (ddt_histogram_t), KM_SLEEP);
279 ddt_get_dedup_histogram(spa, ddh_total);
280 ddt_histogram_total(dds_total, ddh_total);
281 kmem_free(ddh_total, sizeof (ddt_histogram_t));
282 }
283
284 uint64_t
ddt_get_dedup_dspace(spa_t * spa)285 ddt_get_dedup_dspace(spa_t *spa)
286 {
287 ddt_stat_t dds_total;
288
289 if (spa->spa_dedup_dspace != ~0ULL)
290 return (spa->spa_dedup_dspace);
291
292 memset(&dds_total, 0, sizeof (ddt_stat_t));
293
294 /* Calculate and cache the stats */
295 ddt_get_dedup_stats(spa, &dds_total);
296 spa->spa_dedup_dspace = dds_total.dds_ref_dsize - dds_total.dds_dsize;
297 return (spa->spa_dedup_dspace);
298 }
299
300 uint64_t
ddt_get_pool_dedup_ratio(spa_t * spa)301 ddt_get_pool_dedup_ratio(spa_t *spa)
302 {
303 ddt_stat_t dds_total = { 0 };
304
305 ddt_get_dedup_stats(spa, &dds_total);
306 if (dds_total.dds_dsize == 0)
307 return (100);
308
309 return (dds_total.dds_ref_dsize * 100 / dds_total.dds_dsize);
310 }
311
312 int
ddt_get_pool_dedup_cached(spa_t * spa,uint64_t * psize)313 ddt_get_pool_dedup_cached(spa_t *spa, uint64_t *psize)
314 {
315 uint64_t l1sz, l1tot, l2sz, l2tot;
316 int err = 0;
317
318 l1tot = l2tot = 0;
319 *psize = 0;
320 for (enum zio_checksum c = 0; c < ZIO_CHECKSUM_FUNCTIONS; c++) {
321 ddt_t *ddt = spa->spa_ddt[c];
322 if (ddt == NULL)
323 continue;
324 for (ddt_type_t type = 0; type < DDT_TYPES; type++) {
325 for (ddt_class_t class = 0; class < DDT_CLASSES;
326 class++) {
327 err = dmu_object_cached_size(ddt->ddt_os,
328 ddt->ddt_object[type][class], &l1sz, &l2sz);
329 if (err != 0)
330 return (err);
331 l1tot += l1sz;
332 l2tot += l2sz;
333 }
334 }
335 }
336
337 *psize = l1tot + l2tot;
338 return (err);
339 }
340