mirror of https://github.com/red-prig/fpPS4.git
621 lines
14 KiB
Plaintext
621 lines
14 KiB
Plaintext
unit vm_object;
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
{$CALLING SysV_ABI_CDecl}
|
|
|
|
interface
|
|
|
|
uses
|
|
vm,
|
|
sys_vm_object,
|
|
kern_param;
|
|
|
|
procedure vm_object_deallocate (obj:vm_object_t);
|
|
|
|
procedure vm_object_pip_wakeup (obj:vm_object_t);
|
|
procedure vm_object_pip_wakeupn (obj:vm_object_t;i:word);
|
|
procedure vm_object_pip_wait (obj:vm_object_t;waitid:pchar);
|
|
|
|
function vm_object_page_clean(obj:vm_object_t;
|
|
start,__end:vm_ooffset_t;
|
|
flags:Integer):Boolean;
|
|
|
|
procedure vm_object_page_remove(obj:vm_object_t;
|
|
start:vm_pindex_t;
|
|
__end:vm_pindex_t;
|
|
options:Integer);
|
|
|
|
procedure vm_object_collapse (obj:vm_object_t);
|
|
|
|
function vm_object_coalesce(prev_object:vm_object_t;
|
|
prev_offset:vm_ooffset_t;
|
|
prev_size :vm_ooffset_t;
|
|
next_size :vm_ooffset_t;
|
|
reserved :Boolean):Boolean;
|
|
|
|
procedure vm_object_madvise(pmap :Pointer;
|
|
obj :vm_object_t;
|
|
start :vm_offset_t;
|
|
__end :vm_offset_t;
|
|
advise:Integer);
|
|
|
|
function vm_object_sync(obj :vm_object_t;
|
|
offset :vm_ooffset_t;
|
|
size :vm_size_t;
|
|
syncio :Boolean;
|
|
invalidate:Boolean):Boolean;
|
|
|
|
implementation
|
|
|
|
uses
|
|
vmparam,
|
|
vm_pmap,
|
|
vnode,
|
|
vnode_if,
|
|
vmount,
|
|
vfs_subr,
|
|
vfs_vnops,
|
|
kern_mtx,
|
|
systm;
|
|
|
|
//
|
|
|
|
procedure vm_pager_deallocate(obj:vm_object_t); external;
|
|
|
|
//
|
|
|
|
function IDX_TO_OFF(x:QWORD):QWORD; inline;
|
|
begin
|
|
Result:=QWORD(x) shl PAGE_SHIFT;
|
|
end;
|
|
|
|
function OFF_TO_IDX(x:QWORD):QWORD; inline;
|
|
begin
|
|
Result:=QWORD(x) shr PAGE_SHIFT;
|
|
end;
|
|
|
|
{
|
|
vm_object_terminate actually destroys the specified object, freeing
|
|
up all previously used resources.
|
|
|
|
The object must be locked.
|
|
This routine may block.
|
|
}
|
|
procedure vm_object_terminate(obj:vm_object_t); public;
|
|
var
|
|
vp:p_vnode;
|
|
begin
|
|
VM_OBJECT_LOCK_ASSERT(obj);
|
|
|
|
{
|
|
* Make sure no one uses us.
|
|
}
|
|
vm_object_set_flag(obj, OBJ_DEAD);
|
|
|
|
{
|
|
* wait for the pageout daemon to be done with the obj
|
|
}
|
|
vm_object_pip_wait(obj, 'objtrm');
|
|
|
|
Assert(obj^.pip=0,'vm_object_terminate: pageout in progress');
|
|
|
|
vm_object_patch_remove(obj,0,0);
|
|
|
|
{
|
|
* Clean and free the pages, as appropriate. All references to the
|
|
* obj are gone, so we don't need to lock it.
|
|
}
|
|
if (obj^.otype=OBJT_VNODE) then
|
|
begin
|
|
vp:=obj^.handle;
|
|
|
|
{
|
|
* Clean pages and flush buffers.
|
|
}
|
|
vm_object_page_clean(obj, 0, 0, OBJPC_SYNC);
|
|
VM_OBJECT_UNLOCK(obj);
|
|
|
|
vinvalbuf(vp, V_SAVE, 0, 0);
|
|
|
|
VM_OBJECT_LOCK(obj);
|
|
end;
|
|
|
|
Assert(obj^.ref_count=0,'vm_object_terminate: obj with references');
|
|
|
|
vm_pager_deallocate(obj);
|
|
VM_OBJECT_UNLOCK(obj);
|
|
|
|
vm_object_destroy(obj);
|
|
end;
|
|
|
|
{
|
|
Handle deallocating an object of type OBJT_VNODE.
|
|
}
|
|
procedure vm_object_vndeallocate(obj:vm_object_t);
|
|
var
|
|
vp:p_vnode;
|
|
begin
|
|
vp:=obj^.handle;
|
|
|
|
VFS_ASSERT_GIANT(vp^.v_mount);
|
|
VM_OBJECT_LOCK_ASSERT(obj);
|
|
|
|
Assert(obj^.otype=OBJT_VNODE,'vm_object_vndeallocate: not a vnode obj');
|
|
Assert(vp<>nil, 'vm_object_vndeallocate: missing vp');
|
|
|
|
if (obj^.ref_count > 1) then
|
|
begin
|
|
VM_OBJECT_UNLOCK(obj);
|
|
// vrele may need the vnode lock.
|
|
vrele(vp);
|
|
end else
|
|
begin
|
|
vhold(vp);
|
|
VM_OBJECT_UNLOCK(obj);
|
|
|
|
vn_lock(vp, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
|
|
vdrop(vp);
|
|
|
|
VM_OBJECT_LOCK(obj);
|
|
|
|
Dec(obj^.ref_count);
|
|
|
|
if (obj^.otype=OBJT_DEAD) then
|
|
begin
|
|
VM_OBJECT_UNLOCK(obj);
|
|
VOP_UNLOCK(vp, 0);
|
|
end else
|
|
begin
|
|
if (obj^.ref_count=0) then
|
|
begin
|
|
//VOP_UNSET_TEXT(vp);
|
|
end;
|
|
VM_OBJECT_UNLOCK(obj);
|
|
vput(vp);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{
|
|
vm_object_deallocate:
|
|
|
|
Release a reference to the specified object,
|
|
gained either through a vm_object_allocate
|
|
or a vm_object_reference call. When all references
|
|
are gone, storage associated with this object
|
|
may be relinquished.
|
|
|
|
No object may be locked.
|
|
}
|
|
procedure vm_object_deallocate(obj:vm_object_t); public;
|
|
label
|
|
restart;
|
|
var
|
|
vfslocked:Integer;
|
|
vp:p_vnode;
|
|
begin
|
|
if (obj=nil) then Exit;
|
|
|
|
VM_OBJECT_LOCK(obj);
|
|
|
|
if (obj^.otype=OBJT_VNODE) then
|
|
begin
|
|
restart:
|
|
|
|
vp:=obj^.handle;
|
|
|
|
vfslocked:=0;
|
|
if VFS_NEEDSGIANT(vp^.v_mount) then
|
|
begin
|
|
vfslocked:=1;
|
|
if not mtx_trylock(VFS_Giant) then
|
|
begin
|
|
VM_OBJECT_UNLOCK(obj);
|
|
mtx_lock(VFS_Giant);
|
|
goto restart;
|
|
end;
|
|
end;
|
|
|
|
vm_object_vndeallocate(obj);
|
|
VFS_UNLOCK_GIANT(vfslocked);
|
|
Exit;
|
|
end;
|
|
|
|
Dec(obj^.ref_count);
|
|
|
|
if (obj^.ref_count=1) then
|
|
begin
|
|
vm_object_set_flag(obj, OBJ_ONEMAPPING);
|
|
end else
|
|
if (obj^.ref_count=0) then
|
|
if ((obj^.flags and OBJ_DEAD)=0) then
|
|
begin
|
|
vm_object_terminate(obj);
|
|
//object deleted
|
|
Exit;
|
|
end;
|
|
|
|
VM_OBJECT_UNLOCK(obj);
|
|
end;
|
|
|
|
procedure vm_object_pip_wakeup(obj:vm_object_t);
|
|
begin
|
|
if (obj=nil) then Exit;
|
|
|
|
VM_OBJECT_LOCK_ASSERT(obj);
|
|
Dec(obj^.pip);
|
|
if ((obj^.flags and OBJ_PIPWNT)<>0) and (obj^.pip=0) then
|
|
begin
|
|
vm_object_clear_flag(obj, OBJ_PIPWNT);
|
|
wakeup(obj);
|
|
end;
|
|
end;
|
|
|
|
procedure vm_object_pip_wakeupn(obj:vm_object_t;i:word);
|
|
begin
|
|
if (obj=nil) then Exit;
|
|
|
|
VM_OBJECT_LOCK_ASSERT(obj);
|
|
Dec(obj^.pip,i);
|
|
if ((obj^.flags and OBJ_PIPWNT)<>0) and (obj^.pip=0) then
|
|
begin
|
|
vm_object_clear_flag(obj, OBJ_PIPWNT);
|
|
wakeup(obj);
|
|
end;
|
|
end;
|
|
|
|
procedure vm_object_pip_wait(obj:vm_object_t;waitid:pchar); public;
|
|
begin
|
|
if (obj=nil) then Exit;
|
|
|
|
VM_OBJECT_LOCK_ASSERT(obj);
|
|
while (obj^.pip<>0) do
|
|
begin
|
|
obj^.flags:=obj^.flags or OBJ_PIPWNT;
|
|
msleep(obj, VM_OBJECT_MTX(obj), PVM, waitid, 0);
|
|
end;
|
|
end;
|
|
|
|
{
|
|
vm_object_page_clean
|
|
|
|
Clean all dirty pages in the specified range of object. Leaves page
|
|
on whatever queue it is currently on. If NOSYNC is set then do not
|
|
write out pages with VPO_NOSYNC set (originally comes from MAP_NOSYNC),
|
|
leaving the object dirty.
|
|
|
|
When stuffing pages asynchronously, allow clustering. XXX we need a
|
|
synchronous clustering mode implementation.
|
|
|
|
Odd semantics: if start == end, we clean everything.
|
|
|
|
The object must be locked.
|
|
|
|
Returns FALSE if some page from the range was not written, as
|
|
reported by the pager, and TRUE otherwise.
|
|
}
|
|
|
|
function vm_object_page_clean(obj:vm_object_t;
|
|
start,__end:vm_ooffset_t;
|
|
flags:Integer):Boolean; public;
|
|
begin
|
|
Result:=True;
|
|
end;
|
|
|
|
{
|
|
vm_object_page_remove:
|
|
|
|
For the given object, either frees or invalidates each of the
|
|
specified pages. In general, a page is freed. However, if a page is
|
|
wired for any reason other than the existence of a managed, wired
|
|
mapping, then it may be invalidated but not removed from the object.
|
|
Pages are specified by the given range ["start", "end") and the option
|
|
OBJPR_CLEANONLY. As a special case, if "end" is zero, then the range
|
|
extends from "start" to the end of the object. If the option
|
|
OBJPR_CLEANONLY is specified, then only the non-dirty pages within the
|
|
specified range are affected. If the option OBJPR_NOTMAPPED is
|
|
specified, then the pages within the specified range must have no
|
|
mappings. Otherwise, if this option is not specified, any mappings to
|
|
the specified pages are removed before the pages are freed or
|
|
invalidated.
|
|
|
|
In general, this operation should only be performed on objects that
|
|
contain managed pages. There are, however, two exceptions. First, it
|
|
is performed on the kernel and kmem objects by vm_map_entry_delete().
|
|
Second, it is used by msync(..., MS_INVALIDATE) to invalidate device-
|
|
backed pages. In both of these cases, the option OBJPR_CLEANONLY must
|
|
not be specified and the option OBJPR_NOTMAPPED must be specified.
|
|
|
|
The object must be locked.
|
|
}
|
|
|
|
procedure vm_object_page_remove(obj:vm_object_t;
|
|
start:vm_pindex_t;
|
|
__end:vm_pindex_t;
|
|
options:Integer); public;
|
|
begin
|
|
vm_object_patch_remove(obj,start,__end);
|
|
end;
|
|
|
|
{
|
|
vm_object_collapse:
|
|
|
|
Collapse an object with the object backing it.
|
|
Pages in the backing object are moved into the
|
|
parent, and the backing object is deallocated.
|
|
}
|
|
|
|
procedure vm_object_collapse(obj:vm_object_t); public;
|
|
begin
|
|
//
|
|
end;
|
|
|
|
{
|
|
Routine: vm_object_coalesce
|
|
Function: Coalesces two objects backing up adjoining
|
|
regions of memory into a single object.
|
|
|
|
returns TRUE if objects were combined.
|
|
|
|
NOTE: Only works at the moment if the second object is NULL -
|
|
if it's not, which object do we lock first?
|
|
|
|
Parameters:
|
|
prev_object First object to coalesce
|
|
prev_offset Offset into prev_object
|
|
prev_size Size of reference to prev_object
|
|
next_size Size of reference to the second object
|
|
reserved Indicator that extension region has
|
|
swap accounted for
|
|
|
|
Conditions:
|
|
The object must *not* be locked.
|
|
}
|
|
|
|
function vm_object_coalesce(prev_object:vm_object_t;
|
|
prev_offset:vm_ooffset_t;
|
|
prev_size :vm_ooffset_t;
|
|
next_size :vm_ooffset_t;
|
|
reserved :Boolean):Boolean; public;
|
|
var
|
|
next_pindex:vm_pindex_t;
|
|
begin
|
|
if (prev_object=nil) then Exit(TRUE);
|
|
|
|
VM_OBJECT_LOCK(prev_object);
|
|
if (prev_object^.otype<>OBJT_DEFAULT) then
|
|
begin
|
|
VM_OBJECT_UNLOCK(prev_object);
|
|
Exit(FALSE);
|
|
end;
|
|
|
|
{
|
|
* Try to collapse the object first
|
|
}
|
|
vm_object_collapse(prev_object);
|
|
|
|
prev_size:=prev_size shr PAGE_SHIFT;
|
|
next_size:=next_size shr PAGE_SHIFT;
|
|
next_pindex:=OFF_TO_IDX(prev_offset) + prev_size;
|
|
|
|
if (prev_object^.ref_count > 1) and
|
|
(prev_object^.size<>next_pindex) then
|
|
begin
|
|
VM_OBJECT_UNLOCK(prev_object);
|
|
Exit(FALSE);
|
|
end;
|
|
|
|
{
|
|
* Extend the object if necessary.
|
|
}
|
|
if (next_pindex + next_size > prev_object^.size) then
|
|
begin
|
|
prev_object^.size:=next_pindex + next_size;
|
|
end;
|
|
|
|
VM_OBJECT_UNLOCK(prev_object);
|
|
Result:=(TRUE);
|
|
end;
|
|
|
|
{
|
|
vm_object_madvise:
|
|
|
|
Implements the madvise function at the object/page level.
|
|
|
|
MADV_WILLNEED (any object)
|
|
|
|
Activate the specified pages if they are resident.
|
|
|
|
MADV_DONTNEED (any object)
|
|
|
|
Deactivate the specified pages if they are resident.
|
|
|
|
MADV_FREE (OBJT_DEFAULT/OBJT_SWAP objects,
|
|
OBJ_ONEMAPPING only)
|
|
|
|
Deactivate and clean the specified pages if they are
|
|
resident. This permits the process to reuse the pages
|
|
without faulting or the kernel to reclaim the pages
|
|
without I/O.
|
|
}
|
|
procedure vm_object_madvise(pmap :Pointer;
|
|
obj :vm_object_t;
|
|
start :vm_offset_t;
|
|
__end :vm_offset_t;
|
|
advise:Integer);
|
|
label
|
|
unlock_tobject;
|
|
begin
|
|
if (obj=nil) then
|
|
begin
|
|
|
|
case advise of
|
|
MADV_WILLNEED,
|
|
MADV_DONTNEED,
|
|
MADV_FREE :
|
|
begin
|
|
pmap_madvise(pmap,
|
|
obj,
|
|
start,
|
|
__end,
|
|
advise);
|
|
end
|
|
else;
|
|
end;
|
|
|
|
Exit;
|
|
end;
|
|
|
|
VM_OBJECT_LOCK(obj);
|
|
|
|
{
|
|
* MADV_FREE only operates on OBJT_DEFAULT or OBJT_SWAP pages
|
|
* and those pages must be OBJ_ONEMAPPING.
|
|
}
|
|
if (advise=MADV_FREE) then
|
|
begin
|
|
if ((obj^.otype<>OBJT_DEFAULT) and
|
|
(obj^.otype<>OBJT_SELF) and //
|
|
(obj^.otype<>OBJT_SWAP)) or
|
|
((obj^.flags and OBJ_ONEMAPPING)=0) then
|
|
begin
|
|
goto unlock_tobject;
|
|
end;
|
|
end else
|
|
if (obj^.otype=OBJT_PHYS) then
|
|
begin
|
|
goto unlock_tobject;
|
|
end;
|
|
|
|
case advise of
|
|
MADV_WILLNEED,
|
|
MADV_DONTNEED,
|
|
MADV_FREE :
|
|
begin
|
|
pmap_madvise(pmap,
|
|
obj,
|
|
start,
|
|
__end,
|
|
advise);
|
|
end
|
|
else;
|
|
end;
|
|
|
|
unlock_tobject:
|
|
VM_OBJECT_UNLOCK(obj);
|
|
end;
|
|
|
|
{
|
|
* Note that there is absolutely no sense in writing out
|
|
* anonymous objects, so we track down the vnode object
|
|
* to write out.
|
|
* We invalidate (remove) all pages from the address space
|
|
* for semantic correctness.
|
|
*
|
|
* If the backing object is a device object with unmanaged pages, then any
|
|
* mappings to the specified range of pages must be removed before this
|
|
* function is called.
|
|
*
|
|
* Note: certain anonymous maps, such as MAP_NOSYNC maps,
|
|
* may start out with a nil object.
|
|
}
|
|
function vm_object_sync(obj :vm_object_t;
|
|
offset :vm_ooffset_t;
|
|
size :vm_size_t;
|
|
syncio :Boolean;
|
|
invalidate:Boolean):Boolean;
|
|
var
|
|
vp:p_vnode;
|
|
mp:p_mount;
|
|
error,flags:Integer;
|
|
vfslocked:Integer;
|
|
fsync_after:Boolean;
|
|
res:Boolean;
|
|
begin
|
|
if (obj=nil) then Exit(TRUE);
|
|
|
|
res:=TRUE;
|
|
error:=0;
|
|
VM_OBJECT_LOCK(obj);
|
|
|
|
{
|
|
* Flush pages if writing is allowed, invalidate them
|
|
* if invalidation requested. Pages undergoing I/O
|
|
* will be ignored by vm_object_page_remove().
|
|
*
|
|
* We cannot lock the vnode and then wait for paging
|
|
* to complete without deadlocking against vm_fault.
|
|
* Instead we simply call vm_object_page_remove() and
|
|
* allow it to block internally on a page-by-page
|
|
* basis when it encounters pages undergoing async
|
|
* I/O.
|
|
}
|
|
if (obj^.otype=OBJT_VNODE) and
|
|
((obj^.flags and OBJ_MIGHTBEDIRTY)<>0) then
|
|
begin
|
|
vp:=obj^.handle;
|
|
VM_OBJECT_UNLOCK(obj);
|
|
vn_start_write(vp, @mp, V_WAIT);
|
|
vfslocked:=VFS_LOCK_GIANT(vp^.v_mount);
|
|
vn_lock(vp, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
|
|
|
|
if (syncio) and
|
|
(not invalidate) and
|
|
(offset=0) and
|
|
(OFF_TO_IDX(size)=obj^.size) then
|
|
begin
|
|
{
|
|
* If syncing the whole mapping of the file,
|
|
* it is faster to schedule all the writes in
|
|
* async mode, also allowing the clustering,
|
|
* and then wait for i/o to complete.
|
|
}
|
|
flags:=0;
|
|
fsync_after:=TRUE;
|
|
end else
|
|
begin
|
|
flags:=0;
|
|
|
|
if (syncio or invalidate) then
|
|
begin
|
|
flags:=flags or OBJPC_SYNC;
|
|
end;
|
|
|
|
if (invalidate) then
|
|
begin
|
|
flags:=flags or (OBJPC_SYNC or OBJPC_INVAL);
|
|
end;
|
|
|
|
fsync_after:=FALSE;
|
|
end;
|
|
|
|
VM_OBJECT_LOCK(obj);
|
|
res:=vm_object_page_clean(obj, offset, offset + size, flags);
|
|
VM_OBJECT_UNLOCK(obj);
|
|
|
|
if (fsync_after) then
|
|
begin
|
|
error:=VOP_FSYNC(vp, MNT_WAIT);
|
|
end;
|
|
|
|
VOP_UNLOCK(vp, 0);
|
|
VFS_UNLOCK_GIANT(vfslocked);
|
|
vn_finished_write(mp);
|
|
|
|
if (error<>0) then
|
|
begin
|
|
res:=FALSE;
|
|
end;
|
|
|
|
VM_OBJECT_LOCK(obj);
|
|
end;
|
|
|
|
VM_OBJECT_UNLOCK(obj);
|
|
Exit(res);
|
|
end;
|
|
|
|
|
|
|
|
end.
|
|
|