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