Print this page
NEX-4856 SMB2 kstats don't correctly count compound requests
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15578 SMB2 durable handle redesign
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15555 SMB2 async redesign
NEX-15061 smtorture smb2.lock.cancel.cancel is failed
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Also follow-up change to:
 NEX-1643 dtrace provider for smbsrv (remove "done2" probes,
 which don't make sense with the new async design)
NEX-15578 SMB2 durable handle redesign
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15555 SMB2 async redesign
NEX-15061 smtorture smb2.lock.cancel.cancel is failed
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Also follow-up change to:
 NEX-1643 dtrace provider for smbsrv (remove "done2" probes,
 which don't make sense with the new async design)
NEX-1643 dtrace provider for smbsrv
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
NEX-10229 SMB v1 response incorrect when signature verification fails
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@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-5152 immediate SMB cancel may fail
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-5175 want SMB statistics separately for reads, writes, other
Reviewed by: Gordon Ross <gwr@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-4313 want iops, bandwidth, and latency kstats for smb
Portions contributed by: Gordon Ross <gwr@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
NEX-4391 improve smb cancel support
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-4147 Cancelled SMB2 requests sometimes have no response (missed things)
NEX-3611 CLONE NEX-3550 Replace smb2_enable with max_protocol
Reviewed by: Yuri Pankov <Yuri.Pankov@nexenta.com>
NEX-2370 unable to save/modify files (zip,notepad,excel) on CIFS share from Windows 7
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)
SMB-69 read-raw, write-raw are dead code
SMB-39 Use AF_UNIX pipes for RPC
SMB-50 User-mode SMB server
 Includes work by these authors:
 Thomas Keiser <thomas.keiser@nexenta.com>
 Albert Lee <trisk@nexenta.com>
SMB-65 SMB server in non-global zones (use zone_kcred())
SMB-65 SMB server in non-global zones (data structure changes)
Many things move to the smb_server_t object, and
many functions gain an sv arg (which server).
SMB-63 taskq_create_proc ... TQ_DYNAMIC puts tasks in p0
re #11974 CIFS Share - Tree connect fails from Windows 7 Clients
        
@@ -19,11 +19,11 @@
  * CDDL HEADER END
  */
 
 /*
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2019 Nexenta Systems, Inc.  All rights reserved.
  */
 
 /*
  * SMB requests.
  *
@@ -166,13 +166,13 @@
         { "SmbQueryInformation", SMB_SDT_OPS(query_information), /* 0x08 008 */
             0x08, PC_NETWORK_PROGRAM_1_0 },
         { "SmbSetInformation", SMB_SDT_OPS(set_information),    /* 0x09 009 */
             0x09, PC_NETWORK_PROGRAM_1_0 },
         { "SmbRead", SMB_SDT_OPS(read),                         /* 0x0A 010 */
-            0x0A, PC_NETWORK_PROGRAM_1_0 },
+            0x0A, PC_NETWORK_PROGRAM_1_0, SDDF_READOP },
         { "SmbWrite", SMB_SDT_OPS(write),                       /* 0x0B 011 */
-            0x0B, PC_NETWORK_PROGRAM_1_0 },
+            0x0B, PC_NETWORK_PROGRAM_1_0, SDDF_WRITEOP },
         { "SmbLockByteRange", SMB_SDT_OPS(lock_byte_range),     /* 0x0C 012 */
             0x0C, PC_NETWORK_PROGRAM_1_0 },
         { "SmbUnlockByteRange", SMB_SDT_OPS(unlock_byte_range), /* 0x0D 013 */
             0x0D, PC_NETWORK_PROGRAM_1_0 },
         { "SmbCreateTemporary", SMB_SDT_OPS(create_temporary),  /* 0x0E 014 */
@@ -185,22 +185,24 @@
             0x11, PC_NETWORK_PROGRAM_1_0,
             SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID },
         { "SmbSeek", SMB_SDT_OPS(seek),                         /* 0x12 018 */
             0x12, PC_NETWORK_PROGRAM_1_0 },
         { "SmbLockAndRead", SMB_SDT_OPS(lock_and_read),         /* 0x13 019 */
-            0x13, LANMAN1_0 },
+            0x13, LANMAN1_0, SDDF_READOP},
         { "SmbWriteAndUnlock", SMB_SDT_OPS(write_and_unlock),   /* 0x14 020 */
-            0x14, LANMAN1_0 },
+            0x14, LANMAN1_0, SDDF_WRITEOP },
         { "Invalid", SMB_SDT_OPS(invalid), 0x15, 0 },           /* 0x15 021 */
         { "Invalid", SMB_SDT_OPS(invalid), 0x16, 0 },           /* 0x16 022 */
         { "Invalid", SMB_SDT_OPS(invalid), 0x17, 0 },           /* 0x17 023 */
         { "Invalid", SMB_SDT_OPS(invalid), 0x18, 0 },           /* 0x18 024 */
         { "Invalid", SMB_SDT_OPS(invalid), 0x19, 0 },           /* 0x19 025 */
-        { "SmbReadRaw", SMB_SDT_OPS(invalid), 0x1A, 0 },        /* 0x1A 026 */
+        { "SmbReadRaw", SMB_SDT_OPS(read_raw),                  /* 0x1A 026 */
+            0x1A, LANMAN1_0 },
         { "Invalid", SMB_SDT_OPS(invalid), 0x1B, 0 },           /* 0x1B 027 */
         { "Invalid", SMB_SDT_OPS(invalid), 0x1C, 0 },           /* 0x1C 028 */
-        { "SmbWriteRaw", SMB_SDT_OPS(invalid), 0x1D, 0 },       /* 0x1D 029 */
+        { "SmbWriteRaw", SMB_SDT_OPS(write_raw),                /* 0x1D 029 */
+            0x1D, LANMAN1_0 },
         { "Invalid", SMB_SDT_OPS(invalid), 0x1E, 0 },           /* 0x1E 030 */
         { "Invalid", SMB_SDT_OPS(invalid), 0x1F, 0 },           /* 0x1F 031 */
         { "Invalid", SMB_SDT_OPS(invalid), 0x20, 0 },           /* 0x20 032 */
         { "Invalid", SMB_SDT_OPS(invalid), 0x21, 0 },           /* 0x21 033 */
         { "SmbSetInformation2", SMB_SDT_OPS(set_information2),  /* 0x22 034 */
@@ -221,17 +223,17 @@
         { "Invalid", SMB_SDT_OPS(invalid), 0x29, 0 },   /* 0x29 041 */
         { "Invalid", SMB_SDT_OPS(invalid), 0x2A, 0 },   /* 0x2A 042 */
         { "SmbEcho", SMB_SDT_OPS(echo),                         /* 0x2B 043 */
             0x2B, LANMAN1_0, SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID },
         { "SmbWriteAndClose", SMB_SDT_OPS(write_and_close),     /* 0x2C 044 */
-            0x2C, LANMAN1_0 },
+            0x2C, LANMAN1_0, SDDF_WRITEOP },
         { "SmbOpenX", SMB_SDT_OPS(open_andx),                   /* 0x2D 045 */
             0x2D, LANMAN1_0 },
         { "SmbReadX", SMB_SDT_OPS(read_andx),                   /* 0x2E 046 */
-            0x2E, LANMAN1_0 },
+            0x2E, LANMAN1_0, SDDF_READOP },
         { "SmbWriteX", SMB_SDT_OPS(write_andx),                 /* 0x2F 047 */
-            0x2F, LANMAN1_0 },
+            0x2F, LANMAN1_0, SDDF_WRITEOP },
         { "Invalid", SMB_SDT_OPS(invalid), 0x30, 0 },   /* 0x30 048 */
         { "SmbCloseAndTreeDisconnect",
             SMB_SDT_OPS(close_and_tree_disconnect),             /* 0x31 049 */
             0x31, LANMAN1_0 },
         { "SmbTransaction2", SMB_SDT_OPS(transaction2),         /* 0x32 050 */
@@ -406,11 +408,11 @@
         { "Invalid", SMB_SDT_OPS(invalid), 0xBE, 0, 0 },        /* 0xBE 190 */
         { "Invalid", SMB_SDT_OPS(invalid), 0xBF, 0, 0 },        /* 0xBF 191 */
         { "SmbOpenPrintFile", SMB_SDT_OPS(open_print_file),     /* 0xC0 192 */
             0xC0, PC_NETWORK_PROGRAM_1_0, 0 },
         { "SmbWritePrintFile", SMB_SDT_OPS(write_print_file),   /* 0xC1 193 */
-            0xC1, PC_NETWORK_PROGRAM_1_0, 0 },
+            0xC1, PC_NETWORK_PROGRAM_1_0, SDDF_WRITEOP },
         { "SmbClosePrintFile", SMB_SDT_OPS(close_print_file),   /* 0xC2 194 */
             0xC2, PC_NETWORK_PROGRAM_1_0, 0 },
         { "SmbGetPrintQueue", SMB_SDT_OPS(get_print_queue),     /* 0xC3 195 */
             0xC3, PC_NETWORK_PROGRAM_1_0, 0 },
         { "Invalid", SMB_SDT_OPS(invalid), 0xC4, 0, 0 },        /* 0xC4 196 */
@@ -501,15 +503,17 @@
          * Mark this request so we know that we've already cleaned it up.
          * A request should only get cleaned up once so multiple calls to
          * smbsr_cleanup for the same request indicate a bug.
          */
         mutex_enter(&sr->sr_mutex);
-        if (sr->sr_state != SMB_REQ_STATE_CANCELED)
+        if (sr->sr_state != SMB_REQ_STATE_CANCELLED)
                 sr->sr_state = SMB_REQ_STATE_CLEANED_UP;
         mutex_exit(&sr->sr_mutex);
 }
 
+int smb_cancel_in_reader = 1;
+
 /*
  * This is the SMB1 handler for new smb requests, called from
  * smb_session_reader after SMB negotiate is done.  For most SMB
  * requests, we just enqueue them for the smb_session_worker to
  * execute via the task queue, so they can block for resources
@@ -525,24 +529,62 @@
  * will drop the session.
  */
 int
 smb1sr_newrq(smb_request_t *sr)
 {
-        uint32_t magic;
+        uint16_t pid_hi, pid_lo;
+        int rc, save_offset;
 
-        magic = SMB_READ_PROTOCOL(sr->sr_request_buf);
-        if (magic != SMB_PROTOCOL_MAGIC) {
+        /*
+         * Decode the SMB header now (peek) so that
+         * SMB_COM_NT_CANCEL can find this SR.
+         */
+        save_offset = sr->command.chain_offset;
+        rc = smb_mbc_decodef(&sr->command, SMB_HEADER_ED_FMT,
+            &sr->smb_com,
+            &sr->smb_rcls,
+            &sr->smb_reh,
+            &sr->smb_err,
+            &sr->smb_flg,
+            &sr->smb_flg2,
+            &pid_hi,
+            sr->smb_sig,
+            &sr->smb_tid,
+            &pid_lo,
+            &sr->smb_uid,
+            &sr->smb_mid);
+        sr->command.chain_offset = save_offset;
+        if (rc != 0) {
+                /* Failed decoding the header. Drop 'em. */
                 smb_request_free(sr);
                 return (EPROTO);
         }
+        sr->smb_pid = (pid_hi << 16) | pid_lo;
 
+        if (sr->smb_com == SMB_COM_NT_CANCEL) {
         if (sr->session->signing.flags & SMB_SIGNING_ENABLED) {
-                if (SMB_IS_NT_CANCEL(sr)) {
                         sr->session->signing.seqnum++;
                         sr->sr_seqnum = sr->session->signing.seqnum + 1;
                         sr->reply_seqnum = 0;
+                }
+
+                /*
+                 * Normally execute cancel requests immediately,
+                 * (here in the reader thread) so they won't wait
+                 * for other commands already in the task queue.
+                 * Disable this via smb_cancel_in_reader=0 for
+                 * testing or diagnostic efforts, in which case
+                 * cancel runs via taskq_dispatch.
+                 */
+                if (smb_cancel_in_reader != 0) {
+                        rc = smb1sr_newrq_cancel(sr);
+                        smb_request_free(sr);
+                        return (rc);
+                }
                 } else {
+                /* not NT cancel */
+                if (sr->session->signing.flags & SMB_SIGNING_ENABLED) {
                         sr->session->signing.seqnum += 2;
                         sr->sr_seqnum = sr->session->signing.seqnum;
                         sr->reply_seqnum = sr->sr_seqnum + 1;
                 }
         }
@@ -572,28 +614,21 @@
         srq = sr->session->s_srqueue;
         smb_srqueue_waitq_to_runq(srq);
         sr->sr_worker = curthread;
         sr->sr_time_active = gethrtime();
 
+        /*
+         * Always dispatch to the work function, because cancelled
+         * requests need an error reply (NT_STATUS_CANCELLED).
+         */
         mutex_enter(&sr->sr_mutex);
-        switch (sr->sr_state) {
-        case SMB_REQ_STATE_SUBMITTED:
+        if (sr->sr_state == SMB_REQ_STATE_SUBMITTED)
+                sr->sr_state = SMB_REQ_STATE_ACTIVE;
                 mutex_exit(&sr->sr_mutex);
+
                 smb1sr_work(sr);
-                sr = NULL;
-                break;
 
-        default:
-                /*
-                 * SMB1 requests that have been cancelled
-                 * have no reply.  Just free it.
-                 */
-                sr->sr_state = SMB_REQ_STATE_COMPLETED;
-                mutex_exit(&sr->sr_mutex);
-                smb_request_free(sr);
-                break;
-        }
         smb_srqueue_runq_exit(srq);
 }
 
 /*
  * smb1sr_work
@@ -608,10 +643,12 @@
         const smb_disp_entry_t  *sdd;
         smb_disp_stats_t        *sds;
         boolean_t               disconnect = B_FALSE;
         smb_session_t           *session;
         smb_server_t            *server;
+        int32_t                 txbase;
+        uint32_t                rxbytes;
         uint32_t                byte_count;
         uint32_t                max_bytes;
         uint16_t                pid_hi, pid_lo;
 
         session = sr->session;
@@ -676,10 +713,13 @@
             pid_lo,
             sr->smb_uid,
             sr->smb_mid);
         sr->first_smb_com = sr->smb_com;
 
+        /* Need this for early goto report_error cases. */
+        sr->cur_reply_offset = sr->reply.chain_offset;
+
         if ((session->signing.flags & SMB_SIGNING_CHECK) != 0) {
                 if ((sr->smb_flg2 & SMB_FLAGS2_SMB_SECURITY_SIGNATURE) == 0 ||
                     smb_sign_check_request(sr) != 0) {
                         smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
                             ERRDOS, ERROR_ACCESS_DENIED);
@@ -704,14 +744,10 @@
         if (smb_mbc_decodef(&sr->command, "#.w", sr->smb_wct*2, &sr->smb_bcc)) {
                 disconnect = B_TRUE;
                 goto report_error;
         }
 
-        atomic_add_64(&sds->sdt_rxb,
-            (int64_t)(sr->smb_wct * 2 + sr->smb_bcc + 1));
-        sr->sr_txb = sr->reply.chain_offset;
-
         /*
          * Ignore smb_bcc if CAP_LARGE_READX/CAP_LARGE_WRITEX
          * and this is SmbReadX/SmbWriteX since this enables
          * large reads/write and bcc is only 16-bits.
          */
@@ -725,10 +761,14 @@
         } else {
                 /* ordinary case */
                 byte_count = (uint32_t)sr->smb_bcc;
         }
 
+        /* Save these for kstat updates below. */
+        rxbytes = byte_count + 1 + 2 * sr->smb_wct;
+        txbase = sr->reply.chain_offset;
+
         (void) MBC_SHADOW_CHAIN(&sr->smb_data, &sr->command,
             sr->command.chain_offset, byte_count);
 
         sr->command.chain_offset += byte_count;
         if (sr->command.chain_offset > sr->command.max_bytes) {
@@ -750,15 +790,20 @@
                 sr->andx_com = (unsigned char)-1;
         }
 
         mutex_enter(&sr->sr_mutex);
         switch (sr->sr_state) {
-        case SMB_REQ_STATE_SUBMITTED:
+        case SMB_REQ_STATE_ACTIVE:
+                break;
         case SMB_REQ_STATE_CLEANED_UP:
                 sr->sr_state = SMB_REQ_STATE_ACTIVE;
                 break;
-        case SMB_REQ_STATE_CANCELED:
+        case SMB_REQ_STATE_CANCELLED:
+                /*
+                 * Keep cancelled.  Handlers that might block will
+                 * check the state and return NT_STATUS_CANCELLED.
+                 */
                 break;
         default:
                 ASSERT(0);
                 break;
         }
@@ -796,15 +841,51 @@
 
         if (sdrc != SDRC_SR_KEPT) {
                 (*sdd->sdt_post_op)(sr);
                 smbsr_cleanup(sr);
         }
-        smb_latency_add_sample(&sds->sdt_lat, gethrtime() - sr->sr_time_start);
 
-        atomic_add_64(&sds->sdt_txb,
-            (int64_t)(sr->reply.chain_offset - sr->sr_txb));
+        /* Record latency and rx/tx bytes per:  server, session, share. */
+        {
+                hrtime_t        dt;
+                int64_t         rxb, txb;
+                smb_disp_stats_t        *client_sds;    /* session */
+                smb_disp_stats_t        *share_sds;  /* kshare */
+                int     cmd_type;
 
+                if (sdd->sdt_flags & SDDF_READOP) {
+                        cmd_type = SMBSRV_CLSH_READ;
+                } else if (sdd->sdt_flags & SDDF_WRITEOP) {
+                        cmd_type = SMBSRV_CLSH_WRITE;
+                } else {
+                        cmd_type = SMBSRV_CLSH_OTHER;
+                }
+
+                dt = gethrtime() - sr->sr_time_start;
+                rxb = (int64_t)rxbytes;
+                txb = (int64_t)(sr->reply.chain_offset - txbase);
+
+                smb_server_inc_req(server);
+                smb_latency_add_sample(&sds->sdt_lat, dt);
+                atomic_add_64(&sds->sdt_rxb, rxb);
+                atomic_add_64(&sds->sdt_txb, txb);
+
+                client_sds = &session->s_stats[cmd_type];
+                smb_latency_add_sample(&client_sds->sdt_lat, dt);
+                atomic_add_64(&client_sds->sdt_rxb, rxb);
+                atomic_add_64(&client_sds->sdt_txb, txb);
+
+                if ((sr->tid_tree != NULL) &&
+                    (sr->tid_tree->t_kshare != NULL)) {
+                        share_sds =
+                            &sr->tid_tree->t_kshare->shr_stats[cmd_type];
+                        smb_latency_add_sample(&share_sds->sdt_lat, dt);
+                        atomic_add_64(&share_sds->sdt_rxb, rxb);
+                        atomic_add_64(&share_sds->sdt_txb, txb);
+                }
+        }
+
         switch (sdrc) {
         case SDRC_SUCCESS:
                 break;
 
         case SDRC_DROP_VC:
@@ -859,23 +940,12 @@
 
 reply_ready:
         smbsr_send_reply(sr);
 
 drop_connection:
-        if (disconnect) {
-                smb_rwx_rwenter(&session->s_lock, RW_WRITER);
-                switch (session->s_state) {
-                case SMB_SESSION_STATE_DISCONNECTED:
-                case SMB_SESSION_STATE_TERMINATED:
-                        break;
-                default:
-                        smb_soshutdown(session->sock);
-                        session->s_state = SMB_SESSION_STATE_DISCONNECTED;
-                        break;
-                }
-                smb_rwx_rwexit(&session->s_lock);
-        }
+        if (disconnect)
+                smb_session_disconnect(session);
 
 out:
         if (sr != NULL) {
                 mutex_enter(&sr->sr_mutex);
                 sr->sr_state = SMB_REQ_STATE_COMPLETED;
@@ -1184,18 +1254,18 @@
  * transports and NT supports mpx only over connectionless transports.
  */
 smb_sdrc_t
 smb_pre_invalid(smb_request_t *sr)
 {
-        DTRACE_SMB_1(op__Invalid__start, smb_request_t *, sr);
+        DTRACE_SMB_START(op__Invalid, smb_request_t *, sr);
         return (SDRC_SUCCESS);
 }
 
 void
 smb_post_invalid(smb_request_t *sr)
 {
-        DTRACE_SMB_1(op__Invalid__done, smb_request_t *, sr);
+        DTRACE_SMB_DONE(op__Invalid, smb_request_t *, sr);
 }
 
 smb_sdrc_t
 smb_com_invalid(smb_request_t *sr)
 {