Module Name:    src
Committed By:   rmind
Date:           Fri Sep 14 22:20:50 UTC 2012

Modified Files:
        src/sys/uvm: uvm_aobj.c uvm_object.h

Log Message:
- Manage anonymous UVM object reference count with atomic ops.
- Fix an old bug of possible lock against oneself (uao_detach_locked() is
  called from uao_swap_off() with uao_list_lock acquired).  Also removes
  the try-lock dance in uao_swap_off(), since the lock order changes.


To generate a diff of this commit:
cvs rdiff -u -r1.117 -r1.118 src/sys/uvm/uvm_aobj.c
cvs rdiff -u -r1.32 -r1.33 src/sys/uvm/uvm_object.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/uvm/uvm_aobj.c
diff -u src/sys/uvm/uvm_aobj.c:1.117 src/sys/uvm/uvm_aobj.c:1.118
--- src/sys/uvm/uvm_aobj.c:1.117	Fri Sep 14 18:56:15 2012
+++ src/sys/uvm/uvm_aobj.c	Fri Sep 14 22:20:50 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_aobj.c,v 1.117 2012/09/14 18:56:15 rmind Exp $	*/
+/*	$NetBSD: uvm_aobj.c,v 1.118 2012/09/14 22:20:50 rmind Exp $	*/
 
 /*
  * Copyright (c) 1998 Chuck Silvers, Charles D. Cranor and
@@ -38,13 +38,12 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_aobj.c,v 1.117 2012/09/14 18:56:15 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_aobj.c,v 1.118 2012/09/14 22:20:50 rmind Exp $");
 
 #include "opt_uvmhist.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
-#include <sys/proc.h>
 #include <sys/kernel.h>
 #include <sys/kmem.h>
 #include <sys/pool.h>
@@ -59,8 +58,8 @@ __KERNEL_RCSID(0, "$NetBSD: uvm_aobj.c,v
  *
  * Lock order
  *
- *	uvm_object::vmobjlock ->
- *		uao_list_lock
+ *	uao_list_lock ->
+ *		uvm_object::vmobjlock
  */
 
 /*
@@ -153,9 +152,6 @@ static int	uao_get(struct uvm_object *, 
 		    int *, int, vm_prot_t, int, int);
 static int	uao_put(struct uvm_object *, voff_t, voff_t, int);
 
-static void uao_detach_locked(struct uvm_object *);
-static void uao_reference_locked(struct uvm_object *);
-
 #if defined(VMSWAP)
 static struct uao_swhash_elt *uao_find_swhash_elt
     (struct uvm_aobj *, int, bool);
@@ -362,7 +358,8 @@ uao_free(struct uvm_aobj *aobj)
 {
 	struct uvm_object *uobj = &aobj->u_obj;
 
-	uao_dropswap_range(aobj, 0, 0);
+	KASSERT(mutex_owned(uobj->vmobjlock));
+	uao_dropswap_range(uobj, 0, 0);
 	mutex_exit(uobj->vmobjlock);
 
 #if defined(VMSWAP)
@@ -512,124 +509,62 @@ uao_init(void)
 }
 
 /*
- * uao_reference: add a ref to an aobj
- *
- * => aobj must be unlocked
- * => just lock it and call the locked version
+ * uao_reference: hold a reference to an anonymous UVM object.
  */
-
 void
 uao_reference(struct uvm_object *uobj)
 {
-
-	/*
- 	 * kernel_object already has plenty of references, leave it alone.
- 	 */
-
-	if (UVM_OBJ_IS_KERN_OBJECT(uobj))
-		return;
-
-	mutex_enter(uobj->vmobjlock);
-	uao_reference_locked(uobj);
-	mutex_exit(uobj->vmobjlock);
-}
-
-/*
- * uao_reference_locked: add a ref to an aobj that is already locked
- *
- * => aobj must be locked
- * this needs to be separate from the normal routine
- * since sometimes we need to add a reference to an aobj when
- * it's already locked.
- */
-
-static void
-uao_reference_locked(struct uvm_object *uobj)
-{
-	UVMHIST_FUNC("uao_reference"); UVMHIST_CALLED(maphist);
-
-	/*
- 	 * kernel_object already has plenty of references, leave it alone.
- 	 */
-
-	if (UVM_OBJ_IS_KERN_OBJECT(uobj))
+	/* Kernel object is persistent. */
+	if (UVM_OBJ_IS_KERN_OBJECT(uobj)) {
 		return;
-
-	uobj->uo_refs++;
-	UVMHIST_LOG(maphist, "<- done (uobj=0x%x, ref = %d)",
-		    uobj, uobj->uo_refs,0,0);
+	}
+	atomic_inc_uint(&uobj->uo_refs);
 }
 
 /*
- * uao_detach: drop a reference to an aobj
- *
- * => aobj must be unlocked
- * => just lock it and call the locked version
+ * uao_detach: drop a reference to an anonymous UVM object.
  */
-
 void
 uao_detach(struct uvm_object *uobj)
 {
-
-	/*
- 	 * detaching from kernel_object is a noop.
- 	 */
-
-	if (UVM_OBJ_IS_KERN_OBJECT(uobj))
-		return;
-
-	mutex_enter(uobj->vmobjlock);
-	uao_detach_locked(uobj);
-}
-
-/*
- * uao_detach_locked: drop a reference to an aobj
- *
- * => aobj must be locked, and is unlocked (or freed) upon return.
- * this needs to be separate from the normal routine
- * since sometimes we need to detach from an aobj when
- * it's already locked.
- */
-
-static void
-uao_detach_locked(struct uvm_object *uobj)
-{
 	struct uvm_aobj *aobj = (struct uvm_aobj *)uobj;
 	struct vm_page *pg;
+
 	UVMHIST_FUNC("uao_detach"); UVMHIST_CALLED(maphist);
 
 	/*
- 	 * detaching from kernel_object is a noop.
- 	 */
+	 * Detaching from kernel object is a NOP.
+	 */
 
-	if (UVM_OBJ_IS_KERN_OBJECT(uobj)) {
-		mutex_exit(uobj->vmobjlock);
+	if (UVM_OBJ_IS_KERN_OBJECT(uobj))
 		return;
-	}
+
+	/*
+	 * Drop the reference.  If it was the last one, destroy the object.
+	 */
 
 	UVMHIST_LOG(maphist,"  (uobj=0x%x)  ref=%d", uobj,uobj->uo_refs,0,0);
-	uobj->uo_refs--;
-	if (uobj->uo_refs) {
-		mutex_exit(uobj->vmobjlock);
+	if (atomic_dec_uint_nv(&uobj->uo_refs) > 0) {
 		UVMHIST_LOG(maphist, "<- done (rc>0)", 0,0,0,0);
 		return;
 	}
 
 	/*
- 	 * remove the aobj from the global list.
- 	 */
+	 * Remove the aobj from the global list.
+	 */
 
 	mutex_enter(&uao_list_lock);
 	LIST_REMOVE(aobj, u_list);
 	mutex_exit(&uao_list_lock);
 
 	/*
- 	 * free all the pages left in the aobj.  for each page,
-	 * when the page is no longer busy (and thus after any disk i/o that
-	 * it's involved in is complete), release any swap resources and
-	 * free the page itself.
- 	 */
+	 * Free all the pages left in the aobj.  For each page, when the
+	 * page is no longer busy (and thus after any disk I/O that it is
+	 * involved in is complete), release any swap resources and free
+	 * the page itself.
+	 */
 
+	mutex_enter(uobj->vmobjlock);
 	mutex_enter(&uvm_pageqlock);
 	while ((pg = TAILQ_FIRST(&uobj->memq)) != NULL) {
 		pmap_page_protect(pg, VM_PROT_NONE);
@@ -648,8 +583,8 @@ uao_detach_locked(struct uvm_object *uob
 	mutex_exit(&uvm_pageqlock);
 
 	/*
- 	 * finally, free the aobj itself.
- 	 */
+	 * Finally, free the anonymous UVM object itself.
+	 */
 
 	uao_free(aobj);
 }
@@ -1135,8 +1070,8 @@ gotpage:
 				 * it from being used again.
 				 */
 
-				swslot = uao_set_swslot(&aobj->u_obj, pageidx,
-							SWSLOT_BAD);
+				swslot = uao_set_swslot(uobj, pageidx,
+				    SWSLOT_BAD);
 				if (swslot > 0) {
 					uvm_swap_markbad(swslot, 1);
 				}
@@ -1211,74 +1146,56 @@ uao_dropswap(struct uvm_object *uobj, in
 bool
 uao_swap_off(int startslot, int endslot)
 {
-	struct uvm_aobj *aobj, *nextaobj;
-	bool rv;
+	struct uvm_aobj *aobj;
 
 	/*
-	 * walk the list of all aobjs.
+	 * Walk the list of all anonymous UVM objects.  Grab the first.
 	 */
-
-restart:
 	mutex_enter(&uao_list_lock);
-	for (aobj = LIST_FIRST(&uao_list);
-	     aobj != NULL;
-	     aobj = nextaobj) {
-
-		/*
-		 * try to get the object lock, start all over if we fail.
-		 * most of the time we'll get the aobj lock,
-		 * so this should be a rare case.
-		 */
-
-		if (!mutex_tryenter(aobj->u_obj.vmobjlock)) {
-			mutex_exit(&uao_list_lock);
-			/* XXX Better than yielding but inadequate. */
-			kpause("livelock", false, 1, NULL);
-			goto restart;
-		}
-
-		/*
-		 * add a ref to the aobj so it doesn't disappear
-		 * while we're working.
-		 */
+	if ((aobj = LIST_FIRST(&uao_list)) == NULL) {
+		mutex_exit(&uao_list_lock);
+		return false;
+	}
+	uao_reference(&aobj->u_obj);
 
-		uao_reference_locked(&aobj->u_obj);
+	do {
+		struct uvm_aobj *nextaobj;
+		bool rv;
 
 		/*
-		 * now it's safe to unlock the uao list.
+		 * Prefetch the next object and immediately hold a reference
+		 * on it, so neither the current nor the next entry could
+		 * disappear while we are iterating.
 		 */
-
+		if ((nextaobj = LIST_NEXT(aobj, u_list)) != NULL) {
+			uao_reference(&nextaobj->u_obj);
+		}
 		mutex_exit(&uao_list_lock);
 
 		/*
-		 * page in any pages in the swslot range.
-		 * if there's an error, abort and return the error.
+		 * Page in all pages in the swap slot range.
 		 */
-
+		mutex_enter(aobj->u_obj.vmobjlock);
 		rv = uao_pagein(aobj, startslot, endslot);
+		mutex_exit(aobj->u_obj.vmobjlock);
+
+		/* Drop the reference of the current object. */
+		uao_detach(&aobj->u_obj);
 		if (rv) {
-			uao_detach_locked(&aobj->u_obj);
+			if (nextaobj) {
+				uao_detach(&nextaobj->u_obj);
+			}
 			return rv;
 		}
 
-		/*
-		 * we're done with this aobj.
-		 * relock the list and drop our ref on the aobj.
-		 */
-
+		aobj = nextaobj;
 		mutex_enter(&uao_list_lock);
-		nextaobj = LIST_NEXT(aobj, u_list);
-		uao_detach_locked(&aobj->u_obj);
-	}
+	} while (aobj);
 
-	/*
-	 * done with traversal, unlock the list
-	 */
 	mutex_exit(&uao_list_lock);
 	return false;
 }
 
-
 /*
  * page in any pages from aobj in the given range.
  *

Index: src/sys/uvm/uvm_object.h
diff -u src/sys/uvm/uvm_object.h:1.32 src/sys/uvm/uvm_object.h:1.33
--- src/sys/uvm/uvm_object.h:1.32	Sat Jan 28 14:37:35 2012
+++ src/sys/uvm/uvm_object.h	Fri Sep 14 22:20:50 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_object.h,v 1.32 2012/01/28 14:37:35 rmind Exp $	*/
+/*	$NetBSD: uvm_object.h,v 1.33 2012/09/14 22:20:50 rmind Exp $	*/
 
 /*
  * Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -48,6 +48,9 @@
  * is the case for tmpfs and layered file systems.  In such case, vnode's
  * UVM object and the underlying UVM object shares the lock (note that the
  * vnode_t::v_interlock points to uvm_object::vmobjlock).
+ *
+ * The reference count is managed atomically for the anonymous UVM objects.
+ * For other objects, it is arbitrary (may use the lock or atomics).
  */
 
 struct uvm_object {

Reply via email to