1/* 2 * Copyright (c) Jeremy Allison 2007. 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, see <http://www.gnu.org/licenses/>. 16 */ 17 18#include "includes.h" 19 20struct readahead_data { 21 SMB_OFF_T off_bound; 22 SMB_OFF_T len; 23 bool didmsg; 24}; 25 26/* 27 * This module copes with Vista AIO read requests on Linux 28 * by detecting the initial 0x80000 boundary reads and causing 29 * the buffer cache to be filled in advance. 30 */ 31 32/******************************************************************* 33 sendfile wrapper that does readahead/posix_fadvise. 34*******************************************************************/ 35 36static ssize_t readahead_sendfile(struct vfs_handle_struct *handle, 37 int tofd, 38 files_struct *fromfsp, 39 const DATA_BLOB *header, 40 SMB_OFF_T offset, 41 size_t count) 42{ 43 struct readahead_data *rhd = (struct readahead_data *)handle->data; 44 45 if ( offset % rhd->off_bound == 0) { 46#if defined(HAVE_LINUX_READAHEAD) 47 int err = readahead(fromfsp->fh->fd, offset, (size_t)rhd->len); 48 DEBUG(10,("readahead_sendfile: readahead on fd %u, offset %llu, len %u returned %d\n", 49 (unsigned int)fromfsp->fh->fd, 50 (unsigned long long)offset, 51 (unsigned int)rhd->len, 52 err )); 53#elif defined(HAVE_POSIX_FADVISE) 54 int err = posix_fadvise(fromfsp->fh->fd, offset, (off_t)rhd->len, POSIX_FADV_WILLNEED); 55 DEBUG(10,("readahead_sendfile: posix_fadvise on fd %u, offset %llu, len %u returned %d\n", 56 (unsigned int)fromfsp->fh->fd, 57 (unsigned long long)offset, 58 (unsigned int)rhd->len, 59 err )); 60#else 61 if (!rhd->didmsg) { 62 DEBUG(0,("readahead_sendfile: no readahead on this platform\n")); 63 rhd->didmsg = True; 64 } 65#endif 66 } 67 return SMB_VFS_NEXT_SENDFILE(handle, 68 tofd, 69 fromfsp, 70 header, 71 offset, 72 count); 73} 74 75/******************************************************************* 76 pread wrapper that does readahead/posix_fadvise. 77*******************************************************************/ 78 79static ssize_t readahead_pread(vfs_handle_struct *handle, 80 files_struct *fsp, 81 void *data, 82 size_t count, 83 SMB_OFF_T offset) 84{ 85 struct readahead_data *rhd = (struct readahead_data *)handle->data; 86 87 if ( offset % rhd->off_bound == 0) { 88#if defined(HAVE_LINUX_READAHEAD) 89 int err = readahead(fsp->fh->fd, offset, (size_t)rhd->len); 90 DEBUG(10,("readahead_pread: readahead on fd %u, offset %llu, len %u returned %d\n", 91 (unsigned int)fsp->fh->fd, 92 (unsigned long long)offset, 93 (unsigned int)rhd->len, 94 err )); 95#elif defined(HAVE_POSIX_FADVISE) 96 int err = posix_fadvise(fsp->fh->fd, offset, (off_t)rhd->len, POSIX_FADV_WILLNEED); 97 DEBUG(10,("readahead_pread: posix_fadvise on fd %u, offset %llu, len %u returned %d\n", 98 (unsigned int)fsp->fh->fd, 99 (unsigned long long)offset, 100 (unsigned int)rhd->len, 101 err )); 102#else 103 if (!rhd->didmsg) { 104 DEBUG(0,("readahead_pread: no readahead on this platform\n")); 105 rhd->didmsg = True; 106 } 107#endif 108 } 109 return SMB_VFS_NEXT_PREAD(handle, fsp, data, count, offset); 110} 111 112/******************************************************************* 113 Directly called from main smbd when freeing handle. 114*******************************************************************/ 115 116static void free_readahead_data(void **pptr) 117{ 118 SAFE_FREE(*pptr); 119} 120 121/******************************************************************* 122 Allocate the handle specific data so we don't call the expensive 123 conv_str_size function for each sendfile/pread. 124*******************************************************************/ 125 126static int readahead_connect(struct vfs_handle_struct *handle, 127 const char *service, 128 const char *user) 129{ 130 struct readahead_data *rhd; 131 int ret = SMB_VFS_NEXT_CONNECT(handle, service, user); 132 133 if (ret < 0) { 134 return ret; 135 } 136 rhd = SMB_MALLOC_P(struct readahead_data); 137 if (!rhd) { 138 SMB_VFS_NEXT_DISCONNECT(handle); 139 DEBUG(0,("readahead_connect: out of memory\n")); 140 return -1; 141 } 142 ZERO_STRUCTP(rhd); 143 144 rhd->didmsg = False; 145 rhd->off_bound = conv_str_size(lp_parm_const_string(SNUM(handle->conn), 146 "readahead", 147 "offset", 148 NULL)); 149 if (rhd->off_bound == 0) { 150 rhd->off_bound = 0x80000; 151 } 152 rhd->len = conv_str_size(lp_parm_const_string(SNUM(handle->conn), 153 "readahead", 154 "length", 155 NULL)); 156 if (rhd->len == 0) { 157 rhd->len = rhd->off_bound; 158 } 159 160 handle->data = (void *)rhd; 161 handle->free_data = free_readahead_data; 162 return 0; 163} 164 165static struct vfs_fn_pointers vfs_readahead_fns = { 166 .sendfile = readahead_sendfile, 167 .pread = readahead_pread, 168 .connect_fn = readahead_connect 169}; 170 171/******************************************************************* 172 Module initialization boilerplate. 173*******************************************************************/ 174 175NTSTATUS vfs_readahead_init(void); 176NTSTATUS vfs_readahead_init(void) 177{ 178 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "readahead", 179 &vfs_readahead_fns); 180} 181