Module Name:    src
Committed By:   riastradh
Date:           Thu Feb 23 03:00:15 UTC 2023

Modified Files:
        src/sys/kern: kern_descrip.c

Log Message:
kern_descrip.c: Change membar_enter to membar_acquire in fd_getfile.

membar_acquire is cheaper on many CPUs, and unlikely to be costlier
on any CPUs, than the legacy membar_enter.

Add a long comment explaining the interaction between fd_getfile and
fd_close and why membar_acquire is safe.

XXX pullup-10


To generate a diff of this commit:
cvs rdiff -u -r1.253 -r1.254 src/sys/kern/kern_descrip.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/kern/kern_descrip.c
diff -u src/sys/kern/kern_descrip.c:1.253 src/sys/kern/kern_descrip.c:1.254
--- src/sys/kern/kern_descrip.c:1.253	Thu Feb 23 02:58:40 2023
+++ src/sys/kern/kern_descrip.c	Thu Feb 23 03:00:15 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: kern_descrip.c,v 1.253 2023/02/23 02:58:40 riastradh Exp $	*/
+/*	$NetBSD: kern_descrip.c,v 1.254 2023/02/23 03:00:15 riastradh Exp $	*/
 
 /*-
  * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
@@ -70,7 +70,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_descrip.c,v 1.253 2023/02/23 02:58:40 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_descrip.c,v 1.254 2023/02/23 03:00:15 riastradh Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -392,10 +392,45 @@ fd_getfile(unsigned fd)
 		 * Multi threaded: issue a memory barrier to ensure that we
 		 * acquire the file pointer _after_ adding a reference.  If
 		 * no memory barrier, we could fetch a stale pointer.
+		 *
+		 * In particular, we must coordinate the following four
+		 * memory operations:
+		 *
+		 *	A. fd_close store ff->ff_file = NULL
+		 *	B. fd_close refcnt = atomic_dec_uint_nv(&ff->ff_refcnt)
+		 *	C. fd_getfile atomic_inc_uint(&ff->ff_refcnt)
+		 *	D. fd_getfile load fp = ff->ff_file
+		 *
+		 * If the order is D;A;B;C:
+		 *
+		 *	1. D: fp = ff->ff_file
+		 *	2. A: ff->ff_file = NULL
+		 *	3. B: refcnt = atomic_dec_uint_nv(&ff->ff_refcnt)
+		 *	4. C: atomic_inc_uint(&ff->ff_refcnt)
+		 *
+		 * then fd_close determines that there are no more
+		 * references and decides to free fp immediately, at
+		 * the same that fd_getfile ends up with an fp that's
+		 * about to be freed.  *boom*
+		 *
+		 * By making B a release operation in fd_close, and by
+		 * making C an acquire operation in fd_getfile, since
+		 * they are atomic operations on the same object, which
+		 * has a total modification order, we guarantee either:
+		 *
+		 *	- B happens before C.  Then since A is
+		 *	  sequenced before B in fd_close, and C is
+		 *	  sequenced before D in fd_getfile, we
+		 *	  guarantee A happens before D, so fd_getfile
+		 *	  reads a null fp and safely fails.
+		 *
+		 *	- C happens before B.  Then fd_getfile may read
+		 *	  null or nonnull, but either way, fd_close
+		 *	  will safely wait for references to drain.
 		 */
 		atomic_inc_uint(&ff->ff_refcnt);
 #ifndef __HAVE_ATOMIC_AS_MEMBAR
-		membar_enter();
+		membar_acquire();
 #endif
 	}
 

Reply via email to