| /** |
| * @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; |
| } |