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.