FPPS4/sys/vm/vm_object.pas

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.