Changeset View
Standalone View
sys/vm/vnode_pager.c
Show First 20 Lines • Show All 141 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
vm_object_t object; | vm_object_t object; | ||||
vm_ooffset_t size = isize; | vm_ooffset_t size = isize; | ||||
struct vattr va; | struct vattr va; | ||||
if (!vn_isdisk(vp, NULL) && vn_canvmio(vp) == FALSE) | if (!vn_isdisk(vp, NULL) && vn_canvmio(vp) == FALSE) | ||||
return (0); | return (0); | ||||
while ((object = vp->v_object) != NULL) { | object = vp->v_object; | ||||
VM_OBJECT_WLOCK(object); | if (object != NULL) | ||||
if (!(object->flags & OBJ_DEAD)) { | |||||
VM_OBJECT_WUNLOCK(object); | |||||
return (0); | return (0); | ||||
} | |||||
VOP_UNLOCK(vp, 0); | |||||
vm_object_set_flag(object, OBJ_DISCONNECTWNT); | |||||
markj: I believe the OBJ_DISCONNECTWNT flag is now unused. | |||||
VM_OBJECT_SLEEP(object, object, PDROP | PVM, "vodead", 0); | |||||
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); | |||||
} | |||||
if (size == 0) { | if (size == 0) { | ||||
if (vn_isdisk(vp, NULL)) { | if (vn_isdisk(vp, NULL)) { | ||||
size = IDX_TO_OFF(INT_MAX); | size = IDX_TO_OFF(INT_MAX); | ||||
} else { | } else { | ||||
if (VOP_GETATTR(vp, &va, td->td_ucred)) | if (VOP_GETATTR(vp, &va, td->td_ucred)) | ||||
return (0); | return (0); | ||||
size = va.va_size; | size = va.va_size; | ||||
Show All 17 Lines | |||||
void | void | ||||
vnode_destroy_vobject(struct vnode *vp) | vnode_destroy_vobject(struct vnode *vp) | ||||
{ | { | ||||
struct vm_object *obj; | struct vm_object *obj; | ||||
obj = vp->v_object; | obj = vp->v_object; | ||||
if (obj == NULL) | if (obj == NULL) | ||||
return; | return; | ||||
Done Inline ActionsShould we assert obj->type == OBJT_VNODE here? markj: Should we assert obj->type == OBJT_VNODE here? | |||||
ASSERT_VOP_ELOCKED(vp, "vnode_destroy_vobject"); | ASSERT_VOP_ELOCKED(vp, "vnode_destroy_vobject"); | ||||
VM_OBJECT_WLOCK(obj); | VM_OBJECT_WLOCK(obj); | ||||
MPASS(obj->type == OBJT_VNODE); | |||||
umtx_shm_object_terminated(obj); | umtx_shm_object_terminated(obj); | ||||
if (obj->ref_count == 0) { | if (obj->ref_count == 0) { | ||||
/* | /* | ||||
* don't double-terminate the object | * don't double-terminate the object | ||||
*/ | */ | ||||
if ((obj->flags & OBJ_DEAD) == 0) { | if ((obj->flags & OBJ_DEAD) == 0) { | ||||
vm_object_set_flag(obj, OBJ_DEAD); | vm_object_set_flag(obj, OBJ_DEAD); | ||||
Show All 13 Lines | if ((obj->flags & OBJ_DEAD) == 0) { | ||||
vm_object_terminate(obj); | vm_object_terminate(obj); | ||||
} else { | } else { | ||||
/* | /* | ||||
* Waiters were already handled during object | * Waiters were already handled during object | ||||
* termination. The exclusive vnode lock hopefully | * termination. The exclusive vnode lock hopefully | ||||
* prevented new waiters from referencing the dying | * prevented new waiters from referencing the dying | ||||
* object. | * object. | ||||
*/ | */ | ||||
KASSERT((obj->flags & OBJ_DISCONNECTWNT) == 0, | |||||
("OBJ_DISCONNECTWNT set obj %p flags %x", | |||||
obj, obj->flags)); | |||||
vp->v_object = NULL; | vp->v_object = NULL; | ||||
VM_OBJECT_WUNLOCK(obj); | VM_OBJECT_WUNLOCK(obj); | ||||
} | } | ||||
} else { | } else { | ||||
/* | /* | ||||
* Woe to the process that tries to page now :-). | * Woe to the process that tries to page now :-). | ||||
*/ | */ | ||||
vm_pager_deallocate(obj); | vm_pager_deallocate(obj); | ||||
VM_OBJECT_WUNLOCK(obj); | VM_OBJECT_WUNLOCK(obj); | ||||
} | } | ||||
KASSERT(vp->v_object == NULL, ("vp %p obj %p", vp, vp->v_object)); | KASSERT(vp->v_object == NULL, ("vp %p obj %p", vp, vp->v_object)); | ||||
} | } | ||||
/* | /* | ||||
* Allocate (or lookup) pager for a vnode. | * Allocate (or lookup) pager for a vnode. | ||||
* Handle is a vnode pointer. | * Handle is a vnode pointer. | ||||
* | |||||
* MPSAFE | |||||
*/ | */ | ||||
Done Inline ActionsDelete the "MPSAFE" while here? markj: Delete the "MPSAFE" while here? | |||||
vm_object_t | vm_object_t | ||||
vnode_pager_alloc(void *handle, vm_ooffset_t size, vm_prot_t prot, | vnode_pager_alloc(void *handle, vm_ooffset_t size, vm_prot_t prot, | ||||
vm_ooffset_t offset, struct ucred *cred) | vm_ooffset_t offset, struct ucred *cred) | ||||
{ | { | ||||
vm_object_t object; | vm_object_t object; | ||||
struct vnode *vp; | struct vnode *vp; | ||||
/* | /* | ||||
* Pageout to vnode, no can do yet. | * Pageout to vnode, no can do yet. | ||||
*/ | */ | ||||
if (handle == NULL) | if (handle == NULL) | ||||
return (NULL); | return (NULL); | ||||
vp = (struct vnode *) handle; | vp = (struct vnode *)handle; | ||||
ASSERT_VOP_LOCKED(vp, "vnode_pager_alloc"); | |||||
/* | KASSERT(vp->v_usecount != 0, ("vnode_pager_alloc: no vnode reference")); | ||||
* If the object is being terminated, wait for it to | |||||
* go away. | |||||
*/ | |||||
retry: | retry: | ||||
while ((object = vp->v_object) != NULL) { | object = vp->v_object; | ||||
VM_OBJECT_WLOCK(object); | |||||
if ((object->flags & OBJ_DEAD) == 0) | |||||
break; | |||||
vm_object_set_flag(object, OBJ_DISCONNECTWNT); | |||||
VM_OBJECT_SLEEP(object, object, PDROP | PVM, "vadead", 0); | |||||
} | |||||
KASSERT(vp->v_usecount != 0, ("vnode_pager_alloc: no vnode reference")); | |||||
if (object == NULL) { | if (object == NULL) { | ||||
/* | /* | ||||
* Add an object of the appropriate size | * Add an object of the appropriate size | ||||
*/ | */ | ||||
object = vm_object_allocate(OBJT_VNODE, OFF_TO_IDX(round_page(size))); | object = vm_object_allocate(OBJT_VNODE, | ||||
OFF_TO_IDX(round_page(size))); | |||||
object->un_pager.vnp.vnp_size = size; | object->un_pager.vnp.vnp_size = size; | ||||
object->un_pager.vnp.writemappings = 0; | object->un_pager.vnp.writemappings = 0; | ||||
object->domain.dr_policy = vnode_domainset; | object->domain.dr_policy = vnode_domainset; | ||||
object->handle = handle; | object->handle = handle; | ||||
VI_LOCK(vp); | VI_LOCK(vp); | ||||
if (vp->v_object != NULL) { | if (vp->v_object != NULL) { | ||||
/* | /* | ||||
* Object has been created while we were sleeping | * Object has been created while we were allocating. | ||||
Done Inline ActionsCan you explain this race? Does it arise from parallel open()s? I do not see why we want to destroy the object and retry in this case. markj: Can you explain this race? Does it arise from parallel open()s? I do not see why we want to… | |||||
Done Inline ActionsThe vnode_pager_alloc() function may be called with vnode only share-locked. This can happen e.g. for UFS read-only opens indeed, or for ZFS any opens, or for UFS directories when lookup is performed. Since these operations can occur in parallel, we might allocate two vnode objects for the vnode. In this case, allocators synchronize on the vnode interlock to assign to vp->v_object, and one thread wins. I do not see what else the loosing thread can do except to free now unneeded object. goto retry is just to ensure that winner succesfully set v_object, and we can bump ref count on it as normal flow. kib: The vnode_pager_alloc() function may be called with vnode only share-locked. This can happen e. | |||||
Done Inline ActionsI see, for some reason I read the code as destroying the pre-existing vp->v_object. markj: I see, for some reason I read the code as destroying the pre-existing vp->v_object. | |||||
*/ | */ | ||||
VI_UNLOCK(vp); | VI_UNLOCK(vp); | ||||
VM_OBJECT_WLOCK(object); | VM_OBJECT_WLOCK(object); | ||||
KASSERT(object->ref_count == 1, | KASSERT(object->ref_count == 1, | ||||
("leaked ref %p %d", object, object->ref_count)); | ("leaked ref %p %d", object, object->ref_count)); | ||||
object->type = OBJT_DEAD; | object->type = OBJT_DEAD; | ||||
object->ref_count = 0; | object->ref_count = 0; | ||||
VM_OBJECT_WUNLOCK(object); | VM_OBJECT_WUNLOCK(object); | ||||
vm_object_destroy(object); | vm_object_destroy(object); | ||||
goto retry; | goto retry; | ||||
} | } | ||||
vp->v_object = object; | vp->v_object = object; | ||||
VI_UNLOCK(vp); | VI_UNLOCK(vp); | ||||
} else { | } else { | ||||
VM_OBJECT_WLOCK(object); | |||||
object->ref_count++; | object->ref_count++; | ||||
#if VM_NRESERVLEVEL > 0 | #if VM_NRESERVLEVEL > 0 | ||||
vm_object_color(object, 0); | vm_object_color(object, 0); | ||||
#endif | #endif | ||||
VM_OBJECT_WUNLOCK(object); | VM_OBJECT_WUNLOCK(object); | ||||
} | } | ||||
vrefact(vp); | vrefact(vp); | ||||
return (object); | return (object); | ||||
Show All 13 Lines | if (vp == NULL) | ||||
panic("vnode_pager_dealloc: pager already dealloced"); | panic("vnode_pager_dealloc: pager already dealloced"); | ||||
VM_OBJECT_ASSERT_WLOCKED(object); | VM_OBJECT_ASSERT_WLOCKED(object); | ||||
vm_object_pip_wait(object, "vnpdea"); | vm_object_pip_wait(object, "vnpdea"); | ||||
refs = object->ref_count; | refs = object->ref_count; | ||||
object->handle = NULL; | object->handle = NULL; | ||||
object->type = OBJT_DEAD; | object->type = OBJT_DEAD; | ||||
if (object->flags & OBJ_DISCONNECTWNT) { | |||||
vm_object_clear_flag(object, OBJ_DISCONNECTWNT); | |||||
wakeup(object); | |||||
} | |||||
ASSERT_VOP_ELOCKED(vp, "vnode_pager_dealloc"); | ASSERT_VOP_ELOCKED(vp, "vnode_pager_dealloc"); | ||||
if (object->un_pager.vnp.writemappings > 0) { | if (object->un_pager.vnp.writemappings > 0) { | ||||
object->un_pager.vnp.writemappings = 0; | object->un_pager.vnp.writemappings = 0; | ||||
VOP_ADD_WRITECOUNT_CHECKED(vp, -1); | VOP_ADD_WRITECOUNT_CHECKED(vp, -1); | ||||
CTR3(KTR_VFS, "%s: vp %p v_writecount decreased to %d", | CTR3(KTR_VFS, "%s: vp %p v_writecount decreased to %d", | ||||
__func__, vp, vp->v_writecount); | __func__, vp, vp->v_writecount); | ||||
} | } | ||||
vp->v_object = NULL; | vp->v_object = NULL; | ||||
▲ Show 20 Lines • Show All 1,257 Lines • Show Last 20 Lines |
I believe the OBJ_DISCONNECTWNT flag is now unused.