--- old/usr/src/uts/common/fs/smbsrv/smb_dispatch.c	Mon Aug  5 17:26:29 2019
+++ new/usr/src/uts/common/fs/smbsrv/smb_dispatch.c	Mon Aug  5 17:26:29 2019
@@ -21,7 +21,7 @@
 
 /*
  * 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.
  */
 
 /*
@@ -168,9 +168,9 @@
 	{ "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 */
@@ -187,18 +187,20 @@
 	{ "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 */
@@ -223,13 +225,13 @@
 	{ "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 */
@@ -408,7 +410,7 @@
 	{ "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 */
@@ -503,11 +505,13 @@
 	 * 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
@@ -527,20 +531,58 @@
 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->session->signing.flags & SMB_SIGNING_ENABLED) {
-		if (SMB_IS_NT_CANCEL(sr)) {
+	if (sr->smb_com == SMB_COM_NT_CANCEL) {
+		if (sr->session->signing.flags & SMB_SIGNING_ENABLED) {
 			sr->session->signing.seqnum++;
 			sr->sr_seqnum = sr->session->signing.seqnum + 1;
 			sr->reply_seqnum = 0;
-		} else {
+		}
+
+		/*
+		 * 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;
@@ -574,24 +616,17 @@
 	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:
-		mutex_exit(&sr->sr_mutex);
-		smb1sr_work(sr);
-		sr = NULL;
-		break;
+	if (sr->sr_state == SMB_REQ_STATE_SUBMITTED)
+		sr->sr_state = SMB_REQ_STATE_ACTIVE;
+	mutex_exit(&sr->sr_mutex);
 
-	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;
-	}
+	smb1sr_work(sr);
+
 	smb_srqueue_runq_exit(srq);
 }
 
@@ -610,6 +645,8 @@
 	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;
@@ -678,6 +715,9 @@
 	    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) {
@@ -706,10 +746,6 @@
 		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
@@ -727,6 +763,10 @@
 		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);
 
@@ -752,11 +792,16 @@
 
 	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);
@@ -798,11 +843,47 @@
 		(*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;
@@ -861,19 +942,8 @@
 	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) {
@@ -1186,7 +1256,7 @@
 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);
 }
 
@@ -1193,7 +1263,7 @@
 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
