Print this page
4185 New hash algorithm support

*** 20,33 **** --- 20,35 ---- */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013 by Delphix. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright 2013 Saso Kiselkov. All rights reserved. */ #include <sys/zfs_context.h> #include <sys/spa.h> + #include <sys/spa_impl.h> #include <sys/zio.h> #include <sys/zio_checksum.h> #include <sys/zil.h> #include <zfs_fletcher.h>
*** 57,87 **** * * When writing a block, we always checksum it with the latest-and-greatest * checksum function of the appropriate strength. When reading a block, * we compare the expected checksum against the actual checksum, which we * compute via the checksum function specified by BP_GET_CHECKSUM(bp). */ /*ARGSUSED*/ static void ! zio_checksum_off(const void *buf, uint64_t size, zio_cksum_t *zcp) { ZIO_SET_CHECKSUM(zcp, 0, 0, 0, 0); } zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS] = { ! {{NULL, NULL}, 0, 0, 0, "inherit"}, ! {{NULL, NULL}, 0, 0, 0, "on"}, ! {{zio_checksum_off, zio_checksum_off}, 0, 0, 0, "off"}, ! {{zio_checksum_SHA256, zio_checksum_SHA256}, 1, 1, 0, "label"}, ! {{zio_checksum_SHA256, zio_checksum_SHA256}, 1, 1, 0, "gang_header"}, ! {{fletcher_2_native, fletcher_2_byteswap}, 0, 1, 0, "zilog"}, ! {{fletcher_2_native, fletcher_2_byteswap}, 0, 0, 0, "fletcher2"}, ! {{fletcher_4_native, fletcher_4_byteswap}, 1, 0, 0, "fletcher4"}, ! {{zio_checksum_SHA256, zio_checksum_SHA256}, 1, 0, 1, "sha256"}, ! {{fletcher_4_native, fletcher_4_byteswap}, 0, 1, 0, "zilog2"}, ! {{zio_checksum_off, zio_checksum_off}, 0, 0, 0, "noparity"}, }; enum zio_checksum zio_checksum_select(enum zio_checksum child, enum zio_checksum parent) { --- 59,134 ---- * * When writing a block, we always checksum it with the latest-and-greatest * checksum function of the appropriate strength. When reading a block, * we compare the expected checksum against the actual checksum, which we * compute via the checksum function specified by BP_GET_CHECKSUM(bp). + * + * SALTED CHECKSUMS + * + * To enable the use of non-cryptographically secure hash algorithms in + * dedup we introduce the notion of salted checksums (MACs, really). A salted + * checksum is fed both a random 256-bit value (the salt) and the data to be + * checksummed. This salt is kept secret (stored on the pool, but never shown + * to the user), thus even if an attacker knew of collision weaknesses in the + * hash algorithm, they won't be able to mount a known plaintext attack on + * the DDT, since the actual hash value cannot be known ahead of time. How + * the salt is used is algorithm-specific (some might simply prefix it to the + * data block, others might need to utilize a full-blown HMAC). On disk the + * salt is stored in a ZAP object in the MOS (DMU_POOL_CHECKSUM_SALT). + * + * CONTEXT TEMPLATES + * + * Some hashing algorithms need to perform a substantial amount of + * initialization work (e.g. salted checksums above may need to pre-hash the + * salt) before being able to process data. Performing this redundant work + * for each block would be very wasteful, so we instead allow a checksum + * algorithm to do the work once (the first time it's used) and then keep + * this pre-initialized context as a template inside the spa_t + * (spa_cksum_tmpls). If the zio_checksum_info_t contains non-NULL + * ci_tmpl_init and ci_tmpl_free callbacks, they are used to construct and + * destruct the pre-initialized checksum context. The pre-initialized + * context is then reused during each checksum invocation and passed to the + * checksum function. */ /*ARGSUSED*/ static void ! zio_checksum_off(const void *buf, uint64_t size, const zio_cksum_salt_t *salt, ! const void *ctx_template, zio_cksum_t *zcp) { ZIO_SET_CHECKSUM(zcp, 0, 0, 0, 0); } zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS] = { ! {{NULL, NULL}, NULL, NULL, 0, 0, 0, 0, "inherit"}, ! {{NULL, NULL}, NULL, NULL, 0, 0, 0, 0, "on"}, ! {{zio_checksum_off, zio_checksum_off}, ! NULL, NULL, 0, 0, 0, 0, "off"}, ! {{zio_checksum_SHA256, zio_checksum_SHA256}, ! NULL, NULL, 1, 1, 0, 0, "label"}, ! {{zio_checksum_SHA256, zio_checksum_SHA256}, ! NULL, NULL, 1, 1, 0, 0, "gang_header"}, ! {{fletcher_2_native, fletcher_2_byteswap}, ! NULL, NULL, 0, 1, 0, 0, "zilog"}, ! {{fletcher_2_native, fletcher_2_byteswap}, ! NULL, NULL, 0, 0, 0, 0, "fletcher2"}, ! {{fletcher_4_native, fletcher_4_byteswap}, ! NULL, NULL, 1, 0, 0, 0, "fletcher4"}, ! {{zio_checksum_SHA256, zio_checksum_SHA256}, ! NULL, NULL, 1, 0, 1, 0, "sha256"}, ! {{fletcher_4_native, fletcher_4_byteswap}, ! NULL, NULL, 0, 1, 0, 0, "zilog2"}, ! {{zio_checksum_off, zio_checksum_off}, ! NULL, NULL, 0, 0, 0, 0, "noparity"}, ! {{zio_checksum_SHA512_native, zio_checksum_SHA512_byteswap}, ! NULL, NULL, 1, 0, 1, 0, "sha512"}, ! {{zio_checksum_skein_native, zio_checksum_skein_byteswap}, ! zio_checksum_skein_tmpl_init, zio_checksum_skein_tmpl_free, ! 1, 0, 1, 1, "skein"}, ! {{zio_checksum_edonr_native, zio_checksum_edonr_byteswap}, ! zio_checksum_edonr_tmpl_init, zio_checksum_edonr_tmpl_free, ! 1, 0, 1, 1, "edonr"} }; enum zio_checksum zio_checksum_select(enum zio_checksum child, enum zio_checksum parent) {
*** 146,155 **** --- 193,221 ---- { ZIO_SET_CHECKSUM(zcp, offset, 0, 0, 0); } /* + * Calls the template init function of a checksum which supports context + * templates and installs the template into the spa_t. + */ + static void + zio_checksum_template_init(enum zio_checksum checksum, spa_t *spa) + { + zio_checksum_info_t *ci = &zio_checksum_table[checksum]; + + VERIFY(ci->ci_tmpl_init != NULL && ci->ci_tmpl_free != NULL); + mutex_enter(&spa->spa_cksum_tmpls_lock); + if (spa->spa_cksum_tmpls[checksum] == NULL) { + spa->spa_cksum_tmpls[checksum] = + ci->ci_tmpl_init(&spa->spa_cksum_salt); + VERIFY(spa->spa_cksum_tmpls[checksum] != NULL); + } + mutex_exit(&spa->spa_cksum_tmpls_lock); + } + + /* * Generate the checksum. */ void zio_checksum_compute(zio_t *zio, enum zio_checksum checksum, void *data, uint64_t size)
*** 156,169 **** --- 222,239 ---- { blkptr_t *bp = zio->io_bp; uint64_t offset = zio->io_offset; zio_checksum_info_t *ci = &zio_checksum_table[checksum]; zio_cksum_t cksum; + spa_t *spa = zio->io_spa; ASSERT((uint_t)checksum < ZIO_CHECKSUM_FUNCTIONS); ASSERT(ci->ci_func[0] != NULL); + if (ci->ci_tmpl_init != NULL && spa->spa_cksum_tmpls[checksum] == NULL) + zio_checksum_template_init(checksum, spa); + if (ci->ci_eck) { zio_eck_t *eck; if (checksum == ZIO_CHECKSUM_ZILOG2) { zil_chain_t *zilc = data;
*** 179,192 **** else if (checksum == ZIO_CHECKSUM_LABEL) zio_checksum_label_verifier(&eck->zec_cksum, offset); else bp->blk_cksum = eck->zec_cksum; eck->zec_magic = ZEC_MAGIC; ! ci->ci_func[0](data, size, &cksum); eck->zec_cksum = cksum; } else { ! ci->ci_func[0](data, size, &bp->blk_cksum); } } int zio_checksum_error(zio_t *zio, zio_bad_cksum_t *info) --- 249,264 ---- else if (checksum == ZIO_CHECKSUM_LABEL) zio_checksum_label_verifier(&eck->zec_cksum, offset); else bp->blk_cksum = eck->zec_cksum; eck->zec_magic = ZEC_MAGIC; ! ci->ci_func[0](data, size, &spa->spa_cksum_salt, ! spa->spa_cksum_tmpls[checksum], &cksum); eck->zec_cksum = cksum; } else { ! ci->ci_func[0](data, size, &spa->spa_cksum_salt, ! spa->spa_cksum_tmpls[checksum], &bp->blk_cksum); } } int zio_checksum_error(zio_t *zio, zio_bad_cksum_t *info)
*** 200,213 **** --- 272,289 ---- (BP_IS_GANG(bp) ? SPA_GANGBLOCKSIZE : BP_GET_PSIZE(bp))); uint64_t offset = zio->io_offset; void *data = zio->io_data; zio_checksum_info_t *ci = &zio_checksum_table[checksum]; zio_cksum_t actual_cksum, expected_cksum, verifier; + spa_t *spa = zio->io_spa; if (checksum >= ZIO_CHECKSUM_FUNCTIONS || ci->ci_func[0] == NULL) return (SET_ERROR(EINVAL)); + if (ci->ci_tmpl_init != NULL && spa->spa_cksum_tmpls[checksum] == NULL) + zio_checksum_template_init(checksum, spa); + if (ci->ci_eck) { zio_eck_t *eck; if (checksum == ZIO_CHECKSUM_ZILOG2) { zil_chain_t *zilc = data;
*** 241,261 **** if (byteswap) byteswap_uint64_array(&verifier, sizeof (zio_cksum_t)); expected_cksum = eck->zec_cksum; eck->zec_cksum = verifier; ! ci->ci_func[byteswap](data, size, &actual_cksum); eck->zec_cksum = expected_cksum; if (byteswap) byteswap_uint64_array(&expected_cksum, sizeof (zio_cksum_t)); } else { ASSERT(!BP_IS_GANG(bp)); byteswap = BP_SHOULD_BYTESWAP(bp); expected_cksum = bp->blk_cksum; ! ci->ci_func[byteswap](data, size, &actual_cksum); } info->zbc_expected = expected_cksum; info->zbc_actual = actual_cksum; info->zbc_checksum_name = ci->ci_name; --- 317,339 ---- if (byteswap) byteswap_uint64_array(&verifier, sizeof (zio_cksum_t)); expected_cksum = eck->zec_cksum; eck->zec_cksum = verifier; ! ci->ci_func[byteswap](data, size, &spa->spa_cksum_salt, ! spa->spa_cksum_tmpls[checksum], &actual_cksum); eck->zec_cksum = expected_cksum; if (byteswap) byteswap_uint64_array(&expected_cksum, sizeof (zio_cksum_t)); } else { ASSERT(!BP_IS_GANG(bp)); byteswap = BP_SHOULD_BYTESWAP(bp); expected_cksum = bp->blk_cksum; ! ci->ci_func[byteswap](data, size, &spa->spa_cksum_salt, ! spa->spa_cksum_tmpls[checksum], &actual_cksum); } info->zbc_expected = expected_cksum; info->zbc_actual = actual_cksum; info->zbc_checksum_name = ci->ci_name;
*** 272,277 **** --- 350,374 ---- info->zbc_injected = 1; return (error); } return (0); + } + + /* + * Called by a spa_t that's about to be deallocated. This steps through + * all of the checksum context templates and deallocates any that were + * initialized using the algorithm-specific template init function. + */ + void + zio_checksum_templates_free(spa_t *spa) + { + for (int checksum = 0; checksum < ZIO_CHECKSUM_FUNCTIONS; checksum++) { + if (spa->spa_cksum_tmpls[checksum] != NULL) { + zio_checksum_info_t *ci = &zio_checksum_table[checksum]; + + VERIFY(ci->ci_tmpl_free != NULL); + ci->ci_tmpl_free(spa->spa_cksum_tmpls[checksum]); + spa->spa_cksum_tmpls[checksum] = NULL; + } + } }