Blame SOURCES/autofs-5.1.3-only-take-master-map-mutex-for-master-map-update.patch

306fa1
autofs-5.1.3 - only take master map mutex for master map update
306fa1
306fa1
From: Ian Kent <raven@themaw.net>
306fa1
306fa1
When a map read is done it's neccessary to re-initialize the lookup
306fa1
context which requires a write lock to be taken on the master map
306fa1
entry source. Currently a map re-read also takes the master map lock
306fa1
which will block new lookups.
306fa1
306fa1
If the lookup module thinks the map has been modified it will queue
306fa1
a map read which will take these locks.
306fa1
306fa1
Now, when a bind mount (or symlink) is triggered by a lookup it's
306fa1
necessary to trigger automounts that are included in the target path
306fa1
for the dependent path to be valid.
306fa1
306fa1
When the target path triggers automounts in this way and refers to the
306fa1
same master map entry, and a map re-read is scheduled and started and
306fa1
manages to take the master map lock before the dependent lookup gets
306fa1
past the master map lock check, the dependent path lookup will deadlock.
306fa1
306fa1
But the master map lock is meant to gaurd against master map entries
306fa1
going away during lookups so isn't really needed for map reads as long
306fa1
as the master map lock is held during the update of the mounted
306fa1
automounts.
306fa1
306fa1
Signed-off-by: Ian Kent <raven@themaw.net>
306fa1
---
306fa1
 CHANGELOG      |    1 +
306fa1
 daemon/state.c |    3 ---
306fa1
 lib/master.c   |   45 +++++++++++++++++++++++++++++++++++++--------
306fa1
 3 files changed, 38 insertions(+), 11 deletions(-)
306fa1
306fa1
--- autofs-5.0.7.orig/daemon/state.c
306fa1
+++ autofs-5.0.7/daemon/state.c
306fa1
@@ -484,12 +484,9 @@ static void *do_readmap(void *arg)
306fa1
 
306fa1
 	info(ap->logopt, "re-reading map for %s", ap->path);
306fa1
 
306fa1
-	pthread_cleanup_push(master_mutex_lock_cleanup, NULL);
306fa1
-	master_mutex_lock();
306fa1
 	status = lookup_nss_read_map(ap, NULL, now);
306fa1
 	if (!status)
306fa1
 		pthread_exit(NULL);
306fa1
-	pthread_cleanup_pop(1);
306fa1
 
306fa1
 	if (ap->type == LKP_INDIRECT) {
306fa1
 		struct ioctl_ops *ops = get_ioctl_ops();
306fa1
--- autofs-5.0.7.orig/lib/master.c
306fa1
+++ autofs-5.0.7/lib/master.c
306fa1
@@ -1089,6 +1089,39 @@ next:
306fa1
 	free(paths);
306fa1
 }
306fa1
 
306fa1
+static void wait_for_lookups_and_lock(struct master *master)
306fa1
+{
306fa1
+	struct list_head *p, *head;
306fa1
+	int status;
306fa1
+
306fa1
+again:
306fa1
+	master_mutex_lock();
306fa1
+
306fa1
+	head = &master->mounts;
306fa1
+	p = head->next;
306fa1
+	while (p != head) {
306fa1
+		struct master_mapent *this;
306fa1
+
306fa1
+		this = list_entry(p, struct master_mapent, list);
306fa1
+
306fa1
+		status = pthread_rwlock_trywrlock(&this->source_lock);
306fa1
+		if (status) {
306fa1
+			struct timespec t = { 0, 200000000 };
306fa1
+			struct timespec r;
306fa1
+
306fa1
+			master_mutex_unlock();
306fa1
+
306fa1
+			while (nanosleep(&t, &r) == -1 && errno == EINTR)
306fa1
+				memcpy(&t, &r, sizeof(struct timespec));
306fa1
+
306fa1
+			goto again;
306fa1
+		}
306fa1
+		master_source_unlock(this);
306fa1
+
306fa1
+		p = p->next;
306fa1
+	}
306fa1
+}
306fa1
+
306fa1
 int master_read_master(struct master *master, time_t age, int readall)
306fa1
 {
306fa1
 	unsigned int logopt = master->logopt;
306fa1
@@ -1098,7 +1131,7 @@ int master_read_master(struct master *ma
306fa1
 	 * We need to clear and re-populate the null map entry cache
306fa1
 	 * before alowing anyone else to use it.
306fa1
 	 */
306fa1
-	master_mutex_lock();
306fa1
+	wait_for_lookups_and_lock(master);
306fa1
 	if (master->nc) {
306fa1
 		cache_writelock(master->nc);
306fa1
 		nc = master->nc;
306fa1
@@ -1118,21 +1151,19 @@ int master_read_master(struct master *ma
306fa1
 	lookup_nss_read_master(master, age);
306fa1
 	cache_unlock(nc);
306fa1
 	master_add_amd_mount_section_mounts(master, age);
306fa1
-	master_mutex_unlock();
306fa1
 
306fa1
 	if (!master->read_fail)
306fa1
 		master_mount_mounts(master, age, readall);
306fa1
 	else {
306fa1
 		master->read_fail = 0;
306fa1
 		/* HUP signal sets readall == 1 only */
306fa1
-		if (!readall)
306fa1
+		if (!readall) {
306fa1
+			master_mutex_unlock();
306fa1
 			return 0;
306fa1
-		else
306fa1
+		} else
306fa1
 			master_mount_mounts(master, age, readall);
306fa1
 	}
306fa1
 
306fa1
-	master_mutex_lock();
306fa1
-
306fa1
 	if (list_empty(&master->mounts))
306fa1
 		warn(logopt, "no mounts in table");
306fa1
 
306fa1
@@ -1422,7 +1453,6 @@ int master_mount_mounts(struct master *m
306fa1
 	int cur_state;
306fa1
 
306fa1
 	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state);
306fa1
-	master_mutex_lock();
306fa1
 
306fa1
 	head = &master->mounts;
306fa1
 	p = head->next;
306fa1
@@ -1510,7 +1540,6 @@ cont:
306fa1
 		}
306fa1
 	}
306fa1
 
306fa1
-	master_mutex_unlock();
306fa1
 	pthread_setcancelstate(cur_state, NULL);
306fa1
 
306fa1
 	return 1;
306fa1
--- autofs-5.0.7.orig/CHANGELOG
306fa1
+++ autofs-5.0.7/CHANGELOG
306fa1
@@ -257,6 +257,7 @@
306fa1
 - fix some man page problems.
306fa1
 - allow dot in OPTIONSTR value lexer pattern.
306fa1
 - revert fix argc off by one in mount_autofs.c.
306fa1
+- only take master map mutex for master map update.
306fa1
 
306fa1
 25/07/2012 autofs-5.0.7
306fa1
 =======================