1 /* 2 * Copyright 2004-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <unistd.h> 8 #include <stdlib.h> 9 #include <string.h> 10 11 #include <KernelExport.h> 12 #include <fs_cache.h> 13 14 #include <condition_variable.h> 15 #include <file_cache.h> 16 #include <generic_syscall.h> 17 #include <util/AutoLock.h> 18 #include <util/kernel_cpp.h> 19 #include <vfs.h> 20 #include <vm.h> 21 #include <vm_page.h> 22 #include <vm_cache.h> 23 #include <vm_low_memory.h> 24 25 26 //#define TRACE_FILE_MAP 27 #ifdef TRACE_FILE_MAP 28 # define TRACE(x) dprintf x 29 #else 30 # define TRACE(x) ; 31 #endif 32 33 #define CACHED_FILE_EXTENTS 2 34 // must be smaller than MAX_FILE_IO_VECS 35 // ToDo: find out how much of these are typically used 36 37 struct file_extent { 38 off_t offset; 39 file_io_vec disk; 40 }; 41 42 struct file_map { 43 file_map(off_t size); 44 ~file_map(); 45 46 file_extent *operator[](uint32 index); 47 file_extent *ExtentAt(uint32 index); 48 status_t Add(file_io_vec *vecs, size_t vecCount, off_t &lastOffset); 49 void Free(); 50 51 union { 52 file_extent direct[CACHED_FILE_EXTENTS]; 53 file_extent *array; 54 }; 55 size_t count; 56 struct vnode *vnode; 57 off_t size; 58 }; 59 60 61 file_map::file_map(off_t _size) 62 { 63 array = NULL; 64 count = 0; 65 size = _size; 66 } 67 68 69 file_map::~file_map() 70 { 71 Free(); 72 } 73 74 75 file_extent * 76 file_map::operator[](uint32 index) 77 { 78 return ExtentAt(index); 79 } 80 81 82 file_extent * 83 file_map::ExtentAt(uint32 index) 84 { 85 if (index >= count) 86 return NULL; 87 88 if (count > CACHED_FILE_EXTENTS) 89 return &array[index]; 90 91 return &direct[index]; 92 } 93 94 95 status_t 96 file_map::Add(file_io_vec *vecs, size_t vecCount, off_t &lastOffset) 97 { 98 TRACE(("file_map::Add(vecCount = %ld)\n", vecCount)); 99 100 off_t offset = 0; 101 102 if (vecCount <= CACHED_FILE_EXTENTS && count == 0) { 103 // just use the reserved area in the file_cache_ref structure 104 } else { 105 // TODO: once we can invalidate only parts of the file map, 106 // we might need to copy the previously cached file extends 107 // from the direct range 108 file_extent *newMap = (file_extent *)realloc(array, 109 (count + vecCount) * sizeof(file_extent)); 110 if (newMap == NULL) 111 return B_NO_MEMORY; 112 113 array = newMap; 114 115 if (count != 0) { 116 file_extent *extent = ExtentAt(count - 1); 117 offset = extent->offset + extent->disk.length; 118 } 119 } 120 121 int32 start = count; 122 count += vecCount; 123 124 for (uint32 i = 0; i < vecCount; i++) { 125 file_extent *extent = ExtentAt(start + i); 126 127 extent->offset = offset; 128 extent->disk = vecs[i]; 129 130 offset += extent->disk.length; 131 } 132 133 #ifdef TRACE_FILE_MAP 134 for (uint32 i = 0; i < count; i++) { 135 file_extent *extent = ExtentAt(i); 136 dprintf("[%ld] extend offset %Ld, disk offset %Ld, length %Ld\n", 137 i, extent->offset, extent->disk.offset, extent->disk.length); 138 } 139 #endif 140 141 lastOffset = offset; 142 return B_OK; 143 } 144 145 146 void 147 file_map::Free() 148 { 149 if (count > CACHED_FILE_EXTENTS) 150 free(array); 151 152 array = NULL; 153 count = 0; 154 } 155 156 157 // #pragma mark - 158 159 160 static file_extent * 161 find_file_extent(file_map &map, off_t offset, uint32 *_index) 162 { 163 // TODO: do binary search 164 165 for (uint32 index = 0; index < map.count; index++) { 166 file_extent *extent = map[index]; 167 168 if (extent->offset <= offset 169 && extent->offset + extent->disk.length > offset) { 170 if (_index) 171 *_index = index; 172 return extent; 173 } 174 } 175 176 return NULL; 177 } 178 179 180 // #pragma mark - public FS API 181 182 183 extern "C" void * 184 file_map_create(dev_t mountID, ino_t vnodeID, off_t size) 185 { 186 TRACE(("file_map_create(mountID = %ld, vnodeID = %Ld, size = %Ld)\n", 187 mountID, vnodeID, size)); 188 189 file_map *map = new file_map(size); 190 if (map == NULL) 191 return NULL; 192 193 // Get the vnode for the object 194 // (note, this does not grab a reference to the node) 195 if (vfs_lookup_vnode(mountID, vnodeID, &map->vnode) != B_OK) { 196 delete map; 197 return NULL; 198 } 199 200 return map; 201 } 202 203 204 extern "C" void 205 file_map_delete(void *_map) 206 { 207 file_map *map = (file_map *)_map; 208 if (map == NULL) 209 return; 210 211 TRACE(("file_map_delete(map = %p)\n", map)); 212 delete map; 213 } 214 215 216 extern "C" void 217 file_map_set_size(void *_map, off_t size) 218 { 219 if (_map == NULL) 220 return; 221 222 // TODO: honour offset/size parameters 223 file_map *map = (file_map *)_map; 224 // if (size < map->size) 225 map->Free(); 226 map->size = size; 227 } 228 229 230 extern "C" void 231 file_map_invalidate(void *_map, off_t offset, off_t size) 232 { 233 if (_map == NULL) 234 return; 235 236 // TODO: honour offset/size parameters 237 file_map *map = (file_map *)_map; 238 map->Free(); 239 } 240 241 242 extern "C" status_t 243 file_map_translate(void *_map, off_t offset, size_t size, file_io_vec *vecs, 244 size_t *_count) 245 { 246 if (_map == NULL) 247 return B_BAD_VALUE; 248 249 TRACE(("file_map_translate(map %p, offset %Ld, size %ld)\n", 250 _map, offset, size)); 251 252 file_map &map = *(file_map *)_map; 253 size_t maxVecs = *_count; 254 status_t status = B_OK; 255 256 if (offset >= map.size) { 257 *_count = 0; 258 return B_OK; 259 } 260 if (offset + size > map.size) 261 size = map.size - offset; 262 263 if (map.count == 0) { 264 // we don't yet have the map of this file, so let's grab it 265 // (ordered by offset, so that we can do a binary search on them) 266 size_t vecCount = maxVecs; 267 off_t mapOffset = 0; 268 269 while (true) { 270 status = vfs_get_file_map(map.vnode, mapOffset, ~0UL, vecs, 271 &vecCount); 272 if (status < B_OK && status != B_BUFFER_OVERFLOW) 273 return status; 274 275 status_t addStatus = map.Add(vecs, vecCount, mapOffset); 276 if (addStatus != B_OK) { 277 // only clobber the status in case of failure 278 status = addStatus; 279 } 280 281 if (status != B_BUFFER_OVERFLOW) 282 break; 283 284 // when we are here, the map has been stored in the array, and 285 // the array size was still too small to cover the whole file 286 vecCount = maxVecs; 287 } 288 } 289 290 if (status != B_OK) { 291 // We must invalidate the (part of the) map we already 292 // have, as we cannot know if it's complete or not 293 map.Free(); 294 return status; 295 } 296 297 // We now have cached the map of this file, we now need to 298 // translate it for the requested access. 299 300 uint32 index; 301 file_extent *fileExtent = find_file_extent(map, offset, &index); 302 if (fileExtent == NULL) { 303 // access outside file bounds? But that's not our problem 304 *_count = 0; 305 return B_OK; 306 } 307 308 offset -= fileExtent->offset; 309 vecs[0].offset = fileExtent->disk.offset + offset; 310 vecs[0].length = fileExtent->disk.length - offset; 311 312 if (vecs[0].length >= size || index >= map.count - 1) { 313 *_count = 1; 314 return B_OK; 315 } 316 317 // copy the rest of the vecs 318 319 size -= vecs[0].length; 320 321 for (index = 1; index < map.count;) { 322 fileExtent++; 323 324 vecs[index] = fileExtent->disk; 325 index++; 326 327 if (size <= fileExtent->disk.length) 328 break; 329 330 if (index >= maxVecs) { 331 *_count = index; 332 return B_BUFFER_OVERFLOW; 333 } 334 335 size -= fileExtent->disk.length; 336 } 337 338 *_count = index; 339 return B_OK; 340 } 341 342