1267565Sdelphij/*
2267565Sdelphij * CDDL HEADER START
3267565Sdelphij *
4267565Sdelphij * This file and its contents are supplied under the terms of the
5267565Sdelphij * Common Development and Distribution License ("CDDL"), version 1.0.
6267565Sdelphij * You may only use this file in accordance with the terms of version
7267565Sdelphij * 1.0 of the CDDL.
8267565Sdelphij *
9267565Sdelphij * A full copy of the text of the CDDL should have accompanied this
10267565Sdelphij * source.  A copy of the CDDL is also available via the Internet at
11267565Sdelphij * http://www.illumos.org/license/CDDL.
12267565Sdelphij *
13267565Sdelphij * CDDL HEADER END
14267565Sdelphij */
15267565Sdelphij
16267565Sdelphij/*
17267565Sdelphij * Copyright (c) 2013 by Delphix. All rights reserved.
18267565Sdelphij */
19267565Sdelphij
20267565Sdelphij#include <sys/zfs_context.h>
21267565Sdelphij#include <sys/zio.h>
22267565Sdelphij#include <sys/zio_compress.h>
23267565Sdelphij
24267565Sdelphij/*
25267565Sdelphij * Embedded-data Block Pointers
26267565Sdelphij *
27267565Sdelphij * Normally, block pointers point (via their DVAs) to a block which holds data.
28267565Sdelphij * If the data that we need to store is very small, this is an inefficient
29267565Sdelphij * use of space, because a block must be at minimum 1 sector (typically 512
30267565Sdelphij * bytes or 4KB).  Additionally, reading these small blocks tends to generate
31267565Sdelphij * more random reads.
32267565Sdelphij *
33267565Sdelphij * Embedded-data Block Pointers allow small pieces of data (the "payload",
34267565Sdelphij * up to 112 bytes) to be stored in the block pointer itself, instead of
35267565Sdelphij * being pointed to.  The "Pointer" part of this name is a bit of a
36267565Sdelphij * misnomer, as nothing is pointed to.
37267565Sdelphij *
38267565Sdelphij * BP_EMBEDDED_TYPE_DATA block pointers allow highly-compressible data to
39267565Sdelphij * be embedded in the block pointer.  The logic for this is handled in
40267565Sdelphij * the SPA, by the zio pipeline.  Therefore most code outside the zio
41267565Sdelphij * pipeline doesn't need special-cases to handle these block pointers.
42267565Sdelphij *
43267565Sdelphij * See spa.h for details on the exact layout of embedded block pointers.
44267565Sdelphij */
45267565Sdelphij
46267565Sdelphijvoid
47267565Sdelphijencode_embedded_bp_compressed(blkptr_t *bp, void *data,
48267565Sdelphij    enum zio_compress comp, int uncompressed_size, int compressed_size)
49267565Sdelphij{
50267565Sdelphij	uint64_t *bp64 = (uint64_t *)bp;
51267565Sdelphij	uint64_t w = 0;
52267565Sdelphij	uint8_t *data8 = data;
53267565Sdelphij
54267565Sdelphij	ASSERT3U(compressed_size, <=, BPE_PAYLOAD_SIZE);
55267565Sdelphij	ASSERT(uncompressed_size == compressed_size ||
56267565Sdelphij	    comp != ZIO_COMPRESS_OFF);
57267565Sdelphij	ASSERT3U(comp, >=, ZIO_COMPRESS_OFF);
58267565Sdelphij	ASSERT3U(comp, <, ZIO_COMPRESS_FUNCTIONS);
59267565Sdelphij
60267565Sdelphij	bzero(bp, sizeof (*bp));
61267565Sdelphij	BP_SET_EMBEDDED(bp, B_TRUE);
62267565Sdelphij	BP_SET_COMPRESS(bp, comp);
63267565Sdelphij	BP_SET_BYTEORDER(bp, ZFS_HOST_BYTEORDER);
64267565Sdelphij	BPE_SET_LSIZE(bp, uncompressed_size);
65267565Sdelphij	BPE_SET_PSIZE(bp, compressed_size);
66267565Sdelphij
67267565Sdelphij	/*
68267565Sdelphij	 * Encode the byte array into the words of the block pointer.
69267565Sdelphij	 * First byte goes into low bits of first word (little endian).
70267565Sdelphij	 */
71267565Sdelphij	for (int i = 0; i < compressed_size; i++) {
72267565Sdelphij		BF64_SET(w, (i % sizeof (w)) * NBBY, NBBY, data8[i]);
73267565Sdelphij		if (i % sizeof (w) == sizeof (w) - 1) {
74267565Sdelphij			/* we've reached the end of a word */
75267565Sdelphij			ASSERT3P(bp64, <, bp + 1);
76267565Sdelphij			*bp64 = w;
77267565Sdelphij			bp64++;
78267565Sdelphij			if (!BPE_IS_PAYLOADWORD(bp, bp64))
79267565Sdelphij				bp64++;
80267565Sdelphij			w = 0;
81267565Sdelphij		}
82267565Sdelphij	}
83267565Sdelphij	/* write last partial word */
84267565Sdelphij	if (bp64 < (uint64_t *)(bp + 1))
85267565Sdelphij		*bp64 = w;
86267565Sdelphij}
87267565Sdelphij
88267565Sdelphij/*
89267565Sdelphij * buf must be at least BPE_GET_PSIZE(bp) bytes long (which will never be
90267565Sdelphij * more than BPE_PAYLOAD_SIZE bytes).
91267565Sdelphij */
92267565Sdelphijvoid
93267565Sdelphijdecode_embedded_bp_compressed(const blkptr_t *bp, void *buf)
94267565Sdelphij{
95267565Sdelphij	int psize;
96267565Sdelphij	uint8_t *buf8 = buf;
97267565Sdelphij	uint64_t w = 0;
98267565Sdelphij	const uint64_t *bp64 = (const uint64_t *)bp;
99267565Sdelphij
100267565Sdelphij	ASSERT(BP_IS_EMBEDDED(bp));
101267565Sdelphij
102267565Sdelphij	psize = BPE_GET_PSIZE(bp);
103267565Sdelphij
104267565Sdelphij	/*
105267565Sdelphij	 * Decode the words of the block pointer into the byte array.
106267565Sdelphij	 * Low bits of first word are the first byte (little endian).
107267565Sdelphij	 */
108267565Sdelphij	for (int i = 0; i < psize; i++) {
109267565Sdelphij		if (i % sizeof (w) == 0) {
110267565Sdelphij			/* beginning of a word */
111267565Sdelphij			ASSERT3P(bp64, <, bp + 1);
112267565Sdelphij			w = *bp64;
113267565Sdelphij			bp64++;
114267565Sdelphij			if (!BPE_IS_PAYLOADWORD(bp, bp64))
115267565Sdelphij				bp64++;
116267565Sdelphij		}
117267565Sdelphij		buf8[i] = BF64_GET(w, (i % sizeof (w)) * NBBY, NBBY);
118267565Sdelphij	}
119267565Sdelphij}
120