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