xref: /haiku/src/add-ons/kernel/file_systems/packagefs/package/CachedDataReader.cpp (revision 46122852f1dfbbf4bac6f5a634452ff90c5635ce)
1*46122852SIngo Weinhold /*
2*46122852SIngo Weinhold  * Copyright 2010-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3*46122852SIngo Weinhold  * Distributed under the terms of the MIT License.
4*46122852SIngo Weinhold  */
5*46122852SIngo Weinhold 
6*46122852SIngo Weinhold 
7*46122852SIngo Weinhold #include "CachedDataReader.h"
8*46122852SIngo Weinhold 
9*46122852SIngo Weinhold #include <algorithm>
10*46122852SIngo Weinhold 
11*46122852SIngo Weinhold #include <package/hpkg/DataOutput.h>
12*46122852SIngo Weinhold 
13*46122852SIngo Weinhold #include <util/AutoLock.h>
14*46122852SIngo Weinhold #include <vm/VMCache.h>
15*46122852SIngo Weinhold #include <vm/vm_page.h>
16*46122852SIngo Weinhold 
17*46122852SIngo Weinhold #include "DebugSupport.h"
18*46122852SIngo Weinhold 
19*46122852SIngo Weinhold 
20*46122852SIngo Weinhold using BPackageKit::BHPKG::BBufferDataReader;
21*46122852SIngo Weinhold using BPackageKit::BHPKG::BBufferDataOutput;
22*46122852SIngo Weinhold 
23*46122852SIngo Weinhold 
24*46122852SIngo Weinhold static inline bool
25*46122852SIngo Weinhold page_physical_number_less(const vm_page* a, const vm_page* b)
26*46122852SIngo Weinhold {
27*46122852SIngo Weinhold 	return a->physical_page_number < b->physical_page_number;
28*46122852SIngo Weinhold }
29*46122852SIngo Weinhold 
30*46122852SIngo Weinhold 
31*46122852SIngo Weinhold // #pragma mark - PagesDataOutput
32*46122852SIngo Weinhold 
33*46122852SIngo Weinhold 
34*46122852SIngo Weinhold struct CachedDataReader::PagesDataOutput : public BDataOutput {
35*46122852SIngo Weinhold 	PagesDataOutput(vm_page** pages, size_t pageCount)
36*46122852SIngo Weinhold 		:
37*46122852SIngo Weinhold 		fPages(pages),
38*46122852SIngo Weinhold 		fPageCount(pageCount),
39*46122852SIngo Weinhold 		fInPageOffset(0)
40*46122852SIngo Weinhold 	{
41*46122852SIngo Weinhold 	}
42*46122852SIngo Weinhold 
43*46122852SIngo Weinhold 	virtual status_t WriteData(const void* buffer, size_t size)
44*46122852SIngo Weinhold 	{
45*46122852SIngo Weinhold 		while (size > 0) {
46*46122852SIngo Weinhold 			if (fPageCount == 0)
47*46122852SIngo Weinhold 				return B_BAD_VALUE;
48*46122852SIngo Weinhold 
49*46122852SIngo Weinhold 			size_t toCopy = std::min(size, B_PAGE_SIZE - fInPageOffset);
50*46122852SIngo Weinhold 			status_t error = vm_memcpy_to_physical(
51*46122852SIngo Weinhold 				fPages[0]->physical_page_number * B_PAGE_SIZE + fInPageOffset,
52*46122852SIngo Weinhold 				buffer, toCopy, false);
53*46122852SIngo Weinhold 			if (error != B_OK)
54*46122852SIngo Weinhold 				return error;
55*46122852SIngo Weinhold 
56*46122852SIngo Weinhold 			fInPageOffset += toCopy;
57*46122852SIngo Weinhold 			if (fInPageOffset == B_PAGE_SIZE) {
58*46122852SIngo Weinhold 				fInPageOffset = 0;
59*46122852SIngo Weinhold 				fPages++;
60*46122852SIngo Weinhold 				fPageCount--;
61*46122852SIngo Weinhold 			}
62*46122852SIngo Weinhold 
63*46122852SIngo Weinhold 			buffer = (const char*)buffer + toCopy;
64*46122852SIngo Weinhold 			size -= toCopy;
65*46122852SIngo Weinhold 		}
66*46122852SIngo Weinhold 
67*46122852SIngo Weinhold 		return B_OK;
68*46122852SIngo Weinhold 	}
69*46122852SIngo Weinhold 
70*46122852SIngo Weinhold private:
71*46122852SIngo Weinhold 	vm_page**	fPages;
72*46122852SIngo Weinhold 	size_t		fPageCount;
73*46122852SIngo Weinhold 	size_t		fInPageOffset;
74*46122852SIngo Weinhold };
75*46122852SIngo Weinhold 
76*46122852SIngo Weinhold 
77*46122852SIngo Weinhold // #pragma mark - CachedDataReader
78*46122852SIngo Weinhold 
79*46122852SIngo Weinhold 
80*46122852SIngo Weinhold CachedDataReader::CachedDataReader()
81*46122852SIngo Weinhold 	:
82*46122852SIngo Weinhold 	fReader(NULL),
83*46122852SIngo Weinhold 	fCache(NULL),
84*46122852SIngo Weinhold 	fCacheLineLockers()
85*46122852SIngo Weinhold {
86*46122852SIngo Weinhold 	mutex_init(&fLock, "packagefs cached reader");
87*46122852SIngo Weinhold }
88*46122852SIngo Weinhold 
89*46122852SIngo Weinhold 
90*46122852SIngo Weinhold CachedDataReader::~CachedDataReader()
91*46122852SIngo Weinhold {
92*46122852SIngo Weinhold 	if (fCache != NULL) {
93*46122852SIngo Weinhold 		fCache->Lock();
94*46122852SIngo Weinhold 		fCache->ReleaseRefAndUnlock();
95*46122852SIngo Weinhold 	}
96*46122852SIngo Weinhold 
97*46122852SIngo Weinhold 	mutex_destroy(&fLock);
98*46122852SIngo Weinhold }
99*46122852SIngo Weinhold 
100*46122852SIngo Weinhold 
101*46122852SIngo Weinhold status_t
102*46122852SIngo Weinhold CachedDataReader::Init(BAbstractBufferedDataReader* reader, off_t size)
103*46122852SIngo Weinhold {
104*46122852SIngo Weinhold 	fReader = reader;
105*46122852SIngo Weinhold 
106*46122852SIngo Weinhold 	status_t error = fCacheLineLockers.Init();
107*46122852SIngo Weinhold 	if (error != B_OK)
108*46122852SIngo Weinhold 		RETURN_ERROR(error);
109*46122852SIngo Weinhold 
110*46122852SIngo Weinhold 	error = VMCacheFactory::CreateNullCache(VM_PRIORITY_SYSTEM,
111*46122852SIngo Weinhold 		fCache);
112*46122852SIngo Weinhold 	if (error != B_OK)
113*46122852SIngo Weinhold 		RETURN_ERROR(error);
114*46122852SIngo Weinhold 
115*46122852SIngo Weinhold 	AutoLocker<VMCache> locker(fCache);
116*46122852SIngo Weinhold 
117*46122852SIngo Weinhold 	error = fCache->Resize(size, VM_PRIORITY_SYSTEM);
118*46122852SIngo Weinhold 	if (error != B_OK)
119*46122852SIngo Weinhold 		RETURN_ERROR(error);
120*46122852SIngo Weinhold 
121*46122852SIngo Weinhold 	return B_OK;
122*46122852SIngo Weinhold }
123*46122852SIngo Weinhold 
124*46122852SIngo Weinhold 
125*46122852SIngo Weinhold status_t
126*46122852SIngo Weinhold CachedDataReader::ReadData(off_t offset, void* buffer, size_t size)
127*46122852SIngo Weinhold {
128*46122852SIngo Weinhold 	BBufferDataOutput output(buffer, size);
129*46122852SIngo Weinhold 	return ReadDataToOutput(offset, size, &output);
130*46122852SIngo Weinhold }
131*46122852SIngo Weinhold 
132*46122852SIngo Weinhold 
133*46122852SIngo Weinhold status_t
134*46122852SIngo Weinhold CachedDataReader::ReadDataToOutput(off_t offset, size_t size,
135*46122852SIngo Weinhold 	BDataOutput* output)
136*46122852SIngo Weinhold {
137*46122852SIngo Weinhold 	if (offset > fCache->virtual_end
138*46122852SIngo Weinhold 		|| (off_t)size > fCache->virtual_end - offset) {
139*46122852SIngo Weinhold 		return B_BAD_VALUE;
140*46122852SIngo Weinhold 	}
141*46122852SIngo Weinhold 
142*46122852SIngo Weinhold 	if (size == 0)
143*46122852SIngo Weinhold 		return B_OK;
144*46122852SIngo Weinhold 
145*46122852SIngo Weinhold 	while (size > 0) {
146*46122852SIngo Weinhold 		// the start of the current cache line
147*46122852SIngo Weinhold 		off_t lineOffset = (offset / kCacheLineSize) * kCacheLineSize;
148*46122852SIngo Weinhold 
149*46122852SIngo Weinhold 		// intersection of request and cache line
150*46122852SIngo Weinhold 		off_t cacheLineEnd = std::min(lineOffset + (off_t)kCacheLineSize,
151*46122852SIngo Weinhold 			fCache->virtual_end);
152*46122852SIngo Weinhold 		size_t requestLineLength
153*46122852SIngo Weinhold 			= std::min(cacheLineEnd - offset, (off_t)size);
154*46122852SIngo Weinhold 
155*46122852SIngo Weinhold 		// transfer the data of the cache line
156*46122852SIngo Weinhold 		status_t error = _ReadCacheLine(lineOffset, cacheLineEnd - lineOffset,
157*46122852SIngo Weinhold 			offset, requestLineLength, output);
158*46122852SIngo Weinhold 		if (error != B_OK)
159*46122852SIngo Weinhold 			return error;
160*46122852SIngo Weinhold 
161*46122852SIngo Weinhold 		offset = cacheLineEnd;
162*46122852SIngo Weinhold 		size -= requestLineLength;
163*46122852SIngo Weinhold 	}
164*46122852SIngo Weinhold 
165*46122852SIngo Weinhold 	return B_OK;
166*46122852SIngo Weinhold }
167*46122852SIngo Weinhold 
168*46122852SIngo Weinhold 
169*46122852SIngo Weinhold status_t
170*46122852SIngo Weinhold CachedDataReader::_ReadCacheLine(off_t lineOffset, size_t lineSize,
171*46122852SIngo Weinhold 	off_t requestOffset, size_t requestLength, BDataOutput* output)
172*46122852SIngo Weinhold {
173*46122852SIngo Weinhold 	PRINT("CachedDataReader::_ReadCacheLine(%" B_PRIdOFF ", %zu, %" B_PRIdOFF
174*46122852SIngo Weinhold 		", %zu, %p\n", lineOffset, lineSize, requestOffset, requestLength,
175*46122852SIngo Weinhold 		output);
176*46122852SIngo Weinhold 
177*46122852SIngo Weinhold 	CacheLineLocker cacheLineLocker(this, lineOffset);
178*46122852SIngo Weinhold 
179*46122852SIngo Weinhold 	// check whether there are pages of the cache line and the mark them used
180*46122852SIngo Weinhold 	page_num_t firstPageOffset = lineOffset / B_PAGE_SIZE;
181*46122852SIngo Weinhold 	page_num_t linePageCount = (lineSize + B_PAGE_SIZE - 1) / B_PAGE_SIZE;
182*46122852SIngo Weinhold 	vm_page* pages[kPagesPerCacheLine] = {};
183*46122852SIngo Weinhold 
184*46122852SIngo Weinhold 	AutoLocker<VMCache> cacheLocker(fCache);
185*46122852SIngo Weinhold 
186*46122852SIngo Weinhold 	page_num_t firstMissing = 0;
187*46122852SIngo Weinhold 	page_num_t lastMissing = 0;
188*46122852SIngo Weinhold 	page_num_t missingPages = 0;
189*46122852SIngo Weinhold 	page_num_t pageOffset = firstPageOffset;
190*46122852SIngo Weinhold 
191*46122852SIngo Weinhold 	VMCachePagesTree::Iterator it = fCache->pages.GetIterator(pageOffset, true,
192*46122852SIngo Weinhold 		true);
193*46122852SIngo Weinhold 	while (pageOffset < firstPageOffset + linePageCount) {
194*46122852SIngo Weinhold 		vm_page* page = it.Next();
195*46122852SIngo Weinhold 		page_num_t currentPageOffset;
196*46122852SIngo Weinhold 		if (page == NULL
197*46122852SIngo Weinhold 			|| page->cache_offset >= firstPageOffset + linePageCount) {
198*46122852SIngo Weinhold 			page = NULL;
199*46122852SIngo Weinhold 			currentPageOffset = firstPageOffset + linePageCount;
200*46122852SIngo Weinhold 		} else
201*46122852SIngo Weinhold 			currentPageOffset = page->cache_offset;
202*46122852SIngo Weinhold 
203*46122852SIngo Weinhold 		if (pageOffset < currentPageOffset) {
204*46122852SIngo Weinhold 			// pages are missing
205*46122852SIngo Weinhold 			if (missingPages == 0)
206*46122852SIngo Weinhold 				firstMissing = pageOffset;
207*46122852SIngo Weinhold 			lastMissing = currentPageOffset - 1;
208*46122852SIngo Weinhold 			missingPages += currentPageOffset - pageOffset;
209*46122852SIngo Weinhold 
210*46122852SIngo Weinhold 			for (; pageOffset < currentPageOffset; pageOffset++)
211*46122852SIngo Weinhold 				pages[pageOffset - firstPageOffset] = NULL;
212*46122852SIngo Weinhold 		}
213*46122852SIngo Weinhold 
214*46122852SIngo Weinhold 		if (page != NULL) {
215*46122852SIngo Weinhold 			pages[pageOffset++ - firstPageOffset] = page;
216*46122852SIngo Weinhold 			DEBUG_PAGE_ACCESS_START(page);
217*46122852SIngo Weinhold 			vm_page_set_state(page, PAGE_STATE_UNUSED);
218*46122852SIngo Weinhold 			DEBUG_PAGE_ACCESS_END(page);
219*46122852SIngo Weinhold 		}
220*46122852SIngo Weinhold 	}
221*46122852SIngo Weinhold 
222*46122852SIngo Weinhold 	cacheLocker.Unlock();
223*46122852SIngo Weinhold 
224*46122852SIngo Weinhold 	if (missingPages > 0) {
225*46122852SIngo Weinhold // TODO: If the missing pages range doesn't intersect with the request, just
226*46122852SIngo Weinhold // satisfy the request and don't read anything at all.
227*46122852SIngo Weinhold 		// There are pages of the cache line missing. We have to allocate fresh
228*46122852SIngo Weinhold 		// ones.
229*46122852SIngo Weinhold 
230*46122852SIngo Weinhold 		// reserve
231*46122852SIngo Weinhold 		vm_page_reservation reservation;
232*46122852SIngo Weinhold 		if (!vm_page_try_reserve_pages(&reservation, missingPages,
233*46122852SIngo Weinhold 				VM_PRIORITY_SYSTEM)) {
234*46122852SIngo Weinhold 			_DiscardPages(pages, firstMissing - firstPageOffset, missingPages);
235*46122852SIngo Weinhold 
236*46122852SIngo Weinhold 			// fall back to uncached transfer
237*46122852SIngo Weinhold 			return fReader->ReadDataToOutput(requestOffset, requestLength,
238*46122852SIngo Weinhold 				output);
239*46122852SIngo Weinhold 		}
240*46122852SIngo Weinhold 
241*46122852SIngo Weinhold 		// Allocate the missing pages and remove the already existing pages in
242*46122852SIngo Weinhold 		// the range from the cache. We're going to read/write the whole range
243*46122852SIngo Weinhold 		// anyway.
244*46122852SIngo Weinhold 		for (pageOffset = firstMissing; pageOffset <= lastMissing;
245*46122852SIngo Weinhold 				pageOffset++) {
246*46122852SIngo Weinhold 			page_num_t index = pageOffset - firstPageOffset;
247*46122852SIngo Weinhold 			if (pages[index] == NULL) {
248*46122852SIngo Weinhold 				pages[index] = vm_page_allocate_page(&reservation,
249*46122852SIngo Weinhold 					PAGE_STATE_UNUSED);
250*46122852SIngo Weinhold 				DEBUG_PAGE_ACCESS_END(pages[index]);
251*46122852SIngo Weinhold 			} else {
252*46122852SIngo Weinhold 				cacheLocker.Lock();
253*46122852SIngo Weinhold 				fCache->RemovePage(pages[index]);
254*46122852SIngo Weinhold 				cacheLocker.Unlock();
255*46122852SIngo Weinhold 			}
256*46122852SIngo Weinhold 		}
257*46122852SIngo Weinhold 
258*46122852SIngo Weinhold 		missingPages = lastMissing - firstMissing + 1;
259*46122852SIngo Weinhold 
260*46122852SIngo Weinhold 		// add the pages to the cache
261*46122852SIngo Weinhold 		cacheLocker.Lock();
262*46122852SIngo Weinhold 
263*46122852SIngo Weinhold 		for (pageOffset = firstMissing; pageOffset <= lastMissing;
264*46122852SIngo Weinhold 				pageOffset++) {
265*46122852SIngo Weinhold 			page_num_t index = pageOffset - firstPageOffset;
266*46122852SIngo Weinhold 			fCache->InsertPage(pages[index], (off_t)pageOffset * B_PAGE_SIZE);
267*46122852SIngo Weinhold 		}
268*46122852SIngo Weinhold 
269*46122852SIngo Weinhold 		cacheLocker.Unlock();
270*46122852SIngo Weinhold 
271*46122852SIngo Weinhold 		// read in the missing pages
272*46122852SIngo Weinhold 		status_t error = _ReadIntoPages(pages, firstMissing - firstPageOffset,
273*46122852SIngo Weinhold 			missingPages);
274*46122852SIngo Weinhold 		if (error != B_OK) {
275*46122852SIngo Weinhold 			ERROR("CachedDataReader::_ReadCacheLine(): Failed to read into "
276*46122852SIngo Weinhold 				"cache (offset: %" B_PRIdOFF ", length: %" B_PRIuSIZE "), "
277*46122852SIngo Weinhold 				"trying uncached read (offset: %" B_PRIdOFF ", length: %"
278*46122852SIngo Weinhold 				B_PRIuSIZE ")\n", (off_t)firstMissing * B_PAGE_SIZE,
279*46122852SIngo Weinhold 				(size_t)missingPages * B_PAGE_SIZE, requestOffset,
280*46122852SIngo Weinhold 				requestLength);
281*46122852SIngo Weinhold 
282*46122852SIngo Weinhold 			_DiscardPages(pages, firstMissing - firstPageOffset, missingPages);
283*46122852SIngo Weinhold 
284*46122852SIngo Weinhold 			// Try again using an uncached transfer
285*46122852SIngo Weinhold 			return fReader->ReadDataToOutput(requestOffset, requestLength,
286*46122852SIngo Weinhold 				output);
287*46122852SIngo Weinhold 		}
288*46122852SIngo Weinhold 	}
289*46122852SIngo Weinhold 
290*46122852SIngo Weinhold 	// write data to output
291*46122852SIngo Weinhold 	status_t error = _WritePages(pages, requestOffset - lineOffset,
292*46122852SIngo Weinhold 		requestLength, output);
293*46122852SIngo Weinhold 	_CachePages(pages, 0, linePageCount);
294*46122852SIngo Weinhold 	return error;
295*46122852SIngo Weinhold }
296*46122852SIngo Weinhold 
297*46122852SIngo Weinhold 
298*46122852SIngo Weinhold /*!	Frees all pages in given range of the \a pages array.
299*46122852SIngo Weinhold 	\c NULL entries in the range are OK. All non \c NULL entries must refer
300*46122852SIngo Weinhold 	to pages with \c PAGE_STATE_UNUSED. The pages may belong to \c fCache or
301*46122852SIngo Weinhold 	may not have a cache.
302*46122852SIngo Weinhold 	\c fCache must not be locked.
303*46122852SIngo Weinhold */
304*46122852SIngo Weinhold void
305*46122852SIngo Weinhold CachedDataReader::_DiscardPages(vm_page** pages, size_t firstPage,
306*46122852SIngo Weinhold 	size_t pageCount)
307*46122852SIngo Weinhold {
308*46122852SIngo Weinhold 	PRINT("%p->CachedDataReader::_DiscardPages(%" B_PRIuSIZE ", %" B_PRIuSIZE
309*46122852SIngo Weinhold 		")\n", this, firstPage, pageCount);
310*46122852SIngo Weinhold 
311*46122852SIngo Weinhold 	AutoLocker<VMCache> cacheLocker(fCache);
312*46122852SIngo Weinhold 
313*46122852SIngo Weinhold 	for (size_t i = firstPage; i < firstPage + pageCount; i++) {
314*46122852SIngo Weinhold 		vm_page* page = pages[i];
315*46122852SIngo Weinhold 		if (page == NULL)
316*46122852SIngo Weinhold 			continue;
317*46122852SIngo Weinhold 
318*46122852SIngo Weinhold 		DEBUG_PAGE_ACCESS_START(page);
319*46122852SIngo Weinhold 
320*46122852SIngo Weinhold 		ASSERT_PRINT(page->State() == PAGE_STATE_UNUSED,
321*46122852SIngo Weinhold 			"page: %p @! page -m %p", page, page);
322*46122852SIngo Weinhold 
323*46122852SIngo Weinhold 		if (page->Cache() != NULL)
324*46122852SIngo Weinhold 			fCache->RemovePage(page);
325*46122852SIngo Weinhold 
326*46122852SIngo Weinhold 		vm_page_free(NULL, page);
327*46122852SIngo Weinhold 	}
328*46122852SIngo Weinhold }
329*46122852SIngo Weinhold 
330*46122852SIngo Weinhold 
331*46122852SIngo Weinhold /*!	Marks all pages in the given range of the \a pages array cached.
332*46122852SIngo Weinhold 	There must not be any \c NULL entries in the given array range. All pages
333*46122852SIngo Weinhold 	must belong to \c cache and have state \c PAGE_STATE_UNUSED.
334*46122852SIngo Weinhold 	\c fCache must not be locked.
335*46122852SIngo Weinhold */
336*46122852SIngo Weinhold void
337*46122852SIngo Weinhold CachedDataReader::_CachePages(vm_page** pages, size_t firstPage,
338*46122852SIngo Weinhold 	size_t pageCount)
339*46122852SIngo Weinhold {
340*46122852SIngo Weinhold 	PRINT("%p->CachedDataReader::_CachePages(%" B_PRIuSIZE ", %" B_PRIuSIZE
341*46122852SIngo Weinhold 		")\n", this, firstPage, pageCount);
342*46122852SIngo Weinhold 
343*46122852SIngo Weinhold 	AutoLocker<VMCache> cacheLocker(fCache);
344*46122852SIngo Weinhold 
345*46122852SIngo Weinhold 	for (size_t i = firstPage; i < firstPage + pageCount; i++) {
346*46122852SIngo Weinhold 		vm_page* page = pages[i];
347*46122852SIngo Weinhold 		ASSERT(page != NULL);
348*46122852SIngo Weinhold 		ASSERT_PRINT(page->State() == PAGE_STATE_UNUSED
349*46122852SIngo Weinhold 				&& page->Cache() == fCache,
350*46122852SIngo Weinhold 			"page: %p @! page -m %p", page, page);
351*46122852SIngo Weinhold 
352*46122852SIngo Weinhold 		DEBUG_PAGE_ACCESS_START(page);
353*46122852SIngo Weinhold 		vm_page_set_state(page, PAGE_STATE_CACHED);
354*46122852SIngo Weinhold 		DEBUG_PAGE_ACCESS_END(page);
355*46122852SIngo Weinhold 	}
356*46122852SIngo Weinhold }
357*46122852SIngo Weinhold 
358*46122852SIngo Weinhold 
359*46122852SIngo Weinhold /*!	Writes the contents of pages in \c pages to \a output.
360*46122852SIngo Weinhold 	\param pages The pages array.
361*46122852SIngo Weinhold 	\param pagesRelativeOffset The offset relative to \a pages[0] where to
362*46122852SIngo Weinhold 		start writing from.
363*46122852SIngo Weinhold 	\param requestLength The number of bytes to write.
364*46122852SIngo Weinhold 	\param output The output to which the data shall be written.
365*46122852SIngo Weinhold 	\return \c B_OK, if writing went fine, another error code otherwise.
366*46122852SIngo Weinhold */
367*46122852SIngo Weinhold status_t
368*46122852SIngo Weinhold CachedDataReader::_WritePages(vm_page** pages, size_t pagesRelativeOffset,
369*46122852SIngo Weinhold 	size_t requestLength, BDataOutput* output)
370*46122852SIngo Weinhold {
371*46122852SIngo Weinhold 	PRINT("%p->CachedDataReader::_WritePages(%" B_PRIuSIZE ", %" B_PRIuSIZE
372*46122852SIngo Weinhold 		", %p)\n", this, pagesRelativeOffset, requestLength, output);
373*46122852SIngo Weinhold 
374*46122852SIngo Weinhold 	size_t firstPage = pagesRelativeOffset / B_PAGE_SIZE;
375*46122852SIngo Weinhold 	size_t endPage = (pagesRelativeOffset + requestLength + B_PAGE_SIZE - 1)
376*46122852SIngo Weinhold 		/ B_PAGE_SIZE;
377*46122852SIngo Weinhold 
378*46122852SIngo Weinhold 	// fallback to copying individual pages
379*46122852SIngo Weinhold 	size_t inPageOffset = pagesRelativeOffset % B_PAGE_SIZE;
380*46122852SIngo Weinhold 	for (size_t i = firstPage; i < endPage; i++) {
381*46122852SIngo Weinhold 		// map the page
382*46122852SIngo Weinhold 		void* handle;
383*46122852SIngo Weinhold 		addr_t address;
384*46122852SIngo Weinhold 		status_t error = vm_get_physical_page(
385*46122852SIngo Weinhold 			pages[i]->physical_page_number * B_PAGE_SIZE, &address,
386*46122852SIngo Weinhold 			&handle);
387*46122852SIngo Weinhold 		if (error != B_OK)
388*46122852SIngo Weinhold 			return error;
389*46122852SIngo Weinhold 
390*46122852SIngo Weinhold 		// write the page's data
391*46122852SIngo Weinhold 		size_t toCopy = std::min(B_PAGE_SIZE - inPageOffset, requestLength);
392*46122852SIngo Weinhold 		error = output->WriteData((uint8*)(address + inPageOffset), toCopy);
393*46122852SIngo Weinhold 
394*46122852SIngo Weinhold 		// unmap the page
395*46122852SIngo Weinhold 		vm_put_physical_page(address, handle);
396*46122852SIngo Weinhold 
397*46122852SIngo Weinhold 		if (error != B_OK)
398*46122852SIngo Weinhold 			return error;
399*46122852SIngo Weinhold 
400*46122852SIngo Weinhold 		inPageOffset = 0;
401*46122852SIngo Weinhold 		requestLength -= toCopy;
402*46122852SIngo Weinhold 	}
403*46122852SIngo Weinhold 
404*46122852SIngo Weinhold 	return B_OK;
405*46122852SIngo Weinhold }
406*46122852SIngo Weinhold 
407*46122852SIngo Weinhold 
408*46122852SIngo Weinhold status_t
409*46122852SIngo Weinhold CachedDataReader::_ReadIntoPages(vm_page** pages, size_t firstPage,
410*46122852SIngo Weinhold 	size_t pageCount)
411*46122852SIngo Weinhold {
412*46122852SIngo Weinhold 	PagesDataOutput output(pages + firstPage, pageCount);
413*46122852SIngo Weinhold 
414*46122852SIngo Weinhold 	off_t firstPageOffset = (off_t)pages[firstPage]->cache_offset
415*46122852SIngo Weinhold 		* B_PAGE_SIZE;
416*46122852SIngo Weinhold 	generic_size_t requestLength = std::min(
417*46122852SIngo Weinhold 			firstPageOffset + (off_t)pageCount * B_PAGE_SIZE,
418*46122852SIngo Weinhold 			fCache->virtual_end)
419*46122852SIngo Weinhold 		- firstPageOffset;
420*46122852SIngo Weinhold 
421*46122852SIngo Weinhold 	return fReader->ReadDataToOutput(firstPageOffset, requestLength, &output);
422*46122852SIngo Weinhold }
423*46122852SIngo Weinhold 
424*46122852SIngo Weinhold 
425*46122852SIngo Weinhold void
426*46122852SIngo Weinhold CachedDataReader::_LockCacheLine(CacheLineLocker* lineLocker)
427*46122852SIngo Weinhold {
428*46122852SIngo Weinhold 	MutexLocker locker(fLock);
429*46122852SIngo Weinhold 
430*46122852SIngo Weinhold 	CacheLineLocker* otherLineLocker
431*46122852SIngo Weinhold 		= fCacheLineLockers.Lookup(lineLocker->Offset());
432*46122852SIngo Weinhold 	if (otherLineLocker == NULL) {
433*46122852SIngo Weinhold 		fCacheLineLockers.Insert(lineLocker);
434*46122852SIngo Weinhold 		return;
435*46122852SIngo Weinhold 	}
436*46122852SIngo Weinhold 
437*46122852SIngo Weinhold 	// queue and wait
438*46122852SIngo Weinhold 	otherLineLocker->Queue().Add(lineLocker);
439*46122852SIngo Weinhold 	lineLocker->Wait(fLock);
440*46122852SIngo Weinhold }
441*46122852SIngo Weinhold 
442*46122852SIngo Weinhold 
443*46122852SIngo Weinhold void
444*46122852SIngo Weinhold CachedDataReader::_UnlockCacheLine(CacheLineLocker* lineLocker)
445*46122852SIngo Weinhold {
446*46122852SIngo Weinhold 	MutexLocker locker(fLock);
447*46122852SIngo Weinhold 
448*46122852SIngo Weinhold 	fCacheLineLockers.Remove(lineLocker);
449*46122852SIngo Weinhold 
450*46122852SIngo Weinhold 	if (CacheLineLocker* nextLineLocker = lineLocker->Queue().RemoveHead()) {
451*46122852SIngo Weinhold 		nextLineLocker->Queue().MoveFrom(&lineLocker->Queue());
452*46122852SIngo Weinhold 		fCacheLineLockers.Insert(nextLineLocker);
453*46122852SIngo Weinhold 		nextLineLocker->WakeUp();
454*46122852SIngo Weinhold 	}
455*46122852SIngo Weinhold }
456