discover: Integrate ipmi bootdev settings into the priority system
authorJeremy Kerr <jk@ozlabs.org>
Thu, 11 Dec 2014 08:38:21 +0000 (16:38 +0800)
committerJeremy Kerr <jk@ozlabs.org>
Mon, 15 Dec 2014 07:47:40 +0000 (15:47 +0800)
Currently, we expose the boot device priorities through an array in
struct config, which will either be the default (network -> disk), or a
single device type specified by the IPMI code.

Rather than hide the implementation details in this array, we'd like to
expose the details of the machine configuration instead. This allows
user visibility of the real boot configuration (for example, if an IPMI
boot preference is set).

This change removes the priority array, and replaces it with the
ipmi_bootdev data (and a persistent flag). We update the
default-conflict-resolution code to reflect the priorities between IPMI
and UUID preferences.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
discover/device-handler.c
discover/platform-powerpc.c
discover/platform.c
discover/platform.h
lib/pb-config/pb-config.c
lib/pb-protocol/pb-protocol.c
lib/types/types.h

index 64fc9fab2a5031556ea5e27a76be98903629999a..fd6f1d3091618f64e1a309817b7c2e28923c0b38 100644 (file)
 #include "boot.h"
 #include "udev.h"
 #include "network.h"
+#include "ipmi.h"
+
+enum default_priority {
+       DEFAULT_PRIORITY_REMOTE         = 1,
+       DEFAULT_PRIORITY_LOCAL_UUID     = 2,
+       DEFAULT_PRIORITY_LOCAL_FIRST    = 3,
+       DEFAULT_PRIORITY_LOCAL_LAST     = 0xfe,
+       DEFAULT_PRIORITY_DISABLED       = 0xff,
+};
 
 struct device_handler {
        struct discover_server  *server;
@@ -54,6 +63,8 @@ struct device_handler {
        unsigned int            sec_to_boot;
 
        struct discover_boot_option *default_boot_option;
+       int                     default_boot_option_priority;
+
        struct list             unresolved_boot_options;
 
        struct boot_task        *pending_boot;
@@ -429,75 +440,118 @@ static int default_timeout(void *arg)
        return 0;
 }
 
-static bool priority_match(struct boot_priority *prio,
+struct {
+       enum ipmi_bootdev       ipmi_type;
+       enum device_type        device_type;
+} device_type_map[] = {
+       { IPMI_BOOTDEV_NETWORK, DEVICE_TYPE_NETWORK },
+       { IPMI_BOOTDEV_DISK, DEVICE_TYPE_DISK },
+       { IPMI_BOOTDEV_CDROM, DEVICE_TYPE_OPTICAL },
+};
+
+static bool ipmi_device_type_matches(enum ipmi_bootdev ipmi_type,
+               enum device_type device_type)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(device_type_map); i++) {
+               if (device_type_map[i].device_type == device_type)
+                       return device_type_map[i].ipmi_type == ipmi_type;
+       }
+
+       return false;
+}
+
+static bool priority_matches(struct boot_priority *prio,
                struct discover_boot_option *opt)
 {
        return prio->type == opt->device->device->type ||
                prio->type == DEVICE_TYPE_ANY;
 }
 
-static int default_option_priority(struct discover_boot_option *opt)
+/*
+ * We have different priorities to resolve conflicts between boot options that
+ * report to be the default for their device. This function assigns a priority
+ * for these options.
+ */
+static enum default_priority default_option_priority(
+               struct discover_boot_option *opt)
 {
        const struct config *config;
-       struct boot_priority *prio;
+       const char *dev_str;
        unsigned int i;
 
        config = config_get();
 
-       for (i = 0; i < config->n_boot_priorities; i++) {
-               prio = &config->boot_priorities[i];
-               if (priority_match(prio, opt))
-                       return prio->priority;
+       /* We give highest priority to IPMI-configured boot options. If
+        * we have an IPMI bootdev configuration set, then we don't allow
+        * any other defaults */
+       if (config->ipmi_bootdev) {
+               bool ipmi_match = ipmi_device_type_matches(config->ipmi_bootdev,
+                               opt->device->device->type);
+               if (ipmi_match)
+                       return DEFAULT_PRIORITY_REMOTE;
+
+               pb_debug("handler: disabled default priority due to "
+                               "non-matching IPMI type %x\n",
+                               config->ipmi_bootdev);
+               return DEFAULT_PRIORITY_DISABLED;
        }
 
-       return 0;
-}
-
-static bool device_allows_default(struct discover_device *dev)
-{
-       const char *dev_str;
-
-       dev_str = config_get()->boot_device;
+       /* Next, allow matching by device UUID. If we have one set but it
+        * doesn't match, disallow the default entirely */
+       dev_str = config->boot_device;
+       if (dev_str && dev_str[0]) {
+               if (!strcmp(opt->device->uuid, dev_str))
+                       return DEFAULT_PRIORITY_LOCAL_UUID;
 
-       if (!dev_str || !strlen(dev_str))
-               return true;
+               pb_debug("handler: disabled default priority due to "
+                               "non-matching UUID\n");
+               return DEFAULT_PRIORITY_DISABLED;
+       }
 
-       /* default devices are specified by UUIDs at present */
-       if (strcmp(dev->uuid, dev_str))
-               return false;
+       /* Lastly, use the local priorities */
+       for (i = 0; i < config->n_boot_priorities; i++) {
+               struct boot_priority *prio = &config->boot_priorities[i];
+               if (priority_matches(prio, opt))
+                       return DEFAULT_PRIORITY_LOCAL_FIRST + prio->priority;
+       }
 
-       return true;
+       return DEFAULT_PRIORITY_DISABLED;
 }
 
 static void set_default(struct device_handler *handler,
                struct discover_boot_option *opt)
 {
-       int new_prio;
+       enum default_priority cur_prio, new_prio;
 
        if (!handler->autoboot_enabled)
                return;
 
-       /* do we allow default-booting from this device? */
-       if (!device_allows_default(opt->device))
-               return;
+       pb_debug("handler: new default option: %s\n", opt->option->id);
 
        new_prio = default_option_priority(opt);
 
-       /* A negative priority indicates that we don't want to boot this device
-        * by default */
-       if (new_prio < 0)
+       /* Anything outside our range prevents a default boot */
+       if (new_prio >= DEFAULT_PRIORITY_DISABLED)
                return;
 
+       pb_debug("handler: calculated priority %d\n", new_prio);
+
        /* Resolve any conflicts: if we have a new default option, it only
         * replaces the current if it has a higher priority. */
        if (handler->default_boot_option) {
-               int cur_prio;
 
-               cur_prio = default_option_priority(
-                                       handler->default_boot_option);
+               cur_prio = handler->default_boot_option_priority;
 
-               if (new_prio > cur_prio) {
+               if (new_prio < cur_prio) {
+                       pb_log("handler: new prio %d beats "
+                                       "old prio %d for %s\n",
+                                       new_prio, cur_prio,
+                                       handler->default_boot_option
+                                               ->option->id);
                        handler->default_boot_option = opt;
+                       handler->default_boot_option_priority = new_prio;
                        /* extend the timeout a little, so the user sees some
                         * indication of the change */
                        handler->sec_to_boot += 2;
@@ -508,8 +562,9 @@ static void set_default(struct device_handler *handler,
 
        handler->sec_to_boot = config_get()->autoboot_timeout_sec;
        handler->default_boot_option = opt;
+       handler->default_boot_option_priority = new_prio;
 
-       pb_log("Boot option %s set as default, timeout %u sec.\n",
+       pb_log("handler: boot option %s set as default, timeout %u sec.\n",
               opt->option->id, handler->sec_to_boot);
 
        default_timeout(handler);
index 6ae28f4cd59c4acb4043f5afd58f439e2ae42a33..5f6772f6b455dd71d5eb0d629332b6217c4dc372 100644 (file)
@@ -30,7 +30,12 @@ struct param {
 };
 
 struct platform_powerpc {
-       struct list             params;
+       struct list     params;
+       int             (*get_ipmi_bootdev)(
+                               struct platform_powerpc *platform,
+                               uint8_t *bootdev, bool *persistent);
+       int             (*clear_ipmi_bootdev)(
+                               struct platform_powerpc *platform);
 };
 
 static const char *known_params[] = {
@@ -570,32 +575,17 @@ static int update_config(struct platform_powerpc *platform,
        return write_nvram(platform);
 }
 
-static void set_exclusive_devtype(struct config *config,
-               enum device_type devtype)
+static void set_ipmi_bootdev(struct config *config, enum ipmi_bootdev bootdev,
+               bool persistent)
 {
-       config->n_boot_priorities = 2;
-       config->boot_priorities = talloc_realloc(config,
-                       config->boot_priorities, struct boot_priority,
-                       config->n_boot_priorities);
-       config->boot_priorities[0].type = devtype;
-       config->boot_priorities[0].priority = 0;
-       config->boot_priorities[1].type = DEVICE_TYPE_ANY;
-       config->boot_priorities[1].priority = -1;
-}
+       config->ipmi_bootdev = bootdev;
+       config->ipmi_bootdev_persistent = persistent;
 
-static void set_ipmi_bootdev(struct config *config, enum ipmi_bootdev bootdev)
-{
        switch (bootdev) {
        case IPMI_BOOTDEV_NONE:
-               break;
        case IPMI_BOOTDEV_DISK:
-               set_exclusive_devtype(config, DEVICE_TYPE_DISK);
-               break;
        case IPMI_BOOTDEV_NETWORK:
-               set_exclusive_devtype(config, DEVICE_TYPE_NETWORK);
-               break;
        case IPMI_BOOTDEV_CDROM:
-               set_exclusive_devtype(config, DEVICE_TYPE_OPTICAL);
                break;
        case IPMI_BOOTDEV_SETUP:
                config->autoboot_enabled = false;
@@ -684,7 +674,17 @@ static int write_bootdev_sysparam(const char *name, uint8_t val)
        return rc;
 }
 
-static void parse_opal_sysparams(struct config *config)
+static int clear_ipmi_bootdev_sysparams(
+               struct platform_powerpc *platform __attribute__((unused)))
+{
+       /* invalidate next-boot-device setting */
+       write_bootdev_sysparam("next-boot-device", 0xff);
+       return 0;
+}
+
+static int get_ipmi_bootdev_sysparams(
+               struct platform_powerpc *platform __attribute__((unused)),
+               uint8_t *bootdev, bool *persistent)
 {
        uint8_t next_bootdev, default_bootdev;
        bool next_valid, default_valid;
@@ -698,12 +698,11 @@ static void parse_opal_sysparams(struct config *config)
 
        /* nothing valid? no need to change the config */
        if (!next_valid && !default_valid)
-               return;
-
-       if (!next_valid)
-               next_bootdev = default_bootdev;
+               return -1;
 
-       set_ipmi_bootdev(config, next_bootdev);
+       *persistent = !next_valid;
+       *bootdev = next_valid ? next_bootdev : default_bootdev;
+       return 0;
 }
 
 static int load_config(struct platform *p, struct config *config)
@@ -717,7 +716,15 @@ static int load_config(struct platform *p, struct config *config)
 
        populate_config(platform, config);
 
-       parse_opal_sysparams(config);
+       if (platform->get_ipmi_bootdev) {
+               bool bootdev_persistent;
+               uint8_t bootdev;
+               rc = platform->get_ipmi_bootdev(platform, &bootdev,
+                               &bootdev_persistent);
+               if (!rc && ipmi_bootdev_is_valid(bootdev)) {
+                       set_ipmi_bootdev(config, bootdev, bootdev_persistent);
+               }
+       }
 
        return 0;
 }
@@ -737,10 +744,12 @@ static int save_config(struct platform *p, struct config *config)
        return rc;
 }
 
-static void finalise_config(struct platform *platform __attribute__((unused)))
+static void finalise_config(struct platform *p, const struct config *config)
 {
-       /* invalidate next-boot-device setting */
-       write_bootdev_sysparam("next-boot-device", 0xff);
+       struct platform_powerpc *platform = to_platform_powerpc(p);
+
+       if (config->ipmi_bootdev_persistent && platform->clear_ipmi_bootdev)
+               platform->clear_ipmi_bootdev(platform);
 }
 
 static int get_sysinfo(struct platform *p, struct system_info *sysinfo)
@@ -782,6 +791,15 @@ static bool probe(struct platform *p, void *ctx)
        list_init(&platform->params);
 
        p->platform_data = platform;
+
+       if (!stat(sysparams_dir, &statbuf)) {
+               pb_debug("platform: using sysparams for IPMI paramters\n");
+               platform->get_ipmi_bootdev = get_ipmi_bootdev_sysparams;
+               platform->clear_ipmi_bootdev = clear_ipmi_bootdev_sysparams;
+
+       } else {
+               pb_log("platform: no IPMI parameter support\n");
+       }
        return true;
 }
 
index 0a221e295bb87a67992a6ccede556fe3e49271c2..7275a5f0982f4d89143b5f3b4acf63aad3801b57 100644 (file)
@@ -91,6 +91,10 @@ static void dump_config(struct config *config)
                                        prio->priority);
        }
 
+       pb_log("  IPMI boot device 0x%02x%s\n", config->ipmi_bootdev,
+                       config->ipmi_bootdev_persistent ? " (persistent)" : "");
+
+
        pb_log(" language: %s\n", config->lang ?: "");
 }
 
@@ -130,10 +134,13 @@ void config_set_defaults(struct config *config)
        config->boot_priorities = talloc_array(config, struct boot_priority,
                                                config->n_boot_priorities);
        config->boot_priorities[0].type = DEVICE_TYPE_NETWORK;
-       config->boot_priorities[0].priority = 2;
-       config->boot_priorities[1].type = DEVICE_TYPE_DISK;
+       config->boot_priorities[0].priority = 0;
+       config->boot_priorities[1].type = DEVICE_TYPE_ANY;
        config->boot_priorities[1].priority = 1;
 
+       config->ipmi_bootdev = 0;
+       config->ipmi_bootdev_persistent = false;
+
        config->debug = config_debug_on_cmdline();
 }
 
@@ -175,8 +182,10 @@ const struct platform *platform_get(void)
 
 void platform_finalise_config(void)
 {
-       if (platform && platform->finalise_config)
-               platform->finalise_config(platform);
+       const struct config *config = config_get();
+
+       if (platform && config && platform->finalise_config)
+               platform->finalise_config(platform, config);
 }
 
 int platform_get_sysinfo(struct system_info *info)
index 5601b61bbd2165e3d544bdf66606e3e33721c9bd..ab1bd8868d4a40cad1de38bab4cf5f7cebec3dcf 100644 (file)
@@ -8,7 +8,8 @@ struct platform {
        bool            (*probe)(struct platform *, void *);
        int             (*load_config)(struct platform *, struct config *);
        int             (*save_config)(struct platform *, struct config *);
-       void            (*finalise_config)(struct platform *);
+       void            (*finalise_config)(struct platform *,
+                               const struct config *);
        int             (*get_sysinfo)(struct platform *, struct system_info *);
        uint16_t        dhcp_arch_id;
        void            *platform_data;
index fbaa7cb908b04000f7ae8958516aff981183e003..a2272f431e80d49d4ab829563db4716abfd78769 100644 (file)
@@ -74,6 +74,9 @@ struct config *config_copy(void *ctx, const struct config *src)
        else
                dest->boot_device = NULL;
 
+       dest->ipmi_bootdev = src->ipmi_bootdev;
+       dest->ipmi_bootdev_persistent = src->ipmi_bootdev_persistent;
+
        if (src->lang && strlen(src->lang))
                dest->lang = talloc_strdup(dest, src->lang);
        else
index 866673dba1016dc64f3778dfe13f6973e28ae690..439824871ebf1e380561cc76129da2d3f81d3f98 100644 (file)
@@ -284,6 +284,8 @@ int pb_protocol_config_len(const struct config *config)
 
        len += 4 + optional_strlen(config->boot_device);
 
+       len += 4 + 4; /* ipmi_bootdev, ipmi_bootdev_persistent */
+
        len += 4 + optional_strlen(config->lang);
 
        return len;
@@ -488,6 +490,11 @@ int pb_protocol_serialise_config(const struct config *config,
 
        pos += pb_protocol_serialise_string(pos, config->boot_device);
 
+       *(uint32_t *)pos = __cpu_to_be32(config->ipmi_bootdev);
+       pos += 4;
+       *(uint32_t *)pos = config->ipmi_bootdev_persistent;
+       pos += 4;
+
        pos += pb_protocol_serialise_string(pos, config->lang);
 
        assert(pos <= buf + buf_len);
@@ -934,9 +941,14 @@ int pb_protocol_deserialise_config(struct config *config,
 
        if (read_string(config, &pos, &len, &str))
                goto out;
-
        config->boot_device = str;
 
+       if (read_u32(&pos, &len, &config->ipmi_bootdev))
+               goto out;
+       if (read_u32(&pos, &len, &tmp))
+               goto out;
+       config->ipmi_bootdev_persistent = !!tmp;
+
        if (read_string(config, &pos, &len, &str))
                goto out;
 
index 25bf556e8ee1c931625777a5dee2ba818dbbd057..f543b7f7ea3295d39a91783897663152e5350f5d 100644 (file)
@@ -122,9 +122,15 @@ struct config {
        bool                    autoboot_enabled;
        unsigned int            autoboot_timeout_sec;
        struct network_config   network;
+
        struct boot_priority    *boot_priorities;
        unsigned int            n_boot_priorities;
+
        char                    *boot_device;
+
+       unsigned int            ipmi_bootdev;
+       bool                    ipmi_bootdev_persistent;
+
        char                    *lang;
 
        /* not user-settable */