xref: /xnu-11215.41.3/libkern/os/refcnt.c (revision 33de042d024d46de5ff4e89f2471de6608e37fa4)
1 #if KERNEL
2 #include <kern/assert.h>
3 #include <kern/debug.h>
4 #include <pexpert/pexpert.h>
5 #include <kern/btlog.h>
6 #include <kern/backtrace.h>
7 #include <kern/zalloc.h>
8 #include <kern/sched_prim.h>
9 #include <libkern/libkern.h>
10 #endif
11 #include <os/overflow.h>
12 #include <os/atomic_private.h>
13 
14 #include "refcnt.h"
15 
16 #define OS_REFCNT_MAX_COUNT     ((os_ref_count_t)0x0FFFFFFFUL)
17 
18 #if OS_REFCNT_DEBUG
19 extern struct os_refgrp global_ref_group;
20 os_refgrp_decl(, global_ref_group, "all", NULL);
21 
22 extern bool ref_debug_enable;
23 bool ref_debug_enable = false;
24 
25 #define REFLOG_GRP_DEBUG_ENABLED(grp) \
26     __improbable(grp != NULL && (ref_debug_enable || \
27 	(grp->grp_flags & OS_REFGRP_F_ALWAYS_ENABLED) != 0))
28 
29 static const size_t ref_log_nrecords = 1000000;
30 
31 __enum_closed_decl(reflog_op_t, uint8_t, {
32 	REFLOG_RETAIN  = 1,
33 	REFLOG_RELEASE = 2
34 });
35 
36 # define __debug_only
37 # define __os_refgrp_arg(arg)   , arg
38 #else
39 # define __debug_only __unused
40 # define __os_refgrp_arg(arg)
41 #endif /* OS_REFCNT_DEBUG */
42 
43 void
os_ref_panic_live(void * rc)44 os_ref_panic_live(void *rc)
45 {
46 	panic("os_refcnt: unexpected release of final reference (rc=%p)", rc);
47 	__builtin_unreachable();
48 }
49 
50 __abortlike
51 static void
os_ref_panic_underflow(void * rc)52 os_ref_panic_underflow(void *rc)
53 {
54 	panic("os_refcnt: underflow (rc=%p)", rc);
55 	__builtin_unreachable();
56 }
57 
58 __abortlike
59 static void
os_ref_panic_overflow(void * rc)60 os_ref_panic_overflow(void *rc)
61 {
62 	panic("os_refcnt: overflow (rc=%p)", rc);
63 	__builtin_unreachable();
64 }
65 
66 __abortlike
67 static void
os_ref_panic_retain(os_ref_atomic_t * rc)68 os_ref_panic_retain(os_ref_atomic_t *rc)
69 {
70 	if (os_atomic_load(rc, relaxed) >= OS_REFCNT_MAX_COUNT) {
71 		panic("os_refcnt: overflow (rc=%p)", rc);
72 	} else {
73 		panic("os_refcnt: attempted resurrection (rc=%p)", rc);
74 	}
75 }
76 
77 static inline void
os_ref_check_underflow(void * rc,os_ref_count_t count,os_ref_count_t n)78 os_ref_check_underflow(void *rc, os_ref_count_t count, os_ref_count_t n)
79 {
80 	if (__improbable(count < n)) {
81 		os_ref_panic_underflow(rc);
82 	}
83 }
84 
85 static inline void
os_ref_check_overflow(os_ref_atomic_t * rc,os_ref_count_t count)86 os_ref_check_overflow(os_ref_atomic_t *rc, os_ref_count_t count)
87 {
88 	if (__improbable(count >= OS_REFCNT_MAX_COUNT)) {
89 		os_ref_panic_overflow(rc);
90 	}
91 }
92 
93 static inline void
os_ref_check_retain(os_ref_atomic_t * rc,os_ref_count_t count,os_ref_count_t n)94 os_ref_check_retain(os_ref_atomic_t *rc, os_ref_count_t count, os_ref_count_t n)
95 {
96 	if (__improbable(count < n || count >= OS_REFCNT_MAX_COUNT)) {
97 		os_ref_panic_retain(rc);
98 	}
99 }
100 
101 #if OS_REFCNT_DEBUG
102 #if KERNEL
103 __attribute__((cold, noinline))
104 static void
ref_log_op(struct os_refgrp * grp,void * elem,reflog_op_t op)105 ref_log_op(struct os_refgrp *grp, void *elem, reflog_op_t op)
106 {
107 	if (grp == NULL) {
108 		return;
109 	}
110 
111 	if (grp->grp_log == NULL) {
112 		ref_log_op(grp->grp_parent, elem, op);
113 		return;
114 	}
115 
116 	btlog_record((btlog_t)grp->grp_log, elem, op,
117 	    btref_get(__builtin_frame_address(0), BTREF_GET_NOWAIT));
118 }
119 
120 __attribute__((cold, noinline))
121 static void
ref_log_drop(struct os_refgrp * grp,void * elem)122 ref_log_drop(struct os_refgrp *grp, void *elem)
123 {
124 	if (!REFLOG_GRP_DEBUG_ENABLED(grp)) {
125 		return;
126 	}
127 
128 	if (grp->grp_log == NULL) {
129 		ref_log_drop(grp->grp_parent, elem);
130 		return;
131 	}
132 
133 	btlog_erase(grp->grp_log, elem);
134 }
135 
136 __attribute__((cold, noinline))
137 void
os_ref_log_init(struct os_refgrp * grp)138 os_ref_log_init(struct os_refgrp *grp)
139 {
140 	if (grp->grp_log != NULL) {
141 		return;
142 	}
143 
144 	char grpbuf[128];
145 	char *refgrp = grpbuf;
146 	if (!PE_parse_boot_argn("rlog", refgrp, sizeof(grpbuf))) {
147 		return;
148 	}
149 
150 	/*
151 	 * Enable refcount statistics if the rlog boot-arg is present,
152 	 * even when no specific group is logged.
153 	 */
154 	ref_debug_enable = true;
155 
156 	const char *g;
157 	while ((g = strsep(&refgrp, ",")) != NULL) {
158 		if (strcmp(g, grp->grp_name) == 0) {
159 			/* enable logging on this refgrp */
160 			grp->grp_log = btlog_create(BTLOG_HASH,
161 			    ref_log_nrecords, 0);
162 			return;
163 		}
164 	}
165 }
166 
167 
168 __attribute__((cold, noinline))
169 void
os_ref_log_fini(struct os_refgrp * grp)170 os_ref_log_fini(struct os_refgrp *grp)
171 {
172 	if (grp->grp_log == NULL) {
173 		return;
174 	}
175 
176 	btlog_destroy(grp->grp_log);
177 	grp->grp_log = NULL;
178 }
179 
180 #else
181 
182 #ifndef os_ref_log_fini
183 inline void
os_ref_log_fini(struct os_refgrp * grp __unused)184 os_ref_log_fini(struct os_refgrp *grp __unused)
185 {
186 }
187 #endif
188 
189 #ifndef os_ref_log_init
190 inline void
os_ref_log_init(struct os_refgrp * grp __unused)191 os_ref_log_init(struct os_refgrp *grp __unused)
192 {
193 }
194 #endif
195 #ifndef ref_log_op
196 static inline void
ref_log_op(struct os_refgrp * grp __unused,void * rc __unused,reflog_op_t op __unused)197 ref_log_op(struct os_refgrp *grp __unused, void *rc __unused, reflog_op_t op __unused)
198 {
199 }
200 #endif
201 #ifndef ref_log_drop
202 static inline void
ref_log_drop(struct os_refgrp * grp __unused,void * rc __unused)203 ref_log_drop(struct os_refgrp *grp __unused, void *rc __unused)
204 {
205 }
206 #endif
207 
208 #endif /* KERNEL */
209 
210 /*
211  * attach a new refcnt to a group
212  */
213 __attribute__((cold, noinline))
214 static void
ref_attach_to_group(os_ref_atomic_t * rc,struct os_refgrp * grp,os_ref_count_t init_count)215 ref_attach_to_group(os_ref_atomic_t *rc, struct os_refgrp *grp, os_ref_count_t init_count)
216 {
217 	if (grp == NULL) {
218 		return;
219 	}
220 
221 	if (atomic_fetch_add_explicit(&grp->grp_children, 1, memory_order_relaxed) == 0) {
222 		/* First reference count object in this group. Check if we should enable
223 		 * refcount logging. */
224 		os_ref_log_init(grp);
225 	}
226 
227 	atomic_fetch_add_explicit(&grp->grp_count, init_count, memory_order_relaxed);
228 	atomic_fetch_add_explicit(&grp->grp_retain_total, init_count, memory_order_relaxed);
229 
230 	if (grp == &global_ref_group) {
231 		return;
232 	}
233 
234 	if (grp->grp_parent == NULL) {
235 		grp->grp_parent = &global_ref_group;
236 	}
237 
238 	ref_attach_to_group(rc, grp->grp_parent, init_count);
239 }
240 
241 static void
ref_retain_group(struct os_refgrp * grp)242 ref_retain_group(struct os_refgrp *grp)
243 {
244 	if (grp) {
245 		atomic_fetch_add_explicit(&grp->grp_count, 1, memory_order_relaxed);
246 		atomic_fetch_add_explicit(&grp->grp_retain_total, 1, memory_order_relaxed);
247 		ref_retain_group(grp->grp_parent);
248 	}
249 }
250 
251 __attribute__((cold, noinline))
252 static void
ref_release_group(struct os_refgrp * grp)253 ref_release_group(struct os_refgrp *grp)
254 {
255 	if (grp) {
256 		atomic_fetch_sub_explicit(&grp->grp_count, 1, memory_order_relaxed);
257 		atomic_fetch_add_explicit(&grp->grp_release_total, 1, memory_order_relaxed);
258 
259 		ref_release_group(grp->grp_parent);
260 	}
261 }
262 
263 __attribute__((cold, noinline))
264 static void
ref_drop_group(struct os_refgrp * grp)265 ref_drop_group(struct os_refgrp *grp)
266 {
267 	if (grp) {
268 		atomic_fetch_sub_explicit(&grp->grp_children, 1, memory_order_relaxed);
269 		ref_drop_group(grp->grp_parent);
270 	}
271 }
272 
273 __attribute__((cold, noinline))
274 static void
ref_init_debug(void * rc,struct os_refgrp * grp,os_ref_count_t count)275 ref_init_debug(void *rc, struct os_refgrp *grp, os_ref_count_t count)
276 {
277 	ref_attach_to_group(rc, grp, count);
278 
279 	for (os_ref_count_t i = 0; i < count; i++) {
280 		ref_log_op(grp, rc, REFLOG_RETAIN);
281 	}
282 }
283 
284 __attribute__((cold, noinline))
285 static void
ref_retain_debug(void * rc,struct os_refgrp * __debug_only grp)286 ref_retain_debug(void *rc, struct os_refgrp * __debug_only grp)
287 {
288 	ref_retain_group(grp);
289 	ref_log_op(grp, rc, REFLOG_RETAIN);
290 }
291 
292 __attribute__((cold, noinline))
293 static void
ref_release_debug(void * rc,struct os_refgrp * __debug_only grp)294 ref_release_debug(void *rc, struct os_refgrp * __debug_only grp)
295 {
296 	ref_log_op(grp, rc, REFLOG_RELEASE);
297 	ref_release_group(grp);
298 }
299 
300 __attribute__((cold, noinline))
301 static os_ref_count_t
ref_drop_debug(void * rc,struct os_refgrp * __debug_only grp)302 ref_drop_debug(void *rc, struct os_refgrp * __debug_only grp)
303 {
304 	ref_log_drop(grp, rc);
305 	ref_drop_group(grp);
306 	return 0;
307 }
308 #endif
309 
310 void
os_ref_init_count_internal(os_ref_atomic_t * rc,struct os_refgrp * __debug_only grp,os_ref_count_t count)311 os_ref_init_count_internal(os_ref_atomic_t *rc, struct os_refgrp * __debug_only grp, os_ref_count_t count)
312 {
313 	os_ref_check_underflow(rc, count, 1);
314 	atomic_init(rc, count);
315 
316 #if OS_REFCNT_DEBUG
317 	if (REFLOG_GRP_DEBUG_ENABLED(grp)) {
318 		ref_init_debug(rc, grp, count);
319 	}
320 #endif
321 }
322 
323 static inline void
__os_ref_retain(os_ref_atomic_t * rc,os_ref_count_t f,struct os_refgrp * __debug_only grp)324 __os_ref_retain(os_ref_atomic_t *rc, os_ref_count_t f,
325     struct os_refgrp * __debug_only grp)
326 {
327 	os_ref_count_t old = atomic_fetch_add_explicit(rc, 1, memory_order_relaxed);
328 	os_ref_check_retain(rc, old, f);
329 
330 #if OS_REFCNT_DEBUG
331 	if (REFLOG_GRP_DEBUG_ENABLED(grp)) {
332 		ref_retain_debug(rc, grp);
333 	}
334 #endif
335 }
336 
337 void
os_ref_retain_internal(os_ref_atomic_t * rc,struct os_refgrp * grp)338 os_ref_retain_internal(os_ref_atomic_t *rc, struct os_refgrp *grp)
339 {
340 	__os_ref_retain(rc, 1, grp);
341 }
342 
343 void
os_ref_retain_floor_internal(os_ref_atomic_t * rc,os_ref_count_t f,struct os_refgrp * grp)344 os_ref_retain_floor_internal(os_ref_atomic_t *rc, os_ref_count_t f,
345     struct os_refgrp *grp)
346 {
347 	__os_ref_retain(rc, f, grp);
348 }
349 
350 static inline bool
__os_ref_retain_try(os_ref_atomic_t * rc,os_ref_count_t f,struct os_refgrp * __debug_only grp)351 __os_ref_retain_try(os_ref_atomic_t *rc, os_ref_count_t f,
352     struct os_refgrp * __debug_only grp)
353 {
354 	os_ref_count_t cur, next;
355 
356 	os_atomic_rmw_loop(rc, cur, next, relaxed, {
357 		if (__improbable(cur < f)) {
358 		        os_atomic_rmw_loop_give_up(return false);
359 		}
360 
361 		next = cur + 1;
362 	});
363 
364 	os_ref_check_overflow(rc, cur);
365 
366 #if OS_REFCNT_DEBUG
367 	if (REFLOG_GRP_DEBUG_ENABLED(grp)) {
368 		ref_retain_debug(rc, grp);
369 	}
370 #endif
371 
372 	return true;
373 }
374 
375 bool
os_ref_retain_try_internal(os_ref_atomic_t * rc,struct os_refgrp * grp)376 os_ref_retain_try_internal(os_ref_atomic_t *rc, struct os_refgrp *grp)
377 {
378 	return __os_ref_retain_try(rc, 1, grp);
379 }
380 
381 bool
os_ref_retain_floor_try_internal(os_ref_atomic_t * rc,os_ref_count_t f,struct os_refgrp * grp)382 os_ref_retain_floor_try_internal(os_ref_atomic_t *rc, os_ref_count_t f,
383     struct os_refgrp *grp)
384 {
385 	return __os_ref_retain_try(rc, f, grp);
386 }
387 
388 __attribute__((always_inline))
389 static inline os_ref_count_t
_os_ref_release_inline(os_ref_atomic_t * rc,os_ref_count_t n,struct os_refgrp * __debug_only grp,memory_order release_order,memory_order dealloc_order)390 _os_ref_release_inline(os_ref_atomic_t *rc, os_ref_count_t n,
391     struct os_refgrp * __debug_only grp,
392     memory_order release_order, memory_order dealloc_order)
393 {
394 	os_ref_count_t val;
395 
396 #if OS_REFCNT_DEBUG
397 	if (REFLOG_GRP_DEBUG_ENABLED(grp)) {
398 		/*
399 		 * Care not to use 'rc' after the decrement because it might be deallocated
400 		 * under us.
401 		 */
402 		ref_release_debug(rc, grp);
403 	}
404 #endif
405 
406 	val = atomic_fetch_sub_explicit(rc, n, release_order);
407 	os_ref_check_underflow(rc, val, n);
408 	val -= n;
409 	if (__improbable(val < n)) {
410 		atomic_load_explicit(rc, dealloc_order);
411 	}
412 
413 #if OS_REFCNT_DEBUG
414 	/*
415 	 * The only way to safely access the ref count or group after
416 	 * decrementing the count is when the count is zero (as the caller won't
417 	 * see the zero until the function returns).
418 	 */
419 	if (val == 0 && REFLOG_GRP_DEBUG_ENABLED(grp)) {
420 		return ref_drop_debug(rc, grp);
421 	}
422 #endif
423 
424 	return val;
425 }
426 
427 #if OS_REFCNT_DEBUG
428 __attribute__((noinline))
429 static os_ref_count_t
os_ref_release_n_internal(os_ref_atomic_t * rc,os_ref_count_t n,struct os_refgrp * __debug_only grp,memory_order release_order,memory_order dealloc_order)430 os_ref_release_n_internal(os_ref_atomic_t *rc, os_ref_count_t n,
431     struct os_refgrp * __debug_only grp,
432     memory_order release_order, memory_order dealloc_order)
433 {
434 	// Legacy exported interface with bad codegen due to the barriers
435 	// not being immediate
436 	//
437 	// Also serves as the debug function
438 	return _os_ref_release_inline(rc, n, grp, release_order, dealloc_order);
439 }
440 #endif
441 
442 __attribute__((noinline))
443 os_ref_count_t
os_ref_release_internal(os_ref_atomic_t * rc,struct os_refgrp * __debug_only grp,memory_order release_order,memory_order dealloc_order)444 os_ref_release_internal(os_ref_atomic_t *rc, struct os_refgrp * __debug_only grp,
445     memory_order release_order, memory_order dealloc_order)
446 {
447 	// Legacy exported interface with bad codegen due to the barriers
448 	// not being immediate
449 	//
450 	// Also serves as the debug function
451 	return _os_ref_release_inline(rc, 1, grp, release_order, dealloc_order);
452 }
453 
454 os_ref_count_t
os_ref_release_barrier_internal(os_ref_atomic_t * rc,struct os_refgrp * __debug_only grp)455 os_ref_release_barrier_internal(os_ref_atomic_t *rc,
456     struct os_refgrp * __debug_only grp)
457 {
458 #if OS_REFCNT_DEBUG
459 	if (REFLOG_GRP_DEBUG_ENABLED(grp)) {
460 		return os_ref_release_internal(rc, grp,
461 		           memory_order_release, memory_order_acquire);
462 	}
463 #endif
464 	return _os_ref_release_inline(rc, 1, NULL,
465 	           memory_order_release, memory_order_acquire);
466 }
467 
468 os_ref_count_t
os_ref_release_relaxed_internal(os_ref_atomic_t * rc,struct os_refgrp * __debug_only grp)469 os_ref_release_relaxed_internal(os_ref_atomic_t *rc,
470     struct os_refgrp * __debug_only grp)
471 {
472 #if OS_REFCNT_DEBUG
473 	if (REFLOG_GRP_DEBUG_ENABLED(grp)) {
474 		return os_ref_release_internal(rc, grp,
475 		           memory_order_relaxed, memory_order_relaxed);
476 	}
477 #endif
478 	return _os_ref_release_inline(rc, 1, NULL,
479 	           memory_order_relaxed, memory_order_relaxed);
480 }
481 
482 static inline void
__os_ref_retain_locked(os_ref_atomic_t * rc,os_ref_count_t f,struct os_refgrp * __debug_only grp)483 __os_ref_retain_locked(os_ref_atomic_t *rc, os_ref_count_t f,
484     struct os_refgrp * __debug_only grp)
485 {
486 	os_ref_count_t val = os_ref_get_count_internal(rc);
487 	os_ref_check_retain(rc, val, f);
488 	atomic_store_explicit(rc, ++val, memory_order_relaxed);
489 
490 #if OS_REFCNT_DEBUG
491 	if (REFLOG_GRP_DEBUG_ENABLED(grp)) {
492 		ref_retain_debug(rc, grp);
493 	}
494 #endif
495 }
496 
497 void
os_ref_retain_locked_internal(os_ref_atomic_t * rc,struct os_refgrp * grp)498 os_ref_retain_locked_internal(os_ref_atomic_t *rc, struct os_refgrp *grp)
499 {
500 	__os_ref_retain_locked(rc, 1, grp);
501 }
502 
503 void
os_ref_retain_floor_locked_internal(os_ref_atomic_t * rc,os_ref_count_t f,struct os_refgrp * grp)504 os_ref_retain_floor_locked_internal(os_ref_atomic_t *rc, os_ref_count_t f,
505     struct os_refgrp *grp)
506 {
507 	__os_ref_retain_locked(rc, f, grp);
508 }
509 
510 os_ref_count_t
os_ref_release_locked_internal(os_ref_atomic_t * rc,struct os_refgrp * __debug_only grp)511 os_ref_release_locked_internal(os_ref_atomic_t *rc, struct os_refgrp * __debug_only grp)
512 {
513 #if OS_REFCNT_DEBUG
514 	if (REFLOG_GRP_DEBUG_ENABLED(grp)) {
515 		ref_release_debug(rc, grp);
516 	}
517 #endif
518 
519 	os_ref_count_t val = os_ref_get_count_internal(rc);
520 	os_ref_check_underflow(rc, val, 1);
521 	atomic_store_explicit(rc, --val, memory_order_relaxed);
522 
523 #if OS_REFCNT_DEBUG
524 	if (val == 0 && REFLOG_GRP_DEBUG_ENABLED(grp)) {
525 		return ref_drop_debug(rc, grp);
526 	}
527 #endif
528 
529 	return val;
530 }
531 
532 /*
533  * Bitwise API
534  */
535 
536 #undef os_ref_init_count_mask
537 void
os_ref_init_count_mask(os_ref_atomic_t * rc,uint32_t b,struct os_refgrp * __debug_only grp,os_ref_count_t init_count,uint32_t init_bits)538 os_ref_init_count_mask(os_ref_atomic_t *rc, uint32_t b,
539     struct os_refgrp *__debug_only grp,
540     os_ref_count_t init_count, uint32_t init_bits)
541 {
542 	assert(init_bits < (1U << b));
543 	atomic_init(rc, (init_count << b) | init_bits);
544 	os_ref_check_underflow(rc, (init_count << b), 1u << b);
545 
546 #if OS_REFCNT_DEBUG
547 	if (REFLOG_GRP_DEBUG_ENABLED(grp)) {
548 		ref_init_debug(rc, grp, init_count);
549 	}
550 #endif
551 }
552 
553 __attribute__((always_inline))
554 static inline void
os_ref_retain_mask_inline(os_ref_atomic_t * rc,uint32_t n,struct os_refgrp * __debug_only grp,memory_order mo)555 os_ref_retain_mask_inline(os_ref_atomic_t *rc, uint32_t n,
556     struct os_refgrp *__debug_only grp, memory_order mo)
557 {
558 	os_ref_count_t old = atomic_fetch_add_explicit(rc, n, mo);
559 	os_ref_check_retain(rc, old, n);
560 
561 #if OS_REFCNT_DEBUG
562 	if (REFLOG_GRP_DEBUG_ENABLED(grp)) {
563 		ref_retain_debug(rc, grp);
564 	}
565 #endif
566 }
567 
568 void
os_ref_retain_mask_internal(os_ref_atomic_t * rc,uint32_t n,struct os_refgrp * __debug_only grp)569 os_ref_retain_mask_internal(os_ref_atomic_t *rc, uint32_t n,
570     struct os_refgrp *__debug_only grp)
571 {
572 	os_ref_retain_mask_inline(rc, n, grp, memory_order_relaxed);
573 }
574 
575 void
os_ref_retain_acquire_mask_internal(os_ref_atomic_t * rc,uint32_t n,struct os_refgrp * __debug_only grp)576 os_ref_retain_acquire_mask_internal(os_ref_atomic_t *rc, uint32_t n,
577     struct os_refgrp *__debug_only grp)
578 {
579 	os_ref_retain_mask_inline(rc, n, grp, memory_order_acquire);
580 }
581 
582 uint32_t
os_ref_release_barrier_mask_internal(os_ref_atomic_t * rc,uint32_t n,struct os_refgrp * __debug_only grp)583 os_ref_release_barrier_mask_internal(os_ref_atomic_t *rc, uint32_t n,
584     struct os_refgrp *__debug_only grp)
585 {
586 #if OS_REFCNT_DEBUG
587 	if (REFLOG_GRP_DEBUG_ENABLED(grp)) {
588 		return os_ref_release_n_internal(rc, n, grp,
589 		           memory_order_release, memory_order_acquire);
590 	}
591 #endif
592 
593 	return _os_ref_release_inline(rc, n, NULL,
594 	           memory_order_release, memory_order_acquire);
595 }
596 
597 uint32_t
os_ref_release_relaxed_mask_internal(os_ref_atomic_t * rc,uint32_t n,struct os_refgrp * __debug_only grp)598 os_ref_release_relaxed_mask_internal(os_ref_atomic_t *rc, uint32_t n,
599     struct os_refgrp *__debug_only grp)
600 {
601 #if OS_REFCNT_DEBUG
602 	if (REFLOG_GRP_DEBUG_ENABLED(grp)) {
603 		return os_ref_release_n_internal(rc, n, grp,
604 		           memory_order_relaxed, memory_order_relaxed);
605 	}
606 #endif
607 
608 	return _os_ref_release_inline(rc, n, NULL,
609 	           memory_order_relaxed, memory_order_relaxed);
610 }
611 
612 uint32_t
os_ref_retain_try_mask_internal(os_ref_atomic_t * rc,uint32_t n,uint32_t reject_mask,struct os_refgrp * __debug_only grp)613 os_ref_retain_try_mask_internal(os_ref_atomic_t *rc, uint32_t n,
614     uint32_t reject_mask, struct os_refgrp *__debug_only grp)
615 {
616 	os_ref_count_t cur, next;
617 
618 	os_atomic_rmw_loop(rc, cur, next, relaxed, {
619 		if (__improbable(cur < n || (cur & reject_mask))) {
620 		        os_atomic_rmw_loop_give_up(return 0);
621 		}
622 		next = cur + n;
623 	});
624 
625 	os_ref_check_overflow(rc, cur);
626 
627 #if OS_REFCNT_DEBUG
628 	if (REFLOG_GRP_DEBUG_ENABLED(grp)) {
629 		ref_retain_debug(rc, grp);
630 	}
631 #endif
632 
633 	return next;
634 }
635 
636 bool
os_ref_retain_try_acquire_mask_internal(os_ref_atomic_t * rc,uint32_t n,uint32_t reject_mask,struct os_refgrp * __debug_only grp)637 os_ref_retain_try_acquire_mask_internal(os_ref_atomic_t *rc, uint32_t n,
638     uint32_t reject_mask, struct os_refgrp *__debug_only grp)
639 {
640 	os_ref_count_t cur, next;
641 
642 	os_atomic_rmw_loop(rc, cur, next, acquire, {
643 		if (__improbable(cur < n || (cur & reject_mask))) {
644 		        os_atomic_rmw_loop_give_up(return false);
645 		}
646 		next = cur + n;
647 	});
648 
649 	os_ref_check_overflow(rc, cur);
650 
651 #if OS_REFCNT_DEBUG
652 	if (REFLOG_GRP_DEBUG_ENABLED(grp)) {
653 		ref_retain_debug(rc, grp);
654 	}
655 #endif
656 
657 	return true;
658 }
659 
660 #pragma mark os_pcpu
661 
662 #define OS_PCPU_REF_LIVE        1ull
663 #define OS_PCPU_REF_WAITER      2ull
664 #define OS_PCPU_REF_INC         4ull
665 
666 typedef uint64_t _Atomic *__zpercpu     __os_pcpu_ref_t;
667 
668 static inline __os_pcpu_ref_t
os_pcpu_get(os_pcpu_ref_t ref)669 os_pcpu_get(os_pcpu_ref_t ref)
670 {
671 	return (__os_pcpu_ref_t)ref;
672 }
673 
674 static inline uint64_t
os_pcpu_count_to_value(os_ref_count_t cnt)675 os_pcpu_count_to_value(os_ref_count_t cnt)
676 {
677 	return cnt * OS_PCPU_REF_INC;
678 }
679 
680 static inline os_ref_count_t
os_pcpu_value_to_count(uint64_t v)681 os_pcpu_value_to_count(uint64_t v)
682 {
683 	return (os_ref_count_t)(v / OS_PCPU_REF_INC);
684 }
685 
686 __abortlike
687 static void
__os_pcpu_ref_destroy_panic(os_pcpu_ref_t * ref,uint64_t n)688 __os_pcpu_ref_destroy_panic(os_pcpu_ref_t *ref, uint64_t n)
689 {
690 	if (n & OS_PCPU_REF_LIVE) {
691 		panic("os_pcpu_ref: destroying live refcount %p at %p",
692 		    os_pcpu_get(*ref), ref);
693 	}
694 	if (n & OS_PCPU_REF_WAITER) {
695 		panic("os_pcpu_ref: destroying refcount %p with a waiter at %p",
696 		    os_pcpu_get(*ref), ref);
697 	}
698 	panic("os_pcpu_ref: destroying non-zero refcount %p at %p",
699 	    os_pcpu_get(*ref), ref);
700 }
701 
702 __abortlike
703 static void
__os_pcpu_ref_overflow_panic(__os_pcpu_ref_t rc)704 __os_pcpu_ref_overflow_panic(__os_pcpu_ref_t rc)
705 {
706 	panic("os_pcpu_ref: overflow (rc=%p)", rc);
707 }
708 
709 __abortlike
710 static void
__os_pcpu_ref_retain_panic(__os_pcpu_ref_t rc,uint64_t v)711 __os_pcpu_ref_retain_panic(__os_pcpu_ref_t rc, uint64_t v)
712 {
713 	if (v == 0) {
714 		panic("os_pcpu_ref: attempted resurrection (rc=%p)", rc);
715 	} else {
716 		__os_pcpu_ref_overflow_panic(rc);
717 	}
718 }
719 
720 __abortlike
721 static void
__os_pcpu_ref_release_live_panic(__os_pcpu_ref_t rc)722 __os_pcpu_ref_release_live_panic(__os_pcpu_ref_t rc)
723 {
724 	panic("os_pcpu_ref: unexpected release of final reference (rc=%p)", rc);
725 }
726 
727 __abortlike
728 static void
__os_pcpu_ref_release_panic(__os_pcpu_ref_t rc)729 __os_pcpu_ref_release_panic(__os_pcpu_ref_t rc)
730 {
731 	panic("os_pcpu_ref: over-release (rc=%p)", rc);
732 }
733 
734 __abortlike
735 static void
__os_pcpu_ref_kill_panic(__os_pcpu_ref_t rc)736 __os_pcpu_ref_kill_panic(__os_pcpu_ref_t rc)
737 {
738 	panic("os_pcpu_ref: double-kill (rc=%p)", rc);
739 }
740 
741 __abortlike
742 static void
__os_pcpu_ref_invalid_wait_panic(__os_pcpu_ref_t rc,uint64_t ov)743 __os_pcpu_ref_invalid_wait_panic(__os_pcpu_ref_t rc, uint64_t ov)
744 {
745 	if (ov & OS_PCPU_REF_WAITER) {
746 		panic("os_pcpu_ref: double-wait (rc=%p)", rc);
747 	} else {
748 		panic("os_pcpu_ref: wait while still live (rc=%p)", rc);
749 	}
750 }
751 
752 void
753 (os_pcpu_ref_init)(os_pcpu_ref_t * ref, struct os_refgrp *__debug_only grp)
754 {
755 	__os_pcpu_ref_t rc;
756 
757 	rc = zalloc_percpu(percpu_u64_zone, Z_WAITOK | Z_NOFAIL);
zpercpu_foreach_cpu(cpu)758 	zpercpu_foreach_cpu(cpu) {
759 		os_atomic_init(zpercpu_get_cpu(rc, cpu),
760 		    OS_PCPU_REF_LIVE + (cpu ? 0 : OS_PCPU_REF_INC));
761 	}
762 
763 	*ref = (os_pcpu_ref_t)rc;
764 #if OS_REFCNT_DEBUG
765 	if (REFLOG_GRP_DEBUG_ENABLED(grp)) {
766 		ref_retain_debug(rc, grp);
767 	}
768 #endif
769 }
770 
771 void
772 (os_pcpu_ref_destroy)(os_pcpu_ref_t * ref, struct os_refgrp *__debug_only grp)
773 {
774 	__os_pcpu_ref_t rc = os_pcpu_get(*ref);
775 	uint64_t n = 0;
776 
777 	n = os_atomic_load_wide(zpercpu_get_cpu(rc, 0), relaxed);
778 	if (n & OS_PCPU_REF_LIVE) {
779 		n = os_pcpu_ref_kill(*ref, grp);
780 	} else {
781 		for (int cpu = zpercpu_count(); cpu-- > 1;) {
782 			n |= os_atomic_load_wide(zpercpu_get_cpu(rc, cpu), relaxed);
783 		}
784 	}
785 	if (n) {
786 		__os_pcpu_ref_destroy_panic(ref, n);
787 	}
788 
789 	*ref = 0;
790 	zfree_percpu(percpu_u64_zone, rc);
791 }
792 
793 os_ref_count_t
os_pcpu_ref_count(os_pcpu_ref_t ref)794 os_pcpu_ref_count(os_pcpu_ref_t ref)
795 {
796 	uint64_t v;
797 
798 	v = os_atomic_load_wide(zpercpu_get_cpu(os_pcpu_get(ref), 0), relaxed);
799 	if (v & OS_PCPU_REF_LIVE) {
800 		return OS_REFCNT_MAX_COUNT;
801 	}
802 	return os_pcpu_value_to_count(v);
803 }
804 
805 static inline uint64_t
__os_pcpu_ref_delta(__os_pcpu_ref_t rc,int delta,int * cpup)806 __os_pcpu_ref_delta(__os_pcpu_ref_t rc, int delta, int *cpup)
807 {
808 	_Atomic uint64_t *rcp;
809 	uint64_t v;
810 	int cpu;
811 
812 	cpu  = cpu_number();
813 	rcp  = zpercpu_get_cpu(rc, cpu);
814 	v    = os_atomic_load_wide(rcp, relaxed);
815 	if (__improbable((v & OS_PCPU_REF_LIVE) == 0)) {
816 		*cpup = -1;
817 		return v;
818 	}
819 
820 	*cpup = cpu;
821 	if (delta > 0) {
822 		return os_atomic_add_orig(rcp, OS_PCPU_REF_INC, relaxed);
823 	} else {
824 		return os_atomic_sub_orig(rcp, OS_PCPU_REF_INC, release);
825 	}
826 }
827 
828 __attribute__((noinline))
829 static void
__os_pcpu_ref_retain_slow(__os_pcpu_ref_t rc,int cpu,uint64_t v)830 __os_pcpu_ref_retain_slow(__os_pcpu_ref_t rc, int cpu, uint64_t v)
831 {
832 	if (cpu > 0) {
833 		os_atomic_sub(zpercpu_get_cpu(rc, cpu),
834 		    OS_PCPU_REF_INC, relaxed);
835 	}
836 
837 	if (cpu != 0) {
838 		v = os_atomic_add_orig(zpercpu_get_cpu(rc, 0),
839 		    OS_PCPU_REF_INC, relaxed);
840 		if (v & OS_PCPU_REF_LIVE) {
841 			/* we're doing this concurrently to an os_pcpu_ref_kill */
842 			return;
843 		}
844 	}
845 
846 	if (v == 0 || v >= os_pcpu_count_to_value(OS_REFCNT_MAX_COUNT)) {
847 		__os_pcpu_ref_retain_panic(rc, v);
848 	}
849 }
850 
851 void
852 (os_pcpu_ref_retain)(os_pcpu_ref_t ref, struct os_refgrp * __debug_only grp)
853 {
854 	__os_pcpu_ref_t rc = os_pcpu_get(ref);
855 	uint64_t v;
856 	int cpu;
857 
858 	v = __os_pcpu_ref_delta(rc, +1, &cpu);
859 	if (__improbable((v & OS_PCPU_REF_LIVE) == 0)) {
860 		__os_pcpu_ref_retain_slow(rc, cpu, v);
861 	}
862 
863 #if OS_REFCNT_DEBUG
864 	if (REFLOG_GRP_DEBUG_ENABLED(grp)) {
865 		ref_retain_debug(rc, grp);
866 	}
867 #endif
868 }
869 
870 bool
871 (os_pcpu_ref_retain_try)(os_pcpu_ref_t ref, struct os_refgrp *__debug_only grp)
872 {
873 	__os_pcpu_ref_t rc = os_pcpu_get(ref);
874 	_Atomic uint64_t *rcp = zpercpu_get(rc);
875 	uint64_t ov, nv;
876 
877 	os_atomic_rmw_loop(rcp, ov, nv, relaxed, {
878 		if ((ov & OS_PCPU_REF_LIVE) == 0) {
879 		        os_atomic_rmw_loop_give_up(return false);
880 		}
881 		nv = ov + OS_PCPU_REF_INC;
882 	});
883 
884 #if OS_REFCNT_DEBUG
885 	if (REFLOG_GRP_DEBUG_ENABLED(grp)) {
886 		ref_retain_debug(rc, grp);
887 	}
888 #endif
889 	return true;
890 }
891 
892 __attribute__((noinline))
893 static void
__os_pcpu_ref_release_live_slow(__os_pcpu_ref_t rc,int cpu,uint64_t v)894 __os_pcpu_ref_release_live_slow(__os_pcpu_ref_t rc, int cpu, uint64_t v)
895 {
896 	if (cpu > 0) {
897 		os_atomic_add(zpercpu_get_cpu(rc, cpu),
898 		    OS_PCPU_REF_INC, relaxed);
899 	}
900 	if (cpu != 0) {
901 		v = os_atomic_sub_orig(zpercpu_get_cpu(rc, 0),
902 		    OS_PCPU_REF_INC, release);
903 		if (v & OS_PCPU_REF_LIVE) {
904 			/* we're doing this concurrently to an os_pcpu_ref_kill */
905 			return;
906 		}
907 	}
908 
909 	if (v < os_pcpu_count_to_value(2)) {
910 		__os_pcpu_ref_release_live_panic(rc);
911 	}
912 }
913 
914 void
915 (os_pcpu_ref_release_live)(os_pcpu_ref_t ref, struct os_refgrp *__debug_only grp)
916 {
917 	__os_pcpu_ref_t rc = os_pcpu_get(ref);
918 	uint64_t v;
919 	int cpu;
920 
921 #if OS_REFCNT_DEBUG
922 	if (REFLOG_GRP_DEBUG_ENABLED(grp)) {
923 		/*
924 		 * Care not to use 'rc' after the decrement because it might be deallocated
925 		 * under us.
926 		 */
927 		ref_release_debug(rc, grp);
928 	}
929 #endif
930 
931 	v = __os_pcpu_ref_delta(rc, -1, &cpu);
932 
933 	if (__improbable((v & OS_PCPU_REF_LIVE) == 0)) {
934 		__os_pcpu_ref_release_live_slow(rc, cpu, v);
935 	}
936 }
937 
938 __attribute__((noinline))
939 static os_ref_count_t
__os_pcpu_ref_release_slow(__os_pcpu_ref_t rc,int cpu,uint64_t v __os_refgrp_arg (struct os_refgrp * grp))940 __os_pcpu_ref_release_slow(
941 	__os_pcpu_ref_t         rc,
942 	int                     cpu,
943 	uint64_t                v
944 	__os_refgrp_arg(struct os_refgrp *grp))
945 {
946 	uint64_t _Atomic *rc0 = zpercpu_get_cpu(rc, 0);
947 
948 	if (cpu > 0) {
949 		os_atomic_add(zpercpu_get_cpu(rc, cpu),
950 		    OS_PCPU_REF_INC, relaxed);
951 	}
952 	if (cpu != 0) {
953 		v = os_atomic_sub_orig(rc0, OS_PCPU_REF_INC, release);
954 		if (v & OS_PCPU_REF_LIVE) {
955 			/* we're doing this concurrently to an os_pcpu_ref_kill */
956 			return OS_REFCNT_MAX_COUNT;
957 		}
958 	}
959 
960 	if (os_sub_overflow(v, OS_PCPU_REF_INC, &v)) {
961 		__os_pcpu_ref_release_panic(rc);
962 	}
963 
964 	os_atomic_thread_fence(acquire);
965 	if (v == OS_PCPU_REF_WAITER) {
966 		os_atomic_andnot(rc0, OS_PCPU_REF_WAITER, release);
967 		thread_wakeup(rc);
968 		v = 0;
969 	}
970 #if OS_REFCNT_DEBUG
971 	if (v == 0 && REFLOG_GRP_DEBUG_ENABLED(grp)) {
972 		return ref_drop_debug(rc, grp);
973 	}
974 #endif
975 	return os_pcpu_value_to_count(v);
976 }
977 
os_ref_count_t(os_pcpu_ref_release)978 os_ref_count_t
979 (os_pcpu_ref_release)(os_pcpu_ref_t ref, struct os_refgrp *__debug_only grp)
980 {
981 	__os_pcpu_ref_t rc = os_pcpu_get(ref);
982 	uint64_t v;
983 	int cpu;
984 
985 #if OS_REFCNT_DEBUG
986 	if (REFLOG_GRP_DEBUG_ENABLED(grp)) {
987 		ref_release_debug(rc, grp);
988 	}
989 #endif
990 
991 	v = __os_pcpu_ref_delta(rc, -1, &cpu);
992 	if (__improbable((v & OS_PCPU_REF_LIVE) == 0)) {
993 		return __os_pcpu_ref_release_slow(rc, cpu, v __os_refgrp_arg(grp));
994 	}
995 
996 	return OS_REFCNT_MAX_COUNT;
997 }
998 
os_ref_count_t(os_pcpu_ref_kill)999 os_ref_count_t
1000 (os_pcpu_ref_kill)(os_pcpu_ref_t ref, struct os_refgrp *__debug_only grp)
1001 {
1002 	__os_pcpu_ref_t rc = os_pcpu_get(ref);
1003 	uint64_t v = 0, t = 0;
1004 
1005 #if OS_REFCNT_DEBUG
1006 	if (REFLOG_GRP_DEBUG_ENABLED(grp)) {
1007 		ref_release_debug(rc, grp);
1008 	}
1009 #endif
1010 
1011 	for (int cpu = zpercpu_count(); cpu-- > 1;) {
1012 		v = os_atomic_xchg(zpercpu_get_cpu(rc, cpu), 0, relaxed);
1013 		if ((v & OS_PCPU_REF_LIVE) == 0) {
1014 			__os_pcpu_ref_kill_panic(rc);
1015 		}
1016 		t += v - OS_PCPU_REF_LIVE;
1017 	}
1018 	t -= OS_PCPU_REF_LIVE + OS_PCPU_REF_INC;
1019 
1020 	v = os_atomic_add(zpercpu_get_cpu(rc, 0), t, acq_rel);
1021 	if (v & OS_PCPU_REF_LIVE) {
1022 		__os_pcpu_ref_kill_panic(rc);
1023 	}
1024 
1025 	if (v >= os_pcpu_count_to_value(OS_REFCNT_MAX_COUNT)) {
1026 		__os_pcpu_ref_overflow_panic(rc);
1027 	}
1028 
1029 #if OS_REFCNT_DEBUG
1030 	if (v == 0 && REFLOG_GRP_DEBUG_ENABLED(grp)) {
1031 		return ref_drop_debug(rc, grp);
1032 	}
1033 #endif
1034 	return os_pcpu_value_to_count(v);
1035 }
1036 
1037 #if KERNEL
1038 
1039 void
os_pcpu_ref_wait_for_death(os_pcpu_ref_t ref)1040 os_pcpu_ref_wait_for_death(os_pcpu_ref_t ref)
1041 {
1042 	__os_pcpu_ref_t rc = os_pcpu_get(ref);
1043 	uint64_t _Atomic *rc0 = zpercpu_get_cpu(rc, 0);
1044 	uint64_t ov, nv;
1045 
1046 	ov = os_atomic_load(rc0, relaxed);
1047 	if (ov == 0) {
1048 		os_atomic_thread_fence(acquire);
1049 		return;
1050 	}
1051 
1052 	assert_wait(rc, THREAD_UNINT);
1053 
1054 	os_atomic_rmw_loop(rc0, ov, nv, relaxed, {
1055 		if (ov & (OS_PCPU_REF_WAITER | OS_PCPU_REF_LIVE)) {
1056 		        __os_pcpu_ref_invalid_wait_panic(rc, ov);
1057 		}
1058 		if (ov == 0) {
1059 		        os_atomic_rmw_loop_give_up(break);
1060 		}
1061 		nv = ov | OS_PCPU_REF_WAITER;
1062 	});
1063 
1064 	if (ov == 0) {
1065 		os_atomic_thread_fence(acquire);
1066 		clear_wait(current_thread(), THREAD_AWAKENED);
1067 	} else {
1068 		thread_block(THREAD_CONTINUE_NULL);
1069 	}
1070 }
1071 
1072 #endif
1073