1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2015-2019 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3  * SPDX-License-Identifier: MIT
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  */
23 
24 /*!
25  * @file poolalloc.h
26  * @brief This file contains the interfaces for pool allocator.
27  * A chained sub-allocator originally designed to sub-allocate GPU
28  * frame buffer given out by PMA (physical memory allocator).
29  *
30  * The only requirement of a node in the chained allocator is that the ratio
31  * between upSTreamPageSize and allocPageSize is less or equal to 64.
32  *
33  * @bug Make more abstract -- fix up the variable names
34  */
35 
36 
37 #ifndef _NV_POOLALLOC_H_
38 #define _NV_POOLALLOC_H_
39 
40 #include "nvtypes.h"
41 #include "nvstatus.h"
42 #include "nvport/nvport.h"
43 #include "containers/list.h"
44 
45 #ifdef __cplusplus
46 extern "C" {
47 #endif
48 
49 typedef struct poolnode POOLNODE;
50 
51 /*!
52  * Each node corresponds to one page of upStreamPageSize
53  * The pool allocator sub-allocates from each of these pages.
54  */
55 struct poolnode
56 {
57     NvU64    pageAddr;     // Address of the page to sub-allocate
58     NvU64    bitmap;       // The bit map for this page. Only used if the
59                            // node represents a partially allocated node
60     POOLNODE *pParent;     // The upstream pool node in case this node is
61                            // allocated from the upper pool.
62     ListNode node;         // For intrusive lists.
63 };
64 
65 MAKE_INTRUSIVE_LIST(PoolNodeList, POOLNODE, node);
66 
67 /*!
68  * The handle contains a generic metadata field that is needed for fast
69  * access. In the case of a linked list implementation of the pool allocator,
70  * the metadata is the pointer to the node that contains the page it was
71  * sub-allocated from
72  */
73 typedef struct poolallocHandle
74 {
75     NvU64 address;      // The base address for this chunk
76     void  *pMetadata;   // The opaque metadata for storing necessary info
77 } POOLALLOC_HANDLE;
78 
79 
80 // non-intrusive list of page handles
81 MAKE_LIST(PoolPageHandleList, POOLALLOC_HANDLE);
82 
83 /*!
84  * @brief Callback function to upstream allocators for allocating new pages
85  *
86  * This function only allocate 1 page at a time right now
87  *
88  * @param[in]   ctxPtr      Provides context to upstream allocator
89  * @param[in]   pageSize    Not really needed. For debugging only
90  * @param[out]  pPage       The output page handle from upstream
91  *
92  * @return NV_OK            if successfully allocated NvF32 totalTest, doneTest, failTest; the page
93  *         NV_ERR_NO_MEMORY if allocator cannot allocate enough backing
94  *         NV_ERR_BAD_PARAM if any parameter is invalid or size info is not
95  *                          multiple of SMALLEST_PAGE_SIZE
96  *
97  */
98 typedef NV_STATUS (*allocCallback_t)(void *ctxPtr, NvU64 pageSize,
99                    POOLALLOC_HANDLE *pPage);
100 
101 /*!
102  * @brief Callback function to upstream allocators for freeing unused pages
103  *
104  * This function only allocate 1 page at a time right now
105  *
106  * @param[in]   ctxPtr      Provides context to upstream allocator
107  * @param[in]   pageSize    Not really needed. For debugging only
108  * @param[in]   pPage       The input page handle to be freed
109  *
110  */
111 typedef void (*freeCallback_t)(void *ctxPtr, NvU64 pageSize, POOLALLOC_HANDLE *pPage);
112 
113 /*!
114  * Structure representing a pool.
115  */
116 typedef struct poolalloc
117 {
118     PoolNodeList       freeList;         // List of nodes representing free pages
119     PoolNodeList       fullList;         // List of nodes representing fully allocated pages
120     PoolNodeList       partialList;      // List of nodes representing partially allocated pages
121 
122     PORT_MEM_ALLOCATOR *pAllocator;
123 
124     struct
125     {
126         allocCallback_t allocCb;       // Callback to upstream allocator
127         freeCallback_t  freeCb;        // Callback to free pages
128         void            *pUpstreamCtx; // The context to pass to upstream allocator
129     } callBackInfo;
130 
131     NvU64              upstreamPageSize; // Page size for upstream allocations
132     NvU64              allocPageSize;    // Page size to give out
133     NvU32              ratio;            // Ratio == upstreamPageSize / allocPageSize
134     NvU32              flags;            // POOLALLOC_FLAGS_*
135 } POOLALLOC;
136 
137 
138 /*!
139  * Dump the lists maintained by the pools.
140  */
141 void poolAllocPrint(POOLALLOC *pPool);
142 
143 /*!
144  *  If _AUTO_POPULATE is set to ENABLE then poolAllocate will call upstream function to repopulate
145  *  the pool when it runs out of memory. If set to DISABLE, poolAllocate will fail when it runs out of memory
146  *  By default this is disabled as for usecases like page tables or context buffers since upstream function can call
147  *  into PMA with GPU lock held which has a possibility of deadlocking
148  */
149 #define NV_RMPOOL_FLAGS_AUTO_POPULATE         1:0
150 #define NV_RMPOOL_FLAGS_AUTO_POPULATE_DEFAULT 0x0
151 #define NV_RMPOOL_FLAGS_AUTO_POPULATE_DISABLE 0x0
152 #define NV_RMPOOL_FLAGS_AUTO_POPULATE_ENABLE  0x1
153 
154 /*!
155  * @brief This function initializes a pool allocator object
156  *
157  * This function establishes a link from this allocator to its upstream
158  * allocator by registering a callback function that lazily allocates memory
159  * if needed.
160  *
161  * @param[in]   upstreamPageSize    The page size granularity managed by
162  *                                  the allocator
163  * @param[in]   allocPageSize       The page size to hand out
164  * @param[in]   allocCb             The allocation callback function
165  * @param[in]   freeCb              The free callback function
166  * @param[in]   pUpstreamCtxPtr     The context pointer for the upstream
167  *                                  allocator, passed back on callback
168  * @param[in]   mallocFun           The allocator for internal strutures
169  * @param[in]   freeFun             The free for internal structures
170  * @param[in]   pAllocCtxPtr        The context pointer for the special
171  *                                  allocator
172  * @param[in]   flags               POOLALLOC_FLAGS_*
173  *
174  * @return A pointer to a POOLALLOC structure if the initialization
175  * succeeded; NULL otherwise
176  *
177  */
178 
179 POOLALLOC *poolInitialize(NvU64 upstreamPageSize, NvU64 allocPageSize,
180     allocCallback_t allocCb, freeCallback_t freeCb, void *pUpstreamCtxPtr,
181     PORT_MEM_ALLOCATOR *pAllocator, NvU32 flags);
182 
183 
184 /*!
185  * @brief Reserves numPages from upstream allocator. After the call
186  * freeListSize will equal to/greater than numPages.
187  *
188  * Since it will call into the upstream allocator, the page size of those
189  * pages will be the upstream page size.
190  *
191  * @param[in]   pPool       The pool allocator
192  * @param[out]  numPages    Number of pages to reserve
193  *
194  * @return NV_OK            if successful
195  *         NV_ERR_NO_MEMORY if allocator cannot allocate enough backing
196  *         NV_ERR_BAD_PARAM if any parameter is invalid
197  *
198  */
199 NV_STATUS poolReserve(POOLALLOC *pPool, NvU64 numPages);
200 
201 
202 /*!
203  * @brief This call will give back any free pages. After the call
204  * freeListSize will be less or equal to preserveNum.
205  *
206  * If the allocator has less or equal number of pages than preserveNum before
207  * the call, this function will simply return.
208  *
209  * @param[in] pPool         The pool allocator to trim from
210  * @param[in] preserveNum   The number of pages that we try to preserve
211  */
212 void poolTrim(POOLALLOC *pPool, NvU64 preserveNum);
213 
214 
215 /*!
216  * @brief This function allocates memory from the allocator and returns one
217  * page of the fixed allocPageSize as specified in the initialization function
218  *
219  * The implementation does not guarantee the allocated pages are contiguous.
220  * Although there is no potential synchronization issues, if two allocation
221  * happen to lie on upstream page bundaries, the allocation will most likely
222  * be discontiguous.
223  *
224  * This function will also callback to upstream allocator to get more pages if
225  * it does not have enough pages already reserved.
226  *
227  * @param[in]   pPool       The pool allocator
228  * @param[out]  pPageHandle The allocation handle that contains address and
229  *                          metadata for optimization
230  *
231  * @return NV_OK            if successful
232  *         NV_ERR_NO_MEMORY if allocator cannot allocate enough backing
233  *         NV_ERR_BAD_PARAM if any parameter is invalid
234  */
235 NV_STATUS poolAllocate(POOLALLOC *pPool, POOLALLOC_HANDLE *pPageHandle);
236 
237 
238 /*!
239  * @brief This function allocates memory from the allocator and returns numPages
240  * of the fixed allocPageSize as specified in the initialization function
241  *
242  * These pages are allocated contiguously and the single start address is returned.
243  * Although there is no potential synchronization issues, if two allocation
244  * happen to lie on upstream page bundaries, the allocation will most likely
245  * be discontiguous.
246  *
247  * This function will not callback to upstream allocator to get more pages as
248  * this is relying on a single chunk of free pages to make contiguous allocations.
249  * So the max number of pages that can be allocated contiguously is the number of pages
250  * fit in upstream page size i.e the "ratio" of this pool
251  *
252  * @param[in]   pPool           The pool allocator
253  * @param[in]   numPages        The number of pages requested to be allocated
254  * @param[out]  pPageHandleList The allocation handles that contain addresses and
255  *                              metadata for optimization
256  *
257  * @return NV_OK            if successful
258  *         NV_ERR_NO_MEMORY if allocator cannot allocate enough backing
259  *         NV_ERR_BAD_PARAM if any parameter is invalid
260  */
261 NV_STATUS poolAllocateContig(POOLALLOC *pPool, NvU32 numPages, PoolPageHandleList *pPageHandleList);
262 
263 /*!
264  * @brief This function frees the page based on the allocPageSize
265  *
266  * @param[in]   pPool       The pool allocator
267  * @param[out]  pPageHandle The allocation handle that contains address and
268  *                          metadata for optimization
269  *
270  */
271 void poolFree(POOLALLOC *pPool, POOLALLOC_HANDLE *pPageHandle);
272 
273 
274 /*!
275  * @brief Destroys the pool allocator and frees memory
276  */
277 void poolDestroy(POOLALLOC *pPool);
278 
279 /*!
280  * @briefs Returns the lengths of a pool's lists
281  */
282 void poolGetListLength(POOLALLOC *pPool, NvU32 *pFreeListLength,
283                        NvU32 *pPartialListLength, NvU32 *pFullListLength);
284 
285 #ifdef __cplusplus
286 }
287 #endif
288 
289 #endif /* _NV_POOLALLOC_H_ */
290