Print this page
Update from fsd_sep3 webrev to fsd_sep9
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/uts/common/io/fsd/fsd.c
+++ new/usr/src/uts/common/io/fsd/fsd.c
1 1 /*
2 2 * This file and its contents are supplied under the terms of the
3 3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 4 * You may only use this file in accordance with the terms of version
5 5 * 1.0 of the CDDL.
6 6 *
7 7 * A full copy of the text of the CDDL should have accompanied this
8 8 * source. A copy of the CDDL is also available via the Internet at
9 9 * http://www.illumos.org/license/CDDL.
10 10 */
11 11
12 12 /*
13 13 * Copyright 2013 Damian Bogel. All rights reserved.
14 14 */
15 15
16 16 /*
17 17 * Filesystem disturber pseudo-device driver.
18 18 */
19 19
20 20 #include <sys/conf.h>
21 21 #include <sys/ddi.h>
22 22 #include <sys/file.h>
23 23 #include <sys/fsd.h>
24 24 #include <sys/fsh.h>
25 25 #include <sys/kmem.h>
26 26 #include <sys/ksynch.h>
27 27 #include <sys/list.h>
28 28 #include <sys/mkdev.h>
29 29 #include <sys/refstr.h>
30 30 #include <sys/stat.h>
31 31 #include <sys/sunddi.h>
32 32 #include <sys/sysmacros.h>
33 33 #include <sys/types.h>
34 34
35 35 /*
36 36 * TODO:
37 37 * - add checking if a file descriptor passed by the client is indeed
38 38 * a mountpoint (we'd like to avoid disturbing / instead of an
39 39 * unmounted filesystem)
40 40 */
41 41 /*
42 42 * fsd - filesystem disturber
43 43 *
44 44 * 1. Abstract
45 45 * Filesystem disturber is a pseudo-device driver used to inject pathological
46 46 * behaviour into vfs calls. It is NOT a fuzzer. That kind of behaviour
47 47 * should be expected and correctly handled by software. A simple example of
48 48 * such behaviour is read() reading less bytes than it was requested. It's
49 49 * well documented and every read() caller should check the return value of
50 50 * this function before proceeding.
51 51 *
52 52 * 2. Features
53 53 * * per-vfs injections
54 54 * * injection installing on every newly mounted vfs (that's called an
55 55 * omnipresent disturber)
56 56 *
57 57 * 3. Usage
58 58 * fsd_t is a structure which contains all the parameters for the disturbers.
59 59 * This structure is shared by all hooks on a vfs_t.
60 60 *
61 61 * fsd_info_t is filled out by a call to ioctl() and it provides basic
62 62 * information about fsd's current status.
63 63 *
64 64 * fsd_dis_t is passed to ioctl() when a request to disturb a filesystem is
65 65 * made. It's just a descriptor of a representative file and an fsd_t structure.
66 66 *
67 67 * fsd_fs_t is a structure filled out by ioctl() call when the client requests a
68 68 * full list of disturbers installed in the system.
69 69 *
70 70 * fsd_ioc_t is an union for different ioctl() commands.
71 71 *
72 72 * ioctl() commands:
73 73 * FSD_ENABLE:
74 74 * ioctl(fd, FSD_ENABLE);
75 75 * Enables the fsd. When fsd is enabled, any attemps to detach the driver
76 76 * will fail.
77 77 *
78 78 * FSD_DISABLE:
79 79 * ioctl(fd, FSD_DISABLE);
80 80 * Disables the fsd.
81 81 *
82 82 * FSD_GET_PARAM:
83 83 * ioctl(fd, FSD_GET_PARAM, ioc);
84 84 * Get's fsd_t associated with a given filesystem. ioc is fsdioc_mnt when
85 85 * passed to ioctl(). fsdioc_param is the output.
86 86 * Errors:
87 87 * ENOENT - the filesystem is not being disturbed
88 88 *
89 89 * FSD_DISTURB:
90 90 * ioctl(fd, FSD_DISTURB, ioc);
91 91 * Installs a disturber on a given filesystem. If a disturber is already
92 92 * installed on this filesystem, it overwrites it. ioc is fsdioc_dis.
93 93 * Errors:
94 94 * EAGAIN - hook limit exceeded
95 95 * EBADFD - cannot open the file descriptor
96 96 * EINVAL - parameters are invalid
97 97 *
98 98 * FSD_DISTURB_OFF:
99 99 * ioctl(fd, FSD_DISTURB_OFF, ioc);
100 100 * Removes a disturber from a given filesystem. ioc is fsdioc_mnt
101 101 * Errors:
102 102 * EBADFD - cannot open the file descriptor
103 103 * ENOENT - the filesystem is not being disturbed
104 104 *
105 105 * FSD_DISTURB_OMNI:
106 106 * ioctl(fd, FSD_DISTURB_OMNI, ioc);
107 107 * Install an omnipresent disturber. It means that whenever a new vfs_t is
108 108 * being created, this disturber is installed on it. If an omnipresent
109 109 * disturber is already installed, it overwrites it. ioc is fsdioc_param
110 110 * Errors:
111 111 * EINVAL - parameters are invalid
112 112 *
113 113 * FSD_DISTURB_OMNI_OFF:
114 114 * ioctl(fd, FSD_DISTURB_OMNI_OFF);
115 115 * Removes the omnipresent disturber. That does NOT mean that filesystems
116 116 * which are disturbed because of the omnipresent disturber presence in the
117 117 * past are going to stop being disturbed after this call.
118 118 *
119 119 * FSD_GET_LIST:
120 120 * ioctl(fd, FSD_GET_LIST, ioc);
121 121 * Get's a full list of disturbers installed in the system. ioc is
122 122 * fsdioc_list here. This is a structure with two fields, count and listp.
123 123 * The count is the number of fsd_fs_t's allocated on the address that
124 124 * listp is pointing to. There would be at most count fsd_fs_t entries
125 125 * copied out to the caller. Also, count is set to the number of entries
126 126 * copied out.
127 127 *
128 128 * FSD_GET_INFO:
129 129 * ioctl(fd, FSD_GET_INFO, ioc);
↓ open down ↓ |
129 lines elided |
↑ open up ↑ |
130 130 * Get's current information about fsd. ioc is fsdioc_info here.
131 131 *
132 132 * At most one hook is installed per vfs_t, and fsd_t describes all possible
133 133 * disturbance methods. Multiple commands using the fsd should somehow cooperate
134 134 * in order not to destroy each other efforts in installing disturbers.
135 135 *
136 136 * 4. Internals
137 137 * When fsd_enabled is nonzero, fsd_detach() fails.
138 138 *
139 139 * These mount callback is used for installing injections on newly mounted
140 - * vfs_t's (omnipresent).
140 + * vfs_t's (omnipresent). The free callback is used for cleaning up.
141 141 *
142 142 * The list of currently installed hooks is kept in fsd_list.
143 143 *
144 144 * fsd installs at most one hook on a vfs_t.
145 145 *
146 146 * Inside fsd_detach, we go through fsd_hooks list. There is no guarantee that
147 147 * a hook remove callback (fsd_remove_cb) wouldn't execute inside
148 148 * fsh_hook_remove(), thus we can't assume that while walking through fsd_hooks,
149 149 * our iterator will be valid, because fsh_hook_remove() could invalidate it.
150 150 * That's why fsd_detaching flag is introduced.
151 151 *
152 152 * 5. Locking
153 153 * Every modification of fsd_enable, fsd_hooks, fsd_omni_param and fsd_list is
↓ open down ↓ |
3 lines elided |
↑ open up ↑ |
154 154 * protected by fsd_lock.
155 155 *
156 156 * Hooks use only the elements of fsd_list, nothing else. Before an element of
157 157 * fsd_list is destroyed, a hook which uses it is removed. Elements from
158 158 * fsd_lists are removed and destroyed in the hook remove callback
159 159 * (fsd_remove_cb).
160 160 *
161 161 * Because of the fact that fsd_remove_cb() could be called both in the context
162 162 * of the thread that executes fsh_hook_remove() or outside the fsd, we need to
163 163 * use fsd_rem_thread in order not to cause a deadlock. fsh_hook_remove() could
164 - * be called by at most one thread inside fsd (fsd_remove_disturber() holds
164 + * be called by at most one thread inside fsd (fsd_disturber_remove() holds
165 165 * fsd_lock). We just have to check inside fsd_remove_cb() if it was called
166 166 * from fsh_hook_remove() or not. We use fsd_rem_thread to determine that.
167 167 *
168 168 * fsd_int_t.fsdi_param is protected by fsd_int_t.fsdi_lock which is an rwlock.
169 169 */
170 170
171 171 /*
172 172 * Once a set of hooks is installed on a filesystem, there's no need
173 173 * to bother fsh if we want to change the parameters of disturbance.
174 174 * Intead, we use fsd_lock to protect the fsd_int_t when it's being
175 175 * used or changed.
176 176 */
177 177 typedef struct fsd_int {
178 178 krwlock_t fsdi_lock; /* protects fsd_param */
179 179 fsd_t fsdi_param;
180 180 fsh_handle_t fsdi_handle; /* we use fsh's handle in fsd */
181 181 vfs_t *fsdi_vfsp;
182 182 int fsdi_doomed;
183 - list_node_t fsdi_next;
183 + list_node_t fsdi_node;
184 184 } fsd_int_t;
185 185
186 186 static dev_info_t *fsd_devi;
187 187
188 188
189 189 /*
190 190 * fsd_lock protects: fsd_enabled, fsd_omni_param, fsd_list, fsd_cb_handle,
191 191 * fsd_detaching
192 192 */
193 193 static kmutex_t fsd_lock;
194 194
195 195 static kthread_t *fsd_rem_thread;
196 196 static kmutex_t fsd_rem_thread_lock;
197 197
198 198 static fsd_t *fsd_omni_param; /* Argument used by fsd's mount callback. */
199 199 static fsh_callback_handle_t fsd_cb_handle;
200 200 static int fsd_enabled;
201 201 static int fsd_detaching;
202 202
203 203 /*
204 204 * List of fsd_int_t. For every vfs_t on which fsd has installed a set of hooks
205 205 * there exist exactly one fsd_int_t with fsdi_vfsp pointing to this vfs_t.
206 206 */
207 207 static list_t fsd_list;
208 208 static int fsd_list_count;
209 209 static kcondvar_t fsd_cv_empty;
210 210
211 211
212 212 /*
213 213 * Although it's safe to use this kind of pseudo-random number generator,
214 214 * it behaves very regular when it comes to parity. Every fsd_rand() call
215 215 * changes the parity of the seed. That's why when a range of width 2 is set
216 216 * as a parameter, it's highly possible that the random value will always be
217 217 * the same, because fsd_rand() could be called the same number of times in a
218 218 * hook.
219 219 */
220 220 static long fsd_rand_seed;
221 221
222 222 static int
223 223 fsd_rand()
224 224 {
↓ open down ↓ |
31 lines elided |
↑ open up ↑ |
225 225 fsd_rand_seed = fsd_rand_seed * 1103515245L + 12345;
226 226 return (fsd_rand_seed & 0x7ffffffff);
227 227 }
228 228
229 229 /* vnode hooks */
230 230 /*
231 231 * A pointer to a given fsd_int_t is valid always inside fsh_hook_xxx()
232 232 * call, because it's valid until the hooks associated with it are removed.
233 233 * If a hook is removed, it cannot be executing.
234 234 */
235 -static int
236 -fsd_hook_read(fsh_int_t *fshi, void *arg, vnode_t *vp, uio_t *uiop,
237 - int ioflag, cred_t *cr, caller_context_t *ct)
235 +static void
236 +fsd_hook_pre_read(void *arg, void **instancep, vnode_t **vpp, uio_t **uiopp,
237 + int *ioflagp, cred_t **crp, caller_context_t **ctp)
238 238 {
239 + _NOTE(ARGUNUSED(ioflagp));
240 + _NOTE(ARGUNUSED(crp));
241 + _NOTE(ARGUNUSED(ctp));
242 +
239 243 fsd_int_t *fsdi = (fsd_int_t *)arg;
240 - uint64_t count, less, less_chance;
244 + uint64_t less_chance;
241 245
242 246 /*
243 247 * It is used to keep an odd number of fsd_rand() calls in every
244 - * fsd_hook_read() call. That is desired because when a range of width
245 - * 2 is set as a parameter, we don't want to make it a constant.
248 + * fsd_hook_pre_read() call. That is desired because when a range of
249 + * width 2 is set as a parameter, we don't want to make it a constant.
246 250 * The pseudo-random number generator returns a number with different
247 251 * parity with every call. If this function is called in every
248 - * fsd_hook_read() execution even number of times, it would always be
249 - * the same % 2.
252 + * fsd_hook_pre_read() execution even number of times, it would always
253 + * be the same % 2.
250 254 */
251 255 (void) fsd_rand();
252 256
253 - ASSERT(vp->v_vfsp == fsdi->fsdi_vfsp);
257 + ASSERT((*vpp)->v_vfsp == fsdi->fsdi_vfsp);
254 258
255 259 rw_enter(&fsdi->fsdi_lock, RW_READER);
256 260 less_chance = fsdi->fsdi_param.read_less_chance;
257 - less = (uint64_t)fsd_rand() %
258 - (fsdi->fsdi_param.read_less_r[1] + 1 -
259 - fsdi->fsdi_param.read_less_r[0]) + fsdi->fsdi_param.read_less_r[0];
260 261 rw_exit(&fsdi->fsdi_lock);
261 262
262 - count = uiop->uio_iov->iov_len;
263 263 if ((uint64_t)fsd_rand() % 100 < less_chance) {
264 264 extern size_t copyout_max_cached;
265 - int ret;
265 + uint64_t r[2];
266 + uint64_t count, less;
266 267
267 - if (count > less)
268 + count = (*uiopp)->uio_iov->iov_len;
269 + r[0] = fsdi->fsdi_param.read_less_r[0];
270 + r[1] = fsdi->fsdi_param.read_less_r[1];
271 + less = (uint64_t)fsd_rand() % (r[1] + 1 - r[0]) + r[0];
272 +
273 + if (count > less) {
268 274 count -= less;
269 - else
270 - less = 0;
275 + *instancep = kmem_alloc(sizeof (uint64_t), KM_SLEEP);
276 + *(*(uint64_t **)instancep) = less;
277 + } else {
278 + *instancep = NULL;
279 + return;
280 + }
271 281
272 - uiop->uio_iov->iov_len = count;
273 - uiop->uio_resid = count;
282 + (*uiopp)->uio_iov->iov_len = count;
283 + (*uiopp)->uio_resid = count;
274 284 if (count <= copyout_max_cached)
275 - uiop->uio_extflg = UIO_COPY_CACHED;
285 + (*uiopp)->uio_extflg = UIO_COPY_CACHED;
276 286 else
277 - uiop->uio_extflg = UIO_COPY_DEFAULT;
278 -
279 - ret = fsh_next_read(fshi, vp, uiop, ioflag, cr, ct);
280 - uiop->uio_resid += less;
281 - return (ret);
287 + (*uiopp)->uio_extflg = UIO_COPY_DEFAULT;
288 + } else {
289 + *instancep = NULL;
282 290 }
283 -
284 - return (fsh_next_read(fshi, vp, uiop, ioflag, cr, ct));
285 291 }
286 292
293 +static int
294 +fsd_hook_post_read(int ret, void *arg, void *instance, vnode_t *vp,
295 + uio_t *uiop, int oflag, cred_t *cr, caller_context_t *ct)
296 +{
297 + _NOTE(ARGUNUSED(arg));
298 + _NOTE(ARGUNUSED(vp));
299 + _NOTE(ARGUNUSED(oflag));
300 + _NOTE(ARGUNUSED(cr));
301 + _NOTE(ARGUNUSED(ct));
287 302
303 + if (instance != NULL) {
304 + uint64_t *lessp = instance;
305 + uiop->uio_resid += *lessp;
306 + kmem_free(lessp, sizeof (*lessp));
307 + }
308 + return (ret);
309 +}
310 +
288 311 static void
289 312 fsd_remove_cb(void *arg, fsh_handle_t handle)
290 313 {
291 314 _NOTE(ARGUNUSED(handle));
292 315
293 316 fsd_int_t *fsdi = (fsd_int_t *)arg;
294 317 int fsd_context;
295 318
296 319 mutex_enter(&fsd_rem_thread_lock);
297 320 fsd_context = fsd_rem_thread == curthread;
298 321 mutex_exit(&fsd_rem_thread_lock);
299 322
300 323 if (!fsd_context)
301 324 mutex_enter(&fsd_lock);
302 325
303 326 ASSERT(MUTEX_HELD(&fsd_lock));
304 327
305 328 if (!fsd_detaching)
306 329 list_remove(&fsd_list, fsdi);
307 330
308 331 rw_destroy(&fsdi->fsdi_lock);
309 332 kmem_free(fsdi, sizeof (*fsdi));
310 333
311 334 fsd_list_count--;
312 335 if (fsd_list_count == 0)
313 336 cv_signal(&fsd_cv_empty);
314 337
315 338 if (!fsd_context)
316 339 mutex_exit(&fsd_lock);
↓ open down ↓ |
19 lines elided |
↑ open up ↑ |
317 340 }
318 341
319 342 /*
320 343 * Installs a set of hook with given parameters on a vfs_t.
321 344 *
322 345 * It is expected that fsd_lock is being held.
323 346 *
324 347 * Returns 0 on success and non-zero if hook limit exceeded.
325 348 */
326 349 static int
327 -fsd_install_disturber(vfs_t *vfsp, fsd_t *fsd)
350 +fsd_disturber_install(vfs_t *vfsp, fsd_t *fsd)
328 351 {
329 352 fsd_int_t *fsdi;
330 353
331 354 ASSERT(MUTEX_HELD(&fsd_lock));
332 355
333 356 for (fsdi = list_head(&fsd_list); fsdi != NULL;
334 357 fsdi = list_next(&fsd_list, fsdi)) {
335 358 if (fsdi->fsdi_vfsp == vfsp)
336 359 break;
337 360 }
338 361
339 362 if (fsdi != NULL) {
340 363 /* Just change the existing fsd_int_t */
341 364 rw_enter(&fsdi->fsdi_lock, RW_WRITER);
342 365 (void) memcpy(&fsdi->fsdi_param, fsd,
343 366 sizeof (fsdi->fsdi_param));
344 367 rw_exit(&fsdi->fsdi_lock);
↓ open down ↓ |
7 lines elided |
↑ open up ↑ |
345 368 } else {
346 369 fsh_t hook = { 0 };
347 370
348 371 fsdi = kmem_zalloc(sizeof (*fsdi), KM_SLEEP);
349 372 fsdi->fsdi_vfsp = vfsp;
350 373 (void) memcpy(&fsdi->fsdi_param, fsd,
351 374 sizeof (fsdi->fsdi_param));
352 375 rw_init(&fsdi->fsdi_lock, NULL, RW_DRIVER, NULL);
353 376
354 377 hook.arg = fsdi;
355 - hook.read = fsd_hook_read;
378 + hook.pre_read = fsd_hook_pre_read;
379 + hook.post_read = fsd_hook_post_read;
356 380 hook.remove_cb = fsd_remove_cb;
357 381
358 382 /*
359 383 * It is safe to do so, because none of the hooks installed
360 384 * by fsd uses fsdi_handle nor the fsd_list.
361 385 */
362 386 fsdi->fsdi_handle = fsh_hook_install(vfsp, &hook);
363 387 if (fsdi->fsdi_handle == -1) {
364 388 kmem_free(fsdi, sizeof (*fsdi));
365 389 rw_destroy(&fsdi->fsdi_lock);
366 390 return (-1);
367 391 }
368 392 list_insert_head(&fsd_list, fsdi);
369 393 fsd_list_count++;
370 394 }
371 395 return (0);
372 396 }
373 397
374 398 static int
375 -fsd_remove_disturber(vfs_t *vfsp)
399 +fsd_disturber_remove(vfs_t *vfsp)
376 400 {
377 401 fsd_int_t *fsdi;
378 402
379 403 ASSERT(MUTEX_HELD(&fsd_lock));
380 404
381 405 for (fsdi = list_head(&fsd_list); fsdi != NULL;
382 406 fsdi = list_next(&fsd_list, fsdi)) {
383 407 if (fsdi->fsdi_vfsp == vfsp)
384 408 break;
385 409 }
386 410 if (fsdi == NULL || fsdi->fsdi_doomed)
387 411 return (ENOENT);
388 412
389 413 fsdi->fsdi_doomed = 1;
390 414
391 415 mutex_enter(&fsd_rem_thread_lock);
392 416 fsd_rem_thread = curthread;
393 417 mutex_exit(&fsd_rem_thread_lock);
394 418
↓ open down ↓ |
9 lines elided |
↑ open up ↑ |
395 419 ASSERT(fsh_hook_remove(fsdi->fsdi_handle) == 0);
396 420
397 421 mutex_enter(&fsd_rem_thread_lock);
398 422 fsd_rem_thread = NULL;
399 423 mutex_exit(&fsd_rem_thread_lock);
400 424
401 425 return (0);
402 426 }
403 427
404 428 static void
405 -fsd_callback_mount(vfs_t *vfsp, void *arg)
429 +fsd_mount_callback(vfs_t *vfsp, void *arg)
406 430 {
407 431 _NOTE(ARGUNUSED(arg));
408 432
409 433 int error = 0;
410 434
411 435 mutex_enter(&fsd_lock);
412 436 if (fsd_omni_param != NULL)
413 - error = fsd_install_disturber(vfsp, fsd_omni_param);
437 + error = fsd_disturber_install(vfsp, fsd_omni_param);
414 438 mutex_exit(&fsd_lock);
415 439
416 440 if (error != 0) {
417 441 refstr_t *mntref;
418 442
419 443 mntref = vfs_getmntpoint(vfsp);
420 444 (void) cmn_err(CE_NOTE, "Installing disturber for %s failed.\n",
421 445 refstr_value(mntref));
422 446 refstr_rele(mntref);
423 447 }
424 448 }
425 449
450 +/*
451 + * Although, we might delete the fsd_free_callback(), it would make the whole
452 + * proces less clear. There's a time window between firing free callbacks and
453 + * freeing the vfs_t in fsd_disturber_remove() could be called. fsh can
454 + * deal with invalid handles (until there is no collision), but we'd like to
455 + * have a nice assertion instead.
456 + */
426 457 static void
458 +fsd_free_callback(vfs_t *vfsp, void *arg)
459 +{
460 + _NOTE(ARGUNUSED(arg));
461 +
462 + fsd_int_t *fsdi;
463 +
464 + mutex_enter(&fsd_lock);
465 + for (fsdi = list_head(&fsd_list); fsdi != NULL;
466 + fsdi = list_next(&fsd_list, fsdi)) {
467 + if (fsdi->fsdi_vfsp == vfsp) {
468 + if (fsdi->fsdi_doomed)
469 + continue;
470 +
471 + fsdi->fsdi_doomed = 1;
472 + /*
473 + * We make such assertion, because fsd_lock is held
474 + * and that means that neither fsd_disturber_remove()
475 + * nor fsd_remove_cb() has removed this hook in
476 + * different thread.
477 + */
478 + mutex_enter(&fsd_rem_thread_lock);
479 + fsd_rem_thread = curthread;
480 + mutex_exit(&fsd_rem_thread_lock);
481 +
482 + ASSERT(fsh_hook_remove(fsdi->fsdi_handle) == 0);
483 +
484 + mutex_enter(&fsd_rem_thread_lock);
485 + fsd_rem_thread = NULL;
486 + mutex_exit(&fsd_rem_thread_lock);
487 +
488 + /*
489 + * Since there is at most one hook installed by fsd,
490 + * we break.
491 + */
492 + break;
493 + }
494 + }
495 + /*
496 + * We can't write ASSERT(fsdi != NULL) because it is possible that
497 + * there was a concurrent call to fsd_disturber_remove() or
498 + * fsd_detach().
499 + */
500 + mutex_exit(&fsd_lock);
501 +}
502 +
503 +static void
427 504 fsd_enable()
428 505 {
429 506 mutex_enter(&fsd_lock);
430 507 fsd_enabled = 1;
431 508 mutex_exit(&fsd_lock);
432 509 }
433 510
434 511 static void
435 512 fsd_disable()
436 513 {
437 514 mutex_enter(&fsd_lock);
438 515 fsd_enabled = 0;
439 516 mutex_exit(&fsd_lock);
440 517 }
441 518
442 519
443 520 /* Entry points */
444 521 static int
445 522 fsd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
446 523 {
447 524 minor_t instance;
448 525 fsh_callback_t cb = { 0 };
449 526
450 527 if (cmd != DDI_ATTACH)
451 528 return (DDI_FAILURE);
452 529
453 530 if (fsd_devi != NULL)
↓ open down ↓ |
17 lines elided |
↑ open up ↑ |
454 531 return (DDI_FAILURE);
455 532
456 533 instance = ddi_get_instance(dip);
457 534 if (ddi_create_minor_node(dip, "fsd", S_IFCHR, instance,
458 535 DDI_PSEUDO, 0) == DDI_FAILURE)
459 536 return (DDI_FAILURE);
460 537 fsd_devi = dip;
461 538 ddi_report_dev(fsd_devi);
462 539
463 540 list_create(&fsd_list, sizeof (fsd_int_t),
464 - offsetof(fsd_int_t, fsdi_next));
541 + offsetof(fsd_int_t, fsdi_node));
465 542
466 543 fsd_rand_seed = gethrtime();
467 544
468 545 mutex_init(&fsd_lock, NULL, MUTEX_DRIVER, NULL);
469 546 mutex_init(&fsd_rem_thread_lock, NULL, MUTEX_DRIVER, NULL);
470 547 cv_init(&fsd_cv_empty, NULL, CV_DRIVER, NULL);
471 548
472 - cb.fshc_mount = fsd_callback_mount;
549 + cb.fshc_mount = fsd_mount_callback;
550 + cb.fshc_free = fsd_free_callback;
473 551 cb.fshc_arg = fsd_omni_param;
474 552 fsd_cb_handle = fsh_callback_install(&cb);
475 553 if (fsd_cb_handle == -1) {
476 554 /* Cleanup */
477 555 list_destroy(&fsd_list);
478 556 cv_destroy(&fsd_cv_empty);
479 557 mutex_destroy(&fsd_rem_thread_lock);
480 558 mutex_destroy(&fsd_lock);
481 559 ddi_remove_minor_node(fsd_devi, NULL);
482 560 fsd_devi = NULL;
483 561 return (DDI_FAILURE);
484 562 }
485 563
486 564 return (DDI_SUCCESS);
487 565 }
488 566
489 567 /*
490 568 * If fsd_enable() was called and there was no subsequent fsd_disable() call,
491 569 * detach will fail.
492 570 */
493 571 static int
494 572 fsd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
495 573 {
496 574 fsd_int_t *fsdi;
497 575
498 576 if (cmd != DDI_DETACH)
499 577 return (DDI_FAILURE);
500 578
501 579 ASSERT(dip == fsd_devi);
502 580
↓ open down ↓ |
20 lines elided |
↑ open up ↑ |
503 581 /*
504 582 * No need to hold fsd_lock here. Since only the hooks and callbacks
505 583 * might be running at this point.
506 584 */
507 585 if (fsd_enabled)
508 586 return (DDI_FAILURE);
509 587
510 588 ddi_remove_minor_node(dip, NULL);
511 589 fsd_devi = NULL;
512 590
591 + /*
592 + * 1. Remove the hooks.
593 + * 2. Remove the callbacks.
594 + *
595 + * This order has to be preserved, because of the fact that
596 + * fsd_free_callback() is the last stop before a vfs_t is destroyed.
597 + * Without it, this might happen:
598 + * vfs_free() fsd_detach()
599 + * 1. Handle for the hook is
600 + * invalidated.
601 + * 2. Fired fsd_remove_cb().
602 + * 3. fsd_remove_cb() hasn't yet fsd_lock is acquired.
603 + * acquired the fsd_lock.
604 + * 4 Waiting for fsd_lock. That ASSERT(fsh_hook_remove(..) == 0);
605 + * means that the hook hasn't failed, because the handle is
606 + * been removed from fsd_hooks already invalid.
607 + * fsd_hooks yet.
608 + *
609 + * The ASSERT() here is nice and without a good reason, we don't want
610 + * to get rid of it.
611 + */
513 612 mutex_enter(&fsd_lock);
613 + /*
614 + * After we set fsd_detaching to 1, hook remove callback (fsd_remove_cb)
615 + * won't try to remove entries from fsd_list.
616 + */
514 617 fsd_detaching = 1;
515 - while ((fsdi = list_remove_head(&fsd_list)) != NULL)
618 + while ((fsdi = list_remove_head(&fsd_list)) != NULL) {
516 619 if (fsdi->fsdi_doomed == 0) {
517 620 fsdi->fsdi_doomed = 1;
518 621
519 622 mutex_enter(&fsd_rem_thread_lock);
520 623 fsd_rem_thread = curthread;
521 624 mutex_exit(&fsd_rem_thread_lock);
522 -
625 +
523 626 /*
524 627 * fsd_lock is held, so no other thread could have
525 628 * removed this hook.
526 629 */
527 630 ASSERT(fsh_hook_remove(fsdi->fsdi_handle) == 0);
528 631
529 632 mutex_enter(&fsd_rem_thread_lock);
530 633 fsd_rem_thread = NULL;
531 634 mutex_exit(&fsd_rem_thread_lock);
532 635 }
636 + }
533 637
534 638 while (fsd_list_count > 0)
535 639 cv_wait(&fsd_cv_empty, &fsd_lock);
536 640 mutex_exit(&fsd_lock);
537 641 cv_destroy(&fsd_cv_empty);
538 642
539 643 ASSERT(fsh_callback_remove(fsd_cb_handle) == 0);
540 644 if (fsd_omni_param != NULL) {
541 645 kmem_free(fsd_omni_param, sizeof (*fsd_omni_param));
542 646 fsd_omni_param = NULL;
543 647 }
544 648
545 649 /* After removing the callback and hooks, it is safe to remove these */
546 650 list_destroy(&fsd_list);
547 651 mutex_destroy(&fsd_rem_thread_lock);
548 652 mutex_destroy(&fsd_lock);
549 653
550 654 return (DDI_SUCCESS);
551 655 }
552 656
553 657 static int
554 658 fsd_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **resultp)
555 659 {
556 660 _NOTE(ARGUNUSED(dip));
557 661
558 662 switch (infocmd) {
559 663 case DDI_INFO_DEVT2DEVINFO:
560 664 *resultp = fsd_devi;
561 665 return (DDI_SUCCESS);
562 666 case DDI_INFO_DEVT2INSTANCE:
563 667 *resultp = (void *)(uintptr_t)getminor((dev_t)arg);
564 668 return (DDI_SUCCESS);
565 669 default:
566 670 return (DDI_FAILURE);
567 671 }
568 672 }
569 673
570 674 static int
571 675 fsd_open(dev_t *devp, int flag, int otyp, cred_t *credp)
572 676 {
573 677 _NOTE(ARGUNUSED(devp));
574 678
575 679 if (flag & FEXCL || flag & FNDELAY)
576 680 return (EINVAL);
577 681
578 682 if (otyp != OTYP_CHR)
579 683 return (EINVAL);
580 684
581 685 if (!(flag & FREAD && flag & FWRITE))
582 686 return (EINVAL);
583 687
584 688 if (drv_priv(credp) == EPERM)
585 689 return (EPERM);
586 690
587 691 return (0);
588 692 }
589 693
590 694 static int
591 695 fsd_close(dev_t dev, int flag, int otyp, cred_t *credp)
592 696 {
593 697 _NOTE(ARGUNUSED(dev));
594 698 _NOTE(ARGUNUSED(flag));
595 699 _NOTE(ARGUNUSED(otyp));
596 700 _NOTE(ARGUNUSED(credp));
597 701
598 702 return (0);
599 703 }
600 704
601 705
602 706 /* ioctl(9E) and it's support functions */
603 707 static int
604 708 fsd_check_param(fsd_t *fsd)
605 709 {
606 710 if (fsd->read_less_chance > 100 ||
607 711 fsd->read_less_r[0] > fsd->read_less_r[1])
608 712 return (EINVAL);
609 713 return (0);
610 714 }
611 715
612 716 static int
613 717 fsd_ioctl_disturb(fsd_ioc_t *ioc, int mode, int *rvalp)
614 718 {
615 719 file_t *file;
616 720 fsd_dis_t dis;
617 721 int rv;
618 722
619 723 if (ddi_copyin(&ioc->fsdioc_dis, &dis, sizeof (dis), mode))
620 724 return (EFAULT);
621 725
622 726 if ((rv = fsd_check_param(&dis.fsdd_param)) != 0) {
↓ open down ↓ |
80 lines elided |
↑ open up ↑ |
623 727 *rvalp = rv;
624 728 return (0);
625 729 }
626 730
627 731 if ((file = getf((int)dis.fsdd_mnt)) == NULL) {
628 732 *rvalp = EBADFD;
629 733 return (0);
630 734 }
631 735
632 736 mutex_enter(&fsd_lock);
633 - rv = fsd_install_disturber(file->f_vnode->v_vfsp, &dis.fsdd_param);
737 + rv = fsd_disturber_install(file->f_vnode->v_vfsp, &dis.fsdd_param);
634 738 mutex_exit(&fsd_lock);
635 739
636 740 releasef((int)dis.fsdd_mnt);
637 741
638 742 if (rv != 0)
639 743 *rvalp = EAGAIN;
640 744 else
641 745 *rvalp = 0;
642 746
643 747 return (0);
644 748 }
645 749
646 750 static int
647 751 fsd_ioctl_get_param(fsd_ioc_t *ioc, int mode, int *rvalp)
648 752 {
649 753 file_t *file;
650 754 fsd_int_t *fsdi;
651 755 int error = 0;
652 756 int64_t fd;
653 757 vfs_t *vfsp;
654 758
655 759 if (ddi_copyin(&ioc->fsdioc_mnt, &fd, sizeof (fd), mode))
656 760 return (EFAULT);
657 761
658 762 if ((file = getf((int)fd)) == NULL) {
659 763 *rvalp = EBADFD;
660 764 return (0);
661 765 }
662 766 vfsp = file->f_vnode->v_vfsp;
663 767 releasef((int)fd);
664 768
665 769
666 770 mutex_enter(&fsd_lock);
667 771
668 772 for (fsdi = list_head(&fsd_list); fsdi != NULL;
669 773 fsdi = list_next(&fsd_list, fsdi)) {
670 774 if (fsdi->fsdi_vfsp == vfsp)
671 775 break;
672 776 }
673 777 if (fsdi == NULL) {
674 778 *rvalp = ENOENT;
675 779 mutex_exit(&fsd_lock);
676 780 return (0);
677 781 }
678 782 rw_enter(&fsdi->fsdi_lock, RW_READER);
679 783 error = ddi_copyout(&fsdi->fsdi_param, &ioc->fsdioc_param,
680 784 sizeof (fsdi->fsdi_param), mode);
681 785 rw_exit(&fsdi->fsdi_lock);
682 786
683 787 mutex_exit(&fsd_lock);
684 788
685 789
686 790 if (error) {
687 791 return (EFAULT);
688 792 } else {
689 793 *rvalp = 0;
690 794 return (0);
691 795 }
692 796 }
693 797
694 798 static int
695 799 fsd_ioctl_get_info(fsd_ioc_t *ioc, int mode, int *rvalp)
696 800 {
697 801 fsd_info_t info;
698 802
699 803 mutex_enter(&fsd_lock);
700 804 info.fsdinf_enabled = fsd_enabled;
701 805 info.fsdinf_count = fsd_list_count;
702 806 info.fsdinf_omni_on = fsd_omni_param != NULL;
703 807 if (info.fsdinf_omni_on)
704 808 (void) memcpy(&info.fsdinf_omni_param, fsd_omni_param,
705 809 sizeof (info.fsdinf_omni_param));
706 810 mutex_exit(&fsd_lock);
707 811
708 812 if (ddi_copyout(&info, &ioc->fsdioc_info, sizeof (info), mode))
709 813 return (EFAULT);
710 814
711 815 *rvalp = 0;
712 816 return (0);
713 817 }
714 818
715 819 static int
716 820 fsd_ioctl_get_list(fsd_ioc_t *ioc, int mode, int *rvalp)
717 821 {
718 822 fsd_int_t *fsdi;
719 823 fsd_fs_t *fsdfs_list;
720 824 int i;
721 825 int ret = 0;
722 826 int64_t ioc_list_count;
723 827
724 828 *rvalp = 0;
725 829
726 830 /* Get data */
727 831 if (ddi_copyin(&ioc->fsdioc_list.count, &ioc_list_count,
728 832 sizeof (ioc_list_count), mode))
729 833 return (EFAULT);
730 834 if (ddi_copyin(&ioc->fsdioc_list.listp, &fsdfs_list,
731 835 sizeof (fsdfs_list), mode))
732 836 return (EFAULT);
733 837
734 838
735 839 mutex_enter(&fsd_lock);
736 840 if (ioc_list_count > fsd_list_count)
737 841 ioc_list_count = fsd_list_count;
738 842
739 843 /* Copyout */
740 844 if (ddi_copyout(&ioc_list_count, &ioc->fsdioc_list.count,
741 845 sizeof (ioc_list_count), mode)) {
742 846 ret = EFAULT;
743 847 goto out;
744 848 }
745 849 for (fsdi = list_head(&fsd_list), i = 0;
746 850 fsdi != NULL && i < ioc_list_count;
747 851 fsdi = list_next(&fsd_list, fsdi), i++) {
748 852 refstr_t *mntstr = vfs_getmntpoint(fsdi->fsdi_vfsp);
749 853 int len = strlen(refstr_value(mntstr));
750 854
751 855 rw_enter(&fsdi->fsdi_lock, RW_READER);
752 856 if (ddi_copyout(refstr_value(mntstr), fsdfs_list[i].fsdf_name,
753 857 len + 1, mode) ||
754 858 ddi_copyout(&fsdi->fsdi_param, &fsdfs_list[i].fsdf_param,
755 859 sizeof (fsdi->fsdi_param), mode)) {
756 860 ret = EFAULT;
757 861 }
758 862 rw_exit(&fsdi->fsdi_lock);
759 863 refstr_rele(mntstr);
760 864
761 865 if (ret != 0)
762 866 break;
763 867 }
764 868
765 869
766 870 out:
767 871 mutex_exit(&fsd_lock);
768 872 return (ret);
769 873 }
770 874
771 875 static int
772 876 fsd_ioctl_disturb_off(fsd_ioc_t *ioc, int mode, int *rvalp)
773 877 {
774 878 file_t *file;
775 879 int64_t fd;
↓ open down ↓ |
132 lines elided |
↑ open up ↑ |
776 880
777 881 if (ddi_copyin(&ioc->fsdioc_mnt, &fd, sizeof (fd), mode))
778 882 return (EFAULT);
779 883
780 884 if ((file = getf((int)fd)) == NULL) {
781 885 *rvalp = EBADFD;
782 886 return (0);
783 887 }
784 888
785 889 mutex_enter(&fsd_lock);
786 - *rvalp = fsd_remove_disturber(file->f_vnode->v_vfsp);
890 + *rvalp = fsd_disturber_remove(file->f_vnode->v_vfsp);
787 891 releasef((int)fd);
788 892 mutex_exit(&fsd_lock);
789 893
790 894 return (0);
791 895 }
792 896
793 897 static int
794 898 fsd_ioctl_disturb_omni(fsd_ioc_t *ioc, int mode, int *rvalp)
795 899 {
796 900 fsd_t fsd;
797 901 int rv;
798 902
799 903 if (ddi_copyin(&ioc->fsdioc_param, &fsd, sizeof (fsd), mode))
800 904 return (EFAULT);
801 905
802 906 if ((rv = fsd_check_param(&fsd)) != 0) {
803 907 *rvalp = rv;
804 908 return (0);
805 909 }
806 910
807 911 mutex_enter(&fsd_lock);
808 912 if (fsd_omni_param == NULL)
809 913 fsd_omni_param = (fsd_t *)kmem_alloc(sizeof (*fsd_omni_param),
810 914 KM_SLEEP);
811 915 (void) memcpy(fsd_omni_param, &fsd, sizeof (*fsd_omni_param));
812 916 mutex_exit(&fsd_lock);
813 917
814 918 *rvalp = 0;
815 919 return (0);
↓ open down ↓ |
19 lines elided |
↑ open up ↑ |
816 920 }
817 921
818 922
819 923 static int
820 924 fsd_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
821 925 int *rvalp)
822 926 {
823 927 _NOTE(ARGUNUSED(dev));
824 928 _NOTE(ARGUNUSED(credp));
825 929
826 - if (!fsd_enabled && cmd != FSD_ENABLE) {
930 + int enabled;
931 +
932 + mutex_enter(&fsd_lock);
933 + enabled = fsd_enabled;
934 + mutex_exit(&fsd_lock);
935 +
936 + if (!enabled && cmd != FSD_ENABLE) {
827 937 *rvalp = ENOTACTIVE;
828 938 return (0);
829 939 }
830 940
831 941 switch (cmd) {
832 942 case FSD_ENABLE:
833 943 fsd_enable();
834 944 *rvalp = 0;
835 945 return (0);
836 946
837 947 case FSD_DISABLE:
838 948 fsd_disable();
839 949 *rvalp = 0;
840 950 return (0);
841 951
842 952 case FSD_GET_PARAM:
843 953 return (fsd_ioctl_get_param((fsd_ioc_t *)arg, mode, rvalp));
844 954
845 955 case FSD_DISTURB:
846 956 return (fsd_ioctl_disturb((fsd_ioc_t *)arg, mode, rvalp));
847 957
848 958 case FSD_DISTURB_OFF:
849 959 return (fsd_ioctl_disturb_off((fsd_ioc_t *)arg, mode, rvalp));
850 960
851 961 case FSD_DISTURB_OMNI:
852 962 return (fsd_ioctl_disturb_omni((fsd_ioc_t *)arg, mode, rvalp));
853 963
854 964 case FSD_DISTURB_OMNI_OFF:
855 965 mutex_enter(&fsd_lock);
856 966 if (fsd_omni_param != NULL)
857 967 kmem_free(fsd_omni_param, sizeof (*fsd_omni_param));
858 968 fsd_omni_param = NULL;
859 969 mutex_exit(&fsd_lock);
860 970
861 971 *rvalp = 0;
862 972 return (0);
863 973
864 974 case FSD_GET_LIST:
865 975 return (fsd_ioctl_get_list((fsd_ioc_t *)arg, mode, rvalp));
866 976
867 977 case FSD_GET_INFO:
868 978 return (fsd_ioctl_get_info((fsd_ioc_t *)arg, mode, rvalp));
869 979
870 980 default:
871 981 return (ENOTTY);
872 982 }
873 983 }
874 984
875 985 static struct cb_ops cb_ops = {
876 986 fsd_open, /* open(9E) */
877 987 fsd_close, /* close(9E) */
878 988 nodev, /* strategy(9E) */
879 989 nodev, /* print(9E) */
880 990 nodev, /* dump(9E) */
881 991 nodev, /* read(9E) */
882 992 nodev, /* write(9E) */
883 993 fsd_ioctl, /* ioctl(9E) */
884 994 nodev, /* devmap(9E) */
885 995 nodev, /* mmap(9E) */
886 996 nodev, /* segmap(9E) */
887 997 nochpoll, /* chpoll(9E) */
888 998 ddi_prop_op, /* prop_op(9E) */
889 999 NULL, /* streamtab(9E) */
890 1000 D_MP | D_64BIT, /* cb_flag(9E) */
891 1001 CB_REV, /* cb_rev(9E) */
892 1002 nodev, /* aread(9E) */
893 1003 nodev, /* awrite(9E) */
894 1004 };
895 1005
896 1006 static struct dev_ops dev_ops = {
897 1007 DEVO_REV, /* driver build version */
898 1008 0, /* reference count */
899 1009 fsd_getinfo, /* getinfo */
900 1010 nulldev,
901 1011 nulldev, /* probe */
902 1012 fsd_attach, /* attach */
903 1013 fsd_detach, /* detach */
904 1014 nodev,
905 1015 &cb_ops, /* cb_ops */
906 1016 NULL, /* bus_ops */
907 1017 NULL, /* power */
908 1018 ddi_quiesce_not_needed, /* quiesce */
909 1019 };
910 1020
911 1021 static struct modldrv modldrv = {
912 1022 &mod_driverops, "Filesystem disturber", &dev_ops
913 1023 };
914 1024
915 1025 static struct modlinkage modlinkage = {
916 1026 MODREV_1, &modldrv, NULL
917 1027 };
918 1028
919 1029 int
920 1030 _init(void)
921 1031 {
922 1032 return (mod_install(&modlinkage));
923 1033 }
924 1034
925 1035 int
926 1036 _info(struct modinfo *modinfop)
927 1037 {
928 1038 return (mod_info(&modlinkage, modinfop));
929 1039 }
930 1040
931 1041 int
932 1042 _fini(void)
933 1043 {
934 1044 return (mod_remove(&modlinkage));
935 1045 }
↓ open down ↓ |
99 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX