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

Re: [Minios-devel] [UNIKRAFT PATCH 2/7] lib/ukconsdev: Register console device



Hey Costin,

I have some comments for this patch. But overall it looks good.

Thanks,

Simon

On 19.06.19 15:35, Birlea Costin wrote:
This patch introduces the initial consdev API supporting device
registration and data allocation.
We introduce three header files for describing Unikraft's
consdev API:
         uk/consdev_core.h - Core data type definitions
         uk/consdev_driver.h - API for drivers
         uk/consdev.h - API for console applications

Signed-off-by: Birlea Costin <costin.birlea@xxxxxxxxx>
---
  lib/ukconsdev/Makefile.uk                 |   2 +
  lib/ukconsdev/consdev.c                   | 162 ++++++++++++++++++++++++++++++
  lib/ukconsdev/exportsyms.uk               |   8 ++
  lib/ukconsdev/include/uk/consdev.h        | 145 ++++++++++++++++++++++++++
  lib/ukconsdev/include/uk/consdev_core.h   | 109 ++++++++++++++++++++
  lib/ukconsdev/include/uk/consdev_driver.h |  85 ++++++++++++++++
  6 files changed, 511 insertions(+)
  create mode 100644 lib/ukconsdev/consdev.c
  create mode 100644 lib/ukconsdev/exportsyms.uk
  create mode 100644 lib/ukconsdev/include/uk/consdev.h
  create mode 100644 lib/ukconsdev/include/uk/consdev_core.h
  create mode 100644 lib/ukconsdev/include/uk/consdev_driver.h

diff --git a/lib/ukconsdev/Makefile.uk b/lib/ukconsdev/Makefile.uk
index a1512603..b93b5d6d 100644
--- a/lib/ukconsdev/Makefile.uk
+++ b/lib/ukconsdev/Makefile.uk
@@ -2,3 +2,5 @@ $(eval $(call addlib_s,libukconsdev,$(CONFIG_LIBUKCONSDEV)))
CINCLUDES-$(CONFIG_LIBUKCONSDEV) += -I$(LIBUKCONSDEV_BASE)/include
  CXXINCLUDES-$(CONFIG_LIBUKCONSDEV) += -I$(LIBUKCONSDEV_BASE)/include
+
+LIBUKCONSDEV_SRCS-y += $(LIBUKCONSDEV_BASE)/consdev.c
diff --git a/lib/ukconsdev/consdev.c b/lib/ukconsdev/consdev.c
new file mode 100644
index 00000000..f58a3266
--- /dev/null
+++ b/lib/ukconsdev/consdev.c
@@ -0,0 +1,162 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Authors: Costin Birlea <costin.birlea@xxxxxxxxx>
+ *
+ * Copyright (c) 2019, University Politehnica of Bucharest.
+ * 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.
+ */
+/* This is derived from uknetdev because of consistency reasons */
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <uk/consdev.h>
+#include <uk/assert.h>
+#include <uk/print.h>
+
+struct uk_consdev_list uk_consdev_list =
+               UK_TAILQ_HEAD_INITIALIZER(uk_consdev_list);
+
+static uint16_t consdev_count;
+
+unsigned int uk_consdev_count(void)
+{
+       return (unsigned int) consdev_count;
+}
+
+struct uk_consdev *uk_consdev_get(unsigned int id)
+{
+       struct uk_consdev *dev;
+
+       UK_TAILQ_FOREACH(dev, &uk_consdev_list, _list) {
+               UK_ASSERT(dev->_data);
+
+               if (dev->_data->id == id)
+                       return dev;
+       }
+       return NULL;
+}
+
+uint16_t uk_consdev_id_get(struct uk_consdev *dev)
+{
+       UK_ASSERT(dev);
+       return dev->_data->id;
+}
+
+const char *uk_consdev_drv_name_get(struct uk_consdev *dev)
+{
+       UK_ASSERT(dev);
+       UK_ASSERT(dev->_data);
+       return dev->_data->drv_name;
+}
+
+enum uk_consdev_state uk_consdev_state_get(struct uk_consdev *dev)
+{
+       UK_ASSERT(dev);
+       UK_ASSERT(dev->_data);
+       return dev->_data->state;
+}
+
+static struct uk_consdev_data *_alloc_data(struct uk_alloc *a,
+               uint16_t consdev_id, const char *drv_name)
+{
+       struct uk_consdev_data *data;
+
+       data = uk_calloc(a, 1, sizeof(*data));
+       if (!data)
+               return NULL;
+
+       data->drv_name = drv_name;
+       data->state    = UK_CONSDEV_UNCONFIGURED;
+       data->a        = a;
+
+       /* This is the only place where we set the device ID;
+        * during the rest of the device's life time this ID is read-only
+        */
+       *(DECONST(uint16_t *, &data->id)) = consdev_id;
+
+       return data;
+}
+
+int uk_consdev_drv_register(struct uk_consdev *dev, struct uk_alloc *a,
+               const char *drv_name)
+{
+       UK_ASSERT(dev);
+       /* Data must be unallocated. */
+       UK_ASSERT(PTRISERR(dev->_data));
+       /* Assert mandatory configuration. */
+       UK_ASSERT(dev->ops);
+       UK_ASSERT(dev->ops->close);
+
+       dev->_data = _alloc_data(a, consdev_count, drv_name);
+       if (!dev->_data)
+               return -ENOMEM;
+
+       UK_TAILQ_INSERT_TAIL(&uk_consdev_list, dev, _list);
+       uk_pr_info("Registered consdev%"PRIu16": %p (%s)\n",
+                          consdev_count, dev, drv_name);
+
+       return consdev_count++;
+}
+
+void uk_consdev_drv_unregister(struct uk_consdev *dev)
+{
+       struct uk_alloc *a;
+       uint16_t id;
+
+       UK_ASSERT(dev);
+       UK_ASSERT(dev->_data);
+       UK_ASSERT(dev->_data->state != UK_CONSDEV_RUNNING);
+
+       id = dev->_data->id;
+       a = dev->_data->a;
+
+       uk_free(a, dev->_data);
+       UK_TAILQ_REMOVE(&uk_consdev_list, dev, _list);

I think you should remove the item from the list before freeing its memory. This could be harmful otherwise.

+       consdev_count--;
+
+       uk_pr_info("Unregistered consdev%"PRIu16": %p\n",
+                       id, dev);
+}
+
+void uk_consdev_close(struct uk_consdev *dev)
+{
+       uint16_t id;
+
+       UK_ASSERT(dev);
+       UK_ASSERT(dev->_data);
+       UK_ASSERT(dev->ops);
+       UK_ASSERT(dev->ops->close);
+       UK_ASSERT(dev->_data->state != UK_CONSDEV_RUNNING);
+
+       id = dev->_data->id;
+       dev->ops->close(dev);

Doesn't _data->state also need to be updated to enter UNCONFIGURED state?

Maybe calling this function uk_consdev_unconfigure() would be a better fit (and unconfigure for the callback).

+
+       uk_pr_info("Closed consdev%"PRIu16"\n", id);
+}
diff --git a/lib/ukconsdev/exportsyms.uk b/lib/ukconsdev/exportsyms.uk
new file mode 100644
index 00000000..60dd37ee
--- /dev/null
+++ b/lib/ukconsdev/exportsyms.uk
@@ -0,0 +1,8 @@
+uk_consdev_count
+uk_consdev_get
+uk_consdev_id_get
+uk_consdev_drv_name_get
+uk_consdev_state_get
+uk_consdev_drv_register
+uk_consdev_drv_unregister
+uk_consdev_close
\ No newline at end of file
diff --git a/lib/ukconsdev/include/uk/consdev.h 
b/lib/ukconsdev/include/uk/consdev.h
new file mode 100644
index 00000000..0e7af23b
--- /dev/null
+++ b/lib/ukconsdev/include/uk/consdev.h
@@ -0,0 +1,145 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Authors: Costin Birlea <costin.birlea@xxxxxxxxx>
+ *
+ * Copyright (c) 2019, University Politehnica of Bucharest.
+ * 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.
+ */
+/* This is derived from uknetdev because of consistency reasons */
+#ifndef __UK_CONSDEV__
+#define __UK_CONSDEV__
+
+/**
+ * Unikraft Console API
+ *
+ * The Unikraft Console API provides a generalized interface between Unikraft
+ * drivers and low-level application which needs communication with
+ * a character device.
+ *
+ * Most Console API functions take as parameter a reference to the 
corresponding
+ * Unikraft Console Device (struct uk_consdev) which can be obtained with a 
call
+ * to uk_consdev_get(). The console application should store this reference and
+ * use it for all subsequent API calls.
+ *
+ * The functions exported by the Unikraft Console API to setup a device
+ * designated by its ID must be invoked in the following order:
+ *      - uk_consdev_configure()
+ *      - uk_consdev_rx/tx_configure()
+ *      - uk_consdev_start()
+ *
+ * There are 4 states in which a console device can be found:
+ *      - UK_CONSDEV_UNREGISTERED
+ *      - UK_CONSDEV_UNCONFIGURED
+ *      - UK_CONSDEV_CONFIGURED
+ *      - UK_CONSDEV_RUNNING
+ */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <stdint.h>
+#include <uk/list.h>
+#include <uk/alloc.h>
+#include <uk/assert.h>
+#include <uk/errptr.h>
+
+#include "consdev_core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Get the number of available Unikraft Console devices.
+ *
+ * @return
+ *     - (unsigned int): number of console devices.
+ */
+unsigned int uk_consdev_count(void);
+
+/**
+ * Get a reference to a Unikraft Console Device, based on its ID.
+ * This reference should be saved by the application and used for subsequent
+ * API calls.
+ *
+ * @param id
+ *     The identifier of the Unikraft console device to configure.
+ * @return
+ *     - NULL: device not found in list
+ *     - (struct uk_consdev *): reference to be passed to API calls
+ */
+struct uk_consdev *uk_consdev_get(unsigned int id);
+
+/**
+ * Returns the id of a console device
+ *
+ * @param dev
+ *     The Unikraft Console Device.
+ * @return
+ *     - (>=0): Device ID
+ */
+uint16_t uk_consdev_id_get(struct uk_consdev *dev);
+
+/**
+ * Returns the driver name of a consdev device.
+ * The name might be set to NULL.
+ *
+ * @param dev
+ *     The Unikraft Console Device.
+ * @return
+ *     - (NULL): if no name is defined.
+ *     - (const char *): Reference to string if name is available.
+ */
+const char *uk_consdev_drv_name_get(struct uk_consdev *dev);
+
+/**
+ * Returns the current state of a consdev device.
+ *
+ * @param dev
+ *     The Unikraft Console Device.
+ * @return
+ *     - (enum uk_consdev_state): current device state
+ */
+enum uk_consdev_state uk_consdev_state_get(struct uk_consdev *dev);
+
+/**
+ * Close a stopped Unikraft console device.
+ * The function frees all resources except for
+ * the ones needed by the UK_CONSDEV_UNCONFIGURED state.
+ *
+ * @param dev
+ *     The Unikraft Console Device.
+ */
+void uk_consdev_close(struct uk_consdev *dev);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //__UK_CONSDEV__
diff --git a/lib/ukconsdev/include/uk/consdev_core.h 
b/lib/ukconsdev/include/uk/consdev_core.h
new file mode 100644
index 00000000..3694f2be
--- /dev/null
+++ b/lib/ukconsdev/include/uk/consdev_core.h
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Authors: Costin Birlea <costin.birlea@xxxxxxxxx>
+ *
+ * Copyright (c) 2019, University Politehnica of Bucharest.
+ * 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.
+ */
+/* This is derived from uknetdev because of consistency reasons */
+#ifndef __UK_CONSDEV_CORE__
+#define __UK_CONSDEV_CORE__
+
+#include <uk/list.h>
+#include <uk/config.h>
+
+/**
+ * Unikraft console API common declarations.
+ *
+ * This header contains all API data types. Some of them are part of the
+ * public API and some are part of the internal API.
+ *
+ * The device data and operations are separated. This split allows the
+ * function pointer and driver data to be per-process, while the actual
+ * configuration data for the device is shared.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct uk_consdev;
+
+/**
+ * List with devices
+ */
+UK_TAILQ_HEAD(uk_consdev_list, struct uk_consdev);
+
+/**
+ * Enum to describe the possible states of a console device.
+ */
+enum uk_consdev_state {
+       UK_CONSDEV_INVALID = 0,
+       UK_CONSDEV_UNCONFIGURED,
+       UK_CONSDEV_CONFIGURED,
+       UK_CONSDEV_RUNNING,
+};
+
+/** Driver callback type to close an Unikraft console device. */
+typedef void (*uk_consdev_close_t)(struct uk_consdev *dev);
+
+struct uk_consdev_ops {
+       uk_consdev_close_t                  close;
+};
+
+/**
+ * @internal
+ * libukconsdev internal data associated with each console device.
+ */
+struct uk_consdev_data {
+       /* Device id identifier */
+       const uint16_t id;
+       /* Device state */
+       enum uk_consdev_state state;
+       /* Name of device*/
+       const char *drv_name;
+       /* Device allocator */
+       struct uk_alloc *a;
+};
+
+struct uk_consdev {
+       /* Pointer to API-internal state data. */
+       struct uk_consdev_data *_data;
+       /* Functions callbacks by driver. */
+       const struct uk_consdev_ops *ops;
+       /* Entry for list of console devices */
+       UK_TAILQ_ENTRY(struct uk_consdev) _list;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __UK_CONSDEV_CORE__ */
diff --git a/lib/ukconsdev/include/uk/consdev_driver.h 
b/lib/ukconsdev/include/uk/consdev_driver.h
new file mode 100644
index 00000000..75f12b43
--- /dev/null
+++ b/lib/ukconsdev/include/uk/consdev_driver.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Authors: Costin Birlea <costin.birlea@xxxxxxxxx>
+ *
+ * Copyright (c) 2019, University Politehnica of Bucharest.
+ * 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.
+ */
+/* This is derived from uknetdev because of consistency reasons */
+#ifndef __UK_CONSDEV_DRIVER__
+#define __UK_CONSDEV_DRIVER__
+
+#include <uk/consdev_core.h>
+#include <uk/assert.h>
+
+/**
+ * Unikraft console driver API.
+ *
+ * This header contains all API functions that are supposed to be called
+ * by a character device driver.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Adds a Unikraft console device to the device list.
+ * This should be called whenever a driver adds a new found device.
+ *
+ * @param dev
+ *     Struct to Unikraft console device that shall be registered
+ * @param a
+ *     Allocator to be use for libukconsdev private data (dev->_data)
+ * @param drv_name
+ *     (Optional) driver name
+ *     The memory for this string has to stay available as long as the
+ *     device is registered.
+ * @return
+ *     - (-ENOMEM): Allocation of private
+ *     - (>=0): Console device ID on success
+ */
+int uk_consdev_drv_register(struct uk_consdev *dev,
+               struct uk_alloc *a, const char *drv_name);
+
+/**
+ * Frees the data allocated for the Unikraft Console Device.
+ * Removes the console device from the list.
+ *
+ * @param dev
+ *     Unikraft console device
+ */
+void uk_consdev_drv_unregister(struct uk_consdev *dev);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __UK_CONSDEV_DRIVER__ */


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