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