xref: /linux/drivers/md/dm-vdo/memory-alloc.h (revision 6af58aa3b028e364c0a8f8b6be48fca17e571de3)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  * Copyright 2023 Red Hat
4  */
5 
6 #ifndef VDO_MEMORY_ALLOC_H
7 #define VDO_MEMORY_ALLOC_H
8 
9 #include <linux/cache.h>
10 #include <linux/io.h> /* for PAGE_SIZE */
11 #include <linux/overflow.h>
12 
13 #include "permassert.h"
14 #include "thread-registry.h"
15 
16 /* Custom memory allocation function that tracks memory usage */
17 int __must_check vdo_allocate_memory(size_t size, size_t align, const char *what, void *ptr);
18 
19 /*
20  * Allocate storage based on element counts, sizes, and alignment.
21  *
22  * This is a generalized form of our allocation use case: It allocates an array of objects,
23  * optionally preceded by one object of another type (i.e., a struct with trailing variable-length
24  * array), with the alignment indicated.
25  *
26  * Why is this inline? The sizes and alignment will always be constant, when invoked through the
27  * macros below, and often the count will be a compile-time constant 1 or the number of extra bytes
28  * will be a compile-time constant 0. So at least some of the arithmetic can usually be optimized
29  * away, and the run-time selection between allocation functions always can. In many cases, it'll
30  * boil down to just a function call with a constant size.
31  *
32  * @count: The number of objects to allocate
33  * @size: The size of an object
34  * @extra: The number of additional bytes to allocate
35  * @align: The required alignment
36  * @what: What is being allocated (for error logging)
37  * @ptr: A pointer to hold the allocated memory
38  *
39  * Return: VDO_SUCCESS or an error code
40  */
41 static inline int __vdo_do_allocation(size_t count, size_t size, size_t extra,
42 				      size_t align, const char *what, void *ptr)
43 {
44 	size_t total_size = count * size + extra;
45 
46 	/* Overflow check: */
47 	if ((size > 0) && (count > ((SIZE_MAX - extra) / size))) {
48 		/*
49 		 * This is kind of a hack: We rely on the fact that SIZE_MAX would cover the entire
50 		 * address space (minus one byte) and thus the system can never allocate that much
51 		 * and the call will always fail. So we can report an overflow as "out of memory"
52 		 * by asking for "merely" SIZE_MAX bytes.
53 		 */
54 		total_size = SIZE_MAX;
55 	}
56 
57 	return vdo_allocate_memory(total_size, align, what, ptr);
58 }
59 
60 /*
61  * Allocate one or more elements of the indicated type, logging an error if the allocation fails.
62  * The memory will be zeroed.
63  *
64  * @COUNT: The number of objects to allocate
65  * @TYPE: The type of objects to allocate. This type determines the alignment of the allocation.
66  * @WHAT: What is being allocated (for error logging)
67  * @PTR: A pointer to hold the allocated memory
68  *
69  * Return: VDO_SUCCESS or an error code
70  */
71 #define vdo_allocate(COUNT, TYPE, WHAT, PTR) \
72 	__vdo_do_allocation(COUNT, sizeof(TYPE), 0, __alignof__(TYPE), WHAT, PTR)
73 
74 /*
75  * Allocate a structure with a flexible array member, with a specified number of elements, logging
76  * an error if the allocation fails. The memory will be zeroed.
77  *
78  * @COUNT: The number of objects to allocate
79  * @FIELD: The flexible array field at the end of the structure
80  * @WHAT: What is being allocated (for error logging)
81  * @PTR: A pointer to hold the allocated memory
82  *
83  * Return: VDO_SUCCESS or an error code
84  */
85 #define vdo_allocate_extended(COUNT, FIELD, WHAT, PTR)			\
86 	vdo_allocate_memory(struct_size(*(PTR), FIELD, (COUNT)),	\
87 			    __alignof__(typeof(**(PTR))),		\
88 			    WHAT,					\
89 			    (PTR))
90 
91 /*
92  * Allocate memory starting on a cache line boundary, logging an error if the allocation fails. The
93  * memory will be zeroed.
94  *
95  * @size: The number of bytes to allocate
96  * @what: What is being allocated (for error logging)
97  * @ptr: A pointer to hold the allocated memory
98  *
99  * Return: VDO_SUCCESS or an error code
100  */
101 static inline int __must_check vdo_allocate_cache_aligned(size_t size, const char *what, void *ptr)
102 {
103 	return vdo_allocate_memory(size, L1_CACHE_BYTES, what, ptr);
104 }
105 
106 /*
107  * Allocate one element of the indicated type immediately, failing if the required memory is not
108  * immediately available.
109  *
110  * @size: The number of bytes to allocate
111  * @what: What is being allocated (for error logging)
112  *
113  * Return: pointer to the memory, or NULL if the memory is not available.
114  */
115 void *__must_check vdo_allocate_memory_nowait(size_t size, const char *what);
116 
117 int __must_check vdo_reallocate_memory(void *ptr, size_t old_size, size_t size,
118 				       const char *what, void *new_ptr);
119 
120 int __must_check vdo_duplicate_string(const char *string, const char *what,
121 				      char **new_string);
122 
123 /* Free memory allocated with vdo_allocate(). */
124 void vdo_free(void *ptr);
125 
126 static inline void *__vdo_forget(void **ptr_ptr)
127 {
128 	void *ptr = *ptr_ptr;
129 
130 	*ptr_ptr = NULL;
131 	return ptr;
132 }
133 
134 /*
135  * Null out a pointer and return a copy to it. This macro should be used when passing a pointer to
136  * a function for which it is not safe to access the pointer once the function returns.
137  */
138 #define vdo_forget(ptr) __vdo_forget((void **) &(ptr))
139 
140 void vdo_memory_init(void);
141 
142 void vdo_memory_exit(void);
143 
144 void vdo_register_allocating_thread(struct registered_thread *new_thread,
145 				    const bool *flag_ptr);
146 
147 void vdo_unregister_allocating_thread(void);
148 
149 void vdo_get_memory_stats(u64 *bytes_used, u64 *peak_bytes_used);
150 
151 void vdo_report_memory_usage(void);
152 
153 #endif /* VDO_MEMORY_ALLOC_H */
154