XXNU Source Handbook Download offline version from here
Part 11 · Chapter 11.37

Skywalk memory: regions, caches, arenas and task mappings

Networking · Skywalk overview · Packet pools and buflets · Channel lifetimes · IOKit CPU mappings

Skywalk gives a process selected packet, ring and metadata memory while retaining separate kernel-owned state. Its memory layer controls backing allocation, object reuse, permissions, mapping updates and revocation. These are different operations from moving a ring index. This chapter follows the nexus arena path in xnu-12377.121.6 through bsd/skywalk/mem and the locally supplied IOSkywalkSupport.cpp implementation.

The hierarchy, including what is shared

Layer Concrete representation and responsibility
Arena skmem_arena owns a region array, backing IOSKArena, active/defunct flags, references and per-task mapping list. skmem_arena_nexus additionally holds pools and ring/schema caches.
Region skmem_region defines geometry, protections, segment indices, allocation hash, freelists, mirror and attached caches. A region describes a class of storage, not one packet.
Segment sksegment identifies one region segment, its IOSKMemoryBufferRef, kernel address range, allocation/mapping state and index.
Slab and object skmem_slab and buffer-control records subdivide backing into cache objects. skmem_bufctl tracks the buffer allocation and shared-use state.
Cache skmem_cache, CPU magazines and shared depot reuse constructed objects before entering the slab/region layer.
Task mapping skmem_arena_mmap_info records the task, mapper handle, user virtual base/size and redirect state.
Skywalk memory: regions, caches, arenas and task mappings diagram
Skywalk memory: regions, caches, arenas and task mappings diagram · Open SVG
Mermaid source
flowchart TD
    A[Nexus arena] --> R[Ordered user-mappable regions]
    A --> K[Kernel-only metadata and slot regions]
    A --> P[TX and RX packet pool references]
    R --> S[Indexed segments]
    K --> KS[Separate kernel metadata segments]
    S --> D[IOSK memory buffer descriptor]
    D --> M[Per-task IOMemoryMap objects]
    S --> C[Slab objects and constructed cache objects]
    P --> C
    U[Task arena base plus region and segment offsets] --> M

“Mirrored metadata” does not mean the process maps the kernel's private metadata bytes. skmem_region_mirror() pairs geometrically identical regions. skmem_region_mirror_alloc() allocates a separate segment at the same index in the mirror. Constructors receive both object locations so permitted metadata can be copied/validated between them. Payload buffers may be mapped into more than one eligible arena; private kernel pointers remain in kernel-only regions.

Region geometry and protection are chosen before mapping

skmem_region_params separates requested from configured segment/object sizes and counts. skmem_region_params_config() computes the usable geometry. skmem_region_create() validates page/object divisibility, records object/segment geometry, creates allocation-index tracking and a backing IOSKRegion, and translates creation flags into buffer and region specifications.

skmem_region_id_t separates user-mappable schema, rings, payload, user metadata/buflets, descriptors and advisory/statistics regions from kernel metadata, kernel buflets and kernel slot descriptors. The nexus constructor's assertions enforce several concrete rules:

Region flags also control kernel writability, physical contiguity, I/O direction, cache inhibition, persistence, pure-data allocation and memory tagging. On the macOS target, the creation branch marks ordinary non-pseudo/non-guard regions persistent to avoid unsafe fault-in contexts. Do not extrapolate one region's wiring/pageability to every platform or every region type. Pseudo regions instead create a destructible zone; they do not instantiate normal task-mappable segment backing.

Object allocation: magazines, construction and partial success

skmem_cache_batch_alloc() first removes objects from the current CPU's loaded magazine, swaps a previous loaded magazine when possible, then obtains full magazines from the shared depot. Cache modes can disable magazines, including schema/ring caches created by the nexus arena. When the cache layer cannot satisfy the request, it releases CPU protection and asks the slab layer for raw objects.

A raw object is not necessarily ready for a caller. The cache invokes its constructor with skmem_obj_info for the primary object and optional mirror. Constructor failure stops construction, returns remaining raw objects to slabs and preserves already-constructed successes. Temporary object-info records can live in the raw object's storage, so the constructor cannot overwrite them and continue reading them afterward. Packet-pool constructors explicitly copy those records when necessary. See constructor/partial-list handling and packet metadata construction.

Freeing normally returns a constructed object to magazines. skmem_cache_batch_free_nocache() bypasses that reuse path when destruction must happen now; shared packet backing is one consumer. skmem_cache_reap_now() either purges magazines under resize exclusion or reaps shared-depot working-set excess and previous CPU magazines. Reaping cached objects is not the same operation as revoking a task's address mapping.

Getting a segment and its memory descriptor

skmem_region_alloc() holds the region mutex while selecting or growing a free segment index. At its configured limit, it can drop region protection to reap attached caches and then retry. Nonblocking or failure-tolerant requests may return empty; a blocking request can sleep on the segment freelist. Failure to create backing for an already-selected segment has a different bounded retry/watchdog path, which preserves index synchronization for mirrored regions.

sksegment_freelist_remove() creates an IOSKMemoryBuffer, records the kernel virtual range, makes reclaimable backing nonvolatile as applicable, wires persistent non-memtag backing, publishes the descriptor into the indexed IOSK region and invokes the client segment constructor. The constructor is the point where an external consumer may wire/map the memory for I/O. The body specifically rolls back an ENOMEM constructor result; it must not be described as treating every possible errno identically.

bsd/skywalk/mem/skmem_region.c:1842–1863:

	sg->sg_type = SKSEG_TYPE_ALLOC;

	/* any failures after this can make a full call to sksegment_freelist_insert() */

	/*
	 * Let the client wire it and insert to IOMMU, if applicable.
	 * Try to find out if it's wired and set the right state.
	 */
	if (skr->skr_seg_ctor != NULL) {
		ret = skr->skr_seg_ctor(sg, sg->sg_md, skr->skr_private);
		/* Handle segment creation failure from driver power down */
		if (__improbable(ret != 0)) {
			if (ret == ENOMEM) {
				sksegment_freelist_insert(skr, sg, FALSE);
				goto fail;
			}
		}
	}

	sg->sg_state = IOSKBufferIsWired(sg->sg_md) ?
	    SKSEG_STATE_MAPPED_WIRED : SKSEG_STATE_MAPPED;

sg_state records whether the resulting descriptor is wired. A kernel virtual address in sg_start does not establish an IOVA. The actual device mapping belongs to the provider's constructor and its IOKit mapper/DMA operations; the pool/region layer passes a descriptor, not a universal device address.

The inverse sksegment_freelist_insert() calls the client destructor before clearing region backing, unwires persistent memory where applicable, discards purgeable backing consistently, destroys the descriptor and clears kernel addresses/state before reusing the segment index. A destructor is where an I/O consumer must release its mapping/device ownership; freeing a CPU cache object cannot replace that synchronization.

Constructing the arena and mapping it into a process

skmem_arena_create_for_nexus() sets up packet-pool regions, user/kernel slot-descriptor regions, ring/schema caches and optional statistics/advisory objects. It reuses or retains supplied pool/advisory regions where appropriate rather than allocating every region independently. Failure destroys partially created resources; success returns an arena reference and requested pool references.

skmem_arena_create_finalize() collects only regions with SKR_MODE_MMAPOK in region-ID order into IOSKArenaCreate() and marks the arena active. Kernel-only metadata is omitted from the task layout. skmem_arena_mmap() checks active/nondefunct state, creates an IOSK mapper for the process task, records its address/size, retains the arena and links the mapping instance. Failed creation unwinds without publishing an apparently usable ami_arena.

The local IOKit code supplies the actual VM operations:

  1. IOSKMapper::initWithTask() creates per-region mapper objects with cumulative region offsets and reserves the arena's task virtual range with VM_PROT_NONE and VM_INHERIT_NONE.
  2. IOSKArena::createMapperForTask() attaches the mapper, then asks existing regions to refresh their mappings. Empty/unbacked regions stay unavailable.
  3. IOSKRegionMapper::map() maps a present segment descriptor at the reserved address with kIOMapOverwrite, adding kIOMapReadOnly when user writes are forbidden.
  4. Later region buffer insertion/removal updates all applicable arena mappers. Redirected mappers are skipped for normal backing-map installation.

iokit/bsddev/skywalk/IOSkywalkSupport.cpp:387–400:

	if (buffer->fSpec.user_writable == FALSE) {
		options |= kIOMapReadOnly;
	}

	offset = fRegionOffset + (segIndex * fRegion->getSegmentSize());
	assert((offset + fRegion->getSegmentSize()) <= fMapper->fMapSize);
	addr = fMapper->fMapAddr + offset;

	map = buffer->createMappingInTask(fMapper->fTask, addr, options);
	fMemoryMaps[segIndex] = map;
	assert((map == NULL) || (map->getLength() == fRegion->getSegmentSize()));
	if (map == NULL) {
		ret = kIOReturnVMError;
	}

For an illustrative arena with 16 KiB pages, a region beginning at arena offset 0x10000, segment size 0x10000 and segment index 2, that segment begins at task_arena_base + 0x30000. An object at byte offset 0x800 within it appears at task_arena_base + 0x30800. These numbers illustrate the code's arithmetic; production offsets come from the configured region layout. Neither this task virtual address nor the corresponding kernel virtual address is a physical address or IOVA.

IOSKMemoryBuffer::initWithSpec() translates direction, pageability/purgeability, cache and physical-contiguity flags into IOKit buffer options. It distinguishes pure-data allocation from control allocation that may hold pointers. For kernel read-only memory it creates backing with TASK_NULL, then makes a secondary read-only kernel mapping and obtains the CPU pointer from that mapping. This concrete branch prevents accidentally assuming the object's original buffer pointer is always the address exposed to a kernel caller.

Redirecting a mapping revokes backing without immediately deleting its address range

skmem_arena_mredirect() operates on one mapper instance. Through IOSKArena::redirectMap(), IOSKRegion::updateMappingsForArena() skips no-redirect regions and overwrites eligible populated segments using anonymous backing with the corresponding read/write permissions. IOSKRegionMapper::mapOverwrite() performs the fixed-address VM overwrite; the old IOMemoryMap is then released. The address can remain mapped but no longer refers to the live packet/ring backing.

Only after every current mapper is redirected does the arena clear ARF_ACTIVE and request defunct processing. If one active mapper remains, the arena must still support it. Unmapping that final active mapper can later make all remaining mappings redirected and trigger deferred defunct work.

bsd/skywalk/mem/skmem_arena.c:1692–1701:

		/*
		 * Mark the arena as inactive if all mapper instances are
		 * redirected; otherwise, we do this later during unmap.
		 * Once inactive, the arena will not allow further mmap,
		 * and it is ready to be defunct later.
		 */
		if (++ar->ar_maprdrcnt == ar->ar_mapcnt) {
			ar->ar_flags &= ~ARF_ACTIVE;
			*need_defunct = TRUE;
		}

skmem_arena_defunct() rejects an already-defunct arena or an active one (EALREADY/EBUSY), reaps caches and tears down eligible resources, then sets ARF_DEFUNCT. Some kernel slot-descriptor regions can survive defunct while arn_ksd_nodefunct indicates an external nexus owner still refers to them. See nexus teardown.

Skywalk memory: regions, caches, arenas and task mappings diagram
Skywalk memory: regions, caches, arenas and task mappings diagram · Open SVG
Mermaid source
flowchart TD
    A[Active arena and task mappings] -->|redirect one mapper| R[That mapper uses anonymous replacement backing]
    R --> C{Active mappings remain?}
    C -->|yes| W[Arena stays active for those mappings]
    C -->|no| I[Inactive arena]
    W -->|redirect or unmap final active mapper| I
    I -->|purge eligible caches and regions| D[Defunct arena]
    D -->|close releases mapper and references| F[Final arena destruction when references drain]

skmem_arena_munmap_common() destroys the mapper, updates map/redirect counts, removes mapping-list membership and releases its arena reference. It requires the caller to hold an additional arena reference while doing this. The channel-specific wrapper invokes domain-provider defunct finalization when unmap makes deferred teardown possible. IOSKMapper::free() finally releases submaps/arena retention and deallocates its reserved task range.

Completion boundary and remaining surfaces

The local memory layer and IOSK mapping implementation establish permissions, backing replacement, references and segment callback ordering. Device-specific IOMMU programming, firmware ownership and completion must be followed in the actual provider; they cannot be inferred from the existence of a pool or arena. The DMA preparation guide explains descriptors and mappings at that next boundary.

This nexus path does not exhaust NECP/system arena variants, every region geometry, memory-tag implementation or all allocator self-tests. It provides the memory foundation for packet metadata, rings, netif queues and provider teardown. No live mapping/protection or allocator stress test was run.

Search the handbook

Search every chapter, interface and code excerpt.

    Diagram

    Source preview

    Open full file