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 Weinholdusing BPackageKit::BHPKG::BBufferDataReader;
2146122852SIngo Weinhold
2246122852SIngo Weinhold
2346122852SIngo Weinholdstatic inline bool
2446122852SIngo Weinholdpage_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 Weinholdstruct CachedDataReader::PagesDataOutput : public BDataIO {
3446122852SIngo 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
4232832cbeSIngo 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 Weinholdprivate:
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
8146122852SIngo WeinholdCachedDataReader::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
9146122852SIngo WeinholdCachedDataReader::~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 Weinholdstatus_t
10346122852SIngo WeinholdCachedDataReader::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
11646122852SIngo Weinhold	AutoLocker<VMCache> locker(fCache);
11746122852SIngo Weinhold
11846122852SIngo Weinhold	error = fCache->Resize(size, VM_PRIORITY_SYSTEM);
11946122852SIngo Weinhold	if (error != B_OK)
12046122852SIngo Weinhold		RETURN_ERROR(error);
12146122852SIngo Weinhold
12246122852SIngo Weinhold	return B_OK;
12346122852SIngo Weinhold}
12446122852SIngo Weinhold
12546122852SIngo Weinhold
12646122852SIngo Weinholdstatus_t
12746122852SIngo WeinholdCachedDataReader::ReadDataToOutput(off_t offset, size_t size,
12832832cbeSIngo Weinhold	BDataIO* output)
12946122852SIngo Weinhold{
13046122852SIngo Weinhold	if (offset > fCache->virtual_end
13146122852SIngo Weinhold		|| (off_t)size > fCache->virtual_end - offset) {
13246122852SIngo Weinhold		return B_BAD_VALUE;
13346122852SIngo Weinhold	}
13446122852SIngo Weinhold
13546122852SIngo Weinhold	if (size == 0)
13646122852SIngo Weinhold		return B_OK;
13746122852SIngo Weinhold
13846122852SIngo Weinhold	while (size > 0) {
13946122852SIngo Weinhold		// the start of the current cache line
14046122852SIngo Weinhold		off_t lineOffset = (offset / kCacheLineSize) * kCacheLineSize;
14146122852SIngo Weinhold
14246122852SIngo Weinhold		// intersection of request and cache line
14346122852SIngo Weinhold		off_t cacheLineEnd = std::min(lineOffset + (off_t)kCacheLineSize,
14446122852SIngo Weinhold			fCache->virtual_end);
14546122852SIngo Weinhold		size_t requestLineLength
14646122852SIngo Weinhold			= std::min(cacheLineEnd - offset, (off_t)size);
14746122852SIngo Weinhold
14846122852SIngo Weinhold		// transfer the data of the cache line
14946122852SIngo Weinhold		status_t error = _ReadCacheLine(lineOffset, cacheLineEnd - lineOffset,
15046122852SIngo Weinhold			offset, requestLineLength, output);
1514c235c74SIngo Weinhold		if (error != B_OK)
15246122852SIngo Weinhold			return error;
15346122852SIngo Weinhold
15446122852SIngo Weinhold		offset = cacheLineEnd;
15546122852SIngo Weinhold		size -= requestLineLength;
15646122852SIngo Weinhold	}
15746122852SIngo Weinhold
15846122852SIngo Weinhold	return B_OK;
15946122852SIngo Weinhold}
16046122852SIngo Weinhold
16146122852SIngo Weinhold
16246122852SIngo Weinholdstatus_t
16346122852SIngo WeinholdCachedDataReader::_ReadCacheLine(off_t lineOffset, size_t lineSize,
16432832cbeSIngo Weinhold	off_t requestOffset, size_t requestLength, BDataIO* output)
16546122852SIngo Weinhold{
16646122852SIngo Weinhold	PRINT("CachedDataReader::_ReadCacheLine(%" B_PRIdOFF ", %zu, %" B_PRIdOFF
16746122852SIngo Weinhold		", %zu, %p\n", lineOffset, lineSize, requestOffset, requestLength,
16846122852SIngo Weinhold		output);
16946122852SIngo Weinhold
17046122852SIngo Weinhold	CacheLineLocker cacheLineLocker(this, lineOffset);
17146122852SIngo Weinhold
17246122852SIngo Weinhold	// check whether there are pages of the cache line and the mark them used
17346122852SIngo Weinhold	page_num_t firstPageOffset = lineOffset / B_PAGE_SIZE;
17446122852SIngo Weinhold	page_num_t linePageCount = (lineSize + B_PAGE_SIZE - 1) / B_PAGE_SIZE;
17546122852SIngo Weinhold	vm_page* pages[kPagesPerCacheLine] = {};
17646122852SIngo Weinhold
17746122852SIngo Weinhold	AutoLocker<VMCache> cacheLocker(fCache);
17846122852SIngo Weinhold
17946122852SIngo Weinhold	page_num_t firstMissing = 0;
18046122852SIngo Weinhold	page_num_t lastMissing = 0;
18146122852SIngo Weinhold	page_num_t missingPages = 0;
18246122852SIngo Weinhold	page_num_t pageOffset = firstPageOffset;
18346122852SIngo Weinhold
18446122852SIngo Weinhold	VMCachePagesTree::Iterator it = fCache->pages.GetIterator(pageOffset, true,
18546122852SIngo Weinhold		true);
18646122852SIngo Weinhold	while (pageOffset < firstPageOffset + linePageCount) {
18746122852SIngo Weinhold		vm_page* page = it.Next();
18846122852SIngo Weinhold		page_num_t currentPageOffset;
18946122852SIngo Weinhold		if (page == NULL
19046122852SIngo Weinhold			|| page->cache_offset >= firstPageOffset + linePageCount) {
19146122852SIngo Weinhold			page = NULL;
19246122852SIngo Weinhold			currentPageOffset = firstPageOffset + linePageCount;
19346122852SIngo Weinhold		} else
19446122852SIngo Weinhold			currentPageOffset = page->cache_offset;
19546122852SIngo Weinhold
19646122852SIngo Weinhold		if (pageOffset < currentPageOffset) {
19746122852SIngo Weinhold			// pages are missing
19846122852SIngo Weinhold			if (missingPages == 0)
19946122852SIngo Weinhold				firstMissing = pageOffset;
20046122852SIngo Weinhold			lastMissing = currentPageOffset - 1;
20146122852SIngo Weinhold			missingPages += currentPageOffset - pageOffset;
20246122852SIngo Weinhold
20346122852SIngo Weinhold			for (; pageOffset < currentPageOffset; pageOffset++)
20446122852SIngo Weinhold				pages[pageOffset - firstPageOffset] = NULL;
20546122852SIngo Weinhold		}
20646122852SIngo Weinhold
20746122852SIngo Weinhold		if (page != NULL) {
20846122852SIngo Weinhold			pages[pageOffset++ - firstPageOffset] = page;
20946122852SIngo Weinhold			DEBUG_PAGE_ACCESS_START(page);
21046122852SIngo Weinhold			vm_page_set_state(page, PAGE_STATE_UNUSED);
21146122852SIngo Weinhold			DEBUG_PAGE_ACCESS_END(page);
21246122852SIngo Weinhold		}
21346122852SIngo Weinhold	}
21446122852SIngo Weinhold
21546122852SIngo Weinhold	cacheLocker.Unlock();
21646122852SIngo Weinhold
21746122852SIngo Weinhold	if (missingPages > 0) {
21846122852SIngo Weinhold// TODO: If the missing pages range doesn't intersect with the request, just
21946122852SIngo Weinhold// satisfy the request and don't read anything at all.
22046122852SIngo Weinhold		// There are pages of the cache line missing. We have to allocate fresh
22146122852SIngo Weinhold		// ones.
22246122852SIngo Weinhold
22346122852SIngo Weinhold		// reserve
22446122852SIngo Weinhold		vm_page_reservation reservation;
22546122852SIngo Weinhold		if (!vm_page_try_reserve_pages(&reservation, missingPages,
22646122852SIngo Weinhold				VM_PRIORITY_SYSTEM)) {
22746122852SIngo Weinhold			_DiscardPages(pages, firstMissing - firstPageOffset, missingPages);
22846122852SIngo Weinhold
22946122852SIngo Weinhold			// fall back to uncached transfer
23046122852SIngo Weinhold			return fReader->ReadDataToOutput(requestOffset, requestLength,
23146122852SIngo Weinhold				output);
23246122852SIngo Weinhold		}
23346122852SIngo Weinhold
23446122852SIngo Weinhold		// Allocate the missing pages and remove the already existing pages in
23546122852SIngo Weinhold		// the range from the cache. We're going to read/write the whole range
23646122852SIngo Weinhold		// anyway.
23746122852SIngo Weinhold		for (pageOffset = firstMissing; pageOffset <= lastMissing;
23846122852SIngo Weinhold				pageOffset++) {
23946122852SIngo Weinhold			page_num_t index = pageOffset - firstPageOffset;
24046122852SIngo Weinhold			if (pages[index] == NULL) {
24146122852SIngo Weinhold				pages[index] = vm_page_allocate_page(&reservation,
24246122852SIngo Weinhold					PAGE_STATE_UNUSED);
24346122852SIngo Weinhold				DEBUG_PAGE_ACCESS_END(pages[index]);
24446122852SIngo Weinhold			} else {
24546122852SIngo Weinhold				cacheLocker.Lock();
24646122852SIngo Weinhold				fCache->RemovePage(pages[index]);
24746122852SIngo Weinhold				cacheLocker.Unlock();
24846122852SIngo Weinhold			}
24946122852SIngo Weinhold		}
25046122852SIngo Weinhold
25146122852SIngo Weinhold		missingPages = lastMissing - firstMissing + 1;
25246122852SIngo Weinhold
25346122852SIngo Weinhold		// add the pages to the cache
25446122852SIngo Weinhold		cacheLocker.Lock();
25546122852SIngo Weinhold
25646122852SIngo Weinhold		for (pageOffset = firstMissing; pageOffset <= lastMissing;
25746122852SIngo Weinhold				pageOffset++) {
25846122852SIngo Weinhold			page_num_t index = pageOffset - firstPageOffset;
25946122852SIngo Weinhold			fCache->InsertPage(pages[index], (off_t)pageOffset * B_PAGE_SIZE);
26046122852SIngo Weinhold		}
26146122852SIngo Weinhold
26246122852SIngo Weinhold		cacheLocker.Unlock();
26346122852SIngo Weinhold
26446122852SIngo Weinhold		// read in the missing pages
26546122852SIngo Weinhold		status_t error = _ReadIntoPages(pages, firstMissing - firstPageOffset,
26646122852SIngo Weinhold			missingPages);
26746122852SIngo Weinhold		if (error != B_OK) {
26846122852SIngo Weinhold			ERROR("CachedDataReader::_ReadCacheLine(): Failed to read into "
26946122852SIngo Weinhold				"cache (offset: %" B_PRIdOFF ", length: %" B_PRIuSIZE "), "
27046122852SIngo Weinhold				"trying uncached read (offset: %" B_PRIdOFF ", length: %"
27146122852SIngo Weinhold				B_PRIuSIZE ")\n", (off_t)firstMissing * B_PAGE_SIZE,
27246122852SIngo Weinhold				(size_t)missingPages * B_PAGE_SIZE, requestOffset,
27346122852SIngo Weinhold				requestLength);
27446122852SIngo Weinhold
27546122852SIngo Weinhold			_DiscardPages(pages, firstMissing - firstPageOffset, missingPages);
27646122852SIngo Weinhold
27746122852SIngo Weinhold			// Try again using an uncached transfer
27846122852SIngo Weinhold			return fReader->ReadDataToOutput(requestOffset, requestLength,
27946122852SIngo Weinhold				output);
28046122852SIngo Weinhold		}
28146122852SIngo Weinhold	}
28246122852SIngo Weinhold
28346122852SIngo Weinhold	// write data to output
28446122852SIngo Weinhold	status_t error = _WritePages(pages, requestOffset - lineOffset,
28546122852SIngo Weinhold		requestLength, output);
28646122852SIngo Weinhold	_CachePages(pages, 0, linePageCount);
28746122852SIngo Weinhold	return error;
28846122852SIngo Weinhold}
28946122852SIngo Weinhold
29046122852SIngo Weinhold
29146122852SIngo Weinhold/*!	Frees all pages in given range of the \a pages array.
29246122852SIngo Weinhold	\c NULL entries in the range are OK. All non \c NULL entries must refer
29346122852SIngo Weinhold	to pages with \c PAGE_STATE_UNUSED. The pages may belong to \c fCache or
29446122852SIngo Weinhold	may not have a cache.
29546122852SIngo Weinhold	\c fCache must not be locked.
29646122852SIngo Weinhold*/
29746122852SIngo Weinholdvoid
29846122852SIngo WeinholdCachedDataReader::_DiscardPages(vm_page** pages, size_t firstPage,
29946122852SIngo Weinhold	size_t pageCount)
30046122852SIngo Weinhold{
30146122852SIngo Weinhold	PRINT("%p->CachedDataReader::_DiscardPages(%" B_PRIuSIZE ", %" B_PRIuSIZE
30246122852SIngo Weinhold		")\n", this, firstPage, pageCount);
30346122852SIngo Weinhold
30446122852SIngo Weinhold	AutoLocker<VMCache> cacheLocker(fCache);
30546122852SIngo Weinhold
30646122852SIngo Weinhold	for (size_t i = firstPage; i < firstPage + pageCount; i++) {
30746122852SIngo Weinhold		vm_page* page = pages[i];
30846122852SIngo Weinhold		if (page == NULL)
30946122852SIngo Weinhold			continue;
31046122852SIngo Weinhold
31146122852SIngo Weinhold		DEBUG_PAGE_ACCESS_START(page);
31246122852SIngo Weinhold
31346122852SIngo Weinhold		ASSERT_PRINT(page->State() == PAGE_STATE_UNUSED,
31446122852SIngo Weinhold			"page: %p @! page -m %p", page, page);
31546122852SIngo Weinhold
31646122852SIngo Weinhold		if (page->Cache() != NULL)
31746122852SIngo Weinhold			fCache->RemovePage(page);
31846122852SIngo Weinhold
31946122852SIngo Weinhold		vm_page_free(NULL, page);
32046122852SIngo Weinhold	}
32146122852SIngo Weinhold}
32246122852SIngo Weinhold
32346122852SIngo Weinhold
32446122852SIngo Weinhold/*!	Marks all pages in the given range of the \a pages array cached.
32546122852SIngo Weinhold	There must not be any \c NULL entries in the given array range. All pages
32646122852SIngo Weinhold	must belong to \c cache and have state \c PAGE_STATE_UNUSED.
32746122852SIngo Weinhold	\c fCache must not be locked.
32846122852SIngo Weinhold*/
32946122852SIngo Weinholdvoid
33046122852SIngo WeinholdCachedDataReader::_CachePages(vm_page** pages, size_t firstPage,
33146122852SIngo Weinhold	size_t pageCount)
33246122852SIngo Weinhold{
33346122852SIngo Weinhold	PRINT("%p->CachedDataReader::_CachePages(%" B_PRIuSIZE ", %" B_PRIuSIZE
33446122852SIngo Weinhold		")\n", this, firstPage, pageCount);
33546122852SIngo Weinhold
33646122852SIngo Weinhold	AutoLocker<VMCache> cacheLocker(fCache);
33746122852SIngo Weinhold
33846122852SIngo Weinhold	for (size_t i = firstPage; i < firstPage + pageCount; i++) {