diff options
author | esaxe <none@none> | 2007-01-17 18:01:29 -0800 |
---|---|---|
committer | esaxe <none@none> | 2007-01-17 18:01:29 -0800 |
commit | fb2f18f820d90b001aea4fb27dd654bc1263c440 (patch) | |
tree | 4b88b69e1244f360a85d70294a4498ecf57ca283 /deleted_files | |
parent | 9a7670889e9c36ec355371e6b02f2d9084f040dc (diff) | |
download | illumos-gate-fb2f18f820d90b001aea4fb27dd654bc1263c440.tar.gz |
6461311 multi-level CMT scheduling optimizations
6509639 cpu0 is not in the right chip_t if its chipid is not zero
--HG--
rename : usr/src/uts/common/os/chip.c => deleted_files/usr/src/uts/common/os/chip.c
rename : usr/src/uts/common/sys/chip.h => deleted_files/usr/src/uts/common/sys/chip.h
Diffstat (limited to 'deleted_files')
-rw-r--r-- | deleted_files/usr/src/uts/common/os/chip.c | 576 | ||||
-rw-r--r-- | deleted_files/usr/src/uts/common/sys/chip.h | 207 |
2 files changed, 783 insertions, 0 deletions
diff --git a/deleted_files/usr/src/uts/common/os/chip.c b/deleted_files/usr/src/uts/common/os/chip.c new file mode 100644 index 0000000000..ad11827b0f --- /dev/null +++ b/deleted_files/usr/src/uts/common/os/chip.c @@ -0,0 +1,576 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/thread.h> +#include <sys/cpuvar.h> +#include <sys/cpupart.h> +#include <sys/kmem.h> +#include <sys/cmn_err.h> +#include <sys/kstat.h> +#include <sys/processor.h> +#include <sys/disp.h> +#include <sys/chip.h> + +/* + * CMT aware scheduler/dispatcher support + * + * With the introduction of Chip Multi-Threaded (CMT) processor architectures, + * it is no longer necessarily true that a given physical processor + * module (chip) will present itself as a single schedulable entity (cpu_t). + * Rather, each chip may present itself as one or more "logical" CPUs. + * + * The logical CPUs presented may share physical components on the chip + * such as caches, data pipes, FPUs, etc. It is advantageous to have the + * kernel know which logical CPUs are presented by a given chip, + * and what facilities on the chip are shared, since the kernel can then use + * this information to employ scheduling policies that help improve the + * availability of per chip resources, and increase utilization of a thread's + * cache investment. + * + * The "chip_t" structure represents a physical processor. + * It is used to keep track of which logical CPUs are presented by a given + * chip, and to provide a parameterized representation of a chip's + * properties. A count of the number of running threads is also + * maintained, and is used by the dispatcher to balance load across the + * system's chips to improve performance through increased chip resource + * availability. + * + * Locking: + * + * Safely traversing the per lgroup lists requires the same protections + * as traversing the cpu lists. One must either: + * - hold cpu_lock + * - have disabled kernel preemption + * - be at high SPL + * - have cpu's paused + * + * Safely traversing the global "chip_list" requires holding cpu_lock. + * + * A chip's nrunning count should only be modified using the + * CHIP_NRUNNING() macro, through which updates of the count are done + * atomically. + */ + +chip_t cpu0_chip; /* chip structure for first CPU */ +cpu_physid_t cpu0_physid; /* boot CPU's physical id structure */ + +/* + * chip_bootstrap is used on platforms where it is possible to enter the + * dispatcher before a new CPU's chip initialization has happened. + */ +static chip_t chip_bootstrap; + +#define CPU_HAS_NO_CHIP(cp) \ + ((cp)->cpu_chip == NULL || (cp)->cpu_chip == &chip_bootstrap) + +static chip_t *chip_list; /* protected by CPU lock */ +static chip_set_t chip_set; /* bitmap of chips in existence */ + /* indexed by chip_seqid */ +static chipid_t chip_seqid_next = 0; /* next sequential chip id */ +static int nchips = 0; /* num chips in existence */ + +static chip_t *chip_find(chipid_t); +static int chip_kstat_extract(kstat_t *, int); + +/* + * Declare static kstat names (defined in chip.h) + */ +CHIP_KSTAT_NAMES; + +/* + * Find the chip_t with the given chip_id. + */ +static chip_t * +chip_find(chipid_t chipid) +{ + chip_t *chp, *chip_start; + + ASSERT(chip_list == NULL || chip_list->chip_next == chip_list || + MUTEX_HELD(&cpu_lock)); + + if ((chp = chip_start = chip_list) != NULL) { + do { + if (chp->chip_id == chipid) { + return (chp); + } + } while ((chp = chp->chip_next) != chip_start); + } + return (NULL); +} + +chip_t * +chip_lookup(chipid_t chipid) +{ + chip_t *chp; + + mutex_enter(&cpu_lock); + chp = chip_find(chipid); + mutex_exit(&cpu_lock); + + return (chp); +} + +#ifndef sun4v +/* + * Setup the kstats for this chip, if needed + */ +void +chip_kstat_create(chip_t *chp) +{ + chip_stat_t stat; + kstat_t *chip_kstat; + + ASSERT(MUTEX_HELD(&cpu_lock)); + + if (chp->chip_kstat != NULL) + return; /* already initialized */ + + chip_kstat = kstat_create("chip", chp->chip_id, NULL, "misc", + KSTAT_TYPE_NAMED, CHIP_NUM_STATS, + KSTAT_FLAG_VIRTUAL | KSTAT_FLAG_WRITABLE); + + if (chip_kstat != NULL) { + chip_kstat->ks_lock = &chp->chip_kstat_mutex; + mutex_init(chip_kstat->ks_lock, NULL, MUTEX_DEFAULT, NULL); + chip_kstat->ks_private = chp; + chip_kstat->ks_data = chp->chip_kstat_data; + for (stat = 0; stat < CHIP_NUM_STATS; stat++) + kstat_named_init(&chp->chip_kstat_data[stat], + chip_kstat_names[stat], KSTAT_DATA_INT64); + chip_kstat->ks_update = chip_kstat_extract; + chp->chip_kstat = chip_kstat; + kstat_install(chip_kstat); + } +} +#else +/* + * Note: On sun4v systems, chip kstats don't currently + * exist, since "chip" structures and policies are being + * leveraged to implement core level balancing, and exporting + * chip kstats in light of this would be both misleading + * and confusing. + */ +/* ARGSUSED */ +void +chip_kstat_create(chip_t *chp) +{ +} +#endif /* !sun4v */ + +static int +chip_kstat_extract(kstat_t *ksp, int rw) +{ + struct kstat_named *ksd; + chip_t *chp; + + chp = (chip_t *)ksp->ks_private; + + ksd = (struct kstat_named *)ksp->ks_data; + ASSERT(ksd == chp->chip_kstat_data); + + /* + * The chip kstats are read only + */ + if (rw == KSTAT_WRITE) + return (EACCES); + + ksd[CHIP_ID].value.i64 = chp->chip_id; + ksd[CHIP_NCPUS].value.i64 = chp->chip_ncpu; + ksd[CHIP_NRUNNING].value.i64 = chp->chip_nrunning; + ksd[CHIP_RECHOOSE].value.i64 = + rechoose_interval + chp->chip_rechoose_adj; + + return (0); +} + +/* + * If necessary, instantiate a chip_t for this CPU. + * Called when a CPU is being added to the system either in startup, + * or because of DR. The cpu will be assigned to the chip's active + * CPU list later in chip_cpu_assign() + */ +void +chip_cpu_init(cpu_t *cp) +{ + chipid_t cid; + int rechoose; + chip_t *chp; + chip_def_t chp_def; + + ASSERT((chip_list == NULL) || (MUTEX_HELD(&cpu_lock))); + + if (chip_list == NULL) + cp->cpu_physid = &cpu0_physid; + else + cp->cpu_physid = kmem_zalloc(sizeof (cpu_physid_t), KM_SLEEP); + + /* + * Call into the platform to fetch this cpu's chip and core ids. + * The ids are cached in the CPU's physical id structure. + * + * On sun4v platforms, the chip infrastructure is currently being + * leveraged to implement core level load balancing. + */ +#ifdef DO_CORELEVEL_LOADBAL + cid = chip_plat_get_coreid(cp); + cp->cpu_physid->cpu_coreid = cid; + cp->cpu_physid->cpu_chipid = chip_plat_get_chipid(cp); +#else + cid = chip_plat_get_chipid(cp); + cp->cpu_physid->cpu_chipid = cid; + cp->cpu_physid->cpu_coreid = chip_plat_get_coreid(cp); +#endif /* DO_CORELEVEL_LOADBAL */ + + chp = chip_find(cid); + if (chp == NULL) { + + /* + * Create a new chip + */ + if (chip_list == NULL) + chp = &cpu0_chip; + else + chp = kmem_zalloc(sizeof (*chp), KM_SLEEP); + + chp->chip_id = cid; + chp->chip_nrunning = 0; + + /* + * If we're booting, take this moment to perform + * some additional initialization + */ + if (chip_list == NULL) { + CHIP_SET_ZERO(chip_set); + CHIP_SET_ZERO(cp->cpu_part->cp_mach->mc_chipset); + chp->chip_nrunning++; /* for t0 */ + } + + /* + * Find the next free sequential chip id. + * A chip's sequential id exists in the range + * 0 .. CHIP_MAX_CHIPS, and is suitable for use with + * chip sets. + */ + while (CHIP_SET_TEST(chip_set, chip_seqid_next)) + chip_seqid_next++; + chp->chip_seqid = chip_seqid_next++; + CHIP_SET_ADD(chip_set, chp->chip_seqid); + + ASSERT(chip_seqid_next <= CHIP_MAX_CHIPS); + + + /* + * Query the platform specific parameters + * for this chip + */ + chip_plat_define_chip(cp, &chp_def); + chp->chip_rechoose_adj = chp_def.chipd_rechoose_adj; + chp->chip_type = chp_def.chipd_type; + chp->chip_nosteal = chp_def.chipd_nosteal; + + ASSERT((chp->chip_type < CHIP_NUM_TYPES) && + (chp->chip_type >= CHIP_DEFAULT)); + + /* + * Insert this chip in chip_list + */ + if (chip_list == NULL) { + chip_list = chp; + chp->chip_next = chp->chip_prev = chp; + } else { + chip_t *chptr; + + chptr = chip_list; + chp->chip_next = chptr; + chp->chip_prev = chptr->chip_prev; + chptr->chip_prev->chip_next = chp; + chptr->chip_prev = chp; + } + + nchips++; + ASSERT(nchips <= CHIP_MAX_CHIPS); + + /* + * The boot cpu will create the first chip's kstats + * later in cpu_kstat_init() + */ + if (chp != &cpu0_chip) + chip_kstat_create(chp); + } + + /* + * Initialize the effective rechoose interval cached + * in this cpu structure. + */ + rechoose = rechoose_interval + chp->chip_rechoose_adj; + cp->cpu_rechoose = (rechoose < 0) ? 0 : rechoose; + + cp->cpu_chip = chp; + chp->chip_ref++; +} + +/* + * This cpu is being deleted. It has already been removed from + * the chip's active cpu list back in chip_cpu_unassign(). Here + * we remove the cpu's reference to the chip, and cleanup/destroy + * the chip if needed. + */ +void +chip_cpu_fini(cpu_t *cp) +{ + chip_t *chp; + chip_t *prev, *next; + + ASSERT(MUTEX_HELD(&cpu_lock)); + + /* + * This can happen if the CPU failed to power on + */ + if (CPU_HAS_NO_CHIP(cp)) + return; + + chp = cp->cpu_chip; + cp->cpu_chip = NULL; + + /* + * Clear out and free the CPU's physical id structure + */ + cp->cpu_physid->cpu_chipid = -1; + cp->cpu_physid->cpu_coreid = -1; + + if (cp->cpu_physid != &cpu0_physid) { + ASSERT(cp->cpu_physid != NULL); + kmem_free(cp->cpu_physid, sizeof (cpu_physid_t)); + } + cp->cpu_physid = NULL; + + /* + * Delete the chip if its last CPU is being deleted + */ + if (--chp->chip_ref == 0) { + + ASSERT(chp->chip_ncpu == 0); + ASSERT(chp->chip_cpus == NULL); + ASSERT(chp->chip_nrunning == 0); + ASSERT(chp->chip_lgrp == NULL); + ASSERT((chp->chip_next_lgrp == NULL) && + (chp->chip_prev_lgrp == NULL)); + + if (chip_seqid_next > chp->chip_seqid) + chip_seqid_next = chp->chip_seqid; + CHIP_SET_REMOVE(chip_set, chp->chip_seqid); + + chp->chip_id = -1; + chp->chip_seqid = -1; + + /* + * remove the chip from the system's chip list + */ + if (chip_list == chp) + chip_list = chp->chip_next; + + prev = chp->chip_prev; + next = chp->chip_next; + + prev->chip_next = next; + next->chip_prev = prev; + + chp->chip_next = chp->chip_prev = NULL; + + nchips--; + + /* + * clean up any chip kstats + */ + if (chp->chip_kstat) { + kstat_delete(chp->chip_kstat); + chp->chip_kstat = NULL; + } + /* + * If the chip_t structure was dynamically + * allocated, free it. + */ + if (chp != &cpu0_chip) + kmem_free(chp, sizeof (*chp)); + } +} + +/* + * This cpu is becoming active (online). + * Perform all the necessary bookkeeping in it's chip_t + */ +void +chip_cpu_assign(cpu_t *cp) +{ + chip_t *chp; + cpu_t *cptr; + + ASSERT(chip_list == NULL || chip_list->chip_next == chip_list || + MUTEX_HELD(&cpu_lock)); + + chp = cp->cpu_chip; + + /* + * Add this cpu to the chip's cpu list + */ + if (chp->chip_ncpu == 0) { + chp->chip_cpus = cp; + cp->cpu_next_chip = cp->cpu_prev_chip = cp; + } else { + cptr = chp->chip_cpus; + cp->cpu_next_chip = cptr; + cp->cpu_prev_chip = cptr->cpu_prev_chip; + cp->cpu_prev_chip->cpu_next_chip = cp; + cptr->cpu_prev_chip = cp; + } + + chp->chip_ncpu++; + + /* + * Notate this chip's seqid in the cpu partition's chipset + */ + chip_cpu_move_part(cp, NULL, cp->cpu_part); +} + +/* + * This cpu is being offlined, so do the reverse + * of cpu_chip_assign() + */ +void +chip_cpu_unassign(cpu_t *cp) +{ + chip_t *chp; + struct cpu *prev; + struct cpu *next; + + ASSERT(MUTEX_HELD(&cpu_lock)); + + chp = cp->cpu_chip; + + chip_cpu_move_part(cp, cp->cpu_part, NULL); + + /* + * remove this cpu from the chip's cpu list + */ + prev = cp->cpu_prev_chip; + next = cp->cpu_next_chip; + + prev->cpu_next_chip = next; + next->cpu_prev_chip = prev; + + cp->cpu_next_chip = cp->cpu_prev_chip = NULL; + + chp->chip_ncpu--; + + if (chp->chip_ncpu == 0) { + chp->chip_cpus = NULL; + } else if (chp->chip_cpus == cp) { + chp->chip_cpus = next; + } +} + +/* + * A cpu on the chip is moving into and/or out of a cpu partition. + * Maintain the cpuparts' chip membership set. + * oldpp is NULL when a cpu is being offlined. + * newpp is NULL when a cpu is being onlined. + */ +void +chip_cpu_move_part(cpu_t *cp, cpupart_t *oldpp, cpupart_t *newpp) +{ + cpu_t *cpp; + chip_t *chp; + + ASSERT(chip_list->chip_next == chip_list || MUTEX_HELD(&cpu_lock)); + + chp = cp->cpu_chip; + + if (newpp != NULL) { + /* + * Add the chip's seqid to the cpupart's chip set + */ + CHIP_SET_ADD(newpp->cp_mach->mc_chipset, chp->chip_seqid); + } + + if (oldpp != NULL) { + cpp = cp; + while ((cpp = cpp->cpu_next_chip) != cp) { + if (cpp->cpu_part->cp_id == oldpp->cp_id) { + /* + * Another cpu on the chip is in the old + * cpu partition, so we're done + */ + return; + } + } + + /* + * No other cpu on the chip is in the old partition + * so remove the chip's seqid from it's set + */ + CHIP_SET_REMOVE(oldpp->cp_mach->mc_chipset, chp->chip_seqid); + } +} + +/* + * Called to indicate a slave CPU has started up. + */ +void +chip_cpu_startup(cpu_t *cp) +{ + /* + * Indicate that the chip has a new running thread + * (slave startup) + */ + CHIP_NRUNNING(cp->cpu_chip, 1); +} + +/* + * Provide the specified CPU a bootstrap chip + */ +void +chip_bootstrap_cpu(cpu_t *cp) +{ + cp->cpu_chip = &chip_bootstrap; +} + +/* + * Given a chip set, return 1 if it is empty. + */ +int +chip_set_isnull(chip_set_t *set) +{ + int i; + + for (i = 0; i < CHIP_SET_WORDS; i++) { + if (set->csb[i] != 0) + return (0); + } + return (1); +} diff --git a/deleted_files/usr/src/uts/common/sys/chip.h b/deleted_files/usr/src/uts/common/sys/chip.h new file mode 100644 index 0000000000..8889756884 --- /dev/null +++ b/deleted_files/usr/src/uts/common/sys/chip.h @@ -0,0 +1,207 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _CHIP_H +#define _CHIP_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * generic kernel CMT processor support + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#if (defined(_KERNEL) || defined(_KMEMUSER)) +#include <sys/cpuvar.h> +#include <sys/processor.h> +#include <sys/bitmap.h> +#include <sys/atomic.h> +#include <sys/time.h> +#include <sys/types.h> + +/* + * Chip types + */ +typedef enum chip_type { + CHIP_DEFAULT, /* Default, non CMT processor */ + CHIP_SMT, /* SMT, single core */ + CHIP_CMP_SPLIT_CACHE, /* CMP with split caches */ + CHIP_CMP_SHARED_CACHE, /* CMP with shared caches */ + CHIP_CMT, /* CMT w/ multiple cores and threads */ + CHIP_NUM_TYPES +} chip_type_t; + + +/* + * Balancing is possible if multiple chips exist in the lgroup + * but only necessary if the chip has multiple online logical CPUs + */ +#define CHIP_SHOULD_BALANCE(chp) \ + (((chp)->chip_ncpu > 1) && ((chp)->chip_next_lgrp != (chp))) + +/* + * Platform's definition of a chip's properties + */ +typedef struct chip_def { + chip_type_t chipd_type; + int chipd_rechoose_adj; + hrtime_t chipd_nosteal; +} chip_def_t; + +/* + * Per chip kstats + */ +typedef enum chip_stat_types { + CHIP_ID, /* chip "id" */ + CHIP_NCPUS, /* number of active cpus */ + CHIP_NRUNNING, /* number of running threads on chip */ + CHIP_RECHOOSE, /* chip's rechoose_interval */ + CHIP_NUM_STATS /* always last */ +} chip_stat_t; + +#define CHIP_KSTAT_NAMES \ +static char *chip_kstat_names[] = { \ + \ + "chip_id", \ + "logical_cpus", \ + "chip_nrunning", \ + "chip_rechoose_interval", \ +} + +/* + * Physical processor (chip) structure. + */ +typedef struct chip { + chipid_t chip_id; /* chip's "id" */ + chipid_t chip_seqid; /* sequential id */ + struct chip *chip_prev; /* previous chip on list */ + struct chip *chip_next; /* next chip on list */ + struct chip *chip_prev_lgrp; /* prev chip in lgroup */ + struct chip *chip_next_lgrp; /* next chip in lgroup */ + chip_type_t chip_type; /* type of chip */ + uint16_t chip_ncpu; /* number of active cpus */ + uint16_t chip_ref; /* chip's reference count */ + struct cpu *chip_cpus; /* per chip cpu list */ + struct lgrp *chip_lgrp; /* chip lives in this lgroup */ + int chip_rechoose_adj; /* chip specific adjustment */ + + /* + * chip kstats + */ + kstat_t *chip_kstat; + kmutex_t chip_kstat_mutex; + struct kstat_named chip_kstat_data[CHIP_NUM_STATS]; + + struct chip *chip_balance; /* chip to balance against */ + uint32_t chip_nrunning; /* # of running threads */ + hrtime_t chip_nosteal; /* nosteal interval (nsecs) */ +} chip_t; + +/* + * IDs associating a CPU with various physical hardware + */ +typedef struct cpu_physid { + chipid_t cpu_chipid; /* CPU's physical processor */ + id_t cpu_coreid; /* CPU's physical core */ +} cpu_physid_t; + +/* + * Change the number of running threads on the chip + */ +#define CHIP_NRUNNING(chp, n) { \ + atomic_add_32(&((chp)->chip_nrunning), (n)); \ +} + +/* + * True if this CPU is active on the chip + */ +#define CHIP_CPU_ACTIVE(cp) ((cp)->cpu_next_chip != NULL) + +/* + * Sets of chips + * The "id" used here should be a chip's sequential id. + * (chip_seqid) + */ +#if defined(_MACHDEP) + +#define CHIP_MAX_CHIPS NCPU +#define CHIP_SET_WORDS BT_BITOUL(CHIP_MAX_CHIPS) + +typedef struct chip_set { + ulong_t csb[CHIP_SET_WORDS]; +} chip_set_t; + +extern int chip_set_isnull(chip_set_t *); + +#define CHIP_SET_ISNULL(set) chip_set_isnull(&(set)) +#define CHIP_SET_TEST(set, id) BT_TEST((set).csb, id) +#define CHIP_SET_REMOVE(set, id) BT_CLEAR((set).csb, id) +#define CHIP_SET_ADD(set, id) BT_SET((set).csb, id) + +#define CHIP_SET_ZERO(set) { \ + int _i; \ + for (_i = 0; _i < CHIP_SET_WORDS; _i++) \ + (set).csb[_i] = 0; \ +} + +#define CHIP_IN_CPUPART(chp, cp) \ + (CHIP_SET_TEST((cp)->cp_mach->mc_chipset, (chp)->chip_seqid)) + +#endif /* _MACHDEP */ + +/* + * Common kernel chip operations + */ +void chip_cpu_init(cpu_t *); +void chip_cpu_fini(cpu_t *); +void chip_cpu_assign(cpu_t *); +void chip_cpu_unassign(cpu_t *); +void chip_cpu_startup(cpu_t *); +chip_t *chip_lookup(chipid_t); +void chip_bootstrap_cpu(cpu_t *); + +void chip_cpu_move_part(cpu_t *, struct cpupart *, + struct cpupart *); + +void chip_kstat_create(chip_t *); + +/* + * Platform chip operations + */ +chipid_t chip_plat_get_chipid(cpu_t *); +id_t chip_plat_get_coreid(cpu_t *); +int chip_plat_get_clogid(cpu_t *); +void chip_plat_define_chip(cpu_t *, chip_def_t *); + +#endif /* !_KERNEL && !_KMEMUSER */ + +#ifdef __cplusplus +} +#endif + +#endif /* _CHIP_H */ |