v3-6-ctdb: s3:vfs: Add a gpfs_prefetch module.
authorVolker Lendecke <vl@samba.org>
Fri, 21 Jan 2011 13:07:15 +0000 (14:07 +0100)
committerMichael Adam <obnox@samba.org>
Thu, 13 Oct 2011 15:24:43 +0000 (17:24 +0200)
This can not go upstream yet because it uses the gpfs_fcntl call
from the non-GPL libgpfs.

source3/Makefile.in
source3/configure.in
source3/modules/vfs_gpfs_prefetch.c [new file with mode: 0644]

index c5ec5891c94c2473506052d1a4da72f5a09ba55f..55f0141ccc1f86e90c99e4358d997dbae8240fac 100644 (file)
@@ -824,6 +824,7 @@ VFS_PREALLOC_OBJ = modules/vfs_prealloc.o
 VFS_COMMIT_OBJ = modules/vfs_commit.o
 VFS_GPFS_OBJ = modules/vfs_gpfs.o modules/gpfs.o modules/nfs4_acls.o
 VFS_GPFS_HSM_NOTIFY_OBJ = modules/vfs_gpfs_hsm_notify.o
+VFS_GPFS_PREFETCH_OBJ = modules/vfs_gpfs_prefetch.o modules/gpfs.o
 VFS_NOTIFY_FAM_OBJ = modules/vfs_notify_fam.o
 VFS_READAHEAD_OBJ = modules/vfs_readahead.o
 VFS_TSMSM_OBJ = modules/vfs_tsmsm.o
@@ -3011,6 +3012,10 @@ bin/gpfs_hsm_notify.@SHLIBEXT@: $(BINARY_PREREQS) $(VFS_GPFS_HSM_NOTIFY_OBJ)
        @echo "Building plugin $@"
        @$(SHLD_MODULE) $(VFS_GPFS_HSM_NOTIFY_OBJ)
 
+bin/gpfs_prefetch.@SHLIBEXT@: $(BINARY_PREREQS) $(VFS_GPFS_PREFETCH_OBJ)
+       @echo "Building plugin $@"
+       @$(SHLD_MODULE) $(VFS_GPFS_PREFETCH_OBJ)
+
 bin/notify_fam.@SHLIBEXT@: $(BINARY_PREREQS) $(VFS_NOTIFY_FAM_OBJ)
        @echo "Building plugin $@"
        @$(SHLD_MODULE) $(VFS_NOTIFY_FAM_OBJ) @SMB_FAM_LIBS@
index 9b49c96c60109c7b0a309b35e63a5b065b9db464..f8884979122a98a8db8d4d4129e3fe8fe9ea027b 100644 (file)
@@ -1128,6 +1128,7 @@ if test x"$ac_cv_header_gpfs_gpl_h" = x"yes"; then
     AC_DEFINE(HAVE_GPFS,1,[Whether GPFS GPL headers are available])
     default_shared_modules="$default_shared_modules vfs_gpfs"
     default_shared_modules="$default_shared_modules vfs_gpfs_hsm_notify"
+    default_shared_modules="$default_shared_modules vfs_gpfs_prefetch"
 fi
 
 #############################################
@@ -6895,6 +6896,7 @@ SMB_MODULE(vfs_prealloc, \$(VFS_PREALLOC_OBJ), "bin/prealloc.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_commit, \$(VFS_COMMIT_OBJ), "bin/commit.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_gpfs, \$(VFS_GPFS_OBJ), "bin/gpfs.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_gpfs_hsm_notify, \$(VFS_GPFS_PREFETCH_OBJ), "bin/gpfs_hsm_notify.$SHLIBEXT", VFS)
+SMB_MODULE(vfs_gpfs_prefetch, \$(VFS_GPFS_PREFETCH_OBJ), "bin/gpfs_prefetch.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_readahead, \$(VFS_READAHEAD_OBJ), "bin/readahead.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_tsmsm, \$(VFS_TSMSM_OBJ), "bin/tsmsm.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_fileid, \$(VFS_FILEID_OBJ), "bin/fileid.$SHLIBEXT", VFS)
diff --git a/source3/modules/vfs_gpfs_prefetch.c b/source3/modules/vfs_gpfs_prefetch.c
new file mode 100644 (file)
index 0000000..9d79b20
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+   Unix SMB/CIFS implementation.
+   Make use of gpfs prefetch functionality
+
+   Copyright (C) Volker Lendecke 2008
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "smbd/proto.h"
+#include "include/smbprofile.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_VFS
+
+#include <gpfs.h>
+#include <gpfs_fcntl.h>
+
+static int (*gpfs_fcntl_fn)(int fd, void *arg);
+
+static int smbd_gpfs_fcntl(int fd, void *arg)
+{
+       static void *libgpfs_handle = NULL;
+
+       DEBUG(10, ("smbd_gpfs_fcntl called for %d\n", fd));
+
+       if (gpfs_fcntl_fn == NULL) {
+               libgpfs_handle = dlopen("libgpfs.so", RTLD_LAZY);
+
+               if (libgpfs_handle == NULL) {
+                       DEBUG(10, ("sys_dlopen for libgpfs failed: %s\n",
+                                  strerror(errno)));
+                       return -1;
+               }
+
+               gpfs_fcntl_fn = dlsym(libgpfs_handle, "gpfs_fcntl");
+               if (gpfs_fcntl_fn == NULL) {
+                       DEBUG(3, ("libgpfs.so does not contain the symbol "
+                                 "'gpfs_fcntl'\n"));
+                       errno = ENOSYS;
+                       return -1;
+               }
+       }
+
+       return gpfs_fcntl_fn(fd, arg);
+}
+
+struct gpfs_prefetch_config {
+       name_compare_entry *namelist;
+       size_t size;
+};
+
+struct gpfs_prefetch_hints {
+       blksize_t st_blksize;
+       /*
+        * The current center around which config->size bytes are
+        * prefetched
+        */
+       SMB_OFF_T center;
+};
+
+static void gpfs_prefetch_recenter(vfs_handle_struct *handle,
+                                  files_struct *fsp,
+                                  SMB_OFF_T offset, size_t size,
+                                  struct gpfs_prefetch_hints *hints)
+{
+       int ret;
+       SMB_OFF_T new_center;
+
+       struct {
+               gpfsFcntlHeader_t hdr;
+               gpfsMultipleAccessRange_t acc;
+       } arg;
+
+
+       if (hints->st_blksize == 0) {
+               SMB_STRUCT_STAT sbuf;
+
+               if (SMB_VFS_NEXT_FSTAT(handle, fsp, &sbuf) == -1) {
+                       return;
+               }
+               DEBUG(10, ("gpfs_prefetch_recenter: st_blksize = %d\n",
+                          (int)sbuf.st_ex_blksize));
+               hints->st_blksize = sbuf.st_ex_blksize;
+       }
+
+       new_center = (offset > size) ? offset : 0;
+
+       DEBUG(10, ("gpfs_prefetch_recenter: size=%d, offset=%d, "
+                  "old_center=%d, new_center=%d\n", (int)size, (int)offset,
+                  (int)hints->center, (int)new_center));
+
+       ZERO_STRUCT(arg);
+
+       arg.hdr.totalLength = sizeof(arg);
+       arg.hdr.fcntlVersion = GPFS_FCNTL_CURRENT_VERSION;
+       arg.hdr.fcntlReserved = 0;
+       arg.acc.structLen = sizeof(arg.acc);
+       arg.acc.structType = GPFS_MULTIPLE_ACCESS_RANGE;
+       arg.acc.accRangeCnt = 1;
+       arg.acc.relRangeCnt = 1;
+
+       arg.acc.accRangeArray[0].blockNumber = new_center/hints->st_blksize;
+       arg.acc.accRangeArray[0].start = 0;
+       arg.acc.accRangeArray[0].length = size;
+       arg.acc.accRangeArray[0].isWrite = 0;
+
+       arg.acc.relRangeArray[0].blockNumber = hints->center/hints->st_blksize;
+       arg.acc.relRangeArray[0].start = 0;
+       arg.acc.relRangeArray[0].length = size;
+       arg.acc.relRangeArray[0].isWrite = 0;
+
+       ret = smbd_gpfs_fcntl(fsp->fh->fd, &arg);
+       if (ret == -1) {
+               DEBUG(5, ("gpfs_fcntl returned %s\n", strerror(errno)));
+       }
+
+       hints->center = new_center;
+}
+
+static ssize_t gpfs_prefetch_pread(vfs_handle_struct *handle,
+                                  files_struct *fsp, void *data,
+                                  size_t n, SMB_OFF_T offset)
+{
+       struct gpfs_prefetch_config *config =
+               (struct gpfs_prefetch_config *)handle->data;
+       struct gpfs_prefetch_hints *hints = (struct gpfs_prefetch_hints *)
+               VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       SMB_OFF_T out_of_center;
+
+       /*
+        * How far away from the center of the prefetch region is the
+        * request?
+        */
+
+       out_of_center = (offset > hints->center)
+               ? (offset - hints->center) : (hints->center - offset);
+
+       DEBUG(10, ("gpfs_prefetch_pread: n=%d, offset=%d, center=%d, "
+                  "out_of_center=%d, size=%d\n", (int)n, (int)offset,
+                  (int)hints->center, (int)out_of_center,
+                  (int)config->size));
+       /*
+        * Are we completely out of the prefetch range or less than
+        * 10% at its borders?
+        */
+
+       if ((out_of_center > config->size)
+           || ((config->size - out_of_center) * 10 < config->size)) {
+               /*
+                * Re-center the prefetch area
+                */
+               gpfs_prefetch_recenter(handle, fsp, offset, config->size,
+                                      hints);
+       }
+
+       return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
+}
+
+static int gpfs_prefetch_open(struct vfs_handle_struct *handle,
+                             struct smb_filename *smb_fname,
+                             files_struct *fsp,
+                             int flags, mode_t mode)
+{
+       int fd, ret;
+       struct gpfs_prefetch_hints *hints;
+       struct gpfs_prefetch_config *config =
+               (struct gpfs_prefetch_config *)handle->data;
+
+       struct {
+               gpfsFcntlHeader_t hdr;
+               gpfsAccessRange_t acc;
+       } arg;
+
+       DEBUG(10, ("gpfs_prefetch_open called for %s, config=%p, "
+                  "config->namelist = %p, config->size=%d\n",
+                  smb_fname->base_name,
+                  config, config->namelist, (int)config->size));
+
+       if (!is_in_path(smb_fname->base_name, config->namelist,
+                       handle->conn->case_sensitive)) {
+               DEBUG(10, ("gpfs_prefetch_open not in list: %s\n",
+                          smb_fname->base_name));
+               return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags,
+                                        mode);
+       }
+
+       hints = (struct gpfs_prefetch_hints *)VFS_ADD_FSP_EXTENSION(
+               handle, fsp, struct gpfs_prefetch_hints, NULL);
+       if (hints == NULL) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
+       if (fd == -1) {
+               VFS_REMOVE_FSP_EXTENSION(handle, fsp);
+               return -1;
+       }
+
+       arg.hdr.totalLength = sizeof(arg);
+       arg.hdr.fcntlVersion = GPFS_FCNTL_CURRENT_VERSION;
+       arg.hdr.fcntlReserved = 0;
+       arg.acc.structLen = sizeof(arg.acc);
+       arg.acc.structType = GPFS_ACCESS_RANGE;
+       arg.acc.start = 0;
+       arg.acc.length = 1;
+       arg.acc.isWrite = 0;
+
+       ret = smbd_gpfs_fcntl(fd, &arg);
+       if (ret == -1) {
+               DEBUG(5, ("gpfs_fcntl returned %s\n", strerror(errno)));
+       }
+
+       hints->st_blksize = 0;
+       hints->center = 0;
+
+       return fd;
+}
+
+static void gpfs_prefetch_config_free(void **data)
+{
+       struct gpfs_prefetch_config **config =
+               (struct gpfs_prefetch_config **)data;
+
+       free_namearray((*config)->namelist);
+       TALLOC_FREE(*config);
+}
+
+static int gpfs_prefetch_connect(struct vfs_handle_struct *handle,
+                                const char *service,
+                                const char *user)
+{
+       struct gpfs_prefetch_config *config;
+       const char *mask;
+
+       config = talloc(handle, struct gpfs_prefetch_config);
+       if (config == NULL) {
+               DEBUG(0, ("talloc failed\n"));
+               errno = ENOMEM;
+               return -1;
+       }
+
+       mask = lp_parm_const_string(SNUM(handle->conn), "gpfs_prefetch",
+                                   "mask", "");
+
+       set_namearray(&config->namelist, mask);
+       config->size = lp_parm_int(SNUM(handle->conn), "gpfs_prefetch",
+                                  "size", 1024);
+
+       /*
+        * The size calculations in the core routines assume that
+        * config->size is the size from the center to the border of
+        * the prefetched area. So we need to multiply by 1024/2 here
+        * to get the whole prefetch area in kilobytes.
+        */
+       config->size *= 1024/2;
+
+       SMB_VFS_HANDLE_SET_DATA(handle, config, gpfs_prefetch_config_free,
+                               struct gpfs_prefetch_config, goto fail);
+
+       return SMB_VFS_NEXT_CONNECT(handle, service, user);
+
+fail:
+       free_namearray(config->namelist);
+       TALLOC_FREE(config);
+       return -1;
+}
+
+/* VFS operations structure */
+
+static struct vfs_fn_pointers gpfs_prefetch_fns = {
+       .open_fn = gpfs_prefetch_open,
+       .pread = gpfs_prefetch_pread,
+       .connect_fn = gpfs_prefetch_connect,
+};
+
+/*
+ * When done properly upstream (GPL issue resolved), change this
+ * routine name to vfs_gpfs_prefetch_init!!
+ */
+
+NTSTATUS init_samba_module(void);
+NTSTATUS init_samba_module(void)
+{
+       NTSTATUS status;
+
+       DEBUG(10, ("vfs_gpfs_prefetch_init called\n"));
+
+       status = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "gpfs_prefetch",
+                                 &gpfs_prefetch_fns);
+       DEBUG(10, ("smb_register_vfs returned %s\n",
+                  nt_errstr(status)));
+
+       return status;
+}