| /**************************************************************************** |
| * |
| * The MIT License (MIT) |
| * |
| * Copyright (c) 2014 - 2018 Vivante Corporation |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| * DEALINGS IN THE SOFTWARE. |
| * |
| ***************************************************************************** |
| * |
| * The GPL License (GPL) |
| * |
| * Copyright (C) 2014 - 2018 Vivante Corporation |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation; either version 2 |
| * of the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| * |
| ***************************************************************************** |
| * |
| * Note: This software is released under dual MIT and GPL licenses. A |
| * recipient may use this file under the terms of either the MIT license or |
| * GPL License. If you wish to use only one license not the other, you can |
| * indicate your decision by deleting one of the above license notices in your |
| * version of this file. |
| * |
| *****************************************************************************/ |
| |
| |
| #ifdef MODULE |
| #include <linux/module.h> |
| #endif |
| #include <linux/init.h> |
| #include <linux/debugfs.h> |
| #include <linux/slab.h> |
| #ifdef MODVERSIONS |
| #include <linux/modversions.h> |
| #endif |
| #include <linux/stddef.h> |
| #include <linux/sched.h> |
| #include <linux/kernel.h> |
| #include <linux/timer.h> |
| #include <linux/delay.h> |
| #include <linux/errno.h> |
| #include <linux/mutex.h> |
| #include <linux/vmalloc.h> |
| #include <linux/types.h> |
| #include <linux/fs.h> |
| #include <linux/poll.h> |
| #include <asm/uaccess.h> |
| #include <linux/completion.h> |
| #include <linux/seq_file.h> |
| #include "gc_hal_kernel_linux.h" |
| #include "gc_hal_kernel.h" |
| #include "gc_hal_kernel_debug.h" |
| |
| /* |
| Prequsite: |
| |
| 1) Debugfs feature must be enabled in the kernel. |
| 1.a) You can enable this, in the compilation of the uImage, all you have to do is, In the "make menuconfig" part, |
| you have to enable the debugfs in the kernel hacking part of the menu. |
| |
| HOW TO USE: |
| 1) insert the driver with the following option logFileSize, Ex: insmod galcore.ko ...... logFileSize=10240 |
| This gives a circular buffer of 10 MB |
| |
| 2)Usually after inserting the driver, the debug file system is mounted under /sys/kernel/debug/ |
| |
| 2.a)If the debugfs is not mounted, you must do "mount -t debugfs none /sys/kernel/debug" |
| |
| 3) To read what is being printed in the debugfs file system: |
| Ex : cat /sys/kernel/debug/gc/galcore_trace |
| |
| 4)To write into the debug file system from user side : |
| Ex: echo "hello" > cat /sys/kernel/debug/gc/galcore_trace |
| |
| 5)To write into debugfs from kernel side, Use the function called gckDEBUGFS_Print |
| |
| How to Get Video Memory Usage: |
| 1) Select a process whose video memory usage can be dump, no need to reset it until <pid> is needed to be change. |
| echo <pid> > /sys/kernel/debug/gc/vidmem |
| |
| 2) Get video memory usage. |
| cat /sys/kernel/debug/gc/vidmem |
| |
| USECASE Kernel Dump: |
| |
| 1) Go to /hal/inc/gc_hal_options.h, and enable the following flags: |
| - # define gcdDUMP 1 |
| - # define gcdDUMP_IN_KERNEL 1 |
| - # define gcdDUMP_COMMAND 1 |
| |
| 2) Go to /hal/kernel/gc_hal_kernel_command.c and disable the following flag |
| -#define gcdSIMPLE_COMMAND_DUMP 0 |
| |
| 3) Compile the driver |
| 4) insmod it with the logFileSize option |
| 5) Run an application |
| 6) You can get the dump by cat /sys/kernel/debug/gpu/galcore_trace |
| |
| */ |
| |
| /**/ |
| typedef va_list gctDBGARGS ; |
| #define gcmkARGS_START(argument, pointer) va_start(argument, pointer) |
| #define gcmkARGS_END(argument) va_end(argument) |
| |
| #define gcmkDEBUGFS_PRINT(ArgumentSize, Message) \ |
| { \ |
| gctDBGARGS __arguments__; \ |
| gcmkARGS_START(__arguments__, Message); \ |
| _debugfs_res = _DebugFSPrint(ArgumentSize, Message, &__arguments__);\ |
| gcmkARGS_END(__arguments__); \ |
| } |
| |
| |
| static DEFINE_SPINLOCK(traceLock); |
| |
| /* Debug File System Node Struct. */ |
| struct _gcsDEBUGFS_Node |
| { |
| /*wait queues for read and write operations*/ |
| #if defined(DECLARE_WAIT_QUEUE_HEAD) |
| wait_queue_head_t read_q , write_q ; |
| #else |
| struct wait_queue *read_q , *write_q ; |
| #endif |
| struct dentry *parent ; /*parent directory*/ |
| struct dentry *filen ; /*filename*/ |
| struct semaphore sem ; /* mutual exclusion semaphore */ |
| char *data ; /* The circular buffer data */ |
| int size ; /* Size of the buffer pointed to by 'data' */ |
| int refcount ; /* Files that have this buffer open */ |
| int read_point ; /* Offset in circ. buffer of oldest data */ |
| int write_point ; /* Offset in circ. buffer of newest data */ |
| int offset ; /* Byte number of read_point in the stream */ |
| struct _gcsDEBUGFS_Node *next ; |
| |
| caddr_t temp; |
| int tempSize; |
| }; |
| |
| /* amount of data in the queue */ |
| #define gcmkNODE_QLEN(node) ( (node)->write_point >= (node)->read_point ? \ |
| (node)->write_point - (node)->read_point : \ |
| (node)->size - (node)->read_point + (node)->write_point) |
| |
| /* byte number of the last byte in the queue */ |
| #define gcmkNODE_FIRST_EMPTY_BYTE(node) ((node)->offset + gcmkNODE_QLEN(node)) |
| |
| /*Synchronization primitives*/ |
| #define gcmkNODE_READQ(node) (&((node)->read_q)) |
| #define gcmkNODE_WRITEQ(node) (&((node)->write_q)) |
| #define gcmkNODE_SEM(node) (&((node)->sem)) |
| |
| /*Utilities*/ |
| #define gcmkMIN(x, y) ((x) < (y) ? (x) : y) |
| |
| /*Debug File System Struct*/ |
| typedef struct _gcsDEBUGFS_ |
| { |
| gcsDEBUGFS_Node* linkedlist ; |
| gcsDEBUGFS_Node* currentNode ; |
| int isInited ; |
| } gcsDEBUGFS_ ; |
| |
| /*debug file system*/ |
| static gcsDEBUGFS_ gc_dbgfs ; |
| |
| static int gc_debugfs_open(struct inode *inode, struct file *file) |
| { |
| gcsINFO_NODE *node = inode->i_private; |
| |
| return single_open(file, node->info->show, node); |
| } |
| |
| static ssize_t |
| gc_debugfs_write( |
| struct file *file, |
| const char __user *buf, |
| size_t count, |
| loff_t *pos |
| ) |
| { |
| struct seq_file *s = file->private_data; |
| gcsINFO_NODE *node = s->private; |
| gcsINFO *info = node->info; |
| |
| if (info->write) |
| { |
| info->write(buf, count, node); |
| } |
| |
| return count; |
| } |
| |
| static const struct file_operations gc_debugfs_operations = { |
| .owner = THIS_MODULE, |
| .open = gc_debugfs_open, |
| .write = gc_debugfs_write, |
| .read = seq_read, |
| .llseek = seq_lseek, |
| .release = single_release, |
| }; |
| |
| gceSTATUS |
| gckDEBUGFS_DIR_Init( |
| IN gckDEBUGFS_DIR Dir, |
| IN struct dentry *root, |
| IN gctCONST_STRING Name |
| ) |
| { |
| Dir->root = debugfs_create_dir(Name, root); |
| |
| if (!Dir->root) |
| { |
| return gcvSTATUS_NOT_SUPPORTED; |
| } |
| |
| INIT_LIST_HEAD(&Dir->nodeList); |
| |
| return gcvSTATUS_OK; |
| } |
| |
| gceSTATUS |
| gckDEBUGFS_DIR_CreateFiles( |
| IN gckDEBUGFS_DIR Dir, |
| IN gcsINFO * List, |
| IN int count, |
| IN gctPOINTER Data |
| ) |
| { |
| int i; |
| gcsINFO_NODE * node; |
| gceSTATUS status; |
| |
| for (i = 0; i < count; i++) |
| { |
| umode_t mode = 0; |
| |
| /* Create a node. */ |
| node = (gcsINFO_NODE *)kzalloc(sizeof(gcsINFO_NODE), GFP_KERNEL); |
| |
| node->info = &List[i]; |
| node->device = Data; |
| |
| mode |= List[i].show ? S_IRUGO : 0; |
| mode |= List[i].write ? S_IWUSR : 0; |
| |
| node->entry = debugfs_create_file( |
| List[i].name, mode, Dir->root, node, &gc_debugfs_operations); |
| |
| if (!node->entry) |
| { |
| gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); |
| } |
| |
| list_add(&(node->head), &(Dir->nodeList)); |
| } |
| |
| return gcvSTATUS_OK; |
| |
| OnError: |
| gcmkVERIFY_OK(gckDEBUGFS_DIR_RemoveFiles(Dir, List, count)); |
| return status; |
| } |
| |
| gceSTATUS |
| gckDEBUGFS_DIR_RemoveFiles( |
| IN gckDEBUGFS_DIR Dir, |
| IN gcsINFO * List, |
| IN int count |
| ) |
| { |
| int i; |
| gcsINFO_NODE * node; |
| gcsINFO_NODE * temp; |
| |
| for (i = 0; i < count; i++) |
| { |
| list_for_each_entry_safe(node, temp, &Dir->nodeList, head) |
| { |
| if (node->info == &List[i]) |
| { |
| debugfs_remove(node->entry); |
| list_del(&node->head); |
| kfree(node); |
| } |
| } |
| } |
| |
| return gcvSTATUS_OK; |
| } |
| |
| void |
| gckDEBUGFS_DIR_Deinit( |
| IN gckDEBUGFS_DIR Dir |
| ) |
| { |
| if (Dir->root != NULL) |
| { |
| debugfs_remove(Dir->root); |
| Dir->root = NULL; |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** READ & WRITE FUNCTIONS (START) |
| ** |
| *******************************************************************************/ |
| |
| /******************************************************************************* |
| ** |
| ** _ReadFromNode |
| ** |
| ** 1) reading bytes out of a circular buffer with wraparound. |
| ** 2)returns caddr_t, pointer to data read, which the caller must free. |
| ** 3) length is (a pointer to) the number of bytes to be read, which will be set by this function to |
| ** be the number of bytes actually returned |
| ** |
| *******************************************************************************/ |
| static caddr_t |
| _ReadFromNode ( |
| gcsDEBUGFS_Node* Node , |
| size_t *Length , |
| loff_t *Offset |
| ) |
| { |
| caddr_t retval ; |
| int bytes_copied = 0 , n , start_point , remaining ; |
| |
| /* find the smaller of the total bytes we have available and what |
| * the user is asking for */ |
| *Length = gcmkMIN ( *Length , gcmkNODE_QLEN(Node) ) ; |
| |
| remaining = * Length ; |
| |
| /* Get start point. */ |
| start_point = Node->read_point; |
| |
| /* allocate memory to return */ |
| if (remaining > Node->tempSize) |
| { |
| kfree(Node->temp); |
| |
| if ( ( retval = kmalloc ( sizeof (char ) * remaining , GFP_KERNEL ) ) == NULL ) |
| return NULL; |
| |
| Node->temp = retval; |
| Node->tempSize = remaining; |
| } |
| else |
| { |
| retval = Node->temp; |
| } |
| |
| /* copy the (possibly noncontiguous) data to our buffer */ |
| while ( remaining ) |
| { |
| n = gcmkMIN ( remaining , Node->size - start_point ) ; |
| memcpy ( retval + bytes_copied , Node->data + start_point , n ) ; |
| bytes_copied += n ; |
| remaining -= n ; |
| start_point = ( start_point + n ) % Node->size ; |
| } |
| |
| /* advance user's file pointer */ |
| Node->read_point = (Node->read_point + * Length) % Node->size ; |
| |
| return retval ; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** _WriteToNode |
| ** |
| ** 1) writes to a circular buffer with wraparound. |
| ** 2)in case of an overflow, it overwrites the oldest unread data. |
| ** |
| *********************************************************************************/ |
| static void |
| _WriteToNode ( |
| gcsDEBUGFS_Node* Node , |
| caddr_t Buf , |
| int Length |
| ) |
| { |
| int bytes_copied = 0 ; |
| int overflow = 0 ; |
| int n ; |
| |
| if ( Length + gcmkNODE_QLEN ( Node ) >= ( Node->size - 1 ) ) |
| { |
| overflow = 1 ; |
| } |
| |
| while ( Length ) |
| { |
| /* how many contiguous bytes are available from the write point to |
| * the end of the circular buffer? */ |
| n = gcmkMIN ( Length , Node->size - Node->write_point ) ; |
| memcpy ( Node->data + Node->write_point , Buf + bytes_copied , n ) ; |
| bytes_copied += n ; |
| Length -= n ; |
| Node->write_point = ( Node->write_point + n ) % Node->size ; |
| } |
| |
| /* if there is an overflow, reset the read point to read whatever is |
| * the oldest data that we have, that has not yet been |
| * overwritten. */ |
| if ( overflow ) |
| { |
| Node->read_point = ( Node->write_point + 1 ) % Node->size ; |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** PRINTING UTILITY (START) |
| ** |
| *******************************************************************************/ |
| |
| /******************************************************************************* |
| ** |
| ** _GetArgumentSize |
| ** |
| ** |
| *******************************************************************************/ |
| static gctINT |
| _GetArgumentSize ( |
| IN gctCONST_STRING Message |
| ) |
| { |
| gctINT i , count ; |
| |
| for ( i = 0 , count = 0 ; Message[i] ; i += 1 ) |
| { |
| if ( Message[i] == '%' ) |
| { |
| count += 1 ; |
| } |
| } |
| return count * sizeof (unsigned int ) ; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** _AppendString |
| ** |
| ** |
| *******************************************************************************/ |
| static ssize_t |
| _AppendString ( |
| IN gcsDEBUGFS_Node* Node, |
| IN gctCONST_STRING String, |
| IN int Length |
| ) |
| { |
| int n; |
| unsigned long flags; |
| |
| /* if the message is longer than the buffer, just take the beginning |
| * of it, in hopes that the reader (if any) will have time to read |
| * before we wrap around and obliterate it */ |
| n = gcmkMIN ( Length , Node->size - 1 ); |
| |
| spin_lock_irqsave(&traceLock, flags); |
| |
| /* now copy it into the circular buffer and free our temp copy */ |
| _WriteToNode ( Node , (caddr_t)String , n ) ; |
| |
| spin_unlock_irqrestore(&traceLock, flags); |
| |
| return n ; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** _DebugFSPrint |
| ** |
| ** |
| *******************************************************************************/ |
| static ssize_t |
| _DebugFSPrint ( |
| IN unsigned int ArgumentSize , |
| IN const char* Message , |
| IN gctDBGARGS * Arguments |
| |
| ) |
| { |
| char buffer[MAX_LINE_SIZE] ; |
| int len ; |
| ssize_t res=0; |
| |
| len = vsnprintf ( buffer , sizeof (buffer ) , Message , *( va_list * ) Arguments ) ; |
| |
| buffer[len] = '\0' ; |
| |
| /* Add end-of-line if missing. */ |
| if ( buffer[len - 1] != '\n' ) |
| { |
| buffer[len ++] = '\n' ; |
| buffer[len] = '\0' ; |
| } |
| |
| res = _AppendString ( gc_dbgfs.currentNode , buffer , len ) ; |
| |
| wake_up_interruptible ( gcmkNODE_READQ ( gc_dbgfs.currentNode ) ) ; /* blocked in read*/ |
| |
| return res; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** LINUX SYSTEM FUNCTIONS (START) |
| ** |
| *******************************************************************************/ |
| static int |
| _DebugFSOpen ( |
| struct inode* inode, |
| struct file* filp |
| ) |
| { |
| filp->private_data = inode->i_private; |
| |
| return 0; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** _DebugFSRead |
| ** |
| *******************************************************************************/ |
| static ssize_t |
| _DebugFSRead ( |
| struct file *file, |
| char __user * buffer, |
| size_t length, |
| loff_t * offset |
| ) |
| { |
| int retval; |
| caddr_t data_to_return; |
| unsigned long flags; |
| gcsDEBUGFS_Node* node = file->private_data; |
| |
| if (node == NULL) |
| { |
| printk ( "debugfs_read: record not found\n" ); |
| return - EIO ; |
| } |
| |
| spin_lock_irqsave(&traceLock, flags); |
| |
| /* wait until there's data available (unless we do nonblocking reads) */ |
| while (!gcmkNODE_QLEN(node)) |
| { |
| spin_unlock_irqrestore(&traceLock, flags); |
| |
| if (file->f_flags & O_NONBLOCK) |
| { |
| return - EAGAIN ; |
| } |
| |
| if (wait_event_interruptible((*(gcmkNODE_READQ(node))) , (*offset < gcmkNODE_FIRST_EMPTY_BYTE(node)))) |
| { |
| return - ERESTARTSYS ; /* signal: tell the fs layer to handle it */ |
| } |
| |
| spin_lock_irqsave(&traceLock, flags); |
| } |
| |
| data_to_return = _ReadFromNode(node , &length , offset); |
| |
| spin_unlock_irqrestore(&traceLock, flags); |
| |
| if (data_to_return == NULL) |
| { |
| retval = 0; |
| goto unlock; |
| } |
| |
| if (copy_to_user(buffer, data_to_return, length) > 0) |
| { |
| retval = - EFAULT; |
| } |
| else |
| { |
| retval = length; |
| } |
| unlock: |
| |
| wake_up_interruptible(gcmkNODE_WRITEQ(node)); |
| return retval ; |
| } |
| |
| /******************************************************************************* |
| ** |
| **_DebugFSWrite |
| ** |
| *******************************************************************************/ |
| static ssize_t |
| _DebugFSWrite ( |
| struct file *file , |
| const char __user * buffer , |
| size_t length , |
| loff_t * offset |
| ) |
| { |
| caddr_t message = NULL ; |
| int n ; |
| gcsDEBUGFS_Node* node = file->private_data; |
| |
| /* get the metadata about this log */ |
| if (node == NULL) |
| { |
| return - EIO ; |
| } |
| |
| if ( down_interruptible ( gcmkNODE_SEM ( node ) ) ) |
| { |
| return - ERESTARTSYS ; |
| } |
| |
| /* if the message is longer than the buffer, just take the beginning |
| * of it, in hopes that the reader (if any) will have time to read |
| * before we wrap around and obliterate it */ |
| n = gcmkMIN ( length , node->size - 1 ) ; |
| |
| /* make sure we have the memory for it */ |
| if ( ( message = kmalloc ( n , GFP_KERNEL ) ) == NULL ) |
| { |
| up ( gcmkNODE_SEM ( node ) ) ; |
| return - ENOMEM ; |
| } |
| |
| |
| /* copy into our temp buffer */ |
| if ( copy_from_user ( message , buffer , n ) > 0 ) |
| { |
| up ( gcmkNODE_SEM ( node ) ) ; |
| kfree ( message ) ; |
| return - EFAULT ; |
| } |
| |
| /* now copy it into the circular buffer and free our temp copy */ |
| _WriteToNode ( node , message , n ) ; |
| |
| kfree ( message ) ; |
| up ( gcmkNODE_SEM ( node ) ) ; |
| |
| /* wake up any readers that might be waiting for the data. we call |
| * schedule in the vague hope that a reader will run before the |
| * writer's next write, to avoid losing data. */ |
| wake_up_interruptible ( gcmkNODE_READQ ( node ) ) ; |
| |
| return n ; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** File Operations Table |
| ** |
| *******************************************************************************/ |
| static const struct file_operations debugfs_operations = { |
| .owner = THIS_MODULE , |
| .open = _DebugFSOpen , |
| .read = _DebugFSRead , |
| .write = _DebugFSWrite , |
| } ; |
| |
| /******************************************************************************* |
| ** |
| ** INTERFACE FUNCTIONS (START) |
| ** |
| *******************************************************************************/ |
| |
| /******************************************************************************* |
| ** |
| ** gckDEBUGFS_IsEnabled |
| ** |
| ** |
| ** INPUT: |
| ** |
| ** OUTPUT: |
| ** |
| *******************************************************************************/ |
| |
| |
| gctINT |
| gckDEBUGFS_IsEnabled ( void ) |
| { |
| return gc_dbgfs.isInited ; |
| } |
| /******************************************************************************* |
| ** |
| ** gckDEBUGFS_Initialize |
| ** |
| ** |
| ** INPUT: |
| ** |
| ** OUTPUT: |
| ** |
| *******************************************************************************/ |
| |
| gctINT |
| gckDEBUGFS_Initialize ( void ) |
| { |
| if ( ! gc_dbgfs.isInited ) |
| { |
| gc_dbgfs.linkedlist = gcvNULL ; |
| gc_dbgfs.currentNode = gcvNULL ; |
| gc_dbgfs.isInited = 1 ; |
| } |
| return gc_dbgfs.isInited ; |
| } |
| /******************************************************************************* |
| ** |
| ** gckDEBUGFS_Terminate |
| ** |
| ** |
| ** INPUT: |
| ** |
| ** OUTPUT: |
| ** |
| *******************************************************************************/ |
| |
| gctINT |
| gckDEBUGFS_Terminate ( void ) |
| { |
| gcsDEBUGFS_Node * next = gcvNULL ; |
| gcsDEBUGFS_Node * temp = gcvNULL ; |
| if ( gc_dbgfs.isInited ) |
| { |
| temp = gc_dbgfs.linkedlist ; |
| while ( temp != gcvNULL ) |
| { |
| next = temp->next ; |
| gckDEBUGFS_FreeNode ( temp ) ; |
| kfree ( temp ) ; |
| temp = next ; |
| } |
| gc_dbgfs.isInited = 0 ; |
| } |
| return 0 ; |
| } |
| |
| |
| /******************************************************************************* |
| ** |
| ** gckDEBUGFS_CreateNode |
| ** |
| ** |
| ** INPUT: |
| ** |
| ** OUTPUT: |
| ** |
| ** gckDEBUGFS_FreeNode * Device |
| ** Pointer to a variable receiving the gcsDEBUGFS_Node object pointer on |
| ** success. |
| *********************************************************************************/ |
| |
| gctINT |
| gckDEBUGFS_CreateNode ( |
| IN gctPOINTER Device, |
| IN gctINT SizeInKB , |
| IN struct dentry * Root , |
| IN gctCONST_STRING NodeName , |
| OUT gcsDEBUGFS_Node **Node |
| ) |
| { |
| gcsDEBUGFS_Node*node ; |
| /* allocate space for our metadata and initialize it */ |
| if ( ( node = kmalloc ( sizeof (gcsDEBUGFS_Node ) , GFP_KERNEL ) ) == NULL ) |
| goto struct_malloc_failed ; |
| |
| /*Zero it out*/ |
| memset ( node , 0 , sizeof (gcsDEBUGFS_Node ) ) ; |
| |
| /*Init the sync primitives*/ |
| #if defined(DECLARE_WAIT_QUEUE_HEAD) |
| init_waitqueue_head ( gcmkNODE_READQ ( node ) ) ; |
| #else |
| init_waitqueue ( gcmkNODE_READQ ( node ) ) ; |
| #endif |
| |
| #if defined(DECLARE_WAIT_QUEUE_HEAD) |
| init_waitqueue_head ( gcmkNODE_WRITEQ ( node ) ) ; |
| #else |
| init_waitqueue ( gcmkNODE_WRITEQ ( node ) ) ; |
| #endif |
| sema_init ( gcmkNODE_SEM ( node ) , 1 ) ; |
| /*End the sync primitives*/ |
| |
| /*creating the debug file system*/ |
| node->parent = Root; |
| |
| if (SizeInKB) |
| { |
| /* figure out how much of a buffer this should be and allocate the buffer */ |
| node->size = 1024 * SizeInKB ; |
| if ( ( node->data = ( char * ) vmalloc ( sizeof (char ) * node->size ) ) == NULL ) |
| goto data_malloc_failed ; |
| |
| node->tempSize = 0; |
| node->temp = NULL; |
| |
| /*creating the file*/ |
| node->filen = debugfs_create_file(NodeName, S_IRUGO|S_IWUSR, node->parent, node, |
| &debugfs_operations); |
| } |
| |
| /* add it to our linked list */ |
| node->next = gc_dbgfs.linkedlist ; |
| gc_dbgfs.linkedlist = node ; |
| |
| |
| /* pass the struct back */ |
| *Node = node ; |
| return 0 ; |
| |
| |
| data_malloc_failed: |
| kfree ( node ) ; |
| struct_malloc_failed: |
| return - ENOMEM ; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckDEBUGFS_FreeNode |
| ** |
| ** |
| ** INPUT: |
| ** |
| ** OUTPUT: |
| ** |
| *******************************************************************************/ |
| void |
| gckDEBUGFS_FreeNode ( |
| IN gcsDEBUGFS_Node * Node |
| ) |
| { |
| |
| gcsDEBUGFS_Node **ptr ; |
| |
| if ( Node == NULL ) |
| { |
| printk ( "null passed to free_vinfo\n" ) ; |
| return ; |
| } |
| |
| down ( gcmkNODE_SEM ( Node ) ) ; |
| /*free data*/ |
| vfree ( Node->data ) ; |
| |
| kfree(Node->temp); |
| |
| /*Close Debug fs*/ |
| if ( Node->filen ) |
| { |
| debugfs_remove ( Node->filen ) ; |
| } |
| |
| /* now delete the node from the linked list */ |
| ptr = & ( gc_dbgfs.linkedlist ) ; |
| while ( *ptr != Node ) |
| { |
| if ( ! *ptr ) |
| { |
| printk ( "corrupt info list!\n" ) ; |
| break ; |
| } |
| else |
| ptr = & ( ( **ptr ).next ) ; |
| } |
| *ptr = Node->next ; |
| up ( gcmkNODE_SEM ( Node ) ) ; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckDEBUGFS_SetCurrentNode |
| ** |
| ** |
| ** INPUT: |
| ** |
| ** OUTPUT: |
| ** |
| *******************************************************************************/ |
| void |
| gckDEBUGFS_SetCurrentNode ( |
| IN gcsDEBUGFS_Node * Node |
| ) |
| { |
| gc_dbgfs.currentNode = Node ; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckDEBUGFS_GetCurrentNode |
| ** |
| ** |
| ** INPUT: |
| ** |
| ** OUTPUT: |
| ** |
| *******************************************************************************/ |
| void |
| gckDEBUGFS_GetCurrentNode ( |
| OUT gcsDEBUGFS_Node ** Node |
| ) |
| { |
| *Node = gc_dbgfs.currentNode ; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** gckDEBUGFS_Print |
| ** |
| ** |
| ** INPUT: |
| ** |
| ** OUTPUT: |
| ** |
| *******************************************************************************/ |
| ssize_t |
| gckDEBUGFS_Print ( |
| IN gctCONST_STRING Message , |
| ... |
| ) |
| { |
| ssize_t _debugfs_res; |
| gcmkDEBUGFS_PRINT ( _GetArgumentSize ( Message ) , Message ) ; |
| return _debugfs_res; |
| } |