Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 14 Jul 2002 02:16:43 -0700 (PDT)
From:      Don Lewis <dl-freebsd@catspoiler.org>
To:        arch@FreeBSD.org
Subject:   wiring the sysctl output buffer
Message-ID:  <200207140916.g6E9Ghwr020552@gw.catspoiler.org>

next in thread | raw e-mail | index | archive | help
A number of places in the kernel call SYSCTL_OUT() while holding locks.
The problem is that SYSCTL_OUT() can block while it wires down the "old"
buffer in user space.  If the data is small, such as an integer value,
the best solution may be to copy the data and defer calling SYSCTL_OUT()
until after the locks are released.  This extra copy could be wasteful
if the data is large, and it may be cumbersome if the data size is not
known ahead of time, since determining the data size may be expensive.
The data size could also potentially change between the time the size is
calculatated and the time when the data is copied to the temporary
buffer if the locks must be released so that MALLOC() can be called to
allocate the buffer.

I think the best solution to this problem is to add a sysctl system API
to prewire the output buffer that can be called before grabbing the
locks.  Doing so allows the existing code to operate properly with only
minimal changes.

Here's a patch that implements this new API and illustrates how it can
be used to fix the kern.function_list sysctl, which wants to call
SYSCTL_OUT() multiple times while walking a locked data structure.  I
think this is the best fix, though I'd like some feedback on whether
this is the best API.

Index: sys/sysctl.h
===================================================================
RCS file: /home/ncvs/src/sys/sys/sysctl.h,v
retrieving revision 1.105
diff -u -r1.105 sysctl.h
--- sys/sysctl.h	16 May 2002 21:28:26 -0000	1.105
+++ sys/sysctl.h	14 Jul 2002 07:57:29 -0000
@@ -595,6 +595,7 @@
 			size_t *retval);
 int	sysctl_find_oid(int *name, u_int namelen, struct sysctl_oid **noid,
 			int *nindx, struct sysctl_req *req);
+void	sysctl_wire_old_buffer(struct sysctl_req *req);
 
 #else	/* !_KERNEL */
 #include <sys/cdefs.h>
Index: kern/kern_sysctl.c
===================================================================
RCS file: /home/ncvs/src/sys/kern/kern_sysctl.c,v
retrieving revision 1.126
diff -u -r1.126 kern_sysctl.c
--- kern/kern_sysctl.c	29 Jun 2002 02:00:01 -0000	1.126
+++ kern/kern_sysctl.c	14 Jul 2002 07:57:50 -0000
@@ -990,6 +990,18 @@
 	return (error);
 }
 
+/*
+ * Lock the user space destination buffer.
+ */
+void
+sysctl_wire_old_buffer(struct sysctl_req *req)
+{
+	if (req->lock == 1 && req->oldptr && req->oldfunc == sysctl_old_user) {
+		vslock(req->oldptr, req->oldlen);
+		req->lock = 2;
+	}
+}
+
 int
 sysctl_find_oid(int *name, u_int namelen, struct sysctl_oid **noid,
     int *nindx, struct sysctl_req *req)
Index: kern/kern_linker.c
===================================================================
RCS file: /home/ncvs/src/sys/kern/kern_linker.c,v
retrieving revision 1.91
diff -u -r1.91 kern_linker.c
--- kern/kern_linker.c	7 Jul 2002 22:35:47 -0000	1.91
+++ kern/kern_linker.c	14 Jul 2002 07:58:38 -0000
@@ -1794,6 +1794,7 @@
 	linker_file_t lf;
 	int error;
 
+	sysctl_wire_old_buffer(req);
 	mtx_lock(&kld_mtx);
 	TAILQ_FOREACH(lf, &linker_files, link) {
 		error = LINKER_EACH_FUNCTION_NAME(lf,





To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-arch" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200207140916.g6E9Ghwr020552>