1.\" $NetBSD: psref.9,v 1.5 2016/04/27 08:18:40 ozaki-r Exp $ 2.\" 3.\" Copyright (c) 2016 The NetBSD Foundation, Inc. 4.\" All rights reserved. 5.\" 6.\" This code is derived from software contributed to The NetBSD Foundation 7.\" by Taylor R. Campbell. 8.\" 9.\" Redistribution and use in source and binary forms, with or without 10.\" modification, are permitted provided that the following conditions 11.\" are met: 12.\" 1. Redistributions of source code must retain the above copyright 13.\" notice, this list of conditions and the following disclaimer. 14.\" 2. Redistributions in binary form must reproduce the above copyright 15.\" notice, this list of conditions and the following disclaimer in the 16.\" documentation and/or other materials provided with the distribution. 17.\" 18.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 19.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 22.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28.\" POSSIBILITY OF SUCH DAMAGE. 29.\" 30.Dd April 27, 2016 31.Dt PSREF 9 32.Os 33.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 34.Sh NAME 35.Nm psref 36.Nd passive references 37.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 38.Sh SYNOPSIS 39.In sys/psref.h 40.\" 41.Ft struct psref_class * 42.Fn psref_class_create "const char *name" "int ipl" 43.Ft void 44.Fn psref_class_destroy "struct psref_class *class" 45.\" 46.Ft void 47.Fn psref_target_init "struct psref_target *target" "struct psref_class *class" 48.Ft void 49.Fn psref_target_destroy "struct psref_target *target" "struct psref_class *class" 50.\" 51.Ft void 52.Fn psref_acquire "struct psref *ref" "const struct psref_target *target" "struct psref_class *class" 53.Ft void 54.Fn psref_release "struct psref *ref" "const struct psref_target *target" "struct psref_class *class" 55.Ft void 56.Fn psref_copy "struct psref *pto" "const struct psref *pfrom" "struct psref_class *class" 57.\" 58.Pp 59.Fd "#ifdef DIAGNOSTIC" 60.Ft bool 61.Fn psref_held "const struct psref_target *target" "struct psref_class *class" 62.Fd "#endif" 63.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 64.Sh DESCRIPTION 65The 66.Nm 67abstraction allows CPUs to cheaply acquire and release 68.Em passive references 69to a resource, which guarantee the resource will not be destroyed 70until the reference is released. 71Acquiring and releasing passive references requires no interprocessor 72synchronization, except when the resource is pending destruction. 73.\" 74.Pp 75Passive references are an intermediate between 76.Xr pserialize 9 77and reference counting: 78.Bl -hyphen 79.It 80.Xr pserialize 9 81read sections require no interprocessor synchronization, but must be 82of short duration, and may not sleep. 83A 84.Xr pserialize 9 85read section blocks soft interrupts on the local CPU until it is 86complete. 87.It 88Reference counting requires interprocessor synchronization via 89.Xr atomic_ops 3 90or 91.Xr mutex 9 . 92However, with reference counting, a reference may be held for arbitrary 93durations, may be transferred between owners across CPUs and threads, 94and may be held by a caller that sleeps. 95.El 96.\" 97.Pp 98Passive references share some properties of both: passive references 99avoid interprocessor synchronization, and do not block soft interrupts, 100but can be held by a caller that sleeps. 101However, a caller holding a passive reference may not transfer it from 102one LWP to another, and the caller's LWP must be bound to a single CPU 103while it holds any passive references. 104.Pp 105Thus, passive references are useful for incrementally parallelizing 106resources whose operations may sleep, such as in the network stack, 107before comprehensively removing sleeps from the code paths involved. 108.\" 109.Pp 110Resources to which callers may hold passive references are called 111.Em targets , 112and must contain an embedded 113.Vt struct psref_target 114object, initialized with 115.Fn psref_target_init . 116.Pp 117When a caller wants to guarantee that a resource will not be destroyed 118until it is done, it must allocate storage for a 119.Vt struct psref 120object, find the 121.Vt struct psref_target 122for the resource it seeks, and use 123.Fn psref_acquire 124to acquire a passive reference. 125When a caller is done with the resource, it must release the resource 126with 127.Fn psref_release . 128.Pp 129When a resource is about to go away, its passive reference target must 130be passed to 131.Fn psref_target_destroy 132to wait until all extant passive references are released; then the 133resource itself may be freed. 134.\" 135.Pp 136.Vt struct psref_target 137and 138.Vt struct psref 139objects must be allocated by the caller, but they should be treated as 140opaque and should not be inspected or copied. 141.\" 142.Pp 143Passive reference targets are grouped into 144.Em classes , 145represented by an opaque 146.Vt struct psref_class 147object, e.g. the class of all network routes, or the class of all file 148systems mount points, which may be needed at different interrupt 149priority levels. 150.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 151.Sh FUNCTIONS 152.Bl -tag -width abcd 153.It Fn psref_class_create name ipl 154Create a passive reference class with the given name and interrupt 155priority level, and return an opaque pointer describing it. 156The name must be at most eight characters long, and will be shown in 157utilities such as 158.Xr ps 1 159for threads that are waiting to destroy passive reference targets. 160On failure, return 161.Dv NULL 162instead. 163.\"""""""""""""""" 164.It Fn psref_class_destroy class 165Destroy a passive reference class created with 166.Fn psref_class_create . 167There must be no more passive references in this class. 168.\"""""""""""""""" 169.It Fn psref_target_init target class 170Initialize a passive reference target in a 171.Vt struct psref_target 172object allocated by the caller in the given class. 173.Pp 174The caller must issue a 175.Xr membar_producer 3 176after calling 177.Fn psref_target_init 178and before publishing a pointer to the target so that other CPUs can 179see it, e.g. by inserting it into a 180.Xr pslist 9 . 181.\"""""""""""""""" 182.It Fn psref_target_destroy target class 183Wait for all extant passive references to 184.Fa target 185on all CPUs to be released, and then destroy it. 186The passive reference target 187.Fa target 188must have been initialized with 189.Fn psref_target_init 190in the same 191.Fa class . 192May sleep. 193.Pp 194The caller must guarantee that no new references to 195.Fa target 196will be acquired once it calls 197.Fn psref_target_destroy , 198e.g. by removing the target from a 199.Xr pslist 9 200and calling 201.Xr pserialize_perform 9 202to wait for 203.Xr pserialize 9 204readers to complete. 205.Pp 206No further use of the target is allowed unless it is reinitialized with 207.Fn psref_target_init . 208Multiple concurrent calls to 209.Fn psref_target_destroy 210are not allowed. 211.\"""""""""""""""" 212.It Fn psref_acquire ref target class 213Acquire a passive reference to 214.Fa target , 215storing per-CPU bookkeeping in 216.Fa ref . 217The class of 218.Fa target 219must be 220.Fa class . 221.Pp 222The caller must ensure by some other mechanism than passive references 223that the target will not be destroyed before the call to 224.Fn psref_acquire ; 225typically this will be via a 226.Xr pserialize 9 227read section. 228.Pp 229The caller's LWP must be bound to a CPU. 230.\"""""""""""""""" 231.It Fn psref_release ref target class 232Release the passive reference 233.Fa ref , 234which must have been acquired to point at 235.Fa target 236in the class 237.Fa class , 238waking a thread calling 239.Fn psref_target_destroy 240if any. 241.Pp 242Further use of the resource represented by 243.Fa target 244is not allowed, unless it is re-acquired in the same way that it was 245originally acquired. 246.\"""""""""""""""" 247.It Fn psref_copy pto pfrom class 248Copy the passive reference 249.Fa pfrom 250to 251.Fa pto , 252which must be to a target in 253.Fa class . 254The resource represented by the target of the passive references will 255not be destroyed before both references are released. 256.\"""""""""""""""" 257.It Fn psref_held target class 258Return true if the current CPU holds a passive reference to 259.Fa target 260in the passive reference class 261.Fa class , 262or false if not. 263.Pp 264This does not answer about other CPUs \(em it does not tell you whether 265.Em any 266CPU holds a passive reference to 267.Fa target . 268.Pp 269This may be used only in assertions, e.g. with 270.Xr KASSERT 9 , 271not for making run-time decisions. 272This should be used only for positive assertions, as in 273.Li KASSERT(psref_held( Ns Fa target Ns Li , Fa class Ns Li )) , 274not for negative assertions, as in 275.Li KASSERT(!psref_held( Ns Fa target Ns Li , Fa class Ns Li )) , 276unless you are sure you can prove that no caller holds a reference 277either. 278.El 279.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 280.Sh EXAMPLES 281.Bd -literal 282struct frotz { 283 int f_key; 284 ... 285 struct pslist_entry f_entry; 286 struct psref_target f_target; 287}; 288 289static struct { 290 kmutex_t lock; 291 struct pslist_head list; 292} frobbotzim __cacheline_aligned; 293 294static pserialize_t frobbotzim_psz __read_mostly; 295static struct psref_class *frobbotzim_prc __read_mostly; 296 297void 298publish_as_frotz(uint64_t key, ...) 299{ 300 struct frotz *f; 301 302 f = kmem_alloc(sizeof(*f), KM_SLEEP); 303 f->f_key = key; 304 f->f_... = ...; 305 PSLIST_ENTRY_INIT(f, f_entry); 306 psref_target_init(&f->f_target, frobbotzim_prc); 307 308 mutex_enter(&frobbotzim.lock); 309 PSLIST_WRITER_INSERT_HEAD(&frobbotzim.list, f, f_entry); 310 mutex_exit(&frobbotzim.lock); 311} 312 313int 314use_frotz(int key, int op) 315{ 316 struct frotz *f; 317 struct psref ref; 318 319 /* Acquire a passive reference. */ 320 if ((f = lookup_frotz(key, &ref)) == NULL) 321 return ENOENT; 322 323 /* Do something that may sleep. */ 324 do_stuff_with_frotz(f, op); 325 326 /* Release passive reference, possibly waking destroy_frotz. */ 327 psref_release(&ref, &f->f_psref, frobbotzim_prc); 328 329 return 0; 330} 331 332struct frotz * 333lookup_frotz(int key, struct psref *ref) 334{ 335 struct frotz *f; 336 int s; 337 338 /* Look up a frotz in a pserialized list. */ 339 s = pserialize_read_enter(); 340 PSLIST_READER_FOREACH(f, &frobbotzim.list, struct frotz, f_next) { 341 /* f is stable until pserialize_read_exit. */ 342 if (f->f_key == key) { 343 /* Acquire a passive reference. */ 344 psref_acquire(ref, &f->f_target, frobbotzim_prc); 345 /* f is now stable until psref_release. */ 346 break; 347 } 348 } 349 pserialize_read_exit(s); 350 351 return f; 352} 353 354void 355destroy_frotz(int key) 356{ 357 struct frotz *f; 358 359 /* Look up and delete a frotz. */ 360 mutex_enter(&frobbotzim.lock); 361 PSLIST_WRITER_FOREACH(f, &frobbotzim.list, struct frotz, f_entry) { 362 if (f->f_key == key) { 363 /* 364 * Unlink the frotz from the list to stop new 365 * pserialize read sections from seeing it. 366 */ 367 PSLIST_WRITER_REMOVE(f, f_entry); 368 369 /* 370 * Wait until extant pserialize read sections 371 * have completed. 372 */ 373 pserialize_perform(frobbotzim_psz); 374 break; 375 } 376 } 377 mutex_exit(&frobbotzim.lock); 378 379 if (f != NULL) { 380 /* Wait for all readers to drain before freeing. */ 381 psref_target_destroy(&f->f_target, frobbotzim_prc); 382 PSLIST_ENTRY_DESTROY(f, f_entry); 383 kmem_free(f, sizeof(*f)); 384 } 385} 386.Ed 387.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 388.Sh CODE REFERENCES 389The 390.Nm 391abstraction is implemented in 392.Pa sys/kern/subr_psref.c . 393.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 394.Sh SEE ALSO 395.Xr pserialize 9 , 396.Xr pslist 9 397.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 398.Sh HISTORY 399The 400.Nm 401data structure first appeared in 402.Nx 8.0 . 403.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 404.Sh AUTHORS 405.An Taylor R Campbell Aq Mt riastradh@NetBSD.org 406