2 * Copyright (c) 2015 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
28 #include <kern/kalloc.h>
29 #include <kern/locks.h>
30 #include <sys/kernel.h>
31 #include <sys/kernel_types.h>
32 #include <kern/zalloc.h>
33 #include <sys/reason.h>
35 #include <kern/assert.h>
36 #include <kern/debug.h>
39 #include <pexpert/pexpert.h>
41 extern int os_reason_debug_disabled
;
47 * Lock group attributes for os_reason subsystem
49 lck_grp_attr_t
*os_reason_lock_grp_attr
;
50 lck_grp_t
*os_reason_lock_grp
;
51 lck_attr_t
*os_reason_lock_attr
;
53 #define OS_REASON_RESERVE_COUNT 100
54 #define OS_REASON_MAX_COUNT (maxproc + 100)
56 static struct zone
*os_reason_zone
;
57 static int os_reason_alloc_buffer_internal(os_reason_t cur_reason
, uint32_t osr_bufsize
,
63 int reasons_allocated
= 0;
66 * Initialize OS reason group and lock attributes
68 os_reason_lock_grp_attr
= lck_grp_attr_alloc_init();
69 os_reason_lock_grp
= lck_grp_alloc_init("os_reason_lock", os_reason_lock_grp_attr
);
70 os_reason_lock_attr
= lck_attr_alloc_init();
73 * Create OS reason zone.
75 os_reason_zone
= zinit(sizeof(struct os_reason
), OS_REASON_MAX_COUNT
* sizeof(struct os_reason
),
76 OS_REASON_MAX_COUNT
, "os reasons");
77 if (os_reason_zone
== NULL
) {
78 panic("failed to initialize os_reason_zone");
82 * We pre-fill the OS reason zone to reduce the likelihood that
83 * the jetsam thread and others block when they create an exit
84 * reason. This pre-filled memory is not-collectable since it's
85 * foreign memory crammed in as part of zfill().
87 reasons_allocated
= zfill(os_reason_zone
, OS_REASON_RESERVE_COUNT
);
88 assert(reasons_allocated
> 0);
92 * Creates a new reason and initializes it with the provided reason
93 * namespace and code. Also sets up the buffer and kcdata_descriptor
94 * associated with the reason. Returns a pointer to the newly created
98 * REASON_NULL if unable to allocate a reason or initialize the nested buffer
99 * a pointer to the reason otherwise
102 os_reason_create(uint32_t osr_namespace
, uint64_t osr_code
)
104 os_reason_t new_reason
= OS_REASON_NULL
;
106 new_reason
= (os_reason_t
) zalloc(os_reason_zone
);
107 if (new_reason
== OS_REASON_NULL
) {
110 * We rely on OS reasons to communicate important things such
111 * as process exit reason information, we should be aware
112 * when issues prevent us from allocating them.
114 if (os_reason_debug_disabled
) {
115 kprintf("os_reason_create: failed to allocate reason with namespace: %u, code : %llu\n",
116 osr_namespace
, osr_code
);
118 panic("os_reason_create: failed to allocate reason with namespace: %u, code: %llu\n",
119 osr_namespace
, osr_code
);
125 bzero(new_reason
, sizeof(*new_reason
));
127 new_reason
->osr_namespace
= osr_namespace
;
128 new_reason
->osr_code
= osr_code
;
129 new_reason
->osr_flags
= 0;
130 new_reason
->osr_bufsize
= 0;
131 new_reason
->osr_kcd_buf
= NULL
;
133 lck_mtx_init(&new_reason
->osr_lock
, os_reason_lock_grp
, os_reason_lock_attr
);
134 new_reason
->osr_refcount
= 1;
140 os_reason_dealloc_buffer(os_reason_t cur_reason
)
142 assert(cur_reason
!= OS_REASON_NULL
);
143 LCK_MTX_ASSERT(&cur_reason
->osr_lock
, LCK_MTX_ASSERT_OWNED
);
145 if (cur_reason
->osr_kcd_buf
!= NULL
&& cur_reason
->osr_bufsize
!= 0) {
146 kfree(cur_reason
->osr_kcd_buf
, cur_reason
->osr_bufsize
);
149 cur_reason
->osr_bufsize
= 0;
150 cur_reason
->osr_kcd_buf
= NULL
;
151 bzero(&cur_reason
->osr_kcd_descriptor
, sizeof(cur_reason
->osr_kcd_descriptor
));
157 * Allocates and initializes a buffer of specified size for the reason. This function
158 * may block and should not be called from extremely performance sensitive contexts
159 * (i.e. jetsam). Also initializes the kcdata descriptor accordingly. If there is an
160 * existing buffer, we dealloc the buffer before allocating a new one and
161 * clear the associated kcdata descriptor. If osr_bufsize is passed as 0,
162 * we deallocate the existing buffer and then return.
166 * EINVAL if the passed reason pointer is invalid or the requested size is
167 * larger than REASON_BUFFER_MAX_SIZE
168 * EIO if we fail to initialize the kcdata buffer
171 os_reason_alloc_buffer(os_reason_t cur_reason
, uint32_t osr_bufsize
)
173 return os_reason_alloc_buffer_internal(cur_reason
, osr_bufsize
, TRUE
);
177 * Allocates and initializes a buffer of specified size for the reason. Also
178 * initializes the kcdata descriptor accordingly. If there is an existing
179 * buffer, we dealloc the buffer before allocating a new one and
180 * clear the associated kcdata descriptor. If osr_bufsize is passed as 0,
181 * we deallocate the existing buffer and then return.
185 * EINVAL if the passed reason pointer is invalid or the requested size is
186 * larger than REASON_BUFFER_MAX_SIZE
187 * ENOMEM if unable to allocate memory for the buffer
188 * EIO if we fail to initialize the kcdata buffer
191 os_reason_alloc_buffer_noblock(os_reason_t cur_reason
, uint32_t osr_bufsize
)
193 return os_reason_alloc_buffer_internal(cur_reason
, osr_bufsize
, FALSE
);
197 os_reason_alloc_buffer_internal(os_reason_t cur_reason
, uint32_t osr_bufsize
,
200 if (cur_reason
== OS_REASON_NULL
) {
204 if (osr_bufsize
> OS_REASON_BUFFER_MAX_SIZE
) {
208 lck_mtx_lock(&cur_reason
->osr_lock
);
210 os_reason_dealloc_buffer(cur_reason
);
212 if (osr_bufsize
== 0) {
213 lck_mtx_unlock(&cur_reason
->osr_lock
);
218 cur_reason
->osr_kcd_buf
= kalloc_tag(osr_bufsize
, VM_KERN_MEMORY_REASON
);
219 assert(cur_reason
->osr_kcd_buf
!= NULL
);
221 cur_reason
->osr_kcd_buf
= kalloc_noblock_tag(osr_bufsize
, VM_KERN_MEMORY_REASON
);
222 if (cur_reason
->osr_kcd_buf
== NULL
) {
223 lck_mtx_unlock(&cur_reason
->osr_lock
);
228 bzero(cur_reason
->osr_kcd_buf
, osr_bufsize
);
230 cur_reason
->osr_bufsize
= osr_bufsize
;
232 if (kcdata_memory_static_init(&cur_reason
->osr_kcd_descriptor
, (mach_vm_address_t
) cur_reason
->osr_kcd_buf
,
233 KCDATA_BUFFER_BEGIN_OS_REASON
, osr_bufsize
, KCFLAG_USE_MEMCOPY
) != KERN_SUCCESS
) {
234 os_reason_dealloc_buffer(cur_reason
);
236 lck_mtx_unlock(&cur_reason
->osr_lock
);
240 lck_mtx_unlock(&cur_reason
->osr_lock
);
246 * Returns a pointer to the kcdata descriptor associated with the specified
247 * reason if there is a buffer allocated.
249 struct kcdata_descriptor
*
250 os_reason_get_kcdata_descriptor(os_reason_t cur_reason
)
252 if (cur_reason
== OS_REASON_NULL
) {
256 if (cur_reason
->osr_kcd_buf
== NULL
) {
260 assert(cur_reason
->osr_kcd_descriptor
.kcd_addr_begin
== (mach_vm_address_t
) cur_reason
->osr_kcd_buf
);
261 if (cur_reason
->osr_kcd_descriptor
.kcd_addr_begin
!= (mach_vm_address_t
) cur_reason
->osr_kcd_buf
) {
265 return &cur_reason
->osr_kcd_descriptor
;
269 * Takes a reference on the passed reason.
272 os_reason_ref(os_reason_t cur_reason
)
274 if (cur_reason
== OS_REASON_NULL
) {
278 lck_mtx_lock(&cur_reason
->osr_lock
);
280 assert(cur_reason
->osr_refcount
> 0);
281 cur_reason
->osr_refcount
++;
283 lck_mtx_unlock(&cur_reason
->osr_lock
);
289 * Drops a reference on the passed reason, deallocates
290 * the reason if no references remain.
293 os_reason_free(os_reason_t cur_reason
)
295 if (cur_reason
== OS_REASON_NULL
) {
299 lck_mtx_lock(&cur_reason
->osr_lock
);
301 assert(cur_reason
->osr_refcount
> 0);
303 cur_reason
->osr_refcount
--;
304 if (cur_reason
->osr_refcount
!= 0) {
305 lck_mtx_unlock(&cur_reason
->osr_lock
);
309 os_reason_dealloc_buffer(cur_reason
);
311 lck_mtx_unlock(&cur_reason
->osr_lock
);
312 lck_mtx_destroy(&cur_reason
->osr_lock
, os_reason_lock_grp
);
314 zfree(os_reason_zone
, cur_reason
);