Fix a race in vm_page_sleep_if_busy(). Due to vm_object locking

being incomplete, it currently has to know how to drop and pick back
up the vm_object's mutex if it has to sleep and drop the page queue
mutex.  The problem with this is that if the page is busy, while we
are sleeping, the page can be freed and object disappear.  When trying
to lock m->object, we'd get a stale or NULL pointer and crash.

The object is now cached, but this makes the assumption that
the object is referenced in some manner and will not itself
disappear while it is unlocked.  Since this only happens if
the object is locked, I had to remove an assumption earlier in
contigmalloc() that reversed the order of locking the object and
doing vm_page_sleep_if_busy(), not the normal order.
This commit is contained in:
Brian Feldman 2004-07-21 23:56:09 +00:00
parent c5f7772fe7
commit d951b75210
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=132517

View File

@ -414,20 +414,28 @@ vm_page_free_zero(vm_page_t m)
int
vm_page_sleep_if_busy(vm_page_t m, int also_m_busy, const char *msg)
{
vm_object_t object;
int is_object_locked;
mtx_assert(&vm_page_queue_mtx, MA_OWNED);
if ((m->flags & PG_BUSY) || (also_m_busy && m->busy)) {
vm_page_flag_set(m, PG_WANTED | PG_REFERENCED);
/*
* It's possible that while we sleep, the page will get
* unbusied and freed. If we are holding the object
* lock, we will assume we hold a reference to the object
* such that even if m->object changes, we can re-lock
* it.
*
* Remove mtx_owned() after vm_object locking is finished.
*/
if ((is_object_locked = m->object != NULL &&
mtx_owned(&m->object->mtx)))
mtx_unlock(&m->object->mtx);
object = m->object;
if ((is_object_locked = object != NULL &&
mtx_owned(&object->mtx)))
mtx_unlock(&object->mtx);
msleep(m, &vm_page_queue_mtx, PDROP | PVM, msg, 0);
if (is_object_locked)
mtx_lock(&m->object->mtx);
mtx_lock(&object->mtx);
return (TRUE);
}
return (FALSE);