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