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

Re: [Minios-devel] [UNIKRAFT PATCH 3/8] lib/uksched: Introduce thread attributes



Hi Costin,

Please see a few comments inline.


On 9/18/18, 5:27 PM, "Costin Lupu" <costin.lupu@xxxxxxxxx> wrote:

>We introduce thread attributes abstraction which is used for
>configuring thread behaviors. The set of currently available
>attributes was inspired from the pthread interface.

s/thread attributes/a thread attributes
s/thread behaviors/thread behavior.

>Thread attributes can be set either when creating the threads or
>during threads execution by calling the corresponding setters. If
>an attribute is not supported by the underlying scheduler then
>-EINVAL is returned by setters.

s/or during threads/or during thread

>The detach attribute is common to all threads, regardless the
>scheduling policy, and if set it means that the thread resources
>will be automatically freed on thread exit. By default, threads
>are not detached. The complete detach support will be added in a
>following commit.

s/regardless/regardless of

>Time slice and priority make sense only for preemptive schedulers.
>
>Signed-off-by: Costin Lupu <costin.lupu@xxxxxxxxx>
>---
> lib/uksched/Makefile.uk              |   1 +
> lib/uksched/exportsyms.uk            |  12 +++
> lib/uksched/include/uk/sched.h       |  75 +++++++++++++++++--
> lib/uksched/include/uk/thread.h      |  12 ++-
> lib/uksched/include/uk/thread_attr.h |  73 +++++++++++++++++++
> lib/uksched/sched.c                  |   5 +-
> lib/uksched/thread.c                 |  34 +++++++++
> lib/uksched/thread_attr.c            | 137
>+++++++++++++++++++++++++++++++++++
> lib/ukschedcoop/schedcoop.c          |   6 +-
> 9 files changed, 345 insertions(+), 10 deletions(-)
> create mode 100644 lib/uksched/include/uk/thread_attr.h
> create mode 100644 lib/uksched/thread_attr.c
>
>diff --git a/lib/uksched/Makefile.uk b/lib/uksched/Makefile.uk
>index 24816f6..229d847 100644
>--- a/lib/uksched/Makefile.uk
>+++ b/lib/uksched/Makefile.uk
>@@ -5,3 +5,4 @@ CXXINCLUDES-$(CONFIG_LIBUKSCHED)   +=
>-I$(LIBUKSCHED_BASE)/include
> 
> LIBUKSCHED_SRCS-y += $(LIBUKSCHED_BASE)/sched.c
> LIBUKSCHED_SRCS-y += $(LIBUKSCHED_BASE)/thread.c
>+LIBUKSCHED_SRCS-y += $(LIBUKSCHED_BASE)/thread_attr.c
>diff --git a/lib/uksched/exportsyms.uk b/lib/uksched/exportsyms.uk
>index 45a9694..7d8e102 100644
>--- a/lib/uksched/exportsyms.uk
>+++ b/lib/uksched/exportsyms.uk
>@@ -14,3 +14,15 @@ uk_thread_fini
> uk_thread_block_timeout
> uk_thread_block
> uk_thread_wake
>+uk_thread_set_prio
>+uk_thread_get_prio
>+uk_thread_set_timeslice
>+uk_thread_get_timeslice
>+uk_thread_attr_init
>+uk_thread_attr_fini
>+uk_thread_attr_set_detachstate
>+uk_thread_attr_get_detachstate
>+uk_thread_attr_set_prio
>+uk_thread_attr_get_prio
>+uk_thread_attr_set_timeslice
>+uk_thread_attr_get_timeslice
>diff --git a/lib/uksched/include/uk/sched.h
>b/lib/uksched/include/uk/sched.h
>index d2fc8df..b21d65c 100644
>--- a/lib/uksched/include/uk/sched.h
>+++ b/lib/uksched/include/uk/sched.h
>@@ -60,16 +60,31 @@ typedef void  (*uk_sched_yield_func_t)
>               (struct uk_sched *s);
> 
> typedef void  (*uk_sched_thread_add_func_t)
>-              (struct uk_sched *s, struct uk_thread *t);
>+              (struct uk_sched *s, struct uk_thread *t,
>+                      struct uk_thread_attr *attr);
> typedef void  (*uk_sched_thread_remove_func_t)
>               (struct uk_sched *s, struct uk_thread *t);
> 
>+typedef int   (*uk_sched_thread_set_prio_func_t)
>+              (struct uk_sched *s, struct uk_thread *t, prio_t prio);
>+typedef int   (*uk_sched_thread_get_prio_func_t)
>+              (struct uk_sched *s, const struct uk_thread *t, prio_t *prio);
>+typedef int   (*uk_sched_thread_set_tslice_func_t)
>+              (struct uk_sched *s, struct uk_thread *t, int tslice);
>+typedef int   (*uk_sched_thread_get_tslice_func_t)
>+              (struct uk_sched *s, const struct uk_thread *t, int *tslice);
>+
> struct uk_sched {
>       uk_sched_yield_func_t yield;
> 
>       uk_sched_thread_add_func_t      thread_add;
>       uk_sched_thread_remove_func_t   thread_remove;
> 
>+      uk_sched_thread_set_prio_func_t   thread_set_prio;
>+      uk_sched_thread_get_prio_func_t   thread_get_prio;
>+      uk_sched_thread_set_tslice_func_t thread_set_tslice;
>+      uk_sched_thread_get_tslice_func_t thread_get_tslice;
>+
>       /* internal */
>       struct uk_thread idle;
>       struct ukplat_ctx_callbacks plat_ctx_cbs;
>@@ -92,12 +107,12 @@ static inline void uk_sched_yield(void)
> }
> 
> static inline void uk_sched_thread_add(struct uk_sched *s,
>-              struct uk_thread *t)
>+              struct uk_thread *t, struct uk_thread_attr *attr)
> {
>       UK_ASSERT(s);
>       UK_ASSERT(t);
>       t->sched = s;
>-      s->thread_add(s, t);
>+      s->thread_add(s, t, attr);
> }
> 
> static inline void uk_sched_thread_remove(struct uk_sched *s,
>@@ -109,6 +124,49 @@ static inline void uk_sched_thread_remove(struct
>uk_sched *s,
>       t->sched = NULL;
> }
> 
>+static inline int uk_sched_thread_set_prio(struct uk_sched *s,
>+              struct uk_thread *t, prio_t prio)
>+{
>+      UK_ASSERT(s);
>+
>+      if (!s->thread_set_prio)
>+              return -EINVAL;
>+
>+      return s->thread_set_prio(s, t, prio);
>+}
>+
>+static inline int uk_sched_thread_get_prio(struct uk_sched *s,
>+              const struct uk_thread *t, prio_t *prio)
>+{
>+      UK_ASSERT(s);
>+
>+      if (!s->thread_get_prio)
>+              return -EINVAL;
>+
>+      return s->thread_get_prio(s, t, prio);
>+}
>+
>+static inline int uk_sched_thread_set_timeslice(struct uk_sched *s,
>+              struct uk_thread *t, int tslice)
>+{
>+      UK_ASSERT(s);
>+
>+      if (!s->thread_set_tslice)
>+              return -EINVAL;
>+
>+      return s->thread_set_tslice(s, t, tslice);
>+}
>+
>+static inline int uk_sched_thread_get_timeslice(struct uk_sched *s,
>+              const struct uk_thread *t, int *tslice)
>+{
>+      UK_ASSERT(s);
>+
>+      if (!s->thread_get_tslice)
>+              return -EINVAL;
>+
>+      return s->thread_get_tslice(s, t, tslice);
>+}
> 
> /*
>  * Internal scheduler functions
>@@ -132,11 +190,17 @@ static inline struct uk_thread
>*uk_sched_get_idle(struct uk_sched *s)
> void uk_sched_start(struct uk_sched *sched) __noreturn;
> 
> #define uk_sched_init(s, yield_func, \
>-              thread_add_func, thread_remove_func) \
>+              thread_add_func, thread_remove_func, \
>+              thread_set_prio_func, thread_get_prio_func, \
>+              thread_set_tslice_func, thread_get_tslice_func) \
>       do { \
>               (s)->yield           = yield_func; \
>               (s)->thread_add      = thread_add_func; \
>               (s)->thread_remove   = thread_remove_func; \
>+              (s)->thread_set_prio    = thread_set_prio_func; \
>+              (s)->thread_get_prio    = thread_get_prio_func; \
>+              (s)->thread_set_tslice  = thread_set_tslice_func; \
>+              (s)->thread_get_tslice  = thread_get_tslice_func; \
>               uk_sched_register((s)); \
>       } while (0)
> 
>@@ -146,7 +210,8 @@ void uk_sched_start(struct uk_sched *sched)
>__noreturn;
>  */
> 
> struct uk_thread *uk_sched_thread_create(struct uk_sched *sched,
>-              const char *name, void (*function)(void *), void *arg);
>+              const char *name, struct uk_thread_attr *attr,
>+              void (*function)(void *), void *arg);
> void uk_sched_thread_destroy(struct uk_sched *sched,
>               struct uk_thread *thread);
> 
>diff --git a/lib/uksched/include/uk/thread.h
>b/lib/uksched/include/uk/thread.h
>index e66a05b..5ff2dd6 100644
>--- a/lib/uksched/include/uk/thread.h
>+++ b/lib/uksched/include/uk/thread.h
>@@ -35,6 +35,7 @@
> #include <uk/arch/lcpu.h>
> #include <uk/arch/time.h>
> #include <uk/plat/thread.h>
>+#include <uk/thread_attr.h>
> #include <uk/list.h>
> #include <uk/essentials.h>
> 
>@@ -59,11 +60,20 @@ struct uk_thread {
> 
> UK_TAILQ_HEAD(uk_thread_list, struct uk_thread);
> 
>+#define uk_thread_create_attr(name, attr, function, data) \
>+      uk_sched_thread_create(uk_sched_get_default(), \
>+                      name, attr, function, data)
> #define uk_thread_create(name, function, data) \
>-      uk_sched_thread_create(uk_sched_get_default(), name, function, data)
>+      uk_thread_create_attr(name, NULL, function, data)
> #define uk_thread_destroy(thread) \
>       uk_sched_thread_destroy(thread->sched, thread)
> 
>+int uk_thread_set_prio(struct uk_thread *thread, prio_t prio);
>+int uk_thread_get_prio(const struct uk_thread *thread, prio_t *prio);
>+
>+int uk_thread_set_timeslice(struct uk_thread *thread, int timeslice);
>+int uk_thread_get_timeslice(const struct uk_thread *thread, int
>*timeslice);
>+
> static inline
> struct uk_thread *uk_thread_current(void)
> {
>diff --git a/lib/uksched/include/uk/thread_attr.h
>b/lib/uksched/include/uk/thread_attr.h
>new file mode 100644
>index 0000000..30be708
>--- /dev/null
>+++ b/lib/uksched/include/uk/thread_attr.h
>@@ -0,0 +1,73 @@
>+/* 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 __UK_SCHED_THREAD_ATTR_H__
>+#define __UK_SCHED_THREAD_ATTR_H__
>+
>+#ifdef __cplusplus
>+extern "C" {
>+#endif
>+
>+#define UK_THREAD_ATTR_WAITABLE         0
>+#define UK_THREAD_ATTR_DETACHED         1
>+
>+#define UK_THREAD_ATTR_PRIO_MIN         0
>+#define UK_THREAD_ATTR_PRIO_MAX         255
>+#define UK_THREAD_ATTR_PRIO_DEFAULT     127
>+
>+typedef int prio_t;
>+
>+typedef struct uk_thread_attr {
>+      int is_detached;
>+      prio_t prio;
>+      int timeslice;
>+} uk_thread_attr_t;
>+
>+int uk_thread_attr_init(uk_thread_attr_t *attr);
>+int uk_thread_attr_fini(uk_thread_attr_t *attr);
>+
>+int uk_thread_attr_set_detachstate(uk_thread_attr_t *attr, int state);
>+int uk_thread_attr_get_detachstate(const uk_thread_attr_t *attr, int
>*state);
>+
>+int uk_thread_attr_set_prio(uk_thread_attr_t *attr, prio_t prio);
>+int uk_thread_attr_get_prio(const uk_thread_attr_t *attr, prio_t *prio);
>+
>+int uk_thread_attr_set_timeslice(uk_thread_attr_t *attr, int timeslice);
>+int uk_thread_attr_get_timeslice(const uk_thread_attr_t *attr, int
>*timeslice);
>+
>+#ifdef __cplusplus
>+}
>+#endif
>+
>+#endif /* __UK_SCHED_THREAD_ATTR_H__ */
>diff --git a/lib/uksched/sched.c b/lib/uksched/sched.c
>index 8276e15..f2eca33 100644
>--- a/lib/uksched/sched.c
>+++ b/lib/uksched/sched.c
>@@ -170,7 +170,8 @@ void uk_sched_idle_init(struct uk_sched *sched,
> }
> 
> struct uk_thread *uk_sched_thread_create(struct uk_sched *sched,
>-              const char *name, void (*function)(void *), void *arg)
>+              const char *name, struct uk_thread_attr *attr,
>+              void (*function)(void *), void *arg)
> {
>       struct uk_thread *thread = NULL;
>       void *stack = NULL;
>@@ -195,7 +196,7 @@ struct uk_thread *uk_sched_thread_create(struct
>uk_sched *sched,
>       if (rc)
>               goto err;
> 
>-      uk_sched_thread_add(sched, thread);
>+      uk_sched_thread_add(sched, thread, attr);
> 
>       return thread;
> 
>diff --git a/lib/uksched/thread.c b/lib/uksched/thread.c
>index 4b00b1a..a6d90fc 100644
>--- a/lib/uksched/thread.c
>+++ b/lib/uksched/thread.c
>@@ -30,9 +30,11 @@
>  * Ported from Mini-OS
>  */
> #include <stdlib.h>
>+#include <errno.h>
> #include <uk/plat/config.h>
> #include <uk/plat/time.h>
> #include <uk/thread.h>
>+#include <uk/sched.h>
> #include <uk/print.h>
> #include <uk/assert.h>
> 
>@@ -127,3 +129,35 @@ void uk_thread_wake(struct uk_thread *thread)
>       thread->wakeup_time = 0LL;
>       set_runnable(thread);
> }
>+
>+int uk_thread_set_prio(struct uk_thread *thread, prio_t prio)
>+{
>+      if (!thread)
>+              return -EINVAL;
>+
>+      return uk_sched_thread_set_prio(thread->sched, thread, prio);
>+}
>+
>+int uk_thread_get_prio(const struct uk_thread *thread, prio_t *prio)
>+{
>+      if (!thread)
>+              return -EINVAL;
>+
>+      return uk_sched_thread_get_prio(thread->sched, thread, prio);
>+}
>+
>+int uk_thread_set_timeslice(struct uk_thread *thread, int timeslice)
>+{
>+      if (!thread)
>+              return -EINVAL;
>+
>+      return uk_sched_thread_set_timeslice(thread->sched, thread, timeslice);
>+}
>+
>+int uk_thread_get_timeslice(const struct uk_thread *thread, int
>*timeslice)
>+{
>+      if (!thread)
>+              return -EINVAL;
>+
>+      return uk_sched_thread_get_timeslice(thread->sched, thread, timeslice);
>+}
>diff --git a/lib/uksched/thread_attr.c b/lib/uksched/thread_attr.c
>new file mode 100644
>index 0000000..cff1a55
>--- /dev/null
>+++ b/lib/uksched/thread_attr.c
>@@ -0,0 +1,137 @@
>+/* 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.
>+ */
>+
>+#include <stdlib.h>
>+#include <errno.h>
>+#include <uk/plat/time.h>
>+#include <uk/thread_attr.h>
>+#include <uk/assert.h>
>+
>+
>+int uk_thread_attr_init(uk_thread_attr_t *attr)
>+{
>+      if (attr == NULL)
>+              return EINVAL;
>+
>+      attr->is_detached = 0;
>+      attr->prio = -1;
>+      attr->timeslice = 0;
>+
>+      return 0;
>+}

Minor: perhaps define some macros for the 0s and -1s you have here. Also,
return -EINVAL instead of EINVAL.
Same for all uk_thread_attr_* functions below.

>+
>+int uk_thread_attr_fini(uk_thread_attr_t *attr)
>+{
>+      if (attr == NULL)
>+              return EINVAL;
>+
>+      return 0;
>+}
>+
>+int uk_thread_attr_set_detachstate(uk_thread_attr_t *attr, int state)
>+{
>+      if (attr == NULL)
>+              return EINVAL;
>+
>+      if (state == UK_THREAD_ATTR_DETACHED)
>+              attr->is_detached = 1;
>+
>+      else if (state == UK_THREAD_ATTR_WAITABLE)
>+              attr->is_detached = 0;
>+
>+      else
>+              return EINVAL;
>+
>+      return 0;
>+}

Shouldn’t you be setting attr’s members to macros, i.e., attr->is_detached
= UK_THREAD_ATTR_DETACHED ?

>+
>+int uk_thread_attr_get_detachstate(const uk_thread_attr_t *attr, int
>*state)
>+{
>+      if (attr == NULL || state == NULL)
>+              return EINVAL;
>+
>+      if (attr->is_detached)
>+              *state = UK_THREAD_ATTR_DETACHED;
>+      else
>+              *state = UK_THREAD_ATTR_WAITABLE;
>+
>+      return 0;
>+}
>+
>+int uk_thread_attr_set_prio(uk_thread_attr_t *attr, prio_t prio)
>+{
>+      int rc = EINVAL;
>+
>+      if (attr == NULL)
>+              return rc;
>+
>+      if (prio >= UK_THREAD_ATTR_PRIO_MIN &&
>+              prio <= UK_THREAD_ATTR_PRIO_MAX) {
>+              attr->prio = prio;
>+              rc = 0;
>+      }
>+
>+      return rc;
>+}
>+
>+int uk_thread_attr_get_prio(const uk_thread_attr_t *attr, prio_t *prio)
>+{
>+      if (attr == NULL || prio == NULL)
>+              return EINVAL;
>+
>+      *prio = attr->prio;
>+
>+      return 0;
>+}
>+
>+int uk_thread_attr_set_timeslice(uk_thread_attr_t *attr, int timeslice)
>+{
>+      if (attr == NULL)
>+              return EINVAL;
>+
>+      /* TODO check timeslice agains platform tick */
>+      attr->timeslice = timeslice;
>+
>+      return 0;
>+}

s/agains/against. Also, when will this be implemented?

Thanks,

— Felipe

>+
>+int uk_thread_attr_get_timeslice(const uk_thread_attr_t *attr, int
>*timeslice)
>+{
>+      if (attr == NULL || timeslice == NULL)
>+              return EINVAL;
>+
>+      *timeslice = attr->timeslice;
>+
>+      return 0;
>+}
>diff --git a/lib/ukschedcoop/schedcoop.c b/lib/ukschedcoop/schedcoop.c
>index d78ca54..bc02d08 100644
>--- a/lib/ukschedcoop/schedcoop.c
>+++ b/lib/ukschedcoop/schedcoop.c
>@@ -132,7 +132,8 @@ static void schedcoop_schedule(struct uk_sched *s)
>       }
> }
> 
>-static void schedcoop_thread_add(struct uk_sched *s, struct uk_thread *t)
>+static void schedcoop_thread_add(struct uk_sched *s, struct uk_thread *t,
>+              struct uk_thread_attr *attr __unused)
> {
>       unsigned long flags;
>       struct schedcoop_private *prv = s->prv;
>@@ -210,7 +211,8 @@ struct uk_sched *uk_schedcoop_init(struct uk_alloc *a)
>       uk_sched_init(sched,
>                       schedcoop_yield,
>                       schedcoop_thread_add,
>-                      schedcoop_thread_remove);
>+                      schedcoop_thread_remove,
>+                      NULL, NULL, NULL, NULL);
> 
>       return sched;
> }
>-- 
>2.11.0
>

_______________________________________________
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®.