Ian Kent ca38f0
autofs-5.0.6 - fix submount shutdown race
Ian Kent ca38f0
Ian Kent ca38f0
From: Ian Kent <ikent@redhat.com>
Ian Kent ca38f0
Ian Kent ca38f0
Shutdown of submounts is problematic because the kernel doesn't
Ian Kent ca38f0
know when they are going away and so cannot block path walks
Ian Kent ca38f0
while they shut down. After aquiring the locks that cause mount
Ian Kent ca38f0
requests to wait, the daemon checks if the submount is active before
Ian Kent ca38f0
finally umounting it. If the mount is found to be busy the shutdown
Ian Kent ca38f0
is abandoned and the submount returned to a ready state.
Ian Kent ca38f0
Ian Kent ca38f0
But, if a mount request arrives at the same time as the daemon is
Ian Kent ca38f0
attempting to aquire these locks pthreads appears to become confused
Ian Kent ca38f0
and blocks. So change to using the try version of the lock call and
Ian Kent ca38f0
handling the return appropriately.
Ian Kent ca38f0
---
Ian Kent ca38f0
Ian Kent ca38f0
 CHANGELOG          |    1 +
Ian Kent ca38f0
 daemon/automount.c |   76 ++++++++++++++++++++++++++++++++++++++++------------
Ian Kent ca38f0
 2 files changed, 60 insertions(+), 17 deletions(-)
Ian Kent ca38f0
Ian Kent ca38f0
Ian Kent ca38f0
diff --git a/CHANGELOG b/CHANGELOG
Ian Kent ca38f0
index cac450f..cb9ac75 100644
Ian Kent ca38f0
--- a/CHANGELOG
Ian Kent ca38f0
+++ b/CHANGELOG
Ian Kent ca38f0
@@ -10,6 +10,7 @@
Ian Kent ca38f0
 - fix not bind mounting local filesystem.
Ian Kent ca38f0
 - add "dir" map-type.
Ian Kent ca38f0
 - fix wait for master source mutex.
Ian Kent ca38f0
+- fix submount shutdown race.
Ian Kent ca38f0
 
Ian Kent ca38f0
 28/06/2011 autofs-5.0.6
Ian Kent ca38f0
 -----------------------
Ian Kent ca38f0
diff --git a/daemon/automount.c b/daemon/automount.c
Ian Kent ca38f0
index 376e965..4f3151f 100644
Ian Kent ca38f0
--- a/daemon/automount.c
Ian Kent ca38f0
+++ b/daemon/automount.c
Ian Kent ca38f0
@@ -1495,6 +1495,41 @@ static void handle_mounts_cleanup(void *arg)
Ian Kent ca38f0
 	return;
Ian Kent ca38f0
 }
Ian Kent ca38f0
 
Ian Kent ca38f0
+static int submount_source_writelock_nested(struct autofs_point *ap)
Ian Kent ca38f0
+{
Ian Kent ca38f0
+	struct autofs_point *parent = ap->parent;
Ian Kent ca38f0
+	int status;
Ian Kent ca38f0
+
Ian Kent ca38f0
+	status = pthread_rwlock_trywrlock(&parent->entry->source_lock);
Ian Kent ca38f0
+	if (status)
Ian Kent ca38f0
+		goto done;
Ian Kent ca38f0
+
Ian Kent ca38f0
+	mounts_mutex_lock(parent);
Ian Kent ca38f0
+
Ian Kent ca38f0
+	status = pthread_rwlock_trywrlock(&ap->entry->source_lock);
Ian Kent ca38f0
+	if (status) {
Ian Kent ca38f0
+		mounts_mutex_unlock(parent);
Ian Kent ca38f0
+		master_source_unlock(parent->entry);
Ian Kent ca38f0
+	}
Ian Kent ca38f0
+
Ian Kent ca38f0
+done:
Ian Kent ca38f0
+	if (status && status != EBUSY) {
Ian Kent ca38f0
+		logmsg("submount nested master_mapent source write lock failed");
Ian Kent ca38f0
+		fatal(status);
Ian Kent ca38f0
+	}
Ian Kent ca38f0
+
Ian Kent ca38f0
+	return status;
Ian Kent ca38f0
+}
Ian Kent ca38f0
+
Ian Kent ca38f0
+static void submount_source_unlock_nested(struct autofs_point *ap)
Ian Kent ca38f0
+{
Ian Kent ca38f0
+	struct autofs_point *parent = ap->parent;
Ian Kent ca38f0
+
Ian Kent ca38f0
+	master_source_unlock(ap->entry);
Ian Kent ca38f0
+	mounts_mutex_unlock(parent);
Ian Kent ca38f0
+	master_source_unlock(parent->entry);
Ian Kent ca38f0
+}
Ian Kent ca38f0
+
Ian Kent ca38f0
 void *handle_mounts(void *arg)
Ian Kent ca38f0
 {
Ian Kent ca38f0
 	struct startup_cond *suc;
Ian Kent ca38f0
@@ -1565,23 +1600,32 @@ void *handle_mounts(void *arg)
Ian Kent ca38f0
 			master_mutex_lock();
Ian Kent ca38f0
 
Ian Kent ca38f0
 			if (ap->submount) {
Ian Kent ca38f0
-				master_source_writelock(ap->parent->entry);
Ian Kent ca38f0
-				mounts_mutex_lock(ap->parent);
Ian Kent ca38f0
-			}
Ian Kent ca38f0
-
Ian Kent ca38f0
-			master_source_writelock(ap->entry);
Ian Kent ca38f0
+				/*
Ian Kent ca38f0
+				 * If a mount request arrives before the locks are
Ian Kent ca38f0
+				 * aquired just return to ready state.
Ian Kent ca38f0
+				 */
Ian Kent ca38f0
+				ret = submount_source_writelock_nested(ap);
Ian Kent ca38f0
+				if (ret) {
Ian Kent ca38f0
+					warn(ap->logopt,
Ian Kent ca38f0
+					     "can't shutdown submount: mount in progress");
Ian Kent ca38f0
+					/* Return to ST_READY is done immediately */
Ian Kent ca38f0
+					st_add_task(ap, ST_READY);
Ian Kent ca38f0
+					master_mutex_unlock();
Ian Kent ca38f0
+					pthread_setcancelstate(cur_state, NULL);
Ian Kent ca38f0
+					continue;
Ian Kent ca38f0
+				}
Ian Kent ca38f0
+			} else
Ian Kent ca38f0
+				master_source_writelock(ap->entry);
Ian Kent ca38f0
 
Ian Kent ca38f0
 			if (ap->state != ST_SHUTDOWN) {
Ian Kent ca38f0
 				if (!ap->submount)
Ian Kent ca38f0
 					alarm_add(ap, ap->exp_runfreq);
Ian Kent ca38f0
 				/* Return to ST_READY is done immediately */
Ian Kent ca38f0
 				st_add_task(ap, ST_READY);
Ian Kent ca38f0
-				master_source_unlock(ap->entry);
Ian Kent ca38f0
-				if (ap->submount) {
Ian Kent ca38f0
-					mounts_mutex_unlock(ap->parent);
Ian Kent ca38f0
-					master_source_unlock(ap->parent->entry);
Ian Kent ca38f0
-				}
Ian Kent ca38f0
-
Ian Kent ca38f0
+				if (ap->submount)
Ian Kent ca38f0
+					submount_source_unlock_nested(ap);
Ian Kent ca38f0
+				else
Ian Kent ca38f0
+					master_source_unlock(ap->entry);
Ian Kent ca38f0
 				master_mutex_unlock();
Ian Kent ca38f0
 
Ian Kent ca38f0
 				pthread_setcancelstate(cur_state, NULL);
Ian Kent ca38f0
@@ -1621,12 +1665,10 @@ void *handle_mounts(void *arg)
Ian Kent ca38f0
 				alarm_add(ap, ap->exp_runfreq);
Ian Kent ca38f0
 			/* Return to ST_READY is done immediately */
Ian Kent ca38f0
 			st_add_task(ap, ST_READY);
Ian Kent ca38f0
-			master_source_unlock(ap->entry);
Ian Kent ca38f0
-			if (ap->submount) {
Ian Kent ca38f0
-				mounts_mutex_unlock(ap->parent);
Ian Kent ca38f0
-				master_source_unlock(ap->parent->entry);
Ian Kent ca38f0
-			}
Ian Kent ca38f0
-
Ian Kent ca38f0
+			if (ap->submount)
Ian Kent ca38f0
+				submount_source_unlock_nested(ap);
Ian Kent ca38f0
+			else
Ian Kent ca38f0
+				master_source_unlock(ap->entry);
Ian Kent ca38f0
 			master_mutex_unlock();
Ian Kent ca38f0
 
Ian Kent ca38f0
 			pthread_setcancelstate(cur_state, NULL);