From 4e6670df0da2b92a33dd880ce987823358f298bf Mon Sep 17 00:00:00 2001 From: Haren Myneni Date: Fri, 21 Jun 2024 15:45:20 -0700 Subject: [PATCH] drmgr/pci: Add multipath partner device support for hotplug add If the PCI device has multipath partner device, the firmware provides partner DRC index in "ibm,multipath-partner-drc" property and this property is available in the PCI device node. So when the PCI device add is initiated, both paths will be added if the partner path is also configured and enabled with the following steps: - Identify slot and notify user to add the device - Add the path and enable for the specified device - Find the partner path DRC index from "ibm,multipath-partner-drc" property for the specified device - Add the partner path if it is also configured - Notify user about adding partner path Since both paths will be using the same slot, LED indicators and the slot identification will be done only for the primary device. Signed-off-by: Haren Myneni Signed-off-by: Tyrel Datwyler --- src/drmgr/common_pci.c | 2 +- src/drmgr/drslot_chrp_pci.c | 162 +++++++++++++++++++++++------------- src/drmgr/ofdt.h | 1 + 3 files changed, 105 insertions(+), 60 deletions(-) diff --git a/src/drmgr/common_pci.c b/src/drmgr/common_pci.c index 2e0e5fb..2411641 100644 --- a/src/drmgr/common_pci.c +++ b/src/drmgr/common_pci.c @@ -295,7 +295,7 @@ add_child_node(struct dr_node *parent, char *child_path) * @param node * @returns 0 on success, !0 otherwise */ -static int +int init_node(struct dr_node *node) { DIR *d; diff --git a/src/drmgr/drslot_chrp_pci.c b/src/drmgr/drslot_chrp_pci.c index 87edf67..2b8579b 100644 --- a/src/drmgr/drslot_chrp_pci.c +++ b/src/drmgr/drslot_chrp_pci.c @@ -31,6 +31,7 @@ #include "rtas_calls.h" #include "dr.h" #include "drpci.h" +#include "ofdt.h" static char *sw_error = "Internal software error. Contact your service " "representative.\n"; @@ -367,27 +368,38 @@ static int do_identify(struct dr_node *all_nodes) * off, isolated, and the LED is turned off. * * @param slot + * @param partner path or not * @returns 0 on success, !0 on failure */ -static int add_work(struct dr_node *node) +static int add_work(struct dr_node *node, bool partner_device) { - int pow_state; /* Tells us if power was turned on when */ - int iso_state; /* Tells us isolation state after */ + int pow_state = POWER_OFF; /* Tells us if power was turned */ + /* on when */ + int iso_state = ISOLATE; /* Tells us isolation state after */ int rc; struct of_node *new_nodes;/* nodes returned from configure_connector */ - /* if we're continuing, set LED_ON and see if a card is really there. */ - if (process_led(node, LED_ON)) - return -1; + /* + * Already checked the card presence for the original device + * and both multipaths use the same card. So do not need to + * check the card presence again for the partner device. + */ + if (!partner_device) { + /* if we're continuing, set LED_ON and see if a card */ + /* is really there. */ + if (process_led(node, LED_ON)) + return -1; - say(DEBUG, "is calling card_present\n"); - rc = card_present(node, &pow_state, &iso_state); - if (!rc) { - say(ERROR, "No PCI card was detected in the specified " - "PCI slot.\n"); - rtas_set_indicator(ISOLATION_STATE, node->drc_index, ISOLATE); - set_power(node->drc_power, POWER_OFF); - return -1; + say(DEBUG, "is calling card_present\n"); + rc = card_present(node, &pow_state, &iso_state); + if (!rc) { + say(ERROR, "No PCI card was detected in the specified " + "PCI slot.\n"); + rtas_set_indicator(ISOLATION_STATE, node->drc_index, + ISOLATE); + set_power(node->drc_power, POWER_OFF); + return -1; + } } if (!pow_state) { @@ -461,7 +473,7 @@ static int add_work(struct dr_node *node) * power to a slot off. The prompts the user to insert the new card * into the slot. */ -static int do_insert_card_work(struct dr_node *node) +static int do_insert_card_work(struct dr_node *node, bool partner_device) { int rc; @@ -495,18 +507,18 @@ static int do_insert_card_work(struct dr_node *node) return -1; } - if (usr_prompt) { + if (usr_prompt && !partner_device) { /* Prompt user to put in card and to press * Enter to continue or other key to exit. */ if (process_led(node, LED_ACTION)) return -1; - printf("The visual indicator for the specified PCI slot has\n" - "been set to the action state. Insert the PCI card\n" - "into the identified slot, connect any devices to be\n" - "configured and press Enter to continue. Enter x to " - "exit.\n"); + printf("The visual indicator for the PCI slot <%s>\n" + "has been set to the action state. Insert the PCI\n" + "card into the identified slot, connect any devices\n" + "to be configured and press Enter to continue.\n" + "Enter x to exit.\n", node->drc_name); if (!(getchar() == '\n')) { process_led(node, LED_OFF); @@ -543,6 +555,58 @@ find_partner_node(struct dr_node *node, struct dr_node *all_nodes) return partner_node; } +static int insert_add_work(struct dr_node *node, bool partner_device) +{ + int usr_key = USER_CONT; + int rc; + + if (!partner_device) { + /* Prompt user only if in interactive mode. */ + if (usr_prompt) { + if (usr_slot_identification) + usr_key = identify_slot(node); + + if (usr_key == USER_QUIT) { + if (node->children == NULL) + process_led(node, LED_OFF); + else + process_led(node, LED_ON); + return 0; + } + } + + if (node->children != NULL) { + /* If there's already something here, turn the + * LED on and exit with user error. + */ + process_led(node, LED_ON); + say(ERROR, "The specified PCI slot is already occupied.\n"); + return -1; + } + } + + if (!pci_hotplug_only) { + rc = do_insert_card_work(node, partner_device); + if (rc) + return rc; + } + + /* Call the routine which determines + * what the user wants and does it. + */ + rc = add_work(node, partner_device); + if (rc) + return rc; + + /* + * Need to populate w/ children to retrieve partner device + */ + if (init_node(node)) + return -1; + + return 1; +} + /** * do_add * @@ -560,8 +624,7 @@ find_partner_node(struct dr_node *node, struct dr_node *all_nodes) */ static int do_add(struct dr_node *all_nodes) { - struct dr_node *node; - int usr_key = USER_CONT; + struct dr_node *node, *partner_node = NULL; int rc; node = find_slot(usr_drc_name, 0, all_nodes, 0); @@ -573,51 +636,32 @@ static int do_add(struct dr_node *all_nodes) return -1; } - /* Prompt user only if in interactive mode. */ - if (usr_prompt) { - if (usr_slot_identification) - usr_key = identify_slot(node); - - if (usr_key == USER_QUIT) { - if (node->children == NULL) - process_led(node, LED_OFF); - else - process_led(node, LED_ON); - return 0; - } - } - - if (node->children != NULL) { - /* If there's already something here, turn the - * LED on and exit with user error. - */ - process_led(node, LED_ON); - say(ERROR, "The specified PCI slot is already occupied.\n"); - return -1; - } + rc = insert_add_work(node, false); + if (rc <= 0) + return rc; - if (!pci_hotplug_only) { - rc = do_insert_card_work(node); - if (rc) + partner_node = find_partner_node(node, all_nodes); + if (partner_node) { + printf("<%s> and <%s> are\nmultipath partner devices. " + "So <%s> is\nalso added.\n", node->drc_name, + partner_node->drc_name, partner_node->drc_name); + rc = insert_add_work(partner_node, true); + if (rc <= 0) return rc; } - /* Call the routine which determines - * what the user wants and does it. - */ - rc = add_work(node); - if (rc) - return rc; - say(DEBUG, "is calling enable_slot to config adapter\n"); /* Try to config the adapter. The rpaphp module doesn't play well with * qemu pci slots so we let the generic kernel pci code probe the device * by rescanning the bus in the qemu virtio case. */ - if (!pci_virtio) + if (!pci_virtio) { set_hp_adapter_status(PHP_CONFIG_ADAPTER, node->drc_name); - else + if (partner_node) + set_hp_adapter_status(PHP_CONFIG_ADAPTER, + partner_node->drc_name); + } else pci_rescan_bus(); return 0; @@ -896,7 +940,7 @@ static int do_replace(struct dr_node *all_nodes) } } - rc = add_work(repl_node); + rc = add_work(repl_node, false); if (rc) return rc; @@ -917,7 +961,7 @@ static int do_replace(struct dr_node *all_nodes) if (remove_work(repl_node, false)) return -1; - rc = add_work(repl_node); + rc = add_work(repl_node, false); if (!rc) set_hp_adapter_status(PHP_CONFIG_ADAPTER, repl_node->drc_name); diff --git a/src/drmgr/ofdt.h b/src/drmgr/ofdt.h index 08d34e1..e9ebd03 100644 --- a/src/drmgr/ofdt.h +++ b/src/drmgr/ofdt.h @@ -184,6 +184,7 @@ int get_min_common_depth(void); int get_assoc_arrays(const char *dir, struct assoc_arrays *aa, int min_common_depth); int of_associativity_to_node(const char *dir, int min_common_depth); +int init_node(struct dr_node *); static inline int aa_index_to_node(struct assoc_arrays *aa, uint32_t aa_index) {