1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-29 12:03:03 +00:00

Do not sleep for vnode lock while holding map lock in vm_fault. Try to

acquire vnode lock for OBJT_VNODE object after map lock is dropped.
Because we have the busy page(s) in the object, sleeping there would
result in deadlock with vnode resize. Try to get lock without sleeping,
and, if the attempt failed, drop the state, lock the vnode, and restart
the fault handler from the start with already locked vnode.

Because the vnode_pager_lock() function is inlined in vm_fault(),
axe it.

Based on suggestion by:	alc
Reviewed by:	tegge, alc
Tested by:	pho
This commit is contained in:
Konstantin Belousov 2009-02-08 20:23:46 +00:00
parent 0dd9f21c23
commit d2bf64c309
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=188331
3 changed files with 80 additions and 97 deletions

View File

@ -130,6 +130,7 @@ struct faultstate {
vm_map_entry_t entry;
int lookup_still_valid;
struct vnode *vp;
int vfslocked;
};
static inline void
@ -171,13 +172,11 @@ unlock_and_deallocate(struct faultstate *fs)
vm_object_deallocate(fs->first_object);
unlock_map(fs);
if (fs->vp != NULL) {
int vfslocked;
vfslocked = VFS_LOCK_GIANT(fs->vp->v_mount);
vput(fs->vp);
fs->vp = NULL;
VFS_UNLOCK_GIANT(vfslocked);
}
VFS_UNLOCK_GIANT(fs->vfslocked);
fs->vfslocked = 0;
}
/*
@ -218,12 +217,17 @@ vm_fault(vm_map_t map, vm_offset_t vaddr, vm_prot_t fault_type,
vm_object_t next_object;
vm_page_t marray[VM_FAULT_READ];
int hardfault;
int faultcount;
int faultcount, ahead, behind;
struct faultstate fs;
struct vnode *vp;
int locked, error;
hardfault = 0;
growstack = TRUE;
PCPU_INC(cnt.v_vm_faults);
fs.vp = NULL;
fs.vfslocked = 0;
faultcount = behind = 0;
RetryFault:;
@ -289,21 +293,9 @@ RetryFault:;
* Bump the paging-in-progress count to prevent size changes (e.g.
* truncation operations) during I/O. This must be done after
* obtaining the vnode lock in order to avoid possible deadlocks.
*
* XXX vnode_pager_lock() can block without releasing the map lock.
*/
if (fs.first_object->flags & OBJ_NEEDGIANT)
mtx_lock(&Giant);
VM_OBJECT_LOCK(fs.first_object);
vm_object_reference_locked(fs.first_object);
fs.vp = vnode_pager_lock(fs.first_object);
KASSERT(fs.vp == NULL || !fs.map->system_map,
("vm_fault: vnode-backed object mapped by system map"));
KASSERT((fs.first_object->flags & OBJ_NEEDGIANT) == 0 ||
!fs.map->system_map,
("vm_fault: Object requiring giant mapped by system map"));
if (fs.first_object->flags & OBJ_NEEDGIANT)
mtx_unlock(&Giant);
vm_object_pip_add(fs.first_object, 1);
fs.lookup_still_valid = TRUE;
@ -380,14 +372,6 @@ RetryFault:;
fs.first_m = NULL;
}
unlock_map(&fs);
if (fs.vp != NULL) {
int vfslck;
vfslck = VFS_LOCK_GIANT(fs.vp->v_mount);
vput(fs.vp);
fs.vp = NULL;
VFS_UNLOCK_GIANT(vfslck);
}
VM_OBJECT_LOCK(fs.object);
if (fs.m == vm_page_lookup(fs.object,
fs.pindex)) {
@ -441,7 +425,9 @@ RetryFault:;
}
#endif
fs.m = vm_page_alloc(fs.object, fs.pindex,
(fs.vp || fs.object->backing_object)? VM_ALLOC_NORMAL: VM_ALLOC_ZERO);
(fs.object->type == OBJT_VNODE ||
fs.object->backing_object != NULL) ?
VM_ALLOC_NORMAL : VM_ALLOC_ZERO);
}
if (fs.m == NULL) {
unlock_and_deallocate(&fs);
@ -464,7 +450,6 @@ RetryFault:;
if (TRYPAGER) {
int rv;
int reqpage = 0;
int ahead, behind;
u_char behavior = vm_map_entry_behavior(fs.entry);
if (behavior == MAP_ENTRY_BEHAV_RANDOM) {
@ -527,6 +512,65 @@ RetryFault:;
}
if (is_first_object_locked)
VM_OBJECT_UNLOCK(fs.first_object);
/*
* Call the pager to retrieve the data, if any, after
* releasing the lock on the map. We hold a ref on
* fs.object and the pages are VPO_BUSY'd.
*/
unlock_map(&fs);
vnode_lock:
if (fs.object->type == OBJT_VNODE) {
vp = fs.object->handle;
if (vp == fs.vp)
goto vnode_locked;
else if (fs.vp != NULL) {
vput(fs.vp);
fs.vp = NULL;
}
locked = VOP_ISLOCKED(vp);
if (VFS_NEEDSGIANT(vp->v_mount) && !fs.vfslocked) {
fs.vfslocked = 1;
if (!mtx_trylock(&Giant)) {
VM_OBJECT_UNLOCK(fs.object);
mtx_lock(&Giant);
VM_OBJECT_LOCK(fs.object);
goto vnode_lock;
}
}
if (locked != LK_EXCLUSIVE)
locked = LK_SHARED;
/* Do not sleep for vnode lock while fs.m is busy */
error = vget(vp, locked | LK_CANRECURSE |
LK_NOWAIT, curthread);
if (error != 0) {
int vfslocked;
vfslocked = fs.vfslocked;
fs.vfslocked = 0; /* Keep Giant */
vhold(vp);
release_page(&fs);
unlock_and_deallocate(&fs);
error = vget(vp, locked | LK_RETRY |
LK_CANRECURSE, curthread);
vdrop(vp);
fs.vp = vp;
fs.vfslocked = vfslocked;
KASSERT(error == 0,
("vm_fault: vget failed"));
goto RetryFault;
}
fs.vp = vp;
}
vnode_locked:
KASSERT(fs.vp == NULL || !fs.map->system_map,
("vm_fault: vnode-backed object mapped by system map"));
KASSERT((fs.first_object->flags & OBJ_NEEDGIANT) == 0 ||
!fs.map->system_map,
("vm_fault: Object requiring giant mapped by system map"));
/*
* now we find out if any other pages should be paged
* in at this time this routine checks to see if the
@ -547,22 +591,6 @@ RetryFault:;
faultcount = vm_fault_additional_pages(
fs.m, behind, ahead, marray, &reqpage);
/*
* update lastr imperfectly (we do not know how much
* getpages will actually read), but good enough.
*
* XXX The following assignment modifies the map
* without holding a write lock on it.
*/
fs.entry->lastr = fs.pindex + faultcount - behind;
/*
* Call the pager to retrieve the data, if any, after
* releasing the lock on the map. We hold a ref on
* fs.object and the pages are VPO_BUSY'd.
*/
unlock_map(&fs);
rv = faultcount ?
vm_pager_get_pages(fs.object, marray, faultcount,
reqpage) : VM_PAGER_FAIL;
@ -839,6 +867,15 @@ RetryFault:;
prot &= retry_prot;
}
}
/*
* update lastr imperfectly (we do not know how much
* getpages will actually read), but good enough.
*
* XXX The following assignment modifies the map
* without holding a write lock on it.
*/
fs.entry->lastr = fs.pindex + faultcount - behind;
if (prot & VM_PROT_WRITE) {
vm_object_set_writeable_dirty(fs.object);

View File

@ -1174,56 +1174,3 @@ vnode_pager_generic_putpages(vp, m, bytecount, flags, rtvals)
}
return rtvals[0];
}
struct vnode *
vnode_pager_lock(vm_object_t first_object)
{
struct vnode *vp;
vm_object_t backing_object, object;
int locked, lockf;
VM_OBJECT_LOCK_ASSERT(first_object, MA_OWNED);
for (object = first_object; object != NULL; object = backing_object) {
if (object->type != OBJT_VNODE) {
if ((backing_object = object->backing_object) != NULL)
VM_OBJECT_LOCK(backing_object);
if (object != first_object)
VM_OBJECT_UNLOCK(object);
continue;
}
retry:
if (object->flags & OBJ_DEAD) {
if (object != first_object)
VM_OBJECT_UNLOCK(object);
return NULL;
}
vp = object->handle;
locked = VOP_ISLOCKED(vp);
VI_LOCK(vp);
VM_OBJECT_UNLOCK(object);
if (first_object != object)
VM_OBJECT_UNLOCK(first_object);
VFS_ASSERT_GIANT(vp->v_mount);
if (locked == LK_EXCLUSIVE)
lockf = LK_CANRECURSE | LK_INTERLOCK | LK_RETRY |
LK_EXCLUSIVE;
else
lockf = LK_CANRECURSE | LK_INTERLOCK | LK_RETRY |
LK_SHARED;
if (vget(vp, lockf, curthread)) {
VM_OBJECT_LOCK(first_object);
if (object != first_object)
VM_OBJECT_LOCK(object);
if (object->type != OBJT_VNODE) {
if (object != first_object)
VM_OBJECT_UNLOCK(object);
return NULL;
}
printf("vnode_pager_lock: retrying\n");
goto retry;
}
VM_OBJECT_LOCK(first_object);
return (vp);
}
return NULL;
}

View File

@ -39,7 +39,6 @@
#define _VNODE_PAGER_ 1
#ifdef _KERNEL
struct vnode *vnode_pager_lock(vm_object_t);
/*
* XXX Generic routines; currently called by badly written FS code; these