305 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			305 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
commit 4e6670df0da2b92a33dd880ce987823358f298bf
 | 
						|
Author: Haren Myneni <haren@linux.ibm.com>
 | 
						|
Date:   Fri Jun 21 15:45:20 2024 -0700
 | 
						|
 | 
						|
    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 <haren@linux.ibm.com>
 | 
						|
    Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
 | 
						|
 | 
						|
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)
 | 
						|
 {
 |