/*
* Copyright (c) 2024 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
#pragma once
#if defined(__LP64__)
#include <mach/mach_types.h>
#include <mach/vm_reclaim.h>
#include <ptrcheck.h>
#define VM_RECLAIM_MAX_BUFFER_SIZE (128ull << 20)
#define VM_RECLAIM_MAX_CAPACITY ((VM_RECLAIM_MAX_BUFFER_SIZE - \
offsetof(struct mach_vm_reclaim_ring_s, entries)) / \
sizeof(struct mach_vm_reclaim_entry_s))
__BEGIN_DECLS
typedef struct mach_vm_reclaim_entry_s {
mach_vm_address_t address;
uint32_t size;
mach_vm_reclaim_action_t behavior;
uint8_t _unused[3];
} *mach_vm_reclaim_entry_t;
/* This struct is no longer used () */
typedef struct mach_vm_reclaim_indices_s {
_Atomic mach_vm_reclaim_id_t head;
_Atomic mach_vm_reclaim_id_t tail;
_Atomic mach_vm_reclaim_id_t busy;
} *mach_vm_reclaim_indices_t;
/*
* Contains the data used for synchronization with the kernel
*/
struct mach_vm_reclaim_ring_s {
/* no longer used () */
mach_vm_size_t va_in_buffer;
/* no longer used () */
mach_vm_size_t last_accounting_given_to_kernel;
/* The current length of the ringbuffer */
mach_vm_reclaim_count_t len;
/* The maximum length of the ringbuffer */
mach_vm_reclaim_count_t max_len;
/* no longer used () */
struct mach_vm_reclaim_indices_s indices;
/* The minimum period of time between kernel accounting updates */
uint64_t sampling_period_abs;
/* timestamp (MAS) of the last kernel accounting update */
uint64_t last_sample_abs;
/*
* An estimate for the number of reclaimable bytes currently in the ring. This
* is updating atomically after entering a new reclaimable region, after
* successfully cancelling a region, and after reclaiming regions.
*/
_Atomic uint64_t reclaimable_bytes;
/*
* The minimum amount of reclaimable memory in this buffer for the current
* sampling interval.
*/
_Atomic uint64_t reclaimable_bytes_min;
/* Marks IDs which have been reclaimed */
_Atomic mach_vm_reclaim_id_t head;
/* Marks IDs which are in the process of being reclaimed */
_Atomic mach_vm_reclaim_id_t busy;
/* The ID of the most recent entry */
_Atomic mach_vm_reclaim_id_t tail;
/* Pad to a multiple of the entry size */
uint64_t _unused;
/*
* The ringbuffer entries themselves populate the remainder of this
* buffer's vm allocation.
* NB: the fields preceding `entries` should be aligned to a multiple of
* the entry size.
*/
struct mach_vm_reclaim_entry_s entries[] __counted_by(len);
};
/*
* The above definitions exist for the internal implementation in libsyscall /
* xnu and for observability with debugging tools. They should _NOT_ be used by
* clients.
*/
#if !KERNEL
/*
* The below interfaces are intended for observing a task's reclaim ring(s) and
* querying which regions are reclaimable. General usage would look something
* like the following:
*
* - Use `mach_vm_reclaim_get_rings_for_task` to get a list of reclaim rings
* for a task.
* - Use `mach_vm_reclaim_ring_copy` for each ring to map a copy of the
* reclaim ring into your address space.
* - Use `mach_vm_reclaim_copied_ring_query` to query a list of reclaimable
* regions in the ring.
* - Use `mach_vm_reclaim_copied_ring_free` to free the copied reclaim ring.
*/
/// A descriptor for a reclaimable region
typedef struct mach_vm_reclaim_region_s {
mach_vm_address_t vmrr_addr;
mach_vm_size_t vmrr_size;
mach_vm_reclaim_action_t vmrr_behavior;
uint8_t _vmrr_unused[3];
} *mach_vm_reclaim_region_t;
/// A reference to a task's reclaim ring
typedef struct mach_vm_reclaim_ring_ref_s {
mach_vm_address_t addr;
mach_vm_size_t size;
} *mach_vm_reclaim_ring_ref_t;
/// A reclaim ring copied from another task
typedef void *mach_vm_reclaim_ring_copy_t;
/// Get references to another task's reclaim rings.
///
/// - Parameters:
/// - task: The target task
/// - refs_out: A buffer to store the references in. If NULL, only the number
/// of rings will be queried.
/// - count_inout: A pointer to the count of the buffer, which will be
/// overwritten with the number of rings in the target task.
///
/// - Returns: `VM_RECLAIM_SUCCESS` upon success.
__SPI_AVAILABLE(macos(16.0), ios(19.0), tvos(19.0), visionos(3.0))
mach_vm_reclaim_error_t mach_vm_reclaim_get_rings_for_task(
task_read_t task,
mach_vm_reclaim_ring_ref_t refs_out,
mach_vm_reclaim_count_t *count_inout);
/// Copy another task's reclaim ring into this task's VA.
///
/// - Parameters:
/// - task: The task to copy the ring from
/// - ref: The reference to the ring to copy
/// (obtained via mach_vm_reclaim_get_rings_for_task).
/// - ring_out: The pointer to the copied ring to be written out upon success
///
/// - Returns: `VM_RECLAIM_SUCCESS` upon success.
__SPI_AVAILABLE(macos(16.0), ios(19.0), tvos(19.0), visionos(3.0))
mach_vm_reclaim_error_t mach_vm_reclaim_ring_copy(
task_read_t task,
mach_vm_reclaim_ring_ref_t ref,
mach_vm_reclaim_ring_copy_t *ring_out);
/// Free a reclaim ring copied from another task.
///
/// - Parameters:
/// - ring: The copied ring to free.
///
/// - Returns: `VM_RECLAIM_SUCCESS` upon success.
__SPI_AVAILABLE(macos(16.0), ios(19.0), tvos(19.0), visionos(3.0))
mach_vm_reclaim_error_t mach_vm_reclaim_copied_ring_free(
mach_vm_reclaim_ring_copy_t *ring);
/// Query the reclaimable regions in a copied reclaim ring.
///
/// - Parameters:
/// - ring: The ring to query
/// - regions_out: A buffer of `mach_vm_reclaim_region_s` to copy the query
/// results into. If NULL, only the size of the ring will be queried.
/// - count_inout: A pointer to the size, in regions, of the buffer. Will
/// be overwritten with the count of regions in the ring upon success.
///
/// - Returns: `VM_RECLAIM_SUCCESS` on success.
/// `KERN_NO_SPACE` if there is insufficient space in the buffer to store
/// the queried ring's regions.
/// `VM_RECLAIM_INVALID_CAPACITY` if the ringbuffer structure was malformed
/// and had a buffer too small for its reported size. The entries that were
/// able to be queried and the count will still be written out.
__SPI_AVAILABLE(macos(16.0), ios(19.0), tvos(19.0), visionos(3.0))
mach_vm_reclaim_error_t mach_vm_reclaim_copied_ring_query(
mach_vm_reclaim_ring_copy_t *ring,
mach_vm_reclaim_region_t regions_out,
mach_vm_reclaim_count_t *count_inout);
#endif /* !KERNEL */
__END_DECLS
#endif /* __LP64__ */