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