| PSREF(9) | Kernel Developer's Manual | PSREF(9) | 
psref —
#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
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:
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.
psref_class_create(name,
    ipl)NULL instead.psref_class_destroy(class)psref_class_create(). There must be no more
      passive references in this class.psref_target_init(target,
    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)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)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)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)psref_held(target,
    class)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.
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));
	}
}
psref abstraction is implemented in
  sys/kern/subr_psref.c.
psref data structure first appeared in
  NetBSD 8.0.
| April 27, 2016 | NetBSD 10.0 |