1 /*
2 * SPDX-FileCopyrightText: Copyright (c) 2015-2020 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 /* ------------------------ Includes --------------------------------------- */
24 #include "prereq_tracker/prereq_tracker.h"
25
26 /* ------------------------ Static Function Prototypes --------------------- */
27 static NvBool _prereqValid(PrereqTracker *pTracker, PREREQ_ENTRY *pPrereq);
28
29 /* ------------------------ Public Functions ------------------------------ */
30
31 /*!
32 * @brief Construct the prereq tracker object
33 *
34 * @param[in] pTracker PrereqTracker object to be constructed
35 * @param[in] pParent Parent GPU passed into the first parameter of callbacks
36 *
37 * @return NV_OK Successfully constructed tracker
38 * @return NV_ERR_INVALID_STATE If already constructed
39 */
40 NV_STATUS
prereqConstruct_IMPL(PrereqTracker * pTracker,OBJGPU * pParent)41 prereqConstruct_IMPL
42 (
43 PrereqTracker *pTracker,
44 OBJGPU *pParent
45 )
46 {
47 NV_ASSERT_OR_RETURN(!pTracker->bInitialized, NV_ERR_INVALID_STATE);
48 NV_ASSERT_OR_RETURN(pParent != NULL, NV_ERR_INVALID_OBJECT_PARENT);
49
50 bitVectorClrAll(&pTracker->satisfied);
51
52 listInit(&pTracker->prereqList, portMemAllocatorGetGlobalNonPaged());
53 pTracker->bInitialized = NV_TRUE;
54 pTracker->pParent = pParent;
55
56 return NV_OK;
57 }
58
59 /*!
60 * @brief Destroys the prerequisite tracker object
61 *
62 * @param[in] pTracker PrereqTracker object to be destroyed
63 */
64 void
prereqDestruct_IMPL(PrereqTracker * pTracker)65 prereqDestruct_IMPL
66 (
67 PrereqTracker *pTracker
68 )
69 {
70 NV_ASSERT_OR_RETURN_VOID(pTracker->bInitialized);
71
72 listDestroy(&pTracker->prereqList);
73 pTracker->bInitialized = NV_FALSE;
74 }
75
76 /*!
77 * @brief Arms a tracking structure to fire the callback when all prerequisites
78 * are satisfied. May only be called after all prerequisites are specified. No
79 * more prerequisites may be specified after arming.
80 *
81 * @param[in] pTracker PrereqTracker object
82 * @param[in] pPrereq PREREQ_ENTRY object pointer
83 *
84 * @return NV_OK Prerequisite successfully armed.
85 * @return error Errors propagated up from functions called.
86 */
87 static NV_STATUS
_prereqArm(PrereqTracker * pTracker,PREREQ_ENTRY * pPrereq)88 _prereqArm
89 (
90 PrereqTracker *pTracker,
91 PREREQ_ENTRY *pPrereq
92 )
93 {
94 PREREQ_ID_BIT_VECTOR requestedAndSatisfied;
95
96 NV_ASSERT_OR_RETURN(pTracker->bInitialized, NV_ERR_INVALID_STATE);
97 NV_ASSERT_OR_RETURN(_prereqValid(pTracker, pPrereq), NV_ERR_INVALID_OBJECT);
98 NV_ASSERT_OR_RETURN(!pPrereq->bArmed, NV_ERR_INVALID_STATE);
99
100 //
101 // Set the PREREQ_ENTRY state to bArmed. No more PREREQ_IDs may be added
102 // after this point.
103 //
104 pPrereq->bArmed = NV_TRUE;
105
106 //
107 // Put together a mask of PREREQ_IDs which are both satisfied and requested
108 // We do not keep track of satisfied prereqs until armed, so we have no existing
109 // state to worry about here.
110 //
111 NV_ASSERT_OK_OR_RETURN(bitVectorAnd(&requestedAndSatisfied,
112 &pPrereq->requested,
113 &pTracker->satisfied));
114
115 pPrereq->countSatisfied = bitVectorCountSetBits(&requestedAndSatisfied);
116
117 if (PREREQ_IS_SATISFIED(pPrereq))
118 {
119 NV_ASSERT_OK_OR_RETURN(pPrereq->callback(pTracker->pParent, NV_TRUE));
120 }
121
122 return NV_OK;
123 }
124
125 /*!
126 * @brief Creates, adds IDs to, and Arms a prereq tracking structure into the list.
127 * Caller gives up all control of the prereq structure to the prereq tracker, which
128 * will take care of storing the completed, final struct and freeing it once done.
129 *
130 * @param[in] pTracker PrereqTracker object
131 * @param[in] callback Callback function pointer
132 * First parameter passed will be NVOC parent of pTracker
133 * @param[in] pDepends Bitvector of prerequisite IDs to add as requirement
134 * @param[out] ppPrereq PREREQ_ENTRY object pointer created, or NULL if not desired
135 *
136 * @return NV_OK Prerequisite successfully armed.
137 * @return error Errors propagated up from functions called.
138 */
139 NV_STATUS
prereqComposeEntry_IMPL(PrereqTracker * pTracker,GpuPrereqCallback * callback,PREREQ_ID_BIT_VECTOR * pDepends,PREREQ_ENTRY ** ppPrereq)140 prereqComposeEntry_IMPL
141 (
142 PrereqTracker *pTracker,
143 GpuPrereqCallback *callback,
144 PREREQ_ID_BIT_VECTOR *pDepends,
145 PREREQ_ENTRY **ppPrereq
146 )
147 {
148 PREREQ_ENTRY *pPrereq;
149
150 NV_ASSERT_OR_RETURN(pTracker->bInitialized, NV_ERR_INVALID_STATE);
151 NV_ASSERT_OR_RETURN(callback != NULL, NV_ERR_INVALID_POINTER);
152 NV_ASSERT_OR_RETURN(pDepends != NULL, NV_ERR_INVALID_POINTER);
153
154 pPrereq = listAppendNew(&pTracker->prereqList);
155 NV_ASSERT_OR_RETURN(pPrereq != NULL, NV_ERR_NO_MEMORY);
156
157 NV_ASSERT_OK_OR_RETURN(bitVectorCopy(&pPrereq->requested, pDepends));
158
159 pPrereq->countRequested = bitVectorCountSetBits(&pPrereq->requested);
160 pPrereq->countSatisfied = 0;
161 pPrereq->callback = callback;
162
163 NV_ASSERT_OK_OR_RETURN(_prereqArm(pTracker, pPrereq));
164
165 if (ppPrereq != NULL)
166 *ppPrereq = pPrereq;
167
168 return NV_OK;
169 }
170
171 /*!
172 * @brief Notifies that prerequisite was satisfied.
173 *
174 * @param[in] pTracker PrereqTracker object
175 * @param[in] prereqId Prerequisite ID to add as requirement
176 *
177 * @return NV_OK Prerequisite successfully satisfied & all callbacks passed.
178 * @return error Errors propagated up from functions called.
179 */
180 NV_STATUS
prereqSatisfy_IMPL(PrereqTracker * pTracker,PREREQ_ID prereqId)181 prereqSatisfy_IMPL
182 (
183 PrereqTracker *pTracker,
184 PREREQ_ID prereqId
185 )
186 {
187 PREREQ_ENTRY *pPrereq;
188 PrereqListIter it;
189
190 NV_ASSERT_OR_RETURN(pTracker->bInitialized, NV_ERR_INVALID_STATE);
191 NV_ASSERT_OR_RETURN((prereqId < PREREQ_ID_VECTOR_SIZE),
192 NV_ERR_INVALID_REQUEST);
193
194 //
195 // The prerequisite can be satisfied only once. An attempt to satisfy
196 // the prerequisite multiple times should indicate bad code design.
197 //
198 NV_ASSERT_OR_RETURN(!bitVectorTest(&pTracker->satisfied, prereqId),
199 NV_ERR_INVALID_STATE);
200
201 NV_ASSERT_OK_OR_RETURN(bitVectorSet(&pTracker->satisfied, prereqId));
202
203 // Broadcast satisfaction of this PREREQ_ID to all armed PREREQ_ENTRY.
204 it = listIterAll(&pTracker->prereqList);
205 while (listIterNext(&it))
206 {
207 pPrereq = it.pValue;
208 if (pPrereq->bArmed &&
209 bitVectorTest(&pPrereq->requested, prereqId))
210 {
211 pPrereq->countSatisfied++;
212 NV_ASSERT_OR_RETURN(pPrereq->countSatisfied <= pPrereq->countRequested,
213 NV_ERR_INVALID_STATE);
214
215 if (PREREQ_IS_SATISFIED(pPrereq))
216 {
217 NV_ASSERT_OK_OR_RETURN(pPrereq->callback(pTracker->pParent, NV_TRUE));
218 }
219 }
220 }
221
222 return NV_OK;
223 }
224
225 /*!
226 * @brief Notifies that prerequisite will be retracted.
227 *
228 * @param[in] pTracker PrereqTracker object
229 * @param[in] prereqId Prerequisite ID to add as requirement
230 *
231 * @return NV_OK Prerequisite successfully retracted & all callbacks passed.
232 * @return error Errors propagated up from functions called.
233 */
234 NV_STATUS
prereqRetract_IMPL(PrereqTracker * pTracker,PREREQ_ID prereqId)235 prereqRetract_IMPL
236 (
237 PrereqTracker *pTracker,
238 PREREQ_ID prereqId
239 )
240 {
241 PREREQ_ENTRY *pNode;
242 PrereqListIter it;
243 NV_STATUS status = NV_OK;
244
245 NV_ASSERT_OR_RETURN(pTracker != NULL,
246 NV_ERR_INVALID_STATE);
247 NV_ASSERT_OR_RETURN(pTracker->bInitialized,
248 NV_ERR_INVALID_STATE);
249 NV_ASSERT_OR_RETURN((prereqId < PREREQ_ID_VECTOR_SIZE),
250 NV_ERR_INVALID_REQUEST);
251
252 //
253 // The prerequisite can be retracted even if it was not satisfied. This
254 // simplifies client code since it no longer need to track if prerequisite
255 // was satisfied (or not) and allows us avoiding isSatisfied() interface.
256 //
257 if (!bitVectorTest(&pTracker->satisfied, prereqId))
258 return NV_OK;
259
260 NV_ASSERT_OK_OR_RETURN(bitVectorClr(&pTracker->satisfied, prereqId));
261
262 it = listIterAll(&pTracker->prereqList);
263 while (listIterNext(&it))
264 {
265 pNode = it.pValue;
266
267 if (pNode->bArmed &&
268 bitVectorTest(&pNode->requested, prereqId))
269 {
270 if (PREREQ_IS_SATISFIED(pNode))
271 {
272 NV_ASSERT_OK_OR_CAPTURE_FIRST_ERROR(status, pNode->callback(pTracker->pParent, NV_FALSE));
273 }
274
275 pNode->countSatisfied--;
276 if (pNode->countSatisfied < 0)
277 {
278 NV_ASSERT(0);
279 if (status == NV_OK)
280 {
281 status = NV_ERR_INVALID_STATE;
282 }
283 }
284 }
285 }
286
287 return status;
288 }
289
290 /*!
291 * @brief Indicates if a prerequisite ID is currently satisfied.
292 *
293 * @param[in] pTracker PrereqTracker object pointer
294 * @param[in] prereqId Prerequisite ID to check
295 *
296 * @return NV_TRUE Prerequisite ID is in the satisfied mask.
297 * NV_FALSE otherwise
298 */
299 NvBool
prereqIdIsSatisfied_IMPL(PrereqTracker * pTracker,PREREQ_ID prereqId)300 prereqIdIsSatisfied_IMPL
301 (
302 PrereqTracker *pTracker,
303 PREREQ_ID prereqId
304 )
305 {
306 NvBool bIsSatisfied;
307
308 if ((pTracker->bInitialized) &&
309 (prereqId < PREREQ_ID_VECTOR_SIZE))
310 {
311 bIsSatisfied = bitVectorTest(&pTracker->satisfied, prereqId);
312 }
313 else
314 {
315 bIsSatisfied = NV_FALSE;
316 }
317
318 return bIsSatisfied;
319 }
320
321 /* ---------------------- Private Static Functions -------------------------- */
322 /*!
323 * Helper function which determines whether a given PREREQ_ENTRY tracking
324 * structure is valid (i.e. is in the tracker's list at @ref
325 * PrereqTracker::prereqList).
326 *
327 * @param[in] pTracker PrereqTracker object pointer
328 * @param[in] pPrereq PREREQ_ENTRY object pointer
329 *
330 * @return NV_TRUE pPrereq is valid.
331 * @return NV_FALSE pPrereq is invalid.
332 */
333 static NvBool
_prereqValid(PrereqTracker * pTracker,PREREQ_ENTRY * pPrereq)334 _prereqValid
335 (
336 PrereqTracker *pTracker,
337 PREREQ_ENTRY *pPrereq
338 )
339 {
340 PrereqListIter it = listIterAll(&pTracker->prereqList);
341 while (listIterNext(&it))
342 {
343 // pPrereq is valid if found in the list.
344 if (it.pValue == pPrereq)
345 return NV_TRUE;
346 }
347
348 return NV_FALSE;
349 }
350