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
↓ 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.
↓ 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;
↓ 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;
↓ 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          }
↓ 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          }
↓ 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  {
↓ 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;
↓ 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;
↓ 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);
↓ 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;
↓ 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  
↓ open down ↓ 99 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX