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