xref: /haiku/src/system/kernel/cache/file_cache.cpp (revision 5f4e982ea8d3ef2d3fb72ec92482de8ab0c632a9)
1 /*
2  * Copyright 2004-2009, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "vnode_store.h"
8 
9 #include <unistd.h>
10 #include <stdlib.h>
11 #include <string.h>
12 
13 #include <KernelExport.h>
14 #include <fs_cache.h>
15 
16 #include <condition_variable.h>
17 #include <file_cache.h>
18 #include <generic_syscall.h>
19 #include <low_resource_manager.h>
20 #include <thread.h>
21 #include <util/AutoLock.h>
22 #include <util/kernel_cpp.h>
23 #include <vfs.h>
24 #include <vm/vm.h>
25 #include <vm/vm_page.h>
26 #include <vm/VMCache.h>
27 
28 #include "IORequest.h"
29 
30 
31 //#define TRACE_FILE_CACHE
32 #ifdef TRACE_FILE_CACHE
33 #	define TRACE(x) dprintf x
34 #else
35 #	define TRACE(x) ;
36 #endif
37 
38 // maximum number of iovecs per request
39 #define MAX_IO_VECS			32	// 128 kB
40 
41 #define BYPASS_IO_SIZE		65536
42 #define LAST_ACCESSES		3
43 
44 struct file_cache_ref {
45 	VMCache			*cache;
46 	struct vnode	*vnode;
47 	off_t			last_access[LAST_ACCESSES];
48 		// TODO: it would probably be enough to only store the least
49 		//	significant 31 bits, and make this uint32 (one bit for
50 		//	write vs. read)
51 	int32			last_access_index;
52 	uint16			disabled_count;
53 
SetLastAccessfile_cache_ref54 	inline void SetLastAccess(int32 index, off_t access, bool isWrite)
55 	{
56 		// we remember writes as negative offsets
57 		last_access[index] = isWrite ? -access : access;
58 	}
59 
LastAccessfile_cache_ref60 	inline off_t LastAccess(int32 index, bool isWrite) const
61 	{
62 		return isWrite ? -last_access[index] : last_access[index];
63 	}
64 
LastAccessPageOffsetfile_cache_ref65 	inline uint32 LastAccessPageOffset(int32 index, bool isWrite)
66 	{
67 		return LastAccess(index, isWrite) >> PAGE_SHIFT;
68 	}
69 };
70 
71 class PrecacheIO : public AsyncIOCallback {
72 public:
73 								PrecacheIO(file_cache_ref* ref, off_t offset,
74 									generic_size_t size);
75 								~PrecacheIO();
76 
77 			status_t			Prepare(vm_page_reservation* reservation);
78 			void				ReadAsync();
79 
80 	virtual	void				IOFinished(status_t status,
81 									bool partialTransfer,
82 									generic_size_t bytesTransferred);
83 
84 private:
85 			file_cache_ref*		fRef;
86 			VMCache*			fCache;
87 			vm_page**			fPages;
88 			size_t				fPageCount;
89 			ConditionVariable*	fBusyConditions;
90 			generic_io_vec*		fVecs;
91 			off_t				fOffset;
92 			uint32				fVecCount;
93 			generic_size_t		fSize;
94 #if DEBUG_PAGE_ACCESS
95 			thread_id			fAllocatingThread;
96 #endif
97 };
98 
99 typedef status_t (*cache_func)(file_cache_ref* ref, void* cookie, off_t offset,
100 	int32 pageOffset, addr_t buffer, size_t bufferSize, bool useBuffer,
101 	vm_page_reservation* reservation, size_t reservePages);
102 
103 static void add_to_iovec(generic_io_vec* vecs, uint32 &index, uint32 max,
104 	generic_addr_t address, generic_size_t size);
105 
106 
107 static struct cache_module_info* sCacheModule;
108 
109 
110 static const uint32 kZeroVecCount = 32;
111 static const size_t kZeroVecSize = kZeroVecCount * B_PAGE_SIZE;
112 static phys_addr_t sZeroPage;
113 static generic_io_vec sZeroVecs[kZeroVecCount];
114 
115 
116 //	#pragma mark -
117 
118 
PrecacheIO(file_cache_ref * ref,off_t offset,generic_size_t size)119 PrecacheIO::PrecacheIO(file_cache_ref* ref, off_t offset, generic_size_t size)
120 	:
121 	fRef(ref),
122 	fCache(ref->cache),
123 	fPages(NULL),
124 	fVecs(NULL),
125 	fOffset(offset),
126 	fVecCount(0),
127 	fSize(size)
128 {
129 	fPageCount = (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE;
130 	fCache->AcquireRefLocked();
131 	fCache->AcquireStoreRef();
132 }
133 
134 
~PrecacheIO()135 PrecacheIO::~PrecacheIO()
136 {
137 	delete[] fPages;
138 	delete[] fVecs;
139 	fCache->ReleaseStoreRef();
140 	fCache->ReleaseRefLocked();
141 }
142 
143 
144 status_t
Prepare(vm_page_reservation * reservation)145 PrecacheIO::Prepare(vm_page_reservation* reservation)
146 {
147 	if (fPageCount == 0)
148 		return B_BAD_VALUE;
149 
150 	fPages = new(std::nothrow) vm_page*[fPageCount];
151 	if (fPages == NULL)
152 		return B_NO_MEMORY;
153 
154 	fVecs = new(std::nothrow) generic_io_vec[fPageCount];
155 	if (fVecs == NULL)
156 		return B_NO_MEMORY;
157 
158 	// allocate pages for the cache and mark them busy
159 	uint32 i = 0;
160 	for (generic_size_t pos = 0; pos < fSize; pos += B_PAGE_SIZE) {
161 		vm_page* page = vm_page_allocate_page(reservation,
162 			PAGE_STATE_CACHED | VM_PAGE_ALLOC_BUSY);
163 
164 		fCache->InsertPage(page, fOffset + pos);
165 
166 		add_to_iovec(fVecs, fVecCount, fPageCount,
167 			page->physical_page_number * B_PAGE_SIZE, B_PAGE_SIZE);
168 		fPages[i++] = page;
169 	}
170 
171 #if DEBUG_PAGE_ACCESS
172 	fAllocatingThread = find_thread(NULL);
173 #endif
174 
175 	return B_OK;
176 }
177 
178 
179 void
ReadAsync()180 PrecacheIO::ReadAsync()
181 {
182 	// This object is going to be deleted after the I/O request has been
183 	// fulfilled
184 	vfs_asynchronous_read_pages(fRef->vnode, NULL, fOffset, fVecs, fVecCount,
185 		fSize, B_PHYSICAL_IO_REQUEST, this);
186 }
187 
188 
189 void
IOFinished(status_t status,bool partialTransfer,generic_size_t bytesTransferred)190 PrecacheIO::IOFinished(status_t status, bool partialTransfer,
191 	generic_size_t bytesTransferred)
192 {
193 	AutoLocker<VMCache> locker(fCache);
194 
195 	// Make successfully loaded pages accessible again (partially
196 	// transferred pages are considered failed)
197 	phys_size_t pagesTransferred
198 		= (bytesTransferred + B_PAGE_SIZE - 1) / B_PAGE_SIZE;
199 
200 	if ((fOffset + (off_t)bytesTransferred) > fCache->virtual_end)
201 		bytesTransferred = fCache->virtual_end - fOffset;
202 
203 	for (uint32 i = 0; i < pagesTransferred; i++) {
204 		if (i == pagesTransferred - 1
205 			&& (bytesTransferred % B_PAGE_SIZE) != 0) {
206 			// clear partial page
207 			size_t bytesTouched = bytesTransferred % B_PAGE_SIZE;
208 			vm_memset_physical(
209 				((phys_addr_t)fPages[i]->physical_page_number << PAGE_SHIFT)
210 					+ bytesTouched,
211 				0, B_PAGE_SIZE - bytesTouched);
212 		}
213 
214 		DEBUG_PAGE_ACCESS_TRANSFER(fPages[i], fAllocatingThread);
215 
216 		fCache->MarkPageUnbusy(fPages[i]);
217 
218 		DEBUG_PAGE_ACCESS_END(fPages[i]);
219 	}
220 
221 	// Free pages after failed I/O
222 	for (uint32 i = pagesTransferred; i < fPageCount; i++) {
223 		DEBUG_PAGE_ACCESS_TRANSFER(fPages[i], fAllocatingThread);
224 		fCache->NotifyPageEvents(fPages[i], PAGE_EVENT_NOT_BUSY);
225 		fCache->RemovePage(fPages[i]);
226 		vm_page_set_state(fPages[i], PAGE_STATE_FREE);
227 	}
228 
229 	delete this;
230 }
231 
232 
233 //	#pragma mark -
234 
235 
236 static void
add_to_iovec(generic_io_vec * vecs,uint32 & index,uint32 max,generic_addr_t address,generic_size_t size)237 add_to_iovec(generic_io_vec* vecs, uint32 &index, uint32 max,
238 	generic_addr_t address, generic_size_t size)
239 {
240 	if (index > 0 && vecs[index - 1].base + vecs[index - 1].length == address) {
241 		// the iovec can be combined with the previous one
242 		vecs[index - 1].length += size;
243 		return;
244 	}
245 
246 	if (index == max)
247 		panic("no more space for iovecs!");
248 
249 	// we need to start a new iovec
250 	vecs[index].base = address;
251 	vecs[index].length = size;
252 	index++;
253 }
254 
255 
256 static inline bool
access_is_sequential(file_cache_ref * ref)257 access_is_sequential(file_cache_ref* ref)
258 {
259 	return ref->last_access[ref->last_access_index] != 0;
260 }
261 
262 
263 static inline void
push_access(file_cache_ref * ref,off_t offset,generic_size_t bytes,bool isWrite)264 push_access(file_cache_ref* ref, off_t offset, generic_size_t bytes,
265 	bool isWrite)
266 {
267 	TRACE(("%p: push %lld, %ld, %s\n", ref, offset, bytes,
268 		isWrite ? "write" : "read"));
269 
270 	int32 index = ref->last_access_index;
271 	int32 previous = index - 1;
272 	if (previous < 0)
273 		previous = LAST_ACCESSES - 1;
274 
275 	if (offset != ref->LastAccess(previous, isWrite))
276 		ref->last_access[previous] = 0;
277 
278 	ref->SetLastAccess(index, offset + bytes, isWrite);
279 
280 	if (++index >= LAST_ACCESSES)
281 		index = 0;
282 	ref->last_access_index = index;
283 }
284 
285 
286 static void
reserve_pages(file_cache_ref * ref,vm_page_reservation * reservation,size_t reservePages,bool isWrite)287 reserve_pages(file_cache_ref* ref, vm_page_reservation* reservation,
288 	size_t reservePages, bool isWrite)
289 {
290 	if (low_resource_state(B_KERNEL_RESOURCE_PAGES) != B_NO_LOW_RESOURCE) {
291 		VMCache* cache = ref->cache;
292 		cache->Lock();
293 
294 		if (cache->consumers.IsEmpty() && cache->areas == NULL
295 			&& access_is_sequential(ref)) {
296 			// we are not mapped, and we're accessed sequentially
297 
298 			if (isWrite) {
299 				// Just write some pages back, and actually wait until they
300 				// have been written back in order to relieve the page pressure
301 				// a bit.
302 				int32 index = ref->last_access_index;
303 				int32 previous = index - 1;
304 				if (previous < 0)
305 					previous = LAST_ACCESSES - 1;
306 
307 				vm_page_write_modified_page_range(cache,
308 					ref->LastAccessPageOffset(previous, true),
309 					ref->LastAccessPageOffset(index, true));
310 			} else {
311 				// free some pages from our cache
312 				// TODO: start with oldest
313 				uint32 left = reservePages;
314 				vm_page* page;
315 				for (VMCachePagesTree::Iterator it = cache->pages.GetIterator();
316 						(page = it.Next()) != NULL && left > 0;) {
317 					if (page->State() == PAGE_STATE_CACHED && !page->busy) {
318 						DEBUG_PAGE_ACCESS_START(page);
319 						ASSERT(!page->IsMapped());
320 						ASSERT(!page->modified);
321 						cache->RemovePage(page);
322 						vm_page_set_state(page, PAGE_STATE_FREE);
323 						left--;
324 					}
325 				}
326 			}
327 		}
328 		cache->Unlock();
329 	}
330 
331 	vm_page_reserve_pages(reservation, reservePages, VM_PRIORITY_USER);
332 }
333 
334 
335 static inline status_t
read_pages_and_clear_partial(file_cache_ref * ref,void * cookie,off_t offset,const generic_io_vec * vecs,size_t count,uint32 flags,generic_size_t * _numBytes)336 read_pages_and_clear_partial(file_cache_ref* ref, void* cookie, off_t offset,
337 	const generic_io_vec* vecs, size_t count, uint32 flags,
338 	generic_size_t* _numBytes)
339 {
340 	generic_size_t bytesUntouched = *_numBytes;
341 
342 	status_t status = vfs_read_pages(ref->vnode, cookie, offset, vecs, count,
343 		flags, _numBytes);
344 
345 	generic_size_t bytesEnd = *_numBytes;
346 
347 	if (offset + (off_t)bytesEnd > ref->cache->virtual_end)
348 		bytesEnd = ref->cache->virtual_end - offset;
349 
350 	if (status == B_OK && bytesEnd < bytesUntouched) {
351 		// Clear out any leftovers that were not touched by the above read.
352 		// We're doing this here so that not every file system/device has to
353 		// implement this.
354 		bytesUntouched -= bytesEnd;
355 
356 		for (int32 i = count; i-- > 0 && bytesUntouched != 0; ) {
357 			generic_size_t length = min_c(bytesUntouched, vecs[i].length);
358 			vm_memset_physical(vecs[i].base + vecs[i].length - length, 0,
359 				length);
360 
361 			bytesUntouched -= length;
362 		}
363 	}
364 
365 	return status;
366 }
367 
368 
369 /*!	Reads the requested amount of data into the cache, and allocates
370 	pages needed to fulfill that request. This function is called by cache_io().
371 	It can only handle a certain amount of bytes, and the caller must make
372 	sure that it matches that criterion.
373 	The cache_ref lock must be held when calling this function; during
374 	operation it will unlock the cache, though.
375 */
376 static status_t
read_into_cache(file_cache_ref * ref,void * cookie,off_t offset,int32 pageOffset,addr_t buffer,size_t bufferSize,bool useBuffer,vm_page_reservation * reservation,size_t reservePages)377 read_into_cache(file_cache_ref* ref, void* cookie, off_t offset,
378 	int32 pageOffset, addr_t buffer, size_t bufferSize, bool useBuffer,
379 	vm_page_reservation* reservation, size_t reservePages)
380 {
381 	TRACE(("read_into_cache(offset = %lld, pageOffset = %ld, buffer = %#lx, "
382 		"bufferSize = %lu\n", offset, pageOffset, buffer, bufferSize));
383 
384 	VMCache* cache = ref->cache;
385 
386 	// TODO: We're using way too much stack! Rather allocate a sufficiently
387 	// large chunk on the heap.
388 	generic_io_vec vecs[MAX_IO_VECS];
389 	uint32 vecCount = 0;
390 
391 	generic_size_t numBytes = PAGE_ALIGN(pageOffset + bufferSize);
392 	vm_page* pages[MAX_IO_VECS];
393 	int32 pageIndex = 0;
394 
395 	// allocate pages for the cache and mark them busy
396 	for (generic_size_t pos = 0; pos < numBytes; pos += B_PAGE_SIZE) {
397 		vm_page* page = pages[pageIndex++] = vm_page_allocate_page(
398 			reservation, PAGE_STATE_CACHED | VM_PAGE_ALLOC_BUSY);
399 
400 		cache->InsertPage(page, offset + pos);
401 
402 		add_to_iovec(vecs, vecCount, MAX_IO_VECS,
403 			page->physical_page_number * B_PAGE_SIZE, B_PAGE_SIZE);
404 			// TODO: check if the array is large enough (currently panics)!
405 	}
406 
407 	push_access(ref, offset, bufferSize, false);
408 	cache->Unlock();
409 	vm_page_unreserve_pages(reservation);
410 
411 	// read file into reserved pages
412 	status_t status = read_pages_and_clear_partial(ref, cookie, offset, vecs,
413 		vecCount, B_PHYSICAL_IO_REQUEST, &numBytes);
414 	if (status != B_OK) {
415 		// reading failed, free allocated pages
416 
417 		dprintf("file_cache: read pages failed: %s\n", strerror(status));
418 
419 		cache->Lock();
420 
421 		for (int32 i = 0; i < pageIndex; i++) {
422 			cache->NotifyPageEvents(pages[i], PAGE_EVENT_NOT_BUSY);
423 			cache->RemovePage(pages[i]);
424 			vm_page_set_state(pages[i], PAGE_STATE_FREE);
425 		}
426 
427 		return status;
428 	}
429 
430 	// copy the pages if needed and unmap them again
431 
432 	for (int32 i = 0; i < pageIndex; i++) {
433 		if (useBuffer && bufferSize != 0) {
434 			size_t bytes = min_c(bufferSize, (size_t)B_PAGE_SIZE - pageOffset);
435 
436 			vm_memcpy_from_physical((void*)buffer,
437 				pages[i]->physical_page_number * B_PAGE_SIZE + pageOffset,
438 				bytes, IS_USER_ADDRESS(buffer));
439 
440 			buffer += bytes;
441 			bufferSize -= bytes;
442 			pageOffset = 0;
443 		}
444 	}
445 
446 	reserve_pages(ref, reservation, reservePages, false);
447 	cache->Lock();
448 
449 	// make the pages accessible in the cache
450 	for (int32 i = pageIndex; i-- > 0;) {
451 		DEBUG_PAGE_ACCESS_END(pages[i]);
452 
453 		cache->MarkPageUnbusy(pages[i]);
454 	}
455 
456 	return B_OK;
457 }
458 
459 
460 static status_t
read_from_file(file_cache_ref * ref,void * cookie,off_t offset,int32 pageOffset,addr_t buffer,size_t bufferSize,bool useBuffer,vm_page_reservation * reservation,size_t reservePages)461 read_from_file(file_cache_ref* ref, void* cookie, off_t offset,
462 	int32 pageOffset, addr_t buffer, size_t bufferSize, bool useBuffer,
463 	vm_page_reservation* reservation, size_t reservePages)
464 {
465 	TRACE(("read_from_file(offset = %lld, pageOffset = %ld, buffer = %#lx, "
466 		"bufferSize = %lu\n", offset, pageOffset, buffer, bufferSize));
467 
468 	if (!useBuffer)
469 		return B_OK;
470 
471 	generic_io_vec vec;
472 	vec.base = buffer;
473 	vec.length = bufferSize;
474 
475 	push_access(ref, offset, bufferSize, false);
476 	ref->cache->Unlock();
477 	vm_page_unreserve_pages(reservation);
478 
479 	generic_size_t toRead = bufferSize;
480 	status_t status = vfs_read_pages(ref->vnode, cookie, offset + pageOffset,
481 		&vec, 1, 0, &toRead);
482 
483 	if (status == B_OK)
484 		reserve_pages(ref, reservation, reservePages, false);
485 
486 	ref->cache->Lock();
487 
488 	return status;
489 }
490 
491 
492 /*!	Like read_into_cache() but writes data into the cache.
493 	To preserve data consistency, it might also read pages into the cache,
494 	though, if only a partial page gets written.
495 	The same restrictions apply.
496 */
497 static status_t
write_to_cache(file_cache_ref * ref,void * cookie,off_t offset,int32 pageOffset,addr_t buffer,size_t bufferSize,bool useBuffer,vm_page_reservation * reservation,size_t reservePages)498 write_to_cache(file_cache_ref* ref, void* cookie, off_t offset,
499 	int32 pageOffset, addr_t buffer, size_t bufferSize, bool useBuffer,
500 	vm_page_reservation* reservation, size_t reservePages)
501 {
502 	// TODO: We're using way too much stack! Rather allocate a sufficiently
503 	// large chunk on the heap.
504 	generic_io_vec vecs[MAX_IO_VECS];
505 	uint32 vecCount = 0;
506 	generic_size_t numBytes = PAGE_ALIGN(pageOffset + bufferSize);
507 	vm_page* pages[MAX_IO_VECS];
508 	int32 pageIndex = 0;
509 	status_t status = B_OK;
510 
511 	// ToDo: this should be settable somewhere
512 	bool writeThrough = false;
513 
514 	// allocate pages for the cache and mark them busy
515 	for (generic_size_t pos = 0; pos < numBytes; pos += B_PAGE_SIZE) {
516 		// TODO: if space is becoming tight, and this cache is already grown
517 		//	big - shouldn't we better steal the pages directly in that case?
518 		//	(a working set like approach for the file cache)
519 		// TODO: the pages we allocate here should have been reserved upfront
520 		//	in cache_io()
521 		vm_page* page = pages[pageIndex++] = vm_page_allocate_page(
522 			reservation,
523 			(writeThrough ? PAGE_STATE_CACHED : PAGE_STATE_MODIFIED)
524 				| VM_PAGE_ALLOC_BUSY);
525 
526 		page->modified = !writeThrough;
527 
528 		ref->cache->InsertPage(page, offset + pos);
529 
530 		add_to_iovec(vecs, vecCount, MAX_IO_VECS,
531 			page->physical_page_number * B_PAGE_SIZE, B_PAGE_SIZE);
532 	}
533 
534 	push_access(ref, offset, bufferSize, true);
535 	ref->cache->Unlock();
536 	vm_page_unreserve_pages(reservation);
537 
538 	// copy contents (and read in partially written pages first)
539 
540 	if (pageOffset != 0) {
541 		// This is only a partial write, so we have to read the rest of the page
542 		// from the file to have consistent data in the cache
543 		generic_io_vec readVec = { vecs[0].base, B_PAGE_SIZE };
544 		generic_size_t bytesRead = B_PAGE_SIZE;
545 
546 		status = vfs_read_pages(ref->vnode, cookie, offset, &readVec, 1,
547 			B_PHYSICAL_IO_REQUEST, &bytesRead);
548 		// ToDo: handle errors for real!
549 		if (status < B_OK)
550 			panic("1. vfs_read_pages() failed: %s!\n", strerror(status));
551 	}
552 
553 	size_t lastPageOffset = (pageOffset + bufferSize) % B_PAGE_SIZE;
554 	if (lastPageOffset != 0) {
555 		// get the last page in the I/O vectors
556 		generic_addr_t last = vecs[vecCount - 1].base
557 			+ vecs[vecCount - 1].length - B_PAGE_SIZE;
558 
559 		if ((off_t)(offset + pageOffset + bufferSize) == ref->cache->virtual_end) {
560 			// the space in the page after this write action needs to be cleaned
561 			vm_memset_physical(last + lastPageOffset, 0,
562 				B_PAGE_SIZE - lastPageOffset);
563 		} else {
564 			// the end of this write does not happen on a page boundary, so we
565 			// need to fetch the last page before we can update it
566 			generic_io_vec readVec = { last, B_PAGE_SIZE };
567 			generic_size_t bytesRead = B_PAGE_SIZE;
568 
569 			status = vfs_read_pages(ref->vnode, cookie,
570 				PAGE_ALIGN(offset + pageOffset + bufferSize) - B_PAGE_SIZE,
571 				&readVec, 1, B_PHYSICAL_IO_REQUEST, &bytesRead);
572 			// ToDo: handle errors for real!
573 			if (status < B_OK)
574 				panic("vfs_read_pages() failed: %s!\n", strerror(status));
575 
576 			if (bytesRead < B_PAGE_SIZE) {
577 				// the space beyond the file size needs to be cleaned
578 				vm_memset_physical(last + bytesRead, 0,
579 					B_PAGE_SIZE - bytesRead);
580 			}
581 		}
582 	}
583 
584 	for (uint32 i = 0; i < vecCount; i++) {
585 		generic_addr_t base = vecs[i].base;
586 		generic_size_t bytes = min_c((generic_size_t)bufferSize,
587 			generic_size_t(vecs[i].length - pageOffset));
588 
589 		if (useBuffer) {
590 			// copy data from user buffer
591 			vm_memcpy_to_physical(base + pageOffset, (void*)buffer, bytes,
592 				IS_USER_ADDRESS(buffer));
593 		} else {
594 			// clear buffer instead
595 			vm_memset_physical(base + pageOffset, 0, bytes);
596 		}
597 
598 		bufferSize -= bytes;
599 		if (bufferSize == 0)
600 			break;
601 
602 		buffer += bytes;
603 		pageOffset = 0;
604 	}
605 
606 	if (writeThrough) {
607 		// write cached pages back to the file if we were asked to do that
608 		status_t status = vfs_write_pages(ref->vnode, cookie, offset, vecs,
609 			vecCount, B_PHYSICAL_IO_REQUEST, &numBytes);
610 		if (status < B_OK) {
611 			// ToDo: remove allocated pages, ...?
612 			panic("file_cache: remove allocated pages! write pages failed: %s\n",
613 				strerror(status));
614 		}
615 	}
616 
617 	if (status == B_OK)
618 		reserve_pages(ref, reservation, reservePages, true);
619 
620 	ref->cache->Lock();
621 
622 	// make the pages accessible in the cache
623 	for (int32 i = pageIndex; i-- > 0;) {
624 		ref->cache->MarkPageUnbusy(pages[i]);
625 
626 		DEBUG_PAGE_ACCESS_END(pages[i]);
627 	}
628 
629 	return status;
630 }
631 
632 
633 static status_t
write_zeros_to_file(struct vnode * vnode,void * cookie,off_t offset,size_t * _size)634 write_zeros_to_file(struct vnode* vnode, void* cookie, off_t offset,
635 	size_t* _size)
636 {
637 	size_t size = *_size;
638 	status_t status = B_OK;
639 	while (size > 0) {
640 		generic_size_t length = min_c(size, kZeroVecSize);
641 		generic_io_vec* vecs = sZeroVecs;
642 		generic_io_vec vec;
643 		size_t count = kZeroVecCount;
644 		if (length != kZeroVecSize) {
645 			if (length > B_PAGE_SIZE) {
646 				length = ROUNDDOWN(length, B_PAGE_SIZE);
647 				count = length / B_PAGE_SIZE;
648 			} else {
649 				vec.base = sZeroPage;
650 				vec.length = length;
651 				vecs = &vec;
652 				count = 1;
653 			}
654 		}
655 
656 		status = vfs_write_pages(vnode, cookie, offset,
657 			vecs, count, B_PHYSICAL_IO_REQUEST, &length);
658 		if (status != B_OK || length == 0)
659 			break;
660 
661 		offset += length;
662 		size -= length;
663 	}
664 
665 	*_size = *_size - size;
666 	return status;
667 }
668 
669 
670 static status_t
write_to_file(file_cache_ref * ref,void * cookie,off_t offset,int32 pageOffset,addr_t buffer,size_t bufferSize,bool useBuffer,vm_page_reservation * reservation,size_t reservePages)671 write_to_file(file_cache_ref* ref, void* cookie, off_t offset, int32 pageOffset,
672 	addr_t buffer, size_t bufferSize, bool useBuffer,
673 	vm_page_reservation* reservation, size_t reservePages)
674 {
675 	push_access(ref, offset, bufferSize, true);
676 	ref->cache->Unlock();
677 	vm_page_unreserve_pages(reservation);
678 
679 	status_t status = B_OK;
680 
681 	if (!useBuffer) {
682 		status = write_zeros_to_file(ref->vnode, cookie, offset + pageOffset,
683 			&bufferSize);
684 	} else {
685 		generic_io_vec vec;
686 		vec.base = buffer;
687 		vec.length = bufferSize;
688 		generic_size_t toWrite = bufferSize;
689 		status = vfs_write_pages(ref->vnode, cookie, offset + pageOffset,
690 			&vec, 1, 0, &toWrite);
691 	}
692 
693 	if (status == B_OK)
694 		reserve_pages(ref, reservation, reservePages, true);
695 
696 	ref->cache->Lock();
697 
698 	return status;
699 }
700 
701 
702 static inline status_t
satisfy_cache_io(file_cache_ref * ref,void * cookie,cache_func function,off_t offset,addr_t buffer,bool useBuffer,int32 & pageOffset,size_t bytesLeft,size_t & reservePages,off_t & lastOffset,addr_t & lastBuffer,int32 & lastPageOffset,size_t & lastLeft,size_t & lastReservedPages,vm_page_reservation * reservation)703 satisfy_cache_io(file_cache_ref* ref, void* cookie, cache_func function,
704 	off_t offset, addr_t buffer, bool useBuffer, int32 &pageOffset,
705 	size_t bytesLeft, size_t &reservePages, off_t &lastOffset,
706 	addr_t &lastBuffer, int32 &lastPageOffset, size_t &lastLeft,
707 	size_t &lastReservedPages, vm_page_reservation* reservation)
708 {
709 	if (lastBuffer == buffer)
710 		return B_OK;
711 
712 	size_t requestSize = buffer - lastBuffer;
713 	reservePages = min_c(MAX_IO_VECS, (lastLeft - requestSize
714 		+ lastPageOffset + B_PAGE_SIZE - 1) >> PAGE_SHIFT);
715 
716 	status_t status = function(ref, cookie, lastOffset, lastPageOffset,
717 		lastBuffer, requestSize, useBuffer, reservation, reservePages);
718 	if (status == B_OK) {
719 		lastReservedPages = reservePages;
720 		lastBuffer = buffer;
721 		lastLeft = bytesLeft;
722 		lastOffset = offset;
723 		lastPageOffset = 0;
724 		pageOffset = 0;
725 	}
726 	return status;
727 }
728 
729 
730 static status_t
cache_io(void * _cacheRef,void * cookie,off_t offset,addr_t buffer,size_t * _size,bool doWrite)731 cache_io(void* _cacheRef, void* cookie, off_t offset, addr_t buffer,
732 	size_t* _size, bool doWrite)
733 {
734 	if (_cacheRef == NULL)
735 		panic("cache_io() called with NULL ref!\n");
736 
737 	file_cache_ref* ref = (file_cache_ref*)_cacheRef;
738 	VMCache* cache = ref->cache;
739 	bool useBuffer = buffer != 0;
740 
741 	TRACE(("cache_io(ref = %p, offset = %lld, buffer = %p, size = %lu, %s)\n",
742 		ref, offset, (void*)buffer, *_size, doWrite ? "write" : "read"));
743 
744 	int32 pageOffset = offset & (B_PAGE_SIZE - 1);
745 	size_t size = *_size;
746 	offset -= pageOffset;
747 
748 	// "offset" and "lastOffset" are always aligned to B_PAGE_SIZE,
749 	// the "last*" variables always point to the end of the last
750 	// satisfied request part
751 
752 	const uint32 kMaxChunkSize = MAX_IO_VECS * B_PAGE_SIZE;
753 	size_t bytesLeft = size, lastLeft = size;
754 	int32 lastPageOffset = pageOffset;
755 	addr_t lastBuffer = buffer;
756 	off_t lastOffset = offset;
757 	size_t lastReservedPages = min_c(MAX_IO_VECS, (pageOffset + bytesLeft
758 		+ B_PAGE_SIZE - 1) >> PAGE_SHIFT);
759 	size_t reservePages = 0;
760 	size_t pagesProcessed = 0;
761 	cache_func function = NULL;
762 
763 	vm_page_reservation reservation;
764 	reserve_pages(ref, &reservation, lastReservedPages, doWrite);
765 
766 	AutoLocker<VMCache> locker(cache);
767 
768 	while (bytesLeft > 0) {
769 		// Periodically reevaluate the low memory situation and select the
770 		// read/write hook accordingly
771 		if (pagesProcessed % 32 == 0) {
772 			if (size >= BYPASS_IO_SIZE
773 				&& low_resource_state(B_KERNEL_RESOURCE_PAGES)
774 					!= B_NO_LOW_RESOURCE) {
775 				// In low memory situations we bypass the cache beyond a
776 				// certain I/O size.
777 				function = doWrite ? write_to_file : read_from_file;
778 			} else
779 				function = doWrite ? write_to_cache : read_into_cache;
780 		}
781 
782 		// check if this page is already in memory
783 		vm_page* page = cache->LookupPage(offset);
784 		if (page != NULL) {
785 			// The page may be busy - since we need to unlock the cache sometime
786 			// in the near future, we need to satisfy the request of the pages
787 			// we didn't get yet (to make sure no one else interferes in the
788 			// meantime).
789 			status_t status = satisfy_cache_io(ref, cookie, function, offset,
790 				buffer, useBuffer, pageOffset, bytesLeft, reservePages,
791 				lastOffset, lastBuffer, lastPageOffset, lastLeft,
792 				lastReservedPages, &reservation);
793 			if (status != B_OK)
794 				return status;
795 
796 			// Since satisfy_cache_io() unlocks the cache, we need to look up
797 			// the page again.
798 			page = cache->LookupPage(offset);
799 			if (page != NULL && page->busy) {
800 				cache->WaitForPageEvents(page, PAGE_EVENT_NOT_BUSY, true);
801 				continue;
802 			}
803 		}
804 
805 		size_t bytesInPage = min_c(size_t(B_PAGE_SIZE - pageOffset), bytesLeft);
806 
807 		TRACE(("lookup page from offset %lld: %p, size = %lu, pageOffset "
808 			"= %lu\n", offset, page, bytesLeft, pageOffset));
809 
810 		if (page != NULL) {
811 			if (doWrite || useBuffer) {
812 				// Since the following user_mem{cpy,set}() might cause a page
813 				// fault, which in turn might cause pages to be reserved, we
814 				// need to unlock the cache temporarily to avoid a potential
815 				// deadlock. To make sure that our page doesn't go away, we mark
816 				// it busy for the time.
817 				page->busy = true;
818 				locker.Unlock();
819 
820 				// copy the contents of the page already in memory
821 				phys_addr_t pageAddress
822 					= (phys_addr_t)page->physical_page_number * B_PAGE_SIZE
823 						+ pageOffset;
824 				bool userBuffer = IS_USER_ADDRESS(buffer);
825 				if (doWrite) {
826 					if (useBuffer) {
827 						vm_memcpy_to_physical(pageAddress, (void*)buffer,
828 							bytesInPage, userBuffer);
829 					} else {
830 						vm_memset_physical(pageAddress, 0, bytesInPage);
831 					}
832 				} else if (useBuffer) {
833 					vm_memcpy_from_physical((void*)buffer, pageAddress,
834 						bytesInPage, userBuffer);
835 				}
836 
837 				locker.Lock();
838 
839 				if (doWrite) {
840 					DEBUG_PAGE_ACCESS_START(page);
841 
842 					page->modified = true;
843 
844 					if (page->State() != PAGE_STATE_MODIFIED)
845 						vm_page_set_state(page, PAGE_STATE_MODIFIED);
846 
847 					DEBUG_PAGE_ACCESS_END(page);
848 				}
849 
850 				cache->MarkPageUnbusy(page);
851 			}
852 
853 			// If it is cached only, requeue the page, so the respective queue
854 			// roughly remains LRU first sorted.
855 			if (page->State() == PAGE_STATE_CACHED
856 					|| page->State() == PAGE_STATE_MODIFIED) {
857 				DEBUG_PAGE_ACCESS_START(page);
858 				vm_page_requeue(page, true);
859 				DEBUG_PAGE_ACCESS_END(page);
860 			}
861 
862 			if (bytesLeft <= bytesInPage) {
863 				// we've read the last page, so we're done!
864 				locker.Unlock();
865 				vm_page_unreserve_pages(&reservation);
866 				return B_OK;
867 			}
868 
869 			// prepare a potential gap request
870 			lastBuffer = buffer + bytesInPage;
871 			lastLeft = bytesLeft - bytesInPage;
872 			lastOffset = offset + B_PAGE_SIZE;
873 			lastPageOffset = 0;
874 		}
875 
876 		if (bytesLeft <= bytesInPage)
877 			break;
878 
879 		buffer += bytesInPage;
880 		bytesLeft -= bytesInPage;
881 		pageOffset = 0;
882 		offset += B_PAGE_SIZE;
883 		pagesProcessed++;
884 
885 		if (buffer - lastBuffer + lastPageOffset >= kMaxChunkSize) {
886 			status_t status = satisfy_cache_io(ref, cookie, function, offset,
887 				buffer, useBuffer, pageOffset, bytesLeft, reservePages,
888 				lastOffset, lastBuffer, lastPageOffset, lastLeft,
889 				lastReservedPages, &reservation);
890 			if (status != B_OK)
891 				return status;
892 		}
893 	}
894 
895 	// fill the last remaining bytes of the request (either write or read)
896 
897 	return function(ref, cookie, lastOffset, lastPageOffset, lastBuffer,
898 		lastLeft, useBuffer, &reservation, 0);
899 }
900 
901 
902 static status_t
file_cache_control(const char * subsystem,uint32 function,void * buffer,size_t bufferSize)903 file_cache_control(const char* subsystem, uint32 function, void* buffer,
904 	size_t bufferSize)
905 {
906 	switch (function) {
907 		case CACHE_CLEAR:
908 			// ToDo: clear the cache
909 			dprintf("cache_control: clear cache!\n");
910 			return B_OK;
911 
912 		case CACHE_SET_MODULE:
913 		{
914 			cache_module_info* module = sCacheModule;
915 
916 			// unset previous module
917 
918 			if (sCacheModule != NULL) {
919 				sCacheModule = NULL;
920 				snooze(100000);	// 0.1 secs
921 				put_module(module->info.name);
922 			}
923 
924 			// get new module, if any
925 
926 			if (buffer == NULL)
927 				return B_OK;
928 
929 			char name[B_FILE_NAME_LENGTH];
930 			if (!IS_USER_ADDRESS(buffer)
931 				|| user_strlcpy(name, (char*)buffer,
932 						B_FILE_NAME_LENGTH) < B_OK)
933 				return B_BAD_ADDRESS;
934 
935 			if (strncmp(name, CACHE_MODULES_NAME, strlen(CACHE_MODULES_NAME)))
936 				return B_BAD_VALUE;
937 
938 			dprintf("cache_control: set module %s!\n", name);
939 
940 			status_t status = get_module(name, (module_info**)&module);
941 			if (status == B_OK)
942 				sCacheModule = module;
943 
944 			return status;
945 		}
946 	}
947 
948 	return B_BAD_HANDLER;
949 }
950 
951 
952 //	#pragma mark - private kernel API
953 
954 
955 extern "C" void
cache_prefetch_vnode(struct vnode * vnode,off_t offset,size_t size)956 cache_prefetch_vnode(struct vnode* vnode, off_t offset, size_t size)
957 {
958 	if (size == 0)
959 		return;
960 
961 	VMCache* cache;
962 	if (vfs_get_vnode_cache(vnode, &cache, false) != B_OK)
963 		return;
964 	if (cache->type != CACHE_TYPE_VNODE) {
965 		cache->ReleaseRef();
966 		return;
967 	}
968 
969 	file_cache_ref* ref = ((VMVnodeCache*)cache)->FileCacheRef();
970 	off_t fileSize = cache->virtual_end;
971 
972 	if ((off_t)(offset + size) > fileSize)
973 		size = fileSize - offset;
974 
975 	// "offset" and "size" are always aligned to B_PAGE_SIZE,
976 	offset = ROUNDDOWN(offset, B_PAGE_SIZE);
977 	size = ROUNDUP(size, B_PAGE_SIZE);
978 
979 	const size_t pagesCount = size / B_PAGE_SIZE;
980 
981 	// Don't do anything if we don't have the resources left, or the cache
982 	// already contains more than 2/3 of its pages
983 	if (offset >= fileSize || vm_page_num_unused_pages() < 2 * pagesCount
984 		|| (3 * cache->page_count) > (2 * fileSize / B_PAGE_SIZE)) {
985 		cache->ReleaseRef();
986 		return;
987 	}
988 
989 	size_t bytesToRead = 0;
990 	off_t lastOffset = offset;
991 
992 	vm_page_reservation reservation;
993 	vm_page_reserve_pages(&reservation, pagesCount, VM_PRIORITY_USER);
994 
995 	cache->Lock();
996 
997 	while (true) {
998 		// check if this page is already in memory
999 		if (size > 0) {
1000 			vm_page* page = cache->LookupPage(offset);
1001 
1002 			offset += B_PAGE_SIZE;
1003 			size -= B_PAGE_SIZE;
1004 
1005 			if (page == NULL) {
1006 				bytesToRead += B_PAGE_SIZE;
1007 				continue;
1008 			}
1009 		}
1010 		if (bytesToRead != 0) {
1011 			// read the part before the current page (or the end of the request)
1012 			PrecacheIO* io = new(std::nothrow) PrecacheIO(ref, lastOffset,
1013 				bytesToRead);
1014 			if (io == NULL || io->Prepare(&reservation) != B_OK) {
1015 				delete io;
1016 				break;
1017 			}
1018 
1019 			// we must not have the cache locked during I/O
1020 			cache->Unlock();
1021 			io->ReadAsync();
1022 			cache->Lock();
1023 
1024 			bytesToRead = 0;
1025 		}
1026 
1027 		if (size == 0) {
1028 			// we have reached the end of the request
1029 			break;
1030 		}
1031 
1032 		lastOffset = offset;
1033 	}
1034 
1035 	cache->ReleaseRefAndUnlock();
1036 	vm_page_unreserve_pages(&reservation);
1037 }
1038 
1039 
1040 extern "C" void
cache_prefetch(dev_t mountID,ino_t vnodeID,off_t offset,size_t size)1041 cache_prefetch(dev_t mountID, ino_t vnodeID, off_t offset, size_t size)
1042 {
1043 	// ToDo: schedule prefetch
1044 
1045 	TRACE(("cache_prefetch(vnode %ld:%lld)\n", mountID, vnodeID));
1046 
1047 	// get the vnode for the object, this also grabs a ref to it
1048 	struct vnode* vnode;
1049 	if (vfs_get_vnode(mountID, vnodeID, true, &vnode) != B_OK)
1050 		return;
1051 
1052 	cache_prefetch_vnode(vnode, offset, size);
1053 	vfs_put_vnode(vnode);
1054 }
1055 
1056 
1057 extern "C" void
cache_node_opened(struct vnode * vnode,VMCache * cache,dev_t mountID,ino_t parentID,ino_t vnodeID,const char * name)1058 cache_node_opened(struct vnode* vnode, VMCache* cache,
1059 	dev_t mountID, ino_t parentID, ino_t vnodeID, const char* name)
1060 {
1061 	if (sCacheModule == NULL || sCacheModule->node_opened == NULL)
1062 		return;
1063 
1064 	off_t size = -1;
1065 	if (cache != NULL && cache->type == CACHE_TYPE_VNODE) {
1066 		file_cache_ref* ref = ((VMVnodeCache*)cache)->FileCacheRef();
1067 		if (ref != NULL)
1068 			size = cache->virtual_end;
1069 	}
1070 
1071 	sCacheModule->node_opened(vnode, mountID, parentID, vnodeID, name,
1072 		size);
1073 }
1074 
1075 
1076 extern "C" void
cache_node_closed(struct vnode * vnode,VMCache * cache,dev_t mountID,ino_t vnodeID)1077 cache_node_closed(struct vnode* vnode, VMCache* cache,
1078 	dev_t mountID, ino_t vnodeID)
1079 {
1080 	if (sCacheModule == NULL || sCacheModule->node_closed == NULL)
1081 		return;
1082 
1083 	int32 accessType = 0;
1084 	if (cache != NULL && cache->type == CACHE_TYPE_VNODE) {
1085 		// ToDo: set accessType
1086 	}
1087 
1088 	sCacheModule->node_closed(vnode, mountID, vnodeID, accessType);
1089 }
1090 
1091 
1092 extern "C" void
cache_node_launched(size_t argCount,char * const * args)1093 cache_node_launched(size_t argCount, char*  const* args)
1094 {
1095 	if (sCacheModule == NULL || sCacheModule->node_launched == NULL)
1096 		return;
1097 
1098 	sCacheModule->node_launched(argCount, args);
1099 }
1100 
1101 
1102 extern "C" status_t
file_cache_init_post_boot_device(void)1103 file_cache_init_post_boot_device(void)
1104 {
1105 	// ToDo: get cache module out of driver settings
1106 
1107 	if (get_module("file_cache/launch_speedup/v1",
1108 			(module_info**)&sCacheModule) == B_OK) {
1109 		dprintf("** opened launch speedup: %" B_PRId64 "\n", system_time());
1110 	}
1111 	return B_OK;
1112 }
1113 
1114 
1115 extern "C" status_t
file_cache_init(void)1116 file_cache_init(void)
1117 {
1118 	// allocate a clean page we can use for writing zeroes
1119 	vm_page_reservation reservation;
1120 	vm_page_reserve_pages(&reservation, 1, VM_PRIORITY_SYSTEM);
1121 	vm_page* page = vm_page_allocate_page(&reservation,
1122 		PAGE_STATE_WIRED | VM_PAGE_ALLOC_CLEAR);
1123 	vm_page_unreserve_pages(&reservation);
1124 
1125 	sZeroPage = (phys_addr_t)page->physical_page_number * B_PAGE_SIZE;
1126 
1127 	for (uint32 i = 0; i < kZeroVecCount; i++) {
1128 		sZeroVecs[i].base = sZeroPage;
1129 		sZeroVecs[i].length = B_PAGE_SIZE;
1130 	}
1131 
1132 	register_generic_syscall(CACHE_SYSCALLS, file_cache_control, 1, 0);
1133 	return B_OK;
1134 }
1135 
1136 
1137 //	#pragma mark - public FS API
1138 
1139 
1140 extern "C" void*
file_cache_create(dev_t mountID,ino_t vnodeID,off_t size)1141 file_cache_create(dev_t mountID, ino_t vnodeID, off_t size)
1142 {
1143 	TRACE(("file_cache_create(mountID = %ld, vnodeID = %lld, size = %lld)\n",
1144 		mountID, vnodeID, size));
1145 
1146 	file_cache_ref* ref = new file_cache_ref;
1147 	if (ref == NULL)
1148 		return NULL;
1149 
1150 	memset(ref->last_access, 0, sizeof(ref->last_access));
1151 	ref->last_access_index = 0;
1152 	ref->disabled_count = 0;
1153 
1154 	// TODO: delay VMCache creation until data is
1155 	//	requested/written for the first time? Listing lots of
1156 	//	files in Tracker (and elsewhere) could be slowed down.
1157 	//	Since the file_cache_ref itself doesn't have a lock,
1158 	//	we would need to "rent" one during construction, possibly
1159 	//	the vnode lock, maybe a dedicated one.
1160 	//	As there shouldn't be too much contention, we could also
1161 	//	use atomic_test_and_set(), and free the resources again
1162 	//	when that fails...
1163 
1164 	// Get the vnode for the object
1165 	// (note, this does not grab a reference to the node)
1166 	if (vfs_lookup_vnode(mountID, vnodeID, &ref->vnode) != B_OK)
1167 		goto err1;
1168 
1169 	// Gets (usually creates) the cache for the node
1170 	if (vfs_get_vnode_cache(ref->vnode, &ref->cache, true) != B_OK)
1171 		goto err1;
1172 
1173 	ref->cache->virtual_end = size;
1174 	((VMVnodeCache*)ref->cache)->SetFileCacheRef(ref);
1175 	return ref;
1176 
1177 err1:
1178 	delete ref;
1179 	return NULL;
1180 }
1181 
1182 
1183 extern "C" void
file_cache_delete(void * _cacheRef)1184 file_cache_delete(void* _cacheRef)
1185 {
1186 	file_cache_ref* ref = (file_cache_ref*)_cacheRef;
1187 
1188 	if (ref == NULL)
1189 		return;
1190 
1191 	TRACE(("file_cache_delete(ref = %p)\n", ref));
1192 
1193 	ref->cache->ReleaseRef();
1194 	delete ref;
1195 }
1196 
1197 
1198 extern "C" void
file_cache_enable(void * _cacheRef)1199 file_cache_enable(void* _cacheRef)
1200 {
1201 	file_cache_ref* ref = (file_cache_ref*)_cacheRef;
1202 
1203 	AutoLocker<VMCache> _(ref->cache);
1204 
1205 	if (ref->disabled_count == 0) {
1206 		panic("Unbalanced file_cache_enable()!");
1207 		return;
1208 	}
1209 
1210 	ref->disabled_count--;
1211 }
1212 
1213 
1214 extern "C" status_t
file_cache_disable(void * _cacheRef)1215 file_cache_disable(void* _cacheRef)
1216 {
1217 	// TODO: This function only removes all pages from the cache and prevents
1218 	// that the file cache functions add any new ones until re-enabled. The
1219 	// VM (on page fault) can still add pages, if the file is mmap()ed. We
1220 	// should mark the cache to prevent shared mappings of the file and fix
1221 	// the page fault code to deal correctly with private mappings (i.e. only
1222 	// insert pages in consumer caches).
1223 
1224 	file_cache_ref* ref = (file_cache_ref*)_cacheRef;
1225 
1226 	AutoLocker<VMCache> _(ref->cache);
1227 
1228 	// If already disabled, there's nothing to do for us.
1229 	if (ref->disabled_count > 0) {
1230 		ref->disabled_count++;
1231 		return B_OK;
1232 	}
1233 
1234 	// The file cache is not yet disabled. We need to evict all cached pages.
1235 	status_t error = ref->cache->FlushAndRemoveAllPages();
1236 	if (error != B_OK)
1237 		return error;
1238 
1239 	ref->disabled_count++;
1240 	return B_OK;
1241 }
1242 
1243 
1244 extern "C" bool
file_cache_is_enabled(void * _cacheRef)1245 file_cache_is_enabled(void* _cacheRef)
1246 {
1247 	file_cache_ref* ref = (file_cache_ref*)_cacheRef;
1248 	AutoLocker<VMCache> _(ref->cache);
1249 
1250 	return ref->disabled_count == 0;
1251 }
1252 
1253 
1254 extern "C" status_t
file_cache_set_size(void * _cacheRef,off_t newSize)1255 file_cache_set_size(void* _cacheRef, off_t newSize)
1256 {
1257 	file_cache_ref* ref = (file_cache_ref*)_cacheRef;
1258 
1259 	TRACE(("file_cache_set_size(ref = %p, size = %lld)\n", ref, newSize));
1260 
1261 	if (ref == NULL)
1262 		return B_OK;
1263 
1264 	VMCache* cache = ref->cache;
1265 	AutoLocker<VMCache> _(cache);
1266 
1267 	off_t oldSize = cache->virtual_end;
1268 	status_t status = cache->Resize(newSize, VM_PRIORITY_USER);
1269 		// Note, the priority doesn't really matter, since this cache doesn't
1270 		// reserve any memory.
1271 	if (status == B_OK && newSize < oldSize) {
1272 		// We may have a new partial page at the end of the cache that must be
1273 		// cleared.
1274 		uint32 partialBytes = newSize % B_PAGE_SIZE;
1275 		if (partialBytes != 0) {
1276 			vm_page* page = cache->LookupPage(newSize - partialBytes);
1277 			if (page != NULL) {
1278 				vm_memset_physical(page->physical_page_number * B_PAGE_SIZE
1279 					+ partialBytes, 0, B_PAGE_SIZE - partialBytes);
1280 			}
1281 		}
1282 	}
1283 
1284 	return status;
1285 }
1286 
1287 
1288 extern "C" status_t
file_cache_sync(void * _cacheRef)1289 file_cache_sync(void* _cacheRef)
1290 {
1291 	file_cache_ref* ref = (file_cache_ref*)_cacheRef;
1292 	if (ref == NULL)
1293 		return B_BAD_VALUE;
1294 
1295 	return ref->cache->WriteModified();
1296 }
1297 
1298 
1299 extern "C" status_t
file_cache_read(void * _cacheRef,void * cookie,off_t offset,void * buffer,size_t * _size)1300 file_cache_read(void* _cacheRef, void* cookie, off_t offset, void* buffer,
1301 	size_t* _size)
1302 {
1303 	file_cache_ref* ref = (file_cache_ref*)_cacheRef;
1304 
1305 	TRACE(("file_cache_read(ref = %p, offset = %lld, buffer = %p, size = %lu)\n",
1306 		ref, offset, buffer, *_size));
1307 
1308 	// Bounds checking. We do this here so it applies to uncached I/O.
1309 	if (offset < 0)
1310 		return B_BAD_VALUE;
1311 	const off_t fileSize = ref->cache->virtual_end;
1312 	if (offset >= fileSize || *_size == 0) {
1313 		*_size = 0;
1314 		return B_OK;
1315 	}
1316 	if ((off_t)(offset + *_size) > fileSize)
1317 		*_size = fileSize - offset;
1318 
1319 	if (ref->disabled_count > 0) {
1320 		// Caching is disabled -- read directly from the file.
1321 		generic_io_vec vec;
1322 		vec.base = (addr_t)buffer;
1323 		generic_size_t size = vec.length = *_size;
1324 		status_t error = vfs_read_pages(ref->vnode, cookie, offset, &vec, 1, 0,
1325 			&size);
1326 		*_size = size;
1327 		return error;
1328 	}
1329 
1330 	return cache_io(ref, cookie, offset, (addr_t)buffer, _size, false);
1331 }
1332 
1333 
1334 extern "C" status_t
file_cache_write(void * _cacheRef,void * cookie,off_t offset,const void * buffer,size_t * _size)1335 file_cache_write(void* _cacheRef, void* cookie, off_t offset,
1336 	const void* buffer, size_t* _size)
1337 {
1338 	file_cache_ref* ref = (file_cache_ref*)_cacheRef;
1339 
1340 	// We don't do bounds checking here, as we are relying on the
1341 	// file system which called us to already have done that and made
1342 	// adjustments as necessary, unlike in read().
1343 
1344 	if (ref->disabled_count > 0) {
1345 		// Caching is disabled -- write directly to the file.
1346 		if (buffer != NULL) {
1347 			generic_io_vec vec;
1348 			vec.base = (addr_t)buffer;
1349 			generic_size_t size = vec.length = *_size;
1350 
1351 			status_t error = vfs_write_pages(ref->vnode, cookie, offset, &vec,
1352 				1, 0, &size);
1353 			*_size = size;
1354 			return error;
1355 		}
1356 		return write_zeros_to_file(ref->vnode, cookie, offset, _size);
1357 	}
1358 
1359 	status_t status = cache_io(ref, cookie, offset,
1360 		(addr_t)const_cast<void*>(buffer), _size, true);
1361 
1362 	TRACE(("file_cache_write(ref = %p, offset = %lld, buffer = %p, size = %lu)"
1363 		" = %ld\n", ref, offset, buffer, *_size, status));
1364 
1365 	return status;
1366 }
1367