file_cache.cpp revision e50cf876
1f72376a8SAxel Dörfler/*
26102f078SAxel Dörfler * Copyright 2004-2009, Axel D��rfler, axeld@pinc-software.de.
3eb9f0103SAxel Dörfler * Distributed under the terms of the MIT License.
4eb9f0103SAxel Dörfler */
5f72376a8SAxel Dörfler
6f72376a8SAxel Dörfler
7f72376a8SAxel Dörfler#include "vnode_store.h"
8f72376a8SAxel Dörfler
90d871d3cSAxel Dörfler#include <unistd.h>
100d871d3cSAxel Dörfler#include <stdlib.h>
110d871d3cSAxel Dörfler#include <string.h>
120d871d3cSAxel Dörfler
13f72376a8SAxel Dörfler#include <KernelExport.h>
14f72376a8SAxel Dörfler#include <fs_cache.h>
15f72376a8SAxel Dörfler
16279c6b76SIngo Weinhold#include <condition_variable.h>
17f72376a8SAxel Dörfler#include <file_cache.h>
18279c6b76SIngo Weinhold#include <generic_syscall.h>
195c99d639SIngo Weinhold#include <low_resource_manager.h>
207b8683b2SIngo Weinhold#include <thread.h>
210d871d3cSAxel Dörfler#include <util/AutoLock.h>
22279c6b76SIngo Weinhold#include <util/kernel_cpp.h>
23f72376a8SAxel Dörfler#include <vfs.h>
24e50cf876SIngo Weinhold#include <vm/vm.h>
25e50cf876SIngo Weinhold#include <vm/vm_page.h>
26e50cf876SIngo Weinhold#include <vm/VMCache.h>
27f72376a8SAxel Dörfler
28aa4ba93eSIngo Weinhold#include "IORequest.h"
297f12cc54SIngo Weinhold
30f72376a8SAxel Dörfler
31f72376a8SAxel Dörfler//#define TRACE_FILE_CACHE
32f72376a8SAxel Dörfler#ifdef TRACE_FILE_CACHE
33f72376a8SAxel Dörfler#	define TRACE(x) dprintf x
34f72376a8SAxel Dörfler#else
35f72376a8SAxel Dörfler#	define TRACE(x) ;
36f72376a8SAxel Dörfler#endif
37f72376a8SAxel Dörfler
380f6c560eSAxel Dörfler// maximum number of iovecs per request
39279c6b76SIngo Weinhold#define MAX_IO_VECS			32	// 128 kB
400f6c560eSAxel Dörfler#define MAX_FILE_IO_VECS	32
4111a3346cSAxel Dörfler
42cfe386c2SAxel Dörfler#define BYPASS_IO_SIZE		65536
43c6573329SAxel Dörfler#define LAST_ACCESSES		3
44c6573329SAxel Dörfler
45f72376a8SAxel Dörflerstruct file_cache_ref {
46a477e3cfSIngo Weinhold	VMCache			*cache;
4780f54692SAxel Dörfler	struct vnode	*vnode;
48c6573329SAxel Dörfler	off_t			last_access[LAST_ACCESSES];
49c6573329SAxel Dörfler		// TODO: it would probably be enough to only store the least
50c6573329SAxel Dörfler		//	significant 31 bits, and make this uint32 (one bit for
51c6573329SAxel Dörfler		//	write vs. read)
52c6573329SAxel Dörfler	int32			last_access_index;
537491000fSIngo Weinhold	uint16			disabled_count;
548a26f35aSAxel Dörfler
558a26f35aSAxel Dörfler	inline void SetLastAccess(int32 index, off_t access, bool isWrite)
568a26f35aSAxel Dörfler	{
578a26f35aSAxel Dörfler		// we remember writes as negative offsets
588a26f35aSAxel Dörfler		last_access[index] = isWrite ? -access : access;
598a26f35aSAxel Dörfler	}
608a26f35aSAxel Dörfler
618a26f35aSAxel Dörfler	inline off_t LastAccess(int32 index, bool isWrite)
628a26f35aSAxel Dörfler	{
638a26f35aSAxel Dörfler		return isWrite ? -last_access[index] : last_access[index];
648a26f35aSAxel Dörfler	}
658a26f35aSAxel Dörfler
668a26f35aSAxel Dörfler	inline uint32 LastAccessPageOffset(int32 index, bool isWrite)
678a26f35aSAxel Dörfler	{
688a26f35aSAxel Dörfler		return LastAccess(index, isWrite) >> PAGE_SHIFT;
698a26f35aSAxel Dörfler	}
70f72376a8SAxel Dörfler};
71f72376a8SAxel Dörfler
72eb2bd0e8SStephan Aßmusclass PrecacheIO : public AsyncIOCallback {
73eb2bd0e8SStephan Aßmuspublic:
74eb2bd0e8SStephan Aßmus								PrecacheIO(file_cache_ref* ref, off_t offset,
75eb2bd0e8SStephan Aßmus									size_t size);
76eb2bd0e8SStephan Aßmus								~PrecacheIO();
77eb2bd0e8SStephan Aßmus
78a5dbd78bSAxel Dörfler			status_t			Prepare();
79a5dbd78bSAxel Dörfler			void				ReadAsync();
80eb2bd0e8SStephan Aßmus
81eb2bd0e8SStephan Aßmus	virtual	void				IOFinished(status_t status,
82eb2bd0e8SStephan Aßmus									bool partialTransfer,
83eb2bd0e8SStephan Aßmus									size_t bytesTransferred);
84eb2bd0e8SStephan Aßmus
85eb2bd0e8SStephan Aßmusprivate:
86eb2bd0e8SStephan Aßmus			file_cache_ref*		fRef;
87eb2bd0e8SStephan Aßmus			VMCache*			fCache;
88eb2bd0e8SStephan Aßmus			vm_page**			fPages;
89eb2bd0e8SStephan Aßmus			size_t				fPageCount;
90eb2bd0e8SStephan Aßmus			ConditionVariable*	fBusyConditions;
91eb2bd0e8SStephan Aßmus			iovec*				fVecs;
92eb2bd0e8SStephan Aßmus			off_t				fOffset;
93a5dbd78bSAxel Dörfler			uint32				fVecCount;
94eb2bd0e8SStephan Aßmus			size_t				fSize;
95eb2bd0e8SStephan Aßmus};
96eb2bd0e8SStephan Aßmus
9761b6f38cSAxel Dörflertypedef status_t (*cache_func)(file_cache_ref* ref, void* cookie, off_t offset,
98a121b8c8SAxel Dörfler	int32 pageOffset, addr_t buffer, size_t bufferSize, bool useBuffer,
9909149281SAxel Dörfler	size_t lastReservedPages, size_t reservePages);
10009149281SAxel Dörfler
10161b6f38cSAxel Dörflerstatic void add_to_iovec(iovec* vecs, uint32 &index, uint32 max, addr_t address,
102eb2bd0e8SStephan Aßmus	size_t size);
103eb2bd0e8SStephan Aßmus
104f72376a8SAxel Dörfler
10561b6f38cSAxel Dörflerstatic struct cache_module_info* sCacheModule;
1067491000fSIngo Weinholdstatic const uint8 kZeroBuffer[4096] = {};
107324fc66bSAxel Dörfler
108324fc66bSAxel Dörfler
10911a3346cSAxel Dörfler//	#pragma mark -
11011a3346cSAxel Dörfler
11111a3346cSAxel Dörfler
112eb2bd0e8SStephan AßmusPrecacheIO::PrecacheIO(file_cache_ref* ref, off_t offset, size_t size)
113eb2bd0e8SStephan Aßmus	:
114eb2bd0e8SStephan Aßmus	fRef(ref),
115eb2bd0e8SStephan Aßmus	fCache(ref->cache),
116eb2bd0e8SStephan Aßmus	fPages(NULL),
117eb2bd0e8SStephan Aßmus	fBusyConditions(NULL),
118eb2bd0e8SStephan Aßmus	fVecs(NULL),
119eb2bd0e8SStephan Aßmus	fOffset(offset),
120a5dbd78bSAxel Dörfler	fVecCount(0),
121eb2bd0e8SStephan Aßmus	fSize(size)
122eb2bd0e8SStephan Aßmus{
123eb2bd0e8SStephan Aßmus	fPageCount = (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE;
124eb2bd0e8SStephan Aßmus	fCache->AcquireRefLocked();
125eb2bd0e8SStephan Aßmus}
126eb2bd0e8SStephan Aßmus
127eb2bd0e8SStephan Aßmus
128eb2bd0e8SStephan AßmusPrecacheIO::~PrecacheIO()
129eb2bd0e8SStephan Aßmus{
130eb2bd0e8SStephan Aßmus	delete[] fPages;
131eb2bd0e8SStephan Aßmus	delete[] fBusyConditions;
132eb2bd0e8SStephan Aßmus	delete[] fVecs;
133eb2bd0e8SStephan Aßmus	fCache->ReleaseRefLocked();
134eb2bd0e8SStephan Aßmus}
135eb2bd0e8SStephan Aßmus
136eb2bd0e8SStephan Aßmus
137eb2bd0e8SStephan Aßmusstatus_t
138a5dbd78bSAxel DörflerPrecacheIO::Prepare()
139eb2bd0e8SStephan Aßmus{
140eb2bd0e8SStephan Aßmus	if (fPageCount == 0)
141eb2bd0e8SStephan Aßmus		return B_BAD_VALUE;
142eb2bd0e8SStephan Aßmus
143eb2bd0e8SStephan Aßmus	fPages = new(std::nothrow) vm_page*[fPageCount];
144eb2bd0e8SStephan Aßmus	if (fPages == NULL)
145eb2bd0e8SStephan Aßmus		return B_NO_MEMORY;
146eb2bd0e8SStephan Aßmus
147eb2bd0e8SStephan Aßmus	fBusyConditions = new(std::nothrow) ConditionVariable[fPageCount];
148eb2bd0e8SStephan Aßmus	if (fBusyConditions == NULL)
149eb2bd0e8SStephan Aßmus		return B_NO_MEMORY;
150eb2bd0e8SStephan Aßmus
151eb2bd0e8SStephan Aßmus	fVecs = new(std::nothrow) iovec[fPageCount];
152eb2bd0e8SStephan Aßmus	if (fVecs == NULL)
153eb2bd0e8SStephan Aßmus		return B_NO_MEMORY;
154eb2bd0e8SStephan Aßmus
155eb2bd0e8SStephan Aßmus	// allocate pages for the cache and mark them busy
156eb2bd0e8SStephan Aßmus	uint32 i = 0;
157eb2bd0e8SStephan Aßmus	for (size_t pos = 0; pos < fSize; pos += B_PAGE_SIZE) {
158d5ad7629SAxel Dörfler		vm_page* page = vm_page_allocate_page(PAGE_STATE_FREE, true);
159eb2bd0e8SStephan Aßmus		if (page == NULL)
160eb2bd0e8SStephan Aßmus			break;
161eb2bd0e8SStephan Aßmus
162d5ad7629SAxel Dörfler		fBusyConditions[i].Publish(page, "page");
163eb2bd0e8SStephan Aßmus		fCache->InsertPage(page, fOffset + pos);
164eb2bd0e8SStephan Aßmus
165a5dbd78bSAxel Dörfler		add_to_iovec(fVecs, fVecCount, fPageCount,
166eb2bd0e8SStephan Aßmus			page->physical_page_number * B_PAGE_SIZE, B_PAGE_SIZE);
167d5ad7629SAxel Dörfler		fPages[i++] = page;
168eb2bd0e8SStephan Aßmus	}
169eb2bd0e8SStephan Aßmus
170eb2bd0e8SStephan Aßmus	if (i != fPageCount) {
171eb2bd0e8SStephan Aßmus		// allocating pages failed
172eb2bd0e8SStephan Aßmus		while (i-- > 0) {
173eb2bd0e8SStephan Aßmus			fBusyConditions[i].Unpublish();
174eb2bd0e8SStephan Aßmus			fCache->RemovePage(fPages[i]);
175eb2bd0e8SStephan Aßmus			vm_page_set_state(fPages[i], PAGE_STATE_FREE);
176eb2bd0e8SStephan Aßmus		}
177eb2bd0e8SStephan Aßmus		return B_NO_MEMORY;
178eb2bd0e8SStephan Aßmus	}
179eb2bd0e8SStephan Aßmus
180a5dbd78bSAxel Dörfler	return B_OK;
181a5dbd78bSAxel Dörfler}
182aff0fbbeSStephan Aßmus
183aff0fbbeSStephan Aßmus
184a5dbd78bSAxel Dörflervoid
185a5dbd78bSAxel DörflerPrecacheIO::ReadAsync()
186a5dbd78bSAxel Dörfler{
187a5dbd78bSAxel Dörfler	// This object is going to be deleted after the I/O request has been
188a5dbd78bSAxel Dörfler	// fulfilled
189a5dbd78bSAxel Dörfler	vfs_asynchronous_read_pages(fRef->vnode, NULL, fOffset, fVecs, fVecCount,
190a5dbd78bSAxel Dörfler		fSize, B_PHYSICAL_IO_REQUEST, this);
191eb2bd0e8SStephan Aßmus}
192eb2bd0e8SStephan Aßmus
193eb2bd0e8SStephan Aßmus
194eb2bd0e8SStephan Aßmusvoid
195eb2bd0e8SStephan AßmusPrecacheIO::IOFinished(status_t status, bool partialTransfer,
196eb2bd0e8SStephan Aßmus	size_t bytesTransferred)
197eb2bd0e8SStephan Aßmus{
198eb2bd0e8SStephan Aßmus	AutoLocker<VMCache> locker(fCache);
199eb2bd0e8SStephan Aßmus
200eb2bd0e8SStephan Aßmus	// Make successfully loaded pages accessible again (partially
201eb2bd0e8SStephan Aßmus	// transferred pages are considered failed)
202c32499b4SAxel Dörfler	size_t pagesTransferred
203c32499b4SAxel Dörfler		= (bytesTransferred + B_PAGE_SIZE - 1) / B_PAGE_SIZE;
204c32499b4SAxel Dörfler
205c32499b4SAxel Dörfler	if (fOffset + bytesTransferred > fCache->virtual_end)
206c32499b4SAxel Dörfler		bytesTransferred = fCache->virtual_end - fOffset;
207c32499b4SAxel Dörfler
208eb2bd0e8SStephan Aßmus	for (uint32 i = 0; i < pagesTransferred; i++) {
209c32499b4SAxel Dörfler		if (i == pagesTransferred - 1
210c32499b4SAxel Dörfler			&& (bytesTransferred % B_PAGE_SIZE) != 0) {
211c32499b4SAxel Dörfler			// clear partial page
212c32499b4SAxel Dörfler			size_t bytesTouched = bytesTransferred % B_PAGE_SIZE;
213c32499b4SAxel Dörfler			vm_memset_physical((fPages[i]->physical_page_number << PAGE_SHIFT)
214c32499b4SAxel Dörfler				+ bytesTouched, 0, B_PAGE_SIZE - bytesTouched);
215c32499b4SAxel Dörfler		}
216c32499b4SAxel Dörfler
217eb2bd0e8SStephan Aßmus		fPages[i]->state = PAGE_STATE_ACTIVE;
218eb2bd0e8SStephan Aßmus		fBusyConditions[i].Unpublish();
219eb2bd0e8SStephan Aßmus	}
220eb2bd0e8SStephan Aßmus
221eb2bd0e8SStephan Aßmus	// Free pages after failed I/O
222eb2bd0e8SStephan Aßmus	for (uint32 i = pagesTransferred; i < fPageCount; i++) {
223eb2bd0e8SStephan Aßmus		fBusyConditions[i].Unpublish();
224eb2bd0e8SStephan Aßmus		fCache->RemovePage(fPages[i]);
225eb2bd0e8SStephan Aßmus		vm_page_set_state(fPages[i], PAGE_STATE_FREE);
226eb2bd0e8SStephan Aßmus	}
227eb2bd0e8SStephan Aßmus
228eb2bd0e8SStephan Aßmus	delete this;
229eb2bd0e8SStephan Aßmus}
230eb2bd0e8SStephan Aßmus
231eb2bd0e8SStephan Aßmus
232eb2bd0e8SStephan Aßmus//	#pragma mark -
233eb2bd0e8SStephan Aßmus
234eb2bd0e8SStephan Aßmus
235f72376a8SAxel Dörflerstatic void
23661b6f38cSAxel Dörfleradd_to_iovec(iovec* vecs, uint32 &index, uint32 max, addr_t address,
237eb2bd0e8SStephan Aßmus	size_t size)
238f72376a8SAxel Dörfler{
239b50494aaSAxel Dörfler	if (index > 0 && (addr_t)vecs[index - 1].iov_base
240b50494aaSAxel Dörfler			+ vecs[index - 1].iov_len == address) {
241f72376a8SAxel Dörfler		// the iovec can be combined with the previous one
242f72376a8SAxel Dörfler		vecs[index - 1].iov_len += size;
243f72376a8SAxel Dörfler		return;
244f72376a8SAxel Dörfler	}
245f72376a8SAxel Dörfler
246139353cfSAxel Dörfler	if (index == max)
247139353cfSAxel Dörfler		panic("no more space for iovecs!");
248139353cfSAxel Dörfler
249f72376a8SAxel Dörfler	// we need to start a new iovec
25061b6f38cSAxel Dörfler	vecs[index].iov_base = (void*)address;
251f72376a8SAxel Dörfler	vecs[index].iov_len = size;
252f72376a8SAxel Dörfler	index++;
253f72376a8SAxel Dörfler}
254f72376a8SAxel Dörfler
255f72376a8SAxel Dörfler
256c6573329SAxel Dörflerstatic inline bool
25761b6f38cSAxel Dörfleraccess_is_sequential(file_cache_ref* ref)
258c6573329SAxel Dörfler{
259c6573329SAxel Dörfler	return ref->last_access[ref->last_access_index] != 0;
260c6573329SAxel Dörfler}
261c6573329SAxel Dörfler
262c6573329SAxel Dörfler
263c6573329SAxel Dörflerstatic inline void
26461b6f38cSAxel Dörflerpush_access(file_cache_ref* ref, off_t offset, size_t bytes, bool isWrite)
265c6573329SAxel Dörfler{
266c6573329SAxel Dörfler	TRACE(("%p: push %Ld, %ld, %s\n", ref, offset, bytes,
267c6573329SAxel Dörfler		isWrite ? "write" : "read"));
268c6573329SAxel Dörfler
269c6573329SAxel Dörfler	int32 index = ref->last_access_index;
270c6573329SAxel Dörfler	int32 previous = index - 1;
271c6573329SAxel Dörfler	if (previous < 0)
272c6573329SAxel Dörfler		previous = LAST_ACCESSES - 1;
273c6573329SAxel Dörfler
2748a26f35aSAxel Dörfler	if (offset != ref->LastAccess(previous, isWrite))
275c6573329SAxel Dörfler		ref->last_access[previous] = 0;
276c6573329SAxel Dörfler
2778a26f35aSAxel Dörfler	ref->SetLastAccess(index, offset + bytes, isWrite);
278c6573329SAxel Dörfler
279c6573329SAxel Dörfler	if (++index >= LAST_ACCESSES)
280c6573329SAxel Dörfler		index = 0;
281c6573329SAxel Dörfler	ref->last_access_index = index;
282c6573329SAxel Dörfler}
283c6573329SAxel Dörfler
284c6573329SAxel Dörfler
285c6573329SAxel Dörflerstatic void
28661b6f38cSAxel Dörflerreserve_pages(file_cache_ref* ref, size_t reservePages, bool isWrite)
287c6573329SAxel Dörfler{
2885c99d639SIngo Weinhold	if (low_resource_state(B_KERNEL_RESOURCE_PAGES) != B_NO_LOW_RESOURCE) {
289a477e3cfSIngo Weinhold		VMCache* cache = ref->cache;
2905c99d639SIngo Weinhold		cache->Lock();
291c6573329SAxel Dörfler
292c6573329SAxel Dörfler		if (list_is_empty(&cache->consumers) && cache->areas == NULL
293c6573329SAxel Dörfler			&& access_is_sequential(ref)) {
294c6573329SAxel Dörfler			// we are not mapped, and we're accessed sequentially
295c6573329SAxel Dörfler
296c6573329SAxel Dörfler			if (isWrite) {
297c6573329SAxel Dörfler				// just schedule some pages to be written back
2988a26f35aSAxel Dörfler				int32 index = ref->last_access_index;
2998a26f35aSAxel Dörfler				int32 previous = index - 1;
3008a26f35aSAxel Dörfler				if (previous < 0)
3018a26f35aSAxel Dörfler					previous = LAST_ACCESSES - 1;
3028a26f35aSAxel Dörfler
3038a26f35aSAxel Dörfler				vm_page_schedule_write_page_range(cache,
3048a26f35aSAxel Dörfler					ref->LastAccessPageOffset(previous, true),
3058a26f35aSAxel Dörfler					ref->LastAccessPageOffset(index, true));
306c6573329SAxel Dörfler			} else {
307c6573329SAxel Dörfler				// free some pages from our cache
308e1b630c5SIngo Weinhold				// TODO: start with oldest
309c6573329SAxel Dörfler				uint32 left = reservePages;
31061b6f38cSAxel Dörfler				vm_page* page;
311e1b630c5SIngo Weinhold				for (VMCachePagesTree::Iterator it = cache->pages.GetIterator();
312e1b630c5SIngo Weinhold						(page = it.Next()) != NULL && left > 0;) {
313c6573329SAxel Dörfler					if (page->state != PAGE_STATE_MODIFIED
314c6573329SAxel Dörfler						&& page->state != PAGE_STATE_BUSY) {
3155c99d639SIngo Weinhold						cache->RemovePage(page);
316c6573329SAxel Dörfler						vm_page_set_state(page, PAGE_STATE_FREE);
317c6573329SAxel Dörfler						left--;
318c6573329SAxel Dörfler					}
319c6573329SAxel Dörfler				}
320c6573329SAxel Dörfler			}
321c6573329SAxel Dörfler		}
3225c99d639SIngo Weinhold		cache->Unlock();
323c6573329SAxel Dörfler	}
324c6573329SAxel Dörfler
325c6573329SAxel Dörfler	vm_page_reserve_pages(reservePages);
326c6573329SAxel Dörfler}
327c6573329SAxel Dörfler
328c6573329SAxel Dörfler
329c32499b4SAxel Dörflerstatic inline status_t
330c32499b4SAxel Dörflerread_pages_and_clear_partial(file_cache_ref* ref, void* cookie, off_t offset,
331c32499b4SAxel Dörfler	const iovec* vecs, size_t count, uint32 flags, size_t* _numBytes)
332c32499b4SAxel Dörfler{
333c32499b4SAxel Dörfler	size_t bytesUntouched = *_numBytes;
334c32499b4SAxel Dörfler
335c32499b4SAxel Dörfler	status_t status = vfs_read_pages(ref->vnode, cookie, offset, vecs, count,
336c32499b4SAxel Dörfler		flags, _numBytes);
337c32499b4SAxel Dörfler
338c32499b4SAxel Dörfler	size_t bytesEnd = *_numBytes;
339c32499b4SAxel Dörfler
340c32499b4SAxel Dörfler	if (offset + bytesEnd > ref->cache->virtual_end)
341c32499b4SAxel Dörfler		bytesEnd = ref->cache->virtual_end - offset;
342c32499b4SAxel Dörfler
343c32499b4SAxel Dörfler	if (status == B_OK && bytesEnd < bytesUntouched) {
344c32499b4SAxel Dörfler		// Clear out any leftovers that were not touched by the above read.
345c32499b4SAxel Dörfler		// We're doing this here so that not every file system/device has to
346c32499b4SAxel Dörfler		// implement this.
347c32499b4SAxel Dörfler		bytesUntouched -= bytesEnd;
348c32499b4SAxel Dörfler
349c32499b4SAxel Dörfler		for (int32 i = count; i-- > 0 && bytesUntouched != 0; ) {
350c32499b4SAxel Dörfler			size_t length = min_c(bytesUntouched, vecs[i].iov_len);
351c32499b4SAxel Dörfler			vm_memset_physical((addr_t)vecs[i].iov_base + vecs[i].iov_len
352c32499b4SAxel Dörfler				- length, 0, length);
353c32499b4SAxel Dörfler
354c32499b4SAxel Dörfler			bytesUntouched -= length;
355c32499b4SAxel Dörfler		}
356c32499b4SAxel Dörfler	}
357c32499b4SAxel Dörfler
358c32499b4SAxel Dörfler	return status;
359c32499b4SAxel Dörfler}
360c32499b4SAxel Dörfler
361c32499b4SAxel Dörfler
3620633dcc2SAxel Dörfler/*!	Reads the requested amount of data into the cache, and allocates
3630633dcc2SAxel Dörfler	pages needed to fulfill that request. This function is called by cache_io().
3640633dcc2SAxel Dörfler	It can only handle a certain amount of bytes, and the caller must make
365061816eeSAxel Dörfler	sure that it matches that criterion.
3661af7d115SMichael Lotz	The cache_ref lock must be held when calling this function; during
3670633dcc2SAxel Dörfler	operation it will unlock the cache, though.
368061816eeSAxel Dörfler*/
3690710d59cSAxel Dörflerstatic status_t
37061b6f38cSAxel Dörflerread_into_cache(file_cache_ref* ref, void* cookie, off_t offset,
371a121b8c8SAxel Dörfler	int32 pageOffset, addr_t buffer, size_t bufferSize, bool useBuffer,
3723d268edaSAxel Dörfler	size_t lastReservedPages, size_t reservePages)
373f72376a8SAxel Dörfler{
374cfe386c2SAxel Dörfler	TRACE(("read_into_cache(offset = %Ld, pageOffset = %ld, buffer = %#lx, "
375cfe386c2SAxel Dörfler		"bufferSize = %lu\n", offset, pageOffset, buffer, bufferSize));
376f72376a8SAxel Dörfler
377a477e3cfSIngo Weinhold	VMCache* cache = ref->cache;
3780f6c560eSAxel Dörfler
379279c6b76SIngo Weinhold	// TODO: We're using way too much stack! Rather allocate a sufficiently
380279c6b76SIngo Weinhold	// large chunk on the heap.
381f72376a8SAxel Dörfler	iovec vecs[MAX_IO_VECS];
382eb2bd0e8SStephan Aßmus	uint32 vecCount = 0;
383f72376a8SAxel Dörfler
384cfe386c2SAxel Dörfler	size_t numBytes = PAGE_ALIGN(pageOffset + bufferSize);
38561b6f38cSAxel Dörfler	vm_page* pages[MAX_IO_VECS];
3866cef245eSIngo Weinhold	ConditionVariable busyConditions[MAX_IO_VECS];
387f72376a8SAxel Dörfler	int32 pageIndex = 0;
388f72376a8SAxel Dörfler
389f72376a8SAxel Dörfler	// allocate pages for the cache and mark them busy
39070a11cecSAxel Dörfler	for (size_t pos = 0; pos < numBytes; pos += B_PAGE_SIZE) {
39161b6f38cSAxel Dörfler		vm_page* page = pages[pageIndex++] = vm_page_allocate_page(
39220b232e9SAxel Dörfler			PAGE_STATE_FREE, true);
393139353cfSAxel Dörfler		if (page == NULL)
394139353cfSAxel Dörfler			panic("no more pages!");
395139353cfSAxel Dörfler
396279c6b76SIngo Weinhold		busyConditions[pageIndex - 1].Publish(page, "page");
397f72376a8SAxel Dörfler
3985c99d639SIngo Weinhold		cache->InsertPage(page, offset + pos);
399f72376a8SAxel Dörfler
4007f12cc54SIngo Weinhold		add_to_iovec(vecs, vecCount, MAX_IO_VECS,
4017f12cc54SIngo Weinhold			page->physical_page_number * B_PAGE_SIZE, B_PAGE_SIZE);
402061816eeSAxel Dörfler			// TODO: check if the array is large enough (currently panics)!
403f72376a8SAxel Dörfler	}
404f72376a8SAxel Dörfler
4053d268edaSAxel Dörfler	push_access(ref, offset, bufferSize, false);
4065c99d639SIngo Weinhold	cache->Unlock();
40720b232e9SAxel Dörfler	vm_page_unreserve_pages(lastReservedPages);
408a1d09631SAxel Dörfler
409f72376a8SAxel Dörfler	// read file into reserved pages
410c32499b4SAxel Dörfler	status_t status = read_pages_and_clear_partial(ref, cookie, offset, vecs,
4117f12cc54SIngo Weinhold		vecCount, B_PHYSICAL_IO_REQUEST, &numBytes);
412db8fb4fdSAxel Dörfler	if (status != B_OK) {
4132b028fcaSAxel Dörfler		// reading failed, free allocated pages
4142b028fcaSAxel Dörfler
4152b028fcaSAxel Dörfler		dprintf("file_cache: read pages failed: %s\n", strerror(status));
4162b028fcaSAxel Dörfler
4175c99d639SIngo Weinhold		cache->Lock();
418b2707997SStephan Aßmus
4192b028fcaSAxel Dörfler		for (int32 i = 0; i < pageIndex; i++) {
420279c6b76SIngo Weinhold			busyConditions[i].Unpublish();
4215c99d639SIngo Weinhold			cache->RemovePage(pages[i]);
4222b028fcaSAxel Dörfler			vm_page_set_state(pages[i], PAGE_STATE_FREE);
4232b028fcaSAxel Dörfler		}
4242b028fcaSAxel Dörfler
425f72376a8SAxel Dörfler		return status;
426f72376a8SAxel Dörfler	}
427f72376a8SAxel Dörfler
428a121b8c8SAxel Dörfler	// copy the pages if needed and unmap them again
429f72376a8SAxel Dörfler
4307f12cc54SIngo Weinhold	for (int32 i = 0; i < pageIndex; i++) {
431a121b8c8SAxel Dörfler		if (useBuffer && bufferSize != 0) {
4327f12cc54SIngo Weinhold			size_t bytes = min_c(bufferSize, (size_t)B_PAGE_SIZE - pageOffset);
4337f12cc54SIngo Weinhold
43447c40a10SIngo Weinhold			vm_memcpy_from_physical((void*)buffer,
43547c40a10SIngo Weinhold				pages[i]->physical_page_number * B_PAGE_SIZE + pageOffset,
43647c40a10SIngo Weinhold				bytes, true);
43747c40a10SIngo Weinhold
438f72376a8SAxel Dörfler			buffer += bytes;
439f72376a8SAxel Dörfler			bufferSize -= bytes;
4400f6c560eSAxel Dörfler			pageOffset = 0;
441b50494aaSAxel Dörfler		}
442f72376a8SAxel Dörfler	}
443f72376a8SAxel Dörfler
444c6573329SAxel Dörfler	reserve_pages(ref, reservePages, false);
4455c99d639SIngo Weinhold	cache->Lock();
446a1d09631SAxel Dörfler
447f72376a8SAxel Dörfler	// make the pages accessible in the cache
448279c6b76SIngo Weinhold	for (int32 i = pageIndex; i-- > 0;) {
449f72376a8SAxel Dörfler		pages[i]->state = PAGE_STATE_ACTIVE;
4506d4aea47SAxel Dörfler
451279c6b76SIngo Weinhold		busyConditions[i].Unpublish();
452279c6b76SIngo Weinhold	}
453f72376a8SAxel Dörfler
454f72376a8SAxel Dörfler	return B_OK;
455f72376a8SAxel Dörfler}
456f72376a8SAxel Dörfler
457f72376a8SAxel Dörfler
458cfe386c2SAxel Dörflerstatic status_t
45961b6f38cSAxel Dörflerread_from_file(file_cache_ref* ref, void* cookie, off_t offset,
460a121b8c8SAxel Dörfler	int32 pageOffset, addr_t buffer, size_t bufferSize, bool useBuffer,
4613d268edaSAxel Dörfler	size_t lastReservedPages, size_t reservePages)
462cfe386c2SAxel Dörfler{
4639ff70e74SAxel Dörfler	TRACE(("read_from_file(offset = %Ld, pageOffset = %ld, buffer = %#lx, "
4649ff70e74SAxel Dörfler		"bufferSize = %lu\n", offset, pageOffset, buffer, bufferSize));
4659ff70e74SAxel Dörfler
466a121b8c8SAxel Dörfler	if (!useBuffer)
467a121b8c8SAxel Dörfler		return B_OK;
468a121b8c8SAxel Dörfler
469cfe386c2SAxel Dörfler	iovec vec;
47061b6f38cSAxel Dörfler	vec.iov_base = (void*)buffer;
471cfe386c2SAxel Dörfler	vec.iov_len = bufferSize;
472cfe386c2SAxel Dörfler
4733d268edaSAxel Dörfler	push_access(ref, offset, bufferSize, false);
4745c99d639SIngo Weinhold	ref->cache->Unlock();
475cfe386c2SAxel Dörfler	vm_page_unreserve_pages(lastReservedPages);
476cfe386c2SAxel Dörfler
4779ff70e74SAxel Dörfler	status_t status = vfs_read_pages(ref->vnode, cookie, offset + pageOffset,
4787f12cc54SIngo Weinhold		&vec, 1, 0, &bufferSize);
4797f12cc54SIngo Weinhold
480cfe386c2SAxel Dörfler	if (status == B_OK)
481cfe386c2SAxel Dörfler		reserve_pages(ref, reservePages, false);
482cfe386c2SAxel Dörfler
4835c99d639SIngo Weinhold	ref->cache->Lock();
484cfe386c2SAxel Dörfler
485cfe386c2SAxel Dörfler	return status;
486cfe386c2SAxel Dörfler}
487cfe386c2