xref: /illumos-gate/usr/src/uts/common/fs/zfs/zio_checksum.c (revision cde58dbc6a23d4d38db7c8866312be83221c765f)
1fa9e4066Sahrens /*
2fa9e4066Sahrens  * CDDL HEADER START
3fa9e4066Sahrens  *
4fa9e4066Sahrens  * The contents of this file are subject to the terms of the
5ea8dc4b6Seschrock  * Common Development and Distribution License (the "License").
6ea8dc4b6Seschrock  * You may not use this file except in compliance with the License.
7fa9e4066Sahrens  *
8fa9e4066Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9fa9e4066Sahrens  * or http://www.opensolaris.org/os/licensing.
10fa9e4066Sahrens  * See the License for the specific language governing permissions
11fa9e4066Sahrens  * and limitations under the License.
12fa9e4066Sahrens  *
13fa9e4066Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
14fa9e4066Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15fa9e4066Sahrens  * If applicable, add the following below this CDDL HEADER, with the
16fa9e4066Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
17fa9e4066Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
18fa9e4066Sahrens  *
19fa9e4066Sahrens  * CDDL HEADER END
20fa9e4066Sahrens  */
21fa9e4066Sahrens /*
22*cde58dbcSMatthew Ahrens  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23fa9e4066Sahrens  */
24fa9e4066Sahrens 
25fa9e4066Sahrens #include <sys/zfs_context.h>
26fa9e4066Sahrens #include <sys/spa.h>
27fa9e4066Sahrens #include <sys/zio.h>
28fa9e4066Sahrens #include <sys/zio_checksum.h>
296e1f5caaSNeil Perrin #include <sys/zil.h>
30*cde58dbcSMatthew Ahrens #include <zfs_fletcher.h>
31fa9e4066Sahrens 
32fa9e4066Sahrens /*
33fa9e4066Sahrens  * Checksum vectors.
34fa9e4066Sahrens  *
35fa9e4066Sahrens  * In the SPA, everything is checksummed.  We support checksum vectors
36fa9e4066Sahrens  * for three distinct reasons:
37fa9e4066Sahrens  *
38fa9e4066Sahrens  *   1. Different kinds of data need different levels of protection.
39fa9e4066Sahrens  *	For SPA metadata, we always want a very strong checksum.
40fa9e4066Sahrens  *	For user data, we let users make the trade-off between speed
41fa9e4066Sahrens  *	and checksum strength.
42fa9e4066Sahrens  *
43fa9e4066Sahrens  *   2. Cryptographic hash and MAC algorithms are an area of active research.
44fa9e4066Sahrens  *	It is likely that in future hash functions will be at least as strong
45fa9e4066Sahrens  *	as current best-of-breed, and may be substantially faster as well.
46fa9e4066Sahrens  *	We want the ability to take advantage of these new hashes as soon as
47fa9e4066Sahrens  *	they become available.
48fa9e4066Sahrens  *
49fa9e4066Sahrens  *   3. If someone develops hardware that can compute a strong hash quickly,
50fa9e4066Sahrens  *	we want the ability to take advantage of that hardware.
51fa9e4066Sahrens  *
52fa9e4066Sahrens  * Of course, we don't want a checksum upgrade to invalidate existing
53b24ab676SJeff Bonwick  * data, so we store the checksum *function* in eight bits of the bp.
54b24ab676SJeff Bonwick  * This gives us room for up to 256 different checksum functions.
55fa9e4066Sahrens  *
56fa9e4066Sahrens  * When writing a block, we always checksum it with the latest-and-greatest
57fa9e4066Sahrens  * checksum function of the appropriate strength.  When reading a block,
58fa9e4066Sahrens  * we compare the expected checksum against the actual checksum, which we
59b24ab676SJeff Bonwick  * compute via the checksum function specified by BP_GET_CHECKSUM(bp).
60fa9e4066Sahrens  */
61fa9e4066Sahrens 
62fa9e4066Sahrens /*ARGSUSED*/
63fa9e4066Sahrens static void
64fa9e4066Sahrens zio_checksum_off(const void *buf, uint64_t size, zio_cksum_t *zcp)
65fa9e4066Sahrens {
66fa9e4066Sahrens 	ZIO_SET_CHECKSUM(zcp, 0, 0, 0, 0);
67fa9e4066Sahrens }
68fa9e4066Sahrens 
69fa9e4066Sahrens zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS] = {
70b24ab676SJeff Bonwick 	{{NULL,			NULL},			0, 0, 0, "inherit"},
71b24ab676SJeff Bonwick 	{{NULL,			NULL},			0, 0, 0, "on"},
72b24ab676SJeff Bonwick 	{{zio_checksum_off,	zio_checksum_off},	0, 0, 0, "off"},
73b24ab676SJeff Bonwick 	{{zio_checksum_SHA256,	zio_checksum_SHA256},	1, 1, 0, "label"},
74b24ab676SJeff Bonwick 	{{zio_checksum_SHA256,	zio_checksum_SHA256},	1, 1, 0, "gang_header"},
75b24ab676SJeff Bonwick 	{{fletcher_2_native,	fletcher_2_byteswap},	0, 1, 0, "zilog"},
76b24ab676SJeff Bonwick 	{{fletcher_2_native,	fletcher_2_byteswap},	0, 0, 0, "fletcher2"},
77b24ab676SJeff Bonwick 	{{fletcher_4_native,	fletcher_4_byteswap},	1, 0, 0, "fletcher4"},
78b24ab676SJeff Bonwick 	{{zio_checksum_SHA256,	zio_checksum_SHA256},	1, 0, 1, "sha256"},
796e1f5caaSNeil Perrin 	{{fletcher_4_native,	fletcher_4_byteswap},	0, 1, 0, "zilog2"},
80fa9e4066Sahrens };
81fa9e4066Sahrens 
82b24ab676SJeff Bonwick enum zio_checksum
83b24ab676SJeff Bonwick zio_checksum_select(enum zio_checksum child, enum zio_checksum parent)
84fa9e4066Sahrens {
85fa9e4066Sahrens 	ASSERT(child < ZIO_CHECKSUM_FUNCTIONS);
86fa9e4066Sahrens 	ASSERT(parent < ZIO_CHECKSUM_FUNCTIONS);
87fa9e4066Sahrens 	ASSERT(parent != ZIO_CHECKSUM_INHERIT && parent != ZIO_CHECKSUM_ON);
88fa9e4066Sahrens 
89fa9e4066Sahrens 	if (child == ZIO_CHECKSUM_INHERIT)
90fa9e4066Sahrens 		return (parent);
91fa9e4066Sahrens 
92fa9e4066Sahrens 	if (child == ZIO_CHECKSUM_ON)
93fa9e4066Sahrens 		return (ZIO_CHECKSUM_ON_VALUE);
94fa9e4066Sahrens 
95fa9e4066Sahrens 	return (child);
96fa9e4066Sahrens }
97fa9e4066Sahrens 
98b24ab676SJeff Bonwick enum zio_checksum
99b24ab676SJeff Bonwick zio_checksum_dedup_select(spa_t *spa, enum zio_checksum child,
100b24ab676SJeff Bonwick     enum zio_checksum parent)
101b24ab676SJeff Bonwick {
102b24ab676SJeff Bonwick 	ASSERT((child & ZIO_CHECKSUM_MASK) < ZIO_CHECKSUM_FUNCTIONS);
103b24ab676SJeff Bonwick 	ASSERT((parent & ZIO_CHECKSUM_MASK) < ZIO_CHECKSUM_FUNCTIONS);
104b24ab676SJeff Bonwick 	ASSERT(parent != ZIO_CHECKSUM_INHERIT && parent != ZIO_CHECKSUM_ON);
105b24ab676SJeff Bonwick 
106b24ab676SJeff Bonwick 	if (child == ZIO_CHECKSUM_INHERIT)
107b24ab676SJeff Bonwick 		return (parent);
108b24ab676SJeff Bonwick 
109b24ab676SJeff Bonwick 	if (child == ZIO_CHECKSUM_ON)
110b24ab676SJeff Bonwick 		return (spa_dedup_checksum(spa));
111b24ab676SJeff Bonwick 
112b24ab676SJeff Bonwick 	if (child == (ZIO_CHECKSUM_ON | ZIO_CHECKSUM_VERIFY))
113b24ab676SJeff Bonwick 		return (spa_dedup_checksum(spa) | ZIO_CHECKSUM_VERIFY);
114b24ab676SJeff Bonwick 
115b24ab676SJeff Bonwick 	ASSERT(zio_checksum_table[child & ZIO_CHECKSUM_MASK].ci_dedup ||
116b24ab676SJeff Bonwick 	    (child & ZIO_CHECKSUM_VERIFY) || child == ZIO_CHECKSUM_OFF);
117b24ab676SJeff Bonwick 
118b24ab676SJeff Bonwick 	return (child);
119b24ab676SJeff Bonwick }
120b24ab676SJeff Bonwick 
121e14bb325SJeff Bonwick /*
122e14bb325SJeff Bonwick  * Set the external verifier for a gang block based on <vdev, offset, txg>,
123e14bb325SJeff Bonwick  * a tuple which is guaranteed to be unique for the life of the pool.
124e14bb325SJeff Bonwick  */
125e14bb325SJeff Bonwick static void
126e14bb325SJeff Bonwick zio_checksum_gang_verifier(zio_cksum_t *zcp, blkptr_t *bp)
127e14bb325SJeff Bonwick {
128e14bb325SJeff Bonwick 	dva_t *dva = BP_IDENTITY(bp);
129b24ab676SJeff Bonwick 	uint64_t txg = BP_PHYSICAL_BIRTH(bp);
130e14bb325SJeff Bonwick 
131e14bb325SJeff Bonwick 	ASSERT(BP_IS_GANG(bp));
132e14bb325SJeff Bonwick 
133e14bb325SJeff Bonwick 	ZIO_SET_CHECKSUM(zcp, DVA_GET_VDEV(dva), DVA_GET_OFFSET(dva), txg, 0);
134e14bb325SJeff Bonwick }
135e14bb325SJeff Bonwick 
136e14bb325SJeff Bonwick /*
137e14bb325SJeff Bonwick  * Set the external verifier for a label block based on its offset.
138e14bb325SJeff Bonwick  * The vdev is implicit, and the txg is unknowable at pool open time --
139e14bb325SJeff Bonwick  * hence the logic in vdev_uberblock_load() to find the most recent copy.
140e14bb325SJeff Bonwick  */
141e14bb325SJeff Bonwick static void
142e14bb325SJeff Bonwick zio_checksum_label_verifier(zio_cksum_t *zcp, uint64_t offset)
143e14bb325SJeff Bonwick {
144e14bb325SJeff Bonwick 	ZIO_SET_CHECKSUM(zcp, offset, 0, 0, 0);
145e14bb325SJeff Bonwick }
146e14bb325SJeff Bonwick 
147fa9e4066Sahrens /*
148fa9e4066Sahrens  * Generate the checksum.
149fa9e4066Sahrens  */
150fa9e4066Sahrens void
151e14bb325SJeff Bonwick zio_checksum_compute(zio_t *zio, enum zio_checksum checksum,
152e14bb325SJeff Bonwick 	void *data, uint64_t size)
153fa9e4066Sahrens {
154e14bb325SJeff Bonwick 	blkptr_t *bp = zio->io_bp;
155e14bb325SJeff Bonwick 	uint64_t offset = zio->io_offset;
156fa9e4066Sahrens 	zio_checksum_info_t *ci = &zio_checksum_table[checksum];
1576e1f5caaSNeil Perrin 	zio_cksum_t cksum;
158fa9e4066Sahrens 
159e14bb325SJeff Bonwick 	ASSERT((uint_t)checksum < ZIO_CHECKSUM_FUNCTIONS);
160fa9e4066Sahrens 	ASSERT(ci->ci_func[0] != NULL);
161fa9e4066Sahrens 
1626e1f5caaSNeil Perrin 	if (ci->ci_eck) {
1636e1f5caaSNeil Perrin 		zio_eck_t *eck;
1646e1f5caaSNeil Perrin 
1656e1f5caaSNeil Perrin 		if (checksum == ZIO_CHECKSUM_ZILOG2) {
1666e1f5caaSNeil Perrin 			zil_chain_t *zilc = data;
1676e1f5caaSNeil Perrin 
1686e1f5caaSNeil Perrin 			size = P2ROUNDUP_TYPED(zilc->zc_nused, ZIL_MIN_BLKSZ,
1696e1f5caaSNeil Perrin 			    uint64_t);
1706e1f5caaSNeil Perrin 			eck = &zilc->zc_eck;
1716e1f5caaSNeil Perrin 		} else {
1726e1f5caaSNeil Perrin 			eck = (zio_eck_t *)((char *)data + size) - 1;
1736e1f5caaSNeil Perrin 		}
174e14bb325SJeff Bonwick 		if (checksum == ZIO_CHECKSUM_GANG_HEADER)
1756e1f5caaSNeil Perrin 			zio_checksum_gang_verifier(&eck->zec_cksum, bp);
176e14bb325SJeff Bonwick 		else if (checksum == ZIO_CHECKSUM_LABEL)
1776e1f5caaSNeil Perrin 			zio_checksum_label_verifier(&eck->zec_cksum, offset);
178e14bb325SJeff Bonwick 		else
1796e1f5caaSNeil Perrin 			bp->blk_cksum = eck->zec_cksum;
1806e1f5caaSNeil Perrin 		eck->zec_magic = ZEC_MAGIC;
1816e1f5caaSNeil Perrin 		ci->ci_func[0](data, size, &cksum);
1826e1f5caaSNeil Perrin 		eck->zec_cksum = cksum;
183fa9e4066Sahrens 	} else {
184e14bb325SJeff Bonwick 		ci->ci_func[0](data, size, &bp->blk_cksum);
185fa9e4066Sahrens 	}
186fa9e4066Sahrens }
187fa9e4066Sahrens 
188fa9e4066Sahrens int
18922fe2c88SJonathan Adams zio_checksum_error(zio_t *zio, zio_bad_cksum_t *info)
190fa9e4066Sahrens {
191fa9e4066Sahrens 	blkptr_t *bp = zio->io_bp;
192e14bb325SJeff Bonwick 	uint_t checksum = (bp == NULL ? zio->io_prop.zp_checksum :
193e14bb325SJeff Bonwick 	    (BP_IS_GANG(bp) ? ZIO_CHECKSUM_GANG_HEADER : BP_GET_CHECKSUM(bp)));
194e14bb325SJeff Bonwick 	int byteswap;
19522fe2c88SJonathan Adams 	int error;
196e14bb325SJeff Bonwick 	uint64_t size = (bp == NULL ? zio->io_size :
197e14bb325SJeff Bonwick 	    (BP_IS_GANG(bp) ? SPA_GANGBLOCKSIZE : BP_GET_PSIZE(bp)));
198e14bb325SJeff Bonwick 	uint64_t offset = zio->io_offset;
19922fe2c88SJonathan Adams 	void *data = zio->io_data;
200fa9e4066Sahrens 	zio_checksum_info_t *ci = &zio_checksum_table[checksum];
201e14bb325SJeff Bonwick 	zio_cksum_t actual_cksum, expected_cksum, verifier;
202fa9e4066Sahrens 
203fa9e4066Sahrens 	if (checksum >= ZIO_CHECKSUM_FUNCTIONS || ci->ci_func[0] == NULL)
204fa9e4066Sahrens 		return (EINVAL);
205fa9e4066Sahrens 
2066e1f5caaSNeil Perrin 	if (ci->ci_eck) {
2076e1f5caaSNeil Perrin 		zio_eck_t *eck;
2086e1f5caaSNeil Perrin 
2096e1f5caaSNeil Perrin 		if (checksum == ZIO_CHECKSUM_ZILOG2) {
2106e1f5caaSNeil Perrin 			zil_chain_t *zilc = data;
2116e1f5caaSNeil Perrin 			uint64_t nused;
2126e1f5caaSNeil Perrin 
2136e1f5caaSNeil Perrin 			eck = &zilc->zc_eck;
2146e1f5caaSNeil Perrin 			if (eck->zec_magic == ZEC_MAGIC)
2156e1f5caaSNeil Perrin 				nused = zilc->zc_nused;
2166e1f5caaSNeil Perrin 			else if (eck->zec_magic == BSWAP_64(ZEC_MAGIC))
2176e1f5caaSNeil Perrin 				nused = BSWAP_64(zilc->zc_nused);
2186e1f5caaSNeil Perrin 			else
2196e1f5caaSNeil Perrin 				return (ECKSUM);
2206e1f5caaSNeil Perrin 
2216e1f5caaSNeil Perrin 			if (nused > size)
2226e1f5caaSNeil Perrin 				return (ECKSUM);
2236e1f5caaSNeil Perrin 
2246e1f5caaSNeil Perrin 			size = P2ROUNDUP_TYPED(nused, ZIL_MIN_BLKSZ, uint64_t);
2256e1f5caaSNeil Perrin 		} else {
2266e1f5caaSNeil Perrin 			eck = (zio_eck_t *)((char *)data + size) - 1;
2276e1f5caaSNeil Perrin 		}
2286e1f5caaSNeil Perrin 
229fa9e4066Sahrens 		if (checksum == ZIO_CHECKSUM_GANG_HEADER)
230e14bb325SJeff Bonwick 			zio_checksum_gang_verifier(&verifier, bp);
231e14bb325SJeff Bonwick 		else if (checksum == ZIO_CHECKSUM_LABEL)
232e14bb325SJeff Bonwick 			zio_checksum_label_verifier(&verifier, offset);
233e14bb325SJeff Bonwick 		else
234e14bb325SJeff Bonwick 			verifier = bp->blk_cksum;
235e14bb325SJeff Bonwick 
2366e1f5caaSNeil Perrin 		byteswap = (eck->zec_magic == BSWAP_64(ZEC_MAGIC));
237fa9e4066Sahrens 
238e14bb325SJeff Bonwick 		if (byteswap)
239e14bb325SJeff Bonwick 			byteswap_uint64_array(&verifier, sizeof (zio_cksum_t));
240e14bb325SJeff Bonwick 
2416e1f5caaSNeil Perrin 		expected_cksum = eck->zec_cksum;
2426e1f5caaSNeil Perrin 		eck->zec_cksum = verifier;
243e14bb325SJeff Bonwick 		ci->ci_func[byteswap](data, size, &actual_cksum);
2446e1f5caaSNeil Perrin 		eck->zec_cksum = expected_cksum;
245e14bb325SJeff Bonwick 
246e14bb325SJeff Bonwick 		if (byteswap)
247fa9e4066Sahrens 			byteswap_uint64_array(&expected_cksum,
248fa9e4066Sahrens 			    sizeof (zio_cksum_t));
249fa9e4066Sahrens 	} else {
25044cd46caSbillm 		ASSERT(!BP_IS_GANG(bp));
251e14bb325SJeff Bonwick 		byteswap = BP_SHOULD_BYTESWAP(bp);
252e14bb325SJeff Bonwick 		expected_cksum = bp->blk_cksum;
253fa9e4066Sahrens 		ci->ci_func[byteswap](data, size, &actual_cksum);
254fa9e4066Sahrens 	}
255fa9e4066Sahrens 
25622fe2c88SJonathan Adams 	info->zbc_expected = expected_cksum;
25722fe2c88SJonathan Adams 	info->zbc_actual = actual_cksum;
25822fe2c88SJonathan Adams 	info->zbc_checksum_name = ci->ci_name;
25922fe2c88SJonathan Adams 	info->zbc_byteswapped = byteswap;
26022fe2c88SJonathan Adams 	info->zbc_injected = 0;
26122fe2c88SJonathan Adams 	info->zbc_has_cksum = 1;
26222fe2c88SJonathan Adams 
263e14bb325SJeff Bonwick 	if (!ZIO_CHECKSUM_EQUAL(actual_cksum, expected_cksum))
264fa9e4066Sahrens 		return (ECKSUM);
265fa9e4066Sahrens 
26622fe2c88SJonathan Adams 	if (zio_injection_enabled && !zio->io_error &&
26722fe2c88SJonathan Adams 	    (error = zio_handle_fault_injection(zio, ECKSUM)) != 0) {
26822fe2c88SJonathan Adams 
26922fe2c88SJonathan Adams 		info->zbc_injected = 1;
27022fe2c88SJonathan Adams 		return (error);
27122fe2c88SJonathan Adams 	}
272ea8dc4b6Seschrock 
273fa9e4066Sahrens 	return (0);
274fa9e4066Sahrens }
275