s3: Avoid oplock break by storing timestamps with gpfs_set_times
authorChristof Schmitt <cs@samba.org>
Tue, 7 Jan 2014 18:55:46 +0000 (11:55 -0700)
committerChristof Schmitt <cs@samba.org>
Wed, 8 Jan 2014 23:04:47 +0000 (00:04 +0100)
The gpfs_set_times API call allows setting timestamps directly in GPFS
without going through the utime() call. Using this API call fixes an
unecessary oplock break when a client sends a SET_FILE_ALLOCATION_INFO
request and no other client has opened the file. The call to utime()
triggers the oplock break through the Linux kernel. Using the
gpfs_set_times call for updating the timestamp avoids the call to
utime() and the oplock break.

Signed-off-by: Christof Schmitt <cs@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
Autobuild-User(master): Christof Schmitt <cs@samba.org>
Autobuild-Date(master): Thu Jan  9 00:04:48 CET 2014 on sn-devel-104

docs-xml/manpages/vfs_gpfs.8.xml
source3/modules/gpfs.c
source3/modules/vfs_gpfs.c
source3/modules/vfs_gpfs.h

index 20dba68ffc19acede4f00cdbd71b7ba98dc8c241..4ba1b251a77b00502c35763d6199ca0e054ba66f 100644 (file)
 
                <varlistentry>
 
+               <term>gpfs:settimes = [ yes | no ]</term>
+               <listitem>
+               <para>
+               Use the gpfs_set_times API when changing the
+               timestamps of a file or directory. If the GPFS API is
+               not available the old method of using utime and the
+               GPFS winattr call will be used instead.
+               </para>
+
+               <itemizedlist>
+               <listitem><para>
+               <command>yes(default)</command> - Use gpfs_set_times.
+               Fall back to utime and winattr when it is not available.
+               </para></listitem>
+               <listitem><para>
+               <command>no</command> - Do not use gpfs_set_times.
+               </para></listitem>
+               </itemizedlist>
+               </listitem>
+
+               </varlistentry>
+               <varlistentry>
+
                <term>nfs4:mode = [ simple | special ]</term>
                <listitem>
                <para>
index 9730d3af6177ec69df59cae18af923249a7602bd..62502866ac8263b7ce5e7c19f4993d47b8d0d7b2 100644 (file)
@@ -39,6 +39,8 @@ static int (*gpfs_get_winattrs_fn)(int fd, struct gpfs_winattr *attrs);
 static int (*gpfs_prealloc_fn)(int fd, gpfs_off64_t startOffset, gpfs_off64_t bytesToPrealloc);
 static int (*gpfs_ftruncate_fn)(int fd, gpfs_off64_t length);
 static int (*gpfs_lib_init_fn)(int flags);
+static int (*gpfs_set_times_path_fn)(char *pathname, int flags,
+                                    gpfs_timestruc_t times[4]);
 static int (*gpfs_quotactl_fn)(char *pathname, int cmd, int id, void *bufferP);
 static int (*gpfs_fcntl_fn)(gpfs_file_t fileDesc, void *fcntlArgP);
 static int (*gpfs_getfilesetid_fn)(char *pathname, char *name, int *idP);
@@ -290,6 +292,49 @@ void smbd_gpfs_lib_init()
        }
 }
 
+static void timespec_to_gpfs_time(struct timespec ts, gpfs_timestruc_t *gt,
+                                 int idx, int *flags)
+{
+       if (!null_timespec(ts)) {
+               *flags |= 1 << idx;
+               gt[idx].tv_sec = ts.tv_sec;
+               gt[idx].tv_nsec = ts.tv_nsec;
+               DEBUG(10, ("Setting GPFS time %d, flags 0x%x\n", idx, *flags));
+       }
+}
+
+int smbd_gpfs_set_times_path(char *path, struct smb_file_time *ft)
+{
+       gpfs_timestruc_t gpfs_times[4];
+       int flags = 0;
+       int rc;
+
+       if (!gpfs_set_times_path_fn) {
+               errno = ENOSYS;
+               return -1;
+       }
+
+       ZERO_ARRAY(gpfs_times);
+       timespec_to_gpfs_time(ft->atime, gpfs_times, 0, &flags);
+       timespec_to_gpfs_time(ft->mtime, gpfs_times, 1, &flags);
+       /* No good mapping from LastChangeTime to ctime, not storing */
+       timespec_to_gpfs_time(ft->create_time, gpfs_times, 3, &flags);
+
+       if (!flags) {
+               DEBUG(10, ("nothing to do, return to avoid EINVAL\n"));
+               return 0;
+       }
+
+       rc = gpfs_set_times_path_fn(path, flags, gpfs_times);
+
+       if (rc != 0) {
+               DEBUG(1,("gpfs_set_times() returned with error %s\n",
+                       strerror(errno)));
+       }
+
+       return rc;
+}
+
 static bool init_gpfs_function_lib(void *plibhandle_pointer,
                                   const char *libname,
                                   void *pfn_pointer, const char *fn_name)
@@ -354,6 +399,7 @@ void init_gpfs(void)
        init_gpfs_function(&gpfs_prealloc_fn, "gpfs_prealloc");
        init_gpfs_function(&gpfs_ftruncate_fn, "gpfs_ftruncate");
         init_gpfs_function(&gpfs_lib_init_fn,"gpfs_lib_init");
+       init_gpfs_function(&gpfs_set_times_path_fn, "gpfs_set_times_path");
        init_gpfs_function(&gpfs_quotactl_fn, "gpfs_quotactl");
        init_gpfs_function(&gpfs_fcntl_fn, "gpfs_fcntl");
        init_gpfs_function(&gpfs_getfilesetid_fn, "gpfs_getfilesetid");
index c374957c213590bae418a736113ea962ca01328f..5c9981fea94fcfc3c7ba0423332eed93d42e9a1b 100644 (file)
@@ -48,6 +48,7 @@ struct gpfs_config_data {
        bool dfreequota;
        bool prealloc;
        bool acl;
+       bool settimes;
 };
 
 
@@ -1588,6 +1589,24 @@ static int vfs_gpfs_ntimes(struct vfs_handle_struct *handle,
                                struct gpfs_config_data,
                                return -1);
 
+       status = get_full_smb_filename(talloc_tos(), smb_fname, &path);
+       if (!NT_STATUS_IS_OK(status)) {
+               errno = map_errno_from_nt_status(status);
+               return -1;
+       }
+
+       /* Try to use gpfs_set_times if it is enabled and available */
+       if (config->settimes) {
+               ret = smbd_gpfs_set_times_path(path, ft);
+
+               if (ret == 0 || (ret == -1 && errno != ENOSYS)) {
+                       return ret;
+               }
+       }
+
+       DEBUG(10,("gpfs_set_times() not available or disabled, "
+                 "use ntimes and winattr\n"));
+
         ret = SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
         if(ret == -1){
                /* don't complain if access was denied */
@@ -1607,12 +1626,6 @@ static int vfs_gpfs_ntimes(struct vfs_handle_struct *handle,
                return 0;
        }
 
-        status = get_full_smb_filename(talloc_tos(), smb_fname, &path);
-        if (!NT_STATUS_IS_OK(status)) {
-                errno = map_errno_from_nt_status(status);
-                return -1;
-        }
-
         attrs.winAttrs = 0;
         attrs.creationTime.tv_sec = ft->create_time.tv_sec;
         attrs.creationTime.tv_nsec = ft->create_time.tv_nsec;
@@ -1795,6 +1808,9 @@ static int vfs_gpfs_connect(struct vfs_handle_struct *handle,
 
        config->acl = lp_parm_bool(SNUM(handle->conn), "gpfs", "acl", true);
 
+       config->settimes = lp_parm_bool(SNUM(handle->conn), "gpfs",
+                                       "settimes", true);
+
        SMB_VFS_HANDLE_SET_DATA(handle, config,
                                NULL, struct gpfs_config_data,
                                return -1);
index 70355e8738bd00502ca5f833089d0d39e64d6549..728231f146b64465af9eda81eb4d9e9d0e380867 100644 (file)
@@ -42,6 +42,7 @@ int smbd_gpfs_ftruncate(int fd, gpfs_off64_t length);
 int get_gpfs_quota(const char *pathname, int type, int id,
                   struct gpfs_quotaInfo *qi);
 int get_gpfs_fset_id(const char *pathname, int *fset_id);
+int smbd_gpfs_set_times_path(char *path, struct smb_file_time *ft);
 
 void init_gpfs(void);
 void smbd_gpfs_lib_init(void);