* *	XXX Is the "fake" page handling correct???
vm_object_collapse_aux(object)	vm_object_t	object;{	vm_object_t	backing_object = object->shadow;	vm_offset_t	backing_offset = object->shadow_offset;	vm_size_t	size = object->size;	vm_offset_t	offset;	vm_page_t	backing_page, page = NULL;#ifdef DEBUG	if (vmdebug & VMDEBUG_COLLAPSE)		printf("vm_object_collapse_aux(0x%x)\n", object);#endif	/*	 *	First of all get rid of resident pages in the	 *	backing object.  We can guarantee to remove	 *	every page thus we can write the while-test	 *	like this.	 */	while ((backing_page = backing_object->memq.tqh_first) != NULL) {		/*		 *	If the page is outside the shadowing object's		 *	range or if the page is shadowed (either by a		 *	resident "non-fake" page or a paged out one) we		 *	can discard it right away.  Otherwise we need to		 *	move the page to the shadowing object, perhaps		 *	waking up waiters for "fake" pages first.		 *		 *	XXX There is a condition I'm unsure about, both		 *	if it exists and if I handle it right.  The		 *	case that worries me is if an object can hold		 *	"fake" pages at the same time a real one is		 *	paged out.  To me it sounds as this condition		 *	can't exist.  Does anyone know?  The way the		 *	condition below is done, "fake" pages are		 *	handled suboptimally if pagers are guaranteed		 *	not to have such pages in store.		 */		if (backing_page->offset < backing_offset ||		    (offset = backing_page->offset - backing_offset) >= size ||		    ((page = vm_page_lookup(object, offset)) != NULL &&		    !(page->flags & PG_FAKE)) ||		    (object->pager &&		    vm_pager_has_page(object->pager, offset))) {			/*	Just discard the page, noone needs it.	*/			vm_page_lock_queues();			vm_page_free(backing_page);			vm_page_unlock_queues();		} else {			/*			 *	If a "fake" page was found, someone may			 *	be waiting for it.  Wake her up and then			 *	remove the page.			 */			if (page) {				PAGE_WAKEUP(page);				vm_page_lock_queues();				vm_page_free(page);				vm_page_unlock_queues();			}			/*	Move the page up front.	*/			vm_page_rename(backing_page, object, offset);			/*			 *	If the backing page was ever paged out, it was			 *	due to it being dirty at one point.  Unless we			 *	have no pager allocated to the front object			 *	(thus will move forward the shadow's one),			 *	mark it dirty again so it won't be thrown away			 *	without being paged out to the front pager.			 */			if (object->pager != NULL &&			    vm_object_remove_from_pager(backing_object,			    backing_offset, backing_offset + PAGE_SIZE))				backing_page->flags &= ~PG_CLEAN;		}	}	/*	 *	If not both object have pagers we are essentially	 *	ready.  Just see to that any existing pager is	 *	attached to the shadowing object and then get rid	 *	of the backing object (which at that time should	 *	have neither resident nor paged out pages left).	 */	if (object->pager == NULL || backing_object->pager == NULL) {		/*		 *	If the shadowing object don't have a pager the		 *	easiest thing to do now is to just move the		 *	potential backing pager up front.		 */		if (object->pager == NULL) {			object->pager = backing_object->pager;			object->paging_offset = backing_object->paging_offset +			    backing_offset;			backing_object->pager = NULL;		}	} else {		/*		 *	OK we know both objects have pagers so now we need to		 *	check if the backing objects's paged out pages can be		 *	discarded or needs to be moved.		 *		 *	As a first measure we know we can discard everything		 *	that the shadowing object doesn't shadow.		 */		if (backing_offset > 0)			vm_object_remove_from_pager(backing_object, 0,			    backing_offset);		if (backing_offset + size < backing_object->size)			vm_object_remove_from_pager(backing_object,			    backing_offset + size, backing_object->size);		/*		 *	What's left to do is to find all paged out pages		 *	in the backing pager and either discard or move		 *	it to the front object.		 */		offset = 0;		while (backing_object->pager &&		    (offset = vm_pager_next(backing_object->pager, offset)) <		    backing_object->size) {			/*			 *	If the shadowing object has this page, get			 *	rid of it from the backing pager.  Trust			 *	the loop condition to get us out of here			 *	quickly if we remove the last paged out page.			 *			 *	XXX Would clustering several pages at a time			 *	be a win in this situation?			 *			 *	XXX Is the "fake" page handling correct???			 */			if (((page = vm_page_lookup(object,			    offset - backing_offset)) == NULL ||			    (page->flags & PG_FAKE)) &&			    !vm_pager_has_page(object->pager,			    offset - backing_offset)) {				/*				 *	If a "fake" page was found, someone				 *	may be waiting for it.  Wake her up				 *	and then remove the page.				 */				if (page) {#ifdef DIAGNOSTIC					printf("fake page blown away\n");#endif					PAGE_WAKEUP(page);					vm_page_lock_queues();					vm_page_free(page);					vm_page_unlock_queues();				}				/*				 *	Suck the page from the pager and give				 *	it to the shadowing object.				 */#ifdef DEBUG				if (vmdebug & VMDEBUG_COLLAPSE_PAGEIN)					printf("vm_object_collapse_aux: "					    "pagein needed\n");#endif				/*	First allocate a page.	*/				for (;;) {					backing_page =					    vm_page_alloc(backing_object,					    offset);					if (backing_page)						break;					VM_WAIT;				}				/*	Second, start paging it in.	*/				backing_object->paging_in_progress++;				if (vm_pager_get_pages(backing_object->pager,				    &backing_page, 1, TRUE) != VM_PAGER_OK) {					panic("vm_object_collapse_aux: "					    "could not get paged out page");#if 0					return KERN_FAILURE;#endif				}				if (--backing_object->paging_in_progress == 0)					thread_wakeup(backing_object);				/*				 *	Third, relookup in case pager changed				 *	page.  Pager is responsible for				 *	disposition of old page if moved.				 */				backing_page = vm_page_lookup(backing_object,				    offset);				cnt.v_pgpgin++;				/*				 *	This page was once dirty, otherwise				 *	it hadn't been paged out in this				 *	shadow object.  As we now remove the				 *	persistant store of the page, make				 *	sure it will be paged out in the				 *	front pager by dirtying it.				 */				backing_page->flags &= ~PG_CLEAN;				/*				 *	Fourth, move it up front, and wake up				 *	potential waiters.				 */				vm_page_rename(backing_page, object,				    offset - backing_offset);				PAGE_WAKEUP(backing_page);			}			vm_object_remove_from_pager(backing_object, offset,			    offset + PAGE_SIZE);			offset += PAGE_SIZE;		}	}	/*	 *	I've seen this condition once in an out of VM situation.	 *	For the moment I don't know why it occurred, although I suspect	 *	vm_object_page_clean can create a pager even if it won't use	 *	it.	 */	if (backing_object->pager &&	    vm_pager_count(backing_object->pager) == 0) {		vm_pager_deallocate(backing_object->pager);		backing_object->pager = NULL;	}#ifdef DIAGNOSTIC	if (backing_object->pager)		panic("vm_object_collapse_aux: backing_object->pager remains");#endif	/*	 *	Object now shadows whatever backing_object did.	 *	Note that the reference to backing_object->shadow	 *	moves from within backing_object to within object.	 */	if(backing_object->shadow)		vm_object_lock(backing_object->shadow);	vm_object_set_shadow(object, backing_object->shadow);	if(backing_object->shadow) {		vm_object_set_shadow(backing_object, NULL);		vm_object_unlock(backing_object->shadow);	}	object->shadow_offset += backing_object->shadow_offset;	if (object->shadow != NULL && object->shadow->copy != NULL)		panic("vm_object_collapse_aux: we collapsed a copy-object!");	/*	Fast cleanup is the only thing left now.	*/	vm_object_unlock(backing_object);	simple_lock(&vm_object_list_lock);	TAILQ_REMOVE(&vm_object_list, backing_object, object_list);	vm_object_count--;	simple_unlock(&vm_object_list_lock);	free((caddr_t)backing_object, M_VMOBJ);	object_collapses++;	return KERN_SUCCESS;}