Add a gpfs_prefetch module
[Samba.git] / source / modules / vfs_gpfs_prefetch.c
blob77f265fc2421b70647e86f223d9514a50fa10c3d
1 /*
2 Unix SMB/CIFS implementation.
3 Make use of gpfs prefetch functionality
5 Copyright (C) Volker Lendecke 2008
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
24 #undef DBGC_CLASS
25 #define DBGC_CLASS DBGC_VFS
27 #include <gpfs.h>
28 #include <gpfs_fcntl.h>
30 static int (*gpfs_fcntl_fn)(int fd, void *arg);
32 static int smbd_gpfs_fcntl(int fd, void *arg)
34 static void *libgpfs_handle = NULL;
36 DEBUG(10, ("smbd_gpfs_fcntl called for %d\n", fd));
38 if (gpfs_fcntl_fn == NULL) {
39 libgpfs_handle = sys_dlopen("libgpfs.so", RTLD_LAZY);
41 if (libgpfs_handle == NULL) {
42 DEBUG(10, ("sys_dlopen for libgpfs failed: %s\n",
43 strerror(errno)));
44 return;
47 gpfs_fcntl_fn = sys_dlsym(libgpfs_handle, "gpfs_fcntl");
48 if (gpfs_fcntl_fn == NULL) {
49 DEBUG(3, ("libgpfs.so does not contain the symbol "
50 "'gpfs_fcntl'\n"));
51 errno = ENOSYS;
52 return -1;
56 return gpfs_fcntl_fn(fd, arg);
59 struct gpfs_prefetch_config {
60 name_compare_entry *namelist;
61 size_t size;
64 struct gpfs_prefetch_hints {
65 blksize_t st_blksize;
67 * The current center around which config->size bytes are
68 * prefetched
70 SMB_OFF_T center;
73 static void gpfs_prefetch_recenter(vfs_handle_struct *handle,
74 files_struct *fsp,
75 SMB_OFF_T offset, size_t size,
76 struct gpfs_prefetch_hints *hints)
78 int ret;
79 SMB_OFF_T new_center;
81 struct {
82 gpfsFcntlHeader_t hdr;
83 gpfsMultipleAccessRange_t acc;
84 } arg;
87 if (hints->st_blksize == 0) {
88 SMB_STRUCT_STAT sbuf;
90 if (SMB_VFS_NEXT_FSTAT(handle, fsp, &sbuf) == -1) {
91 return;
93 DEBUG(10, ("gpfs_prefetch_recenter: st_blksize = %d\n",
94 (int)sbuf.st_blksize));
95 hints->st_blksize = sbuf.st_blksize;
98 new_center = (offset > size) ? offset : 0;
100 DEBUG(10, ("gpfs_prefetch_recenter: size=%d, offset=%d, "
101 "old_center=%d, new_center=%d\n", (int)size, (int)offset,
102 (int)hints->center, (int)new_center));
104 ZERO_STRUCT(arg);
106 arg.hdr.totalLength = sizeof(arg);
107 arg.hdr.fcntlVersion = GPFS_FCNTL_CURRENT_VERSION;
108 arg.hdr.fcntlReserved = 0;
109 arg.acc.structLen = sizeof(arg.acc);
110 arg.acc.structType = GPFS_MULTIPLE_ACCESS_RANGE;
111 arg.acc.accRangeCnt = 1;
112 arg.acc.relRangeCnt = 1;
114 arg.acc.accRangeArray[0].blockNumber = new_center/hints->st_blksize;
115 arg.acc.accRangeArray[0].start = 0;
116 arg.acc.accRangeArray[0].length = size;
117 arg.acc.accRangeArray[0].isWrite = 0;
119 arg.acc.relRangeArray[0].blockNumber = hints->center/hints->st_blksize;
120 arg.acc.relRangeArray[0].start = 0;
121 arg.acc.relRangeArray[0].length = size;
122 arg.acc.relRangeArray[0].isWrite = 0;
124 ret = smbd_gpfs_fcntl(fsp->fh->fd, &arg);
125 if (ret == -1) {
126 DEBUG(5, ("gpfs_fcntl returned %s\n", strerror(errno)));
129 hints->center = new_center;
132 static ssize_t gpfs_prefetch_pread(vfs_handle_struct *handle,
133 files_struct *fsp, void *data,
134 size_t n, SMB_OFF_T offset)
136 struct gpfs_prefetch_config *config =
137 (struct gpfs_prefetch_config *)handle->data;
138 struct gpfs_prefetch_hints *hints = (struct gpfs_prefetch_hints *)
139 VFS_FETCH_FSP_EXTENSION(handle, fsp);
140 SMB_OFF_T out_of_center;
143 * How far away from the center of the prefetch region is the
144 * request?
147 out_of_center = (offset > hints->center)
148 ? (offset - hints->center) : (hints->center - offset);
150 DEBUG(10, ("gpfs_prefetch_pread: n=%d, offset=%d, center=%d, "
151 "out_of_center=%d, size=%d\n", (int)n, (int)offset,
152 (int)hints->center, (int)out_of_center,
153 (int)config->size));
155 * Are we completely out of the prefetch range or less than
156 * 10% at its borders?
159 if ((out_of_center > config->size)
160 || ((config->size - out_of_center) * 10 < config->size)) {
162 * Re-center the prefetch area
164 gpfs_prefetch_recenter(handle, fsp, offset, config->size,
165 hints);
168 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
171 static int gpfs_prefetch_open(vfs_handle_struct *handle, const char *fname,
172 files_struct *fsp, int flags, mode_t mode)
174 int fd, ret;
175 struct gpfs_prefetch_hints *hints;
176 struct gpfs_prefetch_config *config =
177 (struct gpfs_prefetch_config *)handle->data;
179 struct {
180 gpfsFcntlHeader_t hdr;
181 gpfsAccessRange_t acc;
182 } arg;
184 DEBUG(10, ("gpfs_prefetch_open called for %s, config=%p, "
185 "config->namelist = %p, config->size=%d\n", fname,
186 config, config->namelist, (int)config->size));
188 if (!is_in_path(fname, config->namelist,
189 handle->conn->case_sensitive)) {
190 DEBUG(10, ("gpfs_prefetch_open not in list: %s\n", fname));
191 return SMB_VFS_NEXT_OPEN(handle, fname, fsp, flags, mode);
194 hints = (struct gpfs_prefetch_hints *)VFS_ADD_FSP_EXTENSION(
195 handle, fsp, struct gpfs_prefetch_hints);
196 if (hints == NULL) {
197 errno = ENOMEM;
198 return -1;
201 fd = SMB_VFS_NEXT_OPEN(handle, fname, fsp, flags, mode);
202 if (fd == -1) {
203 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
204 return -1;
207 arg.hdr.totalLength = sizeof(arg);
208 arg.hdr.fcntlVersion = GPFS_FCNTL_CURRENT_VERSION;
209 arg.hdr.fcntlReserved = 0;
210 arg.acc.structLen = sizeof(arg.acc);
211 arg.acc.structType = GPFS_ACCESS_RANGE;
212 arg.acc.start = 0;
213 arg.acc.length = 1;
214 arg.acc.isWrite = 0;
216 ret = smbd_gpfs_fcntl(fd, &arg);
217 if (ret == -1) {
218 DEBUG(5, ("gpfs_fcntl returned %s\n", strerror(errno)));
221 hints->st_blksize = 0;
222 hints->center = 0;
224 return fd;
227 static void gpfs_prefetch_config_free(void **data)
229 struct gpfs_prefetch_config **config =
230 (struct gpfs_prefetch_config **)data;
232 free_namearray((*config)->namelist);
233 TALLOC_FREE(*config);
236 static int gpfs_prefetch_connect(struct vfs_handle_struct *handle,
237 const char *service,
238 const char *user)
240 struct gpfs_prefetch_config *config;
241 const char *mask;
243 config = talloc(handle, struct gpfs_prefetch_config);
244 if (config == NULL) {
245 DEBUG(0, ("talloc failed\n"));
246 errno = ENOMEM;
247 return -1;
250 mask = lp_parm_const_string(SNUM(handle->conn), "gpfs_prefetch",
251 "mask", "");
253 set_namearray(&config->namelist, mask);
254 config->size = lp_parm_int(SNUM(handle->conn), "gpfs_prefetch",
255 "size", 1024);
258 * The size calculations in the core routines assume that
259 * config->size is the size from the center to the border of
260 * the prefetched area. So we need to multiply by 1024/2 here
261 * to get the whole prefetch area in kilobytes.
263 config->size *= 1024/2;
265 SMB_VFS_HANDLE_SET_DATA(handle, config, gpfs_prefetch_config_free,
266 struct gpfs_prefetch_config, goto fail);
268 return SMB_VFS_NEXT_CONNECT(handle, service, user);
270 fail:
271 free_namearray(config->namelist);
272 TALLOC_FREE(config);
273 return -1;
276 /* VFS operations structure */
278 static vfs_op_tuple gpfs_prefetch_op_tuples[] = {
280 {SMB_VFS_OP(gpfs_prefetch_open), SMB_VFS_OP_OPEN,
281 SMB_VFS_LAYER_TRANSPARENT },
282 {SMB_VFS_OP(gpfs_prefetch_pread), SMB_VFS_OP_PREAD,
283 SMB_VFS_LAYER_TRANSPARENT },
284 {SMB_VFS_OP(gpfs_prefetch_connect), SMB_VFS_OP_CONNECT,
285 SMB_VFS_LAYER_TRANSPARENT },
287 { SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP }
291 * When done properly upstream (GPL issue resolved), change this
292 * routine name to vfs_gpfs_prefetch_init!!
295 NTSTATUS init_samba_module(void);
296 NTSTATUS init_samba_module(void)
298 NTSTATUS status;
300 DEBUG(10, ("vfs_gpfs_prefetch_init called\n"));
302 status = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "gpfs_prefetch",
303 gpfs_prefetch_op_tuples);
304 DEBUG(10, ("smb_register_vfs returned %s\n",
305 nt_errstr(status)));
307 return status;