code:
/*
 * Copyright (c) 2017 Richard Braun.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 * XXX Until more hardware is supported, the model used in this implementation
 * remains a very simple one, where interrupt controllers are global to the
 * system, and each interrupt is targeted at a single processor.
 *
 * Shared interrupts are supported.
 */

#include <errno.h>
#include <stdalign.h>
#include <stdbool.h>
#include <stddef.h>

#include <kern/atomic.h>
#include <kern/kmem.h>
#include <kern/init.h>
#include <kern/intr.h>
#include <kern/list.h>
#include <kern/log.h>
#include <kern/macros.h>
#include <kern/panic.h>
#include <kern/spinlock.h>
#include <kern/thread.h>
#include <machine/boot.h>
#include <machine/cpu.h>

struct intr_handler {
    alignas(CPU_L1_SIZE) struct list node;
    intr_handler_fn_t fn;
    void *arg;
};

/*
 * Interrupt controller.
 *
 * All members are currently read-only once all controllers have been
 * registered.
 */
struct intr_ctl {
    struct list node;
    const struct intr_ops *ops;
    void *priv;
    unsigned int first_intr;
    unsigned int last_intr;
};

/*
 * Interrupt table entry, one per vector.
 *
 * Interrupts must be disabled when accessing an entry.
 *
 * Each interrupt can be routed to one processor at most. Make each entry
 * span a cache line to avoid false sharing.
 */
struct intr_entry {
    alignas(CPU_L1_SIZE) struct spinlock lock;
    struct intr_ctl *ctl;
    unsigned int cpu;
    struct list handlers;
};

/*
 * Interrupt table.
 */
static struct intr_entry intr_table[CPU_INTR_TABLE_SIZE];

/*
 * List of registered controllers.
 */
static struct list intr_ctls;

static struct kmem_cache intr_handler_cache;

static unsigned int
intr_select_cpu(void)
{
    /*
     * TODO Interrupt routing.
     * Although the interface supports it, there are currently problems
     * with the I/O APIC that need to be solved first.
     */
    return 0;
}

static int
intr_handler_create(struct intr_handler **handlerp,
                    intr_handler_fn_t fn, void *arg)
{
    struct intr_handler *handler;

    handler = kmem_cache_alloc(&intr_handler_cache);

    if (handler == NULL) {
        return ENOMEM;
    }

    handler->fn = fn;
    handler->arg = arg;
    *handlerp = handler;
    return 0;
}

static void
intr_handler_destroy(struct intr_handler *handler)
{
    kmem_cache_free(&intr_handler_cache, handler);
}

static bool
intr_handler_match(const struct intr_handler *handler, intr_handler_fn_t fn)
{
    return handler->fn == fn;
}

static int
intr_handler_run(struct intr_handler *handler)
{
    return handler->fn(handler->arg);
}

static struct intr_ctl * __init
intr_ctl_create(const struct intr_ops *ops, void *priv,
                unsigned int first_intr, unsigned int last_intr)
{
    struct intr_ctl *ctl;

    assert(ops != NULL);
    assert(first_intr < last_intr);

    ctl = kmem_alloc(sizeof(*ctl));

    if (ctl == NULL) {
        panic("intr: unable to allocate memory for controller");
    }

    ctl->ops = ops;
    ctl->priv = priv;
    ctl->first_intr = first_intr;
    ctl->last_intr = last_intr;
    return ctl;
}

static bool
intr_ctl_has_intr(const struct intr_ctl *ctl, unsigned int intr)
{
    return ((intr >= ctl->first_intr) && (intr <= ctl->last_intr));
}

static void
intr_ctl_enable(struct intr_ctl *ctl, unsigned int intr, unsigned int cpu)
{
    ctl->ops->enable(ctl->priv, intr, cpu);
}

static void
intr_ctl_disable(struct intr_ctl *ctl, unsigned int intr)
{
    ctl->ops->disable(ctl->priv, intr);
}

static void
intr_ctl_eoi(struct intr_ctl *ctl, unsigned int intr)
{
    ctl->ops->eoi(ctl->priv, intr);
}

static struct intr_ctl *
intr_lookup_ctl(unsigned int intr)
{
    struct intr_ctl *ctl;

    list_for_each_entry(&intr_ctls, ctl, node) {
        if (intr_ctl_has_intr(ctl, intr)) {
            return ctl;
        }
    }

    return NULL;
}

static void __init
intr_entry_init(struct intr_entry *entry)
{
    spinlock_init(&entry->lock);
    list_init(&entry->handlers);
}

static bool
intr_entry_empty(const struct intr_entry *entry)
{
    return list_empty(&entry->handlers);
}

static unsigned int
intr_entry_get_intr(const struct intr_entry *entry)
{
    size_t id;

    id = entry - intr_table;
    assert(id < ARRAY_SIZE(intr_table));
    return id;
}

static void
intr_entry_enable(struct intr_entry *entry, struct intr_ctl *ctl, unsigned int cpu)
{
    entry->ctl = ctl;
    entry->cpu = cpu;
    intr_ctl_enable(entry->ctl, intr_entry_get_intr(entry), cpu);
}

static void
intr_entry_disable(struct intr_entry *entry)
{
    intr_ctl_disable(entry->ctl, intr_entry_get_intr(entry));
}

static struct intr_handler *
intr_entry_lookup_handler(const struct intr_entry *entry, intr_handler_fn_t fn)
{
    struct intr_handler *handler;

    list_for_each_entry(&entry->handlers, handler, node) {
        if (intr_handler_match(handler, fn)) {
            return handler;
        }
    }

    return NULL;
}

static int
intr_entry_add(struct intr_entry *entry, struct intr_handler *handler)
{
    struct intr_ctl *ctl;
    unsigned long flags;
    unsigned int cpu;
    int error;

    spinlock_lock_intr_save(&entry->lock, &flags);

    if (intr_entry_empty(entry)) {
        ctl = intr_lookup_ctl(intr_entry_get_intr(entry));

        if (ctl == NULL) {
            error = ENODEV;
            goto out;
        }

        cpu = intr_select_cpu();
        intr_entry_enable(entry, ctl, cpu);
    }

    list_insert_tail(&entry->handlers, &handler->node);
    error = 0;

out:
    spinlock_unlock_intr_restore(&entry->lock, flags);

    return error;
}

static struct intr_handler *
intr_entry_remove(struct intr_entry *entry, intr_handler_fn_t fn)
{
    struct intr_handler *handler;
    unsigned long flags;

    spinlock_lock_intr_save(&entry->lock, &flags);

    handler = intr_entry_lookup_handler(entry, fn);

    if (handler == NULL) {
        goto out;
    }

    list_remove(&handler->node);

    if (intr_entry_empty(entry)) {
        intr_entry_disable(entry);
    }

out:
    spinlock_unlock_intr_restore(&entry->lock, flags);

    return handler;
}

static void
intr_entry_eoi(struct intr_entry *entry, unsigned int intr)
{
    assert(entry->ctl != NULL);
    intr_ctl_eoi(entry->ctl, intr);
}

static struct intr_entry *
intr_get_entry(unsigned int intr)
{
    assert(intr < ARRAY_SIZE(intr_table));
    return &intr_table[intr];
}

static int __init
intr_bootstrap(void)
{
    unsigned int i;

    list_init(&intr_ctls);
    kmem_cache_init(&intr_handler_cache, "intr_handler",
                    sizeof(struct intr_handler), alignof(struct intr_handler),
                    NULL, 0);

    for (i = 0; i < ARRAY_SIZE(intr_table); i++) {
        intr_entry_init(intr_get_entry(i));
    }

    return 0;
}

INIT_OP_DEFINE(intr_bootstrap,
               INIT_OP_DEP(kmem_setup, true),
               INIT_OP_DEP(log_setup, true),
               INIT_OP_DEP(spinlock_setup, true),
               INIT_OP_DEP(thread_bootstrap, true));

static int __init
intr_setup(void)
{
    return 0;
}

INIT_OP_DEFINE(intr_setup,
               INIT_OP_DEP(boot_setup_intr, true),
               INIT_OP_DEP(intr_bootstrap, true));

static void __init
intr_check_range(unsigned int first_intr, unsigned int last_intr)
{
    struct intr_ctl *ctl;
    unsigned int i;

    list_for_each_entry(&intr_ctls, ctl, node) {
        for (i = first_intr; i <= last_intr; i++) {
            if (intr_ctl_has_intr(ctl, i)) {
                panic("intr: controller range conflict");
            }
        }
    }
}

void __init
intr_register_ctl(const struct intr_ops *ops, void *priv,
                  unsigned int first_intr, unsigned int last_intr)
{
    struct intr_ctl *ctl;

    ctl = intr_ctl_create(ops, priv, first_intr, last_intr);
    intr_check_range(first_intr, last_intr);
    list_insert_tail(&intr_ctls, &ctl->node);
}

int
intr_register(unsigned int intr, intr_handler_fn_t fn, void *arg)
{
    struct intr_handler *handler;
    int error;

    error = intr_handler_create(&handler, fn, arg);

    if (error) {
        return error;
    }

    error = intr_entry_add(intr_get_entry(intr), handler);

    if (error) {
        goto error;
    }

    return 0;

error:
    intr_handler_destroy(handler);
    return error;
}

void
intr_unregister(unsigned int intr, intr_handler_fn_t fn)
{
    struct intr_handler *handler;

    handler = intr_entry_remove(intr_get_entry(intr), fn);

    if (handler == NULL) {
        log_warning("intr: attempting to unregister unknown handler");
        return;
    }

    intr_handler_destroy(handler);
}

void
intr_handle(unsigned int intr)
{
    struct intr_handler *handler;
    struct intr_entry *entry;
    int error;

    assert(thread_check_intr_context());

    entry = intr_get_entry(intr);

    spinlock_lock(&entry->lock);

    if (intr_entry_empty(entry)) {
        log_warning("intr: spurious interrupt %u", intr);
        goto out;
    }

    list_for_each_entry(&entry->handlers, handler, node) {
        error = intr_handler_run(handler);

        if (!error) {
            break;
        }
    }

    intr_entry_eoi(entry, intr);

out:
    spinlock_unlock(&entry->lock);
}


code:
/*
 * Copyright (c) 2017 Richard Braun.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 * Machine-independent interrupt management.
 */

#ifndef KERN_INTR_H
#define KERN_INTR_H

#include <kern/init.h>

/*
 * Type for interrupt handler functions.
 *
 * Return codes :
 *  - 0             Interrupt successfully handled
 *  - EAGAIN        Spurious interrupt
 */
typedef int (*intr_handler_fn_t)(void *arg);

/*
 * Operations of an interrupt controller.
 *
 * Operations for interrupts targeting the same processor are serialized.
 */
struct intr_ops {
    void (*enable)(void *priv, unsigned int intr, unsigned int cpu);
    void (*disable)(void *priv, unsigned int intr);
    void (*eoi)(void *priv, unsigned int intr);
};

/*
 * Register an interrupt controller.
 *
 * This function isn't thread-safe and can only be called during system
 * initialization.
 */
void intr_register_ctl(const struct intr_ops *ops, void *priv,
                       unsigned int first_intr, unsigned int last_intr);

/*
 * Register/unregister an interrupt handler.
 */
int intr_register(unsigned int intr, intr_handler_fn_t fn, void *arg);
void intr_unregister(unsigned int intr, intr_handler_fn_t fn);

/*
 * Handle an interrupt.
 */
void intr_handle(unsigned int intr);

/*
 * This init operation provides :
 *  - registration of interrupt controllers and handlers
 */
INIT_OP_DECLARE(intr_bootstrap);

/*
 * This init operation provides :
 *  - all interrupt controllers have been registered
 *  - module fully initialized
 */
INIT_OP_DECLARE(intr_setup);

#endif /* KERN_INTR_H */

license: mit


code:
/*
 * Copyright (c) 2011-2017 Richard Braun.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <kern/init.h>
#include <kern/kernel.h>
#include <kern/thread.h>
#include <machine/cpu.h>
#include <test/test.h>
#include <vm/vm_page.h>

void __init
kernel_main(void)
{
    assert(!cpu_intr_enabled());

    init_setup();
    vm_page_log_info();

#ifdef CONFIG_TEST_MODULE
    test_setup();
#endif /* CONFIG_TEST_MODULE */

    /*
     * Enabling application processors is done late in the boot process for
     * two reasons :
     *  - It's much simpler to bootstrap with interrupts disabled on all
     *    processors, enabling them only when necessary on the BSP.
     *  - Depending on the architecture, the pmap module could create per
     *    processor page tables. Once done, keeping the kernel page tables
     *    synchronized requires interrupts (and potentially scheduling)
     *    enabled on all processors.
     *
     * It is highly recommended not to do anything else than starting the
     * scheduler right after this call.
     */
    cpu_mp_setup();

    thread_run_scheduler();

    /* Never reached */
}

void __init
kernel_ap_main(void)
{
    assert(!cpu_intr_enabled());

    thread_run_scheduler();

    /* Never reached */
}


code:
/*
 * Copyright (c) 2010, 2012, 2014 Richard Braun.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef KERN_KERNEL_H
#define KERN_KERNEL_H

#include <stdnoreturn.h>

/*
 * Kernel properties.
 */
#define KERNEL_NAME     "x15"
#define KERNEL_VERSION  CONFIG_KERNEL_VERSION

/*
 * Machine-independent entry point.
 *
 * Interrupts must be disabled when calling this function.
 */
noreturn void kernel_main(void);

/*
 * Entry point for APs.
 *
 * Interrupts must be disabled when calling this function.
 */
noreturn void kernel_ap_main(void);

#endif /* KERN_KERNEL_H */


code:
/*
 * Copyright (c) 2010-2018 Richard Braun.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 * This allocator is based on the paper "The Slab Allocator: An Object-Caching
 * Kernel Memory Allocator" by Jeff Bonwick.
 *
 * It allows the allocation of objects (i.e. fixed-size typed buffers) from
 * caches and is efficient in both space and time. This implementation follows
 * many of the indications from the paper mentioned. The most notable
 * differences are outlined below.
 *
 * The per-cache self-scaling hash table for buffer-to-bufctl conversion,
 * described in 3.2.3 "Slab Layout for Large Objects", has been replaced with
 * a constant time buffer-to-slab lookup that relies on the VM system.
 *
 * Slabs are allocated from the physical page allocator if they're page-sized,
 * and from kernel virtual memory if they're bigger, in order to prevent
 * physical memory fragmentation from making slab allocations fail.
 *
 * This implementation uses per-CPU pools of objects, which service most
 * allocation requests. These pools act as caches (but are named differently
 * to avoid confusion with CPU caches) that reduce contention on multiprocessor
 * systems. When a pool is empty and cannot provide an object, it is filled by
 * transferring multiple objects from the slab layer. The symmetric case is
 * handled likewise.
 *
 * TODO Rework the CPU pool layer to use the SLQB algorithm by Nick Piggin.
 */

#include <assert.h>
#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include <kern/init.h>
#include <kern/list.h>
#include <kern/log2.h>
#include <kern/kmem.h>
#include <kern/kmem_i.h>
#include <kern/macros.h>
#include <kern/mutex.h>
#include <kern/panic.h>
#include <kern/shell.h>
#include <kern/thread.h>
#include <machine/cpu.h>
#include <machine/page.h>
#include <machine/pmap.h>
#include <vm/vm_kmem.h>
#include <vm/vm_page.h>

/*
 * Minimum required alignment.
 */
#define KMEM_ALIGN_MIN 8

/*
 * Minimum number of buffers per slab.
 *
 * This value is ignored when the slab size exceeds a threshold.
 */
#define KMEM_MIN_BUFS_PER_SLAB 8

/*
 * Special slab size beyond which the minimum number of buffers per slab is
 * ignored when computing the slab size of a cache.
 */
#define KMEM_SLAB_SIZE_THRESHOLD (8 * PAGE_SIZE)

/*
 * Special buffer size under which slab data is unconditionally allocated
 * from its associated slab.
 */
#define KMEM_BUF_SIZE_THRESHOLD (PAGE_SIZE / 8)

/*
 * The transfer size of a CPU pool is computed by dividing the pool size by
 * this value.
 */
#define KMEM_CPU_POOL_TRANSFER_RATIO 2

/*
 * Logarithm of the size of the smallest general cache.
 */
#define KMEM_CACHES_FIRST_ORDER 5

/*
 * Number of caches backing general purpose allocations.
 */
#define KMEM_NR_MEM_CACHES 13

/*
 * Options for kmem_cache_alloc_verify().
 */
#define KMEM_AV_NOCONSTRUCT 0
#define KMEM_AV_CONSTRUCT   1

/*
 * Error codes for kmem_cache_error().
 */
#define KMEM_ERR_INVALID    0   /* Invalid address being freed */
#define KMEM_ERR_DOUBLEFREE 1   /* Freeing already free address */
#define KMEM_ERR_BUFTAG     2   /* Invalid buftag content */
#define KMEM_ERR_MODIFIED   3   /* Buffer modified while free */
#define KMEM_ERR_REDZONE    4   /* Redzone violation */

#ifdef KMEM_USE_CPU_LAYER

/*
 * Available CPU pool types.
 *
 * For each entry, the CPU pool size applies from the entry buf_size
 * (excluded) up to (and including) the buf_size of the preceding entry.
 *
 * See struct kmem_cpu_pool_type for a description of the values.
 */
static struct kmem_cpu_pool_type kmem_cpu_pool_types[] __read_mostly = {
    {  32768,   1, 0,           NULL },
    {   4096,   8, CPU_L1_SIZE, NULL },
    {    256,  64, CPU_L1_SIZE, NULL },
    {      0, 128, CPU_L1_SIZE, NULL }
};

/*
 * Caches where CPU pool arrays are allocated from.
 */
static struct kmem_cache kmem_cpu_array_caches[ARRAY_SIZE(kmem_cpu_pool_types)];

#endif /* KMEM_USE_CPU_LAYER */

/*
 * Cache for off slab data.
 */
static struct kmem_cache kmem_slab_cache;

/*
 * General caches array.
 */
static struct kmem_cache kmem_caches[KMEM_NR_MEM_CACHES];

/*
 * List of all caches managed by the allocator.
 */
static struct list kmem_cache_list;
static struct mutex kmem_cache_list_lock;

static void kmem_cache_error(struct kmem_cache *cache, void *buf, int error,
                             void *arg);
static void * kmem_cache_alloc_from_slab(struct kmem_cache *cache);
static void kmem_cache_free_to_slab(struct kmem_cache *cache, void *buf);

static void *
kmem_buf_verify_bytes(void *buf, void *pattern, size_t size)
{
    char *ptr, *pattern_ptr, *end;

    end = buf + size;

    for (ptr = buf, pattern_ptr = pattern; ptr < end; ptr++, pattern_ptr++) {
        if (*ptr != *pattern_ptr) {
            return ptr;
        }
    }

    return NULL;
}

static void
kmem_buf_fill(void *buf, uint64_t pattern, size_t size)
{
    uint64_t *ptr, *end;

    assert(P2ALIGNED((uintptr_t)buf, sizeof(uint64_t)));
    assert(P2ALIGNED(size, sizeof(uint64_t)));

    end = buf + size;

    for (ptr = buf; ptr < end; ptr++) {
        *ptr = pattern;
    }
}

static void *
kmem_buf_verify_fill(void *buf, uint64_t old, uint64_t new, size_t size)
{
    uint64_t *ptr, *end;

    assert(P2ALIGNED((uintptr_t)buf, sizeof(uint64_t)));
    assert(P2ALIGNED(size, sizeof(uint64_t)));

    end = buf + size;

    for (ptr = buf; ptr < end; ptr++) {
        if (*ptr != old) {
            return kmem_buf_verify_bytes(ptr, &old, sizeof(old));
        }

        *ptr = new;
    }

    return NULL;
}

static inline union kmem_bufctl *
kmem_buf_to_bufctl(void *buf, struct kmem_cache *cache)
{
    return (union kmem_bufctl *)(buf + cache->bufctl_dist);
}

static inline struct kmem_buftag *
kmem_buf_to_buftag(void *buf, struct kmem_cache *cache)
{
    return (struct kmem_buftag *)(buf + cache->buftag_dist);
}

static inline void *
kmem_bufctl_to_buf(union kmem_bufctl *bufctl, struct kmem_cache *cache)
{
    return (void *)bufctl - cache->bufctl_dist;
}

static inline bool
kmem_pagealloc_is_virtual(size_t size)
{
    return (size > PAGE_SIZE);
}

static void *
kmem_pagealloc(size_t size)
{
    if (kmem_pagealloc_is_virtual(size)) {
        return vm_kmem_alloc(size);
    } else {
        struct vm_page *page;

        page = vm_page_alloc(vm_page_order(size), VM_PAGE_SEL_DIRECTMAP,
                             VM_PAGE_KMEM);

        if (page == NULL) {
            return NULL;
        }

        return vm_page_direct_ptr(page);
    }
}

static void
kmem_pagefree(void *ptr, size_t size)
{
    if (kmem_pagealloc_is_virtual(size)) {
        vm_kmem_free(ptr, size);
    } else {
        struct vm_page *page;

        page = vm_page_lookup(vm_page_direct_pa((uintptr_t)ptr));
        assert(page != NULL);
        vm_page_free(page, vm_page_order(size));
    }
}

static void
kmem_slab_create_verify(struct kmem_slab *slab, struct kmem_cache *cache)
{
    struct kmem_buftag *buftag;
    unsigned long buffers;
    size_t buf_size;
    void *buf;

    buf_size = cache->buf_size;
    buf = slab->addr;
    buftag = kmem_buf_to_buftag(buf, cache);

    for (buffers = cache->bufs_per_slab; buffers != 0; buffers--) {
        kmem_buf_fill(buf, KMEM_FREE_PATTERN, cache->bufctl_dist);
        buftag->state = KMEM_BUFTAG_FREE;
        buf += buf_size;
        buftag = kmem_buf_to_buftag(buf, cache);
    }
}

/*
 * Create an empty slab for a cache.
 *
 * The caller must drop all locks before calling this function.
 */
static struct kmem_slab *
kmem_slab_create(struct kmem_cache *cache, size_t color)
{
    struct kmem_slab *slab;
    union kmem_bufctl *bufctl;
    size_t buf_size;
    unsigned long buffers;
    void *slab_buf;

    slab_buf = kmem_pagealloc(cache->slab_size);

    if (slab_buf == NULL) {
        return NULL;
    }

    if (cache->flags & KMEM_CF_SLAB_EXTERNAL) {
        slab = kmem_cache_alloc(&kmem_slab_cache);

        if (slab == NULL) {
            kmem_pagefree(slab_buf, cache->slab_size);
            return NULL;
        }
    } else {
        slab = (struct kmem_slab *)(slab_buf + cache->slab_size) - 1;
    }

    list_node_init(&slab->node);
    slab->nr_refs = 0;
    slab->first_free = NULL;
    slab->addr = slab_buf + color;

    buf_size = cache->buf_size;
    bufctl = kmem_buf_to_bufctl(slab->addr, cache);

    for (buffers = cache->bufs_per_slab; buffers != 0; buffers--) {
        bufctl->next = slab->first_free;
        slab->first_free = bufctl;
        bufctl = (union kmem_bufctl *)((void *)bufctl + buf_size);
    }

    if (cache->flags & KMEM_CF_VERIFY) {
        kmem_slab_create_verify(slab, cache);
    }

    return slab;
}

static inline uintptr_t
kmem_slab_buf(const struct kmem_slab *slab)
{
    return P2ALIGN((uintptr_t)slab->addr, PAGE_SIZE);
}

#ifdef KMEM_USE_CPU_LAYER

static void
kmem_cpu_pool_init(struct kmem_cpu_pool *cpu_pool, struct kmem_cache *cache)
{
    mutex_init(&cpu_pool->lock);
    cpu_pool->flags = cache->flags;
    cpu_pool->size = 0;
    cpu_pool->transfer_size = 0;
    cpu_pool->nr_objs = 0;
    cpu_pool->array = NULL;
}

static inline struct kmem_cpu_pool *
kmem_cpu_pool_get(struct kmem_cache *cache)
{
    return &cache->cpu_pools[cpu_id()];
}

static inline void
kmem_cpu_pool_build(struct kmem_cpu_pool *cpu_pool, struct kmem_cache *cache,
                    void **array)
{
    cpu_pool->size = cache->cpu_pool_type->array_size;
    cpu_pool->transfer_size = (cpu_pool->size
                               + KMEM_CPU_POOL_TRANSFER_RATIO - 1)
                              / KMEM_CPU_POOL_TRANSFER_RATIO;
    cpu_pool->array = array;
}

static inline void *
kmem_cpu_pool_pop(struct kmem_cpu_pool *cpu_pool)
{
    cpu_pool->nr_objs--;
    return cpu_pool->array[cpu_pool->nr_objs];
}

static inline void
kmem_cpu_pool_push(struct kmem_cpu_pool *cpu_pool, void *obj)
{
    cpu_pool->array[cpu_pool->nr_objs] = obj;
    cpu_pool->nr_objs++;
}

static int
kmem_cpu_pool_fill(struct kmem_cpu_pool *cpu_pool, struct kmem_cache *cache)
{
    kmem_ctor_fn_t ctor;
    void *buf;
    int i;

    ctor = (cpu_pool->flags & KMEM_CF_VERIFY) ? NULL : cache->ctor;

    mutex_lock(&cache->lock);

    for (i = 0; i < cpu_pool->transfer_size; i++) {
        buf = kmem_cache_alloc_from_slab(cache);

        if (buf == NULL) {
            break;
        }

        if (ctor != NULL) {
            ctor(buf);
        }

        kmem_cpu_pool_push(cpu_pool, buf);
    }

    mutex_unlock(&cache->lock);

    return i;
}

static void
kmem_cpu_pool_drain(struct kmem_cpu_pool *cpu_pool, struct kmem_cache *cache)
{
    void *obj;
    int i;

    mutex_lock(&cache->lock);

    for (i = cpu_pool->transfer_size; i > 0; i--) {
        obj = kmem_cpu_pool_pop(cpu_pool);
        kmem_cache_free_to_slab(cache, obj);
    }

    mutex_unlock(&cache->lock);
}

#endif /* KMEM_USE_CPU_LAYER */

static void
kmem_cache_error(struct kmem_cache *cache, void *buf, int error, void *arg)
{
    struct kmem_buftag *buftag;

    printf("kmem: error: cache: %s, buffer: %p\n", cache->name, buf);

    switch(error) {
    case KMEM_ERR_INVALID:
        panic("kmem: freeing invalid address");
        break;
    case KMEM_ERR_DOUBLEFREE:
        panic("kmem: attempting to free the same address twice");
        break;
    case KMEM_ERR_BUFTAG:
        buftag = arg;
        panic("kmem: invalid buftag content, buftag state: %p",
              (void *)buftag->state);
        break;
    case KMEM_ERR_MODIFIED:
        panic("kmem: free buffer modified, fault address: %p, "
              "offset in buffer: %td", arg, arg - buf);
        break;
    case KMEM_ERR_REDZONE:
        panic("kmem: write beyond end of buffer, fault address: %p, "
              "offset in buffer: %td", arg, arg - buf);
        break;
    default:
        panic("kmem: unknown error");
    }

    /*
     * Never reached.
     */
}

/*
 * Compute properties such as slab size for the given cache.
 *
 * Once the slab size is known, this function sets the related properties
 * (buffers per slab and maximum color). It can also set some KMEM_CF_xxx
 * flags depending on the resulting layout.
 */
static void
kmem_cache_compute_properties(struct kmem_cache *cache, int flags)
{
    size_t size, waste;
    int embed;

    if (cache->buf_size < KMEM_BUF_SIZE_THRESHOLD) {
        flags |= KMEM_CACHE_NOOFFSLAB;
    }

    cache->slab_size = PAGE_SIZE;

    for (;;) {
        if (flags & KMEM_CACHE_NOOFFSLAB) {
            embed = 1;
        } else {
            waste = cache->slab_size % cache->buf_size;
            embed = (sizeof(struct kmem_slab) <= waste);
        }

        size = cache->slab_size;

        if (embed) {
            size -= sizeof(struct kmem_slab);
        }

        if (size >= cache->buf_size) {
            break;
        }

        cache->slab_size += PAGE_SIZE;
    }

    /*
     * A user may force page allocation in order to guarantee that virtual
     * memory isn't used. This is normally done for objects that are used
     * to implement virtual memory and avoid circular dependencies.
     *
     * When forcing the use of direct page allocation, only allow single
     * page allocations in order to completely prevent physical memory
     * fragmentation from making slab allocations fail.
     */
    if ((flags & KMEM_CACHE_PAGE_ONLY) && (cache->slab_size != PAGE_SIZE)) {
        panic("kmem: unable to guarantee page allocation");
    }

    cache->bufs_per_slab = size / cache->buf_size;
    cache->color_max = size % cache->buf_size;

    /*
     * Make sure the first page of a slab buffer can be found from the
     * address of the first object.
     *
     * See kmem_slab_buf().
     */
    if (cache->color_max >= PAGE_SIZE) {
        cache->color_max = 0;
    }

    if (!embed) {
        cache->flags |= KMEM_CF_SLAB_EXTERNAL;
    }
}

void
kmem_cache_init(struct kmem_cache *cache, const char *name, size_t obj_size,
                size_t align, kmem_ctor_fn_t ctor, int flags)
{
    size_t buf_size;

#ifdef CONFIG_KMEM_DEBUG
    cache->flags = KMEM_CF_VERIFY;
#else /* CONFIG_KMEM_DEBUG */
    cache->flags = 0;
#endif /* CONFIG_KMEM_DEBUG */

    if (flags & KMEM_CACHE_VERIFY) {
        cache->flags |= KMEM_CF_VERIFY;
    }

    if (align < KMEM_ALIGN_MIN) {
        align = KMEM_ALIGN_MIN;
    }

    assert(obj_size > 0);
    assert(ISP2(align));
    assert(align < PAGE_SIZE);

    buf_size = P2ROUND(obj_size, align);

    mutex_init(&cache->lock);
    list_node_init(&cache->node);
    list_init(&cache->partial_slabs);
    list_init(&cache->free_slabs);
    cache->obj_size = obj_size;
    cache->align = align;
    cache->buf_size = buf_size;
    cache->bufctl_dist = buf_size - sizeof(union kmem_bufctl);
    cache->color = 0;
    cache->nr_objs = 0;
    cache->nr_bufs = 0;
    cache->nr_slabs = 0;
    cache->nr_free_slabs = 0;
    cache->ctor = ctor;
    strlcpy(cache->name, name, sizeof(cache->name));
    cache->buftag_dist = 0;
    cache->redzone_pad = 0;

    if (cache->flags & KMEM_CF_VERIFY) {
        cache->bufctl_dist = buf_size;
        cache->buftag_dist = cache->bufctl_dist + sizeof(union kmem_bufctl);
        cache->redzone_pad = cache->bufctl_dist - cache->obj_size;
        buf_size += sizeof(union kmem_bufctl) + sizeof(struct kmem_buftag);
        buf_size = P2ROUND(buf_size, align);
        cache->buf_size = buf_size;
    }

    kmem_cache_compute_properties(cache, flags);

#ifdef KMEM_USE_CPU_LAYER
    for (cache->cpu_pool_type = kmem_cpu_pool_types;
         buf_size <= cache->cpu_pool_type->buf_size;
         cache->cpu_pool_type++);

    for (size_t i = 0; i < ARRAY_SIZE(cache->cpu_pools); i++) {
        kmem_cpu_pool_init(&cache->cpu_pools[i], cache);
    }
#endif /* KMEM_USE_CPU_LAYER */

    mutex_lock(&kmem_cache_list_lock);
    list_insert_tail(&kmem_cache_list, &cache->node);
    mutex_unlock(&kmem_cache_list_lock);
}

static inline int
kmem_cache_empty(struct kmem_cache *cache)
{
    return cache->nr_objs == cache->nr_bufs;
}

static struct kmem_slab *
kmem_cache_buf_to_slab(const struct kmem_cache *cache, void *buf)
{
    if ((cache->flags & KMEM_CF_SLAB_EXTERNAL)
        || (cache->slab_size != PAGE_SIZE)) {
        return NULL;
    }

    return (struct kmem_slab *)vm_page_end((uintptr_t)buf) - 1;
}

static inline bool
kmem_cache_registration_required(const struct kmem_cache *cache)
{
    return ((cache->flags & KMEM_CF_SLAB_EXTERNAL)
            || (cache->flags & KMEM_CF_VERIFY)
            || (cache->slab_size != PAGE_SIZE));
}

static void
kmem_cache_register(struct kmem_cache *cache, struct kmem_slab *slab)
{
    struct vm_page *page;
    uintptr_t va, end;
    phys_addr_t pa;
    bool virtual;
    int error;

    assert(kmem_cache_registration_required(cache));
    assert(slab->nr_refs == 0);

    virtual = kmem_pagealloc_is_virtual(cache->slab_size);

    for (va = kmem_slab_buf(slab), end = va + cache->slab_size;
         va < end;
         va += PAGE_SIZE) {
        if (virtual) {
            error = pmap_kextract(va, &pa);
            assert(!error);
        } else {
            pa = vm_page_direct_pa(va);
        }

        page = vm_page_lookup(pa);
        assert(page != NULL);
        assert((virtual && vm_page_type(page) == VM_PAGE_KERNEL)
               || (!virtual && vm_page_type(page) == VM_PAGE_KMEM));
        assert(vm_page_get_priv(page) == NULL);
        vm_page_set_priv(page, slab);
    }
}

static struct kmem_slab *
kmem_cache_lookup(struct kmem_cache *cache, void *buf)
{
    struct kmem_slab *slab;
    struct vm_page *page;
    uintptr_t va;
    phys_addr_t pa;
    bool virtual;
    int error;

    assert(kmem_cache_registration_required(cache));

    virtual = kmem_pagealloc_is_virtual(cache->slab_size);
    va = (uintptr_t)buf;

    if (virtual) {
        error = pmap_kextract(va, &pa);

        if (error) {
            return NULL;
        }
    } else {
        pa = vm_page_direct_pa(va);
    }

    page = vm_page_lookup(pa);

    if (page == NULL) {
        return NULL;
    }

    if ((virtual && (vm_page_type(page) != VM_PAGE_KERNEL))
        || (!virtual && (vm_page_type(page) != VM_PAGE_KMEM))) {
        return NULL;
    }

    slab = vm_page_get_priv(page);
    assert((uintptr_t)buf >= kmem_slab_buf(slab));
    assert((uintptr_t)buf < (kmem_slab_buf(slab) + cache->slab_size));
    return slab;
}

static int
kmem_cache_grow(struct kmem_cache *cache)
{
    struct kmem_slab *slab;
    size_t color;
    int empty;

    mutex_lock(&cache->lock);

    if (!kmem_cache_empty(cache)) {
        mutex_unlock(&cache->lock);
        return 1;
    }

    color = cache->color;
    cache->color += cache->align;

    if (cache->color > cache->color_max) {
        cache->color = 0;
    }

    mutex_unlock(&cache->lock);

    slab = kmem_slab_create(cache, color);

    mutex_lock(&cache->lock);

    if (slab != NULL) {
        list_insert_head(&cache->free_slabs, &slab->node);
        cache->nr_bufs += cache->bufs_per_slab;
        cache->nr_slabs++;
        cache->nr_free_slabs++;

        if (kmem_cache_registration_required(cache)) {
            kmem_cache_register(cache, slab);
        }
    }

    /*
     * Even if our slab creation failed, another thread might have succeeded
     * in growing the cache.
     */
    empty = kmem_cache_empty(cache);

    mutex_unlock(&cache->lock);

    return !empty;
}

/*
 * Allocate a raw (unconstructed) buffer from the slab layer of a cache.
 *
 * The cache must be locked before calling this function.
 */
static void *
kmem_cache_alloc_from_slab(struct kmem_cache *cache)
{
    struct kmem_slab *slab;
    union kmem_bufctl *bufctl;

    if (!list_empty(&cache->partial_slabs)) {
        slab = list_first_entry(&cache->partial_slabs, struct kmem_slab, node);
    } else if (!list_empty(&cache->free_slabs)) {
        slab = list_first_entry(&cache->free_slabs, struct kmem_slab, node);
    } else {
        return NULL;
    }

    bufctl = slab->first_free;
    assert(bufctl != NULL);
    slab->first_free = bufctl->next;
    slab->nr_refs++;
    cache->nr_objs++;

    if (slab->nr_refs == cache->bufs_per_slab) {
        /* The slab has become complete */
        list_remove(&slab->node);

        if (slab->nr_refs == 1) {
            cache->nr_free_slabs--;
        }
    } else if (slab->nr_refs == 1) {
        /*
         * The slab has become partial. Insert the new slab at the end of
         * the list to reduce fragmentation.
         */
        list_remove(&slab->node);
        list_insert_tail(&cache->partial_slabs, &slab->node);
        cache->nr_free_slabs--;
    }

    return kmem_bufctl_to_buf(bufctl, cache);
}

/*
 * Release a buffer to the slab layer of a cache.
 *
 * The cache must be locked before calling this function.
 */
static void
kmem_cache_free_to_slab(struct kmem_cache *cache, void *buf)
{
    struct kmem_slab *slab;
    union kmem_bufctl *bufctl;

    slab = kmem_cache_buf_to_slab(cache, buf);

    if (slab == NULL) {
        slab = kmem_cache_lookup(cache, buf);
        assert(slab != NULL);
    }

    assert(slab->nr_refs >= 1);
    assert(slab->nr_refs <= cache->bufs_per_slab);
    bufctl = kmem_buf_to_bufctl(buf, cache);
    bufctl->next = slab->first_free;
    slab->first_free = bufctl;
    slab->nr_refs--;
    cache->nr_objs--;

    if (slab->nr_refs == 0) {
        /* The slab has become free */

        /* If it was partial, remove it from its list */
        if (cache->bufs_per_slab != 1) {
            list_remove(&slab->node);
        }

        list_insert_head(&cache->free_slabs, &slab->node);
        cache->nr_free_slabs++;
    } else if (slab->nr_refs == (cache->bufs_per_slab - 1)) {
        /* The slab has become partial */
        list_insert_head(&cache->partial_slabs, &slab->node);
    }
}

static void
kmem_cache_alloc_verify(struct kmem_cache *cache, void *buf, int construct)
{
    struct kmem_buftag *buftag;
    union kmem_bufctl *bufctl;
    void *addr;

    buftag = kmem_buf_to_buftag(buf, cache);

    if (buftag->state != KMEM_BUFTAG_FREE) {
        kmem_cache_error(cache, buf, KMEM_ERR_BUFTAG, buftag);
    }

    addr = kmem_buf_verify_fill(buf, KMEM_FREE_PATTERN, KMEM_UNINIT_PATTERN,
                                cache->bufctl_dist);

    if (addr != NULL) {
        kmem_cache_error(cache, buf, KMEM_ERR_MODIFIED, addr);
    }

    addr = buf + cache->obj_size;
    memset(addr, KMEM_REDZONE_BYTE, cache->redzone_pad);

    bufctl = kmem_buf_to_bufctl(buf, cache);
    bufctl->redzone = KMEM_REDZONE_WORD;
    buftag->state = KMEM_BUFTAG_ALLOC;

    if (construct && (cache->ctor != NULL)) {
        cache->ctor(buf);
    }
}

void *
kmem_cache_alloc(struct kmem_cache *cache)
{
    bool filled;
    void *buf;

#ifdef KMEM_USE_CPU_LAYER
    struct kmem_cpu_pool *cpu_pool;

    thread_pin();
    cpu_pool = kmem_cpu_pool_get(cache);

    mutex_lock(&cpu_pool->lock);

fast_alloc:
    if (likely(cpu_pool->nr_objs > 0)) {
        bool verify;

        buf = kmem_cpu_pool_pop(cpu_pool);
        verify = (cpu_pool->flags & KMEM_CF_VERIFY);
        mutex_unlock(&cpu_pool->lock);
        thread_unpin();

        if (verify) {
            kmem_cache_alloc_verify(cache, buf, KMEM_AV_CONSTRUCT);
        }

        return buf;
    }

    if (cpu_pool->array != NULL) {
        filled = kmem_cpu_pool_fill(cpu_pool, cache);

        if (!filled) {
            mutex_unlock(&cpu_pool->lock);
            thread_unpin();

            filled = kmem_cache_grow(cache);

            if (!filled) {
                return NULL;
            }

            thread_pin();
            cpu_pool = kmem_cpu_pool_get(cache);
            mutex_lock(&cpu_pool->lock);
        }

        goto fast_alloc;
    }

    mutex_unlock(&cpu_pool->lock);
    thread_unpin();
#endif /* KMEM_USE_CPU_LAYER */

slab_alloc:
    mutex_lock(&cache->lock);
    buf = kmem_cache_alloc_from_slab(cache);
    mutex_unlock(&cache->lock);

    if (buf == NULL) {
        filled = kmem_cache_grow(cache);

        if (!filled) {
            return NULL;
        }

        goto slab_alloc;
    }

    if (cache->flags & KMEM_CF_VERIFY) {
        kmem_cache_alloc_verify(cache, buf, KMEM_AV_NOCONSTRUCT);
    }

    if (cache->ctor != NULL) {
        cache->ctor(buf);
    }

    return buf;
}

static void
kmem_cache_free_verify(struct kmem_cache *cache, void *buf)
{
    struct kmem_buftag *buftag;
    struct kmem_slab *slab;
    union kmem_bufctl *bufctl;
    unsigned char *redzone_byte;
    uintptr_t slabend;

    slab = kmem_cache_lookup(cache, buf);

    if (slab == NULL) {
        kmem_cache_error(cache, buf, KMEM_ERR_INVALID, NULL);
    }

    slabend = P2ALIGN((uintptr_t)slab->addr + cache->slab_size, PAGE_SIZE);

    if ((uintptr_t)buf >= slabend) {
        kmem_cache_error(cache, buf, KMEM_ERR_INVALID, NULL);
    }

    if ((((uintptr_t)buf - (uintptr_t)slab->addr) % cache->buf_size)
        != 0) {
        kmem_cache_error(cache, buf, KMEM_ERR_INVALID, NULL);
    }

    /*
     * As the buffer address is valid, accessing its buftag is safe.
     */
    buftag = kmem_buf_to_buftag(buf, cache);

    if (buftag->state != KMEM_BUFTAG_ALLOC) {
        if (buftag->state == KMEM_BUFTAG_FREE) {
            kmem_cache_error(cache, buf, KMEM_ERR_DOUBLEFREE, NULL);
        } else {
            kmem_cache_error(cache, buf, KMEM_ERR_BUFTAG, buftag);
        }
    }

    redzone_byte = buf + cache->obj_size;
    bufctl = kmem_buf_to_bufctl(buf, cache);

    while (redzone_byte < (unsigned char *)bufctl) {
        if (*redzone_byte != KMEM_REDZONE_BYTE) {
            kmem_cache_error(cache, buf, KMEM_ERR_REDZONE, redzone_byte);
        }

        redzone_byte++;
    }

    if (bufctl->redzone != KMEM_REDZONE_WORD) {
        unsigned long word;

        word = KMEM_REDZONE_WORD;
        redzone_byte = kmem_buf_verify_bytes(&bufctl->redzone, &word,
                                             sizeof(bufctl->redzone));
        kmem_cache_error(cache, buf, KMEM_ERR_REDZONE, redzone_byte);
    }

    kmem_buf_fill(buf, KMEM_FREE_PATTERN, cache->bufctl_dist);
    buftag->state = KMEM_BUFTAG_FREE;
}

void
kmem_cache_free(struct kmem_cache *cache, void *obj)
{
#ifdef KMEM_USE_CPU_LAYER
    struct kmem_cpu_pool *cpu_pool;
    void **array;

    thread_pin();
    cpu_pool = kmem_cpu_pool_get(cache);

    if (cpu_pool->flags & KMEM_CF_VERIFY) {
        thread_unpin();

        kmem_cache_free_verify(cache, obj);

        thread_pin();
        cpu_pool = kmem_cpu_pool_get(cache);
    }

    mutex_lock(&cpu_pool->lock);

fast_free:
    if (likely(cpu_pool->nr_objs < cpu_pool->size)) {
        kmem_cpu_pool_push(cpu_pool, obj);
        mutex_unlock(&cpu_pool->lock);
        thread_unpin();
        return;
    }

    if (cpu_pool->array != NULL) {
        kmem_cpu_pool_drain(cpu_pool, cache);
        goto fast_free;
    }

    mutex_unlock(&cpu_pool->lock);

    array = kmem_cache_alloc(cache->cpu_pool_type->array_cache);

    if (array != NULL) {
        mutex_lock(&cpu_pool->lock);

        /*
         * Another thread may have built the CPU pool while the lock was
         * dropped.
         */
        if (cpu_pool->array != NULL) {
            mutex_unlock(&cpu_pool->lock);
            thread_unpin();

            kmem_cache_free(cache->cpu_pool_type->array_cache, array);

            thread_pin();
            cpu_pool = kmem_cpu_pool_get(cache);
            mutex_lock(&cpu_pool->lock);
            goto fast_free;
        }

        kmem_cpu_pool_build(cpu_pool, cache, array);
        goto fast_free;
    }

    thread_unpin();
#else /* KMEM_USE_CPU_LAYER */
    if (cache->flags & KMEM_CF_VERIFY) {
        kmem_cache_free_verify(cache, obj);
    }
#endif /* KMEM_USE_CPU_LAYER */

    mutex_lock(&cache->lock);
    kmem_cache_free_to_slab(cache, obj);
    mutex_unlock(&cache->lock);
}

void
kmem_cache_info(struct kmem_cache *cache)
{
    char flags_str[64];

    snprintf(flags_str, sizeof(flags_str), "%s%s",
             (cache->flags & KMEM_CF_SLAB_EXTERNAL) ? " SLAB_EXTERNAL" : "",
             (cache->flags & KMEM_CF_VERIFY) ? " VERIFY" : "");

    mutex_lock(&cache->lock);

    printf("kmem:         flags: 0x%x%s\n"
           "kmem:      obj_size: %zu\n"
           "kmem:         align: %zu\n"
           "kmem:      buf_size: %zu\n"
           "kmem:   bufctl_dist: %zu\n"
           "kmem:     slab_size: %zu\n"
           "kmem:     color_max: %zu\n"
           "kmem: bufs_per_slab: %lu\n"
           "kmem:       nr_objs: %lu\n"
           "kmem:       nr_bufs: %lu\n"
           "kmem:      nr_slabs: %lu\n"
           "kmem: nr_free_slabs: %lu\n"
           "kmem:   buftag_dist: %zu\n"
           "kmem:   redzone_pad: %zu\n",
           cache->flags, flags_str, cache->obj_size,
           cache->align, cache->buf_size, cache->bufctl_dist,
           cache->slab_size, cache->color_max, cache->bufs_per_slab,
           cache->nr_objs, cache->nr_bufs, cache->nr_slabs,
           cache->nr_free_slabs, cache->buftag_dist, cache->redzone_pad);

#ifdef KMEM_USE_CPU_LAYER
    printf("kmem: cpu_pool_size: %d\n", cache->cpu_pool_type->array_size);
#endif /* KMEM_USE_CPU_LAYER */

    mutex_unlock(&cache->lock);
}

#ifdef CONFIG_SHELL

static struct kmem_cache *
kmem_lookup_cache(const char *name)
{
    struct kmem_cache *cache;

    mutex_lock(&kmem_cache_list_lock);

    list_for_each_entry(&kmem_cache_list, cache, node) {
        if (strcmp(cache->name, name) == 0) {
            goto out;
        }
    }

    cache = NULL;

out:
    mutex_unlock(&kmem_cache_list_lock);

    return cache;
}

static void
kmem_shell_info(struct shell *shell, int argc, char **argv)
{
    struct kmem_cache *cache;

    (void)shell;

    if (argc < 2) {
        kmem_info();
    } else {
        cache = kmem_lookup_cache(argv[1]);

        if (cache == NULL) {
            printf("kmem: info: invalid argument\n");
            return;
        }

        kmem_cache_info(cache);
    }
}

static struct shell_cmd kmem_shell_cmds[] = {
    SHELL_CMD_INITIALIZER("kmem_info", kmem_shell_info,
        "kmem_info [<cache_name>]",
        "display information about kernel memory and caches"),
};

static int __init
kmem_setup_shell(void)
{
    SHELL_REGISTER_CMDS(kmem_shell_cmds, shell_get_main_cmd_set());
    return 0;
}

INIT_OP_DEFINE(kmem_setup_shell,
               INIT_OP_DEP(kmem_setup, true),
               INIT_OP_DEP(printf_setup, true),
               INIT_OP_DEP(shell_setup, true),
               INIT_OP_DEP(thread_setup, true));

#endif /* CONFIG_SHELL */

#ifdef KMEM_USE_CPU_LAYER
static void
kmem_bootstrap_cpu(void)
{
    struct kmem_cpu_pool_type *cpu_pool_type;
    char name[KMEM_NAME_SIZE];
    size_t size;

    for (size_t i = 0; i < ARRAY_SIZE(kmem_cpu_pool_types); i++) {
        cpu_pool_type = &kmem_cpu_pool_types[i];
        cpu_pool_type->array_cache = &kmem_cpu_array_caches[i];
        sprintf(name, "kmem_cpu_array_%d", cpu_pool_type->array_size);
        size = sizeof(void *) * cpu_pool_type->array_size;
        kmem_cache_init(cpu_pool_type->array_cache, name, size,
                        cpu_pool_type->array_align, NULL, 0);
    }
}
#endif /* KMEM_USE_CPU_LAYER */

static int __init
kmem_bootstrap(void)
{
    char name[KMEM_NAME_SIZE];
    size_t size;

    /* Make sure a bufctl can always be stored in a buffer */
    assert(sizeof(union kmem_bufctl) <= KMEM_ALIGN_MIN);

    list_init(&kmem_cache_list);
    mutex_init(&kmem_cache_list_lock);

#ifdef KMEM_USE_CPU_LAYER
    kmem_bootstrap_cpu();
#endif /* KMEM_USE_CPU_LAYER */

    /*
     * Prevent off slab data for the slab cache to avoid infinite recursion.
     */
    kmem_cache_init(&kmem_slab_cache, "kmem_slab", sizeof(struct kmem_slab),
                    0, NULL, KMEM_CACHE_NOOFFSLAB);

    size = 1 << KMEM_CACHES_FIRST_ORDER;

    for (size_t i = 0; i < ARRAY_SIZE(kmem_caches); i++) {
        sprintf(name, "kmem_%zu", size);
        kmem_cache_init(&kmem_caches[i], name, size, 0, NULL, 0);
        size <<= 1;
    }

    return 0;
}

INIT_OP_DEFINE(kmem_bootstrap,
               INIT_OP_DEP(thread_bootstrap, true),
               INIT_OP_DEP(vm_page_setup, true));

static int __init
kmem_setup(void)
{
    return 0;
}

INIT_OP_DEFINE(kmem_setup,
               INIT_OP_DEP(kmem_bootstrap, true),
               INIT_OP_DEP(vm_kmem_setup, true));

static inline size_t
kmem_get_index(unsigned long size)
{
    return log2_order(size) - KMEM_CACHES_FIRST_ORDER;
}

static void
kmem_alloc_verify(struct kmem_cache *cache, void *buf, size_t size)
{
    size_t redzone_size;
    void *redzone;

    assert(size <= cache->obj_size);

    redzone = buf + size;
    redzone_size = cache->obj_size - size;
    memset(redzone, KMEM_REDZONE_BYTE, redzone_size);
}

void *
kmem_alloc(size_t size)
{
    size_t index;
    void *buf;

    if (size == 0) {
        return NULL;
    }

    index = kmem_get_index(size);

    if (index < ARRAY_SIZE(kmem_caches)) {
        struct kmem_cache *cache;

        cache = &kmem_caches[index];
        buf = kmem_cache_alloc(cache);

        if ((buf != NULL) && (cache->flags & KMEM_CF_VERIFY)) {
            kmem_alloc_verify(cache, buf, size);
        }
    } else {
        buf = kmem_pagealloc(size);
    }

    return buf;
}

void *
kmem_zalloc(size_t size)
{
    void *ptr;

    ptr = kmem_alloc(size);

    if (ptr == NULL) {
        return NULL;
    }

    memset(ptr, 0, size);
    return ptr;
}

static void
kmem_free_verify(struct kmem_cache *cache, void *buf, size_t size)
{
    unsigned char *redzone_byte, *redzone_end;

    assert(size <= cache->obj_size);

    redzone_byte = buf + size;
    redzone_end = buf + cache->obj_size;

    while (redzone_byte < redzone_end) {
        if (*redzone_byte != KMEM_REDZONE_BYTE) {
            kmem_cache_error(cache, buf, KMEM_ERR_REDZONE, redzone_byte);
        }

        redzone_byte++;
    }
}

void
kmem_free(void *ptr, size_t size)
{
    size_t index;

    if ((ptr == NULL) || (size == 0)) {
        return;
    }

    index = kmem_get_index(size);

    if (index < ARRAY_SIZE(kmem_caches)) {
        struct kmem_cache *cache;

        cache = &kmem_caches[index];

        if (cache->flags & KMEM_CF_VERIFY) {
            kmem_free_verify(cache, ptr, size);
        }

        kmem_cache_free(cache, ptr);
    } else {
        kmem_pagefree(ptr, size);
    }
}

void
kmem_info(void)
{
    size_t total_reclaim, total_reclaim_physical, total_reclaim_virtual;
    size_t total, total_physical, total_virtual;
    size_t mem_usage, mem_reclaim;
    struct kmem_cache *cache;

    total = 0;
    total_physical = 0;
    total_virtual = 0;
    total_reclaim = 0;
    total_reclaim_physical = 0;
    total_reclaim_virtual = 0;

    printf("kmem: cache                  obj slab  bufs   objs   bufs "
           "   total reclaimable\n"
           "kmem: name                  size size /slab  usage  count "
           "  memory      memory\n");

    mutex_lock(&kmem_cache_list_lock);

    list_for_each_entry(&kmem_cache_list, cache, node) {
        mutex_lock(&cache->lock);

        mem_usage = (cache->nr_slabs * cache->slab_size) >> 10;
        mem_reclaim = (cache->nr_free_slabs * cache->slab_size) >> 10;
        total += mem_usage;
        total_reclaim += mem_reclaim;

        if (kmem_pagealloc_is_virtual(cache->slab_size)) {
            total_virtual += mem_usage;
            total_reclaim_virtual += mem_reclaim;
        } else {
            total_physical += mem_usage;
            total_reclaim_physical += mem_reclaim;
        }

        printf("kmem: %-19s %6zu %3zuk  %4lu %6lu %6lu %7zuk %10zuk\n",
               cache->name, cache->obj_size, cache->slab_size >> 10,
               cache->bufs_per_slab, cache->nr_objs, cache->nr_bufs,
               mem_usage, mem_reclaim);

        mutex_unlock(&cache->lock);
    }

    mutex_unlock(&kmem_cache_list_lock);

    printf("total: %zuk (phys: %zuk virt: %zuk), "
           "reclaim: %zuk (phys: %zuk virt: %zuk)\n",
           total, total_physical, total_virtual,
           total_reclaim, total_reclaim_physical, total_reclaim_virtual);
}


code:
/*
 * Copyright (c) 2010-2014 Richard Braun.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 * Object caching and general purpose memory allocator.
 */

#ifndef KERN_KMEM_H
#define KERN_KMEM_H

#include <stddef.h>

#include <kern/init.h>

/*
 * Object cache.
 */
struct kmem_cache;

/*
 * Type for constructor functions.
 *
 * The pre-constructed state of an object is supposed to include only
 * elements such as e.g. linked lists, locks, reference counters. Therefore
 * constructors are expected to 1) never block, 2) never fail and 3) not
 * need any user-provided data. As a result, object construction never
 * performs dynamic resource allocation, which removes the need for
 * destructors.
 */
typedef void (*kmem_ctor_fn_t)(void *);

#include <kern/kmem_i.h>

/*
 * Cache creation flags.
 */
#define KMEM_CACHE_NOOFFSLAB    0x1 /* Don't allocate external slab data */
#define KMEM_CACHE_PAGE_ONLY    0x2 /* Allocate slabs from the page allocator */
#define KMEM_CACHE_VERIFY       0x4 /* Use debugging facilities */

/*
 * Initialize a cache.
 *
 * Slabs may be allocated either from the page allocator or from kernel
 * virtual memory, unless KMEM_CACHE_PAGE_ONLY is set.
 */
void kmem_cache_init(struct kmem_cache *cache, const char *name,
                     size_t obj_size, size_t align, kmem_ctor_fn_t ctor,
                     int flags);

/*
 * Allocate an object from a cache.
 */
void * kmem_cache_alloc(struct kmem_cache *cache);

/*
 * Release an object to its cache.
 */
void kmem_cache_free(struct kmem_cache *cache, void *obj);

/*
 * Display internal cache information.
 *
 * If cache is NULL, this function displays all managed caches.
 */
void kmem_cache_info(struct kmem_cache *cache);

/*
 * Allocate size bytes of uninitialized memory.
 */
void * kmem_alloc(size_t size);

/*
 * Allocate size bytes of zeroed memory.
 */
void * kmem_zalloc(size_t size);

/*
 * Release memory obtained with kmem_alloc() or kmem_zalloc().
 *
 * The size argument must strictly match the value given at allocation time.
 */
void kmem_free(void *ptr, size_t size);

/*
 * Display global kernel memory information.
 */
void kmem_info(void);

/*
 * This init operation provides :
 *  - allocation from caches backed by the page allocator
 */
INIT_OP_DECLARE(kmem_bootstrap);

/*
 * This init operation provides :
 *  - allocation from all caches
 */
INIT_OP_DECLARE(kmem_setup);

#endif /* KERN_KMEM_H */


code:
/*
 * Copyright (c) 2010-2018 Richard Braun.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef KERN_KMEM_I_H
#define KERN_KMEM_I_H

#include <stdalign.h>
#include <stddef.h>

#include <kern/list.h>
#include <kern/mutex.h>
#include <machine/cpu.h>

#if defined(CONFIG_SMP) && !defined(CONFIG_KMEM_NO_CPU_LAYER)
#define KMEM_USE_CPU_LAYER
#endif

#ifdef KMEM_USE_CPU_LAYER

/*
 * Per-processor cache of pre-constructed objects.
 *
 * The flags member is a read-only CPU-local copy of the parent cache flags.
 */
struct kmem_cpu_pool {
    alignas(CPU_L1_SIZE) struct mutex lock;
    int flags;
    int size;
    int transfer_size;
    int nr_objs;
    void **array;
};

/*
 * When a cache is created, its CPU pool type is determined from the buffer
 * size. For small buffer sizes, many objects can be cached in a CPU pool.
 * Conversely, for large buffer sizes, this would incur much overhead, so only
 * a few objects are stored in a CPU pool.
 */
struct kmem_cpu_pool_type {
    size_t buf_size;
    int array_size;
    size_t array_align;
    struct kmem_cache *array_cache;
};

#endif /* KMEM_USE_CPU_LAYER */

/*
 * Buffer descriptor.
 *
 * For normal caches (i.e. without KMEM_CF_VERIFY), bufctls are located at the
 * end of (but inside) each buffer. If KMEM_CF_VERIFY is set, bufctls are
 * located after each buffer.
 *
 * When an object is allocated to a client, its bufctl isn't used. This memory
 * is instead used for redzoning if cache debugging is in effect.
 */
union kmem_bufctl {
    union kmem_bufctl *next;
    unsigned long redzone;
};

/*
 * Redzone guard word.
 */
#ifdef __LP64__
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define KMEM_REDZONE_WORD 0xfeedfacefeedfaceUL
#else /* __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ */
#define KMEM_REDZONE_WORD 0xcefaedfecefaedfeUL
#endif /* __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ */
#else /* __LP64__ */
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define KMEM_REDZONE_WORD 0xfeedfaceUL
#else /* __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ */
#define KMEM_REDZONE_WORD 0xcefaedfeUL
#endif /* __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ */
#endif /* __LP64__ */

/*
 * Redzone byte for padding.
 */
#define KMEM_REDZONE_BYTE 0xbb

/*
 * Buffer tag.
 *
 * This structure is only used for KMEM_CF_VERIFY caches. It is located after
 * the bufctl and includes information about the state of the buffer it
 * describes (allocated or not). It should be thought of as a debugging
 * extension of the bufctl.
 */
struct kmem_buftag {
    unsigned long state;
};

/*
 * Values the buftag state member can take.
 */
#ifdef __LP64__
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define KMEM_BUFTAG_ALLOC   0xa110c8eda110c8edUL
#define KMEM_BUFTAG_FREE    0xf4eeb10cf4eeb10cUL
#else /* __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ */
#define KMEM_BUFTAG_ALLOC   0xedc810a1edc810a1UL
#define KMEM_BUFTAG_FREE    0x0cb1eef40cb1eef4UL
#endif /* __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ */
#else /* __LP64__ */
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define KMEM_BUFTAG_ALLOC   0xa110c8edUL
#define KMEM_BUFTAG_FREE    0xf4eeb10cUL
#else /* __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ */
#define KMEM_BUFTAG_ALLOC   0xedc810a1UL
#define KMEM_BUFTAG_FREE    0x0cb1eef4UL
#endif /* __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ */
#endif /* __LP64__ */

/*
 * Free and uninitialized patterns.
 *
 * These values are unconditionally 64-bit wide since buffers are at least
 * 8-byte aligned.
 */
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define KMEM_FREE_PATTERN   0xdeadbeefdeadbeefULL
#define KMEM_UNINIT_PATTERN 0xbaddcafebaddcafeULL
#else /* __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ */
#define KMEM_FREE_PATTERN   0xefbeaddeefbeaddeULL
#define KMEM_UNINIT_PATTERN 0xfecaddbafecaddbaULL
#endif /* __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ */

/*
 * Page-aligned collection of unconstructed buffers.
 *
 * This structure is either allocated from the slab cache, or, when internal
 * fragmentation allows it, or if forced by the cache creator, from the slab
 * it describes.
 */
struct kmem_slab {
    struct list node;
    unsigned long nr_refs;
    union kmem_bufctl *first_free;
    void *addr;
};

/*
 * Cache name buffer size.
 */
#define KMEM_NAME_SIZE 32

/*
 * Cache flags.
 *
 * The flags don't change once set and can be tested without locking.
 */
#define KMEM_CF_SLAB_EXTERNAL   0x1 /* Slab data is off slab */
#define KMEM_CF_VERIFY          0x2 /* Debugging facilities enabled */

/*
 * Cache of objects.
 *
 * Locking order : cpu_pool -> cache. CPU pools locking is ordered by CPU ID.
 */
struct kmem_cache {
#ifdef KMEM_USE_CPU_LAYER
    /* CPU pool layer */
    struct kmem_cpu_pool cpu_pools[CONFIG_MAX_CPUS];
    struct kmem_cpu_pool_type *cpu_pool_type;
#endif /* KMEM_USE_CPU_LAYER */

    /* Slab layer */
    struct mutex lock;
    struct list node;   /* Cache list linkage */
    struct list partial_slabs;
    struct list free_slabs;
    int flags;
    size_t obj_size;    /* User-provided size */
    size_t align;
    size_t buf_size;    /* Aligned object size */
    size_t bufctl_dist; /* Distance from buffer to bufctl */
    size_t slab_size;
    size_t color;
    size_t color_max;
    unsigned long bufs_per_slab;
    unsigned long nr_objs;  /* Number of allocated objects */
    unsigned long nr_bufs;  /* Total number of buffers */
    unsigned long nr_slabs;
    unsigned long nr_free_slabs;
    kmem_ctor_fn_t ctor;
    char name[KMEM_NAME_SIZE];
    size_t buftag_dist; /* Distance from buffer to buftag */
    size_t redzone_pad; /* Bytes from end of object to redzone word */
};

#endif /* KERN_KMEM_I_H */


code:
/*
 * Copyright (c) 2018 Agustina Arzille.
 * Copyright (c) 2018 Richard Braun.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 * Architecture-specific code may override any of the type-specific
 * functions by defining a macro of the same name.
 */

#include <kern/atomic_types.h>
#include <kern/latomic.h>
#include <kern/latomic_i.h>
#include <kern/macros.h>
#include <machine/cpu.h>
#include <machine/latomic.h>

#define latomic_swap_n(ptr, val)                    \
MACRO_BEGIN                                         \
    unsigned long flags_;                           \
    typeof(val) ret_;                               \
                                                    \
    cpu_intr_save(&flags_);                         \
    ret_ = *(ptr);                                  \
    *(ptr) = (val);                                 \
    cpu_intr_restore(flags_);                       \
                                                    \
    ret_;                                           \
MACRO_END

#define latomic_cas_n(ptr, oval, nval)              \
MACRO_BEGIN                                         \
    unsigned long flags_;                           \
    typeof(oval) ret_;                              \
                                                    \
    cpu_intr_save(&flags_);                         \
                                                    \
    ret_ = *(ptr);                                  \
                                                    \
    if (ret_ == (oval)) {                           \
        *(ptr) = (nval);                            \
    }                                               \
                                                    \
    cpu_intr_restore(flags_);                       \
                                                    \
    ret_;                                           \
MACRO_END

#define latomic_fetch_op_n(ptr, val, op)            \
MACRO_BEGIN                                         \
    unsigned long flags_;                           \
    typeof(val) ret_;                               \
                                                    \
    cpu_intr_save(&flags_);                         \
    ret_ = *(ptr);                                  \
    *(ptr) = ret_ op (val);                         \
    cpu_intr_restore(flags_);                       \
                                                    \
    ret_;                                           \
MACRO_END

#define latomic_fetch_add_n(ptr, val)   latomic_fetch_op_n(ptr, val, +)
#define latomic_fetch_sub_n(ptr, val)   latomic_fetch_op_n(ptr, val, -)
#define latomic_fetch_and_n(ptr, val)   latomic_fetch_op_n(ptr, val, &)
#define latomic_fetch_or_n(ptr, val)    latomic_fetch_op_n(ptr, val, |)
#define latomic_fetch_xor_n(ptr, val)   latomic_fetch_op_n(ptr, val, ^)

/* latomic_load */

#ifndef latomic_load_32
unsigned int
latomic_load_32(union atomic_constptr_32 ptr, int memorder)
{
    return __atomic_load_n(ptr.ui_ptr, memorder);
}
#endif /* latomic_load_32 */

#ifndef latomic_load_64
#ifdef __LP64__
unsigned long long
latomic_load_64(union atomic_constptr_64 ptr, int memorder)
{
    return __atomic_load_n(ptr.ull_ptr, memorder);
}
#else /* __LP64__ */
unsigned long long
latomic_load_64(union atomic_constptr_64 ptr, int memorder)
{
    unsigned long long ret;
    unsigned long flags;

    (void)memorder;

    cpu_intr_save(&flags);
    ret = *ptr.ull_ptr;
    cpu_intr_restore(flags);

    return ret;
}
#endif /* __LP64__ */
#endif /* latomic_load_64 */

/* latomic_store */

#ifndef latomic_store_32
void
latomic_store_32(union atomic_ptr_32 ptr, union atomic_val_32 val, int memorder)
{
    __atomic_store_n(ptr.ui_ptr, val.ui, memorder);
}
#endif /* latomic_store_32 */

#ifndef latomic_store_64
#ifdef __LP64__
void
latomic_store_64(union atomic_ptr_64 ptr, union atomic_val_64 val, int memorder)
{
    return __atomic_store_n(ptr.ull_ptr, val.ull, memorder);
}
#else /* __LP64__ */
void
latomic_store_64(union atomic_ptr_64 ptr, union atomic_val_64 val, int memorder)
{
    unsigned long flags;

    (void)memorder;

    cpu_intr_save(&flags);
    *ptr.ull_ptr = val.ull;
    cpu_intr_restore(flags);
}
#endif /* __LP64__ */
#endif /* latomic_store_64 */

/* latomic_swap */

#ifndef latomic_swap_32
unsigned int
latomic_swap_32(union atomic_ptr_32 ptr, union atomic_val_32 val, int memorder)
{
    (void)memorder;
    return latomic_swap_n(ptr.ui_ptr, val.ui);
}
#endif /* latomic_swap_32 */

#ifndef latomic_swap_64
unsigned long long
latomic_swap_64(union atomic_ptr_64 ptr, union atomic_val_64 val, int memorder)
{
    (void)memorder;
    return latomic_swap_n(ptr.ull_ptr, val.ull);
}
#endif /* latomic_swap_64 */

/* latomic_cas */

#ifndef latomic_cas_32
unsigned int
latomic_cas_32(union atomic_ptr_32 ptr, union atomic_val_32 oval,
               union atomic_val_32 nval, int memorder)
{
    (void)memorder;
    return latomic_cas_n(ptr.ui_ptr, oval.ui, nval.ui);
}
#endif /* latomic_cas_32 */

#ifndef latomic_cas_64
unsigned long long
latomic_cas_64(union atomic_ptr_64 ptr, union atomic_val_64 oval,
               union atomic_val_64 nval, int memorder)
{
    (void)memorder;
    return latomic_cas_n(ptr.ull_ptr, oval.ull, nval.ull);
}
#endif /* latomic_cas_64 */

/* latomic_fetch_add */

#ifndef latomic_fetch_add_32
unsigned int
latomic_fetch_add_32(union atomic_ptr_32 ptr, union atomic_val_32 val,
                     int memorder)
{
    (void)memorder;
    return latomic_fetch_add_n(ptr.ui_ptr, val.ui);
}
#endif /* latomic_fetch_add_32 */

#ifndef latomic_fetch_add_64
unsigned long long
latomic_fetch_add_64(union atomic_ptr_64 ptr, union atomic_val_64 val,
                     int memorder)
{
    (void)memorder;
    return latomic_fetch_add_n(ptr.ull_ptr, val.ull);
}
#endif /* latomic_fetch_add_64 */

/* latomic_fetch_sub */

#ifndef latomic_fetch_sub_32
unsigned int
latomic_fetch_sub_32(union atomic_ptr_32 ptr, union atomic_val_32 val,
                     int memorder)
{
    (void)memorder;
    return latomic_fetch_sub_n(ptr.ui_ptr, val.ui);
}
#endif /* latomic_fetch_sub_32 */

#ifndef latomic_fetch_sub_64
unsigned long long
latomic_fetch_sub_64(union atomic_ptr_64 ptr, union atomic_val_64 val,
                     int memorder)
{
    (void)memorder;
    return latomic_fetch_sub_n(ptr.ull_ptr, val.ull);
}
#endif /* latomic_fetch_sub_64 */

/* latomic_fetch_and */

#ifndef latomic_fetch_and_32
unsigned int
latomic_fetch_and_32(union atomic_ptr_32 ptr, union atomic_val_32 val,
                     int memorder)
{
    (void)memorder;
    return latomic_fetch_and_n(ptr.ui_ptr, val.ui);
}
#endif /* latomic_fetch_and_32 */

#ifndef latomic_fetch_and_64
unsigned long long
latomic_fetch_and_64(union atomic_ptr_64 ptr, union atomic_val_64 val,
                     int memorder)
{
    (void)memorder;
    return latomic_fetch_and_n(ptr.ull_ptr, val.ull);
}
#endif /* latomic_fetch_and_64 */

/* latomic_fetch_or */

#ifndef latomic_fetch_or_32
unsigned int
latomic_fetch_or_32(union atomic_ptr_32 ptr, union atomic_val_32 val,
                    int memorder)
{
    (void)memorder;
    return latomic_fetch_or_n(ptr.ui_ptr, val.ui);
}
#endif /* latomic_fetch_or_32 */

#ifndef latomic_fetch_or_64
unsigned long long
latomic_fetch_or_64(union atomic_ptr_64 ptr, union atomic_val_64 val,
                    int memorder)
{
    (void)memorder;
    return latomic_fetch_or_n(ptr.ull_ptr, val.ull);
}
#endif /* latomic_fetch_or_64 */

/* latomic_fetch_xor */

#ifndef latomic_fetch_xor_32
unsigned int
latomic_fetch_xor_32(union atomic_ptr_32 ptr, union atomic_val_32 val,
                     int memorder)
{
    (void)memorder;
    return latomic_fetch_xor_n(ptr.ui_ptr, val.ui);
}
#endif /* latomic_fetch_xor_32 */

#ifndef latomic_fetch_xor_64
unsigned long long
latomic_fetch_xor_64(union atomic_ptr_64 ptr, union atomic_val_64 val,
                     int memorder)
{
    (void)memorder;
    return latomic_fetch_xor_n(ptr.ull_ptr, val.ull);
}
#endif /* latomic_fetch_xor_64 */

/* latomic_add */

#ifndef latomic_add_32
void
latomic_add_32(union atomic_ptr_32 ptr, union atomic_val_32 val, int memorder)
{
    (void)memorder;
    latomic_fetch_add_n(ptr.ui_ptr, val.ui);
}
#endif /* latomic_add_32 */

#ifndef latomic_add_64
void
latomic_add_64(union atomic_ptr_64 ptr, union atomic_val_64 val, int memorder)
{
    (void)memorder;
    latomic_fetch_add_n(ptr.ull_ptr, val.ull);
}
#endif /* latomic_add_64 */

/* latomic_sub */

#ifndef latomic_sub_32
void
latomic_sub_32(union atomic_ptr_32 ptr, union atomic_val_32 val, int memorder)
{
    (void)memorder;
    latomic_fetch_sub_n(ptr.ui_ptr, val.ui);
}
#endif /* latomic_sub_32 */

#ifndef latomic_sub_64
void
latomic_sub_64(union atomic_ptr_64 ptr, union atomic_val_64 val, int memorder)
{
    (void)memorder;
    latomic_fetch_sub_n(ptr.ull_ptr, val.ull);
}
#endif /* latomic_sub_64 */

/* latomic_and */

#ifndef latomic_and_32
void
latomic_and_32(union atomic_ptr_32 ptr, union atomic_val_32 val, int memorder)
{
    (void)memorder;
    latomic_fetch_and_n(ptr.ui_ptr, val.ui);
}
#endif /* latomic_and_32 */

#ifndef latomic_and_64
void
latomic_and_64(union atomic_ptr_64 ptr, union atomic_val_64 val, int memorder)
{
    (void)memorder;
    latomic_fetch_and_n(ptr.ull_ptr, val.ull);
}
#endif /* latomic_and_64 */

/* latomic_or */

#ifndef latomic_or_32
void
latomic_or_32(union atomic_ptr_32 ptr, union atomic_val_32 val, int memorder)
{
    (void)memorder;
    latomic_fetch_or_n(ptr.ui_ptr, val.ui);
}
#endif /* latomic_or_32 */

#ifndef latomic_or_64
void
latomic_or_64(union atomic_ptr_64 ptr, union atomic_val_64 val, int memorder)
{
    (void)memorder;
    latomic_fetch_or_n(ptr.ull_ptr, val.ull);
}
#endif /* latomic_or_64 */

/* latomic_xor */

#ifndef latomic_xor_32
void
latomic_xor_32(union atomic_ptr_32 ptr, union atomic_val_32 val, int memorder)
{
    (void)memorder;
    latomic_fetch_xor_n(ptr.ui_ptr, val.ui);
}
#endif /* latomic_xor_32 */

#ifndef latomic_xor_64
void
latomic_xor_64(union atomic_ptr_64 ptr, union atomic_val_64 val, int memorder)
{
    (void)memorder;
    latomic_fetch_xor_n(ptr.ull_ptr, val.ull);
}
#endif /* latomic_xor_64 */


code:
/*
 * Copyright (c) 2018 Agustina Arzille.
 * Copyright (c) 2018 Richard Braun.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 * CPU-local atomic operations.
 *
 * The latomic module provides operations that are "atomic on the local
 * processor", i.e. interrupt-safe. Its interface is similar in spirit
 * and purpose to that of the atomic module, and it can transparently
 * replace it on single-processor configurations.
 *
 * Note that the only operations guaranteed to be truely atomic, i.e.
 * to completely execute in a single atomic instruction, are loads and
 * stores. All other operations may be implemented with multiple
 * instructions, possibly disabling interrupts. The rationale is that
 * atomic loads and stores are required for some types of access, such
 * as memory-mapped device registers, while other operations only require
 * interrupt safety.
 *
 * This header provides a generic implementation. Architectures can
 * individually override any of the operations provided by this module.
 */

#ifndef KERN_LATOMIC_H
#define KERN_LATOMIC_H

#include <assert.h>

#include <kern/macros.h>

/*
 * Memory orders for local atomic operations.
 *
 * These work like those in the atomic module, but are implemented
 * with simple compiler barriers instead of full memory fences.
 */
#define LATOMIC_RELAXED __ATOMIC_RELAXED
#define LATOMIC_ACQUIRE __ATOMIC_ACQUIRE
#define LATOMIC_RELEASE __ATOMIC_RELEASE
#define LATOMIC_ACQ_REL __ATOMIC_ACQ_REL
#define LATOMIC_SEQ_CST __ATOMIC_SEQ_CST

#include <kern/latomic_i.h>

#define latomic_load(ptr, memorder)                                         \
MACRO_BEGIN                                                                 \
    assert(latomic_ptr_aligned(ptr));                                       \
    ((typeof(*(ptr)))latomic_select(ptr, load)(ptr, memorder));             \
MACRO_END

#define latomic_store(ptr, val, memorder)                                   \
MACRO_BEGIN                                                                 \
    assert(latomic_ptr_aligned(ptr));                                       \
    latomic_select(ptr, store)(ptr, val, memorder);                         \
MACRO_END

#define latomic_swap(ptr, val, memorder)                                    \
MACRO_BEGIN                                                                 \
    assert(latomic_ptr_aligned(ptr));                                       \
    ((typeof(*(ptr)))latomic_select(ptr, swap)(ptr, val, memorder));        \
MACRO_END

#define latomic_cas(ptr, oval, nval, memorder)                              \
MACRO_BEGIN                                                                 \
    assert(latomic_ptr_aligned(ptr));                                       \
    ((typeof(*(ptr)))latomic_select(ptr, cas)(ptr, oval, nval, memorder));  \
MACRO_END

#define latomic_fetch_add(ptr, val, memorder)                               \
MACRO_BEGIN                                                                 \
    assert(latomic_ptr_aligned(ptr));                                       \
    ((typeof(*(ptr)))latomic_select(ptr, fetch_add)(ptr, val, memorder));   \
MACRO_END

#define latomic_fetch_sub(ptr, val, memorder)                               \
MACRO_BEGIN                                                                 \
    assert(latomic_ptr_aligned(ptr));                                       \
    ((typeof(*(ptr)))latomic_select(ptr, fetch_sub)(ptr, val, memorder));   \
MACRO_END

#define latomic_fetch_and(ptr, val, memorder)                               \
MACRO_BEGIN                                                                 \
    assert(latomic_ptr_aligned(ptr));                                       \
    ((typeof(*(ptr)))latomic_select(ptr, fetch_and)(ptr, val, memorder));   \
MACRO_END

#define latomic_fetch_or(ptr, val, memorder)                                \
MACRO_BEGIN                                                                 \
    assert(latomic_ptr_aligned(ptr));                                       \
    ((typeof(*(ptr)))latomic_select(ptr, fetch_or)(ptr, val, memorder));    \
MACRO_END

#define latomic_fetch_xor(ptr, val, memorder)                               \
MACRO_BEGIN                                                                 \
    assert(latomic_ptr_aligned(ptr));                                       \
    ((typeof(*(ptr)))latomic_select(ptr, fetch_xor)(ptr, val, memorder));   \
MACRO_END

#define latomic_add(ptr, val, memorder)                                     \
MACRO_BEGIN                                                                 \
    assert(latomic_ptr_aligned(ptr));                                       \
    latomic_select(ptr, add)(ptr, val, memorder);                           \
MACRO_END

#define latomic_sub(ptr, val, memorder)                                     \
MACRO_BEGIN                                                                 \
    assert(latomic_ptr_aligned(ptr));                                       \
    latomic_select(ptr, sub)(ptr, val, memorder);                           \
MACRO_END

#define latomic_and(ptr, val, memorder)                                     \
MACRO_BEGIN                                                                 \
    assert(latomic_ptr_aligned(ptr));                                       \
    latomic_select(ptr, and)(ptr, val, memorder);                           \
MACRO_END

#define latomic_or(ptr, val, memorder)                                      \
MACRO_BEGIN                                                                 \
    assert(latomic_ptr_aligned(ptr));                                       \
    latomic_select(ptr, or)(ptr, val, memorder);                            \
MACRO_END

#define latomic_xor(ptr, val, memorder)                                     \
MACRO_BEGIN                                                                 \
    assert(latomic_ptr_aligned(ptr));                                       \
    latomic_select(ptr, xor)(ptr, val, memorder);                           \
MACRO_END

#define latomic_fence(memorder)             \
MACRO_BEGIN                                 \
    assert(memorder != LATOMIC_RELAXED);    \
    barrier();                              \
MACRO_END

#endif /* KERN_LATOMIC_H */


code:
/*
 * Copyright (c) 2018 Agustina Arzille.
 * Copyright (c) 2018 Richard Braun.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 * Architecture-specific code may override any of the type-specific
 * functions by defining a macro of the same name.
 */

#ifndef KERN_LATOMIC_I_H
#define KERN_LATOMIC_I_H

#include <stdbool.h>
#include <stdint.h>

#include <kern/atomic_types.h>
#include <kern/macros.h>
#include <machine/latomic.h>

#define LATOMIC_ALIGN(ptr) MIN(sizeof(*(ptr)), sizeof(ptr))
#define latomic_ptr_aligned(ptr) P2ALIGNED((uintptr_t)(ptr), LATOMIC_ALIGN(ptr))

/* See atomic_select */
#define latomic_select(ptr, op)                     \
_Generic(*(ptr),                                    \
                 float: latomic_invalid_type,       \
                double: latomic_invalid_type,       \
           long double: latomic_invalid_type,       \
                  bool: latomic_invalid_type,       \
                  char: latomic_invalid_type,       \
           signed char: latomic_invalid_type,       \
         unsigned char: latomic_invalid_type,       \
                 short: latomic_invalid_type,       \
        unsigned short: latomic_invalid_type,       \
                   int: latomic_ ## op ## _32,      \
          unsigned int: latomic_ ## op ## _32,      \
                  long: latomic_ ## op ## _ul,      \
         unsigned long: latomic_ ## op ## _ul,      \
             long long: latomic_ ## op ## _64,      \
    unsigned long long: latomic_ ## op ## _64,      \
               default: latomic_ ## op ## _ptr)

void latomic_invalid_type(void);

/* latomic_load */

#ifndef latomic_load_32
unsigned int latomic_load_32(union atomic_constptr_32 ptr, int memorder);
#endif /* latomic_load_32 */

#ifndef latomic_load_64
#ifdef __LP64__
unsigned long long latomic_load_64(union atomic_constptr_64 ptr, int memorder);
#else /* __LP64__ */
unsigned long long latomic_load_64(union atomic_constptr_64 ptr, int memorder);
#endif /* __LP64__ */
#endif /* latomic_load_64 */

#ifdef __LP64__
#define latomic_load_ul          latomic_load_64
#else /* __LP64__ */
#define latomic_load_ul          latomic_load_32
#endif /* __LP64__ */

#define latomic_load_ptr         latomic_load_ul

/* latomic_store */

#ifndef latomic_store_32
void latomic_store_32(union atomic_ptr_32 ptr, union atomic_val_32 val,
                      int memorder);
#endif /* latomic_store_32 */

#ifndef latomic_store_64
#ifdef __LP64__
void latomic_store_64(union atomic_ptr_64 ptr, union atomic_val_64 val,
                      int memorder);
#else /* __LP64__ */
void latomic_store_64(union atomic_ptr_64 ptr, union atomic_val_64 val,
                      int memorder);
#endif /* __LP64__ */
#endif /* latomic_store_64 */

#ifdef __LP64__
#define latomic_store_ul        latomic_store_64
#else /* __LP64__ */
#define latomic_store_ul        latomic_store_32
#endif /* __LP64__ */

#define latomic_store_ptr       latomic_store_ul

/* latomic_swap */

#ifndef latomic_swap_32
unsigned int latomic_swap_32(union atomic_ptr_32 ptr,
                             union atomic_val_32 val, int memorder);
#endif /* latomic_swap_32 */

#ifndef latomic_swap_64
unsigned long long latomic_swap_64(union atomic_ptr_64 ptr,
                                   union atomic_val_64 val, int memorder);
#endif /* latomic_swap_64 */

#ifdef __LP64__
#define latomic_swap_ul         latomic_swap_64
#else /* __LP64__ */
#define latomic_swap_ul         latomic_swap_32
#endif /* __LP64__ */

#define latomic_swap_ptr        latomic_swap_ul

/* latomic_cas */

#ifndef latomic_cas_32
unsigned int latomic_cas_32(union atomic_ptr_32 ptr, union atomic_val_32 oval,
                            union atomic_val_32 nval, int memorder);
#endif /* latomic_cas_32 */

#ifndef latomic_cas_64
unsigned long long latomic_cas_64(union atomic_ptr_64 ptr,
                                  union atomic_val_64 oval,
                                  union atomic_val_64 nval, int memorder);
#endif /* latomic_cas_64 */

#ifdef __LP64__
#define latomic_cas_ul          latomic_cas_64
#else /* __LP64__ */
#define latomic_cas_ul          latomic_cas_32
#endif /* __LP64__ */

#define latomic_cas_ptr         latomic_cas_ul

/* latomic_fetch_add */

#ifndef latomic_fetch_add_32
unsigned int latomic_fetch_add_32(union atomic_ptr_32 ptr,
                                  union atomic_val_32 val, int memorder);
#endif /* latomic_fetch_add_32 */

#ifndef latomic_fetch_add_64
unsigned long long latomic_fetch_add_64(union atomic_ptr_64 ptr,
                                        union atomic_val_64 val, int memorder);
#endif /* latomic_fetch_add_64 */

#ifdef __LP64__
#define latomic_fetch_add_ul    latomic_fetch_add_64
#else /* __LP64__ */
#define latomic_fetch_add_ul    latomic_fetch_add_32
#endif /* __LP64__ */

#define latomic_fetch_add_ptr   latomic_fetch_add_ul

/* latomic_fetch_sub */

#ifndef latomic_fetch_sub_32
unsigned int latomic_fetch_sub_32(union atomic_ptr_32 ptr,
                                  union atomic_val_32 val, int memorder);
#endif /* latomic_fetch_sub_32 */

#ifndef latomic_fetch_sub_64
unsigned long long latomic_fetch_sub_64(union atomic_ptr_64 ptr,
                                        union atomic_val_64 val, int memorder);
#endif /* latomic_fetch_sub_64 */

#ifdef __LP64__
#define latomic_fetch_sub_ul    latomic_fetch_sub_64
#else /* __LP64__ */
#define latomic_fetch_sub_ul    latomic_fetch_sub_32
#endif /* __LP64__ */

#define latomic_fetch_sub_ptr   latomic_fetch_sub_ul

/* latomic_fetch_and */

#ifndef latomic_fetch_and_32
unsigned int latomic_fetch_and_32(union atomic_ptr_32 ptr,
                                  union atomic_val_32 val, int memorder);
#endif /* latomic_fetch_and_32 */

#ifndef latomic_fetch_and_64
unsigned long long latomic_fetch_and_64(union atomic_ptr_64 ptr,
                                        union atomic_val_64 val, int memorder);
#endif /* latomic_fetch_and_64 */

#ifdef __LP64__
#define latomic_fetch_and_ul    latomic_fetch_and_64
#else /* __LP64__ */
#define latomic_fetch_and_ul    latomic_fetch_and_32
#endif /* __LP64__ */

#define latomic_fetch_and_ptr   latomic_fetch_and_ul

/* latomic_fetch_or */

#ifndef latomic_fetch_or_32
unsigned int latomic_fetch_or_32(union atomic_ptr_32 ptr,
                                 union atomic_val_32 val, int memorder);
#endif /* latomic_fetch_or_32 */

#ifndef latomic_fetch_or_64
unsigned long long latomic_fetch_or_64(union atomic_ptr_64 ptr,
                                       union atomic_val_64 val, int memorder);
#endif /* latomic_fetch_or_64 */

#ifdef __LP64__
#define latomic_fetch_or_ul     latomic_fetch_or_64
#else /* __LP64__ */
#define latomic_fetch_or_ul     latomic_fetch_or_32
#endif /* __LP64__ */

#define latomic_fetch_or_ptr    latomic_fetch_or_ul

/* latomic_fetch_xor */

#ifndef latomic_fetch_xor_32
unsigned int latomic_fetch_xor_32(union atomic_ptr_32 ptr,
                                  union atomic_val_32 val, int memorder);
#endif /* latomic_fetch_xor_32 */

#ifndef latomic_fetch_xor_64
unsigned long long latomic_fetch_xor_64(union atomic_ptr_64 ptr,
                                        union atomic_val_64 val, int memorder);
#endif /* latomic_fetch_xor_64 */

#ifdef __LP64__
#define latomic_fetch_xor_ul    latomic_fetch_xor_64
#else /* __LP64__ */
#define latomic_fetch_xor_ul    latomic_fetch_xor_32
#endif /* __LP64__ */

#define latomic_fetch_xor_ptr   latomic_fetch_xor_ul

/* latomic_add */

#ifndef latomic_add_32
void latomic_add_32(union atomic_ptr_32 ptr, union atomic_val_32 val,
                    int memorder);
#endif /* latomic_add_32 */

#ifndef latomic_add_64
void latomic_add_64(union atomic_ptr_64 ptr, union atomic_val_64 val,
                    int memorder);
#endif /* latomic_add_64 */

#ifdef __LP64__
#define latomic_add_ul          latomic_add_64
#else /* __LP64__ */
#define latomic_add_ul          latomic_add_32
#endif /* __LP64__ */

#define latomic_add_ptr         latomic_add_ul

/* latomic_sub */

#ifndef latomic_sub_32
void latomic_sub_32(union atomic_ptr_32 ptr, union atomic_val_32 val,
                    int memorder);
#endif /* latomic_sub_32 */

#ifndef latomic_sub_64
void latomic_sub_64(union atomic_ptr_64 ptr, union atomic_val_64 val,
                    int memorder);
#endif /* latomic_sub_64 */

#ifdef __LP64__
#define latomic_sub_ul          latomic_sub_64
#else /* __LP64__ */
#define latomic_sub_ul          latomic_sub_32
#endif /* __LP64__ */

#define latomic_sub_ptr         latomic_sub_ul

/* latomic_and */

#ifndef latomic_and_32
void latomic_and_32(union atomic_ptr_32 ptr, union atomic_val_32 val,
                    int memorder);
#endif /* latomic_and_32 */

#ifndef latomic_and_64
void latomic_and_64(union atomic_ptr_64 ptr, union atomic_val_64 val,
                    int memorder);
#endif /* latomic_and_64 */

#ifdef __LP64__
#define latomic_and_ul          latomic_and_64
#else /* __LP64__ */
#define latomic_and_ul          latomic_and_32
#endif /* __LP64__ */

#define latomic_and_ptr         latomic_and_ul

/* latomic_or */

#ifndef latomic_or_32
void latomic_or_32(union atomic_ptr_32 ptr, union atomic_val_32 val,
                   int memorder);
#endif /* latomic_or_32 */

#ifndef latomic_or_64
void latomic_or_64(union atomic_ptr_64 ptr, union atomic_val_64 val,
                   int memorder);
#endif /* latomic_or_64 */

#ifdef __LP64__
#define latomic_or_ul           latomic_or_64
#else /* __LP64__ */
#define latomic_or_ul           latomic_or_32
#endif /* __LP64__ */

#define latomic_or_ptr          latomic_or_ul

/* latomic_xor */

#ifndef latomic_xor_32
void latomic_xor_32(union atomic_ptr_32 ptr, union atomic_val_32 val,
                    int memorder);
#endif /* latomic_xor_32 */

#ifndef latomic_xor_64
void latomic_xor_64(union atomic_ptr_64 ptr, union atomic_val_64 val,
                    int memorder);
#endif /* latomic_xor_64 */

#ifdef __LP64__
#define latomic_xor_ul          latomic_xor_64
#else /* __LP64__ */
#define latomic_xor_ul          latomic_xor_32
#endif /* __LP64__ */

#define latomic_xor_ptr         latomic_xor_ul

#endif /* KERN_LATOMIC_I_H */