dr_mem.c revision 56f33205c9ed776c3c909e07d52e94610a675740
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* sun4v Memory DR Module
*/
#include <sys/memlist_impl.h>
#include <sys/tuneable.h>
#include <vm/seg_kmem.h>
#define SUNDDI_IMPL /* so sunddi.h will not redefine splx() et al */
#include <sys/mem_config.h>
#include <sys/mem_cage.h>
#include <sys/mach_descrip.h>
/*
* DR operations are subject to Memory Alignment restrictions
* for both address and the size of the request.
*/
#define MBLK_IS_VALID(m) \
"sun4v memory DR"
};
static struct modlinkage modlinkage = {
(void *)&modlmisc,
};
static int dr_mem_allow_unload = 0;
typedef int (*fn_t)(dr_mem_blk_t *, int *);
/*
* Global Domain Services (DS) Handle
*/
static ds_svc_hdl_t ds_handle;
/*
* Supported DS Capability Versions
*/
/*
* DS Capability Description
*/
static ds_capability_t dr_mem_cap = {
DR_MEM_DS_ID, /* svc_id */
dr_mem_vers, /* vers */
DR_MEM_NVERS /* nvers */
};
/*
* DS Callbacks
*/
/*
* DS Client Ops Vector
*/
static ds_clnt_ops_t dr_mem_ops = {
dr_mem_reg_handler, /* ds_reg_cb */
dr_mem_unreg_handler, /* ds_unreg_cb */
dr_mem_data_handler, /* ds_data_cb */
NULL /* cb_arg */
};
/*
* Operation Results
*
* Used internally to gather results while an operation on a
* list of mblks is in progress. In particular, it is used to
* keep track of which mblks have already failed so that they are
* not processed further, and the manner in which they failed.
*/
typedef struct {
char *string;
} dr_mem_res_t;
static char *
dr_mem_estr[] = {
"operation succeeded", /* DR_MEM_RES_OK */
"operation failed", /* DR_MEM_RES_FAILURE */
"operation was blocked", /* DR_MEM_RES_BLOCKED */
"memory not defined in MD", /* DR_MEM_RES_NOT_IN_MD */
"memory already in use", /* DR_MEM_RES_ESPAN */
"memory access test failed", /* DR_MEM_RES_EFAULT */
"resource not available", /* DR_MEM_RES_ERESOURCE */
"permanent pages in span", /* DR_MEM_RES_PERM */
"memory span busy", /* DR_MEM_RES_EBUSY */
"VM viability test failed", /* DR_MEM_RES_ENOTVIABLE */
"no pages to unconfigure", /* DR_MEM_RES_ENOWORK */
"operation cancelled", /* DR_MEM_RES_ECANCELLED */
"operation refused", /* DR_MEM_RES_EREFUSED */
"memory span duplicate", /* DR_MEM_RES_EDUP */
"invalid argument" /* DR_MEM_RES_EINVAL */
};
typedef struct {
int error;
int done;
} mem_sync_t;
/*
* Internal Functions
*/
static int dr_mem_init(void);
static int dr_mem_fini(void);
static int dr_mem_unconfigure(dr_mem_blk_t *, int *);
static int dr_mem_configure(dr_mem_blk_t *, int *);
dr_mem_hdr_t **respp);
int
_init(void)
{
int status;
/* check that Memory DR is enabled */
if (dr_is_disabled(DR_TYPE_MEM))
return (ENOTSUP);
if ((status = dr_mem_init()) != 0) {
return (status);
}
(void) dr_mem_fini();
}
return (status);
}
int
{
}
int
_fini(void)
{
int status;
if (dr_mem_allow_unload == 0)
return (EBUSY);
(void) dr_mem_fini();
}
return (status);
}
static int
dr_mem_init(void)
{
int rv;
return (rv);
}
return (0);
}
static int
dr_mem_fini(void)
{
int rv;
}
return (rv);
}
static void
{
}
static void
{
}
/*ARGSUSED*/
static void
{
int resp_len = 0;
/*
* Sanity check the message
*/
if (buflen < sizeof (dr_mem_hdr_t)) {
DR_DBG_MEM("incoming message short: expected at least %ld "
goto done;
}
DR_DBG_MEM("empty message: expected at least %ld bytes\n",
sizeof (dr_mem_hdr_t));
goto done;
}
DR_DBG_MEM("incoming request:\n");
/*
* Process the command
*/
case DR_MEM_CONFIGURE:
case DR_MEM_UNCONFIGURE:
DR_DBG_MEM("No mblks specified for operation\n");
goto done;
}
DR_DBG_MEM("%s failed (%d)\n",
}
break;
case DR_MEM_UNCONF_STATUS:
break;
case DR_MEM_UNCONF_CANCEL:
break;
case DR_MEM_QUERY:
DR_DBG_MEM("No mblks specified for operation\n");
goto done;
}
break;
default:
break;
}
done:
/* check if an error occurred */
resp_len = sizeof (dr_mem_hdr_t);
}
DR_DBG_MEM("outgoing response:\n");
/* send back the response */
DR_DBG_MEM("ds_send failed\n");
}
/* free any allocated memory */
}
}
/*
* Common routine to config or unconfig multiple mblks.
*
* Note: Do not modify result buffer or length on error.
*/
static int
{
int rv;
int idx;
int count;
int result;
int status;
int se_hint;
int drctl_cmd;
int drctl_flags = 0;
size_t drctl_resp_len = 0;
/*
* Extract all information that is specific
* to the various types of operations.
*/
case DR_MEM_CONFIGURE:
break;
case DR_MEM_UNCONFIGURE:
break;
default:
/* Programming error if we reach this. */
ASSERT(0);
return (-1);
}
/* the incoming array of mblks to operate on */
/* allocate drctl request msg based on incoming resource count */
/* copy the size for the drctl call from the incoming request msg */
}
if (rv != 0) {
DR_DBG_MEM("%s: drctl_config_init returned: %d\n",
return (rv);
}
/* create the result scratch array */
/* perform the specified operation on each of the mblks */
/*
* If no action will be taken against the current
* mblk, update the drctl resource information to
* ensure that it gets recovered properly during
* the drctl fini() call.
*/
continue;
}
/* call the function to perform the actual operation */
/* save off results of the operation */
/* save result for drctl fini() reusing init() msg memory */
DR_DBG_MEM("%s: mblk 0x%lx.0x%lx stat %d result %d off '%s'\n",
}
DR_DBG_MEM("%s: drctl_config_fini returned: %d\n",
/*
* Operation completed without any fatal errors.
* Pack the response for transmission.
*/
/* notify interested parties about the operation */
/*
* Deallocate any scratch memory.
*/
return (0);
}
/*
* Allocate and initialize a result array based on the initial
* drctl operation. A valid result array is always returned.
*/
static dr_mem_res_t *
{
int idx;
char *err_str;
/* allocate zero filled buffer to initialize fields */
/*
* Fill in the result information for each resource.
*/
continue;
/*
* Update the state information for this mblk.
*/
/*
* If an error string exists, copy it out of the
* message buffer. This eliminates any dependency
* on the memory allocated for the message buffer
* itself.
*/
}
}
return (res);
}
static void
{
int idx;
/* deallocate the error string if present */
}
}
/* deallocate the result array itself */
}
/*
* Allocate and pack a response message for transmission based
* on the specified result array. A valid response message and
* valid size information is always returned.
*/
static size_t
{
int idx;
/*
* Calculate the size of the response message
* and allocate an appropriately sized buffer.
*/
resp_len = sizeof (dr_mem_hdr_t);
/* add the stat array size */
/* add the size of any error strings */
}
}
/* allocate the message buffer */
/*
* Fill in the header information.
*/
/*
* Fill in the stat information.
*/
/* string offsets start immediately after stat array */
/* copy over the error string */
}
}
/* buffer should be exactly filled */
return (resp_len);
}
static void
{
DR_DBG_MEM("dr_mem_query...\n");
if (!mq.phys_pages)
return;
/*
* Set to the max byte offset within the page.
*/
}
/*
* Do not modify result buffer or length on error.
*/
static int
{
int idx;
int rlen;
int nml;
drctl_block();
/* the incoming array of req_mblks to configure */
/* allocate a response message, should be freed by caller */
nml = 0;
rlen = sizeof (dr_mem_hdr_t);
/*
* Request is for domain's full view of it's memory.
*/
nml++;
} else {
}
/* fill in the known data */
/* stat array for the response */
/* get the status for each of the mblocks */
if (nml) {
}
} else {
}
return (0);
}
static int
{
int rv;
switch (err) {
case KPHYSM_OK:
rv = DR_MEM_RES_OK;
break;
case KPHYSM_ESPAN:
break;
case KPHYSM_EFAULT:
break;
case KPHYSM_ERESOURCE:
break;
case KPHYSM_ENOTSUP:
case KPHYSM_ENOHANDLES:
break;
case KPHYSM_ENONRELOC:
break;
case KPHYSM_EHANDLE:
break;
case KPHYSM_EBUSY:
break;
case KPHYSM_ENOTVIABLE:
break;
case KPHYSM_ESEQUENCE:
break;
case KPHYSM_ENOWORK:
break;
case KPHYSM_ECANCELLED:
break;
case KPHYSM_EREFUSED:
break;
case KPHYSM_ENOTFINISHED:
case KPHYSM_ENOTRUNNING:
break;
case KPHYSM_EDUP:
break;
default:
break;
}
return (rv);
}
static int
{
int rv;
rv = 0;
DR_DBG_MEM("dr_mem_configure...\n");
if (!MBLK_IS_VALID(mbp)) {
DR_DBG_MEM("failed to find mblk 0x%lx.0x%lx (%d)\n",
} else {
}
} else {
if (rv) {
} else {
}
}
return (rv);
}
static int
{
int rv;
DR_DBG_MEM("dr_mem_unconfigure...\n");
if (!MBLK_IS_VALID(mbp)) {
DR_DBG_MEM("invalid mblk 0x%lx.0x%lx\n",
} else {
rv = DR_MEM_RES_OK;
DR_DBG_MEM("mblk 0x%lx.0x%lx unconfigured\n",
}
return (rv);
}
static int
{
int status;
int rlen;
/*
* If a mem delete is in progress, get its status.
*/
/* allocate a response message, should be freed by caller */
rlen = sizeof (dr_mem_hdr_t);
/* fill in the known data */
if (status) {
/* stat struct for the response */
}
return (0);
}
static int
{
int rlen;
/* allocate a response message, should be freed by caller */
rlen = sizeof (dr_mem_hdr_t);
/* fill in the known data */
return (0);
}
static int
{
int num_nodes;
int rv = 0;
int listsz;
char *found = "found";
DR_DBG_MEM("unable to initialize machine description\n");
return (-1);
}
if (memnode == MDE_INVAL_ELEM_COOKIE) {
found = "not found";
}
(void) md_fini_handle(mdp);
return (rv);
}
/*
* Look up a particular mblk in the MD. Returns the mde_cookie_t
* representing that mblk if present, and MDE_INVAL_ELEM_COOKIE
* otherwise. It is assumed the scratch array has already been
* allocated so that it can accommodate the worst case scenario,
* every node in the MD.
*/
static mde_cookie_t
{
int idx;
int nnodes;
/*
* Scan the DAG for all the mem nodes
*/
if (nnodes < 0) {
DR_DBG_MEM("Scan for mblks failed\n");
return (result);
}
/*
* Find the mblk of interest
*/
DR_DBG_MEM("Missing 'base' property for mblk node %d\n",
idx);
break;
}
DR_DBG_MEM("Missing 'size' property for mblk node %d\n",
idx);
break;
}
/* found a match */
DR_DBG_MEM("dr_mem_find_node_md: found mblk "
break;
}
}
if (result == MDE_INVAL_ELEM_COOKIE) {
DR_DBG_MEM("mblk 0x%lx.0x%lx not in MD\n",
}
return (result);
}
static int
{
if (npgs == 0)
return (DR_MEM_RES_OK);
rv);
}
return (rv);
}
static void
{
}
static int
{
int convert = 1;
if (npgs == 0)
return (DR_MEM_RES_OK);
return (rv);
}
!= KPHYSM_OK) {
goto done;
}
if (mq.nonrelocatable) {
DR_DBG_MEM("%s: non-reloc pages = %ld",
goto done;
}
switch (rv) {
case EBUSY:
break;
default:
break;
}
convert = 0; /* conversion done */
goto done;
} else {
del_range++;
}
goto done;
}
!= MEML_SPANOP_OK) {
switch (rv) {
case MEML_SPANOP_ESPAN:
break;
case MEML_SPANOP_EALLOC:
break;
default:
break;
}
convert = 0; /* conversion done */
goto done;
}
/*
* Since we've called drctl_config_init, we are the only
* DR ctl operation in progress. Set dr_mh to the
* delete memhandle for use by stat and cancel.
*/
/*
* Wait for completion or interrupt.
*/
/*
* There is a pending signal.
*/
(void) kphysm_del_cancel(mh);
/*
* Wait for completion.
*/
}
}
} else {
}
done:
/*
* Add back the spans to the kcage growth list.
*/
}
if (convert)
return (rv);
}