[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Minios-devel] [UNIKRAFT PATCH v2 8/8] plat/kvm: Add KVM (x86_64) timer support



Hi,

I have a couple of questions inline.

Simon Kuenzer <simon.kuenzer@xxxxxxxxx> writes:

> From: Costin Lupu <costin.lupu@xxxxxxxxx>
>
> We are using TSC clock as main timer on KVM.
>
> Signed-off-by: Costin Lupu <costin.lupu@xxxxxxxxx>
> ---
>  plat/kvm/Config.uk              |   1 +
>  plat/kvm/Makefile.uk            |   2 +
>  plat/kvm/include/kvm/tscclock.h |  42 +++++
>  plat/kvm/irq.c                  |  10 ++
>  plat/kvm/time.c                 |  65 ++++++++
>  plat/kvm/tscclock.c             | 359 
> ++++++++++++++++++++++++++++++++++++++++
>  6 files changed, 479 insertions(+)
>  create mode 100644 plat/kvm/include/kvm/tscclock.h
>  create mode 100644 plat/kvm/time.c
>  create mode 100644 plat/kvm/tscclock.c
>
> diff --git a/plat/kvm/Config.uk b/plat/kvm/Config.uk
> index 449c381..622c4eb 100644
> --- a/plat/kvm/Config.uk
> +++ b/plat/kvm/Config.uk
> @@ -4,6 +4,7 @@ menuconfig PLAT_KVM
>         depends on (ARCH_X86_64)
>         select LIBUKDEBUG
>         select LIBUKALLOC
> +       select LIBUKTIMECONV
>         select LIBNOLIBC if !HAVE_LIBC
>         help
>                  Create a Unikraft image that runs as a KVM guest
> diff --git a/plat/kvm/Makefile.uk b/plat/kvm/Makefile.uk
> index 46258ff..dcfa517 100644
> --- a/plat/kvm/Makefile.uk
> +++ b/plat/kvm/Makefile.uk
> @@ -34,4 +34,6 @@ LIBKVMPLAT_SRCS-$(ARCH_X86_64) += 
> $(LIBKVMPLAT_BASE)/x86/intctrl.c
>  LIBKVMPLAT_SRCS-y              += $(LIBKVMPLAT_BASE)/shutdown.c
>  LIBKVMPLAT_SRCS-y              += $(LIBKVMPLAT_BASE)/memory.c
>  LIBKVMPLAT_SRCS-y              += $(LIBKVMPLAT_BASE)/irq.c
> +LIBKVMPLAT_SRCS-y              += $(LIBKVMPLAT_BASE)/time.c
> +LIBKVMPLAT_SRCS-y              += $(LIBKVMPLAT_BASE)/tscclock.c
>  LIBKVMPLAT_SRCS-y              += $(UK_PLAT_COMMON_BASE)/lcpu.c|common
> diff --git a/plat/kvm/include/kvm/tscclock.h b/plat/kvm/include/kvm/tscclock.h
> new file mode 100644
> index 0000000..27d0e02
> --- /dev/null
> +++ b/plat/kvm/include/kvm/tscclock.h
> @@ -0,0 +1,42 @@
> +/* SPDX-License-Identifier: BSD-3-Clause */
> +/*
> + * Authors: Costin Lupu <costin.lupu@xxxxxxxxx>
> + *
> + * Copyright (c) 2018, NEC Europe Ltd., NEC Corporation. All rights reserved.
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions
> + * are met:
> + *
> + * 1. Redistributions of source code must retain the above copyright
> + *    notice, this list of conditions and the following disclaimer.
> + * 2. 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.
> + * 3. Neither the name of the copyright holder 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 HOLDER 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.
> + *
> + * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY.
> + */
> +
> +#ifndef __KVM_TSCCLOCK_H__
> +#define __KVM_TSCCLOCK_H__
> +
> +int tscclock_init(void);
> +__u64 tscclock_monotonic(void);
> +__u64 tscclock_epochoffset(void);
> +
> +#endif /* __KVM_TSCCLOCK_H__ */
> diff --git a/plat/kvm/irq.c b/plat/kvm/irq.c
> index b3d8aaa..ab8d0e5 100644
> --- a/plat/kvm/irq.c
> +++ b/plat/kvm/irq.c
> @@ -71,12 +71,22 @@ int ukplat_irq_register(unsigned long irq, 
> irq_handler_func_t func, void *arg)
>       return 0;
>  }
>  
> +/*
> + * TODO: This is a temporary solution used to identify non TSC clock
> + * interrupts in order to stop waiting for interrupts with deadline.
> + */
> +extern long nontsc_interrupt_assert;
> +
>  void _ukplat_irq_handle(unsigned long irq)
>  {
>       struct irq_handler *h;
>       int handled = 0;
>  
>       UK_SLIST_FOREACH(h, &irq_handlers[irq], entries) {
> +             /* TODO define platform wise macro for timer IRQ number */
> +             if (irq != 0)
> +                     nontsc_interrupt_assert = 1;
> +
>               if (h->func(h->arg) == 1) {
>                       handled = 1;
>                       break;
> diff --git a/plat/kvm/time.c b/plat/kvm/time.c
> new file mode 100644
> index 0000000..1fb48bf
> --- /dev/null
> +++ b/plat/kvm/time.c
> @@ -0,0 +1,65 @@
> +/* SPDX-License-Identifier: ISC */
> +/*
> + * Authors: Dan Williams
> + *          Martin Lucina
> + *          Ricardo Koller
> + *          Costin Lupu <costin.lupu@xxxxxxxxx>
> + *
> + * Copyright (c) 2015-2017 IBM
> + * Copyright (c) 2016-2017 Docker, Inc.
> + * Copyright (c) 2018, NEC Europe Ltd., NEC Corporation
> + *
> + * Permission to use, copy, modify, and/or distribute this software
> + * for any purpose with or without fee is hereby granted, provided
> + * that the above copyright notice and this permission notice appear
> + * in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
> + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
> + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
> + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
> + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
> + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
> + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
> + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +/* Taken from solo5 time.c */
> +
> +#include <stdlib.h>
> +#include <uk/plat/time.h>
> +#include <uk/plat/irq.h>
> +#include <kvm/tscclock.h>
> +#include <uk/assert.h>
> +
> +
> +/* return ns since time_init() */
> +__nsec ukplat_monotonic_clock(void)
> +{
> +     return tscclock_monotonic();
> +}
> +
> +/* return wall time in nsecs */
> +__nsec ukplat_clock_wall(void)
> +{
> +     return tscclock_monotonic() + tscclock_epochoffset();
> +}
> +
> +static int timer_handler(void *arg __unused)
> +{
> +     /* Yes, we handled the irq. */
> +     return 1;
> +}
Do I understand correctly, that timer is needed only to have somebody to
wake cpu up from halt(), so it can check if it is enough to loop in halt?

> +
> +/* must be called before interrupts are enabled */
> +void ukplat_time_init(void)
> +{
> +     int rc;
> +
> +     rc = ukplat_irq_register(0, timer_handler, NULL);
> +     if (rc < 0)
> +             UK_CRASH("Failed to register timer interrupt handler\n");
> +
> +     rc = tscclock_init();
> +     if (rc < 0)
> +             UK_CRASH("Failed to initialize TSCCLOCK\n");
> +}
> diff --git a/plat/kvm/tscclock.c b/plat/kvm/tscclock.c
> new file mode 100644
> index 0000000..9ca5b95
> --- /dev/null
> +++ b/plat/kvm/tscclock.c
> @@ -0,0 +1,359 @@
> +/* SPDX-License-Identifier: ISC */
> +/*
> + * Authors: Dan Williams
> + *          Martin Lucina
> + *          Ricardo Koller
> + *          Costin Lupu <costin.lupu@xxxxxxxxx>
> + *
> + * Copyright (c) 2015-2017 IBM
> + * Copyright (c) 2016-2017 Docker, Inc.
> + * Copyright (c) 2018, NEC Europe Ltd., NEC Corporation
> + *
> + * Permission to use, copy, modify, and/or distribute this software
> + * for any purpose with or without fee is hereby granted, provided
> + * that the above copyright notice and this permission notice appear
> + * in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
> + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
> + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
> + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
> + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
> + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
> + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
> + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +/* Taken from solo5 tscclock.c */
> +
> +/*-
> + * Copyright (c) 2014, 2015 Antti Kantee.  All Rights Reserved.
> + * Copyright (c) 2015 Martin Lucina.  All Rights Reserved.
> + * Modified for solo5 by Ricardo Koller <kollerr@xxxxxxxxxx>
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions
> + * are met:
> + * 1. Redistributions of source code must retain the above copyright
> + *    notice, this list of conditions and the following disclaimer.
> + * 2. 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.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
> + */
> +
> +#include <uk/plat/lcpu.h>
> +#include <uk/plat/time.h>
> +#include <x86/cpu.h>
> +#include <uk/timeconv.h>
> +#include <uk/print.h>
> +#include <uk/assert.h>
> +
> +#define NSEC_PER_SEC         1000000000ULL
> +
> +#define TIMER_CNTR           0x40
> +#define TIMER_MODE           0x43
> +#define TIMER_SEL0           0x00
> +#define TIMER_LATCH          0x00
> +#define TIMER_RATEGEN        0x04
> +#define TIMER_ONESHOT        0x08
> +#define TIMER_16BIT          0x30
> +#define TIMER_HZ             1193182
> +
> +#define      RTC_COMMAND          0x70
> +#define      RTC_DATA             0x71
> +#define RTC_NMI_DISABLE      (1<<8)
> +#define RTC_NMI_ENABLE       0
> +#define      RTC_SEC              0x00
> +#define      RTC_MIN              0x02
> +#define      RTC_HOUR             0x04
> +#define      RTC_DAY              0x07
> +#define      RTC_MONTH            0x08
> +#define      RTC_YEAR             0x09
> +#define      RTC_STATUS_A         0x0a
> +#define      RTC_UIP              (1<<7)
> +
> +/* RTC wall time offset at monotonic time base. */
> +static __u64 rtc_epochoffset;
> +
> +/*
> + * TSC clock specific.
> + */
> +
> +/* Base time values at the last call to tscclock_monotonic(). */
> +static __u64 time_base;
> +static __u64 tsc_base;
> +
> +/* Multiplier for converting TSC ticks to nsecs. (0.32) fixed point. */
> +static __u32 tsc_mult;
> +
> +/*
> + * Multiplier for converting nsecs to PIT ticks. (1.32) fixed point.
> + *
> + * Calculated as:
> + *
> + *     f = NSEC_PER_SEC / TIMER_HZ   (0.31) fixed point.
> + *     pit_mult = 1 / f              (1.32) fixed point.
> + */
> +static const __u32 pit_mult =
> +     (1ULL << 63) / ((NSEC_PER_SEC << 31) / TIMER_HZ);
> +
> +
> +/*
> + * Read the current i8254 channel 0 tick count.
> + */
> +static unsigned int i8254_gettick(void)
> +{
> +     __u16 rdval;
> +
> +     outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
> +     rdval  = inb(TIMER_CNTR);
> +     rdval |= (inb(TIMER_CNTR) << 8);
> +     return rdval;
> +}
> +
> +/*
> + * Delay for approximately n microseconds using the i8254 channel 0 counter.
> + * Timer must be programmed appropriately before calling this function.
> + */
> +static void i8254_delay(unsigned int n)
> +{
> +     unsigned int cur_tick, initial_tick;
> +     int remaining;
> +     const unsigned long timer_rval = TIMER_HZ / 100;
> +
> +     initial_tick = i8254_gettick();
> +
> +     remaining = (unsigned long long) n * TIMER_HZ / 1000000;
> +
> +     while (remaining > 1) {
> +             cur_tick = i8254_gettick();
> +             if (cur_tick > initial_tick)
> +                     remaining -= timer_rval - (cur_tick - initial_tick);
> +             else
> +                     remaining -= initial_tick - cur_tick;
> +             initial_tick = cur_tick;
> +     }
> +}
> +
> +/*
> + * Read a RTC register. Due to PC platform braindead-ness also disables NMI.
> + */
> +static inline __u8 rtc_read(__u8 reg)
> +{
> +     outb(RTC_COMMAND, reg | RTC_NMI_DISABLE);
> +     return inb(RTC_DATA);
> +}
> +
> +/*
> + * Return current RTC time. Note that due to waiting for the update cycle to
> + * complete, this call may take some time.
> + */
> +static __u64 rtc_gettimeofday(void)
> +{
> +     struct uktimeconv_bmkclock dt;
> +     unsigned long flags;
> +
> +     flags = ukplat_lcpu_save_irqf();
> +
> +     /*
> +      * If RTC_UIP is down, we have at least 244us to obtain a
> +      * consistent reading before an update can occur.
> +      */
> +     while (rtc_read(RTC_STATUS_A) & RTC_UIP)
> +             continue;
> +
> +     dt.dt_sec = uktimeconv_bcdtobin(rtc_read(RTC_SEC));
> +     dt.dt_min = uktimeconv_bcdtobin(rtc_read(RTC_MIN));
> +     dt.dt_hour = uktimeconv_bcdtobin(rtc_read(RTC_HOUR));
> +     dt.dt_day = uktimeconv_bcdtobin(rtc_read(RTC_DAY));
> +     dt.dt_mon = uktimeconv_bcdtobin(rtc_read(RTC_MONTH));
> +     dt.dt_year = uktimeconv_bcdtobin(rtc_read(RTC_YEAR)) + 2000;
> +
> +     ukplat_lcpu_restore_irqf(flags);
> +
> +     return uktimeconv_bmkclock_to_nsec(&dt);
> +}
> +
> +/*
> + * Beturn monotonic time using TSC clock.
> + */
> +__u64 tscclock_monotonic(void)
> +{
> +     __u64 tsc_now, tsc_delta;
> +
> +     /*
> +      * Update time_base (monotonic time) and tsc_base (TSC time).
> +      */
> +     tsc_now = rdtsc();
> +     tsc_delta = tsc_now - tsc_base;
> +     time_base += mul64_32(tsc_delta, tsc_mult);
> +     tsc_base = tsc_now;
> +
> +     return time_base;
> +}
> +
> +/*
> + * Calibrate TSC and initialise TSC clock.
> + */
> +int tscclock_init(void)
> +{
> +     __u64 tsc_freq, rtc_boot;
> +
> +     /* Initialise i8254 timer channel 0 to mode 2 at 100 Hz */
> +     outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
> +     outb(TIMER_CNTR, (TIMER_HZ / 100) & 0xff);
> +     outb(TIMER_CNTR, (TIMER_HZ / 100) >> 8);
> +
> +     /*
> +      * Read RTC "time at boot". This must be done just before tsc_base is
> +      * initialised in order to get a correct offset below.
> +      */
> +     rtc_boot = rtc_gettimeofday();
> +
> +     /*
> +      * Calculate TSC frequency by calibrating against an 0.1s delay
> +      * using the i8254 timer.
> +      * TODO: Find a more elegant solution that does not require us to
> +      * to delay the boot for 100ms. Does KVM provides us a pre-calculated
> +      * TSC value?
> +      */
> +     tsc_base = rdtsc();
> +     i8254_delay(100000);
> +     tsc_freq = (rdtsc() - tsc_base) * 10;
> +     uk_printd(DLVL_INFO,
> +               "Clock source: TSC, frequency estimate is %llu Hz\n",
> +               (unsigned long long) tsc_freq);
> +
> +     /*
> +      * Calculate TSC scaling multiplier.
> +      *
> +      * (0.32) tsc_mult = NSEC_PER_SEC (32.32) / tsc_freq (32.0)
> +      */
> +     tsc_mult = (NSEC_PER_SEC << 32) / tsc_freq;
> +
> +     /*
> +      * Monotonic time begins at tsc_base (first read of TSC before
> +      * calibration).
> +      */
> +     time_base = mul64_32(tsc_base, tsc_mult);
> +
> +     /*
> +      * Compute RTC epoch offset by subtracting monotonic time_base from RTC
> +      * time at boot.
> +      */
> +     rtc_epochoffset = rtc_boot - time_base;
> +
> +     /*
> +      * Initialise i8254 timer channel 0 to mode 4 (one shot).
> +      */
> +     outb(TIMER_MODE, TIMER_SEL0 | TIMER_ONESHOT | TIMER_16BIT);
> +
> +     return 0;
> +}
> +
> +/*
> + * Return epoch offset (wall time offset to monotonic clock start).
> + */
> +__u64 tscclock_epochoffset(void)
> +{
> +     return rtc_epochoffset;
> +}
> +
> +/*
> + * Minimum delta to sleep using PIT. Programming seems to have an overhead of
> + * 3-4us, but play it safe here.
> + */
> +#define PIT_MIN_DELTA        16
> +
> +/*
> + * Returns early if any interrupts are serviced, or if the requested delay is
> + * too short. Must be called with interrupts disabled, will enable interrupts
> + * "atomically" during idle loop.
> + */
> +static void tscclock_cpu_block(__u64 until)
> +{
> +     __u64 now, delta_ns;
> +     __u64 delta_ticks;
> +     unsigned int ticks;
> +
> +     UK_ASSERT(ukplat_lcpu_irqs_disabled());
> +
> +     now = ukplat_monotonic_clock();
> +
> +     /*
> +      * Compute delta in PIT ticks. Return if it is less than minimum safe
> +      * amount of ticks.  Essentially this will cause us to spin until
> +      * the timeout.
> +      */
> +     delta_ns = until - now;
> +     delta_ticks = mul64_32(delta_ns, pit_mult);
> +     if (delta_ticks < PIT_MIN_DELTA) {
> +             /*
> +              * Since we are "spinning", quickly enable interrupts in
> +              * the hopes that we might get new work and can do something
> +              * else than spin.
> +              */
> +             ukplat_lcpu_enable_irq();
> +             nop(); /* ints are enabled 1 instr after sti */
> +             ukplat_lcpu_disable_irq();
> +             return;
> +     }
> +
> +    /*
> +     * Program the timer to interrupt the CPU after the delay has expired.
> +     * Maximum timer delay is 65535 ticks.
> +     */
> +     if (delta_ticks > 65535)
> +             ticks = 65535;
> +     else
> +             ticks = delta_ticks;
> +
> +     /*
> +      * Note that according to the Intel 82C54 datasheet, p12 the
> +      * interrupt is actually delivered in N + 1 ticks.
> +      */
> +     ticks -= 1;
> +     outb(TIMER_CNTR, ticks & 0xff);
> +     outb(TIMER_CNTR, ticks >> 8);
> +
> +     /*
> +      * Wait for any interrupt. If we got an interrupt then
> +      * just return into the scheduler which will check if there is
> +      * work to do and send us back here if not.
Looks like we return into scheduler only if the function was called from
schedule() function. Is that right?

> +      *
> +      * TODO: It would be more efficient for longer sleeps to be
> +      * able to distinguish if the interrupt was the PIT interrupt
> +      * and no other, but this will do for now.
> +      */
> +     ukplat_lcpu_halt_irq();
> +}
> +
> +long nontsc_interrupt_assert;
> +
> +void time_block_until(__snsec until)
> +{
> +     volatile long *pnontsc_interrupt_assert = &nontsc_interrupt_assert;
> +
> +     while ((__snsec) ukplat_monotonic_clock() < until) {
> +             tscclock_cpu_block(until);
> +
> +             /* who triggered the interrupt? */
> +             if (*pnontsc_interrupt_assert) {
1) Why assessing via pointer? Why we can not just check directly?

2) Why is it needed at all? Is it for interrupts, which have actual
handlers in a thread?

> +                     /* it was another device, stop blocking */
> +                     nontsc_interrupt_assert = 0;
> +                     break;
> +             }
> +             /* it was us */
> +     }
> +}
> -- 
> 2.7.4
>

-- 
Yuri Volchkov
Software Specialist

NEC Europe Ltd
Kurfürsten-Anlage 36
D-69115 Heidelberg

_______________________________________________
Minios-devel mailing list
Minios-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/minios-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.