Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2018 Intel Corporation.
3 : : * All rights reserved.
4 : : * Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
5 : : */
6 : :
7 : : #include "spdk/stdinc.h"
8 : :
9 : : #include "spdk/reduce.h"
10 : : #include "spdk/env.h"
11 : : #include "spdk/string.h"
12 : : #include "spdk/bit_array.h"
13 : : #include "spdk/util.h"
14 : : #include "spdk/log.h"
15 : : #include "spdk/memory.h"
16 : :
17 : : #include "libpmem.h"
18 : :
19 : : /* Always round up the size of the PM region to the nearest cacheline. */
20 : : #define REDUCE_PM_SIZE_ALIGNMENT 64
21 : :
22 : : /* Offset into the backing device where the persistent memory file's path is stored. */
23 : : #define REDUCE_BACKING_DEV_PATH_OFFSET 4096
24 : :
25 : : #define REDUCE_EMPTY_MAP_ENTRY -1ULL
26 : :
27 : : #define REDUCE_NUM_VOL_REQUESTS 256
28 : :
29 : : /* Structure written to offset 0 of both the pm file and the backing device. */
30 : : struct spdk_reduce_vol_superblock {
31 : : uint8_t signature[8];
32 : : struct spdk_reduce_vol_params params;
33 : : uint8_t reserved[4048];
34 : : };
35 : : SPDK_STATIC_ASSERT(sizeof(struct spdk_reduce_vol_superblock) == 4096, "size incorrect");
36 : :
37 : : #define SPDK_REDUCE_SIGNATURE "SPDKREDU"
38 : : /* null terminator counts one */
39 : : SPDK_STATIC_ASSERT(sizeof(SPDK_REDUCE_SIGNATURE) - 1 ==
40 : : SPDK_SIZEOF_MEMBER(struct spdk_reduce_vol_superblock, signature), "size incorrect");
41 : :
42 : : #define REDUCE_PATH_MAX 4096
43 : :
44 : : #define REDUCE_ZERO_BUF_SIZE 0x100000
45 : :
46 : : /**
47 : : * Describes a persistent memory file used to hold metadata associated with a
48 : : * compressed volume.
49 : : */
50 : : struct spdk_reduce_pm_file {
51 : : char path[REDUCE_PATH_MAX];
52 : : void *pm_buf;
53 : : int pm_is_pmem;
54 : : uint64_t size;
55 : : };
56 : :
57 : : #define REDUCE_IO_READV 1
58 : : #define REDUCE_IO_WRITEV 2
59 : :
60 : : struct spdk_reduce_chunk_map {
61 : : uint32_t compressed_size;
62 : : uint32_t reserved;
63 : : uint64_t io_unit_index[0];
64 : : };
65 : :
66 : : struct spdk_reduce_vol_request {
67 : : /**
68 : : * Scratch buffer used for uncompressed chunk. This is used for:
69 : : * 1) source buffer for compression operations
70 : : * 2) destination buffer for decompression operations
71 : : * 3) data buffer when writing uncompressed chunk to disk
72 : : * 4) data buffer when reading uncompressed chunk from disk
73 : : */
74 : : uint8_t *decomp_buf;
75 : : struct iovec *decomp_buf_iov;
76 : :
77 : : /**
78 : : * These are used to construct the iovecs that are sent to
79 : : * the decomp engine, they point to a mix of the scratch buffer
80 : : * and user buffer
81 : : */
82 : : struct iovec decomp_iov[REDUCE_MAX_IOVECS + 2];
83 : : int decomp_iovcnt;
84 : :
85 : : /**
86 : : * Scratch buffer used for compressed chunk. This is used for:
87 : : * 1) destination buffer for compression operations
88 : : * 2) source buffer for decompression operations
89 : : * 3) data buffer when writing compressed chunk to disk
90 : : * 4) data buffer when reading compressed chunk from disk
91 : : */
92 : : uint8_t *comp_buf;
93 : : struct iovec *comp_buf_iov;
94 : : struct iovec *iov;
95 : : bool rmw;
96 : : struct spdk_reduce_vol *vol;
97 : : int type;
98 : : int reduce_errno;
99 : : int iovcnt;
100 : : int num_backing_ops;
101 : : uint32_t num_io_units;
102 : : bool chunk_is_compressed;
103 : : bool copy_after_decompress;
104 : : uint64_t offset;
105 : : uint64_t logical_map_index;
106 : : uint64_t length;
107 : : uint64_t chunk_map_index;
108 : : struct spdk_reduce_chunk_map *chunk;
109 : : spdk_reduce_vol_op_complete cb_fn;
110 : : void *cb_arg;
111 : : TAILQ_ENTRY(spdk_reduce_vol_request) tailq;
112 : : struct spdk_reduce_vol_cb_args backing_cb_args;
113 : : };
114 : :
115 : : struct spdk_reduce_vol {
116 : : struct spdk_reduce_vol_params params;
117 : : uint32_t backing_io_units_per_chunk;
118 : : uint32_t backing_lba_per_io_unit;
119 : : uint32_t logical_blocks_per_chunk;
120 : : struct spdk_reduce_pm_file pm_file;
121 : : struct spdk_reduce_backing_dev *backing_dev;
122 : : struct spdk_reduce_vol_superblock *backing_super;
123 : : struct spdk_reduce_vol_superblock *pm_super;
124 : : uint64_t *pm_logical_map;
125 : : uint64_t *pm_chunk_maps;
126 : :
127 : : struct spdk_bit_array *allocated_chunk_maps;
128 : : struct spdk_bit_array *allocated_backing_io_units;
129 : :
130 : : struct spdk_reduce_vol_request *request_mem;
131 : : TAILQ_HEAD(, spdk_reduce_vol_request) free_requests;
132 : : TAILQ_HEAD(, spdk_reduce_vol_request) executing_requests;
133 : : TAILQ_HEAD(, spdk_reduce_vol_request) queued_requests;
134 : :
135 : : /* Single contiguous buffer used for all request buffers for this volume. */
136 : : uint8_t *buf_mem;
137 : : struct iovec *buf_iov_mem;
138 : : };
139 : :
140 : : static void _start_readv_request(struct spdk_reduce_vol_request *req);
141 : : static void _start_writev_request(struct spdk_reduce_vol_request *req);
142 : : static uint8_t *g_zero_buf;
143 : : static int g_vol_count = 0;
144 : :
145 : : /*
146 : : * Allocate extra metadata chunks and corresponding backing io units to account for
147 : : * outstanding IO in worst case scenario where logical map is completely allocated
148 : : * and no data can be compressed. We need extra chunks in this case to handle
149 : : * in-flight writes since reduce never writes data in place.
150 : : */
151 : : #define REDUCE_NUM_EXTRA_CHUNKS 128
152 : :
153 : : static void
154 : 1637836 : _reduce_persist(struct spdk_reduce_vol *vol, const void *addr, size_t len)
155 : : {
156 [ - + ]: 1637836 : if (vol->pm_file.pm_is_pmem) {
157 : 0 : pmem_persist(addr, len);
158 : : } else {
159 : 1637836 : pmem_msync(addr, len);
160 : : }
161 : 1637836 : }
162 : :
163 : : static uint64_t
164 : 24 : _get_pm_logical_map_size(uint64_t vol_size, uint64_t chunk_size)
165 : : {
166 : : uint64_t chunks_in_logical_map, logical_map_size;
167 : :
168 [ - + ]: 24 : chunks_in_logical_map = vol_size / chunk_size;
169 : 24 : logical_map_size = chunks_in_logical_map * sizeof(uint64_t);
170 : :
171 : : /* Round up to next cacheline. */
172 : 24 : return spdk_divide_round_up(logical_map_size, REDUCE_PM_SIZE_ALIGNMENT) *
173 : : REDUCE_PM_SIZE_ALIGNMENT;
174 : : }
175 : :
176 : : static uint64_t
177 : 2753543 : _get_total_chunks(uint64_t vol_size, uint64_t chunk_size)
178 : : {
179 : : uint64_t num_chunks;
180 : :
181 [ - + ]: 2753543 : num_chunks = vol_size / chunk_size;
182 : 2753543 : num_chunks += REDUCE_NUM_EXTRA_CHUNKS;
183 : :
184 : 2753543 : return num_chunks;
185 : : }
186 : :
187 : : static inline uint32_t
188 : 3572443 : _reduce_vol_get_chunk_struct_size(uint64_t backing_io_units_per_chunk)
189 : : {
190 : 3572443 : return sizeof(struct spdk_reduce_chunk_map) + sizeof(uint64_t) * backing_io_units_per_chunk;
191 : : }
192 : :
193 : : static uint64_t
194 : 12 : _get_pm_total_chunks_size(uint64_t vol_size, uint64_t chunk_size, uint64_t backing_io_unit_size)
195 : : {
196 : : uint64_t io_units_per_chunk, num_chunks, total_chunks_size;
197 : :
198 : 12 : num_chunks = _get_total_chunks(vol_size, chunk_size);
199 [ - + ]: 12 : io_units_per_chunk = chunk_size / backing_io_unit_size;
200 : :
201 : 12 : total_chunks_size = num_chunks * _reduce_vol_get_chunk_struct_size(io_units_per_chunk);
202 : :
203 : 12 : return spdk_divide_round_up(total_chunks_size, REDUCE_PM_SIZE_ALIGNMENT) *
204 : : REDUCE_PM_SIZE_ALIGNMENT;
205 : : }
206 : :
207 : : static struct spdk_reduce_chunk_map *
208 : 2753519 : _reduce_vol_get_chunk_map(struct spdk_reduce_vol *vol, uint64_t chunk_map_index)
209 : : {
210 : : uintptr_t chunk_map_addr;
211 : :
212 [ - + ]: 2753519 : assert(chunk_map_index < _get_total_chunks(vol->params.vol_size, vol->params.chunk_size));
213 : :
214 : 2753519 : chunk_map_addr = (uintptr_t)vol->pm_chunk_maps;
215 : 2753519 : chunk_map_addr += chunk_map_index *
216 : 2753519 : _reduce_vol_get_chunk_struct_size(vol->backing_io_units_per_chunk);
217 : :
218 : 2753519 : return (struct spdk_reduce_chunk_map *)chunk_map_addr;
219 : : }
220 : :
221 : : static int
222 : 12 : _validate_vol_params(struct spdk_reduce_vol_params *params)
223 : : {
224 [ - + ]: 12 : if (params->vol_size > 0) {
225 : : /**
226 : : * User does not pass in the vol size - it gets calculated by libreduce from
227 : : * values in this structure plus the size of the backing device.
228 : : */
229 : 0 : return -EINVAL;
230 : : }
231 : :
232 [ + - + - ]: 12 : if (params->chunk_size == 0 || params->backing_io_unit_size == 0 ||
233 [ - + ]: 12 : params->logical_block_size == 0) {
234 : 0 : return -EINVAL;
235 : : }
236 : :
237 : : /* Chunk size must be an even multiple of the backing io unit size. */
238 [ - + - + ]: 12 : if ((params->chunk_size % params->backing_io_unit_size) != 0) {
239 : 0 : return -EINVAL;
240 : : }
241 : :
242 : : /* Chunk size must be an even multiple of the logical block size. */
243 [ - + - + ]: 12 : if ((params->chunk_size % params->logical_block_size) != 0) {
244 : 0 : return -1;
245 : : }
246 : :
247 : 12 : return 0;
248 : : }
249 : :
250 : : static uint64_t
251 : 12 : _get_vol_size(uint64_t chunk_size, uint64_t backing_dev_size)
252 : : {
253 : : uint64_t num_chunks;
254 : :
255 [ - + ]: 12 : num_chunks = backing_dev_size / chunk_size;
256 [ - + ]: 12 : if (num_chunks <= REDUCE_NUM_EXTRA_CHUNKS) {
257 : 0 : return 0;
258 : : }
259 : :
260 : 12 : num_chunks -= REDUCE_NUM_EXTRA_CHUNKS;
261 : 12 : return num_chunks * chunk_size;
262 : : }
263 : :
264 : : static uint64_t
265 : 12 : _get_pm_file_size(struct spdk_reduce_vol_params *params)
266 : : {
267 : : uint64_t total_pm_size;
268 : :
269 : 12 : total_pm_size = sizeof(struct spdk_reduce_vol_superblock);
270 : 12 : total_pm_size += _get_pm_logical_map_size(params->vol_size, params->chunk_size);
271 : 12 : total_pm_size += _get_pm_total_chunks_size(params->vol_size, params->chunk_size,
272 : 12 : params->backing_io_unit_size);
273 : 12 : return total_pm_size;
274 : : }
275 : :
276 : : const struct spdk_uuid *
277 : 0 : spdk_reduce_vol_get_uuid(struct spdk_reduce_vol *vol)
278 : : {
279 : 0 : return &vol->params.uuid;
280 : : }
281 : :
282 : : static void
283 : 12 : _initialize_vol_pm_pointers(struct spdk_reduce_vol *vol)
284 : : {
285 : : uint64_t logical_map_size;
286 : :
287 : : /* Superblock is at the beginning of the pm file. */
288 : 12 : vol->pm_super = (struct spdk_reduce_vol_superblock *)vol->pm_file.pm_buf;
289 : :
290 : : /* Logical map immediately follows the super block. */
291 : 12 : vol->pm_logical_map = (uint64_t *)(vol->pm_super + 1);
292 : :
293 : : /* Chunks maps follow the logical map. */
294 : 12 : logical_map_size = _get_pm_logical_map_size(vol->params.vol_size, vol->params.chunk_size);
295 : 12 : vol->pm_chunk_maps = (uint64_t *)((uint8_t *)vol->pm_logical_map + logical_map_size);
296 : 12 : }
297 : :
298 : : /* We need 2 iovs during load - one for the superblock, another for the path */
299 : : #define LOAD_IOV_COUNT 2
300 : :
301 : : struct reduce_init_load_ctx {
302 : : struct spdk_reduce_vol *vol;
303 : : struct spdk_reduce_vol_cb_args backing_cb_args;
304 : : spdk_reduce_vol_op_with_handle_complete cb_fn;
305 : : void *cb_arg;
306 : : struct iovec iov[LOAD_IOV_COUNT];
307 : : void *path;
308 : : };
309 : :
310 : : static inline bool
311 : 822508 : _addr_crosses_huge_page(const void *addr, size_t *size)
312 : : {
313 : : size_t _size;
314 : : uint64_t rc;
315 : :
316 [ - + ]: 822508 : assert(size);
317 : :
318 : 822508 : _size = *size;
319 : 822508 : rc = spdk_vtophys(addr, size);
320 : :
321 [ + - + + ]: 822508 : return rc == SPDK_VTOPHYS_ERROR || _size != *size;
322 : : }
323 : :
324 : : static inline int
325 : 6144 : _set_buffer(uint8_t **vol_buffer, uint8_t **_addr, uint8_t *addr_range, size_t buffer_size)
326 : : {
327 : : uint8_t *addr;
328 : 6144 : size_t size_tmp = buffer_size;
329 : :
330 : 6144 : addr = *_addr;
331 : :
332 : : /* Verify that addr + buffer_size doesn't cross huge page boundary */
333 [ - + ]: 6144 : if (_addr_crosses_huge_page(addr, &size_tmp)) {
334 : : /* Memory start is aligned on 2MiB, so buffer should be located at the end of the page.
335 : : * Skip remaining bytes and continue from the beginning of the next page */
336 : 0 : addr += size_tmp;
337 : : }
338 : :
339 [ - + ]: 6144 : if (addr + buffer_size > addr_range) {
340 : 0 : SPDK_ERRLOG("Vol buffer %p out of range %p\n", addr, addr_range);
341 : 0 : return -ERANGE;
342 : : }
343 : :
344 : 6144 : *vol_buffer = addr;
345 : 6144 : *_addr = addr + buffer_size;
346 : :
347 : 6144 : return 0;
348 : : }
349 : :
350 : : static int
351 : 12 : _allocate_vol_requests(struct spdk_reduce_vol *vol)
352 : : {
353 : : struct spdk_reduce_vol_request *req;
354 : : uint32_t reqs_in_2mb_page, huge_pages_needed;
355 : 12 : uint8_t *buffer, *buffer_end;
356 : 12 : int i = 0;
357 : 12 : int rc = 0;
358 : :
359 : : /* It is needed to allocate comp and decomp buffers so that they do not cross physical
360 : : * page boundaries. Assume that the system uses default 2MiB pages and chunk_size is not
361 : : * necessarily power of 2
362 : : * Allocate 2x since we need buffers for both read/write and compress/decompress
363 : : * intermediate buffers. */
364 [ - + ]: 12 : reqs_in_2mb_page = VALUE_2MB / (vol->params.chunk_size * 2);
365 [ - + ]: 12 : if (!reqs_in_2mb_page) {
366 : 0 : return -EINVAL;
367 : : }
368 [ - + ]: 12 : huge_pages_needed = SPDK_CEIL_DIV(REDUCE_NUM_VOL_REQUESTS, reqs_in_2mb_page);
369 : :
370 : 12 : vol->buf_mem = spdk_dma_malloc(VALUE_2MB * huge_pages_needed, VALUE_2MB, NULL);
371 [ - + ]: 12 : if (vol->buf_mem == NULL) {
372 : 0 : return -ENOMEM;
373 : : }
374 : :
375 : 12 : vol->request_mem = calloc(REDUCE_NUM_VOL_REQUESTS, sizeof(*req));
376 [ - + ]: 12 : if (vol->request_mem == NULL) {
377 : 0 : spdk_free(vol->buf_mem);
378 : 0 : vol->buf_mem = NULL;
379 : 0 : return -ENOMEM;
380 : : }
381 : :
382 : : /* Allocate 2x since we need iovs for both read/write and compress/decompress intermediate
383 : : * buffers.
384 : : */
385 : 12 : vol->buf_iov_mem = calloc(REDUCE_NUM_VOL_REQUESTS,
386 : 12 : 2 * sizeof(struct iovec) * vol->backing_io_units_per_chunk);
387 [ - + ]: 12 : if (vol->buf_iov_mem == NULL) {
388 : 0 : free(vol->request_mem);
389 : 0 : spdk_free(vol->buf_mem);
390 : 0 : vol->request_mem = NULL;
391 : 0 : vol->buf_mem = NULL;
392 : 0 : return -ENOMEM;
393 : : }
394 : :
395 : 12 : buffer = vol->buf_mem;
396 : 12 : buffer_end = buffer + VALUE_2MB * huge_pages_needed;
397 : :
398 [ + + ]: 3084 : for (i = 0; i < REDUCE_NUM_VOL_REQUESTS; i++) {
399 : 3072 : req = &vol->request_mem[i];
400 [ + + ]: 3072 : TAILQ_INSERT_HEAD(&vol->free_requests, req, tailq);
401 : 3072 : req->decomp_buf_iov = &vol->buf_iov_mem[(2 * i) * vol->backing_io_units_per_chunk];
402 : 3072 : req->comp_buf_iov = &vol->buf_iov_mem[(2 * i + 1) * vol->backing_io_units_per_chunk];
403 : :
404 : 3072 : rc = _set_buffer(&req->comp_buf, &buffer, buffer_end, vol->params.chunk_size);
405 [ - + ]: 3072 : if (rc) {
406 : 0 : SPDK_ERRLOG("Failed to set comp buffer for req idx %u, addr %p, start %p, end %p\n", i, buffer,
407 : : vol->buf_mem, buffer_end);
408 : 0 : break;
409 : : }
410 : 3072 : rc = _set_buffer(&req->decomp_buf, &buffer, buffer_end, vol->params.chunk_size);
411 [ - + ]: 3072 : if (rc) {
412 : 0 : SPDK_ERRLOG("Failed to set decomp buffer for req idx %u, addr %p, start %p, end %p\n", i, buffer,
413 : : vol->buf_mem, buffer_end);
414 : 0 : break;
415 : : }
416 : : }
417 : :
418 [ - + ]: 12 : if (rc) {
419 : 0 : free(vol->buf_iov_mem);
420 : 0 : free(vol->request_mem);
421 : 0 : spdk_free(vol->buf_mem);
422 : 0 : vol->buf_mem = NULL;
423 : 0 : vol->buf_iov_mem = NULL;
424 : 0 : vol->request_mem = NULL;
425 : : }
426 : :
427 : 12 : return rc;
428 : : }
429 : :
430 : : static void
431 : 1079 : _init_load_cleanup(struct spdk_reduce_vol *vol, struct reduce_init_load_ctx *ctx)
432 : : {
433 [ + + ]: 1079 : if (ctx != NULL) {
434 : 1055 : spdk_free(ctx->path);
435 : 1055 : free(ctx);
436 : : }
437 : :
438 [ + + ]: 1079 : if (vol != NULL) {
439 [ + + ]: 1055 : if (vol->pm_file.pm_buf != NULL) {
440 : 12 : pmem_unmap(vol->pm_file.pm_buf, vol->pm_file.size);
441 : : }
442 : :
443 : 1055 : spdk_free(vol->backing_super);
444 : 1055 : spdk_bit_array_free(&vol->allocated_chunk_maps);
445 : 1055 : spdk_bit_array_free(&vol->allocated_backing_io_units);
446 : 1055 : free(vol->request_mem);
447 : 1055 : free(vol->buf_iov_mem);
448 : 1055 : spdk_free(vol->buf_mem);
449 : 1055 : free(vol);
450 : : }
451 : 1079 : }
452 : :
453 : : static int
454 : 1055 : _alloc_zero_buff(void)
455 : : {
456 : 1055 : int rc = 0;
457 : :
458 : : /* The zero buffer is shared between all volumes and just used
459 : : * for reads so allocate one global instance here if not already
460 : : * allocated when another vol init'd or loaded.
461 : : */
462 [ + + ]: 1055 : if (g_vol_count++ == 0) {
463 : 132 : g_zero_buf = spdk_zmalloc(REDUCE_ZERO_BUF_SIZE,
464 : : 64, NULL, SPDK_ENV_LCORE_ID_ANY,
465 : : SPDK_MALLOC_DMA);
466 [ - + ]: 132 : if (g_zero_buf == NULL) {
467 : 0 : rc = -ENOMEM;
468 : : }
469 : : }
470 : 1055 : return rc;
471 : : }
472 : :
473 : : static void
474 : 12 : _init_write_super_cpl(void *cb_arg, int reduce_errno)
475 : : {
476 : 12 : struct reduce_init_load_ctx *init_ctx = cb_arg;
477 : : int rc;
478 : :
479 : 12 : rc = _allocate_vol_requests(init_ctx->vol);
480 [ - + ]: 12 : if (rc != 0) {
481 : 0 : init_ctx->cb_fn(init_ctx->cb_arg, NULL, rc);
482 : 0 : _init_load_cleanup(init_ctx->vol, init_ctx);
483 : 0 : return;
484 : : }
485 : :
486 : 12 : rc = _alloc_zero_buff();
487 [ - + ]: 12 : if (rc != 0) {
488 : 0 : init_ctx->cb_fn(init_ctx->cb_arg, NULL, rc);
489 : 0 : _init_load_cleanup(init_ctx->vol, init_ctx);
490 : 0 : return;
491 : : }
492 : :
493 : 12 : init_ctx->cb_fn(init_ctx->cb_arg, init_ctx->vol, reduce_errno);
494 : : /* Only clean up the ctx - the vol has been passed to the application
495 : : * for use now that initialization was successful.
496 : : */
497 : 12 : _init_load_cleanup(NULL, init_ctx);
498 : : }
499 : :
500 : : static void
501 : 12 : _init_write_path_cpl(void *cb_arg, int reduce_errno)
502 : : {
503 : 12 : struct reduce_init_load_ctx *init_ctx = cb_arg;
504 : 12 : struct spdk_reduce_vol *vol = init_ctx->vol;
505 : :
506 : 12 : init_ctx->iov[0].iov_base = vol->backing_super;
507 : 12 : init_ctx->iov[0].iov_len = sizeof(*vol->backing_super);
508 : 12 : init_ctx->backing_cb_args.cb_fn = _init_write_super_cpl;
509 : 12 : init_ctx->backing_cb_args.cb_arg = init_ctx;
510 : 24 : vol->backing_dev->writev(vol->backing_dev, init_ctx->iov, 1,
511 [ - + ]: 12 : 0, sizeof(*vol->backing_super) / vol->backing_dev->blocklen,
512 : : &init_ctx->backing_cb_args);
513 : 12 : }
514 : :
515 : : static int
516 : 12 : _allocate_bit_arrays(struct spdk_reduce_vol *vol)
517 : : {
518 : : uint64_t total_chunks, total_backing_io_units;
519 : : uint32_t i, num_metadata_io_units;
520 : :
521 : 12 : total_chunks = _get_total_chunks(vol->params.vol_size, vol->params.chunk_size);
522 : 12 : vol->allocated_chunk_maps = spdk_bit_array_create(total_chunks);
523 [ - + ]: 12 : total_backing_io_units = total_chunks * (vol->params.chunk_size / vol->params.backing_io_unit_size);
524 : 12 : vol->allocated_backing_io_units = spdk_bit_array_create(total_backing_io_units);
525 : :
526 [ + - - + ]: 12 : if (vol->allocated_chunk_maps == NULL || vol->allocated_backing_io_units == NULL) {
527 : 0 : return -ENOMEM;
528 : : }
529 : :
530 : : /* Set backing io unit bits associated with metadata. */
531 : 0 : num_metadata_io_units = (sizeof(*vol->backing_super) + REDUCE_PATH_MAX) /
532 [ - + ]: 12 : vol->backing_dev->blocklen;
533 [ + + ]: 204 : for (i = 0; i < num_metadata_io_units; i++) {
534 : 192 : spdk_bit_array_set(vol->allocated_backing_io_units, i);
535 : : }
536 : :
537 : 12 : return 0;
538 : : }
539 : :
540 : : void
541 : 12 : spdk_reduce_vol_init(struct spdk_reduce_vol_params *params,
542 : : struct spdk_reduce_backing_dev *backing_dev,
543 : : const char *pm_file_dir,
544 : : spdk_reduce_vol_op_with_handle_complete cb_fn, void *cb_arg)
545 : : {
546 : : struct spdk_reduce_vol *vol;
547 : : struct reduce_init_load_ctx *init_ctx;
548 : : uint64_t backing_dev_size;
549 : 12 : size_t mapped_len;
550 : : int dir_len, max_dir_len, rc;
551 : :
552 : : /* We need to append a path separator and the UUID to the supplied
553 : : * path.
554 : : */
555 : 12 : max_dir_len = REDUCE_PATH_MAX - SPDK_UUID_STRING_LEN - 1;
556 [ - + ]: 12 : dir_len = strnlen(pm_file_dir, max_dir_len);
557 : : /* Strip trailing slash if the user provided one - we will add it back
558 : : * later when appending the filename.
559 : : */
560 [ - + ]: 12 : if (pm_file_dir[dir_len - 1] == '/') {
561 : 0 : dir_len--;
562 : : }
563 [ - + ]: 12 : if (dir_len == max_dir_len) {
564 : 0 : SPDK_ERRLOG("pm_file_dir (%s) too long\n", pm_file_dir);
565 : 0 : cb_fn(cb_arg, NULL, -EINVAL);
566 : 0 : return;
567 : : }
568 : :
569 : 12 : rc = _validate_vol_params(params);
570 [ - + ]: 12 : if (rc != 0) {
571 : 0 : SPDK_ERRLOG("invalid vol params\n");
572 : 0 : cb_fn(cb_arg, NULL, rc);
573 : 0 : return;
574 : : }
575 : :
576 : 12 : backing_dev_size = backing_dev->blockcnt * backing_dev->blocklen;
577 : 12 : params->vol_size = _get_vol_size(params->chunk_size, backing_dev_size);
578 [ - + ]: 12 : if (params->vol_size == 0) {
579 : 0 : SPDK_ERRLOG("backing device is too small\n");
580 : 0 : cb_fn(cb_arg, NULL, -EINVAL);
581 : 0 : return;
582 : : }
583 : :
584 [ + - + - ]: 12 : if (backing_dev->readv == NULL || backing_dev->writev == NULL ||
585 [ - + ]: 12 : backing_dev->unmap == NULL) {
586 : 0 : SPDK_ERRLOG("backing_dev function pointer not specified\n");
587 : 0 : cb_fn(cb_arg, NULL, -EINVAL);
588 : 0 : return;
589 : : }
590 : :
591 : 12 : vol = calloc(1, sizeof(*vol));
592 [ - + ]: 12 : if (vol == NULL) {
593 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
594 : 0 : return;
595 : : }
596 : :
597 : 12 : TAILQ_INIT(&vol->free_requests);
598 : 12 : TAILQ_INIT(&vol->executing_requests);
599 : 12 : TAILQ_INIT(&vol->queued_requests);
600 : :
601 : 12 : vol->backing_super = spdk_zmalloc(sizeof(*vol->backing_super), 0, NULL,
602 : : SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
603 [ - + ]: 12 : if (vol->backing_super == NULL) {
604 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
605 : 0 : _init_load_cleanup(vol, NULL);
606 : 0 : return;
607 : : }
608 : :
609 : 12 : init_ctx = calloc(1, sizeof(*init_ctx));
610 [ - + ]: 12 : if (init_ctx == NULL) {
611 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
612 : 0 : _init_load_cleanup(vol, NULL);
613 : 0 : return;
614 : : }
615 : :
616 : 12 : init_ctx->path = spdk_zmalloc(REDUCE_PATH_MAX, 0, NULL,
617 : : SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
618 [ - + ]: 12 : if (init_ctx->path == NULL) {
619 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
620 : 0 : _init_load_cleanup(vol, init_ctx);
621 : 0 : return;
622 : : }
623 : :
624 [ + - ]: 12 : if (spdk_uuid_is_null(¶ms->uuid)) {
625 : 12 : spdk_uuid_generate(¶ms->uuid);
626 : : }
627 : :
628 [ - + - + ]: 12 : memcpy(vol->pm_file.path, pm_file_dir, dir_len);
629 : 12 : vol->pm_file.path[dir_len] = '/';
630 : 12 : spdk_uuid_fmt_lower(&vol->pm_file.path[dir_len + 1], SPDK_UUID_STRING_LEN,
631 : 12 : ¶ms->uuid);
632 : 12 : vol->pm_file.size = _get_pm_file_size(params);
633 : 12 : vol->pm_file.pm_buf = pmem_map_file(vol->pm_file.path, vol->pm_file.size,
634 : : PMEM_FILE_CREATE | PMEM_FILE_EXCL, 0600,
635 : : &mapped_len, &vol->pm_file.pm_is_pmem);
636 [ - + ]: 12 : if (vol->pm_file.pm_buf == NULL) {
637 : 0 : SPDK_ERRLOG("could not pmem_map_file(%s): %s\n",
638 : : vol->pm_file.path, strerror(errno));
639 : 0 : cb_fn(cb_arg, NULL, -errno);
640 : 0 : _init_load_cleanup(vol, init_ctx);
641 : 0 : return;
642 : : }
643 : :
644 [ - + ]: 12 : if (vol->pm_file.size != mapped_len) {
645 : 0 : SPDK_ERRLOG("could not map entire pmem file (size=%" PRIu64 " mapped=%" PRIu64 ")\n",
646 : : vol->pm_file.size, mapped_len);
647 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
648 : 0 : _init_load_cleanup(vol, init_ctx);
649 : 0 : return;
650 : : }
651 : :
652 [ - + ]: 12 : vol->backing_io_units_per_chunk = params->chunk_size / params->backing_io_unit_size;
653 [ - + ]: 12 : vol->logical_blocks_per_chunk = params->chunk_size / params->logical_block_size;
654 [ - + ]: 12 : vol->backing_lba_per_io_unit = params->backing_io_unit_size / backing_dev->blocklen;
655 [ - + - + ]: 12 : memcpy(&vol->params, params, sizeof(*params));
656 : :
657 : 12 : vol->backing_dev = backing_dev;
658 : :
659 : 12 : rc = _allocate_bit_arrays(vol);
660 [ - + ]: 12 : if (rc != 0) {
661 : 0 : cb_fn(cb_arg, NULL, rc);
662 : 0 : _init_load_cleanup(vol, init_ctx);
663 : 0 : return;
664 : : }
665 : :
666 [ - + ]: 12 : memcpy(vol->backing_super->signature, SPDK_REDUCE_SIGNATURE,
667 : : sizeof(vol->backing_super->signature));
668 [ - + - + ]: 12 : memcpy(&vol->backing_super->params, params, sizeof(*params));
669 : :
670 : 12 : _initialize_vol_pm_pointers(vol);
671 : :
672 [ - + - + ]: 12 : memcpy(vol->pm_super, vol->backing_super, sizeof(*vol->backing_super));
673 : : /* Writing 0xFF's is equivalent of filling it all with SPDK_EMPTY_MAP_ENTRY.
674 : : * Note that this writes 0xFF to not just the logical map but the chunk maps as well.
675 : : */
676 [ - + ]: 12 : memset(vol->pm_logical_map, 0xFF, vol->pm_file.size - sizeof(*vol->backing_super));
677 : 12 : _reduce_persist(vol, vol->pm_file.pm_buf, vol->pm_file.size);
678 : :
679 : 12 : init_ctx->vol = vol;
680 : 12 : init_ctx->cb_fn = cb_fn;
681 : 12 : init_ctx->cb_arg = cb_arg;
682 : :
683 [ - + - + ]: 12 : memcpy(init_ctx->path, vol->pm_file.path, REDUCE_PATH_MAX);
684 : 12 : init_ctx->iov[0].iov_base = init_ctx->path;
685 : 12 : init_ctx->iov[0].iov_len = REDUCE_PATH_MAX;
686 : 12 : init_ctx->backing_cb_args.cb_fn = _init_write_path_cpl;
687 : 12 : init_ctx->backing_cb_args.cb_arg = init_ctx;
688 : : /* Write path to offset 4K on backing device - just after where the super
689 : : * block will be written. We wait until this is committed before writing the
690 : : * super block to guarantee we don't get the super block written without the
691 : : * the path if the system crashed in the middle of a write operation.
692 : : */
693 : 24 : vol->backing_dev->writev(vol->backing_dev, init_ctx->iov, 1,
694 [ - + ]: 12 : REDUCE_BACKING_DEV_PATH_OFFSET / vol->backing_dev->blocklen,
695 [ - + ]: 12 : REDUCE_PATH_MAX / vol->backing_dev->blocklen,
696 : : &init_ctx->backing_cb_args);
697 : : }
698 : :
699 : : static void destroy_load_cb(void *cb_arg, struct spdk_reduce_vol *vol, int reduce_errno);
700 : :
701 : : static void
702 : 1043 : _load_read_super_and_path_cpl(void *cb_arg, int reduce_errno)
703 : : {
704 : 1043 : struct reduce_init_load_ctx *load_ctx = cb_arg;
705 : 1043 : struct spdk_reduce_vol *vol = load_ctx->vol;
706 : : uint64_t backing_dev_size;
707 : : uint64_t i, num_chunks, logical_map_index;
708 : : struct spdk_reduce_chunk_map *chunk;
709 : 1043 : size_t mapped_len;
710 : : uint32_t j;
711 : : int rc;
712 : :
713 : 1043 : rc = _alloc_zero_buff();
714 [ - + ]: 1043 : if (rc) {
715 : 0 : goto error;
716 : : }
717 : :
718 [ - + + + ]: 1043 : if (memcmp(vol->backing_super->signature,
719 : : SPDK_REDUCE_SIGNATURE,
720 : : sizeof(vol->backing_super->signature)) != 0) {
721 : : /* This backing device isn't a libreduce backing device. */
722 : 1031 : rc = -EILSEQ;
723 : 1031 : goto error;
724 : : }
725 : :
726 : : /* If the cb_fn is destroy_load_cb, it means we are wanting to destroy this compress bdev.
727 : : * So don't bother getting the volume ready to use - invoke the callback immediately
728 : : * so destroy_load_cb can delete the metadata off of the block device and delete the
729 : : * persistent memory file if it exists.
730 : : */
731 [ - + - + ]: 12 : memcpy(vol->pm_file.path, load_ctx->path, sizeof(vol->pm_file.path));
732 [ + - ]: 12 : if (load_ctx->cb_fn == (*destroy_load_cb)) {
733 : 12 : load_ctx->cb_fn(load_ctx->cb_arg, vol, 0);
734 : 12 : _init_load_cleanup(NULL, load_ctx);
735 : 12 : return;
736 : : }
737 : :
738 [ # # # # ]: 0 : memcpy(&vol->params, &vol->backing_super->params, sizeof(vol->params));
739 [ # # ]: 0 : vol->backing_io_units_per_chunk = vol->params.chunk_size / vol->params.backing_io_unit_size;
740 [ # # ]: 0 : vol->logical_blocks_per_chunk = vol->params.chunk_size / vol->params.logical_block_size;
741 [ # # ]: 0 : vol->backing_lba_per_io_unit = vol->params.backing_io_unit_size / vol->backing_dev->blocklen;
742 : :
743 : 0 : rc = _allocate_bit_arrays(vol);
744 [ # # ]: 0 : if (rc != 0) {
745 : 0 : goto error;
746 : : }
747 : :
748 : 0 : backing_dev_size = vol->backing_dev->blockcnt * vol->backing_dev->blocklen;
749 [ # # ]: 0 : if (_get_vol_size(vol->params.chunk_size, backing_dev_size) < vol->params.vol_size) {
750 : 0 : SPDK_ERRLOG("backing device size %" PRIi64 " smaller than expected\n",
751 : : backing_dev_size);
752 : 0 : rc = -EILSEQ;
753 : 0 : goto error;
754 : : }
755 : :
756 : 0 : vol->pm_file.size = _get_pm_file_size(&vol->params);
757 : 0 : vol->pm_file.pm_buf = pmem_map_file(vol->pm_file.path, 0, 0, 0, &mapped_len,
758 : : &vol->pm_file.pm_is_pmem);
759 [ # # ]: 0 : if (vol->pm_file.pm_buf == NULL) {
760 : 0 : SPDK_ERRLOG("could not pmem_map_file(%s): %s\n", vol->pm_file.path, strerror(errno));
761 : 0 : rc = -errno;
762 : 0 : goto error;
763 : : }
764 : :
765 [ # # ]: 0 : if (vol->pm_file.size != mapped_len) {
766 : 0 : SPDK_ERRLOG("could not map entire pmem file (size=%" PRIu64 " mapped=%" PRIu64 ")\n",
767 : : vol->pm_file.size, mapped_len);
768 : 0 : rc = -ENOMEM;
769 : 0 : goto error;
770 : : }
771 : :
772 : 0 : rc = _allocate_vol_requests(vol);
773 [ # # ]: 0 : if (rc != 0) {
774 : 0 : goto error;
775 : : }
776 : :
777 : 0 : _initialize_vol_pm_pointers(vol);
778 : :
779 [ # # ]: 0 : num_chunks = vol->params.vol_size / vol->params.chunk_size;
780 [ # # ]: 0 : for (i = 0; i < num_chunks; i++) {
781 : 0 : logical_map_index = vol->pm_logical_map[i];
782 [ # # ]: 0 : if (logical_map_index == REDUCE_EMPTY_MAP_ENTRY) {
783 : 0 : continue;
784 : : }
785 : 0 : spdk_bit_array_set(vol->allocated_chunk_maps, logical_map_index);
786 : 0 : chunk = _reduce_vol_get_chunk_map(vol, logical_map_index);
787 [ # # ]: 0 : for (j = 0; j < vol->backing_io_units_per_chunk; j++) {
788 [ # # ]: 0 : if (chunk->io_unit_index[j] != REDUCE_EMPTY_MAP_ENTRY) {
789 : 0 : spdk_bit_array_set(vol->allocated_backing_io_units, chunk->io_unit_index[j]);
790 : : }
791 : : }
792 : : }
793 : :
794 : 0 : load_ctx->cb_fn(load_ctx->cb_arg, vol, 0);
795 : : /* Only clean up the ctx - the vol has been passed to the application
796 : : * for use now that volume load was successful.
797 : : */
798 : 0 : _init_load_cleanup(NULL, load_ctx);
799 : 0 : return;
800 : :
801 : 1031 : error:
802 : 1031 : load_ctx->cb_fn(load_ctx->cb_arg, NULL, rc);
803 : 1031 : _init_load_cleanup(vol, load_ctx);
804 : : }
805 : :
806 : : void
807 : 1043 : spdk_reduce_vol_load(struct spdk_reduce_backing_dev *backing_dev,
808 : : spdk_reduce_vol_op_with_handle_complete cb_fn, void *cb_arg)
809 : : {
810 : : struct spdk_reduce_vol *vol;
811 : : struct reduce_init_load_ctx *load_ctx;
812 : :
813 [ + - + - ]: 1043 : if (backing_dev->readv == NULL || backing_dev->writev == NULL ||
814 [ - + ]: 1043 : backing_dev->unmap == NULL) {
815 : 0 : SPDK_ERRLOG("backing_dev function pointer not specified\n");
816 : 0 : cb_fn(cb_arg, NULL, -EINVAL);
817 : 0 : return;
818 : : }
819 : :
820 : 1043 : vol = calloc(1, sizeof(*vol));
821 [ - + ]: 1043 : if (vol == NULL) {
822 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
823 : 0 : return;
824 : : }
825 : :
826 : 1043 : TAILQ_INIT(&vol->free_requests);
827 : 1043 : TAILQ_INIT(&vol->executing_requests);
828 : 1043 : TAILQ_INIT(&vol->queued_requests);
829 : :
830 : 1043 : vol->backing_super = spdk_zmalloc(sizeof(*vol->backing_super), 64, NULL,
831 : : SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
832 [ - + ]: 1043 : if (vol->backing_super == NULL) {
833 : 0 : _init_load_cleanup(vol, NULL);
834 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
835 : 0 : return;
836 : : }
837 : :
838 : 1043 : vol->backing_dev = backing_dev;
839 : :
840 : 1043 : load_ctx = calloc(1, sizeof(*load_ctx));
841 [ - + ]: 1043 : if (load_ctx == NULL) {
842 : 0 : _init_load_cleanup(vol, NULL);
843 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
844 : 0 : return;
845 : : }
846 : :
847 : 1043 : load_ctx->path = spdk_zmalloc(REDUCE_PATH_MAX, 64, NULL,
848 : : SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
849 [ - + ]: 1043 : if (load_ctx->path == NULL) {
850 : 0 : _init_load_cleanup(vol, load_ctx);
851 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
852 : 0 : return;
853 : : }
854 : :
855 : 1043 : load_ctx->vol = vol;
856 : 1043 : load_ctx->cb_fn = cb_fn;
857 : 1043 : load_ctx->cb_arg = cb_arg;
858 : :
859 : 1043 : load_ctx->iov[0].iov_base = vol->backing_super;
860 : 1043 : load_ctx->iov[0].iov_len = sizeof(*vol->backing_super);
861 : 1043 : load_ctx->iov[1].iov_base = load_ctx->path;
862 : 1043 : load_ctx->iov[1].iov_len = REDUCE_PATH_MAX;
863 : 1043 : load_ctx->backing_cb_args.cb_fn = _load_read_super_and_path_cpl;
864 : 1043 : load_ctx->backing_cb_args.cb_arg = load_ctx;
865 : 2086 : vol->backing_dev->readv(vol->backing_dev, load_ctx->iov, LOAD_IOV_COUNT, 0,
866 : 0 : (sizeof(*vol->backing_super) + REDUCE_PATH_MAX) /
867 [ - + ]: 1043 : vol->backing_dev->blocklen,
868 : : &load_ctx->backing_cb_args);
869 : : }
870 : :
871 : : void
872 : 24 : spdk_reduce_vol_unload(struct spdk_reduce_vol *vol,
873 : : spdk_reduce_vol_op_complete cb_fn, void *cb_arg)
874 : : {
875 [ - + ]: 24 : if (vol == NULL) {
876 : : /* This indicates a programming error. */
877 : 0 : assert(false);
878 : : cb_fn(cb_arg, -EINVAL);
879 : : return;
880 : : }
881 : :
882 [ - + ]: 24 : if (--g_vol_count == 0) {
883 : 0 : spdk_free(g_zero_buf);
884 : : }
885 [ - + ]: 24 : assert(g_vol_count >= 0);
886 : 24 : _init_load_cleanup(vol, NULL);
887 : 24 : cb_fn(cb_arg, 0);
888 : : }
889 : :
890 : : struct reduce_destroy_ctx {
891 : : spdk_reduce_vol_op_complete cb_fn;
892 : : void *cb_arg;
893 : : struct spdk_reduce_vol *vol;
894 : : struct spdk_reduce_vol_superblock *super;
895 : : struct iovec iov;
896 : : struct spdk_reduce_vol_cb_args backing_cb_args;
897 : : int reduce_errno;
898 : : char pm_path[REDUCE_PATH_MAX];
899 : : };
900 : :
901 : : static void
902 : 12 : destroy_unload_cpl(void *cb_arg, int reduce_errno)
903 : : {
904 : 12 : struct reduce_destroy_ctx *destroy_ctx = cb_arg;
905 : :
906 [ + - ]: 12 : if (destroy_ctx->reduce_errno == 0) {
907 [ - + - + ]: 12 : if (unlink(destroy_ctx->pm_path)) {
908 : 0 : SPDK_ERRLOG("%s could not be unlinked: %s\n",
909 : : destroy_ctx->pm_path, strerror(errno));
910 : : }
911 : : }
912 : :
913 : : /* Even if the unload somehow failed, we still pass the destroy_ctx
914 : : * reduce_errno since that indicates whether or not the volume was
915 : : * actually destroyed.
916 : : */
917 : 12 : destroy_ctx->cb_fn(destroy_ctx->cb_arg, destroy_ctx->reduce_errno);
918 : 12 : spdk_free(destroy_ctx->super);
919 : 12 : free(destroy_ctx);
920 : 12 : }
921 : :
922 : : static void
923 : 12 : _destroy_zero_super_cpl(void *cb_arg, int reduce_errno)
924 : : {
925 : 12 : struct reduce_destroy_ctx *destroy_ctx = cb_arg;
926 : 12 : struct spdk_reduce_vol *vol = destroy_ctx->vol;
927 : :
928 : 12 : destroy_ctx->reduce_errno = reduce_errno;
929 : 12 : spdk_reduce_vol_unload(vol, destroy_unload_cpl, destroy_ctx);
930 : 12 : }
931 : :
932 : : static void
933 : 12 : destroy_load_cb(void *cb_arg, struct spdk_reduce_vol *vol, int reduce_errno)
934 : : {
935 : 12 : struct reduce_destroy_ctx *destroy_ctx = cb_arg;
936 : :
937 [ - + ]: 12 : if (reduce_errno != 0) {
938 : 0 : destroy_ctx->cb_fn(destroy_ctx->cb_arg, reduce_errno);
939 : 0 : spdk_free(destroy_ctx->super);
940 : 0 : free(destroy_ctx);
941 : 0 : return;
942 : : }
943 : :
944 : 12 : destroy_ctx->vol = vol;
945 [ - + - + ]: 12 : memcpy(destroy_ctx->pm_path, vol->pm_file.path, sizeof(destroy_ctx->pm_path));
946 : 12 : destroy_ctx->iov.iov_base = destroy_ctx->super;
947 : 12 : destroy_ctx->iov.iov_len = sizeof(*destroy_ctx->super);
948 : 12 : destroy_ctx->backing_cb_args.cb_fn = _destroy_zero_super_cpl;
949 : 12 : destroy_ctx->backing_cb_args.cb_arg = destroy_ctx;
950 : 24 : vol->backing_dev->writev(vol->backing_dev, &destroy_ctx->iov, 1, 0,
951 [ - + ]: 12 : sizeof(*destroy_ctx->super) / vol->backing_dev->blocklen,
952 : : &destroy_ctx->backing_cb_args);
953 : : }
954 : :
955 : : void
956 : 12 : spdk_reduce_vol_destroy(struct spdk_reduce_backing_dev *backing_dev,
957 : : spdk_reduce_vol_op_complete cb_fn, void *cb_arg)
958 : : {
959 : : struct reduce_destroy_ctx *destroy_ctx;
960 : :
961 : 12 : destroy_ctx = calloc(1, sizeof(*destroy_ctx));
962 [ - + ]: 12 : if (destroy_ctx == NULL) {
963 : 0 : cb_fn(cb_arg, -ENOMEM);
964 : 0 : return;
965 : : }
966 : :
967 : 12 : destroy_ctx->super = spdk_zmalloc(sizeof(*destroy_ctx->super), 64, NULL,
968 : : SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
969 [ - + ]: 12 : if (destroy_ctx->super == NULL) {
970 : 0 : free(destroy_ctx);
971 : 0 : cb_fn(cb_arg, -ENOMEM);
972 : 0 : return;
973 : : }
974 : 12 : destroy_ctx->cb_fn = cb_fn;
975 : 12 : destroy_ctx->cb_arg = cb_arg;
976 : 12 : spdk_reduce_vol_load(backing_dev, destroy_load_cb, destroy_ctx);
977 : : }
978 : :
979 : : static bool
980 : 1639554 : _request_spans_chunk_boundary(struct spdk_reduce_vol *vol, uint64_t offset, uint64_t length)
981 : : {
982 : : uint64_t start_chunk, end_chunk;
983 : :
984 [ - + ]: 1639554 : start_chunk = offset / vol->logical_blocks_per_chunk;
985 [ - + ]: 1639554 : end_chunk = (offset + length - 1) / vol->logical_blocks_per_chunk;
986 : :
987 : 1639554 : return (start_chunk != end_chunk);
988 : : }
989 : :
990 : : typedef void (*reduce_request_fn)(void *_req, int reduce_errno);
991 : :
992 : : static void
993 : 1627059 : _reduce_vol_complete_req(struct spdk_reduce_vol_request *req, int reduce_errno)
994 : : {
995 : : struct spdk_reduce_vol_request *next_req;
996 : 1627059 : struct spdk_reduce_vol *vol = req->vol;
997 : :
998 : 1627059 : req->cb_fn(req->cb_arg, reduce_errno);
999 [ + + ]: 1627059 : TAILQ_REMOVE(&vol->executing_requests, req, tailq);
1000 : :
1001 [ + + ]: 6721168 : TAILQ_FOREACH(next_req, &vol->queued_requests, tailq) {
1002 [ + + ]: 5632224 : if (next_req->logical_map_index == req->logical_map_index) {
1003 [ + + ]: 538115 : TAILQ_REMOVE(&vol->queued_requests, next_req, tailq);
1004 [ + + ]: 538115 : if (next_req->type == REDUCE_IO_READV) {
1005 : 440098 : _start_readv_request(next_req);
1006 : : } else {
1007 [ - + ]: 98017 : assert(next_req->type == REDUCE_IO_WRITEV);
1008 : 98017 : _start_writev_request(next_req);
1009 : : }
1010 : 538115 : break;
1011 : : }
1012 : : }
1013 : :
1014 [ + - ]: 1627059 : TAILQ_INSERT_HEAD(&vol->free_requests, req, tailq);
1015 : 1627059 : }
1016 : :
1017 : : static void
1018 : 818912 : _write_write_done(void *_req, int reduce_errno)
1019 : : {
1020 : 818912 : struct spdk_reduce_vol_request *req = _req;
1021 : 818912 : struct spdk_reduce_vol *vol = req->vol;
1022 : : uint64_t old_chunk_map_index;
1023 : : struct spdk_reduce_chunk_map *old_chunk;
1024 : : uint32_t i;
1025 : :
1026 [ - + ]: 818912 : if (reduce_errno != 0) {
1027 : 0 : req->reduce_errno = reduce_errno;
1028 : : }
1029 : :
1030 [ - + ]: 818912 : assert(req->num_backing_ops > 0);
1031 [ - + ]: 818912 : if (--req->num_backing_ops > 0) {
1032 : 0 : return;
1033 : : }
1034 : :
1035 [ - + ]: 818912 : if (req->reduce_errno != 0) {
1036 : 0 : _reduce_vol_complete_req(req, req->reduce_errno);
1037 : 0 : return;
1038 : : }
1039 : :
1040 : 818912 : old_chunk_map_index = vol->pm_logical_map[req->logical_map_index];
1041 [ + + ]: 818912 : if (old_chunk_map_index != REDUCE_EMPTY_MAP_ENTRY) {
1042 : 760826 : old_chunk = _reduce_vol_get_chunk_map(vol, old_chunk_map_index);
1043 [ + - ]: 1521652 : for (i = 0; i < vol->backing_io_units_per_chunk; i++) {
1044 [ + + ]: 1521652 : if (old_chunk->io_unit_index[i] == REDUCE_EMPTY_MAP_ENTRY) {
1045 : 760826 : break;
1046 : : }
1047 [ - + ]: 760826 : assert(spdk_bit_array_get(vol->allocated_backing_io_units, old_chunk->io_unit_index[i]) == true);
1048 : 760826 : spdk_bit_array_clear(vol->allocated_backing_io_units, old_chunk->io_unit_index[i]);
1049 : 760826 : old_chunk->io_unit_index[i] = REDUCE_EMPTY_MAP_ENTRY;
1050 : : }
1051 : 760826 : spdk_bit_array_clear(vol->allocated_chunk_maps, old_chunk_map_index);
1052 : : }
1053 : :
1054 : : /*
1055 : : * We don't need to persist the clearing of the old chunk map here. The old chunk map
1056 : : * becomes invalid after we update the logical map, since the old chunk map will no
1057 : : * longer have a reference to it in the logical map.
1058 : : */
1059 : :
1060 : : /* Persist the new chunk map. This must be persisted before we update the logical map. */
1061 : 818912 : _reduce_persist(vol, req->chunk,
1062 : 818912 : _reduce_vol_get_chunk_struct_size(vol->backing_io_units_per_chunk));
1063 : :
1064 : 818912 : vol->pm_logical_map[req->logical_map_index] = req->chunk_map_index;
1065 : :
1066 : 818912 : _reduce_persist(vol, &vol->pm_logical_map[req->logical_map_index], sizeof(uint64_t));
1067 : :
1068 : 818912 : _reduce_vol_complete_req(req, 0);
1069 : : }
1070 : :
1071 : : static void
1072 : 1992693 : _issue_backing_ops(struct spdk_reduce_vol_request *req, struct spdk_reduce_vol *vol,
1073 : : reduce_request_fn next_fn, bool is_write)
1074 : : {
1075 : : struct iovec *iov;
1076 : : uint8_t *buf;
1077 : : uint32_t i;
1078 : :
1079 [ - + + - ]: 1992693 : if (req->chunk_is_compressed) {
1080 : 1992693 : iov = req->comp_buf_iov;
1081 : 1992693 : buf = req->comp_buf;
1082 : : } else {
1083 : 0 : iov = req->decomp_buf_iov;
1084 : 0 : buf = req->decomp_buf;
1085 : : }
1086 : :
1087 : 1992693 : req->num_backing_ops = req->num_io_units;
1088 : 1992693 : req->backing_cb_args.cb_fn = next_fn;
1089 : 1992693 : req->backing_cb_args.cb_arg = req;
1090 [ + + ]: 3985386 : for (i = 0; i < req->num_io_units; i++) {
1091 : 1992693 : iov[i].iov_base = buf + i * vol->params.backing_io_unit_size;
1092 : 1992693 : iov[i].iov_len = vol->params.backing_io_unit_size;
1093 [ + + ]: 1992693 : if (is_write) {
1094 : 1637824 : vol->backing_dev->writev(vol->backing_dev, &iov[i], 1,
1095 : 818912 : req->chunk->io_unit_index[i] * vol->backing_lba_per_io_unit,
1096 : : vol->backing_lba_per_io_unit, &req->backing_cb_args);
1097 : : } else {
1098 : 2347562 : vol->backing_dev->readv(vol->backing_dev, &iov[i], 1,
1099 : 1173781 : req->chunk->io_unit_index[i] * vol->backing_lba_per_io_unit,
1100 : : vol->backing_lba_per_io_unit, &req->backing_cb_args);
1101 : : }
1102 : : }
1103 : 1992693 : }
1104 : :
1105 : : static void
1106 : 818912 : _reduce_vol_write_chunk(struct spdk_reduce_vol_request *req, reduce_request_fn next_fn,
1107 : : uint32_t compressed_size)
1108 : : {
1109 : 818912 : struct spdk_reduce_vol *vol = req->vol;
1110 : : uint32_t i;
1111 : 818912 : uint64_t chunk_offset, remainder, total_len = 0;
1112 : : uint8_t *buf;
1113 : : int j;
1114 : :
1115 : 818912 : req->chunk_map_index = spdk_bit_array_find_first_clear(vol->allocated_chunk_maps, 0);
1116 : :
1117 : : /* TODO: fail if no chunk map found - but really this should not happen if we
1118 : : * size the number of requests similarly to number of extra chunk maps
1119 : : */
1120 [ - + ]: 818912 : assert(req->chunk_map_index != UINT32_MAX);
1121 : 818912 : spdk_bit_array_set(vol->allocated_chunk_maps, req->chunk_map_index);
1122 : :
1123 : 818912 : req->chunk = _reduce_vol_get_chunk_map(vol, req->chunk_map_index);
1124 : 818912 : req->num_io_units = spdk_divide_round_up(compressed_size,
1125 : 818912 : vol->params.backing_io_unit_size);
1126 : 818912 : req->chunk_is_compressed = (req->num_io_units != vol->backing_io_units_per_chunk);
1127 : 818912 : req->chunk->compressed_size =
1128 [ - + + - ]: 818912 : req->chunk_is_compressed ? compressed_size : vol->params.chunk_size;
1129 : :
1130 : : /* if the chunk is uncompressed we need to copy the data from the host buffers. */
1131 [ - + - + ]: 818912 : if (req->chunk_is_compressed == false) {
1132 [ # # ]: 0 : chunk_offset = req->offset % vol->logical_blocks_per_chunk;
1133 : 0 : buf = req->decomp_buf;
1134 : 0 : total_len = chunk_offset * vol->params.logical_block_size;
1135 : :
1136 : : /* zero any offset into chunk */
1137 [ # # # # : 0 : if (req->rmw == false && chunk_offset) {
# # ]
1138 [ # # ]: 0 : memset(buf, 0, total_len);
1139 : : }
1140 : 0 : buf += total_len;
1141 : :
1142 : : /* copy the data */
1143 [ # # ]: 0 : for (j = 0; j < req->iovcnt; j++) {
1144 [ # # # # ]: 0 : memcpy(buf, req->iov[j].iov_base, req->iov[j].iov_len);
1145 : 0 : buf += req->iov[j].iov_len;
1146 : 0 : total_len += req->iov[j].iov_len;
1147 : : }
1148 : :
1149 : : /* zero any remainder */
1150 : 0 : remainder = vol->params.chunk_size - total_len;
1151 : 0 : total_len += remainder;
1152 [ # # # # : 0 : if (req->rmw == false && remainder) {
# # ]
1153 [ # # ]: 0 : memset(buf, 0, remainder);
1154 : : }
1155 [ # # ]: 0 : assert(total_len == vol->params.chunk_size);
1156 : : }
1157 : :
1158 [ + + ]: 1637824 : for (i = 0; i < req->num_io_units; i++) {
1159 : 818912 : req->chunk->io_unit_index[i] = spdk_bit_array_find_first_clear(vol->allocated_backing_io_units, 0);
1160 : : /* TODO: fail if no backing block found - but really this should also not
1161 : : * happen (see comment above).
1162 : : */
1163 [ - + ]: 818912 : assert(req->chunk->io_unit_index[i] != UINT32_MAX);
1164 : 818912 : spdk_bit_array_set(vol->allocated_backing_io_units, req->chunk->io_unit_index[i]);
1165 : : }
1166 : :
1167 : 818912 : _issue_backing_ops(req, vol, next_fn, true /* write */);
1168 : 818912 : }
1169 : :
1170 : : static void
1171 : 818912 : _write_compress_done(void *_req, int reduce_errno)
1172 : : {
1173 : 818912 : struct spdk_reduce_vol_request *req = _req;
1174 : :
1175 : : /* Negative reduce_errno indicates failure for compression operations.
1176 : : * Just write the uncompressed data instead. Force this to happen
1177 : : * by just passing the full chunk size to _reduce_vol_write_chunk.
1178 : : * When it sees the data couldn't be compressed, it will just write
1179 : : * the uncompressed buffer to disk.
1180 : : */
1181 [ - + ]: 818912 : if (reduce_errno < 0) {
1182 : 0 : req->backing_cb_args.output_size = req->vol->params.chunk_size;
1183 : : }
1184 : :
1185 : 818912 : _reduce_vol_write_chunk(req, _write_write_done, req->backing_cb_args.output_size);
1186 : 818912 : }
1187 : :
1188 : : static void
1189 : 818912 : _reduce_vol_compress_chunk(struct spdk_reduce_vol_request *req, reduce_request_fn next_fn)
1190 : : {
1191 : 818912 : struct spdk_reduce_vol *vol = req->vol;
1192 : :
1193 : 818912 : req->backing_cb_args.cb_fn = next_fn;
1194 : 818912 : req->backing_cb_args.cb_arg = req;
1195 : 818912 : req->comp_buf_iov[0].iov_base = req->comp_buf;
1196 : 818912 : req->comp_buf_iov[0].iov_len = vol->params.chunk_size;
1197 : 1637824 : vol->backing_dev->compress(vol->backing_dev,
1198 : 818912 : req->decomp_iov, req->decomp_iovcnt, req->comp_buf_iov, 1,
1199 : : &req->backing_cb_args);
1200 : 818912 : }
1201 : :
1202 : : static void
1203 : 365634 : _reduce_vol_decompress_chunk_scratch(struct spdk_reduce_vol_request *req, reduce_request_fn next_fn)
1204 : : {
1205 : 365634 : struct spdk_reduce_vol *vol = req->vol;
1206 : :
1207 : 365634 : req->backing_cb_args.cb_fn = next_fn;
1208 : 365634 : req->backing_cb_args.cb_arg = req;
1209 : 365634 : req->comp_buf_iov[0].iov_base = req->comp_buf;
1210 : 365634 : req->comp_buf_iov[0].iov_len = req->chunk->compressed_size;
1211 : 365634 : req->decomp_buf_iov[0].iov_base = req->decomp_buf;
1212 : 365634 : req->decomp_buf_iov[0].iov_len = vol->params.chunk_size;
1213 : 365634 : vol->backing_dev->decompress(vol->backing_dev,
1214 : : req->comp_buf_iov, 1, req->decomp_buf_iov, 1,
1215 : : &req->backing_cb_args);
1216 : 365634 : }
1217 : :
1218 : : static void
1219 : 808147 : _reduce_vol_decompress_chunk(struct spdk_reduce_vol_request *req, reduce_request_fn next_fn)
1220 : : {
1221 : 808147 : struct spdk_reduce_vol *vol = req->vol;
1222 : 808147 : uint64_t chunk_offset, remainder = 0;
1223 : 808147 : uint64_t ttl_len = 0;
1224 : 808147 : size_t iov_len;
1225 : : int i;
1226 : :
1227 : 808147 : req->decomp_iovcnt = 0;
1228 [ - + ]: 808147 : chunk_offset = req->offset % vol->logical_blocks_per_chunk;
1229 : :
1230 : : /* If backing device doesn't support SGL output then we should copy the result of decompression to user's buffer
1231 : : * if at least one of the conditions below is true:
1232 : : * 1. User's buffer is fragmented
1233 : : * 2. Length of the user's buffer is less than the chunk
1234 : : * 3. User's buffer is contig, equals chunk_size but crosses huge page boundary */
1235 : 808147 : iov_len = req->iov[0].iov_len;
1236 [ - + + - : 1616290 : req->copy_after_decompress = !vol->backing_dev->sgl_out && (req->iovcnt > 1 ||
+ + ]
1237 [ + + + + ]: 1216325 : req->iov[0].iov_len < vol->params.chunk_size ||
1238 : 408182 : _addr_crosses_huge_page(req->iov[0].iov_base, &iov_len));
1239 [ - + + + ]: 808147 : if (req->copy_after_decompress) {
1240 : 401991 : req->decomp_iov[0].iov_base = req->decomp_buf;
1241 : 401991 : req->decomp_iov[0].iov_len = vol->params.chunk_size;
1242 : 401991 : req->decomp_iovcnt = 1;
1243 : 401991 : goto decompress;
1244 : : }
1245 : :
1246 [ - + ]: 406156 : if (chunk_offset) {
1247 : : /* first iov point to our scratch buffer for any offset into the chunk */
1248 : 0 : req->decomp_iov[0].iov_base = req->decomp_buf;
1249 : 0 : req->decomp_iov[0].iov_len = chunk_offset * vol->params.logical_block_size;
1250 : 0 : ttl_len += req->decomp_iov[0].iov_len;
1251 : 0 : req->decomp_iovcnt = 1;
1252 : : }
1253 : :
1254 : : /* now the user data iov, direct to the user buffer */
1255 [ + + ]: 812312 : for (i = 0; i < req->iovcnt; i++) {
1256 : 406156 : req->decomp_iov[i + req->decomp_iovcnt].iov_base = req->iov[i].iov_base;
1257 : 406156 : req->decomp_iov[i + req->decomp_iovcnt].iov_len = req->iov[i].iov_len;
1258 : 406156 : ttl_len += req->decomp_iov[i + req->decomp_iovcnt].iov_len;
1259 : : }
1260 : 406156 : req->decomp_iovcnt += req->iovcnt;
1261 : :
1262 : : /* send the rest of the chunk to our scratch buffer */
1263 : 406156 : remainder = vol->params.chunk_size - ttl_len;
1264 [ - + ]: 406156 : if (remainder) {
1265 : 0 : req->decomp_iov[req->decomp_iovcnt].iov_base = req->decomp_buf + ttl_len;
1266 : 0 : req->decomp_iov[req->decomp_iovcnt].iov_len = remainder;
1267 : 0 : ttl_len += req->decomp_iov[req->decomp_iovcnt].iov_len;
1268 : 0 : req->decomp_iovcnt++;
1269 : : }
1270 [ - + ]: 406156 : assert(ttl_len == vol->params.chunk_size);
1271 : :
1272 : 808147 : decompress:
1273 [ - + + + : 808147 : assert(!req->copy_after_decompress || (req->copy_after_decompress && req->decomp_iovcnt == 1));
- + + - +
- ]
1274 : 808147 : req->backing_cb_args.cb_fn = next_fn;
1275 : 808147 : req->backing_cb_args.cb_arg = req;
1276 : 808147 : req->comp_buf_iov[0].iov_base = req->comp_buf;
1277 : 808147 : req->comp_buf_iov[0].iov_len = req->chunk->compressed_size;
1278 : 1616294 : vol->backing_dev->decompress(vol->backing_dev,
1279 : 808147 : req->comp_buf_iov, 1, req->decomp_iov, req->decomp_iovcnt,
1280 : : &req->backing_cb_args);
1281 : 808147 : }
1282 : :
1283 : : static inline void
1284 : 410730 : _prepare_compress_chunk_copy_user_buffers(struct spdk_reduce_vol_request *req, bool zero_paddings)
1285 : : {
1286 : 410730 : struct spdk_reduce_vol *vol = req->vol;
1287 [ + + ]: 410730 : char *padding_buffer = zero_paddings ? g_zero_buf : req->decomp_buf;
1288 : 410730 : uint64_t chunk_offset, ttl_len = 0;
1289 : 410730 : uint64_t remainder = 0;
1290 : 410730 : char *copy_offset = NULL;
1291 : 410730 : uint32_t lbsize = vol->params.logical_block_size;
1292 : : int i;
1293 : :
1294 : 410730 : req->decomp_iov[0].iov_base = req->decomp_buf;
1295 : 410730 : req->decomp_iov[0].iov_len = vol->params.chunk_size;
1296 : 410730 : req->decomp_iovcnt = 1;
1297 : 410730 : copy_offset = req->decomp_iov[0].iov_base;
1298 [ - + ]: 410730 : chunk_offset = req->offset % vol->logical_blocks_per_chunk;
1299 : :
1300 [ + + ]: 410730 : if (chunk_offset) {
1301 : 308101 : ttl_len += chunk_offset * lbsize;
1302 : : /* copy_offset already points to padding buffer if zero_paddings=false */
1303 [ + + ]: 308101 : if (zero_paddings) {
1304 [ - + - + ]: 9383 : memcpy(copy_offset, padding_buffer, ttl_len);
1305 : : }
1306 : 308101 : copy_offset += ttl_len;
1307 : : }
1308 : :
1309 : : /* now the user data iov, direct from the user buffer */
1310 [ + + ]: 821520 : for (i = 0; i < req->iovcnt; i++) {
1311 [ - + - + ]: 410790 : memcpy(copy_offset, req->iov[i].iov_base, req->iov[i].iov_len);
1312 : 410790 : copy_offset += req->iov[i].iov_len;
1313 : 410790 : ttl_len += req->iov[i].iov_len;
1314 : : }
1315 : :
1316 : 410730 : remainder = vol->params.chunk_size - ttl_len;
1317 [ + + ]: 410730 : if (remainder) {
1318 : : /* copy_offset already points to padding buffer if zero_paddings=false */
1319 [ + + ]: 307927 : if (zero_paddings) {
1320 [ - + - + ]: 41919 : memcpy(copy_offset, padding_buffer + ttl_len, remainder);
1321 : : }
1322 : 307927 : ttl_len += remainder;
1323 : : }
1324 : :
1325 [ - + ]: 410730 : assert(ttl_len == req->vol->params.chunk_size);
1326 : 410730 : }
1327 : :
1328 : : /* This function can be called when we are compressing a new data or in case of read-modify-write
1329 : : * In the first case possible paddings should be filled with zeroes, in the second case the paddings
1330 : : * should point to already read and decompressed buffer */
1331 : : static inline void
1332 : 818912 : _prepare_compress_chunk(struct spdk_reduce_vol_request *req, bool zero_paddings)
1333 : : {
1334 : 818912 : struct spdk_reduce_vol *vol = req->vol;
1335 [ + + ]: 818912 : char *padding_buffer = zero_paddings ? g_zero_buf : req->decomp_buf;
1336 : 818912 : uint64_t chunk_offset, ttl_len = 0;
1337 : 818912 : uint64_t remainder = 0;
1338 : 818912 : uint32_t lbsize = vol->params.logical_block_size;
1339 : 818912 : size_t iov_len;
1340 : : int i;
1341 : :
1342 : : /* If backing device doesn't support SGL input then we should copy user's buffer into decomp_buf
1343 : : * if at least one of the conditions below is true:
1344 : : * 1. User's buffer is fragmented
1345 : : * 2. Length of the user's buffer is less than the chunk
1346 : : * 3. User's buffer is contig, equals chunk_size but crosses huge page boundary */
1347 : 818912 : iov_len = req->iov[0].iov_len;
1348 [ - + + - : 818912 : if (!vol->backing_dev->sgl_in && (req->iovcnt > 1 ||
+ + ]
1349 [ + + - + ]: 1227090 : req->iov[0].iov_len < vol->params.chunk_size ||
1350 : 408182 : _addr_crosses_huge_page(req->iov[0].iov_base, &iov_len))) {
1351 : 410730 : _prepare_compress_chunk_copy_user_buffers(req, zero_paddings);
1352 : 410730 : return;
1353 : : }
1354 : :
1355 : 408182 : req->decomp_iovcnt = 0;
1356 [ - + ]: 408182 : chunk_offset = req->offset % vol->logical_blocks_per_chunk;
1357 : :
1358 [ - + ]: 408182 : if (chunk_offset != 0) {
1359 : 0 : ttl_len += chunk_offset * lbsize;
1360 : 0 : req->decomp_iov[0].iov_base = padding_buffer;
1361 : 0 : req->decomp_iov[0].iov_len = ttl_len;
1362 : 0 : req->decomp_iovcnt = 1;
1363 : : }
1364 : :
1365 : : /* now the user data iov, direct from the user buffer */
1366 [ + + ]: 816364 : for (i = 0; i < req->iovcnt; i++) {
1367 : 408182 : req->decomp_iov[i + req->decomp_iovcnt].iov_base = req->iov[i].iov_base;
1368 : 408182 : req->decomp_iov[i + req->decomp_iovcnt].iov_len = req->iov[i].iov_len;
1369 : 408182 : ttl_len += req->iov[i].iov_len;
1370 : : }
1371 : 408182 : req->decomp_iovcnt += req->iovcnt;
1372 : :
1373 : 408182 : remainder = vol->params.chunk_size - ttl_len;
1374 [ - + ]: 408182 : if (remainder) {
1375 : 0 : req->decomp_iov[req->decomp_iovcnt].iov_base = padding_buffer + ttl_len;
1376 : 0 : req->decomp_iov[req->decomp_iovcnt].iov_len = remainder;
1377 : 0 : req->decomp_iovcnt++;
1378 : 0 : ttl_len += remainder;
1379 : : }
1380 [ - + ]: 408182 : assert(ttl_len == req->vol->params.chunk_size);
1381 : : }
1382 : :
1383 : : static void
1384 : 365634 : _write_decompress_done(void *_req, int reduce_errno)
1385 : : {
1386 : 365634 : struct spdk_reduce_vol_request *req = _req;
1387 : :
1388 : : /* Negative reduce_errno indicates failure for compression operations. */
1389 [ - + ]: 365634 : if (reduce_errno < 0) {
1390 : 0 : _reduce_vol_complete_req(req, reduce_errno);
1391 : 0 : return;
1392 : : }
1393 : :
1394 : : /* Positive reduce_errno indicates that the output size field in the backing_cb_args
1395 : : * represents the output_size.
1396 : : */
1397 [ - + ]: 365634 : if (req->backing_cb_args.output_size != req->vol->params.chunk_size) {
1398 : 0 : _reduce_vol_complete_req(req, -EIO);
1399 : 0 : return;
1400 : : }
1401 : :
1402 : 365634 : _prepare_compress_chunk(req, false);
1403 : 365634 : _reduce_vol_compress_chunk(req, _write_compress_done);
1404 : : }
1405 : :
1406 : : static void
1407 : 365634 : _write_read_done(void *_req, int reduce_errno)
1408 : : {
1409 : 365634 : struct spdk_reduce_vol_request *req = _req;
1410 : :
1411 [ - + ]: 365634 : if (reduce_errno != 0) {
1412 : 0 : req->reduce_errno = reduce_errno;
1413 : : }
1414 : :
1415 [ - + ]: 365634 : assert(req->num_backing_ops > 0);
1416 [ - + ]: 365634 : if (--req->num_backing_ops > 0) {
1417 : 0 : return;
1418 : : }
1419 : :
1420 [ - + ]: 365634 : if (req->reduce_errno != 0) {
1421 : 0 : _reduce_vol_complete_req(req, req->reduce_errno);
1422 : 0 : return;
1423 : : }
1424 : :
1425 [ - + + - ]: 365634 : if (req->chunk_is_compressed) {
1426 : 365634 : _reduce_vol_decompress_chunk_scratch(req, _write_decompress_done);
1427 : : } else {
1428 : 0 : req->backing_cb_args.output_size = req->chunk->compressed_size;
1429 : :
1430 : 0 : _write_decompress_done(req, 0);
1431 : : }
1432 : : }
1433 : :
1434 : : static void
1435 : 808147 : _read_decompress_done(void *_req, int reduce_errno)
1436 : : {
1437 : 808147 : struct spdk_reduce_vol_request *req = _req;
1438 : 808147 : struct spdk_reduce_vol *vol = req->vol;
1439 : :
1440 : : /* Negative reduce_errno indicates failure for compression operations. */
1441 [ - + ]: 808147 : if (reduce_errno < 0) {
1442 : 0 : _reduce_vol_complete_req(req, reduce_errno);
1443 : 0 : return;
1444 : : }
1445 : :
1446 : : /* Positive reduce_errno indicates that the output size field in the backing_cb_args
1447 : : * represents the output_size.
1448 : : */
1449 [ - + ]: 808147 : if (req->backing_cb_args.output_size != vol->params.chunk_size) {
1450 : 0 : _reduce_vol_complete_req(req, -EIO);
1451 : 0 : return;
1452 : : }
1453 : :
1454 [ - + + + ]: 808147 : if (req->copy_after_decompress) {
1455 [ - + ]: 401991 : uint64_t chunk_offset = req->offset % vol->logical_blocks_per_chunk;
1456 : 401991 : char *decomp_buffer = (char *)req->decomp_buf + chunk_offset * vol->params.logical_block_size;
1457 : : int i;
1458 : :
1459 [ + + ]: 804042 : for (i = 0; i < req->iovcnt; i++) {
1460 [ - + - + ]: 402051 : memcpy(req->iov[i].iov_base, decomp_buffer, req->iov[i].iov_len);
1461 : 402051 : decomp_buffer += req->iov[i].iov_len;
1462 [ - + ]: 402051 : assert(decomp_buffer <= (char *)req->decomp_buf + vol->params.chunk_size);
1463 : : }
1464 : : }
1465 : :
1466 : 808147 : _reduce_vol_complete_req(req, 0);
1467 : : }
1468 : :
1469 : : static void
1470 : 808147 : _read_read_done(void *_req, int reduce_errno)
1471 : : {
1472 : 808147 : struct spdk_reduce_vol_request *req = _req;
1473 : : uint64_t chunk_offset;
1474 : : uint8_t *buf;
1475 : : int i;
1476 : :
1477 [ - + ]: 808147 : if (reduce_errno != 0) {
1478 : 0 : req->reduce_errno = reduce_errno;
1479 : : }
1480 : :
1481 [ - + ]: 808147 : assert(req->num_backing_ops > 0);
1482 [ - + ]: 808147 : if (--req->num_backing_ops > 0) {
1483 : 0 : return;
1484 : : }
1485 : :
1486 [ - + ]: 808147 : if (req->reduce_errno != 0) {
1487 : 0 : _reduce_vol_complete_req(req, req->reduce_errno);
1488 : 0 : return;
1489 : : }
1490 : :
1491 [ - + + - ]: 808147 : if (req->chunk_is_compressed) {
1492 : 808147 : _reduce_vol_decompress_chunk(req, _read_decompress_done);
1493 : : } else {
1494 : :
1495 : : /* If the chunk was compressed, the data would have been sent to the
1496 : : * host buffers by the decompression operation, if not we need to memcpy here.
1497 : : */
1498 [ # # ]: 0 : chunk_offset = req->offset % req->vol->logical_blocks_per_chunk;
1499 : 0 : buf = req->decomp_buf + chunk_offset * req->vol->params.logical_block_size;
1500 [ # # ]: 0 : for (i = 0; i < req->iovcnt; i++) {
1501 [ # # # # ]: 0 : memcpy(req->iov[i].iov_base, buf, req->iov[i].iov_len);
1502 : 0 : buf += req->iov[i].iov_len;
1503 : : }
1504 : :
1505 : 0 : req->backing_cb_args.output_size = req->chunk->compressed_size;
1506 : :
1507 : 0 : _read_decompress_done(req, 0);
1508 : : }
1509 : : }
1510 : :
1511 : : static void
1512 : 1173781 : _reduce_vol_read_chunk(struct spdk_reduce_vol_request *req, reduce_request_fn next_fn)
1513 : : {
1514 : 1173781 : struct spdk_reduce_vol *vol = req->vol;
1515 : :
1516 : 1173781 : req->chunk_map_index = vol->pm_logical_map[req->logical_map_index];
1517 [ - + ]: 1173781 : assert(req->chunk_map_index != UINT32_MAX);
1518 : :
1519 : 1173781 : req->chunk = _reduce_vol_get_chunk_map(vol, req->chunk_map_index);
1520 : 1173781 : req->num_io_units = spdk_divide_round_up(req->chunk->compressed_size,
1521 : 1173781 : vol->params.backing_io_unit_size);
1522 : 1173781 : req->chunk_is_compressed = (req->num_io_units != vol->backing_io_units_per_chunk);
1523 : :
1524 : 1173781 : _issue_backing_ops(req, vol, next_fn, false /* read */);
1525 : 1173781 : }
1526 : :
1527 : : static bool
1528 : 1639554 : _iov_array_is_valid(struct spdk_reduce_vol *vol, struct iovec *iov, int iovcnt,
1529 : : uint64_t length)
1530 : : {
1531 : 1639554 : uint64_t size = 0;
1532 : : int i;
1533 : :
1534 [ - + ]: 1639554 : if (iovcnt > REDUCE_MAX_IOVECS) {
1535 : 0 : return false;
1536 : : }
1537 : :
1538 [ + + ]: 3279228 : for (i = 0; i < iovcnt; i++) {
1539 : 1639674 : size += iov[i].iov_len;
1540 : : }
1541 : :
1542 : 1639554 : return size == (length * vol->params.logical_block_size);
1543 : : }
1544 : :
1545 : : static bool
1546 : 1639554 : _check_overlap(struct spdk_reduce_vol *vol, uint64_t logical_map_index)
1547 : : {
1548 : : struct spdk_reduce_vol_request *req;
1549 : :
1550 [ + + ]: 85703686 : TAILQ_FOREACH(req, &vol->executing_requests, tailq) {
1551 [ + + ]: 84602247 : if (logical_map_index == req->logical_map_index) {
1552 : 538115 : return true;
1553 : : }
1554 : : }
1555 : :
1556 : 1101439 : return false;
1557 : : }
1558 : :
1559 : : static void
1560 : 808147 : _start_readv_request(struct spdk_reduce_vol_request *req)
1561 : : {
1562 : 808147 : TAILQ_INSERT_TAIL(&req->vol->executing_requests, req, tailq);
1563 : 808147 : _reduce_vol_read_chunk(req, _read_read_done);
1564 : 808147 : }
1565 : :
1566 : : void
1567 : 820642 : spdk_reduce_vol_readv(struct spdk_reduce_vol *vol,
1568 : : struct iovec *iov, int iovcnt, uint64_t offset, uint64_t length,
1569 : : spdk_reduce_vol_op_complete cb_fn, void *cb_arg)
1570 : : {
1571 : : struct spdk_reduce_vol_request *req;
1572 : : uint64_t logical_map_index;
1573 : : bool overlapped;
1574 : : int i;
1575 : :
1576 [ - + ]: 820642 : if (length == 0) {
1577 : 0 : cb_fn(cb_arg, 0);
1578 : 0 : return;
1579 : : }
1580 : :
1581 [ - + ]: 820642 : if (_request_spans_chunk_boundary(vol, offset, length)) {
1582 : 0 : cb_fn(cb_arg, -EINVAL);
1583 : 0 : return;
1584 : : }
1585 : :
1586 [ - + ]: 820642 : if (!_iov_array_is_valid(vol, iov, iovcnt, length)) {
1587 : 0 : cb_fn(cb_arg, -EINVAL);
1588 : 0 : return;
1589 : : }
1590 : :
1591 [ - + ]: 820642 : logical_map_index = offset / vol->logical_blocks_per_chunk;
1592 : 820642 : overlapped = _check_overlap(vol, logical_map_index);
1593 : :
1594 [ + + + + ]: 820642 : if (!overlapped && vol->pm_logical_map[logical_map_index] == REDUCE_EMPTY_MAP_ENTRY) {
1595 : : /*
1596 : : * This chunk hasn't been allocated. So treat the data as all
1597 : : * zeroes for this chunk - do the memset and immediately complete
1598 : : * the operation.
1599 : : */
1600 [ + + ]: 24990 : for (i = 0; i < iovcnt; i++) {
1601 [ - + ]: 12495 : memset(iov[i].iov_base, 0, iov[i].iov_len);
1602 : : }
1603 : 12495 : cb_fn(cb_arg, 0);
1604 : 12495 : return;
1605 : : }
1606 : :
1607 : 808147 : req = TAILQ_FIRST(&vol->free_requests);
1608 [ - + ]: 808147 : if (req == NULL) {
1609 : 0 : cb_fn(cb_arg, -ENOMEM);
1610 : 0 : return;
1611 : : }
1612 : :
1613 [ + - ]: 808147 : TAILQ_REMOVE(&vol->free_requests, req, tailq);
1614 : 808147 : req->type = REDUCE_IO_READV;
1615 : 808147 : req->vol = vol;
1616 : 808147 : req->iov = iov;
1617 : 808147 : req->iovcnt = iovcnt;
1618 : 808147 : req->offset = offset;
1619 : 808147 : req->logical_map_index = logical_map_index;
1620 : 808147 : req->length = length;
1621 : 808147 : req->copy_after_decompress = false;
1622 : 808147 : req->cb_fn = cb_fn;
1623 : 808147 : req->cb_arg = cb_arg;
1624 : :
1625 [ + + ]: 808147 : if (!overlapped) {
1626 : 368049 : _start_readv_request(req);
1627 : : } else {
1628 : 440098 : TAILQ_INSERT_TAIL(&vol->queued_requests, req, tailq);
1629 : : }
1630 : : }
1631 : :
1632 : : static void
1633 : 818912 : _start_writev_request(struct spdk_reduce_vol_request *req)
1634 : : {
1635 : 818912 : struct spdk_reduce_vol *vol = req->vol;
1636 : :
1637 : 818912 : TAILQ_INSERT_TAIL(&req->vol->executing_requests, req, tailq);
1638 [ + + ]: 818912 : if (vol->pm_logical_map[req->logical_map_index] != REDUCE_EMPTY_MAP_ENTRY) {
1639 [ + + ]: 760826 : if ((req->length * vol->params.logical_block_size) < vol->params.chunk_size) {
1640 : : /* Read old chunk, then overwrite with data from this write
1641 : : * operation.
1642 : : */
1643 : 365634 : req->rmw = true;
1644 : 365634 : _reduce_vol_read_chunk(req, _write_read_done);
1645 : 365634 : return;
1646 : : }
1647 : : }
1648 : :
1649 : 453278 : req->rmw = false;
1650 : :
1651 : 453278 : _prepare_compress_chunk(req, true);
1652 : 453278 : _reduce_vol_compress_chunk(req, _write_compress_done);
1653 : : }
1654 : :
1655 : : void
1656 : 818912 : spdk_reduce_vol_writev(struct spdk_reduce_vol *vol,
1657 : : struct iovec *iov, int iovcnt, uint64_t offset, uint64_t length,
1658 : : spdk_reduce_vol_op_complete cb_fn, void *cb_arg)
1659 : : {
1660 : : struct spdk_reduce_vol_request *req;
1661 : : uint64_t logical_map_index;
1662 : : bool overlapped;
1663 : :
1664 [ - + ]: 818912 : if (length == 0) {
1665 : 0 : cb_fn(cb_arg, 0);
1666 : 0 : return;
1667 : : }
1668 : :
1669 [ - + ]: 818912 : if (_request_spans_chunk_boundary(vol, offset, length)) {
1670 : 0 : cb_fn(cb_arg, -EINVAL);
1671 : 0 : return;
1672 : : }
1673 : :
1674 [ - + ]: 818912 : if (!_iov_array_is_valid(vol, iov, iovcnt, length)) {
1675 : 0 : cb_fn(cb_arg, -EINVAL);
1676 : 0 : return;
1677 : : }
1678 : :
1679 [ - + ]: 818912 : logical_map_index = offset / vol->logical_blocks_per_chunk;
1680 : 818912 : overlapped = _check_overlap(vol, logical_map_index);
1681 : :
1682 : 818912 : req = TAILQ_FIRST(&vol->free_requests);
1683 [ - + ]: 818912 : if (req == NULL) {
1684 : 0 : cb_fn(cb_arg, -ENOMEM);
1685 : 0 : return;
1686 : : }
1687 : :
1688 [ + - ]: 818912 : TAILQ_REMOVE(&vol->free_requests, req, tailq);
1689 : 818912 : req->type = REDUCE_IO_WRITEV;
1690 : 818912 : req->vol = vol;
1691 : 818912 : req->iov = iov;
1692 : 818912 : req->iovcnt = iovcnt;
1693 : 818912 : req->offset = offset;
1694 : 818912 : req->logical_map_index = logical_map_index;
1695 : 818912 : req->length = length;
1696 : 818912 : req->copy_after_decompress = false;
1697 : 818912 : req->cb_fn = cb_fn;
1698 : 818912 : req->cb_arg = cb_arg;
1699 : :
1700 [ + + ]: 818912 : if (!overlapped) {
1701 : 720895 : _start_writev_request(req);
1702 : : } else {
1703 : 98017 : TAILQ_INSERT_TAIL(&vol->queued_requests, req, tailq);
1704 : : }
1705 : : }
1706 : :
1707 : : const struct spdk_reduce_vol_params *
1708 : 0 : spdk_reduce_vol_get_params(struct spdk_reduce_vol *vol)
1709 : : {
1710 : 0 : return &vol->params;
1711 : : }
1712 : :
1713 : : void
1714 : 0 : spdk_reduce_vol_print_info(struct spdk_reduce_vol *vol)
1715 : : {
1716 : : uint64_t logical_map_size, num_chunks, ttl_chunk_sz;
1717 : : uint32_t struct_size;
1718 : : uint64_t chunk_map_size;
1719 : :
1720 : 0 : SPDK_NOTICELOG("vol info:\n");
1721 : 0 : SPDK_NOTICELOG("\tvol->params.backing_io_unit_size = 0x%x\n", vol->params.backing_io_unit_size);
1722 : 0 : SPDK_NOTICELOG("\tvol->params.logical_block_size = 0x%x\n", vol->params.logical_block_size);
1723 : 0 : SPDK_NOTICELOG("\tvol->params.chunk_size = 0x%x\n", vol->params.chunk_size);
1724 : 0 : SPDK_NOTICELOG("\tvol->params.vol_size = 0x%" PRIx64 "\n", vol->params.vol_size);
1725 : 0 : num_chunks = _get_total_chunks(vol->params.vol_size, vol->params.chunk_size);
1726 : 0 : SPDK_NOTICELOG("\ttotal chunks (including extra) = 0x%" PRIx64 "\n", num_chunks);
1727 [ # # ]: 0 : SPDK_NOTICELOG("\ttotal chunks (excluding extra) = 0x%" PRIx64 "\n",
1728 : : vol->params.vol_size / vol->params.chunk_size);
1729 : 0 : ttl_chunk_sz = _get_pm_total_chunks_size(vol->params.vol_size, vol->params.chunk_size,
1730 : 0 : vol->params.backing_io_unit_size);
1731 : 0 : SPDK_NOTICELOG("\ttotal_chunks_size = 0x%" PRIx64 "\n", ttl_chunk_sz);
1732 : 0 : struct_size = _reduce_vol_get_chunk_struct_size(vol->backing_io_units_per_chunk);
1733 : 0 : SPDK_NOTICELOG("\tchunk_struct_size = 0x%x\n", struct_size);
1734 : :
1735 : 0 : SPDK_NOTICELOG("pmem info:\n");
1736 : 0 : SPDK_NOTICELOG("\tvol->pm_file.size = 0x%" PRIx64 "\n", vol->pm_file.size);
1737 : 0 : SPDK_NOTICELOG("\tvol->pm_file.pm_buf = %p\n", (void *)vol->pm_file.pm_buf);
1738 : 0 : SPDK_NOTICELOG("\tvol->pm_super = %p\n", (void *)vol->pm_super);
1739 : 0 : SPDK_NOTICELOG("\tvol->pm_logical_map = %p\n", (void *)vol->pm_logical_map);
1740 : 0 : logical_map_size = _get_pm_logical_map_size(vol->params.vol_size,
1741 : 0 : vol->params.chunk_size);
1742 : 0 : SPDK_NOTICELOG("\tlogical_map_size = 0x%" PRIx64 "\n", logical_map_size);
1743 : 0 : SPDK_NOTICELOG("\tvol->pm_chunk_maps = %p\n", (void *)vol->pm_chunk_maps);
1744 : 0 : chunk_map_size = _get_pm_total_chunks_size(vol->params.vol_size, vol->params.chunk_size,
1745 : 0 : vol->params.backing_io_unit_size);
1746 : 0 : SPDK_NOTICELOG("\tchunk_map_size = 0x%" PRIx64 "\n", chunk_map_size);
1747 : 0 : }
1748 : :
1749 : 162 : SPDK_LOG_REGISTER_COMPONENT(reduce)
|