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;
+ }
+ }
}