Print this page
NEX-5665 SMB2 oplock leases
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-5665 SMB2 oplock leases
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@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-4083 Upstream changes from illumos 5917 and 5995
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-3620 need upstream cleanups for smbsrv
Reviewed by: Hans Rosenfeld <hans.rosenfeld@nexenta.com>
SMB-58 smbsrv should be immune to its own FEM hooks
SMB-65 SMB server in non-global zones (kmem_caches)
common kmem_cache instances across zones
separate GZ-only init from NGZ init
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

@@ -18,11 +18,11 @@
  *
  * CDDL HEADER END
  */
 /*
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
  * Copyright 2015 Joyent, Inc.
  */
 
 #include <smbsrv/smb_kproto.h>
 #include <smbsrv/smb_fsops.h>

@@ -79,11 +79,10 @@
     struct caller_context *);
 static int smb_fem_oplock_write(femarg_t *, uio_t *, int, cred_t *,
     struct caller_context *);
 static int smb_fem_oplock_setattr(femarg_t *, vattr_t *, int, cred_t *,
     caller_context_t *);
-static int smb_fem_oplock_rwlock(femarg_t *, int, caller_context_t *);
 static int smb_fem_oplock_space(femarg_t *, int, flock64_t *, int,
     offset_t, cred_t *, caller_context_t *);
 static int smb_fem_oplock_vnevent(femarg_t *, vnevent_t, vnode_t *, char *,
     caller_context_t *);
 

@@ -90,17 +89,16 @@
 static const fs_operation_def_t smb_oplock_tmpl[] = {
         VOPNAME_OPEN,   { .femop_open = smb_fem_oplock_open },
         VOPNAME_READ,   { .femop_read = smb_fem_oplock_read },
         VOPNAME_WRITE,  { .femop_write = smb_fem_oplock_write },
         VOPNAME_SETATTR, { .femop_setattr = smb_fem_oplock_setattr },
-        VOPNAME_RWLOCK, { .femop_rwlock = smb_fem_oplock_rwlock },
         VOPNAME_SPACE,  { .femop_space = smb_fem_oplock_space },
         VOPNAME_VNEVENT, { .femop_vnevent = smb_fem_oplock_vnevent },
         NULL, NULL
 };
 
-static int smb_fem_oplock_break(femarg_t *, caller_context_t *, uint32_t);
+static int smb_fem_oplock_wait(smb_node_t *, caller_context_t *);
 
 /*
  * smb_fem_init
  *
  * This function is not multi-thread safe. The caller must make sure only one

@@ -153,19 +151,24 @@
                 smb_oplock_ops = NULL;
         }
         smb_fem_initialized = B_FALSE;
 }
 
+/*
+ * Install our fem hooks for change notify.
+ * Not using hold/rele function here because we
+ * remove the fem hooks before node destroy.
+ */
 int
 smb_fem_fcn_install(smb_node_t *node)
 {
         int rc;
 
         if (smb_fcn_ops == NULL)
                 return (ENOSYS);
         rc = fem_install(node->vp, smb_fcn_ops, (void *)node, OPARGUNIQ,
-            (fem_func_t)smb_node_ref, (fem_func_t)smb_node_release);
+            NULL, NULL);
         return (rc);
 }
 
 void
 smb_fem_fcn_uninstall(smb_node_t *node)

@@ -421,20 +424,41 @@
     femarg_t            *arg,
     int                 mode,
     cred_t              *cr,
     caller_context_t    *ct)
 {
-        uint32_t        flags;
+        smb_node_t      *node;
+        uint32_t        status;
         int             rc = 0;
 
         if (ct != &smb_ct) {
-                if (mode & (FWRITE|FTRUNC))
-                        flags = SMB_OPLOCK_BREAK_TO_NONE;
-                else
-                        flags = SMB_OPLOCK_BREAK_TO_LEVEL_II;
-                rc = smb_fem_oplock_break(arg, ct, flags);
+                uint32_t req_acc = FILE_READ_DATA;
+                uint32_t cr_disp = FILE_OPEN_IF;
+
+                node = (smb_node_t *)(arg->fa_fnode->fn_available);
+                SMB_NODE_VALID(node);
+
+                /*
+                 * Get req_acc, cr_disp just accurate enough so
+                 * the oplock break call does the right thing.
+                 */
+                if (mode & FWRITE) {
+                        req_acc = FILE_READ_DATA | FILE_WRITE_DATA;
+                        cr_disp = (mode & FTRUNC) ?
+                            FILE_OVERWRITE_IF : FILE_OPEN_IF;
+                } else {
+                        req_acc = FILE_READ_DATA;
+                        cr_disp = FILE_OPEN_IF;
         }
+
+                status = smb_oplock_break_OPEN(node, NULL,
+                    req_acc, cr_disp);
+                if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)
+                        rc = smb_fem_oplock_wait(node, ct);
+                else if (status != 0)
+                        rc = EIO;
+        }
         if (rc == 0)
                 rc = vnext_open(arg, mode, cr, ct);
 
         return (rc);
 }

@@ -450,15 +474,23 @@
     uio_t               *uiop,
     int                 ioflag,
     cred_t              *cr,
     caller_context_t    *ct)
 {
+        smb_node_t      *node;
+        uint32_t        status;
         int     rc = 0;
 
         if (ct != &smb_ct) {
-                rc = smb_fem_oplock_break(arg, ct,
-                    SMB_OPLOCK_BREAK_TO_LEVEL_II);
+                node = (smb_node_t *)(arg->fa_fnode->fn_available);
+                SMB_NODE_VALID(node);
+
+                status = smb_oplock_break_READ(node, NULL);
+                if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)
+                        rc = smb_fem_oplock_wait(node, ct);
+                else if (status != 0)
+                        rc = EIO;
         }
         if (rc == 0)
                 rc = vnext_read(arg, uiop, ioflag, cr, ct);
 
         return (rc);

@@ -475,14 +507,24 @@
     uio_t               *uiop,
     int                 ioflag,
     cred_t              *cr,
     caller_context_t    *ct)
 {
+        smb_node_t      *node;
+        uint32_t        status;
         int     rc = 0;
 
-        if (ct != &smb_ct)
-                rc = smb_fem_oplock_break(arg, ct, SMB_OPLOCK_BREAK_TO_NONE);
+        if (ct != &smb_ct) {
+                node = (smb_node_t *)(arg->fa_fnode->fn_available);
+                SMB_NODE_VALID(node);
+
+                status = smb_oplock_break_WRITE(node, NULL);
+                if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)
+                        rc = smb_fem_oplock_wait(node, ct);
+                else if (status != 0)
+                        rc = EIO;
+        }
         if (rc == 0)
                 rc = vnext_write(arg, uiop, ioflag, cr, ct);
 
         return (rc);
 }

@@ -493,38 +535,27 @@
     vattr_t             *vap,
     int                 flags,
     cred_t              *cr,
     caller_context_t    *ct)
 {
+        smb_node_t      *node;
+        uint32_t        status;
         int     rc = 0;
 
-        if (ct != &smb_ct && (vap->va_mask & AT_SIZE) != 0)
-                rc = smb_fem_oplock_break(arg, ct, SMB_OPLOCK_BREAK_TO_NONE);
-        if (rc == 0)
-                rc = vnext_setattr(arg, vap, flags, cr, ct);
-        return (rc);
-}
+        if (ct != &smb_ct && (vap->va_mask & AT_SIZE) != 0) {
+                node = (smb_node_t *)(arg->fa_fnode->fn_available);
+                SMB_NODE_VALID(node);
 
-static int
-smb_fem_oplock_rwlock(
-    femarg_t            *arg,
-    int                 write_lock,
-    caller_context_t    *ct)
-{
-        uint32_t        flags;
-        int             rc = 0;
-
-        if (ct != &smb_ct) {
-                if (write_lock)
-                        flags = SMB_OPLOCK_BREAK_TO_NONE;
-                else
-                        flags = SMB_OPLOCK_BREAK_TO_LEVEL_II;
-                rc = smb_fem_oplock_break(arg, ct, flags);
+                status = smb_oplock_break_SETINFO(node, NULL,
+                    FileEndOfFileInformation);
+                if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)
+                        rc = smb_fem_oplock_wait(node, ct);
+                else if (status != 0)
+                        rc = EIO;
         }
         if (rc == 0)
-                rc = vnext_rwlock(arg, write_lock, ct);
-
+                rc = vnext_setattr(arg, vap, flags, cr, ct);
         return (rc);
 }
 
 static int
 smb_fem_oplock_space(

@@ -534,14 +565,25 @@
     int                 flag,
     offset_t            offset,
     cred_t              *cr,
     caller_context_t    *ct)
 {
+        smb_node_t      *node;
+        uint32_t        status;
         int     rc = 0;
 
-        if (ct != &smb_ct)
-                rc = smb_fem_oplock_break(arg, ct, SMB_OPLOCK_BREAK_TO_NONE);
+        if (ct != &smb_ct) {
+                node = (smb_node_t *)(arg->fa_fnode->fn_available);
+                SMB_NODE_VALID(node);
+
+                status = smb_oplock_break_SETINFO(node, NULL,
+                    FileAllocationInformation);
+                if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)
+                        rc = smb_fem_oplock_wait(node, ct);
+                else if (status != 0)
+                        rc = EIO;
+        }
         if (rc == 0)
                 rc = vnext_space(arg, cmd, bfp, flag, offset, cr, ct);
         return (rc);
 }
 

@@ -565,56 +607,59 @@
     vnevent_t           vnevent,
     vnode_t             *dvp,
     char                *name,
     caller_context_t    *ct)
 {
-        uint32_t        flags;
+        smb_node_t      *node;
+        uint32_t        status;
         int             rc = 0;
 
         if (ct != &smb_ct) {
+                node = (smb_node_t *)(arg->fa_fnode->fn_available);
+                SMB_NODE_VALID(node);
+
                 switch (vnevent) {
                 case VE_REMOVE:
                 case VE_PRE_RENAME_DEST:
                 case VE_RENAME_DEST:
-                        flags = SMB_OPLOCK_BREAK_TO_NONE |
-                            SMB_OPLOCK_BREAK_BATCH;
-                        rc = smb_fem_oplock_break(arg, ct, flags);
+                        status = smb_oplock_break_HANDLE(node, NULL);
                         break;
                 case VE_PRE_RENAME_SRC:
                 case VE_RENAME_SRC:
-                        flags = SMB_OPLOCK_BREAK_TO_LEVEL_II |
-                            SMB_OPLOCK_BREAK_BATCH;
-                        rc = smb_fem_oplock_break(arg, ct, flags);
+                        status = smb_oplock_break_SETINFO(node, NULL,
+                            FileRenameInformation);
                         break;
                 default:
-                        rc = 0;
+                        status = 0;
                         break;
                 }
+                if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)
+                        rc = smb_fem_oplock_wait(node, ct);
+                else if (status != 0)
+                        rc = EIO;
         }
         if (rc == 0)
                 rc = vnext_vnevent(arg, vnevent, dvp, name, ct);
 
         return (rc);
 }
 
+int smb_fem_oplock_timeout = 5000; /* mSec. */
+
 static int
-smb_fem_oplock_break(femarg_t *arg, caller_context_t *ct, uint32_t flags)
+smb_fem_oplock_wait(smb_node_t *node, caller_context_t *ct)
 {
-        smb_node_t      *node;
-        int             rc;
+        int             rc = 0;
 
-        node = (smb_node_t *)((arg)->fa_fnode->fn_available);
-        SMB_NODE_VALID(node);
-
         ASSERT(ct != &smb_ct);
 
         if (ct && (ct->cc_flags & CC_DONTBLOCK)) {
-                flags |= SMB_OPLOCK_BREAK_NOWAIT;
-                rc = smb_oplock_break(NULL, node, flags);
-                if (rc == EAGAIN)
                         ct->cc_flags |= CC_WOULDBLOCK;
+                rc = EAGAIN;
         } else {
-                rc = smb_oplock_break(NULL, node, flags);
+                (void) smb_oplock_wait_break(node,
+                    smb_fem_oplock_timeout);
+                rc = 0;
         }
 
         return (rc);
 }