RK3368 GPU version: Rogue L 0.22
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / rogue / 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
49 #define PVR_DEBUGFS_DIR_NAME "pvr"
50
51 static struct dentry *gpsPVRDebugFSEntryDir = NULL;
52
53 /* Lock used when adjusting refCounts and deleting entries */
54 static struct mutex gDebugFSLock;
55
56 /*************************************************************************/ /*!
57  Statistic entry read functions
58 */ /**************************************************************************/
59
60 typedef struct _PVR_DEBUGFS_DRIVER_STAT_
61 {
62         void                            *pvData;
63         PVRSRV_GET_NEXT_STAT_FUNC       *pfnGetNextStat;
64         PVRSRV_INC_STAT_MEM_REFCOUNT_FUNC       *pfnIncStatMemRefCount;
65         PVRSRV_DEC_STAT_MEM_REFCOUNT_FUNC       *pfnDecStatMemRefCount;
66         IMG_UINT32                      ui32RefCount;
67         IMG_INT32                       i32StatValue;
68         IMG_CHAR                        *pszStatFormat;
69         PVR_DEBUGFS_ENTRY_DATA  *pvDebugFSEntry;
70 } PVR_DEBUGFS_DRIVER_STAT;
71 typedef struct _PVR_DEBUGFS_DIR_DATA_
72 {
73         struct dentry *psDir;
74         PVR_DEBUGFS_DIR_DATA *psParentDir;
75         IMG_UINT32      ui32RefCount;
76 } PVR_DEBUGFS_DIR_DATA;
77 typedef struct _PVR_DEBUGFS_ENTRY_DATA_
78 {
79         struct dentry *psEntry;
80         PVR_DEBUGFS_DIR_DATA *psParentDir;
81         IMG_UINT32      ui32RefCount;
82         PVR_DEBUGFS_DRIVER_STAT *psStatData;
83 } PVR_DEBUGFS_ENTRY_DATA;
84 typedef struct _PVR_DEBUGFS_PRIV_DATA_
85 {
86         struct seq_operations   *psReadOps;
87         PVRSRV_ENTRY_WRITE_FUNC *pfnWrite;
88         void                    *pvData;
89         IMG_BOOL                bValid;
90         PVR_DEBUGFS_ENTRY_DATA *psDebugFSEntry;
91 } PVR_DEBUGFS_PRIV_DATA;
92 static void _RefDirEntry(PVR_DEBUGFS_DIR_DATA *psDirEntry);
93 static void _UnrefAndMaybeDestroyDirEntry(PVR_DEBUGFS_DIR_DATA *psDirEntry);
94 static void _UnrefAndMaybeDestroyDirEntryWhileLocked(PVR_DEBUGFS_DIR_DATA *psDirEntry);
95 static IMG_BOOL _RefDebugFSEntryNoLock(PVR_DEBUGFS_ENTRY_DATA *psDebugFSEntry);
96 static void _UnrefAndMaybeDestroyDebugFSEntry(PVR_DEBUGFS_ENTRY_DATA *psDebugFSEntry);
97 static IMG_BOOL _RefStatEntry(PVR_DEBUGFS_DRIVER_STAT *psStatEntry);
98 static IMG_BOOL _UnrefAndMaybeDestroyStatEntry(PVR_DEBUGFS_DRIVER_STAT *psStatEntry);
99
100 static void *_DebugFSStatisticSeqStart(struct seq_file *psSeqFile, loff_t *puiPosition)
101 {
102         PVR_DEBUGFS_DRIVER_STAT *psStatData = (PVR_DEBUGFS_DRIVER_STAT *)psSeqFile->private;
103         IMG_BOOL bResult = IMG_FALSE;
104
105         if (psStatData)
106         {
107                 if (psStatData->pvData)
108                 {
109                         /* take reference on psStatData (for duration of stat iteration) */
110                         if (!_RefStatEntry((void*)psStatData))
111
112                         {
113                                 return NULL;
114                         }
115
116                 }
117                 bResult = psStatData->pfnGetNextStat(psStatData->pvData,
118                                                         (IMG_UINT32)(*puiPosition),
119                                                         &psStatData->i32StatValue,
120                                                         &psStatData->pszStatFormat);
121         }
122
123         return bResult ? psStatData : NULL;
124 }
125
126 static void _DebugFSStatisticSeqStop(struct seq_file *psSeqFile, void *pvData)
127 {
128         PVR_DEBUGFS_DRIVER_STAT *psStatData = (PVR_DEBUGFS_DRIVER_STAT *)psSeqFile->private;
129
130         if (psStatData)
131         {
132                 /* drop ref taken on stat memory, and if it is now zero, be sure we don't try to read it again */
133                 if ((psStatData->ui32RefCount > 0) && (psStatData->pvData))
134                 {
135                         /* drop reference on psStatData (held for duration of stat iteration) */
136                         _UnrefAndMaybeDestroyStatEntry((void*)psStatData);
137                 }
138         }
139 }
140
141 static void *_DebugFSStatisticSeqNext(struct seq_file *psSeqFile,
142                                           void *pvData,
143                                           loff_t *puiPosition)
144 {
145         PVR_DEBUGFS_DRIVER_STAT *psStatData = (PVR_DEBUGFS_DRIVER_STAT *)psSeqFile->private;
146         IMG_BOOL bResult = IMG_FALSE;
147
148         if (puiPosition && psStatData && psStatData->pvData)
149         {
150                 bResult = psStatData->pfnGetNextStat(psStatData->pvData,
151                                                      (IMG_UINT32)(*puiPosition) + 1,
152                                                      &psStatData->i32StatValue,
153                                                      &psStatData->pszStatFormat);
154
155                 if (psStatData->pszStatFormat)
156                 {
157                         IMG_CHAR tmp_buff[1];
158                         IMG_INT32 i32Size = snprintf(tmp_buff, 0, psStatData->pszStatFormat,
159                                                      psStatData->i32StatValue);
160                         if ((i32Size < 0) || (psSeqFile->size - psSeqFile->count < i32Size))
161                                 return NULL;
162                 }
163
164                 if (bResult)
165                         (*puiPosition)++;
166
167         }
168         return bResult ? psStatData : NULL;
169 }
170
171 static int _DebugFSStatisticSeqShow(struct seq_file *psSeqFile, void *pvData)
172 {
173         PVR_DEBUGFS_DRIVER_STAT *psStatData = (PVR_DEBUGFS_DRIVER_STAT *)pvData;
174
175         if (psStatData != NULL)
176         {
177                 int ret;
178
179                 if (psStatData->pszStatFormat == NULL)
180                 {
181                         return -EINVAL;
182                 }
183
184                 if ((ret = seq_printf(psSeqFile, psStatData->pszStatFormat, psStatData->i32StatValue)))
185                 {
186                         PVR_DPF((PVR_DBG_WARNING, "%s: Overflow when writing to seq_file (%d).", __FUNCTION__, ret));
187                         return ret;
188                 }
189         }
190
191         return 0;
192 }
193
194 static struct seq_operations gsDebugFSStatisticReadOps =
195 {
196         .start = _DebugFSStatisticSeqStart,
197         .stop = _DebugFSStatisticSeqStop,
198         .next = _DebugFSStatisticSeqNext,
199         .show = _DebugFSStatisticSeqShow,
200 };
201
202
203 /*************************************************************************/ /*!
204  Common internal API
205 */ /**************************************************************************/
206
207 static int _DebugFSFileOpen(struct inode *psINode, struct file *psFile)
208 {
209         PVR_DEBUGFS_PRIV_DATA *psPrivData;
210         int iResult = -EIO;
211         IMG_BOOL bRefRet = IMG_FALSE;
212         PVR_DEBUGFS_ENTRY_DATA *psDebugFSEntry = NULL;
213
214         mutex_lock(&gDebugFSLock);
215
216         PVR_ASSERT(psINode);
217         psPrivData = (PVR_DEBUGFS_PRIV_DATA *)psINode->i_private;
218         if (psPrivData)
219         {
220                 /* Check that psPrivData is still valid to use */
221                 if (psPrivData->bValid)
222                 {
223                         psDebugFSEntry = psPrivData->psDebugFSEntry;
224
225                         /* Take ref on stat entry before opening seq file - this ref will be dropped if we
226                          * fail to open the seq file or when we close it
227                          */
228                         if (psDebugFSEntry)
229                         {
230                                 bRefRet = _RefDebugFSEntryNoLock(psDebugFSEntry);
231                                 mutex_unlock(&gDebugFSLock);
232                                 if (bRefRet)
233                                 {
234                                         iResult = seq_open(psFile, psPrivData->psReadOps);
235                                         if (iResult == 0)
236                                         {
237                                                 struct seq_file *psSeqFile = psFile->private_data;
238
239                                                 psSeqFile->private = psPrivData->pvData;
240                                         }
241                                         else
242                                         {
243                                                 /* Drop ref if we failed to open seq file */
244                                                 _UnrefAndMaybeDestroyDebugFSEntry(psDebugFSEntry);
245                                                 PVR_DPF((PVR_DBG_ERROR, "%s: Failed to seq_open psFile, returning %d", __FUNCTION__, iResult));
246                                         }
247                                 }
248                         }
249                         else
250                         {
251                                 mutex_unlock(&gDebugFSLock);
252                         }
253                 }
254                 else
255                 {
256                         mutex_unlock(&gDebugFSLock);
257                 }
258         }
259         else
260         {
261                 mutex_unlock(&gDebugFSLock);
262         }
263
264         return iResult;
265 }
266
267 static int _DebugFSFileClose(struct inode *psINode, struct file *psFile)
268 {
269         int iResult;
270         PVR_DEBUGFS_PRIV_DATA *psPrivData = (PVR_DEBUGFS_PRIV_DATA *)psINode->i_private;
271         PVR_DEBUGFS_ENTRY_DATA *psDebugFSEntry = NULL;
272
273         if (psPrivData)
274         {
275                 psDebugFSEntry = psPrivData->psDebugFSEntry;
276         }
277         iResult = seq_release(psINode, psFile);
278         if (psDebugFSEntry)
279         {
280                 _UnrefAndMaybeDestroyDebugFSEntry(psDebugFSEntry);
281         }
282         return iResult;
283 }
284
285 static ssize_t _DebugFSFileWrite(struct file *psFile,
286                                  const char __user *pszBuffer,
287                                  size_t uiCount,
288                                  loff_t *puiPosition)
289 {
290         struct inode *psINode = psFile->f_path.dentry->d_inode;
291         PVR_DEBUGFS_PRIV_DATA *psPrivData = (PVR_DEBUGFS_PRIV_DATA *)psINode->i_private;
292
293         if (psPrivData->pfnWrite == NULL)
294         {
295                 PVR_DPF((PVR_DBG_ERROR, "%s: Called for file '%s', which does not have pfnWrite defined, returning -EIO(%d)", __FUNCTION__, psFile->f_path.dentry->d_iname, -EIO));
296                 return -EIO;
297         }
298
299         return psPrivData->pfnWrite(pszBuffer, uiCount, *puiPosition, psPrivData->pvData);
300 }
301
302 static const struct file_operations gsPVRDebugFSFileOps =
303 {
304         .owner = THIS_MODULE,
305         .open = _DebugFSFileOpen,
306         .read = seq_read,
307         .write = _DebugFSFileWrite,
308         .llseek = seq_lseek,
309         .release = _DebugFSFileClose,
310 };
311
312
313 /*************************************************************************/ /*!
314  Public API
315 */ /**************************************************************************/
316
317 /*************************************************************************/ /*!
318 @Function       PVRDebugFSInit
319 @Description    Initialise PVR debugfs support. This should be called before
320                 using any PVRDebugFS functions.
321 @Return         int      On success, returns 0. Otherwise, returns an
322                          error code.
323 */ /**************************************************************************/
324 int PVRDebugFSInit(void)
325 {
326         PVR_ASSERT(gpsPVRDebugFSEntryDir == NULL);
327
328         mutex_init(&gDebugFSLock);
329
330         gpsPVRDebugFSEntryDir = debugfs_create_dir(PVR_DEBUGFS_DIR_NAME, NULL);
331         if (gpsPVRDebugFSEntryDir == NULL)
332         {
333                 PVR_DPF((PVR_DBG_ERROR,
334                          "%s: Cannot create '%s' debugfs root directory",
335                          __FUNCTION__, PVR_DEBUGFS_DIR_NAME));
336
337                 return -ENOMEM;
338         }
339
340         return 0;
341 }
342
343 /*************************************************************************/ /*!
344 @Function       PVRDebugFSDeInit
345 @Description    Deinitialise PVR debugfs support. This should be called only
346                 if PVRDebugFSInit() has already been called. All debugfs
347                 directories and entries should be removed otherwise this
348                 function will fail.
349 @Return         void
350 */ /**************************************************************************/
351 void PVRDebugFSDeInit(void)
352 {
353         PVR_ASSERT(gpsPVRDebugFSEntryDir != NULL);
354
355         debugfs_remove(gpsPVRDebugFSEntryDir);
356         gpsPVRDebugFSEntryDir = NULL;
357         mutex_destroy(&gDebugFSLock);
358 }
359
360 /*************************************************************************/ /*!
361 @Function       PVRDebugFSCreateEntryDir
362 @Description    Create a directory for debugfs entries that will be located
363                 under the root directory, as created by
364                 PVRDebugFSCreateEntries().
365 @Input          pszName      String containing the name for the directory.
366 @Input          psParentDir  The parent directory in which to create the new
367                              directory. This should either be NULL, meaning it
368                              should be created in the root directory, or a
369                              pointer to a directory as returned by this
370                              function.
371 @Output         ppsDir       On success, points to the newly created
372                              directory.
373 @Return         int          On success, returns 0. Otherwise, returns an
374                              error code.
375 */ /**************************************************************************/
376 int PVRDebugFSCreateEntryDir(IMG_CHAR *pszName,
377                                  PVR_DEBUGFS_DIR_DATA *psParentDir,
378                                  PVR_DEBUGFS_DIR_DATA **ppsNewDir)
379 {
380         PVR_DEBUGFS_DIR_DATA *psNewDir;
381
382         PVR_ASSERT(gpsPVRDebugFSEntryDir != NULL);
383
384         if (pszName == NULL || ppsNewDir == NULL)
385         {
386                 PVR_DPF((PVR_DBG_ERROR, "%s:   Invalid param", __FUNCTION__));
387                 return -EINVAL;
388         }
389
390         psNewDir = kmalloc(sizeof(*psNewDir), GFP_KERNEL);
391
392         if (psNewDir == IMG_NULL)
393         {
394                 PVR_DPF((PVR_DBG_ERROR,
395                          "%s: Cannot allocate memory for '%s' pvr_debugfs structure",
396                          __FUNCTION__, pszName));
397                 return -ENOMEM;
398         }
399
400         psNewDir->psParentDir = psParentDir;
401         psNewDir->psDir = debugfs_create_dir(pszName, (psNewDir->psParentDir) ? psNewDir->psParentDir->psDir : gpsPVRDebugFSEntryDir);
402
403         if (psNewDir->psDir == NULL)
404         {
405                 PVR_DPF((PVR_DBG_ERROR,
406                          "%s: Cannot create '%s' debugfs directory",
407                          __FUNCTION__, pszName));
408
409                 kfree(psNewDir);
410                 return -ENOMEM;
411         }
412
413         *ppsNewDir = psNewDir;
414         psNewDir->ui32RefCount = 1;
415
416         /* if parent directory is not gpsPVRDebugFSEntryDir, increment its refCount */
417         if (psNewDir->psParentDir)
418         {
419                 _RefDirEntry(psNewDir->psParentDir);
420         }
421         return 0;
422 }
423
424 /*************************************************************************/ /*!
425 @Function       PVRDebugFSRemoveEntryDir
426 @Description    Remove a directory that was created by
427                 PVRDebugFSCreateEntryDir(). Any directories or files created
428                 under the directory being removed should be removed first.
429 @Input          psDir        Pointer representing the directory to be removed.
430 @Return         void
431 */ /**************************************************************************/
432 void PVRDebugFSRemoveEntryDir(PVR_DEBUGFS_DIR_DATA *psDir)
433 {
434         _UnrefAndMaybeDestroyDirEntry(psDir);
435 }
436
437 /*************************************************************************/ /*!
438 @Function       PVRDebugFSCreateEntry
439 @Description    Create an entry in the specified directory.
440 @Input          pszName         String containing the name for the entry.
441 @Input          psParentDir     Pointer from PVRDebugFSCreateEntryDir()
442                                 representing the directory in which to create
443                                 the entry or NULL for the root directory.
444 @Input          psReadOps       Pointer to structure containing the necessary
445                                 functions to read from the entry.
446 @Input          pfnWrite        Callback function used to write to the entry.
447 @Input          pvData          Private data to be passed to the read
448                                 functions, in the seq_file private member, and
449                                 the write function callback.
450 @Output         ppsNewEntry     On success, points to the newly created entry.
451 @Return         int             On success, returns 0. Otherwise, returns an
452                                 error code.
453 */ /**************************************************************************/
454 int PVRDebugFSCreateEntry(const char *pszName,
455                           PVR_DEBUGFS_DIR_DATA *psParentDir,
456                           struct seq_operations *psReadOps,
457                           PVRSRV_ENTRY_WRITE_FUNC *pfnWrite,
458                           void *pvData,
459                           PVR_DEBUGFS_ENTRY_DATA **ppsNewEntry)
460 {
461         PVR_DEBUGFS_PRIV_DATA *psPrivData;
462         PVR_DEBUGFS_ENTRY_DATA *psDebugFSEntry;
463         struct dentry *psEntry;
464         umode_t uiMode;
465
466         PVR_ASSERT(gpsPVRDebugFSEntryDir != NULL);
467
468         psPrivData = kmalloc(sizeof(*psPrivData), GFP_KERNEL);
469         if (psPrivData == NULL)
470         {
471                 return -ENOMEM;
472         }
473         psDebugFSEntry = kmalloc(sizeof(*psDebugFSEntry), GFP_KERNEL);
474         if (psDebugFSEntry == NULL)
475         {
476                 kfree(psPrivData);
477                 return -ENOMEM;
478         }
479
480         psPrivData->psReadOps = psReadOps;
481         psPrivData->pfnWrite = pfnWrite;
482         psPrivData->pvData = (void*)pvData;
483         psPrivData->bValid = IMG_TRUE;
484         /* Store ptr to debugFSEntry in psPrivData, so a ref can be taken on it
485          * when the client opens a file */
486         psPrivData->psDebugFSEntry = psDebugFSEntry;
487
488         uiMode = S_IFREG;
489
490         if (psReadOps != NULL)
491         {
492                 uiMode |= S_IRUGO;
493         }
494
495         if (pfnWrite != NULL)
496         {
497                 uiMode |= S_IWUSR;
498         }
499
500         psDebugFSEntry->psParentDir = psParentDir;
501         psDebugFSEntry->ui32RefCount = 1;
502         psDebugFSEntry->psStatData = (PVR_DEBUGFS_DRIVER_STAT*)pvData;
503
504         if (psDebugFSEntry->psParentDir)
505         {
506                 /* increment refCount of parent directory */
507                 _RefDirEntry(psDebugFSEntry->psParentDir);
508         }
509
510         psEntry = debugfs_create_file(pszName,
511                                       uiMode,
512                                       (psParentDir != NULL) ? psParentDir->psDir : gpsPVRDebugFSEntryDir,
513                                       psPrivData,
514                                       &gsPVRDebugFSFileOps);
515         if (IS_ERR(psEntry))
516         {
517                 PVR_DPF((PVR_DBG_ERROR,
518                          "%s: Cannot create debugfs '%s' file",
519                          __FUNCTION__, pszName));
520
521                 return PTR_ERR(psEntry);
522         }
523
524         psDebugFSEntry->psEntry = psEntry;
525         *ppsNewEntry = (void*)psDebugFSEntry;
526
527         return 0;
528 }
529
530 /*************************************************************************/ /*!
531 @Function       PVRDebugFSRemoveEntry
532 @Description    Removes an entry that was created by PVRDebugFSCreateEntry().
533 @Input          psDebugFSEntry  Pointer representing the entry to be removed.
534 @Return         void
535 */ /**************************************************************************/
536 void PVRDebugFSRemoveEntry(PVR_DEBUGFS_ENTRY_DATA *psDebugFSEntry)
537 {
538         _UnrefAndMaybeDestroyDebugFSEntry(psDebugFSEntry);
539 }
540
541 /*************************************************************************/ /*!
542 @Function       PVRDebugFSCreateStatisticEntry
543 @Description    Create a statistic entry in the specified directory.
544 @Input          pszName         String containing the name for the entry.
545 @Input          psDir           Pointer from PVRDebugFSCreateEntryDir()
546                                 representing the directory in which to create
547                                 the entry or NULL for the root directory.
548 @Input          pfnStatsPrint   A callback function used to print all the
549                                 statistics when reading from the statistic
550                                 entry.
551 @Input          pfnIncStatMemRefCount   A callback function used take a
552                                                                                 reference on the memory backing the
553                                                 statistic.
554 @Input          pfnDecStatMemRefCount   A callback function used drop a
555                                                                                 reference on the memory backing the
556                                                 statistic.
557 @Input          pvData          Private data to be passed to the provided
558                                 callback function.
559
560 @Return         PVR_DEBUGFS_DRIVER_STAT*   On success, a pointer representing
561                                                                                    the newly created statistic entry.
562                                                                                    Otherwise, NULL.
563 */ /**************************************************************************/
564 PVR_DEBUGFS_DRIVER_STAT *PVRDebugFSCreateStatisticEntry(const char *pszName,
565                                          PVR_DEBUGFS_DIR_DATA *psDir,
566                                          PVRSRV_GET_NEXT_STAT_FUNC *pfnGetNextStat,
567                                          PVRSRV_INC_STAT_MEM_REFCOUNT_FUNC      *pfnIncStatMemRefCount,
568                                          PVRSRV_INC_STAT_MEM_REFCOUNT_FUNC      *pfnDecStatMemRefCount,
569                                          void *pvData)
570 {
571         PVR_DEBUGFS_DRIVER_STAT *psStatData;
572         PVR_DEBUGFS_ENTRY_DATA * psDebugFSEntry;
573         int iResult;
574
575         if (pszName == NULL || pfnGetNextStat == NULL)
576         {
577                 return NULL;
578         }
579         if ((pfnIncStatMemRefCount != NULL || pfnDecStatMemRefCount != NULL) && pvData == NULL)
580         {
581                 return NULL;
582         }
583
584         psStatData = kzalloc(sizeof(*psStatData), GFP_KERNEL);
585         if (psStatData == NULL)
586         {
587                 return NULL;
588         }
589         psStatData->pvData = pvData;
590         psStatData->pfnGetNextStat = pfnGetNextStat;
591         psStatData->pfnIncStatMemRefCount = pfnIncStatMemRefCount;
592         psStatData->pfnDecStatMemRefCount = pfnDecStatMemRefCount;
593         psStatData->ui32RefCount = 1;
594
595         iResult = PVRDebugFSCreateEntry(pszName,
596                                         psDir,
597                                         &gsDebugFSStatisticReadOps,
598                                         NULL,
599                                         psStatData,
600                                         &psDebugFSEntry);
601         if (iResult != 0)
602         {
603                 kfree(psStatData);
604                 return NULL;
605         }
606         psStatData->pvDebugFSEntry = (void*)psDebugFSEntry;
607
608         if (pfnIncStatMemRefCount)
609         {
610                 /* call function to take reference on the memory holding the stat */
611                 psStatData->pfnIncStatMemRefCount((void*)psStatData->pvData);
612         }
613
614         psDebugFSEntry->ui32RefCount = 1;
615
616     return psStatData;
617 }
618
619 /*************************************************************************/ /*!
620 @Function       PVRDebugFSRemoveStatisticEntry
621 @Description    Removes a statistic entry that was created by
622                 PVRDebugFSCreateStatisticEntry().
623 @Input          psStatEntry  Pointer representing the statistic entry to be
624                                  removed.
625 @Return         void
626 */ /**************************************************************************/
627 void PVRDebugFSRemoveStatisticEntry(PVR_DEBUGFS_DRIVER_STAT *psStatEntry)
628 {
629         /* drop reference on pvStatEntry*/
630         _UnrefAndMaybeDestroyStatEntry(psStatEntry);
631 }
632
633 static void _RefDirEntry(PVR_DEBUGFS_DIR_DATA *psDirEntry)
634 {
635         mutex_lock(&gDebugFSLock);
636
637         if (psDirEntry->ui32RefCount > 0)
638         {
639                 /* Increment refCount */
640                 psDirEntry->ui32RefCount++;
641         }
642
643         mutex_unlock(&gDebugFSLock);
644 }
645
646 static void _UnrefAndMaybeDestroyDirEntryWhileLocked(PVR_DEBUGFS_DIR_DATA *psDirEntry)
647 {
648         if (psDirEntry->ui32RefCount > 0)
649         {
650                 /* Decrement refCount and free if now zero */
651                 if (--psDirEntry->ui32RefCount == 0)
652                 {
653                         /* if parent directory is not gpsPVRDebugFSEntryDir, decrement its refCount */
654                         debugfs_remove(psDirEntry->psDir);
655                         if (psDirEntry->psParentDir)
656                         {
657                                 _UnrefAndMaybeDestroyDirEntryWhileLocked(psDirEntry->psParentDir);
658                         }
659                         kfree(psDirEntry);
660                 }
661         }
662 }
663
664 static void _UnrefAndMaybeDestroyDirEntry(PVR_DEBUGFS_DIR_DATA *psDirEntry)
665 {
666         mutex_lock(&gDebugFSLock);
667
668         if (psDirEntry->ui32RefCount > 0)
669         {
670                 /* Decrement refCount and free if now zero */
671                 if (--psDirEntry->ui32RefCount == 0)
672                 {
673                         /* if parent directory is not gpsPVRDebugFSEntryDir, decrement its refCount */
674                         debugfs_remove(psDirEntry->psDir);
675                         if (psDirEntry->psParentDir)
676                         {
677                                 _UnrefAndMaybeDestroyDirEntryWhileLocked(psDirEntry->psParentDir);
678                         }
679                         kfree(psDirEntry);
680                 }
681         }
682
683         mutex_unlock(&gDebugFSLock);
684 }
685
686 static IMG_BOOL _RefDebugFSEntryNoLock(PVR_DEBUGFS_ENTRY_DATA *psDebugFSEntry)
687 {
688         IMG_BOOL bResult = IMG_FALSE;
689
690         PVR_ASSERT(psDebugFSEntry != NULL);
691
692         bResult = (psDebugFSEntry->ui32RefCount > 0);
693         if (bResult)
694         {
695                 /* Increment refCount of psDebugFSEntry */
696                 psDebugFSEntry->ui32RefCount++;
697         }
698
699         return bResult;
700 }
701
702 static void _UnrefAndMaybeDestroyDebugFSEntry(PVR_DEBUGFS_ENTRY_DATA *psDebugFSEntry)
703 {
704         mutex_lock(&gDebugFSLock);
705         /* Decrement refCount of psDebugFSEntry, and free if now zero */
706         PVR_ASSERT(psDebugFSEntry != IMG_NULL);
707
708         if (psDebugFSEntry->ui32RefCount > 0)
709         {
710                 if (--psDebugFSEntry->ui32RefCount == 0)
711                 {
712                         struct dentry *psEntry = psDebugFSEntry->psEntry;
713
714                         if (psEntry)
715                         {
716                                 /* Free any private data that was provided to debugfs_create_file() */
717                                 if (psEntry->d_inode->i_private != NULL)
718                                 {
719                                         PVR_DEBUGFS_PRIV_DATA *psPrivData = (PVR_DEBUGFS_PRIV_DATA*)psDebugFSEntry->psEntry->d_inode->i_private;
720
721                                         psPrivData->bValid = IMG_FALSE;
722                                         psPrivData->psDebugFSEntry = NULL;
723                                         kfree(psEntry->d_inode->i_private);
724                                         psEntry->d_inode->i_private = IMG_NULL;
725                                 }
726                                 debugfs_remove(psEntry);
727                         }
728
729                         /* decrement refcount of parent directory */
730                         if (psDebugFSEntry->psParentDir)
731                         {
732                                 _UnrefAndMaybeDestroyDirEntryWhileLocked(psDebugFSEntry->psParentDir);
733                         }
734
735                         /* now free the memory allocated for psDebugFSEntry */
736                         kfree(psDebugFSEntry);
737                 }
738         }
739
740         mutex_unlock(&gDebugFSLock);
741
742 }
743
744 static IMG_BOOL _RefStatEntry(PVR_DEBUGFS_DRIVER_STAT *psStatEntry)
745 {
746         IMG_BOOL bResult = IMG_FALSE;
747
748         PVR_ASSERT(psStatEntry != NULL);
749
750         mutex_lock(&gDebugFSLock);
751
752         bResult = (psStatEntry->ui32RefCount > 0);
753         if (bResult)
754         {
755                 /* Increment refCount of psStatEntry */
756                 psStatEntry->ui32RefCount++;
757         }
758
759         mutex_unlock(&gDebugFSLock);
760
761         return bResult;
762 }
763
764 static IMG_BOOL _UnrefAndMaybeDestroyStatEntry(PVR_DEBUGFS_DRIVER_STAT *psStatEntry)
765 {
766         IMG_BOOL bResult;
767
768         PVR_ASSERT(psStatEntry != IMG_NULL);
769
770         mutex_lock(&gDebugFSLock);
771
772         bResult = (psStatEntry->ui32RefCount > 0);
773
774         if (bResult)
775         {
776                 /* Decrement refCount of psStatData, and free if now zero */
777                 if (--psStatEntry->ui32RefCount == 0)
778                 {
779                         mutex_unlock(&gDebugFSLock);
780
781                         if (psStatEntry->pvDebugFSEntry)
782                         {
783                                 _UnrefAndMaybeDestroyDebugFSEntry((PVR_DEBUGFS_ENTRY_DATA*)psStatEntry->pvDebugFSEntry);
784                         }
785                         if (psStatEntry->pfnDecStatMemRefCount)
786                         {
787                                 /* call function to drop reference on the memory holding the stat */
788                                 psStatEntry->pfnDecStatMemRefCount((void*)psStatEntry->pvData);
789                         }
790                 }
791                 else
792                 {
793                         mutex_unlock(&gDebugFSLock);
794                 }
795         }
796         else
797         {
798                 mutex_unlock(&gDebugFSLock);
799         }
800
801         return bResult;
802 }