162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-or-later */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2021-2023 Oracle. All Rights Reserved. 462306a36Sopenharmony_ci * Author: Darrick J. Wong <djwong@kernel.org> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#ifndef __XFS_SCRUB_XFARRAY_H__ 762306a36Sopenharmony_ci#define __XFS_SCRUB_XFARRAY_H__ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* xfile array index type, along with cursor initialization */ 1062306a36Sopenharmony_citypedef uint64_t xfarray_idx_t; 1162306a36Sopenharmony_ci#define XFARRAY_CURSOR_INIT ((__force xfarray_idx_t)0) 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* Iterate each index of an xfile array. */ 1462306a36Sopenharmony_ci#define foreach_xfarray_idx(array, idx) \ 1562306a36Sopenharmony_ci for ((idx) = XFARRAY_CURSOR_INIT; \ 1662306a36Sopenharmony_ci (idx) < xfarray_length(array); \ 1762306a36Sopenharmony_ci (idx)++) 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistruct xfarray { 2062306a36Sopenharmony_ci /* Underlying file that backs the array. */ 2162306a36Sopenharmony_ci struct xfile *xfile; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci /* Number of array elements. */ 2462306a36Sopenharmony_ci xfarray_idx_t nr; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci /* Maximum possible array size. */ 2762306a36Sopenharmony_ci xfarray_idx_t max_nr; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci /* Number of unset slots in the array below @nr. */ 3062306a36Sopenharmony_ci uint64_t unset_slots; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci /* Size of an array element. */ 3362306a36Sopenharmony_ci size_t obj_size; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci /* log2 of array element size, if possible. */ 3662306a36Sopenharmony_ci int obj_size_log; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ciint xfarray_create(const char *descr, unsigned long long required_capacity, 4062306a36Sopenharmony_ci size_t obj_size, struct xfarray **arrayp); 4162306a36Sopenharmony_civoid xfarray_destroy(struct xfarray *array); 4262306a36Sopenharmony_ciint xfarray_load(struct xfarray *array, xfarray_idx_t idx, void *ptr); 4362306a36Sopenharmony_ciint xfarray_unset(struct xfarray *array, xfarray_idx_t idx); 4462306a36Sopenharmony_ciint xfarray_store(struct xfarray *array, xfarray_idx_t idx, const void *ptr); 4562306a36Sopenharmony_ciint xfarray_store_anywhere(struct xfarray *array, const void *ptr); 4662306a36Sopenharmony_cibool xfarray_element_is_null(struct xfarray *array, const void *ptr); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* Append an element to the array. */ 4962306a36Sopenharmony_cistatic inline int xfarray_append(struct xfarray *array, const void *ptr) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci return xfarray_store(array, array->nr, ptr); 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ciuint64_t xfarray_length(struct xfarray *array); 5562306a36Sopenharmony_ciint xfarray_load_next(struct xfarray *array, xfarray_idx_t *idx, void *rec); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* Declarations for xfile array sort functionality. */ 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_citypedef cmp_func_t xfarray_cmp_fn; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* Perform an in-memory heapsort for small subsets. */ 6262306a36Sopenharmony_ci#define XFARRAY_ISORT_SHIFT (4) 6362306a36Sopenharmony_ci#define XFARRAY_ISORT_NR (1U << XFARRAY_ISORT_SHIFT) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* Evalulate this many points to find the qsort pivot. */ 6662306a36Sopenharmony_ci#define XFARRAY_QSORT_PIVOT_NR (9) 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistruct xfarray_sortinfo { 6962306a36Sopenharmony_ci struct xfarray *array; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* Comparison function for the sort. */ 7262306a36Sopenharmony_ci xfarray_cmp_fn cmp_fn; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* Maximum height of the partition stack. */ 7562306a36Sopenharmony_ci uint8_t max_stack_depth; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* Current height of the partition stack. */ 7862306a36Sopenharmony_ci int8_t stack_depth; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* Maximum stack depth ever used. */ 8162306a36Sopenharmony_ci uint8_t max_stack_used; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* XFARRAY_SORT_* flags; see below. */ 8462306a36Sopenharmony_ci unsigned int flags; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* Cache a page here for faster access. */ 8762306a36Sopenharmony_ci struct xfile_page xfpage; 8862306a36Sopenharmony_ci void *page_kaddr; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#ifdef DEBUG 9162306a36Sopenharmony_ci /* Performance statistics. */ 9262306a36Sopenharmony_ci uint64_t loads; 9362306a36Sopenharmony_ci uint64_t stores; 9462306a36Sopenharmony_ci uint64_t compares; 9562306a36Sopenharmony_ci uint64_t heapsorts; 9662306a36Sopenharmony_ci#endif 9762306a36Sopenharmony_ci /* 9862306a36Sopenharmony_ci * Extra bytes are allocated beyond the end of the structure to store 9962306a36Sopenharmony_ci * quicksort information. C does not permit multiple VLAs per struct, 10062306a36Sopenharmony_ci * so we document all of this in a comment. 10162306a36Sopenharmony_ci * 10262306a36Sopenharmony_ci * Pretend that we have a typedef for array records: 10362306a36Sopenharmony_ci * 10462306a36Sopenharmony_ci * typedef char[array->obj_size] xfarray_rec_t; 10562306a36Sopenharmony_ci * 10662306a36Sopenharmony_ci * First comes the quicksort partition stack: 10762306a36Sopenharmony_ci * 10862306a36Sopenharmony_ci * xfarray_idx_t lo[max_stack_depth]; 10962306a36Sopenharmony_ci * xfarray_idx_t hi[max_stack_depth]; 11062306a36Sopenharmony_ci * 11162306a36Sopenharmony_ci * union { 11262306a36Sopenharmony_ci * 11362306a36Sopenharmony_ci * If for a given subset we decide to use an in-memory sort, we use a 11462306a36Sopenharmony_ci * block of scratchpad records here to compare items: 11562306a36Sopenharmony_ci * 11662306a36Sopenharmony_ci * xfarray_rec_t scratch[ISORT_NR]; 11762306a36Sopenharmony_ci * 11862306a36Sopenharmony_ci * Otherwise, we want to partition the records to partition the array. 11962306a36Sopenharmony_ci * We store the chosen pivot record at the start of the scratchpad area 12062306a36Sopenharmony_ci * and use the rest to sample some records to estimate the median. 12162306a36Sopenharmony_ci * The format of the qsort_pivot array enables us to use the kernel 12262306a36Sopenharmony_ci * heapsort function to place the median value in the middle. 12362306a36Sopenharmony_ci * 12462306a36Sopenharmony_ci * struct { 12562306a36Sopenharmony_ci * xfarray_rec_t pivot; 12662306a36Sopenharmony_ci * struct { 12762306a36Sopenharmony_ci * xfarray_rec_t rec; (rounded up to 8 bytes) 12862306a36Sopenharmony_ci * xfarray_idx_t idx; 12962306a36Sopenharmony_ci * } qsort_pivot[QSORT_PIVOT_NR]; 13062306a36Sopenharmony_ci * }; 13162306a36Sopenharmony_ci * } 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* Sort can be interrupted by a fatal signal. */ 13662306a36Sopenharmony_ci#define XFARRAY_SORT_KILLABLE (1U << 0) 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ciint xfarray_sort(struct xfarray *array, xfarray_cmp_fn cmp_fn, 13962306a36Sopenharmony_ci unsigned int flags); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci#endif /* __XFS_SCRUB_XFARRAY_H__ */ 142