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.26-rc7 326 lines 6.5 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 if (!dcs) 97 return NULL; 98 99 path->dentry->d_cookie = dcs; 100 dcs->path = *path; 101 path_get(path); 102 hash_dcookie(dcs); 103 return dcs; 104} 105 106 107/* This is the main kernel-side routine that retrieves the cookie 108 * value for a dentry/vfsmnt pair. 109 */ 110int get_dcookie(struct path *path, unsigned long *cookie) 111{ 112 int err = 0; 113 struct dcookie_struct * dcs; 114 115 mutex_lock(&dcookie_mutex); 116 117 if (!is_live()) { 118 err = -EINVAL; 119 goto out; 120 } 121 122 dcs = path->dentry->d_cookie; 123 124 if (!dcs) 125 dcs = alloc_dcookie(path); 126 127 if (!dcs) { 128 err = -ENOMEM; 129 goto out; 130 } 131 132 *cookie = dcookie_value(dcs); 133 134out: 135 mutex_unlock(&dcookie_mutex); 136 return err; 137} 138 139 140/* And here is where the userspace process can look up the cookie value 141 * to retrieve the path. 142 */ 143asmlinkage long sys_lookup_dcookie(u64 cookie64, char __user * buf, size_t len) 144{ 145 unsigned long cookie = (unsigned long)cookie64; 146 int err = -EINVAL; 147 char * kbuf; 148 char * path; 149 size_t pathlen; 150 struct dcookie_struct * dcs; 151 152 /* we could leak path information to users 153 * without dir read permission without this 154 */ 155 if (!capable(CAP_SYS_ADMIN)) 156 return -EPERM; 157 158 mutex_lock(&dcookie_mutex); 159 160 if (!is_live()) { 161 err = -EINVAL; 162 goto out; 163 } 164 165 if (!(dcs = find_dcookie(cookie))) 166 goto out; 167 168 err = -ENOMEM; 169 kbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); 170 if (!kbuf) 171 goto out; 172 173 /* FIXME: (deleted) ? */ 174 path = d_path(&dcs->path, kbuf, PAGE_SIZE); 175 176 if (IS_ERR(path)) { 177 err = PTR_ERR(path); 178 goto out_free; 179 } 180 181 err = -ERANGE; 182 183 pathlen = kbuf + PAGE_SIZE - path; 184 if (pathlen <= len) { 185 err = pathlen; 186 if (copy_to_user(buf, path, pathlen)) 187 err = -EFAULT; 188 } 189 190out_free: 191 kfree(kbuf); 192out: 193 mutex_unlock(&dcookie_mutex); 194 return err; 195} 196 197 198static int dcookie_init(void) 199{ 200 struct list_head * d; 201 unsigned int i, hash_bits; 202 int err = -ENOMEM; 203 204 dcookie_cache = kmem_cache_create("dcookie_cache", 205 sizeof(struct dcookie_struct), 206 0, 0, NULL); 207 208 if (!dcookie_cache) 209 goto out; 210 211 dcookie_hashtable = kmalloc(PAGE_SIZE, GFP_KERNEL); 212 if (!dcookie_hashtable) 213 goto out_kmem; 214 215 err = 0; 216 217 /* 218 * Find the power-of-two list-heads that can fit into the allocation.. 219 * We don't guarantee that "sizeof(struct list_head)" is necessarily 220 * a power-of-two. 221 */ 222 hash_size = PAGE_SIZE / sizeof(struct list_head); 223 hash_bits = 0; 224 do { 225 hash_bits++; 226 } while ((hash_size >> hash_bits) != 0); 227 hash_bits--; 228 229 /* 230 * Re-calculate the actual number of entries and the mask 231 * from the number of bits we can fit. 232 */ 233 hash_size = 1UL << hash_bits; 234 235 /* And initialize the newly allocated array */ 236 d = dcookie_hashtable; 237 i = hash_size; 238 do { 239 INIT_LIST_HEAD(d); 240 d++; 241 i--; 242 } while (i); 243 244out: 245 return err; 246out_kmem: 247 kmem_cache_destroy(dcookie_cache); 248 goto out; 249} 250 251 252static void free_dcookie(struct dcookie_struct * dcs) 253{ 254 dcs->path.dentry->d_cookie = NULL; 255 path_put(&dcs->path); 256 kmem_cache_free(dcookie_cache, dcs); 257} 258 259 260static void dcookie_exit(void) 261{ 262 struct list_head * list; 263 struct list_head * pos; 264 struct list_head * pos2; 265 struct dcookie_struct * dcs; 266 size_t i; 267 268 for (i = 0; i < hash_size; ++i) { 269 list = dcookie_hashtable + i; 270 list_for_each_safe(pos, pos2, list) { 271 dcs = list_entry(pos, struct dcookie_struct, hash_list); 272 list_del(&dcs->hash_list); 273 free_dcookie(dcs); 274 } 275 } 276 277 kfree(dcookie_hashtable); 278 kmem_cache_destroy(dcookie_cache); 279} 280 281 282struct dcookie_user { 283 struct list_head next; 284}; 285 286struct dcookie_user * dcookie_register(void) 287{ 288 struct dcookie_user * user; 289 290 mutex_lock(&dcookie_mutex); 291 292 user = kmalloc(sizeof(struct dcookie_user), GFP_KERNEL); 293 if (!user) 294 goto out; 295 296 if (!is_live() && dcookie_init()) 297 goto out_free; 298 299 list_add(&user->next, &dcookie_users); 300 301out: 302 mutex_unlock(&dcookie_mutex); 303 return user; 304out_free: 305 kfree(user); 306 user = NULL; 307 goto out; 308} 309 310 311void dcookie_unregister(struct dcookie_user * user) 312{ 313 mutex_lock(&dcookie_mutex); 314 315 list_del(&user->next); 316 kfree(user); 317 318 if (!is_live()) 319 dcookie_exit(); 320 321 mutex_unlock(&dcookie_mutex); 322} 323 324EXPORT_SYMBOL_GPL(dcookie_register); 325EXPORT_SYMBOL_GPL(dcookie_unregister); 326EXPORT_SYMBOL_GPL(get_dcookie);