diff options
-rw-r--r-- | share/man/man9/refcnt_init.9 | 16 | ||||
-rw-r--r-- | sys/kern/kern_synch.c | 13 | ||||
-rw-r--r-- | sys/sys/refcnt.h | 1 |
3 files changed, 30 insertions, 0 deletions
diff --git a/share/man/man9/refcnt_init.9 b/share/man/man9/refcnt_init.9 index 1b19d3b653a..8f45785d45b 100644 --- a/share/man/man9/refcnt_init.9 +++ b/share/man/man9/refcnt_init.9 @@ -20,6 +20,7 @@ .Sh NAME .Nm refcnt_init , .Nm refcnt_take , +.Nm refcnt_take_if_gt , .Nm refcnt_rele , .Nm refcnt_rele_wake , .Nm refcnt_finalize , @@ -32,6 +33,8 @@ .Ft void .Fn "refcnt_take" "struct refcnt *r" .Ft int +.Fn "refcnt_take_if_gt" "struct refcnt *r" "unsigned int n" +.Ft int .Fn "refcnt_rele" "struct refcnt *r" .Ft void .Fn "refcnt_rele_wake" "struct refcnt *r" @@ -51,6 +54,14 @@ is used to acquire a new reference. It is the responsibility of the caller to guarantee that it holds a valid reference before taking a new reference. .Pp +.Fn refcnt_take_if_gt +is used to conditionally acquire a new reference. +If the count is greater than +.Fa n , +a reference is taken. +This allows the caller to safely reference a SMR-protected object in an SMR +read-side critical section. +.Pp .Fn refcnt_rele is used to release an existing reference. .Pp @@ -73,6 +84,7 @@ initialises a declaration of a refcnt to 1. .Sh CONTEXT .Fn refcnt_init , .Fn refcnt_take , +.Fn refcnt_take_if_gt , .Fn refcnt_rele , and .Fn refcnt_rele_wake @@ -82,6 +94,10 @@ context. .Fn refcnt_finalize can be called from process context. .Sh RETURN VALUES +.Fn refcnt_take_if_gt +returns a non-zero value if a reference has been taken, +otherwise 0. +.Pp .Fn refcnt_rele returns a non-zero value if the last reference has been released, otherwise 0. diff --git a/sys/kern/kern_synch.c b/sys/kern/kern_synch.c index b476a6b4253..fe616fdfbfe 100644 --- a/sys/kern/kern_synch.c +++ b/sys/kern/kern_synch.c @@ -818,6 +818,19 @@ refcnt_take(struct refcnt *r) } int +refcnt_take_if_gt(struct refcnt *r, u_int n) +{ + u_int old; + while (1) { + old = READ_ONCE(r->refs); + if (old <= n) + return 0; + if (atomic_cas_uint(&r->refs, old, old + 1) == old) + return 1; + } +} + +int refcnt_rele(struct refcnt *r) { u_int refcnt; diff --git a/sys/sys/refcnt.h b/sys/sys/refcnt.h index 85e84cfdc2d..847e51ce124 100644 --- a/sys/sys/refcnt.h +++ b/sys/sys/refcnt.h @@ -29,6 +29,7 @@ struct refcnt { void refcnt_init(struct refcnt *); void refcnt_take(struct refcnt *); +int refcnt_take_if_gt(struct refcnt *, unsigned int); int refcnt_rele(struct refcnt *); void refcnt_rele_wake(struct refcnt *); void refcnt_finalize(struct refcnt *, const char *); |