FPPS4/sys/vm/vnode_pager.pas

326 lines
6.0 KiB
Plaintext

unit vnode_pager;
{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}
interface
uses
vnode,
vm,
vmparam,
sys_vm_object;
function vnode_pager_alloc(handle:Pointer;
size:vm_ooffset_t;
prot:vm_prot_t;
offset:vm_ooffset_t):vm_object_t;
function vnode_create_vobject(vp:p_vnode;isize:vm_ooffset_t):Integer;
procedure vnode_destroy_vobject(vp:p_vnode);
procedure vnode_pager_dealloc(obj:vm_object_t);
procedure vnode_pager_setsize(vp:p_vnode;nsize:vm_ooffset_t);
implementation
uses
vnode_if,
vfs_subr,
vfs_vnops,
systm,
kern_param,
kern_mtx;
//
procedure vm_object_terminate(obj:vm_object_t); external;
procedure vm_pager_deallocate(obj:vm_object_t); external;
procedure vm_object_pip_wait(obj:vm_object_t;waitid:pchar); 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;
{
* Allocate (or lookup) pager for a vnode.
* Handle is a vnode pointer.
*
* MPSAFE
}
function vnode_pager_alloc(handle:Pointer;
size:vm_ooffset_t;
prot:vm_prot_t;
offset:vm_ooffset_t):vm_object_t;
label
retry;
var
obj:vm_object_t;
vp:p_vnode;
begin
{
* Pageout to vnode, no can do yet.
}
if (handle=nil) then Exit(nil);
vp:=handle;
{
* If the obj is being terminated, wait for it to
* go away.
}
retry:
obj:=vp^.v_object;
while (obj<>nil) do
begin
VM_OBJECT_LOCK(obj);
if ((obj^.flags and OBJ_DEAD)=0) then
begin
break;
end;
vm_object_set_flag(obj, OBJ_DISCONNECTWNT);
msleep(obj, VM_OBJECT_MTX(obj), PDROP or PVM, 'vadead', 0);
obj:=vp^.v_object;
end;
Assert(vp^.v_usecount<>0, 'vnode_pager_alloc: no vnode reference');
if (obj=nil) then
begin
{
* Add an obj of the appropriate size
}
obj:=vm_object_allocate(OBJT_VNODE, OFF_TO_IDX(round_page(size)));
obj^.un_pager.vnp.vnp_size:=size;
obj^.un_pager.vnp.writemappings:=0;
obj^.handle:=handle;
VI_LOCK(vp);
if (vp^.v_object<>nil) then
begin
// Obj has been created while we were sleeping
VI_UNLOCK(vp);
VM_OBJECT_LOCK(obj);
Assert(obj^.ref_count=1, 'leaked ref %p %d');
obj^.otype:=OBJT_DEAD;
obj^.ref_count:=0;
VM_OBJECT_UNLOCK(obj);
vm_object_destroy(obj);
goto retry;
end;
vp^.v_object:=obj;
VI_UNLOCK(vp);
end else
begin
Inc(obj^.ref_count);
VM_OBJECT_UNLOCK(obj);
end;
vref(vp);
Exit(obj);
end;
{ Create the VM system backing object for this vnode }
function vnode_create_vobject(vp:p_vnode;isize:vm_ooffset_t):Integer;
var
obj:vm_object_t;
size:QWORD;
va:t_vattr;
begin
size:=isize;
if (not vn_isdisk(vp, nil)) and (vn_canvmio(vp)=FALSE) then
begin
Exit(0);
end;
obj:=vp^.v_object;
while (obj<>nil) do
begin
VM_OBJECT_LOCK(obj);
if ((obj^.flags and OBJ_DEAD)=0) then
begin
VM_OBJECT_UNLOCK(obj);
Exit(0);
end;
VOP_UNLOCK(vp, 0);
vm_object_set_flag(obj, OBJ_DISCONNECTWNT);
msleep(obj, VM_OBJECT_MTX(obj), PDROP or PVM, 'vodead', 0);
vn_lock(vp, LK_EXCLUSIVE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
//
obj:=vp^.v_object;
end;
if (size=0) then
begin
if (vn_isdisk(vp, nil)) then
begin
size:=IDX_TO_OFF(High(Integer));
end else
begin
if (VOP_GETATTR(vp, @va)<>0) then
begin
Exit(0);
end;
size:=va.va_size;
end;
end;
obj:=vnode_pager_alloc(vp, size, 0, 0);
{
* Dereference the reference we just created. This assumes
* that the obj is associated with the vp.
}
VM_OBJECT_LOCK(obj);
Dec(obj^.ref_count);
VM_OBJECT_UNLOCK(obj);
vrele(vp);
Assert(vp^.v_object<>nil, 'vnode_create_vobject: nil obj');
Exit(0);
end;
procedure vnode_destroy_vobject(vp:p_vnode);
var
obj:vm_object_t;
begin
obj:=vp^.v_object;
if (obj=nil) then Exit;
ASSERT_VOP_ELOCKED(vp, 'vnode_destroy_vobject');
VM_OBJECT_LOCK(obj);
if (obj^.ref_count=0) then
begin
{
* vclean() may be called twice. The first time
* removes the primary reference to the object,
* the second time goes one further and is a
* special-case to terminate the object.
*
* don't double-terminate the object
}
if ((obj^.flags and OBJ_DEAD)=0) then
vm_object_terminate(obj)
else
VM_OBJECT_UNLOCK(obj);
end else
begin
{
* Woe to the process that tries to page now :-).
}
vm_pager_deallocate(obj);
VM_OBJECT_UNLOCK(obj);
end;
vp^.v_object:=nil;
end;
{
* The object must be locked.
}
procedure vnode_pager_dealloc(obj:vm_object_t);
var
vp:p_vnode;
refs:Integer;
begin
vp:=obj^.handle;
if (vp=nil) then
begin
Writeln(StdErr,'vnode_pager_dealloc: pager already dealloced');
Exit;
end;
VM_OBJECT_LOCK_ASSERT(obj);
vm_object_pip_wait(obj, 'vnpdea');
refs:=obj^.ref_count;
obj^.handle:=nil;
obj^.otype:=OBJT_DEAD;
if ((obj^.flags and OBJ_DISCONNECTWNT)<>0) then
begin
vm_object_clear_flag(obj, OBJ_DISCONNECTWNT);
wakeup(obj);
end;
ASSERT_VOP_ELOCKED(vp, 'vnode_pager_dealloc');
if (obj^.un_pager.vnp.writemappings > 0) then
begin
obj^.un_pager.vnp.writemappings:=0;
VOP_ADD_WRITECOUNT(vp, -1);
end;
vp^.v_object:=nil;
//VOP_UNSET_TEXT(vp);
VM_OBJECT_UNLOCK(obj);
while (refs>0) do
begin
vunref(vp);
Dec(refs);
end;
VM_OBJECT_LOCK(obj);
end;
{
Lets the VM system know about a change in size for a file.
We adjust our own internal size and flush any cached pages in
the associated object that are affected by the size change.
Note: this routine may be invoked as a result of a pager put
operation (possibly at object termination time), so we must be careful.
}
procedure vnode_pager_setsize(vp:p_vnode;nsize:vm_ooffset_t);
var
obj:vm_object_t;
nobjsize:DWORD;
begin
obj:=vp^.v_object;
if (obj=nil) then Exit;
nobjsize:=OFF_TO_IDX(nsize + PAGE_MASK);
VM_OBJECT_LOCK(obj);
if (obj^.otype=OBJT_DEAD) then
begin
VM_OBJECT_UNLOCK(obj);
Exit;
end;
Assert(obj^.otype=OBJT_VNODE,'not vnode-backed obj %p');
obj^.un_pager.vnp.vnp_size:=nsize;
obj^.size:=nobjsize;
VM_OBJECT_UNLOCK(obj);
end;
end.