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 v3.7-rc2 345 lines 7.0 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/export.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 mutex_unlock(&dcookie_mutex); 182 183 if (IS_ERR(path)) { 184 err = PTR_ERR(path); 185 goto out_free; 186 } 187 188 err = -ERANGE; 189 190 pathlen = kbuf + PAGE_SIZE - path; 191 if (pathlen <= len) { 192 err = pathlen; 193 if (copy_to_user(buf, path, pathlen)) 194 err = -EFAULT; 195 } 196 197out_free: 198 kfree(kbuf); 199 return err; 200out: 201 mutex_unlock(&dcookie_mutex); 202 return err; 203} 204#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS 205asmlinkage long SyS_lookup_dcookie(u64 cookie64, long buf, long len) 206{ 207 return SYSC_lookup_dcookie(cookie64, (char __user *) buf, (size_t) len); 208} 209SYSCALL_ALIAS(sys_lookup_dcookie, SyS_lookup_dcookie); 210#endif 211 212static int dcookie_init(void) 213{ 214 struct list_head * d; 215 unsigned int i, hash_bits; 216 int err = -ENOMEM; 217 218 dcookie_cache = kmem_cache_create("dcookie_cache", 219 sizeof(struct dcookie_struct), 220 0, 0, NULL); 221 222 if (!dcookie_cache) 223 goto out; 224 225 dcookie_hashtable = kmalloc(PAGE_SIZE, GFP_KERNEL); 226 if (!dcookie_hashtable) 227 goto out_kmem; 228 229 err = 0; 230 231 /* 232 * Find the power-of-two list-heads that can fit into the allocation.. 233 * We don't guarantee that "sizeof(struct list_head)" is necessarily 234 * a power-of-two. 235 */ 236 hash_size = PAGE_SIZE / sizeof(struct list_head); 237 hash_bits = 0; 238 do { 239 hash_bits++; 240 } while ((hash_size >> hash_bits) != 0); 241 hash_bits--; 242 243 /* 244 * Re-calculate the actual number of entries and the mask 245 * from the number of bits we can fit. 246 */ 247 hash_size = 1UL << hash_bits; 248 249 /* And initialize the newly allocated array */ 250 d = dcookie_hashtable; 251 i = hash_size; 252 do { 253 INIT_LIST_HEAD(d); 254 d++; 255 i--; 256 } while (i); 257 258out: 259 return err; 260out_kmem: 261 kmem_cache_destroy(dcookie_cache); 262 goto out; 263} 264 265 266static void free_dcookie(struct dcookie_struct * dcs) 267{ 268 struct dentry *d = dcs->path.dentry; 269 270 spin_lock(&d->d_lock); 271 d->d_flags &= ~DCACHE_COOKIE; 272 spin_unlock(&d->d_lock); 273 274 path_put(&dcs->path); 275 kmem_cache_free(dcookie_cache, dcs); 276} 277 278 279static void dcookie_exit(void) 280{ 281 struct list_head * list; 282 struct list_head * pos; 283 struct list_head * pos2; 284 struct dcookie_struct * dcs; 285 size_t i; 286 287 for (i = 0; i < hash_size; ++i) { 288 list = dcookie_hashtable + i; 289 list_for_each_safe(pos, pos2, list) { 290 dcs = list_entry(pos, struct dcookie_struct, hash_list); 291 list_del(&dcs->hash_list); 292 free_dcookie(dcs); 293 } 294 } 295 296 kfree(dcookie_hashtable); 297 kmem_cache_destroy(dcookie_cache); 298} 299 300 301struct dcookie_user { 302 struct list_head next; 303}; 304 305struct dcookie_user * dcookie_register(void) 306{ 307 struct dcookie_user * user; 308 309 mutex_lock(&dcookie_mutex); 310 311 user = kmalloc(sizeof(struct dcookie_user), GFP_KERNEL); 312 if (!user) 313 goto out; 314 315 if (!is_live() && dcookie_init()) 316 goto out_free; 317 318 list_add(&user->next, &dcookie_users); 319 320out: 321 mutex_unlock(&dcookie_mutex); 322 return user; 323out_free: 324 kfree(user); 325 user = NULL; 326 goto out; 327} 328 329 330void dcookie_unregister(struct dcookie_user * user) 331{ 332 mutex_lock(&dcookie_mutex); 333 334 list_del(&user->next); 335 kfree(user); 336 337 if (!is_live()) 338 dcookie_exit(); 339 340 mutex_unlock(&dcookie_mutex); 341} 342 343EXPORT_SYMBOL_GPL(dcookie_register); 344EXPORT_SYMBOL_GPL(dcookie_unregister); 345EXPORT_SYMBOL_GPL(get_dcookie);