xref: /netbsd-src/share/man/man9/psref.9 (revision 0250b4d7d62c340c121c12c438e28a974d6c857b)
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