xref: /haiku/src/system/kernel/cache/file_map.cpp (revision 020cbad9d40235a2c50a81a42d69912a5ff8fbc4)
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