Print this page
    
NEX-17589 Get "too high" smbd error when copy big file to cifs share (redo)
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-16943 network outages with thread stuck in smb2_scoreboard_cancel
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-1643 dtrace provider for smbsrv
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
NEX-9864 Some SMB cancel races remain after NEX-5845
Revert (part of) "NEX-5845 rework SMB immediate cancel"
reverts (part of) commit 7a5da69f6d42b17ebcc95ca3d02925d07a01343e.
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5845 rework SMB immediate cancel
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-2975 SMB2 Cancel may fail (nits)
NEX-2975 SMB2 Cancel may fail
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Tony Nguyen <tony.nguyen@nexenta.com>
SMB-11 SMB2 message parse & dispatch
SMB-12 SMB2 Negotiate Protocol
SMB-13 SMB2 Session Setup
SMB-14 SMB2 Logoff
SMB-15 SMB2 Tree Connect
SMB-16 SMB2 Tree Disconnect
SMB-17 SMB2 Create
SMB-18 SMB2 Close
SMB-19 SMB2 Flush
SMB-20 SMB2 Read
SMB-21 SMB2 Write
SMB-22 SMB2 Lock/Unlock
SMB-23 SMB2 Ioctl
SMB-24 SMB2 Cancel
SMB-25 SMB2 Echo
SMB-26 SMB2 Query Dir
SMB-27 SMB2 Change Notify
SMB-28 SMB2 Query Info
SMB-29 SMB2 Set Info
SMB-30 SMB2 Oplocks
SMB-53 SMB2 Create Context options
(SMB2 code review cleanup 1, 2, 3)
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/uts/common/fs/smbsrv/smb2_cancel.c
          +++ new/usr/src/uts/common/fs/smbsrv/smb2_cancel.c
   1    1  /*
   2    2   * This file and its contents are supplied under the terms of the
  
    | 
      ↓ open down ↓ | 
    2 lines elided | 
    
      ↑ open up ↑ | 
  
   3    3   * Common Development and Distribution License ("CDDL"), version 1.0.
   4    4   * You may only use this file in accordance with the terms of version
   5    5   * 1.0 of the CDDL.
   6    6   *
   7    7   * A full copy of the text of the CDDL should have accompanied this
   8    8   * source.  A copy of the CDDL is also available via the Internet at
   9    9   * http://www.illumos.org/license/CDDL.
  10   10   */
  11   11  
  12   12  /*
  13      - * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
       13 + * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  14   14   */
  15   15  
  16   16  /*
  17   17   * Dispatch function for SMB2_CANCEL
  18   18   */
  19   19  
  20   20  #include <smbsrv/smb2_kproto.h>
  21   21  
  22      -static void smb2sr_cancel_async(smb_request_t *);
  23      -static void smb2sr_cancel_sync(smb_request_t *);
       22 +static void smb2_cancel_async(smb_request_t *);
       23 +static void smb2_cancel_sync(smb_request_t *);
  24   24  
  25   25  /*
  26   26   * This handles an SMB2_CANCEL request when seen in the reader.
  27   27   * (See smb2sr_newrq)  Handle this immediately, rather than
  28   28   * going through the normal taskq dispatch mechanism.
  29   29   * Note that Cancel does NOT get a response.
       30 + *
       31 + * Any non-zero return causes disconnect.
       32 + * SMB2 header is already decoded.
  30   33   */
  31   34  int
  32      -smb2sr_newrq_cancel(smb_request_t *sr)
       35 +smb2_newrq_cancel(smb_request_t *sr)
  33   36  {
  34      -        int rc;
  35   37  
  36   38          /*
  37      -         * Decode the header
       39 +         * If we get SMB2 cancel as part of a compound,
       40 +         * that's a protocol violation.  Drop 'em!
  38   41           */
  39      -        if ((rc = smb2_decode_header(sr)) != 0)
  40      -                return (rc);
       42 +        if (sr->smb2_next_command != 0)
       43 +                return (EINVAL);
  41   44  
       45 +        DTRACE_SMB2_START(op__Cancel, smb_request_t *, sr);
       46 +
  42   47          if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND)
  43      -                smb2sr_cancel_async(sr);
       48 +                smb2_cancel_async(sr);
  44   49          else
  45      -                smb2sr_cancel_sync(sr);
       50 +                smb2_cancel_sync(sr);
  46   51  
       52 +        DTRACE_SMB2_DONE(op__Cancel, smb_request_t *, sr);
       53 +
  47   54          return (0);
  48   55  }
  49   56  
       57 +/*
       58 + * Dispatch handler for SMB2_CANCEL.
       59 + * Note that Cancel does NOT get a response.
       60 + */
       61 +smb_sdrc_t
       62 +smb2_cancel(smb_request_t *sr)
       63 +{
       64 +
       65 +        /*
       66 +         * If we get SMB2 cancel as part of a compound,
       67 +         * that's a protocol violation.  Drop 'em!
       68 +         */
       69 +        if (sr->smb2_cmd_hdr != 0 || sr->smb2_next_command != 0)
       70 +                return (SDRC_DROP_VC);
       71 +
       72 +        DTRACE_SMB2_START(op__Cancel, smb_request_t *, sr);
       73 +
       74 +        if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) {
       75 +                smb2_cancel_async(sr);
       76 +        } else {
       77 +                smb2_cancel_sync(sr);
       78 +        }
       79 +
       80 +        DTRACE_SMB2_DONE(op__Cancel, smb_request_t *, sr);
       81 +
       82 +        return (SDRC_NO_REPLY);
       83 +}
       84 +
       85 +/*
       86 + * SMB2 Cancel (sync) has an inherent race with the request being
       87 + * cancelled.  The request may have been received but not yet
       88 + * executed by a worker thread, in which case we'll mark the
       89 + * request state as cancelled, and when a worker thread starts
       90 + * on this request we'll cancel everything in the compound.
       91 + */
  50   92  static void
  51      -smb2sr_cancel_sync(smb_request_t *sr)
       93 +smb2_cancel_sync(smb_request_t *sr)
  52   94  {
  53   95          struct smb_request *req;
  54   96          struct smb_session *session = sr->session;
  55   97          int cnt = 0;
  56   98  
       99 +        if (sr->smb2_messageid == 0)
      100 +                goto failure;
      101 +
  57  102          smb_slist_enter(&session->s_req_list);
  58      -        req = smb_slist_head(&session->s_req_list);
  59      -        while (req) {
  60      -                ASSERT(req->sr_magic == SMB_REQ_MAGIC);
  61      -                if ((req != sr) &&
  62      -                    (req->smb2_messageid == sr->smb2_messageid)) {
      103 +        for (req = smb_slist_head(&session->s_req_list); req != NULL;
      104 +            req = smb_slist_next(&session->s_req_list, req)) {
      105 +
      106 +                /* never cancel self */
      107 +                if (req == sr)
      108 +                        continue;
      109 +
      110 +                if (sr->smb2_messageid >= req->smb2_first_msgid &&
      111 +                    sr->smb2_messageid < (req->smb2_first_msgid +
      112 +                    req->smb2_total_credits)) {
  63  113                          smb_request_cancel(req);
  64  114                          cnt++;
  65  115                  }
  66      -                req = smb_slist_next(&session->s_req_list, req);
  67  116          }
      117 +        smb_slist_exit(&session->s_req_list);
      118 +
  68  119          if (cnt != 1) {
      120 +        failure:
  69  121                  DTRACE_PROBE2(smb2__cancel__error,
  70  122                      uint64_t, sr->smb2_messageid, int, cnt);
      123 +#ifdef  DEBUG
      124 +                /*
      125 +                 * It's somewhat common that we may see a cancel for a
      126 +                 * request that has already completed, so report that
      127 +                 * only in debug builds.
      128 +                 */
      129 +                cmn_err(CE_WARN, "SMB2 cancel failed, "
      130 +                    "client=%s, MID=0x%llx",
      131 +                    sr->session->ip_addr_str,
      132 +                    (u_longlong_t)sr->smb2_messageid);
      133 +#endif
  71  134          }
  72      -        smb_slist_exit(&session->s_req_list);
  73  135  }
  74  136  
      137 +/*
      138 + * Note that cancelling an async request doesn't have a race
      139 + * because the client doesn't learn about the async ID until we
      140 + * send it to them in an interim reply, and by that point the
      141 + * request has progressed to the point where smb_cancel can find
      142 + * the request and cancel it.
      143 + */
  75  144  static void
  76      -smb2sr_cancel_async(smb_request_t *sr)
      145 +smb2_cancel_async(smb_request_t *sr)
  77  146  {
  78  147          struct smb_request *req;
  79  148          struct smb_session *session = sr->session;
  80  149          int cnt = 0;
  81  150  
  82  151          smb_slist_enter(&session->s_req_list);
  83  152          req = smb_slist_head(&session->s_req_list);
  84  153          while (req) {
  85  154                  ASSERT(req->sr_magic == SMB_REQ_MAGIC);
  86  155                  if ((req != sr) &&
  87  156                      (req->smb2_async_id == sr->smb2_async_id)) {
  88  157                          smb_request_cancel(req);
  89  158                          cnt++;
  90  159                  }
  91  160                  req = smb_slist_next(&session->s_req_list, req);
  92  161          }
  93  162          if (cnt != 1) {
  94  163                  DTRACE_PROBE2(smb2__cancel__error,
  95  164                      uint64_t, sr->smb2_async_id, int, cnt);
      165 +                /*
      166 +                 * Not logging here, as this is normal, i.e.
      167 +                 * when both a cancel and a handle close
      168 +                 * terminates an SMB2_notify request.
      169 +                 */
  96  170          }
  97  171          smb_slist_exit(&session->s_req_list);
  98  172  }
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX