Print this page
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-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-6402 SMB2 change notify response wrong when STATUS_NOTIFY_ENUM_DIR
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-3906 Prefer that SMB change notify not tie up a worker thread
NEX-5278 SMB notify should buffer per file handle
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-3310 smbstat misreports change notify
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Dan Fields <dan.fields@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)
re #13470 rb4432 Sync some SMB differences from illumos
re #11215 rb3676 sesctl to SGI JBOD hangs in biowait() with a command stuck in mptsas driver
re #10734 NT Trans. Notify returning too quickly

@@ -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 2017 Nexenta Systems, Inc.  All rights reserved.
  */
 
 /*
  * File Change Notification (FCN)
  * SMB1 specific part.

@@ -56,19 +56,10 @@
  */
 
 #include <smbsrv/smb_kproto.h>
 
 /*
- * We add this flag to the CompletionFilter (see above) when the
- * client sets WatchTree.  Must not overlap FILE_NOTIFY_VALID_MASK.
- */
-#define NODE_FLAGS_WATCH_TREE           0x10000000
-#if (NODE_FLAGS_WATCH_TREE & FILE_NOTIFY_VALID_MASK)
-#error "NODE_FLAGS_WATCH_TREE"
-#endif
-
-/*
  * smb_nt_transact_notify_change
  *
  * Handle and SMB NT transact NOTIFY CHANGE request.
  * Basically, wait until "something has changed", and either
  * return information about what changed, or return a special

@@ -80,40 +71,177 @@
  * then send the reply to the client.
  */
 smb_sdrc_t
 smb_nt_transact_notify_change(smb_request_t *sr, struct smb_xa *xa)
 {
+        mbuf_chain_t            tmp_mbc;
+        uint32_t                oBufSize;
         uint32_t                CompletionFilter;
         unsigned char           WatchTree;
         uint32_t                status;
-        hrtime_t                t1, t2;
 
         if (smb_mbc_decodef(&xa->req_setup_mb, "lwb",
             &CompletionFilter, &sr->smb_fid, &WatchTree) != 0) {
                 smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0);
                 return (SDRC_ERROR);
         }
-        CompletionFilter &= FILE_NOTIFY_VALID_MASK;
-        if (WatchTree)
-                CompletionFilter |= NODE_FLAGS_WATCH_TREE;
 
         smbsr_lookup_file(sr);
+        if (sr->fid_ofile == NULL) {
+                smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0);
+                return (SDRC_ERROR);
+        }
 
-        t1 = gethrtime();
-        status = smb_notify_common(sr, &xa->rep_data_mb, CompletionFilter);
-        t2 = gethrtime();
+        oBufSize = xa->rep_param_mb.max_bytes;
+        CompletionFilter &= FILE_NOTIFY_VALID_MASK;
+        if (WatchTree)
+                CompletionFilter |= FILE_NOTIFY_CHANGE_EV_SUBDIR;
 
         /*
-         * We don't want to include the (indefinite) wait time of the
-         * smb_notify_common() call in the SMB1 transact latency.
-         * The easiest way to do that, without adding special case
-         * logic to the common SMB1 dispatch handler is to adjust the
-         * start time of this request to effectively subtract out the
-         * time we were blocked in smb_notify_common().
+         * Check for events and consume, non-blocking.
+         * Special return STATUS_PENDING means:
+         *   No events; caller must call "act2" next.
          */
-        sr->sr_time_start += (t2 - t1);
+        status = smb_notify_act1(sr, oBufSize, CompletionFilter);
+        if (status == NT_STATUS_PENDING) {
+                status = smb_notify_act2(sr);
+                if (status == NT_STATUS_PENDING) {
+                        /* See: smb_nt_transact_notify_finish */
+                        return (SDRC_SR_KEPT);
+                }
+                /* else: some other error, or even success */
+        }
 
-        if (status != 0)
-                smbsr_error(sr, status, 0, 0);
+        /*
+         * SMB1 expects an empty trans response after the
+         * FID we're watching is closed.
+         */
+        if (status == NT_STATUS_NOTIFY_CLEANUP) {
+                status = 0;
+                MBC_FLUSH(&sr->raw_data);
+        }
 
+        if (status != 0) {
+                smbsr_status(sr, status, 0, 0);
+                if (NT_SC_SEVERITY(status) == NT_STATUS_SEVERITY_ERROR)
+                        return (SDRC_ERROR);
+                /* Else continue with NT_STATUS_NOTIFY_ENUM_DIR etc. */
+        }
+
+        /*
+         * The nt_trans call expects the output in rep_param_mb,
+         * but our common code puts it in raw_data.  Move it
+         * where the caller expects it via swaping the two,
+         * which lets the normal cleanup take care of both.
+         */
+        tmp_mbc = xa->rep_param_mb;
+        xa->rep_param_mb = sr->raw_data;
+        sr->raw_data = tmp_mbc;
+
         return (SDRC_SUCCESS);
+}
+
+/*
+ * This is called via taskq_dispatch in smb_notify.c
+ * to finish up an NT transact notify change request.
+ */
+void
+smb_nt_transact_notify_finish(void *arg)
+{
+        smb_request_t   *sr = arg;
+        struct smb_xa   *xa;
+        smb_disp_stats_t *sds;
+        int             total_bytes, n_setup, n_param, n_data;
+        int             param_off, param_pad, data_off, data_pad;
+        uint32_t        status;
+
+        SMB_REQ_VALID(sr);
+
+        /*
+         * Common part of notify, puts data in sr->raw_data
+         */
+        status = smb_notify_act3(sr);
+
+        /*
+         * SMB1 expects an empty trans response after the
+         * FID we're watching is closed.
+         */
+        if (status == NT_STATUS_NOTIFY_CLEANUP) {
+                status = 0;
+                MBC_FLUSH(&sr->raw_data);
+        }
+
+        if (status != 0) {
+                smbsr_status(sr, status, 0, 0);
+                if (NT_SC_SEVERITY(status) == NT_STATUS_SEVERITY_ERROR) {
+                        (void) smb_mbc_encodef(&sr->reply, "bwbw",
+                            (short)0, 0L, (short)0, 0L);
+                        goto sendit;
+                }
+                /* Else continue with NT_STATUS_NOTIFY_ENUM_DIR etc. */
+        }
+
+        /*
+         * setup the NT transact reply
+         *
+         * Note that this is a copy/paste of code from
+         * smb_nt_trans_dispatch(), with minor changes.
+         * Intentionally keeping this similar to the
+         * original rather than hand-optimizing.
+         *
+         * The "setup" and "data" parts of this trans reply
+         * (n_setup, n_data, rep_setup_mb, rep_data_mb) are
+         * always empty.  sr->raw_data replaces rep_param_mb.
+         */
+        xa = sr->r_xa;
+        n_setup = MBC_LENGTH(&xa->rep_setup_mb);
+        n_param = MBC_LENGTH(&sr->raw_data);
+        n_data  = MBC_LENGTH(&xa->rep_data_mb);
+
+        n_setup = (n_setup + 1) / 2;    /* Convert to setup words */
+        param_pad = 1;                  /* must be one */
+        param_off = param_pad + 32 + 37 + (n_setup << 1) + 2;
+        /* Pad to 4 bytes */
+        data_pad = (4 - ((param_off + n_param) & 3)) % 4;
+        /* Param off from hdr */
+        data_off = param_off + n_param + data_pad;
+        total_bytes = param_pad + n_param + data_pad + n_data;
+
+        (void) smbsr_encode_result(sr, 18+n_setup, total_bytes,
+            "b3.llllllllbCw#.C#.C",
+            18 + n_setup,       /* wct */
+            n_param,            /* Total Parameter Bytes */
+            n_data,             /* Total Data Bytes */
+            n_param,            /* Total Parameter Bytes this buffer */
+            param_off,          /* Param offset from header start */
+            0,                  /* Param displacement */
+            n_data,             /* Total Data Bytes this buffer */
+            data_off,           /* Data offset from header start */
+            0,                  /* Data displacement */
+            n_setup,            /* suwcnt */
+            &xa->rep_setup_mb,  /* setup[] */
+            total_bytes,        /* Total data bytes */
+            param_pad,
+            &sr->raw_data,      /* output mbc */
+            data_pad,
+            &xa->rep_data_mb);
+
+sendit:
+        /*
+         * When smb_nt_transact_notify_change returned SDRC_SR_KEPT
+         * the dispatcher skipped the "done" probe, so do it now.
+         * Note: Don't use this probe in response time statistics.
+         */
+        DTRACE_SMB_DONE(op__NtTransact, smb_request_t *, sr);
+
+        sds = &sr->sr_server->sv_disp_stats1[sr->smb_com];
+        atomic_add_64(&sds->sdt_txb, (int64_t)sr->reply.chain_offset);
+
+        smbsr_send_reply(sr);   /* also puts the SMB header. */
+        smbsr_cleanup(sr);
+
+        mutex_enter(&sr->sr_mutex);
+        sr->sr_state = SMB_REQ_STATE_COMPLETED;
+        mutex_exit(&sr->sr_mutex);
+
+        smb_request_free(sr);
 }