浏览代码

nfsd: pass extra info in env vars to upcalls to allow for early grace period end

In order to support lifting the grace period early, we must tell
nfsdcltrack what sort of client the "create" upcall is for. We can't
reliably tell if a v4.0 client has completed reclaiming, so we can only
lift the grace period once all the v4.1+ clients have issued a
RECLAIM_COMPLETE and if there are no v4.0 clients.

Also, in order to lift the grace period, we have to tell userland when
the grace period started so that it can tell whether a RECLAIM_COMPLETE
has been issued for each client since then.

Since this is all optional info, we pass it along in environment
variables to the "init" and "create" upcalls. By doing this, we don't
need to revise the upcall format. The UMH upcall can simply make use of
this info if it happens to be present. If it's not then it can just
avoid lifting the grace period early.

Signed-off-by: Jeff Layton <jlayton@primarydata.com>
Jeff Layton 11 年之前
父节点
当前提交
d4318acd5d
共有 2 个文件被更改,包括 85 次插入15 次删除
  1. 83 13
      fs/nfsd/nfs4recover.c
  2. 2 2
      fs/nfsd/nfs4state.c

+ 83 - 13
fs/nfsd/nfs4recover.c

@@ -1067,6 +1067,8 @@ MODULE_PARM_DESC(cltrack_legacy_disable,
 
 
 #define LEGACY_TOPDIR_ENV_PREFIX "NFSDCLTRACK_LEGACY_TOPDIR="
 #define LEGACY_TOPDIR_ENV_PREFIX "NFSDCLTRACK_LEGACY_TOPDIR="
 #define LEGACY_RECDIR_ENV_PREFIX "NFSDCLTRACK_LEGACY_RECDIR="
 #define LEGACY_RECDIR_ENV_PREFIX "NFSDCLTRACK_LEGACY_RECDIR="
+#define HAS_SESSION_ENV_PREFIX "NFSDCLTRACK_CLIENT_HAS_SESSION="
+#define GRACE_START_ENV_PREFIX "NFSDCLTRACK_GRACE_START="
 
 
 static char *
 static char *
 nfsd4_cltrack_legacy_topdir(void)
 nfsd4_cltrack_legacy_topdir(void)
@@ -1131,10 +1133,60 @@ nfsd4_cltrack_legacy_recdir(const struct xdr_netobj *name)
 	return result;
 	return result;
 }
 }
 
 
+static char *
+nfsd4_cltrack_client_has_session(struct nfs4_client *clp)
+{
+	int copied;
+	size_t len;
+	char *result;
+
+	/* prefix + Y/N character + terminating NULL */
+	len = strlen(HAS_SESSION_ENV_PREFIX) + 1 + 1;
+
+	result = kmalloc(len, GFP_KERNEL);
+	if (!result)
+		return result;
+
+	copied = snprintf(result, len, HAS_SESSION_ENV_PREFIX "%c",
+				clp->cl_minorversion ? 'Y' : 'N');
+	if (copied >= len) {
+		/* just return nothing if output was truncated */
+		kfree(result);
+		return NULL;
+	}
+
+	return result;
+}
+
+static char *
+nfsd4_cltrack_grace_start(time_t grace_start)
+{
+	int copied;
+	size_t len;
+	char *result;
+
+	/* prefix + max width of int64_t string + terminating NULL */
+	len = strlen(GRACE_START_ENV_PREFIX) + 22 + 1;
+
+	result = kmalloc(len, GFP_KERNEL);
+	if (!result)
+		return result;
+
+	copied = snprintf(result, len, GRACE_START_ENV_PREFIX "%ld",
+				grace_start);
+	if (copied >= len) {
+		/* just return nothing if output was truncated */
+		kfree(result);
+		return NULL;
+	}
+
+	return result;
+}
+
 static int
 static int
-nfsd4_umh_cltrack_upcall(char *cmd, char *arg, char *legacy)
+nfsd4_umh_cltrack_upcall(char *cmd, char *arg, char *env0, char *env1)
 {
 {
-	char *envp[2];
+	char *envp[3];
 	char *argv[4];
 	char *argv[4];
 	int ret;
 	int ret;
 
 
@@ -1145,10 +1197,12 @@ nfsd4_umh_cltrack_upcall(char *cmd, char *arg, char *legacy)
 
 
 	dprintk("%s: cmd: %s\n", __func__, cmd);
 	dprintk("%s: cmd: %s\n", __func__, cmd);
 	dprintk("%s: arg: %s\n", __func__, arg ? arg : "(null)");
 	dprintk("%s: arg: %s\n", __func__, arg ? arg : "(null)");
-	dprintk("%s: legacy: %s\n", __func__, legacy ? legacy : "(null)");
+	dprintk("%s: env0: %s\n", __func__, env0 ? env0 : "(null)");
+	dprintk("%s: env1: %s\n", __func__, env1 ? env1 : "(null)");
 
 
-	envp[0] = legacy;
-	envp[1] = NULL;
+	envp[0] = env0;
+	envp[1] = env1;
+	envp[2] = NULL;
 
 
 	argv[0] = (char *)cltrack_prog;
 	argv[0] = (char *)cltrack_prog;
 	argv[1] = cmd;
 	argv[1] = cmd;
@@ -1192,28 +1246,40 @@ bin_to_hex_dup(const unsigned char *src, int srclen)
 }
 }
 
 
 static int
 static int
-nfsd4_umh_cltrack_init(struct net __attribute__((unused)) *net)
+nfsd4_umh_cltrack_init(struct net *net)
 {
 {
+	int ret;
+	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+	char *grace_start = nfsd4_cltrack_grace_start(nn->boot_time);
+
 	/* XXX: The usermode helper s not working in container yet. */
 	/* XXX: The usermode helper s not working in container yet. */
 	if (net != &init_net) {
 	if (net != &init_net) {
 		WARN(1, KERN_ERR "NFSD: attempt to initialize umh client "
 		WARN(1, KERN_ERR "NFSD: attempt to initialize umh client "
 			"tracking in a container!\n");
 			"tracking in a container!\n");
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
-	return nfsd4_umh_cltrack_upcall("init", NULL, NULL);
+
+	ret = nfsd4_umh_cltrack_upcall("init", NULL, grace_start, NULL);
+	kfree(grace_start);
+	return ret;
 }
 }
 
 
 static void
 static void
 nfsd4_umh_cltrack_create(struct nfs4_client *clp)
 nfsd4_umh_cltrack_create(struct nfs4_client *clp)
 {
 {
-	char *hexid;
+	char *hexid, *has_session, *grace_start;
+	struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
 
 
 	hexid = bin_to_hex_dup(clp->cl_name.data, clp->cl_name.len);
 	hexid = bin_to_hex_dup(clp->cl_name.data, clp->cl_name.len);
 	if (!hexid) {
 	if (!hexid) {
 		dprintk("%s: can't allocate memory for upcall!\n", __func__);
 		dprintk("%s: can't allocate memory for upcall!\n", __func__);
 		return;
 		return;
 	}
 	}
-	nfsd4_umh_cltrack_upcall("create", hexid, NULL);
+	has_session = nfsd4_cltrack_client_has_session(clp);
+	grace_start = nfsd4_cltrack_grace_start(nn->boot_time);
+	nfsd4_umh_cltrack_upcall("create", hexid, has_session, grace_start);
+	kfree(has_session);
+	kfree(grace_start);
 	kfree(hexid);
 	kfree(hexid);
 }
 }
 
 
@@ -1227,7 +1293,7 @@ nfsd4_umh_cltrack_remove(struct nfs4_client *clp)
 		dprintk("%s: can't allocate memory for upcall!\n", __func__);
 		dprintk("%s: can't allocate memory for upcall!\n", __func__);
 		return;
 		return;
 	}
 	}
-	nfsd4_umh_cltrack_upcall("remove", hexid, NULL);
+	nfsd4_umh_cltrack_upcall("remove", hexid, NULL, NULL);
 	kfree(hexid);
 	kfree(hexid);
 }
 }
 
 
@@ -1235,17 +1301,21 @@ static int
 nfsd4_umh_cltrack_check(struct nfs4_client *clp)
 nfsd4_umh_cltrack_check(struct nfs4_client *clp)
 {
 {
 	int ret;
 	int ret;
-	char *hexid, *legacy;
+	char *hexid, *has_session, *legacy;
 
 
 	hexid = bin_to_hex_dup(clp->cl_name.data, clp->cl_name.len);
 	hexid = bin_to_hex_dup(clp->cl_name.data, clp->cl_name.len);
 	if (!hexid) {
 	if (!hexid) {
 		dprintk("%s: can't allocate memory for upcall!\n", __func__);
 		dprintk("%s: can't allocate memory for upcall!\n", __func__);
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
+
+	has_session = nfsd4_cltrack_client_has_session(clp);
 	legacy = nfsd4_cltrack_legacy_recdir(&clp->cl_name);
 	legacy = nfsd4_cltrack_legacy_recdir(&clp->cl_name);
-	ret = nfsd4_umh_cltrack_upcall("check", hexid, legacy);
+	ret = nfsd4_umh_cltrack_upcall("check", hexid, has_session, legacy);
+	kfree(has_session);
 	kfree(legacy);
 	kfree(legacy);
 	kfree(hexid);
 	kfree(hexid);
+
 	return ret;
 	return ret;
 }
 }
 
 
@@ -1257,7 +1327,7 @@ nfsd4_umh_cltrack_grace_done(struct nfsd_net *nn)
 
 
 	sprintf(timestr, "%ld", nn->boot_time);
 	sprintf(timestr, "%ld", nn->boot_time);
 	legacy = nfsd4_cltrack_legacy_topdir();
 	legacy = nfsd4_cltrack_legacy_topdir();
-	nfsd4_umh_cltrack_upcall("gracedone", timestr, legacy);
+	nfsd4_umh_cltrack_upcall("gracedone", timestr, legacy, NULL);
 	kfree(legacy);
 	kfree(legacy);
 }
 }
 
 

+ 2 - 2
fs/nfsd/nfs4state.c

@@ -6350,10 +6350,10 @@ nfs4_state_start_net(struct net *net)
 	ret = nfs4_state_create_net(net);
 	ret = nfs4_state_create_net(net);
 	if (ret)
 	if (ret)
 		return ret;
 		return ret;
-	nfsd4_client_tracking_init(net);
 	nn->boot_time = get_seconds();
 	nn->boot_time = get_seconds();
-	locks_start_grace(net, &nn->nfsd4_manager);
 	nn->grace_ended = false;
 	nn->grace_ended = false;
+	locks_start_grace(net, &nn->nfsd4_manager);
+	nfsd4_client_tracking_init(net);
 	printk(KERN_INFO "NFSD: starting %ld-second grace period (net %p)\n",
 	printk(KERN_INFO "NFSD: starting %ld-second grace period (net %p)\n",
 	       nn->nfsd4_grace, net);
 	       nn->nfsd4_grace, net);
 	queue_delayed_work(laundry_wq, &nn->laundromat_work, nn->nfsd4_grace * HZ);
 	queue_delayed_work(laundry_wq, &nn->laundromat_work, nn->nfsd4_grace * HZ);