changeset 7899:9ae13a31f010

FWARC 2008/623 Improved Error Reporting for DR Domain Services 6719903 DR error message should be more helpful when 'drd' is disabled
author James Marks - Sun Microsystems <James.Marks@Sun.COM>
date Tue, 21 Oct 2008 16:19:49 -0700
parents aabf3b67d20c
children 416a9ce3cd79
files usr/src/uts/sun4v/io/dr_cpu.c usr/src/uts/sun4v/io/dr_io.c usr/src/uts/sun4v/io/drctl.c usr/src/uts/sun4v/io/drctl_impl.c usr/src/uts/sun4v/sys/dr_util.h usr/src/uts/sun4v/sys/drctl.h
diffstat 6 files changed, 430 insertions(+), 168 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/sun4v/io/dr_cpu.c	Tue Oct 21 12:07:10 2008 -0700
+++ b/usr/src/uts/sun4v/io/dr_cpu.c	Tue Oct 21 16:19:49 2008 -0700
@@ -68,9 +68,11 @@
 /*
  * Supported DS Capability Versions
  */
-static ds_ver_t		dr_cpu_vers[] = { { 1, 0 } };
+static ds_ver_t		dr_cpu_vers[] = { { 1, 1 }, { 1, 0 } };
 #define	DR_CPU_NVERS	(sizeof (dr_cpu_vers) / sizeof (dr_cpu_vers[0]))
 
+static ds_ver_t		version;
+
 /*
  * DS Capability Description
  */
@@ -80,6 +82,13 @@
 	DR_CPU_NVERS		/* nvers */
 };
 
+#define	DRCPU_VERS_EQ(_maj, _min) \
+	((version.major == (_maj)) && (version.minor == (_min)))
+
+#define	DRCPU_VERS_GTEQ(_maj, _min) \
+	((version.major > (_maj)) ||					\
+	((version.major == (_maj)) && (version.minor >= (_min))))
+
 /*
  * DS Callbacks
  */
@@ -220,6 +229,8 @@
 	DR_DBG_CPU("reg_handler: arg=0x%p, ver=%d.%d, hdl=0x%lx\n", arg,
 	    ver->major, ver->minor, hdl);
 
+	version.major = ver->major;
+	version.minor = ver->minor;
 	ds_handle = hdl;
 }
 
@@ -316,12 +327,42 @@
 	}
 
 	/* free any allocated memory */
-	if (resp != &err_resp) {
+	if (DRCPU_VERS_GTEQ(1, 1) || (resp != &err_resp)) {
+		DR_DBG_KMEM("%s: free addr %p size %d\n",
+		    __func__, (void *)resp, resp_len);
 		kmem_free(resp, resp_len);
 	}
 }
 
 /*
+ * Create a response message which consists of a header followed
+ * by the error string passed in.
+ */
+static size_t
+dr_cpu_err_resp(dr_cpu_hdr_t *req, dr_cpu_hdr_t **respp, char *msg)
+{
+	size_t size;
+	dr_cpu_hdr_t *resp;
+
+	ASSERT((msg != NULL) && (strlen(msg) > 0));
+
+	size = sizeof (*req) + strlen(msg) + 1;
+	resp = kmem_alloc(size, KM_SLEEP);
+	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
+	    __func__, (void *)resp, size);
+
+	resp->req_num = req->req_num;
+	resp->msg_type = DR_CPU_ERROR;
+	resp->num_records = 0;
+
+	(void) strcpy((char *)(resp) + sizeof (*resp), msg);
+
+	*respp = resp;
+
+	return (size);
+}
+
+/*
  * Common routine to config or unconfig multiple cpus.  The unconfig
  * case checks with the OS to see if the removal of cpus will be
  * permitted, but can be overridden by the "force" version of the
@@ -344,12 +385,11 @@
 	int		drctl_flags = 0;
 	drctl_rsrc_t	*drctl_req;
 	size_t		drctl_req_len;
-	drctl_rsrc_t	*drctl_res;
-	size_t		drctl_res_len = 0;
+	drctl_resp_t	*drctl_resp;
+	drctl_rsrc_t	*drctl_rsrc;
+	size_t		drctl_resp_len = 0;
 	drctl_cookie_t	drctl_res_ck;
 
-	static const char me[] = "dr_cpu_list_wrk";
-
 	ASSERT((req != NULL) && (req->num_records != 0));
 
 	count = req->num_records;
@@ -375,7 +415,8 @@
 		break;
 	default:
 		/* Programming error if we reach this. */
-		cmn_err(CE_NOTE, "%s: bad msg_type %d\n", me, req->msg_type);
+		cmn_err(CE_NOTE,
+		    "%s: bad msg_type %d\n", __func__, req->msg_type);
 		ASSERT(0);
 		return (-1);
 	}
@@ -386,22 +427,48 @@
 	/* allocate drctl request msg based on incoming resource count */
 	drctl_req_len = sizeof (drctl_rsrc_t) * count;
 	drctl_req = kmem_zalloc(drctl_req_len, KM_SLEEP);
+	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
+	    __func__, (void *)drctl_req, drctl_req_len);
 
 	/* copy the cpuids for the drctl call from the incoming request msg */
 	for (idx = 0; idx < count; idx++)
 		drctl_req[idx].res_cpu_id = req_cpus[idx];
 
-	if ((rv = drctl_config_init(drctl_cmd, drctl_flags, drctl_req,
-	    count, &drctl_res, &drctl_res_len, &drctl_res_ck)) != 0) {
-		DR_DBG_CPU("%s: drctl_config_init returned: %d\n", me, rv);
+	rv = drctl_config_init(drctl_cmd, drctl_flags, drctl_req,
+	    count, &drctl_resp, &drctl_resp_len, &drctl_res_ck);
+
+	ASSERT((drctl_resp != NULL) && (drctl_resp_len != 0));
+
+	if (rv != 0) {
+		DR_DBG_CPU("%s: drctl_config_init "
+		    "returned: %d\n", __func__, rv);
+
+		if (DRCPU_VERS_EQ(1, 0)) {
+			rv = -1;
+		} else {
+			ASSERT(DRCPU_VERS_GTEQ(1, 1));
+			ASSERT(drctl_resp->resp_type == DRCTL_RESP_ERR);
+
+			*resp_len = dr_cpu_err_resp(req,
+			    resp, drctl_resp->resp_err_msg);
+		}
+
+		DR_DBG_KMEM("%s: free addr %p size %ld\n",
+		    __func__, (void *)drctl_resp, drctl_resp_len);
+		kmem_free(drctl_resp, drctl_resp_len);
+		DR_DBG_KMEM("%s: free addr %p size %ld\n",
+		    __func__, (void *)drctl_req, drctl_req_len);
 		kmem_free(drctl_req, drctl_req_len);
-		return (-1);
+
+		return (rv);
 	}
 
-	ASSERT((drctl_res != NULL) && (drctl_res_len != 0));
+	ASSERT(drctl_resp->resp_type == DRCTL_RESP_OK);
+
+	drctl_rsrc = drctl_resp->resp_resources;
 
 	/* create the result scratch array */
-	res = dr_cpu_res_array_init(req, drctl_res, count);
+	res = dr_cpu_res_array_init(req, drctl_rsrc, count);
 
 	/*
 	 * For unconfigure, check if there are any conditions
@@ -440,12 +507,13 @@
 		    DRCTL_STATUS_CONFIG_FAILURE : DRCTL_STATUS_CONFIG_SUCCESS;
 
 		DR_DBG_CPU("%s: cpuid %d status %d result %d off '%s'\n",
-		    me, req_cpus[idx], drctl_req[idx].status, result,
+		    __func__, req_cpus[idx], drctl_req[idx].status, result,
 		    (res[idx].string) ? res[idx].string : "");
 	}
 
 	if ((rv = drctl_config_fini(&drctl_res_ck, drctl_req, count)) != 0)
-		DR_DBG_CPU("%s: drctl_config_fini returned: %d\n", me, rv);
+		DR_DBG_CPU("%s: drctl_config_fini "
+		    "returned: %d\n", __func__, rv);
 
 	/*
 	 * Operation completed without any fatal errors.
@@ -459,7 +527,11 @@
 	/*
 	 * Deallocate any scratch memory.
 	 */
-	kmem_free(drctl_res, drctl_res_len);
+	DR_DBG_KMEM("%s: free addr %p size %ld\n",
+	    __func__, (void *)drctl_resp, drctl_resp_len);
+	kmem_free(drctl_resp, drctl_resp_len);
+	DR_DBG_KMEM("%s: free addr %p size %ld\n",
+	    __func__, (void *)drctl_req, drctl_req_len);
 	kmem_free(drctl_req, drctl_req_len);
 
 	dr_cpu_res_array_fini(res, count);
@@ -481,6 +553,8 @@
 
 	/* allocate zero filled buffer to initialize fields */
 	res = kmem_zalloc(nrsrc * sizeof (dr_cpu_res_t), KM_SLEEP);
+	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
+	    __func__, (void *)res, nrsrc * sizeof (dr_cpu_res_t));
 
 	/*
 	 * Fill in the result information for each resource.
@@ -510,6 +584,8 @@
 			err_len = strlen(err_str) + 1;
 
 			res[idx].string = kmem_alloc(err_len, KM_SLEEP);
+			DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
+			    __func__, (void *)(res[idx].string), err_len);
 			bcopy(err_str, res[idx].string, err_len);
 		}
 	}
@@ -527,11 +603,15 @@
 		/* deallocate the error string if present */
 		if (res[idx].string) {
 			str_len = strlen(res[idx].string) + 1;
+			DR_DBG_KMEM("%s: free addr %p size %ld\n",
+			    __func__, (void *)(res[idx].string), str_len);
 			kmem_free(res[idx].string, str_len);
 		}
 	}
 
 	/* deallocate the result array itself */
+	DR_DBG_KMEM("%s: free addr %p size %ld\n",
+	    __func__, (void *)res, sizeof (dr_cpu_res_t) * nres);
 	kmem_free(res, sizeof (dr_cpu_res_t) * nres);
 }
 
@@ -575,6 +655,8 @@
 
 	/* allocate the message buffer */
 	resp = kmem_zalloc(resp_len, KM_SLEEP);
+	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
+	    __func__, (void *)resp, resp_len);
 
 	/*
 	 * Fill in the header information.
@@ -704,6 +786,8 @@
 	 * the number of CPUs.
 	 */
 	psrset = kmem_zalloc(sizeof (*psrset) * nres, KM_SLEEP);
+	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
+	    __func__, (void *)psrset, sizeof (*psrset) * nres);
 
 	for (cpu_idx = 0; cpu_idx < nres; cpu_idx++) {
 
@@ -754,12 +838,16 @@
 			err_len = strlen(err_str) + 1;
 
 			res[cpu_idx].string = kmem_alloc(err_len, KM_SLEEP);
+			DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
+			    __func__, (void *)(res[cpu_idx].string), err_len);
 			bcopy(err_str, res[cpu_idx].string, err_len);
 
 			DR_DBG_CPU("cpu %d: %s\n", cpuids[cpu_idx], err_str);
 		}
 	}
 
+	DR_DBG_KMEM("%s: free addr %p size %ld\n",
+	    __func__, (void *)psrset, sizeof (*psrset) * nres);
 	kmem_free(psrset, sizeof (*psrset) * nres);
 }
 
@@ -840,6 +928,8 @@
 		err_len = strlen(err_str) + 1;
 
 		res->string = kmem_alloc(err_len, KM_SLEEP);
+		DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
+		    __func__, (void *)(res->string), err_len);
 		bcopy(err_str, res->string, err_len);
 
 		DR_DBG_CPU("cpu %d: %s\n", cp->cpu_id, err_str);
@@ -875,6 +965,7 @@
 	rlen = sizeof (dr_cpu_hdr_t);
 	rlen += req->num_records * sizeof (dr_cpu_stat_t);
 	rp = kmem_zalloc(rlen, KM_SLEEP);
+	DR_DBG_KMEM("%s: alloc addr %p size %d\n", __func__, (void *)rp, rlen);
 
 	/* fill in the known data */
 	rp->req_num = req->req_num;
@@ -917,6 +1008,8 @@
 
 	listsz = num_nodes * sizeof (mde_cookie_t);
 	listp = kmem_zalloc(listsz, KM_SLEEP);
+	DR_DBG_KMEM("%s: alloc addr %p size %d\n",
+	    __func__, (void *)listp, listsz);
 
 	for (idx = 0; idx < req->num_records; idx++) {
 
@@ -935,6 +1028,8 @@
 		}
 	}
 
+	DR_DBG_KMEM("%s: free addr %p size %d\n",
+	    __func__, (void *)listp, listsz);
 	kmem_free(listp, listsz);
 
 	(void) md_fini_handle(mdp);
@@ -1339,6 +1434,8 @@
 
 	listsz = num_nodes * sizeof (mde_cookie_t);
 	listp = kmem_zalloc(listsz, KM_SLEEP);
+	DR_DBG_KMEM("%s: alloc addr %p size %d\n",
+	    __func__, (void *)listp, listsz);
 
 	cpunode = dr_cpu_find_node_md(cpuid, mdp, listp);
 
@@ -1368,8 +1465,11 @@
 	rv = 0;
 
 done:
-	if (listp)
+	if (listp) {
+		DR_DBG_KMEM("%s: free addr %p size %d\n",
+		    __func__, (void *)listp, listsz);
 		kmem_free(listp, listsz);
+	}
 
 	if (mdp)
 		(void) md_fini_handle(mdp);
@@ -1393,6 +1493,8 @@
 	if (e_ddi_branch_destroy(dip, &fdip, 0)) {
 		char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
 
+		DR_DBG_KMEM("%s: alloc addr %p size %d\n",
+		    __func__, (void *)path, MAXPATHLEN);
 		/*
 		 * If non-NULL, fdip is held and must be released.
 		 */
@@ -1405,6 +1507,8 @@
 		cmn_err(CE_NOTE, "node removal failed: %s (%p)",
 		    path, (fdip) ? (void *)fdip : (void *)dip);
 
+		DR_DBG_KMEM("%s: free addr %p size %d\n",
+		    __func__, (void *)path, MAXPATHLEN);
 		kmem_free(path, MAXPATHLEN);
 
 		return (-1);
--- a/usr/src/uts/sun4v/io/dr_io.c	Tue Oct 21 12:07:10 2008 -0700
+++ b/usr/src/uts/sun4v/io/dr_io.c	Tue Oct 21 16:19:49 2008 -0700
@@ -404,6 +404,8 @@
 	max_nodes = md_node_count(mdp);
 	listsz = max_nodes * sizeof (mde_cookie_t);
 	listp = kmem_zalloc(listsz, KM_SLEEP);
+	DR_DBG_KMEM("%s: alloc addr %p size %d\n",
+	    __func__, (void *)listp, listsz);
 
 	num_nodes = md_scan_dag(mdp, node,
 	    md_find_name(mdp, "channel-devices"),
@@ -414,6 +416,8 @@
 	if (num_nodes == 1)
 		pnode = listp[0];
 
+	DR_DBG_KMEM("%s: free addr %p size %d\n",
+	    __func__, (void *)listp, listsz);
 	kmem_free(listp, listsz);
 
 	return (pnode);
@@ -441,11 +445,11 @@
 	int		drctl_flags = 0;
 	drctl_rsrc_t	*drctl_req;
 	size_t		drctl_req_len;
-	drctl_rsrc_t	*drctl_res = NULL;
-	size_t		drctl_res_len = 0;
+	drctl_rsrc_t	*drctl_rsrc = NULL;
 	drctl_cookie_t	drctl_res_ck;
 	char		*p;
-	size_t		reason_len;
+	drctl_resp_t	*drctl_resp;
+	size_t		drctl_resp_len = 0;
 
 	res->result = DR_VIO_RES_FAILURE;
 
@@ -473,6 +477,8 @@
 
 	listsz = nnodes * sizeof (mde_cookie_t);
 	listp = kmem_zalloc(listsz, KM_SLEEP);
+	DR_DBG_KMEM("%s: alloc addr %p size %d\n",
+	    __func__, (void *)listp, listsz);
 
 	/*
 	 * Get the MD device node.
@@ -520,6 +526,8 @@
 
 	drctl_req_len = sizeof (drctl_rsrc_t) + MAXPATHLEN;
 	drctl_req = kmem_zalloc(drctl_req_len, KM_SLEEP);
+	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
+	    __func__, (void *)drctl_req, drctl_req_len);
 	drctl_req->status = DRCTL_STATUS_INIT;
 
 	drctl_cmd = DRCTL_IO_CONFIG_REQUEST;
@@ -534,24 +542,38 @@
 	(void) sprintf(p + strlen(p), "/%s@%ld", name, devid);
 	DR_DBG_IO("%s: devpath=%s\n", __func__, drctl_req->res_dev_path);
 
-	if ((rv = drctl_config_init(drctl_cmd, drctl_flags, drctl_req,
-	    1, &drctl_res, &drctl_res_len, &drctl_res_ck)) != 0) {
+	rv = drctl_config_init(drctl_cmd, drctl_flags, drctl_req,
+	    1, &drctl_resp, &drctl_resp_len, &drctl_res_ck);
+
+	ASSERT((drctl_resp != NULL) && (drctl_resp_len != 0));
+
+	drctl_rsrc = drctl_resp->resp_resources;
 
+	if (rv != 0) {
 		DR_DBG_IO("%s: drctl_config_init failed: %d\n", __func__, rv);
+
+		ASSERT(drctl_resp->resp_type == DRCTL_RESP_ERR);
+
+		(void) strlcpy(res->reason,
+		    drctl_resp->resp_err_msg, DR_VIO_MAXREASONLEN);
+
+		DR_DBG_IO("%s: %s\n", __func__, res->reason);
+
 		goto done;
 
-	} else if (drctl_res->status == DRCTL_STATUS_DENY) {
+	}
+
+	ASSERT(drctl_resp->resp_type == DRCTL_RESP_OK);
+
+	if (drctl_rsrc->status == DRCTL_STATUS_DENY) {
+
 		res->result = DR_VIO_RES_BLOCKED;
 
 		DR_DBG_IO("%s: drctl_config_init denied\n", __func__);
-		p = (char *)drctl_res + drctl_res->offset;
-		reason_len = strlen(p);
+		p = (char *)drctl_rsrc + drctl_rsrc->offset;
 
-		if (reason_len >= DR_VIO_MAXREASONLEN)
-			reason_len = DR_VIO_MAXREASONLEN - 1;
+		(void) strlcpy(res->reason, p, DR_VIO_MAXREASONLEN);
 
-		(void) strncpy(res->reason, p, reason_len);
-		res->reason[reason_len] = '\0';
 		DR_DBG_IO("%s: %s\n", __func__, res->reason);
 
 		drctl_req->status = DRCTL_STATUS_CONFIG_FAILURE;
@@ -579,8 +601,11 @@
 		DR_DBG_IO("%s: drctl_config_fini returned: %d\n", __func__, rv);
 
 done:
-	if (listp)
+	if (listp) {
+		DR_DBG_KMEM("%s: free addr %p size %d\n",
+		    __func__, (void *)listp, listsz);
 		kmem_free(listp, listsz);
+	}
 
 	if (mdp)
 		(void) md_fini_handle(mdp);
@@ -588,9 +613,15 @@
 	if (pdip)
 		e_ddi_branch_rele(pdip);
 
+	DR_DBG_KMEM("%s: free addr %p size %ld\n",
+	    __func__, (void *)drctl_req, drctl_req_len);
 	kmem_free(drctl_req, drctl_req_len);
-	if (drctl_res)
-		kmem_free(drctl_res, drctl_res_len);
+
+	if (drctl_resp) {
+		DR_DBG_KMEM("%s: free addr %p size %ld\n",
+		    __func__, (void *)drctl_resp, drctl_resp_len);
+		kmem_free(drctl_resp, drctl_resp_len);
+	}
 
 	if (rv == 0) {
 		res->result = DR_VIO_RES_OK;
@@ -618,10 +649,10 @@
 	int		drctl_flags = 0;
 	drctl_rsrc_t	*drctl_req;
 	size_t		drctl_req_len;
-	drctl_rsrc_t	*drctl_res = NULL;
-	size_t		drctl_res_len = 0;
+	drctl_rsrc_t	*drctl_rsrc = NULL;
 	drctl_cookie_t	drctl_res_ck;
-	size_t		reason_len;
+	drctl_resp_t	*drctl_resp;
+	size_t		drctl_resp_len;
 
 	if ((dip = dr_io_find_node(name, devid)) == NULL) {
 		DR_DBG_IO("%s: %s@%ld already unconfigured\n",
@@ -640,6 +671,8 @@
 
 	drctl_req_len = sizeof (drctl_rsrc_t) + MAXPATHLEN;
 	drctl_req = kmem_zalloc(drctl_req_len, KM_SLEEP);
+	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
+	    __func__, (void *)drctl_req, drctl_req_len);
 	drctl_req->status = DRCTL_STATUS_INIT;
 
 	drctl_cmd = DRCTL_IO_UNCONFIG_REQUEST;
@@ -652,24 +685,35 @@
 	(void) ddi_pathname(dip, p + strlen(p));
 	DR_DBG_IO("%s: devpath=%s\n", __func__, drctl_req->res_dev_path);
 
-	if ((rv = drctl_config_init(drctl_cmd, drctl_flags, drctl_req,
-	    1, &drctl_res, &drctl_res_len, &drctl_res_ck)) != 0) {
+	rv = drctl_config_init(drctl_cmd, drctl_flags, drctl_req,
+	    1, &drctl_resp, &drctl_resp_len, &drctl_res_ck);
+
+	ASSERT((drctl_resp != NULL) && (drctl_resp_len != 0));
+
+	drctl_rsrc = drctl_resp->resp_resources;
+
+	if (rv != 0) {
 
 		DR_DBG_IO("%s: drctl_config_init failed: %d\n", __func__, rv);
-		goto done;
+
+		ASSERT(drctl_resp->resp_type == DRCTL_RESP_ERR);
+
+		(void) strlcpy(res->reason,
+		    drctl_resp->resp_err_msg, DR_VIO_MAXREASONLEN);
 
-	} else if (drctl_res->status == DRCTL_STATUS_DENY) {
+		DR_DBG_IO("%s: %s\n", __func__, res->reason);
+
+		goto done;
+	}
+
+	if (drctl_rsrc->status == DRCTL_STATUS_DENY) {
 		res->result = DR_VIO_RES_BLOCKED;
 
 		DR_DBG_IO("%s: drctl_config_init denied\n", __func__);
-		p = (char *)drctl_res + drctl_res->offset;
-		reason_len = strlen(p);
+		p = (char *)drctl_rsrc + drctl_rsrc->offset;
 
-		if (reason_len >= DR_VIO_MAXREASONLEN)
-			reason_len = DR_VIO_MAXREASONLEN - 1;
+		(void) strlcpy(res->reason, p, DR_VIO_MAXREASONLEN);
 
-		(void) strncpy(res->reason, p, reason_len);
-		res->reason[reason_len] = '\0';
 		DR_DBG_IO("%s: %s\n", __func__, res->reason);
 
 		drctl_req->status = DRCTL_STATUS_CONFIG_FAILURE;
@@ -678,6 +722,8 @@
 	} else if (rv = e_ddi_branch_destroy(dip, &fdip, 0)) {
 		char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
 
+		DR_DBG_KMEM("%s: alloc addr %p size %d\n",
+		    __func__, (void *)path, MAXPATHLEN);
 		/*
 		 * If non-NULL, fdip is held and must be released.
 		 */
@@ -693,6 +739,8 @@
 
 		drctl_req->status = DRCTL_STATUS_CONFIG_FAILURE;
 
+		DR_DBG_KMEM("%s: free addr %p size %d\n",
+		    __func__, (void *)path, MAXPATHLEN);
 		kmem_free(path, MAXPATHLEN);
 	} else {
 		drctl_req->status = DRCTL_STATUS_CONFIG_SUCCESS;
@@ -711,10 +759,15 @@
 		dr_generate_event(DR_TYPE_VIO, SE_HINT_REMOVE);
 	}
 done:
+	DR_DBG_KMEM("%s: free addr %p size %ld\n",
+	    __func__, (void *)drctl_req, drctl_req_len);
 	kmem_free(drctl_req, drctl_req_len);
 
-	if (drctl_res)
-		kmem_free(drctl_res, drctl_res_len);
+	if (drctl_resp) {
+		DR_DBG_KMEM("%s: free addr %p size %ld\n",
+		    __func__, (void *)drctl_resp, drctl_resp_len);
+		kmem_free(drctl_resp, drctl_resp_len);
+	}
 
 	return (rv);
 }
@@ -734,6 +787,8 @@
 	 */
 	res_len = sizeof (dr_vio_res_t) + DR_VIO_MAXREASONLEN;
 	res = kmem_zalloc(res_len, KM_SLEEP);
+	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
+	    __func__, (void *)res, res_len);
 	res->result = DR_VIO_RES_FAILURE;
 
 	/*
@@ -776,8 +831,11 @@
 	if (ds_cap_send(ds_vio_handle, res, res_len) != 0)
 		DR_DBG_IO("ds_send failed\n");
 
-	if (res)
+	if (res) {
+		DR_DBG_KMEM("%s: free addr %p size %ld\n",
+		    __func__, (void *)res, res_len);
 		kmem_free(res, res_len);
+	}
 }
 
 static void
--- a/usr/src/uts/sun4v/io/drctl.c	Tue Oct 21 12:07:10 2008 -0700
+++ b/usr/src/uts/sun4v/io/drctl.c	Tue Oct 21 16:19:49 2008 -0700
@@ -24,7 +24,6 @@
  * Use is subject to license terms.
  */
 
-
 /*
  * DR control module for LDoms
  */
@@ -53,8 +52,8 @@
 static int drctl_close(dev_t, int, int, cred_t *);
 static int drctl_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
 
-static void *pack_message(int, int, int, void *, size_t *);
-static int send_message(void *, size_t, void **, size_t *);
+static void *pack_message(int, int, int, void *, size_t *, size_t *);
+static int send_message(void *, size_t, drctl_resp_t **, size_t *);
 
 
 /*
@@ -273,41 +272,115 @@
 }
 
 /*
- * This driver guarantees that if drctl_config_init returns 0,
- * a valid response buffer will be passed back to the caller.  This
- * routine can be used to generate that response in cases where the
- * upcall has not resulted in a response message from userland.
+ * Create a reponse structure which includes an array of drctl_rsrc_t
+ * structures in which each status element is set to the 'status'
+ * arg.  There is no error text, so set the 'offset' elements to 0.
  */
-static drctl_rsrc_t *
+static drctl_resp_t *
 drctl_generate_resp(drctl_rsrc_t *res,
     int count, size_t *rsize, drctl_status_t status)
 {
-	int		idx;
+	int		i;
 	size_t		size;
-	drctl_rsrc_t	*rbuf;
+	drctl_rsrc_t	*rsrc;
+	drctl_resp_t	*resp;
 
-	size = count * sizeof (*res);
-	rbuf  = kmem_alloc(size, KM_SLEEP);
+	size = offsetof(drctl_resp_t, resp_resources) + (count * sizeof (*res));
+	resp  = kmem_alloc(size, KM_SLEEP);
+	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
+	    __func__, (void *)resp, size);
 
-	bcopy(res, rbuf, size);
+	resp->resp_type = DRCTL_RESP_OK;
+	rsrc = resp->resp_resources;
 
-	for (idx = 0; idx < count; idx++) {
-		rbuf[idx].status = status;
-		rbuf[idx].offset = 0;
+	bcopy(res, rsrc, count * sizeof (*res));
+
+	for (i = 0; i < count; i++) {
+		rsrc[i].status = status;
+		rsrc[i].offset = 0;
 	}
 
 	*rsize = size;
-	return (rbuf);
+
+	return (resp);
+}
+
+/*
+ * Generate an error response message.
+ */
+static drctl_resp_t *
+drctl_generate_err_resp(char *msg, size_t *size)
+{
+	drctl_resp_t	*resp;
+
+	ASSERT(msg != NULL);
+	ASSERT(size != NULL);
+
+	*size = offsetof(drctl_resp_t, resp_err_msg) + strlen(msg) + 1;
+	resp = kmem_alloc(*size, KM_SLEEP);
+	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
+	    __func__, (void *)resp, *size);
+
+	resp->resp_type = DRCTL_RESP_ERR;
+	(void) strcpy(resp->resp_err_msg, msg);
+
+	return (resp);
 }
 
+/*
+ * Since response comes from userland, verify that it is at least the
+ * minimum size based on the size of the original request.  Verify
+ * that any offsets to error strings are within the string area of
+ * the response and, force the string area to be null-terminated.
+ */
+static int
+verify_response(int cmd,
+    int count, drctl_resp_t *resp, size_t sent_len, size_t resp_len)
+{
+	drctl_rsrc_t *rsrc = resp->resp_resources;
+	size_t rcvd_len = resp_len - (offsetof(drctl_resp_t, resp_resources));
+	int is_cpu = 0;
+	int i;
+
+	switch (cmd) {
+	case DRCTL_CPU_CONFIG_REQUEST:
+	case DRCTL_CPU_UNCONFIG_REQUEST:
+		if (rcvd_len < sent_len)
+			return (EIO);
+		is_cpu = 1;
+		break;
+	case DRCTL_IO_UNCONFIG_REQUEST:
+	case DRCTL_IO_CONFIG_REQUEST:
+		if (count != 1)
+			return (EIO);
+		break;
+	default:
+		return (EIO);
+	}
+
+	for (i = 0; i < count; i++)
+		if ((rsrc[i].offset > 0) &&
+		    /* string can't be inside the bounds of original request */
+		    (((rsrc[i].offset < sent_len) && is_cpu) ||
+		    /* string must start inside the message */
+		    (rsrc[i].offset >= rcvd_len)))
+			return (EIO);
+
+	/* If there are any strings, terminate the string area. */
+	if (rcvd_len > sent_len)
+		*((char *)rsrc + rcvd_len - 1) = '\0';
+
+	return (0);
+}
+
+
 static int
 drctl_config_common(int cmd, int flags, drctl_rsrc_t *res,
-    int count, drctl_rsrc_t **rbuf, size_t *rsize)
+    int count, drctl_resp_t **rbuf, size_t *rsize, size_t *rq_size)
 {
 	int	rv = 0;
 	size_t	size;
 	char	*bufp;
-	static const char me[] = "drctl_config_common";
 
 	switch (cmd) {
 	case DRCTL_CPU_CONFIG_REQUEST:
@@ -329,7 +402,7 @@
 	}
 
 	if (rv != 0) {
-		DR_DBG_CTL("%s: invalid cmd %d\n", me, cmd);
+		DR_DBG_CTL("%s: invalid cmd %d\n", __func__, cmd);
 		return (rv);
 	}
 
@@ -339,124 +412,98 @@
 	 * response, so generate a response with all ops 'allowed'.
 	 */
 	if (flags == DRCTL_FLAG_FORCE) {
-		if (rbuf != NULL) {
-			*rbuf = drctl_generate_resp(res, count, &size,
-			    DRCTL_STATUS_ALLOW);
-			*rsize = size;
-		}
+		if (rbuf != NULL)
+			*rbuf = drctl_generate_resp(res,
+			    count, rsize, DRCTL_STATUS_ALLOW);
 		return (0);
 	}
 
-	bufp = pack_message(cmd, flags, count, (void *)res, &size);
+	bufp = pack_message(cmd, flags, count, (void *)res, &size, rq_size);
 	DR_DBG_CTL("%s: from pack_message, bufp = %p size %ld\n",
-	    me, (void *)bufp, size);
-	if (bufp == NULL || size == 0)
-		return (EIO);
-
-	rv = send_message(bufp, size, (void **)rbuf, rsize);
-
-	/*
-	 * For failure, as part of our contract with the caller,
-	 * generate a response message, but mark all proposed
-	 * changes as 'denied'.
-	 */
-	if (rv != 0 && rbuf != NULL) {
-		*rbuf = drctl_generate_resp(res, count, &size,
-		    DRCTL_STATUS_DENY);
-		*rsize = size;
-	}
-
-	return (rv);
-}
+	    __func__, (void *)bufp, size);
 
-/*
- * Since the response comes from userland, make sure it is
- * at least the minimum size and, if it contains error
- * strings, that the string area is null-terminated.
- */
-static int
-verify_response(int count, drctl_rsrc_t *resp, size_t size)
-{
-	int idx;
-	int need_terminator = 0;
-	static const char me[] = "verify_response";
-
-	if (resp == NULL || size < count * sizeof (*resp)) {
-		DR_DBG_CTL("%s: BAD size - count %d size %ld\n",
-		    me, count, size);
-		return (EIO);
-	}
+	if (bufp == NULL || size == 0)
+		return (EINVAL);
 
-	for (idx = 0; idx < count; idx++) {
-
-		if (resp[idx].offset != 0)
-			need_terminator++;
-	}
-
-	if (need_terminator && *((caddr_t)(resp) + size - 1) != '\0') {
-		DR_DBG_CTL("%s: unterm. strings: resp %p size %ld char %d\n",
-		    me, (void *)resp, size, *((caddr_t)(resp) + size - 1));
-		/* Don't fail the transaction, but don't advertise strings */
-		for (idx = 0; idx < count; idx++)
-			resp[idx].offset = 0;
-	}
-
-	return (0);
+	return (send_message(bufp, size, rbuf, rsize));
 }
 
-
 /*
  * Prepare for a reconfig operation.
  */
 int
 drctl_config_init(int cmd, int flags, drctl_rsrc_t *res,
-    int count, drctl_rsrc_t **rbuf, size_t *rsize, drctl_cookie_t ck)
+    int count, drctl_resp_t **rbuf, size_t *rsize, drctl_cookie_t ck)
 {
-	static char me[] = "drctl_config_init";
-	int idx;
+	static char inval_msg[] = "Invalid command format received.\n";
+	static char unsup_msg[] = "Unsuppported command received.\n";
+	static char unk_msg  [] = "Failure reason unknown.\n";
+	static char rsp_msg  [] = "Invalid response from "
+	    "reconfiguration daemon.\n";
+	static char drd_msg  [] = "Cannot communicate with reconfiguration "
+	    "daemon (drd) in target domain.\n"
+	    "drd(1M) SMF service may not be enabled.\n";
+	static char busy_msg [] = "Busy executing earlier command; "
+	    "please try again later.\n";
+	size_t rq_size;
+	char *ermsg;
 	int rv;
 
-	if (ck == 0)
+	if (ck == 0) {
+		*rbuf = drctl_generate_err_resp(inval_msg, rsize);
+
 		return (EINVAL);
+	}
 
 	mutex_enter(&drctlp->drc_lock);
 
 	if (drctlp->drc_busy != NULL) {
 		mutex_exit(&drctlp->drc_lock);
+		*rbuf = drctl_generate_err_resp(busy_msg, rsize);
+
 		return (EBUSY);
 	}
 
 	DR_DBG_CTL("%s: cmd %d flags %d res %p count %d\n",
-	    me, cmd, flags, (void *)res, count);
+	    __func__, cmd, flags, (void *)res, count);
 
 	/* Mark the link busy.  Below we will fill in the actual cookie. */
 	drctlp->drc_busy = (drctl_cookie_t)-1;
 	mutex_exit(&drctlp->drc_lock);
 
-	if ((rv = drctl_config_common(cmd,
-	    flags, res, count, rbuf, rsize)) == 0 &&
-	    verify_response(count, *rbuf, *rsize) == 0) {
-		drctlp->drc_busy = ck;
-		drctlp->drc_cmd = cmd;
-		drctlp->drc_flags = flags;
-
+	rv = drctl_config_common(cmd, flags, res, count, rbuf, rsize, &rq_size);
+	if (rv == 0) {
 		/*
-		 * If there wasn't a valid response msg passed back,
-		 * create a response with each resource op denied.
+		 * If the upcall to the daemon returned successfully, we
+		 * still need to validate the format of the returned msg.
 		 */
-		if (*rbuf == NULL || *rsize == 0) {
-			drctl_rsrc_t *bp = *rbuf;
-
-			*rsize = count * sizeof (*bp);
-			bp = kmem_zalloc(*rsize, KM_SLEEP);
-			bcopy(res, bp, *rsize);
-
-			for (idx = 0; idx < count; idx++) {
-				bp[idx].status = DRCTL_STATUS_DENY;
-				bp[idx].offset = 0;
-			}
+		if ((rv = verify_response(cmd,
+		    count, *rbuf, rq_size, *rsize)) != 0) {
+			DR_DBG_KMEM("%s: free addr %p size %ld\n",
+			    __func__, (void *)*rbuf, *rsize);
+			kmem_free(*rbuf, *rsize);
+			*rbuf = drctl_generate_err_resp(rsp_msg, rsize);
+			drctlp->drc_busy = NULL;
+		} else { /* message format is valid */
+			drctlp->drc_busy = ck;
+			drctlp->drc_cmd = cmd;
+			drctlp->drc_flags = flags;
 		}
 	} else {
+		switch (rv) {
+		case ENOTSUP:
+			ermsg = unsup_msg;
+			break;
+		case EIO:
+			ermsg = drd_msg;
+			break;
+		default:
+			ermsg = unk_msg;
+			break;
+		}
+
+		*rbuf = drctl_generate_err_resp(ermsg, rsize);
+
 		drctlp->drc_cmd = -1;
 		drctlp->drc_flags = 0;
 		drctlp->drc_busy = NULL;
@@ -474,6 +521,7 @@
 	int rv;
 	int notify_cmd;
 	int flags;
+	size_t rq_size;
 
 	mutex_enter(&drctlp->drc_lock);
 
@@ -519,7 +567,8 @@
 		goto done;
 	}
 
-	rv = drctl_config_common(notify_cmd, flags, res, count, NULL, 0);
+	rv = drctl_config_common(notify_cmd,
+	    flags, res, count, NULL, 0, &rq_size);
 
 done:
 	drctlp->drc_cmd = -1;
@@ -556,36 +605,63 @@
  * back in obufp, its size in osize.
  */
 static int
-send_message(void *msg, size_t size, void **obufp, size_t *osize)
+send_message(void *msg, size_t size, drctl_resp_t **obufp, size_t *osize)
 {
+	drctl_resp_t *bufp;
+	drctl_rsrc_t *rsrcs;
+	size_t rsrcs_size;
 	int rv;
 
-	rv = i_drctl_send(msg, size, obufp, osize);
+	rv = i_drctl_send(msg, size, (void **)&rsrcs, &rsrcs_size);
+
+	if ((rv == 0) && ((rsrcs == NULL) ||(rsrcs_size == 0)))
+		rv = EINVAL;
+
+	if (rv == 0) {
+		if (obufp != NULL) {
+			ASSERT(osize != NULL);
 
+			*osize =
+			    offsetof(drctl_resp_t, resp_resources) + rsrcs_size;
+			bufp =
+			    kmem_alloc(*osize, KM_SLEEP);
+			DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
+			    __func__, (void *)bufp, *osize);
+			bufp->resp_type = DRCTL_RESP_OK;
+			bcopy(rsrcs, bufp->resp_resources, rsrcs_size);
+			*obufp = bufp;
+		}
+
+		DR_DBG_KMEM("%s: free addr %p size %ld\n",
+		    __func__, (void *)rsrcs, rsrcs_size);
+		kmem_free(rsrcs, rsrcs_size);
+	}
+
+	DR_DBG_KMEM("%s:free addr %p size %ld\n", __func__, msg, size);
 	kmem_free(msg, size);
 
 	return (rv);
 }
 
 static void *
-pack_message(int cmd, int flags, int count, void *data, size_t *osize)
+pack_message(int cmd,
+    int flags, int count, void *data, size_t *osize, size_t *data_size)
 {
 	drd_msg_t *msgp = NULL;
 	size_t hdr_size = offsetof(drd_msg_t, data);
-	size_t data_size = 0;
 
 	switch (cmd) {
 	case DRCTL_CPU_CONFIG_REQUEST:
 	case DRCTL_CPU_CONFIG_NOTIFY:
 	case DRCTL_CPU_UNCONFIG_REQUEST:
 	case DRCTL_CPU_UNCONFIG_NOTIFY:
-		data_size = count * sizeof (drctl_rsrc_t);
+		*data_size = count * sizeof (drctl_rsrc_t);
 		break;
 	case DRCTL_IO_CONFIG_REQUEST:
 	case DRCTL_IO_CONFIG_NOTIFY:
 	case DRCTL_IO_UNCONFIG_REQUEST:
 	case DRCTL_IO_UNCONFIG_NOTIFY:
-		data_size = sizeof (drctl_rsrc_t) +
+		*data_size = sizeof (drctl_rsrc_t) +
 		    strlen(((drctl_rsrc_t *)data)->res_dev_path);
 		break;
 	default:
@@ -595,12 +671,14 @@
 	}
 
 	if (data_size) {
-		*osize = hdr_size + data_size;
+		*osize = hdr_size + *data_size;
 		msgp = kmem_alloc(*osize, KM_SLEEP);
+		DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
+		    __func__, (void *)msgp, *osize);
 		msgp->cmd = cmd;
 		msgp->count = count;
 		msgp->flags = flags;
-		bcopy(data, msgp->data, data_size);
+		bcopy(data, msgp->data, *data_size);
 	}
 
 	return (msgp);
--- a/usr/src/uts/sun4v/io/drctl_impl.c	Tue Oct 21 12:07:10 2008 -0700
+++ b/usr/src/uts/sun4v/io/drctl_impl.c	Tue Oct 21 16:19:49 2008 -0700
@@ -24,8 +24,6 @@
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/types.h>
 #include <sys/door.h>
 #include <sys/note.h>
@@ -109,12 +107,17 @@
 		if (obufp != NULL) {
 			*obufp = door_args.rbuf;
 			*osize = door_args.rsize;
+			DR_DBG_KMEM("%s: (door) alloc addr %p size %ld\n",
+			    __func__,
+			    (void *)(door_args.rbuf), door_args.rsize);
 		} else {
 			/*
 			 * No output buffer pointer was passed in,
 			 * so the response buffer allocated by the
 			 * door code must be deallocated.
 			 */
+			DR_DBG_KMEM("%s: free addr %p size %ld\n", __func__,
+			    (void *)(door_args.rbuf), door_args.rsize);
 			kmem_free(door_args.rbuf, door_args.rsize);
 		}
 	} else {
--- a/usr/src/uts/sun4v/sys/dr_util.h	Tue Oct 21 12:07:10 2008 -0700
+++ b/usr/src/uts/sun4v/sys/dr_util.h	Tue Oct 21 16:19:49 2008 -0700
@@ -27,8 +27,6 @@
 #ifndef _DR_UTIL_H
 #define	_DR_UTIL_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * sun4v Common DR Header
  */
@@ -53,6 +51,7 @@
 #define	DR_DBG_FLAG_MEM		0x04
 #define	DR_DBG_FLAG_IO		0x08
 #define	DR_DBG_FLAG_TRANS	0x10
+#define	DR_DBG_FLAG_KMEM	0x20
 
 #define	DR_DBG_ALL	if (dr_debug)			  printf
 #define	DR_DBG_CTL	if (dr_debug & DR_DBG_FLAG_CTL)	  printf
@@ -60,6 +59,7 @@
 #define	DR_DBG_MEM	if (dr_debug & DR_DBG_FLAG_MEM)	  printf
 #define	DR_DBG_IO	if (dr_debug & DR_DBG_FLAG_IO)	  printf
 #define	DR_DBG_TRANS	if (dr_debug & DR_DBG_FLAG_TRANS) printf
+#define	DR_DBG_KMEM	if (dr_debug & DR_DBG_FLAG_KMEM)  printf
 
 #define	DR_DBG_DUMP_MSG(buf, len)	dr_dbg_dump_msg(buf, len)
 
@@ -73,6 +73,7 @@
 #define	DR_DBG_MEM	DR_DBG_ALL
 #define	DR_DBG_IO	DR_DBG_ALL
 #define	DR_DBG_TRANS	DR_DBG_ALL
+#define	DR_DBG_KMEM	DR_DBG_ALL
 
 #define	DR_DBG_DUMP_MSG(buf, len)
 
--- a/usr/src/uts/sun4v/sys/drctl.h	Tue Oct 21 12:07:10 2008 -0700
+++ b/usr/src/uts/sun4v/sys/drctl.h	Tue Oct 21 16:19:49 2008 -0700
@@ -20,15 +20,13 @@
  */
 
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef _SYS_DRCTL_H
 #define	_SYS_DRCTL_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifdef	__cplusplus
 extern "C" {
 #endif
@@ -96,6 +94,26 @@
 #define	res_dev_path	un.dev.path
 
 /*
+ * Response structure passed back by drctl to its clients
+ * (resource-specific DR modules).
+ */
+typedef enum {
+	DRCTL_RESP_ERR,
+	DRCTL_RESP_OK
+} drctl_resp_type_t;
+
+typedef struct drctl_resp {
+	drctl_resp_type_t resp_type;
+	union {
+		char err_msg[1];
+		drctl_rsrc_t  resources[1];
+	} un;
+} drctl_resp_t;
+
+#define	resp_err_msg		un.err_msg
+#define	resp_resources		un.resources
+
+/*
  * Message sent to DR daemon
  */
 typedef struct drd_msg {
@@ -115,7 +133,7 @@
  * _fini call.
  */
 extern int drctl_config_init(int, int,
-    drctl_rsrc_t *, int, drctl_rsrc_t **, size_t *, drctl_cookie_t);
+    drctl_rsrc_t *, int, drctl_resp_t **, size_t *, drctl_cookie_t);
 extern int drctl_config_fini(drctl_cookie_t, drctl_rsrc_t *, int);
 
 /*