svn commit: r368199 - head/sys/kern

Alexander V. Chernikov melifaro at FreeBSD.org
Mon Nov 30 21:59:53 UTC 2020


Author: melifaro
Date: Mon Nov 30 21:59:52 2020
New Revision: 368199
URL: https://svnweb.freebsd.org/changeset/base/368199

Log:
  Move inner loop logic out of sysctl_sysctl_next_ls().
  
  Refactor sysctl_sysctl_next_ls():
  * Move huge inner loop out of sysctl_sysctl_next_ls() into a separate
   non-recursive function, returning the next step to be taken.
  * Update resulting node oid parts only on successful lookup
  * Make sysctl_sysctl_next_ls() return boolean success/failure instead of errno,
   slightly simplifying logic
  
  Reviewed by:	freqlabs
  Differential Revision:	https://reviews.freebsd.org/D27029

Modified:
  head/sys/kern/kern_sysctl.c

Modified: head/sys/kern/kern_sysctl.c
==============================================================================
--- head/sys/kern/kern_sysctl.c	Mon Nov 30 21:42:55 2020	(r368198)
+++ head/sys/kern/kern_sysctl.c	Mon Nov 30 21:59:52 2020	(r368199)
@@ -1100,109 +1100,148 @@ sysctl_sysctl_name(SYSCTL_HANDLER_ARGS)
 static SYSCTL_NODE(_sysctl, CTL_SYSCTL_NAME, name, CTLFLAG_RD |
     CTLFLAG_MPSAFE | CTLFLAG_CAPRD, sysctl_sysctl_name, "");
 
+enum sysctl_iter_action {
+	ITER_SIBLINGS,	/* Not matched, continue iterating siblings */
+	ITER_CHILDREN,	/* Node has children we need to iterate over them */
+	ITER_FOUND,	/* Matching node was found */
+};
+
 /*
- * Walk the sysctl subtree at lsp until we find the given name,
- * and return the next name in order by oid_number.
+ * Tries to find the next node for @name and @namelen.
+ *
+ * Returns next action to take. 
  */
-static int
-sysctl_sysctl_next_ls(struct sysctl_oid_list *lsp, int *name, u_int namelen, 
-    int *next, int *len, int level, bool honor_skip)
+static enum sysctl_iter_action
+sysctl_sysctl_next_node(struct sysctl_oid *oidp, int *name, unsigned int namelen,
+    bool honor_skip)
 {
-	struct sysctl_oid *oidp;
 
-	SYSCTL_ASSERT_LOCKED();
-	*len = level;
-	SLIST_FOREACH(oidp, lsp, oid_link) {
-		*next = oidp->oid_number;
+	if ((oidp->oid_kind & CTLFLAG_DORMANT) != 0)
+		return (ITER_SIBLINGS);
 
-		if ((oidp->oid_kind & CTLFLAG_DORMANT) != 0)
-			continue;
+	if (honor_skip && (oidp->oid_kind & CTLFLAG_SKIP) != 0)
+		return (ITER_SIBLINGS);
 
-		if (honor_skip && (oidp->oid_kind & CTLFLAG_SKIP) != 0)
-			continue;
+	if (namelen == 0) {
+		/*
+		 * We have reached a node with a full name match and are
+		 * looking for the next oid in its children.
+		 *
+		 * For CTL_SYSCTL_NEXTNOSKIP we are done.
+		 *
+		 * For CTL_SYSCTL_NEXT we skip CTLTYPE_NODE (unless it
+		 * has a handler) and move on to the children.
+		 */
+		if (!honor_skip)
+			return (ITER_FOUND);
+		if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) 
+			return (ITER_FOUND);
+		/* If node does not have an iterator, treat it as leaf */
+		if (oidp->oid_handler) 
+			return (ITER_FOUND);
 
-		if (namelen == 0) {
-			/*
-			 * We have reached a node with a full name match and are
-			 * looking for the next oid in its children.
-			 *
-			 * For CTL_SYSCTL_NEXTNOSKIP we are done.
-			 *
-			 * For CTL_SYSCTL_NEXT we skip CTLTYPE_NODE (unless it
-			 * has a handler) and move on to the children.
-			 */
-			if (!honor_skip)
-				return (0);
-			if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE) 
-				return (0);
-			if (oidp->oid_handler) 
-				return (0);
-			lsp = SYSCTL_CHILDREN(oidp);
-			if (!sysctl_sysctl_next_ls(lsp, NULL, 0, next + 1, len,
-			    level + 1, honor_skip))
-				return (0);
-			/*
-			 * There were no useable children in this node.
-			 * Continue searching for the next oid at this level.
-			 */
-			goto emptynode;
-		}
+		/* Report oid as a node to iterate */
+		return (ITER_CHILDREN);
+	}
 
+	/*
+	 * No match yet. Continue seeking the given name.
+	 *
+	 * We are iterating in order by oid_number, so skip oids lower
+	 * than the one we are looking for.
+	 *
+	 * When the current oid_number is higher than the one we seek,
+	 * that means we have reached the next oid in the sequence and
+	 * should return it.
+	 *
+	 * If the oid_number matches the name at this level then we
+	 * have to find a node to continue searching at the next level.
+	 */
+	if (oidp->oid_number < *name)
+		return (ITER_SIBLINGS);
+	if (oidp->oid_number > *name) {
 		/*
-		 * No match yet. Continue seeking the given name.
+		 * We have reached the next oid.
 		 *
-		 * We are iterating in order by oid_number, so skip oids lower
-		 * than the one we are looking for.
+		 * For CTL_SYSCTL_NEXTNOSKIP we are done.
 		 *
-		 * When the current oid_number is higher than the one we seek,
-		 * that means we have reached the next oid in the sequence and
-		 * should return it.
-		 *
-		 * If the oid_number matches the name at this level then we
-		 * have to find a node to continue searching at the next level.
+		 * For CTL_SYSCTL_NEXT we skip CTLTYPE_NODE (unless it
+		 * has a handler) and move on to the children.
 		 */
-		if (oidp->oid_number < *name)
-			continue;
-		if (oidp->oid_number > *name) {
-			/*
-			 * We have reached the next oid.
-			 *
-			 * For CTL_SYSCTL_NEXTNOSKIP we are done.
-			 *
-			 * For CTL_SYSCTL_NEXT we skip CTLTYPE_NODE (unless it
-			 * has a handler) and move on to the children.
-			 */
-			if (!honor_skip)
-				return (0);
-			if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE)
-				return (0);
-			if (oidp->oid_handler)
-				return (0);
-			lsp = SYSCTL_CHILDREN(oidp);
-			if (!sysctl_sysctl_next_ls(lsp, name + 1, namelen - 1,
-			    next + 1, len, level + 1, honor_skip))
-				return (0);
-			goto next;
-		}
+		if (!honor_skip)
+			return (ITER_FOUND);
 		if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE)
-			continue;
+			return (ITER_FOUND);
+		/* If node does not have an iterator, treat it as leaf */
 		if (oidp->oid_handler)
+			return (ITER_FOUND);
+		return (ITER_CHILDREN);
+	}
+
+	/* match at a current level */
+	if ((oidp->oid_kind & CTLTYPE) != CTLTYPE_NODE)
+		return (ITER_SIBLINGS);
+	if (oidp->oid_handler)
+		return (ITER_SIBLINGS);
+
+	return (ITER_CHILDREN);
+}
+
+/*
+ * Recursively walk the sysctl subtree at lsp until we find the given name.
+ * Returns true and fills in next oid data in @next and @len if oid is found.
+ */
+static bool
+sysctl_sysctl_next_action(struct sysctl_oid_list *lsp, int *name, u_int namelen, 
+    int *next, int *len, int level, bool honor_skip)
+{
+	struct sysctl_oid *oidp;
+	bool success = false;
+	enum sysctl_iter_action action;
+
+	SYSCTL_ASSERT_LOCKED();
+	SLIST_FOREACH(oidp, lsp, oid_link) {
+		action = sysctl_sysctl_next_node(oidp, name, namelen, honor_skip);
+		if (action == ITER_SIBLINGS)
 			continue;
+		if (action == ITER_FOUND) {
+			success = true;
+			break;
+		}
+		KASSERT((action== ITER_CHILDREN), ("ret(%d)!=ITER_CHILDREN", action));
+
 		lsp = SYSCTL_CHILDREN(oidp);
-		if (!sysctl_sysctl_next_ls(lsp, name + 1, namelen - 1,
-		    next + 1, len, level + 1, honor_skip))
-			return (0);
-	next:
-		/*
-		 * There were no useable children in this node.
-		 * Continue searching for the next oid at the root level.
-		 */
-		namelen = 1;
-	emptynode:
-		/* Reset len in case a failed recursive call changed it. */
-		*len = level;
+		if (namelen == 0) {
+			success = sysctl_sysctl_next_action(lsp, NULL, 0,
+			    next + 1, len, level + 1, honor_skip);
+		} else {
+			success = sysctl_sysctl_next_action(lsp, name + 1, namelen - 1,
+			    next + 1, len, level + 1, honor_skip);
+			if (!success) {
+
+				/*
+				 * We maintain the invariant that current node oid
+				 * is >= the oid provided in @name.
+				 * As there are no usable children at this node,
+				 *  current node oid is strictly > than the requested
+				 *  oid.
+				 * Hence, reduce namelen to 0 to allow for picking first
+				 *  nodes/leafs in the next node in list.
+				 */
+				namelen = 0;
+			}
+		}
+		if (success)
+			break;
 	}
-	return (ENOENT);
+
+	if (success) {
+		*next = oidp->oid_number;
+		if (level > *len)
+			*len = level;
+	}
+
+	return (success);
 }
 
 static int
@@ -1211,16 +1250,18 @@ sysctl_sysctl_next(SYSCTL_HANDLER_ARGS)
 	int *name = (int *) arg1;
 	u_int namelen = arg2;
 	int len, error;
+	bool success;
 	struct sysctl_oid_list *lsp = &sysctl__children;
 	struct rm_priotracker tracker;
 	int next[CTL_MAXNAME];
 
+	len = 0;
 	SYSCTL_RLOCK(&tracker);
-	error = sysctl_sysctl_next_ls(lsp, name, namelen, next, &len, 1,
+	success = sysctl_sysctl_next_action(lsp, name, namelen, next, &len, 1,
 	    oidp->oid_number == CTL_SYSCTL_NEXT);
 	SYSCTL_RUNLOCK(&tracker);
-	if (error)
-		return (error);
+	if (!success)
+		return (ENOENT);
 	error = SYSCTL_OUT(req, next, len * sizeof (int));
 	return (error);
 }


More information about the svn-src-head mailing list