subr_psref.c revision 1.9 1 /* $NetBSD: subr_psref.c,v 1.9 2017/12/14 05:45:55 ozaki-r Exp $ */
2
3 /*-
4 * Copyright (c) 2016 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Taylor R. Campbell.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * Passive references
34 *
35 * Passive references are references to objects that guarantee the
36 * object will not be destroyed until the reference is released.
37 *
38 * Passive references require no interprocessor synchronization to
39 * acquire or release. However, destroying the target of passive
40 * references requires expensive interprocessor synchronization --
41 * xcalls to determine on which CPUs the object is still in use.
42 *
43 * Passive references may be held only on a single CPU and by a
44 * single LWP. They require the caller to allocate a little stack
45 * space, a struct psref object. Sleeping while a passive
46 * reference is held is allowed, provided that the owner's LWP is
47 * bound to a CPU -- e.g., the owner is a softint or a bound
48 * kthread. However, sleeping should be kept to a short duration,
49 * e.g. sleeping on an adaptive lock.
50 *
51 * Passive references serve as an intermediate stage between
52 * reference counting and passive serialization (pserialize(9)):
53 *
54 * - If you need references to transfer from CPU to CPU or LWP to
55 * LWP, or if you need long-term references, you must use
56 * reference counting, e.g. with atomic operations or locks,
57 * which incurs interprocessor synchronization for every use --
58 * cheaper than an xcall, but not scalable.
59 *
60 * - If all users *guarantee* that they will not sleep, then it is
61 * not necessary to use passive references: you may as well just
62 * use the even cheaper pserialize(9), because you have
63 * satisfied the requirements of a pserialize read section.
64 */
65
66 #include <sys/cdefs.h>
67 __KERNEL_RCSID(0, "$NetBSD: subr_psref.c,v 1.9 2017/12/14 05:45:55 ozaki-r Exp $");
68
69 #include <sys/types.h>
70 #include <sys/condvar.h>
71 #include <sys/cpu.h>
72 #include <sys/intr.h>
73 #include <sys/kmem.h>
74 #include <sys/lwp.h>
75 #include <sys/mutex.h>
76 #include <sys/percpu.h>
77 #include <sys/psref.h>
78 #include <sys/queue.h>
79 #include <sys/xcall.h>
80
81 SLIST_HEAD(psref_head, psref);
82
83 static bool _psref_held(const struct psref_target *, struct psref_class *,
84 bool);
85
86 /*
87 * struct psref_class
88 *
89 * Private global state for a class of passive reference targets.
90 * Opaque to callers.
91 */
92 struct psref_class {
93 kmutex_t prc_lock;
94 kcondvar_t prc_cv;
95 struct percpu *prc_percpu; /* struct psref_cpu */
96 ipl_cookie_t prc_iplcookie;
97 };
98
99 /*
100 * struct psref_cpu
101 *
102 * Private per-CPU state for a class of passive reference targets.
103 * Not exposed by the API.
104 */
105 struct psref_cpu {
106 struct psref_head pcpu_head;
107 };
108
109 /*
110 * psref_class_create(name, ipl)
111 *
112 * Create a new passive reference class, with the given wchan name
113 * and ipl.
114 */
115 struct psref_class *
116 psref_class_create(const char *name, int ipl)
117 {
118 struct psref_class *class;
119
120 ASSERT_SLEEPABLE();
121
122 class = kmem_alloc(sizeof(*class), KM_SLEEP);
123 class->prc_percpu = percpu_alloc(sizeof(struct psref_cpu));
124 mutex_init(&class->prc_lock, MUTEX_DEFAULT, ipl);
125 cv_init(&class->prc_cv, name);
126 class->prc_iplcookie = makeiplcookie(ipl);
127
128 return class;
129 }
130
131 #ifdef DIAGNOSTIC
132 static void
133 psref_cpu_drained_p(void *p, void *cookie, struct cpu_info *ci __unused)
134 {
135 const struct psref_cpu *pcpu = p;
136 bool *retp = cookie;
137
138 if (!SLIST_EMPTY(&pcpu->pcpu_head))
139 *retp = false;
140 }
141
142 static bool
143 psref_class_drained_p(const struct psref_class *prc)
144 {
145 bool ret = true;
146
147 percpu_foreach(prc->prc_percpu, &psref_cpu_drained_p, &ret);
148
149 return ret;
150 }
151 #endif /* DIAGNOSTIC */
152
153 /*
154 * psref_class_destroy(class)
155 *
156 * Destroy a passive reference class and free memory associated
157 * with it. All targets in this class must have been drained and
158 * destroyed already.
159 */
160 void
161 psref_class_destroy(struct psref_class *class)
162 {
163
164 KASSERT(psref_class_drained_p(class));
165
166 cv_destroy(&class->prc_cv);
167 mutex_destroy(&class->prc_lock);
168 percpu_free(class->prc_percpu, sizeof(struct psref_cpu));
169 kmem_free(class, sizeof(*class));
170 }
171
172 /*
173 * psref_target_init(target, class)
174 *
175 * Initialize a passive reference target in the specified class.
176 * The caller is responsible for issuing a membar_producer after
177 * psref_target_init and before exposing a pointer to the target
178 * to other CPUs.
179 */
180 void
181 psref_target_init(struct psref_target *target,
182 struct psref_class *class)
183 {
184
185 target->prt_class = class;
186 target->prt_draining = false;
187 }
188
189 #ifdef DEBUG
190 static bool
191 psref_exist(struct psref_cpu *pcpu, struct psref *psref)
192 {
193 struct psref *_psref;
194
195 SLIST_FOREACH(_psref, &pcpu->pcpu_head, psref_entry) {
196 if (_psref == psref)
197 return true;
198 }
199 return false;
200 }
201
202 static void
203 psref_check_duplication(struct psref_cpu *pcpu, struct psref *psref,
204 const struct psref_target *target)
205 {
206 bool found = false;
207
208 found = psref_exist(pcpu, psref);
209 if (found) {
210 panic("The psref is already in the list (acquiring twice?): "
211 "psref=%p target=%p", psref, target);
212 }
213 }
214
215 static void
216 psref_check_existence(struct psref_cpu *pcpu, struct psref *psref,
217 const struct psref_target *target)
218 {
219 bool found = false;
220
221 found = psref_exist(pcpu, psref);
222 if (!found) {
223 panic("The psref isn't in the list (releasing unused psref?): "
224 "psref=%p target=%p", psref, target);
225 }
226 }
227 #endif /* DEBUG */
228
229 /*
230 * psref_acquire(psref, target, class)
231 *
232 * Acquire a passive reference to the specified target, which must
233 * be in the specified class.
234 *
235 * The caller must guarantee that the target will not be destroyed
236 * before psref_acquire returns.
237 *
238 * The caller must additionally guarantee that it will not switch
239 * CPUs before releasing the passive reference, either by
240 * disabling kpreemption and avoiding sleeps, or by being in a
241 * softint or in an LWP bound to a CPU.
242 */
243 void
244 psref_acquire(struct psref *psref, const struct psref_target *target,
245 struct psref_class *class)
246 {
247 struct psref_cpu *pcpu;
248 int s;
249
250 KASSERTMSG((kpreempt_disabled() || cpu_softintr_p() ||
251 ISSET(curlwp->l_pflag, LP_BOUND)),
252 "passive references are CPU-local,"
253 " but preemption is enabled and the caller is not"
254 " in a softint or CPU-bound LWP");
255 KASSERTMSG((target->prt_class == class),
256 "mismatched psref target class: %p (ref) != %p (expected)",
257 target->prt_class, class);
258 KASSERTMSG(!target->prt_draining, "psref target already destroyed: %p",
259 target);
260
261 /* Block interrupts and acquire the current CPU's reference list. */
262 s = splraiseipl(class->prc_iplcookie);
263 pcpu = percpu_getref(class->prc_percpu);
264
265 #ifdef DEBUG
266 /* Sanity-check if the target is already acquired with the same psref. */
267 psref_check_duplication(pcpu, psref, target);
268 #endif
269
270 /* Record our reference. */
271 SLIST_INSERT_HEAD(&pcpu->pcpu_head, psref, psref_entry);
272 psref->psref_target = target;
273 psref->psref_lwp = curlwp;
274 psref->psref_cpu = curcpu();
275
276 /* Release the CPU list and restore interrupts. */
277 percpu_putref(class->prc_percpu);
278 splx(s);
279 }
280
281 /*
282 * psref_release(psref, target, class)
283 *
284 * Release a passive reference to the specified target, which must
285 * be in the specified class.
286 *
287 * The caller must not have switched CPUs or LWPs since acquiring
288 * the passive reference.
289 */
290 void
291 psref_release(struct psref *psref, const struct psref_target *target,
292 struct psref_class *class)
293 {
294 struct psref_cpu *pcpu;
295 int s;
296
297 KASSERTMSG((kpreempt_disabled() || cpu_softintr_p() ||
298 ISSET(curlwp->l_pflag, LP_BOUND)),
299 "passive references are CPU-local,"
300 " but preemption is enabled and the caller is not"
301 " in a softint or CPU-bound LWP");
302 KASSERTMSG((target->prt_class == class),
303 "mismatched psref target class: %p (ref) != %p (expected)",
304 target->prt_class, class);
305
306 /* Make sure the psref looks sensible. */
307 KASSERTMSG((psref->psref_target == target),
308 "passive reference target mismatch: %p (ref) != %p (expected)",
309 psref->psref_target, target);
310 KASSERTMSG((psref->psref_lwp == curlwp),
311 "passive reference transferred from lwp %p to lwp %p",
312 psref->psref_lwp, curlwp);
313 KASSERTMSG((psref->psref_cpu == curcpu()),
314 "passive reference transferred from CPU %u to CPU %u",
315 cpu_index(psref->psref_cpu), cpu_index(curcpu()));
316
317 /*
318 * Block interrupts and remove the psref from the current CPU's
319 * list. No need to percpu_getref or get the head of the list,
320 * and the caller guarantees that we are bound to a CPU anyway
321 * (as does blocking interrupts).
322 */
323 s = splraiseipl(class->prc_iplcookie);
324 pcpu = percpu_getref(class->prc_percpu);
325 #ifdef DEBUG
326 /* Sanity-check if the target is surely acquired before. */
327 psref_check_existence(pcpu, psref, target);
328 #endif
329 SLIST_REMOVE(&pcpu->pcpu_head, psref, psref, psref_entry);
330 percpu_putref(class->prc_percpu);
331 splx(s);
332
333 /* If someone is waiting for users to drain, notify 'em. */
334 if (__predict_false(target->prt_draining))
335 cv_broadcast(&class->prc_cv);
336 }
337
338 /*
339 * psref_copy(pto, pfrom, class)
340 *
341 * Copy a passive reference from pfrom, which must be in the
342 * specified class, to pto. Both pfrom and pto must later be
343 * released with psref_release.
344 *
345 * The caller must not have switched CPUs or LWPs since acquiring
346 * pfrom, and must not switch CPUs or LWPs before releasing both
347 * pfrom and pto.
348 */
349 void
350 psref_copy(struct psref *pto, const struct psref *pfrom,
351 struct psref_class *class)
352 {
353 struct psref_cpu *pcpu;
354 int s;
355
356 KASSERTMSG((kpreempt_disabled() || cpu_softintr_p() ||
357 ISSET(curlwp->l_pflag, LP_BOUND)),
358 "passive references are CPU-local,"
359 " but preemption is enabled and the caller is not"
360 " in a softint or CPU-bound LWP");
361 KASSERTMSG((pto != pfrom),
362 "can't copy passive reference to itself: %p",
363 pto);
364
365 /* Make sure the pfrom reference looks sensible. */
366 KASSERTMSG((pfrom->psref_lwp == curlwp),
367 "passive reference transferred from lwp %p to lwp %p",
368 pfrom->psref_lwp, curlwp);
369 KASSERTMSG((pfrom->psref_cpu == curcpu()),
370 "passive reference transferred from CPU %u to CPU %u",
371 cpu_index(pfrom->psref_cpu), cpu_index(curcpu()));
372 KASSERTMSG((pfrom->psref_target->prt_class == class),
373 "mismatched psref target class: %p (ref) != %p (expected)",
374 pfrom->psref_target->prt_class, class);
375
376 /* Block interrupts and acquire the current CPU's reference list. */
377 s = splraiseipl(class->prc_iplcookie);
378 pcpu = percpu_getref(class->prc_percpu);
379
380 /* Record the new reference. */
381 SLIST_INSERT_HEAD(&pcpu->pcpu_head, pto, psref_entry);
382 pto->psref_target = pfrom->psref_target;
383 pto->psref_lwp = curlwp;
384 pto->psref_cpu = curcpu();
385
386 /* Release the CPU list and restore interrupts. */
387 percpu_putref(class->prc_percpu);
388 splx(s);
389 }
390
391 /*
392 * struct psreffed
393 *
394 * Global state for draining a psref target.
395 */
396 struct psreffed {
397 struct psref_class *class;
398 struct psref_target *target;
399 bool ret;
400 };
401
402 static void
403 psreffed_p_xc(void *cookie0, void *cookie1 __unused)
404 {
405 struct psreffed *P = cookie0;
406
407 /*
408 * If we hold a psref to the target, then answer true.
409 *
410 * This is the only dynamic decision that may be made with
411 * psref_held.
412 *
413 * No need to lock anything here: every write transitions from
414 * false to true, so there can be no conflicting writes. No
415 * need for a memory barrier here because P->ret is read only
416 * after xc_wait, which has already issued any necessary memory
417 * barriers.
418 */
419 if (_psref_held(P->target, P->class, true))
420 P->ret = true;
421 }
422
423 static bool
424 psreffed_p(struct psref_target *target, struct psref_class *class)
425 {
426 struct psreffed P = {
427 .class = class,
428 .target = target,
429 .ret = false,
430 };
431
432 /* Ask all CPUs to say whether they hold a psref to the target. */
433 xc_wait(xc_broadcast(0, &psreffed_p_xc, &P, NULL));
434
435 return P.ret;
436 }
437
438 /*
439 * psref_target_destroy(target, class)
440 *
441 * Destroy a passive reference target. Waits for all existing
442 * references to drain. Caller must guarantee no new references
443 * will be acquired once it calls psref_target_destroy, e.g. by
444 * removing the target from a global list first. May sleep.
445 */
446 void
447 psref_target_destroy(struct psref_target *target, struct psref_class *class)
448 {
449
450 ASSERT_SLEEPABLE();
451
452 KASSERTMSG((target->prt_class == class),
453 "mismatched psref target class: %p (ref) != %p (expected)",
454 target->prt_class, class);
455
456 /* Request psref_release to notify us when done. */
457 KASSERTMSG(!target->prt_draining, "psref target already destroyed: %p",
458 target);
459 target->prt_draining = true;
460
461 /* Wait until there are no more references on any CPU. */
462 while (psreffed_p(target, class)) {
463 /*
464 * This enter/wait/exit business looks wrong, but it is
465 * both necessary, because psreffed_p performs a
466 * low-priority xcall and hence cannot run while a
467 * mutex is locked, and OK, because the wait is timed
468 * -- explicit wakeups are only an optimization.
469 */
470 mutex_enter(&class->prc_lock);
471 (void)cv_timedwait(&class->prc_cv, &class->prc_lock, 1);
472 mutex_exit(&class->prc_lock);
473 }
474
475 /* No more references. Cause subsequent psref_acquire to kassert. */
476 target->prt_class = NULL;
477 }
478
479 static bool
480 _psref_held(const struct psref_target *target, struct psref_class *class,
481 bool lwp_mismatch_ok)
482 {
483 const struct psref_cpu *pcpu;
484 const struct psref *psref;
485 int s;
486 bool held = false;
487
488 KASSERTMSG((kpreempt_disabled() || cpu_softintr_p() ||
489 ISSET(curlwp->l_pflag, LP_BOUND)),
490 "passive references are CPU-local,"
491 " but preemption is enabled and the caller is not"
492 " in a softint or CPU-bound LWP");
493 KASSERTMSG((target->prt_class == class),
494 "mismatched psref target class: %p (ref) != %p (expected)",
495 target->prt_class, class);
496
497 /* Block interrupts and acquire the current CPU's reference list. */
498 s = splraiseipl(class->prc_iplcookie);
499 pcpu = percpu_getref(class->prc_percpu);
500
501 /* Search through all the references on this CPU. */
502 SLIST_FOREACH(psref, &pcpu->pcpu_head, psref_entry) {
503 /* Sanity-check the reference's CPU. */
504 KASSERTMSG((psref->psref_cpu == curcpu()),
505 "passive reference transferred from CPU %u to CPU %u",
506 cpu_index(psref->psref_cpu), cpu_index(curcpu()));
507
508 /* If it doesn't match, skip it and move on. */
509 if (psref->psref_target != target)
510 continue;
511
512 /*
513 * Sanity-check the reference's LWP if we are asserting
514 * via psref_held that this LWP holds it, but not if we
515 * are testing in psref_target_destroy whether any LWP
516 * still holds it.
517 */
518 KASSERTMSG((lwp_mismatch_ok || psref->psref_lwp == curlwp),
519 "passive reference transferred from lwp %p to lwp %p",
520 psref->psref_lwp, curlwp);
521
522 /* Stop here and report that we found it. */
523 held = true;
524 break;
525 }
526
527 /* Release the CPU list and restore interrupts. */
528 percpu_putref(class->prc_percpu);
529 splx(s);
530
531 return held;
532 }
533
534 /*
535 * psref_held(target, class)
536 *
537 * True if the current CPU holds a passive reference to target,
538 * false otherwise. May be used only inside assertions.
539 */
540 bool
541 psref_held(const struct psref_target *target, struct psref_class *class)
542 {
543
544 return _psref_held(target, class, false);
545 }
546