changeset 10042:c022bae2f0d3

6853590 Reboot stops at ok prompt when domain services cannot be initialized 6855530 Failure to store boot command can lead to unexpected reboot behavior
author Mike Christensen <Michael.Christensen@Sun.COM>
date Mon, 06 Jul 2009 17:03:56 -0700
parents b0f22b3cef0c
children 397682150a87
files usr/src/uts/sun4v/io/ds_common.c
diffstat 1 files changed, 115 insertions(+), 86 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/sun4v/io/ds_common.c	Mon Jul 06 16:54:01 2009 -0700
+++ b/usr/src/uts/sun4v/io/ds_common.c	Mon Jul 06 17:03:56 2009 -0700
@@ -168,6 +168,8 @@
 /* service utilities */
 static void ds_reset_svc(ds_svc_t *svc, ds_port_t *port);
 static int ds_svc_register_onport(ds_svc_t *svc, ds_port_t *port);
+static int ds_svc_register_onport_walker(ds_svc_t *svc, void *arg);
+static void ds_set_port_ready(ds_port_t *port, uint16_t major, uint16_t minor);
 
 /* port utilities */
 static void ds_port_reset(ds_port_t *port);
@@ -195,7 +197,6 @@
 static ds_svc_t *ds_find_svc_by_id_port(char *svc_id, int is_client,
     ds_port_t *port);
 static ds_svc_t *ds_svc_clone(ds_svc_t *svc);
-static void ds_portset_del_active_clients(char *service, ds_portset_t *portsp);
 static void ds_check_for_dup_services(ds_svc_t *svc);
 static void ds_delete_svc_entry(ds_svc_t *svc);
 
@@ -691,6 +692,8 @@
 	    __func__, msglen);
 	DS_DUMP_MSG(DS_DBG_FLAG_LDC, msg, msglen);
 
+	(void) ds_log_add_msg(DS_LOG_OUT(port->id), (uint8_t *)msg, msglen);
+
 	/*
 	 * Ensure that no other messages can be sent on this port by holding
 	 * the tx_lock mutex in case the write doesn't get sent with one write.
@@ -714,9 +717,10 @@
 			    (loopcnt++ < ds_retries)) {
 				drv_usecwait(ds_delay);
 			} else {
-				cmn_err(CE_WARN, "ds@%lx: send_msg: ldc_write "
-				    "failed (%d), %d bytes remaining" DS_EOL,
-				    PORTID(port), rv, (int)amt_left);
+				DS_DBG_PRCL(CE_NOTE, "ds@%lx: send_msg: "
+				    "ldc_write failed (%d), %d bytes "
+				    "remaining" DS_EOL, PORTID(port), rv,
+				    (int)amt_left);
 				goto error;
 			}
 		} else {
@@ -805,6 +809,10 @@
 	 */
 	(void) ds_send_msg(port, msg, msglen);
 	DS_FREE(msg, msglen);
+
+	if (match) {
+		ds_set_port_ready(port, req->major_vers, ack->minor_vers);
+	}
 }
 
 static void
@@ -812,6 +820,8 @@
 {
 	ds_init_ack_t	*ack;
 	ds_ver_t	*ver;
+	uint16_t	major;
+	uint16_t	minor;
 	size_t		explen = DS_MSG_LEN(ds_init_ack_t);
 
 	/* sanity check the incoming message */
@@ -826,6 +836,13 @@
 
 	mutex_enter(&port->lock);
 
+	if (port->state == DS_PORT_READY) {
+		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_ack: port ready" DS_EOL,
+		    PORTID(port));
+		mutex_exit(&port->lock);
+		return;
+	}
+
 	if (port->state != DS_PORT_INIT_REQ) {
 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_ack: invalid state: %d"
 		    DS_EOL, PORTID(port), port->state);
@@ -834,49 +851,14 @@
 	}
 
 	ver = &(ds_vers[port->ver_idx]);
-
-	/* agreed upon a major version */
-	port->ver.major = ver->major;
-
-	/*
-	 * If the returned minor version is larger than
-	 * the requested minor version, use the lower of
-	 * the two, i.e. the requested version.
-	 */
-	if (ack->minor_vers >= ver->minor) {
-		/*
-		 * Use the minor version specified in the
-		 * original request.
-		 */
-		port->ver.minor = ver->minor;
-	} else {
-		/*
-		 * Use the lower minor version returned in
-		 * the ack. By definition, all lower minor
-		 * versions must be supported.
-		 */
-		port->ver.minor = ack->minor_vers;
-	}
-
-	port->state = DS_PORT_READY;
+	major = ver->major;
+	minor = MIN(ver->minor, ack->minor_vers);
+	mutex_exit(&port->lock);
 
 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_ack: port ready v%d.%d" DS_EOL,
-	    PORTID(port), port->ver.major, port->ver.minor);
-
-	mutex_exit(&port->lock);
-
-	/*
-	 * The port came up, so update all the services
-	 * with this information. Follow that up with an
-	 * attempt to register any service that is not
-	 * already registered.
-	 */
-	mutex_enter(&ds_svcs.lock);
-
-	(void) ds_walk_svcs(ds_svc_port_up, port);
-	(void) ds_walk_svcs(ds_svc_register, NULL);
-
-	mutex_exit(&ds_svcs.lock);
+	    PORTID(port), major, minor);
+
+	ds_set_port_ready(port, major, minor);
 }
 
 static void
@@ -1270,11 +1252,10 @@
 	/*
 	 * Get the ports that haven't been tried yet and are available to try.
 	 */
-	DS_PORTSET_SETNULL(totry);
+	DS_PORTSET_DUP(totry, svc->avail);
 	for (i = 0; i < DS_MAX_PORTS; i++) {
-		if (!DS_PORT_IN_SET(svc->tried, i) &&
-		    DS_PORT_IN_SET(svc->avail, i))
-			DS_PORTSET_ADD(totry, i);
+		if (DS_PORT_IN_SET(svc->tried, i))
+			DS_PORTSET_DEL(totry, i);
 	}
 
 	if (DS_PORTSET_ISNULL(totry))
@@ -1351,8 +1332,9 @@
 
 	/* make sure the message makes sense */
 	if (svc->state != DS_SVC_REG_PENDING) {
-		cmn_err(CE_WARN, "ds@%lx: <reg_nack: invalid state (%d)" DS_EOL,
-		    PORTID(port), svc->state);
+		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_nack: '%s' handle: 0x%llx "
+		    "invalid state (%d)" DS_EOL, PORTID(port), svc->cap.svc_id,
+		    (u_longlong_t)nack->svc_handle, svc->state);
 		goto done;
 	}
 
@@ -1447,7 +1429,7 @@
 		mutex_exit(&port->lock);
 		if (!is_up)
 			return;
-		cmn_err(CE_WARN, "ds@%lx: <unreg_req: invalid handle 0x%llx"
+		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <unreg_req: invalid handle 0x%llx"
 		    DS_EOL, PORTID(port), (u_longlong_t)req->svc_handle);
 		ds_send_unreg_nack(port, req->svc_handle);
 		return;
@@ -2103,6 +2085,23 @@
 	return (0);
 }
 
+static void
+ds_set_svc_port_tried(char *svc_id, ds_port_t *port)
+{
+	int		idx;
+	ds_svc_t	*svc;
+
+	ASSERT(MUTEX_HELD(&ds_svcs.lock));
+
+	/* walk every table entry */
+	for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
+		svc = ds_svcs.tbl[idx];
+		if (!DS_SVC_ISFREE(svc) && (svc->flags & DSSF_ISCLIENT) != 0 &&
+		    strcmp(svc_id, svc->cap.svc_id) == 0)
+			DS_PORTSET_ADD(svc->tried, PORTID(port));
+	}
+}
+
 static int
 ds_svc_register_onport(ds_svc_t *svc, ds_port_t *port)
 {
@@ -2114,7 +2113,23 @@
 	if (!DS_PORT_IN_SET(svc->avail, PORTID(port)))
 		return (0);
 
-	DS_PORTSET_ADD(svc->tried, PORTID(port));
+	if (DS_PORT_IN_SET(svc->tried, PORTID(port)))
+		return (0);
+
+	if ((svc->flags & DSSF_ISCLIENT) == 0) {
+		DS_PORTSET_ADD(svc->tried, PORTID(port));
+		if (svc->state != DS_SVC_INACTIVE)
+			return (0);
+	} else {
+		ds_set_svc_port_tried(svc->cap.svc_id, port);
+
+		/*
+		 * Never send a client reg req to the SP.
+		 */
+		if (PORTID(port) == ds_sp_port_id) {
+			return (0);
+		}
+	}
 
 	if (ds_send_reg_req(svc, port) == 0) {
 		/* register sent successfully */
@@ -2128,6 +2143,18 @@
 	return (0);
 }
 
+static int
+ds_svc_register_onport_walker(ds_svc_t *svc, void *arg)
+{
+	ASSERT(MUTEX_HELD(&ds_svcs.lock));
+
+	if (DS_SVC_ISFREE(svc))
+		return (0);
+
+	(void) ds_svc_register_onport(svc, arg);
+	return (0);
+}
+
 int
 ds_svc_register(ds_svc_t *svc, void *arg)
 {
@@ -2143,7 +2170,10 @@
 
 	DS_PORTSET_DUP(ports, svc->avail);
 	if (svc->flags & DSSF_ISCLIENT) {
-		ds_portset_del_active_clients(svc->cap.svc_id, &ports);
+		for (idx = 0; idx < DS_MAX_PORTS; idx++) {
+			if (DS_PORT_IN_SET(svc->tried, idx))
+				DS_PORTSET_DEL(ports, idx);
+		}
 	} else if (svc->state != DS_SVC_INACTIVE)
 		return (0);
 
@@ -2169,7 +2199,6 @@
 		if (ds_svc_register_onport(svc, port)) {
 			if ((svc->flags & DSSF_ISCLIENT) == 0)
 				break;
-			DS_PORTSET_DEL(svc->avail, idx);
 		}
 	}
 
@@ -2242,6 +2271,37 @@
 	return (0);
 }
 
+static void
+ds_set_port_ready(ds_port_t *port, uint16_t major, uint16_t minor)
+{
+	boolean_t was_ready;
+
+	mutex_enter(&port->lock);
+	was_ready = (port->state == DS_PORT_READY);
+	if (!was_ready) {
+		port->state = DS_PORT_READY;
+		port->ver.major = major;
+		port->ver.minor = minor;
+	}
+	mutex_exit(&port->lock);
+
+	if (!was_ready) {
+
+		/*
+		 * The port came up, so update all the services
+		 * with this information. Follow that up with an
+		 * attempt to register any service that is not
+		 * already registered.
+		 */
+		mutex_enter(&ds_svcs.lock);
+
+		(void) ds_walk_svcs(ds_svc_port_up, port);
+		(void) ds_walk_svcs(ds_svc_register_onport_walker, port);
+
+		mutex_exit(&ds_svcs.lock);
+	}
+}
+
 ds_svc_t *
 ds_alloc_svc(void)
 {
@@ -3175,37 +3235,6 @@
 	return (0);
 }
 
-static void
-ds_portset_del_active_clients(char *service, ds_portset_t *portsp)
-{
-	ds_portset_t ports;
-	int idx;
-	ds_svc_t *svc;
-
-	ASSERT(MUTEX_HELD(&ds_svcs.lock));
-
-	DS_PORTSET_DUP(ports, *portsp);
-	for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
-		svc = ds_svcs.tbl[idx];
-		if (DS_SVC_ISFREE(svc))
-			continue;
-		if (strcmp(svc->cap.svc_id, service) == 0 &&
-		    (svc->flags & DSSF_ISCLIENT) != 0 &&
-		    svc->state != DS_SVC_INACTIVE &&
-		    svc->port != NULL) {
-			DS_PORTSET_DEL(ports, PORTID(svc->port));
-		}
-	}
-
-	/*
-	 * Never send a client reg req to the SP.
-	 */
-	if (ds_sp_port_id != DS_PORTID_INVALID) {
-		DS_PORTSET_DEL(ports, ds_sp_port_id);
-	}
-	DS_PORTSET_DUP(*portsp, ports);
-}
-
 /*
  * After an UNREG REQ, check if this is a client service with multiple
  * handles.  If it is, then we can eliminate this entry.