Print this page
Update from fsd_sep3 webrev to fsd_sep9


 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));