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 /* 17 * Copyright (c) 2013, 2016 by Delphix. All rights reserved. 18 */ 19 20 #include <sys/blkptr.h> 21 #include <sys/zfs_context.h> 22 #include <sys/zio.h> 23 #include <sys/zio_compress.h> 24 25 /* 26 * Embedded-data Block Pointers 27 * 28 * Normally, block pointers point (via their DVAs) to a block which holds data. 29 * If the data that we need to store is very small, this is an inefficient 30 * use of space, because a block must be at minimum 1 sector (typically 512 31 * bytes or 4KB). Additionally, reading these small blocks tends to generate 32 * more random reads. 33 * 34 * Embedded-data Block Pointers allow small pieces of data (the "payload", 35 * up to 112 bytes) to be stored in the block pointer itself, instead of 36 * being pointed to. The "Pointer" part of this name is a bit of a 37 * misnomer, as nothing is pointed to. 38 * 39 * BP_EMBEDDED_TYPE_DATA block pointers allow highly-compressible data to 40 * be embedded in the block pointer. The logic for this is handled in 41 * the SPA, by the zio pipeline. Therefore most code outside the zio 42 * pipeline doesn't need special-cases to handle these block pointers. 43 * 44 * See spa.h for details on the exact layout of embedded block pointers. 45 */ 46 47 void 48 encode_embedded_bp_compressed(blkptr_t *bp, void *data, 49 enum zio_compress comp, int uncompressed_size, int compressed_size) 50 { 51 uint64_t *bp64 = (uint64_t *)bp; 52 uint64_t w = 0; 53 uint8_t *data8 = data; 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 (int 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 101 ASSERT(BP_IS_EMBEDDED(bp)); 102 103 psize = BPE_GET_PSIZE(bp); 104 105 /* 106 * Decode the words of the block pointer into the byte array. 107 * Low bits of first word are the first byte (little endian). 108 */ 109 for (int i = 0; i < psize; i++) { 110 if (i % sizeof (w) == 0) { 111 /* beginning of a word */ 112 ASSERT3P(bp64, <, bp + 1); 113 w = *bp64; 114 bp64++; 115 if (!BPE_IS_PAYLOADWORD(bp, bp64)) 116 bp64++; 117 } 118 buf8[i] = BF64_GET(w, (i % sizeof (w)) * NBBY, NBBY); 119 } 120 } 121 122 /* 123 * Fill in the buffer with the (decompressed) payload of the embedded 124 * blkptr_t. Takes into account compression and byteorder (the payload is 125 * treated as a stream of bytes). 126 * Return 0 on success, or ENOSPC if it won't fit in the buffer. 127 */ 128 int 129 decode_embedded_bp(const blkptr_t *bp, void *buf, int buflen) 130 { 131 int lsize, psize; 132 133 ASSERT(BP_IS_EMBEDDED(bp)); 134 135 lsize = BPE_GET_LSIZE(bp); 136 psize = BPE_GET_PSIZE(bp); 137 138 if (lsize > buflen) 139 return (SET_ERROR(ENOSPC)); 140 ASSERT3U(lsize, ==, buflen); 141 142 if (BP_GET_COMPRESS(bp) != ZIO_COMPRESS_OFF) { 143 uint8_t dstbuf[BPE_PAYLOAD_SIZE]; 144 decode_embedded_bp_compressed(bp, dstbuf); 145 VERIFY0(zio_decompress_data_buf(BP_GET_COMPRESS(bp), 146 dstbuf, buf, psize, buflen, NULL)); 147 } else { 148 ASSERT3U(lsize, ==, psize); 149 decode_embedded_bp_compressed(bp, buf); 150 } 151 152 return (0); 153 } 154