This makes the refcnt implementation issue memory barriers when
releasing references, splitting memory activity cleanly into preceding
and succeeding stages around refcnt 1->0 transition.

I think the while loop could be optimized a little by re-reading
r->r_refs just after sleep_finish(). That should avoid re-entering
the SCHED_LOCK()'ed section. However, that is not part of this patch.

OK?

Index: share/man/man9/refcnt_init.9
===================================================================
RCS file: src/share/man/man9/refcnt_init.9,v
retrieving revision 1.1
diff -u -p -r1.1 refcnt_init.9
--- share/man/man9/refcnt_init.9        11 Sep 2015 19:13:22 -0000      1.1
+++ share/man/man9/refcnt_init.9        13 Mar 2022 11:40:12 -0000
@@ -68,6 +68,18 @@ There may only be one caller to
 per refcnt
 .Fa r .
 .Pp
+.Fn refcnt_rele ,
+.Fn refcnt_rele_wake
+and
+.Fn refcnt_finalize
+provide release memory ordering.
+The caller's prior memory loads and stores are completed
+before the reference is released.
+The functions provide acquire memory ordering after all the references
+have been released.
+This ensures the object's destructor sees all updates
+done during the lifetime of the object.
+.Pp
 .Fn REFCNT_INITIALIZER
 initialises a declaration of a refcnt to 1.
 .Sh CONTEXT
Index: sys/kern/kern_synch.c
===================================================================
RCS file: src/sys/kern/kern_synch.c,v
retrieving revision 1.183
diff -u -p -r1.183 kern_synch.c
--- sys/kern/kern_synch.c       10 Mar 2022 15:21:08 -0000      1.183
+++ sys/kern/kern_synch.c       13 Mar 2022 11:40:13 -0000
@@ -825,10 +825,16 @@ refcnt_rele(struct refcnt *r)
 {
        u_int refcnt;
 
+       membar_exit_before_atomic();
        refcnt = atomic_dec_int_nv(&r->r_refs);
        KASSERT(refcnt != ~0);
 
-       return (refcnt == 0);
+       if (refcnt == 0) {
+               membar_enter_after_atomic();
+               return (1);
+       }
+
+       return (0);
 }
 
 void
@@ -844,12 +850,20 @@ refcnt_finalize(struct refcnt *r, const 
        struct sleep_state sls;
        u_int refcnt;
 
+       membar_exit_before_atomic();
        refcnt = atomic_dec_int_nv(&r->r_refs);
+       if (refcnt == 0) {
+               membar_enter_after_atomic();
+               return;
+       }
+
        while (refcnt) {
                sleep_setup(&sls, r, PWAIT, wmesg, 0);
                refcnt = atomic_load_int(&r->r_refs);
                sleep_finish(&sls, refcnt);
        }
+       /* Provide acquire ordering after seeing refcnt == 0. */
+       membar_sync();
 }
 
 void

Reply via email to