xref: /haiku/src/add-ons/kernel/file_systems/packagefs/package/CachedDataReader.cpp (revision c2aac52c17e2f429871d77929d1a24693676bfd0)
146122852SIngo Weinhold /*
232832cbeSIngo Weinhold  * Copyright 2010-2014, Ingo Weinhold, ingo_weinhold@gmx.de.
346122852SIngo Weinhold  * Distributed under the terms of the MIT License.
446122852SIngo Weinhold  */
546122852SIngo Weinhold 
646122852SIngo Weinhold 
746122852SIngo Weinhold #include "CachedDataReader.h"
846122852SIngo Weinhold 
946122852SIngo Weinhold #include <algorithm>
1046122852SIngo Weinhold 
1132832cbeSIngo Weinhold #include <DataIO.h>
1246122852SIngo Weinhold 
1346122852SIngo Weinhold #include <util/AutoLock.h>
1446122852SIngo Weinhold #include <vm/VMCache.h>
1546122852SIngo Weinhold #include <vm/vm_page.h>
1646122852SIngo Weinhold 
1746122852SIngo Weinhold #include "DebugSupport.h"
1846122852SIngo Weinhold 
1946122852SIngo Weinhold 
2046122852SIngo Weinhold using BPackageKit::BHPKG::BBufferDataReader;
2146122852SIngo Weinhold 
2246122852SIngo Weinhold 
2346122852SIngo Weinhold static inline bool
page_physical_number_less(const vm_page * a,const vm_page * b)2446122852SIngo Weinhold page_physical_number_less(const vm_page* a, const vm_page* b)
2546122852SIngo Weinhold {
2646122852SIngo Weinhold 	return a->physical_page_number < b->physical_page_number;
2746122852SIngo Weinhold }
2846122852SIngo Weinhold 
2946122852SIngo Weinhold 
3046122852SIngo Weinhold // #pragma mark - PagesDataOutput
3146122852SIngo Weinhold 
3246122852SIngo Weinhold 
3332832cbeSIngo Weinhold struct CachedDataReader::PagesDataOutput : public BDataIO {
PagesDataOutputCachedDataReader::PagesDataOutput3446122852SIngo Weinhold 	PagesDataOutput(vm_page** pages, size_t pageCount)
3546122852SIngo Weinhold 		:
3646122852SIngo Weinhold 		fPages(pages),
3746122852SIngo Weinhold 		fPageCount(pageCount),
3846122852SIngo Weinhold 		fInPageOffset(0)
3946122852SIngo Weinhold 	{
4046122852SIngo Weinhold 	}
4146122852SIngo Weinhold 
WriteCachedDataReader::PagesDataOutput4232832cbeSIngo Weinhold 	virtual ssize_t Write(const void* buffer, size_t size)
4346122852SIngo Weinhold 	{
4432832cbeSIngo Weinhold 		size_t bytesRemaining = size;
4532832cbeSIngo Weinhold 		while (bytesRemaining > 0) {
4646122852SIngo Weinhold 			if (fPageCount == 0)
4746122852SIngo Weinhold 				return B_BAD_VALUE;
4846122852SIngo Weinhold 
4932832cbeSIngo Weinhold 			size_t toCopy = std::min(bytesRemaining,
5032832cbeSIngo Weinhold 				B_PAGE_SIZE - fInPageOffset);
5146122852SIngo Weinhold 			status_t error = vm_memcpy_to_physical(
5246122852SIngo Weinhold 				fPages[0]->physical_page_number * B_PAGE_SIZE + fInPageOffset,
5346122852SIngo Weinhold 				buffer, toCopy, false);
544c235c74SIngo Weinhold 			if (error != B_OK)
5546122852SIngo Weinhold 				return error;
5646122852SIngo Weinhold 
5746122852SIngo Weinhold 			fInPageOffset += toCopy;
5846122852SIngo Weinhold 			if (fInPageOffset == B_PAGE_SIZE) {
5946122852SIngo Weinhold 				fInPageOffset = 0;
6046122852SIngo Weinhold 				fPages++;
6146122852SIngo Weinhold 				fPageCount--;
6246122852SIngo Weinhold 			}
6346122852SIngo Weinhold 
6446122852SIngo Weinhold 			buffer = (const char*)buffer + toCopy;
6532832cbeSIngo Weinhold 			bytesRemaining -= toCopy;
6646122852SIngo Weinhold 		}
6746122852SIngo Weinhold 
6832832cbeSIngo Weinhold 		return size;
6946122852SIngo Weinhold 	}
7046122852SIngo Weinhold 
7146122852SIngo Weinhold private:
7246122852SIngo Weinhold 	vm_page**	fPages;
7346122852SIngo Weinhold 	size_t		fPageCount;
7446122852SIngo Weinhold 	size_t		fInPageOffset;
7546122852SIngo Weinhold };
7646122852SIngo Weinhold 
7746122852SIngo Weinhold 
7846122852SIngo Weinhold // #pragma mark - CachedDataReader
7946122852SIngo Weinhold 
8046122852SIngo Weinhold 
CachedDataReader()8146122852SIngo Weinhold CachedDataReader::CachedDataReader()
8246122852SIngo Weinhold 	:
8346122852SIngo Weinhold 	fReader(NULL),
8446122852SIngo Weinhold 	fCache(NULL),
8546122852SIngo Weinhold 	fCacheLineLockers()
8646122852SIngo Weinhold {
8746122852SIngo Weinhold 	mutex_init(&fLock, "packagefs cached reader");
8846122852SIngo Weinhold }
8946122852SIngo Weinhold 
9046122852SIngo Weinhold 
~CachedDataReader()9146122852SIngo Weinhold CachedDataReader::~CachedDataReader()
9246122852SIngo Weinhold {
9346122852SIngo Weinhold 	if (fCache != NULL) {
9446122852SIngo Weinhold 		fCache->Lock();
9546122852SIngo Weinhold 		fCache->ReleaseRefAndUnlock();
9646122852SIngo Weinhold 	}
9746122852SIngo Weinhold 
9846122852SIngo Weinhold 	mutex_destroy(&fLock);
9946122852SIngo Weinhold }
10046122852SIngo Weinhold 
10146122852SIngo Weinhold 
10246122852SIngo Weinhold status_t
Init(BAbstractBufferedDataReader * reader,off_t size)10346122852SIngo Weinhold CachedDataReader::Init(BAbstractBufferedDataReader* reader, off_t size)
10446122852SIngo Weinhold {
10546122852SIngo Weinhold 	fReader = reader;
10646122852SIngo Weinhold 
10746122852SIngo Weinhold 	status_t error = fCacheLineLockers.Init();
10846122852SIngo Weinhold 	if (error != B_OK)
10946122852SIngo Weinhold 		RETURN_ERROR(error);
11046122852SIngo Weinhold 
11146122852SIngo Weinhold 	error = VMCacheFactory::CreateNullCache(VM_PRIORITY_SYSTEM,
11246122852SIngo Weinhold 		fCache);
11346122852SIngo Weinhold 	if (error != B_OK)
11446122852SIngo Weinhold 		RETURN_ERROR(error);
11546122852SIngo Weinhold 
116*c2aac52cSAugustin Cavalier 	fCache->virtual_end = size;
11746122852SIngo Weinhold 	return B_OK;
11846122852SIngo Weinhold }
11946122852SIngo Weinhold 
12046122852SIngo Weinhold 
12146122852SIngo Weinhold status_t
ReadDataToOutput(off_t offset,size_t size,BDataIO * output)12246122852SIngo Weinhold CachedDataReader::ReadDataToOutput(off_t offset, size_t size,
12332832cbeSIngo Weinhold 	BDataIO* output)
12446122852SIngo Weinhold {
12546122852SIngo Weinhold 	if (offset > fCache->virtual_end
12646122852SIngo Weinhold 		|| (off_t)size > fCache->virtual_end - offset) {
12746122852SIngo Weinhold 		return B_BAD_VALUE;
12846122852SIngo Weinhold 	}
12946122852SIngo Weinhold 
13046122852SIngo Weinhold 	if (size == 0)
13146122852SIngo Weinhold 		return B_OK;
13246122852SIngo Weinhold 
13346122852SIngo Weinhold 	while (size > 0) {
13446122852SIngo Weinhold 		// the start of the current cache line
13546122852SIngo Weinhold 		off_t lineOffset = (offset / kCacheLineSize) * kCacheLineSize;
13646122852SIngo Weinhold 
13746122852SIngo Weinhold 		// intersection of request and cache line
13846122852SIngo Weinhold 		off_t cacheLineEnd = std::min(lineOffset + (off_t)kCacheLineSize,
13946122852SIngo Weinhold 			fCache->virtual_end);
14046122852SIngo Weinhold 		size_t requestLineLength
14146122852SIngo Weinhold 			= std::min(cacheLineEnd - offset, (off_t)size);
14246122852SIngo Weinhold 
14346122852SIngo Weinhold 		// transfer the data of the cache line
14446122852SIngo Weinhold 		status_t error = _ReadCacheLine(lineOffset, cacheLineEnd - lineOffset,
14546122852SIngo Weinhold 			offset, requestLineLength, output);
1464c235c74SIngo Weinhold 		if (error != B_OK)
14746122852SIngo Weinhold 			return error;
14846122852SIngo Weinhold 
14946122852SIngo Weinhold 		offset = cacheLineEnd;
15046122852SIngo Weinhold 		size -= requestLineLength;
15146122852SIngo Weinhold 	}
15246122852SIngo Weinhold 
15346122852SIngo Weinhold 	return B_OK;
15446122852SIngo Weinhold }
15546122852SIngo Weinhold 
15646122852SIngo Weinhold 
15746122852SIngo Weinhold status_t
_ReadCacheLine(off_t lineOffset,size_t lineSize,off_t requestOffset,size_t requestLength,BDataIO * output)15846122852SIngo Weinhold CachedDataReader::_ReadCacheLine(off_t lineOffset, size_t lineSize,
15932832cbeSIngo Weinhold 	off_t requestOffset, size_t requestLength, BDataIO* output)
16046122852SIngo Weinhold {
16146122852SIngo Weinhold 	PRINT("CachedDataReader::_ReadCacheLine(%" B_PRIdOFF ", %zu, %" B_PRIdOFF
16246122852SIngo Weinhold 		", %zu, %p\n", lineOffset, lineSize, requestOffset, requestLength,
16346122852SIngo Weinhold 		output);
16446122852SIngo Weinhold 
16546122852SIngo Weinhold 	CacheLineLocker cacheLineLocker(this, lineOffset);
16646122852SIngo Weinhold 
16746122852SIngo Weinhold 	// check whether there are pages of the cache line and the mark them used
16846122852SIngo Weinhold 	page_num_t firstPageOffset = lineOffset / B_PAGE_SIZE;
16946122852SIngo Weinhold 	page_num_t linePageCount = (lineSize + B_PAGE_SIZE - 1) / B_PAGE_SIZE;
17046122852SIngo Weinhold 	vm_page* pages[kPagesPerCacheLine] = {};
17146122852SIngo Weinhold 
17246122852SIngo Weinhold 	AutoLocker<VMCache> cacheLocker(fCache);
17346122852SIngo Weinhold 
17446122852SIngo Weinhold 	page_num_t firstMissing = 0;
17546122852SIngo Weinhold 	page_num_t lastMissing = 0;
17646122852SIngo Weinhold 	page_num_t missingPages = 0;
17746122852SIngo Weinhold 	page_num_t pageOffset = firstPageOffset;
17846122852SIngo Weinhold 
17946122852SIngo Weinhold 	VMCachePagesTree::Iterator it = fCache->pages.GetIterator(pageOffset, true,
18046122852SIngo Weinhold 		true);
18146122852SIngo Weinhold 	while (pageOffset < firstPageOffset + linePageCount) {
18246122852SIngo Weinhold 		vm_page* page = it.Next();
18346122852SIngo Weinhold 		page_num_t currentPageOffset;
18446122852SIngo Weinhold 		if (page == NULL
18546122852SIngo Weinhold 			|| page->cache_offset >= firstPageOffset + linePageCount) {
18646122852SIngo Weinhold 			page = NULL;
18746122852SIngo Weinhold 			currentPageOffset = firstPageOffset + linePageCount;
18846122852SIngo Weinhold 		} else
18946122852SIngo Weinhold 			currentPageOffset = page->cache_offset;
19046122852SIngo Weinhold 
19146122852SIngo Weinhold 		if (pageOffset < currentPageOffset) {
19246122852SIngo Weinhold 			// pages are missing
19346122852SIngo Weinhold 			if (missingPages == 0)
19446122852SIngo Weinhold 				firstMissing = pageOffset;
19546122852SIngo Weinhold 			lastMissing = currentPageOffset - 1;
19646122852SIngo Weinhold 			missingPages += currentPageOffset - pageOffset;
19746122852SIngo Weinhold 
19846122852SIngo Weinhold 			for (; pageOffset < currentPageOffset; pageOffset++)
19946122852SIngo Weinhold 				pages[pageOffset - firstPageOffset] = NULL;
20046122852SIngo Weinhold 		}
20146122852SIngo Weinhold 
20246122852SIngo Weinhold 		if (page != NULL) {
20346122852SIngo Weinhold 			pages[pageOffset++ - firstPageOffset] = page;
20446122852SIngo Weinhold 			DEBUG_PAGE_ACCESS_START(page);
20546122852SIngo Weinhold 			vm_page_set_state(page, PAGE_STATE_UNUSED);
20646122852SIngo Weinhold 			DEBUG_PAGE_ACCESS_END(page);
20746122852SIngo Weinhold 		}
20846122852SIngo Weinhold 	}
20946122852SIngo Weinhold 
21046122852SIngo Weinhold 	cacheLocker.Unlock();
21146122852SIngo Weinhold 
21246122852SIngo Weinhold 	if (missingPages > 0) {
213bd6434b6SMáximo Castañeda // TODO: If the missing pages range doesn't intersect with the request, just
214bd6434b6SMáximo Castañeda // satisfy the request and don't read anything at all.
215bd6434b6SMáximo Castañeda 		// There are pages of the cache line missing. We have to allocate fresh
216bd6434b6SMáximo Castañeda 		// ones.
21746122852SIngo Weinhold 
21846122852SIngo Weinhold 		// reserve
21946122852SIngo Weinhold 		vm_page_reservation reservation;
22046122852SIngo Weinhold 		if (!vm_page_try_reserve_pages(&reservation, missingPages,
22146122852SIngo Weinhold 				VM_PRIORITY_SYSTEM)) {
22246122852SIngo Weinhold 			_DiscardPages(pages, firstMissing - firstPageOffset, missingPages);
22346122852SIngo Weinhold 
22446122852SIngo Weinhold 			// fall back to uncached transfer
22546122852SIngo Weinhold 			return fReader->ReadDataToOutput(requestOffset, requestLength,
22646122852SIngo Weinhold 				output);
22746122852SIngo Weinhold 		}
22846122852SIngo Weinhold 
22946122852SIngo Weinhold 		// Allocate the missing pages and remove the already existing pages in
23046122852SIngo Weinhold 		// the range from the cache. We're going to read/write the whole range
23146122852SIngo Weinhold 		// anyway.
23246122852SIngo Weinhold 		for (pageOffset = firstMissing; pageOffset <= lastMissing;
23346122852SIngo Weinhold 				pageOffset++) {
23446122852SIngo Weinhold 			page_num_t index = pageOffset - firstPageOffset;
23546122852SIngo Weinhold 			if (pages[index] == NULL) {
23646122852SIngo Weinhold 				pages[index] = vm_page_allocate_page(&reservation,
23746122852SIngo Weinhold 					PAGE_STATE_UNUSED);
23846122852SIngo Weinhold 				DEBUG_PAGE_ACCESS_END(pages[index]);
23946122852SIngo Weinhold 			} else {
24046122852SIngo Weinhold 				cacheLocker.Lock();
24146122852SIngo Weinhold 				fCache->RemovePage(pages[index]);
24246122852SIngo Weinhold 				cacheLocker.Unlock();
24346122852SIngo Weinhold 			}
24446122852SIngo Weinhold 		}
24546122852SIngo Weinhold 
24646122852SIngo Weinhold 		missingPages = lastMissing - firstMissing + 1;
24746122852SIngo Weinhold 
24846122852SIngo Weinhold 		// add the pages to the cache
24946122852SIngo Weinhold 		cacheLocker.Lock();
25046122852SIngo Weinhold 
25146122852SIngo Weinhold 		for (pageOffset = firstMissing; pageOffset <= lastMissing;
25246122852SIngo Weinhold 				pageOffset++) {
25346122852SIngo Weinhold 			page_num_t index = pageOffset - firstPageOffset;
25446122852SIngo Weinhold 			fCache->InsertPage(pages[index], (off_t)pageOffset * B_PAGE_SIZE);
25546122852SIngo Weinhold 		}
25646122852SIngo Weinhold 
25746122852SIngo Weinhold 		cacheLocker.Unlock();
25846122852SIngo Weinhold 
25946122852SIngo Weinhold 		// read in the missing pages
26046122852SIngo Weinhold 		status_t error = _ReadIntoPages(pages, firstMissing - firstPageOffset,
26146122852SIngo Weinhold 			missingPages);
26246122852SIngo Weinhold 		if (error != B_OK) {
26346122852SIngo Weinhold 			ERROR("CachedDataReader::_ReadCacheLine(): Failed to read into "
26446122852SIngo Weinhold 				"cache (offset: %" B_PRIdOFF ", length: %" B_PRIuSIZE "), "
26546122852SIngo Weinhold 				"trying uncached read (offset: %" B_PRIdOFF ", length: %"
26646122852SIngo Weinhold 				B_PRIuSIZE ")\n", (off_t)firstMissing * B_PAGE_SIZE,
26746122852SIngo Weinhold 				(size_t)missingPages * B_PAGE_SIZE, requestOffset,
26846122852SIngo Weinhold 				requestLength);
26946122852SIngo Weinhold 
27046122852SIngo Weinhold 			_DiscardPages(pages, firstMissing - firstPageOffset, missingPages);
27146122852SIngo Weinhold 
27246122852SIngo Weinhold 			// Try again using an uncached transfer
27346122852SIngo Weinhold 			return fReader->ReadDataToOutput(requestOffset, requestLength,
27446122852SIngo Weinhold 				output);
27546122852SIngo Weinhold 		}
27646122852SIngo Weinhold 	}
27746122852SIngo Weinhold 
27846122852SIngo Weinhold 	// write data to output
27946122852SIngo Weinhold 	status_t error = _WritePages(pages, requestOffset - lineOffset,
28046122852SIngo Weinhold 		requestLength, output);
28146122852SIngo Weinhold 	_CachePages(pages, 0, linePageCount);
28246122852SIngo Weinhold 	return error;
28346122852SIngo Weinhold }
28446122852SIngo Weinhold 
28546122852SIngo Weinhold 
28646122852SIngo Weinhold /*!	Frees all pages in given range of the \a pages array.
28746122852SIngo Weinhold 	\c NULL entries in the range are OK. All non \c NULL entries must refer
28846122852SIngo Weinhold 	to pages with \c PAGE_STATE_UNUSED. The pages may belong to \c fCache or
28946122852SIngo Weinhold 	may not have a cache.
29046122852SIngo Weinhold 	\c fCache must not be locked.
29146122852SIngo Weinhold */
29246122852SIngo Weinhold void
_DiscardPages(vm_page ** pages,size_t firstPage,size_t pageCount)29346122852SIngo Weinhold CachedDataReader::_DiscardPages(vm_page** pages, size_t firstPage,
29446122852SIngo Weinhold 	size_t pageCount)
29546122852SIngo Weinhold {
29646122852SIngo Weinhold 	PRINT("%p->CachedDataReader::_DiscardPages(%" B_PRIuSIZE ", %" B_PRIuSIZE
29746122852SIngo Weinhold 		")\n", this, firstPage, pageCount);
29846122852SIngo Weinhold 
29946122852SIngo Weinhold 	AutoLocker<VMCache> cacheLocker(fCache);
30046122852SIngo Weinhold 
30146122852SIngo Weinhold 	for (size_t i = firstPage; i < firstPage + pageCount; i++) {
30246122852SIngo Weinhold 		vm_page* page = pages[i];
30346122852SIngo Weinhold 		if (page == NULL)
30446122852SIngo Weinhold 			continue;
30546122852SIngo Weinhold 
30646122852SIngo Weinhold 		DEBUG_PAGE_ACCESS_START(page);
30746122852SIngo Weinhold 
30846122852SIngo Weinhold 		ASSERT_PRINT(page->State() == PAGE_STATE_UNUSED,
30946122852SIngo Weinhold 			"page: %p @! page -m %p", page, page);
31046122852SIngo Weinhold 
31146122852SIngo Weinhold 		if (page->Cache() != NULL)
31246122852SIngo Weinhold 			fCache->RemovePage(page);
31346122852SIngo Weinhold 
31446122852SIngo Weinhold 		vm_page_free(NULL, page);
31546122852SIngo Weinhold 	}
31646122852SIngo Weinhold }
31746122852SIngo Weinhold 
31846122852SIngo Weinhold 
31946122852SIngo Weinhold /*!	Marks all pages in the given range of the \a pages array cached.
32046122852SIngo Weinhold 	There must not be any \c NULL entries in the given array range. All pages
32146122852SIngo Weinhold 	must belong to \c cache and have state \c PAGE_STATE_UNUSED.
32246122852SIngo Weinhold 	\c fCache must not be locked.
32346122852SIngo Weinhold */
32446122852SIngo Weinhold void
_CachePages(vm_page ** pages,size_t firstPage,size_t pageCount)32546122852SIngo Weinhold CachedDataReader::_CachePages(vm_page** pages, size_t firstPage,
32646122852SIngo Weinhold 	size_t pageCount)
32746122852SIngo Weinhold {
32846122852SIngo Weinhold 	PRINT("%p->CachedDataReader::_CachePages(%" B_PRIuSIZE ", %" B_PRIuSIZE
32946122852SIngo Weinhold 		")\n", this, firstPage, pageCount);
33046122852SIngo Weinhold 
33146122852SIngo Weinhold 	AutoLocker<VMCache> cacheLocker(fCache);
33246122852SIngo Weinhold 
33346122852SIngo Weinhold 	for (size_t i = firstPage; i < firstPage + pageCount; i++) {
33446122852SIngo Weinhold 		vm_page* page = pages[i];
33546122852SIngo Weinhold 		ASSERT(page != NULL);
33646122852SIngo Weinhold 		ASSERT_PRINT(page->State() == PAGE_STATE_UNUSED
33746122852SIngo Weinhold 				&& page->Cache() == fCache,
33846122852SIngo Weinhold 			"page: %p @! page -m %p", page, page);
33946122852SIngo Weinhold 
34046122852SIngo Weinhold 		DEBUG_PAGE_ACCESS_START(page);
34146122852SIngo Weinhold 		vm_page_set_state(page, PAGE_STATE_CACHED);
34246122852SIngo Weinhold 		DEBUG_PAGE_ACCESS_END(page);
34346122852SIngo Weinhold 	}
34446122852SIngo Weinhold }
34546122852SIngo Weinhold 
34646122852SIngo Weinhold 
34746122852SIngo Weinhold /*!	Writes the contents of pages in \c pages to \a output.
34846122852SIngo Weinhold 	\param pages The pages array.
34946122852SIngo Weinhold 	\param pagesRelativeOffset The offset relative to \a pages[0] where to
35046122852SIngo Weinhold 		start writing from.
35146122852SIngo Weinhold 	\param requestLength The number of bytes to write.
35246122852SIngo Weinhold 	\param output The output to which the data shall be written.
35346122852SIngo Weinhold 	\return \c B_OK, if writing went fine, another error code otherwise.
35446122852SIngo Weinhold */
35546122852SIngo Weinhold status_t
_WritePages(vm_page ** pages,size_t pagesRelativeOffset,size_t requestLength,BDataIO * output)35646122852SIngo Weinhold CachedDataReader::_WritePages(vm_page** pages, size_t pagesRelativeOffset,
35732832cbeSIngo Weinhold 	size_t requestLength, BDataIO* output)
35846122852SIngo Weinhold {
35946122852SIngo Weinhold 	PRINT("%p->CachedDataReader::_WritePages(%" B_PRIuSIZE ", %" B_PRIuSIZE
36046122852SIngo Weinhold 		", %p)\n", this, pagesRelativeOffset, requestLength, output);
36146122852SIngo Weinhold 
36246122852SIngo Weinhold 	size_t firstPage = pagesRelativeOffset / B_PAGE_SIZE;
36346122852SIngo Weinhold 	size_t endPage = (pagesRelativeOffset + requestLength + B_PAGE_SIZE - 1)
36446122852SIngo Weinhold 		/ B_PAGE_SIZE;
36546122852SIngo Weinhold 
36646122852SIngo Weinhold 	// fallback to copying individual pages
36746122852SIngo Weinhold 	size_t inPageOffset = pagesRelativeOffset % B_PAGE_SIZE;
36846122852SIngo Weinhold 	for (size_t i = firstPage; i < endPage; i++) {
36946122852SIngo Weinhold 		// map the page
37046122852SIngo Weinhold 		void* handle;
37146122852SIngo Weinhold 		addr_t address;
37246122852SIngo Weinhold 		status_t error = vm_get_physical_page(
37346122852SIngo Weinhold 			pages[i]->physical_page_number * B_PAGE_SIZE, &address,
37446122852SIngo Weinhold 			&handle);
37546122852SIngo Weinhold 		if (error != B_OK)
37646122852SIngo Weinhold 			return error;
37746122852SIngo Weinhold 
37846122852SIngo Weinhold 		// write the page's data
37946122852SIngo Weinhold 		size_t toCopy = std::min(B_PAGE_SIZE - inPageOffset, requestLength);
38032832cbeSIngo Weinhold 		error = output->WriteExactly((uint8*)(address + inPageOffset), toCopy);
38146122852SIngo Weinhold 
38246122852SIngo Weinhold 		// unmap the page
38346122852SIngo Weinhold 		vm_put_physical_page(address, handle);
38446122852SIngo Weinhold 
3854c235c74SIngo Weinhold 		if (error != B_OK)
38646122852SIngo Weinhold 			return error;
38746122852SIngo Weinhold 
38846122852SIngo Weinhold 		inPageOffset = 0;
38946122852SIngo Weinhold 		requestLength -= toCopy;
39046122852SIngo Weinhold 	}
39146122852SIngo Weinhold 
39246122852SIngo Weinhold 	return B_OK;
39346122852SIngo Weinhold }
39446122852SIngo Weinhold 
39546122852SIngo Weinhold 
39646122852SIngo Weinhold status_t
_ReadIntoPages(vm_page ** pages,size_t firstPage,size_t pageCount)39746122852SIngo Weinhold CachedDataReader::_ReadIntoPages(vm_page** pages, size_t firstPage,
39846122852SIngo Weinhold 	size_t pageCount)
39946122852SIngo Weinhold {
40046122852SIngo Weinhold 	PagesDataOutput output(pages + firstPage, pageCount);
40146122852SIngo Weinhold 
40246122852SIngo Weinhold 	off_t firstPageOffset = (off_t)pages[firstPage]->cache_offset
40346122852SIngo Weinhold 		* B_PAGE_SIZE;
40446122852SIngo Weinhold 	generic_size_t requestLength = std::min(
40546122852SIngo Weinhold 			firstPageOffset + (off_t)pageCount * B_PAGE_SIZE,
40646122852SIngo Weinhold 			fCache->virtual_end)
40746122852SIngo Weinhold 		- firstPageOffset;
40846122852SIngo Weinhold 
40946122852SIngo Weinhold 	return fReader->ReadDataToOutput(firstPageOffset, requestLength, &output);
41046122852SIngo Weinhold }
41146122852SIngo Weinhold 
41246122852SIngo Weinhold 
41346122852SIngo Weinhold void
_LockCacheLine(CacheLineLocker * lineLocker)41446122852SIngo Weinhold CachedDataReader::_LockCacheLine(CacheLineLocker* lineLocker)
41546122852SIngo Weinhold {
41646122852SIngo Weinhold 	MutexLocker locker(fLock);
41746122852SIngo Weinhold 
41846122852SIngo Weinhold 	CacheLineLocker* otherLineLocker
41946122852SIngo Weinhold 		= fCacheLineLockers.Lookup(lineLocker->Offset());
42046122852SIngo Weinhold 	if (otherLineLocker == NULL) {
42146122852SIngo Weinhold 		fCacheLineLockers.Insert(lineLocker);
42246122852SIngo Weinhold 		return;
42346122852SIngo Weinhold 	}
42446122852SIngo Weinhold 
42546122852SIngo Weinhold 	// queue and wait
42646122852SIngo Weinhold 	otherLineLocker->Queue().Add(lineLocker);
42746122852SIngo Weinhold 	lineLocker->Wait(fLock);
42846122852SIngo Weinhold }
42946122852SIngo Weinhold 
43046122852SIngo Weinhold 
43146122852SIngo Weinhold void
_UnlockCacheLine(CacheLineLocker * lineLocker)43246122852SIngo Weinhold CachedDataReader::_UnlockCacheLine(CacheLineLocker* lineLocker)
43346122852SIngo Weinhold {
43446122852SIngo Weinhold 	MutexLocker locker(fLock);
43546122852SIngo Weinhold 
43646122852SIngo Weinhold 	fCacheLineLockers.Remove(lineLocker);
43746122852SIngo Weinhold 
43846122852SIngo Weinhold 	if (CacheLineLocker* nextLineLocker = lineLocker->Queue().RemoveHead()) {
43946122852SIngo Weinhold 		nextLineLocker->Queue().MoveFrom(&lineLocker->Queue());
44046122852SIngo Weinhold 		fCacheLineLockers.Insert(nextLineLocker);
44146122852SIngo Weinhold 		nextLineLocker->WakeUp();
44246122852SIngo Weinhold 	}
44346122852SIngo Weinhold }
444