]>
Commit | Line | Data |
---|---|---|
70e083d2 TG |
1 | /* |
2 | * CDDL HEADER START | |
3 | * | |
4 | * This file and its contents are supplied under the terms of the | |
5 | * Common Development and Distribution License ("CDDL"), version 1.0. | |
6 | * You may only use this file in accordance with the terms of version | |
7 | * 1.0 of the CDDL. | |
8 | * | |
9 | * A full copy of the text of the CDDL should have accompanied this | |
10 | * source. A copy of the CDDL is also available via the Internet at | |
11 | * http://www.illumos.org/license/CDDL. | |
12 | * | |
13 | * CDDL HEADER END | |
14 | */ | |
15 | ||
16 | /* | |
86e3c28a | 17 | * Copyright (c) 2013, 2016 by Delphix. All rights reserved. |
70e083d2 TG |
18 | */ |
19 | ||
20 | #include <sys/zfs_context.h> | |
21 | #include <sys/zio.h> | |
22 | #include <sys/zio_compress.h> | |
23 | ||
24 | /* | |
25 | * Embedded-data Block Pointers | |
26 | * | |
27 | * Normally, block pointers point (via their DVAs) to a block which holds data. | |
28 | * If the data that we need to store is very small, this is an inefficient | |
29 | * use of space, because a block must be at minimum 1 sector (typically 512 | |
30 | * bytes or 4KB). Additionally, reading these small blocks tends to generate | |
31 | * more random reads. | |
32 | * | |
33 | * Embedded-data Block Pointers allow small pieces of data (the "payload", | |
34 | * up to 112 bytes) to be stored in the block pointer itself, instead of | |
35 | * being pointed to. The "Pointer" part of this name is a bit of a | |
36 | * misnomer, as nothing is pointed to. | |
37 | * | |
38 | * BP_EMBEDDED_TYPE_DATA block pointers allow highly-compressible data to | |
39 | * be embedded in the block pointer. The logic for this is handled in | |
40 | * the SPA, by the zio pipeline. Therefore most code outside the zio | |
41 | * pipeline doesn't need special-cases to handle these block pointers. | |
42 | * | |
43 | * See spa.h for details on the exact layout of embedded block pointers. | |
44 | */ | |
45 | ||
46 | void | |
47 | encode_embedded_bp_compressed(blkptr_t *bp, void *data, | |
48 | enum zio_compress comp, int uncompressed_size, int compressed_size) | |
49 | { | |
50 | uint64_t *bp64 = (uint64_t *)bp; | |
51 | uint64_t w = 0; | |
52 | uint8_t *data8 = data; | |
53 | int i; | |
54 | ||
55 | ASSERT3U(compressed_size, <=, BPE_PAYLOAD_SIZE); | |
56 | ASSERT(uncompressed_size == compressed_size || | |
57 | comp != ZIO_COMPRESS_OFF); | |
58 | ASSERT3U(comp, >=, ZIO_COMPRESS_OFF); | |
59 | ASSERT3U(comp, <, ZIO_COMPRESS_FUNCTIONS); | |
60 | ||
61 | bzero(bp, sizeof (*bp)); | |
62 | BP_SET_EMBEDDED(bp, B_TRUE); | |
63 | BP_SET_COMPRESS(bp, comp); | |
64 | BP_SET_BYTEORDER(bp, ZFS_HOST_BYTEORDER); | |
65 | BPE_SET_LSIZE(bp, uncompressed_size); | |
66 | BPE_SET_PSIZE(bp, compressed_size); | |
67 | ||
68 | /* | |
69 | * Encode the byte array into the words of the block pointer. | |
70 | * First byte goes into low bits of first word (little endian). | |
71 | */ | |
72 | for (i = 0; i < compressed_size; i++) { | |
73 | BF64_SET(w, (i % sizeof (w)) * NBBY, NBBY, data8[i]); | |
74 | if (i % sizeof (w) == sizeof (w) - 1) { | |
75 | /* we've reached the end of a word */ | |
76 | ASSERT3P(bp64, <, bp + 1); | |
77 | *bp64 = w; | |
78 | bp64++; | |
79 | if (!BPE_IS_PAYLOADWORD(bp, bp64)) | |
80 | bp64++; | |
81 | w = 0; | |
82 | } | |
83 | } | |
84 | /* write last partial word */ | |
85 | if (bp64 < (uint64_t *)(bp + 1)) | |
86 | *bp64 = w; | |
87 | } | |
88 | ||
89 | /* | |
90 | * buf must be at least BPE_GET_PSIZE(bp) bytes long (which will never be | |
91 | * more than BPE_PAYLOAD_SIZE bytes). | |
92 | */ | |
93 | void | |
94 | decode_embedded_bp_compressed(const blkptr_t *bp, void *buf) | |
95 | { | |
96 | int psize; | |
97 | uint8_t *buf8 = buf; | |
98 | uint64_t w = 0; | |
99 | const uint64_t *bp64 = (const uint64_t *)bp; | |
100 | int i; | |
101 | ||
102 | ASSERT(BP_IS_EMBEDDED(bp)); | |
103 | ||
104 | psize = BPE_GET_PSIZE(bp); | |
105 | ||
106 | /* | |
107 | * Decode the words of the block pointer into the byte array. | |
108 | * Low bits of first word are the first byte (little endian). | |
109 | */ | |
110 | for (i = 0; i < psize; i++) { | |
111 | if (i % sizeof (w) == 0) { | |
112 | /* beginning of a word */ | |
113 | ASSERT3P(bp64, <, bp + 1); | |
114 | w = *bp64; | |
115 | bp64++; | |
116 | if (!BPE_IS_PAYLOADWORD(bp, bp64)) | |
117 | bp64++; | |
118 | } | |
119 | buf8[i] = BF64_GET(w, (i % sizeof (w)) * NBBY, NBBY); | |
120 | } | |
121 | } | |
86e3c28a CIK |
122 | |
123 | /* | |
124 | * Fill in the buffer with the (decompressed) payload of the embedded | |
125 | * blkptr_t. Takes into account compression and byteorder (the payload is | |
126 | * treated as a stream of bytes). | |
127 | * Return 0 on success, or ENOSPC if it won't fit in the buffer. | |
128 | */ | |
129 | int | |
130 | decode_embedded_bp(const blkptr_t *bp, void *buf, int buflen) | |
131 | { | |
132 | int lsize, psize; | |
133 | ||
134 | ASSERT(BP_IS_EMBEDDED(bp)); | |
135 | ||
136 | lsize = BPE_GET_LSIZE(bp); | |
137 | psize = BPE_GET_PSIZE(bp); | |
138 | ||
139 | if (lsize > buflen) | |
140 | return (ENOSPC); | |
141 | ASSERT3U(lsize, ==, buflen); | |
142 | ||
143 | if (BP_GET_COMPRESS(bp) != ZIO_COMPRESS_OFF) { | |
144 | uint8_t dstbuf[BPE_PAYLOAD_SIZE]; | |
145 | decode_embedded_bp_compressed(bp, dstbuf); | |
146 | VERIFY0(zio_decompress_data_buf(BP_GET_COMPRESS(bp), | |
147 | dstbuf, buf, psize, buflen)); | |
148 | } else { | |
149 | ASSERT3U(lsize, ==, psize); | |
150 | decode_embedded_bp_compressed(bp, buf); | |
151 | } | |
152 | ||
153 | return (0); | |
154 | } |