19651SAndreas.Sandberg@ARM.com/*
29651SAndreas.Sandberg@ARM.com * Copyright (c) 2012 ARM Limited
39651SAndreas.Sandberg@ARM.com * All rights reserved
49651SAndreas.Sandberg@ARM.com *
59651SAndreas.Sandberg@ARM.com * The license below extends only to copyright in the software and shall
69651SAndreas.Sandberg@ARM.com * not be construed as granting a license to any other intellectual
79651SAndreas.Sandberg@ARM.com * property including but not limited to intellectual property relating
89651SAndreas.Sandberg@ARM.com * to a hardware implementation of the functionality of the software
99651SAndreas.Sandberg@ARM.com * licensed hereunder.  You may use the software subject to the license
109651SAndreas.Sandberg@ARM.com * terms below provided that you ensure that this notice is replicated
119651SAndreas.Sandberg@ARM.com * unmodified and in its entirety in all distributions of the software,
129651SAndreas.Sandberg@ARM.com * modified or unmodified, in source code or in binary form.
139651SAndreas.Sandberg@ARM.com *
149651SAndreas.Sandberg@ARM.com * Redistribution and use in source and binary forms, with or without
159651SAndreas.Sandberg@ARM.com * modification, are permitted provided that the following conditions are
169651SAndreas.Sandberg@ARM.com * met: redistributions of source code must retain the above copyright
179651SAndreas.Sandberg@ARM.com * notice, this list of conditions and the following disclaimer;
189651SAndreas.Sandberg@ARM.com * redistributions in binary form must reproduce the above copyright
199651SAndreas.Sandberg@ARM.com * notice, this list of conditions and the following disclaimer in the
209651SAndreas.Sandberg@ARM.com * documentation and/or other materials provided with the distribution;
219651SAndreas.Sandberg@ARM.com * neither the name of the copyright holders nor the names of its
229651SAndreas.Sandberg@ARM.com * contributors may be used to endorse or promote products derived from
239651SAndreas.Sandberg@ARM.com * this software without specific prior written permission.
249651SAndreas.Sandberg@ARM.com *
259651SAndreas.Sandberg@ARM.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
269651SAndreas.Sandberg@ARM.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
279651SAndreas.Sandberg@ARM.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
289651SAndreas.Sandberg@ARM.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
299651SAndreas.Sandberg@ARM.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
309651SAndreas.Sandberg@ARM.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
319651SAndreas.Sandberg@ARM.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
329651SAndreas.Sandberg@ARM.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
339651SAndreas.Sandberg@ARM.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
349651SAndreas.Sandberg@ARM.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
359651SAndreas.Sandberg@ARM.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
369651SAndreas.Sandberg@ARM.com *
379651SAndreas.Sandberg@ARM.com * Authors: Andreas Sandberg
389651SAndreas.Sandberg@ARM.com */
399651SAndreas.Sandberg@ARM.com
4011793Sbrandon.potter@amd.com#include "cpu/kvm/timer.hh"
4111793Sbrandon.potter@amd.com
4211793Sbrandon.potter@amd.com#include <sys/syscall.h>
4311793Sbrandon.potter@amd.com#include <unistd.h>
4411793Sbrandon.potter@amd.com
459881Sandreas@sandberg.pp.se#include <algorithm>
469651SAndreas.Sandberg@ARM.com#include <csignal>
479651SAndreas.Sandberg@ARM.com#include <ctime>
489651SAndreas.Sandberg@ARM.com
4912334Sgabeblack@google.com#include "base/logging.hh"
509651SAndreas.Sandberg@ARM.com#include "base/trace.hh"
519651SAndreas.Sandberg@ARM.com#include "debug/KvmTimer.hh"
529651SAndreas.Sandberg@ARM.com
5310073Sandreas@sandberg.pp.se/* According to timer_create(2), the value SIGEV_THREAD_ID can be used
5410073Sandreas@sandberg.pp.se * to specify which thread a timer signal gets delivered to. According
5510073Sandreas@sandberg.pp.se * to the man page, the member sigev_notify_thread is used to specify
5610073Sandreas@sandberg.pp.se * the TID. This member is currently not defined by default in
5710073Sandreas@sandberg.pp.se * siginfo.h on x86, so we define it here as a workaround.
5810073Sandreas@sandberg.pp.se */
5910073Sandreas@sandberg.pp.se#ifndef sigev_notify_thread_id
6010073Sandreas@sandberg.pp.se#define sigev_notify_thread_id     _sigev_un._tid
6110073Sandreas@sandberg.pp.se#endif
6210073Sandreas@sandberg.pp.se
6310073Sandreas@sandberg.pp.sestatic pid_t
6410073Sandreas@sandberg.pp.segettid()
6510073Sandreas@sandberg.pp.se{
6610073Sandreas@sandberg.pp.se    return syscall(__NR_gettid);
6710073Sandreas@sandberg.pp.se}
6810073Sandreas@sandberg.pp.se
699881Sandreas@sandberg.pp.se/**
709881Sandreas@sandberg.pp.se * Minimum number of cycles that a host can spend in a KVM call (used
719881Sandreas@sandberg.pp.se * to calculate the resolution of some timers).
729881Sandreas@sandberg.pp.se *
739881Sandreas@sandberg.pp.se * The value of this constant is a bit arbitrary, but in practice, we
749881Sandreas@sandberg.pp.se * can't really do anything useful in less than ~1000 cycles.
759881Sandreas@sandberg.pp.se */
769881Sandreas@sandberg.pp.sestatic const uint64_t MIN_HOST_CYCLES = 1000;
779651SAndreas.Sandberg@ARM.com
789651SAndreas.Sandberg@ARM.comPosixKvmTimer::PosixKvmTimer(int signo, clockid_t clockID,
799651SAndreas.Sandberg@ARM.com                             float hostFactor, Tick hostFreq)
809651SAndreas.Sandberg@ARM.com    : BaseKvmTimer(signo, hostFactor, hostFreq),
819651SAndreas.Sandberg@ARM.com      clockID(clockID)
829651SAndreas.Sandberg@ARM.com{
839651SAndreas.Sandberg@ARM.com    struct sigevent sev;
849651SAndreas.Sandberg@ARM.com
8510073Sandreas@sandberg.pp.se    sev.sigev_notify = SIGEV_THREAD_ID;
869651SAndreas.Sandberg@ARM.com    sev.sigev_signo = signo;
8710073Sandreas@sandberg.pp.se    sev.sigev_notify_thread_id = gettid();
889651SAndreas.Sandberg@ARM.com    sev.sigev_value.sival_ptr = NULL;
899734Sandreas@sandberg.pp.se
909734Sandreas@sandberg.pp.se    while (timer_create(clockID, &sev, &timer) == -1) {
919734Sandreas@sandberg.pp.se        if (errno != EAGAIN)
929734Sandreas@sandberg.pp.se            panic("timer_create: %i", errno);
939734Sandreas@sandberg.pp.se    }
949651SAndreas.Sandberg@ARM.com}
959651SAndreas.Sandberg@ARM.com
969651SAndreas.Sandberg@ARM.comPosixKvmTimer::~PosixKvmTimer()
979651SAndreas.Sandberg@ARM.com{
989651SAndreas.Sandberg@ARM.com    timer_delete(timer);
999651SAndreas.Sandberg@ARM.com}
1009651SAndreas.Sandberg@ARM.com
1019651SAndreas.Sandberg@ARM.comvoid
1029651SAndreas.Sandberg@ARM.comPosixKvmTimer::arm(Tick ticks)
1039651SAndreas.Sandberg@ARM.com{
1049651SAndreas.Sandberg@ARM.com    struct itimerspec ts;
1059651SAndreas.Sandberg@ARM.com    memset(&ts, 0, sizeof(ts));
1069651SAndreas.Sandberg@ARM.com
1079651SAndreas.Sandberg@ARM.com    ts.it_interval.tv_sec = 0;
1089651SAndreas.Sandberg@ARM.com    ts.it_interval.tv_nsec = 0;
1099651SAndreas.Sandberg@ARM.com    ts.it_value.tv_sec = hostNs(ticks) / 1000000000ULL;
1109651SAndreas.Sandberg@ARM.com    ts.it_value.tv_nsec = hostNs(ticks) % 1000000000ULL;
1119651SAndreas.Sandberg@ARM.com
1129881Sandreas@sandberg.pp.se    assert(ts.it_value.tv_nsec > 0 || ts.it_value.tv_sec > 0);
1139881Sandreas@sandberg.pp.se
1149651SAndreas.Sandberg@ARM.com    DPRINTF(KvmTimer, "Arming POSIX timer: %i ticks (%is%ins)\n",
1159651SAndreas.Sandberg@ARM.com            ticks, ts.it_value.tv_sec, ts.it_value.tv_nsec);
1169651SAndreas.Sandberg@ARM.com
1179651SAndreas.Sandberg@ARM.com    if (timer_settime(timer, 0, &ts, NULL) == -1)
1189651SAndreas.Sandberg@ARM.com        panic("PosixKvmTimer: Failed to arm timer\n");
1199651SAndreas.Sandberg@ARM.com}
1209651SAndreas.Sandberg@ARM.com
1219651SAndreas.Sandberg@ARM.comvoid
1229651SAndreas.Sandberg@ARM.comPosixKvmTimer::disarm()
1239651SAndreas.Sandberg@ARM.com{
1249651SAndreas.Sandberg@ARM.com    struct itimerspec ts;
1259651SAndreas.Sandberg@ARM.com    memset(&ts, 0, sizeof(ts));
1269651SAndreas.Sandberg@ARM.com
1279651SAndreas.Sandberg@ARM.com    DPRINTF(KvmTimer, "Disarming POSIX timer\n");
1289651SAndreas.Sandberg@ARM.com
1299651SAndreas.Sandberg@ARM.com    if (timer_settime(timer, 0, &ts, NULL) == -1)
1309651SAndreas.Sandberg@ARM.com        panic("PosixKvmTimer: Failed to disarm timer\n");
1319651SAndreas.Sandberg@ARM.com}
1329651SAndreas.Sandberg@ARM.com
1339651SAndreas.Sandberg@ARM.comTick
1349651SAndreas.Sandberg@ARM.comPosixKvmTimer::calcResolution()
1359651SAndreas.Sandberg@ARM.com{
1369651SAndreas.Sandberg@ARM.com    struct timespec ts;
1379651SAndreas.Sandberg@ARM.com
1389651SAndreas.Sandberg@ARM.com    if (clock_getres(clockID, &ts) == -1)
1399651SAndreas.Sandberg@ARM.com        panic("PosixKvmTimer: Failed to get timer resolution\n");
1409651SAndreas.Sandberg@ARM.com
1419881Sandreas@sandberg.pp.se    const uint64_t res_ns(ts.tv_sec * 1000000000ULL + ts.tv_nsec);
1429881Sandreas@sandberg.pp.se    // We preferrably want ticksFromHostNs() to calculate the the
1439881Sandreas@sandberg.pp.se    // ceiling rather than truncating the value. However, there are
1449881Sandreas@sandberg.pp.se    // other cases where truncating is fine, so we just add 1 here to
1459881Sandreas@sandberg.pp.se    // make sure that the actual resolution is strictly less than what
1469881Sandreas@sandberg.pp.se    // we return. We could get all kinds of nasty behavior if
1479881Sandreas@sandberg.pp.se    // arm(resolution) is called and the resulting time is 0 (which
1489881Sandreas@sandberg.pp.se    // could happen if we truncate the results and the resolution is
1499881Sandreas@sandberg.pp.se    // 1ns).
1509881Sandreas@sandberg.pp.se    const Tick resolution(ticksFromHostNs(res_ns) + 1);
1519881Sandreas@sandberg.pp.se    // It might not make sense to enter into KVM for less than a
1529881Sandreas@sandberg.pp.se    // certain number of host cycles. In some systems (e.g., Linux)
1539881Sandreas@sandberg.pp.se    // the resolution of the timer we use is 1ns (a few cycles on most
1549881Sandreas@sandberg.pp.se    // CPUs), which isn't very useful.
1559881Sandreas@sandberg.pp.se    const Tick min_cycles(ticksFromHostCycles(MIN_HOST_CYCLES));
1569651SAndreas.Sandberg@ARM.com
1579881Sandreas@sandberg.pp.se    return std::max(resolution, min_cycles);
1589651SAndreas.Sandberg@ARM.com}
1599655SAndreas.Sandberg@ARM.com
1609655SAndreas.Sandberg@ARM.com
1619655SAndreas.Sandberg@ARM.comPerfKvmTimer::PerfKvmTimer(PerfKvmCounter &ctr,
1629655SAndreas.Sandberg@ARM.com                           int signo, float hostFactor, Tick hostFreq)
1639655SAndreas.Sandberg@ARM.com    : BaseKvmTimer(signo, hostFactor, hostFreq),
1649655SAndreas.Sandberg@ARM.com      hwOverflow(ctr)
1659655SAndreas.Sandberg@ARM.com{
1669655SAndreas.Sandberg@ARM.com    hwOverflow.enableSignals(signo);
1679655SAndreas.Sandberg@ARM.com}
1689655SAndreas.Sandberg@ARM.com
1699655SAndreas.Sandberg@ARM.comPerfKvmTimer::~PerfKvmTimer()
1709655SAndreas.Sandberg@ARM.com{
1719655SAndreas.Sandberg@ARM.com}
1729655SAndreas.Sandberg@ARM.com
1739655SAndreas.Sandberg@ARM.comvoid
1749655SAndreas.Sandberg@ARM.comPerfKvmTimer::arm(Tick ticks)
1759655SAndreas.Sandberg@ARM.com{
1769655SAndreas.Sandberg@ARM.com    hwOverflow.period(hostCycles(ticks));
1779655SAndreas.Sandberg@ARM.com    hwOverflow.refresh(1);
1789655SAndreas.Sandberg@ARM.com}
1799655SAndreas.Sandberg@ARM.com
1809655SAndreas.Sandberg@ARM.comvoid
1819655SAndreas.Sandberg@ARM.comPerfKvmTimer::disarm()
1829655SAndreas.Sandberg@ARM.com{
1839655SAndreas.Sandberg@ARM.com    hwOverflow.stop();
1849655SAndreas.Sandberg@ARM.com}
1859655SAndreas.Sandberg@ARM.com
1869655SAndreas.Sandberg@ARM.comTick
1879655SAndreas.Sandberg@ARM.comPerfKvmTimer::calcResolution()
1889655SAndreas.Sandberg@ARM.com{
1899881Sandreas@sandberg.pp.se    return ticksFromHostCycles(MIN_HOST_CYCLES);
1909655SAndreas.Sandberg@ARM.com}
191