/** * @file op_dname.c * dentry stack walking * * @remark Copyright 2002 OProfile authors * @remark Read the file COPYING * * @author John Levon * @author Philippe Elie */ #include <linux/sched.h> #include <linux/unistd.h> #include <linux/mman.h> #include <linux/file.h> #include "oprofile.h" #include "op_dcache.h" #include "op_util.h" /* --------- device routines ------------- */ uint op_dname_top; struct qstr ** op_dname_stack; char * op_pool_pos; char * op_pool_start; char * op_pool_end; static ulong hash_map_open; static struct op_hash_index * hash_map; unsigned long is_map_ready(void) { return hash_map_open; } int oprof_init_hashmap(void) { uint i; op_dname_stack = kmalloc(DNAME_STACK_MAX * sizeof(struct qstr *), GFP_KERNEL); if (!op_dname_stack) return -EFAULT; op_dname_top = 0; memset(op_dname_stack, 0, DNAME_STACK_MAX * sizeof(struct qstr *)); hash_map = rvmalloc(PAGE_ALIGN(OP_HASH_MAP_SIZE)); if (!hash_map) return -EFAULT; for (i = 0; i < OP_HASH_MAP_NR; ++i) { hash_map[i].name = 0; hash_map[i].parent = -1; } op_pool_start = (char *)(hash_map + OP_HASH_MAP_NR); op_pool_end = op_pool_start + POOL_SIZE; op_pool_pos = op_pool_start; /* Ensure that the zero hash map entry is never used, we use this * value as end of path terminator */ hash_map[0].name = alloc_in_pool("/", 1); hash_map[0].parent = 0; return 0; } void oprof_free_hashmap(void) { kfree(op_dname_stack); rvfree(hash_map, PAGE_ALIGN(OP_HASH_MAP_SIZE)); } int oprof_hash_map_open(void) { if (test_and_set_bit(0, &hash_map_open)) return -EBUSY; return 0; } int oprof_hash_map_release(void) { if (!hash_map_open) return -EFAULT; clear_bit(0, &hash_map_open); return 0; } int oprof_hash_map_mmap(struct file * file, struct vm_area_struct * vma) { ulong start = (ulong)vma->vm_start; ulong page, pos; ulong size = (ulong)(vma->vm_end-vma->vm_start); if (size > PAGE_ALIGN(OP_HASH_MAP_SIZE) || (vma->vm_flags & VM_WRITE) || GET_VM_OFFSET(vma)) return -EINVAL; pos = (ulong)hash_map; while (size > 0) { page = kvirt_to_pa(pos); if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) return -EAGAIN; start += PAGE_SIZE; pos += PAGE_SIZE; size -= PAGE_SIZE; } return 0; } #ifndef NEED_2_2_DENTRIES int wind_dentries_2_4(struct dentry * dentry, struct vfsmount * vfsmnt, struct dentry * root, struct vfsmount * rootmnt) { struct dentry * d = dentry; struct vfsmount * v = vfsmnt; /* wind the dentries onto the stack pages */ for (;;) { /* deleted ? */ if (!IS_ROOT(d) && list_empty(&d->d_hash)) return 0; /* the root */ if (d == root && v == rootmnt) break; if (d == v->mnt_root || IS_ROOT(d)) { if (v->mnt_parent == v) break; /* cross the mount point */ d = v->mnt_mountpoint; v = v->mnt_parent; } push_dname(&d->d_name); d = d->d_parent; } return 1; } /* called with note_lock held */ uint do_path_hash_2_4(struct dentry * dentry, struct vfsmount * vfsmnt) { uint value; struct vfsmount * rootmnt; struct dentry * root; read_lock(¤t->fs->lock); rootmnt = mntget(current->fs->rootmnt); root = dget(current->fs->root); read_unlock(¤t->fs->lock); spin_lock(&dcache_lock); value = do_hash(dentry, vfsmnt, root, rootmnt); spin_unlock(&dcache_lock); dput(root); mntput(rootmnt); return value; } #endif /* NEED_2_2_DENTRIES */ /* called with note_lock held */ uint do_hash(struct dentry * dentry, struct vfsmount * vfsmnt, struct dentry * root, struct vfsmount * rootmnt) { struct qstr * dname; uint value = -1; uint firsthash; uint incr; uint parent = 0; struct op_hash_index * entry; if (!wind_dentries(dentry, vfsmnt, root, rootmnt)) goto out; /* unwind and hash */ while ((dname = pop_dname())) { /* if N is prime, value in [0-N[ and incr = max(1, value) then * iteration: value = (value + incr) % N covers the range [0-N[ * in N iterations */ incr = firsthash = value = name_hash(dname->name, dname->len, parent); if (incr == 0) incr = 1; retry: entry = &hash_map[value]; /* existing entry ? */ if (streq(get_from_pool(entry->name), dname->name) && entry->parent == parent) goto next; /* new entry ? */ if (entry->parent == -1) { if (add_hash_entry(entry, parent, dname->name, dname->len)) goto fullpool; goto next; } /* nope, find another place in the table */ value = (value + incr) % OP_HASH_MAP_NR; if (value == firsthash) goto fulltable; goto retry; next: parent = value; } out: op_dname_top = 0; return value; fullpool: printk(KERN_ERR "oprofile: string pool exhausted.\n"); value = -1; goto out; fulltable: printk(KERN_ERR "oprofile: component hash table full :(\n"); value = -1; goto out; }