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