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