120 * ioctl(fd, FSD_GET_LIST, ioc);
121 * Get's a full list of disturbers installed in the system. ioc is
122 * fsdioc_list here. This is a structure with two fields, count and listp.
123 * The count is the number of fsd_fs_t's allocated on the address that
124 * listp is pointing to. There would be at most count fsd_fs_t entries
125 * copied out to the caller. Also, count is set to the number of entries
126 * copied out.
127 *
128 * FSD_GET_INFO:
129 * ioctl(fd, FSD_GET_INFO, ioc);
130 * Get's current information about fsd. ioc is fsdioc_info here.
131 *
132 * At most one hook is installed per vfs_t, and fsd_t describes all possible
133 * disturbance methods. Multiple commands using the fsd should somehow cooperate
134 * in order not to destroy each other efforts in installing disturbers.
135 *
136 * 4. Internals
137 * When fsd_enabled is nonzero, fsd_detach() fails.
138 *
139 * These mount callback is used for installing injections on newly mounted
140 * vfs_t's (omnipresent).
141 *
142 * The list of currently installed hooks is kept in fsd_list.
143 *
144 * fsd installs at most one hook on a vfs_t.
145 *
146 * Inside fsd_detach, we go through fsd_hooks list. There is no guarantee that
147 * a hook remove callback (fsd_remove_cb) wouldn't execute inside
148 * fsh_hook_remove(), thus we can't assume that while walking through fsd_hooks,
149 * our iterator will be valid, because fsh_hook_remove() could invalidate it.
150 * That's why fsd_detaching flag is introduced.
151 *
152 * 5. Locking
153 * Every modification of fsd_enable, fsd_hooks, fsd_omni_param and fsd_list is
154 * protected by fsd_lock.
155 *
156 * Hooks use only the elements of fsd_list, nothing else. Before an element of
157 * fsd_list is destroyed, a hook which uses it is removed. Elements from
158 * fsd_lists are removed and destroyed in the hook remove callback
159 * (fsd_remove_cb).
160 *
161 * Because of the fact that fsd_remove_cb() could be called both in the context
162 * of the thread that executes fsh_hook_remove() or outside the fsd, we need to
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
165 * fsd_lock). We just have to check inside fsd_remove_cb() if it was called
166 * from fsh_hook_remove() or not. We use fsd_rem_thread to determine that.
167 *
168 * fsd_int_t.fsdi_param is protected by fsd_int_t.fsdi_lock which is an rwlock.
169 */
170
171 /*
172 * Once a set of hooks is installed on a filesystem, there's no need
173 * to bother fsh if we want to change the parameters of disturbance.
174 * Intead, we use fsd_lock to protect the fsd_int_t when it's being
175 * used or changed.
176 */
177 typedef struct fsd_int {
178 krwlock_t fsdi_lock; /* protects fsd_param */
179 fsd_t fsdi_param;
180 fsh_handle_t fsdi_handle; /* we use fsh's handle in fsd */
181 vfs_t *fsdi_vfsp;
182 int fsdi_doomed;
183 list_node_t fsdi_next;
184 } fsd_int_t;
185
186 static dev_info_t *fsd_devi;
187
188
189 /*
190 * fsd_lock protects: fsd_enabled, fsd_omni_param, fsd_list, fsd_cb_handle,
191 * fsd_detaching
192 */
193 static kmutex_t fsd_lock;
194
195 static kthread_t *fsd_rem_thread;
196 static kmutex_t fsd_rem_thread_lock;
197
198 static fsd_t *fsd_omni_param; /* Argument used by fsd's mount callback. */
199 static fsh_callback_handle_t fsd_cb_handle;
200 static int fsd_enabled;
201 static int fsd_detaching;
202
203 /*
215 * changes the parity of the seed. That's why when a range of width 2 is set
216 * as a parameter, it's highly possible that the random value will always be
217 * the same, because fsd_rand() could be called the same number of times in a
218 * hook.
219 */
220 static long fsd_rand_seed;
221
222 static int
223 fsd_rand()
224 {
225 fsd_rand_seed = fsd_rand_seed * 1103515245L + 12345;
226 return (fsd_rand_seed & 0x7ffffffff);
227 }
228
229 /* vnode hooks */
230 /*
231 * A pointer to a given fsd_int_t is valid always inside fsh_hook_xxx()
232 * call, because it's valid until the hooks associated with it are removed.
233 * If a hook is removed, it cannot be executing.
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)
238 {
239 fsd_int_t *fsdi = (fsd_int_t *)arg;
240 uint64_t count, less, less_chance;
241
242 /*
243 * 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.
246 * The pseudo-random number generator returns a number with different
247 * 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.
250 */
251 (void) fsd_rand();
252
253 ASSERT(vp->v_vfsp == fsdi->fsdi_vfsp);
254
255 rw_enter(&fsdi->fsdi_lock, RW_READER);
256 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 rw_exit(&fsdi->fsdi_lock);
261
262 count = uiop->uio_iov->iov_len;
263 if ((uint64_t)fsd_rand() % 100 < less_chance) {
264 extern size_t copyout_max_cached;
265 int ret;
266
267 if (count > less)
268 count -= less;
269 else
270 less = 0;
271
272 uiop->uio_iov->iov_len = count;
273 uiop->uio_resid = count;
274 if (count <= copyout_max_cached)
275 uiop->uio_extflg = UIO_COPY_CACHED;
276 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);
282 }
283
284 return (fsh_next_read(fshi, vp, uiop, ioflag, cr, ct));
285 }
286
287
288 static void
289 fsd_remove_cb(void *arg, fsh_handle_t handle)
290 {
291 _NOTE(ARGUNUSED(handle));
292
293 fsd_int_t *fsdi = (fsd_int_t *)arg;
294 int fsd_context;
295
296 mutex_enter(&fsd_rem_thread_lock);
297 fsd_context = fsd_rem_thread == curthread;
298 mutex_exit(&fsd_rem_thread_lock);
299
300 if (!fsd_context)
301 mutex_enter(&fsd_lock);
302
303 ASSERT(MUTEX_HELD(&fsd_lock));
304
305 if (!fsd_detaching)
306 list_remove(&fsd_list, fsdi);
307
308 rw_destroy(&fsdi->fsdi_lock);
309 kmem_free(fsdi, sizeof (*fsdi));
310
311 fsd_list_count--;
312 if (fsd_list_count == 0)
313 cv_signal(&fsd_cv_empty);
314
315 if (!fsd_context)
316 mutex_exit(&fsd_lock);
317 }
318
319 /*
320 * Installs a set of hook with given parameters on a vfs_t.
321 *
322 * It is expected that fsd_lock is being held.
323 *
324 * Returns 0 on success and non-zero if hook limit exceeded.
325 */
326 static int
327 fsd_install_disturber(vfs_t *vfsp, fsd_t *fsd)
328 {
329 fsd_int_t *fsdi;
330
331 ASSERT(MUTEX_HELD(&fsd_lock));
332
333 for (fsdi = list_head(&fsd_list); fsdi != NULL;
334 fsdi = list_next(&fsd_list, fsdi)) {
335 if (fsdi->fsdi_vfsp == vfsp)
336 break;
337 }
338
339 if (fsdi != NULL) {
340 /* Just change the existing fsd_int_t */
341 rw_enter(&fsdi->fsdi_lock, RW_WRITER);
342 (void) memcpy(&fsdi->fsdi_param, fsd,
343 sizeof (fsdi->fsdi_param));
344 rw_exit(&fsdi->fsdi_lock);
345 } else {
346 fsh_t hook = { 0 };
347
348 fsdi = kmem_zalloc(sizeof (*fsdi), KM_SLEEP);
349 fsdi->fsdi_vfsp = vfsp;
350 (void) memcpy(&fsdi->fsdi_param, fsd,
351 sizeof (fsdi->fsdi_param));
352 rw_init(&fsdi->fsdi_lock, NULL, RW_DRIVER, NULL);
353
354 hook.arg = fsdi;
355 hook.read = fsd_hook_read;
356 hook.remove_cb = fsd_remove_cb;
357
358 /*
359 * It is safe to do so, because none of the hooks installed
360 * by fsd uses fsdi_handle nor the fsd_list.
361 */
362 fsdi->fsdi_handle = fsh_hook_install(vfsp, &hook);
363 if (fsdi->fsdi_handle == -1) {
364 kmem_free(fsdi, sizeof (*fsdi));
365 rw_destroy(&fsdi->fsdi_lock);
366 return (-1);
367 }
368 list_insert_head(&fsd_list, fsdi);
369 fsd_list_count++;
370 }
371 return (0);
372 }
373
374 static int
375 fsd_remove_disturber(vfs_t *vfsp)
376 {
377 fsd_int_t *fsdi;
378
379 ASSERT(MUTEX_HELD(&fsd_lock));
380
381 for (fsdi = list_head(&fsd_list); fsdi != NULL;
382 fsdi = list_next(&fsd_list, fsdi)) {
383 if (fsdi->fsdi_vfsp == vfsp)
384 break;
385 }
386 if (fsdi == NULL || fsdi->fsdi_doomed)
387 return (ENOENT);
388
389 fsdi->fsdi_doomed = 1;
390
391 mutex_enter(&fsd_rem_thread_lock);
392 fsd_rem_thread = curthread;
393 mutex_exit(&fsd_rem_thread_lock);
394
395 ASSERT(fsh_hook_remove(fsdi->fsdi_handle) == 0);
396
397 mutex_enter(&fsd_rem_thread_lock);
398 fsd_rem_thread = NULL;
399 mutex_exit(&fsd_rem_thread_lock);
400
401 return (0);
402 }
403
404 static void
405 fsd_callback_mount(vfs_t *vfsp, void *arg)
406 {
407 _NOTE(ARGUNUSED(arg));
408
409 int error = 0;
410
411 mutex_enter(&fsd_lock);
412 if (fsd_omni_param != NULL)
413 error = fsd_install_disturber(vfsp, fsd_omni_param);
414 mutex_exit(&fsd_lock);
415
416 if (error != 0) {
417 refstr_t *mntref;
418
419 mntref = vfs_getmntpoint(vfsp);
420 (void) cmn_err(CE_NOTE, "Installing disturber for %s failed.\n",
421 refstr_value(mntref));
422 refstr_rele(mntref);
423 }
424 }
425
426 static void
427 fsd_enable()
428 {
429 mutex_enter(&fsd_lock);
430 fsd_enabled = 1;
431 mutex_exit(&fsd_lock);
432 }
433
434 static void
435 fsd_disable()
436 {
437 mutex_enter(&fsd_lock);
438 fsd_enabled = 0;
439 mutex_exit(&fsd_lock);
440 }
441
442
443 /* Entry points */
444 static int
445 fsd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
446 {
447 minor_t instance;
448 fsh_callback_t cb = { 0 };
449
450 if (cmd != DDI_ATTACH)
451 return (DDI_FAILURE);
452
453 if (fsd_devi != NULL)
454 return (DDI_FAILURE);
455
456 instance = ddi_get_instance(dip);
457 if (ddi_create_minor_node(dip, "fsd", S_IFCHR, instance,
458 DDI_PSEUDO, 0) == DDI_FAILURE)
459 return (DDI_FAILURE);
460 fsd_devi = dip;
461 ddi_report_dev(fsd_devi);
462
463 list_create(&fsd_list, sizeof (fsd_int_t),
464 offsetof(fsd_int_t, fsdi_next));
465
466 fsd_rand_seed = gethrtime();
467
468 mutex_init(&fsd_lock, NULL, MUTEX_DRIVER, NULL);
469 mutex_init(&fsd_rem_thread_lock, NULL, MUTEX_DRIVER, NULL);
470 cv_init(&fsd_cv_empty, NULL, CV_DRIVER, NULL);
471
472 cb.fshc_mount = fsd_callback_mount;
473 cb.fshc_arg = fsd_omni_param;
474 fsd_cb_handle = fsh_callback_install(&cb);
475 if (fsd_cb_handle == -1) {
476 /* Cleanup */
477 list_destroy(&fsd_list);
478 cv_destroy(&fsd_cv_empty);
479 mutex_destroy(&fsd_rem_thread_lock);
480 mutex_destroy(&fsd_lock);
481 ddi_remove_minor_node(fsd_devi, NULL);
482 fsd_devi = NULL;
483 return (DDI_FAILURE);
484 }
485
486 return (DDI_SUCCESS);
487 }
488
489 /*
490 * If fsd_enable() was called and there was no subsequent fsd_disable() call,
491 * detach will fail.
492 */
493 static int
494 fsd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
495 {
496 fsd_int_t *fsdi;
497
498 if (cmd != DDI_DETACH)
499 return (DDI_FAILURE);
500
501 ASSERT(dip == fsd_devi);
502
503 /*
504 * No need to hold fsd_lock here. Since only the hooks and callbacks
505 * might be running at this point.
506 */
507 if (fsd_enabled)
508 return (DDI_FAILURE);
509
510 ddi_remove_minor_node(dip, NULL);
511 fsd_devi = NULL;
512
513 mutex_enter(&fsd_lock);
514 fsd_detaching = 1;
515 while ((fsdi = list_remove_head(&fsd_list)) != NULL)
516 if (fsdi->fsdi_doomed == 0) {
517 fsdi->fsdi_doomed = 1;
518
519 mutex_enter(&fsd_rem_thread_lock);
520 fsd_rem_thread = curthread;
521 mutex_exit(&fsd_rem_thread_lock);
522
523 /*
524 * fsd_lock is held, so no other thread could have
525 * removed this hook.
526 */
527 ASSERT(fsh_hook_remove(fsdi->fsdi_handle) == 0);
528
529 mutex_enter(&fsd_rem_thread_lock);
530 fsd_rem_thread = NULL;
531 mutex_exit(&fsd_rem_thread_lock);
532 }
533
534 while (fsd_list_count > 0)
535 cv_wait(&fsd_cv_empty, &fsd_lock);
536 mutex_exit(&fsd_lock);
537 cv_destroy(&fsd_cv_empty);
538
539 ASSERT(fsh_callback_remove(fsd_cb_handle) == 0);
540 if (fsd_omni_param != NULL) {
541 kmem_free(fsd_omni_param, sizeof (*fsd_omni_param));
542 fsd_omni_param = NULL;
543 }
544
545 /* After removing the callback and hooks, it is safe to remove these */
546 list_destroy(&fsd_list);
547 mutex_destroy(&fsd_rem_thread_lock);
548 mutex_destroy(&fsd_lock);
549
550 return (DDI_SUCCESS);
551 }
552
613 fsd_ioctl_disturb(fsd_ioc_t *ioc, int mode, int *rvalp)
614 {
615 file_t *file;
616 fsd_dis_t dis;
617 int rv;
618
619 if (ddi_copyin(&ioc->fsdioc_dis, &dis, sizeof (dis), mode))
620 return (EFAULT);
621
622 if ((rv = fsd_check_param(&dis.fsdd_param)) != 0) {
623 *rvalp = rv;
624 return (0);
625 }
626
627 if ((file = getf((int)dis.fsdd_mnt)) == NULL) {
628 *rvalp = EBADFD;
629 return (0);
630 }
631
632 mutex_enter(&fsd_lock);
633 rv = fsd_install_disturber(file->f_vnode->v_vfsp, &dis.fsdd_param);
634 mutex_exit(&fsd_lock);
635
636 releasef((int)dis.fsdd_mnt);
637
638 if (rv != 0)
639 *rvalp = EAGAIN;
640 else
641 *rvalp = 0;
642
643 return (0);
644 }
645
646 static int
647 fsd_ioctl_get_param(fsd_ioc_t *ioc, int mode, int *rvalp)
648 {
649 file_t *file;
650 fsd_int_t *fsdi;
651 int error = 0;
652 int64_t fd;
653 vfs_t *vfsp;
766 out:
767 mutex_exit(&fsd_lock);
768 return (ret);
769 }
770
771 static int
772 fsd_ioctl_disturb_off(fsd_ioc_t *ioc, int mode, int *rvalp)
773 {
774 file_t *file;
775 int64_t fd;
776
777 if (ddi_copyin(&ioc->fsdioc_mnt, &fd, sizeof (fd), mode))
778 return (EFAULT);
779
780 if ((file = getf((int)fd)) == NULL) {
781 *rvalp = EBADFD;
782 return (0);
783 }
784
785 mutex_enter(&fsd_lock);
786 *rvalp = fsd_remove_disturber(file->f_vnode->v_vfsp);
787 releasef((int)fd);
788 mutex_exit(&fsd_lock);
789
790 return (0);
791 }
792
793 static int
794 fsd_ioctl_disturb_omni(fsd_ioc_t *ioc, int mode, int *rvalp)
795 {
796 fsd_t fsd;
797 int rv;
798
799 if (ddi_copyin(&ioc->fsdioc_param, &fsd, sizeof (fsd), mode))
800 return (EFAULT);
801
802 if ((rv = fsd_check_param(&fsd)) != 0) {
803 *rvalp = rv;
804 return (0);
805 }
806
807 mutex_enter(&fsd_lock);
808 if (fsd_omni_param == NULL)
809 fsd_omni_param = (fsd_t *)kmem_alloc(sizeof (*fsd_omni_param),
810 KM_SLEEP);
811 (void) memcpy(fsd_omni_param, &fsd, sizeof (*fsd_omni_param));
812 mutex_exit(&fsd_lock);
813
814 *rvalp = 0;
815 return (0);
816 }
817
818
819 static int
820 fsd_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
821 int *rvalp)
822 {
823 _NOTE(ARGUNUSED(dev));
824 _NOTE(ARGUNUSED(credp));
825
826 if (!fsd_enabled && cmd != FSD_ENABLE) {
827 *rvalp = ENOTACTIVE;
828 return (0);
829 }
830
831 switch (cmd) {
832 case FSD_ENABLE:
833 fsd_enable();
834 *rvalp = 0;
835 return (0);
836
837 case FSD_DISABLE:
838 fsd_disable();
839 *rvalp = 0;
840 return (0);
841
842 case FSD_GET_PARAM:
843 return (fsd_ioctl_get_param((fsd_ioc_t *)arg, mode, rvalp));
844
845 case FSD_DISTURB:
846 return (fsd_ioctl_disturb((fsd_ioc_t *)arg, mode, rvalp));
|
120 * ioctl(fd, FSD_GET_LIST, ioc);
121 * Get's a full list of disturbers installed in the system. ioc is
122 * fsdioc_list here. This is a structure with two fields, count and listp.
123 * The count is the number of fsd_fs_t's allocated on the address that
124 * listp is pointing to. There would be at most count fsd_fs_t entries
125 * copied out to the caller. Also, count is set to the number of entries
126 * copied out.
127 *
128 * FSD_GET_INFO:
129 * ioctl(fd, FSD_GET_INFO, ioc);
130 * Get's current information about fsd. ioc is fsdioc_info here.
131 *
132 * At most one hook is installed per vfs_t, and fsd_t describes all possible
133 * disturbance methods. Multiple commands using the fsd should somehow cooperate
134 * in order not to destroy each other efforts in installing disturbers.
135 *
136 * 4. Internals
137 * When fsd_enabled is nonzero, fsd_detach() fails.
138 *
139 * These mount callback is used for installing injections on newly mounted
140 * vfs_t's (omnipresent). The free callback is used for cleaning up.
141 *
142 * The list of currently installed hooks is kept in fsd_list.
143 *
144 * fsd installs at most one hook on a vfs_t.
145 *
146 * Inside fsd_detach, we go through fsd_hooks list. There is no guarantee that
147 * a hook remove callback (fsd_remove_cb) wouldn't execute inside
148 * fsh_hook_remove(), thus we can't assume that while walking through fsd_hooks,
149 * our iterator will be valid, because fsh_hook_remove() could invalidate it.
150 * That's why fsd_detaching flag is introduced.
151 *
152 * 5. Locking
153 * Every modification of fsd_enable, fsd_hooks, fsd_omni_param and fsd_list is
154 * protected by fsd_lock.
155 *
156 * Hooks use only the elements of fsd_list, nothing else. Before an element of
157 * fsd_list is destroyed, a hook which uses it is removed. Elements from
158 * fsd_lists are removed and destroyed in the hook remove callback
159 * (fsd_remove_cb).
160 *
161 * Because of the fact that fsd_remove_cb() could be called both in the context
162 * of the thread that executes fsh_hook_remove() or outside the fsd, we need to
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_disturber_remove() holds
165 * fsd_lock). We just have to check inside fsd_remove_cb() if it was called
166 * from fsh_hook_remove() or not. We use fsd_rem_thread to determine that.
167 *
168 * fsd_int_t.fsdi_param is protected by fsd_int_t.fsdi_lock which is an rwlock.
169 */
170
171 /*
172 * Once a set of hooks is installed on a filesystem, there's no need
173 * to bother fsh if we want to change the parameters of disturbance.
174 * Intead, we use fsd_lock to protect the fsd_int_t when it's being
175 * used or changed.
176 */
177 typedef struct fsd_int {
178 krwlock_t fsdi_lock; /* protects fsd_param */
179 fsd_t fsdi_param;
180 fsh_handle_t fsdi_handle; /* we use fsh's handle in fsd */
181 vfs_t *fsdi_vfsp;
182 int fsdi_doomed;
183 list_node_t fsdi_node;
184 } fsd_int_t;
185
186 static dev_info_t *fsd_devi;
187
188
189 /*
190 * fsd_lock protects: fsd_enabled, fsd_omni_param, fsd_list, fsd_cb_handle,
191 * fsd_detaching
192 */
193 static kmutex_t fsd_lock;
194
195 static kthread_t *fsd_rem_thread;
196 static kmutex_t fsd_rem_thread_lock;
197
198 static fsd_t *fsd_omni_param; /* Argument used by fsd's mount callback. */
199 static fsh_callback_handle_t fsd_cb_handle;
200 static int fsd_enabled;
201 static int fsd_detaching;
202
203 /*
215 * changes the parity of the seed. That's why when a range of width 2 is set
216 * as a parameter, it's highly possible that the random value will always be
217 * the same, because fsd_rand() could be called the same number of times in a
218 * hook.
219 */
220 static long fsd_rand_seed;
221
222 static int
223 fsd_rand()
224 {
225 fsd_rand_seed = fsd_rand_seed * 1103515245L + 12345;
226 return (fsd_rand_seed & 0x7ffffffff);
227 }
228
229 /* vnode hooks */
230 /*
231 * A pointer to a given fsd_int_t is valid always inside fsh_hook_xxx()
232 * call, because it's valid until the hooks associated with it are removed.
233 * If a hook is removed, it cannot be executing.
234 */
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 {
239 _NOTE(ARGUNUSED(ioflagp));
240 _NOTE(ARGUNUSED(crp));
241 _NOTE(ARGUNUSED(ctp));
242
243 fsd_int_t *fsdi = (fsd_int_t *)arg;
244 uint64_t less_chance;
245
246 /*
247 * It is used to keep an odd number of fsd_rand() calls in every
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.
250 * The pseudo-random number generator returns a number with different
251 * parity with every call. If this function is called in every
252 * fsd_hook_pre_read() execution even number of times, it would always
253 * be the same % 2.
254 */
255 (void) fsd_rand();
256
257 ASSERT((*vpp)->v_vfsp == fsdi->fsdi_vfsp);
258
259 rw_enter(&fsdi->fsdi_lock, RW_READER);
260 less_chance = fsdi->fsdi_param.read_less_chance;
261 rw_exit(&fsdi->fsdi_lock);
262
263 if ((uint64_t)fsd_rand() % 100 < less_chance) {
264 extern size_t copyout_max_cached;
265 uint64_t r[2];
266 uint64_t count, less;
267
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) {
274 count -= less;
275 *instancep = kmem_alloc(sizeof (uint64_t), KM_SLEEP);
276 *(*(uint64_t **)instancep) = less;
277 } else {
278 *instancep = NULL;
279 return;
280 }
281
282 (*uiopp)->uio_iov->iov_len = count;
283 (*uiopp)->uio_resid = count;
284 if (count <= copyout_max_cached)
285 (*uiopp)->uio_extflg = UIO_COPY_CACHED;
286 else
287 (*uiopp)->uio_extflg = UIO_COPY_DEFAULT;
288 } else {
289 *instancep = NULL;
290 }
291 }
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));
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
311 static void
312 fsd_remove_cb(void *arg, fsh_handle_t handle)
313 {
314 _NOTE(ARGUNUSED(handle));
315
316 fsd_int_t *fsdi = (fsd_int_t *)arg;
317 int fsd_context;
318
319 mutex_enter(&fsd_rem_thread_lock);
320 fsd_context = fsd_rem_thread == curthread;
321 mutex_exit(&fsd_rem_thread_lock);
322
323 if (!fsd_context)
324 mutex_enter(&fsd_lock);
325
326 ASSERT(MUTEX_HELD(&fsd_lock));
327
328 if (!fsd_detaching)
329 list_remove(&fsd_list, fsdi);
330
331 rw_destroy(&fsdi->fsdi_lock);
332 kmem_free(fsdi, sizeof (*fsdi));
333
334 fsd_list_count--;
335 if (fsd_list_count == 0)
336 cv_signal(&fsd_cv_empty);
337
338 if (!fsd_context)
339 mutex_exit(&fsd_lock);
340 }
341
342 /*
343 * Installs a set of hook with given parameters on a vfs_t.
344 *
345 * It is expected that fsd_lock is being held.
346 *
347 * Returns 0 on success and non-zero if hook limit exceeded.
348 */
349 static int
350 fsd_disturber_install(vfs_t *vfsp, fsd_t *fsd)
351 {
352 fsd_int_t *fsdi;
353
354 ASSERT(MUTEX_HELD(&fsd_lock));
355
356 for (fsdi = list_head(&fsd_list); fsdi != NULL;
357 fsdi = list_next(&fsd_list, fsdi)) {
358 if (fsdi->fsdi_vfsp == vfsp)
359 break;
360 }
361
362 if (fsdi != NULL) {
363 /* Just change the existing fsd_int_t */
364 rw_enter(&fsdi->fsdi_lock, RW_WRITER);
365 (void) memcpy(&fsdi->fsdi_param, fsd,
366 sizeof (fsdi->fsdi_param));
367 rw_exit(&fsdi->fsdi_lock);
368 } else {
369 fsh_t hook = { 0 };
370
371 fsdi = kmem_zalloc(sizeof (*fsdi), KM_SLEEP);
372 fsdi->fsdi_vfsp = vfsp;
373 (void) memcpy(&fsdi->fsdi_param, fsd,
374 sizeof (fsdi->fsdi_param));
375 rw_init(&fsdi->fsdi_lock, NULL, RW_DRIVER, NULL);
376
377 hook.arg = fsdi;
378 hook.pre_read = fsd_hook_pre_read;
379 hook.post_read = fsd_hook_post_read;
380 hook.remove_cb = fsd_remove_cb;
381
382 /*
383 * It is safe to do so, because none of the hooks installed
384 * by fsd uses fsdi_handle nor the fsd_list.
385 */
386 fsdi->fsdi_handle = fsh_hook_install(vfsp, &hook);
387 if (fsdi->fsdi_handle == -1) {
388 kmem_free(fsdi, sizeof (*fsdi));
389 rw_destroy(&fsdi->fsdi_lock);
390 return (-1);
391 }
392 list_insert_head(&fsd_list, fsdi);
393 fsd_list_count++;
394 }
395 return (0);
396 }
397
398 static int
399 fsd_disturber_remove(vfs_t *vfsp)
400 {
401 fsd_int_t *fsdi;
402
403 ASSERT(MUTEX_HELD(&fsd_lock));
404
405 for (fsdi = list_head(&fsd_list); fsdi != NULL;
406 fsdi = list_next(&fsd_list, fsdi)) {
407 if (fsdi->fsdi_vfsp == vfsp)
408 break;
409 }
410 if (fsdi == NULL || fsdi->fsdi_doomed)
411 return (ENOENT);
412
413 fsdi->fsdi_doomed = 1;
414
415 mutex_enter(&fsd_rem_thread_lock);
416 fsd_rem_thread = curthread;
417 mutex_exit(&fsd_rem_thread_lock);
418
419 ASSERT(fsh_hook_remove(fsdi->fsdi_handle) == 0);
420
421 mutex_enter(&fsd_rem_thread_lock);
422 fsd_rem_thread = NULL;
423 mutex_exit(&fsd_rem_thread_lock);
424
425 return (0);
426 }
427
428 static void
429 fsd_mount_callback(vfs_t *vfsp, void *arg)
430 {
431 _NOTE(ARGUNUSED(arg));
432
433 int error = 0;
434
435 mutex_enter(&fsd_lock);
436 if (fsd_omni_param != NULL)
437 error = fsd_disturber_install(vfsp, fsd_omni_param);
438 mutex_exit(&fsd_lock);
439
440 if (error != 0) {
441 refstr_t *mntref;
442
443 mntref = vfs_getmntpoint(vfsp);
444 (void) cmn_err(CE_NOTE, "Installing disturber for %s failed.\n",
445 refstr_value(mntref));
446 refstr_rele(mntref);
447 }
448 }
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 */
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
504 fsd_enable()
505 {
506 mutex_enter(&fsd_lock);
507 fsd_enabled = 1;
508 mutex_exit(&fsd_lock);
509 }
510
511 static void
512 fsd_disable()
513 {
514 mutex_enter(&fsd_lock);
515 fsd_enabled = 0;
516 mutex_exit(&fsd_lock);
517 }
518
519
520 /* Entry points */
521 static int
522 fsd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
523 {
524 minor_t instance;
525 fsh_callback_t cb = { 0 };
526
527 if (cmd != DDI_ATTACH)
528 return (DDI_FAILURE);
529
530 if (fsd_devi != NULL)
531 return (DDI_FAILURE);
532
533 instance = ddi_get_instance(dip);
534 if (ddi_create_minor_node(dip, "fsd", S_IFCHR, instance,
535 DDI_PSEUDO, 0) == DDI_FAILURE)
536 return (DDI_FAILURE);
537 fsd_devi = dip;
538 ddi_report_dev(fsd_devi);
539
540 list_create(&fsd_list, sizeof (fsd_int_t),
541 offsetof(fsd_int_t, fsdi_node));
542
543 fsd_rand_seed = gethrtime();
544
545 mutex_init(&fsd_lock, NULL, MUTEX_DRIVER, NULL);
546 mutex_init(&fsd_rem_thread_lock, NULL, MUTEX_DRIVER, NULL);
547 cv_init(&fsd_cv_empty, NULL, CV_DRIVER, NULL);
548
549 cb.fshc_mount = fsd_mount_callback;
550 cb.fshc_free = fsd_free_callback;
551 cb.fshc_arg = fsd_omni_param;
552 fsd_cb_handle = fsh_callback_install(&cb);
553 if (fsd_cb_handle == -1) {
554 /* Cleanup */
555 list_destroy(&fsd_list);
556 cv_destroy(&fsd_cv_empty);
557 mutex_destroy(&fsd_rem_thread_lock);
558 mutex_destroy(&fsd_lock);
559 ddi_remove_minor_node(fsd_devi, NULL);
560 fsd_devi = NULL;
561 return (DDI_FAILURE);
562 }
563
564 return (DDI_SUCCESS);
565 }
566
567 /*
568 * If fsd_enable() was called and there was no subsequent fsd_disable() call,
569 * detach will fail.
570 */
571 static int
572 fsd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
573 {
574 fsd_int_t *fsdi;
575
576 if (cmd != DDI_DETACH)
577 return (DDI_FAILURE);
578
579 ASSERT(dip == fsd_devi);
580
581 /*
582 * No need to hold fsd_lock here. Since only the hooks and callbacks
583 * might be running at this point.
584 */
585 if (fsd_enabled)
586 return (DDI_FAILURE);
587
588 ddi_remove_minor_node(dip, NULL);
589 fsd_devi = NULL;
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 */
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 */
617 fsd_detaching = 1;
618 while ((fsdi = list_remove_head(&fsd_list)) != NULL) {
619 if (fsdi->fsdi_doomed == 0) {
620 fsdi->fsdi_doomed = 1;
621
622 mutex_enter(&fsd_rem_thread_lock);
623 fsd_rem_thread = curthread;
624 mutex_exit(&fsd_rem_thread_lock);
625
626 /*
627 * fsd_lock is held, so no other thread could have
628 * removed this hook.
629 */
630 ASSERT(fsh_hook_remove(fsdi->fsdi_handle) == 0);
631
632 mutex_enter(&fsd_rem_thread_lock);
633 fsd_rem_thread = NULL;
634 mutex_exit(&fsd_rem_thread_lock);
635 }
636 }
637
638 while (fsd_list_count > 0)
639 cv_wait(&fsd_cv_empty, &fsd_lock);
640 mutex_exit(&fsd_lock);
641 cv_destroy(&fsd_cv_empty);
642
643 ASSERT(fsh_callback_remove(fsd_cb_handle) == 0);
644 if (fsd_omni_param != NULL) {
645 kmem_free(fsd_omni_param, sizeof (*fsd_omni_param));
646 fsd_omni_param = NULL;
647 }
648
649 /* After removing the callback and hooks, it is safe to remove these */
650 list_destroy(&fsd_list);
651 mutex_destroy(&fsd_rem_thread_lock);
652 mutex_destroy(&fsd_lock);
653
654 return (DDI_SUCCESS);
655 }
656
717 fsd_ioctl_disturb(fsd_ioc_t *ioc, int mode, int *rvalp)
718 {
719 file_t *file;
720 fsd_dis_t dis;
721 int rv;
722
723 if (ddi_copyin(&ioc->fsdioc_dis, &dis, sizeof (dis), mode))
724 return (EFAULT);
725
726 if ((rv = fsd_check_param(&dis.fsdd_param)) != 0) {
727 *rvalp = rv;
728 return (0);
729 }
730
731 if ((file = getf((int)dis.fsdd_mnt)) == NULL) {
732 *rvalp = EBADFD;
733 return (0);
734 }
735
736 mutex_enter(&fsd_lock);
737 rv = fsd_disturber_install(file->f_vnode->v_vfsp, &dis.fsdd_param);
738 mutex_exit(&fsd_lock);
739
740 releasef((int)dis.fsdd_mnt);
741
742 if (rv != 0)
743 *rvalp = EAGAIN;
744 else
745 *rvalp = 0;
746
747 return (0);
748 }
749
750 static int
751 fsd_ioctl_get_param(fsd_ioc_t *ioc, int mode, int *rvalp)
752 {
753 file_t *file;
754 fsd_int_t *fsdi;
755 int error = 0;
756 int64_t fd;
757 vfs_t *vfsp;
870 out:
871 mutex_exit(&fsd_lock);
872 return (ret);
873 }
874
875 static int
876 fsd_ioctl_disturb_off(fsd_ioc_t *ioc, int mode, int *rvalp)
877 {
878 file_t *file;
879 int64_t fd;
880
881 if (ddi_copyin(&ioc->fsdioc_mnt, &fd, sizeof (fd), mode))
882 return (EFAULT);
883
884 if ((file = getf((int)fd)) == NULL) {
885 *rvalp = EBADFD;
886 return (0);
887 }
888
889 mutex_enter(&fsd_lock);
890 *rvalp = fsd_disturber_remove(file->f_vnode->v_vfsp);
891 releasef((int)fd);
892 mutex_exit(&fsd_lock);
893
894 return (0);
895 }
896
897 static int
898 fsd_ioctl_disturb_omni(fsd_ioc_t *ioc, int mode, int *rvalp)
899 {
900 fsd_t fsd;
901 int rv;
902
903 if (ddi_copyin(&ioc->fsdioc_param, &fsd, sizeof (fsd), mode))
904 return (EFAULT);
905
906 if ((rv = fsd_check_param(&fsd)) != 0) {
907 *rvalp = rv;
908 return (0);
909 }
910
911 mutex_enter(&fsd_lock);
912 if (fsd_omni_param == NULL)
913 fsd_omni_param = (fsd_t *)kmem_alloc(sizeof (*fsd_omni_param),
914 KM_SLEEP);
915 (void) memcpy(fsd_omni_param, &fsd, sizeof (*fsd_omni_param));
916 mutex_exit(&fsd_lock);
917
918 *rvalp = 0;
919 return (0);
920 }
921
922
923 static int
924 fsd_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
925 int *rvalp)
926 {
927 _NOTE(ARGUNUSED(dev));
928 _NOTE(ARGUNUSED(credp));
929
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) {
937 *rvalp = ENOTACTIVE;
938 return (0);
939 }
940
941 switch (cmd) {
942 case FSD_ENABLE:
943 fsd_enable();
944 *rvalp = 0;
945 return (0);
946
947 case FSD_DISABLE:
948 fsd_disable();
949 *rvalp = 0;
950 return (0);
951
952 case FSD_GET_PARAM:
953 return (fsd_ioctl_get_param((fsd_ioc_t *)arg, mode, rvalp));
954
955 case FSD_DISTURB:
956 return (fsd_ioctl_disturb((fsd_ioc_t *)arg, mode, rvalp));
|