diff mbox

[2/7] fedfsd: Flush kernel's exports cache after junction operations

Message ID 20120104210629.8810.39328.stgit@degas.1015granger.net
State Accepted
Headers show

Commit Message

Chuck Lever Jan. 4, 2012, 9:06 p.m. UTC
We want the effects of junction operations to appear to NFS clients
immediately.  But NFSD's export cache will hang onto old junction
information for a time.

All we can do is flush that cache after any operation that changes a
junction.  FedFS ADMIN clients can explicitly request cache flushing
by using certain parameters to the FEDFS_LOOKUP_JUNCTION operation.

Introduce an API to libjunction that can request an export cache
flush.  Call that function in appropriate places within fedfsd.  This
is cribbed from the exportfs command in nfs-utils.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 src/fedfsd/svc.c               |   25 ++++++--
 src/include/junction.h         |    2 +
 src/libjunction/Makefile.am    |    2 -
 src/libjunction/export-cache.c |  121 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 141 insertions(+), 9 deletions(-)
 create mode 100644 src/libjunction/export-cache.c
diff mbox

Patch

diff --git a/src/fedfsd/svc.c b/src/fedfsd/svc.c
index 316042f..6d548c1 100644
--- a/src/fedfsd/svc.c
+++ b/src/fedfsd/svc.c
@@ -538,15 +538,16 @@  fedfsd_svc_create_junction_1(SVCXPRT *xprt)
 	}
 
 	result = fedfs_store_fsn(pathname, fsn_uuid, host);
-	if (result != FEDFS_OK)
+	if (result != FEDFS_OK) {
 		xlog(D_GENERAL, "%s: fedfs_store_fsn", __func__);
-	else {
-		xlog(D_CALL, "%s: uuid: %s",
-			__func__, fsn_uuid);
-		xlog(D_CALL, "%s: nsdb: %s:%u",
-			__func__, nsdb_hostname(host), nsdb_port(host));
+		goto out;
 	}
 
+	(void)junction_flush_exports_cache();
+	xlog(D_CALL, "%s: uuid: %s", __func__, fsn_uuid);
+	xlog(D_CALL, "%s: nsdb: %s:%u",
+			__func__, nsdb_hostname(host), nsdb_port(host));
+
 out:
 	xlog(D_CALL, "%s: Replying with %s",
 			__func__, nsdb_display_fedfsstatus(result));
@@ -643,6 +644,7 @@  fedfsd_svc_delete_junction_1(SVCXPRT *xprt)
 		goto out;
 
 	fedfsd_rmdir(pathname);
+	(void)junction_flush_exports_cache();
 	result = FEDFS_OK;
 
 out:
@@ -800,9 +802,11 @@  fedfsd_svc_lookup_junction_1(SVCXPRT *xprt)
 	result.status = FEDFS_ERR_BADXDR;
 	switch (args.resolve) {
 	case FEDFS_RESOLVE_NONE:
-	case FEDFS_RESOLVE_CACHE:
 	case FEDFS_RESOLVE_NSDB:
 		break;
+	case FEDFS_RESOLVE_CACHE:
+		result.status = FEDFS_ERR_UNKNOWN_CACHE;
+		goto out;
 	default:
 		goto out;
 	}
@@ -839,7 +843,6 @@  fedfsd_svc_lookup_junction_1(SVCXPRT *xprt)
 
 	switch (args.resolve) {
 	case FEDFS_RESOLVE_NONE:
-	case FEDFS_RESOLVE_CACHE:
 		break;
 	case FEDFS_RESOLVE_NSDB:
 		result.status = nsdb_open_nsdb(host, NULL, NULL, &ldap_err);
@@ -857,6 +860,12 @@  fedfsd_svc_lookup_junction_1(SVCXPRT *xprt)
 			break;
 		result.status = fedfsd_prepare_fedfsfsl_array(fsls, resok);
 		nsdb_free_fedfs_fsls(fsls);
+		if (result.status != FEDFS_OK)
+			break;
+		result.status = junction_flush_exports_cache();
+		if (result.status != FEDFS_OK &&
+		    result.status != FEDFS_ERR_NO_CACHE_UPDATE)
+			result.status = FEDFS_OK;
 		break;
 	default:
 		result.status = FEDFS_ERR_SVRFAULT;
diff --git a/src/include/junction.h b/src/include/junction.h
index e09cf5b..edbab7b 100644
--- a/src/include/junction.h
+++ b/src/include/junction.h
@@ -39,4 +39,6 @@  FedFsStatus	 fedfs_is_junction(const char *pathname);
 FedFsStatus	 fedfs_save_mode(const char *pathname);
 FedFsStatus	 fedfs_restore_mode(const char *pathname);
 
+FedFsStatus	 junction_flush_exports_cache(void);
+
 #endif	/* !_FEDFS_JUNCTION_H_ */
diff --git a/src/libjunction/Makefile.am b/src/libjunction/Makefile.am
index bc97bf3..4c43e66 100644
--- a/src/libjunction/Makefile.am
+++ b/src/libjunction/Makefile.am
@@ -24,7 +24,7 @@ 
 ##
 
 noinst_LTLIBRARIES	= libjunction.la
-libjunction_la_SOURCES	= junction.c
+libjunction_la_SOURCES	= export-cache.c junction.c
 
 CLEANFILES		= cscope.in.out cscope.out cscope.po.out *~
 DISTCLEANFILES		= Makefile.in
diff --git a/src/libjunction/export-cache.c b/src/libjunction/export-cache.c
new file mode 100644
index 0000000..e344547
--- /dev/null
+++ b/src/libjunction/export-cache.c
@@ -0,0 +1,121 @@ 
+/**
+ * @file src/libjunction/export-cache.c
+ * @brief Try to flush NFSD's exports cache
+ */
+
+/*
+ * Copyright 2011 Oracle.  All rights reserved.
+ *
+ * This file is part of fedfs-utils.
+ *
+ * fedfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * fedfs-utils is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with fedfs-utils.  If not, see:
+ *
+ *	http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+//#include <errno.h>
+#include <time.h>
+
+
+#include "fedfs.h"
+#include "nsdb.h"
+#include "junction.h"
+#include "xlog.h"
+
+/**
+ * Ordered list of proc files to poke when requesting an NFSD cache flush
+ */
+static const char *junction_proc_files[] = {
+	"/proc/net/rpc/auth.unix.ip/flush",
+	"/proc/net/rpc/auth.unix.gid/flush",
+	"/proc/net/rpc/nfsd.fh/flush",
+	"/proc/net/rpc/nfsd.export/flush",
+	NULL,
+};
+
+/**
+ * Write time into one file
+ *
+ * @param pathname NUL-terminated C string containing POSIX pathname of file to write
+ * @param flushtime NUL-terminated C string containing current time in seconds since the Epoch
+ * @return a FedFsStatus code
+ */
+static FedFsStatus
+junction_write_time(const char *pathname, const char *flushtime)
+{
+	FedFsStatus retval;
+	ssize_t len;
+	int fd;
+
+	fd = open(pathname, O_RDWR);
+	if (fd == -1) {
+		xlog(D_GENERAL, "%s: Failed to open %s: %m",
+			__func__, pathname);
+		/* If the proc files don't exist, no server
+		 * is running on this system */
+		return FEDFS_ERR_NO_CACHE_UPDATE;
+	}
+
+	len = write(fd, flushtime, strlen(flushtime));
+	if (len != (ssize_t)strlen(flushtime)) {
+		xlog(D_GENERAL, "%s: Failed to write %s: %m",
+			__func__, pathname);
+		/* If the proc files exist but the update failed,
+		 * we don't know the state of the cache */
+		retval = FEDFS_ERR_UNKNOWN_CACHE;
+	} else
+		/* Cache flush succeeded */
+		retval = FEDFS_OK;
+	
+	(void)close(fd);
+	return retval;
+}
+
+/**
+ * Flush the kernel NFSD's exports cache
+ *
+ * @return a FedFsStatus code
+ */
+FedFsStatus
+junction_flush_exports_cache(void)
+{
+	FedFsStatus retval;
+	char flushtime[20];
+	unsigned int i;
+	time_t now;
+
+	xlog(D_CALL, "%s: Flushing NFSD caches...", __func__);
+
+	now = time(NULL);
+	if (now == -1) {
+		xlog(D_GENERAL, "%s: time(3) failed", __func__);
+		return FEDFS_ERR_SVRFAULT;
+	}
+	snprintf(flushtime, sizeof(flushtime), "%ld\n", now);
+
+	for (i = 0; junction_proc_files[i] != NULL; i++) {
+		retval = junction_write_time(junction_proc_files[i], flushtime);
+		if (retval != FEDFS_OK)
+			return retval;
+	}
+	return FEDFS_OK;
+}