]> git.hungrycats.org Git - linux/commitdiff
[PATCH] oprofile - dcookies
authorJohn Levon <levon@movementarian.org>
Tue, 15 Oct 2002 11:30:32 +0000 (04:30 -0700)
committerLinus Torvalds <torvalds@home.transmeta.com>
Tue, 15 Oct 2002 11:30:32 +0000 (04:30 -0700)
This implements the persistent path-to-dcookies mapping, and adds a
system call for the user-space profiler to look up the profile data, so
it can tag profiles to specific binaries.

arch/i386/kernel/entry.S
fs/Makefile
fs/dcache.c
fs/dcookies.c [new file with mode: 0644]
include/asm-i386/unistd.h
include/linux/dcache.h
include/linux/dcookies.h [new file with mode: 0644]
kernel/sys.c

index 557b684431c51b00a8179eb9f3823175ac7b40f7..e873703e0c34a08c5a0459da62f2bff6cd9700d2 100644 (file)
@@ -736,6 +736,7 @@ ENTRY(sys_call_table)
        .long sys_alloc_hugepages /* 250 */
        .long sys_free_hugepages
        .long sys_exit_group
+       .long sys_lookup_dcookie
 
        .rept NR_syscalls-(.-sys_call_table)/4
                .long sys_ni_syscall
index d902bdd8bda3bc4117793435156cadb3c2890e29..a4320cf860acbd30fcc69a815fc6d37f0249a716 100644 (file)
@@ -6,7 +6,7 @@
 # 
 
 export-objs := open.o dcache.o buffer.o bio.o inode.o dquot.o mpage.o aio.o \
-                fcntl.o read_write.o
+                fcntl.o read_write.o dcookies.o
 
 obj-y :=       open.o read_write.o devices.o file_table.o buffer.o \
                bio.o super.o block_dev.o char_dev.o stat.o exec.o pipe.o \
@@ -40,6 +40,8 @@ obj-y                         += partitions/
 obj-y                          += driverfs/
 obj-y                          += devpts/
 
+obj-$(CONFIG_PROFILING)                += dcookies.o
 # Do not add any filesystems before this line
 obj-$(CONFIG_EXT3_FS)          += ext3/ # Before ext2 so root fs can be ext3
 obj-$(CONFIG_JBD)              += jbd/
index ef0871dbcdb204f7b2dad1e826f568ccc70331ff..d0fcfeba16eeb3c2a1461bc01af56b189eff2bff 100644 (file)
@@ -637,6 +637,7 @@ struct dentry * d_alloc(struct dentry * parent, const struct qstr *name)
        dentry->d_op = NULL;
        dentry->d_fsdata = NULL;
        dentry->d_mounted = 0;
+       dentry->d_cookie = NULL;
        INIT_LIST_HEAD(&dentry->d_hash);
        INIT_LIST_HEAD(&dentry->d_lru);
        INIT_LIST_HEAD(&dentry->d_subdirs);
diff --git a/fs/dcookies.c b/fs/dcookies.c
new file mode 100644 (file)
index 0000000..0236c14
--- /dev/null
@@ -0,0 +1,323 @@
+/*
+ * dcookies.c
+ *
+ * Copyright 2002 John Levon <levon@movementarian.org>
+ *
+ * Persistent cookie-path mappings. These are used by
+ * profilers to convert a per-task EIP value into something
+ * non-transitory that can be processed at a later date.
+ * This is done by locking the dentry/vfsmnt pair in the
+ * kernel until released by the tasks needing the persistent
+ * objects. The tag is simply an unsigned long that refers
+ * to the pair and can be looked up from userspace.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/mount.h>
+#include <linux/dcache.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/dcookies.h>
+#include <asm/uaccess.h>
+
+/* The dcookies are allocated from a kmem_cache and
+ * hashed onto a small number of lists. None of the
+ * code here is particularly performance critical
+ */
+struct dcookie_struct {
+       struct dentry * dentry;
+       struct vfsmount * vfsmnt;
+       struct list_head hash_list;
+};
+
+static LIST_HEAD(dcookie_users);
+static DECLARE_MUTEX(dcookie_sem);
+static kmem_cache_t * dcookie_cache;
+static struct list_head * dcookie_hashtable;
+static size_t hash_size;
+
+static inline int is_live(void)
+{
+       return !(list_empty(&dcookie_users));
+}
+
+
+/* The dentry is locked, its address will do for the cookie */
+static inline unsigned long dcookie_value(struct dcookie_struct * dcs)
+{
+       return (unsigned long)dcs->dentry;
+}
+
+
+static size_t dcookie_hash(unsigned long dcookie)
+{
+       return (dcookie >> 2) & (hash_size - 1);
+}
+
+
+static struct dcookie_struct * find_dcookie(unsigned long dcookie)
+{
+       struct dcookie_struct * found = 0;
+       struct dcookie_struct * dcs;
+       struct list_head * pos;
+       struct list_head * list;
+
+       list = dcookie_hashtable + dcookie_hash(dcookie);
+
+       list_for_each(pos, list) {
+               dcs = list_entry(pos, struct dcookie_struct, hash_list);
+               if (dcookie_value(dcs) == dcookie) {
+                       found = dcs;
+                       break;
+               }
+       }
+
+       return found;
+}
+
+
+static void hash_dcookie(struct dcookie_struct * dcs)
+{
+       struct list_head * list = dcookie_hashtable + dcookie_hash(dcookie_value(dcs));
+       list_add(&dcs->hash_list, list);
+}
+
+
+static struct dcookie_struct * alloc_dcookie(struct dentry * dentry,
+       struct vfsmount * vfsmnt)
+{
+       struct dcookie_struct * dcs = kmem_cache_alloc(dcookie_cache, GFP_KERNEL);
+       if (!dcs)
+               return NULL;
+
+       atomic_inc(&dentry->d_count);
+       atomic_inc(&vfsmnt->mnt_count);
+       dentry->d_cookie = dcs;
+
+       dcs->dentry = dentry;
+       dcs->vfsmnt = vfsmnt;
+       hash_dcookie(dcs);
+
+       return dcs;
+}
+
+
+/* This is the main kernel-side routine that retrieves the cookie
+ * value for a dentry/vfsmnt pair.
+ */
+int get_dcookie(struct dentry * dentry, struct vfsmount * vfsmnt,
+       unsigned long * cookie)
+{
+       int err = 0;
+       struct dcookie_struct * dcs;
+
+       down(&dcookie_sem);
+
+       if (!is_live()) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       dcs = dentry->d_cookie;
+
+       if (!dcs)
+               dcs = alloc_dcookie(dentry, vfsmnt);
+
+       if (!dcs) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       *cookie = dcookie_value(dcs);
+
+out:
+       up(&dcookie_sem);
+       return err;
+}
+
+
+/* And here is where the userspace process can look up the cookie value
+ * to retrieve the path.
+ */
+asmlinkage int sys_lookup_dcookie(unsigned long cookie, char * buf, size_t len)
+{
+       char * kbuf;
+       char * path;
+       int err = -EINVAL;
+       size_t pathlen;
+       struct dcookie_struct * dcs;
+
+       /* we could leak path information to users
+        * without dir read permission without this
+        */
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       down(&dcookie_sem);
+
+       if (!is_live()) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       if (!(dcs = find_dcookie(cookie)))
+               goto out;
+
+       err = -ENOMEM;
+       kbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+       if (!kbuf)
+               goto out;
+       memset(kbuf, 0, PAGE_SIZE);
+
+       /* FIXME: (deleted) ? */
+       path = d_path(dcs->dentry, dcs->vfsmnt, kbuf, PAGE_SIZE);
+
+       err = 0;
+
+       pathlen = kbuf + PAGE_SIZE - path;
+       if (len > pathlen)
+               len = pathlen;
+
+       if (copy_to_user(buf, path, len))
+               err = -EFAULT;
+
+       kfree(kbuf);
+out:
+       up(&dcookie_sem);
+       return err;
+}
+
+
+static int dcookie_init(void)
+{
+       struct list_head * d;
+       unsigned int i, hash_bits;
+       int err = -ENOMEM;
+
+       dcookie_cache = kmem_cache_create("dcookie_cache",
+               sizeof(struct dcookie_struct),
+               0, 0, NULL, NULL);
+
+       if (!dcookie_cache)
+               goto out;
+
+       dcookie_hashtable = kmalloc(PAGE_SIZE, GFP_KERNEL);
+       if (!dcookie_hashtable)
+               goto out_kmem;
+
+       err = 0;
+
+       /*
+        * Find the power-of-two list-heads that can fit into the allocation..
+        * We don't guarantee that "sizeof(struct list_head)" is necessarily
+        * a power-of-two.
+        */
+       hash_size = PAGE_SIZE / sizeof(struct list_head);
+       hash_bits = 0;
+       do {
+               hash_bits++;
+       } while ((hash_size >> hash_bits) != 0);
+       hash_bits--;
+
+       /*
+        * Re-calculate the actual number of entries and the mask
+        * from the number of bits we can fit.
+        */
+       hash_size = 1UL << hash_bits;
+
+       /* And initialize the newly allocated array */
+       d = dcookie_hashtable;
+       i = hash_size;
+       do {
+               INIT_LIST_HEAD(d);
+               d++;
+               i--;
+       } while (i);
+
+out:
+       return err;
+out_kmem:
+       kmem_cache_destroy(dcookie_cache);
+       goto out;
+}
+
+
+static void free_dcookie(struct dcookie_struct * dcs)
+{
+       dcs->dentry->d_cookie = NULL;
+       dput(dcs->dentry);
+       mntput(dcs->vfsmnt);
+       kmem_cache_free(dcookie_cache, dcs);
+}
+
+
+static void dcookie_exit(void)
+{
+       struct list_head * list;
+       struct list_head * pos;
+       struct list_head * pos2;
+       struct dcookie_struct * dcs;
+       size_t i;
+
+       for (i = 0; i < hash_size; ++i) {
+               list = dcookie_hashtable + i;
+               list_for_each_safe(pos, pos2, list) {
+                       dcs = list_entry(pos, struct dcookie_struct, hash_list);
+                       list_del(&dcs->hash_list);
+                       free_dcookie(dcs);
+               }
+       }
+
+       kfree(dcookie_hashtable);
+       kmem_cache_destroy(dcookie_cache);
+}
+
+
+struct dcookie_user {
+       struct list_head next;
+};
+struct dcookie_user * dcookie_register(void)
+{
+       struct dcookie_user * user;
+
+       down(&dcookie_sem);
+
+       user = kmalloc(sizeof(struct dcookie_user), GFP_KERNEL);
+       if (!user)
+               goto out;
+
+       if (!is_live() && dcookie_init())
+               goto out_free;
+
+       list_add(&user->next, &dcookie_users);
+
+out:
+       up(&dcookie_sem);
+       return user;
+out_free:
+       kfree(user);
+       user = NULL;
+       goto out;
+}
+
+
+void dcookie_unregister(struct dcookie_user * user)
+{
+       down(&dcookie_sem);
+
+       list_del(&user->next);
+       kfree(user);
+
+       if (!is_live())
+               dcookie_exit();
+
+       up(&dcookie_sem);
+}
+
+EXPORT_SYMBOL_GPL(dcookie_register);
+EXPORT_SYMBOL_GPL(dcookie_unregister);
+EXPORT_SYMBOL_GPL(get_dcookie);
index 8765a0f82affbde680d87ea875fa671663b31a9a..159dfa7fefe16414f5213e575d4b2cbb96493027 100644 (file)
 #define __NR_alloc_hugepages   250
 #define __NR_free_hugepages    251
 #define __NR_exit_group                252
+#define __NR_lookup_dcookie    253
+  
 
 /* user-visible error numbers are in the range -1 - -124: see <asm-i386/errno.h> */
 
index 71708edafce9f09aaa749cc9582a3c9790080c09..76a5085043e167ade65da057f9baf9a41e4ae203 100644 (file)
@@ -66,6 +66,8 @@ static __inline__ unsigned int full_name_hash(const unsigned char * name, unsign
 
 #define DNAME_INLINE_LEN 16
 
+struct dcookie_struct;
 struct dentry {
        atomic_t d_count;
        unsigned int d_flags;
@@ -84,6 +86,7 @@ struct dentry {
        unsigned long d_vfs_flags;
        void * d_fsdata;                /* fs-specific data */
        unsigned char d_iname[DNAME_INLINE_LEN]; /* small names */
+       struct dcookie_struct * d_cookie; /* cookie, if any */
 };
 
 struct dentry_operations {
diff --git a/include/linux/dcookies.h b/include/linux/dcookies.h
new file mode 100644 (file)
index 0000000..b2ae969
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * dcookies.h
+ *
+ * Persistent cookie-path mappings
+ *
+ * Copyright 2002 John Levon <levon@movementarian.org>
+ */
+
+#ifndef DCOOKIES_H
+#define DCOOKIES_H
+#include <linux/config.h>
+
+#ifdef CONFIG_PROFILING
+#include <linux/types.h>
+struct dcookie_user;
+/**
+ * dcookie_register - register a user of dcookies
+ *
+ * Register as a dcookie user. Returns %NULL on failure.
+ */
+struct dcookie_user * dcookie_register(void);
+
+/**
+ * dcookie_unregister - unregister a user of dcookies
+ *
+ * Unregister as a dcookie user. This may invalidate
+ * any dcookie values returned from get_dcookie().
+ */
+void dcookie_unregister(struct dcookie_user * user);
+  
+/**
+ * get_dcookie - acquire a dcookie
+ *
+ * Convert the given dentry/vfsmount pair into
+ * a cookie value.
+ *
+ * Returns -EINVAL if no living task has registered as a
+ * dcookie user.
+ *
+ * Returns 0 on success, with *cookie filled in
+ */
+int get_dcookie(struct dentry * dentry, struct vfsmount * vfsmnt,
+       unsigned long * cookie);
+
+#else
+
+struct dcookie_user * dcookie_register(void)
+{
+       return 0;
+}
+
+void dcookie_unregister(struct dcookie_user * user)
+{
+       return;
+}
+static inline int get_dcookie(struct dentry * dentry,
+       struct vfsmount * vfsmnt, unsigned long * cookie)
+{
+       return -ENOSYS;
+} 
+#endif /* CONFIG_PROFILING */
+#endif /* DCOOKIES_H */
index 5b7e84384cfa1823e4f4b7e167594b30afc158d5..3c2992ac68f2fa9f9649405e473de476dd6ffcb3 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/device.h>
 #include <linux/times.h>
 #include <linux/security.h>
+#include <linux/dcookies.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -202,6 +203,7 @@ asmlinkage long sys_ni_syscall(void)
 cond_syscall(sys_nfsservctl)
 cond_syscall(sys_quotactl)
 cond_syscall(sys_acct)
+cond_syscall(sys_lookup_dcookie)
 
 static int set_one_prio(struct task_struct *p, int niceval, int error)
 {