xref: /haiku/src/add-ons/kernel/file_systems/ramfs/DataContainer.cpp (revision f940c524eba3d616bd13b41c8bbaa1208e94fba9)
1b9795fafSAugustin Cavalier /*
2b9795fafSAugustin Cavalier  * Copyright 2007, Ingo Weinhold, ingo_weinhold@gmx.de.
36cfbbceeSAugustin Cavalier  * Copyright 2019-2024, Haiku, Inc. All rights reserved.
46cfbbceeSAugustin Cavalier  * Distributed under the terms of the MIT license.
5b9795fafSAugustin Cavalier  */
6cbc07268SAugustin Cavalier #include "DataContainer.h"
7cbc07268SAugustin Cavalier 
83d393797SAugustin Cavalier #include <StackOrHeapArray.h>
9cbc07268SAugustin Cavalier #include <util/AutoLock.h>
10396e3dfbSAugustin Cavalier #include <util/BitUtils.h>
11c0a12a6bSAugustin Cavalier #include <slab/Slab.h>
12cbc07268SAugustin Cavalier 
13c0a12a6bSAugustin Cavalier #include <vfs.h>
14cbc07268SAugustin Cavalier #include <vm/VMCache.h>
15cbc07268SAugustin Cavalier #include <vm/vm_page.h>
16c0a12a6bSAugustin Cavalier #include "VMAnonymousNoSwapCache.h"
17950900a9SAugustin Cavalier #include "vnode_store.h"
18224e7c42SIngo Weinhold 
19224e7c42SIngo Weinhold #include "AllocationInfo.h"
20a41d815cSAugustin Cavalier #include "DebugSupport.h"
21224e7c42SIngo Weinhold #include "Misc.h"
22224e7c42SIngo Weinhold #include "Volume.h"
23ddfd8f81SAugustin Cavalier #include "cache_support.h"
24224e7c42SIngo Weinhold 
25396e3dfbSAugustin Cavalier 
26396e3dfbSAugustin Cavalier // Initial size of the DataContainer's small buffer. If it contains data up to
27396e3dfbSAugustin Cavalier // this size, nothing is allocated, but the small buffer is used instead.
28396e3dfbSAugustin Cavalier // 16 bytes are for free, since they are shared with the block list.
29396e3dfbSAugustin Cavalier // (actually even more, since the list has an initial size).
30396e3dfbSAugustin Cavalier // I ran a test analyzing what sizes the attributes in my system have:
31396e3dfbSAugustin Cavalier //     size   percentage   bytes used in average
32396e3dfbSAugustin Cavalier //   <=   0         0.00                   93.45
33396e3dfbSAugustin Cavalier //   <=   4        25.46                   75.48
34396e3dfbSAugustin Cavalier //   <=   8        30.54                   73.02
35396e3dfbSAugustin Cavalier //   <=  16        52.98                   60.37
36396e3dfbSAugustin Cavalier //   <=  32        80.19                   51.74
37396e3dfbSAugustin Cavalier //   <=  64        94.38                   70.54
38396e3dfbSAugustin Cavalier //   <= 126        96.90                  128.23
39396e3dfbSAugustin Cavalier //
40396e3dfbSAugustin Cavalier // For average memory usage it is assumed, that attributes larger than 126
41396e3dfbSAugustin Cavalier // bytes have size 127, that the list has an initial capacity of 10 entries
42396e3dfbSAugustin Cavalier // (40 bytes), that the block reference consumes 4 bytes and the block header
43396e3dfbSAugustin Cavalier // 12 bytes. The optimal length is actually 35, with 51.05 bytes per
44396e3dfbSAugustin Cavalier // attribute, but I conservatively rounded to 32.
45396e3dfbSAugustin Cavalier static const off_t kMinimumSmallBufferSize = 32;
46396e3dfbSAugustin Cavalier static const off_t kMaximumSmallBufferSize = (B_PAGE_SIZE / 4);
47396e3dfbSAugustin Cavalier 
48396e3dfbSAugustin Cavalier 
49c0a12a6bSAugustin Cavalier // We don't use VMVnodeCache because it's for caching pages that exist on disk.
50c0a12a6bSAugustin Cavalier // All we need is an AnonymousCache that tracks when the vnode is referenced.
51c0a12a6bSAugustin Cavalier class VMForVnodeCache final : public VMAnonymousNoSwapCache {
52c0a12a6bSAugustin Cavalier public:
Init()53c0a12a6bSAugustin Cavalier 	status_t Init()
54c0a12a6bSAugustin Cavalier 	{
55c0a12a6bSAugustin Cavalier 		fVnode = NULL;
56c0a12a6bSAugustin Cavalier 		return VMAnonymousNoSwapCache::Init(false, 0, 0, 0);
57c0a12a6bSAugustin Cavalier 	}
58c0a12a6bSAugustin Cavalier 
AcquireUnreferencedStoreRef()59c0a12a6bSAugustin Cavalier 	status_t AcquireUnreferencedStoreRef() override
60c0a12a6bSAugustin Cavalier 	{
61c0a12a6bSAugustin Cavalier 		return B_NOT_SUPPORTED;
62c0a12a6bSAugustin Cavalier 	}
63c0a12a6bSAugustin Cavalier 
AcquireStoreRef()64c0a12a6bSAugustin Cavalier 	void AcquireStoreRef() override
65c0a12a6bSAugustin Cavalier 	{
66c0a12a6bSAugustin Cavalier 		vfs_acquire_vnode(fVnode);
67c0a12a6bSAugustin Cavalier 	}
68c0a12a6bSAugustin Cavalier 
ReleaseStoreRef()69c0a12a6bSAugustin Cavalier 	void ReleaseStoreRef() override
70c0a12a6bSAugustin Cavalier 	{
71c0a12a6bSAugustin Cavalier 		vfs_put_vnode(fVnode);
72c0a12a6bSAugustin Cavalier 	}
73c0a12a6bSAugustin Cavalier 
74c0a12a6bSAugustin Cavalier protected:
DeleteObject()75c0a12a6bSAugustin Cavalier 	virtual	void DeleteObject()
76c0a12a6bSAugustin Cavalier 	{
77950900a9SAugustin Cavalier 		static_assert(sizeof(VMForVnodeCache) <= sizeof(VMVnodeCache), "cache too large");
78c0a12a6bSAugustin Cavalier 		object_cache_delete(gVnodeCacheObjectCache, this);
79c0a12a6bSAugustin Cavalier 	}
80c0a12a6bSAugustin Cavalier 
81c0a12a6bSAugustin Cavalier private:
82c0a12a6bSAugustin Cavalier 	friend class DataContainer;
83c0a12a6bSAugustin Cavalier 	struct vnode* fVnode;
84c0a12a6bSAugustin Cavalier };
85c0a12a6bSAugustin Cavalier 
866cfbbceeSAugustin Cavalier 
DataContainer(Volume * volume)87224e7c42SIngo Weinhold DataContainer::DataContainer(Volume *volume)
88224e7c42SIngo Weinhold 	: fVolume(volume),
89cbc07268SAugustin Cavalier 	  fSize(0),
90396e3dfbSAugustin Cavalier 	  fCache(NULL),
91396e3dfbSAugustin Cavalier 	  fSmallBuffer(NULL),
92396e3dfbSAugustin Cavalier 	  fSmallBufferSize(0)
93224e7c42SIngo Weinhold {
94224e7c42SIngo Weinhold }
95224e7c42SIngo Weinhold 
966cfbbceeSAugustin Cavalier 
~DataContainer()97224e7c42SIngo Weinhold DataContainer::~DataContainer()
98224e7c42SIngo Weinhold {
99cbc07268SAugustin Cavalier 	if (fCache != NULL) {
100cbc07268SAugustin Cavalier 		fCache->Lock();
101cbc07268SAugustin Cavalier 		fCache->ReleaseRefAndUnlock();
102cbc07268SAugustin Cavalier 		fCache = NULL;
103cbc07268SAugustin Cavalier 	}
104396e3dfbSAugustin Cavalier 	if (fSmallBuffer != NULL) {
105396e3dfbSAugustin Cavalier 		free(fSmallBuffer);
106396e3dfbSAugustin Cavalier 		fSmallBuffer = NULL;
107396e3dfbSAugustin Cavalier 	}
108224e7c42SIngo Weinhold }
109224e7c42SIngo Weinhold 
1106cfbbceeSAugustin Cavalier 
111224e7c42SIngo Weinhold status_t
InitCheck() const112224e7c42SIngo Weinhold DataContainer::InitCheck() const
113224e7c42SIngo Weinhold {
114cbc07268SAugustin Cavalier 	return (fVolume != NULL ? B_OK : B_ERROR);
115224e7c42SIngo Weinhold }
116224e7c42SIngo Weinhold 
1176cfbbceeSAugustin Cavalier 
11858a582ffSAugustin Cavalier VMCache*
GetCache(struct vnode * vnode)119c0a12a6bSAugustin Cavalier DataContainer::GetCache(struct vnode* vnode)
12058a582ffSAugustin Cavalier {
121396e3dfbSAugustin Cavalier 	// TODO: Because we always get the cache for files on creation vs. on demand,
122396e3dfbSAugustin Cavalier 	// this means files (no matter how small) always use cache mode at present.
12358a582ffSAugustin Cavalier 	if (!_IsCacheMode())
12458a582ffSAugustin Cavalier 		_SwitchToCacheMode();
125c0a12a6bSAugustin Cavalier 	((VMForVnodeCache*)fCache)->fVnode = vnode;
12658a582ffSAugustin Cavalier 	return fCache;
12758a582ffSAugustin Cavalier }
12858a582ffSAugustin Cavalier 
1296cfbbceeSAugustin Cavalier 
130224e7c42SIngo Weinhold status_t
Resize(off_t newSize)131224e7c42SIngo Weinhold DataContainer::Resize(off_t newSize)
132224e7c42SIngo Weinhold {
133425ac1b6SAlexander von Gluck IV //	PRINT("DataContainer::Resize(%lld), fSize: %lld\n", newSize, fSize);
134cbc07268SAugustin Cavalier 
135224e7c42SIngo Weinhold 	status_t error = B_OK;
136396e3dfbSAugustin Cavalier 	if (_RequiresCacheMode(newSize)) {
137cbc07268SAugustin Cavalier 		if (newSize < fSize) {
138cbc07268SAugustin Cavalier 			// shrink
139cbc07268SAugustin Cavalier 			// resize the VMCache, which will automatically free pages
140cbc07268SAugustin Cavalier 			AutoLocker<VMCache> _(fCache);
14143feec01SAugustin Cavalier 			error = fCache->Resize(newSize, VM_PRIORITY_USER);
142cbc07268SAugustin Cavalier 			if (error != B_OK)
143cbc07268SAugustin Cavalier 				return error;
144cbc07268SAugustin Cavalier 		} else {
145cbc07268SAugustin Cavalier 			// grow
146cbc07268SAugustin Cavalier 			if (!_IsCacheMode())
14758a582ffSAugustin Cavalier 				error = _SwitchToCacheMode();
148cbc07268SAugustin Cavalier 			if (error != B_OK)
149cbc07268SAugustin Cavalier 				return error;
150cbc07268SAugustin Cavalier 
151cbc07268SAugustin Cavalier 			AutoLocker<VMCache> _(fCache);
15243feec01SAugustin Cavalier 			fCache->Resize(newSize, VM_PRIORITY_USER);
153cbc07268SAugustin Cavalier 
154cbc07268SAugustin Cavalier 			// pages will be added as they are written to; so nothing else
155cbc07268SAugustin Cavalier 			// needs to be done here.
156cbc07268SAugustin Cavalier 		}
157396e3dfbSAugustin Cavalier 	} else if (fSmallBufferSize < newSize
158396e3dfbSAugustin Cavalier 			|| (fSmallBufferSize - newSize) > (kMaximumSmallBufferSize / 2)) {
159396e3dfbSAugustin Cavalier 		const size_t newBufferSize = max_c(next_power_of_2(newSize),
160396e3dfbSAugustin Cavalier 			kMinimumSmallBufferSize);
161396e3dfbSAugustin Cavalier 		void* newBuffer = realloc(fSmallBuffer, newBufferSize);
162396e3dfbSAugustin Cavalier 		if (newBuffer == NULL)
163396e3dfbSAugustin Cavalier 			return B_NO_MEMORY;
164396e3dfbSAugustin Cavalier 
165396e3dfbSAugustin Cavalier 		fSmallBufferSize = newBufferSize;
166396e3dfbSAugustin Cavalier 		fSmallBuffer = (uint8*)newBuffer;
167cbc07268SAugustin Cavalier 	}
168cbc07268SAugustin Cavalier 
169cbc07268SAugustin Cavalier 	fSize = newSize;
170cbc07268SAugustin Cavalier 
171425ac1b6SAlexander von Gluck IV //	PRINT("DataContainer::Resize() done: %lx, fSize: %lld\n", error, fSize);
172224e7c42SIngo Weinhold 	return error;
173224e7c42SIngo Weinhold }
174224e7c42SIngo Weinhold 
1756cfbbceeSAugustin Cavalier 
176224e7c42SIngo Weinhold status_t
ReadAt(off_t offset,void * _buffer,size_t size,size_t * bytesRead)177224e7c42SIngo Weinhold DataContainer::ReadAt(off_t offset, void *_buffer, size_t size,
178224e7c42SIngo Weinhold 	size_t *bytesRead)
179224e7c42SIngo Weinhold {
180224e7c42SIngo Weinhold 	uint8 *buffer = (uint8*)_buffer;
181cbc07268SAugustin Cavalier 	status_t error = (buffer && offset >= 0 &&
182cbc07268SAugustin Cavalier 		bytesRead ? B_OK : B_BAD_VALUE);
183cbc07268SAugustin Cavalier 	if (error != B_OK)
184cbc07268SAugustin Cavalier 		return error;
185cbc07268SAugustin Cavalier 
186224e7c42SIngo Weinhold 	// read not more than we have to offer
187224e7c42SIngo Weinhold 	offset = min(offset, fSize);
188224e7c42SIngo Weinhold 	size = min(size, size_t(fSize - offset));
189cbc07268SAugustin Cavalier 
190cbc07268SAugustin Cavalier 	if (!_IsCacheMode()) {
19105b654c6SAugustin Cavalier 		// in non-cache mode, use the "small buffer"
19205b654c6SAugustin Cavalier 		if (IS_USER_ADDRESS(buffer)) {
19305b654c6SAugustin Cavalier 			error = user_memcpy(buffer, fSmallBuffer + offset, size);
19405b654c6SAugustin Cavalier 			if (error != B_OK)
19505b654c6SAugustin Cavalier 				size = 0;
19605b654c6SAugustin Cavalier 		} else {
197cbc07268SAugustin Cavalier 			memcpy(buffer, fSmallBuffer + offset, size);
19805b654c6SAugustin Cavalier 		}
19905b654c6SAugustin Cavalier 
200cbc07268SAugustin Cavalier 		if (bytesRead != NULL)
201cbc07268SAugustin Cavalier 			*bytesRead = size;
20205b654c6SAugustin Cavalier 		return error;
203224e7c42SIngo Weinhold 	}
204cbc07268SAugustin Cavalier 
205cbc07268SAugustin Cavalier 	// cache mode
206cbc07268SAugustin Cavalier 	error = _DoCacheIO(offset, buffer, size, bytesRead, false);
207cbc07268SAugustin Cavalier 
208224e7c42SIngo Weinhold 	return error;
209224e7c42SIngo Weinhold }
210224e7c42SIngo Weinhold 
2116cfbbceeSAugustin Cavalier 
212224e7c42SIngo Weinhold status_t
WriteAt(off_t offset,const void * _buffer,size_t size,size_t * bytesWritten)213224e7c42SIngo Weinhold DataContainer::WriteAt(off_t offset, const void *_buffer, size_t size,
214224e7c42SIngo Weinhold 	size_t *bytesWritten)
215224e7c42SIngo Weinhold {
216425ac1b6SAlexander von Gluck IV 	PRINT("DataContainer::WriteAt(%lld, %p, %lu, %p), fSize: %lld\n", offset, _buffer, size, bytesWritten, fSize);
217cbc07268SAugustin Cavalier 
218224e7c42SIngo Weinhold 	const uint8 *buffer = (const uint8*)_buffer;
219224e7c42SIngo Weinhold 	status_t error = (buffer && offset >= 0 && bytesWritten
220224e7c42SIngo Weinhold 		? B_OK : B_BAD_VALUE);
221cbc07268SAugustin Cavalier 	if (error != B_OK)
222224e7c42SIngo Weinhold 		return error;
223cbc07268SAugustin Cavalier 
224cbc07268SAugustin Cavalier 	// resize the container, if necessary
225c8641d3aSPulkoMandy 	if ((offset + (off_t)size) > fSize)
226cbc07268SAugustin Cavalier 		error = Resize(offset + size);
227cbc07268SAugustin Cavalier 	if (error != B_OK)
228cbc07268SAugustin Cavalier 		return error;
229cbc07268SAugustin Cavalier 
230cbc07268SAugustin Cavalier 	if (!_IsCacheMode()) {
231396e3dfbSAugustin Cavalier 		// in non-cache mode, use the "small buffer"
23205b654c6SAugustin Cavalier 		if (IS_USER_ADDRESS(buffer)) {
23305b654c6SAugustin Cavalier 			error = user_memcpy(fSmallBuffer + offset, buffer, size);
23405b654c6SAugustin Cavalier 			if (error != B_OK)
23505b654c6SAugustin Cavalier 				size = 0;
23605b654c6SAugustin Cavalier 		} else {
237cbc07268SAugustin Cavalier 			memcpy(fSmallBuffer + offset, buffer, size);
23805b654c6SAugustin Cavalier 		}
23905b654c6SAugustin Cavalier 
240cbc07268SAugustin Cavalier 		if (bytesWritten != NULL)
241cbc07268SAugustin Cavalier 			*bytesWritten = size;
24205b654c6SAugustin Cavalier 		return error;
243224e7c42SIngo Weinhold 	}
244224e7c42SIngo Weinhold 
245cbc07268SAugustin Cavalier 	// cache mode
246cbc07268SAugustin Cavalier 	error = _DoCacheIO(offset, (uint8*)buffer, size, bytesWritten, true);
247cbc07268SAugustin Cavalier 
248425ac1b6SAlexander von Gluck IV 	PRINT("DataContainer::WriteAt() done: %lx, fSize: %lld\n", error, fSize);
249cbc07268SAugustin Cavalier 	return error;
250224e7c42SIngo Weinhold }
251224e7c42SIngo Weinhold 
2526cfbbceeSAugustin Cavalier 
253224e7c42SIngo Weinhold void
GetAllocationInfo(AllocationInfo & info)254224e7c42SIngo Weinhold DataContainer::GetAllocationInfo(AllocationInfo &info)
255224e7c42SIngo Weinhold {
256cbc07268SAugustin Cavalier 	if (_IsCacheMode()) {
257cbc07268SAugustin Cavalier 		info.AddAreaAllocation(fCache->committed_size);
258224e7c42SIngo Weinhold 	} else {
259224e7c42SIngo Weinhold 		// ...
260224e7c42SIngo Weinhold 	}
261224e7c42SIngo Weinhold }
262224e7c42SIngo Weinhold 
2636cfbbceeSAugustin Cavalier 
264cbc07268SAugustin Cavalier inline bool
_RequiresCacheMode(size_t size)265cbc07268SAugustin Cavalier DataContainer::_RequiresCacheMode(size_t size)
266224e7c42SIngo Weinhold {
267cbc07268SAugustin Cavalier 	// we cannot back out of cache mode after entering it,
268cbc07268SAugustin Cavalier 	// as there may be other consumers of our VMCache
269396e3dfbSAugustin Cavalier 	return _IsCacheMode() || (size > kMaximumSmallBufferSize);
270224e7c42SIngo Weinhold }
271224e7c42SIngo Weinhold 
2726cfbbceeSAugustin Cavalier 
273cbc07268SAugustin Cavalier inline bool
_IsCacheMode() const274cbc07268SAugustin Cavalier DataContainer::_IsCacheMode() const
275224e7c42SIngo Weinhold {
276cbc07268SAugustin Cavalier 	return fCache != NULL;
277224e7c42SIngo Weinhold }
278224e7c42SIngo Weinhold 
2796cfbbceeSAugustin Cavalier 
280cbc07268SAugustin Cavalier inline int32
_CountBlocks() const281224e7c42SIngo Weinhold DataContainer::_CountBlocks() const
282224e7c42SIngo Weinhold {
283cbc07268SAugustin Cavalier 	if (_IsCacheMode())
284cbc07268SAugustin Cavalier 		return fCache->page_count;
285224e7c42SIngo Weinhold 	else if (fSize == 0)	// small buffer mode, empty buffer
286224e7c42SIngo Weinhold 		return 0;
287224e7c42SIngo Weinhold 	return 1;	// small buffer mode, non-empty buffer
288224e7c42SIngo Weinhold }
289224e7c42SIngo Weinhold 
2906cfbbceeSAugustin Cavalier 
291224e7c42SIngo Weinhold status_t
_SwitchToCacheMode()29258a582ffSAugustin Cavalier DataContainer::_SwitchToCacheMode()
293224e7c42SIngo Weinhold {
294c0a12a6bSAugustin Cavalier 	VMForVnodeCache* cache = new(gVnodeCacheObjectCache, 0) VMForVnodeCache;
295c0a12a6bSAugustin Cavalier 	if (cache == NULL)
296c0a12a6bSAugustin Cavalier 		return B_NO_MEMORY;
297c0a12a6bSAugustin Cavalier 
298c0a12a6bSAugustin Cavalier 	status_t error = cache->Init();
299cbc07268SAugustin Cavalier 	if (error != B_OK)
300cbc07268SAugustin Cavalier 		return error;
301cbc07268SAugustin Cavalier 
302*f940c524SAugustin Cavalier 	AutoLocker<VMCache> locker(cache);
303*f940c524SAugustin Cavalier 
304c0a12a6bSAugustin Cavalier 	fCache = cache;
305cbc07268SAugustin Cavalier 	fCache->temporary = 1;
306a8877df1SAugustin Cavalier 	fCache->unmergeable = 1;
30758a582ffSAugustin Cavalier 	fCache->virtual_end = fSize;
308cbc07268SAugustin Cavalier 
30943feec01SAugustin Cavalier 	error = fCache->Commit(fSize, VM_PRIORITY_USER);
310cbc07268SAugustin Cavalier 	if (error != B_OK)
311cbc07268SAugustin Cavalier 		return error;
312cbc07268SAugustin Cavalier 
313cbc07268SAugustin Cavalier 	if (fSize != 0)
314cbc07268SAugustin Cavalier 		error = _DoCacheIO(0, fSmallBuffer, fSize, NULL, true);
315cbc07268SAugustin Cavalier 
316396e3dfbSAugustin Cavalier 	free(fSmallBuffer);
317396e3dfbSAugustin Cavalier 	fSmallBuffer = NULL;
318396e3dfbSAugustin Cavalier 	fSmallBufferSize = 0;
319396e3dfbSAugustin Cavalier 
320cbc07268SAugustin Cavalier 	return error;
321224e7c42SIngo Weinhold }
322cbc07268SAugustin Cavalier 
3236cfbbceeSAugustin Cavalier 
324cbc07268SAugustin Cavalier status_t
_DoCacheIO(const off_t offset,uint8 * buffer,ssize_t length,size_t * bytesProcessed,bool isWrite)325cbc07268SAugustin Cavalier DataContainer::_DoCacheIO(const off_t offset, uint8* buffer, ssize_t length,
326cbc07268SAugustin Cavalier 	size_t* bytesProcessed, bool isWrite)
327cbc07268SAugustin Cavalier {
328cbc07268SAugustin Cavalier 	const size_t originalLength = length;
329cbc07268SAugustin Cavalier 	const bool user = IS_USER_ADDRESS(buffer);
330cbc07268SAugustin Cavalier 
331cbc07268SAugustin Cavalier 	const off_t rounded_offset = ROUNDDOWN(offset, B_PAGE_SIZE);
332cbc07268SAugustin Cavalier 	const size_t rounded_len = ROUNDUP((length) + (offset - rounded_offset),
333cbc07268SAugustin Cavalier 		B_PAGE_SIZE);
3343d393797SAugustin Cavalier 	BStackOrHeapArray<vm_page*, 16> pages(rounded_len / B_PAGE_SIZE);
3353d393797SAugustin Cavalier 	if (!pages.IsValid())
336cbc07268SAugustin Cavalier 		return B_NO_MEMORY;
337cbc07268SAugustin Cavalier 
338ddfd8f81SAugustin Cavalier 	cache_get_pages(fCache, rounded_offset, rounded_len, isWrite, pages);
339cbc07268SAugustin Cavalier 
340cbc07268SAugustin Cavalier 	status_t error = B_OK;
341cbc07268SAugustin Cavalier 	size_t index = 0;
342cbc07268SAugustin Cavalier 
343cbc07268SAugustin Cavalier 	while (length > 0) {
344cbc07268SAugustin Cavalier 		vm_page* page = pages[index];
34513249ba0SAugustin Cavalier 		phys_addr_t at = (page != NULL)
34613249ba0SAugustin Cavalier 			? (page->physical_page_number * B_PAGE_SIZE) : 0;
347cbc07268SAugustin Cavalier 		ssize_t bytes = B_PAGE_SIZE;
348cbc07268SAugustin Cavalier 		if (index == 0) {
349cbc07268SAugustin Cavalier 			const uint32 pageoffset = (offset % B_PAGE_SIZE);
350cbc07268SAugustin Cavalier 			at += pageoffset;
351cbc07268SAugustin Cavalier 			bytes -= pageoffset;
352cbc07268SAugustin Cavalier 		}
353cbc07268SAugustin Cavalier 		bytes = min(length, bytes);
354cbc07268SAugustin Cavalier 
355cbc07268SAugustin Cavalier 		if (isWrite) {
356cbc07268SAugustin Cavalier 			page->modified = true;
357cbc07268SAugustin Cavalier 			error = vm_memcpy_to_physical(at, buffer, bytes, user);
358cbc07268SAugustin Cavalier 		} else {
359cbc07268SAugustin Cavalier 			if (page != NULL) {
360cbc07268SAugustin Cavalier 				error = vm_memcpy_from_physical(buffer, at, bytes, user);
361cbc07268SAugustin Cavalier 			} else {
362cbc07268SAugustin Cavalier 				if (user) {
363cbc07268SAugustin Cavalier 					error = user_memset(buffer, 0, bytes);
364cbc07268SAugustin Cavalier 				} else {
365cbc07268SAugustin Cavalier 					memset(buffer, 0, bytes);
366cbc07268SAugustin Cavalier 				}
367cbc07268SAugustin Cavalier 			}
368cbc07268SAugustin Cavalier 		}
369cbc07268SAugustin Cavalier 		if (error != B_OK)
370cbc07268SAugustin Cavalier 			break;
371cbc07268SAugustin Cavalier 
372cbc07268SAugustin Cavalier 		buffer += bytes;
373cbc07268SAugustin Cavalier 		length -= bytes;
374cbc07268SAugustin Cavalier 		index++;
375cbc07268SAugustin Cavalier 	}
376cbc07268SAugustin Cavalier 
377ddfd8f81SAugustin Cavalier 	cache_put_pages(fCache, rounded_offset, rounded_len, pages, error == B_OK);
378cbc07268SAugustin Cavalier 
379cbc07268SAugustin Cavalier 	if (bytesProcessed != NULL)
380cbc07268SAugustin Cavalier 		*bytesProcessed = length > 0 ? originalLength - length : originalLength;
381cbc07268SAugustin Cavalier 
382cbc07268SAugustin Cavalier 	return error;
383cbc07268SAugustin Cavalier }
384