Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.20-rc4 329 lines 6.6 kB view raw
1/* 2 * dcookies.c 3 * 4 * Copyright 2002 John Levon <levon@movementarian.org> 5 * 6 * Persistent cookie-path mappings. These are used by 7 * profilers to convert a per-task EIP value into something 8 * non-transitory that can be processed at a later date. 9 * This is done by locking the dentry/vfsmnt pair in the 10 * kernel until released by the tasks needing the persistent 11 * objects. The tag is simply an unsigned long that refers 12 * to the pair and can be looked up from userspace. 13 */ 14 15#include <linux/syscalls.h> 16#include <linux/module.h> 17#include <linux/slab.h> 18#include <linux/list.h> 19#include <linux/mount.h> 20#include <linux/capability.h> 21#include <linux/dcache.h> 22#include <linux/mm.h> 23#include <linux/errno.h> 24#include <linux/dcookies.h> 25#include <linux/mutex.h> 26#include <asm/uaccess.h> 27 28/* The dcookies are allocated from a kmem_cache and 29 * hashed onto a small number of lists. None of the 30 * code here is particularly performance critical 31 */ 32struct dcookie_struct { 33 struct dentry * dentry; 34 struct vfsmount * vfsmnt; 35 struct list_head hash_list; 36}; 37 38static LIST_HEAD(dcookie_users); 39static DEFINE_MUTEX(dcookie_mutex); 40static struct kmem_cache *dcookie_cache __read_mostly; 41static struct list_head *dcookie_hashtable __read_mostly; 42static size_t hash_size __read_mostly; 43 44static inline int is_live(void) 45{ 46 return !(list_empty(&dcookie_users)); 47} 48 49 50/* The dentry is locked, its address will do for the cookie */ 51static inline unsigned long dcookie_value(struct dcookie_struct * dcs) 52{ 53 return (unsigned long)dcs->dentry; 54} 55 56 57static size_t dcookie_hash(unsigned long dcookie) 58{ 59 return (dcookie >> L1_CACHE_SHIFT) & (hash_size - 1); 60} 61 62 63static struct dcookie_struct * find_dcookie(unsigned long dcookie) 64{ 65 struct dcookie_struct *found = NULL; 66 struct dcookie_struct * dcs; 67 struct list_head * pos; 68 struct list_head * list; 69 70 list = dcookie_hashtable + dcookie_hash(dcookie); 71 72 list_for_each(pos, list) { 73 dcs = list_entry(pos, struct dcookie_struct, hash_list); 74 if (dcookie_value(dcs) == dcookie) { 75 found = dcs; 76 break; 77 } 78 } 79 80 return found; 81} 82 83 84static void hash_dcookie(struct dcookie_struct * dcs) 85{ 86 struct list_head * list = dcookie_hashtable + dcookie_hash(dcookie_value(dcs)); 87 list_add(&dcs->hash_list, list); 88} 89 90 91static struct dcookie_struct * alloc_dcookie(struct dentry * dentry, 92 struct vfsmount * vfsmnt) 93{ 94 struct dcookie_struct * dcs = kmem_cache_alloc(dcookie_cache, GFP_KERNEL); 95 if (!dcs) 96 return NULL; 97 98 dentry->d_cookie = dcs; 99 100 dcs->dentry = dget(dentry); 101 dcs->vfsmnt = mntget(vfsmnt); 102 hash_dcookie(dcs); 103 104 return dcs; 105} 106 107 108/* This is the main kernel-side routine that retrieves the cookie 109 * value for a dentry/vfsmnt pair. 110 */ 111int get_dcookie(struct dentry * dentry, struct vfsmount * vfsmnt, 112 unsigned long * cookie) 113{ 114 int err = 0; 115 struct dcookie_struct * dcs; 116 117 mutex_lock(&dcookie_mutex); 118 119 if (!is_live()) { 120 err = -EINVAL; 121 goto out; 122 } 123 124 dcs = dentry->d_cookie; 125 126 if (!dcs) 127 dcs = alloc_dcookie(dentry, vfsmnt); 128 129 if (!dcs) { 130 err = -ENOMEM; 131 goto out; 132 } 133 134 *cookie = dcookie_value(dcs); 135 136out: 137 mutex_unlock(&dcookie_mutex); 138 return err; 139} 140 141 142/* And here is where the userspace process can look up the cookie value 143 * to retrieve the path. 144 */ 145asmlinkage long sys_lookup_dcookie(u64 cookie64, char __user * buf, size_t len) 146{ 147 unsigned long cookie = (unsigned long)cookie64; 148 int err = -EINVAL; 149 char * kbuf; 150 char * path; 151 size_t pathlen; 152 struct dcookie_struct * dcs; 153 154 /* we could leak path information to users 155 * without dir read permission without this 156 */ 157 if (!capable(CAP_SYS_ADMIN)) 158 return -EPERM; 159 160 mutex_lock(&dcookie_mutex); 161 162 if (!is_live()) { 163 err = -EINVAL; 164 goto out; 165 } 166 167 if (!(dcs = find_dcookie(cookie))) 168 goto out; 169 170 err = -ENOMEM; 171 kbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); 172 if (!kbuf) 173 goto out; 174 175 /* FIXME: (deleted) ? */ 176 path = d_path(dcs->dentry, dcs->vfsmnt, kbuf, PAGE_SIZE); 177 178 if (IS_ERR(path)) { 179 err = PTR_ERR(path); 180 goto out_free; 181 } 182 183 err = -ERANGE; 184 185 pathlen = kbuf + PAGE_SIZE - path; 186 if (pathlen <= len) { 187 err = pathlen; 188 if (copy_to_user(buf, path, pathlen)) 189 err = -EFAULT; 190 } 191 192out_free: 193 kfree(kbuf); 194out: 195 mutex_unlock(&dcookie_mutex); 196 return err; 197} 198 199 200static int dcookie_init(void) 201{ 202 struct list_head * d; 203 unsigned int i, hash_bits; 204 int err = -ENOMEM; 205 206 dcookie_cache = kmem_cache_create("dcookie_cache", 207 sizeof(struct dcookie_struct), 208 0, 0, NULL, NULL); 209 210 if (!dcookie_cache) 211 goto out; 212 213 dcookie_hashtable = kmalloc(PAGE_SIZE, GFP_KERNEL); 214 if (!dcookie_hashtable) 215 goto out_kmem; 216 217 err = 0; 218 219 /* 220 * Find the power-of-two list-heads that can fit into the allocation.. 221 * We don't guarantee that "sizeof(struct list_head)" is necessarily 222 * a power-of-two. 223 */ 224 hash_size = PAGE_SIZE / sizeof(struct list_head); 225 hash_bits = 0; 226 do { 227 hash_bits++; 228 } while ((hash_size >> hash_bits) != 0); 229 hash_bits--; 230 231 /* 232 * Re-calculate the actual number of entries and the mask 233 * from the number of bits we can fit. 234 */ 235 hash_size = 1UL << hash_bits; 236 237 /* And initialize the newly allocated array */ 238 d = dcookie_hashtable; 239 i = hash_size; 240 do { 241 INIT_LIST_HEAD(d); 242 d++; 243 i--; 244 } while (i); 245 246out: 247 return err; 248out_kmem: 249 kmem_cache_destroy(dcookie_cache); 250 goto out; 251} 252 253 254static void free_dcookie(struct dcookie_struct * dcs) 255{ 256 dcs->dentry->d_cookie = NULL; 257 dput(dcs->dentry); 258 mntput(dcs->vfsmnt); 259 kmem_cache_free(dcookie_cache, dcs); 260} 261 262 263static void dcookie_exit(void) 264{ 265 struct list_head * list; 266 struct list_head * pos; 267 struct list_head * pos2; 268 struct dcookie_struct * dcs; 269 size_t i; 270 271 for (i = 0; i < hash_size; ++i) { 272 list = dcookie_hashtable + i; 273 list_for_each_safe(pos, pos2, list) { 274 dcs = list_entry(pos, struct dcookie_struct, hash_list); 275 list_del(&dcs->hash_list); 276 free_dcookie(dcs); 277 } 278 } 279 280 kfree(dcookie_hashtable); 281 kmem_cache_destroy(dcookie_cache); 282} 283 284 285struct dcookie_user { 286 struct list_head next; 287}; 288 289struct dcookie_user * dcookie_register(void) 290{ 291 struct dcookie_user * user; 292 293 mutex_lock(&dcookie_mutex); 294 295 user = kmalloc(sizeof(struct dcookie_user), GFP_KERNEL); 296 if (!user) 297 goto out; 298 299 if (!is_live() && dcookie_init()) 300 goto out_free; 301 302 list_add(&user->next, &dcookie_users); 303 304out: 305 mutex_unlock(&dcookie_mutex); 306 return user; 307out_free: 308 kfree(user); 309 user = NULL; 310 goto out; 311} 312 313 314void dcookie_unregister(struct dcookie_user * user) 315{ 316 mutex_lock(&dcookie_mutex); 317 318 list_del(&user->next); 319 kfree(user); 320 321 if (!is_live()) 322 dcookie_exit(); 323 324 mutex_unlock(&dcookie_mutex); 325} 326 327EXPORT_SYMBOL_GPL(dcookie_register); 328EXPORT_SYMBOL_GPL(dcookie_unregister); 329EXPORT_SYMBOL_GPL(get_dcookie);