[PATCH 05/10] AXFS: axfs_profiling.c

Jared Hulbert
2008-08-21T05:46:49Z

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 <jaredeh@gmail.com>
---
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 <jaredeh@gmail.com>
+ * 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 <linux/axfs.h>
+#ifdef CONFIG_AXFS_PROFILING
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/proc_fs.h>
+
+#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 */