Add a gpfs_prefetch module
[samba.git] / source3 / modules / vfs_gpfs_prefetch.c
1 /*
2    Unix SMB/CIFS implementation.
3    Make use of gpfs prefetch functionality
4
5    Copyright (C) Volker Lendecke 2008
6
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.
11
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.
16
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/>.
19
20 */
21
22 #include "includes.h"
23
24 #undef DBGC_CLASS
25 #define DBGC_CLASS DBGC_VFS
26
27 #include <gpfs.h>
28 #include <gpfs_fcntl.h>
29
30 static int (*gpfs_fcntl_fn)(int fd, void *arg);
31
32 static int smbd_gpfs_fcntl(int fd, void *arg)
33 {
34         static void *libgpfs_handle = NULL;
35
36         DEBUG(10, ("smbd_gpfs_fcntl called for %d\n", fd));
37
38         if (gpfs_fcntl_fn == NULL) {
39                 libgpfs_handle = sys_dlopen("libgpfs.so", RTLD_LAZY);
40
41                 if (libgpfs_handle == NULL) {
42                         DEBUG(10, ("sys_dlopen for libgpfs failed: %s\n",
43                                    strerror(errno)));
44                         return;
45                 }
46
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;
53                 }
54         }
55
56         return gpfs_fcntl_fn(fd, arg);
57 }
58
59 struct gpfs_prefetch_config {
60         name_compare_entry *namelist;
61         size_t size;
62 };
63
64 struct gpfs_prefetch_hints {
65         blksize_t st_blksize;
66         /*
67          * The current center around which config->size bytes are
68          * prefetched
69          */
70         SMB_OFF_T center;
71 };
72
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)
77 {
78         int ret;
79         SMB_OFF_T new_center;
80
81         struct {
82                 gpfsFcntlHeader_t hdr;
83                 gpfsMultipleAccessRange_t acc;
84         } arg;
85
86
87         if (hints->st_blksize == 0) {
88                 SMB_STRUCT_STAT sbuf;
89
90                 if (SMB_VFS_NEXT_FSTAT(handle, fsp, &sbuf) == -1) {
91                         return;
92                 }
93                 DEBUG(10, ("gpfs_prefetch_recenter: st_blksize = %d\n",
94                            (int)sbuf.st_blksize));
95                 hints->st_blksize = sbuf.st_blksize;
96         }
97
98         new_center = (offset > size) ? offset : 0;
99
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));
103
104         ZERO_STRUCT(arg);
105
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;
113
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;
118
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;
123
124         ret = smbd_gpfs_fcntl(fsp->fh->fd, &arg);
125         if (ret == -1) {
126                 DEBUG(5, ("gpfs_fcntl returned %s\n", strerror(errno)));
127         }
128
129         hints->center = new_center;
130 }
131
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)
135 {
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;
141
142         /*
143          * How far away from the center of the prefetch region is the
144          * request?
145          */
146
147         out_of_center = (offset > hints->center)
148                 ? (offset - hints->center) : (hints->center - offset);
149
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));
154         /*
155          * Are we completely out of the prefetch range or less than
156          * 10% at its borders?
157          */
158
159         if ((out_of_center > config->size)
160             || ((config->size - out_of_center) * 10 < config->size)) {
161                 /*
162                  * Re-center the prefetch area
163                  */
164                 gpfs_prefetch_recenter(handle, fsp, offset, config->size,
165                                        hints);
166         }
167
168         return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
169 }
170
171 static int gpfs_prefetch_open(vfs_handle_struct *handle,  const char *fname,
172                               files_struct *fsp, int flags, mode_t mode)
173 {
174         int fd, ret;
175         struct gpfs_prefetch_hints *hints;
176         struct gpfs_prefetch_config *config =
177                 (struct gpfs_prefetch_config *)handle->data;
178
179         struct {
180                 gpfsFcntlHeader_t hdr;
181                 gpfsAccessRange_t acc;
182         } arg;
183
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));
187
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);
192         }
193
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;
199         }
200
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;
205         }
206
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;
215
216         ret = smbd_gpfs_fcntl(fd, &arg);
217         if (ret == -1) {
218                 DEBUG(5, ("gpfs_fcntl returned %s\n", strerror(errno)));
219         }
220
221         hints->st_blksize = 0;
222         hints->center = 0;
223
224         return fd;
225 }
226
227 static void gpfs_prefetch_config_free(void **data)
228 {
229         struct gpfs_prefetch_config **config =
230                 (struct gpfs_prefetch_config **)data;
231
232         free_namearray((*config)->namelist);
233         TALLOC_FREE(*config);
234 }
235
236 static int gpfs_prefetch_connect(struct vfs_handle_struct *handle,
237                                  const char *service,
238                                  const char *user)
239 {
240         struct gpfs_prefetch_config *config;
241         const char *mask;
242
243         config = talloc(handle, struct gpfs_prefetch_config);
244         if (config == NULL) {
245                 DEBUG(0, ("talloc failed\n"));
246                 errno = ENOMEM;
247                 return -1;
248         }
249
250         mask = lp_parm_const_string(SNUM(handle->conn), "gpfs_prefetch",
251                                     "mask", "");
252
253         set_namearray(&config->namelist, mask);
254         config->size = lp_parm_int(SNUM(handle->conn), "gpfs_prefetch",
255                                    "size", 1024);
256
257         /*
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.
262          */
263         config->size *= 1024/2;
264
265         SMB_VFS_HANDLE_SET_DATA(handle, config, gpfs_prefetch_config_free,
266                                 struct gpfs_prefetch_config, goto fail);
267
268         return SMB_VFS_NEXT_CONNECT(handle, service, user);
269
270 fail:
271         free_namearray(config->namelist);
272         TALLOC_FREE(config);
273         return -1;
274 }
275
276 /* VFS operations structure */
277
278 static vfs_op_tuple gpfs_prefetch_op_tuples[] = {
279
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 },
286
287         { SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP }
288 };
289
290 /*
291  * When done properly upstream (GPL issue resolved), change this
292  * routine name to vfs_gpfs_prefetch_init!!
293  */
294
295 NTSTATUS init_samba_module(void);
296 NTSTATUS init_samba_module(void)
297 {
298         NTSTATUS status;
299
300         DEBUG(10, ("vfs_gpfs_prefetch_init called\n"));
301
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)));
306
307         return status;
308 }