Updated: 2022/Sep/29

Please read Privacy Policy. It's for your privacy.


PSREF(9)                   Kernel Developer's Manual                  PSREF(9)

NAME
     psref - passive references

SYNOPSIS
     #include <sys/psref.h>

     struct psref_class *
     psref_class_create(const char *name, int ipl);

     void
     psref_class_destroy(struct psref_class *class);

     void
     psref_target_init(struct psref_target *target,
         struct psref_class *class);

     void
     psref_target_destroy(struct psref_target *target,
         struct psref_class *class);

     void
     psref_acquire(struct psref *ref, const struct psref_target *target,
         struct psref_class *class);

     void
     psref_release(struct psref *ref, const struct psref_target *target,
         struct psref_class *class);

     void
     psref_copy(struct psref *pto, const struct psref *pfrom,
         struct psref_class *class);

     #ifdef DIAGNOSTIC

     bool
     psref_held(const struct psref_target *target, struct psref_class *class);

     #endif

DESCRIPTION
     The psref abstraction allows CPUs to cheaply acquire and release passive
     references to a resource, which guarantee the resource will not be
     destroyed until the reference is released.  Acquiring and releasing
     passive references requires no interprocessor synchronization, except
     when the resource is pending destruction.

     Passive references are an intermediate between pserialize(9) and
     reference counting:

     -   pserialize(9) read sections require no interprocessor
         synchronization, but must be of short duration, and may not sleep.  A
         pserialize(9) read section blocks soft interrupts on the local CPU
         until it is complete.

     -   Reference counting requires interprocessor synchronization via
         atomic_ops(3) or mutex(9).  However, with reference counting, a
         reference may be held for arbitrary durations, may be transferred
         between owners across CPUs and threads, and may be held by a caller
         that sleeps.

     Passive references share some properties of both: passive references
     avoid interprocessor synchronization, and do not block soft interrupts,
     but can be held by a caller that sleeps.  However, a caller holding a
     passive reference may not transfer it from one LWP to another, and the
     caller's LWP must be bound to a single CPU while it holds any passive
     references.

     Thus, passive references are useful for incrementally parallelizing
     resources whose operations may sleep, such as in the network stack,
     before comprehensively removing sleeps from the code paths involved.

     Resources to which callers may hold passive references are called
     targets, and must contain an embedded struct psref_target object,
     initialized with psref_target_init().

     When a caller wants to guarantee that a resource will not be destroyed
     until it is done, it must allocate storage for a struct psref object,
     find the struct psref_target for the resource it seeks, and use
     psref_acquire() to acquire a passive reference.  When a caller is done
     with the resource, it must release the resource with psref_release().

     When a resource is about to go away, its passive reference target must be
     passed to psref_target_destroy() to wait until all extant passive
     references are released; then the resource itself may be freed.

     struct psref_target and struct psref objects must be allocated by the
     caller, but they should be treated as opaque and should not be inspected
     or copied.

     Passive reference targets are grouped into classes, represented by an
     opaque struct psref_class object, e.g. the class of all network routes,
     or the class of all file systems mount points, which may be needed at
     different interrupt priority levels.

FUNCTIONS
     psref_class_create(name, ipl)
           Create a passive reference class with the given name and interrupt
           priority level, and return an opaque pointer describing it.  The
           name must be at most eight characters long, and will be shown in
           utilities such as ps(1) for threads that are waiting to destroy
           passive reference targets.  On failure, return NULL instead.

     psref_class_destroy(class)
           Destroy a passive reference class created with
           psref_class_create().  There must be no more passive references in
           this class.

     psref_target_init(target, class)
           Initialize a passive reference target in a struct psref_target
           object allocated by the caller in the given class.

           The caller must issue a membar_producer(3) after calling
           psref_target_init() and before publishing a pointer to the target
           so that other CPUs can see it, e.g. by inserting it into a
           pslist(9).

     psref_target_destroy(target, class)
           Wait for all extant passive references to target on all CPUs to be
           released, and then destroy it.  The passive reference target target
           must have been initialized with psref_target_init() in the same
           class.  May sleep.

           The caller must guarantee that no new references to target will be
           acquired once it calls psref_target_destroy(), e.g. by removing the
           target from a pslist(9) and calling pserialize_perform(9) to wait
           for pserialize(9) readers to complete.

           No further use of the target is allowed unless it is reinitialized
           with psref_target_init().  Multiple concurrent calls to
           psref_target_destroy() are not allowed.

     psref_acquire(ref, target, class)
           Acquire a passive reference to target, storing per-CPU bookkeeping
           in ref.  The class of target must be class.

           The caller must ensure by some other mechanism than passive
           references that the target will not be destroyed before the call to
           psref_acquire(); typically this will be via a pserialize(9) read
           section.

           The caller's LWP must be bound to a CPU.

     psref_release(ref, target, class)
           Release the passive reference ref, which must have been acquired to
           point at target in the class class, waking a thread calling
           psref_target_destroy() if any.

           Further use of the resource represented by target is not allowed,
           unless it is re-acquired in the same way that it was originally
           acquired.

     psref_copy(pto, pfrom, class)
           Copy the passive reference pfrom to pto, which must be to a target
           in class.  The resource represented by the target of the passive
           references will not be destroyed before both references are
           released.

     psref_held(target, class)
           Return true if the current CPU holds a passive reference to target
           in the passive reference class class, or false if not.

           This does not answer about other CPUs -- it does not tell you
           whether any CPU holds a passive reference to target.

           This may be used only in assertions, e.g. with KASSERT(9), not for
           making run-time decisions.  This should be used only for positive
           assertions, as in KASSERT(psref_held(target, class)), not for
           negative assertions, as in KASSERT(!psref_held(target, class)),
           unless you are sure you can prove that no caller holds a reference
           either.

EXAMPLES
     struct frotz {
             int                     f_key;
             ...
             struct pslist_entry     f_entry;
             struct psref_target     f_target;
     };

     static struct {
             kmutex_t                lock;
             struct pslist_head      list;
     } frobbotzim __cacheline_aligned;

     static pserialize_t             frobbotzim_psz __read_mostly;
     static struct psref_class       *frobbotzim_prc __read_mostly;

     void
     publish_as_frotz(uint64_t key, ...)
     {
             struct frotz *f;

             f = kmem_alloc(sizeof(*f), KM_SLEEP);
             f->f_key = key;
             f->f_... = ...;
             PSLIST_ENTRY_INIT(f, f_entry);
             psref_target_init(&f->f_target, frobbotzim_prc);

             mutex_enter(&frobbotzim.lock);
             PSLIST_WRITER_INSERT_HEAD(&frobbotzim.list, f, f_entry);
             mutex_exit(&frobbotzim.lock);
     }

     int
     use_frotz(int key, int op)
     {
             struct frotz *f;
             struct psref ref;

             /* Acquire a passive reference.  */
             if ((f = lookup_frotz(key, &ref)) == NULL)
                     return ENOENT;

             /* Do something that may sleep.  */
             do_stuff_with_frotz(f, op);

             /* Release passive reference, possibly waking destroy_frotz.  */
             psref_release(&ref, &f->f_psref, frobbotzim_prc);

             return 0;
     }

     struct frotz *
     lookup_frotz(int key, struct psref *ref)
     {
             struct frotz *f;
             int s;

             /* Look up a frotz in a pserialized list.  */
             s = pserialize_read_enter();
             PSLIST_READER_FOREACH(f, &frobbotzim.list, struct frotz, f_next) {
                     /* f is stable until pserialize_read_exit.  */
                     if (f->f_key == key) {
                             /* Acquire a passive reference.  */
                             psref_acquire(ref, &f->f_target, frobbotzim_prc);
                             /* f is now stable until psref_release.  */
                             break;
                     }
             }
             pserialize_read_exit(s);

             return f;
     }

     void
     destroy_frotz(int key)
     {
             struct frotz *f;

             /* Look up and delete a frotz.  */
             mutex_enter(&frobbotzim.lock);
             PSLIST_WRITER_FOREACH(f, &frobbotzim.list, struct frotz, f_entry) {
                     if (f->f_key == key) {
                             /*
                              * Unlink the frotz from the list to stop new
                              * pserialize read sections from seeing it.
                              */
                             PSLIST_WRITER_REMOVE(f, f_entry);

                             /*
                              * Wait until extant pserialize read sections
                              * have completed.
                              */
                             pserialize_perform(frobbotzim_psz);
                             break;
                     }
             }
             mutex_exit(&frobbotzim.lock);

             if (f != NULL) {
                     /* Wait for all readers to drain before freeing.  */
                     psref_target_destroy(&f->f_target, frobbotzim_prc);
                     PSLIST_ENTRY_DESTROY(f, f_entry);
                     kmem_free(f, sizeof(*f));
             }
     }

CODE REFERENCES
     The psref abstraction is implemented in sys/kern/subr_psref.c.

SEE ALSO
     pserialize(9), pslist(9)

HISTORY
     The psref data structure first appeared in NetBSD 8.0.

AUTHORS
     Taylor R Campbell <riastradh@NetBSD.org>

NetBSD 10.99                    April 27, 2016                    NetBSD 10.99