Mercurial > illumos > illumos-gate
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); /*