namei: avoid needless relocking for absolute lookups

From: Mateusz Guzik <mjguzik_at_gmail.com>
Date: Mon, 1 Sep 2014 21:55:20 +0200
Currently for absolute lookups the kernel vrefs fd_cdir and
immediately unrefs it and vrefs root vnode.

Patch below changes the code to start with vrefing root vnode for
absolute lookups.

In a crap microbenchmark of 16 threads opening /foo file I got a ~6%
speedup.

The code may require further refactoring later.

diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c
index e4f9d64..421adb6 100644
--- a/sys/kern/vfs_lookup.c
+++ b/sys/kern/vfs_lookup.c
_at__at_ -129,6 +129,27 _at__at_ namei_cleanup_cnp(struct componentname *cnp)
 #endif
 }
 
+static int
+namei_handle_root(struct nameidata *ndp, struct vnode **dpp)
+{
+	struct componentname *cnp = &ndp->ni_cnd;
+
+	if (ndp->ni_strictrelative != 0) {
+#ifdef KTRACE
+		if (KTRPOINT(curthread, KTR_CAPFAIL))
+			ktrcapfail(CAPFAIL_LOOKUP, NULL, NULL);
+#endif
+		return (ENOTCAPABLE);
+	}
+	while (*(cnp->cn_nameptr) == '/') {
+		cnp->cn_nameptr++;
+		ndp->ni_pathlen--;
+	}
+	*dpp = ndp->ni_rootdir;
+	VREF(*dpp);
+	return (0);
+}
+
 int
 namei(struct nameidata *ndp)
 {
_at__at_ -221,6 +242,7 _at__at_ namei(struct nameidata *ndp)
 		AUDIT_ARG_UPATH2(td, ndp->ni_dirfd, cnp->cn_pnbuf);
 
 	dp = NULL;
+	cnp->cn_nameptr = cnp->cn_pnbuf;
 	if (cnp->cn_pnbuf[0] != '/') {
 		if (ndp->ni_startdir != NULL) {
 			dp = ndp->ni_startdir;
_at__at_ -263,6 +285,15 _at__at_ namei(struct nameidata *ndp)
 			namei_cleanup_cnp(cnp);
 			return (error);
 		}
+	} else {
+		error = namei_handle_root(ndp, &dp);
+		FILEDESC_SUNLOCK(fdp);
+		if (ndp->ni_startdir != NULL)
+			vrele(ndp->ni_startdir);
+		if (error != 0) {
+			namei_cleanup_cnp(cnp);
+			return (error);
+		}
 	}
 	if (dp == NULL) {
 		dp = fdp->fd_cdir;
_at__at_ -274,28 +305,6 _at__at_ namei(struct nameidata *ndp)
 	SDT_PROBE(vfs, namei, lookup, entry, dp, cnp->cn_pnbuf,
 	    cnp->cn_flags, 0, 0);
 	for (;;) {
-		/*
-		 * Check if root directory should replace current directory.
-		 * Done at start of translation and after symbolic link.
-		 */
-		cnp->cn_nameptr = cnp->cn_pnbuf;
-		if (*(cnp->cn_nameptr) == '/') {
-			vrele(dp);
-			if (ndp->ni_strictrelative != 0) {
-#ifdef KTRACE
-				if (KTRPOINT(curthread, KTR_CAPFAIL))
-					ktrcapfail(CAPFAIL_LOOKUP, NULL, NULL);
-#endif
-				namei_cleanup_cnp(cnp);
-				return (ENOTCAPABLE);
-			}
-			while (*(cnp->cn_nameptr) == '/') {
-				cnp->cn_nameptr++;
-				ndp->ni_pathlen--;
-			}
-			dp = ndp->ni_rootdir;
-			VREF(dp);
-		}
 		ndp->ni_startdir = dp;
 		error = lookup(ndp);
 		if (error) {
_at__at_ -370,6 +379,18 _at__at_ namei(struct nameidata *ndp)
 		ndp->ni_pathlen += linklen;
 		vput(ndp->ni_vp);
 		dp = ndp->ni_dvp;
+		cnp->cn_nameptr = cnp->cn_pnbuf;
+		/*
+		 * Check if root directory should replace current directory.
+		 */
+		if (*(cnp->cn_nameptr) == '/') {
+			vrele(dp);
+			error = namei_handle_root(ndp, &dp);
+			if (error != 0) {
+				namei_cleanup_cnp(cnp);
+				return (error);
+			}
+		}
 	}
 	namei_cleanup_cnp(cnp);
 	vput(ndp->ni_vp);
-- 
Mateusz Guzik <mjguzik gmail.com>
Received on Mon Sep 01 2014 - 17:55:26 UTC

This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:40:51 UTC