Release 4.10 virt/kvm/arm/arch_timer.c
/*
* Copyright (C) 2012 ARM Ltd.
* Author: Marc Zyngier <marc.zyngier@arm.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/cpu.h>
#include <linux/kvm.h>
#include <linux/kvm_host.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <clocksource/arm_arch_timer.h>
#include <asm/arch_timer.h>
#include <asm/kvm_hyp.h>
#include <kvm/arm_vgic.h>
#include <kvm/arm_arch_timer.h>
#include "trace.h"
static struct timecounter *timecounter;
static unsigned int host_vtimer_irq;
static u32 host_vtimer_irq_flags;
void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu)
{
vcpu->arch.timer_cpu.active_cleared_last = false;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
marc zyngier | marc zyngier | 20 | 100.00% | 1 | 100.00% |
| Total | 20 | 100.00% | 1 | 100.00% |
static u64 kvm_phys_timer_read(void)
{
return timecounter->cc->read(timecounter->cc);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
marc zyngier | marc zyngier | 19 | 95.00% | 1 | 50.00% |
thomas gleixner | thomas gleixner | 1 | 5.00% | 1 | 50.00% |
| Total | 20 | 100.00% | 2 | 100.00% |
static bool timer_is_armed(struct arch_timer_cpu *timer)
{
return timer->armed;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
marc zyngier | marc zyngier | 16 | 100.00% | 1 | 100.00% |
| Total | 16 | 100.00% | 1 | 100.00% |
/* timer_arm: as in "arm the timer", not as in ARM the company */
static void timer_arm(struct arch_timer_cpu *timer, u64 ns)
{
timer->armed = true;
hrtimer_start(&timer->timer, ktime_add_ns(ktime_get(), ns),
HRTIMER_MODE_ABS);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
marc zyngier | marc zyngier | 38 | 100.00% | 1 | 100.00% |
| Total | 38 | 100.00% | 1 | 100.00% |
static void timer_disarm(struct arch_timer_cpu *timer)
{
if (timer_is_armed(timer)) {
hrtimer_cancel(&timer->timer);
cancel_work_sync(&timer->expired);
timer->armed = false;
}
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
marc zyngier | marc zyngier | 42 | 100.00% | 1 | 100.00% |
| Total | 42 | 100.00% | 1 | 100.00% |
static irqreturn_t kvm_arch_timer_handler(int irq, void *dev_id)
{
struct kvm_vcpu *vcpu = *(struct kvm_vcpu **)dev_id;
/*
* We disable the timer in the world switch and let it be
* handled by kvm_timer_sync_hwstate(). Getting a timer
* interrupt at this point is a sure sign of some major
* breakage.
*/
pr_warn("Unexpected interrupt %d on vcpu %p\n", irq, vcpu);
return IRQ_HANDLED;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
marc zyngier | marc zyngier | 40 | 100.00% | 1 | 100.00% |
| Total | 40 | 100.00% | 1 | 100.00% |
/*
* Work function for handling the backup timer that we schedule when a vcpu is
* no longer running, but had a timer programmed to fire in the future.
*/
static void kvm_timer_inject_irq_work(struct work_struct *work)
{
struct kvm_vcpu *vcpu;
vcpu = container_of(work, struct kvm_vcpu, arch.timer_cpu.expired);
/*
* If the vcpu is blocked we want to wake it up so that it will see
* the timer has expired when entering the guest.
*/
kvm_vcpu_kick(vcpu);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
marc zyngier | marc zyngier | 36 | 94.74% | 1 | 50.00% |
christoffer dall | christoffer dall | 2 | 5.26% | 1 | 50.00% |
| Total | 38 | 100.00% | 2 | 100.00% |
static u64 kvm_timer_compute_delta(struct kvm_vcpu *vcpu)
{
u64 cval, now;
cval = vcpu->arch.timer_cpu.cntv_cval;
now = kvm_phys_timer_read() - vcpu->kvm->arch.timer.cntvoff;
if (now < cval) {
u64 ns;
ns = cyclecounter_cyc2ns(timecounter->cc,
cval - now,
timecounter->mask,
&timecounter->frac);
return ns;
}
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
marc zyngier | marc zyngier | 79 | 98.75% | 1 | 50.00% |
thomas gleixner | thomas gleixner | 1 | 1.25% | 1 | 50.00% |
| Total | 80 | 100.00% | 2 | 100.00% |
static enum hrtimer_restart kvm_timer_expire(struct hrtimer *hrt)
{
struct arch_timer_cpu *timer;
struct kvm_vcpu *vcpu;
u64 ns;
timer = container_of(hrt, struct arch_timer_cpu, timer);
vcpu = container_of(timer, struct kvm_vcpu, arch.timer_cpu);
/*
* Check that the timer has really expired from the guest's
* PoV (NTP on the host may have forced it to expire
* early). If we should have slept longer, restart it.
*/
ns = kvm_timer_compute_delta(vcpu);
if (unlikely(ns)) {
hrtimer_forward_now(hrt, ns_to_ktime(ns));
return HRTIMER_RESTART;
}
schedule_work(&timer->expired);
return HRTIMER_NORESTART;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
marc zyngier | marc zyngier | 91 | 98.91% | 2 | 66.67% |
bhaktipriya shridhar | bhaktipriya shridhar | 1 | 1.09% | 1 | 33.33% |
| Total | 92 | 100.00% | 3 | 100.00% |
static bool kvm_timer_irq_can_fire(struct kvm_vcpu *vcpu)
{
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
return !(timer->cntv_ctl & ARCH_TIMER_CTRL_IT_MASK) &&
(timer->cntv_ctl & ARCH_TIMER_CTRL_ENABLE);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
christoffer dall | christoffer dall | 41 | 100.00% | 2 | 100.00% |
| Total | 41 | 100.00% | 2 | 100.00% |
bool kvm_timer_should_fire(struct kvm_vcpu *vcpu)
{
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
u64 cval, now;
if (!kvm_timer_irq_can_fire(vcpu))
return false;
cval = timer->cntv_cval;
now = kvm_phys_timer_read() - vcpu->kvm->arch.timer.cntvoff;
return cval <= now;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
christoffer dall | christoffer dall | 63 | 98.44% | 2 | 66.67% |
thomas gleixner | thomas gleixner | 1 | 1.56% | 1 | 33.33% |
| Total | 64 | 100.00% | 3 | 100.00% |
static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level)
{
int ret;
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
BUG_ON(!vgic_initialized(vcpu->kvm));
timer->active_cleared_last = false;
timer->irq.level = new_level;
trace_kvm_timer_update_irq(vcpu->vcpu_id, timer->irq.irq,
timer->irq.level);
ret = kvm_vgic_inject_mapped_irq(vcpu->kvm, vcpu->vcpu_id,
timer->irq.irq,
timer->irq.level);
WARN_ON(ret);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
christoffer dall | christoffer dall | 91 | 88.35% | 2 | 50.00% |
andre przywara | andre przywara | 6 | 5.83% | 1 | 25.00% |
marc zyngier | marc zyngier | 6 | 5.83% | 1 | 25.00% |
| Total | 103 | 100.00% | 4 | 100.00% |
/*
* Check if there was a change in the timer state (should we raise or lower
* the line level to the GIC).
*/
static int kvm_timer_update_state(struct kvm_vcpu *vcpu)
{
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
/*
* If userspace modified the timer registers via SET_ONE_REG before
* the vgic was initialized, we mustn't set the timer->irq.level value
* because the guest would never see the interrupt. Instead wait
* until we call this function from kvm_timer_flush_hwstate.
*/
if (!vgic_initialized(vcpu->kvm) || !timer->enabled)
return -ENODEV;
if (kvm_timer_should_fire(vcpu) != timer->irq.level)
kvm_timer_update_irq(vcpu, !timer->irq.level);
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
christoffer dall | christoffer dall | 63 | 88.73% | 2 | 66.67% |
andre przywara | andre przywara | 8 | 11.27% | 1 | 33.33% |
| Total | 71 | 100.00% | 3 | 100.00% |
/*
* Schedule the background timer before calling kvm_vcpu_block, so that this
* thread is removed from its waitqueue and made runnable when there's a timer
* interrupt to handle.
*/
void kvm_timer_schedule(struct kvm_vcpu *vcpu)
{
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
BUG_ON(timer_is_armed(timer));
/*
* No need to schedule a background timer if the guest timer has
* already expired, because kvm_vcpu_block will return before putting
* the thread to sleep.
*/
if (kvm_timer_should_fire(vcpu))
return;
/*
* If the timer is not capable of raising interrupts (disabled or
* masked), then there's no more work for us to do.
*/
if (!kvm_timer_irq_can_fire(vcpu))
return;
/* The timer has not yet expired, schedule a background timer */
timer_arm(timer, kvm_timer_compute_delta(vcpu));
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
christoffer dall | christoffer dall | 56 | 93.33% | 1 | 50.00% |
marc zyngier | marc zyngier | 4 | 6.67% | 1 | 50.00% |
| Total | 60 | 100.00% | 2 | 100.00% |
void kvm_timer_unschedule(struct kvm_vcpu *vcpu)
{
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
timer_disarm(timer);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
christoffer dall | christoffer dall | 27 | 100.00% | 1 | 100.00% |
| Total | 27 | 100.00% | 1 | 100.00% |
/**
* kvm_timer_flush_hwstate - prepare to move the virt timer to the cpu
* @vcpu: The vcpu pointer
*
* Check if the virtual timer has expired while we were running in the host,
* and inject an interrupt if that was the case.
*/
void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
{
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
bool phys_active;
int ret;
if (kvm_timer_update_state(vcpu))
return;
/*
* If we enter the guest with the virtual input level to the VGIC
* asserted, then we have already told the VGIC what we need to, and
* we don't need to exit from the guest until the guest deactivates
* the already injected interrupt, so therefore we should set the
* hardware active state to prevent unnecessary exits from the guest.
*
* Also, if we enter the guest with the virtual timer interrupt active,
* then it must be active on the physical distributor, because we set
* the HW bit and the guest must be able to deactivate the virtual and
* physical interrupt at the same time.
*
* Conversely, if the virtual input level is deasserted and the virtual
* interrupt is not active, then always clear the hardware active state
* to ensure that hardware interrupts from the timer triggers a guest
* exit.
*/
phys_active = timer->irq.level ||
kvm_vgic_map_is_active(vcpu, timer->irq.irq);
/*
* We want to avoid hitting the (re)distributor as much as
* possible, as this is a potentially expensive MMIO access
* (not to mention locks in the irq layer), and a solution for
* this is to cache the "active" state in memory.
*
* Things to consider: we cannot cache an "active set" state,
* because the HW can change this behind our back (it becomes
* "clear" in the HW). We must then restrict the caching to
* the "clear" state.
*
* The cache is invalidated on:
* - vcpu put, indicating that the HW cannot be trusted to be
* in a sane state on the next vcpu load,
* - any change in the interrupt state
*
* Usage conditions:
* - cached value is "active clear"
* - value to be programmed is "active clear"
*/
if (timer->active_cleared_last && !phys_active)
return;
ret = irq_set_irqchip_state(host_vtimer_irq,
IRQCHIP_STATE_ACTIVE,
phys_active);
WARN_ON(ret);
timer->active_cleared_last = !phys_active;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
christoffer dall | christoffer dall | 41 | 45.56% | 5 | 50.00% |
marc zyngier | marc zyngier | 40 | 44.44% | 2 | 20.00% |
andre przywara | andre przywara | 9 | 10.00% | 3 | 30.00% |
| Total | 90 | 100.00% | 10 | 100.00% |
/**
* kvm_timer_sync_hwstate - sync timer state from cpu
* @vcpu: The vcpu pointer
*
* Check if the virtual timer has expired while we were running in the guest,
* and inject an interrupt if that was the case.
*/
void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu)
{
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
BUG_ON(timer_is_armed(timer));
/*
* The guest could have modified the timer registers or the timer
* could have expired, update the timer state.
*/
kvm_timer_update_state(vcpu);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
marc zyngier | marc zyngier | 22 | 61.11% | 1 | 33.33% |
christoffer dall | christoffer dall | 14 | 38.89% | 2 | 66.67% |
| Total | 36 | 100.00% | 3 | 100.00% |
int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu,
const struct kvm_irq_level *irq)
{
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
/*
* The vcpu timer irq number cannot be determined in
* kvm_timer_vcpu_init() because it is called much before
* kvm_vcpu_set_target(). To handle this, we determine
* vcpu timer irq number when the vcpu is reset.
*/
timer->irq.irq = irq->irq;
/*
* The bits in CNTV_CTL are architecturally reset to UNKNOWN for ARMv8
* and to 0 for ARMv7. We provide an implementation that always
* resets the timer to be disabled and unmasked and is compliant with
* the ARMv7 architecture.
*/
timer->cntv_ctl = 0;
kvm_timer_update_state(vcpu);
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
anup patel | anup patel | 34 | 62.96% | 1 | 16.67% |
christoffer dall | christoffer dall | 18 | 33.33% | 4 | 66.67% |
marc zyngier | marc zyngier | 2 | 3.70% | 1 | 16.67% |
| Total | 54 | 100.00% | 6 | 100.00% |
void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu)
{
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
INIT_WORK(&timer->expired, kvm_timer_inject_irq_work);
hrtimer_init(&timer->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
timer->timer.function = kvm_timer_expire;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
marc zyngier | marc zyngier | 52 | 100.00% | 1 | 100.00% |
| Total | 52 | 100.00% | 1 | 100.00% |
static void kvm_timer_init_interrupt(void *info)
{
enable_percpu_irq(host_vtimer_irq, host_vtimer_irq_flags);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
marc zyngier | marc zyngier | 16 | 94.12% | 2 | 66.67% |
anup patel | anup patel | 1 | 5.88% | 1 | 33.33% |
| Total | 17 | 100.00% | 3 | 100.00% |
int kvm_arm_timer_set_reg(struct kvm_vcpu *vcpu, u64 regid, u64 value)
{
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
switch (regid) {
case KVM_REG_ARM_TIMER_CTL:
timer->cntv_ctl = value;
break;
case KVM_REG_ARM_TIMER_CNT:
vcpu->kvm->arch.timer.cntvoff = kvm_phys_timer_read() - value;
break;
case KVM_REG_ARM_TIMER_CVAL:
timer->cntv_cval = value;
break;
default:
return -1;
}
kvm_timer_update_state(vcpu);
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
andre przywara | andre przywara | 81 | 94.19% | 1 | 50.00% |
christoffer dall | christoffer dall | 5 | 5.81% | 1 | 50.00% |
| Total | 86 | 100.00% | 2 | 100.00% |
u64 kvm_arm_timer_get_reg(struct kvm_vcpu *vcpu, u64 regid)
{
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
switch (regid) {
case KVM_REG_ARM_TIMER_CTL:
return timer->cntv_ctl;
case KVM_REG_ARM_TIMER_CNT:
return kvm_phys_timer_read() - vcpu->kvm->arch.timer.cntvoff;
case KVM_REG_ARM_TIMER_CVAL:
return timer->cntv_cval;
}
return (u64)-1;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
andre przywara | andre przywara | 71 | 100.00% | 1 | 100.00% |
| Total | 71 | 100.00% | 1 | 100.00% |
static int kvm_timer_starting_cpu(unsigned int cpu)
{
kvm_timer_init_interrupt(NULL);
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
marc zyngier | marc zyngier | 12 | 66.67% | 1 | 50.00% |
richard cochran | richard cochran | 6 | 33.33% | 1 | 50.00% |
| Total | 18 | 100.00% | 2 | 100.00% |
static int kvm_timer_dying_cpu(unsigned int cpu)
{
disable_percpu_irq(host_vtimer_irq);
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
richard cochran | richard cochran | 10 | 55.56% | 1 | 33.33% |
marc zyngier | marc zyngier | 7 | 38.89% | 1 | 33.33% |
anup patel | anup patel | 1 | 5.56% | 1 | 33.33% |
| Total | 18 | 100.00% | 3 | 100.00% |
int kvm_timer_hyp_init(void)
{
struct arch_timer_kvm_info *info;
int err;
info = arch_timer_get_kvm_info();
timecounter = &info->timecounter;
if (!timecounter->cc) {
kvm_err("kvm_arch_timer: uninitialized timecounter\n");
return -ENODEV;
}
if (info->virtual_irq <= 0) {
kvm_err("kvm_arch_timer: invalid virtual timer IRQ: %d\n",
info->virtual_irq);
return -ENODEV;
}
host_vtimer_irq = info->virtual_irq;
host_vtimer_irq_flags = irq_get_trigger_type(host_vtimer_irq);
if (host_vtimer_irq_flags != IRQF_TRIGGER_HIGH &&
host_vtimer_irq_flags != IRQF_TRIGGER_LOW) {
kvm_err("Invalid trigger for IRQ%d, assuming level low\n",
host_vtimer_irq);
host_vtimer_irq_flags = IRQF_TRIGGER_LOW;
}
err = request_percpu_irq(host_vtimer_irq, kvm_arch_timer_handler,
"kvm guest timer", kvm_get_running_vcpus());
if (err) {
kvm_err("kvm_arch_timer: can't request interrupt %d (%d)\n",
host_vtimer_irq, err);
return err;
}
kvm_info("virtual timer IRQ%d\n", host_vtimer_irq);
cpuhp_setup_state(CPUHP_AP_KVM_ARM_TIMER_STARTING,
"kvm/arm/timer:starting", kvm_timer_starting_cpu,
kvm_timer_dying_cpu);
return err;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
marc zyngier | marc zyngier | 104 | 66.24% | 2 | 28.57% |
julien grall | julien grall | 26 | 16.56% | 1 | 14.29% |
christoffer dall | christoffer dall | 18 | 11.46% | 1 | 14.29% |
richard cochran | richard cochran | 5 | 3.18% | 1 | 14.29% |
paolo bonzini | paolo bonzini | 3 | 1.91% | 1 | 14.29% |
thomas gleixner | thomas gleixner | 1 | 0.64% | 1 | 14.29% |
| Total | 157 | 100.00% | 7 | 100.00% |
void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu)
{
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
timer_disarm(timer);
kvm_vgic_unmap_phys_irq(vcpu, timer->irq.irq);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
marc zyngier | marc zyngier | 35 | 92.11% | 2 | 66.67% |
andre przywara | andre przywara | 3 | 7.89% | 1 | 33.33% |
| Total | 38 | 100.00% | 3 | 100.00% |
int kvm_timer_enable(struct kvm_vcpu *vcpu)
{
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
struct irq_desc *desc;
struct irq_data *data;
int phys_irq;
int ret;
if (timer->enabled)
return 0;
/*
* Find the physical IRQ number corresponding to the host_vtimer_irq
*/
desc = irq_to_desc(host_vtimer_irq);
if (!desc) {
kvm_err("%s: no interrupt descriptor\n", __func__);
return -EINVAL;
}
data = irq_desc_get_irq_data(desc);
while (data->parent_data)
data = data->parent_data;
phys_irq = data->hwirq;
/*
* Tell the VGIC that the virtual interrupt is tied to a
* physical interrupt. We do that once per VCPU.
*/
ret = kvm_vgic_map_phys_irq(vcpu, timer->irq.irq, phys_irq);
if (ret)
return ret;
timer->enabled = 1;
return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
christoffer dall | christoffer dall | 122 | 93.85% | 2 | 66.67% |
marc zyngier | marc zyngier | 8 | 6.15% | 1 | 33.33% |
| Total | 130 | 100.00% | 3 | 100.00% |
void kvm_timer_init(struct kvm *kvm)
{
kvm->arch.timer.cntvoff = kvm_phys_timer_read();
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
christoffer dall | christoffer dall | 12 | 57.14% | 1 | 50.00% |
marc zyngier | marc zyngier | 9 | 42.86% | 1 | 50.00% |
| Total | 21 | 100.00% | 2 | 100.00% |
/*
* On VHE system, we only need to configure trap on physical timer and counter
* accesses in EL0 and EL1 once, not for every world switch.
* The host kernel runs at EL2 with HCR_EL2.TGE == 1,
* and this makes those bits have no effect for the host kernel execution.
*/
void kvm_timer_init_vhe(void)
{
/* When HCR_EL2.E2H ==1, EL1PCEN and EL1PCTEN are shifted by 10 */
u32 cnthctl_shift = 10;
u64 val;
/*
* Disallow physical timer access for the guest.
* Physical counter access is allowed.
*/
val = read_sysreg(cnthctl_el2);
val &= ~(CNTHCTL_EL1PCEN << cnthctl_shift);
val |= (CNTHCTL_EL1PCTEN << cnthctl_shift);
write_sysreg(val, cnthctl_el2);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
jintack | jintack | 48 | 100.00% | 1 | 100.00% |
| Total | 48 | 100.00% | 1 | 100.00% |
Overall Contributors
| Person | Tokens | Prop | Commits | CommitProp |
marc zyngier | marc zyngier | 733 | 44.59% | 6 | 20.00% |
christoffer dall | christoffer dall | 584 | 35.52% | 11 | 36.67% |
andre przywara | andre przywara | 178 | 10.83% | 4 | 13.33% |
jintack | jintack | 52 | 3.16% | 1 | 3.33% |
anup patel | anup patel | 39 | 2.37% | 1 | 3.33% |
julien grall | julien grall | 26 | 1.58% | 1 | 3.33% |
richard cochran | richard cochran | 21 | 1.28% | 1 | 3.33% |
thomas gleixner | thomas gleixner | 4 | 0.24% | 2 | 6.67% |
paolo bonzini | paolo bonzini | 3 | 0.18% | 1 | 3.33% |
mark rutland | mark rutland | 3 | 0.18% | 1 | 3.33% |
bhaktipriya shridhar | bhaktipriya shridhar | 1 | 0.06% | 1 | 3.33% |
| Total | 1644 | 100.00% | 30 | 100.00% |
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.