Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756547AbYHUFqt (ORCPT ); Thu, 21 Aug 2008 01:46:49 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1755541AbYHUFpW (ORCPT ); Thu, 21 Aug 2008 01:45:22 -0400 Received: from rv-out-0506.google.com ([209.85.198.227]:25301 "EHLO rv-out-0506.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755525AbYHUFpS (ORCPT ); Thu, 21 Aug 2008 01:45:18 -0400 DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=message-id:date:from:reply-to:user-agent:mime-version:to:subject :content-type:content-transfer-encoding; b=BMzdp8kJ5XI4O1dMEPSScgFS5FZkHHbqxWlF3tuvgVETvNDzDZ0dXLg/Gck5DbVooW B1I4qlv/s+B4KQ9vpH7Es+r/phmiDIIkFSzQLkPZTlvkLFR1lkV0/EoPjsYfvLL7BCka zZT555gmLBFfW+qfNcU6GUEuBKjaZxR3AGCgI= Message-ID: <48AD00F8.1030004@gmail.com> Date: Wed, 20 Aug 2008 22:45:28 -0700 From: Jared Hulbert Reply-To: jaredeh@gmail.com User-Agent: Thunderbird 2.0.0.12 (Macintosh/20080213) MIME-Version: 1.0 To: Linux-kernel@vger.kernel.org, linux-embedded@vger.kernel.org, linux-mtd , =?ISO-8859-1?Q?J=F6rn_Engel?= , tim.bird@AM.SONY.COM, cotte@de.ibm.com, nickpiggin@yahoo.com.au Subject: [PATCH 05/10] AXFS: axfs_profiling.c Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 16946 Lines: 613 Profiling is a fault instrumentation and /proc formating system. This is used to get an accurate picture of what the pages are actually used. Using this info the image can be optimized for XIP Signed-off-by: Jared Hulbert --- diff --git a/fs/axfs/axfs_profiling.c b/fs/axfs/axfs_profiling.c new file mode 100644 index 0000000..30d881c --- /dev/null +++ b/fs/axfs/axfs_profiling.c @@ -0,0 +1,594 @@ +/* + * Advanced XIP File System for Linux - AXFS + * Readonly, compressed, and XIP filesystem for Linux systems big and small + * + * Copyright(c) 2008 Numonyx + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * Authors: + * Eric Anderson + * Jared Hulbert + * Sujaya Srinivasan + * Justin Treon + * + * More info and current contacts at http://axfs.sourceforge.net + * + * axfs_profiling.c - + * Tracks pages of files that enter the page cache. Outputs through a proc + * file which generates a comma separated data file with path, page offset, + * count of times entered page cache. + */ + +#include +#ifdef CONFIG_AXFS_PROFILING +#include +#include +#include + +#define AXFS_PROC_DIR_NAME "axfs" + +struct axfs_profiling_manager { + struct axfs_profiling_data *profiling_data; + struct axfs_super *sbi; + u32 *dir_structure; + u32 size; +}; + +#define MAX_STRING_LEN 1024 + +/* Handles for our Directory and File */ +static struct proc_dir_entry *axfs_proc_dir; +static u32 proc_name_inc; + +/****************************************************************************** + * + * axfs_init_profile_dir_structure + * + * Description: + * Creates the structures for tracking the page usage data and creates the + * proc file that will be used to get the data. + * + * Parameters: + * (IN) manager - pointer to the profile manager for the filing system + * + * (IN) num_inodes - number of files in the system + * + * Returns: + * 0 + * + *****************************************************************************/ +static int axfs_init_profile_dir_structure(struct axfs_profiling_manager + *manager, u32 num_inodes) +{ + + struct axfs_super *sbi = (struct axfs_super *)manager->sbi; + u32 child_index = 0, i, j; + u32 *dir_structure = manager->dir_structure; + + /* loop through each inode in the image and find all + of the directories and mark their children */ + for (i = 0; i < num_inodes; i++) { + /* determine if the entry is a directory */ + if (!S_ISDIR(AXFS_GET_MODE(sbi, i))) + continue; + + /* get the index number for this directory */ + child_index = AXFS_GET_INODE_ARRAY_INDEX(sbi, i); + + /* get the offset to its children */ + for (j = 0; j < AXFS_GET_INODE_NUM_ENTRIES(sbi, i); j++) { + if (dir_structure[child_index + j] != 0) { + printk(KERN_ERR + "axfs: ERROR inode was already set old " + "%lu new %lu\n", (unsigned long) + dir_structure[child_index + j], + (unsigned long)i); + } + dir_structure[child_index + j] = i; + } + } + + return 0; +} + +/****************************************************************************** + * + * axfs_get_directory_path + * + * Description: + * Determines the directory path of every file for printing the spreadsheet. + * + * Parameters: + * (IN) manager - Pointer to axfs profile manager + * + * (OUT) buffer - Pointer to the printable directory path for each file + * + * (IN) inode_number - Inode number of file to look up + * + * Returns: + * Size of the path to the file + * + * + **************************************************************************/ +static int axfs_get_directory_path(struct axfs_profiling_manager *manager, + char *buffer, u32 inode_number) +{ + u32 path_depth = 0; + u32 path_size = 0; + u32 string_len = 0; + u32 index = inode_number; + u32 dir_number; + u8 **path_array = NULL; + struct axfs_super *sbi = (struct axfs_super *)manager->sbi; + int i; + + /* determine how deep the directory path is and how big the name + string will be walk back until the root directory index is found + (index 0 is root) */ + while (manager->dir_structure[index] != 0) { + path_depth++; + /* set the index to the index of the parent directory */ + index = manager->dir_structure[index]; + } + + if (path_depth != 0) { + /* create an array that will hold a pointer for each of the + directories names */ + path_array = vmalloc(path_depth * sizeof(*path_array)); + if (path_array == NULL) { + printk(KERN_DEBUG + "axfs: directory_path vmalloc failed.\n"); + goto out; + } + } + + index = manager->dir_structure[inode_number]; + for (i = path_depth; i > 0; i--) { + /* get the array_index for the directory corresponding to + index */ + dir_number = AXFS_GET_INODE_ARRAY_INDEX(sbi, index); + + /* store a pointer to the name in the array */ + path_array[(i - 1)] = (u8 *) AXFS_GET_INODE_NAME(sbi, index); + + index = manager->dir_structure[index]; + } + + /* now print out the directory structure from the begining */ + string_len = sprintf(buffer, "./"); + path_size += string_len; + for (i = 0; i < path_depth; i++) { + buffer = buffer + string_len; + string_len = sprintf(buffer, "%s/", (char *)path_array[i]); + path_size += string_len; + } + + if (path_array != NULL) + vfree(path_array); + +out: + return path_size; + +} + +static ssize_t axfs_procfile_read(char *buffer, + char **buffer_location, + off_t offset, int buffer_length, int *eof, + void *data) +{ + struct axfs_profiling_manager *man; + struct axfs_profiling_data *profile; + struct axfs_super *sbi; + u64 array_index; + u64 loop_size, inode_page_offset, node_offset, inode_number; + u64 print_len = 0; + unsigned long addr; + int len = 0; + int i; + char *buff, *name = NULL; + + man = (struct axfs_profiling_manager *)data; + sbi = man->sbi; + + loop_size = man->size / sizeof(*profile); + + /* If all data has been returned set EOF */ + if (offset >= loop_size) { + *eof = 1; + return 0; + } + + buff = buffer; + /* print as much as the buffer can take */ + for (i = offset; i < loop_size; i++) { + + if ((print_len + MAX_STRING_LEN) > buffer_length) + break; + /* get the first profile data structure */ + profile = &(man->profiling_data[i]); + + if (profile->count == 0) + continue; + + inode_number = profile->inode_number; + + /* file names can be duplicated so we must print out the path */ + len = axfs_get_directory_path(man, buff, inode_number); + + print_len += len; + buff += len; + + /* get a pointer to the inode name */ + array_index = AXFS_GET_INODE_ARRAY_INDEX(sbi, inode_number); + name = (char *)AXFS_GET_INODE_NAME(sbi, inode_number); + + /* need to convert the page number in the node area to + the page number within the file */ + node_offset = i; + /* gives the offset of the node in the node list area + then substract that from the */ + inode_page_offset = node_offset - array_index; + + /* set everything up to print out */ + addr = (unsigned long)(inode_page_offset * PAGE_SIZE); + len = sprintf(buff, "%s,%lu,%lu\n", name, addr, profile->count); + + print_len += len; + buff += len; + } + + /* return the number of items printed. + This will be added to offset and passed back to us */ + *buffer_location = (char *)(i - offset); + + return print_len; +} + +static ssize_t axfs_procfile_write(struct file *file, + const char *buffer, unsigned long count, + void *data) +{ + struct axfs_profiling_manager *man_ptr = + (struct axfs_profiling_manager *)data; + + if ((count >= 2) && (0 == memcmp(buffer, "on", 2))) { + man_ptr->sbi->profiling_on = TRUE; + } else if ((count >= 3) && (0 == memcmp(buffer, "off", 3))) { + man_ptr->sbi->profiling_on = FALSE; + } else if ((count >= 5) && (0 == memcmp(buffer, "clear", 5))) { + memset(man_ptr->profiling_data, 0, man_ptr->size); + } else { + printk(KERN_INFO + "axfs: Unknown command. Supported options are:\n"); + printk(KERN_INFO "\t\"on\"\tTurn on profiling\n"); + printk(KERN_INFO "\t\"off\"\tTurn off profiling\n"); + printk(KERN_INFO "\t\"clear\"\tClear profiling buffer\n"); + } + + return count; +} + +static int axfs_create_proc_directory(void) +{ + if (axfs_proc_dir == NULL) { + axfs_proc_dir = proc_mkdir(AXFS_PROC_DIR_NAME, NULL); + if (!axfs_proc_dir) { + printk(KERN_WARNING + "axfs: Failed to create directory\n"); + return FALSE; + } + } + return TRUE; +} + +static void axfs_delete_proc_directory(void) +{ + /* Determine if there are any directory elements + and remove if all of the proc files are removed. */ + if (axfs_proc_dir != NULL) { + if (axfs_proc_dir->subdir == NULL) { + remove_proc_entry(AXFS_PROC_DIR_NAME, NULL); + axfs_proc_dir = NULL; + } + } +} + +/****************************************************************************** + * + * axfs_delete_proc_file + * + * Description: + * Will search through the proc directory for the correct proc file, + * then delete it + * + * Parameters: + * (IN) sbi- axfs superblock pointer to determine which proc file to remove + * + * Returns: + * The profiling manager pointer for the proc file. + * + *****************************************************************************/ +static struct axfs_profiling_manager *axfs_delete_proc_file(struct axfs_super + *sbi) +{ + struct proc_dir_entry *current_proc_file; + struct axfs_profiling_manager *manager; + void *rv = NULL; + + if (!axfs_proc_dir) + return NULL; + + /* Walk through the proc file entries to find the matching sbi */ + current_proc_file = axfs_proc_dir->subdir; + + while (current_proc_file != NULL) { + manager = current_proc_file->data; + if (manager == NULL) { + printk(KERN_WARNING + "axfs: Error removing proc file private " + "data was NULL.\n"); + rv = NULL; + break; + } + if (manager->sbi == sbi) { + /* we found the match */ + remove_proc_entry(current_proc_file->name, + axfs_proc_dir); + rv = (void *)manager; + break; + } + current_proc_file = axfs_proc_dir->next; + } + return (struct axfs_profiling_manager *)rv; +} + +/****************************************************************************** + * + * axfs_register_profiling_proc + * + * Description: + * Will register the instance of the proc file for a given volume. + * + * Parameters: + * (IN) manager - Pointer to the profiling manager for the axfs volume + * + * Returns: + * 0 or error number + * + *****************************************************************************/ +static int axfs_register_profiling_proc(struct axfs_profiling_manager *manager) +{ + int rv = 0; + struct proc_dir_entry *proc_file; + char file_name[20]; + + if (!axfs_create_proc_directory()) { + rv = -ENOMEM; + goto out; + } + + sprintf(file_name, "volume%d", proc_name_inc); + proc_file = create_proc_entry(file_name, (mode_t) 0644, axfs_proc_dir); + if (proc_file == NULL) { + remove_proc_entry(file_name, axfs_proc_dir); + axfs_delete_proc_directory(); + rv = -ENOMEM; + goto out; + } + + proc_name_inc++; + proc_file->read_proc = axfs_procfile_read; + proc_file->write_proc = axfs_procfile_write; + proc_file->owner = THIS_MODULE; + proc_file->mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + proc_file->uid = 0; + proc_file->gid = 0; + proc_file->data = manager; + + printk(KERN_DEBUG "axfs: Proc entry created\n"); + +out: + return rv; +} + +/****************************************************************************** + * + * axfs_unregister_profiling_proc + * + * Description: + * Will unregister the instance of the proc file for the volume that was + * mounted. If this is the last volume mounted then the proc directory + * will also be removed. + * + * Parameters: + * (IN) sbi- axfs superblock pointer to determine which proc file to remove + * + * Returns: + * The profiling manager pointer for the proc file. + * + *****************************************************************************/ +static struct axfs_profiling_manager *axfs_unregister_profiling_proc(struct + axfs_super + *sbi) +{ + struct axfs_profiling_manager *manager; + manager = axfs_delete_proc_file(sbi); + axfs_delete_proc_directory(); + return manager; +} + +/****************************************************************************** + * + * axfs_init_profiling + * + * Description: + * Creates the structures for tracking the page usage data and creates the + * proc file that will be used to get the data. + * + * Parameters: + * (IN) sbi- axfs superblock pointer + * + * Returns: + * TRUE or FALSE + * + *****************************************************************************/ +int axfs_init_profiling(struct axfs_super *sbi) +{ + + u32 num_nodes, num_inodes; + struct axfs_profiling_manager *manager = NULL; + struct axfs_profiling_data *profile_data = NULL; + int err = -ENOMEM; + + /* determine the max number of pages in the FS */ + num_nodes = sbi->blocks; + if (!num_nodes) + return 0; + + manager = vmalloc(sizeof(*manager)); + if (!manager) + goto out; + + profile_data = vmalloc(num_nodes * sizeof(*profile_data)); + if (!profile_data) + goto out; + + memset(profile_data, 0, num_nodes * sizeof(*profile_data)); + + /* determine the max number of inodes in the FS */ + num_inodes = sbi->files; + + manager->dir_structure = vmalloc(num_inodes * sizeof(u32 *)); + if (!manager->dir_structure) + goto out; + + memset(manager->dir_structure, 0, (num_inodes * sizeof(u32 *))); + + manager->profiling_data = profile_data; + manager->size = num_nodes * sizeof(*profile_data); + manager->sbi = sbi; + sbi->profiling_on = TRUE; /* Turn on profiling by default */ + sbi->profile_data_ptr = profile_data; + + err = axfs_init_profile_dir_structure(manager, num_inodes); + if (err) + goto out; + + err = axfs_register_profiling_proc(manager); + if (err) + goto out; + + return 0; + +out: + if (manager->dir_structure) + vfree(manager->dir_structure); + if (profile_data) + vfree(profile_data); + if (manager) + vfree(manager); + return err; +} + +/****************************************************************************** + * + * axfs_shutdown_profiling + * + * Description: + * Remove the proc file for this volume and release the memory in the + * profiling manager + * + * Parameters: + * (IN) sbi- axfs superblock pointer + * + * Returns: + * TRUE or FALSE + * + *****************************************************************************/ +int axfs_shutdown_profiling(struct axfs_super *sbi) +{ + struct axfs_profiling_manager *manager; + /* remove the proc file for this volume and release the memory in the + profiling manager */ + + if (!sbi) + return TRUE; + + if (!sbi->profile_data_ptr) + return TRUE; + + manager = axfs_unregister_profiling_proc(sbi); + + if (manager == NULL) + return FALSE; + + if (manager->dir_structure) + vfree(manager->dir_structure); + if (manager->profiling_data) + vfree(manager->profiling_data); + if (manager) + vfree(manager); + return TRUE; +} + +/****************************************************************************** + * + * axfs_profiling_add + * + * Description: + * Log when a node is paged into memory by incrementing the count in the + * array profile data structure. + * + * Parameters: + * (IN) sbi- axfs superblock pointer + * + * (IN) array_index - The offset into the nodes table of file (node number) + * + * (IN) axfs_inode_number - Inode of the node to determine file name later + * + * Returns: + * none + * + *****************************************************************************/ +void axfs_profiling_add(struct axfs_super *sbi, unsigned long array_index, + unsigned int axfs_inode_number) +{ + unsigned long addr; + struct axfs_profiling_data *profile_data; + + if (sbi->profiling_on != TRUE) + return; + + addr = (unsigned long)sbi->profile_data_ptr; + addr += array_index * sizeof(*profile_data); + + profile_data = (struct axfs_profiling_data *)addr; + + /* Record the inode number to determine the file name later. */ + profile_data->inode_number = axfs_inode_number; + + /* Increment the number of times the node has been paged in */ + profile_data->count++; +} + +#else + +int axfs_init_profiling(struct axfs_super *sbi) +{ + return 0; +} + +int axfs_shutdown_profiling(struct axfs_super *sbi) +{ + return 0; +} + +void axfs_profiling_add(struct axfs_super *sbi, unsigned long array_index, + unsigned int axfs_inode_number) +{ +} + +#endif /* CONFIG_AXFS_PROFILING */ -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/