RK3368 GPU version Rogue M 1.28
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / rogue_m / services / server / env / linux / pvr_debugfs.c
1 /*************************************************************************/ /*!
2 @File
3 @Title          Functions for creating debugfs directories and entries.
4 @Copyright      Copyright (c) Imagination Technologies Ltd. All Rights Reserved
5 @License        Dual MIT/GPLv2
6
7 The contents of this file are subject to the MIT license as set out below.
8
9 Permission is hereby granted, free of charge, to any person obtaining a copy
10 of this software and associated documentation files (the "Software"), to deal
11 in the Software without restriction, including without limitation the rights
12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom the Software is
14 furnished to do so, subject to the following conditions:
15
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
18
19 Alternatively, the contents of this file may be used under the terms of
20 the GNU General Public License Version 2 ("GPL") in which case the provisions
21 of GPL are applicable instead of those above.
22
23 If you wish to allow use of your version of this file only under the terms of
24 GPL, and not to allow others to use your version of this file under the terms
25 of the MIT license, indicate your decision by deleting the provisions above
26 and replace them with the notice and other provisions required by GPL as set
27 out in the file called "GPL-COPYING" included in this distribution. If you do
28 not delete the provisions above, a recipient may use your version of this file
29 under the terms of either the MIT license or GPL.
30
31 This License is also included in this distribution in the file called
32 "MIT-COPYING".
33
34 EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS
35 PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
36 BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
37 PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR
38 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
39 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
40 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
41 */ /**************************************************************************/
42
43 #include <linux/module.h>
44 #include <linux/slab.h>
45
46 #include "pvr_debug.h"
47 #include "pvr_debugfs.h"
48 #include "allocmem.h"
49
50 #define PVR_DEBUGFS_DIR_NAME "pvr"
51
52 /* Define to set the PVR_DPF debug output level for pvr_debugfs.
53  * Normally, leave this set to PVR_DBGDRIV_MESSAGE, but when debugging
54  * you can temporarily change this to PVR_DBG_ERROR.
55  */
56 #if defined(PVRSRV_NEED_PVR_DPF)
57 #define PVR_DEBUGFS_PVR_DPF_LEVEL      PVR_DBGDRIV_MESSAGE
58 #else
59 #define PVR_DEBUGFS_PVR_DPF_LEVEL      0
60 #endif
61
62 static struct dentry *gpsPVRDebugFSEntryDir = NULL;
63
64 /* Lock used when adjusting refCounts and deleting entries */
65 static struct mutex gDebugFSLock;
66
67 /*************************************************************************/ /*!
68  Statistic entry read functions
69 */ /**************************************************************************/
70
71 typedef struct _PVR_DEBUGFS_DRIVER_STAT_
72 {
73         void                             *pvData;
74         OS_STATS_PRINT_FUNC  *pfnStatsPrint;
75         PVRSRV_INC_STAT_MEM_REFCOUNT_FUNC       *pfnIncStatMemRefCount;
76         PVRSRV_DEC_STAT_MEM_REFCOUNT_FUNC       *pfnDecStatMemRefCount;
77         IMG_UINT32                              ui32RefCount;
78         PVR_DEBUGFS_ENTRY_DATA  *pvDebugFSEntry;
79 } PVR_DEBUGFS_DRIVER_STAT;
80
81 typedef struct _PVR_DEBUGFS_DIR_DATA_
82 {
83         struct dentry *psDir;
84         PVR_DEBUGFS_DIR_DATA *psParentDir;
85         IMG_UINT32      ui32RefCount;
86 } PVR_DEBUGFS_DIR_DATA;
87
88 typedef struct _PVR_DEBUGFS_ENTRY_DATA_
89 {
90         struct dentry *psEntry;
91         PVR_DEBUGFS_DIR_DATA *psParentDir;
92         IMG_UINT32      ui32RefCount;
93         PVR_DEBUGFS_DRIVER_STAT *psStatData;
94 } PVR_DEBUGFS_ENTRY_DATA;
95
96 typedef struct _PVR_DEBUGFS_PRIV_DATA_
97 {
98         struct seq_operations   *psReadOps;
99         PVRSRV_ENTRY_WRITE_FUNC *pfnWrite;
100         void                    *pvData;
101         IMG_BOOL                bValid;
102         PVR_DEBUGFS_ENTRY_DATA *psDebugFSEntry;
103 } PVR_DEBUGFS_PRIV_DATA;
104
105 static void _RefDirEntry(PVR_DEBUGFS_DIR_DATA *psDirEntry);
106 static void _UnrefAndMaybeDestroyDirEntry(PVR_DEBUGFS_DIR_DATA *psDirEntry);
107 static void _UnrefAndMaybeDestroyDirEntryWhileLocked(PVR_DEBUGFS_DIR_DATA *psDirEntry);
108 static IMG_BOOL _RefDebugFSEntry(PVR_DEBUGFS_ENTRY_DATA *psDebugFSEntry);
109 static void _UnrefAndMaybeDestroyDebugFSEntry(PVR_DEBUGFS_ENTRY_DATA *psDebugFSEntry);
110 static IMG_BOOL _RefStatEntry(PVR_DEBUGFS_DRIVER_STAT *psStatEntry);
111 static IMG_BOOL _UnrefAndMaybeDestroyStatEntry(PVR_DEBUGFS_DRIVER_STAT *psStatEntry);
112
113 static void _StatsSeqPrintf(void *pvFile, const IMG_CHAR *pszFormat, ...)
114 {
115         IMG_CHAR  szBuffer[PVR_MAX_DEBUG_MESSAGE_LEN];
116         va_list  ArgList;
117
118         va_start(ArgList, pszFormat);
119         vsnprintf(szBuffer, PVR_MAX_DEBUG_MESSAGE_LEN, pszFormat, ArgList);
120         seq_printf((struct seq_file *)pvFile, "%s", szBuffer);
121         va_end(ArgList);
122 }
123
124 static void *_DebugFSStatisticSeqStart(struct seq_file *psSeqFile, loff_t *puiPosition)
125 {
126         PVR_DEBUGFS_DRIVER_STAT *psStatData = (PVR_DEBUGFS_DRIVER_STAT *)psSeqFile->private;
127
128         if (psStatData)
129         {
130                 if (psStatData->pvData)
131                 {
132                         /* take reference on psStatData (for duration of stat iteration) */
133                         if (!_RefStatEntry((void*)psStatData))
134                         {
135                                 PVR_DPF((PVR_DEBUGFS_PVR_DPF_LEVEL, "%s: Called for '%s' but failed to take ref on stat entry, returning -EIO(%d)", __FUNCTION__, psStatData->pvDebugFSEntry->psEntry->d_iname, -EIO));
136                                 return NULL;
137                         }
138                 }
139                 else
140                 {
141                         /* NB This is valid if the stat has no structure associated with it (eg. driver_stats, which prints totals stored in a number of global vars) */
142                 }
143
144                 if (*puiPosition == 0)
145                 {
146                         return psStatData;
147                 }
148         }
149         else
150         {
151                 PVR_DPF((PVR_DEBUGFS_PVR_DPF_LEVEL, "%s: Called when psStatData is NULL", __FUNCTION__));
152         }
153
154         return NULL;
155 }
156
157 static void _DebugFSStatisticSeqStop(struct seq_file *psSeqFile, void *pvData)
158 {
159         PVR_DEBUGFS_DRIVER_STAT *psStatData = (PVR_DEBUGFS_DRIVER_STAT *)psSeqFile->private;
160         PVR_UNREFERENCED_PARAMETER(pvData);
161
162         if (psStatData)
163         {
164                 /* drop ref taken on stat memory, and if it is now zero, be sure we don't try to read it again */
165                 if ((psStatData->ui32RefCount > 0) && (psStatData->pvData))
166                 {
167                         /* drop reference on psStatData (held for duration of stat iteration) */
168                         _UnrefAndMaybeDestroyStatEntry((void*)psStatData);
169                 }
170                 else
171                 {
172                         if (psStatData->ui32RefCount > 0)
173                         {
174                                 /* psStatData->pvData is NULL */
175                                 /* NB This is valid if the stat has no structure associated with it (eg. driver_stats, which prints totals stored in a number of global vars) */
176                         }
177                         if (psStatData->pvData)
178                         {
179                                 /* psStatData->ui32RefCount is zero */
180                                 PVR_DPF((PVR_DEBUGFS_PVR_DPF_LEVEL, "%s: Called when psStatData->ui32RefCount is %d", __FUNCTION__, psStatData->ui32RefCount));
181                         }
182                 }
183         }
184         else
185         {
186                 PVR_DPF((PVR_DEBUGFS_PVR_DPF_LEVEL, "%s: Called when psStatData is NULL", __FUNCTION__));
187         }
188 }
189
190 static void *_DebugFSStatisticSeqNext(struct seq_file *psSeqFile,
191                                       void *pvData,
192                                       loff_t *puiPosition)
193 {
194         PVR_DEBUGFS_DRIVER_STAT *psStatData = (PVR_DEBUGFS_DRIVER_STAT *)psSeqFile->private;
195         PVR_UNREFERENCED_PARAMETER(pvData);
196
197         if (psStatData)
198         {
199                 if (psStatData->pvData)
200                 {
201                         if (puiPosition)
202                         {
203                                 (*puiPosition)++;
204                         }
205                         else
206                         {
207                                 PVR_DPF((PVR_DEBUGFS_PVR_DPF_LEVEL, "%s: Called with puiPosition NULL", __FUNCTION__));
208                         }
209                 }
210                 else
211                 {
212                         /* psStatData->pvData is NULL */
213                         /* NB This is valid if the stat has no structure associated with it (eg. driver_stats, which prints totals stored in a number of global vars) */
214                 }
215         }
216         else
217         {
218                 PVR_DPF((PVR_DEBUGFS_PVR_DPF_LEVEL, "%s: Called when psStatData is NULL", __FUNCTION__));
219         }
220
221         return NULL;
222 }
223
224 static int _DebugFSStatisticSeqShow(struct seq_file *psSeqFile, void *pvData)
225 {
226         PVR_DEBUGFS_DRIVER_STAT *psStatData = (PVR_DEBUGFS_DRIVER_STAT *)pvData;
227
228         if (psStatData != NULL)
229         {
230                 psStatData->pfnStatsPrint((void*)psSeqFile, psStatData->pvData, _StatsSeqPrintf);
231                 return 0;
232         }
233         else
234         {
235                 PVR_DPF((PVR_DEBUGFS_PVR_DPF_LEVEL, "%s: Called when psStatData is NULL, returning -ENODATA(%d)", __FUNCTION__, -ENODATA));
236         }
237
238         return -ENODATA;
239 }
240
241 static struct seq_operations gsDebugFSStatisticReadOps =
242 {
243         .start = _DebugFSStatisticSeqStart,
244         .stop  = _DebugFSStatisticSeqStop,
245         .next  = _DebugFSStatisticSeqNext,
246         .show  = _DebugFSStatisticSeqShow,
247 };
248
249
250 /*************************************************************************/ /*!
251  Common internal API
252 */ /**************************************************************************/
253
254 static int _DebugFSFileOpen(struct inode *psINode, struct file *psFile)
255 {
256         PVR_DEBUGFS_PRIV_DATA *psPrivData;
257         int iResult = -EIO;
258         IMG_BOOL bRefRet = IMG_FALSE;
259         PVR_DEBUGFS_ENTRY_DATA *psDebugFSEntry = NULL;
260
261         PVR_ASSERT(psINode);
262         psPrivData = (PVR_DEBUGFS_PRIV_DATA *)psINode->i_private;
263
264         if (psPrivData)
265         {
266                 /* Check that psPrivData is still valid to use */
267                 if (psPrivData->bValid)
268                 {
269                         psDebugFSEntry = psPrivData->psDebugFSEntry;
270
271                         /* Take ref on stat entry before opening seq file - this ref will be dropped if we
272                          * fail to open the seq file or when we close it
273                          */
274                         if (psDebugFSEntry)
275                         {
276                                 bRefRet = _RefDebugFSEntry(psDebugFSEntry);
277                                 if (bRefRet)
278                                 {
279                                         iResult = seq_open(psFile, psPrivData->psReadOps);
280                                         if (iResult == 0)
281                                         {
282                                                 struct seq_file *psSeqFile = psFile->private_data;
283
284                                                 psSeqFile->private = psPrivData->pvData;
285                                         }
286                                         else
287                                         {
288                                                 /* Drop ref if we failed to open seq file */
289                                                 _UnrefAndMaybeDestroyDebugFSEntry(psDebugFSEntry);
290                                                 PVR_DPF((PVR_DBG_ERROR, "%s: Failed to seq_open psFile, returning %d", __FUNCTION__, iResult));
291                                         }
292                                 }
293                         }
294                 }
295         }
296
297         return iResult;
298 }
299
300 static int _DebugFSFileClose(struct inode *psINode, struct file *psFile)
301 {
302         int iResult;
303         PVR_DEBUGFS_PRIV_DATA *psPrivData = (PVR_DEBUGFS_PRIV_DATA *)psINode->i_private;
304         PVR_DEBUGFS_ENTRY_DATA *psDebugFSEntry = NULL;
305
306         if (psPrivData)
307         {
308                 psDebugFSEntry = psPrivData->psDebugFSEntry;
309         }
310         iResult = seq_release(psINode, psFile);
311         if (psDebugFSEntry)
312         {
313                 _UnrefAndMaybeDestroyDebugFSEntry(psDebugFSEntry);
314         }
315         return iResult;
316 }
317
318 static ssize_t _DebugFSFileWrite(struct file *psFile,
319                                  const char __user *pszBuffer,
320                                  size_t uiCount,
321                                  loff_t *puiPosition)
322 {
323         struct inode *psINode = psFile->f_path.dentry->d_inode;
324         PVR_DEBUGFS_PRIV_DATA *psPrivData = (PVR_DEBUGFS_PRIV_DATA *)psINode->i_private;
325
326         if (psPrivData->pfnWrite == NULL)
327         {
328                 PVR_DPF((PVR_DEBUGFS_PVR_DPF_LEVEL, "%s: Called for file '%s', which does not have pfnWrite defined, returning -EIO(%d)", __FUNCTION__, psFile->f_path.dentry->d_iname, -EIO));
329                 return -EIO;
330         }
331
332         return psPrivData->pfnWrite(pszBuffer, uiCount, *puiPosition, psPrivData->pvData);
333 }
334
335 static const struct file_operations gsPVRDebugFSFileOps =
336 {
337         .owner = THIS_MODULE,
338         .open = _DebugFSFileOpen,
339         .read = seq_read,
340         .write = _DebugFSFileWrite,
341         .llseek = seq_lseek,
342         .release = _DebugFSFileClose,
343 };
344
345
346 /*************************************************************************/ /*!
347  Public API
348 */ /**************************************************************************/
349
350 /*************************************************************************/ /*!
351 @Function       PVRDebugFSInit
352 @Description    Initialise PVR debugfs support. This should be called before
353                 using any PVRDebugFS functions.
354 @Return         int      On success, returns 0. Otherwise, returns an
355                          error code.
356 */ /**************************************************************************/
357 int PVRDebugFSInit(void)
358 {
359         PVR_ASSERT(gpsPVRDebugFSEntryDir == NULL);
360
361         mutex_init(&gDebugFSLock);
362
363         gpsPVRDebugFSEntryDir = debugfs_create_dir(PVR_DEBUGFS_DIR_NAME, NULL);
364         if (gpsPVRDebugFSEntryDir == NULL)
365         {
366                 PVR_DPF((PVR_DBG_ERROR,
367                          "%s: Cannot create '%s' debugfs root directory",
368                          __FUNCTION__, PVR_DEBUGFS_DIR_NAME));
369
370                 return -ENOMEM;
371         }
372
373         return 0;
374 }
375
376 /*************************************************************************/ /*!
377 @Function       PVRDebugFSDeInit
378 @Description    Deinitialise PVR debugfs support. This should be called only
379                 if PVRDebugFSInit() has already been called. All debugfs
380                 directories and entries should be removed otherwise this
381                 function will fail.
382 @Return         void
383 */ /**************************************************************************/
384 void PVRDebugFSDeInit(void)
385 {
386         debugfs_remove(gpsPVRDebugFSEntryDir);
387         gpsPVRDebugFSEntryDir = NULL;
388         mutex_destroy(&gDebugFSLock);
389 }
390
391 /*************************************************************************/ /*!
392 @Function       PVRDebugFSCreateEntryDir
393 @Description    Create a directory for debugfs entries that will be located
394                 under the root directory, as created by
395                 PVRDebugFSCreateEntries().
396 @Input          pszName      String containing the name for the directory.
397 @Input          psParentDir  The parent directory in which to create the new
398                              directory. This should either be NULL, meaning it
399                              should be created in the root directory, or a
400                              pointer to a directory as returned by this
401                              function.
402 @Output         ppsNewDir    On success, points to the newly created
403                              directory.
404 @Return         int          On success, returns 0. Otherwise, returns an
405                              error code.
406 */ /**************************************************************************/
407 int PVRDebugFSCreateEntryDir(IMG_CHAR *pszName,
408                                  PVR_DEBUGFS_DIR_DATA *psParentDir,
409                                  PVR_DEBUGFS_DIR_DATA **ppsNewDir)
410 {
411         PVR_DEBUGFS_DIR_DATA *psNewDir;
412
413         PVR_ASSERT(gpsPVRDebugFSEntryDir != NULL);
414
415         if (pszName == NULL || ppsNewDir == NULL)
416         {
417                 PVR_DPF((PVR_DBG_ERROR, "%s:   Invalid param", __FUNCTION__));
418                 return -EINVAL;
419         }
420
421         psNewDir = OSAllocMemstatMem(sizeof(*psNewDir));
422
423         if (psNewDir == IMG_NULL)
424         {
425                 PVR_DPF((PVR_DBG_ERROR,
426                          "%s: Cannot allocate memory for '%s' pvr_debugfs structure",
427                          __FUNCTION__, pszName));
428                 return -ENOMEM;
429         }
430
431         psNewDir->psParentDir = psParentDir;
432         psNewDir->psDir = debugfs_create_dir(pszName, (psNewDir->psParentDir) ? psNewDir->psParentDir->psDir : gpsPVRDebugFSEntryDir);
433
434         if (psNewDir->psDir == NULL)
435         {
436                 PVR_DPF((PVR_DBG_ERROR,
437                          "%s: Cannot create '%s' debugfs directory",
438                          __FUNCTION__, pszName));
439
440                 OSFreeMemstatMem(psNewDir);
441                 return -ENOMEM;
442         }
443
444         *ppsNewDir = psNewDir;
445         psNewDir->ui32RefCount = 1;
446
447         /* if parent directory is not gpsPVRDebugFSEntryDir, increment its refCount */
448         if (psNewDir->psParentDir)
449         {
450                 _RefDirEntry(psNewDir->psParentDir);
451         }
452         return 0;
453 }
454
455 /*************************************************************************/ /*!
456 @Function       PVRDebugFSRemoveEntryDir
457 @Description    Remove a directory that was created by
458                 PVRDebugFSCreateEntryDir(). Any directories or files created
459                 under the directory being removed should be removed first.
460 @Input          psDir        Pointer representing the directory to be removed.
461 @Return         void
462 */ /**************************************************************************/
463 void PVRDebugFSRemoveEntryDir(PVR_DEBUGFS_DIR_DATA *psDir)
464 {
465         _UnrefAndMaybeDestroyDirEntry(psDir);
466 }
467
468 /*************************************************************************/ /*!
469 @Function       PVRDebugFSCreateEntry
470 @Description    Create an entry in the specified directory.
471 @Input          pszName         String containing the name for the entry.
472 @Input          psParentDir     Pointer from PVRDebugFSCreateEntryDir()
473                                 representing the directory in which to create
474                                 the entry or NULL for the root directory.
475 @Input          psReadOps       Pointer to structure containing the necessary
476                                 functions to read from the entry.
477 @Input          pfnWrite        Callback function used to write to the entry.
478 @Input          pvData          Private data to be passed to the read
479                                 functions, in the seq_file private member, and
480                                 the write function callback.
481 @Output         ppsNewEntry     On success, points to the newly created entry.
482 @Return         int             On success, returns 0. Otherwise, returns an
483                                 error code.
484 */ /**************************************************************************/
485 int PVRDebugFSCreateEntry(const char *pszName,
486                           PVR_DEBUGFS_DIR_DATA *psParentDir,
487                           struct seq_operations *psReadOps,
488                           PVRSRV_ENTRY_WRITE_FUNC *pfnWrite,
489                           void *pvData,
490                           PVR_DEBUGFS_ENTRY_DATA **ppsNewEntry)
491 {
492         PVR_DEBUGFS_PRIV_DATA *psPrivData;
493         PVR_DEBUGFS_ENTRY_DATA *psDebugFSEntry;
494         struct dentry *psEntry;
495         umode_t uiMode;
496
497         PVR_ASSERT(gpsPVRDebugFSEntryDir != NULL);
498
499         psPrivData = OSAllocMemstatMem(sizeof(*psPrivData));
500         if (psPrivData == NULL)
501         {
502                 return -ENOMEM;
503         }
504         psDebugFSEntry = OSAllocMemstatMem(sizeof(*psDebugFSEntry));
505         if (psDebugFSEntry == NULL)
506         {
507                 OSFreeMemstatMem(psPrivData);
508                 return -ENOMEM;
509         }
510
511         psPrivData->psReadOps = psReadOps;
512         psPrivData->pfnWrite = pfnWrite;
513         psPrivData->pvData = (void*)pvData;
514         psPrivData->bValid = IMG_TRUE;
515         /* Store ptr to debugFSEntry in psPrivData, so a ref can be taken on it
516          * when the client opens a file */
517         psPrivData->psDebugFSEntry = psDebugFSEntry;
518
519         uiMode = S_IFREG;
520
521         if (psReadOps != NULL)
522         {
523                 uiMode |= S_IRUGO;
524         }
525
526         if (pfnWrite != NULL)
527         {
528                 uiMode |= S_IWUSR;
529         }
530
531         psDebugFSEntry->psParentDir = psParentDir;
532         psDebugFSEntry->ui32RefCount = 1;
533         psDebugFSEntry->psStatData = (PVR_DEBUGFS_DRIVER_STAT*)pvData;
534
535         if (psDebugFSEntry->psParentDir)
536         {
537                 /* increment refCount of parent directory */
538                 _RefDirEntry(psDebugFSEntry->psParentDir);
539         }
540
541         psEntry = debugfs_create_file(pszName,
542                                           uiMode,
543                                           (psParentDir != NULL) ? psParentDir->psDir : gpsPVRDebugFSEntryDir,
544                                           psPrivData,
545                                           &gsPVRDebugFSFileOps);
546         if (IS_ERR(psEntry))
547         {
548                 PVR_DPF((PVR_DBG_ERROR,
549                          "%s: Cannot create debugfs '%s' file",
550                          __FUNCTION__, pszName));
551
552                 return PTR_ERR(psEntry);
553         }
554
555         psDebugFSEntry->psEntry = psEntry;
556         *ppsNewEntry = (void*)psDebugFSEntry;
557
558         return 0;
559 }
560
561 /*************************************************************************/ /*!
562 @Function       PVRDebugFSRemoveEntry
563 @Description    Removes an entry that was created by PVRDebugFSCreateEntry().
564 @Input          psDebugFSEntry  Pointer representing the entry to be removed.
565 @Return         void
566 */ /**************************************************************************/
567 void PVRDebugFSRemoveEntry(PVR_DEBUGFS_ENTRY_DATA *psDebugFSEntry)
568 {
569         _UnrefAndMaybeDestroyDebugFSEntry(psDebugFSEntry);
570 }
571
572 /*************************************************************************/ /*!
573 @Function       PVRDebugFSCreateStatisticEntry
574 @Description    Create a statistic entry in the specified directory.
575 @Input          pszName         String containing the name for the entry.
576 @Input          psDir           Pointer from PVRDebugFSCreateEntryDir()
577                                 representing the directory in which to create
578                                 the entry or NULL for the root directory.
579 @Input          pfnStatsPrint   A callback function used to print all the
580                                 statistics when reading from the statistic
581                                 entry.
582 @Input          pfnIncStatMemRefCount   A callback function used take a
583                                                                                 reference on the memory backing the
584                                                 statistic.
585 @Input          pfnDecStatMemRefCount   A callback function used drop a
586                                                                                 reference on the memory backing the
587                                                 statistic.
588 @Input          pvData          Private data to be passed to the provided
589                                 callback function.
590
591 @Return         PVR_DEBUGFS_DRIVER_STAT*   On success, a pointer representing
592                                                                                    the newly created statistic entry.
593                                                                                    Otherwise, NULL.
594 */ /**************************************************************************/
595 PVR_DEBUGFS_DRIVER_STAT *PVRDebugFSCreateStatisticEntry(const char *pszName,
596                                          PVR_DEBUGFS_DIR_DATA *psDir,
597                                      OS_STATS_PRINT_FUNC *pfnStatsPrint,
598                                          PVRSRV_INC_STAT_MEM_REFCOUNT_FUNC      *pfnIncStatMemRefCount,
599                                          PVRSRV_INC_STAT_MEM_REFCOUNT_FUNC      *pfnDecStatMemRefCount,
600                                          void *pvData)
601 {
602         PVR_DEBUGFS_DRIVER_STAT *psStatData;
603         PVR_DEBUGFS_ENTRY_DATA * psDebugFSEntry;
604
605         int iResult;
606
607         if (pszName == NULL || pfnStatsPrint == NULL)
608         {
609                 return NULL;
610         }
611         if ((pfnIncStatMemRefCount != NULL || pfnDecStatMemRefCount != NULL) && pvData == NULL)
612         {
613                 return NULL;
614         }
615
616         psStatData = OSAllocMemstatZMem(sizeof(*psStatData));
617         if (psStatData == NULL)
618         {
619                 return NULL;
620         }
621
622         psStatData->pvData = pvData;
623         psStatData->pfnStatsPrint = pfnStatsPrint;
624         psStatData->pfnIncStatMemRefCount = pfnIncStatMemRefCount;
625         psStatData->pfnDecStatMemRefCount = pfnDecStatMemRefCount;
626         psStatData->ui32RefCount = 1;
627
628         iResult = PVRDebugFSCreateEntry(pszName,
629                                         psDir,
630                                         &gsDebugFSStatisticReadOps,
631                                         NULL,
632                                         psStatData,
633                                         &psDebugFSEntry);
634         if (iResult != 0)
635         {
636                 OSFreeMemstatMem(psStatData);
637                 return NULL;
638         }
639         psStatData->pvDebugFSEntry = (void*)psDebugFSEntry;
640
641         if (pfnIncStatMemRefCount)
642         {
643                 /* call function to take reference on the memory holding the stat */
644                 psStatData->pfnIncStatMemRefCount((void*)psStatData->pvData);
645         }
646
647         psDebugFSEntry->ui32RefCount = 1;
648
649         return psStatData;
650 }
651
652 /*************************************************************************/ /*!
653 @Function       PVRDebugFSRemoveStatisticEntry
654 @Description    Removes a statistic entry that was created by
655                 PVRDebugFSCreateStatisticEntry().
656 @Input          psStatEntry  Pointer representing the statistic entry to be
657                                  removed.
658 @Return         void
659 */ /**************************************************************************/
660 void PVRDebugFSRemoveStatisticEntry(PVR_DEBUGFS_DRIVER_STAT *psStatEntry)
661 {
662         /* drop reference on pvStatEntry*/
663         _UnrefAndMaybeDestroyStatEntry(psStatEntry);
664 }
665
666 static void _RefDirEntry(PVR_DEBUGFS_DIR_DATA *psDirEntry)
667 {
668         mutex_lock(&gDebugFSLock);
669
670         if (psDirEntry->ui32RefCount > 0)
671         {
672                 /* Increment refCount */
673                 psDirEntry->ui32RefCount++;
674         }
675         else
676         {
677                 PVR_DPF((PVR_DEBUGFS_PVR_DPF_LEVEL, "%s: Called to ref psDirEntry '%s' when ui32RefCount is zero", __FUNCTION__, psDirEntry->psDir->d_iname));
678         }
679
680         mutex_unlock(&gDebugFSLock);
681 }
682
683 static void _UnrefAndMaybeDestroyDirEntryWhileLocked(PVR_DEBUGFS_DIR_DATA *psDirEntry)
684 {
685         if (psDirEntry->ui32RefCount > 0)
686         {
687                 /* Decrement refCount and free if now zero */
688                 if (--psDirEntry->ui32RefCount == 0)
689                 {
690                         /* if parent directory is not gpsPVRDebugFSEntryDir, decrement its refCount */
691                         debugfs_remove(psDirEntry->psDir);
692                         if (psDirEntry->psParentDir)
693                         {
694                                 _UnrefAndMaybeDestroyDirEntryWhileLocked(psDirEntry->psParentDir);
695                         }
696                         OSFreeMemstatMem(psDirEntry);
697                 }
698         }
699         else
700         {
701                 PVR_DPF((PVR_DEBUGFS_PVR_DPF_LEVEL, "%s: Called to unref psDirEntry '%s' when ui32RefCount is zero", __FUNCTION__, psDirEntry->psDir->d_iname));
702         }
703 }
704
705 static void _UnrefAndMaybeDestroyDirEntry(PVR_DEBUGFS_DIR_DATA *psDirEntry)
706 {
707         mutex_lock(&gDebugFSLock);
708
709         if (psDirEntry->ui32RefCount > 0)
710         {
711                 /* Decrement refCount and free if now zero */
712                 if (--psDirEntry->ui32RefCount == 0)
713                 {
714                         /* if parent directory is not gpsPVRDebugFSEntryDir, decrement its refCount */
715                         debugfs_remove(psDirEntry->psDir);
716                         if (psDirEntry->psParentDir)
717                         {
718                                 _UnrefAndMaybeDestroyDirEntryWhileLocked(psDirEntry->psParentDir);
719                         }
720                         OSFreeMemstatMem(psDirEntry);
721                 }
722         }
723         else
724         {
725                 PVR_DPF((PVR_DEBUGFS_PVR_DPF_LEVEL, "%s: Called to unref psDirEntry '%s' when ui32RefCount is zero", __FUNCTION__, psDirEntry->psDir->d_iname));
726         }
727
728         mutex_unlock(&gDebugFSLock);
729 }
730
731 static IMG_BOOL _RefDebugFSEntry(PVR_DEBUGFS_ENTRY_DATA *psDebugFSEntry)
732 {
733         IMG_BOOL bResult = IMG_FALSE;
734
735         PVR_ASSERT(psDebugFSEntry != NULL);
736
737         mutex_lock(&gDebugFSLock);
738
739         bResult = (psDebugFSEntry->ui32RefCount > 0);
740         if (bResult)
741         {
742                 /* Increment refCount of psDebugFSEntry */
743                 psDebugFSEntry->ui32RefCount++;
744         }
745
746         mutex_unlock(&gDebugFSLock);
747
748         return bResult;
749 }
750
751 static void _UnrefAndMaybeDestroyDebugFSEntry(PVR_DEBUGFS_ENTRY_DATA *psDebugFSEntry)
752 {
753         mutex_lock(&gDebugFSLock);
754         /* Decrement refCount of psDebugFSEntry, and free if now zero */
755         PVR_ASSERT(psDebugFSEntry != IMG_NULL);
756
757         if (psDebugFSEntry->ui32RefCount > 0)
758         {
759                 if (--psDebugFSEntry->ui32RefCount == 0)
760                 {
761                         struct dentry *psEntry = psDebugFSEntry->psEntry;
762
763                         if (psEntry)
764                         {
765                                 /* Free any private data that was provided to debugfs_create_file() */
766                                 if (psEntry->d_inode->i_private != NULL)
767                                 {
768                                         PVR_DEBUGFS_PRIV_DATA *psPrivData = (PVR_DEBUGFS_PRIV_DATA*)psDebugFSEntry->psEntry->d_inode->i_private;
769
770                                         psPrivData->bValid = IMG_FALSE;
771                                         psPrivData->psDebugFSEntry = NULL;
772                                         OSFreeMemstatMem(psEntry->d_inode->i_private);
773                                 }
774                                 debugfs_remove(psEntry);
775                         }
776                         /* decrement refcount of parent directory */
777                         if (psDebugFSEntry->psParentDir)
778                         {
779                                 _UnrefAndMaybeDestroyDirEntryWhileLocked(psDebugFSEntry->psParentDir);
780                         }
781
782                         /* now free the memory allocated for psDebugFSEntry */
783                         OSFreeMemstatMem(psDebugFSEntry);
784                 }
785         }
786         else
787         {
788                 PVR_DPF((PVR_DEBUGFS_PVR_DPF_LEVEL, "%s: Called to unref psDebugFSEntry '%s' when ui32RefCount is zero", __FUNCTION__, psDebugFSEntry->psEntry->d_iname));
789         }
790
791         mutex_unlock(&gDebugFSLock);
792 }
793
794 static IMG_BOOL _RefStatEntry(PVR_DEBUGFS_DRIVER_STAT *psStatEntry)
795 {
796         IMG_BOOL bResult = IMG_FALSE;
797
798         PVR_ASSERT(psStatEntry != NULL);
799
800         mutex_lock(&gDebugFSLock);
801
802         bResult = (psStatEntry->ui32RefCount > 0);
803         if (bResult)
804         {
805                 /* Increment refCount of psStatEntry */
806                 psStatEntry->ui32RefCount++;
807         }
808         else
809         {
810                 PVR_DPF((PVR_DEBUGFS_PVR_DPF_LEVEL, "%s: Called to ref psStatEntry '%s' when ui32RefCount is zero", __FUNCTION__, psStatEntry->pvDebugFSEntry->psEntry->d_iname));
811         }
812
813         mutex_unlock(&gDebugFSLock);
814
815         return bResult;
816 }
817
818 static IMG_BOOL _UnrefAndMaybeDestroyStatEntry(PVR_DEBUGFS_DRIVER_STAT *psStatEntry)
819 {
820         IMG_BOOL bResult;
821
822         PVR_ASSERT(psStatEntry != IMG_NULL);
823
824         mutex_lock(&gDebugFSLock);
825
826         bResult = (psStatEntry->ui32RefCount > 0);
827
828         if (bResult)
829         {
830                 /* Decrement refCount of psStatData, and free if now zero */
831                 if (--psStatEntry->ui32RefCount == 0)
832                 {
833                         mutex_unlock(&gDebugFSLock);
834
835                         if (psStatEntry->pvDebugFSEntry)
836                         {
837                                 _UnrefAndMaybeDestroyDebugFSEntry((PVR_DEBUGFS_ENTRY_DATA*)psStatEntry->pvDebugFSEntry);
838                         }
839                         if (psStatEntry->pfnDecStatMemRefCount)
840                         {
841                                 /* call function to drop reference on the memory holding the stat */
842                                 psStatEntry->pfnDecStatMemRefCount((void*)psStatEntry->pvData);
843                         }
844                 }
845                 else
846                 {
847                         mutex_unlock(&gDebugFSLock);
848                 }
849         }
850         else
851         {
852                 PVR_DPF((PVR_DEBUGFS_PVR_DPF_LEVEL, "%s: Called to unref psStatEntry '%s' when ui32RefCount is zero", __FUNCTION__, psStatEntry->pvDebugFSEntry->psEntry->d_iname));
853                 mutex_unlock(&gDebugFSLock);
854         }
855
856         return bResult;
857 }