Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/fs/dcookies.c
15109 views
1
/*
2
* dcookies.c
3
*
4
* Copyright 2002 John Levon <[email protected]>
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
*/
34
struct dcookie_struct {
35
struct path path;
36
struct list_head hash_list;
37
};
38
39
static LIST_HEAD(dcookie_users);
40
static DEFINE_MUTEX(dcookie_mutex);
41
static struct kmem_cache *dcookie_cache __read_mostly;
42
static struct list_head *dcookie_hashtable __read_mostly;
43
static size_t hash_size __read_mostly;
44
45
static 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 */
52
static inline unsigned long dcookie_value(struct dcookie_struct * dcs)
53
{
54
return (unsigned long)dcs->path.dentry;
55
}
56
57
58
static size_t dcookie_hash(unsigned long dcookie)
59
{
60
return (dcookie >> L1_CACHE_SHIFT) & (hash_size - 1);
61
}
62
63
64
static 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
85
static 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
92
static 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
*/
115
int 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
139
out:
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
*/
148
SYSCALL_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
197
out_free:
198
kfree(kbuf);
199
return err;
200
out:
201
mutex_unlock(&dcookie_mutex);
202
return err;
203
}
204
#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS
205
asmlinkage long SyS_lookup_dcookie(u64 cookie64, long buf, long len)
206
{
207
return SYSC_lookup_dcookie(cookie64, (char __user *) buf, (size_t) len);
208
}
209
SYSCALL_ALIAS(sys_lookup_dcookie, SyS_lookup_dcookie);
210
#endif
211
212
static 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
258
out:
259
return err;
260
out_kmem:
261
kmem_cache_destroy(dcookie_cache);
262
goto out;
263
}
264
265
266
static 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
279
static 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
301
struct dcookie_user {
302
struct list_head next;
303
};
304
305
struct 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
320
out:
321
mutex_unlock(&dcookie_mutex);
322
return user;
323
out_free:
324
kfree(user);
325
user = NULL;
326
goto out;
327
}
328
329
330
void 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
343
EXPORT_SYMBOL_GPL(dcookie_register);
344
EXPORT_SYMBOL_GPL(dcookie_unregister);
345
EXPORT_SYMBOL_GPL(get_dcookie);
346
347