libibumad: allow choose port with state DOWN in case of explicit user port

[ Upstream commit 862aede44e4f961fa1ea8aa24410097682ef7d56 ]

Today libibumad rejects ports that are not ACTIVE during selection, which
prevents users from targeting a specific port that is administratively DOWN.
This change permits selecting a port in DOWN state when the user has
explicitly specified the port and the device (e.g., by index/identifier).

Automatic port selection behavior is unchanged and will continue to prefer
only ACTIVE ports. Validation of the requested port’s existence and basic
attributes remains intact.

if user explicitly chose device name AND:
 - port number
 - port is 0 and device is switch
 - Only one port for device

we skip the verification of port state.

This is mainly done to suport opensm standalone feature

Fixes: be54b52e94be ("libibumad: Add new API to support SMI/GSI seperation")
Signed-off-by: Asaf Mazor <amazor@nvidia.com>
Signed-off-by: Nicolas Morey <nmorey@suse.com>
diff --git a/libibumad/umad.c b/libibumad/umad.c
index b37b335..9cb2d28 100644
--- a/libibumad/umad.c
+++ b/libibumad/umad.c
@@ -1568,12 +1568,22 @@
 	return 1;
 }
 
-static int find_preferred_ports(struct umad_ca_pair *ca_pair, const umad_ca_t *ca, bool is_gsi, int portnum)
+static int find_preferred_ports(struct umad_ca_pair *ca_pair, const umad_ca_t *ca, bool is_gsi, int portnum, const char *name)
 {
-	if (portnum) {
+	if (ca && ca->numports == 1) {
+		size_t i = 0;
+		for (i = 0; i < (size_t)ca->numports + 1; ++i) {
+			if (ca->ports[i]) {
+				portnum = ca->ports[i]->portnum;
+				break;
+			}
+		}
+	}
+	if (portnum || (portnum == 0 && ca && ca->node_type == 2)) {
 		//in case we have same device, use same port for smi/gsi
 		if (!strncmp(ca_pair->gsi_name, ca_pair->smi_name, UMAD_CA_NAME_LEN)) {
-			if (!umad_check_active(ca, portnum)) {
+			//in case of explicit portnum and device name, no need to check active device
+			if ((name) || !umad_check_active(ca, portnum)) {
 				ca_pair->gsi_preferred_port = portnum;
 				ca_pair->smi_preferred_port = portnum;
 				return 0;
@@ -1583,9 +1593,11 @@
 		uint32_t *port_to_set = is_gsi ?
 					&ca_pair->gsi_preferred_port :
 					&ca_pair->smi_preferred_port;
-		if (!umad_check_active(ca, portnum)) {
+		if ((name) || !umad_check_active(ca, portnum)) {
 			*port_to_set = portnum;
-			return umad_find_active(ca_pair, ca, !is_gsi);
+			//if find active device falis, do not fail if user chose explicit device name and portnum
+			int rc = umad_find_active(ca_pair, ca, !is_gsi);
+			return name ? 0 : rc;
 		}
 		return 1;
 	}
@@ -1648,7 +1660,7 @@
 
 		// fill candidate
 		*ca_pair = cas_pair[i];
-		rc = find_preferred_ports(ca_pair, &ca, is_gsi, portnum);
+		rc = find_preferred_ports(ca_pair, &ca, is_gsi, portnum, name);
 		umad_release_ca(&ca);
 
 		if (!rc)