/* * Copyright 2018 Google, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer; * redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution; * neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Authors: Gabe Black */ #include "base/fiber.hh" #if HAVE_VALGRIND #include #endif #include #include #include #include #include #include "base/logging.hh" using namespace std; namespace { /* * The PrimaryFiber class is a special case that attaches to the currently * executing context. That makes handling the "primary" fiber, aka the one * which most of gem5 is running under, no different than other Fibers. */ class PrimaryFiber : public Fiber { public: PrimaryFiber() : Fiber(nullptr, 0) { setStarted(); } void main() { panic("PrimaryFiber main executed.\n"); } }; PrimaryFiber _primaryFiber; // A pointer to whatever the currently executing Fiber is. Fiber *_currentFiber = &_primaryFiber; // A pointer to the Fiber which is currently being started/initialized. Fiber *startingFiber = nullptr; } // anonymous namespace void Fiber::entryTrampoline() { startingFiber->start(); } Fiber::Fiber(size_t stack_size) : Fiber(primaryFiber(), stack_size) {} Fiber::Fiber(Fiber *link, size_t stack_size) : link(link), stack(nullptr), stackSize(stack_size), guardPage(nullptr), guardPageSize(sysconf(_SC_PAGE_SIZE)), started(false), _finished(false) { if (stack_size) { guardPage = mmap(nullptr, guardPageSize + stack_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (guardPage == (void *)MAP_FAILED) { perror("mmap"); fatal("Could not mmap %d byte fiber stack.\n", stack_size); } stack = (void *)((uint8_t *)guardPage + guardPageSize); if (mprotect(guardPage, guardPageSize, PROT_NONE)) { perror("mprotect"); fatal("Could not forbid access to fiber stack guard page."); } } #if HAVE_VALGRIND valgrindStackId = VALGRIND_STACK_REGISTER( stack, (uint8_t *)stack + stack_size); #endif } Fiber::~Fiber() { panic_if(stack && _currentFiber == this, "Fiber stack is in use."); #if HAVE_VALGRIND VALGRIND_STACK_DEREGISTER(valgrindStackId); #endif if (guardPage) munmap(guardPage, guardPageSize + stackSize); } void Fiber::createContext() { // Set up a context for the new fiber, starting it in the trampoline. getcontext(&ctx); ctx.uc_stack.ss_sp = stack; ctx.uc_stack.ss_size = stackSize; ctx.uc_link = nullptr; makecontext(&ctx, &entryTrampoline, 0); // Swap to the new context so it can enter its start() function. It // will then swap itself back out and return here. startingFiber = this; panic_if(!_currentFiber, "No active Fiber object."); swapcontext(&_currentFiber->ctx, &ctx); // The new context is now ready and about to call main(). } void Fiber::start() { // Avoid a dangling pointer. startingFiber = nullptr; setStarted(); // Swap back to the parent context which is still considered "current", // now that we're ready to go. int ret M5_VAR_USED = swapcontext(&ctx, &_currentFiber->ctx); panic_if(ret == -1, strerror(errno)); // Call main() when we're been reactivated for the first time. main(); // main has returned, so this Fiber has finished. Switch to the "link" // Fiber. _finished = true; link->run(); } void Fiber::run() { panic_if(_finished, "Fiber has already run to completion."); // If we're already running this fiber, we're done. if (_currentFiber == this) return; if (!started) createContext(); // Switch out of the current Fiber's context and this one's in. Fiber *prev = _currentFiber; Fiber *next = this; _currentFiber = next; swapcontext(&prev->ctx, &next->ctx); } Fiber *Fiber::currentFiber() { return _currentFiber; } Fiber *Fiber::primaryFiber() { return &_primaryFiber; }