Print this page
    
NEX-15279 support NFS server in zone
NEX-15520 online NFS shares cause zoneadm halt to hang in nfs_export_zone_fini
Portions contributed by: Dan Kruchinin dan.kruchinin@nexenta.com
Portions contributed by: Stepan Zastupov stepan.zastupov@gmail.com
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
Reviewed by: Rob Gittins <rob.gittins@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
NEX-6778 NFS kstats leak and cause system to hang
Revert "NEX-4261 Per-client NFS server IOPS, bandwidth, and latency kstats"
This reverts commit 586c3ab1927647487f01c337ddc011c642575a52.
Revert "NEX-5354 Aggregated IOPS, bandwidth, and latency kstats for NFS server"
This reverts commit c91d7614da8618ef48018102b077f60ecbbac8c2.
Revert "NEX-5667 nfssrv_stats_flags does not work for aggregated kstats"
This reverts commit 3dcf42618be7dd5f408c327f429c81e07ca08e74.
Revert "NEX-5750 Time values for aggregated NFS server kstats should be normalized"
This reverts commit 1f4d4f901153b0191027969fa4a8064f9d3b9ee1.
Revert "NEX-5942 Panic in rfs4_minorvers_mismatch() with NFSv4.1 client"
This reverts commit 40766417094a162f5e4cc8786c0fa0a7e5871cd9.
Revert "NEX-5752 NFS server: namespace collision in kstats"
This reverts commit ae81e668db86050da8e483264acb0cce0444a132.
Reviewed by: Rob Gittins <rob.gittins@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-5942 Panic in rfs4_minorvers_mismatch() with NFSv4.1 client
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
NEX-4261 Per-client NFS server IOPS, bandwidth, and latency kstats
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-3097 IOPS, bandwidth, and latency kstats for NFS server
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/uts/common/fs/nfs/nfs4_dispatch.c
          +++ new/usr/src/uts/common/fs/nfs/nfs4_dispatch.c
   1    1  /*
   2    2   * CDDL HEADER START
   3    3   *
   4    4   * The contents of this file are subject to the terms of the
   5    5   * Common Development and Distribution License (the "License").
   6    6   * You may not use this file except in compliance with the License.
   7    7   *
   8    8   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9    9   * or http://www.opensolaris.org/os/licensing.
  10   10   * See the License for the specific language governing permissions
  11   11   * and limitations under the License.
  12   12   *
  13   13   * When distributing Covered Code, include this CDDL HEADER in each
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  
    | 
      ↓ open down ↓ | 
    16 lines elided | 
    
      ↑ open up ↑ | 
  
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  
  22   22  /*
  23   23   * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  24   24   * Use is subject to license terms.
  25   25   */
  26   26  
       27 +/*
       28 + * Copyright 2018 Nexenta Systems, Inc.
       29 + */
       30 +
  27   31  #include <sys/systm.h>
  28   32  #include <sys/sdt.h>
  29   33  #include <rpc/types.h>
  30   34  #include <rpc/auth.h>
  31   35  #include <rpc/auth_unix.h>
  32   36  #include <rpc/auth_des.h>
  33   37  #include <rpc/svc.h>
  34   38  #include <rpc/xdr.h>
  35   39  #include <nfs/nfs4.h>
  36   40  #include <nfs/nfs_dispatch.h>
  37   41  #include <nfs/nfs4_drc.h>
  38   42  
  39   43  #define NFS4_MAX_MINOR_VERSION  0
  40   44  
  41   45  /*
  42      - * This is the duplicate request cache for NFSv4
  43      - */
  44      -rfs4_drc_t *nfs4_drc = NULL;
  45      -
  46      -/*
  47   46   * The default size of the duplicate request cache
  48   47   */
  49   48  uint32_t nfs4_drc_max = 8 * 1024;
  50   49  
  51   50  /*
  52   51   * The number of buckets we'd like to hash the
  53   52   * replies into.. do not change this on the fly.
  54   53   */
  55   54  uint32_t nfs4_drc_hash = 541;
  56   55  
  57   56  static void rfs4_resource_err(struct svc_req *req, COMPOUND4args *argsp);
  58   57  
       58 +extern zone_key_t rfs4_zone_key;
       59 +
  59   60  /*
  60   61   * Initialize a duplicate request cache.
  61   62   */
  62   63  rfs4_drc_t *
  63   64  rfs4_init_drc(uint32_t drc_size, uint32_t drc_hash_size)
  64   65  {
  65   66          rfs4_drc_t *drc;
  66   67          uint32_t   bki;
  67   68  
  68   69          ASSERT(drc_size);
  69   70          ASSERT(drc_hash_size);
  70   71  
  71   72          drc = kmem_alloc(sizeof (rfs4_drc_t), KM_SLEEP);
  72   73  
  73   74          drc->max_size = drc_size;
  74   75          drc->in_use = 0;
  75   76  
  76   77          mutex_init(&drc->lock, NULL, MUTEX_DEFAULT, NULL);
  77   78  
  78   79          drc->dr_hash = drc_hash_size;
  79   80  
  80   81          drc->dr_buckets = kmem_alloc(sizeof (list_t)*drc_hash_size, KM_SLEEP);
  81   82  
  82   83          for (bki = 0; bki < drc_hash_size; bki++) {
  83   84                  list_create(&drc->dr_buckets[bki], sizeof (rfs4_dupreq_t),
  84   85                      offsetof(rfs4_dupreq_t, dr_bkt_next));
  85   86          }
  86   87  
  
    | 
      ↓ open down ↓ | 
    18 lines elided | 
    
      ↑ open up ↑ | 
  
  87   88          list_create(&(drc->dr_cache), sizeof (rfs4_dupreq_t),
  88   89              offsetof(rfs4_dupreq_t, dr_next));
  89   90  
  90   91          return (drc);
  91   92  }
  92   93  
  93   94  /*
  94   95   * Destroy a duplicate request cache.
  95   96   */
  96   97  void
  97      -rfs4_fini_drc(rfs4_drc_t *drc)
       98 +rfs4_fini_drc(void)
  98   99  {
      100 +        nfs4_srv_t *nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
      101 +        rfs4_drc_t *drc = nsrv4->nfs4_drc;
  99  102          rfs4_dupreq_t *drp, *drp_next;
 100  103  
 101      -        ASSERT(drc);
 102      -
 103  104          /* iterate over the dr_cache and free the enties */
 104  105          for (drp = list_head(&(drc->dr_cache)); drp != NULL; drp = drp_next) {
 105  106  
 106  107                  if (drp->dr_state == NFS4_DUP_REPLAY)
 107  108                          rfs4_compound_free(&(drp->dr_res));
 108  109  
 109  110                  if (drp->dr_addr.buf != NULL)
 110  111                          kmem_free(drp->dr_addr.buf, drp->dr_addr.maxlen);
 111  112  
 112  113                  drp_next = list_next(&(drc->dr_cache), drp);
 113  114  
 114  115                  kmem_free(drp, sizeof (rfs4_dupreq_t));
 115  116          }
 116  117  
 117  118          mutex_destroy(&drc->lock);
 118  119          kmem_free(drc->dr_buckets,
 119  120              sizeof (list_t)*drc->dr_hash);
 120  121          kmem_free(drc, sizeof (rfs4_drc_t));
 121  122  }
 122  123  
 123  124  /*
 124  125   * rfs4_dr_chstate:
 125  126   *
 126  127   * Change the state of a rfs4_dupreq. If it's not in transition
 127  128   * to the FREE state, return. If we are moving to the FREE state
 128  129   * then we need to clean up the compound results and move the entry
 129  130   * to the end of the list.
 130  131   */
 131  132  void
 132  133  rfs4_dr_chstate(rfs4_dupreq_t *drp, int new_state)
 133  134  {
 134  135          rfs4_drc_t *drc;
 135  136  
 136  137          ASSERT(drp);
 137  138          ASSERT(drp->drc);
 138  139          ASSERT(drp->dr_bkt);
 139  140          ASSERT(MUTEX_HELD(&drp->drc->lock));
 140  141  
 141  142          drp->dr_state = new_state;
 142  143  
 143  144          if (new_state != NFS4_DUP_FREE)
 144  145                  return;
 145  146  
 146  147          drc = drp->drc;
 147  148  
 148  149          /*
 149  150           * Remove entry from the bucket and
 150  151           * dr_cache list, free compound results.
 151  152           */
 152  153          list_remove(drp->dr_bkt, drp);
 153  154          list_remove(&(drc->dr_cache), drp);
 154  155          rfs4_compound_free(&(drp->dr_res));
 155  156  }
 156  157  
 157  158  /*
 158  159   * rfs4_alloc_dr:
 159  160   *
 160  161   * Malloc a new one if we have not reached our maximum cache
 161  162   * limit, otherwise pick an entry off the tail -- Use if it
 162  163   * is marked as NFS4_DUP_FREE, or is an entry in the
 163  164   * NFS4_DUP_REPLAY state.
 164  165   */
 165  166  rfs4_dupreq_t *
 166  167  rfs4_alloc_dr(rfs4_drc_t *drc)
 167  168  {
 168  169          rfs4_dupreq_t *drp_tail, *drp = NULL;
 169  170  
 170  171          ASSERT(drc);
 171  172          ASSERT(MUTEX_HELD(&drc->lock));
 172  173  
 173  174          /*
 174  175           * Have we hit the cache limit yet ?
 175  176           */
 176  177          if (drc->in_use < drc->max_size) {
 177  178                  /*
 178  179                   * nope, so let's malloc a new one
 179  180                   */
 180  181                  drp = kmem_zalloc(sizeof (rfs4_dupreq_t), KM_SLEEP);
 181  182                  drp->drc = drc;
 182  183                  drc->in_use++;
 183  184                  DTRACE_PROBE1(nfss__i__drc_new, rfs4_dupreq_t *, drp);
 184  185                  return (drp);
 185  186          }
 186  187  
 187  188          /*
 188  189           * Cache is all allocated now traverse the list
 189  190           * backwards to find one we can reuse.
 190  191           */
 191  192          for (drp_tail = list_tail(&drc->dr_cache); drp_tail != NULL;
 192  193              drp_tail = list_prev(&drc->dr_cache, drp_tail)) {
 193  194  
 194  195                  switch (drp_tail->dr_state) {
 195  196  
 196  197                  case NFS4_DUP_FREE:
 197  198                          list_remove(&(drc->dr_cache), drp_tail);
 198  199                          DTRACE_PROBE1(nfss__i__drc_freeclaim,
 199  200                              rfs4_dupreq_t *, drp_tail);
 200  201                          return (drp_tail);
 201  202                          /* NOTREACHED */
 202  203  
 203  204                  case NFS4_DUP_REPLAY:
 204  205                          /* grab it. */
 205  206                          rfs4_dr_chstate(drp_tail, NFS4_DUP_FREE);
 206  207                          DTRACE_PROBE1(nfss__i__drc_replayclaim,
 207  208                              rfs4_dupreq_t *, drp_tail);
 208  209                          return (drp_tail);
 209  210                          /* NOTREACHED */
 210  211                  }
 211  212          }
 212  213          DTRACE_PROBE1(nfss__i__drc_full, rfs4_drc_t *, drc);
 213  214          return (NULL);
 214  215  }
 215  216  
 216  217  /*
 217  218   * rfs4_find_dr:
 218  219   *
 219  220   * Search for an entry in the duplicate request cache by
 220  221   * calculating the hash index based on the XID, and examining
 221  222   * the entries in the hash bucket. If we find a match, return.
 222  223   * Once we have searched the bucket we call rfs4_alloc_dr() to
 223  224   * allocate a new entry, or reuse one that is available.
 224  225   */
 225  226  int
 226  227  rfs4_find_dr(struct svc_req *req, rfs4_drc_t *drc, rfs4_dupreq_t **dup)
 227  228  {
 228  229  
 229  230          uint32_t        the_xid;
 230  231          list_t          *dr_bkt;
 231  232          rfs4_dupreq_t   *drp;
 232  233          int             bktdex;
 233  234  
 234  235          /*
 235  236           * Get the XID, calculate the bucket and search to
 236  237           * see if we need to replay from the cache.
 237  238           */
 238  239          the_xid = req->rq_xprt->xp_xid;
 239  240          bktdex = the_xid % drc->dr_hash;
 240  241  
 241  242          dr_bkt = (list_t *)
 242  243              &(drc->dr_buckets[(the_xid % drc->dr_hash)]);
 243  244  
 244  245          DTRACE_PROBE3(nfss__i__drc_bktdex,
 245  246              int, bktdex,
 246  247              uint32_t, the_xid,
 247  248              list_t *, dr_bkt);
 248  249  
 249  250          *dup = NULL;
 250  251  
 251  252          mutex_enter(&drc->lock);
 252  253          /*
 253  254           * Search the bucket for a matching xid and address.
 254  255           */
 255  256          for (drp = list_head(dr_bkt); drp != NULL;
 256  257              drp = list_next(dr_bkt, drp)) {
 257  258  
 258  259                  if (drp->dr_xid == the_xid &&
 259  260                      drp->dr_addr.len == req->rq_xprt->xp_rtaddr.len &&
 260  261                      bcmp((caddr_t)drp->dr_addr.buf,
 261  262                      (caddr_t)req->rq_xprt->xp_rtaddr.buf,
 262  263                      drp->dr_addr.len) == 0) {
 263  264  
 264  265                          /*
 265  266                           * Found a match so REPLAY the Reply
 266  267                           */
 267  268                          if (drp->dr_state == NFS4_DUP_REPLAY) {
 268  269                                  rfs4_dr_chstate(drp, NFS4_DUP_INUSE);
 269  270                                  mutex_exit(&drc->lock);
 270  271                                  *dup = drp;
 271  272                                  DTRACE_PROBE1(nfss__i__drc_replay,
 272  273                                      rfs4_dupreq_t *, drp);
 273  274                                  return (NFS4_DUP_REPLAY);
 274  275                          }
 275  276  
 276  277                          /*
 277  278                           * This entry must be in transition, so return
 278  279                           * the 'pending' status.
 279  280                           */
 280  281                          mutex_exit(&drc->lock);
 281  282                          return (NFS4_DUP_PENDING);
 282  283                  }
 283  284          }
 284  285  
 285  286          drp = rfs4_alloc_dr(drc);
 286  287          mutex_exit(&drc->lock);
 287  288  
 288  289          /*
 289  290           * The DRC is full and all entries are in use. Upper function
 290  291           * should error out this request and force the client to
 291  292           * retransmit -- effectively this is a resource issue. NFSD
 292  293           * threads tied up with native File System, or the cache size
 293  294           * is too small for the server load.
 294  295           */
 295  296          if (drp == NULL)
 296  297                  return (NFS4_DUP_ERROR);
 297  298  
 298  299          /*
 299  300           * Init the state to NEW.
 300  301           */
 301  302          drp->dr_state = NFS4_DUP_NEW;
 302  303  
 303  304          /*
 304  305           * If needed, resize the address buffer
 305  306           */
 306  307          if (drp->dr_addr.maxlen < req->rq_xprt->xp_rtaddr.len) {
 307  308                  if (drp->dr_addr.buf != NULL)
 308  309                          kmem_free(drp->dr_addr.buf, drp->dr_addr.maxlen);
 309  310                  drp->dr_addr.maxlen = req->rq_xprt->xp_rtaddr.len;
 310  311                  drp->dr_addr.buf = kmem_alloc(drp->dr_addr.maxlen, KM_NOSLEEP);
 311  312                  if (drp->dr_addr.buf == NULL) {
 312  313                          /*
 313  314                           * If the malloc fails, mark the entry
 314  315                           * as free and put on the tail.
 315  316                           */
 316  317                          drp->dr_addr.maxlen = 0;
 317  318                          drp->dr_state = NFS4_DUP_FREE;
 318  319                          mutex_enter(&drc->lock);
 319  320                          list_insert_tail(&(drc->dr_cache), drp);
 320  321                          mutex_exit(&drc->lock);
 321  322                          return (NFS4_DUP_ERROR);
 322  323                  }
 323  324          }
 324  325  
 325  326  
 326  327          /*
 327  328           * Copy the address.
 328  329           */
 329  330          drp->dr_addr.len = req->rq_xprt->xp_rtaddr.len;
 330  331  
 331  332          bcopy((caddr_t)req->rq_xprt->xp_rtaddr.buf,
 332  333              (caddr_t)drp->dr_addr.buf,
 333  334              drp->dr_addr.len);
 334  335  
 335  336          drp->dr_xid = the_xid;
 336  337          drp->dr_bkt = dr_bkt;
 337  338  
 338  339          /*
 339  340           * Insert at the head of the bucket and
 340  341           * the drc lists..
 341  342           */
 342  343          mutex_enter(&drc->lock);
 343  344          list_insert_head(&drc->dr_cache, drp);
 344  345          list_insert_head(dr_bkt, drp);
 345  346          mutex_exit(&drc->lock);
 346  347  
 347  348          *dup = drp;
 348  349  
 349  350          return (NFS4_DUP_NEW);
 350  351  }
 351  352  
 352  353  /*
  
    | 
      ↓ open down ↓ | 
    240 lines elided | 
    
      ↑ open up ↑ | 
  
 353  354   *
 354  355   * This function handles the duplicate request cache,
 355  356   * NULL_PROC and COMPOUND procedure calls for NFSv4;
 356  357   *
 357  358   * Passed into this function are:-
 358  359   *
 359  360   *      disp    A pointer to our dispatch table entry
 360  361   *      req     The request to process
 361  362   *      xprt    The server transport handle
 362  363   *      ap      A pointer to the arguments
      364 + *      rlen    A pointer to the reply length (output)
 363  365   *
 364  366   *
 365  367   * When appropriate this function is responsible for inserting
 366  368   * the reply into the duplicate cache or replaying an existing
 367  369   * cached reply.
 368  370   *
 369  371   * dr_stat      reflects the state of the duplicate request that
 370  372   *              has been inserted into or retrieved from the cache
 371  373   *
 372  374   * drp          is the duplicate request entry
 373  375   *
 374  376   */
 375  377  int
 376  378  rfs4_dispatch(struct rpcdisp *disp, struct svc_req *req,
 377      -                SVCXPRT *xprt, char *ap)
      379 +    SVCXPRT *xprt, char *ap, size_t *rlen)
 378  380  {
 379  381  
 380  382          COMPOUND4res     res_buf;
 381  383          COMPOUND4res    *rbp;
 382  384          COMPOUND4args   *cap;
 383  385          cred_t          *cr = NULL;
 384  386          int              error = 0;
 385  387          int              dis_flags = 0;
 386  388          int              dr_stat = NFS4_NOT_DUP;
 387  389          rfs4_dupreq_t   *drp = NULL;
 388  390          int              rv;
      391 +        nfs4_srv_t *nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
      392 +        rfs4_drc_t *nfs4_drc = nsrv4->nfs4_drc;
 389  393  
 390  394          ASSERT(disp);
 391  395  
 392  396          /*
 393  397           * Short circuit the RPC_NULL proc.
 394  398           */
 395  399          if (disp->dis_proc == rpc_null) {
 396  400                  DTRACE_NFSV4_1(null__start, struct svc_req *, req);
 397  401                  if (!svc_sendreply(xprt, xdr_void, NULL)) {
 398  402                          DTRACE_NFSV4_1(null__done, struct svc_req *, req);
 399  403                          svcerr_systemerr(xprt);
 400  404                          return (1);
 401  405                  }
 402  406                  DTRACE_NFSV4_1(null__done, struct svc_req *, req);
      407 +                *rlen = xdr_sizeof(xdr_void, NULL);
 403  408                  return (0);
 404  409          }
 405  410  
 406  411          /* Only NFSv4 Compounds from this point onward */
 407  412  
 408  413          rbp = &res_buf;
 409  414          cap = (COMPOUND4args *)ap;
 410  415  
 411  416          /*
      417 +         * Update kstats
      418 +         */
      419 +        rfs4_compound_kstat_args(cap);
      420 +
      421 +        /*
 412  422           * Figure out the disposition of the whole COMPOUND
 413  423           * and record it's IDEMPOTENTCY.
 414  424           */
 415  425          rfs4_compound_flagproc(cap, &dis_flags);
 416  426  
 417  427          /*
 418  428           * If NON-IDEMPOTENT then we need to figure out if this
 419  429           * request can be replied from the duplicate cache.
 420  430           *
 421  431           * If this is a new request then we need to insert the
 422  432           * reply into the duplicate cache.
 423  433           */
 424  434          if (!(dis_flags & RPC_IDEMPOTENT)) {
 425  435                  /* look for a replay from the cache or allocate */
 426  436                  dr_stat = rfs4_find_dr(req, nfs4_drc, &drp);
 427  437  
 428  438                  switch (dr_stat) {
 429  439  
 430  440                  case NFS4_DUP_ERROR:
 431  441                          rfs4_resource_err(req, cap);
 432  442                          return (1);
 433  443                          /* NOTREACHED */
 434  444  
 435  445                  case NFS4_DUP_PENDING:
 436  446                          /*
 437  447                           * reply has previously been inserted into the
 438  448                           * duplicate cache, however the reply has
 439  449                           * not yet been sent via svc_sendreply()
 440  450                           */
 441  451                          return (1);
 442  452                          /* NOTREACHED */
 443  453  
 444  454                  case NFS4_DUP_NEW:
 445  455                          curthread->t_flag |= T_DONTPEND;
 446  456                          /* NON-IDEMPOTENT proc call */
 447  457                          rfs4_compound(cap, rbp, NULL, req, cr, &rv);
 448  458                          curthread->t_flag &= ~T_DONTPEND;
 449  459  
 450  460                          if (rv)         /* short ckt sendreply on error */
 451  461                                  return (rv);
 452  462  
 453  463                          /*
 454  464                           * dr_res must be initialized before calling
 455  465                           * rfs4_dr_chstate (it frees the reply).
 456  466                           */
 457  467                          drp->dr_res = res_buf;
 458  468                          if (curthread->t_flag & T_WOULDBLOCK) {
 459  469                                  curthread->t_flag &= ~T_WOULDBLOCK;
 460  470                                  /*
 461  471                                   * mark this entry as FREE and plop
 462  472                                   * on the end of the cache list
 463  473                                   */
 464  474                                  mutex_enter(&drp->drc->lock);
 465  475                                  rfs4_dr_chstate(drp, NFS4_DUP_FREE);
 466  476                                  list_insert_tail(&(drp->drc->dr_cache), drp);
 467  477                                  mutex_exit(&drp->drc->lock);
 468  478                                  return (1);
 469  479                          }
 470  480                          break;
 471  481  
 472  482                  case NFS4_DUP_REPLAY:
 473  483                          /* replay from the cache */
 474  484                          rbp = &(drp->dr_res);
 475  485                          break;
 476  486                  }
 477  487          } else {
 478  488                  curthread->t_flag |= T_DONTPEND;
 479  489                  /* IDEMPOTENT proc call */
 480  490                  rfs4_compound(cap, rbp, NULL, req, cr, &rv);
 481  491                  curthread->t_flag &= ~T_DONTPEND;
 482  492  
 483  493                  if (rv)         /* short ckt sendreply on error */
 484  494                          return (rv);
  
    | 
      ↓ open down ↓ | 
    63 lines elided | 
    
      ↑ open up ↑ | 
  
 485  495  
 486  496                  if (curthread->t_flag & T_WOULDBLOCK) {
 487  497                          curthread->t_flag &= ~T_WOULDBLOCK;
 488  498                          return (1);
 489  499                  }
 490  500          }
 491  501  
 492  502          /*
 493  503           * Send out the replayed reply or the 'real' one.
 494  504           */
 495      -        if (!svc_sendreply(xprt,  xdr_COMPOUND4res_srv, (char *)rbp)) {
      505 +        if (!svc_sendreply(xprt, xdr_COMPOUND4res_srv, (char *)rbp)) {
 496  506                  DTRACE_PROBE2(nfss__e__dispatch_sendfail,
 497  507                      struct svc_req *, xprt,
 498  508                      char *, rbp);
 499  509                  svcerr_systemerr(xprt);
 500  510                  error++;
      511 +        } else {
      512 +                /*
      513 +                 * Update kstats
      514 +                 */
      515 +                rfs4_compound_kstat_res(rbp);
      516 +                *rlen = xdr_sizeof(xdr_COMPOUND4res_srv, rbp);
 501  517          }
 502  518  
 503  519          /*
 504  520           * If this reply was just inserted into the duplicate cache
 505  521           * or it was replayed from the dup cache; (re)mark it as
 506  522           * available for replay
 507  523           *
 508  524           * At first glance, this 'if' statement seems a little strange;
 509  525           * testing for NFS4_DUP_REPLAY, and then calling...
 510  526           *
 511  527           *      rfs4_dr_chatate(NFS4_DUP_REPLAY)
 512  528           *
 513  529           * ... but notice that we are checking dr_stat, and not the
 514  530           * state of the entry itself, the entry will be NFS4_DUP_INUSE,
 515  531           * we do that so that we know not to prematurely reap it whilst
 516  532           * we resent it to the client.
 517  533           *
 518  534           */
 519  535          if (dr_stat == NFS4_DUP_NEW || dr_stat == NFS4_DUP_REPLAY) {
 520  536                  mutex_enter(&drp->drc->lock);
 521  537                  rfs4_dr_chstate(drp, NFS4_DUP_REPLAY);
 522  538                  mutex_exit(&drp->drc->lock);
 523  539          } else if (dr_stat == NFS4_NOT_DUP) {
 524  540                  rfs4_compound_free(rbp);
 525  541          }
 526  542  
 527  543          return (error);
 528  544  }
 529  545  
 530  546  bool_t
 531  547  rfs4_minorvers_mismatch(struct svc_req *req, SVCXPRT *xprt, void *args)
 532  548  {
 533  549          COMPOUND4args *argsp;
 534  550          COMPOUND4res res_buf, *resp;
 535  551  
 536  552          if (req->rq_vers != 4)
 537  553                  return (FALSE);
 538  554  
 539  555          argsp = (COMPOUND4args *)args;
 540  556  
 541  557          if (argsp->minorversion <= NFS4_MAX_MINOR_VERSION)
 542  558                  return (FALSE);
 543  559  
 544  560          resp = &res_buf;
 545  561  
 546  562          /*
 547  563           * Form a reply tag by copying over the reqeuest tag.
 548  564           */
 549  565          resp->tag.utf8string_val =
 550  566              kmem_alloc(argsp->tag.utf8string_len, KM_SLEEP);
 551  567          resp->tag.utf8string_len = argsp->tag.utf8string_len;
 552  568          bcopy(argsp->tag.utf8string_val, resp->tag.utf8string_val,
 553  569              resp->tag.utf8string_len);
 554  570          resp->array_len = 0;
 555  571          resp->array = NULL;
 556  572          resp->status = NFS4ERR_MINOR_VERS_MISMATCH;
 557  573          if (!svc_sendreply(xprt,  xdr_COMPOUND4res_srv, (char *)resp)) {
 558  574                  DTRACE_PROBE2(nfss__e__minorvers_mismatch,
 559  575                      SVCXPRT *, xprt, char *, resp);
 560  576                  svcerr_systemerr(xprt);
 561  577          }
 562  578          rfs4_compound_free(resp);
 563  579          return (TRUE);
 564  580  }
 565  581  
 566  582  void
 567  583  rfs4_resource_err(struct svc_req *req, COMPOUND4args *argsp)
 568  584  {
 569  585          COMPOUND4res res_buf, *rbp;
 570  586          nfs_resop4 *resop;
 571  587          PUTFH4res *resp;
 572  588  
 573  589          rbp = &res_buf;
 574  590  
 575  591          /*
 576  592           * Form a reply tag by copying over the request tag.
 577  593           */
 578  594          rbp->tag.utf8string_val =
 579  595              kmem_alloc(argsp->tag.utf8string_len, KM_SLEEP);
 580  596          rbp->tag.utf8string_len = argsp->tag.utf8string_len;
 581  597          bcopy(argsp->tag.utf8string_val, rbp->tag.utf8string_val,
 582  598              rbp->tag.utf8string_len);
 583  599  
 584  600          rbp->array_len = 1;
 585  601          rbp->array = kmem_zalloc(rbp->array_len * sizeof (nfs_resop4),
 586  602              KM_SLEEP);
 587  603          resop = &rbp->array[0];
 588  604          resop->resop = argsp->array[0].argop;   /* copy first op over */
 589  605  
 590  606          /* Any op will do, just need to access status field */
 591  607          resp = &resop->nfs_resop4_u.opputfh;
 592  608  
 593  609          /*
 594  610           * NFS4ERR_RESOURCE is allowed for all ops, except OP_ILLEGAL.
 595  611           * Note that all op numbers in the compound array were already
 596  612           * validated by the XDR decoder (xdr_COMPOUND4args_srv()).
 597  613           */
 598  614          resp->status = (resop->resop == OP_ILLEGAL ?
 599  615              NFS4ERR_OP_ILLEGAL : NFS4ERR_RESOURCE);
 600  616  
 601  617          /* compound status is same as first op status */
 602  618          rbp->status = resp->status;
 603  619  
 604  620          if (!svc_sendreply(req->rq_xprt, xdr_COMPOUND4res_srv, (char *)rbp)) {
 605  621                  DTRACE_PROBE2(nfss__rsrc_err__sendfail,
 606  622                      struct svc_req *, req->rq_xprt, char *, rbp);
 607  623                  svcerr_systemerr(req->rq_xprt);
 608  624          }
 609  625  
 610  626          UTF8STRING_FREE(rbp->tag);
 611  627          kmem_free(rbp->array, rbp->array_len * sizeof (nfs_resop4));
 612  628  }
  
    | 
      ↓ open down ↓ | 
    102 lines elided | 
    
      ↑ open up ↑ | 
  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX